summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS12
-rw-r--r--COPYING340
-rw-r--r--MAINTAINERS11
-rw-r--r--Makefile.am116
-rw-r--r--NEWS2810
-rw-r--r--README21
-rw-r--r--TODO3
-rwxr-xr-xautogen.sh23
-rw-r--r--backend/Makefile.am40
-rw-r--r--backend/backend.symbols1
-rw-r--r--backend/comics/Makefile.am32
-rw-r--r--backend/comics/comics-document.c936
-rw-r--r--backend/comics/comics-document.h38
-rw-r--r--backend/comics/comicsdocument.evince-backend.in4
-rw-r--r--backend/djvu/Makefile.am38
-rw-r--r--backend/djvu/djvu-document-private.h48
-rw-r--r--backend/djvu/djvu-document.c706
-rw-r--r--backend/djvu/djvu-document.h38
-rw-r--r--backend/djvu/djvu-links.c434
-rw-r--r--backend/djvu/djvu-links.h35
-rw-r--r--backend/djvu/djvu-text-page.c445
-rw-r--r--backend/djvu/djvu-text-page.h59
-rw-r--r--backend/djvu/djvudocument.evince-backend.in4
-rw-r--r--backend/dvi/Makefile.am47
-rw-r--r--backend/dvi/cairo-device.c373
-rw-r--r--backend/dvi/cairo-device.h41
-rw-r--r--backend/dvi/dvi-document.c610
-rw-r--r--backend/dvi/dvi-document.h38
-rw-r--r--backend/dvi/dvidocument.evince-backend.in4
-rw-r--r--backend/dvi/fonts.c57
-rw-r--r--backend/dvi/fonts.h6
-rw-r--r--backend/dvi/mdvi-lib/Makefile.am43
-rw-r--r--backend/dvi/mdvi-lib/afmparse.c1303
-rw-r--r--backend/dvi/mdvi-lib/afmparse.h307
-rw-r--r--backend/dvi/mdvi-lib/bitmap.c1035
-rw-r--r--backend/dvi/mdvi-lib/bitmap.h145
-rw-r--r--backend/dvi/mdvi-lib/color.c136
-rw-r--r--backend/dvi/mdvi-lib/color.h34
-rw-r--r--backend/dvi/mdvi-lib/common.c168
-rw-r--r--backend/dvi/mdvi-lib/common.h285
-rw-r--r--backend/dvi/mdvi-lib/defaults.h71
-rw-r--r--backend/dvi/mdvi-lib/dviopcodes.h72
-rw-r--r--backend/dvi/mdvi-lib/dviread.c1585
-rw-r--r--backend/dvi/mdvi-lib/files.c80
-rw-r--r--backend/dvi/mdvi-lib/font.c519
-rw-r--r--backend/dvi/mdvi-lib/fontmap.c1174
-rw-r--r--backend/dvi/mdvi-lib/fontmap.h82
-rw-r--r--backend/dvi/mdvi-lib/fontsrch.c371
-rw-r--r--backend/dvi/mdvi-lib/gf.c395
-rw-r--r--backend/dvi/mdvi-lib/hash.c224
-rw-r--r--backend/dvi/mdvi-lib/hash.h49
-rw-r--r--backend/dvi/mdvi-lib/list.c120
-rw-r--r--backend/dvi/mdvi-lib/mdvi.h623
-rw-r--r--backend/dvi/mdvi-lib/pagesel.c491
-rw-r--r--backend/dvi/mdvi-lib/paper.c171
-rw-r--r--backend/dvi/mdvi-lib/paper.h32
-rw-r--r--backend/dvi/mdvi-lib/pk.c570
-rw-r--r--backend/dvi/mdvi-lib/private.h65
-rw-r--r--backend/dvi/mdvi-lib/setup.c48
-rw-r--r--backend/dvi/mdvi-lib/sp-epsf.c311
-rw-r--r--backend/dvi/mdvi-lib/special.c249
-rw-r--r--backend/dvi/mdvi-lib/sysdeps.h116
-rw-r--r--backend/dvi/mdvi-lib/t1.c630
-rw-r--r--backend/dvi/mdvi-lib/tfm.c214
-rw-r--r--backend/dvi/mdvi-lib/tfmfile.c747
-rw-r--r--backend/dvi/mdvi-lib/tt.c495
-rw-r--r--backend/dvi/mdvi-lib/util.c550
-rw-r--r--backend/dvi/mdvi-lib/vf.c241
-rw-r--r--backend/dvi/texmfcnf.c64
-rw-r--r--backend/dvi/texmfcnf.h34
-rw-r--r--backend/impress/Makefile.am57
-rw-r--r--backend/impress/common.h40
-rw-r--r--backend/impress/document.c140
-rw-r--r--backend/impress/f_oasis.c170
-rw-r--r--backend/impress/f_oo13.c181
-rw-r--r--backend/impress/iksemel.c1882
-rw-r--r--backend/impress/iksemel.h402
-rw-r--r--backend/impress/imposter.h84
-rw-r--r--backend/impress/impress-document.c548
-rw-r--r--backend/impress/impress-document.h39
-rw-r--r--backend/impress/impressdocument.evince-backend.in4
-rw-r--r--backend/impress/internal.h85
-rw-r--r--backend/impress/r_back.c46
-rw-r--r--backend/impress/r_draw.c120
-rw-r--r--backend/impress/r_geometry.c208
-rw-r--r--backend/impress/r_gradient.c387
-rw-r--r--backend/impress/r_style.c111
-rw-r--r--backend/impress/r_text.c386
-rw-r--r--backend/impress/render.c54
-rw-r--r--backend/impress/zip.c349
-rw-r--r--backend/impress/zip.h18
-rw-r--r--backend/pdf/Makefile.am34
-rw-r--r--backend/pdf/ev-poppler.cc3290
-rw-r--r--backend/pdf/ev-poppler.h40
-rw-r--r--backend/pdf/pdfdocument.evince-backend.in6
-rw-r--r--backend/pixbuf/Makefile.am30
-rw-r--r--backend/pixbuf/pixbuf-document.c208
-rw-r--r--backend/pixbuf/pixbuf-document.h38
-rw-r--r--backend/pixbuf/pixbufdocument.evince-backend.in4
-rw-r--r--backend/ps/Makefile.am32
-rw-r--r--backend/ps/ev-spectre.c473
-rw-r--r--backend/ps/ev-spectre.h48
-rw-r--r--backend/ps/psdocument.evince-backend.in5
-rw-r--r--backend/tiff/Makefile.am33
-rw-r--r--backend/tiff/tiff-document.c533
-rw-r--r--backend/tiff/tiff-document.h38
-rw-r--r--backend/tiff/tiff2ps.c1872
-rw-r--r--backend/tiff/tiff2ps.h30
-rw-r--r--backend/tiff/tiffdocument.evince-backend.in4
-rw-r--r--config.h.in~191
-rw-r--r--configure.ac884
-rw-r--r--cut-n-paste/Makefile.am3
-rw-r--r--cut-n-paste/gimpcellrenderertoggle/Makefile.am38
-rw-r--r--cut-n-paste/gimpcellrenderertoggle/gimpcellrenderertoggle.c492
-rw-r--r--cut-n-paste/gimpcellrenderertoggle/gimpcellrenderertoggle.h77
-rw-r--r--cut-n-paste/gimpcellrenderertoggle/gimpwidgetsmarshal.list26
-rw-r--r--cut-n-paste/smclient/Makefile.am42
-rw-r--r--cut-n-paste/smclient/eggdesktopfile.c1477
-rw-r--r--cut-n-paste/smclient/eggdesktopfile.h159
-rw-r--r--cut-n-paste/smclient/eggsmclient-osx.c235
-rw-r--r--cut-n-paste/smclient/eggsmclient-private.h53
-rw-r--r--cut-n-paste/smclient/eggsmclient-win32.c353
-rw-r--r--cut-n-paste/smclient/eggsmclient-xsmp.c1370
-rw-r--r--cut-n-paste/smclient/eggsmclient.c589
-rw-r--r--cut-n-paste/smclient/eggsmclient.h117
-rw-r--r--cut-n-paste/synctex/Makefile.am14
-rw-r--r--cut-n-paste/synctex/synctex_parser.c4171
-rw-r--r--cut-n-paste/synctex/synctex_parser.h345
-rw-r--r--cut-n-paste/synctex/synctex_parser_utils.c462
-rw-r--r--cut-n-paste/synctex/synctex_parser_utils.h123
-rw-r--r--cut-n-paste/toolbar-editor/Makefile.am108
-rw-r--r--cut-n-paste/toolbar-editor/egg-editable-toolbar.c1828
-rw-r--r--cut-n-paste/toolbar-editor/egg-editable-toolbar.h91
-rw-r--r--cut-n-paste/toolbar-editor/egg-toolbar-editor.c672
-rw-r--r--cut-n-paste/toolbar-editor/egg-toolbar-editor.h63
-rw-r--r--cut-n-paste/toolbar-editor/egg-toolbars-model.c987
-rw-r--r--cut-n-paste/toolbar-editor/egg-toolbars-model.h190
-rw-r--r--cut-n-paste/toolbar-editor/eggmarshalers.list1
-rw-r--r--cut-n-paste/totem-screensaver/Makefile.am16
-rw-r--r--cut-n-paste/totem-screensaver/README3
-rw-r--r--cut-n-paste/totem-screensaver/totem-scrsaver.c554
-rw-r--r--cut-n-paste/totem-screensaver/totem-scrsaver.h57
-rw-r--r--cut-n-paste/zoom-control/Makefile.am16
-rw-r--r--cut-n-paste/zoom-control/ephy-zoom-action.c336
-rw-r--r--cut-n-paste/zoom-control/ephy-zoom-action.h68
-rw-r--r--cut-n-paste/zoom-control/ephy-zoom-control.c345
-rw-r--r--cut-n-paste/zoom-control/ephy-zoom-control.h63
-rw-r--r--cut-n-paste/zoom-control/ephy-zoom.c68
-rw-r--r--cut-n-paste/zoom-control/ephy-zoom.h81
-rw-r--r--data/Makefile.am109
-rw-r--r--data/evince-previewer-ui.xml34
-rw-r--r--data/evince-toolbar.xml39
-rw-r--r--data/evince-ui.xml138
-rw-r--r--data/evince.141
-rw-r--r--data/evince.convert2
-rw-r--r--data/evince.desktop.in.in17
-rw-r--r--data/evince.icobin0 -> 24998 bytes
-rw-r--r--data/hand-open.pngbin0 -> 1088 bytes
-rw-r--r--data/icons/16x16/Makefile.am3
-rw-r--r--data/icons/16x16/actions/Makefile.am27
-rw-r--r--data/icons/16x16/actions/object-rotate-left.pngbin0 -> 607 bytes
-rw-r--r--data/icons/16x16/actions/object-rotate-right.pngbin0 -> 632 bytes
-rw-r--r--data/icons/16x16/actions/view-page-continuous.pngbin0 -> 446 bytes
-rw-r--r--data/icons/16x16/actions/view-page-continuous.xcfbin0 -> 2190 bytes
-rw-r--r--data/icons/16x16/actions/view-page-facing.pngbin0 -> 471 bytes
-rw-r--r--data/icons/16x16/actions/view-page-facing.xcfbin0 -> 2158 bytes
-rw-r--r--data/icons/16x16/actions/zoom-fit-height.pngbin0 -> 811 bytes
-rw-r--r--data/icons/16x16/actions/zoom-fit-page.svg282
-rw-r--r--data/icons/16x16/actions/zoom-fit-width.pngbin0 -> 771 bytes
-rw-r--r--data/icons/16x16/actions/zoom-fit-width.svg278
-rw-r--r--data/icons/16x16/actions/zoom.pngbin0 -> 876 bytes
-rw-r--r--data/icons/16x16/actions/zoom.svg247
-rw-r--r--data/icons/16x16/apps/Makefile.am16
-rw-r--r--data/icons/16x16/apps/evince.pngbin0 -> 840 bytes
-rw-r--r--data/icons/16x16/mimetypes/Makefile.am14
-rw-r--r--data/icons/16x16/mimetypes/x-office-presentation.pngbin0 -> 668 bytes
-rw-r--r--data/icons/22x22/Makefile.am3
-rw-r--r--data/icons/22x22/actions/Makefile.am28
-rw-r--r--data/icons/22x22/actions/eye.pngbin0 -> 617 bytes
-rw-r--r--data/icons/22x22/actions/object-rotate-left.pngbin0 -> 911 bytes
-rw-r--r--data/icons/22x22/actions/object-rotate-right.pngbin0 -> 939 bytes
-rw-r--r--data/icons/22x22/actions/view-page-continuous.pngbin0 -> 722 bytes
-rw-r--r--data/icons/22x22/actions/view-page-continuous.xcfbin0 -> 4004 bytes
-rw-r--r--data/icons/22x22/actions/view-page-facing.pngbin0 -> 483 bytes
-rw-r--r--data/icons/22x22/actions/view-page-facing.xcfbin0 -> 1871 bytes
-rw-r--r--data/icons/22x22/actions/zoom-fit-page.pngbin0 -> 1377 bytes
-rw-r--r--data/icons/22x22/actions/zoom-fit-page.svg254
-rw-r--r--data/icons/22x22/actions/zoom-fit-width.pngbin0 -> 1405 bytes
-rw-r--r--data/icons/22x22/actions/zoom-fit-width.svg252
-rw-r--r--data/icons/22x22/actions/zoom.pngbin0 -> 1294 bytes
-rw-r--r--data/icons/22x22/actions/zoom.svg215
-rw-r--r--data/icons/22x22/apps/Makefile.am16
-rw-r--r--data/icons/22x22/apps/evince.pngbin0 -> 1209 bytes
-rw-r--r--data/icons/22x22/mimetypes/Makefile.am14
-rw-r--r--data/icons/22x22/mimetypes/x-office-presentation.pngbin0 -> 1024 bytes
-rw-r--r--data/icons/24x24/Makefile.am3
-rw-r--r--data/icons/24x24/actions/Makefile.am23
-rw-r--r--data/icons/24x24/actions/object-rotate-left.pngbin0 -> 924 bytes
-rw-r--r--data/icons/24x24/actions/object-rotate-right.pngbin0 -> 943 bytes
-rw-r--r--data/icons/24x24/actions/stock_filters-invert.pngbin0 -> 402 bytes
-rw-r--r--data/icons/24x24/actions/view-page-continuous.pngbin0 -> 724 bytes
-rw-r--r--data/icons/24x24/actions/view-page-facing.pngbin0 -> 471 bytes
-rw-r--r--data/icons/24x24/actions/zoom-fit-height.pngbin0 -> 1146 bytes
-rw-r--r--data/icons/24x24/actions/zoom-fit-width.pngbin0 -> 1170 bytes
-rw-r--r--data/icons/24x24/actions/zoom.pngbin0 -> 1344 bytes
-rw-r--r--data/icons/24x24/apps/Makefile.am16
-rw-r--r--data/icons/24x24/apps/evince.pngbin0 -> 1226 bytes
-rw-r--r--data/icons/24x24/mimetypes/Makefile.am14
-rw-r--r--data/icons/24x24/mimetypes/x-office-presentation.pngbin0 -> 1643 bytes
-rw-r--r--data/icons/32x32/Makefile.am3
-rw-r--r--data/icons/32x32/actions/Makefile.am19
-rw-r--r--data/icons/32x32/actions/object-rotate-left.pngbin0 -> 1319 bytes
-rw-r--r--data/icons/32x32/actions/object-rotate-right.pngbin0 -> 1353 bytes
-rw-r--r--data/icons/32x32/actions/view-page-continuous.pngbin0 -> 849 bytes
-rw-r--r--data/icons/32x32/actions/view-page-facing.pngbin0 -> 943 bytes
-rw-r--r--data/icons/32x32/mimetypes/Makefile.am14
-rw-r--r--data/icons/32x32/mimetypes/x-office-presentation.pngbin0 -> 1618 bytes
-rw-r--r--data/icons/48x48/Makefile.am4
-rw-r--r--data/icons/48x48/actions/Makefile.am20
-rw-r--r--data/icons/48x48/actions/close.pngbin0 -> 363 bytes
-rw-r--r--data/icons/48x48/actions/resize-se.pngbin0 -> 312 bytes
-rw-r--r--data/icons/48x48/actions/resize-sw.pngbin0 -> 295 bytes
-rw-r--r--data/icons/48x48/actions/view-page-continuous.pngbin0 -> 1494 bytes
-rw-r--r--data/icons/48x48/actions/view-page-facing.pngbin0 -> 1485 bytes
-rw-r--r--data/icons/48x48/apps/Makefile.am16
-rw-r--r--data/icons/48x48/apps/evince.pngbin0 -> 3314 bytes
-rw-r--r--data/icons/Makefile.am4
-rw-r--r--data/icons/scalable/Makefile.am3
-rw-r--r--data/icons/scalable/actions/Makefile.am15
-rw-r--r--data/icons/scalable/actions/object-rotate-left.svg208
-rw-r--r--data/icons/scalable/actions/object-rotate-right.svg210
-rw-r--r--data/icons/scalable/apps/Makefile.am16
-rw-r--r--data/icons/scalable/apps/evince.svg500
-rw-r--r--data/icons/scalable/mimetypes/Makefile.am14
-rw-r--r--data/icons/scalable/mimetypes/x-office-presentation.svg686
-rw-r--r--data/org.mate.Evince.gschema.xml.in51
-rw-r--r--data/org.mate.evince.Daemon.service.in3
-rw-r--r--evince-document.h62
-rw-r--r--evince-document.pc.in12
-rw-r--r--evince-view.h34
-rw-r--r--evince-view.pc.in11
-rw-r--r--help/C/evince.xml774
-rw-r--r--help/C/figures/evince_start_window.pngbin0 -> 17271 bytes
-rw-r--r--help/C/legal.xml76
-rw-r--r--help/ChangeLog362
-rw-r--r--help/Makefile.am17
-rw-r--r--help/bg/bg.po1015
-rw-r--r--help/bg/figures/evince_start_window.pngbin0 -> 22691 bytes
-rw-r--r--help/ca/ca.po1281
-rw-r--r--help/ca/figures/evince_start_window.pngbin0 -> 15084 bytes
-rw-r--r--help/cs/cs.po1257
-rw-r--r--help/cs/figures/evince_start_window.pngbin0 -> 16696 bytes
-rw-r--r--help/de/de.po1275
-rw-r--r--help/de/figures/evince_start_window.pngbin0 -> 12327 bytes
-rw-r--r--help/el/el.po943
-rw-r--r--help/el/figures/evince_start_window.pngbin0 -> 21766 bytes
-rw-r--r--help/en_GB/en_GB.po1256
-rw-r--r--help/es/es.po1281
-rw-r--r--help/es/figures/evince_start_window.pngbin0 -> 16965 bytes
-rw-r--r--help/eu/eu.po626
-rw-r--r--help/eu/figures/evince_start_window.pngbin0 -> 18535 bytes
-rw-r--r--help/evince.omf.in11
-rw-r--r--help/fi/fi.po1244
-rw-r--r--help/fi/figures/evince_start_window.pngbin0 -> 16351 bytes
-rw-r--r--help/fr/figures/evince_start_window.pngbin0 -> 15794 bytes
-rw-r--r--help/fr/fr.po1297
-rw-r--r--help/it/figures/evince_start_window.pngbin0 -> 13779 bytes
-rw-r--r--help/it/it.po1285
-rw-r--r--help/ja/figures/evince_start_window.pngbin0 -> 16465 bytes
-rw-r--r--help/ja/ja.po948
-rw-r--r--help/nl/figures/evince_start_window.pngbin0 -> 10972 bytes
-rw-r--r--help/nl/nl.po929
-rw-r--r--help/oc/oc.po817
-rw-r--r--help/pt_BR/pt_BR.po1272
-rw-r--r--help/reference/Makefile.am2
-rw-r--r--help/reference/libdocument/Makefile.am132
-rw-r--r--help/reference/libdocument/libevdocument-docs.xml191
-rw-r--r--help/reference/libdocument/libevdocument-overrides.txt0
-rw-r--r--help/reference/libdocument/libevdocument-sections.txt781
-rw-r--r--help/reference/libdocument/libevdocument.types55
-rw-r--r--help/reference/libdocument/version.xml.in1
-rw-r--r--help/reference/libview/Makefile.am139
-rw-r--r--help/reference/libview/libevview-docs.xml145
-rw-r--r--help/reference/libview/libevview-overrides.txt0
-rw-r--r--help/reference/libview/libevview-sections.txt338
-rw-r--r--help/reference/libview/libevview.types29
-rw-r--r--help/reference/libview/version.xml.in1
-rw-r--r--help/reference/shell/Makefile.am150
-rw-r--r--help/reference/shell/evince-docs.xml163
-rw-r--r--help/reference/shell/evince-overrides.txt0
-rw-r--r--help/reference/shell/evince-sections.txt472
-rw-r--r--help/reference/shell/evince.types24
-rw-r--r--help/reference/shell/version.xml.in1
-rw-r--r--help/ru/ru.po1238
-rw-r--r--help/sl/sl.po906
-rw-r--r--help/sr/sr.po1626
-rw-r--r--help/sv/figures/evince_start_window.pngbin0 -> 17118 bytes
-rw-r--r--help/sv/sv.po900
-rw-r--r--help/uk/figures/evince_start_window.pngbin0 -> 14049 bytes
-rw-r--r--help/uk/uk.po998
-rw-r--r--help/vi/vi.po898
-rw-r--r--help/zh_CN/zh_CN.po1183
-rw-r--r--libdocument/Makefile.am193
-rw-r--r--libdocument/ev-annotation.c1002
-rw-r--r--libdocument/ev-annotation.h161
-rw-r--r--libdocument/ev-async-renderer.c65
-rw-r--r--libdocument/ev-async-renderer.h65
-rw-r--r--libdocument/ev-attachment.c439
-rw-r--r--libdocument/ev-attachment.h79
-rw-r--r--libdocument/ev-backends-manager.c333
-rw-r--r--libdocument/ev-backends-manager.h48
-rw-r--r--libdocument/ev-debug.c177
-rw-r--r--libdocument/ev-debug.h106
-rw-r--r--libdocument/ev-document-annotations.c75
-rw-r--r--libdocument/ev-document-annotations.h99
-rw-r--r--libdocument/ev-document-attachments.c49
-rw-r--r--libdocument/ev-document-attachments.h59
-rw-r--r--libdocument/ev-document-factory.c397
-rw-r--r--libdocument/ev-document-factory.h39
-rw-r--r--libdocument/ev-document-find.c42
-rw-r--r--libdocument/ev-document-find.h65
-rw-r--r--libdocument/ev-document-fonts.c59
-rw-r--r--libdocument/ev-document-fonts.h78
-rw-r--r--libdocument/ev-document-forms.c161
-rw-r--r--libdocument/ev-document-forms.h128
-rw-r--r--libdocument/ev-document-images.c47
-rw-r--r--libdocument/ev-document-images.h65
-rw-r--r--libdocument/ev-document-info.h154
-rw-r--r--libdocument/ev-document-layers.c74
-rw-r--r--libdocument/ev-document-layers.h85
-rw-r--r--libdocument/ev-document-links.c129
-rw-r--r--libdocument/ev-document-links.h87
-rw-r--r--libdocument/ev-document-misc.c400
-rw-r--r--libdocument/ev-document-misc.h67
-rw-r--r--libdocument/ev-document-print.c41
-rw-r--r--libdocument/ev-document-print.h59
-rw-r--r--libdocument/ev-document-security.c48
-rw-r--r--libdocument/ev-document-security.h67
-rw-r--r--libdocument/ev-document-text.c70
-rw-r--r--libdocument/ev-document-text.h75
-rw-r--r--libdocument/ev-document-thumbnails.c63
-rw-r--r--libdocument/ev-document-thumbnails.h69
-rw-r--r--libdocument/ev-document-transition.c57
-rw-r--r--libdocument/ev-document-transition.h64
-rw-r--r--libdocument/ev-document-type-builtins.c.template44
-rw-r--r--libdocument/ev-document-type-builtins.h.template29
-rw-r--r--libdocument/ev-document.c867
-rw-r--r--libdocument/ev-document.h303
-rw-r--r--libdocument/ev-file-exporter.c84
-rw-r--r--libdocument/ev-file-exporter.h101
-rw-r--r--libdocument/ev-file-helpers.c683
-rw-r--r--libdocument/ev-file-helpers.h71
-rw-r--r--libdocument/ev-form-field.c209
-rw-r--r--libdocument/ev-form-field.h214
-rw-r--r--libdocument/ev-image.c181
-rw-r--r--libdocument/ev-image.h68
-rw-r--r--libdocument/ev-init.c154
-rw-r--r--libdocument/ev-init.h41
-rw-r--r--libdocument/ev-layer.c86
-rw-r--r--libdocument/ev-layer.h62
-rw-r--r--libdocument/ev-link-action.c341
-rw-r--r--libdocument/ev-link-action.h73
-rw-r--r--libdocument/ev-link-dest.c511
-rw-r--r--libdocument/ev-link-dest.h95
-rw-r--r--libdocument/ev-link.c202
-rw-r--r--libdocument/ev-link.h55
-rw-r--r--libdocument/ev-mapping-list.c146
-rw-r--r--libdocument/ev-mapping-list.h53
-rw-r--r--libdocument/ev-module.c196
-rw-r--r--libdocument/ev-module.h74
-rw-r--r--libdocument/ev-page.c62
-rw-r--r--libdocument/ev-page.h63
-rw-r--r--libdocument/ev-render-context.c101
-rw-r--r--libdocument/ev-render-context.h70
-rw-r--r--libdocument/ev-selection.c77
-rw-r--r--libdocument/ev-selection.h93
-rw-r--r--libdocument/ev-transition-effect.c224
-rw-r--r--libdocument/ev-transition-effect.h87
-rw-r--r--libdocument/ev-version.h.in69
-rw-r--r--libmisc/Makefile.am22
-rw-r--r--libmisc/ev-page-action-widget.c495
-rw-r--r--libmisc/ev-page-action-widget.h46
-rw-r--r--libmisc/ev-page-action.c239
-rw-r--r--libmisc/ev-page-action.h68
-rw-r--r--libview/Makefile.am163
-rw-r--r--libview/ev-annotation-window.c654
-rw-r--r--libview/ev-annotation-window.h57
-rw-r--r--libview/ev-document-model.c571
-rw-r--r--libview/ev-document-model.h87
-rw-r--r--libview/ev-job-scheduler.c305
-rw-r--r--libview/ev-job-scheduler.h48
-rw-r--r--libview/ev-jobs.c1577
-rw-r--r--libview/ev-jobs.h502
-rw-r--r--libview/ev-loading-window.c278
-rw-r--r--libview/ev-loading-window.h49
-rw-r--r--libview/ev-page-cache.c521
-rw-r--r--libview/ev-page-cache.h71
-rw-r--r--libview/ev-pixbuf-cache.c1192
-rw-r--r--libview/ev-pixbuf-cache.h89
-rw-r--r--libview/ev-print-operation.c2028
-rw-r--r--libview/ev-print-operation.h71
-rw-r--r--libview/ev-stock-icons.c143
-rw-r--r--libview/ev-stock-icons.h57
-rw-r--r--libview/ev-timeline.c450
-rw-r--r--libview/ev-timeline.h90
-rw-r--r--libview/ev-transition-animation.c676
-rw-r--r--libview/ev-transition-animation.h73
-rw-r--r--libview/ev-view-accessible.c911
-rw-r--r--libview/ev-view-accessible.h33
-rw-r--r--libview/ev-view-cursor.c55
-rw-r--r--libview/ev-view-cursor.h47
-rw-r--r--libview/ev-view-marshal.list2
-rw-r--r--libview/ev-view-presentation.c1498
-rw-r--r--libview/ev-view-presentation.h51
-rw-r--r--libview/ev-view-private.h225
-rw-r--r--libview/ev-view-type-builtins.c.template44
-rw-r--r--libview/ev-view-type-builtins.h.template29
-rw-r--r--libview/ev-view.c6274
-rw-r--r--libview/ev-view.h115
-rw-r--r--m4/gtk-doc.m461
-rw-r--r--m4/intltool.m4216
-rw-r--r--m4/libtool.m47831
-rw-r--r--m4/ltoptions.m4369
-rw-r--r--m4/ltsugar.m4123
-rw-r--r--m4/ltversion.m423
-rw-r--r--m4/lt~obsolete.m498
-rw-r--r--m4/mate-doc-utils.m456
-rw-r--r--po/ChangeLog4424
-rw-r--r--po/LINGUAS85
-rw-r--r--po/POTFILES.in66
-rw-r--r--po/POTFILES.skip16
-rw-r--r--po/af.po1423
-rw-r--r--po/ar.po1835
-rw-r--r--po/as.po1624
-rw-r--r--po/ast.po1813
-rw-r--r--po/be.po1354
-rw-r--r--po/[email protected]1428
-rw-r--r--po/bg.po1723
-rw-r--r--po/bn.po1813
-rw-r--r--po/bn_IN.po1647
-rw-r--r--po/br.po1475
-rw-r--r--po/ca.po2211
-rw-r--r--po/[email protected]2211
-rw-r--r--po/cs.po1562
-rw-r--r--po/cy.po1115
-rw-r--r--po/da.po2155
-rw-r--r--po/de.po1812
-rw-r--r--po/dz.po1603
-rw-r--r--po/el.po1709
-rw-r--r--po/[email protected]1537
-rw-r--r--po/en_CA.po1260
-rw-r--r--po/en_GB.po2359
-rw-r--r--po/eo.po1648
-rw-r--r--po/es.po1853
-rw-r--r--po/et.po1344
-rw-r--r--po/eu.po1722
-rw-r--r--po/fa.po1108
-rw-r--r--po/fi.po1764
-rw-r--r--po/fr.po1747
-rw-r--r--po/ga.po1462
-rw-r--r--po/gl.po1757
-rw-r--r--po/gu.po1740
-rw-r--r--po/he.po1719
-rw-r--r--po/hi.po1459
-rw-r--r--po/hu.po1694
-rw-r--r--po/id.po1717
-rw-r--r--po/it.po1742
-rw-r--r--po/ja.po1739
-rw-r--r--po/ka.po1251
-rw-r--r--po/kk.po1672
-rw-r--r--po/kn.po1620
-rw-r--r--po/ko.po1707
-rw-r--r--po/ks.po1393
-rw-r--r--po/ku.po1287
-rw-r--r--po/lt.po1818
-rw-r--r--po/lv.po1783
-rw-r--r--po/mai.po1404
-rw-r--r--po/mg.po1379
-rw-r--r--po/mk.po1331
-rw-r--r--po/ml.po1638
-rw-r--r--po/mn.po1601
-rw-r--r--po/mr.po1711
-rw-r--r--po/ms.po1680
-rw-r--r--po/nb.po1710
-rw-r--r--po/nds.po1464
-rw-r--r--po/ne.po1302
-rw-r--r--po/nl.po1801
-rw-r--r--po/nn.po1920
-rw-r--r--po/oc.po1304
-rw-r--r--po/or.po1598
-rw-r--r--po/pa.po1950
-rw-r--r--po/pl.po1723
-rw-r--r--po/ps.po1241
-rw-r--r--po/pt.po2105
-rw-r--r--po/pt_BR.po1969
-rw-r--r--po/ro.po1797
-rw-r--r--po/ru.po1676
-rw-r--r--po/rw.po841
-rw-r--r--po/si.po1363
-rw-r--r--po/sk.po1323
-rw-r--r--po/sl.po1776
-rw-r--r--po/sq.po1322
-rw-r--r--po/sr.po2123
-rw-r--r--po/[email protected]2123
-rw-r--r--po/sv.po1755
-rw-r--r--po/ta.po1770
-rw-r--r--po/te.po1790
-rw-r--r--po/th.po2196
-rw-r--r--po/tr.po1477
-rw-r--r--po/uk.po1546
-rw-r--r--po/vi.po1424
-rw-r--r--po/wa.po682
-rw-r--r--po/zh_CN.po1853
-rw-r--r--po/zh_HK.po1926
-rw-r--r--po/zh_TW.po1941
-rw-r--r--previewer/Makefile.am37
-rw-r--r--previewer/ev-previewer-window.c723
-rw-r--r--previewer/ev-previewer-window.h51
-rw-r--r--previewer/ev-previewer.c205
-rw-r--r--properties/Makefile.am35
-rw-r--r--properties/ev-properties-main.c159
-rw-r--r--properties/ev-properties-view.c399
-rw-r--r--properties/ev-properties-view.h50
-rw-r--r--shell/Makefile.am160
-rw-r--r--shell/eggfindbar.c740
-rw-r--r--shell/eggfindbar.h82
-rw-r--r--shell/ev-annotation-properties-dialog.c327
-rw-r--r--shell/ev-annotation-properties-dialog.h54
-rw-r--r--shell/ev-application.c1151
-rw-r--r--shell/ev-application.h88
-rw-r--r--shell/ev-convert-metadata.c302
-rw-r--r--shell/ev-daemon.c553
-rw-r--r--shell/ev-file-monitor.c168
-rw-r--r--shell/ev-file-monitor.h57
-rw-r--r--shell/ev-history.c148
-rw-r--r--shell/ev-history.h65
-rw-r--r--shell/ev-keyring.c116
-rw-r--r--shell/ev-keyring.h37
-rw-r--r--shell/ev-marshal.list1
-rw-r--r--shell/ev-media-player-keys.c233
-rw-r--r--shell/ev-media-player-keys.h49
-rw-r--r--shell/ev-message-area.c354
-rw-r--r--shell/ev-message-area.h74
-rw-r--r--shell/ev-metadata.c313
-rw-r--r--shell/ev-metadata.h69
-rw-r--r--shell/ev-navigation-action-widget.c214
-rw-r--r--shell/ev-navigation-action-widget.h46
-rw-r--r--shell/ev-navigation-action.c247
-rw-r--r--shell/ev-navigation-action.h64
-rw-r--r--shell/ev-open-recent-action.c101
-rw-r--r--shell/ev-open-recent-action.h52
-rw-r--r--shell/ev-password-view.c434
-rw-r--r--shell/ev-password-view.h59
-rw-r--r--shell/ev-progress-message-area.c198
-rw-r--r--shell/ev-progress-message-area.h64
-rw-r--r--shell/ev-properties-dialog.c138
-rw-r--r--shell/ev-properties-dialog.h47
-rw-r--r--shell/ev-properties-fonts.c224
-rw-r--r--shell/ev-properties-fonts.h48
-rw-r--r--shell/ev-properties-license.c169
-rw-r--r--shell/ev-properties-license.h50
-rw-r--r--shell/ev-sidebar-annotations.c542
-rw-r--r--shell/ev-sidebar-annotations.h62
-rw-r--r--shell/ev-sidebar-attachments.c707
-rw-r--r--shell/ev-sidebar-attachments.h62
-rw-r--r--shell/ev-sidebar-layers.c411
-rw-r--r--shell/ev-sidebar-layers.h58
-rw-r--r--shell/ev-sidebar-links.c760
-rw-r--r--shell/ev-sidebar-links.h66
-rw-r--r--shell/ev-sidebar-page.c92
-rw-r--r--shell/ev-sidebar-page.h64
-rw-r--r--shell/ev-sidebar-thumbnails.c974
-rw-r--r--shell/ev-sidebar-thumbnails.h59
-rw-r--r--shell/ev-sidebar.c543
-rw-r--r--shell/ev-sidebar.h67
-rw-r--r--shell/ev-utils.c389
-rw-r--r--shell/ev-utils.h47
-rw-r--r--shell/ev-window-title.c203
-rw-r--r--shell/ev-window-title.h47
-rw-r--r--shell/ev-window.c7149
-rw-r--r--shell/ev-window.h93
-rw-r--r--shell/evince-icon.rc1
-rw-r--r--shell/main.c328
-rw-r--r--test/Makefile.am19
-rw-r--r--test/test-encrypt.pdfbin0 -> 26501 bytes
-rw-r--r--test/test-links.pdfbin0 -> 15151 bytes
-rw-r--r--test/test-mime.binbin0 -> 15151 bytes
-rw-r--r--test/test-page-labels.pdfbin0 -> 10150 bytes
-rwxr-xr-xtest/test1.py36
-rwxr-xr-xtest/test2.py28
-rwxr-xr-xtest/test3.py15
-rwxr-xr-xtest/test4.py20
-rwxr-xr-xtest/test5.py24
-rwxr-xr-xtest/test6.py31
-rwxr-xr-xtest/test7.py33
-rw-r--r--thumbnailer/Makefile.am83
-rw-r--r--thumbnailer/evince-thumbnailer-comics.schemas.in106
-rw-r--r--thumbnailer/evince-thumbnailer-djvu.schemas.in30
-rw-r--r--thumbnailer/evince-thumbnailer-dvi.schemas.in83
-rw-r--r--thumbnailer/evince-thumbnailer-ps.schemas.in154
-rw-r--r--thumbnailer/evince-thumbnailer.c309
-rw-r--r--thumbnailer/evince-thumbnailer.schemas.in80
602 files changed, 287728 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..64fbaefb
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,12 @@
+The Xpdf software and documentation are
+Copyright 1996-2003 Glyph & Cog, LLC.
+
+Martin Kretzschmar <[email protected]>
+Marco Pesenti Gritti <[email protected]>
+Jonathan Blandford <[email protected]>
+Nickolay V. Shmyrev <[email protected]>
+Bryan Clark <[email protected]>
+Carlos Garcia Campos <[email protected]>
+Wouter Bolsterlee <[email protected]>
+
+And many others
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..ecb192b2
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 00000000..3c7bfb8e
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,11 @@
+Jonathan Blandford
+Userid: jrb
+
+Nickolay V. Shmyrev
+Userid: nshmyrev
+
+Carlos Garcia Campos
+Userid: carlosgc
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000..2468dae4
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,116 @@
+ACLOCAL_AMFLAGS = -I m4
+
+# Set the minimum required Windows version to Windows 2000 (0x0500).
+if PLATFORM_WIN32
+AM_CPPFLAGS = -D_WIN32_WINNT=0x0500
+endif
+
+SUBDIRS = \
+ cut-n-paste \
+ data \
+ libdocument \
+ backend \
+ libview \
+ libmisc \
+ properties \
+ shell \
+ po \
+ help
+
+if ENABLE_TESTS
+SUBDIRS += test
+endif
+
+if ENABLE_THUMBNAILER
+SUBDIRS += thumbnailer
+endif
+
+if ENABLE_PREVIEWER
+SUBDIRS += previewer
+endif
+
+NULL =
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = \
+ evince-document-$(EV_API_VERSION).pc \
+ evince-view-$(EV_API_VERSION).pc \
+ $(NULL)
+
+headerdir = $(includedir)/evince/$(EV_API_VERSION)
+header_DATA = \
+ evince-document.h \
+ evince-view.h \
+ $(NULL)
+
+# Applications
+
+intltool_extra = intltool-extract.in intltool-merge.in intltool-update.in
+
+EXTRA_DIST = \
+ $(intltool_extra) \
+ $(header_DATA) \
+ autogen.sh \
+ mate-doc-utils.make \
+ MAINTAINERS
+
+DISTCLEANFILES = \
+ mate-doc-utils.make \
+ intltool-extract \
+ intltool-merge \
+ intltool-update
+
+MAINTAINERCLEANFILES = \
+ ChangeLog \
+ $(srcdir)/INSTALL \
+ $(srcdir)/aclocal.m4 \
+ $(srcdir)/autoscan.log \
+ $(srcdir)/compile \
+ $(srcdir)/config.guess \
+ $(srcdir)/config.h.in \
+ $(srcdir)/config.sub \
+ $(srcdir)/configure \
+ $(srcdir)/configure.scan \
+ $(srcdir)/depcomp \
+ $(srcdir)/install-sh \
+ $(srcdir)/ltmain.sh \
+ $(srcdir)/missing \
+ $(srcdir)/mkinstalldirs \
+ $(srcdir)/omf.make \
+ $(srcdir)/xmldocs.make \
+ $(srcdir)/gtk-doc.make \
+ $(srcdir)/po/Makefile.in.in \
+ `find "$(srcdir)" -type f -name Makefile.in -print`
+
+DISTCHECK_CONFIGURE_FLAGS = \
+ --disable-schemas-install \
+ --disable-scrollkeeper \
+ --enable-gtk-doc \
+ --disable-caja \
+ --disable-tests \
+ --disable-silent-rules
+
+# Ignore scrollkeeper issues for now. @#*$& scrollkeeper
+distuninstallcheck_listfiles = find . -type f -print | grep -v scrollkeeper | grep -v /share/mate/help/ | grep -v \.omf
+
+distclean-local:
+ if test "$(srcdir)" = "."; then :; else \
+ rm -f ChangeLog; \
+ fi
+
+ChangeLog:
+ $(AM_V_GEN) if test -d "$(srcdir)/.git"; then \
+ (GIT_DIR=$(top_srcdir)/.git ./missing --run git log -M -C --name-status --date=short --no-color) | fmt --split-only > [email protected] \
+ || ($(RM) [email protected]; \
+ echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \
+ (test -f [email protected] || echo git log is required to generate this file >> [email protected])); \
+ else \
+ test -f [email protected] || \
+ (echo A git checkout and git log is required to generate ChangeLog >&2 && \
+ echo A git checkout and git log is required to generate this file >> [email protected]); \
+ fi
+
+.PHONY: ChangeLog
+
+-include $(top_srcdir)/git.mk
diff --git a/NEWS b/NEWS
new file mode 100644
index 00000000..0af06a59
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,2810 @@
+================
+Evince 2.32.0
+================
+
+Bug fixes:
+
+ * Make "Shrink to Printable Area" default option for Page Scaling
+ (Marek Kasik)
+ * Fix build with --disable-dbus (#629498, Didier Roche)
+
+Translation updates:
+
+ * Damyan Ivanov (bg)
+ * Ivar Smolin (et)
+ * Iñaki Larrañaga Murgoitio (eu)
+ * Milan Bouchet-Valat (fr)
+ * Francesco Marletta (it)
+ * Shushi Kurose (ja)
+ * Changwoo Ryu (ko)
+ * Wouter Bolsterlee (nl)
+ * Piotr Drąg (pl)
+ * Henrique P. Machado (pt_BR)
+ * Lucian Adrian Grijincu (ro)
+ * Yuri Myasoedov (ru)
+
+================
+Evince 2.31.92
+================
+
+Bug fixes:
+
+ * Change gsettings schema path from /apps/evince to
+ /org/mate/evince (Christian Persch)
+ * A D-Bus signal is now emitted when document is loaded (#626561,
+ José Aliste)
+ * Use MateConf again for lockdown settings (#628945, Christian Persch)
+ * Fix a crash during selection when evince is built with gtk2
+ (Carlos Garcia Campos)
+ * Evince daemon can spawn an evince instance when finding a
+ document (#625971, José Aliste)
+
+Translation updates:
+
+ * Khaled Hosny (ar)
+ * Kenneth Nielsen (da)
+ * Christian Kirbach (de)
+ * Michael Kotsarinis (el)
+ * Bruce Cowan (en_GB)
+ * Ivar Smolin (et)
+ * Gabor Kelemen (hu)
+ * Andika Triwidada (id)
+ * Shushi Kurose (ja)
+ * Baurzhan Muftakhidinov (kk)
+ * Aurimas Černius (lt)
+ * Kjartan Maraas (nb)
+ * Duarte Loreto (pt)
+ * Miloš Popović ([email protected])
+ * Милош Поповић (sr)
+ * Daniel Nylander (sv)
+ * 朱涛 (zh_CN)
+ * Chao-Hsiung Liao (zh_HK)
+ * Chao-Hsiung Liao (zh_TW)
+
+================
+Evince 2.31.90
+================
+
+Bug fixes:
+
+ * Do not jump to link dest when clicking with GDK_CONTROL_MASK
+ (#626407, Carlos Garcia Campos)
+ * Use GDK_BLANK_CURSOR instead of creating our own empty cursor
+ (Carlos Garcia Campos)
+ * Improve findbar message when there are no matches (#626334,
+ Carlos Garcia Campos, José Aliste)
+ * Remove #ifdefs for old cairo versions (#626147, Juanjo Marín)
+
+Translation updates:
+
+ * Paul Seyfert (de)
+ * Jorge González (es)
+ * Fran Dieguez (gl)
+ * Yaron Shahrabani (he)
+ * Torstein Adolf Winterseth (nn)
+ * A S Alam (pa)
+ * Matej Urbančič (sl)
+ * Xhacker Liu (zh_CN)
+
+================
+Evince 2.31.6.1
+================
+
+New Features and UI Improvements:
+
+ * Add --with-gtk=2.0|3.0 configure flag (#626030, Vincent Untz)
+
+Bug fixes:
+
+ * Fix a crash with DVI files and empty glyphs (#600552, Marek Kasik)
+ * Fix a crash when a page is manually entered (#624936, Marek Kasik)
+
+Translation updates:
+
+ * Jorge González (es)
+ * Fran Dieguez (gl)
+ * Yaron Shahrabani (he)
+ * Andika Triwidada (id)
+ * Kjartan Maraas (nb)
+ * Matej Urbančič (sl)
+ * Dr.T.Vasudevan (ta)
+
+================
+Evince 2.31.6
+================
+
+New Features and UI Improvements:
+
+ * Preliminary support for adding new annotations (#168304, Carlos
+ Garcia Campos)
+ * Add confirmation dialog on closing window when document has been
+ modified (Form fields have been filled out or annots have been
+ added/modified) (#168304, Carlos Garcia Campos)
+ * Add an action to edit menu to save current settings as
+ default. Default settings will be used only for new documents
+ that don't have metadata (#620325, Carlos Garcia Campos)
+ * Migrate lockdown to gsettings (Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Update synctex parser from TexLive SVN repository (#624532,
+ Marek Kasik)
+ * Fix a crash when loading document with inverted colors enabled
+ (Carlos Garcia Campos)
+ * Several build improvements (Hib Eris)
+ * Fix memory leaks when document is reloaded (Carlos Garcia Campos)
+ * Fix position of window annotations when moved by the user (Jose
+ Aliste, Carlos Garcia Campos)
+ * Fix build with poppler 0.14 (#624904, Carlos Garcia Campos)
+ * Use cairo instead of gdk_draw API (Carlos Garcia Campos)
+ * Only set text and text_layout flags in page cache when a11y
+ might be enabled (Carlos Garcia Campos)
+
+Translation updates:
+
+ * Xandru Armesto (ast)
+ * Mario Blättermann (de)
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Fran Diéguez (gl)
+ * Sweta Kothari (gu)
+ * Yaron Shahrabani (he)
+ * Shankar Prasad (kn)
+ * Sandeep Shedmake (mr)
+ * Kjartan Maraas (nb)
+ * Daniel Nylander (sv)
+ * Krishna Babu K (te)
+ * Eleanor Chen (zh_CN)
+ * Chao-Hsiung Liao (zh_HK)
+ * Chao-Hsiung Liao (zh_TW)
+
+================
+Evince 2.31.5
+================
+
+New Features and UI Improvements:
+
+ * Add a new sidebar page to show the list of annotations (Carlos
+ Garcia Campos)
+ * Finish DBus API for synctex support (#543503, Jose Aliste)
+ * Switch to GTK+ 3
+
+Bug fixes:
+
+ * Make sure annot popups never show up outside the main window
+ * (#604125, Carlos Garcia Campos)
+ * Set word wrapping mode for text in popup annotations (#623456,
+
+Translation updates:
+
+ * Baurzhan Muftakhidinov (kk)
+ * 朱涛 (zh_CN)
+
+================
+Evince 2.31.4.1
+================
+
+Bug fixes:
+
+ * Fix build when poppler_page_get_text_layout is not available
+ (#623080, Carlos Garcia Campos)
+
+================
+Evince 2.31.4
+================
+
+New Features and UI Improvements:
+
+ * Implement AtkText interface in EvView (#309015, Daniel Garcia)
+ * Add preliminary SyncTex support (#543503, Jose Aliste, Carlos
+ Garcia Campos)
+
+Bug fixes:
+
+ * Don't save print document settings into global file (#530018,
+ Carlos Garcia Campos)
+ * Reload presentation view when document changes (#621750, Carlos
+ Garcia Campos)
+ * Remove hard-coded reason in totem-scrsaver (#621386, Bastien
+ Nocera)
+ * Fix opening cbz files with [] in archive (#619248, Juanjo Marín)
+
+Translation updates:
+
+ * Carles Ferrando Garcia (ca)
+ * Kristjan SCHMIDT (eo)
+ * Fran Diéguez (gl)
+ * Kjartan Maraas (nb)
+ * Matej Urbančič (sl)
+ * 杨章 (zh_CN)
+
+================
+Evince 2.31.3
+================
+
+New Features and UI Improvements:
+
+ * Use a dynamic pixbuf cache size based on document page
+ size. It allows us caching more pages for lower scale factors
+ and increase zoom level by caching fewer pages. (#303365, Carlos
+ Garcia Campos)
+
+Bug fixes:
+
+ * Fix fade animations (#619825, Carlos Garcia Campos)
+ * Fix a crash when starting animation in presentation mode
+ (#619948, Carlos Garcia Campos)
+ * Remove set_focus_on_map and set NOTIFICATION type hint for
+ loading window. It prevents focus stealing and skips taskbar and
+ pager hints (#620548, Carlos Garcia Campos)
+ * Fix build with GSEAL enabled (#605776, Carlos Garcia Campos)
+ * Remove circular dependencies in Makefiles and fix some compiler
+ warnings (Hib Eris)
+
+Translation updates:
+
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Fran Diéguez (gl)
+ * Yaron Shahrabani (he)
+ * Kjartan Maraas (nb)
+ * Sira Nokyoongtong (th)
+ * 甘露(Gan Lu) (zh_CN)
+
+================
+Evince 2.31.2
+================
+
+New Features and UI Improvements:
+
+ * Port to GDBus (Christian Persch, Carlos Garcia Campos)
+ * Do not use the mateconf gsettings backend any more (#619335,
+ Matthias Clasen)
+ * Use cairo to draw search results (Carlos Garcia Campos)
+ * Use a loading window to show loading progress information
+ instead of the old 'Loading ...' text rendered on every page
+ (Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Fix localization on Windows (#610548, Hib Eris)
+ * Convert relative path to absolute path for evince-previewer
+ (Marek Kasik)
+ * Fix opening files with '#' in its name (#616515, Carlos Garcia
+ Campos)
+ * Accept keyboard focus in window annotations (Carlos Garcia
+ Campos)
+ * Fix most of the GSEAL build issues (#605776, Carlos Garcia
+ Campos)
+ * Add .c files to the introspection sources (#617736, Christian
+ Persch)
+
+Translation updates:
+
+ * Thomas Thurman ([email protected])
+ * Kjartan Maraas (nb)
+ * Wouter Bolsterlee (nl)
+ * Manoj Kumar Giri (or)
+
+================
+Evince 2.31.1
+================
+
+New Features and UI Improvements:
+
+ * Add saved documents with "save a copy" to recent file list
+ (#617580, Carlos Garcia Campos)
+ * Port override_restrictions mateconf key to GSettings (Carlos Garcia
+ Campos)
+ * Add icons to the recent items, and always show them in menu
+ (#614400, Christian Persch)
+
+Bug fixes:
+
+ * Invert colors of pages and thumbanils when loading in inverted
+ color mode (#616110, #616111, Juanjo Marín)
+ * Take default settings from last document opened. Fixes
+ regression caused by migration to gio metadata (#606090,
+ Carlos Garcia Campos)
+ * Ellipsise the recent action's label (Christian Persch)
+ * Make inverted colors mode work in presentation mode too
+ (#614693, Carlos Garcia Campos)
+ * Respect MATE22_USER_DIR env variable (#613637, Ray Strode)
+ * Fix loading of local documents when uri contains a page
+ destination (#616515, Carlos Garcia Campos)
+ * Make mate-doc-utils optional (Hib Eris)
+ * Update icons to match mate-icon-theme appearance (#614747,
+ Hylke Bons, Carlos Garcia Campos)
+ * Make sure there's a new valid page range before updating caches
+ (fdo#27599, Carlos Garcia Campos)
+ * Fix .desktop file according to CorrectDesktopFiles GNIOME Goal
+ (#592733, Javier Jardón)
+ * Update FSF address everywhere (#514607, Arun Persaud)
+ * Fix loading of compressed password-protected documents (#613959,
+ Carlos Garcia Campos)
+ * Add command line option to open document at a given page index
+ (#613449, Daniel M. German)
+ * Fix build with GSEAL_ENABLE for libview and libdocument
+ (#605776, Hib Eris, Carlos Garcia Campos)
+ * Close previewer window with control + w (#612972, Carlos Garcia
+ Campos)
+ * Fix keybindings in previewer window (#612972, Carlos Garcia
+ Campos)
+
+Translation updates:
+
+ * Runa Bhattacharjee (bn_IN)
+ * Gil Forcada (ca)
+ * Gil Forcada ([email protected])
+ * Jorge González (es)
+ * Fran Diéguez (gl)
+ * Shankar Prasad (kn)
+ * Ani Peter (ml)
+ * Sandeep Shedmake (mr)
+ * Wouter Bolsterlee (nl)
+ * Andrej Žnidaršič (sl)
+
+================
+Evince 2.30.0
+================
+
+Bug fixes:
+
+ * Fix scale calculation on rotated monitors in presentation mode
+ (José Aliste, Carlos Garcia Campos)
+ * Save current page when closing window in presentation mode
+ (#612656, Carlos Garcia Campos)
+ * Fix rendering of documents with non uniform page size when in
+ presentation mode (Carlos Garcia Campos)
+ * Use monitor height instead of screen height to calculate page
+ scale in presentation mode (#608924, Carlos Garcia Campos)
+
+Translation updates:
+
+ * Sadia Afroz (bn)
+ * Lucas Lommer (cs)
+ * Kenneth Nielsen (da)
+ * Iñaki Larrañaga Murgoitio (eu)
+ * Tommi Vainikainen (fi)
+ * Gabor Kelemen (hu)
+ * Andika Triwidada (id)
+ * Francesco Marletta (it)
+ * Changwoo Ryu (ko)
+ * Badral (mn)
+ * Nils-Christoph Fiedler (nds)
+ * Wouter Bolsterlee (nl)
+ * A S Alam (pa)
+ * Duarte Loreto (pt)
+ * Daniel Nylander (sv)
+ * Maxim Dziumanenko (uk)
+
+================
+Evince 2.29.92
+================
+
+Bug fixes:
+
+ * Fix build with gtk+ >= 2.19.7
+
+Translation updates:
+
+ * Gil Forcada (ca)
+ * Bruce Cowan (en_GB)
+ * Iñaki Larrañaga Murgoitio (eu)
+ * Gabor Kelemen (hu)
+ * Gintautas Miliauskas (lt)
+ * Anita Reitere (lv)
+ * Umarzuki bin Mochlis Moktar (ms)
+ * Piotr Drąg (pl)
+ * Yuri Kozlov (ru)
+ * Miloš Popović ([email protected])
+ * Милош Поповић (sr)
+ * Daniel Nylander (sv)
+
+================
+Evince 2.29.91
+================
+
+New Features and UI Improvements:
+
+ * DVI backend support on windows (#602910, Hib Eris)
+
+Documentation fixes:
+
+ * Modernise API docs (Christian Persch)
+
+Bug fixes:
+
+ * Fix dpi calculation (#608586, Juanjo Marín)
+ * Allow adding actions for more than one item in view popup menu
+ (#609497, Carlos Garcia Campos)
+ * Add mateconf schemas for comic cbt files (#609501, Carlos Garcia
+ Campos)
+ * Update totem-scrsaver from totem sources (#610173, Carlos Garcia
+ Campos)
+ * Fix linking with pedantic linkers (#609895, Hib Eris)
+ * Do not link evince shell to poppler directly (#608832, Hib Eris)
+ * Several translatable messages fixed (#608698, Philip Withnall)
+
+Translation updates:
+
+ * Khaled Hosny (ar)
+ * Krasimir \"Bfaf\" Chonov (bg)
+ * Sadia Afroz (bn)
+ * Christian Kirbach (de)
+ * nikosCharonitakis (el)
+ * Philip Withnall (en_GB)
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Claude Paroz (fr)
+ * Fran Diéguez (gl)
+ * Hideki Yamane (Debian-JP) (ja)
+ * Kjartan Maraas (nb)
+ * Wouter Bolsterlee (nl)
+ * Henrique P. Machado (pt_BR)
+ * Adi Roiban (ro)
+ * Matej Urbančič (sl)
+ * Dr,T,Vasudevan (ta)
+ * Theppitak Karoonboonyanan (th)
+ * Chao-Hsiung Liao (zh_HK)
+ * Chao-Hsiung Liao (zh_TW)
+
+================
+Evince 2.29.5
+================
+
+New Features and UI Improvements:
+
+ * Make Home/End keys go to first/last page in presentation mode
+ too (#358462, Carlos Garcia Campos)
+ * Allow finish presentation by clicking on end page (#309364,
+ Carlos Garcia Campos)
+ * Add custom print tab to printing dialog with several page scale
+ options (#599468, #599470, #599547, Adrian Johnson)
+ * Add support for remote files in evince thumbnailers (#605218,
+ Carlos Garcia Campos)
+ * Add evince icon to the windows executable (#596401, Hib Eris)
+
+Bug fixes:
+
+ * Fix transition of pages with duration=0 in presentation mode
+ (Carlos Garcia Campos)
+ * Fix vertical white line shown in presentation mode with
+ documents with black background (#438760, Carlos Garcia Campos)
+ * Fix fickering and blank screen issues when changing pages fast
+ in presentation mode (#602738, Carlos Garcia Campos)
+ * Fix printing on Windows (#604705, Hib Eris)
+ * Disable text selection in presentation mode (#605554, Carlos
+ Garcia Campos)
+ * Make comics backend also compile on Windows (#605146, Hib Eris)
+ * Remove comics_regex_quote() in favor of always using
+ g_shell_quote() (#605092, Juanjo Marín)
+ * Properly redrawn properties view when document is reloaded
+ (#605169, Carlos Garcia Campos)
+
+Translation updates:
+
+ * Khaled Hosny (ar)
+ * Jorge González (es)
+ * Kjartan Maraas (nb)
+ * Matej Urbančič (sl)
+ * Daniel Nylander (sv)
+ * Theppitak Karoonboonyanan (th)
+ * Maxim Dziumanenko (uk)
+
+================
+Evince 2.29.4
+================
+
+New Features and UI Improvements:
+
+ * Always show rotate icons (#599398, Carlos Garcia Campos)
+ * EvPrintOperation has been moved to libview so that it can
+ be used by EvView users (#604750, Tomeu Vizoso)
+ * Support for cbt comic files (#588266, Juanjo Marín)
+
+Bug fixes:
+
+ * Do not fail to open external uri links that are relative paths
+ (#604716, Carlos Garcia Campos)
+ * Do not hardcode BINDIR on Windows (#605001, Hib Eris)
+ * Fix a crash in djvu backend on windows (#604919, Hib Eris)
+ * Replace mkdtemp with our own _ev_g_mkdtemp to make it portable
+ (#604917, Hib Eris)
+ * Include a copy of some icons from mate-icon-theme to be used as
+ fallback on platforms like window where mate-icon-theme is not
+ available (#596400, Hib Eris)
+ * Fix 0x0 page size always shown in properties view (Carlos Garcia
+ Campos)
+ * Do not hide page selector widget on invalid input (#603714,
+ Carlos Garcia Campos)
+ * Split EvPixbufCache into dynamic and static data. Static page
+ data (links, annots, images, text and forms) that don't
+ depend on current scale/rotation, have been moved to EvPageCache
+ and are never removed. (#602405, Carlos Garcia Campos)
+ * Fix a typo in EV_RENDER_CONTEXT macro definition (#603857,
+ Gustavo Carneiro)
+ * Code cleanups and improvements in libview (#603858, Carlos
+ Garcia Campos)
+
+Translation updates:
+
+ * Khaled Hosny (ar)
+ * Yaron Shahrabani (he)
+ * Kjartan Maraas (nb)
+ * Matej Urbančič (sl)
+ * Tao Wei (zh_CN)
+
+================
+Evince 2.29.3
+================
+
+New Features and UI Improvements:
+
+ * Add inverted colors view mode (#321823, Juanjo Marín, Carlos
+ Garcia Campos)
+ * Add options to open/save attachment annotations to context menu
+ (Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Fix infinite loop when scrollbar visibility changes in fit-with
+ mode (Carlos Garcia Campos)
+ * Fix rendering issue in dual/continuous mode (#602742, José
+ Aliste)
+ * Do not resize the window when zoom in/out with CTRL+Scroll
+ (#602798, Carlos Garcia Campos)
+ * Keep scroll position when reloading (#602445, Carlos Garcia
+ Campos)
+ * Make "p" and "n" accels unsensitive while searching (Carlos
+ Garcia Campos)
+
+Translation updates:
+
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Yaron Shahrabani (he)
+ * Nils-Christoph Fiedler (nds)
+ * Matej Urbančič (sl)
+ * Tao Wei (zh_CN)
+
+================
+Evince 2.29.2
+================
+
+New Features and UI Improvements:
+
+ * Add support for PDF File Attachment Annotations (#601839, Carlos
+ Garcia Campos)
+ * Use "n" and "p" keys for page jump (#601217, Carlos Garcia
+ Campos)
+ * Update actions sensitivity when mateconf keys change (#568433,
+ Carlos Garcia Campos)
+
+Documentation fixes:
+
+ * Make series ID unique (#599726, Christian Persch)
+
+Bug fixes:
+
+ * Fix saving attachments (Carlos Garcia Campos)
+ * Make sure view is redrawn as soon as current page is rendered
+ (#601433, Carlos Garcia Campos)
+ * Fix documentation of default flag values in configure (Emmanuel
+ Fleury)
+ * Do not mark properties name and desc as translatable (#509824,
+ Carlos Garcia Campos)
+ * Fix build with glib < 2.22.0 (#600338, Christian Spurk)
+ * Disable print page setup options according to lockdown (#568433,
+ Carlos Garcia Campos)
+ * Fix loading of remote files (Carlos Garcia Campos)
+ * Correctly process --with-smclient arguments (#590174, Christian
+ Persch)
+
+Translation updates:
+
+ * Thomas Thurman ([email protected])
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Antón Méixome (gl)
+ * Kjartan Maraas (nb)
+ * Matej Urbančič (sl)
+ * Daniel Nylander (sv)
+ * Dr.T.Vasudevan (ta)
+ * 樊栖江(Fan Qijiang) (zh_CN)
+
+================
+Evince 2.29.1
+================
+
+New Features and UI Improvements:
+
+ * Embed page setup settings in the print dialog (#591551, Marek
+ Kašík, Carlos Garcia Campos)
+ * A new tab has been added to properties dialog to show
+ * information about the document license (#349173, Juanjo Marín,
+ Carlos Garcia Campos)
+ * Use different processes for every document opened. (#583680,
+ #434966, #497388, #524633, #586087, Carlos Garcia Campos)
+ * Use GVFS metadata instead of our own xml based
+ implementation. (#586841, Carlos Garcia Campos)
+ * When scrolling a page, move one screen leaving one line context
+ to help to visually track the document as it moves. (#170871,
+ Carlos Garcia Campos)
+ * Use GtkInfoBar instead of GeditMessageArea for the message area
+ (#592785, Carlos Garcia Campos)
+ * EvPageCache has been splitted up into document part (moved to
+ EvDocument which has been converted into an abstract class
+ instead of an interface) and view part: EvDocumentModel. This
+ allows to have more than one EvView for the same document, so
+ that "Open a Copy" can be implemented without using the symlink
+ hack. (#573641, Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Several fixes and cleanups in comics backend (Christian Persch)
+ * Don't exit with non-writable $HOME (Christian Persch)
+ * Clean up temp file handling to make sure we can cope with not
+ being able to create our temp directory (#595372, Christian Persch)
+ * Set page orientation of each page when printing so that
+ documents with mixed portrait/landscape pages print correctly
+ (#599470, Adrian Johnson)
+ * Preserve aspect ratio when scaling page for printing (#599468,
+ Adrian Johnson)
+ * Fix a crash when we don't have a DBUS connection (Christian Persch)
+ * Make sure total_num_bytes > 0 before using it in progress
+ callbacks (#597691, Carlos Garcia Campos)
+ * Escape document URI before showing it in progress area (#597691,
+ Carlos Garcia Campos)
+ * Fix a crash due to an uninitialized variable (#597154, Carlos
+ Garcia Campos)
+ * Fix a crash when there are form field widgets active during
+ rotation (#593316, Carlos Garcia Campos)
+ * Remarks the selected thumbnail after rotation (#595548, Juanjo Marín)
+ * Don't change current page when rotating documents with different
+ page sizes (#595704, Carlos Garcia Campos)
+ * Fix thumbnails rotation when loading a rotated document
+ (#595718, Carlos Garcia Campos)
+ * Save images as png or jpg when the filename has no extensions
+ (#595079, Patrick Ammann)
+ * Populate destination page when reloading the document (#570054,
+ Fernando Herrera)
+
+Translation updates:
+
+ * Khaled Hosny (ar)
+ * Astur (ast)
+ * Krasimir \"Bfaf\" Chonov (bg)
+ * Runa Bhattacharjee (bn_IN)
+ * Gil Forcada (ca)
+ * Marek Černocký (cs)
+ * Kenneth Nielsen (da)
+ * Mario Blättermann (de)
+ * Kostas Papadimas (el)
+ * Philip Withnall (en_GB)
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Iñaki Larrañaga Murgoitio (eu)
+ * Tommi Vainikainen (fi)
+ * Claude Paroz (fr)
+ * Antón Méixome (gl)
+ * Sweta Kothari (gu)
+ * Gabor Kelemen (hu)
+ * Andika Triwidada (id)
+ * Francesco Marletta (it)
+ * Shushi Kurose (ja)
+ * Shankar Prasad (kn)
+ * Changwoo Ryu (ko)
+ * Gintautas Miliauskas (lt)
+ * Sangeeta Kumari (mai)
+ * Ani Peter (ml)
+ * Sandeep Shedmake (mr)
+ * Kjartan Maraas (nb)
+ * Reinout van Schouwen (nl)
+ * Manoj Kumar Giri (or)
+ * A S Alam (pa)
+ * Piotr Drąg (pl)
+ * André Gondim (pt_BR)
+ * Duarte Loreto (pt)
+ * Lucian Adrian Grijincu (ro)
+ * Matej Urbančič (sl)
+ * Miloš Popović ([email protected])
+ * Милош Поповић (sr)
+ * Daniel Nylander (sv)
+ * Dr.T.Vasudevan (ta)
+ * Krishna Babu K (te)
+ * Theppitak Karoonboonyanan (th)
+ * Baris Cicek (tr)
+ * Maxim Dziumanenko (uk)
+ * Aron Xu (zh_CN)
+ * Chao-Hsiung Liao (zh_HK)
+ * Chao-Hsiung Liao (zh_TW)
+
+================
+Evince 2.27.90
+================
+
+Bug fixes:
+
+ * Let scroll wheel change page when in non-continuous best-fit
+ mode (#562257, Philip Langdale)
+ * Show print progress information when using GtkPrintOperation
+ * Don't show crash recovery dialog when requested file is the only
+ one to be recovered (#578894, Carlos Garcia Campos)
+ * Disable crash recovery when D-BUS is disabled (#578894, Carlos
+ Garcia Campos)
+ * Grab focus on view widget after setting metadata (#589300,
+ Nickolay V. Shmyrev)
+ * Fixes segmentation violation when print file format is empty
+ (#589226, Andreas Liebe)
+ * Use AM_V_GEN to make custom commands silent (#585355, Javier Jardón)
+ * Don't install schema files when building without mateconf (Nickolay
+ V. Shmyrev)
+
+Translation updates:
+
+ * Khaled Hosny (ar)
+ * Maruf Ovee (bn)
+ * Denis (br)
+ * Gil Forcada (ca)
+ * Carles Ferrando Garcia ([email protected])
+ * Marek Černocký (cs)
+ * Mario Blättermann (de)
+ * Seán de Búrca (ga)
+ * Antón Méixome (gl)
+ * Kjartan Maraas (nb)
+ * André Gondim (pt_BR)
+ * Daniel Nylander (sv)
+ * Theppitak Karoonboonyanan (th)
+ * Chao-Hsiung Liao (zh_HK)
+ * Chao-Hsiung Liao (zh_TW)
+
+================
+Evince 2.27.4
+================
+
+Bug fixes:
+
+ * Compute selections on button press for word/line selections
+ (#562059, Carlos Garcia Campos)
+ * Fix several even/odd multipage issues (#583429, Bartek Kostrzewa)
+ * Register thumbnailing for gzip/gzip dvi files (#588013, Emilio
+ Pozuelo Monfort)
+ * Get rid of shave with AM_SILENT_RULES automake option (#585355,
+ Javier Jardón)
+ * Make evince output pdf on supported printers (#585442, Johan Brannlund)
+ * Fix build on windows (Fridrich Strba)
+ * Determine data directory on runtime on windows (Fridrich Strba)
+ * Fix build without MateConf (Nickolay V. Shmyrev)
+ * Fix several introspection build issues (#585971, Christian Persch)
+
+Translation updates:
+
+ * Giannis Katsampirhs (el)
+ * Jonathan Ernst (fr)
+ * Daniel Nylander (sv)
+ * Maxim Dziumanenko (uk)
+ * Clytie Siddall (vi)
+ * Nguyễn Đình Trung (vi)
+
+================
+Evince 2.27.3
+================
+
+New Features and UI Improvements:
+
+ * Update the hildon port (Christian Persch)
+ * Complete the win32 port (#339172, Hib Eris)
+ * Allow activate the menubar in fullscreen mode (#504243, Carlos
+ Garcia Campos)
+ * Do not create popup window if the annot doesn't have a popup
+ (Carlos Garcia Campos)
+ * Add tests for printer settings (#583976, Bartek Kostrzewa)
+ * Remember and reuse "Save a Copy..." path (#485195, Carlos Garcia Campos)
+ * Support for cb7 using the p7zip commands in comics backend
+ (#565174, Juanjo Marín)
+ * Parse xml metadata to detect PDF/A documents (#582206, Davide Capodaglio)
+
+Bug fixes:
+
+ * Several build fixes and improvements (Christian Persch)
+ * Fix page transitions in presentation mode (#583652, #581881,
+ Carlos Garcia Campos)
+ * Scale according to paper size before rendering for printing
+ (Carlos Garcia Campos)
+ * Fix multipage even/odd printing issues (#583429, #583388, Bartek
+ Kostrzewa)
+ * Gracefully work with FITB destinations and friends (#583276,
+ Matthias Drochner)
+ * Fix double to int conversion in thumbnails_get_dimensions
+ (#581524, Carlos Garcia Campos)
+ * Ohter bugs fixed: #584617, #585497
+
+Translation updates:
+
+ * Runa Bhattacharjee (bn_IN)
+ * Mario Blättermann (de)
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Kjartan Maraas (nb)
+ * Vladimir Melo (pt_BR)
+ * Dr.T.Vaasudevan (ta)
+
+================
+Evince 2.27.1
+================
+
+New Features and UI Improvements:
+
+ * Use GtkPrintOperation when printing for the backends that
+ support rendering into a cairo context (#557112, Carlos Garcia Campos)
+ * Recover previous session when running evince after a crash
+ (#578894, Carlos Garcia Campos)
+ * Preliminary annotations support (#315002, Carlos Garcia Campos,
+ Iñigo Martínez)
+ * Rename Print Setup menu entry as Page Setup for consistency with
+ the GTK+ dialog title (#581109, Nickolay V. Shmyrev)
+ * Added F3 as a find-next accelerator key (#579072, Michael J. Chudobiak)
+ * Support the free Gna! unrar tool in comics backend (#552074,
+ Juanjo Marín)
+ * Add evince-previewer as a separate applicaton that implements
+ the printing preview (Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Fix handling of the tmp folder (#582108, Juanjo Marín)
+ * Abort dnd operations originated in the same Evince window
+ (#582077, Carlos Garcia Campos)
+ * Disable bouncing during scrolling (#533534, Martin Pohlack)
+ * Fix build without mateconftool-2 (#581441, Christian Persch)
+ * Fix documentation build (#579671, Nickolay V. Shmyrev)
+ * Fix error handling of broken documents (#580886, Nickolay V. Shmyrev)
+ * Fix several memory leaks in comics backend (#552074, Juanjo Marín)
+ * Escape URIs for display (#581064, Nickolay V. Shmyrev)
+ * Resync with libegg to remove deprecated GTK+ symbols (#580556,
+ Javier Jardón)
+ * Correct check for exit status of commands in comics backend
+ (#579656, Juanjo Marín)
+ * Add -no-undefined flag for Cygwin build (#580058, Yaakov Selkowitz)
+ * Use g_file_make_symbolic_link to create symlinks (#339172, Hib Eris)
+ * Delete the temp symlink created when opening a copy (Carlos
+ Garcia Campos)
+ * Don't redraw again when zoom is set more than once to the same
+ scale factor (Carlos Garcia Campos)
+ * Fix print preview of empty selection (#517735, Juanjo Marín)
+ * Don't prevent unmounting in case the initial cwd is on an
+ external device (#575436, Carlos Garcia Campos)
+ * Create and load the document based on the mime-type provided by
+ caja instead of using our own documents factory. (#533917,
+ Carlos Garcia Campos)
+ * Fix endianess issues in dvi and tiff backends (#578433, #509920,
+ Benjamin Berg)
+ * Fix memory leak in tiff backend (#578285, Nickolay V. Shmyrev)
+ * Fix path where accels file is saved (#577500, Nickolay
+ V. Shmyrev)
+ * Fix fading animations (Nickolay V. Shmyrev)
+ * Translate the categories in the caja properties tab
+ (#575085, Christian Persch)
+ * Other bugs fixed: #539972, #578436, #582543
+
+Translation updates:
+
+ * Jorge Gonzalez (es)
+ * Manoj Kumar Giri (or)
+ * Ivar Smolin (et)
+ * Vladimir Melo (pt_BR)
+ * Matej Urban (sl)
+ * Khaled Hosny (ar)
+ * Miloš Popović (sr)
+ * Funda Wang (zh_CN)
+ * Amitakhya Phukan (as)
+ * Baris Cicek (tr)
+
+================
+Evince 2.26.0
+================
+
+Bug fixes:
+
+ * Fix compiler warning when building with -Wformat (#574168,
+ Tobias Mueller)
+ * Fix a crash with encrypted documents (Christian Persch)
+
+Translation updates:
+
+ * F Wolff (af)
+ * Martin Picek (cs)
+ * Fotis Tsamis (el)
+ * Ivar Smolin (et)
+ * Suso Baleato (gl)
+ * Yaron Shahrabani (he)
+ * Gabor Kelemen (hu)
+ * Francesco Marletta (it)
+ * Shankar Prasad (kn)
+ * Gintautas Miliauskas (lt)
+ * Sangeeta Kumari (mai)
+ * Ani Peter (ml)
+ * Sandeep Shedmake (mr)
+ * Manoj Kumar Giri (or)
+ * Fabrício Godoy (pt_BR)
+ * Mișu Moldovan (ro)
+ * Yuriy Penkin (ru)
+ * I. Felix (ta)
+ * Krishna Babu K (te)
+
+================
+Evince 2.25.92
+================
+
+Bug fixes:
+
+ * Show the 'jump to page' window in the right GdkScreen (#560541,
+ Carlos Garcia Campos)
+ * Make our own thumbnail when the provided one doesn't have the
+ needed size (#323198, #307357, Christian Spurk, Carlos Garcia Campos)
+ * Reduce the restriction on the minimum size of thumbnails from 40
+ to 1 (#323198, Christian Spurk)
+ * Use GOption in thumbnailer (Carlos Garcia Campos)
+ * Fix saving a copy of encrypted PDF documents (#566791, Carlos
+ Garcia Campos)
+ * Don't use g_quark_from_static_string in a loadable module
+ (Christian Persch)
+ * Use g_object_unref() instead of gdk_pixbuf_unref() in impress
+ backend (#571707, Thomas H.P. Andersen)
+
+Translation updates:
+
+ * Ihar Hrachyshka ([email protected])
+ * Alexander Shopov (bg)
+ * Gil Forcada (ca)
+ * Petr Kovar (cs)
+ * Christian Kirbach (de)
+ * David Lodge (en_GB)
+ * Juanje Ojeda Croissier (es)
+ * Iñaki Larrañaga Murgoitio (eu)
+ * Ilkka Tuohela (fi)
+ * Claude Paroz (fr)
+ * Sweta Kothari (gu)
+ * Yuval Tanny (he)
+ * Takeshi AIHANA (ja)
+ * Changwoo Ryu (ko)
+ * Kjartan Maraas (nb)
+ * Duarte Loreto (pt)
+ * Daniel Nylander (sv)
+ * Chao-Hsiung Liao (zh_HK)
+ * Chao-Hsiung Liao (zh_TW)
+
+================
+Evince 2.25.91
+================
+
+Bug fixes:
+
+ * Fix several memory leaks (Christian Persch)
+ * Fix errors handling and propagation (Christian Persch)
+ * Fix several translation strings in properties dialog (#571787,
+ Nickolay V. Shmyrev)
+ * Do not resize the window on reloading (#571051, #304249, Carlos
+ Garcia Campos)
+ * Experimental introspection support (#569083, Christian Persch)
+ * Use g_set_error_literal() (Christian Persch)
+ * Other firxes for win32 (#339172, Hib Eris)
+ * Make session manager code compile for win32 (#339172, Carlos
+ Garcia Campos)
+ * Update totem-screensaver from totem svn to make it build for
+ win32 (#339172, Hib Eris)
+ * Make use of MateConf optional (#339172, Hib Eris, Carlos Garcia Campos)
+ * Fix i18n in libdocument (Christian Persch)
+ * Include gi18n-lib.h instead of gi18n.h in libs and backends
+ (Christian Persch)
+
+Translation updates:
+
+ * Mikel González (ast)
+ * Ihar Hrachyshka ([email protected])
+ * Kenneth Nielsen (da)
+ * Dawa pemo (dz)
+ * Juanje Ojeda Croissier (es)
+ * Ivar Smolin (et)
+ * Iñaki Larrañaga Murgoitio (eu)
+ * Gabor Kelemen (hu)
+ * Takeshi AIHANA (ja)
+ * Shankar Prasad (kn)
+ * Arangel Angov (mk)
+ * Wouter Bolsterlee (nl)
+ * Tomasz Dominikowski (pl)
+ * Taylon (pt_BR)
+ * Горан Ракић (sr)
+ * Daniel Nylander (sv)
+ * Theppitak Karoonboonyanan (th)
+ * Clytie Siddall (vi)
+ * Gan Lu (zh_CN)
+ * Chao-Hsiung Liao (zh_HK)
+ * Chao-Hsiung Liao (zh_TW)
+
+================
+Evince 2.25.90
+================
+
+Bug fixes:
+
+ * Split API documentation into libdocument, libview and shell
+ (#568465, Carlos Garcia Campos)
+ * Fix symbols conflict in impress backend (#569998, Christian Persch)
+ * Add mnemonics to buttons in search bar (#569212, Carlos Garcia
+ Campos)
+ * Fix a crash when printing (#569328, Carlos Garcia Campos)
+ * Fix --version command line option (#562869, Christian Persch)
+ * Use versioned directory for backends (#569082, Christian Persch)
+ * Add EV_DEFINE_BOXED_TYPE and EV_DEFINE_INTERFACE macros
+ (#568228, Carlos Garcia Campos)
+ * Sync EggToolbarEditor with libegg (Nickolay V. Shmyrev)
+ * Respect directory umask and setgid when saving files (#568593,
+ Michael J. Chudobiak)
+ * Add libtool versioning, pkg-config files and single headers for
+ libdocument and libview (#568224, #568220, #568229, #568227,
+ Christian Persch)
+ * Remove G_OBJECT casts from g_signal_connect calls (#568386, Hiroyuki
+ Ikezoe)
+ * Install several missing header files of libdocument and libview
+ (#567787, #567790, Tomeu Vizoso)
+ * Other bugs fixed: #569120, #569231, #569327, #570077
+
+Translation updates:
+
+ * Hendrik Richter (de)
+ * Ivar Smolin (et)
+ * Gabor Kelemen (hu)
+ * Changwoo Ryu (ko)
+ * Kjartan Maraas (nb)
+ * Daniel Nylander (sv)
+
+================
+Evince 2.25.5
+================
+
+Code changes:
+
+ * Move EvView specific code to a libevview library so that it can
+ be embbeded in other applications (#567751, Tomeu Vizoso, Carlos
+ Garcia Campos)
+ * Other improvements for embbeders (#567785, #567788, #567789,
+ Tomeu Vizoso)
+
+Bug fixes:
+
+ * Fix a minor typo (Michal Vaner)
+ * Fix the API docs build (#568171, Christian Persch)
+ * Simplify drag data handling (#558084, Christian Persch)
+ * Several portability issues (#339172, Hib Eris)
+ * Fix mnemonic conflict (#567937, Carlos Garcia Campos)
+ * Other bugs fixed: #567910
+
+Translation updates:
+
+ * Gil Forcada (ca)
+ * Hendrik Richter (de)
+ * Juanje Ojeda Croissier (es)
+ * Kjartan Maraas (nb)
+ * Gan Lu (zh_CN)
+
+================
+Evince 2.25.4
+================
+
+New Features and UI Improvements:
+
+ * Show progress information when loading/saving remote files
+ (#370958, Carlos Garcia Campos)
+ * Remember page setup options (#525185, #349102, Carlos Garcia Campos)
+ * Show a confirmation dialog when there are pending print jobs
+ while closing the main window (#480964, Carlos Garcia Campos)
+ * Show progress information when printing (#482770, Carlos Garcia Campos)
+ * Add document URI to properties page (#555376, Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Bump glib requirement to fix build with older versions
+ (Christian Spurk)
+ * Fix mime type handling (Hib Eris)
+ * Fix saving images to remote locations (Carlos Garcia Campos)
+ * Do not try to jump to the location of the find result for every
+ match, but only for the first one. It makes searching really
+ faster. (#564774, Michael Hunold)
+ * Fix zip mime-type in comics backend (Juanjo Marín)
+ * Fix several memory leaks (Carlos Garcia Campos)
+
+Translation updates:
+
+ * Ihar Hrachyshka ([email protected])
+ * Juanje Ojeda Croissier (es)
+ * Yuval Tanny (he)
+ * Kjartan Maraas (nb)
+ * Taylon (pt_BR)
+ * Gan Lu (zh_CN)
+
+================
+Evince 2.25.2
+================
+
+New Features and UI Improvements:
+
+ * PDF Optional content (layers) support. (Carlos Garcia Campos)
+ * The password dialog has been reworked in order to make it more
+ hig compliant and consistent (#562496, Carlos Garcia Campos)
+ * Improved keyring support. Password dialog is not shown anymore
+ when the password is already in the keyring (Carlos Garcia Campos)
+ * Support for monitor and reload of remote documents
+ (#555399, Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Fix opening comic documents with a wrong but valid extension
+ (#562143, Carlos Garcia Campos)
+ * Fix a crash when searching (#558377, Carlos Garcia Campos)
+ * Fix several crashes when selecting text (#561393, Marek Kašík)
+
+Translation updates:
+
+ * Jorge González (es)
+ * Nguyễn Đình Trung (vi)
+
+================
+Evince 2.25.1
+================
+
+New Features and UI Improvements:
+
+ * Use the message area instead of popup dialogs for any error and
+ warning messages (Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Adapt to the new single-include policy (#558064, Christian Persch)
+ * Fix page transition in presentation mode (#516749, Nickolay V. Shmyrev)
+ * Other bugs fixed: (#558066, Christian Persch)
+
+Translation updates:
+
+ * Hendrik Richter (de)
+
+================
+Evince 2.24.1
+================
+
+Bug fixes:
+
+ * Disable toggle function of F5 when in presentation mode
+ (#556162, Dave Neary)
+ * Create a loading thumbnail for every different page size
+ (#556264, Carlos Garcia Campos)
+ * Fix several memory leaks (Carlos Garcia Campos)
+ * Do not show bad titles like 'Microsoft Word' in the window title
+ bar (#534684, Robin Sonefors and Nickolay V. Shmyrev)
+ * Fix mailto links which were considered as http uris (#555801,
+ Carlos Garcia Campos)
+ * Fix launching external applications (#554500, Carlos Garcia Campos)
+ * Other bugs fixed (#552382, #553369, #555134, Carlos Garcia Campos)
+
+Translation updates:
+
+ * Ihar Hrachyshka ([email protected])
+ * Adrian Guniš (cs)
+ * Kenneth Nielsen (da)
+ * Ivar Smolin (et)
+ * Takeshi AIHANA (ja)
+
+================
+Evince 2.24.0
+================
+
+Bug fixes:
+
+ * Use GdkAppLaunchContext when available to launch external uris
+ (Carlos Garcia Campos)
+ * Use the message area to show errors when lauching external uris
+ instead of a popup window (Carlos Garcia Campos)
+ * Assume invalid uris are http uris (#552071, Carlos Garcia
+ Campos)
+
+Translation updates:
+
+ * Usama Akkad (ar)
+ * Gil Forcada (ca)
+ * Kenneth Nielsen (da)
+ * Hendrik Richter (de)
+ * nikosCharonitakis (el)
+ * Ilkka Tuohela (fi)
+ * Sweta Kothari (gu)
+ * Gabor Kelemen (hu)
+ * Francesco Marletta (it)
+ * Shankar Prasad (kn)
+ * Changwoo Ryu (ko)
+ * Gintautas Miliauskas (lt)
+ * Arangel Angov (mk)
+ * Santhosh Thottingal (ml)
+ * Sandeep Shedmake (mr)
+ * wadim dziedzic (pl)
+ * Mișu Moldovan (ro)
+ * Alexandre Prokoudine (ru)
+ * Горан Ракић (sr)
+ * Dr.T.Vasudevan (ta)
+ * Baris Cicek (tr)
+ * Chao-Hsiung Liao (zh_HK)
+ * Chao-Hsiung Liao (zh_TW)
+
+================
+Evince 2.23.92
+================
+
+Bug fixes:
+
+ * Allow page scrolling with PageUp/PageDown keys when find bar is
+ active (#529833, Carlos Garcia Campos)
+ * Search in page labels is now case-insensitive (#550136, Michael Lee)
+ * Do not append the file extension twice when saving an image
+ (Carlos Garcia Campos)
+ * Correctly restore the cursor after showing the context menu
+ (Carlos Garcia Campos)
+ * Grab the mediakeys with a low priority so that it doesn't
+ interfere with other applications like a music player
+ (#547164, Eric Piel)
+
+Translation updates:
+
+ * Alexander Shopov (bg)
+ * Petr Kovar (cs)
+ * David Lodge (en_GB)
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Iñaki Larrañaga Murgoitio (eu)
+ * Robert-André Mauchin (fr)
+ * Ignacio Casal Quinteiro (gl)
+ * Kjartan Maraas (nb)
+ * Wouter Bolsterlee (nl)
+ * Leonardo Ferreira Fontenelle (pt_BR)
+ * Duarte Loreto (pt)
+ * Laurent Dhima (sq)
+ * Daniel Nylander (sv)
+ * Theppitak Karoonboonyanan (th)
+ * Clytie Siddall (vi)
+ * Funda Wang (zh_CN)
+
+================
+Evince 2.23.91
+================
+
+New Features and UI Improvements:
+
+ * Use EggSMClient from libegg instead of mate-client and remove
+ libmate and libmateui dependencies (Carlos Garcia Campos)
+ * Implement document_get_page_label in tiff backend (Carlos Garcia
+ Campos)
+
+Bug fixes:
+
+ * Start up in the right workspace when resuming a previous session
+ (Carlos Garcia Campos)
+ * Fix marco warnings shown in ~/.xsession-errors (Carlos
+ Garcia Campos)
+ * Fix a crash due to an idle function called with an already
+ destroyed object (#549163, Carlos Garcia Campos)
+ * Fix problems when opening or saving attachments (#548653, Carlos
+ Garcia Campos)
+ * Handle document orientation in tiff backend (#548444, Carlos
+ Garcia Campos)
+ * Fix a crash when adjustment page size is 0 (#547440, Carlos
+ Garcia Campos)
+ * Other bugs fixed: #548462
+
+Translation updates:
+
+ * Runa Bhattacharjee (bn_IN)
+ * Gil Forcada (ca)
+ * Mario Blättermann (de)
+ * Dawa pemo (dz)
+ * Jorge González (es)
+ * Iñaki Larrañaga Murgoitio (eu)
+ * Ilkka Tuohela (fi)
+ * Takeshi AIHANA (ja)
+ * Shankar Prasad (kn)
+ * Gintautas Miliauskas (lt)
+ * Arangel Angov (mk)
+ * Manoj Kumar Giri (or)
+ * Vladimir Melo (pt_BR)
+ * Chao-Hsiung Liao (zh_HK)
+
+================
+Evince 2.23.6
+================
+
+Bug fixes:
+
+ * Correctly build desktop file (#544237, Götz Waschk)
+
+Other changes:
+
+ * Cleanup #include statements. (Wouter Bolsterlee)
+ * The jobs system has been reworked in order to make it simpler
+ and easier to extend. (Carlos Garcia Campos)
+ * Add a profile mode available when debug is enabled (Carlos
+ Garcia Campos)
+
+Translation updates:
+
+ * Djihed Afifi (ar)
+ * Adrian Guniš (cs)
+ * Pierre Lemaire (fr)
+ * Duarte Loreto (pt)
+
+================
+Evince 2.23.5
+================
+
+New Features and UI Improvements:
+
+ * Intercept window manager's fullscreen request in order to
+ run/stop fullscreen mode (#493541, Carlos Garcia Campos)
+ * 7-zip compressed commics support (#532312, Kartik Rustagi)
+ * Add Alt+Left and Alt+Right shortcuts to rotate (#539972, Bastien
+ Nocera)
+ * Media player keys support (#539971, Bastien Nocera)
+
+Bug fixes:
+
+ * Restart the job search when the find bar is opened for the
+ second time (#531956, Carlos Garcia Campos)
+ * Fixes caja crash caused by evince properties page extension
+ (#542548, Carlos Garcia Campos)
+ * Fix Solaris build (#542924, Darren Kenny)
+ * Fix endianess bug (#540950, Benjamin Jacobs)
+ * Other bugs fixed: #542001
+
+Translation updates:
+
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Ignacio Casal Quinteiro (gl)
+ * Yuval Tanny (he)
+ * Rakesh Pandit (ks)
+ * Kjartan Maraas (nb)
+ * Wouter Bolsterlee (nl)
+ * Zabeeh Khan (ps)
+ * Daniel Nylander (sv)
+ * Theppitak Karoonboonyanan (th)
+
+================
+Evince 2.23.4
+================
+
+New Features and UI Improvements:
+
+ * A warning message is shown in the message area when the document
+ has no pages (#171588, Carlos Garcia Campos)
+ * Automaticly reload the document when the file has changed on
+ disk (#304249, Carlos Garcia Campos)
+ * Added Ctrl-Insert keybinding for copying text and Ctrl+N for
+ opening a copy (#526523, #532239, Carlos Garcia Campos)
+ * Save as action has been added as a toolbar button too. (Abhishek Mukherjee)
+ * Primary selection is also updated when copying a link address
+ (#520855, Carlos Garcia Campos)
+ * Added GtkMountOperation, copied from gtk+. (Carlos Garcia Campos)
+ * GS code has been removed, libspectre is now required to build the
+ PostScript backend. (Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Fixed a crash when opening documents with no pages
+ (#537574, Carlos Garcia Campos)
+ * Fixed a crash when running in debug mode (Daniel M. German)
+ * Fixed a crash in impress backend (#530852, Hans Petter Jansson)
+ * Improved error handling in djvu backend (#530202, Carlos Garcia Campos)
+ * Do not use the pixbuf backend for rendering tiff documents when
+ the tiff backend is available (#520290, Carlos Garcia Campos)
+ * Fixed T1 font rendering in dvi backend (#536883, Mattias Nissler)
+ * Other bugs fixed: #533323, #533897, #533896, #534493, #537535
+
+Thanks:
+
+ Matthias Drochner, Frederic Peters, Cosimo Cecchi
+
+Translation updates:
+
+ * Djihed Afifi (ar)
+
+
+================
+Evince 2.22.1.1
+================
+
+Bug fixes:
+
+ * Fix build in FreeBSD (#526799, Michael Johnson, Carlos Garcia Campos)
+ * Fixes djvu backend links (#526517, Carlos Garcia Campos)
+
+===============
+Evince 2.22.1
+===============
+
+Bug fixes:
+
+ * Add compressed dvi to the list of supported mime types
+ (#307087, Ed Catmur)
+ * Fix a crash when printing a range that doesn't specify the start
+ or end page (#524288, Carlos Garcia Campos)
+ * Fix a crash when thumbnail failed to render in PS backend
+ (#525015, Carlos Garcia Campos)
+ * Open default application when launching an external uri
+ (#525009, Emil Soleyman)
+ * Add a configure option to enable/diable DBus (#521797, Rémi Cardona)
+ * Fix printing in documents with rotated pages (#512648, Eugen Dedu)
+ * Do not append file extension twice when saving an image
+ (#523069, Carlos Garcia Campos, Wouter Bolsterlee)
+ * Cancel find operation when the find bar is closed
+ (#508845, Carlos Garcia Campos)
+ * Several printing fixes: collate, reverse and copies were broken
+ (#365332, Eugen Dedu)
+ * Fix selection rendering when poppler is compiled with splash
+ backend (Carlos Garcia Campos).
+ * Other bugs fixed: #521224, #524112
+
+Translation updates:
+
+ * Alexander Nyakhaychyk (be)
+ * Yavor Doganov (bg)
+ * David Lodge (en_GB)
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Clytie Siddall (vi)
+
+===============
+Evince 2.22.0
+===============
+
+Bug fixes:
+
+ * Do not print when Escape is pressed to close printing dialog
+ (#495107, Wouter Bolsterlee)
+ * Fix a crash in caja properties page (#519679, Carlos Garcia Campos)
+ * Fix a crash with transition effects (#519106, Carlos Garnacho)
+ * Fix document mime type detection (#518874, Carlos Garcia Campos)
+ * Fix a crash in forms (#518831, Carlos Garcia Campos)
+ * Fix images extraction (#516237, Carlos Garcia Campos)
+
+Translation updates:
+
+ * Djihed Afifi (ar)
+ * Ihar Hrachyshka ([email protected])
+ * Petr Kovar (cs)
+ * Kenneth Nielsen (da)
+ * nikosCharonitakis (el)
+ * David Lodge (en_GB)
+ * Jorge González (es)
+ * Ivar Smolin (et)
+ * Ilkka Tuohela (fi)
+ * Claude Paroz (fr)
+ * Ignacio Casal Quinteiro (gl)
+ * Ankit Patel (gu)
+ * Yuval Tanny (he)
+ * Gabor Kelemen (hu)
+ * Luca Ferretti (it)
+ * Takeshi Aihana (ja)
+ * Shankar Prasad (kn)
+ * Changwoo Ryu (ko)
+ * Gintautas Miliauskas (lt)
+ * Arangel Angov (mk)
+ * Sandeep Shedmake (mr)
+ * Narayan Kumar Magar (ne)
+ * Åsmund Skjæveland (nn)
+ * Yannig Marchegay (Kokoyaya) (oc)
+ * Hugo Doria (pt_BR)
+ * Laurent Dhima (sq)
+
+===============
+Evince 2.21.91
+===============
+
+Bug fixes:
+
+ * mate-print code has been removed (#512370, Carlos Garcia Campos)
+ * Fix thumbnailer crash (#513934, Matthias Clasen)
+ * Fix djvu rendering (#482720, Carlos Garcia Campos)
+ * Fix mime type detection based on file contents (Cosimo Cecchi, Carlos Garcia Campos)
+ * Other bugs fixed: #512719, #512720, #512718, #512771, #513203
+
+Translation updates:
+
+ * Khaled Hosny (ar)
+ * Amitakhya Phukan (as)
+ * Hendrik Richter (de)
+ * Jorge González (es)
+ * Kjartan Maraas (nb)
+ * Yannig Marchegay (Kokoyaya) (oc)
+ * Tomasz Dominikowski (pl)
+ * Vladimir Melo (pt_BR)
+ * Duarte Loreto (pt)
+ * Theppitak Karoonboonyanan (th)
+ * Serdar CICEK (tr)
+ * Woodman Tuen (zh_HK)
+
+===============
+Evince 2.21.90
+===============
+
+Important notice for distributors:
+
+ * This release overrides document permissions by default. See
+ http://bugzilla.mate.org/show_bug.cgi?id=382700 for more information
+ about this.
+ * Ghostscript interface for PostScript documents is now deprecated.
+ Please use libspectre (http://libspectre.freedesktop.org/)
+ instead.
+
+New features and UI improvements:
+
+ * Port to gio and drop mate-vfs dependency. (#510401, Cosimo Cecchi, Carlos Garcia Campos)
+ * Scroll pages in page entry with mouse wheel (#324122, David Turner)
+ * Automatic scrolling in context menu (#323670, David Turner)
+ * Kinetic scrolling (#461271, David Turner)
+ * New plugin system for backends (#351348, Carlos Garcia Campos)
+ * Allow saving images in formats other than PNG (#500209, Carl-Anton
+ Ingmarsson, Nickolay V. Shmyrev)
+ * Add support for page transitions (#458460, Carlos Garnacho)
+ * Override document restrictions by default (#382700, Wouter Bolsterlee)
+ * Use up/down arrows instead of left/right (#170081, Wouter Bolsterlee)
+
+Bug fixes:
+
+ * Reset cursor correctly (#501603, #509958, Carlos Garcia Campos)
+ * Use GSlice to allocate memory (#475972, Christian Persch)
+ * Do not limit minimum zoom factor in best fit/fit width mode (#503805,
+ Carlos Garcia Campos)
+ * Use libspectre, if available, for the ps backend. (#317106, #499787,
+ #501235, #421879, #445797, #443859, #486547, #386005, #507705, Carlos Garcia Campos)
+ * Put RTL marker in recent file list (#509076, Djihed Afifi)
+ * Put Caja extension in right place (#505359, Matthias Clasen, Brian
+ Pepple, Damien Carbery, Wouter Bolsterlee)
+ * Avoid filename quoting issues (#502500, Tom Parker)
+ * Repair horizontal scrolling with shift (#483412, Nickolay V. Shmyrev)
+ * Mate Keyring is now an optional dependency (#509676, Marcelo Lira)
+ * Reduce memory consumption by rendering images on demand (Carlos Garcia Campos)
+ * Fix printing in dvi backend when filename contains white spaces. (#502839, Carlos Garcia Campos)
+ * Other bugs fixed: #502843, #460658, #504721, #511635
+
+Thanks:
+
+ * Carlos Garcia Campos
+ * Christian Persch
+ * Yaakov Selkowitz
+ * David Turner
+ * Carl-Anton Ingmarsson
+ * Carlos Garnacho
+ * Djihed Afifi
+ * Nickolay V. Shmyrev
+ * Wouter Bolsterlee
+ * Marcelo Lira
+
+Translation updates:
+
+ * Djihed Afifi (ar)
+ * Ihar Hrachyshka ([email protected])
+ * Gil Forcada (ca)
+ * Jorge Gonzalez (es)
+ * Priit Laes (et)
+ * Inaki Larranaga Murgoitio (eu)
+ * Stéphane Raimbault (fr)
+ * Seán de Búrca (ga)
+ * Ignacio Casal Quinteiro (gl)
+ * Kjartan Maraas (nb)
+ * Wouter Bolsterlee (nl)
+ * Thierry Randrianiriana (mg)
+ * Yannig Marchegay (oc)
+ * Leonardo Ferreira Fontenelle, Hugo Doria, Rodrigo Flores (pt_BR)
+ * Daniel Nylander (sv)
+ * Clytie Siddall (vi)
+
+===============
+Evince 2.21.1
+===============
+
+New Features and UI Improvements:
+
+ * Use message area for notification about errors
+ * Some print settings for documents are stored in metadata
+ * Added command line option for search
+ * Expander with a recent documents is added to toolbar
+ * Egg-recent code dropped
+ * Djvu, dvi and comics are enabled by default
+ * Support for links with URI's like mailto (Pascal Terjan)
+ * Removed the shadow of the fullscreen toolbar
+
+Bug fixes:
+
+ * A lot of bug fixes from a stable branch
+
+Thanks:
+
+ To Carlos Garcia Campos who did the all the amazing work above :)
+
+Translations:
+
+ Changwoo Ryu (ko)
+ Daniel Nylander (sv)
+ Djihed Afifi (ar)
+ Jorge Gonzalez (es)
+ Francesco Marletta (it)
+ Gabor Kelemen (hu)
+ Gintautas Miliauskas (lt)
+ Ignacio Casal Quinteiro (gl)
+ Ihar Hrachyshka ([email protected])
+ Ilkka Tuohela (fi)
+ Ivar Smolin (et)
+ Jorge González (es)
+ Kjartan Maraas (nb)
+ Matej Urbančič (sl)
+ Peter Bach (da)
+ Yannig Marchegay (Kokoyaya) (oc)
+
+===============
+Evince 2.20.2
+===============
+
+Bug fixes:
+
+ * Tiff documents were rendered with wrong colors (Matthias Clasen)
+ * Fix endless loop when zero pages per sheet is selected (Darren Kenny)
+ * Do not jump to the first page when reloading from command line (Carlos Garcia Campos)
+ * Memory leak fix (Hiroyuki Ikezoe)
+ * Fix printing with poppler splash backend (Carlos Garcia Campos)
+ * Fix a crash when printing with the mate-print dialog (Carlos Garcia Campos)
+ * Warning fix (Sebastien Bacher)
+
+Translations:
+
+ Matej Urbančič (sl)
+ Youssef Chahibi (ar)
+
+===============
+Evince 2.20.1
+===============
+
+Bug fixes:
+
+ * Printing multiple pages per sheet fixes (Carlos Garcia Campos)
+ * Respect ranges order during print (Carlos Garcia Campos)
+ * Silently ignore unknown form fields (Matthias Drochner)
+ * Do not show the toolbar in fullscreen mode if the main toolbar is not visible (Carlos Garcia Campos)
+ * Give priority to form fields over images (Carlos Garcia Campos)
+
+Translations:
+
+ Changwoo Ryu (ko)
+ Francesco Marletta (it)
+ Ilkka Tuohela (fi)
+ Peter Bach (da)
+
+===============
+Evince 2.20.0
+===============
+
+Bug fixes:
+
+ * Forms support broken by forgotten ifdef fix (Carlos Garcia Campos)
+ * Issue with making a copy and xfer to different file system (Carlos Garcia Campos)
+ * Desktop file fixes (Götz Waschk)
+
+Translations:
+
+ Youssef Chahibi (ar)
+ Alexander Shopov (bg)
+ Peter Bach (da)
+ Ivar Smolin (et)
+ Ignacio Casal Quinteiro (gl)
+ Francesco Marletta (it)
+ Gintautas Miliauskas (lt)
+ Espen Stefansen (nb)
+ Yannig Marchegay (Kokoyaya) (oc)
+ Mişu Moldovan (ro)
+ Peter Tuhársky (sk)
+ Laurent Dhima (sq)
+ Горан Ракић (sr)
+ Maxim Dziumanenko (uk)
+
+===============
+Evince 2.19.92
+===============
+
+Bug fixes:
+
+ * Bump requirements to poppler 0.6 (Carlos Garcia Campos)
+ * Restart the search when reloading a document with the find bar visible (Carlos Garcia Campos)
+ * Do not jump to the first page when reloading a document from the last page (Carlos Garcia Campos)
+ * Do not update visibility when changing a chrome flag (Carlos Garcia Campos)
+ * Use GTK_UNIT_POINTS instead of GTK_UNIT_PIXEL (Carlos Garcia Campos)
+ * Make sure current-page <= end-page in continuous mode (Carlos Garcia Campos)
+ * Ignore outline items without a title (Carlos Garcia Campos)
+ * Use only drag mouse cursor during a drag operation (Carlos Garcia Campos)
+ * Actually fix printing regressions (Carlos Garcia Campos)
+ * Add support for double and triple click selections (Carlos Garcia Campos)
+
+Translations:
+
+ Gil Forcada (ca)
+
+===============
+Evince 2.19.4
+===============
+
+Bug fixes:
+
+ * Find works when findbar is hidden (Justin Blanchard)
+ * Use directly cairo_surface_get_content (Jeff Muizelaar)
+ * Great speedup by using CAIRO_FORMAT_RGB24 instead of CAIRO_FORMAT_ARGB32 (Jeff Muizelaar)
+ * Fix for embedded thumbnails dimension and rotation (Carlos Garcia Campos)
+ * Avoid ifdefs inside macros (Jens Granseuer)
+ * Memory leak fixes (Justin Blanchard, Hiroyuki Ikezoe)
+ * New icon sizes (Andreas Nilsson)
+ * Invalid read fix (Justin Blanchard)
+ * Fix multihead problems in toolbar editor (Carlos Garcia Campos)
+ * Use the same cairo context for every page in the same expose event (Carlos Garcia Campos)
+
+Translations:
+
+ Adam Weinberger (en_CA)
+ Danishka Navin (si)
+ Duarte Loreto (pt)
+ Hendrik Richter (de)
+ Ihar Hrachyshka ([email protected])
+ Raphael Higino (pt_BR)
+ Stéphane Raimbault (fr)
+ Takeshi Aihana (ja)
+
+==============
+Evince 0.9.3
+==============
+
+New Features and UI Improvements:
+
+ * Use new GTK+ tootlips (Carlos Garcia Campos)
+ * Printing multiple pages per sheet (Carlos Garcia Campos)
+ * Added UNIX-like key bindings hjkl (Cosimo Cecchi)
+
+Bug fixes:
+
+ * Do not block while enumerating printers in preview mode (Christian Persch)
+ * Show new pages as soon as they are rendered even if other page
+ elements like forms, images or links are not ready yet (Carlos Garcia Campos)
+ * Provide different options in the print dialog depending on document
+ type backend capabilities (Carlos Garcia Campos)
+
+Translations:
+
+ Jorge González (es)
+ Iñaki Larrañaga Murgoitio (eu)
+ Ilkka Tuohela (fi)
+ Gabor Kelemen (hu)
+ Arangel Angov (mk)
+ Wouter Bolsterlee (nl)
+ Funda Wang (zh_CN)
+
+==============
+Evince 0.9.2
+==============
+
+New Features and UI Improvements:
+
+ * Forms support (Julien Rebetez, Carlos Garcia Campos)
+ * Toolbar editor icons on dragging (Jaap Haitsma)
+
+Bug fixes:
+
+ * Program description translation issue fixed (Gabor Kelemen)
+ * Do not change page after presentation (Cesar Fernandez)
+
+Translations:
+
+ Ankit Patel (gu)
+ Arangel Angov (mk)
+ Bharat Kumar (te)
+ Clytie Siddall (vi)
+ Daniel Nylander (sv)
+ Dr.T.Vasudevan (ta)
+ Espen Stefansen (nb)
+ Ivar Smolin (et)
+ Takeshi Aihana (ja)
+ Theppitak Karoonboonyanan (th)
+ Tomasz Dominikowski (pl)
+ Tshewang Norbu (dz)
+
+==============
+Evince 0.9.1
+==============
+
+New Features and UI Improvements:
+
+ * Cairo-based renderer for pages and selections of PDF, DVJU and DVI documents (Carlos Garcia Campos)
+ * PDF exporter for printing DVI documents (Alaska Subedi)
+ * Zoom icon artwork (Michael Monreal)
+ * Patch to avoid frequent process wakeup for metadata handling (Bastien Nocera, Arjan van de Ven)
+ * Bumped poppler requirements to 0.5.9 (Carlos Garcia Campos)
+ * Changed sidebar ordering (Wouter Bolsterlee)
+ * Removed extra locking of a main loop to speed up rendering (Carlos Garcia Campos)
+
+Bug fixes:
+
+ * Print job is released quickly (Carlos Garcia Campos)
+ * Update a cursor and tooltips after scrolling (Carlos Garcia Campos)
+ * More safe mkdir_with_parents (Eduardo Lima)
+ * Safe saving of a document copy to a remote location (Carlos Garcia Campos)
+ * Build fix without libmate (Marc Brockschmidt)
+ * Selections don't disappear after zoom (Carlos Garcia Campos)
+ * Preview button added to the print dialog (Carlos Garcia Campos)
+
+Translations:
+
+ Daniel Nylander (sv)
+ David Lodge (en_GB)
+ Dr.T.Vasudevan (ta)
+ Espen Stefansen (nb)
+ Gil Forcada (ca)
+ Ignacio Casal Quinteiro (gl)
+ Ivar Smolin (et)
+ Jorge González (es)
+ Sonam Pelden (dz)
+ Theppitak Karoonboonyanan (th)
+ Yuval Tanny (he)
+
+==============
+Evince 0.9.0
+==============
+
+New Features and UI Improvements:
+
+ * Printing support in djvu documents (Alaska Subedi)
+ * Optional drop of libmate dependency (Ross Burton)
+ * Print button for preview mode (Carlos Garcia Campos)
+ * Remember print settings (Carlos Garcia Campos)
+ * Use gtk+ builtin paper list to identify the document's paper size (Carlos Garcia Campos)
+ * Start rendering pages before any other jobs (Carlos Garcia Campos)
+ * Developer documentation updated. (Iñigo Martínez)
+ * History button improvements (Nickolay Shmyrev)
+
+Bug Fixes:
+
+ * Fixes for issues with fullscreen toolbar (Carlos Garcia Campos)
+ * Open document in empty window with EggRecent (Eduardo Lima)
+ * Compilation fixes for FreeBSD (Roy Marples)
+ * A lot of calls to parent class methos added (Christian Persch)
+ * Update gtk-icon-cache in uninstall hook (Brian Pepple)
+ * Fix for crash in comics backend when filename contains quote (Nickolay Shmyrev)
+ * Always add application-specific icons to the theme (Nickolay Shmyrev)
+
+Translations:
+
+ Jakub Friedl (cs)
+ Dimitris Glezos (el)
+ David Lodge (en_GB)
+ Jorge González (es)
+ Ivar Smolin (et)
+ Francesco Marletta (it)
+ Espen Stefansen (nb)
+ Yannig Marchegay (oc)
+ Washington Lins (pt_BR)
+ Daniel Nylander (sv)
+
+==============
+Evince 0.8.1
+==============
+
+Bug Fixes:
+
+ * Default resolution is used when it's not provided by TIFF
+ document (Carlos Garcia Campos)
+ * Added support for ps, eps and compressed documents
+ thumbnails (Carlos Garcia Campos)
+ * Implemented font color specials in the DVI backend (Ricardo Markiewicz)
+ * DOCUMENTS folder from xdg-data-dirs is used as default folder when
+ opening a file chooser (Matthias Clasen)
+ * unlink-tempfile is used instead of unlink-temp-file (Carlos Garcia Campos)
+
+Translations:
+
+ Gil Forcada (ca)
+ Ignacio Casal Quinteiro (gl)
+ Iñaki Larrañaga Murgoitio (eu)
+ Jorge González (es)
+ Jakub Friedl (cs)
+ Sonam Pelden (dz)
+
+==============
+Evince 0.8.0
+==============
+
+Features and UI Improvements:
+
+ * Thumbnails for postscript documents (Carlos Garcia Campos)
+ * Copy of compressed document is saved compressed (Carlos Garcia Campos)
+
+Bug Fixes:
+
+ * Properly handle PostScript page orientation (Carlos Garcia Campos)
+ * Do not render thumbnails when sidebar thumbnail page is not visible
+ on startup (Carlos Garcia Campos)
+ * Warning when document has one page (Carlos Garcia Campos)
+ * Djvu backend builds on Mac (Carlos Garcia Campos)
+ * Allow deleting in goto window in presentation mode (Carlos Garcia Campos)
+
+Translations:
+
+ Ankit Patel (gu)
+ Changwoo Ryu (ko)
+ Christian Kintner (de)
+ Clytie Siddall (vi)
+ Daniel Nylander (sv)
+ Djihed Afifi (ar)
+ Duarte Loreto (pt)
+ Erdal Ronahi (ku)
+ Funda Wang (zh_CN)
+ Gabor Kelemen (hu)
+ Gil Forcada (ca)
+ Gintautas Miliauskas (lt)
+ Ihar Hrachyshka (be)
+ Ivar Smolin (et)
+ Kjartan Maraas (nb)
+ Matic Žgur (sl)
+ Maxim Dziumanenko (uk)
+ Mişu Moldovan (ro)
+ Nickolay V. Shmyrev (ru)
+ Raphael Higino (pt_BR)
+ Takeshi Aihana (ja)
+ Thierry Randrianiriana (mg)
+ Wadim Dziedzic (pl)
+ Woodman Tuen (zh_HK)
+ Woodman Tuen (zh_TW)
+ Yuval Tanny (he)
+ Игор Несторовић (sr)
+
+==============
+Evince 0.7.2
+==============
+
+New Features and UI Improvements:
+
+ * Document thumbnail is used as window icon (Carlos Garcia Campos)
+ * Support for gzipped and bzipped
+ document types (Carlos Garcia Campos)
+ * A lot of new icons (Luca Feretti, Andreas Nilsson)
+ * Finally working history button (Nickolay V. Shmyrev)
+
+Internal Improvements:
+
+ * Developer gtk-doc based documentation (Nickolay V. Shmyrev)
+ * More dogtail tests (Nickolay V. Shmyrev)
+
+Bug Fixes:
+
+ * Fontconfig lock to avoid crashes (Carlos Garcia Campos)
+ * Critical crash fix by checking return value from
+ psscan (Tom Parker)
+ * Critical crash fix in caja property page and
+ broken ODF (Tom Parker)
+ * Critical crash fix with multifile djvu
+ documents (Carlos Garcia Campos)
+ * Multithreaded gtk problems fix by using async
+ mate-vfs (Carlos Garcia Campos)
+
+Translations:
+
+ Changwoo Ryu (ko)
+ Clytie Siddall (vi)
+ Daniel Nylander (sv)
+ David Lodge (en_GB)
+ Dimitris Glezos (el)
+ Dorji Tashi (dz)
+ Duarte Loreto (pt)
+ Gabor Kelemen (hu)
+ Ihar Hrachyshka (be)
+ Ilkka Tuohela (fi)
+ Ivar Smolin (et)
+ Jovan Naumovski (mk)
+ Khaled Hosny (ar)
+ Kjartan Maraas (nb)
+ Nickolay V. Shmyrev (ru)
+ Stéphane Raimbault (fr)
+ Theppitak Karoonboonyanan (th)
+ Vladimir Petkov (bg)
+ Wouter Bolsterlee (nl)
+ Yuval Tanny (he)
+
+Thanks:
+
+ Ed Catmur, Esteban Sanchez, Theppitak Karoonboonyanan, Wouter Bolsterlee
+
+==============
+Evince 0.7.1
+==============
+
+New Features:
+
+ * Popup window to jump to another page in presentation mode. (Carlos Garcia Campos)
+ * Page transition support in presentation mode. (Carlos Garcia Campos)
+
+Bug Fixes:
+
+ * Fix random crashes related to threads and Font Config (Carlos Garcia Campos)
+ * Fix icon name in desktop file (Luca Ferretti)
+ * Always use "best fit" zoom in presentation mode (Wouter Bolsterlee)
+ * Don't restore the view on the last page when a document is reopened
+ (Wouter Bolsterlee)
+ * Fix build due to libxml2 (Elijah Newren)
+
+Translations:
+
+ * Djihed Afifi (ar)
+ * Ales Nyakhaychyk (be)
+ * Alexander Shopov (bg)
+ * Josep Puigdemont i Casamajó (ca)
+ * David Lodge (en_GB)
+ * Francisco Javier F. Serrador (es)
+ * Priit Laes (et)
+ * Raivis Dejus (lv)
+ * Jovan Naumovski (mk)
+ * Kjartan Maraas (nb)
+ * Wouter Bolsterlee (nl)
+ * Matic Žgur (sl)
+ * Daniel Nylander (sv)
+ * Theppitak Karoonboonyanan (th)
+ * Clytie Siddall (vi)
+
+Thanks:
+
+ Damien Carbery, Jens Granseuer, Yevgen Muntyan
+
+==============
+Evince 0.7.0
+==============
+
+New Features:
+
+ * History. Preliminary implementation, not finished yet. (Nickolay V. Shmyrev)
+ * Option to open a copy of the document in a new window. (Carlos Garcia Campos)
+ * Multihead systems support. (Carlos Garcia Campos)
+ * Added paper size field to the properties dialog. (Kasper Svendsen, Wouter Bolsterlee)
+ * Added automated UI tests with dogtail (Nickolay V. Shmyrev)
+
+Bug Fixes:
+
+ * Links support in rotated documents. (Radoslav Dorcik)
+ * Side pane disabled when entering and leaving fullscreen mode. (Nickolay V. Shmyrev)
+ * Fixed crash in DVI backend. (Nickolay V. Shmyrev)
+ * Printing settings scale, copies, collate, reverse, odd/even work again (Carlos Garcia Campos)
+ * Added page setup dialog for printing. (Carlos Garcia Campos)
+ * Support for printing to a PDF file. (Carlos Garcia Campos)
+ * Allow blank screen in presentation mode. (Carlos Garcia Campos)
+ * Added "End of presentation" page in presentation mode. (Carlos Garcia Campos)
+ * Screensaver detection in presentation mode. (Wouter Bolsterlee)
+ * "Loading..." text is not shown in presentation mode. (Wouter Bolsterlee)
+
+Security Fixes:
+
+ * Buffer overflow in PS backend. CVE-2006-5864. (Carlos Garcia Campos)
+
+Translations:
+
+ * Djihed Afifi (ar)
+ * Runa Bhattacharjee (bn_IN)
+ * Gil Forcada (ca)
+ * Jakub Friedl (cs)
+ * Mindu Dorji (dz)
+ * Dimitris Glezos (el)
+ * David Lodge (en_GB)
+ * Francisco Javier F. Serrador (es)
+ * Ilkka Tuohela (fi)
+ * Claude Paroz (fr)
+ * Ankit Patel (gu)
+ * Rajesh Ranjan (hi)
+ * Gabor Kelemen (hu)
+ * Satoru SATOH (ja)
+ * Vladimer Sichinava (ka)
+ * Fano Rajaonarisoa (mg)
+ * Kjartan Maraas (nb)
+ * Wouter Bolsterlee (nl)
+ * Leonardo Ferreira Fontenelle (pt_BR)
+ * Leonid Kanter (ru)
+ * Steve Murphy (rw)
+ * Daniel Nylander (sv)
+ * Theppitak Karoonboonyanan (th)
+ * Baris Cicek (tr)
+
+Thanks:
+
+ Claudio Saavedra, Gabriel Felipe Cornejo, Mathias Hasselmann, Michael Monreal,
+ Julien Rebetez, P. Henrique Silva, Yevgen Muntyan
+
+==============
+Evince 0.6.1
+==============
+
+Bug Fixes:
+
+ * Current page follows find page
+ * Reopen reloads document
+ * Document size is guessed correctly
+ * Icons are installed in correct folders
+ * Memory leak fixed, etc
+
+Translations:
+
+ * da dz el en_GB es fa gl it ka nn ro
+ * documentation: fi it sv
+
+Thanks:
+
+ Carlos Garcia Campos, Mathias Hasselmann, Wouter Bolsterlee,
+ Christian Persch
+
+ and especially translators:
+
+ Alessio Frusciante, Åsmund Skjæveland, David Lodge,
+ Francisco Javier F. Serrador, Ignacio Casal Quinteiro, Kostas Papadimas,
+ Mugurel Tudorm, Ole Laursen, Pema Geyleg, Roozbeh Pournader,
+ Vladimer Sichinava, Daniel Nylander, Tommi Vainikainen, Luca Ferretti
+
+==============
+Evince 0.6.0
+==============
+
+Bug Fixes:
+
+ * Links aren't lost while resizing
+ * Tiff backend crash is fixed
+ * Recent code and icons code are screen safe
+ * Crash while handling password-protected documents is fixed
+
+Translations:
+
+ * bg bn bn_IN ca de dz el et fa fi fr gu hi hu ja ko lt lv mk ml
+ mr nb or pt pt_BR ro ru sl sv th tr vi zh_CN zh_HK zh_TW
+
+Thanks:
+
+ Carlos Garcia Campos, Julien Rebetez, Sebastien Bacher, Wouter Bolsterlee
+
+ and especially translators:
+
+ Abel Cheung, Alexander Shopov, Ani Peter, Ankit Patel, Baris Cicek,
+ Changwoo Ryu, Chao-Hsiung Liao, Christophe Merlet, Claude Paroz, Clytie
+ Siddall, Daniel Nylander, Duarte Loreto, Funda Wang, Gabor Kelemen,
+ Guntupalli Karunakar, Hendrik Richter, Ilkka Tuohela, Ivar Smolin, Josep
+ Puigdemont i Casamajó, Jovan Naumovski, Khandakar Mujahidul Islam,
+ Kjartan Maraas, Leonardo Ferreira Fontenelle, Leonid Kanter, Lucas
+ Rocha, Matic Žgur, Meelad Zakaria, Mişu Moldovan, Mugurel Tudor, Nikos
+ Charonitakis, Priit Laes, Rahul Bhalerao, Raivis Dejus, Rajesh Ranjan,
+ Roozbeh Pournader, Runa Bhattacharjee, Satoru SATOH, Subhransu Behera,
+ Theppitak Karoonboonyanan, Žygimantas Beručka
+
+==============
+Evince 0.5.5
+==============
+
+New Features:
+
+ * Evince have preview mode for using with gtk+
+ * New print dialog is used with gtk 2.10
+ * New recent files framework is used
+
+UI Improvements:
+
+ * Sidebar is unified with Epiphany
+ * Escape unfocus page entry
+
+Bug Fixes:
+
+ * Automake updates
+ * Link coordinates now used correctly
+ * Desktop file is corrected
+ * Memory leak fixes
+
+Translations:
+
+ * ca cs de dz el es et eu fi fr gu ja ko mk nl ru sl sv th zh_HK zh_TW
+
+Thanks:
+
+ Carlos Garcia Campos, Christian Persch, Radoslav Dorcik,
+ Ricardo Markiewicz, Sebastien Bacher, Wouter Bolsterlee
+
+ and especially translators:
+
+ Ankit Patel, Arangel Angov, Benoît Dejean, Changwoo Ryu, Chao-Hsiung
+ Liao, Christophe Merlet, Daniel Nylander, Francisco Javier F. Serrador,
+ Guntupalli Karunakar, Hendrik Richter, Ilkka Tuohela, Inaki Larranaga,
+ Jakub Friedl, Josep Puigdemont i Casamajó, Jovan Naumovski, Nikos
+ Charonitakis, Priit Laes, Takeshi Aihana, Theppitak Karoonboonyanan
+
+==============
+Evince 0.5.4
+==============
+
+New Features:
+
+ * Evince supports impress slides with --enable-impress
+ * URI's with anchors can be opened
+
+Bug Fixes:
+
+ * PAPI printing crash was fixed
+ * Threads-related crash was fixed
+ * Assertion on document types that don't support
+ links don't cause crash now
+ * Reload crash was fixed as loading of
+ password-protected documents
+ * Page-label command line option works again
+
+Translations:
+
+ * be bg bn_IN ca cs de dz es et eu fi fr gl gu he id ko mk nb ne
+ nl ru ta th vi zh_CN zh_HK zh_TW
+
+Thanks:
+
+ Bastien Nocera, Carlos Garcia Campos, Ghee Teo, Pat Rondon,
+ Ricardo Markiewicz, Wouter Bolsterlee
+
+ and especially translators:
+
+ Ahmad Riza H Nst, Ales Nyakhaychyk, Alexander Shopov, Ankit Patel,
+ Benoît Dejean, Changwoo Ryu, Chao-Hsiung Liao, Clytie Siddall,
+ Francisco Javier F. Serrador, Funda Wang, Hendrik Richter,
+ I. Felix, Ignacio Casal Quinteiro, Ilkka Tuohela, Inaki Larranaga,
+ Jakub Friedl, Jayaradha, Josep Puigdemont Casamajó,
+ Jovan Naumovski, Kjartan Maraas, Pawan Chitrakar, Pema Geyleg,
+ Priit Laes, Runa Bhattacharjee, Theppitak Karoonboonyanan,
+ Vincent van Adrighem, Yair Hershkovitz
+
+==============
+Evince 0.5.3
+==============
+
+New Features:
+
+ * Evince supports attachments in PDF files
+ * We require gtk 2.8 and djvulibre 3.5.17 now
+ * Links system is reworked allowing named links
+
+Interface imporovements:
+
+ * Esc closes properties dialog
+ * Cursor is hidden in presentation mode
+ * Find previous item is added to the menu
+
+Bug Fixes:
+
+ * GOption port and po/LINGUAS work
+ * Fixed mime type detection by file contents
+ * Several memory leaks were found
+ * Crash on exit related to thumbnailer
+ * Current page is selected more intelligently
+ * Work around libtool build bug is created
+
+Translations:
+
+
+ * bg bn br dz es et fi gl gu nb nl nn no ro vi zh_CN zh_HK zh_TW
+
+Thanks:
+
+ Michael Plump, Carlos Garcia Campos, Wouter Bolsterlee, Francisco
+ Javier F. Serrador, Kjartan Maraas and especially all translators.
+
+==============
+Evince 0.5.2
+==============
+
+Bug Fixes:
+
+ * View don't jump on scrolling
+ * In presentation mode we scroll by one page
+
+Translations:
+
+ * bg bn da de et fa fr hi hu ja ka pt pt_BR ro ru sv uk vi
+
+==============
+Evince 0.5.1
+==============
+
+New Features:
+
+ * Evince can search text in DJVU files
+
+Interface improvements:
+
+ * Odd pages are placed to the right in dual page mode
+
+Bug Fixes:
+
+ * Produce thumbnails for comic books and carefully work with
+ unzip
+ * Correctly interoperate with DBUS 0.60
+ * Ghostscript interpreter is checked in runtime
+ * PS mime types are listed in desktop file
+ * It's possible to add sidebar checkbutton on toolbar
+ * It's possible to print in inverse order
+ * Filechooser now will ask for overwrite confirmation
+ * Search now takes account for scrolling
+ * It's allowed to scroll view from find entry
+ * Evince don't jump to find result if window was scrolled during search
+ * It's now clear what page the toolbar page number applies to
+
+Translations:
+
+ * bg ca cs cy da el en_CA es et eu fi gl gu he hu ja lt nb nl no pl
+ pt_BR pt ru sq sr th vi zh_CN zh_HK zh_TW
+ * Spanish docs are updated and we have Bulgarian translation now
+
+Thanks:
+
+ Ricardo Markiewicz, Rostislav Raykov, Stanislav Slusny,
+ Francisco Javier F. Serrador, Antoine Dopffer, Michael Hofmann,
+ Gary Coady, Luca Ferretti, Carlos Garcia Campos, Eduardo de Barros Lima,
+ Christopher Aillon and all translators.
+
+==============
+Evince 0.5.0
+==============
+
+New Features:
+
+ * Various types of PDF links are now supported
+ * New backend for comic books (CBR/CBZ archives)
+ * Storage of passwords for protected documents in mate-keyring
+ * Layout settings for new documents are taken from the last used document
+
+Interface Improvements:
+
+ * Statusbar was removed to save space
+ * Notification about page processing is shown on the page
+ * More key bindings
+ * Improved intelligent window title that shouldn't confuse users
+ * Show index by default if available
+ * Tooltips for links
+ * Now we have right click context menu
+
+Bug Fixes:
+
+ * Crashes on reload and window close are fixed
+ * DBus usage cleaned
+ * Printing of multiple copies start to work
+ * Crash on structured ps document
+ * Improved session handling
+ * Vertical and horizontal scrolling on selection
+
+Translations:
+
+ * be, bg, bn, ca, cs, de, en_CA, es, et, eu, fi, fr, gl, gu
+ hu, it, ku, lt, nb, nl, no, ro, ru, sr, sv, th, vi, zh_CN, zh_HK, zh_TW
+ * nl for documentation.
+
+==============
+Evince 0.4.0
+==============
+
+Bug Fixes:
+
+ * Depend on poppler 0.4.0
+ * Pass in timestamp to to handle opening multiple windows over dbus
+ better (Ryan Lortie)
+ * Really quit when rendering postscript files.
+ * Set textdomain to libglade.
+ * Fix rotation of thumbnails multiple times
+ * Fix selection for non-continuous case
+ * Disable selection and links for rotated case
+ * findbar fixes
+
+==============
+Evince 0.3.4
+==============
+
+Bug Fixes:
+
+ * Depend on poppler 0.4.0
+ * Convert filenames to utf8 when showing them in the UI
+ * Return focus back to view on escape
+ * Fix compatibility with dbus 0.34.2
+ * Fix some build warnings
+ * Fix dvi backend compilation with t1lib-5.0
+
+==============
+Evince 0.3.3
+==============
+
+New Features:
+
+ * real text selection, complete with theming and I-Beam support
+ * Respect lockdown mateconf keys
+ * Metadata support per document
+ * Font properties dialog
+ * dbus support to create a factory
+ * Implement caja plugin for properties.
+
+Interface Improvements:
+
+ * HIG Fixes
+ * Hide menu entry in .desktop file
+ * Get the correct mime types in the open dialog
+
+Bug Fixes:
+
+ * Improved rotation support
+ * Backwards searching
+ * Use unzipped document when printing
+ * Persist the print settings
+ * Support pdf's with 0 pages
+ * Fix find flickering
+ * mate-doc-utils for manual
+ * Add keypad accelerators for zooming. Fix for bug 308128.
+ * fix mem leaks
+ * MIME Fixes
+ * tiff/djvu backend fixes
+
+Translations:
+
+ * eu, gl, gu, he, hi, ne, pa, pl, sk, sr, [email protected], tr
+
+==============
+Evince 0.3.2
+==============
+
+New Features:
+
+ * Multi page tiff support
+ * Document properties
+ * Document rotation
+ * Mate vfs support
+
+Interface Improvements:
+
+ * Faster dvi rendering
+ * More compact layout for documents with different page formats
+ * Stock icons for all toolbar editor items
+ * Multiple selection in the open dialog
+ * Loading progress feedback
+ * Multiple column thumbnails
+ * Expand links when specified by the document
+ * Several new handy keybindings
+ * Ellipsize recent items labels
+
+Bug Fixes:
+
+ * Fix rendering of several postscript documents
+ * Fix reloading of postcript documents
+ * Fix a lot of memory leaks
+ * Fix compilation on gcc 4
+ * Several minor fixes
+
+==============
+Evince 0.3.1
+==============
+
+New Features:
+
+ * Toolbar editor
+ * Middle button scrolling
+ * Support for printing page ranges
+ * Support for printing chapters (from the index)
+
+Interface Improvements:
+
+ * Move best fit items in the toolbar control at the top
+ * In the sidebar make drop down menu as large as the button
+ * Several keybindings improvements
+ * Faster scrolling
+ * Only render the visible thumbnails (less memory usage)
+ * Get rid of confusing Normal size menu
+
+Bug Fixes:
+
+ * Better mime detection
+ * Several minor fixes
+ * Fix printing on LPD printers
+
+==============
+Evince 0.3.0
+==============
+
+New Features:
+
+ * Continous mode
+ * Dual page mode
+ * Control + Scroll does zooming
+ * Shift + Scroll scrolls horizontally
+ * Zoom control in the toolbar
+
+Interface Improvements:
+
+ * If all page labels are numeric show only
+ the page total
+
+Bug Fixes:
+
+ * Fix size of dvi thumbnails
+ * Fix crash on exit
+ * Make thumbnailer schemas translatable
+ * Fix build problems with t1lib
+
+==============
+Evince 0.2.1
+==============
+
+New Features:
+
+ * new backend to support DJVU files.
+ * new backend to support DVI.
+ * Initial implementation of doc metadata. No UI for this yet,
+ though we respect docs requesting UI changes.
+ * support application/x-gzpostscript
+ * consolidate mime handling code so thumbnailer and main page
+ can use them. Thumbnail all types we support.
+ * Respect document orientation
+ * Implement print and save a copy for the ps backend.
+ * Badass completion of the document in the entry. Only works
+ with GTK+ >= 2.7.1
+ * User docs
+
+Bug Fixes:
+
+ * Faster Zooming
+ * Clean up Schemas
+ * README added.
+ * Small rework of sidebar code. Now sidebar pages should implement
+ EvSidebarPage interface.
+ * lots of small bug fixes
+ * Actually fix the "FIXME: update priority" comment.
+ * Update egg-recent from libegg. This should fix long startup
+ problem.
+
+Interface Improvements:
+
+ * Shift + Space scrolls up.
+ * Removed fullscreen toolbar hard-coded style
+ * Fix normal size zoom. Make zoom in/zoom out unsensitive when
+ they are behyond the limits.
+ * Doesn't show resize grip when maximized
+ * don't draw border in fullscreen
+ * Doesn't show thumbnails if document has 1 page. Make sidebar
+ menu entries sensitive only if if they have sense.
+ * Work around the Escape conflict (find bar and unfullscreen)
+ * Save accelerators on exit.
+ * Add Edit->Find Next
+
+Translations:
+
+ * Make all toolbar items translatable
+ * ru (Nickolay Shmyrev)
+
+==============
+Evince 0.2.0
+==============
+
+Code changes
+
+ * Use poppler glib api
+
+Interface improvements
+
+ * Rewrite most of the backend code to use threads and make
+ the interface more responsive
+
+New features
+
+ * Use page labels as page numbers (Jonathan)
+
+==============
+Evince 0.1.9
+==============
+
+ * Fix a crash in the caja thumbnailer (Fernando Herrera)
+
+==============
+Evince 0.1.8
+==============
+
+BugFixes
+
+ * Fix crash when opening pdf documents with only 1 page
+
+==============
+Evince 0.1.7
+==============
+
+New features
+
+ * Caja thumbnailer for pdf (Fernando Herrera)
+
+BugFixes
+
+ * Make code C89 compliant (Jens Granseuer)
+ * Fix some issues in the caja thumbnailer (Marco)
+ * Do not read ps defaults from ggv prefs! (Marco)
+
+==============
+Evince 0.1.6
+==============
+
+Code changes
+
+ * Use poppler instead of our own xpdf fork (Kristian Høgsberg)
+
+Interface improvements
+
+ * Use an entry for the page control instead of
+ spin buttons (Marco)
+ * Expand the sidebar selection widget (Carlos)
+
+Bug fixes
+
+ * Fix crash in links loading (Jonathan)
+ * Fix crashes when reloading document (Marco)
+
+==============
+Evince 0.1.5
+==============
+
+New features
+
+ * Reload menu (Kai Willadsen)
+
+Interface improvements
+
+ * Support for DnD of files (Carlos Garcia Campos)
+ * Set the "Previous" and "Next" toolbar
+ buttons as important (Vincent Noel)
+ * Make the "best fit" and "fit width"
+ values act as toggle buttons (Jonathan)
+ * Autohide the fullscreen button (Kristian Høgsberg)
+ * Added ellipsis to Print item (Bryan)
+ * Change the fullscreen toolbar to always be in
+ the popup window (Jonathan)
+ * Improved sidebar widget (Carlos Garcia Campos)
+ * Minimal size for the sidebar (Marco)
+ * Hide the sidebar if the type doesn't
+ support thumbnailing and indexing (Jonathan)
+ * Remember the sidebar size (Carlos Garcia Campos)
+
+Bug fixes
+
+ * Fix compilation on non-gcc platforms (Marco)
+ * Fix thumbnails generation (Crispin Flowerday)
+ * Fix best fit size allocation (Martin)
+ * Replace newlines in the title by spaces (Martin)
+ * Get rid of the black flash when resizing the window (Jonathan)
+ * Sync selection with zoom levels (Marco)
+ * Fix clipboard operations (Marco)
+ * Fix a crash when the ps document cannot be opened (Marco)
+ * Fix postcript page selection (Marco)
+ * Fix page sizing to be idempotent (Marco)
+ * Fix page sizing toggle buttons (Marco)
+ * Fix freezing on document with unrecognized links (Marco)
+ * Check ghostscript minimal version (Marco)
+ * Fix rendering of landscape documents (Marco)
+ * Unescape filenames for display (Marco)
+
+==============
+Evince 0.1.4
+==============
+
+Code changes
+
+ * Add debugging/logging helpers (Marco)
+ * Add warnings about unimplemented/unknown link
+ types (Marco)
+
+Interface improvements
+
+ * Epiphany like fullscreen mode (Christian Persch)
+ * Save chromes state between sessions (Christian Persch)
+ * Improve toolbar layout and icons (Luca Ferretti)
+
+Bug fixes
+
+ * Really fix postscript rendering (Marco)
+ * Do not leak gs processes on exit (Marco)
+ * Fix a buffer overflow fix (Marco)
+
+==============
+Evince 0.1.3
+==============
+
+Code changes
+
+ * Beginnings of continuous viewing (Jonathan)
+
+New features
+
+ * Support eps (Marco)
+ * Support gzipped ps (Marco)
+
+Bug fixes
+
+ * Fix a crash with the postscript view (Marco)
+ * Fix image view (Jeff Muizelaar)
+ * Fix a warning when opening documents without
+ thumbnails (Martin)
+ * Fix crashes when searched word is not found (Marco)
+ * Fix a crash when changing page with keys (Marco)
+
+==============
+Evince 0.1.2
+==============
+
+New features
+
+ * Make search really work (Marco)
+
+Interface improvements
+
+ * Show password state in the view (Jonathan)
+ * add GTK_SHADOW_IN to the scrolled window (Martin)
+ * make sure the selected thumbnail stays visible (Martin)
+ * Remove history buttons from the toolbar (Marco)
+
+Bug fixes
+
+ * Fix CID fonts with freetype 2.1.9 (Marco)
+ * Fix inverted up/down buttons (Marco)
+
+==============
+Evince 0.1.1
+==============
+
+New features
+
+ * Actually print PDF files
+ * Navigation history
+ * Support Links
+ * Support encrypted PDFs
+ * A desktop file
+
+Interface improvements
+
+ * Rename Bookmarks sidebar to Index
+ * Use filename as title if the file doesn't have a title (Jeff
+ Muizelaar) or if the title is empty
+ * Make navigation with thumbnails possible (Anders Carlsson)
+ * (De)Sensitize actions as necessary (Dave Malcolm)
+ * Show a wait cursor while the page is rendering
+ * Center the page; draw a page border
+ * Undo selection on page change
+ * Shorter toolbar item labels
+ * Make selection work in any direction
+
+Code changes
+
+ * Render PDFs directly as packed RGB
+ * Update egg-recent code (not used yet)
+ * Update to latest Xpdf patchlevel
+
+Bug fixes
+
+ * PostScript backend fixes
+ * Pixbuf backend fixes (Jeff Muizelaar)
+ * Set page spin button limits correctly (Anders Carlsson)
+ * Fix problem when base fonts are installed as pcf files.
+ * Keep search selection highlighting correct even when
+ zooming/changing window size (Stephane LOEUILLET)
+
+Translation updates
+
+ * cs (Miloslav Trmac)
+ * de (Frank Arnold)
+ * en_CA (Adam Weinberger)
+ * es (Antonio Ognio)
+ * nb (Kjartan Maraas)
+ * nl (Tino Meinen)
+
+evince 0.1.0
+------------
+
+Initial release.
diff --git a/README b/README
new file mode 100644
index 00000000..88a44a24
--- /dev/null
+++ b/README
@@ -0,0 +1,21 @@
+Evince
+==================================================
+Evince is a document viewer capable of displaying
+multiple and single page document formats like PDF
+and Postscript. For more general information about
+Evince please visit our website at
+
+http://www.mate.org/projects/evince/
+
+This software is licensed under the GPL
+
+Evince Requirements
+==================================================
+MATE Platform libraries [ http://www.mate.org/start/ ]
+Poppler for PDF viewing [ http://poppler.freedesktop.org/ ]
+GhostScript for Postscript viewing [ http://www.cs.wisc.edu/~ghost/ ]
+
+Evince Optional Backend Libraries
+==================================================
+DjVuLibre for DjVu viewing [ http://djvulibre.djvuzone.org/ ]
+Rar for viewing CBR comics [ http://www.rarsoft.com/ ]
diff --git a/TODO b/TODO
new file mode 100644
index 00000000..715d57b3
--- /dev/null
+++ b/TODO
@@ -0,0 +1,3 @@
+See our bugzilla for list of entries we are working at
+
+http://bugzilla.mate.org/buglist.cgi?product=evince&bug_status=NEW \ No newline at end of file
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 00000000..eb12863a
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+PKG_NAME="evince"
+
+(test -f $srcdir/configure.ac) || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level $PKG_NAME directory"
+ exit 1
+}
+
+which mate-autogen.sh || {
+ echo "You need to install mate-common from the MATE CVS"
+ exit 1
+}
+
+REQUIRED_AUTOMAKE_VERSION=1.10
+REQUIRED_GTK_DOC_VERSION=1.13
+USE_MATE2_MACROS=1
+. mate-autogen.sh
diff --git a/backend/Makefile.am b/backend/Makefile.am
new file mode 100644
index 00000000..a6dc6e58
--- /dev/null
+++ b/backend/Makefile.am
@@ -0,0 +1,40 @@
+SUBDIRS =
+
+# Backends
+
+if ENABLE_PDF
+SUBDIRS += pdf
+endif
+
+if ENABLE_PS
+SUBDIRS += ps
+endif
+
+if ENABLE_PIXBUF
+SUBDIRS += pixbuf
+endif
+
+if ENABLE_DJVU
+SUBDIRS += djvu
+endif
+
+if ENABLE_TIFF
+SUBDIRS += tiff
+endif
+
+if ENABLE_DVI
+SUBDIRS += dvi
+endif
+
+if ENABLE_COMICS
+ SUBDIRS += comics
+endif
+
+if ENABLE_IMPRESS
+ SUBDIRS += impress
+endif
+
+EXTRA_DIST = \
+ backend.symbols
+
+-include $(top_srcdir)/git.mk
diff --git a/backend/backend.symbols b/backend/backend.symbols
new file mode 100644
index 00000000..6c41cf8f
--- /dev/null
+++ b/backend/backend.symbols
@@ -0,0 +1 @@
+register_evince_backend
diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am
new file mode 100644
index 00000000..0b06ecbc
--- /dev/null
+++ b/backend/comics/Makefile.am
@@ -0,0 +1,32 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ -DMATELOCALEDIR=\"$(datadir)/locale\" \
+ -DEVINCE_COMPILATION \
+ $(BACKEND_CFLAGS) \
+ $(LIB_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED)
+
+backend_LTLIBRARIES = libcomicsdocument.la
+
+libcomicsdocument_la_SOURCES = \
+ comics-document.c \
+ comics-document.h
+
+libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS)
+libcomicsdocument_la_LIBADD = \
+ $(top_builddir)/libdocument/libevdocument.la \
+ $(BACKEND_LIBS) \
+ $(LIB_LIBS)
+
+backend_in_files = comicsdocument.evince-backend.in
+backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend)
+
+EXTRA_DIST = $(backend_in_files)
+
+CLEANFILES = $(backend_DATA)
+
+
+-include $(top_srcdir)/git.mk
diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c
new file mode 100644
index 00000000..4d74385a
--- /dev/null
+++ b/backend/comics/comics-document.c
@@ -0,0 +1,936 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2009-2010 Juanjo Marín <[email protected]>
+ * Copyright (C) 2005, Teemu Tervo <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#ifdef G_OS_WIN32
+# define WIFEXITED(x) ((x) != 3)
+# define WEXITSTATUS(x) (x)
+#else
+# include <sys/wait.h>
+#endif
+
+#include "comics-document.h"
+#include "ev-document-misc.h"
+#include "ev-document-thumbnails.h"
+#include "ev-file-helpers.h"
+
+#ifdef G_OS_WIN32
+/* On windows g_spawn_command_line_sync reads stdout in O_BINARY mode, not in O_TEXT mode.
+ * As a consequence, newlines are in a platform dependent representation (\r\n). This
+ * might be considered a bug in glib.
+ */
+#define EV_EOL "\r\n"
+#else
+#define EV_EOL "\n"
+#endif
+
+typedef enum
+{
+ RARLABS,
+ GNAUNRAR,
+ UNZIP,
+ P7ZIP,
+ TAR
+} ComicBookDecompressType;
+
+typedef struct _ComicsDocumentClass ComicsDocumentClass;
+
+struct _ComicsDocumentClass
+{
+ EvDocumentClass parent_class;
+};
+
+struct _ComicsDocument
+{
+ EvDocument parent_instance;
+
+ gchar *archive, *dir;
+ GPtrArray *page_names;
+ gchar *selected_command, *alternative_command;
+ gchar *extract_command, *list_command, *decompress_tmp;
+ gboolean regex_arg;
+ gint offset;
+ ComicBookDecompressType command_usage;
+};
+
+#define OFFSET_7Z 53
+#define OFFSET_ZIP 2
+#define NO_OFFSET 0
+
+/* For perfomance reasons of 7z* we've choosen to decompress on the temporary
+ * directory instead of decompressing on the stdout */
+
+/**
+ * @extract: command line arguments to pass to extract a file from the archive
+ * to stdout.
+ * @list: command line arguments to list the archive contents
+ * @decompress_tmp: command line arguments to pass to extract the archive
+ * into a directory.
+ * @regex_arg: whether the command can accept regex expressions
+ * @offset: the position offset of the filename on each line in the output of
+ * running the @list command
+ */
+typedef struct {
+ char *extract;
+ char *list;
+ char *decompress_tmp;
+ gboolean regex_arg;
+ gint offset;
+} ComicBookDecompressCommand;
+
+static const ComicBookDecompressCommand command_usage_def[] = {
+ /* RARLABS unrar */
+ {"%s p -c- -ierr --", "%s vb -c- -- %s", NULL , FALSE, NO_OFFSET},
+
+ /* GNA! unrar */
+ {NULL , "%s t %s" , "%s -xf %s %s" , FALSE, NO_OFFSET},
+
+ /* unzip */
+ {"%s -p -C --" , "%s %s" , NULL , TRUE , OFFSET_ZIP},
+
+ /* 7zip */
+ {NULL , "%s l -- %s" , "%s x -y %s -o%s", FALSE, OFFSET_7Z},
+
+ /* tar */
+ {"%s -xOf" , "%s -tf %s" , NULL , FALSE, NO_OFFSET}
+};
+
+static void comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
+
+static GSList* get_supported_image_extensions (void);
+static void get_page_size_area_prepared_cb (GdkPixbufLoader *loader,
+ gpointer data);
+static void render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
+ gint width,
+ gint height,
+ gpointer data);
+static char** extract_argv (EvDocument *document,
+ gint page);
+
+
+EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document,
+ {
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+ comics_document_document_thumbnails_iface_init);
+ } );
+
+/**
+ * comics_regex_quote:
+ * @unquoted_string: a literal string
+ *
+ * Quotes a string so unzip will not interpret the regex expressions of
+ * @unquoted_string. Basically, this functions uses [] to disable regex
+ * expressions. The return value must be freed with * g_free()
+ *
+ * Return value: quoted and disabled-regex string
+ **/
+static gchar *
+comics_regex_quote (const gchar *unquoted_string)
+{
+ const gchar *p;
+ GString *dest;
+
+ dest = g_string_new ("'");
+
+ p = unquoted_string;
+
+ while (*p) {
+ switch (*p) {
+ /* * matches a sequence of 0 or more characters */
+ case ('*'):
+ /* ? matches exactly 1 charactere */
+ case ('?'):
+ /* [...] matches any single character found inside
+ * the brackets. Disabling the first bracket is enough.
+ */
+ case ('['):
+ g_string_append (dest, "[");
+ g_string_append_c (dest, *p);
+ g_string_append (dest, "]");
+ break;
+ /* Because \ escapes regex expressions that we are
+ * disabling for unzip, we need to disable \ too */
+ case ('\\'):
+ g_string_append (dest, "[\\\\]");
+ break;
+ /* Escape single quote inside the string */
+ case ('\''):
+ g_string_append (dest, "'\\''");
+ break;
+ default:
+ g_string_append_c (dest, *p);
+ break;
+ }
+ ++p;
+ }
+ g_string_append_c (dest, '\'');
+ return g_string_free (dest, FALSE);
+}
+
+
+/* This function manages the command for decompressing a comic book */
+static gboolean
+comics_decompress_temp_dir (const gchar *command_decompress_tmp,
+ const gchar *command,
+ GError **error)
+{
+ gboolean success;
+ gchar *std_out, *basename;
+ GError *err = NULL;
+ gint retval;
+
+ success = g_spawn_command_line_sync (command_decompress_tmp, &std_out,
+ NULL, &retval, &err);
+ basename = g_path_get_basename (command);
+ if (!success) {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("Error launching the command “%s” in order to "
+ "decompress the comic book: %s"),
+ basename,
+ err->message);
+ g_error_free (err);
+ } else if (WIFEXITED (retval)) {
+ if (WEXITSTATUS (retval) == EXIT_SUCCESS) {
+ g_free (std_out);
+ g_free (basename);
+ return TRUE;
+ } else {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("The command “%s” failed at "
+ "decompressing the comic book."),
+ basename);
+ g_free (std_out);
+ }
+ } else {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("The command “%s” did not end normally."),
+ basename);
+ g_free (std_out);
+ }
+ g_free (basename);
+ return FALSE;
+}
+
+/* This function shows how to use the choosen command for decompressing a
+ * comic book file. It modifies fields of the ComicsDocument struct with
+ * this information */
+static gboolean
+comics_generate_command_lines (ComicsDocument *comics_document,
+ GError **error)
+{
+ gchar *quoted_file, *quoted_file_aux;
+ gchar *quoted_command;
+ ComicBookDecompressType type;
+
+ type = comics_document->command_usage;
+ comics_document->regex_arg = command_usage_def[type].regex_arg;
+ quoted_command = g_shell_quote (comics_document->selected_command);
+ if (comics_document->regex_arg) {
+ quoted_file = comics_regex_quote (comics_document->archive);
+ quoted_file_aux = g_shell_quote (comics_document->archive);
+ comics_document->list_command =
+ g_strdup_printf (command_usage_def[type].list,
+ comics_document->alternative_command,
+ quoted_file_aux);
+ g_free (quoted_file_aux);
+ } else {
+ quoted_file = g_shell_quote (comics_document->archive);
+ comics_document->list_command =
+ g_strdup_printf (command_usage_def[type].list,
+ quoted_command, quoted_file);
+ }
+ comics_document->extract_command =
+ g_strdup_printf (command_usage_def[type].extract,
+ quoted_command);
+ comics_document->offset = command_usage_def[type].offset;
+ if (command_usage_def[type].decompress_tmp) {
+ comics_document->dir = ev_mkdtemp ("evince-comics-XXXXXX", error);
+ if (comics_document->dir == NULL)
+ return FALSE;
+
+ /* unrar-free can't create directories, but ev_mkdtemp already created the dir */
+
+ comics_document->decompress_tmp =
+ g_strdup_printf (command_usage_def[type].decompress_tmp,
+ quoted_command, quoted_file,
+ comics_document->dir);
+ g_free (quoted_file);
+ g_free (quoted_command);
+
+ if (!comics_decompress_temp_dir (comics_document->decompress_tmp,
+ comics_document->selected_command, error))
+ return FALSE;
+ else
+ return TRUE;
+ } else {
+ g_free (quoted_file);
+ g_free (quoted_command);
+ return TRUE;
+ }
+
+}
+
+/* This function chooses an external command for decompressing a comic
+ * book based on its mime tipe. */
+static gboolean
+comics_check_decompress_command (gchar *mime_type,
+ ComicsDocument *comics_document,
+ GError **error)
+{
+ gboolean success;
+ gchar *std_out, *std_err;
+ gint retval;
+ GError *err = NULL;
+
+ /* FIXME, use proper cbr/cbz mime types once they're
+ * included in shared-mime-info */
+
+ if (!strcmp (mime_type, "application/x-cbr") ||
+ !strcmp (mime_type, "application/x-rar")) {
+ /* The RARLAB provides a no-charge proprietary (freeware)
+ * decompress-only client for Linux called unrar. Another
+ * option is a GPLv2-licensed command-line tool developed by
+ * the Gna! project. Confusingly enough, the free software RAR
+ * decoder is also named unrar. For this reason we need to add
+ * some lines for disambiguation. Sorry for the added the
+ * complexity but it's life :)
+ * Finally, some distributions, like Debian, rename this free
+ * option as unrar-free.
+ * */
+ comics_document->selected_command =
+ g_find_program_in_path ("unrar");
+ if (comics_document->selected_command) {
+ /* We only use std_err to avoid printing useless error
+ * messages on the terminal */
+ success =
+ g_spawn_command_line_sync (
+ comics_document->selected_command,
+ &std_out, &std_err,
+ &retval, &err);
+ if (!success) {
+ g_propagate_error (error, err);
+ g_error_free (err);
+ return FALSE;
+ /* I don't check retval status because RARLAB unrar
+ * doesn't have a way to return 0 without involving an
+ * operation with a file*/
+ } else if (WIFEXITED (retval)) {
+ if (g_strrstr (std_out,"freeware") != NULL)
+ /* The RARLAB freeware client */
+ comics_document->command_usage = RARLABS;
+ else
+ /* The Gna! free software client */
+ comics_document->command_usage = GNAUNRAR;
+
+ g_free (std_out);
+ g_free (std_err);
+ return TRUE;
+ }
+ }
+ /* The Gna! free software client with Debian naming convention */
+ comics_document->selected_command =
+ g_find_program_in_path ("unrar-free");
+ if (comics_document->selected_command) {
+ comics_document->command_usage = GNAUNRAR;
+ return TRUE;
+ }
+
+ } else if (!strcmp (mime_type, "application/x-cbz") ||
+ !strcmp (mime_type, "application/zip")) {
+ /* InfoZIP's unzip program */
+ comics_document->selected_command =
+ g_find_program_in_path ("unzip");
+ comics_document->alternative_command =
+ g_find_program_in_path ("zipnote");
+ if (comics_document->selected_command &&
+ comics_document->alternative_command) {
+ comics_document->command_usage = UNZIP;
+ return TRUE;
+ }
+
+ } else if (!strcmp (mime_type, "application/x-cb7") ||
+ !strcmp (mime_type, "application/x-7z-compressed")) {
+ /* 7zr, 7za and 7z are the commands from the p7zip project able
+ * to decompress .7z files */
+ comics_document->selected_command =
+ g_find_program_in_path ("7zr");
+ if (comics_document->selected_command) {
+ comics_document->command_usage = P7ZIP;
+ return TRUE;
+ }
+ comics_document->selected_command =
+ g_find_program_in_path ("7za");
+ if (comics_document->selected_command) {
+ comics_document->command_usage = P7ZIP;
+ return TRUE;
+ }
+ comics_document->selected_command =
+ g_find_program_in_path ("7z");
+ if (comics_document->selected_command) {
+ comics_document->command_usage = P7ZIP;
+ return TRUE;
+ }
+ } else if (!strcmp (mime_type, "application/x-cbt") ||
+ !strcmp (mime_type, "application/x-tar")) {
+ /* tar utility (Tape ARchive) */
+ comics_document->selected_command =
+ g_find_program_in_path ("tar");
+ if (comics_document->selected_command) {
+ comics_document->command_usage = TAR;
+ return TRUE;
+ }
+ } else {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("Not a comic book MIME type: %s"),
+ mime_type);
+ return FALSE;
+ }
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("Can't find an appropriate command to "
+ "decompress this type of comic book"));
+ return FALSE;
+}
+
+static int
+sort_page_names (gconstpointer a,
+ gconstpointer b)
+{
+ return strcmp (* (const char **) a, * (const char **) b);
+}
+
+static gboolean
+comics_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+ GSList *supported_extensions;
+ gchar *std_out;
+ gchar *mime_type;
+ gchar **cb_files, *cb_file;
+ gboolean success;
+ int i, retval;
+ GError *err = NULL;
+
+ comics_document->archive = g_filename_from_uri (uri, NULL, error);
+ if (!comics_document->archive)
+ return FALSE;
+
+ mime_type = ev_file_get_mime_type (uri, FALSE, &err);
+ if (!mime_type) {
+ if (err) {
+ g_propagate_error (error, err);
+ } else {
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("Unknown MIME Type"));
+ }
+
+ return FALSE;
+ }
+
+ if (!comics_check_decompress_command (mime_type, comics_document,
+ error)) {
+ g_free (mime_type);
+ return FALSE;
+ } else if (!comics_generate_command_lines (comics_document, error)) {
+ g_free (mime_type);
+ return FALSE;
+ }
+
+ g_free (mime_type);
+
+ /* Get list of files in archive */
+ success = g_spawn_command_line_sync (comics_document->list_command,
+ &std_out, NULL, &retval, error);
+
+ if (!success) {
+ return FALSE;
+ } else if (!WIFEXITED(retval) || WEXITSTATUS(retval) != EXIT_SUCCESS) {
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("File corrupted"));
+ return FALSE;
+ }
+
+ /* FIXME: is this safe against filenames containing \n in the archive ? */
+ cb_files = g_strsplit (std_out, EV_EOL, 0);
+
+ g_free (std_out);
+
+ if (!cb_files) {
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("No files in archive"));
+ return FALSE;
+ }
+
+ comics_document->page_names = g_ptr_array_sized_new (64);
+
+ supported_extensions = get_supported_image_extensions ();
+ for (i = 0; cb_files[i] != NULL; i++) {
+ if (comics_document->offset != NO_OFFSET) {
+ if (g_utf8_strlen (cb_files[i],-1) >
+ comics_document->offset) {
+ cb_file =
+ g_utf8_offset_to_pointer (cb_files[i],
+ comics_document->offset);
+ } else {
+ continue;
+ }
+ } else {
+ cb_file = cb_files[i];
+ }
+ gchar *suffix = g_strrstr (cb_file, ".");
+ if (!suffix)
+ continue;
+ suffix = g_ascii_strdown (suffix + 1, -1);
+ if (g_slist_find_custom (supported_extensions, suffix,
+ (GCompareFunc) strcmp) != NULL) {
+ g_ptr_array_add (comics_document->page_names,
+ g_strstrip (g_strdup (cb_file)));
+ }
+ g_free (suffix);
+ }
+ g_strfreev (cb_files);
+ g_slist_foreach (supported_extensions, (GFunc) g_free, NULL);
+ g_slist_free (supported_extensions);
+
+ if (comics_document->page_names->len == 0) {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("No images found in archive %s"),
+ uri);
+ return FALSE;
+ }
+
+ /* Now sort the pages */
+ g_ptr_array_sort (comics_document->page_names, sort_page_names);
+
+ return TRUE;
+}
+
+
+static gboolean
+comics_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+
+ return ev_xfer_uri_simple (comics_document->archive, uri, error);
+}
+
+static int
+comics_document_get_n_pages (EvDocument *document)
+{
+ ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+
+ if (comics_document->page_names == NULL)
+ return 0;
+
+ return comics_document->page_names->len;
+}
+
+static void
+comics_document_get_page_size (EvDocument *document,
+ EvPage *page,
+ double *width,
+ double *height)
+{
+ GdkPixbufLoader *loader;
+ char **argv;
+ guchar buf[1024];
+ gboolean success, got_size = FALSE;
+ gint outpipe = -1;
+ GPid child_pid;
+ gssize bytes;
+ GdkPixbuf *pixbuf;
+ gchar *filename;
+ ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+
+ if (!comics_document->decompress_tmp) {
+ argv = extract_argv (document, page->index);
+ success = g_spawn_async_with_pipes (NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH |
+ G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL,
+ &child_pid,
+ NULL, &outpipe, NULL, NULL);
+ g_strfreev (argv);
+ g_return_if_fail (success == TRUE);
+
+ loader = gdk_pixbuf_loader_new ();
+ g_signal_connect (loader, "area-prepared",
+ G_CALLBACK (get_page_size_area_prepared_cb),
+ &got_size);
+
+ while (outpipe >= 0) {
+ bytes = read (outpipe, buf, 1024);
+
+ if (bytes > 0)
+ gdk_pixbuf_loader_write (loader, buf, bytes, NULL);
+ if (bytes <= 0 || got_size) {
+ close (outpipe);
+ outpipe = -1;
+ gdk_pixbuf_loader_close (loader, NULL);
+ }
+ }
+
+ if (gdk_pixbuf_loader_get_pixbuf (loader)) {
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (width)
+ *width = gdk_pixbuf_get_width (pixbuf);
+ if (height)
+ *height = gdk_pixbuf_get_height (pixbuf);
+ }
+
+ g_spawn_close_pid (child_pid);
+ g_object_unref (loader);
+ } else {
+ filename = g_build_filename (comics_document->dir,
+ (char *) comics_document->page_names->pdata[page->index],
+ NULL);
+ pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+ g_free (filename);
+ if (width)
+ *width = gdk_pixbuf_get_width (pixbuf);
+ if (height)
+ *height = gdk_pixbuf_get_height (pixbuf);
+ }
+}
+
+static void
+get_page_size_area_prepared_cb (GdkPixbufLoader *loader,
+ gpointer data)
+{
+ gboolean *got_size = data;
+ *got_size = TRUE;
+}
+
+static GdkPixbuf *
+comics_document_render_pixbuf (EvDocument *document,
+ EvRenderContext *rc)
+{
+ GdkPixbufLoader *loader;
+ GdkPixbuf *rotated_pixbuf;
+ char **argv;
+ guchar buf[4096];
+ gboolean success;
+ gint outpipe = -1;
+ GPid child_pid;
+ gssize bytes;
+ gint width, height;
+ gchar *filename;
+ ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+
+ if (!comics_document->decompress_tmp) {
+ argv = extract_argv (document, rc->page->index);
+ success = g_spawn_async_with_pipes (NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH |
+ G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL,
+ &child_pid,
+ NULL, &outpipe, NULL, NULL);
+ g_strfreev (argv);
+ g_return_val_if_fail (success == TRUE, NULL);
+
+ loader = gdk_pixbuf_loader_new ();
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (render_pixbuf_size_prepared_cb),
+ &rc->scale);
+
+ while (outpipe >= 0) {
+ bytes = read (outpipe, buf, 4096);
+
+ if (bytes > 0) {
+ gdk_pixbuf_loader_write (loader, buf, bytes,
+ NULL);
+ } else if (bytes <= 0) {
+ close (outpipe);
+ gdk_pixbuf_loader_close (loader, NULL);
+ outpipe = -1;
+ }
+ }
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (
+ gdk_pixbuf_loader_get_pixbuf (loader),
+ 360 - rc->rotation);
+ g_spawn_close_pid (child_pid);
+ g_object_unref (loader);
+ } else {
+ filename =
+ g_build_filename (comics_document->dir,
+ (char *) comics_document->page_names->pdata[rc->page->index],
+ NULL);
+
+ gdk_pixbuf_get_file_info (filename, &width, &height);
+
+ rotated_pixbuf =
+ gdk_pixbuf_rotate_simple (gdk_pixbuf_new_from_file_at_size (
+ filename, width * (rc->scale) + 0.5,
+ height * (rc->scale) + 0.5, NULL),
+ 360 - rc->rotation);
+ g_free (filename);
+
+ }
+ return rotated_pixbuf;
+}
+
+static cairo_surface_t *
+comics_document_render (EvDocument *document,
+ EvRenderContext *rc)
+{
+ GdkPixbuf *pixbuf;
+ cairo_surface_t *surface;
+
+ pixbuf = comics_document_render_pixbuf (document, rc);
+ surface = ev_document_misc_surface_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ return surface;
+}
+
+static void
+render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
+ gint width,
+ gint height,
+ gpointer data)
+{
+ double *scale = data;
+ int w = (width * (*scale) + 0.5);
+ int h = (height * (*scale) + 0.5);
+
+ gdk_pixbuf_loader_set_size (loader, w, h);
+}
+
+/**
+ * comics_remove_dir: Removes a directory recursively.
+ * Returns:
+ * 0 if it was successfully deleted,
+ * -1 if an error occurred
+ */
+static int
+comics_remove_dir (gchar *path_name)
+{
+ GDir *content_dir;
+ const gchar *filename;
+ gchar *filename_with_path;
+
+ if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) {
+ content_dir = g_dir_open (path_name, 0, NULL);
+ filename = g_dir_read_name (content_dir);
+ while (filename) {
+ filename_with_path =
+ g_build_filename (path_name,
+ filename, NULL);
+ comics_remove_dir (filename_with_path);
+ g_free (filename_with_path);
+ filename = g_dir_read_name (content_dir);
+ }
+ g_dir_close (content_dir);
+ }
+ /* Note from g_remove() documentation: on Windows, it is in general not
+ * possible to remove a file that is open to some process, or mapped
+ * into memory.*/
+ return (g_remove (path_name));
+}
+
+static void
+comics_document_finalize (GObject *object)
+{
+ ComicsDocument *comics_document = COMICS_DOCUMENT (object);
+
+ if (comics_document->decompress_tmp) {
+ if (comics_remove_dir (comics_document->dir) == -1)
+ g_warning (_("There was an error deleting “%s”."),
+ comics_document->dir);
+ g_free (comics_document->dir);
+ }
+
+ if (comics_document->page_names) {
+ g_ptr_array_foreach (comics_document->page_names, (GFunc) g_free, NULL);
+ g_ptr_array_free (comics_document->page_names, TRUE);
+ }
+
+ g_free (comics_document->archive);
+ g_free (comics_document->selected_command);
+ g_free (comics_document->alternative_command);
+ g_free (comics_document->extract_command);
+ g_free (comics_document->list_command);
+
+ G_OBJECT_CLASS (comics_document_parent_class)->finalize (object);
+}
+
+static void
+comics_document_class_init (ComicsDocumentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
+
+ gobject_class->finalize = comics_document_finalize;
+
+ ev_document_class->load = comics_document_load;
+ ev_document_class->save = comics_document_save;
+ ev_document_class->get_n_pages = comics_document_get_n_pages;
+ ev_document_class->get_page_size = comics_document_get_page_size;
+ ev_document_class->render = comics_document_render;
+}
+
+static void
+comics_document_init (ComicsDocument *comics_document)
+{
+ comics_document->archive = NULL;
+ comics_document->page_names = NULL;
+ comics_document->extract_command = NULL;
+}
+
+/* Returns a list of file extensions supported by gdk-pixbuf */
+static GSList*
+get_supported_image_extensions()
+{
+ GSList *extensions = NULL;
+ GSList *formats = gdk_pixbuf_get_formats ();
+ GSList *l;
+
+ for (l = formats; l != NULL; l = l->next) {
+ int i;
+ gchar **ext = gdk_pixbuf_format_get_extensions (l->data);
+
+ for (i = 0; ext[i] != NULL; i++) {
+ extensions = g_slist_append (extensions,
+ g_strdup (ext[i]));
+ }
+
+ g_strfreev (ext);
+ }
+
+ g_slist_free (formats);
+ return extensions;
+}
+
+static GdkPixbuf *
+comics_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ EvRenderContext *rc,
+ gboolean border)
+{
+ GdkPixbuf *thumbnail;
+
+ thumbnail = comics_document_render_pixbuf (EV_DOCUMENT (document), rc);
+
+ if (border) {
+ GdkPixbuf *tmp_pixbuf = thumbnail;
+
+ thumbnail = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+ }
+
+ return thumbnail;
+}
+
+static void
+comics_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ EvRenderContext *rc,
+ gint *width,
+ gint *height)
+{
+ gdouble page_width, page_height;
+
+ comics_document_get_page_size (EV_DOCUMENT (document), rc->page,
+ &page_width, &page_height);
+
+ if (rc->rotation == 90 || rc->rotation == 270) {
+ *width = (gint) (page_height * rc->scale);
+ *height = (gint) (page_width * rc->scale);
+ } else {
+ *width = (gint) (page_width * rc->scale);
+ *height = (gint) (page_height * rc->scale);
+ }
+}
+
+static void
+comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface)
+{
+ iface->get_thumbnail = comics_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = comics_document_thumbnails_get_dimensions;
+}
+
+static char**
+extract_argv (EvDocument *document, gint page)
+{
+ ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+ char **argv;
+ char *command_line, *quoted_archive, *quoted_filename;
+ GError *err = NULL;
+
+ if (page >= comics_document->page_names->len)
+ return NULL;
+
+ if (comics_document->regex_arg) {
+ quoted_archive = comics_regex_quote (comics_document->archive);
+ quoted_filename =
+ comics_regex_quote (comics_document->page_names->pdata[page]);
+ } else {
+ quoted_archive = g_shell_quote (comics_document->archive);
+ quoted_filename = g_shell_quote (comics_document->page_names->pdata[page]);
+ }
+
+ command_line = g_strdup_printf ("%s %s %s",
+ comics_document->extract_command,
+ quoted_archive,
+ quoted_filename);
+ g_shell_parse_argv (command_line, NULL, &argv, &err);
+
+ if (err) {
+ g_warning (_("Error %s"), err->message);
+ g_error_free (err);
+ return NULL;
+ }
+
+ g_free (command_line);
+ g_free (quoted_archive);
+ g_free (quoted_filename);
+ return argv;
+}
diff --git a/backend/comics/comics-document.h b/backend/comics/comics-document.h
new file mode 100644
index 00000000..76feef05
--- /dev/null
+++ b/backend/comics/comics-document.h
@@ -0,0 +1,38 @@
+/* comics-document.h: Implementation of EvDocument for comic book archives
+ * Copyright (C) 2005, Teemu Tervo <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __COMICS_DOCUMENT_H__
+#define __COMICS_DOCUMENT_H__
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+#define COMICS_TYPE_DOCUMENT (comics_document_get_type ())
+#define COMICS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COMICS_TYPE_DOCUMENT, ComicsDocument))
+#define COMICS_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COMICS_TYPE_DOCUMENT))
+
+typedef struct _ComicsDocument ComicsDocument;
+
+GType comics_document_get_type (void) G_GNUC_CONST;
+
+G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __COMICS_DOCUMENT_H__ */
diff --git a/backend/comics/comicsdocument.evince-backend.in b/backend/comics/comicsdocument.evince-backend.in
new file mode 100644
index 00000000..90d4c01b
--- /dev/null
+++ b/backend/comics/comicsdocument.evince-backend.in
@@ -0,0 +1,4 @@
+[Evince Backend]
+Module=comicsdocument
+_TypeDescription=Comic Books
+MimeType=application/x-cbr;application/x-cbz;application/x-cb7;application/x-cbt;
diff --git a/backend/djvu/Makefile.am b/backend/djvu/Makefile.am
new file mode 100644
index 00000000..6bc9d06e
--- /dev/null
+++ b/backend/djvu/Makefile.am
@@ -0,0 +1,38 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ -DMATEICONDIR=\""${prefix}/${DATADIRNAME}/pixmaps"\" \
+ -DMATELOCALEDIR=\"$(datadir)/locale\" \
+ -DEVINCE_COMPILATION \
+ $(BACKEND_CFLAGS) \
+ $(DJVU_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED)
+
+backend_LTLIBRARIES = libdjvudocument.la
+
+libdjvudocument_la_SOURCES = \
+ djvu-document.c \
+ djvu-document.h \
+ djvu-document-private.h \
+ djvu-links.c \
+ djvu-links.h \
+ djvu-text-page.c \
+ djvu-text-page.h
+
+libdjvudocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS)
+libdjvudocument_la_LIBADD = \
+ $(top_builddir)/libdocument/libevdocument.la \
+ $(BACKEND_LIBS) \
+ $(DJVU_LIBS)
+
+backend_in_files = djvudocument.evince-backend.in
+backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend)
+
+EXTRA_DIST = $(backend_in_files)
+
+CLEANFILES = $(backend_DATA)
+
+
+-include $(top_srcdir)/git.mk
diff --git a/backend/djvu/djvu-document-private.h b/backend/djvu/djvu-document-private.h
new file mode 100644
index 00000000..2ec9b67a
--- /dev/null
+++ b/backend/djvu/djvu-document-private.h
@@ -0,0 +1,48 @@
+/*
+ * Declarations used throughout the djvu classes
+ *
+ * Copyright (C) 2006, Michael Hofmann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DJVU_DOCUMENT_INTERNAL_H__
+#define __DJVU_DOCUMENT_INTERNAL_H__
+
+#include "djvu-document.h"
+
+#include <libdjvu/ddjvuapi.h>
+
+struct _DjvuDocument {
+ EvDocument parent_instance;
+
+ ddjvu_context_t *d_context;
+ ddjvu_document_t *d_document;
+ ddjvu_format_t *d_format;
+ ddjvu_format_t *thumbs_format;
+
+ gchar *uri;
+
+ /* PS exporter */
+ gchar *ps_filename;
+ GString *opts;
+};
+
+int djvu_document_get_n_pages (EvDocument *document);
+void djvu_handle_events (DjvuDocument *djvu_document,
+ int wait,
+ GError **error);
+
+#endif /* __DJVU_DOCUMENT_INTERNAL_H__ */
diff --git a/backend/djvu/djvu-document.c b/backend/djvu/djvu-document.c
new file mode 100644
index 00000000..aa0e595d
--- /dev/null
+++ b/backend/djvu/djvu-document.c
@@ -0,0 +1,706 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005, Nickolay V. Shmyrev <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <config.h>
+#include "djvu-document.h"
+#include "djvu-text-page.h"
+#include "djvu-links.h"
+#include "djvu-document-private.h"
+#include "ev-document-thumbnails.h"
+#include "ev-file-exporter.h"
+#include "ev-document-misc.h"
+#include "ev-document-find.h"
+#include "ev-document-links.h"
+#include "ev-selection.h"
+#include "ev-file-helpers.h"
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+#define SCALE_FACTOR 0.2
+
+enum {
+ PROP_0,
+ PROP_TITLE
+};
+
+struct _DjvuDocumentClass
+{
+ EvDocumentClass parent_class;
+};
+
+typedef struct _DjvuDocumentClass DjvuDocumentClass;
+
+static void djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
+static void djvu_document_file_exporter_iface_init (EvFileExporterInterface *iface);
+static void djvu_document_find_iface_init (EvDocumentFindInterface *iface);
+static void djvu_document_document_links_iface_init (EvDocumentLinksInterface *iface);
+static void djvu_selection_iface_init (EvSelectionInterface *iface);
+
+EV_BACKEND_REGISTER_WITH_CODE (DjvuDocument, djvu_document,
+ {
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, djvu_document_document_thumbnails_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, djvu_document_file_exporter_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND, djvu_document_find_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS, djvu_document_document_links_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION, djvu_selection_iface_init);
+ });
+
+
+#define EV_DJVU_ERROR ev_djvu_error_quark ()
+
+static GQuark
+ev_djvu_error_quark (void)
+{
+ static GQuark q = 0;
+ if (q == 0)
+ q = g_quark_from_string ("ev-djvu-quark");
+
+ return q;
+}
+
+static void
+handle_message (const ddjvu_message_t *msg, GError **error)
+{
+ switch (msg->m_any.tag) {
+ case DDJVU_ERROR: {
+ gchar *error_str;
+
+ if (msg->m_error.filename) {
+ error_str = g_strdup_printf ("DjvuLibre error: %s:%d",
+ msg->m_error.filename,
+ msg->m_error.lineno);
+ } else {
+ error_str = g_strdup_printf ("DjvuLibre error: %s",
+ msg->m_error.message);
+ }
+
+ if (error) {
+ g_set_error_literal (error, EV_DJVU_ERROR, 0, error_str);
+ } else {
+ g_warning ("%s", error_str);
+ }
+
+ g_free (error_str);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void
+djvu_handle_events (DjvuDocument *djvu_document, int wait, GError **error)
+{
+ ddjvu_context_t *ctx = djvu_document->d_context;
+ const ddjvu_message_t *msg;
+
+ if (!ctx)
+ return;
+
+ if (wait)
+ ddjvu_message_wait (ctx);
+
+ while ((msg = ddjvu_message_peek (ctx))) {
+ handle_message (msg, error);
+ ddjvu_message_pop (ctx);
+ if (error && *error)
+ return;
+ }
+}
+
+static void
+djvu_wait_for_message (DjvuDocument *djvu_document, ddjvu_message_tag_t message, GError **error)
+{
+ ddjvu_context_t *ctx = djvu_document->d_context;
+ const ddjvu_message_t *msg;
+
+ ddjvu_message_wait (ctx);
+ while ((msg = ddjvu_message_peek (ctx)) && (msg->m_any.tag != message)) {
+ handle_message (msg, error);
+ ddjvu_message_pop (ctx);
+ if (error && *error)
+ return;
+ }
+ if (msg && msg->m_any.tag == message)
+ ddjvu_message_pop (ctx);
+}
+
+static gboolean
+djvu_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ ddjvu_document_t *doc;
+ gchar *filename;
+ gboolean missing_files = FALSE;
+ GError *djvu_error = NULL;
+
+ /* FIXME: We could actually load uris */
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ return FALSE;
+
+ doc = ddjvu_document_create_by_filename (djvu_document->d_context, filename, TRUE);
+
+ if (!doc) {
+ g_free (filename);
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("DjVu document has incorrect format"));
+ return FALSE;
+ }
+
+ if (djvu_document->d_document)
+ ddjvu_document_release (djvu_document->d_document);
+
+ djvu_document->d_document = doc;
+
+ djvu_wait_for_message (djvu_document, DDJVU_DOCINFO, &djvu_error);
+ if (djvu_error) {
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ djvu_error->message);
+ g_error_free (djvu_error);
+ g_free (filename);
+ ddjvu_document_release (djvu_document->d_document);
+ djvu_document->d_document = NULL;
+
+ return FALSE;
+ }
+
+ if (ddjvu_document_decoding_error (djvu_document->d_document))
+ djvu_handle_events (djvu_document, TRUE, &djvu_error);
+
+ if (djvu_error) {
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ djvu_error->message);
+ g_error_free (djvu_error);
+ g_free (filename);
+ ddjvu_document_release (djvu_document->d_document);
+ djvu_document->d_document = NULL;
+
+ return FALSE;
+ }
+
+ g_free (djvu_document->uri);
+ djvu_document->uri = g_strdup (uri);
+
+ if (ddjvu_document_get_type (djvu_document->d_document) == DDJVU_DOCTYPE_INDIRECT) {
+ gint n_files;
+ gint i;
+ gchar *base;
+
+ base = g_path_get_dirname (filename);
+
+ n_files = ddjvu_document_get_filenum (djvu_document->d_document);
+ for (i = 0; i < n_files; i++) {
+ struct ddjvu_fileinfo_s fileinfo;
+ gchar *file;
+
+ ddjvu_document_get_fileinfo (djvu_document->d_document,
+ i, &fileinfo);
+
+ if (fileinfo.type != 'P')
+ continue;
+
+ file = g_build_filename (base, fileinfo.id, NULL);
+ if (!g_file_test (file, G_FILE_TEST_EXISTS)) {
+ missing_files = TRUE;
+ g_free (file);
+
+ break;
+ }
+ g_free (file);
+ }
+ g_free (base);
+ }
+ g_free (filename);
+
+ if (missing_files) {
+ g_set_error_literal (error,
+ G_FILE_ERROR,
+ G_FILE_ERROR_EXIST,
+ _("The document is composed of several files. "
+ "One or more of these files cannot be accessed."));
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+djvu_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+
+ return ev_xfer_uri_simple (djvu_document->uri, uri, error);
+}
+
+int
+djvu_document_get_n_pages (EvDocument *document)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+
+ g_return_val_if_fail (djvu_document->d_document, 0);
+
+ return ddjvu_document_get_pagenum (djvu_document->d_document);
+}
+
+static void
+document_get_page_size (DjvuDocument *djvu_document,
+ gint page,
+ double *width,
+ double *height)
+{
+ ddjvu_pageinfo_t info;
+ ddjvu_status_t r;
+
+ while ((r = ddjvu_document_get_pageinfo(djvu_document->d_document, page, &info)) < DDJVU_JOB_OK)
+ djvu_handle_events(djvu_document, TRUE, NULL);
+
+ if (r >= DDJVU_JOB_FAILED)
+ djvu_handle_events(djvu_document, TRUE, NULL);
+
+ *width = info.width * SCALE_FACTOR;
+ *height = info.height * SCALE_FACTOR;
+}
+
+static void
+djvu_document_get_page_size (EvDocument *document,
+ EvPage *page,
+ double *width,
+ double *height)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+
+ g_return_if_fail (djvu_document->d_document);
+
+ document_get_page_size (djvu_document, page->index,
+ width, height);
+}
+
+static cairo_surface_t *
+djvu_document_render (EvDocument *document,
+ EvRenderContext *rc)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ cairo_surface_t *surface;
+ gchar *pixels;
+ gint rowstride;
+ ddjvu_rect_t rrect;
+ ddjvu_rect_t prect;
+ ddjvu_page_t *d_page;
+ ddjvu_page_rotation_t rotation;
+ double page_width, page_height, tmp;
+
+ d_page = ddjvu_page_create_by_pageno (djvu_document->d_document, rc->page->index);
+
+ while (!ddjvu_page_decoding_done (d_page))
+ djvu_handle_events(djvu_document, TRUE, NULL);
+
+ page_width = ddjvu_page_get_width (d_page) * rc->scale * SCALE_FACTOR + 0.5;
+ page_height = ddjvu_page_get_height (d_page) * rc->scale * SCALE_FACTOR + 0.5;
+
+ switch (rc->rotation) {
+ case 90:
+ rotation = DDJVU_ROTATE_90;
+ tmp = page_height;
+ page_height = page_width;
+ page_width = tmp;
+
+ break;
+ case 180:
+ rotation = DDJVU_ROTATE_180;
+
+ break;
+ case 270:
+ rotation = DDJVU_ROTATE_270;
+ tmp = page_height;
+ page_height = page_width;
+ page_width = tmp;
+
+ break;
+ default:
+ rotation = DDJVU_ROTATE_0;
+ }
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ page_width, page_height);
+ rowstride = cairo_image_surface_get_stride (surface);
+ pixels = (gchar *)cairo_image_surface_get_data (surface);
+
+ prect.x = 0;
+ prect.y = 0;
+ prect.w = page_width;
+ prect.h = page_height;
+ rrect = prect;
+
+ ddjvu_page_set_rotation (d_page, rotation);
+
+ ddjvu_page_render (d_page, DDJVU_RENDER_COLOR,
+ &prect,
+ &rrect,
+ djvu_document->d_format,
+ rowstride,
+ pixels);
+
+ cairo_surface_mark_dirty (surface);
+
+ return surface;
+}
+
+static void
+djvu_document_finalize (GObject *object)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (object);
+
+ if (djvu_document->d_document)
+ ddjvu_document_release (djvu_document->d_document);
+
+ if (djvu_document->opts)
+ g_string_free (djvu_document->opts, TRUE);
+
+ if (djvu_document->ps_filename)
+ g_free (djvu_document->ps_filename);
+
+ ddjvu_context_release (djvu_document->d_context);
+ ddjvu_format_release (djvu_document->d_format);
+ ddjvu_format_release (djvu_document->thumbs_format);
+ g_free (djvu_document->uri);
+
+ G_OBJECT_CLASS (djvu_document_parent_class)->finalize (object);
+}
+
+static void
+djvu_document_class_init (DjvuDocumentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
+
+ gobject_class->finalize = djvu_document_finalize;
+
+ ev_document_class->load = djvu_document_load;
+ ev_document_class->save = djvu_document_save;
+ ev_document_class->get_n_pages = djvu_document_get_n_pages;
+ ev_document_class->get_page_size = djvu_document_get_page_size;
+ ev_document_class->render = djvu_document_render;
+}
+
+static gchar *
+djvu_text_copy (DjvuDocument *djvu_document,
+ gint page,
+ EvRectangle *rectangle)
+{
+ miniexp_t page_text;
+ gchar *text = NULL;
+
+ while ((page_text =
+ ddjvu_document_get_pagetext (djvu_document->d_document,
+ page, "char")) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE, NULL);
+
+ if (page_text != miniexp_nil) {
+ DjvuTextPage *page = djvu_text_page_new (page_text);
+
+ text = djvu_text_page_copy (page, rectangle);
+ djvu_text_page_free (page);
+ ddjvu_miniexp_release (djvu_document->d_document, page_text);
+ }
+
+ return text;
+}
+
+static gchar *
+djvu_selection_get_selected_text (EvSelection *selection,
+ EvPage *page,
+ EvSelectionStyle style,
+ EvRectangle *points)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (selection);
+ double width, height;
+ EvRectangle rectangle;
+ gchar *text;
+
+ djvu_document_get_page_size (EV_DOCUMENT (djvu_document),
+ page, &width, &height);
+ rectangle.x1 = points->x1 / SCALE_FACTOR;
+ rectangle.y1 = (height - points->y2) / SCALE_FACTOR;
+ rectangle.x2 = points->x2 / SCALE_FACTOR;
+ rectangle.y2 = (height - points->y1) / SCALE_FACTOR;
+
+ text = djvu_text_copy (djvu_document, page->index, &rectangle);
+
+ if (text == NULL)
+ text = g_strdup ("");
+
+ return text;
+}
+
+static void
+djvu_selection_iface_init (EvSelectionInterface *iface)
+{
+ iface->get_selected_text = djvu_selection_get_selected_text;
+}
+
+static void
+djvu_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ EvRenderContext *rc,
+ gint *width,
+ gint *height)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ gdouble page_width, page_height;
+
+ djvu_document_get_page_size (EV_DOCUMENT(djvu_document), rc->page,
+ &page_width, &page_height);
+
+ if (rc->rotation == 90 || rc->rotation == 270) {
+ *width = (gint) (page_height * rc->scale);
+ *height = (gint) (page_width * rc->scale);
+ } else {
+ *width = (gint) (page_width * rc->scale);
+ *height = (gint) (page_height * rc->scale);
+ }
+}
+
+static GdkPixbuf *
+djvu_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ EvRenderContext *rc,
+ gboolean border)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ GdkPixbuf *pixbuf, *rotated_pixbuf;
+ gdouble page_width, page_height;
+ gint thumb_width, thumb_height;
+ guchar *pixels;
+
+ g_return_val_if_fail (djvu_document->d_document, NULL);
+
+ djvu_document_get_page_size (EV_DOCUMENT(djvu_document), rc->page,
+ &page_width, &page_height);
+
+ thumb_width = (gint) (page_width * rc->scale);
+ thumb_height = (gint) (page_height * rc->scale);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
+ thumb_width, thumb_height);
+ gdk_pixbuf_fill (pixbuf, 0xffffffff);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ while (ddjvu_thumbnail_status (djvu_document->d_document, rc->page->index, 1) < DDJVU_JOB_OK)
+ djvu_handle_events(djvu_document, TRUE, NULL);
+
+ ddjvu_thumbnail_render (djvu_document->d_document, rc->page->index,
+ &thumb_width, &thumb_height,
+ djvu_document->thumbs_format,
+ gdk_pixbuf_get_rowstride (pixbuf),
+ (gchar *)pixels);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
+ g_object_unref (pixbuf);
+
+ if (border) {
+ GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
+
+ rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+ }
+
+ return rotated_pixbuf;
+}
+
+static void
+djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface)
+{
+ iface->get_thumbnail = djvu_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = djvu_document_thumbnails_get_dimensions;
+}
+
+/* EvFileExporterIface */
+static void
+djvu_document_file_exporter_begin (EvFileExporter *exporter,
+ EvFileExporterContext *fc)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (exporter);
+
+ if (djvu_document->ps_filename)
+ g_free (djvu_document->ps_filename);
+ djvu_document->ps_filename = g_strdup (fc->filename);
+
+ g_string_assign (djvu_document->opts, "-page=");
+}
+
+static void
+djvu_document_file_exporter_do_page (EvFileExporter *exporter,
+ EvRenderContext *rc)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (exporter);
+
+ g_string_append_printf (djvu_document->opts, "%d,", (rc->page->index) + 1);
+}
+
+static void
+djvu_document_file_exporter_end (EvFileExporter *exporter)
+{
+ int d_optc = 1;
+ const char *d_optv[d_optc];
+
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (exporter);
+
+ FILE *fn = fopen (djvu_document->ps_filename, "w");
+ if (fn == NULL) {
+ g_warning ("Cannot open file “%s”.", djvu_document->ps_filename);
+ return;
+ }
+
+ d_optv[0] = djvu_document->opts->str;
+
+ ddjvu_job_t * job = ddjvu_document_print(djvu_document->d_document, fn, d_optc, d_optv);
+ while (!ddjvu_job_done(job)) {
+ djvu_handle_events (djvu_document, TRUE, NULL);
+ }
+
+ fclose(fn);
+}
+
+static EvFileExporterCapabilities
+djvu_document_file_exporter_get_capabilities (EvFileExporter *exporter)
+{
+ return EV_FILE_EXPORTER_CAN_PAGE_SET |
+ EV_FILE_EXPORTER_CAN_COPIES |
+ EV_FILE_EXPORTER_CAN_COLLATE |
+ EV_FILE_EXPORTER_CAN_REVERSE |
+ EV_FILE_EXPORTER_CAN_GENERATE_PS;
+}
+
+static void
+djvu_document_file_exporter_iface_init (EvFileExporterInterface *iface)
+{
+ iface->begin = djvu_document_file_exporter_begin;
+ iface->do_page = djvu_document_file_exporter_do_page;
+ iface->end = djvu_document_file_exporter_end;
+ iface->get_capabilities = djvu_document_file_exporter_get_capabilities;
+}
+
+static void
+djvu_document_init (DjvuDocument *djvu_document)
+{
+ guint masks[4] = { 0xff0000, 0xff00, 0xff, 0xff000000 };
+
+ djvu_document->d_context = ddjvu_context_create ("Evince");
+ djvu_document->d_format = ddjvu_format_create (DDJVU_FORMAT_RGBMASK32, 4, masks);
+ ddjvu_format_set_row_order (djvu_document->d_format, 1);
+
+ djvu_document->thumbs_format = ddjvu_format_create (DDJVU_FORMAT_RGB24, 0, 0);
+ ddjvu_format_set_row_order (djvu_document->thumbs_format, 1);
+
+ djvu_document->ps_filename = NULL;
+ djvu_document->opts = g_string_new ("");
+
+ djvu_document->d_document = NULL;
+}
+
+static GList *
+djvu_document_find_find_text (EvDocumentFind *document,
+ EvPage *page,
+ const char *text,
+ gboolean case_sensitive)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ miniexp_t page_text;
+ gdouble width, height;
+ GList *matches = NULL, *l;
+
+ g_return_val_if_fail (text != NULL, NULL);
+
+ while ((page_text = ddjvu_document_get_pagetext (djvu_document->d_document,
+ page->index,
+ "char")) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE, NULL);
+
+ if (page_text != miniexp_nil) {
+ DjvuTextPage *tpage = djvu_text_page_new (page_text);
+
+ djvu_text_page_prepare_search (tpage, case_sensitive);
+ if (tpage->links->len > 0) {
+ djvu_text_page_search (tpage, text);
+ matches = tpage->results;
+ }
+ djvu_text_page_free (tpage);
+ ddjvu_miniexp_release (djvu_document->d_document, page_text);
+ }
+
+ if (!matches)
+ return NULL;
+
+ document_get_page_size (djvu_document, page->index, &width, &height);
+ for (l = matches; l && l->data; l = g_list_next (l)) {
+ EvRectangle *r = (EvRectangle *)l->data;
+ gdouble tmp;
+
+ tmp = r->y1;
+
+ r->x1 *= SCALE_FACTOR;
+ r->x2 *= SCALE_FACTOR;
+
+ tmp = r->y1;
+ r->y1 = height - r->y2 * SCALE_FACTOR;
+ r->y2 = height - tmp * SCALE_FACTOR;
+ }
+
+
+ return matches;
+}
+
+static void
+djvu_document_find_iface_init (EvDocumentFindInterface *iface)
+{
+ iface->find_text = djvu_document_find_find_text;
+}
+
+static EvMappingList *
+djvu_document_links_get_links (EvDocumentLinks *document_links,
+ EvPage *page)
+{
+ return djvu_links_get_links (document_links, page->index, SCALE_FACTOR);
+}
+
+static void
+djvu_document_document_links_iface_init (EvDocumentLinksInterface *iface)
+{
+ iface->has_document_links = djvu_links_has_document_links;
+ iface->get_links_model = djvu_links_get_links_model;
+ iface->get_links = djvu_document_links_get_links;
+ iface->find_link_dest = djvu_links_find_link_dest;
+}
diff --git a/backend/djvu/djvu-document.h b/backend/djvu/djvu-document.h
new file mode 100644
index 00000000..bcb916af
--- /dev/null
+++ b/backend/djvu/djvu-document.h
@@ -0,0 +1,38 @@
+/* djvu-document.h: Implementation of EvDocument for djvu documents
+ * Copyright (C) 2005, Nickolay V. Shmyrev <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DJVU_DOCUMENT_H__
+#define __DJVU_DOCUMENT_H__
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+#define DJVU_TYPE_DOCUMENT (djvu_document_get_type ())
+#define DJVU_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DJVU_TYPE_DOCUMENT, DjvuDocument))
+#define DJVU_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DJVU_TYPE_DOCUMENT))
+
+typedef struct _DjvuDocument DjvuDocument;
+
+GType djvu_document_get_type (void) G_GNUC_CONST;
+
+G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __DJVU_DOCUMENT_H__ */
diff --git a/backend/djvu/djvu-links.c b/backend/djvu/djvu-links.c
new file mode 100644
index 00000000..d13af0be
--- /dev/null
+++ b/backend/djvu/djvu-links.c
@@ -0,0 +1,434 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Implements hyperlink functionality for Djvu files.
+ * Copyright (C) 2006 Pauli Virtanen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib.h>
+#include <libdjvu/miniexp.h>
+#include "djvu-document.h"
+#include "djvu-links.h"
+#include "djvu-document-private.h"
+#include "ev-document-links.h"
+#include "ev-mapping-list.h"
+
+static gboolean number_from_miniexp(miniexp_t sexp, int *number)
+{
+ if (miniexp_numberp (sexp)) {
+ *number = miniexp_to_int (sexp);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static gboolean string_from_miniexp(miniexp_t sexp, const char **str)
+{
+ if (miniexp_stringp (sexp)) {
+ *str = miniexp_to_str (sexp);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static gboolean number_from_string_10(const gchar *str, guint64 *number)
+{
+ gchar *end_ptr;
+
+ *number = g_ascii_strtoull(str, &end_ptr, 10);
+ if (*end_ptr == '\0') {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static EvLinkDest *
+get_djvu_link_dest (const DjvuDocument *djvu_document, const gchar *link_name, int base_page)
+{
+ guint64 page_num = 0;
+
+ /* #pagenum, #+pageoffset, #-pageoffset */
+ if (g_str_has_prefix (link_name, "#")) {
+ if (base_page > 0 && g_str_has_prefix (link_name+1, "+")) {
+ if (number_from_string_10 (link_name + 2, &page_num)) {
+ return ev_link_dest_new_page (base_page + page_num);
+ }
+ } else if (base_page > 0 && g_str_has_prefix (link_name+1, "-")) {
+ if (number_from_string_10 (link_name + 2, &page_num)) {
+ return ev_link_dest_new_page (base_page - page_num);
+ }
+ } else {
+ if (number_from_string_10 (link_name + 1, &page_num)) {
+ return ev_link_dest_new_page (page_num - 1);
+ }
+ }
+ } else {
+ /* FIXME: component file identifiers */
+ }
+
+ return NULL;
+}
+
+static EvLinkAction *
+get_djvu_link_action (const DjvuDocument *djvu_document, const gchar *link_name, int base_page)
+{
+ EvLinkDest *ev_dest = NULL;
+ EvLinkAction *ev_action = NULL;
+
+ ev_dest = get_djvu_link_dest (djvu_document, link_name, base_page);
+
+ if (ev_dest) {
+ ev_action = ev_link_action_new_dest (ev_dest);
+ } else if (strstr(link_name, "://") != NULL) {
+ /* It's probably an URI */
+ ev_action = ev_link_action_new_external_uri (link_name);
+ } else {
+ /* FIXME: component file identifiers */
+ }
+
+ return ev_action;
+}
+
+static gchar *
+str_to_utf8 (const gchar *text)
+{
+ static const gchar *encodings_to_try[2];
+ static gint n_encodings_to_try = 0;
+ gchar *utf8_text = NULL;
+ gint i;
+
+ if (n_encodings_to_try == 0) {
+ const gchar *charset;
+ gboolean charset_is_utf8;
+
+ charset_is_utf8 = g_get_charset (&charset);
+ if (!charset_is_utf8) {
+ encodings_to_try[n_encodings_to_try++] = charset;
+ }
+
+ if (g_ascii_strcasecmp (charset, "ISO-8859-1") != 0) {
+ encodings_to_try[n_encodings_to_try++] = "ISO-8859-1";
+ }
+ }
+
+ for (i = 0; i < n_encodings_to_try; i++) {
+ utf8_text = g_convert (text, -1, "UTF-8",
+ encodings_to_try[i],
+ NULL, NULL, NULL);
+ if (utf8_text)
+ break;
+ }
+
+ return utf8_text;
+}
+
+/**
+ * Builds the index GtkTreeModel from DjVu s-expr
+ *
+ * (bookmarks
+ * ("title1" "dest1"
+ * ("title12" "dest12"
+ * ... )
+ * ... )
+ * ("title2" "dest2"
+ * ... )
+ * ... )
+ */
+static void
+build_tree (const DjvuDocument *djvu_document,
+ GtkTreeModel *model,
+ GtkTreeIter *parent,
+ miniexp_t iter)
+{
+ const char *title, *link_dest;
+ char *title_markup;
+
+ EvLinkAction *ev_action = NULL;
+ EvLink *ev_link = NULL;
+ GtkTreeIter tree_iter;
+
+ if (miniexp_car (iter) == miniexp_symbol ("bookmarks")) {
+ /* The (bookmarks) cons */
+ iter = miniexp_cdr (iter);
+ } else if ( miniexp_length (iter) >= 2 ) {
+ gchar *utf8_title = NULL;
+
+ /* An entry */
+ if (!string_from_miniexp (miniexp_car (iter), &title)) goto unknown_entry;
+ if (!string_from_miniexp (miniexp_cadr (iter), &link_dest)) goto unknown_entry;
+
+
+ if (!g_utf8_validate (title, -1, NULL)) {
+ utf8_title = str_to_utf8 (title);
+ title_markup = g_markup_escape_text (utf8_title, -1);
+ } else {
+ title_markup = g_markup_escape_text (title, -1);
+ }
+
+ ev_action = get_djvu_link_action (djvu_document, link_dest, -1);
+
+ if (g_str_has_suffix (link_dest, ".djvu")) {
+ /* FIXME: component file identifiers */
+ } else if (ev_action) {
+ ev_link = ev_link_new (utf8_title ? utf8_title : title, ev_action);
+ gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent);
+ gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter,
+ EV_DOCUMENT_LINKS_COLUMN_MARKUP, title_markup,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, ev_link,
+ EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
+ -1);
+ g_object_unref (ev_link);
+ } else {
+ gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent);
+ gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter,
+ EV_DOCUMENT_LINKS_COLUMN_MARKUP, title_markup,
+ EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
+ -1);
+ }
+
+ g_free (title_markup);
+ g_free (utf8_title);
+ iter = miniexp_cddr (iter);
+ parent = &tree_iter;
+ } else {
+ goto unknown_entry;
+ }
+
+ for (; iter != miniexp_nil; iter = miniexp_cdr (iter)) {
+ build_tree (djvu_document, model, parent, miniexp_car (iter));
+ }
+ return;
+
+ unknown_entry:
+ g_warning ("DjvuLibre error: Unknown entry in bookmarks");
+ return;
+}
+
+static gboolean
+get_djvu_hyperlink_area (ddjvu_pageinfo_t *page_info,
+ miniexp_t sexp,
+ EvMapping *ev_link_mapping)
+{
+ miniexp_t iter;
+
+ iter = sexp;
+
+ if ((miniexp_car (iter) == miniexp_symbol ("rect") || miniexp_car (iter) == miniexp_symbol ("oval"))
+ && miniexp_length (iter) == 5) {
+ /* FIXME: get bounding box for (oval) since Evince doesn't support shaped links */
+ int minx, miny, width, height;
+
+ iter = miniexp_cdr (iter);
+ if (!number_from_miniexp (miniexp_car (iter), &minx)) goto unknown_link;
+ iter = miniexp_cdr (iter);
+ if (!number_from_miniexp (miniexp_car (iter), &miny)) goto unknown_link;
+ iter = miniexp_cdr (iter);
+ if (!number_from_miniexp (miniexp_car (iter), &width)) goto unknown_link;
+ iter = miniexp_cdr (iter);
+ if (!number_from_miniexp (miniexp_car (iter), &height)) goto unknown_link;
+
+ ev_link_mapping->area.x1 = minx;
+ ev_link_mapping->area.x2 = (minx + width);
+ ev_link_mapping->area.y1 = (page_info->height - (miny + height));
+ ev_link_mapping->area.y2 = (page_info->height - miny);
+ } else if (miniexp_car (iter) == miniexp_symbol ("poly")
+ && miniexp_length (iter) >= 5 && miniexp_length (iter) % 2 == 1) {
+
+ /* FIXME: get bounding box since Evince doesn't support shaped links */
+ int minx = G_MAXINT, miny = G_MAXINT;
+ int maxx = G_MININT, maxy = G_MININT;
+
+ iter = miniexp_cdr(iter);
+ while (iter != miniexp_nil) {
+ int x, y;
+
+ if (!number_from_miniexp (miniexp_car(iter), &x)) goto unknown_link;
+ iter = miniexp_cdr (iter);
+ if (!number_from_miniexp (miniexp_car(iter), &y)) goto unknown_link;
+ iter = miniexp_cdr (iter);
+
+ minx = MIN (minx, x);
+ miny = MIN (miny, y);
+ maxx = MAX (maxx, x);
+ maxy = MAX (maxy, y);
+ }
+
+ ev_link_mapping->area.x1 = minx;
+ ev_link_mapping->area.x2 = maxx;
+ ev_link_mapping->area.y1 = (page_info->height - maxy);
+ ev_link_mapping->area.y2 = (page_info->height - miny);
+ } else {
+ /* unknown */
+ goto unknown_link;
+ }
+
+ return TRUE;
+
+ unknown_link:
+ g_warning("DjvuLibre error: Unknown hyperlink area %s", miniexp_to_name(miniexp_car(sexp)));
+ return FALSE;
+}
+
+static EvMapping *
+get_djvu_hyperlink_mapping (DjvuDocument *djvu_document,
+ int page,
+ ddjvu_pageinfo_t *page_info,
+ miniexp_t sexp)
+{
+ EvMapping *ev_link_mapping = NULL;
+ EvLinkAction *ev_action = NULL;
+ miniexp_t iter;
+ const char *url, *url_target, *comment;
+
+ ev_link_mapping = g_new (EvMapping, 1);
+
+ iter = sexp;
+
+ if (miniexp_car (iter) != miniexp_symbol ("maparea")) goto unknown_mapping;
+
+ iter = miniexp_cdr(iter);
+
+ if (miniexp_caar(iter) == miniexp_symbol("url")) {
+ if (!string_from_miniexp (miniexp_cadr (miniexp_car (iter)), &url)) goto unknown_mapping;
+ if (!string_from_miniexp (miniexp_caddr (miniexp_car (iter)), &url_target)) goto unknown_mapping;
+ } else {
+ if (!string_from_miniexp (miniexp_car(iter), &url)) goto unknown_mapping;
+ url_target = NULL;
+ }
+
+ iter = miniexp_cdr (iter);
+ if (!string_from_miniexp (miniexp_car(iter), &comment)) goto unknown_mapping;
+
+ iter = miniexp_cdr (iter);
+ if (!get_djvu_hyperlink_area (page_info, miniexp_car(iter), ev_link_mapping)) goto unknown_mapping;
+
+ iter = miniexp_cdr (iter);
+ /* FIXME: DjVu hyperlink attributes are ignored */
+
+ ev_action = get_djvu_link_action (djvu_document, url, page);
+ if (!ev_action) goto unknown_mapping;
+
+ ev_link_mapping->data = ev_link_new (comment, ev_action);
+
+ return ev_link_mapping;
+
+ unknown_mapping:
+ if (ev_link_mapping) g_free(ev_link_mapping);
+ g_warning("DjvuLibre error: Unknown hyperlink %s", miniexp_to_name(miniexp_car(sexp)));
+ return NULL;
+}
+
+
+gboolean
+djvu_links_has_document_links (EvDocumentLinks *document_links)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document_links);
+ miniexp_t outline;
+
+ while ((outline = ddjvu_document_get_outline (djvu_document->d_document)) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE, NULL);
+
+ if (outline) {
+ ddjvu_miniexp_release (djvu_document->d_document, outline);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+EvMappingList *
+djvu_links_get_links (EvDocumentLinks *document_links,
+ gint page,
+ double scale_factor)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document_links);
+ GList *retval = NULL;
+ miniexp_t page_annotations = miniexp_nil;
+ miniexp_t *hyperlinks = NULL, *iter = NULL;
+ EvMapping *ev_link_mapping;
+ ddjvu_pageinfo_t page_info;
+
+ while ((page_annotations = ddjvu_document_get_pageanno (djvu_document->d_document, page)) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE, NULL);
+
+ while (ddjvu_document_get_pageinfo (djvu_document->d_document, page, &page_info) < DDJVU_JOB_OK)
+ djvu_handle_events(djvu_document, TRUE, NULL);
+
+ if (page_annotations) {
+ hyperlinks = ddjvu_anno_get_hyperlinks (page_annotations);
+ if (hyperlinks) {
+ for (iter = hyperlinks; *iter; ++iter) {
+ ev_link_mapping = get_djvu_hyperlink_mapping (djvu_document, page, &page_info, *iter);
+ if (ev_link_mapping) {
+ ev_link_mapping->area.x1 *= scale_factor;
+ ev_link_mapping->area.x2 *= scale_factor;
+ ev_link_mapping->area.y1 *= scale_factor;
+ ev_link_mapping->area.y2 *= scale_factor;
+ retval = g_list_prepend (retval, ev_link_mapping);
+ }
+ }
+ free (hyperlinks);
+ }
+ ddjvu_miniexp_release (djvu_document->d_document, page_annotations);
+ }
+
+ return ev_mapping_list_new (page, retval, (GDestroyNotify)g_object_unref);
+}
+
+EvLinkDest *
+djvu_links_find_link_dest (EvDocumentLinks *document_links,
+ const gchar *link_name)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document_links);
+ EvLinkDest *ev_dest = NULL;
+
+ ev_dest = get_djvu_link_dest (djvu_document, link_name, -1);
+
+ if (!ev_dest) {
+ g_warning ("DjvuLibre error: unknown link destination %s", link_name);
+ }
+
+ return ev_dest;
+}
+
+GtkTreeModel *
+djvu_links_get_links_model (EvDocumentLinks *document_links)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document_links);
+ GtkTreeModel *model = NULL;
+ miniexp_t outline = miniexp_nil;
+
+ while ((outline = ddjvu_document_get_outline (djvu_document->d_document)) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE, NULL);
+
+ if (outline) {
+ model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING);
+ build_tree (djvu_document, model, NULL, outline);
+
+ ddjvu_miniexp_release (djvu_document->d_document, outline);
+ }
+
+ return model;
+}
diff --git a/backend/djvu/djvu-links.h b/backend/djvu/djvu-links.h
new file mode 100644
index 00000000..76d9072c
--- /dev/null
+++ b/backend/djvu/djvu-links.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2006 Pauli Virtanen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DJVU_LINK_H__
+#define __DJVU_LINK_H__
+
+#include "ev-document-links.h"
+#include "djvu-document.h"
+
+#include <glib.h>
+
+GtkTreeModel *djvu_links_get_links_model (EvDocumentLinks *document_links);
+EvMappingList *djvu_links_get_links (EvDocumentLinks *document_links,
+ gint page,
+ double scale_factor);
+EvLinkDest *djvu_links_find_link_dest (EvDocumentLinks *document_links,
+ const gchar *link_name);
+gboolean djvu_links_has_document_links (EvDocumentLinks *document_links);
+
+#endif /* __DJVU_LINK_H__ */
diff --git a/backend/djvu/djvu-text-page.c b/backend/djvu/djvu-text-page.c
new file mode 100644
index 00000000..3f171d1e
--- /dev/null
+++ b/backend/djvu/djvu-text-page.c
@@ -0,0 +1,445 @@
+/*
+ * Implements search and copy functionality for Djvu files.
+ * Copyright (C) 2006 Michael Hofmann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib.h>
+#include <libdjvu/miniexp.h>
+#include "djvu-text-page.h"
+
+
+/**
+ * djvu_text_page_selection_process:
+ * @page: #DjvuTextPage instance
+ * @p: s-expression to append
+ * @delimit: character/word/... delimiter
+ *
+ * Appends the string in @p to the page text.
+ *
+ * Returns: whether the end was not reached in this s-expression
+ */
+static gboolean
+djvu_text_page_selection_process (DjvuTextPage *page,
+ miniexp_t p,
+ int delimit)
+{
+ if (page->text || p == page->start) {
+ char *token_text = (char *) miniexp_to_str (miniexp_nth (5, p));
+ if (page->text) {
+ char *new_text =
+ g_strjoin (delimit & 2 ? "\n" :
+ delimit & 1 ? " " : NULL,
+ page->text, token_text,
+ NULL);
+ g_free (page->text);
+ page->text = new_text;
+ } else
+ page->text = g_strdup (token_text);
+ if (p == page->end)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * djvu_text_page_selection:
+ * @page: #DjvuTextPage instance
+ * @p: tree to append
+ * @delimit: character/word/... delimiter
+ *
+ * Walks the tree in @p and appends the text with
+ * djvu_text_page_selection_process() for all s-expressions
+ * between the start and end fields.
+ *
+ * Returns: whether the end was not reached in this subtree
+ */
+static gboolean
+djvu_text_page_selection (DjvuTextPage *page,
+ miniexp_t p,
+ int delimit)
+{
+ g_return_val_if_fail (miniexp_consp (p) && miniexp_symbolp
+ (miniexp_car (p)), FALSE);
+
+ if (miniexp_car (p) != page->char_symbol)
+ delimit |= miniexp_car (p) == page->word_symbol ? 1 : 2;
+
+ miniexp_t deeper = miniexp_cddr (miniexp_cdddr (p));
+ while (deeper != miniexp_nil) {
+ miniexp_t str = miniexp_car (deeper);
+ if (miniexp_stringp (str)) {
+ if (!djvu_text_page_selection_process
+ (page, p, delimit))
+ return FALSE;
+ } else {
+ if (!djvu_text_page_selection
+ (page, str, delimit))
+ return FALSE;
+ }
+ delimit = 0;
+ deeper = miniexp_cdr (deeper);
+ }
+ return TRUE;
+}
+
+static void
+djvu_text_page_limits_process (DjvuTextPage *page,
+ miniexp_t p,
+ EvRectangle *rect)
+{
+ EvRectangle current;
+
+ current.x1 = miniexp_to_int (miniexp_nth (1, p));
+ current.y1 = miniexp_to_int (miniexp_nth (2, p));
+ current.x2 = miniexp_to_int (miniexp_nth (3, p));
+ current.y2 = miniexp_to_int (miniexp_nth (4, p));
+ if (current.x2 >= rect->x1 && current.y1 <= rect->y2 &&
+ current.x1 <= rect->x2 && current.y2 >= rect->y1) {
+ if (page->start == miniexp_nil)
+ page->start = p;
+ page->end = p;
+ }
+}
+
+
+static void
+djvu_text_page_limits (DjvuTextPage *page,
+ miniexp_t p,
+ EvRectangle *rect)
+{
+ g_return_if_fail (miniexp_consp (p) &&
+ miniexp_symbolp (miniexp_car (p)));
+
+ miniexp_t deeper = miniexp_cddr (miniexp_cdddr (p));
+ while (deeper != miniexp_nil) {
+ miniexp_t str = miniexp_car (deeper);
+ if (miniexp_stringp (str))
+ djvu_text_page_limits_process (page, p, rect);
+ else
+ djvu_text_page_limits (page, str, rect);
+
+ deeper = miniexp_cdr (deeper);
+ }
+}
+
+char *
+djvu_text_page_copy (DjvuTextPage *page,
+ EvRectangle *rectangle)
+{
+ char* text;
+
+ page->start = miniexp_nil;
+ page->end = miniexp_nil;
+ djvu_text_page_limits (page, page->text_structure, rectangle);
+ djvu_text_page_selection (page, page->text_structure, 0);
+
+ /* Do not free the string */
+ text = page->text;
+ page->text = NULL;
+
+ return text;
+}
+
+/**
+ * djvu_text_page_position:
+ * @page: #DjvuTextPage instance
+ * @position: index in the page text
+ *
+ * Returns the closest s-expression that contains the given position in
+ * the page text.
+ *
+ * Returns: closest s-expression
+ */
+static miniexp_t
+djvu_text_page_position (DjvuTextPage *page,
+ int position)
+{
+ GArray *links = page->links;
+ int low = 0;
+ int hi = links->len - 1;
+ int mid = 0;
+
+ g_return_val_if_fail (hi >= 0, miniexp_nil);
+
+ /* Shamelessly copied from GNU classpath */
+ while (low <= hi) {
+ mid = (low + hi) >> 1;
+ DjvuTextLink *link =
+ &g_array_index (links, DjvuTextLink, mid);
+ if (link->position == position)
+ break;
+ else if (link->position > position)
+ hi = --mid;
+ else
+ low = mid + 1;
+ }
+
+ return g_array_index (page->links, DjvuTextLink, mid).pair;
+}
+
+/**
+ * djvu_text_page_union:
+ * @target: first rectangle and result
+ * @source: second rectangle
+ *
+ * Calculates the bounding box of two rectangles and stores the reuslt
+ * in the first.
+ */
+static void
+djvu_text_page_union (EvRectangle *target,
+ EvRectangle *source)
+{
+ if (source->x1 < target->x1)
+ target->x1 = source->x1;
+ if (source->x2 > target->x2)
+ target->x2 = source->x2;
+ if (source->y1 < target->y1)
+ target->y1 = source->y1;
+ if (source->y2 > target->y2)
+ target->y2 = source->y2;
+}
+
+/**
+ * djvu_text_page_sexpr_process:
+ * @page: #DjvuTextPage instance
+ * @p: s-expression to append
+ * @start: first s-expression in the selection
+ * @end: last s-expression in the selection
+ *
+ * Appends the rectangle defined by @p to the internal bounding box rectangle.
+ *
+ * Returns: whether the end was not reached in this s-expression
+ */
+static gboolean
+djvu_text_page_sexpr_process (DjvuTextPage *page,
+ miniexp_t p,
+ miniexp_t start,
+ miniexp_t end)
+{
+ if (page->bounding_box || p == start) {
+ EvRectangle *new_rectangle = ev_rectangle_new ();
+ new_rectangle->x1 = miniexp_to_int (miniexp_nth (1, p));
+ new_rectangle->y1 = miniexp_to_int (miniexp_nth (2, p));
+ new_rectangle->x2 = miniexp_to_int (miniexp_nth (3, p));
+ new_rectangle->y2 = miniexp_to_int (miniexp_nth (4, p));
+ if (page->bounding_box) {
+ djvu_text_page_union (page->bounding_box,
+ new_rectangle);
+ g_free (new_rectangle);
+ } else
+ page->bounding_box = new_rectangle;
+ if (p == end)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * djvu_text_page_sexpr:
+ * @page: #DjvuTextPage instance
+ * @p: tree to append
+ * @start: first s-expression in the selection
+ * @end: last s-expression in the selection
+ *
+ * Walks the tree in @p and extends the rectangle with
+ * djvu_text_page_process() for all s-expressions between @start and @end.
+ *
+ * Returns: whether the end was not reached in this subtree
+ */
+static gboolean
+djvu_text_page_sexpr (DjvuTextPage *page,
+ miniexp_t p,
+ miniexp_t start,
+ miniexp_t end)
+{
+ g_return_val_if_fail (miniexp_consp (p) && miniexp_symbolp
+ (miniexp_car (p)), FALSE);
+
+ miniexp_t deeper = miniexp_cddr (miniexp_cdddr (p));
+ while (deeper != miniexp_nil) {
+ miniexp_t str = miniexp_car (deeper);
+ if (miniexp_stringp (str)) {
+ if (!djvu_text_page_sexpr_process
+ (page, p, start, end))
+ return FALSE;
+ } else {
+ if (!djvu_text_page_sexpr
+ (page, str, start, end))
+ return FALSE;
+ }
+ deeper = miniexp_cdr (deeper);
+ }
+ return TRUE;
+}
+
+/**
+ * djvu_text_page_box:
+ * @page: #DjvuTextPage instance
+ * @start: first s-expression in the selection
+ * @end: last s-expression in the selection
+ *
+ * Builds a rectangle that contains all s-expressions in the given range.
+ */
+static EvRectangle *
+djvu_text_page_box (DjvuTextPage *page,
+ miniexp_t start,
+ miniexp_t end)
+{
+ page->bounding_box = NULL;
+ djvu_text_page_sexpr (page, page->text_structure, start, end);
+ return page->bounding_box;
+}
+
+/**
+ * djvu_text_page_append_search:
+ * @page: #DjvuTextPage instance
+ * @p: tree to append
+ * @case_sensitive: do not ignore case
+ * @delimit: insert spaces because of higher (sentence/paragraph/...) break
+ *
+ * Appends the tree in @p to the internal text string.
+ */
+static void
+djvu_text_page_append_text (DjvuTextPage *page,
+ miniexp_t p,
+ gboolean case_sensitive,
+ gboolean delimit)
+{
+ char *token_text;
+
+ g_return_if_fail (miniexp_consp (p) &&
+ miniexp_symbolp (miniexp_car (p)));
+
+ delimit |= page->char_symbol != miniexp_car (p);
+
+ miniexp_t deeper = miniexp_cddr (miniexp_cdddr (p));
+ while (deeper != miniexp_nil) {
+ miniexp_t data = miniexp_car (deeper);
+ if (miniexp_stringp (data)) {
+ DjvuTextLink link;
+ link.position = page->text == NULL ? 0 :
+ strlen (page->text);
+ link.pair = p;
+ g_array_append_val (page->links, link);
+
+ token_text = (char *) miniexp_to_str (data);
+ if (!case_sensitive)
+ token_text = g_utf8_casefold (token_text, -1);
+ if (page->text == NULL)
+ page->text = g_strdup (token_text);
+ else {
+ char *new_text =
+ g_strjoin (delimit ? " " : NULL,
+ page->text, token_text,
+ NULL);
+ g_free (page->text);
+ page->text = new_text;
+ }
+ if (!case_sensitive)
+ g_free (token_text);
+ } else
+ djvu_text_page_append_text (page, data,
+ case_sensitive, delimit);
+ delimit = FALSE;
+ deeper = miniexp_cdr (deeper);
+ }
+}
+
+/**
+ * djvu_text_page_search:
+ * @page: #DjvuTextPage instance
+ * @text: text to search
+ *
+ * Searches the page for the given text. The results list has to be
+ * externally freed afterwards.
+ */
+void
+djvu_text_page_search (DjvuTextPage *page,
+ const char *text)
+{
+ char *haystack = page->text;
+ int search_len;
+ EvRectangle *result;
+ if (page->links->len == 0)
+ return;
+
+ search_len = strlen (text);
+ while ((haystack = strstr (haystack, text)) != NULL) {
+ int start_p = haystack - page->text;
+ miniexp_t start = djvu_text_page_position (page, start_p);
+ int end_p = start_p + search_len - 1;
+ miniexp_t end = djvu_text_page_position (page, end_p);
+ result = djvu_text_page_box (page, start, end);
+ g_assert (result);
+ page->results = g_list_prepend (page->results, result);
+ haystack = haystack + search_len;
+ }
+ page->results = g_list_reverse (page->results);
+}
+
+
+/**
+ * djvu_text_page_prepare_search:
+ * @page: #DjvuTextPage instance
+ * @case_sensitive: do not ignore case
+ *
+ * Indexes the page text and prepares the page for subsequent searches.
+ */
+void
+djvu_text_page_prepare_search (DjvuTextPage *page,
+ gboolean case_sensitive)
+{
+ djvu_text_page_append_text (page, page->text_structure,
+ case_sensitive, FALSE);
+}
+
+/**
+ * djvu_text_page_new:
+ * @text: S-expression of the page text
+ *
+ * Creates a new page to search.
+ *
+ * Returns: new #DjvuTextPage instance
+ */
+DjvuTextPage *
+djvu_text_page_new (miniexp_t text)
+{
+ DjvuTextPage *page;
+
+ page = g_new0 (DjvuTextPage, 1);
+ page->links = g_array_new (FALSE, FALSE, sizeof (DjvuTextLink));
+ page->char_symbol = miniexp_symbol ("char");
+ page->word_symbol = miniexp_symbol ("word");
+ page->text_structure = text;
+ return page;
+}
+
+/**
+ * djvu_text_page_free:
+ * @page: #DjvuTextPage instance
+ *
+ * Frees the given #DjvuTextPage instance.
+ */
+void
+djvu_text_page_free (DjvuTextPage *page)
+{
+ g_free (page->text);
+ g_array_free (page->links, TRUE);
+ g_free (page);
+}
diff --git a/backend/djvu/djvu-text-page.h b/backend/djvu/djvu-text-page.h
new file mode 100644
index 00000000..6e16f259
--- /dev/null
+++ b/backend/djvu/djvu-text-page.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2006 Michael Hofmann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DJVU_TEXT_PAGE_H__
+#define __DJVU_TEXT_PAGE_H__
+
+#include "ev-document.h"
+
+#include <string.h>
+#include <glib.h>
+#include <libdjvu/miniexp.h>
+
+
+typedef struct _DjvuTextPage DjvuTextPage;
+typedef struct _DjvuTextLink DjvuTextLink;
+
+struct _DjvuTextPage {
+ char *text;
+ GArray *links;
+ GList *results;
+ miniexp_t char_symbol;
+ miniexp_t word_symbol;
+ EvRectangle *bounding_box;
+ miniexp_t text_structure;
+ miniexp_t start;
+ miniexp_t end;
+};
+
+struct _DjvuTextLink {
+ int position;
+ miniexp_t pair;
+};
+
+char * djvu_text_page_copy (DjvuTextPage *page,
+ EvRectangle *rectangle);
+void djvu_text_page_prepare_search (DjvuTextPage *page,
+ gboolean case_sensitive);
+void djvu_text_page_search (DjvuTextPage *page,
+ const char *text);
+DjvuTextPage* djvu_text_page_new (miniexp_t text);
+void djvu_text_page_free (DjvuTextPage *page);
+
+#endif /* __DJVU_TEXT_PAGE_H__ */
+
diff --git a/backend/djvu/djvudocument.evince-backend.in b/backend/djvu/djvudocument.evince-backend.in
new file mode 100644
index 00000000..485af118
--- /dev/null
+++ b/backend/djvu/djvudocument.evince-backend.in
@@ -0,0 +1,4 @@
+[Evince Backend]
+Module=djvudocument
+_TypeDescription=DjVu Documents
+MimeType=image/vnd.djvu
diff --git a/backend/dvi/Makefile.am b/backend/dvi/Makefile.am
new file mode 100644
index 00000000..6dcd50ed
--- /dev/null
+++ b/backend/dvi/Makefile.am
@@ -0,0 +1,47 @@
+SUBDIRS = mdvi-lib
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ -I$(srcdir)/mdvi-lib \
+ -DMATELOCALEDIR=\"$(datadir)/locale\" \
+ -DEVINCE_COMPILATION \
+ $(WARN_CFLAGS) \
+ $(BACKEND_CFLAGS) \
+ $(SPECTRE_CFLAGS) \
+ $(DISABLE_DEPRECATED)
+
+backend_LTLIBRARIES = libdvidocument.la
+
+libdvidocument_la_SOURCES = \
+ dvi-document.c \
+ dvi-document.h \
+ cairo-device.c \
+ cairo-device.h \
+ texmfcnf.c \
+ texmfcnf.h \
+ fonts.c \
+ fonts.h
+
+libdvidocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS)
+libdvidocument_la_LIBADD = \
+ mdvi-lib/libmdvi.la \
+ -lkpathsea \
+ $(top_builddir)/libdocument/libevdocument.la \
+ $(BACKEND_LIBS) \
+ $(SPECTRE_LIBS)
+
+if WITH_TYPE1_FONTS
+libdvidocument_la_LIBADD += -lt1
+endif
+
+backend_in_files = dvidocument.evince-backend.in
+backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend)
+
+EXTRA_DIST = $(backend_in_files)
+
+CLEANFILES = $(backend_DATA)
+
+
+-include $(top_srcdir)/git.mk
diff --git a/backend/dvi/cairo-device.c b/backend/dvi/cairo-device.c
new file mode 100644
index 00000000..47425cad
--- /dev/null
+++ b/backend/dvi/cairo-device.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2007 Carlos Garcia Campos <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <gdk/gdk.h>
+#ifdef HAVE_SPECTRE
+#include <libspectre/spectre.h>
+#endif
+
+#include "cairo-device.h"
+
+typedef struct {
+ cairo_t *cr;
+
+ gint xmargin;
+ gint ymargin;
+
+ gdouble scale;
+
+ Ulong fg;
+ Ulong bg;
+
+} DviCairoDevice;
+
+static void
+dvi_cairo_draw_glyph (DviContext *dvi,
+ DviFontChar *ch,
+ int x0,
+ int y0)
+{
+ DviCairoDevice *cairo_device;
+ int x, y, w, h;
+ gboolean isbox;
+ DviGlyph *glyph;
+ cairo_surface_t *surface;
+
+ cairo_device = (DviCairoDevice *) dvi->device.device_data;
+
+ glyph = &ch->grey;
+
+ isbox = (glyph->data == NULL ||
+ (dvi->params.flags & MDVI_PARAM_CHARBOXES) ||
+ MDVI_GLYPH_ISEMPTY (glyph->data));
+
+ x = - glyph->x + x0 + cairo_device->xmargin;
+ y = - glyph->y + y0 + cairo_device->ymargin;
+ w = glyph->w;
+ h = glyph->h;
+
+ surface = cairo_get_target (cairo_device->cr);
+ if (x < 0 || y < 0
+ || x + w > cairo_image_surface_get_width (surface)
+ || y + h > cairo_image_surface_get_height (surface))
+ return;
+
+ cairo_save (cairo_device->cr);
+ if (isbox) {
+ cairo_rectangle (cairo_device->cr,
+ x - cairo_device->xmargin,
+ y - cairo_device->ymargin,
+ w, h);
+ cairo_stroke (cairo_device->cr);
+ } else {
+ cairo_translate (cairo_device->cr, x, y);
+ cairo_set_source_surface (cairo_device->cr,
+ (cairo_surface_t *) glyph->data,
+ 0, 0);
+ cairo_paint (cairo_device->cr);
+ }
+
+ cairo_restore (cairo_device->cr);
+}
+
+static void
+dvi_cairo_draw_rule (DviContext *dvi,
+ int x,
+ int y,
+ Uint width,
+ Uint height,
+ int fill)
+{
+ DviCairoDevice *cairo_device;
+ Ulong color;
+
+ cairo_device = (DviCairoDevice *) dvi->device.device_data;
+
+ color = cairo_device->fg;
+
+ cairo_save (cairo_device->cr);
+
+ cairo_set_line_width (cairo_device->cr,
+ cairo_get_line_width (cairo_device->cr) * cairo_device->scale);
+ cairo_set_source_rgb (cairo_device->cr,
+ ((color >> 16) & 0xff) / 255.,
+ ((color >> 8) & 0xff) / 255.,
+ ((color >> 0) & 0xff) / 255.);
+
+ cairo_rectangle (cairo_device->cr,
+ x + cairo_device->xmargin,
+ y + cairo_device->ymargin,
+ width, height);
+ if (fill == 0) {
+ cairo_stroke (cairo_device->cr);
+ } else {
+ cairo_fill (cairo_device->cr);
+ }
+
+ cairo_restore (cairo_device->cr);
+}
+
+#ifdef HAVE_SPECTRE
+static void
+dvi_cairo_draw_ps (DviContext *dvi,
+ const char *filename,
+ int x,
+ int y,
+ Uint width,
+ Uint height)
+{
+ DviCairoDevice *cairo_device;
+ unsigned char *data = NULL;
+ int row_length;
+ SpectreDocument *psdoc;
+ SpectreRenderContext *rc;
+ int w, h;
+ SpectreStatus status;
+ cairo_surface_t *image;
+
+ cairo_device = (DviCairoDevice *) dvi->device.device_data;
+
+ psdoc = spectre_document_new ();
+ spectre_document_load (psdoc, filename);
+ if (spectre_document_status (psdoc)) {
+ spectre_document_free (psdoc);
+ return;
+ }
+
+ spectre_document_get_page_size (psdoc, &w, &h);
+
+ rc = spectre_render_context_new ();
+ spectre_render_context_set_scale (rc,
+ (double)width / w,
+ (double)height / h);
+ spectre_document_render_full (psdoc, rc, &data, &row_length);
+ status = spectre_document_status (psdoc);
+
+ spectre_render_context_free (rc);
+ spectre_document_free (psdoc);
+
+ if (status) {
+ g_warning ("Error rendering PS document %s: %s\n",
+ filename, spectre_status_to_string (status));
+ free (data);
+
+ return;
+ }
+
+ image = cairo_image_surface_create_for_data ((unsigned char *)data,
+ CAIRO_FORMAT_RGB24,
+ width, height,
+ row_length);
+
+ cairo_save (cairo_device->cr);
+
+ cairo_translate (cairo_device->cr,
+ x + cairo_device->xmargin,
+ y + cairo_device->ymargin);
+ cairo_set_source_surface (cairo_device->cr, image, 0, 0);
+ cairo_paint (cairo_device->cr);
+
+ cairo_restore (cairo_device->cr);
+
+ cairo_surface_destroy (image);
+ free (data);
+}
+#endif /* HAVE_SPECTRE */
+
+static int
+dvi_cairo_alloc_colors (void *device_data,
+ Ulong *pixels,
+ int npixels,
+ Ulong fg,
+ Ulong bg,
+ double gamma,
+ int density)
+{
+ double frac;
+ GdkColor color, color_fg, color_bg;
+ int i, n;
+
+ color_bg.red = (bg >> 16) & 0xff;
+ color_bg.green = (bg >> 8) & 0xff;
+ color_bg.blue = (bg >> 0) & 0xff;
+
+ color_fg.red = (fg >> 16) & 0xff;
+ color_fg.green = (fg >> 8) & 0xff;
+ color_fg.blue = (fg >> 0) & 0xff;
+
+ n = npixels - 1;
+ for (i = 0; i < npixels; i++) {
+ frac = (gamma > 0) ?
+ pow ((double)i / n, 1 / gamma) :
+ 1 - pow ((double)(n - i) / n, -gamma);
+
+ color.red = frac * ((double)color_fg.red - color_bg.red) + color_bg.red;
+ color.green = frac * ((double)color_fg.green - color_bg.green) + color_bg.green;
+ color.blue = frac * ((double)color_fg.blue - color_bg.blue) + color_bg.blue;
+
+ pixels[i] = (color.red << 16) + (color.green << 8) + color.blue + 0xff000000;
+ }
+
+ return npixels;
+}
+
+static void *
+dvi_cairo_create_image (void *device_data,
+ Uint width,
+ Uint height,
+ Uint bpp)
+{
+ return cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+}
+
+static void
+dvi_cairo_free_image (void *ptr)
+{
+ cairo_surface_destroy ((cairo_surface_t *)ptr);
+}
+
+static void
+dvi_cairo_put_pixel (void *image, int x, int y, Ulong color)
+{
+ cairo_surface_t *surface;
+ gint rowstride;
+ guint32 *p;
+
+ surface = (cairo_surface_t *) image;
+
+ rowstride = cairo_image_surface_get_stride (surface);
+ p = (guint32*) (cairo_image_surface_get_data (surface) + y * rowstride + x * 4);
+
+ *p = color;
+}
+
+static void
+dvi_cairo_set_color (void *device_data, Ulong fg, Ulong bg)
+{
+ DviCairoDevice *cairo_device = (DviCairoDevice *) device_data;
+
+ cairo_device->fg = fg;
+ cairo_device->bg = bg;
+}
+
+/* Public methods */
+void
+mdvi_cairo_device_init (DviDevice *device)
+{
+ device->device_data = g_new0 (DviCairoDevice, 1);
+
+ device->draw_glyph = dvi_cairo_draw_glyph;
+ device->draw_rule = dvi_cairo_draw_rule;
+ device->alloc_colors = dvi_cairo_alloc_colors;
+ device->create_image = dvi_cairo_create_image;
+ device->free_image = dvi_cairo_free_image;
+ device->put_pixel = dvi_cairo_put_pixel;
+ device->set_color = dvi_cairo_set_color;
+#ifdef HAVE_SPECTRE
+ device->draw_ps = dvi_cairo_draw_ps;
+#else
+ device->draw_ps = NULL;
+#endif
+ device->refresh = NULL;
+}
+
+void
+mdvi_cairo_device_free (DviDevice *device)
+{
+ DviCairoDevice *cairo_device;
+
+ cairo_device = (DviCairoDevice *) device->device_data;
+
+ if (cairo_device->cr)
+ cairo_destroy (cairo_device->cr);
+
+ g_free (cairo_device);
+}
+
+cairo_surface_t *
+mdvi_cairo_device_get_surface (DviDevice *device)
+{
+ DviCairoDevice *cairo_device;
+
+ cairo_device = (DviCairoDevice *) device->device_data;
+
+ return cairo_surface_reference (cairo_get_target (cairo_device->cr));
+}
+
+void
+mdvi_cairo_device_render (DviContext* dvi)
+{
+ DviCairoDevice *cairo_device;
+ gint page_width;
+ gint page_height;
+ cairo_surface_t *surface;
+ guchar *pixels;
+ gint rowstride;
+ static const cairo_user_data_key_t key;
+
+ cairo_device = (DviCairoDevice *) dvi->device.device_data;
+
+ if (cairo_device->cr)
+ cairo_destroy (cairo_device->cr);
+
+ page_width = dvi->dvi_page_w * dvi->params.conv + 2 * cairo_device->xmargin;
+ page_height = dvi->dvi_page_h * dvi->params.vconv + 2 * cairo_device->ymargin;
+
+ rowstride = page_width * 4;
+ pixels = (guchar *) g_malloc (page_height * rowstride);
+ memset (pixels, 0xff, page_height * rowstride);
+
+ surface = cairo_image_surface_create_for_data (pixels,
+ CAIRO_FORMAT_RGB24,
+ page_width, page_height,
+ rowstride);
+ cairo_surface_set_user_data (surface, &key,
+ pixels, (cairo_destroy_func_t)g_free);
+
+ cairo_device->cr = cairo_create (surface);
+ cairo_surface_destroy (surface);
+
+ mdvi_dopage (dvi, dvi->currpage);
+}
+
+void
+mdvi_cairo_device_set_margins (DviDevice *device,
+ gint xmargin,
+ gint ymargin)
+{
+ DviCairoDevice *cairo_device;
+
+ cairo_device = (DviCairoDevice *) device->device_data;
+
+ cairo_device->xmargin = xmargin;
+ cairo_device->ymargin = ymargin;
+}
+
+void
+mdvi_cairo_device_set_scale (DviDevice *device,
+ gdouble scale)
+{
+ DviCairoDevice *cairo_device;
+
+ cairo_device = (DviCairoDevice *) device->device_data;
+
+ cairo_device->scale = scale;
+}
diff --git a/backend/dvi/cairo-device.h b/backend/dvi/cairo-device.h
new file mode 100644
index 00000000..bf76d2af
--- /dev/null
+++ b/backend/dvi/cairo-device.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 Carlos Garcia Campos <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MDVI_CAIRO_DEVICE
+#define MDVI_CAIRO_DEVICE
+
+#include <glib.h>
+#include <cairo.h>
+
+#include "mdvi.h"
+
+G_BEGIN_DECLS
+
+void mdvi_cairo_device_init (DviDevice *device);
+void mdvi_cairo_device_free (DviDevice *device);
+cairo_surface_t *mdvi_cairo_device_get_surface (DviDevice *device);
+void mdvi_cairo_device_render (DviContext* dvi);
+void mdvi_cairo_device_set_margins (DviDevice *device,
+ gint xmargin,
+ gint ymargin);
+void mdvi_cairo_device_set_scale (DviDevice *device,
+ gdouble scale);
+
+G_END_DECLS
+
+#endif /* MDVI_CAIRO_DEVICE */
diff --git a/backend/dvi/dvi-document.c b/backend/dvi/dvi-document.c
new file mode 100644
index 00000000..a4a3dc6d
--- /dev/null
+++ b/backend/dvi/dvi-document.c
@@ -0,0 +1,610 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005, Nickolay V. Shmyrev <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "dvi-document.h"
+#include "texmfcnf.h"
+#include "ev-document-thumbnails.h"
+#include "ev-document-misc.h"
+#include "ev-file-exporter.h"
+#include "ev-file-helpers.h"
+
+#include "mdvi.h"
+#include "fonts.h"
+#include "color.h"
+#include "cairo-device.h"
+
+#include <glib/gi18n-lib.h>
+#include <ctype.h>
+#ifdef G_OS_WIN32
+# define WIFEXITED(x) ((x) != 3)
+# define WEXITSTATUS(x) (x)
+#else
+# include <sys/wait.h>
+#endif
+#include <stdlib.h>
+
+GMutex *dvi_context_mutex = NULL;
+
+enum {
+ PROP_0,
+ PROP_TITLE
+};
+
+struct _DviDocumentClass
+{
+ EvDocumentClass parent_class;
+};
+
+struct _DviDocument
+{
+ EvDocument parent_instance;
+
+ DviContext *context;
+ DviPageSpec *spec;
+ DviParams *params;
+
+ /* To let document scale we should remember width and height */
+ double base_width;
+ double base_height;
+
+ gchar *uri;
+
+ /* PDF exporter */
+ gchar *exporter_filename;
+ GString *exporter_opts;
+};
+
+typedef struct _DviDocumentClass DviDocumentClass;
+
+static void dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
+static void dvi_document_file_exporter_iface_init (EvFileExporterInterface *iface);
+static void dvi_document_do_color_special (DviContext *dvi,
+ const char *prefix,
+ const char *arg);
+
+EV_BACKEND_REGISTER_WITH_CODE (DviDocument, dvi_document,
+ {
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, dvi_document_document_thumbnails_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, dvi_document_file_exporter_iface_init);
+ });
+
+static gboolean
+dvi_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ gchar *filename;
+ DviDocument *dvi_document = DVI_DOCUMENT(document);
+
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ return FALSE;
+
+ g_mutex_lock (dvi_context_mutex);
+ if (dvi_document->context)
+ mdvi_destroy_context (dvi_document->context);
+
+ dvi_document->context = mdvi_init_context(dvi_document->params, dvi_document->spec, filename);
+ g_mutex_unlock (dvi_context_mutex);
+ g_free (filename);
+
+ if (!dvi_document->context) {
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("DVI document has incorrect format"));
+ return FALSE;
+ }
+
+ mdvi_cairo_device_init (&dvi_document->context->device);
+
+
+ dvi_document->base_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv
+ + 2 * unit2pix(dvi_document->params->dpi, MDVI_HMARGIN) / dvi_document->params->hshrink;
+
+ dvi_document->base_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv
+ + 2 * unit2pix(dvi_document->params->vdpi, MDVI_VMARGIN) / dvi_document->params->vshrink;
+
+ g_free (dvi_document->uri);
+ dvi_document->uri = g_strdup (uri);
+
+ return TRUE;
+}
+
+
+static gboolean
+dvi_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT (document);
+
+ return ev_xfer_uri_simple (dvi_document->uri, uri, error);
+}
+
+static int
+dvi_document_get_n_pages (EvDocument *document)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT (document);
+
+ return dvi_document->context->npages;
+}
+
+static void
+dvi_document_get_page_size (EvDocument *document,
+ EvPage *page,
+ double *width,
+ double *height)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT (document);
+
+ *width = dvi_document->base_width;
+ *height = dvi_document->base_height;;
+}
+
+static cairo_surface_t *
+dvi_document_render (EvDocument *document,
+ EvRenderContext *rc)
+{
+ cairo_surface_t *surface;
+ cairo_surface_t *rotated_surface;
+ DviDocument *dvi_document = DVI_DOCUMENT(document);
+ gint required_width, required_height;
+ gint proposed_width, proposed_height;
+ gint xmargin = 0, ymargin = 0;
+
+ /* We should protect our context since it's not
+ * thread safe. The work to the future -
+ * let context render page independently
+ */
+ g_mutex_lock (dvi_context_mutex);
+
+ mdvi_setpage (dvi_document->context, rc->page->index);
+
+ mdvi_set_shrink (dvi_document->context,
+ (int)((dvi_document->params->hshrink - 1) / rc->scale) + 1,
+ (int)((dvi_document->params->vshrink - 1) / rc->scale) + 1);
+
+ required_width = dvi_document->base_width * rc->scale + 0.5;
+ required_height = dvi_document->base_height * rc->scale + 0.5;
+ proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv;
+ proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv;
+
+ if (required_width >= proposed_width)
+ xmargin = (required_width - proposed_width) / 2;
+ if (required_height >= proposed_height)
+ ymargin = (required_height - proposed_height) / 2;
+
+ mdvi_cairo_device_set_margins (&dvi_document->context->device, xmargin, ymargin);
+ mdvi_cairo_device_set_scale (&dvi_document->context->device, rc->scale);
+ mdvi_cairo_device_render (dvi_document->context);
+ surface = mdvi_cairo_device_get_surface (&dvi_document->context->device);
+
+ g_mutex_unlock (dvi_context_mutex);
+
+ rotated_surface = ev_document_misc_surface_rotate_and_scale (surface,
+ required_width,
+ required_height,
+ rc->rotation);
+ cairo_surface_destroy (surface);
+
+ return rotated_surface;
+}
+
+static void
+dvi_document_finalize (GObject *object)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT(object);
+
+ g_mutex_lock (dvi_context_mutex);
+ if (dvi_document->context) {
+ mdvi_cairo_device_free (&dvi_document->context->device);
+ mdvi_destroy_context (dvi_document->context);
+ }
+ g_mutex_unlock (dvi_context_mutex);
+
+ if (dvi_document->params)
+ g_free (dvi_document->params);
+
+ if (dvi_document->exporter_filename)
+ g_free (dvi_document->exporter_filename);
+
+ if (dvi_document->exporter_opts)
+ g_string_free (dvi_document->exporter_opts, TRUE);
+
+ g_free (dvi_document->uri);
+
+ G_OBJECT_CLASS (dvi_document_parent_class)->finalize (object);
+}
+
+static gboolean
+dvi_document_support_synctex (EvDocument *document)
+{
+ return TRUE;
+}
+
+static void
+dvi_document_class_init (DviDocumentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
+ gchar *texmfcnf;
+
+ gobject_class->finalize = dvi_document_finalize;
+
+ texmfcnf = get_texmfcnf();
+ mdvi_init_kpathsea ("evince", MDVI_MFMODE, MDVI_FALLBACK_FONT, MDVI_DPI, texmfcnf);
+ g_free(texmfcnf);
+
+ mdvi_register_special ("Color", "color", NULL, dvi_document_do_color_special, 1);
+ mdvi_register_fonts ();
+
+ dvi_context_mutex = g_mutex_new ();
+
+ ev_document_class->load = dvi_document_load;
+ ev_document_class->save = dvi_document_save;
+ ev_document_class->get_n_pages = dvi_document_get_n_pages;
+ ev_document_class->get_page_size = dvi_document_get_page_size;
+ ev_document_class->render = dvi_document_render;
+ ev_document_class->support_synctex = dvi_document_support_synctex;
+}
+
+static void
+dvi_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ EvRenderContext *rc,
+ gint *width,
+ gint *height)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT (document);
+ gdouble page_width = dvi_document->base_width;
+ gdouble page_height = dvi_document->base_height;
+
+ if (rc->rotation == 90 || rc->rotation == 270) {
+ *width = (gint) (page_height * rc->scale);
+ *height = (gint) (page_width * rc->scale);
+ } else {
+ *width = (gint) (page_width * rc->scale);
+ *height = (gint) (page_height * rc->scale);
+ }
+}
+
+static GdkPixbuf *
+dvi_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ EvRenderContext *rc,
+ gboolean border)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT (document);
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *rotated_pixbuf;
+ cairo_surface_t *surface;
+ gint thumb_width, thumb_height;
+ gint proposed_width, proposed_height;
+
+ thumb_width = (gint) (dvi_document->base_width * rc->scale);
+ thumb_height = (gint) (dvi_document->base_height * rc->scale);
+
+ g_mutex_lock (dvi_context_mutex);
+
+ mdvi_setpage (dvi_document->context, rc->page->index);
+
+ mdvi_set_shrink (dvi_document->context,
+ (int)dvi_document->base_width * dvi_document->params->hshrink / thumb_width,
+ (int)dvi_document->base_height * dvi_document->params->vshrink / thumb_height);
+
+ proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv;
+ proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv;
+
+ if (border) {
+ mdvi_cairo_device_set_margins (&dvi_document->context->device,
+ MAX (thumb_width - proposed_width, 0) / 2,
+ MAX (thumb_height - proposed_height, 0) / 2);
+ } else {
+ mdvi_cairo_device_set_margins (&dvi_document->context->device,
+ MAX (thumb_width - proposed_width - 2, 0) / 2,
+ MAX (thumb_height - proposed_height - 2, 0) / 2);
+ }
+
+ mdvi_cairo_device_set_scale (&dvi_document->context->device, rc->scale);
+ mdvi_cairo_device_render (dvi_document->context);
+ surface = mdvi_cairo_device_get_surface (&dvi_document->context->device);
+ g_mutex_unlock (dvi_context_mutex);
+
+ pixbuf = ev_document_misc_pixbuf_from_surface (surface);
+ cairo_surface_destroy (surface);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
+ g_object_unref (pixbuf);
+
+ if (border) {
+ GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
+
+ rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+ }
+
+ return rotated_pixbuf;
+}
+
+static void
+dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface)
+{
+ iface->get_thumbnail = dvi_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = dvi_document_thumbnails_get_dimensions;
+}
+
+/* EvFileExporterIface */
+static void
+dvi_document_file_exporter_begin (EvFileExporter *exporter,
+ EvFileExporterContext *fc)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT(exporter);
+
+ if (dvi_document->exporter_filename)
+ g_free (dvi_document->exporter_filename);
+ dvi_document->exporter_filename = g_strdup (fc->filename);
+
+ if (dvi_document->exporter_opts) {
+ g_string_free (dvi_document->exporter_opts, TRUE);
+ }
+ dvi_document->exporter_opts = g_string_new ("-s ");
+}
+
+static void
+dvi_document_file_exporter_do_page (EvFileExporter *exporter,
+ EvRenderContext *rc)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT(exporter);
+
+ g_string_append_printf (dvi_document->exporter_opts, "%d,", (rc->page->index) + 1);
+}
+
+static void
+dvi_document_file_exporter_end (EvFileExporter *exporter)
+{
+ gchar *command_line;
+ gint exit_stat;
+ GError *err = NULL;
+ gboolean success;
+
+ DviDocument *dvi_document = DVI_DOCUMENT(exporter);
+
+ command_line = g_strdup_printf ("dvipdfm %s -o %s \"%s\"", /* dvipdfm -s 1,2,.., -o exporter_filename dvi_filename */
+ dvi_document->exporter_opts->str,
+ dvi_document->exporter_filename,
+ dvi_document->context->filename);
+
+ success = g_spawn_command_line_sync (command_line,
+ NULL,
+ NULL,
+ &exit_stat,
+ &err);
+
+ g_free (command_line);
+
+ if (success == FALSE) {
+ g_warning ("Error: %s", err->message);
+ } else if (!WIFEXITED(exit_stat) || WEXITSTATUS(exit_stat) != EXIT_SUCCESS){
+ g_warning ("Error: dvipdfm does not end normally or exit with a failure status.");
+ }
+
+ if (err)
+ g_error_free (err);
+}
+
+static EvFileExporterCapabilities
+dvi_document_file_exporter_get_capabilities (EvFileExporter *exporter)
+{
+ return EV_FILE_EXPORTER_CAN_PAGE_SET |
+ EV_FILE_EXPORTER_CAN_COPIES |
+ EV_FILE_EXPORTER_CAN_COLLATE |
+ EV_FILE_EXPORTER_CAN_REVERSE |
+ EV_FILE_EXPORTER_CAN_GENERATE_PDF;
+}
+
+static void
+dvi_document_file_exporter_iface_init (EvFileExporterInterface *iface)
+{
+ iface->begin = dvi_document_file_exporter_begin;
+ iface->do_page = dvi_document_file_exporter_do_page;
+ iface->end = dvi_document_file_exporter_end;
+ iface->get_capabilities = dvi_document_file_exporter_get_capabilities;
+}
+
+#define RGB2ULONG(r,g,b) ((0xFF<<24)|(r<<16)|(g<<8)|(b))
+
+static gboolean
+hsb2rgb (float h, float s, float v, guchar *red, guchar *green, guchar *blue)
+{
+ float i, f, p, q, t, r, g, b;
+
+ if (h == 360)
+ h = 0;
+ else if ((h > 360) || (h < 0))
+ return FALSE;
+
+ s /= 100;
+ v /= 100;
+ h /= 60;
+ i = floor (h);
+ f = h - i;
+ p = v * (1 - s);
+ q = v * (1 - (s * f));
+ t = v * (1 - (s * (1 - f)));
+
+ if (i == 0) {
+ r = v;
+ g = t;
+ b = p;
+ } else if (i == 1) {
+ r = q;
+ g = v;
+ b = p;
+ } else if (i == 2) {
+ r = p;
+ g = v;
+ b = t;
+ } else if (i == 3) {
+ r = p;
+ g = q;
+ b = v;
+ } else if (i == 4) {
+ r = t;
+ g = p;
+ b = v;
+ } else if (i == 5) {
+ r = v;
+ g = p;
+ b = q;
+ }
+
+ *red = (guchar)floor(r * 255.0);
+ *green = (guchar)floor(g * 255.0);
+ *blue = (guchar)floor(b * 255.0);
+
+ return TRUE;
+}
+
+static void
+parse_color (const gchar *ptr,
+ gdouble *color,
+ gint n_color)
+{
+ gchar *p = (gchar *)ptr;
+ gint i;
+
+ for (i = 0; i < n_color; i++) {
+ while (isspace (*p)) p++;
+ color[i] = g_ascii_strtod (p, NULL);
+ while (!isspace (*p) && *p != '\0') p++;
+ if (*p == '\0')
+ break;
+ }
+}
+
+static void
+dvi_document_do_color_special (DviContext *dvi, const char *prefix, const char *arg)
+{
+ if (strncmp (arg, "pop", 3) == 0) {
+ mdvi_pop_color (dvi);
+ } else if (strncmp (arg, "push", 4) == 0) {
+ /* Find color source: Named, CMYK or RGB */
+ const char *tmp = arg + 4;
+
+ while (isspace (*tmp)) tmp++;
+
+ if (!strncmp ("rgb", tmp, 3)) {
+ gdouble rgb[3];
+ guchar red, green, blue;
+
+ parse_color (tmp + 4, rgb, 3);
+
+ red = 255 * rgb[0];
+ green = 255 * rgb[1];
+ blue = 255 * rgb[2];
+
+ mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
+ } else if (!strncmp ("hsb", tmp, 4)) {
+ gdouble hsb[3];
+ guchar red, green, blue;
+
+ parse_color (tmp + 4, hsb, 3);
+
+ if (hsb2rgb (hsb[0], hsb[1], hsb[2], &red, &green, &blue))
+ mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
+ } else if (!strncmp ("cmyk", tmp, 4)) {
+ gdouble cmyk[4];
+ double r, g, b;
+ guchar red, green, blue;
+
+ parse_color (tmp + 5, cmyk, 4);
+
+ r = 1.0 - cmyk[0] - cmyk[3];
+ if (r < 0.0)
+ r = 0.0;
+ g = 1.0 - cmyk[1] - cmyk[3];
+ if (g < 0.0)
+ g = 0.0;
+ b = 1.0 - cmyk[2] - cmyk[3];
+ if (b < 0.0)
+ b = 0.0;
+
+ red = r * 255 + 0.5;
+ green = g * 255 + 0.5;
+ blue = b * 255 + 0.5;
+
+ mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
+ } else if (!strncmp ("gray ", tmp, 5)) {
+ gdouble gray;
+ guchar rgb;
+
+ parse_color (tmp + 5, &gray, 1);
+
+ rgb = gray * 255 + 0.5;
+
+ mdvi_push_color (dvi, RGB2ULONG (rgb, rgb, rgb), 0xFFFFFFFF);
+ } else {
+ GdkColor color;
+
+ if (gdk_color_parse (tmp, &color)) {
+ guchar red, green, blue;
+
+ red = color.red * 255 / 65535.;
+ green = color.green * 255 / 65535.;
+ blue = color.blue * 255 / 65535.;
+
+ mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF);
+ }
+ }
+ }
+}
+
+static void
+dvi_document_init_params (DviDocument *dvi_document)
+{
+ dvi_document->params = g_new0 (DviParams, 1);
+
+ dvi_document->params->dpi = MDVI_DPI;
+ dvi_document->params->vdpi = MDVI_VDPI;
+ dvi_document->params->mag = MDVI_MAGNIFICATION;
+ dvi_document->params->density = MDVI_DEFAULT_DENSITY;
+ dvi_document->params->gamma = MDVI_DEFAULT_GAMMA;
+ dvi_document->params->flags = MDVI_PARAM_ANTIALIASED;
+ dvi_document->params->hdrift = 0;
+ dvi_document->params->vdrift = 0;
+ dvi_document->params->hshrink = MDVI_SHRINK_FROM_DPI(dvi_document->params->dpi);
+ dvi_document->params->vshrink = MDVI_SHRINK_FROM_DPI(dvi_document->params->vdpi);
+ dvi_document->params->orientation = MDVI_ORIENT_TBLR;
+
+ dvi_document->spec = NULL;
+
+ dvi_document->params->bg = 0xffffffff;
+ dvi_document->params->fg = 0xff000000;
+}
+
+static void
+dvi_document_init (DviDocument *dvi_document)
+{
+ dvi_document->context = NULL;
+ dvi_document_init_params (dvi_document);
+
+ dvi_document->exporter_filename = NULL;
+ dvi_document->exporter_opts = NULL;
+}
diff --git a/backend/dvi/dvi-document.h b/backend/dvi/dvi-document.h
new file mode 100644
index 00000000..845301b6
--- /dev/null
+++ b/backend/dvi/dvi-document.h
@@ -0,0 +1,38 @@
+/* dvi-document.h: Implementation of EvDocument for dvi documents
+ * Copyright (C) 2005, Nickolay V. Shmyrev <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DVI_DOCUMENT_H__
+#define __DVI_DOCUMENT_H__
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+#define DVI_TYPE_DOCUMENT (dvi_document_get_type ())
+#define DVI_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DVI_TYPE_DOCUMENT, DviDocument))
+#define DVI_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DVI_TYPE_DOCUMENT))
+
+typedef struct _DviDocument DviDocument;
+
+GType dvi_document_get_type (void) G_GNUC_CONST;
+
+G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __DVI_DOCUMENT_H__ */
diff --git a/backend/dvi/dvidocument.evince-backend.in b/backend/dvi/dvidocument.evince-backend.in
new file mode 100644
index 00000000..cb5fe6ed
--- /dev/null
+++ b/backend/dvi/dvidocument.evince-backend.in
@@ -0,0 +1,4 @@
+[Evince Backend]
+Module=dvidocument
+_TypeDescription=DVI Documents
+MimeType=application/x-dvi;application/x-bzdvi;application/x-gzdvi
diff --git a/backend/dvi/fonts.c b/backend/dvi/fonts.c
new file mode 100644
index 00000000..99be63ce
--- /dev/null
+++ b/backend/dvi/fonts.c
@@ -0,0 +1,57 @@
+#include "config.h"
+#include "fonts.h"
+#include "mdvi.h"
+
+static int registered = 0;
+
+extern DviFontInfo pk_font_info;
+extern DviFontInfo pkn_font_info;
+extern DviFontInfo gf_font_info;
+extern DviFontInfo vf_font_info;
+extern DviFontInfo ovf_font_info;
+#if 0
+extern DviFontInfo tt_font_info;
+#endif
+#ifdef WITH_TYPE1_FONTS
+extern DviFontInfo t1_font_info;
+#endif
+extern DviFontInfo afm_font_info;
+extern DviFontInfo tfm_font_info;
+extern DviFontInfo ofm_font_info;
+
+static struct fontinfo {
+ DviFontInfo *info;
+ char *desc;
+ int klass;
+} known_fonts[] = {
+ {&vf_font_info, "Virtual fonts", 0},
+ {&ovf_font_info, "Omega's virtual fonts", 0},
+#if 0
+ {&tt_font_info, "TrueType fonts", 0},
+#endif
+#ifdef WITH_TYPE1_FONTS
+ {&t1_font_info, "Type1 PostScript fonts", 0},
+#endif
+ {&pk_font_info, "Packed bitmap (auto-generated)", 1},
+ {&pkn_font_info, "Packed bitmap", -2},
+ {&gf_font_info, "Metafont's generic font format", 1},
+ {&ofm_font_info, "Omega font metrics", -1},
+ {&tfm_font_info, "TeX font metrics", -1},
+ {&afm_font_info, "Adobe font metrics", -1},
+ {0, 0}
+};
+
+void mdvi_register_fonts (void)
+{
+ struct fontinfo *type;
+
+ if (!registered) {
+ for(type = known_fonts; type->info; type++) {
+ mdvi_register_font_type(type->info, type->klass);
+ }
+ registered = 1;
+ }
+ return;
+}
+
+
diff --git a/backend/dvi/fonts.h b/backend/dvi/fonts.h
new file mode 100644
index 00000000..e55a8ddf
--- /dev/null
+++ b/backend/dvi/fonts.h
@@ -0,0 +1,6 @@
+#ifndef MDVI_FONTS_REGISTRATION_H
+#define MDVI_FONTS_REGISTRATION_H
+
+void mdvi_register_fonts (void);
+
+#endif /* MDVI_FONTS_REGISTRATION_H */
diff --git a/backend/dvi/mdvi-lib/Makefile.am b/backend/dvi/mdvi-lib/Makefile.am
new file mode 100644
index 00000000..e976aa60
--- /dev/null
+++ b/backend/dvi/mdvi-lib/Makefile.am
@@ -0,0 +1,43 @@
+INCLUDES = $(WARN_CFLAGS)
+noinst_LTLIBRARIES = libmdvi.la
+
+libmdvi_la_SOURCES = \
+ afmparse.c \
+ afmparse.h \
+ bitmap.c \
+ bitmap.h \
+ color.c \
+ color.h \
+ common.c \
+ common.h \
+ defaults.h \
+ dviopcodes.h \
+ dviread.c \
+ files.c \
+ font.c \
+ fontmap.c \
+ fontmap.h \
+ fontsrch.c \
+ gf.c \
+ hash.c \
+ hash.h \
+ list.c \
+ mdvi.h \
+ pagesel.c \
+ paper.c \
+ paper.h \
+ pk.c \
+ private.h \
+ setup.c \
+ special.c \
+ sp-epsf.c \
+ sysdeps.h \
+ t1.c \
+ tfm.c \
+ tfmfile.c \
+ tt.c \
+ util.c \
+ vf.c
+
+
+-include $(top_srcdir)/git.mk
diff --git a/backend/dvi/mdvi-lib/afmparse.c b/backend/dvi/mdvi-lib/afmparse.c
new file mode 100644
index 00000000..164366b0
--- /dev/null
+++ b/backend/dvi/mdvi-lib/afmparse.c
@@ -0,0 +1,1303 @@
+/*
+ * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved.
+ *
+ * This file may be freely copied and redistributed as long as:
+ * 1) This entire notice continues to be included in the file,
+ * 2) If the file has been modified in any way, a notice of such
+ * modification is conspicuously indicated.
+ *
+ * PostScript, Display PostScript, and Adobe are registered trademarks of
+ * Adobe Systems Incorporated.
+ *
+ * ************************************************************************
+ * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
+ * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
+ * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
+ * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
+ * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
+ * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * ************************************************************************
+ */
+
+/*
+ * modified for MDVI:
+ * - some names changed to avoid conflicts with T1lib
+ * - changed to ANSI C prototypes, as used by MDVI
+ * mal - 3/01
+ */
+
+/* parseAFM.c
+ *
+ * This file is used in conjuction with the parseAFM.h header file.
+ * This file contains several procedures that are used to parse AFM
+ * files. It is intended to work with an application program that needs
+ * font metric information. The program can be used as is by making a
+ * procedure call to "parseFile" (passing in the expected parameters)
+ * and having it fill in a data structure with the data from the
+ * AFM file, or an application developer may wish to customize this
+ * code.
+ *
+ * There is also a file, parseAFMclient.c, that is a sample application
+ * showing how to call the "parseFile" procedure and how to use the data
+ * after "parseFile" has returned.
+ *
+ * Please read the comments in parseAFM.h and parseAFMclient.c.
+ *
+ * History:
+ * original: DSM Thu Oct 20 17:39:59 PDT 1988
+ * modified: DSM Mon Jul 3 14:17:50 PDT 1989
+ * - added 'storageProblem' return code
+ * - fixed bug of not allocating extra byte for string duplication
+ * - fixed typos
+ * modified: DSM Tue Apr 3 11:18:34 PDT 1990
+ * - added free(ident) at end of parseFile routine
+ * modified: DSM Tue Jun 19 10:16:29 PDT 1990
+ * - changed (width == 250) to (width = 250) in initializeArray
+ */
+
+#include <config.h>
+#include "sysdeps.h"
+
+#ifdef WITH_AFM_FILES
+
+#include <stdlib.h> /* added for MDVI */
+#include <string.h> /* added for MDVI */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/file.h>
+#include <math.h>
+#include "afmparse.h"
+#undef VERSION
+
+#define lineterm EOL /* line terminating character */
+#define normalEOF 1 /* return code from parsing routines used only */
+ /* in this module */
+#define Space "space" /* used in string comparison to look for the width */
+ /* of the space character to init the widths array */
+#define False "false" /* used in string comparison to check the value of */
+ /* boolean keys (e.g. IsFixedPitch) */
+
+#define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0)
+
+
+
+/*************************** GLOBALS ***********************/
+
+static char *ident = NULL; /* storage buffer for keywords */
+
+
+/* "shorts" for fast case statement
+ * The values of each of these enumerated items correspond to an entry in the
+ * table of strings defined below. Therefore, if you add a new string as
+ * new keyword into the keyStrings table, you must also add a corresponding
+ * parseKey AND it MUST be in the same position!
+ *
+ * IMPORTANT: since the sorting algorithm is a binary search, the strings of
+ * keywords must be placed in lexicographical order, below. [Therefore, the
+ * enumerated items are not necessarily in lexicographical order, depending
+ * on the name chosen. BUT, they must be placed in the same position as the
+ * corresponding key string.] The NOPE shall remain in the last position,
+ * since it does not correspond to any key string, and it is used in the
+ * "recognize" procedure to calculate how many possible keys there are.
+ */
+
+enum parseKey {
+ ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT,
+ DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES,
+ ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
+ FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISFIXEDPITCH,
+ ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME,
+ NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES,
+ STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
+ STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION,
+ UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
+ NOPE } ;
+
+/* keywords for the system:
+ * This a table of all of the current strings that are vaild AFM keys.
+ * Each entry can be referenced by the appropriate parseKey value (an
+ * enumerated data type defined above). If you add a new keyword here,
+ * a corresponding parseKey MUST be added to the enumerated data type
+ * defined above, AND it MUST be added in the same position as the
+ * string is in this table.
+ *
+ * IMPORTANT: since the sorting algorithm is a binary search, the keywords
+ * must be placed in lexicographical order. And, NULL should remain at the
+ * end.
+ */
+
+static char *keyStrings[] = {
+ "Ascender", "B", "C", "CC", "CapHeight", "Comment",
+ "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites",
+ "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern",
+ "FamilyName", "FontBBox", "FontName", "FullName", "IsFixedPitch",
+ "ItalicAngle", "KP", "KPX", "L", "N",
+ "Notice", "PCC", "StartCharMetrics", "StartComposites",
+ "StartFontMetrics", "StartKernData", "StartKernPairs",
+ "StartTrackKern", "TrackKern", "UnderlinePosition",
+ "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
+ NULL };
+
+/*************************** PARSING ROUTINES **************/
+
+/*************************** token *************************/
+
+/* A "AFM File Conventions" tokenizer. That means that it will
+ * return the next token delimited by white space. See also
+ * the `linetoken' routine, which does a similar thing but
+ * reads all tokens until the next end-of-line.
+ */
+
+static char *token(FILE *stream)
+{
+ int ch, idx;
+
+ /* skip over white space */
+ while ((ch = fgetc(stream)) == ' ' || ch == lineterm ||
+ ch == ',' || ch == '\t' || ch == ';');
+
+ idx = 0;
+ while (ch != EOF && ch != ' ' && ch != lineterm
+ && ch != '\t' && ch != ':' && ch != ';')
+ {
+ ident[idx++] = ch;
+ ch = fgetc(stream);
+ } /* while */
+
+ if (ch == EOF && idx < 1) return ((char *)NULL);
+ if (idx >= 1 && ch != ':' ) ungetc(ch, stream);
+ if (idx < 1 ) ident[idx++] = ch; /* single-character token */
+ ident[idx] = 0;
+
+ return(ident); /* returns pointer to the token */
+
+} /* token */
+
+
+/*************************** linetoken *************************/
+
+/* "linetoken" will get read all tokens until the EOL character from
+ * the given stream. This is used to get any arguments that can be
+ * more than one word (like Comment lines and FullName).
+ */
+
+static char *linetoken(FILE *stream)
+{
+ int ch, idx;
+
+ while ((ch = fgetc(stream)) == ' ' || ch == '\t' );
+
+ idx = 0;
+ while (ch != EOF && ch != lineterm)
+ {
+ ident[idx++] = ch;
+ ch = fgetc(stream);
+ } /* while */
+
+ ungetc(ch, stream);
+ ident[idx] = 0;
+
+ return(ident); /* returns pointer to the token */
+
+} /* linetoken */
+
+
+/*************************** recognize *************************/
+
+/* This function tries to match a string to a known list of
+ * valid AFM entries (check the keyStrings array above).
+ * "ident" contains everything from white space through the
+ * next space, tab, or ":" character.
+ *
+ * The algorithm is a standard Knuth binary search.
+ */
+
+static enum parseKey recognize(char *ident)
+{
+ int lower = 0, upper = (int) NOPE, midpoint, cmpvalue;
+ BOOL found = FALSE;
+
+ while ((upper >= lower) && !found)
+ {
+ midpoint = (lower + upper)/2;
+ if (keyStrings[midpoint] == NULL) break;
+ cmpvalue = strncmp(ident, keyStrings[midpoint], MAX_NAME);
+ if (cmpvalue == 0) found = TRUE;
+ else if (cmpvalue < 0) upper = midpoint - 1;
+ else lower = midpoint + 1;
+ } /* while */
+
+ if (found) return (enum parseKey) midpoint;
+ else return NOPE;
+
+} /* recognize */
+
+
+/************************* parseGlobals *****************************/
+
+/* This function is called by "parseFile". It will parse the AFM File
+ * up to the "StartCharMetrics" keyword, which essentially marks the
+ * end of the Global Font Information and the beginning of the character
+ * metrics information.
+ *
+ * If the caller of "parseFile" specified that it wanted the Global
+ * Font Information (as defined by the "AFM File Specification"
+ * document), then that information will be stored in the returned
+ * data structure.
+ *
+ * Any Global Font Information entries that are not found in a
+ * given file, will have the usual default initialization value
+ * for its type (i.e. entries of type int will be 0, etc).
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static BOOL parseGlobals(FILE *fp, GlobalFontInfo *gfi)
+{
+ BOOL cont = TRUE, save = (gfi != NULL);
+ int error = ok;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp);
+
+ if (keyword == NULL)
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Global Font info section */
+ /* without saving any of the data */
+ switch (recognize(keyword))
+ {
+ case STARTCHARMETRICS:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire global font info section, */
+ /* saving the data */
+ switch(recognize(keyword))
+ {
+ case STARTFONTMETRICS:
+ keyword = token(fp);
+ gfi->afmVersion = (char *) malloc(strlen(keyword) + 1);
+ strcpy(gfi->afmVersion, keyword);
+ break;
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case FONTNAME:
+ keyword = token(fp);
+ gfi->fontName = (char *) malloc(strlen(keyword) + 1);
+ strcpy(gfi->fontName, keyword);
+ break;
+ case ENCODINGSCHEME:
+ keyword = token(fp);
+ gfi->encodingScheme = (char *)
+ malloc(strlen(keyword) + 1);
+ strcpy(gfi->encodingScheme, keyword);
+ break;
+ case FULLNAME:
+ keyword = linetoken(fp);
+ gfi->fullName = (char *) malloc(strlen(keyword) + 1);
+ strcpy(gfi->fullName, keyword);
+ break;
+ case FAMILYNAME:
+ keyword = linetoken(fp);
+ gfi->familyName = (char *) malloc(strlen(keyword) + 1);
+ strcpy(gfi->familyName, keyword);
+ break;
+ case WEIGHT:
+ keyword = token(fp);
+ gfi->weight = (char *) malloc(strlen(keyword) + 1);
+ strcpy(gfi->weight, keyword);
+ break;
+ case ITALICANGLE:
+ keyword = token(fp);
+ gfi->italicAngle = atof(keyword);
+ if (errno == ERANGE) error = parseError;
+ break;
+ case ISFIXEDPITCH:
+ keyword = token(fp);
+ if (MATCH(keyword, False))
+ gfi->isFixedPitch = 0;
+ else
+ gfi->isFixedPitch = 1;
+ break;
+ case UNDERLINEPOSITION:
+ keyword = token(fp);
+ gfi->underlinePosition = atoi(keyword);
+ break;
+ case UNDERLINETHICKNESS:
+ keyword = token(fp);
+ gfi->underlineThickness = atoi(keyword);
+ break;
+ case VERSION:
+ keyword = token(fp);
+ gfi->version = (char *) malloc(strlen(keyword) + 1);
+ strcpy(gfi->version, keyword);
+ break;
+ case NOTICE:
+ keyword = linetoken(fp);
+ gfi->notice = (char *) malloc(strlen(keyword) + 1);
+ strcpy(gfi->notice, keyword);
+ break;
+ case FONTBBOX:
+ keyword = token(fp);
+ gfi->fontBBox.llx = atoi(keyword);
+ keyword = token(fp);
+ gfi->fontBBox.lly = atoi(keyword);
+ keyword = token(fp);
+ gfi->fontBBox.urx = atoi(keyword);
+ keyword = token(fp);
+ gfi->fontBBox.ury = atoi(keyword);
+ break;
+ case CAPHEIGHT:
+ keyword = token(fp);
+ gfi->capHeight = atoi(keyword);
+ break;
+ case XHEIGHT:
+ keyword = token(fp);
+ gfi->xHeight = atoi(keyword);
+ break;
+ case DESCENDER:
+ keyword = token(fp);
+ gfi->descender = atoi(keyword);
+ break;
+ case ASCENDER:
+ keyword = token(fp);
+ gfi->ascender = atoi(keyword);
+ break;
+ case STARTCHARMETRICS:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ return(error);
+
+} /* parseGlobals */
+
+
+
+#if 0 /* this function does not seem to be used anywhere */
+/************************* initializeArray ************************/
+
+/* Unmapped character codes are (at Adobe Systems) assigned the
+ * width of the space character (if one exists) else they get the
+ * value of 250 ems. This function initializes all entries in the
+ * char widths array to have this value. Then any mapped character
+ * codes will be replaced with the width of the appropriate character
+ * when parsing the character metric section.
+
+ * This function parses the Character Metrics Section looking
+ * for a space character (by comparing character names). If found,
+ * the width of the space character will be used to initialize the
+ * values in the array of character widths.
+ *
+ * Before returning, the position of the read/write pointer of the
+ * file is reset to be where it was upon entering this function.
+ */
+
+static int initializeArray(FILE *fp, int *cwi)
+{
+ BOOL cont = TRUE, found = FALSE;
+ long opos = ftell(fp);
+ int code = 0, width = 0, i = 0, error = 0;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp);
+ if (keyword == NULL)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ switch(recognize(keyword))
+ {
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case CODE:
+ code = atoi(token(fp));
+ break;
+ case XWIDTH:
+ width = atoi(token(fp));
+ break;
+ case CHARNAME:
+ keyword = token(fp);
+ if (MATCH(keyword, Space))
+ {
+ cont = FALSE;
+ found = TRUE;
+ }
+ break;
+ case ENDCHARMETRICS:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if (!found)
+ width = 250;
+
+ for (i = 0; i < 256; ++i)
+ cwi[i] = width;
+
+ fseek(fp, opos, 0);
+
+ return(error);
+
+} /* initializeArray */
+#endif /* unused */
+
+/************************* parseCharWidths **************************/
+
+/* This function is called by "parseFile". It will parse the AFM File
+ * up to the "EndCharMetrics" keyword. It will save the character
+ * width info (as opposed to all of the character metric information)
+ * if requested by the caller of parseFile. Otherwise, it will just
+ * parse through the section without saving any information.
+ *
+ * If data is to be saved, parseCharWidths is passed in a pointer
+ * to an array of widths that has already been initialized by the
+ * standard value for unmapped character codes. This function parses
+ * the Character Metrics section only storing the width information
+ * for the encoded characters into the array using the character code
+ * as the index into that array.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseCharWidths(FILE *fp, int *cwi)
+{
+ BOOL cont = TRUE, save = (cwi != NULL);
+ int pos = 0, error = ok;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp);
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ if (keyword == NULL)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Char Metrics section without */
+ /* saving any of the data*/
+ switch (recognize(keyword))
+ {
+ case ENDCHARMETRICS:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire char metrics section, saving */
+ /* only the char x-width info */
+ switch(recognize(keyword))
+ {
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case CODE:
+ keyword = token(fp);
+ pos = atoi(keyword);
+ break;
+ case XYWIDTH:
+ /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
+ keyword = token(fp); keyword = token(fp); /* eat values */
+ error = parseError;
+ break;
+ case XWIDTH:
+ keyword = token(fp);
+ if (pos >= 0) /* ignore unmapped chars */
+ cwi[pos] = atoi(keyword);
+ break;
+ case ENDCHARMETRICS:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ case CHARNAME: /* eat values (so doesn't cause parseError) */
+ keyword = token(fp);
+ break;
+ case CHARBBOX:
+ keyword = token(fp); keyword = token(fp);
+ keyword = token(fp); keyword = token(fp);
+ break;
+ case LIGATURE:
+ keyword = token(fp); keyword = token(fp);
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ return(error);
+
+} /* parseCharWidths */
+
+
+/************************* parseCharMetrics ************************/
+
+/* This function is called by parseFile if the caller of parseFile
+ * requested that all character metric information be saved
+ * (as opposed to only the character width information).
+ *
+ * parseCharMetrics is passed in a pointer to an array of records
+ * to hold information on a per character basis. This function
+ * parses the Character Metrics section storing all character
+ * metric information for the ALL characters (mapped and unmapped)
+ * into the array.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseCharMetrics(FILE *fp, FontInfo *fi)
+{
+ BOOL cont = TRUE, firstTime = TRUE;
+ int error = ok, count = 0;
+ register CharMetricInfo *temp = fi->cmi;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp);
+ if (keyword == NULL)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ switch(recognize(keyword))
+ {
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case CODE:
+ if (count < fi->numOfChars)
+ {
+ if (firstTime) firstTime = FALSE;
+ else temp++;
+ temp->code = atoi(token(fp));
+ count++;
+ }
+ else
+ {
+ error = parseError;
+ cont = FALSE;
+ }
+ break;
+ case XYWIDTH:
+ temp->wx = atoi(token(fp));
+ temp->wy = atoi(token(fp));
+ break;
+ case XWIDTH:
+ temp->wx = atoi(token(fp));
+ break;
+ case CHARNAME:
+ keyword = token(fp);
+ temp->name = (char *) malloc(strlen(keyword) + 1);
+ strcpy(temp->name, keyword);
+ break;
+ case CHARBBOX:
+ temp->charBBox.llx = atoi(token(fp));
+ temp->charBBox.lly = atoi(token(fp));
+ temp->charBBox.urx = atoi(token(fp));
+ temp->charBBox.ury = atoi(token(fp));
+ break;
+ case LIGATURE: {
+ Ligature **tail = &(temp->ligs);
+ Ligature *node = *tail;
+
+ if (*tail != NULL)
+ {
+ while (node->next != NULL)
+ node = node->next;
+ tail = &(node->next);
+ }
+
+ *tail = (Ligature *) calloc(1, sizeof(Ligature));
+ keyword = token(fp);
+ (*tail)->succ = (char *) malloc(strlen(keyword) + 1);
+ strcpy((*tail)->succ, keyword);
+ keyword = token(fp);
+ (*tail)->lig = (char *) malloc(strlen(keyword) + 1);
+ strcpy((*tail)->lig, keyword);
+ break; }
+ case ENDCHARMETRICS:
+ cont = FALSE;;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if ((error == ok) && (count != fi->numOfChars))
+ error = parseError;
+
+ return(error);
+
+} /* parseCharMetrics */
+
+
+
+/************************* parseTrackKernData ***********************/
+
+/* This function is called by "parseFile". It will parse the AFM File
+ * up to the "EndTrackKern" or "EndKernData" keywords. It will save the
+ * track kerning data if requested by the caller of parseFile.
+ *
+ * parseTrackKernData is passed in a pointer to the FontInfo record.
+ * If data is to be saved, the FontInfo record will already contain
+ * a valid pointer to storage for the track kerning data.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseTrackKernData(FILE *fp, FontInfo *fi)
+{
+ BOOL cont = TRUE, save = (fi->tkd != NULL);
+ int pos = 0, error = ok, tcount = 0;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp);
+
+ if (keyword == NULL)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Track Kerning Data */
+ /* section without saving any of the data */
+ switch(recognize(keyword))
+ {
+ case ENDTRACKKERN:
+ case ENDKERNDATA:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire Track Kerning Data section, */
+ /* saving the data */
+ switch(recognize(keyword))
+ {
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case TRACKKERN:
+ if (tcount < fi->numOfTracks)
+ {
+ keyword = token(fp);
+ fi->tkd[pos].degree = atoi(keyword);
+ keyword = token(fp);
+ fi->tkd[pos].minPtSize = atof(keyword);
+ if (errno == ERANGE) error = parseError;
+ keyword = token(fp);
+ fi->tkd[pos].minKernAmt = atof(keyword);
+ if (errno == ERANGE) error = parseError;
+ keyword = token(fp);
+ fi->tkd[pos].maxPtSize = atof(keyword);
+ if (errno == ERANGE) error = parseError;
+ keyword = token(fp);
+ fi->tkd[pos++].maxKernAmt = atof(keyword);
+ if (errno == ERANGE) error = parseError;
+ tcount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = FALSE;
+ }
+ break;
+ case ENDTRACKKERN:
+ case ENDKERNDATA:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if (error == ok && tcount != fi->numOfTracks)
+ error = parseError;
+
+ return(error);
+
+} /* parseTrackKernData */
+
+
+/************************* parsePairKernData ************************/
+
+/* This function is called by "parseFile". It will parse the AFM File
+ * up to the "EndKernPairs" or "EndKernData" keywords. It will save
+ * the pair kerning data if requested by the caller of parseFile.
+ *
+ * parsePairKernData is passed in a pointer to the FontInfo record.
+ * If data is to be saved, the FontInfo record will already contain
+ * a valid pointer to storage for the pair kerning data.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parsePairKernData(FILE *fp, FontInfo *fi)
+{
+ BOOL cont = TRUE, save = (fi->pkd != NULL);
+ int pos = 0, error = ok, pcount = 0;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp);
+
+ if (keyword == NULL)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Pair Kerning Data */
+ /* section without saving any of the data */
+ switch(recognize(keyword))
+ {
+ case ENDKERNPAIRS:
+ case ENDKERNDATA:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire Pair Kerning Data section, */
+ /* saving the data */
+ switch(recognize(keyword))
+ {
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case KERNPAIR:
+ if (pcount < fi->numOfPairs)
+ {
+ keyword = token(fp);
+ fi->pkd[pos].name1 = (char *)
+ malloc(strlen(keyword) + 1);
+ strcpy(fi->pkd[pos].name1, keyword);
+ keyword = token(fp);
+ fi->pkd[pos].name2 = (char *)
+ malloc(strlen(keyword) + 1);
+ strcpy(fi->pkd[pos].name2, keyword);
+ keyword = token(fp);
+ fi->pkd[pos].xamt = atoi(keyword);
+ keyword = token(fp);
+ fi->pkd[pos++].yamt = atoi(keyword);
+ pcount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = FALSE;
+ }
+ break;
+ case KERNPAIRXAMT:
+ if (pcount < fi->numOfPairs)
+ {
+ keyword = token(fp);
+ fi->pkd[pos].name1 = (char *)
+ malloc(strlen(keyword) + 1);
+ strcpy(fi->pkd[pos].name1, keyword);
+ keyword = token(fp);
+ fi->pkd[pos].name2 = (char *)
+ malloc(strlen(keyword) + 1);
+ strcpy(fi->pkd[pos].name2, keyword);
+ keyword = token(fp);
+ fi->pkd[pos++].xamt = atoi(keyword);
+ pcount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = FALSE;
+ }
+ break;
+ case ENDKERNPAIRS:
+ case ENDKERNDATA:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if (error == ok && pcount != fi->numOfPairs)
+ error = parseError;
+
+ return(error);
+
+} /* parsePairKernData */
+
+
+/************************* parseCompCharData **************************/
+
+/* This function is called by "parseFile". It will parse the AFM File
+ * up to the "EndComposites" keyword. It will save the composite
+ * character data if requested by the caller of parseFile.
+ *
+ * parseCompCharData is passed in a pointer to the FontInfo record, and
+ * a boolean representing if the data should be saved.
+ *
+ * This function will create the appropriate amount of storage for
+ * the composite character data and store a pointer to the storage
+ * in the FontInfo record.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseCompCharData(FILE *fp, FontInfo *fi)
+{
+ BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
+ int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp);
+ if (keyword == NULL)
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (ccount > fi->numOfComps)
+ {
+ error = parseError;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Composite Character info */
+ /* section without saving any of the data */
+ switch(recognize(keyword))
+ {
+ case ENDCOMPOSITES:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire Composite Character info section, */
+ /* saving the data */
+ switch(recognize(keyword))
+ {
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case COMPCHAR:
+ if (ccount < fi->numOfComps)
+ {
+ keyword = token(fp);
+ if (pcount != fi->ccd[pos].numOfPieces)
+ error = parseError;
+ pcount = 0;
+ if (firstTime) firstTime = FALSE;
+ else pos++;
+ fi->ccd[pos].ccName = (char *)
+ malloc(strlen(keyword) + 1);
+ strcpy(fi->ccd[pos].ccName, keyword);
+ keyword = token(fp);
+ fi->ccd[pos].numOfPieces = atoi(keyword);
+ fi->ccd[pos].pieces = (Pcc *)
+ calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc));
+ j = 0;
+ ccount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = FALSE;
+ }
+ break;
+ case COMPCHARPIECE:
+ if (pcount < fi->ccd[pos].numOfPieces)
+ {
+ keyword = token(fp);
+ fi->ccd[pos].pieces[j].pccName = (char *)
+ malloc(strlen(keyword) + 1);
+ strcpy(fi->ccd[pos].pieces[j].pccName, keyword);
+ keyword = token(fp);
+ fi->ccd[pos].pieces[j].deltax = atoi(keyword);
+ keyword = token(fp);
+ fi->ccd[pos].pieces[j++].deltay = atoi(keyword);
+ pcount++;
+ }
+ else
+ error = parseError;
+ break;
+ case ENDCOMPOSITES:
+ cont = FALSE;
+ break;
+ case ENDFONTMETRICS:
+ cont = FALSE;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if (error == ok && ccount != fi->numOfComps)
+ error = parseError;
+
+ return(error);
+
+} /* parseCompCharData */
+
+
+
+
+/*************************** 'PUBLIC' FUNCTION ********************/
+
+
+/*************************** parseFile *****************************/
+
+/* parseFile is the only 'public' procedure available. It is called
+ * from an application wishing to get information from an AFM file.
+ * The caller of this function is responsible for locating and opening
+ * an AFM file and handling all errors associated with that task.
+ *
+ * parseFile expects 3 parameters: a vaild file pointer, a pointer
+ * to a (FontInfo *) variable (for which storage will be allocated and
+ * the data requested filled in), and a mask specifying which
+ * data from the AFM File should be saved in the FontInfo structure.
+ *
+ * The file will be parsed and the requested data will be stored in
+ * a record of type FontInfo (refer to ParseAFM.h).
+ *
+ * parseFile returns an error code as defined in parseAFM.h.
+ *
+ * The position of the read/write pointer associated with the file
+ * pointer upon return of this function is undefined.
+ */
+
+extern int afm_parse_file(FILE *fp, FontInfo **fi, FLAGS flags)
+{
+
+ int code = ok; /* return code from each of the parsing routines */
+ int error = ok; /* used as the return code from this function */
+
+ register char *keyword; /* used to store a token */
+
+
+ /* storage data for the global variable ident */
+ ident = (char *) calloc(MAX_NAME, sizeof(char));
+ if (ident == NULL) {error = storageProblem; return(error);}
+
+ (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo));
+ if ((*fi) == NULL) {error = storageProblem; return(error);}
+
+ if (flags & P_G)
+ {
+ (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo));
+ if ((*fi)->gfi == NULL) {error = storageProblem; return(error);}
+ }
+
+ /* The AFM File begins with Global Font Information. This section */
+ /* will be parsed whether or not information should be saved. */
+ code = parseGlobals(fp, (*fi)->gfi);
+
+ if (code < 0) error = code;
+
+ /* The Global Font Information is followed by the Character Metrics */
+ /* section. Which procedure is used to parse this section depends on */
+ /* how much information should be saved. If all of the metrics info */
+ /* is wanted, parseCharMetrics is called. If only the character widths */
+ /* is wanted, parseCharWidths is called. parseCharWidths will also */
+ /* be called in the case that no character data is to be saved, just */
+ /* to parse through the section. */
+
+ if ((code != normalEOF) && (code != earlyEOF))
+ {
+ (*fi)->numOfChars = atoi(token(fp));
+ if (flags & (P_M ^ P_W))
+ {
+ (*fi)->cmi = (CharMetricInfo *)
+ calloc((*fi)->numOfChars, sizeof(CharMetricInfo));
+ if ((*fi)->cmi == NULL) {error = storageProblem; return(error);}
+ code = parseCharMetrics(fp, *fi);
+ }
+ else
+ {
+ if (flags & P_W)
+ {
+ (*fi)->cwi = (int *) calloc(256, sizeof(int));
+ if ((*fi)->cwi == NULL)
+ {
+ error = storageProblem;
+ return(error);
+ }
+ }
+ /* parse section regardless */
+ code = parseCharWidths(fp, (*fi)->cwi);
+ } /* else */
+ } /* if */
+
+ if ((error != earlyEOF) && (code < 0)) error = code;
+
+ /* The remaining sections of the AFM are optional. This code will */
+ /* look at the next keyword in the file to determine what section */
+ /* is next, and then allocate the appropriate amount of storage */
+ /* for the data (if the data is to be saved) and call the */
+ /* appropriate parsing routine to parse the section. */
+
+ while ((code != normalEOF) && (code != earlyEOF))
+ {
+ keyword = token(fp);
+ if (keyword == NULL)
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ {
+ code = earlyEOF;
+ break; /* get out of loop */
+ }
+ switch(recognize(keyword))
+ {
+ case STARTKERNDATA:
+ break;
+ case ENDKERNDATA:
+ break;
+ case STARTTRACKKERN:
+ keyword = token(fp);
+ if (flags & P_T)
+ {
+ (*fi)->numOfTracks = atoi(keyword);
+ (*fi)->tkd = (TrackKernData *)
+ calloc((*fi)->numOfTracks, sizeof(TrackKernData));
+ if ((*fi)->tkd == NULL)
+ {
+ error = storageProblem;
+ return(error);
+ }
+ } /* if */
+ code = parseTrackKernData(fp, *fi);
+ break;
+ case STARTKERNPAIRS:
+ keyword = token(fp);
+ if (flags & P_P)
+ {
+ (*fi)->numOfPairs = atoi(keyword);
+ (*fi)->pkd = (PairKernData *)
+ calloc((*fi)->numOfPairs, sizeof(PairKernData));
+ if ((*fi)->pkd == NULL)
+ {
+ error = storageProblem;
+ return(error);
+ }
+ } /* if */
+ code = parsePairKernData(fp, *fi);
+ break;
+ case STARTCOMPOSITES:
+ keyword = token(fp);
+ if (flags & P_C)
+ {
+ (*fi)->numOfComps = atoi(keyword);
+ (*fi)->ccd = (CompCharData *)
+ calloc((*fi)->numOfComps, sizeof(CompCharData));
+ if ((*fi)->ccd == NULL)
+ {
+ error = storageProblem;
+ return(error);
+ }
+ } /* if */
+ code = parseCompCharData(fp, *fi);
+ break;
+ case ENDFONTMETRICS:
+ code = normalEOF;
+ break;
+ case NOPE:
+ default:
+ code = parseError;
+ break;
+ } /* switch */
+
+ if ((error != earlyEOF) && (code < 0)) error = code;
+
+ } /* while */
+
+ if ((error != earlyEOF) && (code < 0)) error = code;
+
+ if (ident != NULL) { free(ident); ident = NULL; }
+
+ return(error);
+
+} /* parseFile */
+
+/* added for MDVI: this function was copied from `parseAFMclient.c' */
+
+void afm_free_fontinfo(FontInfo *fi)
+{
+ if (fi != NULL)
+ {
+ if (fi->gfi != NULL)
+ {
+ free(fi->gfi->afmVersion); fi->gfi->afmVersion = NULL;
+ free(fi->gfi->fontName); fi->gfi->fontName = NULL;
+ free(fi->gfi->fullName); fi->gfi->fullName = NULL;
+ free(fi->gfi->familyName); fi->gfi->familyName = NULL;
+ free(fi->gfi->weight); fi->gfi->weight = NULL;
+ free(fi->gfi->version); fi->gfi->version = NULL;
+ free(fi->gfi->notice); fi->gfi->notice = NULL;
+ free(fi->gfi->encodingScheme); fi->gfi->encodingScheme = NULL;
+ free(fi->gfi); fi->gfi = NULL;
+ }
+
+ if (fi->cwi != NULL)
+ { free(fi->cwi); fi->cwi = NULL; }
+
+ if (fi->cmi != NULL)
+ {
+ int i = 0;
+ CharMetricInfo *temp = fi->cmi;
+ Ligature *node = temp->ligs;
+
+ for (i = 0; i < fi->numOfChars; ++i)
+ {
+ for (node = temp->ligs; node != NULL; node = node->next)
+ {
+ free(node->succ); node->succ = NULL;
+ free(node->lig); node->lig = NULL;
+ }
+
+ free(temp->name); temp->name = NULL;
+ temp++;
+ }
+
+ free(fi->cmi); fi->cmi = NULL;
+ }
+
+ if (fi->tkd != NULL)
+ { free(fi->tkd); fi->tkd = NULL; }
+
+ if (fi->pkd != NULL)
+ {
+ free(fi->pkd->name1); fi->pkd->name1 = NULL;
+ free(fi->pkd->name2); fi->pkd->name2 = NULL;
+ free(fi->pkd); fi->pkd = NULL;
+ }
+
+ if (fi->ccd != NULL)
+ {
+ int i = 0, j = 0;
+ CompCharData *ccd = fi->ccd;
+
+ for (i = 0; i < fi->numOfComps; ++i)
+ {
+ for (j = 0; j < ccd[i].numOfPieces; ++j)
+ {
+ free(ccd[i].pieces[j].pccName);
+ ccd[i].pieces[j].pccName = NULL;
+ }
+
+ free(ccd[i].ccName); ccd[i].ccName = NULL;
+ }
+
+ free(fi->ccd); fi->ccd = NULL;
+ }
+
+ free(fi);
+
+ } /* if */
+
+} /* afm_free_fontinfo */
+
+#endif /* WITH_AFM_FILES */
diff --git a/backend/dvi/mdvi-lib/afmparse.h b/backend/dvi/mdvi-lib/afmparse.h
new file mode 100644
index 00000000..6cce780c
--- /dev/null
+++ b/backend/dvi/mdvi-lib/afmparse.h
@@ -0,0 +1,307 @@
+/* modified for MDVI -- some names changed to avoid conflicts with T1lib */
+/*
+ * (C) 1988, 1989 by Adobe Systems Incorporated. All rights reserved.
+ *
+ * This file may be freely copied and redistributed as long as:
+ * 1) This entire notice continues to be included in the file,
+ * 2) If the file has been modified in any way, a notice of such
+ * modification is conspicuously indicated.
+ *
+ * PostScript, Display PostScript, and Adobe are registered trademarks of
+ * Adobe Systems Incorporated.
+ *
+ * ************************************************************************
+ * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
+ * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
+ * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
+ * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
+ * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
+ * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * ************************************************************************
+ */
+
+/* ParseAFM.h
+ *
+ * This header file is used in conjuction with the parseAFM.c file.
+ * Together these files provide the functionality to parse Adobe Font
+ * Metrics files and store the information in predefined data structures.
+ * It is intended to work with an application program that needs font metric
+ * information. The program can be used as is by making a procedure call to
+ * parse an AFM file and have the data stored, or an application developer
+ * may wish to customize the code.
+ *
+ * This header file defines the data structures used as well as the key
+ * strings that are currently recognized by this version of the AFM parser.
+ * This program is based on the document "Adobe Font Metrics Files,
+ * Specification Version 2.0".
+ *
+ * AFM files are separated into distinct sections of different data. Because
+ * of this, the parseAFM program can parse a specified file to only save
+ * certain sections of information based on the application's needs. A record
+ * containing the requested information will be returned to the application.
+ *
+ * AFM files are divided into five sections of data:
+ * 1) The Global Font Information
+ * 2) The Character Metrics Information
+ * 3) The Track Kerning Data
+ * 4) The Pair-Wise Kerning Data
+ * 5) The Composite Character Data
+ *
+ * Basically, the application can request any of these sections independent
+ * of what other sections are requested. In addition, in recognizing that
+ * many applications will want ONLY the x-width of characters and not all
+ * of the other character metrics information, there is a way to receive
+ * only the width information so as not to pay the storage cost for the
+ * unwanted data. An application should never request both the
+ * "quick and dirty" char metrics (widths only) and the Character Metrics
+ * Information since the Character Metrics Information will contain all
+ * of the character widths as well.
+ *
+ * There is a procedure in parseAFM.c, called parseFile, that can be
+ * called from any application wishing to get information from the AFM File.
+ * This procedure expects 3 parameters: a vaild file descriptor, a pointer
+ * to a (FontInfo *) variable (for which space will be allocated and then
+ * will be filled in with the data requested), and a mask specifying
+ * which data from the AFM File should be saved in the FontInfo structure.
+ *
+ * The flags that can be used to set the appropriate mask are defined below.
+ * In addition, several commonly used masks have already been defined.
+ *
+ * History:
+ * original: DSM Thu Oct 20 17:39:59 PDT 1988
+ * modified: DSM Mon Jul 3 14:17:50 PDT 1989
+ * - added 'storageProblem' return code
+ * - fixed typos
+ */
+#ifndef _MDVI_PARSEAFM_H
+#define _MDVI_PARSEAFM_H 1
+
+#include "sysdeps.h"
+
+#include <stdio.h>
+
+/* your basic constants */
+#define TRUE 1
+#define FALSE 0
+#define EOL '\n' /* end-of-line indicator */
+#define MAX_NAME 4096 /* max length for identifiers */
+#define BOOL int
+#define FLAGS int
+
+/* Flags that can be AND'ed together to specify exactly what
+ * information from the AFM file should be saved. */
+#define P_G 0x01 /* 0000 0001 */ /* Global Font Info */
+#define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */
+#define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */
+#define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */
+#define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */
+#define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */
+
+/* Commonly used flags */
+#define P_GW (P_G | P_W)
+#define P_GM (P_G | P_M)
+#define P_GMP (P_G | P_M | P_P)
+#define P_GMK (P_G | P_M | P_P | P_T)
+#define P_ALL (P_G | P_M | P_P | P_T | P_C)
+
+/* Possible return codes from the parseFile procedure.
+ *
+ * ok means there were no problems parsing the file.
+ *
+ * parseError means that there was some kind of parsing error, but the
+ * parser went on. This could include problems like the count for any given
+ * section does not add up to how many entries there actually were, or
+ * there was a key that was not recognized. The return record may contain
+ * vaild data or it may not.
+ *
+ * earlyEOF means that an End of File was encountered before expected. This
+ * may mean that the AFM file had been truncated, or improperly formed.
+ *
+ * storageProblem means that there were problems allocating storage for
+ * the data structures that would have contained the AFM data.
+ */
+#define ok 0
+#define parseError -1
+#define earlyEOF -2
+#define storageProblem -3
+
+/************************* TYPES *********************************/
+/* Below are all of the data structure definitions. These structures
+ * try to map as closely as possible to grouping and naming of data
+ * in the AFM Files.
+ */
+
+/* Bounding box definition. Used for the Font BBox as well as the
+ * Character BBox.
+ */
+typedef struct
+{
+ int llx; /* lower left x-position */
+ int lly; /* lower left y-position */
+ int urx; /* upper right x-position */
+ int ury; /* upper right y-position */
+} BBox;
+
+/* Global Font information.
+ * The key that each field is associated with is in comments. For an
+ * explanation about each key and its value please refer to the AFM
+ * documentation (full title & version given above).
+ */
+typedef struct
+{
+ char *afmVersion; /* key: StartFontMetrics */
+ char *fontName; /* key: FontName */
+ char *fullName; /* key: FullName */
+ char *familyName; /* key: FamilyName */
+ char *weight; /* key: Weight */
+ float italicAngle; /* key: ItalicAngle */
+ BOOL isFixedPitch; /* key: IsFixedPitch */
+ BBox fontBBox; /* key: FontBBox */
+ int underlinePosition; /* key: UnderlinePosition */
+ int underlineThickness; /* key: UnderlineThickness */
+ char *version; /* key: Version */
+ char *notice; /* key: Notice */
+ char *encodingScheme; /* key: EncodingScheme */
+ int capHeight; /* key: CapHeight */
+ int xHeight; /* key: XHeight */
+ int ascender; /* key: Ascender */
+ int descender; /* key: Descender */
+} GlobalFontInfo;
+
+/* Ligature definition is a linked list since any character can have
+ * any number of ligatures.
+ */
+typedef struct _t_ligature
+{
+ char *succ, *lig;
+ struct _t_ligature *next;
+} Ligature;
+
+/* Character Metric Information. This structure is used only if ALL
+ * character metric information is requested. If only the character
+ * widths is requested, then only an array of the character x-widths
+ * is returned.
+ *
+ * The key that each field is associated with is in comments. For an
+ * explanation about each key and its value please refer to the
+ * Character Metrics section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ int code, /* key: C */
+ wx, /* key: WX */
+ wy; /* together wx and wy are associated with key: W */
+ char *name; /* key: N */
+ BBox charBBox; /* key: B */
+ Ligature *ligs; /* key: L (linked list; not a fixed number of Ls */
+} CharMetricInfo;
+
+/* Track kerning data structure.
+ * The fields of this record are the five values associated with every
+ * TrackKern entry.
+ *
+ * For an explanation about each value please refer to the
+ * Track Kerning section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ int degree;
+ float minPtSize,
+ minKernAmt,
+ maxPtSize,
+ maxKernAmt;
+} TrackKernData;
+
+/* Pair Kerning data structure.
+ * The fields of this record are the four values associated with every
+ * KP entry. For KPX entries, the yamt will be zero.
+ *
+ * For an explanation about each value please refer to the
+ * Pair Kerning section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ char *name1;
+ char *name2;
+ int xamt,
+ yamt;
+} PairKernData;
+
+/* PCC is a piece of a composite character. This is a sub structure of a
+ * compCharData described below.
+ * These fields will be filled in with the values from the key PCC.
+ *
+ * For an explanation about each key and its value please refer to the
+ * Composite Character section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ char *pccName;
+ int deltax,
+ deltay;
+} Pcc;
+
+/* Composite Character Information data structure.
+ * The fields ccName and numOfPieces are filled with the values associated
+ * with the key CC. The field pieces points to an array (size = numOfPieces)
+ * of information about each of the parts of the composite character. That
+ * array is filled in with the values from the key PCC.
+ *
+ * For an explanation about each key and its value please refer to the
+ * Composite Character section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ char *ccName;
+ int numOfPieces;
+ Pcc *pieces;
+} CompCharData;
+
+/* FontInfo
+ * Record type containing pointers to all of the other data
+ * structures containing information about a font.
+ * A a record of this type is filled with data by the
+ * parseFile function.
+ */
+typedef struct
+{
+ GlobalFontInfo *gfi; /* ptr to a GlobalFontInfo record */
+ int *cwi; /* ptr to 256 element array of just char widths */
+ int numOfChars; /* number of entries in char metrics array */
+ CharMetricInfo *cmi; /* ptr to char metrics array */
+ int numOfTracks; /* number to entries in track kerning array */
+ TrackKernData *tkd; /* ptr to track kerning array */
+ int numOfPairs; /* number to entries in pair kerning array */
+ PairKernData *pkd; /* ptr to pair kerning array */
+ int numOfComps; /* number to entries in comp char array */
+ CompCharData *ccd; /* ptr to comp char array */
+} FontInfo;
+
+/************************* PROCEDURES ****************************/
+
+/* Call this procedure to do the grunt work of parsing an AFM file.
+ *
+ * "fp" should be a valid file pointer to an AFM file.
+ *
+ * "fi" is a pointer to a pointer to a FontInfo record sturcture
+ * (defined above). Storage for the FontInfo structure will be
+ * allocated in parseFile and the structure will be filled in
+ * with the requested data from the AFM File.
+ *
+ * "flags" is a mask with bits set representing what data should
+ * be saved. Defined above are valid flags that can be used to set
+ * the mask, as well as a few commonly used masks.
+ *
+ * The possible return codes from parseFile are defined above.
+ */
+
+extern int afm_parse_file __PROTO((FILE *, FontInfo **, FLAGS));
+extern void afm_free_fontinfo __PROTO((FontInfo *));
+
+#endif /* _MDVI_PARSEAFM_H */
diff --git a/backend/dvi/mdvi-lib/bitmap.c b/backend/dvi/mdvi-lib/bitmap.c
new file mode 100644
index 00000000..53f21207
--- /dev/null
+++ b/backend/dvi/mdvi-lib/bitmap.c
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* Bitmap manipulation routines */
+
+#include <config.h>
+#include <stdlib.h>
+
+#include "mdvi.h"
+#include "color.h"
+
+/* bit_masks[n] contains a BmUnit with `n' contiguous bits */
+
+static BmUnit bit_masks[] = {
+ 0x0, 0x1, 0x3, 0x7,
+ 0xf, 0x1f, 0x3f, 0x7f,
+ 0xff,
+#if BITMAP_BYTES > 1
+ 0x1ff, 0x3ff, 0x7ff,
+ 0xfff, 0x1fff, 0x3fff, 0x7fff,
+ 0xffff,
+#if BITMAP_BYTES > 2
+ 0x1ffff, 0x3ffff, 0x7ffff,
+ 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff,
+ 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff,
+ 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+ 0xffffffff
+#endif /* BITMAP_BYTES > 2 */
+#endif /* BITMAP_BYTES > 1 */
+};
+
+#ifndef NODEBUG
+#define SHOW_OP_DATA (DEBUGGING(BITMAP_OPS) && DEBUGGING(BITMAP_DATA))
+#endif
+
+/*
+ * Some useful macros to manipulate bitmap data
+ * SEGMENT(m,n) = bit mask for a segment of `m' contiguous bits
+ * starting at column `n'. These macros assume that
+ * m + n <= BITMAP_BITS, 0 <= m, n.
+ */
+#ifdef WORD_BIG_ENDIAN
+#define SEGMENT(m,n) (bit_masks[m] << (BITMAP_BITS - (m) - (n)))
+#else
+#define SEGMENT(m,n) (bit_masks[m] << (n))
+#endif
+
+/* sampling and shrinking routines shamelessly stolen from xdvi */
+
+static int sample_count[] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+};
+
+/* bit_swap[j] = j with all bits inverted (i.e. msb -> lsb) */
+static Uchar bit_swap[] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+
+/*
+ * next we have three bitmap functions to convert bitmaps in LSB bit order
+ * with 8, 16 and 32 bits per unit, to our internal format. The differences
+ * are minimal, but writing a generic function to handle all unit sizes is
+ * hopelessly slow.
+ */
+
+BITMAP *bitmap_convert_lsb8(Uchar *bits, int w, int h, int stride)
+{
+ BITMAP *bm;
+ int i;
+ Uchar *unit;
+ register Uchar *curr;
+ Uchar *end;
+ int bytes;
+
+ DEBUG((DBG_BITMAP_OPS, "convert LSB %dx%[email protected] -> bitmap\n", w, h));
+
+ bm = bitmap_alloc_raw(w, h);
+
+ /* this is the number of bytes in the original bitmap */
+ bytes = ROUND(w, 8);
+ unit = (Uchar *)bm->data;
+ end = unit + bm->stride;
+ curr = bits;
+ /* we try to do this as fast as we can */
+ for(i = 0; i < h; i++) {
+#ifdef WORD_LITTLE_ENDIAN
+ memcpy(unit, curr, bytes);
+ curr += stride;
+#else
+ int j;
+
+ for(j = 0; j < bytes; curr++, j++)
+ unit[j] = bit_swap[*curr];
+ cur += stride - bytes;
+#endif
+ memzero(unit + bytes, bm->stride - bytes);
+ unit += bm->stride;
+ }
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+ return bm;
+}
+
+BITMAP *bitmap_convert_msb8(Uchar *data, int w, int h, int stride)
+{
+ BITMAP *bm;
+ Uchar *unit;
+ Uchar *curr;
+ int i;
+ int bytes;
+
+ bm = bitmap_alloc(w, h);
+ bytes = ROUND(w, 8);
+ unit = (Uchar *)bm->data;
+ curr = data;
+ for(i = 0; i < h; i++) {
+#ifdef WORD_LITTLE_ENDIAN
+ int j;
+
+ for(j = 0; j < bytes; curr++, j++)
+ unit[j] = bit_swap[*curr];
+ curr += stride - bytes;
+#else
+ memcpy(unit, curr, bytes);
+ curr += stride;
+#endif
+ memzero(unit + bytes, bm->stride - bytes);
+ unit += bm->stride;
+ }
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+ return bm;
+}
+
+
+BITMAP *bitmap_copy(BITMAP *bm)
+{
+ BITMAP *nb = bitmap_alloc(bm->width, bm->height);
+
+ DEBUG((DBG_BITMAP_OPS, "copy %dx%d\n", bm->width, bm->height));
+ memcpy(nb->data, bm->data, bm->height * bm->stride);
+ return nb;
+}
+
+BITMAP *bitmap_alloc(int w, int h)
+{
+ BITMAP *bm;
+
+ bm = xalloc(BITMAP);
+ bm->width = w;
+ bm->height = h;
+ bm->stride = BM_BYTES_PER_LINE(bm);
+ if(h && bm->stride)
+ bm->data = (BmUnit *)mdvi_calloc(h, bm->stride);
+ else
+ bm->data = NULL;
+
+ return bm;
+}
+
+BITMAP *bitmap_alloc_raw(int w, int h)
+{
+ BITMAP *bm;
+
+ bm = xalloc(BITMAP);
+ bm->width = w;
+ bm->height = h;
+ bm->stride = BM_BYTES_PER_LINE(bm);
+ if(h && bm->stride)
+ bm->data = (BmUnit *)mdvi_malloc(h * bm->stride);
+ else
+ bm->data = NULL;
+
+ return bm;
+}
+
+void bitmap_destroy(BITMAP *bm)
+{
+ if(bm->data)
+ free(bm->data);
+ free(bm);
+}
+
+void bitmap_print(FILE *out, BITMAP *bm)
+{
+ int i, j;
+ BmUnit *a, mask;
+ static const char labels[] = {
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'
+ };
+ int sub;
+
+ a = bm->data;
+ fprintf(out, " ");
+ if(bm->width > 10) {
+ putchar('0');
+ sub = 0;
+ for(j = 2; j <= bm->width; j++)
+ if((j %10) == 0) {
+ if((j % 100) == 0) {
+ fprintf(out, "*");
+ sub += 100;
+ } else
+ fprintf(out, "%d", (j - sub)/10);
+ } else
+ putc(' ', out);
+ fprintf(out, "\n ");
+ }
+ for(j = 0; j < bm->width; j++)
+ putc(labels[j % 10], out);
+ putchar('\n');
+ for(i = 0; i < bm->height; i++) {
+ mask = FIRSTMASK;
+ a = (BmUnit *)((char *)bm->data + i * bm->stride);
+ fprintf(out, "%3d ", i+1);
+ for(j = 0; j < bm->width; j++) {
+ if(*a & mask)
+ putc('#', out);
+ else
+ putc('.', out);
+ if(mask == LASTMASK) {
+ a++;
+ mask = FIRSTMASK;
+ } else
+ NEXTMASK(mask);
+ }
+ putchar('\n');
+ }
+}
+
+void bitmap_set_col(BITMAP *bm, int row, int col, int count, int state)
+{
+ BmUnit *ptr;
+ BmUnit mask;
+
+ ptr = __bm_unit_ptr(bm, col, row);
+ mask = FIRSTMASKAT(col);
+
+ while(count-- > 0) {
+ if(state)
+ *ptr |= mask;
+ else
+ *ptr &= ~mask;
+ /* move to next row */
+ ptr = bm_offset(ptr, bm->stride);
+ }
+}
+
+/*
+ * to use this function you should first make sure that
+ * there is room for `count' bits in the scanline
+ *
+ * A general-purpose (but not very efficient) function to paint `n' pixels
+ * on a bitmap, starting at position (x, y) would be:
+ *
+ * bitmap_paint_bits(__bm_unit_ptr(bitmap, x, y), x % BITMAP_BITS, n)
+ *
+ */
+void bitmap_paint_bits(BmUnit *ptr, int n, int count)
+{
+ /* paint the head */
+ if(n + count > BITMAP_BITS) {
+ *ptr |= SEGMENT(BITMAP_BITS - n, n);
+ count -= BITMAP_BITS - n;
+ ptr++;
+ } else {
+ *ptr |= SEGMENT(count, n);
+ return;
+ }
+
+ /* paint the middle */
+ for(; count >= BITMAP_BITS; count -= BITMAP_BITS)
+ *ptr++ = bit_masks[BITMAP_BITS];
+
+ /* paint the tail */
+ if(count > 0)
+ *ptr |= SEGMENT(count, 0);
+}
+
+/*
+ * same as paint_bits but clears pixels instead of painting them. Written
+ * as a separate function for efficiency reasons.
+ */
+void bitmap_clear_bits(BmUnit *ptr, int n, int count)
+{
+ if(n + count > BITMAP_BITS) {
+ *ptr &= ~SEGMENT(BITMAP_BITS - n, n);
+ count -= BITMAP_BITS;
+ ptr++;
+ } else {
+ *ptr &= ~SEGMENT(count, n);
+ return;
+ }
+
+ for(; count >= BITMAP_BITS; count -= BITMAP_BITS)
+ *ptr++ = 0;
+
+ if(count > 0)
+ *ptr &= ~SEGMENT(count, 0);
+}
+
+/* the general function to paint rows. Still used by the PK reader, but that
+ * will change soon (The GF reader already uses bitmap_paint_bits()).
+ */
+void bitmap_set_row(BITMAP *bm, int row, int col, int count, int state)
+{
+ BmUnit *ptr;
+
+ ptr = __bm_unit_ptr(bm, col, row);
+ if(state)
+ bitmap_paint_bits(ptr, col & (BITMAP_BITS-1), count);
+ else
+ bitmap_clear_bits(ptr, col & (BITMAP_BITS-1), count);
+}
+
+/*
+ * Now several `flipping' operations
+ */
+
+void bitmap_flip_horizontally(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->width;
+ nb.height = bm->height;
+ nb.stride = bm->stride;
+ nb.data = mdvi_calloc(bm->height, bm->stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, nb.width-1, 0);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fline = fptr;
+ tline = tptr;
+ fmask = FIRSTMASK;
+ tmask = FIRSTMASKAT(nb.width-1);
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ if(tmask == FIRSTMASK) {
+ tmask = LASTMASK;
+ tline--;
+ } else
+ PREVMASK(tmask);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ tptr = bm_offset(tptr, bm->stride);
+ }
+ DEBUG((DBG_BITMAP_OPS, "flip_horizontally (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ mdvi_free(bm->data);
+ bm->data = nb.data;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_flip_vertically(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask;
+ int w, h;
+
+ nb.width = bm->width;
+ nb.height = bm->height;
+ nb.stride = bm->stride;
+ nb.data = mdvi_calloc(bm->height, bm->stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, 0, nb.height-1);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fline = fptr;
+ tline = tptr;
+ fmask = FIRSTMASK;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= fmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ tline++;
+ } else
+ NEXTMASK(fmask);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ tptr = (BmUnit *)((char *)tptr - bm->stride);
+ }
+ DEBUG((DBG_BITMAP_OPS, "flip_vertically (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ mdvi_free(bm->data);
+ bm->data = nb.data;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_flip_diagonally(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->width;
+ nb.height = bm->height;
+ nb.stride = bm->stride;
+ nb.data = mdvi_calloc(bm->height, bm->stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, nb.width-1, nb.height-1);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fline = fptr;
+ tline = tptr;
+ fmask = FIRSTMASK;
+ tmask = FIRSTMASKAT(nb.width-1);
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ if(tmask == FIRSTMASK) {
+ tmask = LASTMASK;
+ tline--;
+ } else
+ PREVMASK(tmask);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ tptr = bm_offset(tptr, -nb.stride);
+ }
+ DEBUG((DBG_BITMAP_OPS, "flip_diagonally (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ mdvi_free(bm->data);
+ bm->data = nb.data;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_rotate_clockwise(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->height;
+ nb.height = bm->width;
+ nb.stride = BM_BYTES_PER_LINE(&nb);
+ nb.data = mdvi_calloc(nb.height, nb.stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, nb.width - 1, 0);
+
+ tmask = FIRSTMASKAT(nb.width-1);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fmask = FIRSTMASK;
+ fline = fptr;
+ tline = tptr;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ /* go to next row */
+ tline = bm_offset(tline, nb.stride);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ if(tmask == FIRSTMASK) {
+ tmask = LASTMASK;
+ tptr--;
+ } else
+ PREVMASK(tmask);
+ }
+
+ DEBUG((DBG_BITMAP_OPS, "rotate_clockwise (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ mdvi_free(bm->data);
+ bm->data = nb.data;
+ bm->width = nb.width;
+ bm->height = nb.height;
+ bm->stride = nb.stride;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_rotate_counter_clockwise(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->height;
+ nb.height = bm->width;
+ nb.stride = BM_BYTES_PER_LINE(&nb);
+ nb.data = mdvi_calloc(nb.height, nb.stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, 0, nb.height - 1);
+
+ tmask = FIRSTMASK;
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fmask = FIRSTMASK;
+ fline = fptr;
+ tline = tptr;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ /* go to previous row */
+ tline = bm_offset(tline, -nb.stride);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ if(tmask == LASTMASK) {
+ tmask = FIRSTMASK;
+ tptr++;
+ } else
+ NEXTMASK(tmask);
+ }
+
+ DEBUG((DBG_BITMAP_OPS, "rotate_counter_clockwise (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ mdvi_free(bm->data);
+ bm->data = nb.data;
+ bm->width = nb.width;
+ bm->height = nb.height;
+ bm->stride = nb.stride;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_flip_rotate_clockwise(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->height;
+ nb.height = bm->width;
+ nb.stride = BM_BYTES_PER_LINE(&nb);
+ nb.data = mdvi_calloc(nb.height, nb.stride);
+
+ fptr = bm->data;
+ tptr = __bm_unit_ptr(&nb, nb.width-1, nb.height-1);
+
+ tmask = FIRSTMASKAT(nb.width-1);
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fmask = FIRSTMASK;
+ fline = fptr;
+ tline = tptr;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ /* go to previous line */
+ tline = bm_offset(tline, -nb.stride);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ if(tmask == FIRSTMASK) {
+ tmask = LASTMASK;
+ tptr--;
+ } else
+ PREVMASK(tmask);
+ }
+ DEBUG((DBG_BITMAP_OPS, "flip_rotate_clockwise (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ mdvi_free(bm->data);
+ bm->data = nb.data;
+ bm->width = nb.width;
+ bm->height = nb.height;
+ bm->stride = nb.stride;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+void bitmap_flip_rotate_counter_clockwise(BITMAP *bm)
+{
+ BITMAP nb;
+ BmUnit *fptr, *tptr;
+ BmUnit fmask, tmask;
+ int w, h;
+
+ nb.width = bm->height;
+ nb.height = bm->width;
+ nb.stride = BM_BYTES_PER_LINE(&nb);
+ nb.data = mdvi_calloc(nb.height, nb.stride);
+
+ fptr = bm->data;
+ tptr = nb.data;
+ tmask = FIRSTMASK;
+
+ for(h = 0; h < bm->height; h++) {
+ BmUnit *fline, *tline;
+
+ fmask = FIRSTMASK;
+ fline = fptr;
+ tline = tptr;
+ for(w = 0; w < bm->width; w++) {
+ if(*fline & fmask)
+ *tline |= tmask;
+ if(fmask == LASTMASK) {
+ fmask = FIRSTMASK;
+ fline++;
+ } else
+ NEXTMASK(fmask);
+ /* go to next line */
+ tline = bm_offset(tline, nb.stride);
+ }
+ fptr = bm_offset(fptr, bm->stride);
+ if(tmask == LASTMASK) {
+ tmask = FIRSTMASK;
+ tptr++;
+ } else
+ NEXTMASK(tmask);
+ }
+
+ DEBUG((DBG_BITMAP_OPS, "flip_rotate_counter_clockwise (%d,%d) -> (%d,%d)\n",
+ bm->width, bm->height, nb.width, nb.height));
+ mdvi_free(bm->data);
+ bm->data = nb.data;
+ bm->width = nb.width;
+ bm->height = nb.height;
+ bm->stride = nb.stride;
+ if(SHOW_OP_DATA)
+ bitmap_print(stderr, bm);
+}
+
+#if 0
+void bitmap_transform(BITMAP *map, DviOrientation orient)
+{
+ switch(orient) {
+ case MDVI_ORIENT_TBLR:
+ break;
+ case MDVI_ORIENT_TBRL:
+ bitmap_flip_horizontally(map);
+ break;
+ case MDVI_ORIENT_BTLR:
+ bitmap_flip_vertically(map);
+ break;
+ case MDVI_ORIENT_BTRL:
+ bitmap_flip_diagonally(map);
+ break;
+ case MDVI_ORIENT_RP90:
+ bitmap_rotate_counter_clockwise(map);
+ break;
+ case MDVI_ORIENT_RM90:
+ bitmap_rotate_clockwise(map);
+ break;
+ case MDVI_ORIENT_IRP90:
+ bitmap_flip_rotate_counter_clockwise(map);
+ break;
+ case MDVI_ORIENT_IRM90:
+ bitmap_flip_rotate_clockwise(map);
+ break;
+ }
+}
+#endif
+
+/*
+ * Count the number of non-zero bits in a box of dimensions w x h, starting
+ * at column `step' in row `data'.
+ *
+ * Shamelessly stolen from xdvi.
+ */
+static int do_sample(BmUnit *data, int stride, int step, int w, int h)
+{
+ BmUnit *ptr, *end, *cp;
+ int shift, n;
+ int bits_left;
+ int wid;
+
+ ptr = data + step / BITMAP_BITS;
+ end = bm_offset(data, h * stride);
+ shift = FIRSTSHIFTAT(step);
+ bits_left = w;
+ n = 0;
+ while(bits_left) {
+#ifndef WORD_BIG_ENDIAN
+ wid = BITMAP_BITS - shift;
+#else
+ wid = shift;
+#endif
+ if(wid > bits_left)
+ wid = bits_left;
+ if(wid > 8)
+ wid = 8;
+#ifdef WORD_BIG_ENDIAN
+ shift -= wid;
+#endif
+ for(cp = ptr; cp < end; cp = bm_offset(cp, stride))
+ n += sample_count[(*cp >> shift) & bit_masks[wid]];
+#ifndef WORD_BIG_ENDIAN
+ shift += wid;
+#endif
+#ifdef WORD_BIG_ENDIAN
+ if(shift == 0) {
+ shift = BITMAP_BITS;
+ ptr++;
+ }
+#else
+ if(shift == BITMAP_BITS) {
+ shift = 0;
+ ptr++;
+ }
+#endif
+ bits_left -= wid;
+ }
+ return n;
+}
+
+void mdvi_shrink_box(DviContext *dvi, DviFont *font,
+ DviFontChar *pk, DviGlyph *dest)
+{
+ int x, y, z;
+ DviGlyph *glyph;
+ int hs, vs;
+
+ hs = dvi->params.hshrink;
+ vs = dvi->params.vshrink;
+ glyph = &pk->glyph;
+
+ x = (int)glyph->x / hs;
+ if((int)glyph->x - x * hs > 0)
+ x++;
+ dest->w = x + ROUND((int)glyph->w - glyph->x, hs);
+
+ z = (int)glyph->y + 1;
+ y = z / vs;
+ if(z - y * vs <= 0)
+ y--;
+ dest->h = y + ROUND((int)glyph->h - z, vs) + 1;
+ dest->x = x;
+ dest->y = glyph->y / vs;
+ dest->data = MDVI_GLYPH_EMPTY;
+ DEBUG((DBG_BITMAPS, "shrink_box: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
+ glyph->w, glyph->h, glyph->x, glyph->y,
+ dest->w, dest->h, dest->x, dest->y));
+}
+
+void mdvi_shrink_glyph(DviContext *dvi, DviFont *font,
+ DviFontChar *pk, DviGlyph *dest)
+{
+ int rows_left, rows, init_cols;
+ int cols_left, cols;
+ BmUnit *old_ptr, *new_ptr;
+ BITMAP *oldmap, *newmap;
+ BmUnit m, *cp;
+ DviGlyph *glyph;
+ int sample, min_sample;
+ int old_stride;
+ int new_stride;
+ int x, y;
+ int w, h;
+ int hs, vs;
+
+ hs = dvi->params.hshrink;
+ vs = dvi->params.vshrink;
+
+ min_sample = vs * hs * dvi->params.density / 100;
+
+ glyph = &pk->glyph;
+ oldmap = (BITMAP *)glyph->data;
+
+ x = (int)glyph->x / hs;
+ init_cols = (int)glyph->x - x * hs;
+ if(init_cols <= 0)
+ init_cols += hs;
+ else
+ x++;
+ w = x + ROUND((int)glyph->w - glyph->x, hs);
+
+ cols = (int)glyph->y + 1;
+ y = cols / vs;
+ rows = cols - y * vs;
+ if(rows <= 0) {
+ rows += vs;
+ y--;
+ }
+ h = y + ROUND((int)glyph->h - cols, vs) + 1;
+
+ /* create the new glyph */
+ newmap = bitmap_alloc(w, h);
+ dest->data = newmap;
+ dest->x = x;
+ dest->y = glyph->y / vs;
+ dest->w = w;
+ dest->h = h;
+
+ old_ptr = oldmap->data;
+ old_stride = oldmap->stride;
+ new_ptr = newmap->data;
+ new_stride = newmap->stride;
+ rows_left = glyph->h;
+
+ while(rows_left) {
+ if(rows > rows_left)
+ rows = rows_left;
+ cols_left = glyph->w;
+ m = FIRSTMASK;
+ cp = new_ptr;
+ cols = init_cols;
+ while(cols_left > 0) {
+ if(cols > cols_left)
+ cols = cols_left;
+ sample = do_sample(old_ptr, old_stride,
+ glyph->w - cols_left, cols, rows);
+ if(sample >= min_sample)
+ *cp |= m;
+ if(m == LASTMASK) {
+ m = FIRSTMASK;
+ cp++;
+ } else
+ NEXTMASK(m);
+ cols_left -= cols;
+ cols = hs;
+ }
+ new_ptr = bm_offset(new_ptr, new_stride);
+ old_ptr = bm_offset(old_ptr, rows * old_stride);
+ rows_left -= rows;
+ rows = vs;
+ }
+ DEBUG((DBG_BITMAPS, "shrink_glyph: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
+ glyph->w, glyph->h, glyph->x, glyph->y,
+ dest->w, dest->h, dest->x, dest->y));
+ if(DEBUGGING(BITMAP_DATA))
+ bitmap_print(stderr, newmap);
+}
+
+void mdvi_shrink_glyph_grey(DviContext *dvi, DviFont *font,
+ DviFontChar *pk, DviGlyph *dest)
+{
+ int rows_left, rows;
+ int cols_left, cols, init_cols;
+ long sampleval, samplemax;
+ BmUnit *old_ptr;
+ void *image;
+ int w, h;
+ int x, y;
+ DviGlyph *glyph;
+ BITMAP *map;
+ Ulong *pixels;
+ int npixels;
+ Ulong colortab[2];
+ int hs, vs;
+ DviDevice *dev;
+
+ hs = dvi->params.hshrink;
+ vs = dvi->params.vshrink;
+ dev = &dvi->device;
+
+ glyph = &pk->glyph;
+ map = (BITMAP *)glyph->data;
+
+ x = (int)glyph->x / hs;
+ init_cols = (int)glyph->x - x * hs;
+ if(init_cols <= 0)
+ init_cols += hs;
+ else
+ x++;
+ w = x + ROUND((int)glyph->w - glyph->x, hs);
+
+ cols = (int)glyph->y + 1;
+ y = cols / vs;
+ rows = cols - y * vs;
+ if(rows <= 0) {
+ rows += vs;
+ y--;
+ }
+ h = y + ROUND((int)glyph->h - cols, vs) + 1;
+ ASSERT(w && h);
+
+ /* before touching anything, do this */
+ image = dev->create_image(dev->device_data, w, h, BITMAP_BITS);
+ if(image == NULL) {
+ mdvi_shrink_glyph(dvi, font, pk, dest);
+ return;
+ }
+
+ /* save these colors */
+ pk->fg = MDVI_CURRFG(dvi);
+ pk->bg = MDVI_CURRBG(dvi);
+
+ samplemax = vs * hs;
+ npixels = samplemax + 1;
+ pixels = get_color_table(&dvi->device, npixels, pk->fg, pk->bg,
+ dvi->params.gamma, dvi->params.density);
+ if(pixels == NULL) {
+ npixels = 2;
+ colortab[0] = pk->fg;
+ colortab[1] = pk->bg;
+ pixels = &colortab[0];
+ }
+
+ /* setup the new glyph */
+ dest->data = image;
+ dest->x = x;
+ dest->y = glyph->y / vs;
+ dest->w = w;
+ dest->h = h;
+
+ y = 0;
+ old_ptr = map->data;
+ rows_left = glyph->h;
+
+ while(rows_left && y < h) {
+ x = 0;
+ if(rows > rows_left)
+ rows = rows_left;
+ cols_left = glyph->w;
+ cols = init_cols;
+ while(cols_left && x < w) {
+ if(cols > cols_left)
+ cols = cols_left;
+ sampleval = do_sample(old_ptr, map->stride,
+ glyph->w - cols_left, cols, rows);
+ /* scale the sample value by the number of grey levels */
+ if(npixels - 1 != samplemax)
+ sampleval = ((npixels-1) * sampleval) / samplemax;
+ ASSERT(sampleval < npixels);
+ dev->put_pixel(image, x, y, pixels[sampleval]);
+ cols_left -= cols;
+ cols = hs;
+ x++;
+ }
+ for(; x < w; x++)
+ dev->put_pixel(image, x, y, pixels[0]);
+ old_ptr = bm_offset(old_ptr, rows * map->stride);
+ rows_left -= rows;
+ rows = vs;
+ y++;
+ }
+
+ for(; y < h; y++) {
+ for(x = 0; x < w; x++)
+ dev->put_pixel(image, x, y, pixels[0]);
+ }
+ DEBUG((DBG_BITMAPS, "shrink_glyph_grey: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
+ glyph->w, glyph->h, glyph->x, glyph->y,
+ dest->w, dest->h, dest->x, dest->y));
+}
+
diff --git a/backend/dvi/mdvi-lib/bitmap.h b/backend/dvi/mdvi-lib/bitmap.h
new file mode 100644
index 00000000..4d98fecd
--- /dev/null
+++ b/backend/dvi/mdvi-lib/bitmap.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef _BITMAP_H
+#define _BITMAP_H 1
+
+#include "sysdeps.h"
+
+/* Structures and functions to manipulate bitmaps */
+
+/* bitmap unit (as in X's docs) */
+typedef Uint32 BmUnit;
+
+/* size (in bytes) of a bitmap atom */
+#define BITMAP_BYTES 4
+
+/* size (in bits) of a bitmap atom */
+#define BITMAP_BITS (BITMAP_BYTES << 3)
+
+typedef struct {
+ int width;
+ int height;
+ int stride;
+ BmUnit *data;
+} BITMAP;
+
+#define BM_BYTES_PER_LINE(b) \
+ (ROUND((b)->width, BITMAP_BITS) * BITMAP_BYTES)
+#define BM_WIDTH(b) (((BITMAP *)(b))->width)
+#define BM_HEIGHT(b) (((BITMAP *)(b))->height)
+
+#define BMBIT(n) ((BmUnit)1 << (n))
+
+/* Macros to manipulate individual pixels in a bitmap
+ * (they are slow, don't use them)
+ */
+
+#define bm_offset(b,o) (BmUnit *)((Uchar *)(b) + (o))
+
+#define __bm_unit_ptr(b,x,y) \
+ bm_offset((b)->data, (y) * (b)->stride + \
+ ((x) / BITMAP_BITS) * BITMAP_BYTES)
+
+#define __bm_unit(b,x,y) __bm_unit_ptr((b), (x), (y))[0]
+
+#define BM_GETPIXEL(b,x,y) __bm_unit((b), (x), (y))
+#define BM_SETPIXEL(b,x,y) (__bm_unit((b), (x), (y)) |= FIRSTMASKAT(x))
+#define BM_CLRPIXEL(b,x,y) (__bm_unit((b), (x), (y)) &= ~FIRSTMASKAT(x))
+
+/*
+ * These macros are used to access pixels in a bitmap. They are supposed
+ * to be used like this:
+ */
+#if 0
+ BmUnit *row, mask;
+
+ mask = FIRSTMASK;
+
+ /* position `unit' at coordinates (column_number, row_number) */
+ unit = (BmUnit *)((char *)bitmap->data + row_number * bitmap->stride
+ + (column_number / BITMAP_BITS);
+ /* loop over all pixels IN THE SAME ROW */
+ for(i = 0; i < number_of_pixels; i++) {
+ /* to test if a pixel is set */
+ if(*unit & mask) {
+ /* yes, it is, do something with it */
+ }
+ /* to set/clear a pixel */
+ if(painting)
+ *unit |= mask; /* now you see it */
+ else
+ *unit &= ~mask; /* now you don't */
+ /* move to next pixel */
+ if(mask == LASTMASK) {
+ unit++;
+ UPDATEMASK(mask);
+ }
+ }
+/* end of sample code */
+#endif
+
+/* bitmaps are stored in native byte order */
+#ifdef WORD_BIG_ENDIAN
+#define FIRSTSHIFT (BITMAP_BITS - 1)
+#define LASTSHIFT 0
+#define NEXTMASK(m) ((m) >>= 1)
+#define PREVMASK(m) ((m) <<= 1)
+#define FIRSTSHIFTAT(c) (BITMAP_BITS - ((c) % BITMAP_BITS) - 1)
+#else
+#define FIRSTSHIFT 0
+#define LASTSHIFT (BITMAP_BITS - 1)
+#define NEXTMASK(m) ((m) <<= 1)
+#define PREVMASK(m) ((m) >>= 1)
+#define FIRSTSHIFTAT(c) ((c) % BITMAP_BITS)
+#endif
+
+#define FIRSTMASK BMBIT(FIRSTSHIFT)
+#define FIRSTMASKAT(c) BMBIT(FIRSTSHIFTAT(c))
+#define LASTMASK BMBIT(LASTSHIFT)
+
+extern BITMAP *bitmap_alloc __PROTO((int, int));
+extern BITMAP *bitmap_alloc_raw __PROTO((int, int));
+extern void bitmap_destroy __PROTO((BITMAP *));
+
+/*
+ * set_row(bm, row, col, count, state):
+ * sets `count' pixels to state `onoff', starting from pixel
+ * at position (col, row). All pixels must lie in the same
+ * row.
+ */
+extern void bitmap_set_col __PROTO((BITMAP *, int, int, int, int));
+extern void bitmap_set_row __PROTO((BITMAP *, int, int, int, int));
+
+extern void bitmap_paint_bits __PROTO((BmUnit *, int, int));
+extern void bitmap_clear_bits __PROTO((BmUnit *, int, int));
+
+extern BITMAP *bitmap_copy __PROTO((BITMAP *));
+extern void bitmap_flip_horizontally __PROTO((BITMAP *));
+extern void bitmap_flip_vertically __PROTO((BITMAP *));
+extern void bitmap_flip_diagonally __PROTO((BITMAP *));
+extern void bitmap_rotate_clockwise __PROTO((BITMAP *));
+extern void bitmap_rotate_counter_clockwise __PROTO((BITMAP *));
+extern void bitmap_flip_rotate_clockwise __PROTO((BITMAP *));
+extern void bitmap_flip_rotate_counter_clockwise __PROTO((BITMAP *));
+extern BITMAP *bitmap_convert_lsb8 __PROTO((Uchar *, int, int, int));
+extern BITMAP *bitmap_convert_msb8 __PROTO((Uchar *, int, int, int));
+
+#include <stdio.h>
+extern void bitmap_print __PROTO((FILE *, BITMAP *));
+
+#endif /* _BITMAP_H */
diff --git a/backend/dvi/mdvi-lib/color.c b/backend/dvi/mdvi-lib/color.c
new file mode 100644
index 00000000..c28107fd
--- /dev/null
+++ b/backend/dvi/mdvi-lib/color.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include "mdvi.h"
+#include "color.h"
+
+void mdvi_set_color(DviContext *dvi, Ulong fg, Ulong bg)
+{
+ if(dvi->curr_fg != fg || dvi->curr_bg != bg) {
+ DEBUG((DBG_DEVICE, "setting color to (%lu,%lu)\n", fg, bg));
+ if(dvi->device.set_color)
+ dvi->device.set_color(dvi->device.device_data, fg, bg);
+ dvi->curr_fg = fg;
+ dvi->curr_bg = bg;
+ }
+}
+
+void mdvi_push_color(DviContext *dvi, Ulong fg, Ulong bg)
+{
+ if(dvi->color_top == dvi->color_size) {
+ dvi->color_size += 32;
+ dvi->color_stack = mdvi_realloc(dvi->color_stack,
+ dvi->color_size * sizeof(DviColorPair));
+ }
+ dvi->color_stack[dvi->color_top].fg = dvi->curr_fg;
+ dvi->color_stack[dvi->color_top].bg = dvi->curr_bg;
+ dvi->color_top++;
+ mdvi_set_color(dvi, fg, bg);
+}
+
+void mdvi_pop_color(DviContext *dvi)
+{
+ Ulong fg, bg;
+
+ if(dvi->color_top == 0)
+ return;
+ dvi->color_top--;
+ fg = dvi->color_stack[dvi->color_top].fg;
+ bg = dvi->color_stack[dvi->color_top].bg;
+ mdvi_set_color(dvi, fg, bg);
+}
+
+void mdvi_reset_color(DviContext *dvi)
+{
+ dvi->color_top = 0;
+ mdvi_set_color(dvi, dvi->params.fg, dvi->params.bg);
+}
+
+/* cache for color tables, to avoid creating them for every glyph */
+typedef struct {
+ Ulong fg;
+ Ulong bg;
+ Uint nlevels;
+ Ulong *pixels;
+ int density;
+ double gamma;
+ Uint hits;
+} ColorCache;
+
+#define CCSIZE 256
+static ColorCache color_cache[CCSIZE];
+static int cc_entries;
+
+#define GAMMA_DIFF 0.005
+
+
+/* create a color table */
+Ulong *get_color_table(DviDevice *dev,
+ int nlevels, Ulong fg, Ulong bg, double gamma, int density)
+{
+ ColorCache *cc, *tofree;
+ int lohits;
+ Ulong *pixels;
+ int status;
+
+ lohits = color_cache[0].hits;
+ tofree = &color_cache[0];
+ /* look in the cache and see if we have one that matches this request */
+ for(cc = &color_cache[0]; cc < &color_cache[cc_entries]; cc++) {
+ if(cc->hits < lohits) {
+ lohits = cc->hits;
+ tofree = cc;
+ }
+ if(cc->fg == fg && cc->bg == bg && cc->density == density &&
+ cc->nlevels == nlevels && fabs(cc->gamma - gamma) <= GAMMA_DIFF)
+ break;
+ }
+
+ if(cc < &color_cache[cc_entries]) {
+ cc->hits++;
+ return cc->pixels;
+ }
+
+ DEBUG((DBG_DEVICE, "Adding color table to cache (fg=%lu, bg=%lu, n=%d)\n",
+ fg, bg, nlevels));
+
+ /* no entry was found in the cache, create a new one */
+ if(cc_entries < CCSIZE) {
+ cc = &color_cache[cc_entries++];
+ cc->pixels = NULL;
+ } else {
+ cc = tofree;
+ mdvi_free(cc->pixels);
+ }
+ pixels = xnalloc(Ulong, nlevels);
+ status = dev->alloc_colors(dev->device_data,
+ pixels, nlevels, fg, bg, gamma, density);
+ if(status < 0) {
+ mdvi_free(pixels);
+ return NULL;
+ }
+ cc->fg = fg;
+ cc->bg = bg;
+ cc->gamma = gamma;
+ cc->density = density;
+ cc->nlevels = nlevels;
+ cc->pixels = pixels;
+ cc->hits = 1;
+ return pixels;
+}
diff --git a/backend/dvi/mdvi-lib/color.h b/backend/dvi/mdvi-lib/color.h
new file mode 100644
index 00000000..35b2f923
--- /dev/null
+++ b/backend/dvi/mdvi-lib/color.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef _COLOR_H_
+#define _COLOR_H_
+
+#include "common.h"
+
+extern Ulong *get_color_table(DviDevice *dev,
+ int nlevels, Ulong fg, Ulong bg, double gamma, int density);
+
+extern void mdvi_set_color __PROTO((DviContext *, Ulong, Ulong));
+extern void mdvi_push_color __PROTO((DviContext *, Ulong, Ulong));
+extern void mdvi_pop_color __PROTO((DviContext *));
+extern void mdvi_reset_color __PROTO((DviContext *));
+
+#endif /* _COLOR_H_ */
+
diff --git a/backend/dvi/mdvi-lib/common.c b/backend/dvi/mdvi-lib/common.c
new file mode 100644
index 00000000..97b34b56
--- /dev/null
+++ b/backend/dvi/mdvi-lib/common.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+long fsgetn(FILE *p, size_t n)
+{
+ long v;
+
+ v = fgetbyte(p);
+ if(v & 0x80)
+ v -= 0x100;
+ while(--n > 0)
+ v = (v << 8) | fgetbyte(p);
+ return v;
+}
+
+Ulong fugetn(FILE *p, size_t n)
+{
+ Ulong v;
+
+ v = fgetbyte(p);
+ while(--n > 0)
+ v = (v << 8) | fgetbyte(p);
+ return v;
+}
+
+long msgetn(const Uchar *p, size_t n)
+{
+ long v = (long)*p++;
+
+ if(v & 0x80)
+ v -= 0x100;
+ while(--n > 0)
+ v = (v << 8) | *p++;
+ return v;
+}
+
+Ulong mugetn(const Uchar *p, size_t n)
+{
+ Ulong v = (Ulong)*p++;
+
+ while(--n > 0)
+ v = (v << 8) | *p++;
+ return v;
+}
+
+char *read_string(FILE *in, int s, char *buffer, size_t len)
+{
+ int n;
+ char *str;
+
+ n = fugetn(in, s ? s : 1);
+ if((str = buffer) == NULL || n + 1 > len)
+ str = mdvi_malloc(n + 1);
+ if(fread(str, 1, n, in) != n) {
+ if(str != buffer) mdvi_free(str);
+ return NULL;
+ }
+ str[n] = 0;
+ return str;
+}
+
+size_t read_bcpl(FILE *in, char *buffer, size_t maxlen, size_t wanted)
+{
+ size_t i;
+
+ i = (int)fuget1(in);
+ if(maxlen && i > maxlen)
+ i = maxlen;
+ if(fread(buffer, i, 1, in) != 1)
+ return -1;
+ buffer[i] = '\0';
+ while(wanted-- > i)
+ (void)fgetc(in);
+ return i;
+}
+
+char *read_alloc_bcpl(FILE *in, size_t maxlen, size_t *size)
+{
+ size_t i;
+ char *buffer;
+
+ i = (size_t)fuget1(in);
+ if(maxlen && i > maxlen)
+ i = maxlen;
+ buffer = (char *)malloc(i + 1);
+ if(buffer == NULL)
+ return NULL;
+ if(fread(buffer, i, 1, in) != 1) {
+ free(buffer);
+ return NULL;
+ }
+ buffer[i] = '\0';
+ if(size) *size = i;
+ return buffer;
+}
+
+/* buffers */
+
+void buff_free(Buffer *buf)
+{
+ if(buf->data)
+ mdvi_free(buf->data);
+ buff_init(buf);
+}
+
+void buff_init(Buffer *buf)
+{
+ buf->data = NULL;
+ buf->size = 0;
+ buf->length = 0;
+}
+
+size_t buff_add(Buffer *buf, const char *data, size_t len)
+{
+ if(!len && data)
+ len = strlen(data);
+ if(buf->length + len + 1 > buf->size) {
+ buf->size = buf->length + len + 256;
+ buf->data = mdvi_realloc(buf->data, buf->size);
+ }
+ memcpy(buf->data + buf->length, data, len);
+ buf->length += len;
+ return buf->length;
+}
+
+char *buff_gets(Buffer *buf, size_t *length)
+{
+ char *ptr;
+ char *ret;
+ size_t len;
+
+ ptr = strchr(buf->data, '\n');
+ if(ptr == NULL)
+ return NULL;
+ ptr++; /* include newline */
+ len = ptr - buf->data;
+ ret = mdvi_malloc(len + 1);
+ if(len > 0) {
+ memcpy(ret, buf->data, len);
+ memmove(buf->data, buf->data + len, buf->length - len);
+ buf->length -= len;
+ }
+ ret[len] = 0;
+ if(length) *length = len;
+ return ret;
+}
+
diff --git a/backend/dvi/mdvi-lib/common.h b/backend/dvi/mdvi-lib/common.h
new file mode 100644
index 00000000..27a7d8f9
--- /dev/null
+++ b/backend/dvi/mdvi-lib/common.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef _MDVI_COMMON_H
+#define _MDVI_COMMON_H 1
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <math.h>
+
+#include "sysdeps.h"
+
+#if STDC_HEADERS
+# include <string.h>
+#endif
+
+#if !defined(STDC_HEADERS) || defined(__STRICT_ANSI__)
+# ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+# ifndef HAVE_MEMCPY
+# define memcpy(a,b,n) bcopy((b), (a), (n))
+# define memmove(a,b,n) bcopy((b), (a), (n))
+# endif
+#endif
+
+#if defined(STDC_HEADERS) || defined(HAVE_MEMCPY)
+#define memzero(a,n) memset((a), 0, (n))
+#else
+#define memzero(a,n) bzero((a), (n))
+#endif
+
+typedef struct _List {
+ struct _List *next;
+ struct _List *prev;
+} List;
+#define LIST(x) ((List *)(x))
+
+typedef struct {
+ char *data;
+ size_t size;
+ size_t length;
+} Buffer;
+
+typedef struct {
+ List *head;
+ List *tail;
+ int count;
+} ListHead;
+#define MDVI_EMPTY_LIST_HEAD {NULL, NULL, 0}
+
+typedef struct {
+ char *data;
+ size_t size;
+ size_t length;
+} Dstring;
+
+/* Functions to read numbers from streams and memory */
+
+#define fgetbyte(p) ((unsigned)getc(p))
+
+extern char *program_name;
+
+extern Ulong fugetn __PROTO((FILE *, size_t));
+extern long fsgetn __PROTO((FILE *, size_t));
+extern Ulong mugetn __PROTO((const Uchar *, size_t));
+extern long msgetn __PROTO((const Uchar *, size_t));
+
+/* To read from a stream (fu: unsigned, fs: signed) */
+#define fuget4(p) fugetn((p), 4)
+#define fuget3(p) fugetn((p), 3)
+#define fuget2(p) fugetn((p), 2)
+#define fuget1(p) fgetbyte(p)
+#define fsget4(p) fsgetn((p), 4)
+#define fsget3(p) fsgetn((p), 3)
+#define fsget2(p) fsgetn((p), 2)
+#define fsget1(p) fsgetn((p), 1)
+
+/* To read from memory (mu: unsigned, ms: signed) */
+#define MUGETN(p,n) ((p) += (n), mugetn((p)-(n), (n)))
+#define MSGETN(p,n) ((p) += (n), msgetn((p)-(n), (n)))
+#define muget4(p) MUGETN((p), 4)
+#define muget3(p) MUGETN((p), 3)
+#define muget2(p) MUGETN((p), 2)
+#define muget1(p) MUGETN((p), 1)
+#define msget4(p) MSGETN((p), 4)
+#define msget3(p) MSGETN((p), 3)
+#define msget2(p) MSGETN((p), 2)
+#define msget1(p) MSGETN((p), 1)
+
+#define ROUND(x,y) (((x) + (y) - 1) / (y))
+#define FROUND(x) (int)((x) + 0.5)
+#define SFROUND(x) (int)((x) >= 0 ? floor((x) + 0.5) : ceil((x) + 0.5))
+
+#define Max(a,b) (((a) > (b)) ? (a) : (b))
+#define Min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* make 2byte number from 2 8bit quantities */
+#define HALFWORD(a,b) ((((a) << 8) & 0xf) | (b))
+#define FULLWORD(a,b,c,d) \
+ ((((Int8)(a) << 24) & 0xff000000) | \
+ (((Uint8)(b) << 16) & 0x00ff0000) | \
+ (((Uint8)(c) << 8) & 0x0000ff00) | \
+ ((Uint8)(d) & 0xff))
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define SWAPINT(a,b) \
+ ({ int _s = a; a = b; b = _s; })
+#else
+#define SWAPINT(a,b) do { int _s = a; a = b; b = _s; } while(0)
+#endif
+
+#define STREQ(a,b) (strcmp((a), (b)) == 0)
+#define STRNEQ(a,b,n) (strncmp((a), (b), (n)) == 0)
+#define STRCEQ(a,b) (strcasecmp((a), (b)) == 0)
+#define STRNCEQ(a,b,n) (strncasecmp((a), (b), (n)) == 0)
+
+extern char *read_string __PROTO((FILE *, int, char *, size_t));
+extern size_t read_bcpl __PROTO((FILE *, char *, size_t, size_t));
+extern char *read_alloc_bcpl __PROTO((FILE *, size_t, size_t *));
+
+/* miscellaneous */
+
+extern void mdvi_message __PROTO((const char *, ...));
+extern void mdvi_crash __PROTO((const char *, ...));
+extern void mdvi_fatal __PROTO((const char *, ...));
+extern void mdvi_error __PROTO((const char *, ...));
+extern void mdvi_warning __PROTO((const char *, ...));
+extern int unit2pix __PROTO((int, const char *));
+extern double unit2pix_factor __PROTO((const char *));
+
+#define LOG_NONE -1
+#define LOG_INFO 0
+#define LOG_WARN 1
+#define LOG_ERROR 2
+#define LOG_DEBUG 3
+
+#define DBG_OPCODE (1 << 0)
+#define DBG_FONTS (1 << 1)
+#define DBG_FILES (1 << 2)
+#define DBG_DVI (1 << 3)
+#define DBG_PARAMS (1 << 4)
+#define DBG_SPECIAL (1 << 5)
+#define DBG_DEVICE (1 << 6)
+#define DBG_GLYPHS (1 << 7)
+#define DBG_BITMAPS (1 << 8)
+#define DBG_PATHS (1 << 9)
+#define DBG_SEARCH (1 << 10)
+#define DBG_VARS (1 << 11)
+#define DBG_BITMAP_OPS (1 << 12)
+#define DBG_BITMAP_DATA (1 << 13)
+#define DBG_TYPE1 (1 << 14)
+#define DBG_TT (1 << 15)
+#define DBG_FT2 (1 << 16)
+#define DBG_FMAP (1 << 17)
+
+#define DBG_SILENT (1 << 31)
+
+#ifdef NODEBUG
+#define DEBUGGING(x) 0
+#else
+#define DEBUGGING(x) (_mdvi_debug_mask & DBG_##x)
+#endif
+
+#ifndef NODEBUG
+extern Uint32 _mdvi_debug_mask;
+extern void __debug __PROTO((int, const char *, ...));
+#define DEBUG(x) __debug x
+#define ASSERT(x) do { \
+ if(!(x)) mdvi_crash("%s:%d: Assertion %s failed\n", \
+ __FILE__, __LINE__, #x); \
+ } while(0)
+#define ASSERT_VALUE(x,y) do { \
+ if((x) != (y)) \
+ mdvi_crash("%s:%d: Assertion failed (%d = %s != %s)\n", \
+ __FILE__, __LINE__, (x), #x, #x); \
+ } while(0)
+#else
+#define DEBUG(x) do { } while(0)
+#define ASSERT(x) do { } while(0)
+#define ASSERT_VALUE(x,y) do { } while(0)
+#endif
+
+#define set_debug_mask(m) (_mdvi_debug_mask = (Uint32)(m))
+#define add_debug_mask(m) (_mdvi_debug_mask |= (Uint32)(m))
+#define get_debug_mask() _mdvi_debug_mask
+
+/* memory allocation */
+
+extern void mdvi_free __PROTO((void *));
+extern void *mdvi_malloc __PROTO((size_t));
+extern void *mdvi_realloc __PROTO((void *, size_t));
+extern void *mdvi_calloc __PROTO((size_t, size_t));
+extern char *mdvi_strncpy __PROTO((char *, const char *, size_t));
+extern char *mdvi_strdup __PROTO((const char *));
+extern char *mdvi_strndup __PROTO((const char *, size_t));
+extern void *mdvi_memdup __PROTO((const void *, size_t));
+extern char *mdvi_build_path_from_cwd __PROTO((const char *));
+extern char *mdvi_strrstr __PROTO((const char *, const char *));
+
+/* macros to make memory allocation nicer */
+#define xalloc(t) (t *)mdvi_malloc(sizeof(t))
+#define xnalloc(t,n) (t *)mdvi_calloc((n), sizeof(t))
+#define xresize(p,t,n) (t *)mdvi_realloc((p), (n) * sizeof(t))
+
+extern char *xstradd __PROTO((char *, size_t *, size_t, const char *, size_t));
+
+extern Ulong get_mtime __PROTO((int));
+
+/* lists */
+extern void listh_init __PROTO((ListHead *));
+extern void listh_prepend __PROTO((ListHead *, List *));
+extern void listh_append __PROTO((ListHead *, List *));
+extern void listh_add_before __PROTO((ListHead *, List *, List *));
+extern void listh_add_after __PROTO((ListHead *, List *, List *));
+extern void listh_remove __PROTO((ListHead *, List *));
+extern void listh_concat __PROTO((ListHead *, ListHead *));
+extern void listh_catcon __PROTO((ListHead *, ListHead *));
+
+extern void buff_init __PROTO((Buffer *));
+extern size_t buff_add __PROTO((Buffer *, const char *, size_t));
+extern char *buff_gets __PROTO((Buffer *, size_t *));
+extern void buff_free __PROTO((Buffer *));
+
+extern char *getword __PROTO((char *, const char *, char **));
+extern char *getstring __PROTO((char *, const char *, char **));
+
+extern void dstring_init __PROTO((Dstring *));
+extern int dstring_new __PROTO((Dstring *, const char *, int));
+extern int dstring_append __PROTO((Dstring *, const char *, int));
+extern int dstring_copy __PROTO((Dstring *, int, const char *, int));
+extern int dstring_insert __PROTO((Dstring *, int, const char *, int));
+extern void dstring_reset __PROTO((Dstring *));
+
+#define dstring_length(d) ((d)->length)
+#define dstring_strcat(d,s) dstring_append((d), (s), -1)
+
+extern char *dgets __PROTO((Dstring *, FILE *));
+extern int file_readable __PROTO((const char *));
+extern int file_exists __PROTO((const char *));
+extern const char *file_basename __PROTO((const char *));
+extern const char *file_extension __PROTO((const char *));
+
+/*
+ * Miscellaneous macros
+ */
+
+#define LIST_FOREACH(ptr, type, list) \
+ for(ptr = (type *)(list)->head; ptr; ptr = (ptr)->next)
+
+#define Size(x) (sizeof(x) / sizeof((x)[0]))
+
+/* multiply a fix_word by a 32bit number */
+#define B0(x) ((x) & 0xff)
+#define B1(x) B0((x) >> 8)
+#define B2(x) B0((x) >> 16)
+#define B3(x) B0((x) >> 24)
+#define __tfm_mul(z,t) \
+ (((((B0(t) * (z)) >> 8) + (B1(t) * (z))) >> 8) + B2(t) * (z))
+#define TFMSCALE(z,t,a,b) \
+ ((B3(t) == 255) ? \
+ __tfm_mul((z), (t)) / (b) - (a) : \
+ __tfm_mul((z), (t)) / (b))
+#define TFMPREPARE(x,z,a,b) do { \
+ a = 16; z = (x); \
+ while(z > 040000000L) { z >>= 1; a <<= 1; } \
+ b = 256 / a; a *= z; \
+ } while(0)
+
+#endif /* _MDVI_COMMON_H */
diff --git a/backend/dvi/mdvi-lib/defaults.h b/backend/dvi/mdvi-lib/defaults.h
new file mode 100644
index 00000000..7e63f81e
--- /dev/null
+++ b/backend/dvi/mdvi-lib/defaults.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef _MDVI_DEFAULTS_H
+#define _MDVI_DEFAULTS_H 1
+
+/* resolution */
+#define MDVI_DPI 600
+#define MDVI_VDPI MDVI_DPI
+
+/* horizontal margins */
+#define MDVI_HMARGIN "1in"
+
+/* vertical margins */
+#define MDVI_VMARGIN "1in"
+
+/* rulers */
+#define MDVI_HRUNITS "1in"
+#define MDVI_VRUNITS "1in"
+
+/* paper */
+#define MDVI_PAPERNAME "letter"
+
+/* magnification */
+#define MDVI_MAGNIFICATION 1.0
+
+/* fallback font */
+#define MDVI_FALLBACK_FONT "cmr10"
+
+/* metafont mode */
+#define MDVI_MFMODE NULL
+
+/* default shrinking factor */
+#define MDVI_DEFAULT_SHRINKING -1 /* based on resolution */
+
+/* default pixel density */
+#define MDVI_DEFAULT_DENSITY 50
+
+/* default gamma correction */
+#define MDVI_DEFAULT_GAMMA 1.0
+
+/* default window geometry */
+#define MDVI_GEOMETRY NULL
+
+/* default orientation */
+#define MDVI_ORIENTATION "tblr"
+
+/* colors */
+#define MDVI_FOREGROUND "black"
+#define MDVI_BACKGROUND "white"
+
+/* flags */
+#define MDVI_DEFAULT_FLAGS MDVI_ANTIALIASED
+
+#define MDVI_DEFAULT_CONFIG "mdvi.conf"
+
+#endif /* _MDVI_DEAFAULTS_H */
diff --git a/backend/dvi/mdvi-lib/dviopcodes.h b/backend/dvi/mdvi-lib/dviopcodes.h
new file mode 100644
index 00000000..f99af05e
--- /dev/null
+++ b/backend/dvi/mdvi-lib/dviopcodes.h
@@ -0,0 +1,72 @@
+#ifndef _MDVI_DVIOPCODES_H
+#define _MDVI_DVIOPCODES_H 1
+
+#define DVI_SET_CHAR0 0
+#define DVI_SET_CHAR1 1
+#define DVI_SET_CHAR_MAX 127
+#define DVI_SET1 128
+#define DVI_SET2 129
+#define DVI_SET3 130
+#define DVI_SET4 131
+#define DVI_SET_RULE 132
+#define DVI_PUT1 133
+#define DVI_PUT2 134
+#define DVI_PUT3 135
+#define DVI_PUT4 136
+#define DVI_PUT_RULE 137
+#define DVI_NOOP 138
+#define DVI_BOP 139
+#define DVI_EOP 140
+#define DVI_PUSH 141
+#define DVI_POP 142
+#define DVI_RIGHT1 143
+#define DVI_RIGHT2 144
+#define DVI_RIGHT3 145
+#define DVI_RIGHT4 146
+#define DVI_W0 147
+#define DVI_W1 148
+#define DVI_W2 149
+#define DVI_W3 150
+#define DVI_W4 151
+#define DVI_X0 152
+#define DVI_X1 153
+#define DVI_X2 154
+#define DVI_X3 155
+#define DVI_X4 156
+#define DVI_DOWN1 157
+#define DVI_DOWN2 158
+#define DVI_DOWN3 159
+#define DVI_DOWN4 160
+#define DVI_Y0 161
+#define DVI_Y1 162
+#define DVI_Y2 163
+#define DVI_Y3 164
+#define DVI_Y4 165
+#define DVI_Z0 166
+#define DVI_Z1 167
+#define DVI_Z2 168
+#define DVI_Z3 169
+#define DVI_Z4 170
+#define DVI_FNT_NUM0 171
+#define DVI_FNT_NUM1 172
+#define DVI_FNT_NUM_MAX 234
+#define DVI_FNT1 235
+#define DVI_FNT2 236
+#define DVI_FNT3 237
+#define DVI_FNT4 238
+#define DVI_XXX1 239
+#define DVI_XXX2 240
+#define DVI_XXX3 241
+#define DVI_XXX4 242
+#define DVI_FNT_DEF1 243
+#define DVI_FNT_DEF2 244
+#define DVI_FNT_DEF3 245
+#define DVI_FNT_DEF4 246
+#define DVI_PRE 247
+#define DVI_POST 248
+#define DVI_POST_POST 249
+
+#define DVI_ID 2
+#define DVI_TRAILER 223
+
+#endif /* _MDVI_DVIOPCODES_H */
diff --git a/backend/dvi/mdvi-lib/dviread.c b/backend/dvi/mdvi-lib/dviread.c
new file mode 100644
index 00000000..97b7b844
--- /dev/null
+++ b/backend/dvi/mdvi-lib/dviread.c
@@ -0,0 +1,1585 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "mdvi.h"
+#include "private.h"
+#include "color.h"
+
+typedef int (*DviCommand) __PROTO((DviContext *, int));
+
+#define DVICMDDEF(x) static int x __PROTO((DviContext *, int))
+
+DVICMDDEF(set_char);
+DVICMDDEF(set_rule);
+DVICMDDEF(no_op);
+DVICMDDEF(push);
+DVICMDDEF(pop);
+DVICMDDEF(move_right);
+DVICMDDEF(move_down);
+DVICMDDEF(move_w);
+DVICMDDEF(move_x);
+DVICMDDEF(move_y);
+DVICMDDEF(move_z);
+DVICMDDEF(sel_font);
+DVICMDDEF(sel_fontn);
+DVICMDDEF(special);
+DVICMDDEF(def_font);
+DVICMDDEF(undefined);
+DVICMDDEF(unexpected);
+
+static const DviCommand dvi_commands[] = {
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char,
+ set_char, set_char, set_char, set_char, /* 0 - 127 */
+ set_char, set_char, set_char, set_char, /* 128 - 131 */
+ set_rule, /* 132 */
+ set_char, set_char, set_char, set_char, /* 133 - 136 */
+ set_rule, /* 137 */
+ no_op, /* 138 */
+ unexpected, /* 139 (BOP) */
+ unexpected, /* 140 (EOP) */
+ push, /* 141 */
+ pop, /* 142 */
+ move_right, move_right, move_right, move_right, /* 143 - 146 */
+ move_w, move_w, move_w, move_w, move_w, /* 147 - 151 */
+ move_x, move_x, move_x, move_x, move_x, /* 152 - 156 */
+ move_down, move_down, move_down, move_down, /* 157 - 160 */
+ move_y, move_y, move_y, move_y, move_y, /* 161 - 165 */
+ move_z, move_z, move_z, move_z, move_z, /* 166 - 170 */
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font,
+ sel_font, sel_font, sel_font, sel_font, /* 171 - 234 */
+ sel_fontn, sel_fontn, sel_fontn, sel_fontn, /* 235 - 238 */
+ special, special, special, special, /* 239 - 242 */
+ def_font, def_font, def_font, def_font, /* 243 - 246 */
+ unexpected, /* 247 (PRE) */
+ unexpected, /* 248 (POST) */
+ unexpected, /* 249 (POST_POST) */
+ undefined, undefined, undefined,
+ undefined, undefined, undefined /* 250 - 255 */
+};
+
+#define DVI_BUFLEN 4096
+
+static int mdvi_run_macro(DviContext *dvi, Uchar *macro, size_t len);
+
+static void dummy_draw_glyph(DviContext *dvi, DviFontChar *ch, int x, int y)
+{
+}
+
+static void dummy_draw_rule(DviContext *dvi, int x, int y, Uint w, Uint h, int f)
+{
+}
+
+static int dummy_alloc_colors(void *a, Ulong *b, int c, Ulong d, Ulong e, double f, int g)
+{
+ return -1;
+}
+
+static void *dummy_create_image(void *a, Uint b, Uint c, Uint d)
+{
+ return NULL;
+}
+
+static void dummy_free_image(void *a)
+{
+}
+
+static void dummy_dev_destroy(void *a)
+{
+}
+
+static void dummy_dev_putpixel(void *a, int x, int y, Ulong c)
+{
+}
+
+static void dummy_dev_refresh(DviContext *a, void *b)
+{
+}
+
+static void dummy_dev_set_color(void *a, Ulong b, Ulong c)
+{
+}
+
+/* functions to report errors */
+static void dvierr(DviContext *dvi, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ fprintf(stderr, "%s[%d]: Error: ",
+ dvi->filename, dvi->currpage);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+
+static void dviwarn(DviContext *dvi, const char *format, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s[%d]: Warning: ",
+ dvi->filename, dvi->currpage);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+
+#define NEEDBYTES(d,n) \
+ ((d)->buffer.pos + (n) > (d)->buffer.length)
+
+static int get_bytes(DviContext *dvi, size_t n)
+{
+ /*
+ * caller wants to read `n' bytes from dvi->buffer + dvi->pos.
+ * Make sure there is enough data to satisfy the request
+ */
+ if(NEEDBYTES(dvi, n)) {
+ size_t required;
+ int newlen;
+
+ if(dvi->buffer.frozen || dvi->in == NULL || feof(dvi->in)) {
+ /* this is EOF */
+ dviwarn(dvi, _("unexpected EOF\n"));
+ return -1;
+ }
+ /* get more data */
+ if(dvi->buffer.data == NULL) {
+ /* first allocation */
+ dvi->buffer.size = Max(DVI_BUFLEN, n);
+ dvi->buffer.data = (Uchar *)mdvi_malloc(dvi->buffer.size);
+ dvi->buffer.length = 0;
+ dvi->buffer.frozen = 0;
+ } else if(dvi->buffer.pos < dvi->buffer.length) {
+ /* move whatever we want to keep */
+ dvi->buffer.length -= dvi->buffer.pos;
+ memmove(dvi->buffer.data,
+ dvi->buffer.data + dvi->buffer.pos,
+ dvi->buffer.length);
+ } else {
+ /* we can discard all the data in this buffer */
+ dvi->buffer.length = 0;
+ }
+
+ required = n - dvi->buffer.length;
+ if(required > dvi->buffer.size - dvi->buffer.length) {
+ /* need to allocate more memory */
+ dvi->buffer.size = dvi->buffer.length + required + 128;
+ dvi->buffer.data = (Uchar *)xresize(dvi->buffer.data,
+ char, dvi->buffer.size);
+ }
+ /* now read into the buffer */
+ newlen = fread(dvi->buffer.data + dvi->buffer.length,
+ 1, dvi->buffer.size - dvi->buffer.length, dvi->in);
+ if(newlen == -1) {
+ mdvi_error("%s: %s\n", dvi->filename, strerror(errno));
+ return -1;
+ }
+ dvi->buffer.length += newlen;
+ dvi->buffer.pos = 0;
+ }
+ return 0;
+}
+
+/* only relative forward seeks are supported by this function */
+static int dskip(DviContext *dvi, long offset)
+{
+ ASSERT(offset > 0);
+
+ if(NEEDBYTES(dvi, offset) && get_bytes(dvi, offset) == -1)
+ return -1;
+ dvi->buffer.pos += offset;
+ return 0;
+}
+
+/* DVI I/O functions (note: here `n' must be <= 4) */
+static long dsgetn(DviContext *dvi, size_t n)
+{
+ long val;
+
+ if(NEEDBYTES(dvi, n) && get_bytes(dvi, n) == -1)
+ return -1;
+ val = msgetn(dvi->buffer.data + dvi->buffer.pos, n);
+ dvi->buffer.pos += n;
+ return val;
+}
+
+static int dread(DviContext *dvi, char *buffer, size_t len)
+{
+ if(NEEDBYTES(dvi, len) && get_bytes(dvi, len) == -1)
+ return -1;
+ memcpy(buffer, dvi->buffer.data + dvi->buffer.pos, len);
+ dvi->buffer.pos += len;
+ return 0;
+}
+
+static long dugetn(DviContext *dvi, size_t n)
+{
+ long val;
+
+ if(NEEDBYTES(dvi, n) && get_bytes(dvi, n) == -1)
+ return -1;
+ val = mugetn(dvi->buffer.data + dvi->buffer.pos, n);
+ dvi->buffer.pos += n;
+ return val;
+}
+
+static long dtell(DviContext *dvi)
+{
+ return dvi->depth ?
+ dvi->buffer.pos :
+ ftell(dvi->in) - dvi->buffer.length + dvi->buffer.pos;
+}
+
+static void dreset(DviContext *dvi)
+{
+ if(!dvi->buffer.frozen && dvi->buffer.data)
+ mdvi_free(dvi->buffer.data);
+ dvi->buffer.data = NULL;
+ dvi->buffer.size = 0;
+ dvi->buffer.length = 0;
+ dvi->buffer.pos = 0;
+}
+
+#define dsget1(d) dsgetn((d), 1)
+#define dsget2(d) dsgetn((d), 2)
+#define dsget3(d) dsgetn((d), 3)
+#define dsget4(d) dsgetn((d), 4)
+#define duget1(d) dugetn((d), 1)
+#define duget2(d) dugetn((d), 2)
+#define duget3(d) dugetn((d), 3)
+#define duget4(d) dugetn((d), 4)
+
+#ifndef NODEBUG
+static void dviprint(DviContext *dvi, const char *command, int sub, const char *fmt, ...)
+{
+ int i;
+ va_list ap;
+
+ printf("%s: ", dvi->filename);
+ for(i = 0; i < dvi->depth; i++)
+ printf(" ");
+ printf("%4lu: %s", dtell(dvi), command);
+ if(sub >= 0) printf("%d", sub);
+ if(*fmt) printf(": ");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+#define SHOWCMD(x) \
+ if(_mdvi_debug_mask & DBG_OPCODE) do { dviprint x; } while(0)
+#else
+#define SHOWCMD(x) do { } while(0)
+#endif
+
+int mdvi_find_tex_page(DviContext *dvi, int tex_page)
+{
+ int i;
+
+ for(i = 0; i < dvi->npages; i++)
+ if(dvi->pagemap[i][1] == tex_page)
+ return i;
+ return -1;
+}
+
+/* page sorting functions */
+static int sort_up(const void *p1, const void *p2)
+{
+ return ((long *)p1)[1] - ((long *)p2)[1];
+}
+static int sort_down(const void *p1, const void *p2)
+{
+ return ((long *)p2)[1] - ((long *)p1)[1];
+}
+static int sort_random(const void *p1, const void *p2)
+{
+ return (rand() % 1) ? -1 : 1;
+}
+static int sort_dvi_up(const void *p1, const void *p2)
+{
+ return ((long *)p1)[0] - ((long *)p2)[0];
+}
+static int sort_dvi_down(const void *p1, const void *p2)
+{
+ return ((long *)p1)[0] - ((long *)p2)[0];
+}
+
+void mdvi_sort_pages(DviContext *dvi, DviPageSort type)
+{
+ int (*sortfunc) __PROTO((const void *, const void *));
+
+ switch(type) {
+ case MDVI_PAGE_SORT_UP:
+ sortfunc = sort_up;
+ break;
+ case MDVI_PAGE_SORT_DOWN:
+ sortfunc = sort_down;
+ break;
+ case MDVI_PAGE_SORT_RANDOM:
+ sortfunc = sort_random;
+ break;
+ case MDVI_PAGE_SORT_DVI_UP:
+ sortfunc = sort_dvi_up;
+ break;
+ case MDVI_PAGE_SORT_DVI_DOWN:
+ sortfunc = sort_dvi_down;
+ break;
+ case MDVI_PAGE_SORT_NONE:
+ default:
+ sortfunc = NULL;
+ break;
+ }
+
+ if(sortfunc)
+ qsort(dvi->pagemap, dvi->npages, sizeof(PageNum), sortfunc);
+}
+
+static DviFontRef *define_font(DviContext *dvi, int op)
+{
+ Int32 arg;
+ Int32 scale;
+ Int32 dsize;
+ Int32 checksum;
+ int hdpi;
+ int vdpi;
+ int n;
+ char *name;
+ DviFontRef *ref;
+
+ arg = dugetn(dvi, op - DVI_FNT_DEF1 + 1);
+ checksum = duget4(dvi);
+ scale = duget4(dvi);
+ dsize = duget4(dvi);
+ hdpi = FROUND(dvi->params.mag * dvi->params.dpi * scale / dsize);
+ vdpi = FROUND(dvi->params.mag * dvi->params.vdpi * scale / dsize);
+ n = duget1(dvi) + duget1(dvi);
+ name = mdvi_malloc(n + 1);
+ dread(dvi, name, n);
+ name[n] = 0;
+ DEBUG((DBG_FONTS, "requesting font %d = `%s' at %.1fpt (%dx%d dpi)\n",
+ arg, name, (double)scale / (dvi->params.tfm_conv * 0x100000),
+ hdpi, vdpi));
+ ref = font_reference(&dvi->params, arg, name, checksum, hdpi, vdpi, scale);
+ if(ref == NULL) {
+ mdvi_error(_("could not load font `%s'\n"), name);
+ mdvi_free(name);
+ return NULL;
+ }
+ mdvi_free(name);
+ return ref;
+}
+
+static char *opendvi(const char *name)
+{
+ int len;
+ char *file;
+
+ len = strlen(name);
+ /* if file ends with .dvi and it exists, that's it */
+ if(len >= 4 && STREQ(name+len-4, ".dvi")) {
+ DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", name));
+ if(access(name, R_OK) == 0)
+ return mdvi_strdup(name);
+ }
+
+ /* try appending .dvi */
+ file = mdvi_malloc(len + 5);
+ strcpy(file, name);
+ strcpy(file+len, ".dvi");
+ DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", file));
+ if(access(file, R_OK) == 0)
+ return file;
+ /* try the given name */
+ file[len] = 0;
+ DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", file));
+ if(access(file, R_OK) == 0)
+ return file;
+ mdvi_free(file);
+ return NULL;
+}
+
+int mdvi_reload(DviContext *dvi, DviParams *np)
+{
+ DviContext *newdvi;
+ DviParams *pars;
+
+ /* close our file */
+ if(dvi->in) {
+ fclose(dvi->in);
+ dvi->in = NULL;
+ }
+
+ pars = np ? np : &dvi->params;
+ DEBUG((DBG_DVI, "%s: reloading\n", dvi->filename));
+
+ /* load it again */
+ newdvi = mdvi_init_context(pars, dvi->pagesel, dvi->filename);
+ if(newdvi == NULL) {
+ mdvi_warning(_("could not reload `%s'\n"), dvi->filename);
+ return -1;
+ }
+
+ /* drop all our font references */
+ font_drop_chain(dvi->fonts);
+ /* destroy our font map */
+ if(dvi->fontmap)
+ mdvi_free(dvi->fontmap);
+ dvi->currfont = NULL;
+
+ /* and use the ones we just loaded */
+ dvi->fonts = newdvi->fonts;
+ dvi->fontmap = newdvi->fontmap;
+ dvi->nfonts = newdvi->nfonts;
+
+ /* copy the new information */
+ dvi->params = newdvi->params;
+ dvi->num = newdvi->num;
+ dvi->den = newdvi->den;
+ dvi->dvimag = newdvi->dvimag;
+ dvi->dviconv = newdvi->dviconv;
+ dvi->dvivconv = newdvi->dvivconv;
+ dvi->modtime = newdvi->modtime;
+
+ if(dvi->fileid) mdvi_free(dvi->fileid);
+ dvi->fileid = newdvi->fileid;
+
+ dvi->dvi_page_w = newdvi->dvi_page_w;
+ dvi->dvi_page_h = newdvi->dvi_page_h;
+
+ mdvi_free(dvi->pagemap);
+ dvi->pagemap = newdvi->pagemap;
+ dvi->npages = newdvi->npages;
+ if(dvi->currpage > dvi->npages-1)
+ dvi->currpage = 0;
+
+ mdvi_free(dvi->stack);
+ dvi->stack = newdvi->stack;
+ dvi->stacksize = newdvi->stacksize;
+
+ /* remove fonts that are not being used anymore */
+ font_free_unused(&dvi->device);
+
+ mdvi_free(newdvi->filename);
+ mdvi_free(newdvi);
+
+ DEBUG((DBG_DVI, "%s: reload successful\n", dvi->filename));
+ if(dvi->device.refresh)
+ dvi->device.refresh(dvi, dvi->device.device_data);
+
+ return 0;
+}
+
+/* function to change parameters ia DVI context
+ * The DVI context is modified ONLY if this function is successful */
+int mdvi_configure(DviContext *dvi, DviParamCode option, ...)
+{
+ va_list ap;
+ int reset_all;
+ int reset_font;
+ DviParams np;
+
+ va_start(ap, option);
+
+ reset_font = 0;
+ reset_all = 0;
+ np = dvi->params; /* structure copy */
+ while(option != MDVI_PARAM_LAST) {
+ switch(option) {
+ case MDVI_SET_DPI:
+ np.dpi = np.vdpi = va_arg(ap, Uint);
+ reset_all = 1;
+ break;
+ case MDVI_SET_XDPI:
+ np.dpi = va_arg(ap, Uint);
+ reset_all = 1;
+ break;
+ case MDVI_SET_YDPI:
+ np.vdpi = va_arg(ap, Uint);
+ break;
+ case MDVI_SET_SHRINK:
+ np.hshrink = np.vshrink = va_arg(ap, Uint);
+ reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP;
+ break;
+ case MDVI_SET_XSHRINK:
+ np.hshrink = va_arg(ap, Uint);
+ reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP;
+ break;
+ case MDVI_SET_YSHRINK:
+ np.vshrink = va_arg(ap, Uint);
+ reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP;
+ break;
+ case MDVI_SET_ORIENTATION:
+ np.orientation = va_arg(ap, DviOrientation);
+ reset_font = MDVI_FONTSEL_GLYPH;
+ break;
+ case MDVI_SET_GAMMA:
+ np.gamma = va_arg(ap, double);
+ reset_font = MDVI_FONTSEL_GREY;
+ break;
+ case MDVI_SET_DENSITY:
+ np.density = va_arg(ap, Uint);
+ reset_font = MDVI_FONTSEL_BITMAP;
+ break;
+ case MDVI_SET_MAGNIFICATION:
+ np.mag = va_arg(ap, double);
+ reset_all = 1;
+ break;
+ case MDVI_SET_DRIFT:
+ np.hdrift = np.vdrift = va_arg(ap, int);
+ break;
+ case MDVI_SET_HDRIFT:
+ np.hdrift = va_arg(ap, int);
+ break;
+ case MDVI_SET_VDRIFT:
+ np.vdrift = va_arg(ap, int);
+ break;
+ case MDVI_SET_FOREGROUND:
+ np.fg = va_arg(ap, Ulong);
+ reset_font = MDVI_FONTSEL_GREY;
+ break;
+ case MDVI_SET_BACKGROUND:
+ np.bg = va_arg(ap, Ulong);
+ reset_font = MDVI_FONTSEL_GREY;
+ break;
+ default:
+ break;
+ }
+ option = va_arg(ap, DviParamCode);
+ }
+ va_end(ap);
+
+ /* check that all values make sense */
+ if(np.dpi <= 0 || np.vdpi <= 0)
+ return -1;
+ if(np.mag <= 0.0)
+ return -1;
+ if(np.density < 0)
+ return -1;
+ if(np.hshrink < 1 || np.vshrink < 1)
+ return -1;
+ if(np.hdrift < 0 || np.vdrift < 0)
+ return -1;
+ if(np.fg == np.bg)
+ return -1;
+
+ /*
+ * If the dpi or the magnification change, we basically have to reload
+ * the DVI file again from scratch.
+ */
+
+ if(reset_all)
+ return (mdvi_reload(dvi, &np) == 0);
+
+ if(np.hshrink != dvi->params.hshrink) {
+ np.conv = dvi->dviconv;
+ if(np.hshrink)
+ np.conv /= np.hshrink;
+ }
+ if(np.vshrink != dvi->params.vshrink) {
+ np.vconv = dvi->dvivconv;
+ if(np.vshrink)
+ np.vconv /= np.vshrink;
+ }
+
+ if(reset_font) {
+ font_reset_chain_glyphs(&dvi->device, dvi->fonts, reset_font);
+ }
+ dvi->params = np;
+ if((reset_font & MDVI_FONTSEL_GLYPH) && dvi->device.refresh) {
+ dvi->device.refresh(dvi, dvi->device.device_data);
+ return 0;
+ }
+
+ return 1;
+}
+/*
+ * Read the initial data from the DVI file. If something is wrong with the
+ * file, we just spit out an error message and refuse to load the file,
+ * without giving any details. This makes sense because DVI files are ok
+ * 99.99% of the time, and dvitype(1) can be used to check the other 0.01%.
+ */
+DviContext *mdvi_init_context(DviParams *par, DviPageSpec *spec, const char *file)
+{
+ FILE *p;
+ Int32 arg;
+ int op;
+ long offset;
+ int n;
+ DviContext *dvi;
+ char *filename;
+ int pagecount;
+
+ /*
+ * 1. Open the file and initialize the DVI context
+ */
+
+ filename = opendvi(file);
+ if(filename == NULL) {
+ perror(file);
+ return NULL;
+ }
+ p = fopen(filename, "rb");
+ if(p == NULL) {
+ perror(file);
+ mdvi_free(filename);
+ return NULL;
+ }
+ dvi = xalloc(DviContext);
+ memzero(dvi, sizeof(DviContext));
+ dvi->pagemap = NULL;
+ dvi->filename = filename;
+ dvi->stack = NULL;
+ dvi->modtime = get_mtime(fileno(p));
+ dvi->buffer.data = NULL;
+ dvi->pagesel = spec;
+ dvi->in = p; /* now we can use the dget*() functions */
+
+ /*
+ * 2. Read the preamble, extract scaling information, and
+ * setup the DVI parameters.
+ */
+
+ if(fuget1(p) != DVI_PRE)
+ goto bad_dvi;
+ if((arg = fuget1(p)) != DVI_ID) {
+ mdvi_error(_("%s: unsupported DVI format (version %u)\n"),
+ file, arg);
+ goto error; /* jump to the end of this routine,
+ * where we handle errors */
+ }
+ /* get dimensions */
+ dvi->num = fuget4(p);
+ dvi->den = fuget4(p);
+ dvi->dvimag = fuget4(p);
+
+ /* check that these numbers make sense */
+ if(!dvi->num || !dvi->den || !dvi->dvimag)
+ goto bad_dvi;
+
+ dvi->params.mag =
+ (par->mag > 0 ? par->mag : (double)dvi->dvimag / 1000.0);
+ dvi->params.hdrift = par->hdrift;
+ dvi->params.vdrift = par->vdrift;
+ dvi->params.dpi = par->dpi ? par->dpi : MDVI_DPI;
+ dvi->params.vdpi = par->vdpi ? par->vdpi : par->dpi;
+ dvi->params.hshrink = par->hshrink;
+ dvi->params.vshrink = par->vshrink;
+ dvi->params.density = par->density;
+ dvi->params.gamma = par->gamma;
+ dvi->params.conv = (double)dvi->num / dvi->den;
+ dvi->params.conv *= (dvi->params.dpi / 254000.0) * dvi->params.mag;
+ dvi->params.vconv = (double)dvi->num / dvi->den;
+ dvi->params.vconv *= (dvi->params.vdpi / 254000.0) * dvi->params.mag;
+ dvi->params.tfm_conv = (25400000.0 / dvi->num) *
+ ((double)dvi->den / 473628672) / 16.0;
+ dvi->params.flags = par->flags;
+ dvi->params.orientation = par->orientation;
+ dvi->params.fg = par->fg;
+ dvi->params.bg = par->bg;
+
+ /* initialize colors */
+ dvi->curr_fg = par->fg;
+ dvi->curr_bg = par->bg;
+ dvi->color_stack = NULL;
+ dvi->color_top = 0;
+ dvi->color_size = 0;
+
+ /* pixel conversion factors */
+ dvi->dviconv = dvi->params.conv;
+ dvi->dvivconv = dvi->params.vconv;
+ if(dvi->params.hshrink)
+ dvi->params.conv /= dvi->params.hshrink;
+ if(dvi->params.vshrink)
+ dvi->params.vconv /= dvi->params.vshrink;
+
+ /* get the comment from the preamble */
+ n = fuget1(p);
+ dvi->fileid = mdvi_malloc(n + 1);
+ fread(dvi->fileid, 1, n, p);
+ dvi->fileid[n] = 0;
+ DEBUG((DBG_DVI, "%s: %s\n", filename, dvi->fileid));
+
+ /*
+ * 3. Read postamble, extract page information (number of
+ * pages, dimensions) and stack depth.
+ */
+
+ /* jump to the end of the file */
+ if(fseek(p, (long)-1, SEEK_END) == -1)
+ goto error;
+ for(n = 0; (op = fuget1(p)) == DVI_TRAILER; n++)
+ if(fseek(p, (long)-2, SEEK_CUR) < 0)
+ break;
+ if(op != arg || n < 4)
+ goto bad_dvi;
+ /* get the pointer to postamble */
+ fseek(p, (long)-5, SEEK_CUR);
+ arg = fuget4(p);
+ /* jump to it */
+ fseek(p, (long)arg, SEEK_SET);
+ if(fuget1(p) != DVI_POST)
+ goto bad_dvi;
+ offset = fuget4(p);
+ if(dvi->num != fuget4(p) || dvi->den != fuget4(p) ||
+ dvi->dvimag != fuget4(p))
+ goto bad_dvi;
+ dvi->dvi_page_h = fuget4(p);
+ dvi->dvi_page_w = fuget4(p);
+ dvi->stacksize = fuget2(p);
+ dvi->npages = fuget2(p);
+ DEBUG((DBG_DVI, "%s: from postamble: stack depth %d, %d page%s\n",
+ filename, dvi->stacksize, dvi->npages, dvi->npages > 1 ? "s" : ""));
+
+ /*
+ * 4. Process font definitions.
+ */
+
+ /* process font definitions */
+ dvi->nfonts = 0;
+ dvi->fontmap = NULL;
+ /*
+ * CAREFUL: here we need to use the dvi->buffer, but it might leave the
+ * the file cursor in the wrong position after reading fonts (because of
+ * buffering). It's ok, though, because after the font definitions we read
+ * the page offsets, and we fseek() to the relevant part of the file with
+ * SEEK_SET. Nothing is read after the page offsets.
+ */
+ while((op = duget1(dvi)) != DVI_POST_POST) {
+ DviFontRef *ref;
+
+ if(op == DVI_NOOP)
+ continue;
+ else if(op < DVI_FNT_DEF1 || op > DVI_FNT_DEF4)
+ goto error;
+ ref = define_font(dvi, op);
+ if(ref == NULL)
+ goto error;
+ ref->next = dvi->fonts;
+ dvi->fonts = ref;
+ dvi->nfonts++;
+ }
+ /* we don't need the buffer anymore */
+ dreset(dvi);
+
+ if(op != DVI_POST_POST)
+ goto bad_dvi;
+ font_finish_definitions(dvi);
+ DEBUG((DBG_DVI, "%s: %d font%s required by this job\n",
+ filename, dvi->nfonts, dvi->nfonts > 1 ? "s" : ""));
+ dvi->findref = font_find_mapped;
+
+ /*
+ * 5. Build the page map.
+ */
+
+ dvi->pagemap = xnalloc(PageNum, dvi->npages);
+ memzero(dvi->pagemap, sizeof(PageNum) * dvi->npages);
+
+ n = dvi->npages - 1;
+ pagecount = n;
+ while(offset != -1) {
+ int i;
+ PageNum page;
+
+ fseek(p, offset, SEEK_SET);
+ op = fuget1(p);
+ if(op != DVI_BOP || n < 0)
+ goto bad_dvi;
+ for(i = 1; i <= 10; i++)
+ page[i] = fsget4(p);
+ page[0] = offset;
+ offset = fsget4(p);
+ /* check if the page is selected */
+ if(spec && mdvi_page_selected(spec, page, n) == 0) {
+ DEBUG((DBG_DVI, "Page %d (%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld) ignored by request\n",
+ n, page[1], page[2], page[3], page[4], page[5],
+ page[6], page[7], page[8], page[9], page[10]));
+ } else {
+ memcpy(&dvi->pagemap[pagecount], page, sizeof(PageNum));
+ pagecount--;
+ }
+ n--;
+ }
+ pagecount++;
+ if(pagecount >= dvi->npages) {
+ mdvi_error(_("no pages selected\n"));
+ goto error;
+ }
+ if(pagecount) {
+ DEBUG((DBG_DVI, "%d of %d pages selected\n",
+ dvi->npages - pagecount, dvi->npages));
+ dvi->npages -= pagecount;
+ memmove(dvi->pagemap, &dvi->pagemap[pagecount],
+ dvi->npages * sizeof(PageNum));
+ }
+
+ /*
+ * 6. Setup stack, initialize device functions
+ */
+
+ dvi->curr_layer = 0;
+ dvi->stack = xnalloc(DviState, dvi->stacksize + 8);
+
+ dvi->device.draw_glyph = dummy_draw_glyph;
+ dvi->device.draw_rule = dummy_draw_rule;
+ dvi->device.alloc_colors = dummy_alloc_colors;
+ dvi->device.create_image = dummy_create_image;
+ dvi->device.free_image = dummy_free_image;
+ dvi->device.dev_destroy = dummy_dev_destroy;
+ dvi->device.put_pixel = dummy_dev_putpixel;
+ dvi->device.refresh = dummy_dev_refresh;
+ dvi->device.set_color = dummy_dev_set_color;
+ dvi->device.device_data = NULL;
+
+ DEBUG((DBG_DVI, "%s read successfully\n", filename));
+ return dvi;
+
+bad_dvi:
+ mdvi_error(_("%s: File corrupted, or not a DVI file\n"), file);
+error:
+ /* if we came from the font definitions, this will be non-trivial */
+ dreset(dvi);
+ mdvi_destroy_context(dvi);
+ return NULL;
+}
+
+void mdvi_destroy_context(DviContext *dvi)
+{
+ if(dvi->device.dev_destroy)
+ dvi->device.dev_destroy(dvi->device.device_data);
+ /* release all fonts */
+ if(dvi->fonts) {
+ font_drop_chain(dvi->fonts);
+ font_free_unused(&dvi->device);
+ }
+ if(dvi->fontmap)
+ mdvi_free(dvi->fontmap);
+ if(dvi->filename)
+ mdvi_free(dvi->filename);
+ if(dvi->stack)
+ mdvi_free(dvi->stack);
+ if(dvi->pagemap)
+ mdvi_free(dvi->pagemap);
+ if(dvi->fileid)
+ mdvi_free(dvi->fileid);
+ if(dvi->in)
+ fclose(dvi->in);
+ if(dvi->buffer.data && !dvi->buffer.frozen)
+ mdvi_free(dvi->buffer.data);
+ if(dvi->color_stack)
+ mdvi_free(dvi->color_stack);
+
+ mdvi_free(dvi);
+}
+
+void mdvi_setpage(DviContext *dvi, int pageno)
+{
+ if(pageno < 0)
+ pageno = 0;
+ if(pageno > dvi->npages-1)
+ pageno = dvi->npages - 1;
+ dvi->currpage = pageno;
+}
+
+static int mdvi_run_macro(DviContext *dvi, Uchar *macro, size_t len)
+{
+ DviFontRef *curr, *fonts;
+ DviBuffer saved_buffer;
+ FILE *saved_file;
+ int opcode;
+ int oldtop;
+
+ dvi->depth++;
+ push(dvi, DVI_PUSH);
+ dvi->pos.w = 0;
+ dvi->pos.x = 0;
+ dvi->pos.y = 0;
+ dvi->pos.z = 0;
+
+ /* save our state */
+ curr = dvi->currfont;
+ fonts = dvi->fonts;
+ saved_buffer = dvi->buffer;
+ saved_file = dvi->in;
+ dvi->currfont = curr->ref->subfonts;
+ dvi->fonts = curr->ref->subfonts;
+ dvi->buffer.data = macro;
+ dvi->buffer.pos = 0;
+ dvi->buffer.length = len;
+ dvi->buffer.frozen = 1;
+ dvi->in = NULL;
+ oldtop = dvi->stacktop;
+
+ /* execute commands */
+ while((opcode = duget1(dvi)) != DVI_EOP) {
+ if(dvi_commands[opcode](dvi, opcode) < 0)
+ break;
+ }
+ if(opcode != DVI_EOP)
+ dviwarn(dvi, _("%s: vf macro had errors\n"),
+ curr->ref->fontname);
+ if(dvi->stacktop != oldtop)
+ dviwarn(dvi, _("%s: stack not empty after vf macro\n"),
+ curr->ref->fontname);
+
+ /* restore things */
+ pop(dvi, DVI_POP);
+ dvi->currfont = curr;
+ dvi->fonts = fonts;
+ dvi->buffer = saved_buffer;
+ dvi->in = saved_file;
+ dvi->depth--;
+
+ return (opcode != DVI_EOP ? -1 : 0);
+}
+
+int mdvi_dopage(DviContext *dvi, int pageno)
+{
+ int op;
+ int ppi;
+ int reloaded = 0;
+
+again:
+ if(dvi->in == NULL) {
+ /* try reopening the file */
+ dvi->in = fopen(dvi->filename, "rb");
+ if(dvi->in == NULL) {
+ mdvi_warning(_("%s: could not reopen file (%s)\n"),
+ dvi->filename,
+ strerror(errno));
+ return -1;
+ }
+ DEBUG((DBG_FILES, "reopen(%s) -> Ok\n", dvi->filename));
+ }
+
+ /* check if we need to reload the file */
+ if(!reloaded && get_mtime(fileno(dvi->in)) > dvi->modtime) {
+ mdvi_reload(dvi, &dvi->params);
+ /* we have to reopen the file, again */
+ reloaded = 1;
+ goto again;
+ }
+
+ if(pageno < 0 || pageno > dvi->npages-1) {
+ mdvi_error(_("%s: page %d out of range\n"),
+ dvi->filename, pageno);
+ return -1;
+ }
+
+ fseek(dvi->in, (long)dvi->pagemap[pageno][0], SEEK_SET);
+ if((op = fuget1(dvi->in)) != DVI_BOP) {
+ mdvi_error(_("%s: bad offset at page %d\n"),
+ dvi->filename, pageno+1);
+ return -1;
+ }
+
+ /* skip bop */
+ fseek(dvi->in, (long)44, SEEK_CUR);
+
+ /* reset state */
+ dvi->currfont = NULL;
+ memzero(&dvi->pos, sizeof(DviState));
+ dvi->stacktop = 0;
+ dvi->currpage = pageno;
+ dvi->curr_layer = 0;
+
+ if(dvi->buffer.data && !dvi->buffer.frozen)
+ mdvi_free(dvi->buffer.data);
+
+ /* reset our buffer */
+ dvi->buffer.data = NULL;
+ dvi->buffer.length = 0;
+ dvi->buffer.pos = 0;
+ dvi->buffer.frozen = 0;
+
+#if 0 /* make colors survive page breaks */
+ /* reset color stack */
+ mdvi_reset_color(dvi);
+#endif
+
+ /* set max horizontal and vertical drift (from dvips) */
+ if(dvi->params.hdrift < 0) {
+ ppi = dvi->params.dpi / dvi->params.hshrink; /* shrunk pixels per inch */
+ if(ppi < 600)
+ dvi->params.hdrift = ppi / 100;
+ else if(ppi < 1200)
+ dvi->params.hdrift = ppi / 200;
+ else
+ dvi->params.hdrift = ppi / 400;
+ }
+ if(dvi->params.vdrift < 0) {
+ ppi = dvi->params.vdpi / dvi->params.vshrink; /* shrunk pixels per inch */
+ if(ppi < 600)
+ dvi->params.vdrift = ppi / 100;
+ else if(ppi < 1200)
+ dvi->params.vdrift = ppi / 200;
+ else
+ dvi->params.vdrift = ppi / 400;
+ }
+
+ dvi->params.thinsp = FROUND(0.025 * dvi->params.dpi / dvi->params.conv);
+ dvi->params.vsmallsp = FROUND(0.025 * dvi->params.vdpi / dvi->params.vconv);
+
+ /* execute all the commands in the page */
+ while((op = duget1(dvi)) != DVI_EOP) {
+ if(dvi_commands[op](dvi, op) < 0)
+ break;
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+ if(op != DVI_EOP)
+ return -1;
+ if(dvi->stacktop)
+ dviwarn(dvi, _("stack not empty at end of page\n"));
+ return 0;
+}
+
+static int inline move_vertical(DviContext *dvi, int amount)
+{
+ int rvv;
+
+ dvi->pos.v += amount;
+ rvv = vpixel_round(dvi, dvi->pos.v);
+ if(!dvi->params.vdrift)
+ return rvv;
+ if(amount > dvi->params.vsmallsp || amount <= -dvi->params.vsmallsp)
+ return rvv;
+ else {
+ int newvv;
+
+ newvv = dvi->pos.vv + vpixel_round(dvi, amount);
+ if(rvv - newvv > dvi->params.vdrift)
+ return rvv - dvi->params.vdrift;
+ else if(newvv - rvv > dvi->params.vdrift)
+ return rvv + dvi->params.vdrift;
+ else
+ return newvv;
+ }
+}
+
+static int inline move_horizontal(DviContext *dvi, int amount)
+{
+ int rhh;
+
+ dvi->pos.h += amount;
+ rhh = pixel_round(dvi, dvi->pos.h);
+ if(!dvi->params.hdrift)
+ return rhh;
+ else if(amount > dvi->params.thinsp || amount <= -6 * dvi->params.thinsp)
+ return rhh;
+ else {
+ int newhh;
+
+ newhh = dvi->pos.hh + pixel_round(dvi, amount);
+ if(rhh - newhh > dvi->params.hdrift)
+ return rhh - dvi->params.hdrift;
+ else if(newhh - rhh > dvi->params.hdrift)
+ return rhh + dvi->params.hdrift;
+ else
+ return newhh;
+ }
+}
+
+static void inline fix_after_horizontal(DviContext *dvi)
+{
+ int rhh;
+
+ rhh = pixel_round(dvi, dvi->pos.h);
+ if(!dvi->params.hdrift)
+ dvi->pos.hh = rhh;
+ else if(rhh - dvi->pos.hh > dvi->params.hdrift)
+ dvi->pos.hh = rhh - dvi->params.hdrift;
+ else if(dvi->pos.hh - rhh > dvi->params.hdrift)
+ dvi->pos.hh = rhh + dvi->params.hdrift;
+}
+
+/* commands */
+
+#define DBGSUM(a,b,c) \
+ (a), (b) > 0 ? '+' : '-', \
+ (b) > 0 ? (b) : -(b), (c)
+
+/*
+ * Draw rules with some sort of antialias support. Usefult for high-rate
+ * scale factors.
+ */
+
+static void draw_shrink_rule (DviContext *dvi, int x, int y, Uint w, Uint h, int f)
+{
+ int hs, vs, npixels;
+ Ulong fg, bg;
+ Ulong *pixels;
+
+ hs = dvi->params.hshrink;
+ vs = dvi->params.vshrink;
+ fg = dvi->curr_fg;
+ bg = dvi->curr_bg;
+
+ if (MDVI_ENABLED(dvi, MDVI_PARAM_ANTIALIASED)) {
+ npixels = vs * hs + 1;
+ pixels = get_color_table(&dvi->device, npixels, bg, fg,
+ dvi->params.gamma, dvi->params.density);
+
+ if (pixels) {
+ int color;
+
+ /* Lines with width 1 should be perfectly visible
+ * in shrink about 15. That is the reason of constant
+ */
+
+ color = (pow (vs / h * hs, 2) + pow (hs / w * vs, 2)) / 225;
+ if (color < npixels) {
+ fg = pixels[color];
+ } else {
+ fg = pixels[npixels - 1];
+ }
+ }
+ }
+
+ mdvi_push_color (dvi, fg, bg);
+ dvi->device.draw_rule(dvi, x, y, w, h, f);
+ mdvi_pop_color (dvi);
+
+ return;
+}
+
+/*
+ * The only commands that actually draw something are:
+ * set_char, set_rule
+ */
+
+static void draw_box(DviContext *dvi, DviFontChar *ch)
+{
+ DviGlyph *glyph = NULL;
+ int x, y, w, h;
+
+ if(!MDVI_GLYPH_UNSET(ch->shrunk.data))
+ glyph = &ch->shrunk;
+ else if(!MDVI_GLYPH_UNSET(ch->grey.data))
+ glyph = &ch->grey;
+ else if(!MDVI_GLYPH_UNSET(ch->glyph.data))
+ glyph = &ch->glyph;
+ if(glyph == NULL)
+ return;
+ x = glyph->x;
+ y = glyph->y;
+ w = glyph->w;
+ h = glyph->h;
+ /* this is bad -- we have to undo the orientation */
+ switch(dvi->params.orientation) {
+ case MDVI_ORIENT_TBLR:
+ break;
+ case MDVI_ORIENT_TBRL:
+ x = w - x;
+ break;
+ case MDVI_ORIENT_BTLR:
+ y = h - y;
+ break;
+ case MDVI_ORIENT_BTRL:
+ x = w - x;
+ y = h - y;
+ break;
+ case MDVI_ORIENT_RP90:
+ SWAPINT(w, h);
+ SWAPINT(x, y);
+ x = w - x;
+ break;
+ case MDVI_ORIENT_RM90:
+ SWAPINT(w, h);
+ SWAPINT(x, y);
+ y = h - y;
+ break;
+ case MDVI_ORIENT_IRP90:
+ SWAPINT(w, h);
+ SWAPINT(x, y);
+ break;
+ case MDVI_ORIENT_IRM90:
+ SWAPINT(w, h);
+ SWAPINT(x, y);
+ x = w - x;
+ y = h - y;
+ break;
+ }
+
+ draw_shrink_rule(dvi, dvi->pos.hh - x, dvi->pos.vv - y, w, h, 1);
+}
+
+int set_char(DviContext *dvi, int opcode)
+{
+ int num;
+ int h;
+ int hh;
+ DviFontChar *ch;
+ DviFont *font;
+
+ if(opcode < 128)
+ num = opcode;
+ else
+ num = dugetn(dvi, opcode - DVI_SET1 + 1);
+ if(dvi->currfont == NULL) {
+ dvierr(dvi, _("no default font set yet\n"));
+ return -1;
+ }
+ font = dvi->currfont->ref;
+ ch = font_get_glyph(dvi, font, num);
+ if(ch == NULL || ch->missing) {
+ /* try to display something anyway */
+ ch = FONTCHAR(font, num);
+ if(!glyph_present(ch)) {
+ dviwarn(dvi,
+ _("requested character %d does not exist in `%s'\n"),
+ num, font->fontname);
+ return 0;
+ }
+ draw_box(dvi, ch);
+ } else if(dvi->curr_layer <= dvi->params.layer) {
+ if(ISVIRTUAL(font))
+ mdvi_run_macro(dvi, (Uchar *)font->private +
+ ch->offset, ch->width);
+ else if(ch->width && ch->height)
+ dvi->device.draw_glyph(dvi, ch,
+ dvi->pos.hh, dvi->pos.vv);
+ }
+ if(opcode >= DVI_PUT1 && opcode <= DVI_PUT4) {
+ SHOWCMD((dvi, "putchar", opcode - DVI_PUT1 + 1,
+ "char %d (%s)\n",
+ num, dvi->currfont->ref->fontname));
+ } else {
+ h = dvi->pos.h + ch->tfmwidth;
+ hh = dvi->pos.hh + pixel_round(dvi, ch->tfmwidth);
+ SHOWCMD((dvi, "setchar", num, "(%d,%d) h:=%d%c%d=%d, hh:=%d (%s)\n",
+ dvi->pos.hh, dvi->pos.vv,
+ DBGSUM(dvi->pos.h, ch->tfmwidth, h), hh,
+ font->fontname));
+ dvi->pos.h = h;
+ dvi->pos.hh = hh;
+ fix_after_horizontal(dvi);
+ }
+
+ return 0;
+}
+
+int set_rule(DviContext *dvi, int opcode)
+{
+ Int32 a, b;
+ int h, w;
+
+ a = dsget4(dvi);
+ b = dsget4(dvi); w = rule_round(dvi, b);
+ if(a > 0 && b > 0) {
+ h = vrule_round(dvi, a);
+ SHOWCMD((dvi, opcode == DVI_SET_RULE ? "setrule" : "putrule", -1,
+ "width %d, height %d (%dx%d pixels)\n",
+ b, a, w, h));
+ /* the `draw' functions expect the origin to be at the top left
+ * corner of the rule, not the bottom left, as in DVI files */
+ if(dvi->curr_layer <= dvi->params.layer) {
+ draw_shrink_rule(dvi,
+ dvi->pos.hh, dvi->pos.vv - h + 1, w, h, 1);
+ }
+ } else {
+ SHOWCMD((dvi, opcode == DVI_SET_RULE ? "setrule" : "putrule", -1,
+ "(moving left only, by %d)\n", b));
+ }
+
+ if(opcode == DVI_SET_RULE) {
+ dvi->pos.h += b;
+ dvi->pos.hh += w;
+ fix_after_horizontal(dvi);
+ }
+ return 0;
+}
+
+int no_op(DviContext *dvi, int opcode)
+{
+ SHOWCMD((dvi, "noop", -1, ""));
+ return 0;
+}
+
+int push(DviContext *dvi, int opcode)
+{
+ if(dvi->stacktop == dvi->stacksize) {
+ if(!dvi->depth)
+ dviwarn(dvi, _("enlarging stack\n"));
+ dvi->stacksize += 8;
+ dvi->stack = xresize(dvi->stack,
+ DviState, dvi->stacksize);
+ }
+ memcpy(&dvi->stack[dvi->stacktop], &dvi->pos, sizeof(DviState));
+ SHOWCMD((dvi, "push", -1,
+ "level %d: (h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=%d,vv=%d)\n",
+ dvi->stacktop,
+ dvi->pos.h, dvi->pos.v, dvi->pos.w, dvi->pos.x,
+ dvi->pos.y, dvi->pos.z, dvi->pos.hh, dvi->pos.vv));
+ dvi->stacktop++;
+ return 0;
+}
+
+int pop(DviContext *dvi, int opcode)
+{
+ if(dvi->stacktop == 0) {
+ dvierr(dvi, _("stack underflow\n"));
+ return -1;
+ }
+ memcpy(&dvi->pos, &dvi->stack[dvi->stacktop-1], sizeof(DviState));
+ SHOWCMD((dvi, "pop", -1,
+ "level %d: (h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=%d,vv=%d)\n",
+ dvi->stacktop,
+ dvi->pos.h, dvi->pos.v, dvi->pos.w, dvi->pos.x,
+ dvi->pos.y, dvi->pos.z, dvi->pos.hh, dvi->pos.vv));
+ dvi->stacktop--;
+ return 0;
+}
+
+int move_right(DviContext *dvi, int opcode)
+{
+ Int32 arg;
+ int h, hh;
+
+ arg = dsgetn(dvi, opcode - DVI_RIGHT1 + 1);
+ h = dvi->pos.h;
+ hh = move_horizontal(dvi, arg);
+ SHOWCMD((dvi, "right", opcode - DVI_RIGHT1 + 1,
+ "%d h:=%d%c%d=%d, hh:=%d\n",
+ arg, DBGSUM(h, arg, dvi->pos.h), hh));
+ dvi->pos.hh = hh;
+ return 0;
+}
+
+int move_down(DviContext *dvi, int opcode)
+{
+ Int32 arg;
+ int v, vv;
+
+ arg = dsgetn(dvi, opcode - DVI_DOWN1 + 1);
+ v = dvi->pos.v;
+ vv = move_vertical(dvi, arg);
+ SHOWCMD((dvi, "down", opcode - DVI_DOWN1 + 1,
+ "%d v:=%d%c%d=%d, vv:=%d\n",
+ arg, DBGSUM(v, arg, dvi->pos.v), vv));
+ dvi->pos.vv = vv;
+ return 0;
+}
+
+int move_w(DviContext *dvi, int opcode)
+{
+ int h, hh;
+
+ if(opcode != DVI_W0)
+ dvi->pos.w = dsgetn(dvi, opcode - DVI_W0);
+ h = dvi->pos.h;
+ hh = move_horizontal(dvi, dvi->pos.w);
+ SHOWCMD((dvi, "w", opcode - DVI_W0,
+ "%d h:=%d%c%d=%d, hh:=%d\n",
+ dvi->pos.w, DBGSUM(h, dvi->pos.w, dvi->pos.h), hh));
+ dvi->pos.hh = hh;
+ return 0;
+}
+
+int move_x(DviContext *dvi, int opcode)
+{
+ int h, hh;
+
+ if(opcode != DVI_X0)
+ dvi->pos.x = dsgetn(dvi, opcode - DVI_X0);
+ h = dvi->pos.h;
+ hh = move_horizontal(dvi, dvi->pos.x);
+ SHOWCMD((dvi, "x", opcode - DVI_X0,
+ "%d h:=%d%c%d=%d, hh:=%d\n",
+ dvi->pos.x, DBGSUM(h, dvi->pos.x, dvi->pos.h), hh));
+ dvi->pos.hh = hh;
+ return 0;
+}
+
+int move_y(DviContext *dvi, int opcode)
+{
+ int v, vv;
+
+ if(opcode != DVI_Y0)
+ dvi->pos.y = dsgetn(dvi, opcode - DVI_Y0);
+ v = dvi->pos.v;
+ vv = move_vertical(dvi, dvi->pos.y);
+ SHOWCMD((dvi, "y", opcode - DVI_Y0,
+ "%d h:=%d%c%d=%d, hh:=%d\n",
+ dvi->pos.y, DBGSUM(v, dvi->pos.y, dvi->pos.v), vv));
+ dvi->pos.vv = vv;
+ return 0;
+}
+
+int move_z(DviContext *dvi, int opcode)
+{
+ int v, vv;
+
+ if(opcode != DVI_Z0)
+ dvi->pos.z = dsgetn(dvi, opcode - DVI_Z0);
+ v = dvi->pos.v;
+ vv = move_vertical(dvi, dvi->pos.z);
+ SHOWCMD((dvi, "z", opcode - DVI_Z0,
+ "%d h:=%d%c%d=%d, hh:=%d\n",
+ dvi->pos.z, DBGSUM(v, dvi->pos.z, dvi->pos.v), vv));
+ dvi->pos.vv = vv;
+ return 0;
+}
+
+int sel_font(DviContext *dvi, int opcode)
+{
+ DviFontRef *ref;
+ int ndx;
+
+ ndx = opcode - DVI_FNT_NUM0;
+ if(dvi->depth)
+ ref = font_find_flat(dvi, ndx);
+ else
+ ref = dvi->findref(dvi, ndx);
+ if(ref == NULL) {
+ dvierr(dvi, _("font %d is not defined\n"),
+ opcode - DVI_FNT_NUM0);
+ return -1;
+ }
+ SHOWCMD((dvi, "fntnum", opcode - DVI_FNT_NUM0,
+ "current font is %s\n",
+ ref->ref->fontname));
+ dvi->currfont = ref;
+ return 0;
+}
+
+int sel_fontn(DviContext *dvi, int opcode)
+{
+ Int32 arg;
+ DviFontRef *ref;
+
+ arg = dugetn(dvi, opcode - DVI_FNT1 + 1);
+ if(dvi->depth)
+ ref = font_find_flat(dvi, arg);
+ else
+ ref = dvi->findref(dvi, arg);
+ if(ref == NULL) {
+ dvierr(dvi, _("font %d is not defined\n"), arg);
+ return -1;
+ }
+ SHOWCMD((dvi, "fnt", opcode - DVI_FNT1 + 1,
+ "current font is %s (id %d)\n",
+ ref->ref->fontname, arg));
+ dvi->currfont = ref;
+ return 0;
+}
+
+int special(DviContext *dvi, int opcode)
+{
+ char *s;
+ Int32 arg;
+
+ arg = dugetn(dvi, opcode - DVI_XXX1 + 1);
+ s = mdvi_malloc(arg + 1);
+ dread(dvi, s, arg);
+ s[arg] = 0;
+ mdvi_do_special(dvi, s);
+ SHOWCMD((dvi, "XXXX", opcode - DVI_XXX1 + 1,
+ "[%s]", s));
+ mdvi_free(s);
+ return 0;
+}
+
+int def_font(DviContext *dvi, int opcode)
+{
+ DviFontRef *ref;
+ Int32 arg;
+
+ arg = dugetn(dvi, opcode - DVI_FNT_DEF1 + 1);
+ if(dvi->depth)
+ ref = font_find_flat(dvi, arg);
+ else
+ ref = dvi->findref(dvi, arg);
+ /* skip the rest */
+ dskip(dvi, 12);
+ dskip(dvi, duget1(dvi) + duget1(dvi));
+ if(ref == NULL) {
+ dvierr(dvi, _("font %d is not defined in postamble\n"), arg);
+ return -1;
+ }
+ SHOWCMD((dvi, "fntdef", opcode - DVI_FNT_DEF1 + 1,
+ "%d -> %s (%d links)\n",
+ ref->fontid, ref->ref->fontname,
+ ref->ref->links));
+ return 0;
+}
+
+int unexpected(DviContext *dvi, int opcode)
+{
+ dvierr(dvi, _("unexpected opcode %d\n"), opcode);
+ return -1;
+}
+
+int undefined(DviContext *dvi, int opcode)
+{
+ dvierr(dvi, _("undefined opcode %d\n"), opcode);
+ return -1;
+}
+
diff --git a/backend/dvi/mdvi-lib/files.c b/backend/dvi/mdvi-lib/files.c
new file mode 100644
index 00000000..b7065068
--- /dev/null
+++ b/backend/dvi/mdvi-lib/files.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "common.h"
+
+char *dgets(Dstring *dstr, FILE *in)
+{
+ char buffer[256];
+
+ dstr->length = 0;
+ if(feof(in))
+ return NULL;
+ while(fgets(buffer, 256, in) != NULL) {
+ int len = strlen(buffer);
+
+ if(buffer[len-1] == '\n') {
+ dstring_append(dstr, buffer, len - 1);
+ break;
+ }
+ dstring_append(dstr, buffer, len);
+ }
+ if(dstr->data)
+ dstr->data[dstr->length] = 0;
+ return dstr->data;
+}
+
+/* some simple helper functions to manipulate file names */
+
+const char *file_basename(const char *filename)
+{
+ const char *ptr = strrchr(filename, '/');
+
+ return (ptr ? ptr + 1 : filename);
+}
+
+const char *file_extension(const char *filename)
+{
+ const char *ptr = strchr(file_basename(filename), '.');
+
+ return (ptr ? ptr + 1 : NULL);
+}
+
+int file_readable(const char *filename)
+{
+ int status = (access(filename, R_OK) == 0);
+
+ DEBUG((DBG_FILES, "file_redable(%s) -> %s\n",
+ filename, status ? "Yes" : "No"));
+ return status;
+}
+
+int file_exists(const char *filename)
+{
+ int status = (access(filename, F_OK) == 0);
+
+ DEBUG((DBG_FILES, "file_exists(%s) -> %s\n",
+ filename, status ? "Yes" : "No"));
+ return status;
+}
+
diff --git a/backend/dvi/mdvi-lib/font.c b/backend/dvi/mdvi-lib/font.c
new file mode 100644
index 00000000..2f655df0
--- /dev/null
+++ b/backend/dvi/mdvi-lib/font.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+static ListHead fontlist;
+
+extern char *_mdvi_fallback_font;
+
+extern void vf_free_macros(DviFont *);
+
+#define finfo search.info
+#define TYPENAME(font) \
+ ((font)->finfo ? (font)->finfo->name : "none")
+
+int font_reopen(DviFont *font)
+{
+ if(font->in)
+ fseek(font->in, (long)0, SEEK_SET);
+ else if((font->in = fopen(font->filename, "rb")) == NULL) {
+ DEBUG((DBG_FILES, "reopen(%s) -> Error\n", font->filename));
+ return -1;
+ }
+ DEBUG((DBG_FILES, "reopen(%s) -> Ok.\n", font->filename));
+ return 0;
+}
+
+/* used from context: params and device */
+static int load_font_file(DviParams *params, DviFont *font)
+{
+ int status;
+
+ if(SEARCH_DONE(font->search))
+ return -1;
+ if(font->in == NULL && font_reopen(font) < 0)
+ return -1;
+ DEBUG((DBG_FONTS, "%s: loading %s font from `%s'\n",
+ font->fontname,
+ font->finfo->name, font->filename));
+ do {
+ status = font->finfo->load(params, font);
+ } while(status < 0 && mdvi_font_retry(params, font) == 0);
+ if(status < 0)
+ return -1;
+ if(font->in) {
+ fclose(font->in);
+ font->in = NULL;
+ }
+ DEBUG((DBG_FONTS, "reload_font(%s) -> %s\n",
+ font->fontname, status < 0 ? "Error" : "Ok"));
+ return 0;
+}
+
+void font_drop_one(DviFontRef *ref)
+{
+ DviFont *font;
+
+ font = ref->ref;
+ mdvi_free(ref);
+ /* drop all children */
+ for(ref = font->subfonts; ref; ref = ref->next) {
+ /* just adjust the reference counts */
+ ref->ref->links--;
+ }
+ if(--font->links == 0) {
+ /*
+ * this font doesn't have any more references, but
+ * we still keep it around in case a virtual font
+ * requests it.
+ */
+ if(font->in) {
+ fclose(font->in);
+ font->in = NULL;
+ }
+ if(LIST(font) != fontlist.tail) {
+ /* move it to the end of the list */
+ listh_remove(&fontlist, LIST(font));
+ listh_append(&fontlist, LIST(font));
+ }
+ }
+ DEBUG((DBG_FONTS, "%s: reference dropped, %d more left\n",
+ font->fontname, font->links));
+}
+
+void font_drop_chain(DviFontRef *head)
+{
+ DviFontRef *ptr;
+
+ for(; (ptr = head); ) {
+ head = ptr->next;
+ font_drop_one(ptr);
+ }
+}
+
+int font_free_unused(DviDevice *dev)
+{
+ DviFont *font, *next;
+ int count = 0;
+
+ DEBUG((DBG_FONTS, "destroying unused fonts\n"));
+ for(font = (DviFont *)fontlist.head; font; font = next) {
+ DviFontRef *ref;
+
+ next = font->next;
+ if(font->links)
+ continue;
+ count++;
+ DEBUG((DBG_FONTS, "removing unused %s font `%s'\n",
+ TYPENAME(font), font->fontname));
+ listh_remove(&fontlist, LIST(font));
+ if(font->in)
+ fclose(font->in);
+ /* get rid of subfonts (but can't use `drop_chain' here) */
+ for(; (ref = font->subfonts); ) {
+ font->subfonts = ref->next;
+ mdvi_free(ref);
+ }
+ /* remove this font */
+ font_reset_font_glyphs(dev, font, MDVI_FONTSEL_GLYPH);
+ /* let the font destroy its private data */
+ if(font->finfo->freedata)
+ font->finfo->freedata(font);
+ /* destroy characters */
+ if(font->chars)
+ mdvi_free(font->chars);
+ mdvi_free(font->fontname);
+ mdvi_free(font->filename);
+ mdvi_free(font);
+ }
+ DEBUG((DBG_FONTS, "%d unused fonts removed\n", count));
+ return count;
+}
+
+/* used from context: params and device */
+DviFontRef *
+font_reference(
+ DviParams *params, /* rendering parameters */
+ Int32 id, /* external id number */
+ const char *name, /* font name */
+ Int32 sum, /* checksum (from DVI of VF) */
+ int hdpi, /* resolution */
+ int vdpi,
+ Int32 scale) /* scaling factor (from DVI or VF) */
+{
+ DviFont *font;
+ DviFontRef *ref;
+ DviFontRef *subfont_ref;
+
+ /* see if there is a font with the same characteristics */
+ for(font = (DviFont *)fontlist.head; font; font = font->next) {
+ if(strcmp(name, font->fontname) == 0
+ && (!sum || !font->checksum || font->checksum == sum)
+ && font->hdpi == hdpi
+ && font->vdpi == vdpi
+ && font->scale == scale)
+ break;
+ }
+ /* try to load the font */
+ if(font == NULL) {
+ font = mdvi_add_font(name, sum, hdpi, vdpi, scale);
+ if(font == NULL)
+ return NULL;
+ listh_append(&fontlist, LIST(font));
+ }
+ if(!font->links && !font->chars && load_font_file(params, font) < 0) {
+ DEBUG((DBG_FONTS, "font_reference(%s) -> Error\n", name));
+ return NULL;
+ }
+ ref = xalloc(DviFontRef);
+ ref->ref = font;
+
+ font->links++;
+ for(subfont_ref = font->subfonts; subfont_ref; subfont_ref = subfont_ref->next) {
+ /* just adjust the reference counts */
+ subfont_ref->ref->links++;
+ }
+
+ ref->fontid = id;
+
+ if(LIST(font) != fontlist.head) {
+ listh_remove(&fontlist, LIST(font));
+ listh_prepend(&fontlist, LIST(font));
+ }
+
+ DEBUG((DBG_FONTS, "font_reference(%s) -> %d links\n",
+ font->fontname, font->links));
+ return ref;
+}
+
+void font_transform_glyph(DviOrientation orient, DviGlyph *g)
+{
+ BITMAP *map;
+ int x, y;
+
+ map = (BITMAP *)g->data;
+ if(MDVI_GLYPH_ISEMPTY(map))
+ map = NULL;
+
+ /* put the glyph in the right orientation */
+ switch(orient) {
+ case MDVI_ORIENT_TBLR:
+ break;
+ case MDVI_ORIENT_TBRL:
+ g->x = g->w - g->x;
+ if(map) bitmap_flip_horizontally(map);
+ break;
+ case MDVI_ORIENT_BTLR:
+ g->y = g->h - g->y;
+ if(map) bitmap_flip_vertically(map);
+ break;
+ case MDVI_ORIENT_BTRL:
+ g->x = g->w - g->x;
+ g->y = g->h - g->y;
+ if(map) bitmap_flip_diagonally(map);
+ break;
+ case MDVI_ORIENT_RP90:
+ if(map) bitmap_rotate_counter_clockwise(map);
+ y = g->y;
+ x = g->w - g->x;
+ g->x = y;
+ g->y = x;
+ SWAPINT(g->w, g->h);
+ break;
+ case MDVI_ORIENT_RM90:
+ if(map) bitmap_rotate_clockwise(map);
+ y = g->h - g->y;
+ x = g->x;
+ g->x = y;
+ g->y = x;
+ SWAPINT(g->w, g->h);
+ break;
+ case MDVI_ORIENT_IRP90:
+ if(map) bitmap_flip_rotate_counter_clockwise(map);
+ y = g->y;
+ x = g->x;
+ g->x = y;
+ g->y = x;
+ SWAPINT(g->w, g->h);
+ break;
+ case MDVI_ORIENT_IRM90:
+ if(map) bitmap_flip_rotate_clockwise(map);
+ y = g->h - g->y;
+ x = g->w - g->x;
+ g->x = y;
+ g->y = x;
+ SWAPINT(g->w, g->h);
+ break;
+ }
+}
+
+static int load_one_glyph(DviContext *dvi, DviFont *font, int code)
+{
+ BITMAP *map;
+ DviFontChar *ch;
+ int status;
+
+#ifndef NODEBUG
+ ch = FONTCHAR(font, code);
+ DEBUG((DBG_GLYPHS, "loading glyph code %d in %s (at %u)\n",
+ code, font->fontname, ch->offset));
+#endif
+ if(font->finfo->getglyph == NULL) {
+ /* font type does not need to load glyphs (e.g. vf) */
+ return 0;
+ }
+
+ status = font->finfo->getglyph(&dvi->params, font, code);
+ if(status < 0)
+ return -1;
+ /* get the glyph again (font->chars may have changed) */
+ ch = FONTCHAR(font, code);
+#ifndef NODEBUG
+ map = (BITMAP *)ch->glyph.data;
+ if(DEBUGGING(BITMAP_DATA)) {
+ DEBUG((DBG_BITMAP_DATA,
+ "%s: new %s bitmap for character %d:\n",
+ font->fontname, TYPENAME(font), code));
+ if(MDVI_GLYPH_ISEMPTY(map))
+ DEBUG((DBG_BITMAP_DATA, "blank bitmap\n"));
+ else
+ bitmap_print(stderr, map);
+ }
+#endif
+ /* check if we have to scale it */
+ if(!font->finfo->scalable && font->hdpi != font->vdpi) {
+ int hs, vs, d;
+
+ /* we scale it ourselves */
+ d = Max(font->hdpi, font->vdpi);
+ hs = d / font->hdpi;
+ vs = d / font->vdpi;
+ if(ch->width && ch->height && (hs > 1 || vs > 1)) {
+ int h, v;
+ DviGlyph glyph;
+
+ DEBUG((DBG_FONTS,
+ "%s: scaling glyph %d to resolution %dx%d\n",
+ font->fontname, code, font->hdpi, font->vdpi));
+ h = dvi->params.hshrink;
+ v = dvi->params.vshrink;
+ d = dvi->params.density;
+ dvi->params.hshrink = hs;
+ dvi->params.vshrink = vs;
+ dvi->params.density = 50;
+ /* shrink it */
+ font->finfo->shrink0(dvi, font, ch, &glyph);
+ /* restore parameters */
+ dvi->params.hshrink = h;
+ dvi->params.vshrink = v;
+ dvi->params.density = d;
+ /* update glyph data */
+ if(!MDVI_GLYPH_ISEMPTY(ch->glyph.data))
+ bitmap_destroy((BITMAP *)ch->glyph.data);
+ ch->glyph.data = glyph.data;
+ ch->glyph.x = glyph.x;
+ ch->glyph.y = glyph.y;
+ ch->glyph.w = glyph.w;
+ ch->glyph.h = glyph.h;
+ }
+
+ }
+ font_transform_glyph(dvi->params.orientation, &ch->glyph);
+
+ return 0;
+}
+
+DviFontChar *font_get_glyph(DviContext *dvi, DviFont *font, int code)
+{
+ DviFontChar *ch;
+
+again:
+ /* if we have not loaded the font yet, do so now */
+ if(!font->chars && load_font_file(&dvi->params, font) < 0)
+ return NULL;
+
+ /* get the unscaled glyph, maybe loading it from disk */
+ ch = FONTCHAR(font, code);
+ if(!ch || !glyph_present(ch))
+ return NULL;
+ if(!ch->loaded && load_one_glyph(dvi, font, code) == -1) {
+ if(font->chars == NULL) {
+ /* we need to try another font class */
+ goto again;
+ }
+ return NULL;
+ }
+ /* yes, we have to do this again */
+ ch = FONTCHAR(font, code);
+
+ /* Got the glyph. If we also have the right scaled glyph, do no more */
+ if(!ch->width || !ch->height ||
+ font->finfo->getglyph == NULL ||
+ (dvi->params.hshrink == 1 && dvi->params.vshrink == 1))
+ return ch;
+
+ /* If the glyph is empty, we just need to shrink the box */
+ if(ch->missing || MDVI_GLYPH_ISEMPTY(ch->glyph.data)) {
+ if(MDVI_GLYPH_UNSET(ch->shrunk.data))
+ mdvi_shrink_box(dvi, font, ch, &ch->shrunk);
+ return ch;
+ } else if(MDVI_ENABLED(dvi, MDVI_PARAM_ANTIALIASED)) {
+ if(ch->grey.data &&
+ !MDVI_GLYPH_ISEMPTY(ch->grey.data) &&
+ ch->fg == dvi->curr_fg &&
+ ch->bg == dvi->curr_bg)
+ return ch;
+ if(ch->grey.data &&
+ !MDVI_GLYPH_ISEMPTY(ch->grey.data)) {
+ if(dvi->device.free_image)
+ dvi->device.free_image(ch->grey.data);
+ ch->grey.data = NULL;
+ }
+ font->finfo->shrink1(dvi, font, ch, &ch->grey);
+ } else if(!ch->shrunk.data)
+ font->finfo->shrink0(dvi, font, ch, &ch->shrunk);
+
+ return ch;
+}
+
+void font_reset_one_glyph(DviDevice *dev, DviFontChar *ch, int what)
+{
+ if(!glyph_present(ch))
+ return;
+ if(what & MDVI_FONTSEL_BITMAP) {
+ if(MDVI_GLYPH_NONEMPTY(ch->shrunk.data))
+ bitmap_destroy((BITMAP *)ch->shrunk.data);
+ ch->shrunk.data = NULL;
+ }
+ if(what & MDVI_FONTSEL_GREY) {
+ if(MDVI_GLYPH_NONEMPTY(ch->grey.data)) {
+ if(dev->free_image)
+ dev->free_image(ch->grey.data);
+ }
+ ch->grey.data = NULL;
+ }
+ if(what & MDVI_FONTSEL_GLYPH) {
+ if(MDVI_GLYPH_NONEMPTY(ch->glyph.data))
+ bitmap_destroy((BITMAP *)ch->glyph.data);
+ ch->glyph.data = NULL;
+ ch->loaded = 0;
+ }
+}
+
+void font_reset_font_glyphs(DviDevice *dev, DviFont *font, int what)
+{
+ int i;
+ DviFontChar *ch;
+
+ if(what & MDVI_FONTSEL_GLYPH)
+ what |= MDVI_FONTSEL_BITMAP|MDVI_FONTSEL_GREY;
+ if(font->subfonts) {
+ DviFontRef *ref;
+
+ for(ref = font->subfonts; ref; ref = ref->next)
+ font_reset_font_glyphs(dev, ref->ref, what);
+ }
+ if(font->in) {
+ DEBUG((DBG_FILES, "close(%s)\n", font->filename));
+ fclose(font->in);
+ font->in = NULL;
+ }
+ if(font->finfo->getglyph == NULL)
+ return;
+ DEBUG((DBG_FONTS, "resetting glyphs in font `%s'\n", font->fontname));
+ for(ch = font->chars, i = font->loc; i <= font->hic; ch++, i++) {
+ if(glyph_present(ch))
+ font_reset_one_glyph(dev, ch, what);
+ }
+ if((what & MDVI_FONTSEL_GLYPH) && font->finfo->reset)
+ font->finfo->reset(font);
+}
+
+void font_reset_chain_glyphs(DviDevice *dev, DviFontRef *head, int what)
+{
+ DviFontRef *ref;
+
+ for(ref = head; ref; ref = ref->next)
+ font_reset_font_glyphs(dev, ref->ref, what);
+}
+
+static int compare_refs(const void *p1, const void *p2)
+{
+ return ((*(DviFontRef **)p1)->fontid - (*(DviFontRef **)p2)->fontid);
+}
+
+void font_finish_definitions(DviContext *dvi)
+{
+ int count;
+ DviFontRef **map, *ref;
+
+ /* first get rid of unused fonts */
+ font_free_unused(&dvi->device);
+
+ if(dvi->fonts == NULL) {
+ mdvi_warning(_("%s: no fonts defined\n"), dvi->filename);
+ return;
+ }
+ map = xnalloc(DviFontRef *, dvi->nfonts);
+ for(count = 0, ref = dvi->fonts; ref; ref = ref->next)
+ map[count++] = ref;
+ /* sort the array by font id */
+ qsort(map, dvi->nfonts, sizeof(DviFontRef *), compare_refs);
+ dvi->fontmap = map;
+}
+
+DviFontRef *font_find_flat(DviContext *dvi, Int32 id)
+{
+ DviFontRef *ref;
+
+ for(ref = dvi->fonts; ref; ref = ref->next)
+ if(ref->fontid == id)
+ break;
+ return ref;
+}
+
+DviFontRef *font_find_mapped(DviContext *dvi, Int32 id)
+{
+ int lo, hi, n;
+ DviFontRef **map;
+
+ /* do a binary search */
+ lo = 0; hi = dvi->nfonts;
+ map = dvi->fontmap;
+ while(lo < hi) {
+ int sign;
+
+ n = (hi + lo) >> 1;
+ sign = (map[n]->fontid - id);
+ if(sign == 0)
+ break;
+ else if(sign < 0)
+ lo = n;
+ else
+ hi = n;
+ }
+ if(lo >= hi)
+ return NULL;
+ return map[n];
+}
+
diff --git a/backend/dvi/mdvi-lib/fontmap.c b/backend/dvi/mdvi-lib/fontmap.c
new file mode 100644
index 00000000..c3c3a8d3
--- /dev/null
+++ b/backend/dvi/mdvi-lib/fontmap.c
@@ -0,0 +1,1174 @@
+/* encoding.c - functions to manipulate encodings and fontmaps */
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+#include <kpathsea/expand.h>
+#include <kpathsea/pathsearch.h>
+
+typedef struct _DviFontMap DviFontMap;
+
+struct _DviFontMap {
+ ListHead entries;
+ DviHashTable fonts;
+};
+
+typedef struct _PSFontMap {
+ struct _PSFontMap *next;
+ struct _PSFontMap *prev;
+ char *psname;
+ char *mapname;
+ char *fullname;
+} PSFontMap;
+
+/* these variables control PS font maps */
+static char *pslibdir = NULL; /* path where we look for PS font maps */
+static char *psfontdir = NULL; /* PS font search path */
+static int psinitialized = 0; /* did we expand the path already? */
+
+static ListHead psfonts = MDVI_EMPTY_LIST_HEAD;
+static DviHashTable pstable = MDVI_EMPTY_HASH_TABLE;
+
+static ListHead fontmaps;
+static DviHashTable maptable;
+static int fontmaps_loaded = 0;
+
+#define MAP_HASH_SIZE 57
+#define ENC_HASH_SIZE 31
+#define PSMAP_HASH_SIZE 57
+
+/* this hash table should be big enough to
+ * hold (ideally) one glyph name per bucket */
+#define ENCNAME_HASH_SIZE 131 /* most TeX fonts have 128 glyphs */
+
+static ListHead encodings = MDVI_EMPTY_LIST_HEAD;
+static DviEncoding *tex_text_encoding = NULL;
+static DviEncoding *default_encoding = NULL;
+
+/* we keep two hash tables for encodings: one for their base files (e.g.
+ * "8r.enc"), and another one for their names (e.g. "TeXBase1Encoding") */
+static DviHashTable enctable = MDVI_EMPTY_HASH_TABLE;
+static DviHashTable enctable_file = MDVI_EMPTY_HASH_TABLE;
+
+/* the TeX text encoding, from dvips */
+static char *tex_text_vector[256] = {
+ "Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon",
+ "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle",
+ "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave",
+ "acute", "caron", "breve", "macron", "ring", "cedilla",
+ "germandbls", "ae", "oe", "oslash", "AE", "OE", "Oslash", "space",
+ "exclam", "quotedbl", "numbersign", "dollar", "percent",
+ "ampersand", "quoteright", "parenleft", "parenright", "asterisk",
+ "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
+ "three", "four", "five", "six", "seven", "eight", "nine", "colon",
+ "semicolon", "less", "equal", "greater", "question", "at", "A", "B",
+ "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
+ "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+ "bracketleft", "backslash", "bracketright", "circumflex",
+ "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h",
+ "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
+ "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "tilde",
+ "dieresis", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void ps_init_default_paths __PROTO((void));
+static int mdvi_set_default_encoding __PROTO((const char *name));
+static int mdvi_init_fontmaps __PROTO((void));
+
+/*
+ * What we do here is allocate one block large enough to hold the entire
+ * file (these files are small) minus the leading comments. This is much
+ * better than allocating up to 256 tiny strings per encoding vector. */
+static int read_encoding(DviEncoding *enc)
+{
+ FILE *in;
+ int curr;
+ char *line;
+ char *name;
+ char *next;
+ struct stat st;
+
+ ASSERT(enc->private == NULL);
+
+ in = fopen(enc->filename, "rb");
+ if(in == NULL) {
+ DEBUG((DBG_FMAP, "%s: could not read `%s' (%s)\n",
+ enc->name, enc->filename, strerror(errno)));
+ return -1;
+ }
+ if(fstat(fileno(in), &st) < 0) {
+ /* should not happen */
+ fclose(in);
+ return -1;
+ }
+ st.st_size -= enc->offset;
+
+ /* this will be one big string */
+ enc->private = (char *)malloc(st.st_size + 1);
+ /* setup the hash table */
+ mdvi_hash_create(&enc->nametab, ENCNAME_HASH_SIZE);
+ /* setup the encoding vector */
+ enc->vector = (char **)mdvi_malloc(256 * sizeof(char *));
+
+ /* jump to the beginning of the interesting part */
+ fseek(in, enc->offset, SEEK_SET);
+ /* and read everything */
+ if(fread(enc->private, st.st_size, 1, in) != 1) {
+ fclose(in);
+ mdvi_free(enc->private);
+ enc->private = NULL;
+ return -1;
+ }
+ /* we don't need this anymore */
+ fclose(in);
+ curr = 0;
+
+ next = name = NULL;
+ DEBUG((DBG_FMAP, "%s: reading encoding vector\n", enc->name));
+ for(line = enc->private; *line && curr < 256; line = next) {
+ SKIPSP(line);
+ if(*line == ']') {
+ line++; SKIPSP(line);
+ if(STRNEQ(line, "def", 3))
+ break;
+ }
+ name = getword(line, " \t\n", &next);
+ if(name == NULL)
+ break;
+ /* next > line */
+ if(*name < ' ')
+ continue;
+ if(*name == '%') {
+ while(*next && *next != '\n')
+ next++;
+ if(*next) next++; /* skip \n */
+ continue;
+ }
+
+ /* got a name */
+ if(*next) *next++ = 0;
+
+ if(*name == '/')
+ name++;
+ enc->vector[curr] = name;
+ /* add it to the hash table */
+ if(!STREQ(name, ".notdef")) {
+ mdvi_hash_add(&enc->nametab, MDVI_KEY(name),
+ Int2Ptr(curr + 1), MDVI_HASH_REPLACE);
+ }
+ curr++;
+ }
+ if(curr == 0) {
+ mdvi_hash_reset(&enc->nametab, 0);
+ mdvi_free(enc->private);
+ mdvi_free(enc);
+ return -1;
+ }
+ while(curr < 256)
+ enc->vector[curr++] = NULL;
+ return 0;
+}
+
+static DviEncoding *find_encoding(const char *name)
+{
+ return (DviEncoding *)(encodings.count ?
+ mdvi_hash_lookup(&enctable, MDVI_KEY(name)) : NULL);
+}
+
+static void destroy_encoding(DviEncoding *enc)
+{
+ if(enc == default_encoding) {
+ default_encoding = tex_text_encoding;
+ /* now we use reference counts again */
+ mdvi_release_encoding(enc, 1);
+ }
+ if(enc != tex_text_encoding) {
+ mdvi_hash_reset(&enc->nametab, 0);
+ if(enc->private) {
+ mdvi_free(enc->private);
+ mdvi_free(enc->vector);
+ }
+ if(enc->name)
+ mdvi_free(enc->name);
+ if(enc->filename)
+ mdvi_free(enc->filename);
+ mdvi_free(enc);
+ }
+}
+
+/* this is used for the `enctable_file' hash table */
+static void file_hash_free(DviHashKey key, void *data)
+{
+ mdvi_free(key);
+}
+
+static DviEncoding *register_encoding(const char *basefile, int replace)
+{
+ DviEncoding *enc;
+ FILE *in;
+ char *filename;
+ char *name;
+ Dstring input;
+ char *line;
+ long offset;
+
+ DEBUG((DBG_FMAP, "register_encoding(%s)\n", basefile));
+
+ if(encodings.count) {
+ enc = mdvi_hash_lookup(&enctable_file, MDVI_KEY(basefile));
+ if(enc != NULL) {
+ DEBUG((DBG_FMAP, "%s: already there\n", basefile));
+ return enc; /* no error */
+ }
+ }
+
+ /* try our own files first */
+ filename = kpse_find_file(basefile,
+ kpse_program_text_format, 0);
+
+ /* then try the system-wide ones */
+ if(filename == NULL)
+ filename = kpse_find_file(basefile,
+ kpse_tex_ps_header_format, 0);
+ if(filename == NULL)
+ filename = kpse_find_file(basefile,
+ kpse_dvips_config_format, 0);
+
+ /* finally try the given name */
+ if(filename == NULL)
+ filename = mdvi_strdup(basefile);
+
+ in = fopen(filename, "rb");
+ if(in == NULL) {
+ mdvi_free(filename);
+ return NULL;
+ }
+
+ /* just lookup the name of the encoding */
+ name = NULL;
+ dstring_init(&input);
+ while((line = dgets(&input, in)) != NULL) {
+ if(STRNEQ(line, "Encoding=", 9)) {
+ name = getword(line + 9, " \t", &line);
+ if(*line) *line++ = 0;
+ break;
+ } else if(*line == '/') {
+ char *label = getword(line + 1, " \t", &line);
+ if(*line) {
+ *line++ = 0;
+ SKIPSP(line);
+ if(*line == '[') {
+ *line = 0;
+ name = label;
+ break;
+ }
+ }
+ }
+ }
+ offset = ftell(in);
+ fclose(in);
+ if(name == NULL || *name == 0) {
+ DEBUG((DBG_FMAP,
+ "%s: could not determine name of encoding\n",
+ basefile));
+ mdvi_free(filename);
+ return NULL;
+ }
+
+ /* check if the encoding is already there */
+ enc = find_encoding(name);
+ if(enc == tex_text_encoding) {
+ /* A special case: if the vector we found is the static one,
+ * allow the user to override it with an external file */
+ listh_remove(&encodings, LIST(enc));
+ mdvi_hash_remove(&enctable, MDVI_KEY(enc->name));
+ if(enc == default_encoding)
+ default_encoding = NULL;
+ } else if(enc) {
+ /* if the encoding is being used, refuse to remove it */
+ if(enc->links) {
+ mdvi_free(filename);
+ dstring_reset(&input);
+ return NULL;
+ }
+ if(replace) {
+ mdvi_hash_remove(&enctable, MDVI_KEY(name));
+ mdvi_hash_remove(&enctable_file, MDVI_KEY(basefile));
+ listh_remove(&encodings, LIST(enc));
+ if(enc == default_encoding) {
+ default_encoding = NULL;
+ mdvi_release_encoding(enc, 1);
+ }
+ DEBUG((DBG_FMAP, "%s: overriding encoding\n", name));
+ destroy_encoding(enc);
+ } else {
+ mdvi_free(filename);
+ dstring_reset(&input);
+ return enc; /* no error */
+ }
+ }
+ enc = xalloc(DviEncoding);
+ enc->name = mdvi_strdup(name);
+ enc->filename = filename;
+ enc->links = 0;
+ enc->offset = offset;
+ enc->private = NULL;
+ enc->vector = NULL;
+ mdvi_hash_init(&enc->nametab);
+ dstring_reset(&input);
+ if(default_encoding == NULL)
+ default_encoding = enc;
+ mdvi_hash_add(&enctable, MDVI_KEY(enc->name),
+ enc, MDVI_HASH_UNCHECKED);
+ mdvi_hash_add(&enctable_file, MDVI_KEY(mdvi_strdup(basefile)),
+ enc, MDVI_HASH_REPLACE);
+ listh_prepend(&encodings, LIST(enc));
+ DEBUG((DBG_FMAP, "%s: encoding `%s' registered\n",
+ basefile, enc->name));
+ return enc;
+}
+
+DviEncoding *mdvi_request_encoding(const char *name)
+{
+ DviEncoding *enc = find_encoding(name);
+
+ if(enc == NULL) {
+ DEBUG((DBG_FMAP, "%s: encoding not found, returning default `%s'\n",
+ name, default_encoding->name));
+ return default_encoding;
+ }
+ /* we don't keep reference counts for this */
+ if(enc == tex_text_encoding)
+ return enc;
+ if(!enc->private && read_encoding(enc) < 0)
+ return NULL;
+ enc->links++;
+
+ /* if the hash table is empty, rebuild it */
+ if(enc->nametab.nkeys == 0) {
+ int i;
+
+ DEBUG((DBG_FMAP, "%s: rehashing\n", enc->name));
+ for(i = 0; i < 256; i++) {
+ if(enc->vector[i] == NULL)
+ continue;
+ mdvi_hash_add(&enc->nametab,
+ MDVI_KEY(enc->vector[i]),
+ (DviHashKey)Int2Ptr(i),
+ MDVI_HASH_REPLACE);
+ }
+ }
+ return enc;
+}
+
+void mdvi_release_encoding(DviEncoding *enc, int should_free)
+{
+ /* ignore our static encoding */
+ if(enc == tex_text_encoding)
+ return;
+ if(!enc->links || --enc->links > 0 || !should_free)
+ return;
+ DEBUG((DBG_FMAP, "%s: resetting encoding vector\n", enc->name));
+ mdvi_hash_reset(&enc->nametab, 1); /* we'll reuse it */
+}
+
+int mdvi_encode_glyph(DviEncoding *enc, const char *name)
+{
+ void *data;
+
+ data = mdvi_hash_lookup(&enc->nametab, MDVI_KEY(name));
+ if(data == NULL)
+ return -1;
+ /* we added +1 to the hashed index just to distinguish
+ * a failed lookup from a zero index. Adjust it now. */
+ return (Ptr2Int(data) - 1);
+}
+
+/****************
+ * Fontmaps *
+ ****************/
+
+static void parse_spec(DviFontMapEnt *ent, char *spec)
+{
+ char *arg, *command;
+
+ /* this is a ridiculously simple parser, and recognizes only
+ * things of the form <argument> <command>. Of these, only
+ * command=SlantFont, ExtendFont and ReEncodeFont are handled */
+ while(*spec) {
+ arg = getword(spec, " \t", &spec);
+ if(*spec) *spec++ = 0;
+ command = getword(spec, " \t", &spec);
+ if(*spec) *spec++ = 0;
+ if(!arg || !command)
+ continue;
+ if(STREQ(command, "SlantFont")) {
+ double x = 10000 * strtod(arg, 0);
+
+ /* SFROUND evaluates arguments twice */
+ ent->slant = SFROUND(x);
+ } else if(STREQ(command, "ExtendFont")) {
+ double x = 10000 * strtod(arg, 0);
+
+ ent->extend = SFROUND(x);
+ } else if(STREQ(command, "ReEncodeFont")) {
+ if(ent->encoding)
+ mdvi_free(ent->encoding);
+ ent->encoding = mdvi_strdup(arg);
+ }
+ }
+}
+
+#if 0
+static void print_ent(DviFontMapEnt *ent)
+{
+ printf("Entry for `%s':\n", ent->fontname);
+ printf(" PS name: %s\n", ent->psname ? ent->psname : "(none)");
+ printf(" Encoding: %s\n", ent->encoding ? ent->encoding : "(default)");
+ printf(" EncFile: %s\n", ent->encfile ? ent->encfile : "(none)");
+ printf(" FontFile: %s\n", ent->fontfile ? ent->fontfile : "(same)");
+ printf(" Extend: %ld\n", ent->extend);
+ printf(" Slant: %ld\n", ent->slant);
+}
+#endif
+
+DviFontMapEnt *mdvi_load_fontmap(const char *file)
+{
+ char *ptr;
+ FILE *in;
+ int lineno = 1;
+ Dstring input;
+ ListHead list;
+ DviFontMapEnt *ent;
+ DviEncoding *last_encoding;
+ char *last_encfile;
+
+ ptr = kpse_find_file(file, kpse_program_text_format, 0);
+ if(ptr == NULL)
+ ptr = kpse_find_file(file, kpse_tex_ps_header_format, 0);
+ if(ptr == NULL)
+ ptr = kpse_find_file(file, kpse_dvips_config_format, 0);
+ if(ptr == NULL)
+ in = fopen(file, "rb");
+ else {
+ in = fopen(ptr, "rb");
+ mdvi_free(ptr);
+ }
+ if(in == NULL)
+ return NULL;
+
+ ent = NULL;
+ listh_init(&list);
+ dstring_init(&input);
+ last_encoding = NULL;
+ last_encfile = NULL;
+
+ while((ptr = dgets(&input, in)) != NULL) {
+ char *font_file;
+ char *tex_name;
+ char *ps_name;
+ char *vec_name;
+ int is_encoding;
+ DviEncoding *enc;
+
+ lineno++;
+ SKIPSP(ptr);
+
+ /* we skip what dvips does */
+ if(*ptr <= ' ' || *ptr == '*' || *ptr == '#' ||
+ *ptr == ';' || *ptr == '%')
+ continue;
+
+ font_file = NULL;
+ tex_name = NULL;
+ ps_name = NULL;
+ vec_name = NULL;
+ is_encoding = 0;
+
+ if(ent == NULL) {
+ ent = xalloc(DviFontMapEnt);
+ ent->encoding = NULL;
+ ent->slant = 0;
+ ent->extend = 0;
+ }
+ while(*ptr) {
+ char *hdr_name = NULL;
+
+ while(*ptr && *ptr <= ' ')
+ ptr++;
+ if(*ptr == 0)
+ break;
+ if(*ptr == '"') {
+ char *str;
+
+ str = getstring(ptr, " \t", &ptr);
+ if(*ptr) *ptr++ = 0;
+ parse_spec(ent, str);
+ continue;
+ } else if(*ptr == '<') {
+ ptr++;
+ if(*ptr == '<')
+ ptr++;
+ else if(*ptr == '[') {
+ is_encoding = 1;
+ ptr++;
+ }
+ SKIPSP(ptr);
+ hdr_name = ptr;
+ } else if(!tex_name)
+ tex_name = ptr;
+ else if(!ps_name)
+ ps_name = ptr;
+ else
+ hdr_name = ptr;
+
+ /* get next word */
+ getword(ptr, " \t", &ptr);
+ if(*ptr) *ptr++ = 0;
+
+ if(hdr_name) {
+ const char *ext = file_extension(hdr_name);
+
+ if(is_encoding || (ext && STRCEQ(ext, "enc")))
+ vec_name = hdr_name;
+ else
+ font_file = hdr_name;
+ }
+ }
+
+ if(tex_name == NULL)
+ continue;
+ ent->fontname = mdvi_strdup(tex_name);
+ ent->psname = ps_name ? mdvi_strdup(ps_name) : NULL;
+ ent->fontfile = font_file ? mdvi_strdup(font_file) : NULL;
+ ent->encfile = vec_name ? mdvi_strdup(vec_name) : NULL;
+ ent->fullfile = NULL;
+ enc = NULL; /* we don't have this yet */
+
+ /* if we have an encoding file, register it */
+ if(ent->encfile) {
+ /* register_encoding is smart enough not to load the
+ * same file twice */
+ if(!last_encfile || !STREQ(last_encfile, ent->encfile)) {
+ last_encfile = ent->encfile;
+ last_encoding = register_encoding(ent->encfile, 1);
+ }
+ enc = last_encoding;
+ }
+ if(ent->encfile && enc){
+ if(ent->encoding && !STREQ(ent->encoding, enc->name)) {
+ mdvi_warning(
+ _("%s: %d: [%s] requested encoding `%s' does not match vector `%s'\n"),
+ file, lineno, ent->encfile,
+ ent->encoding, enc->name);
+ } else if(!ent->encoding)
+ ent->encoding = mdvi_strdup(enc->name);
+ }
+
+ /* add it to the list */
+ /*print_ent(ent);*/
+ listh_append(&list, LIST(ent));
+ ent = NULL;
+ }
+ dstring_reset(&input);
+ fclose(in);
+
+ return (DviFontMapEnt *)list.head;
+}
+
+static void free_ent(DviFontMapEnt *ent)
+{
+ ASSERT(ent->fontname != NULL);
+ mdvi_free(ent->fontname);
+ if(ent->psname)
+ mdvi_free(ent->psname);
+ if(ent->fontfile)
+ mdvi_free(ent->fontfile);
+ if(ent->encoding)
+ mdvi_free(ent->encoding);
+ if(ent->encfile)
+ mdvi_free(ent->encfile);
+ if(ent->fullfile)
+ mdvi_free(ent->fullfile);
+ mdvi_free(ent);
+}
+
+void mdvi_install_fontmap(DviFontMapEnt *head)
+{
+ DviFontMapEnt *ent, *next;
+
+ for(ent = head; ent; ent = next) {
+ /* add all the entries, overriding old ones */
+ DviFontMapEnt *old;
+
+ old = (DviFontMapEnt *)
+ mdvi_hash_remove(&maptable, MDVI_KEY(ent->fontname));
+ if(old != NULL) {
+ DEBUG((DBG_FMAP, "%s: overriding fontmap entry\n",
+ old->fontname));
+ listh_remove(&fontmaps, LIST(old));
+ free_ent(old);
+ }
+ next = ent->next;
+ mdvi_hash_add(&maptable, MDVI_KEY(ent->fontname),
+ ent, MDVI_HASH_UNCHECKED);
+ listh_append(&fontmaps, LIST(ent));
+ }
+}
+
+static void init_static_encoding()
+{
+ DviEncoding *encoding;
+ int i;
+
+ DEBUG((DBG_FMAP, "installing static TeX text encoding\n"));
+ encoding = xalloc(DviEncoding);
+ encoding->private = "";
+ encoding->filename = "";
+ encoding->name = "TeXTextEncoding";
+ encoding->vector = tex_text_vector;
+ encoding->links = 1;
+ encoding->offset = 0;
+ mdvi_hash_create(&encoding->nametab, ENCNAME_HASH_SIZE);
+ for(i = 0; i < 256; i++) {
+ if(encoding->vector[i]) {
+ mdvi_hash_add(&encoding->nametab,
+ MDVI_KEY(encoding->vector[i]),
+ (DviHashKey)Int2Ptr(i),
+ MDVI_HASH_UNCHECKED);
+ }
+ }
+ ASSERT_VALUE(encodings.count, 0);
+ mdvi_hash_create(&enctable, ENC_HASH_SIZE);
+ mdvi_hash_create(&enctable_file, ENC_HASH_SIZE);
+ enctable_file.hash_free = file_hash_free;
+ mdvi_hash_add(&enctable, MDVI_KEY(encoding->name),
+ encoding, MDVI_HASH_UNCHECKED);
+ listh_prepend(&encodings, LIST(encoding));
+ tex_text_encoding = encoding;
+ default_encoding = tex_text_encoding;
+}
+
+static int mdvi_set_default_encoding(const char *name)
+{
+ DviEncoding *enc, *old;
+
+ enc = find_encoding(name);
+ if(enc == NULL)
+ return -1;
+ if(enc == default_encoding)
+ return 0;
+ /* this will read it from file if necessary,
+ * but it can fail if the file is corrupted */
+ enc = mdvi_request_encoding(name);
+ if(enc == NULL)
+ return -1;
+ old = default_encoding;
+ default_encoding = enc;
+ if(old != tex_text_encoding)
+ mdvi_release_encoding(old, 1);
+ return 0;
+}
+
+static int mdvi_init_fontmaps(void)
+{
+ char *file;
+ char *line;
+ FILE *in;
+ Dstring input;
+ int count = 0;
+ char *config;
+
+ if(fontmaps_loaded)
+ return 0;
+ /* we will only try this once */
+ fontmaps_loaded = 1;
+
+ DEBUG((DBG_FMAP, "reading fontmaps\n"));
+
+ /* make sure the static encoding is there */
+ init_static_encoding();
+
+ /* create the fontmap hash table */
+ mdvi_hash_create(&maptable, MAP_HASH_SIZE);
+
+ /* get the name of our configuration file */
+ config = kpse_cnf_get("mdvi-config");
+ if(config == NULL)
+ config = MDVI_DEFAULT_CONFIG;
+ /* let's ask kpathsea for the file first */
+ file = kpse_find_file(config, kpse_program_text_format, 0);
+ if(file == NULL)
+ in = fopen(config, "rb");
+ else {
+ in = fopen(file, "rb");
+ mdvi_free(file);
+ }
+ if(in == NULL)
+ return -1;
+ dstring_init(&input);
+ while((line = dgets(&input, in)) != NULL) {
+ char *arg;
+
+ SKIPSP(line);
+ if(*line < ' ' || *line == '#' || *line == '%')
+ continue;
+ if(STRNEQ(line, "fontmap", 7)) {
+ DviFontMapEnt *ent;
+
+ arg = getstring(line + 7, " \t", &line); *line = 0;
+ DEBUG((DBG_FMAP, "%s: loading fontmap\n", arg));
+ ent = mdvi_load_fontmap(arg);
+ if(ent == NULL)
+ mdvi_warning(_("%s: could not load fontmap\n"), arg);
+ else {
+ DEBUG((DBG_FMAP,
+ "%s: installing fontmap\n", arg));
+ mdvi_install_fontmap(ent);
+ count++;
+ }
+ } else if(STRNEQ(line, "encoding", 8)) {
+ arg = getstring(line + 8, " \t", &line); *line = 0;
+ if(arg && *arg)
+ register_encoding(arg, 1);
+ } else if(STRNEQ(line, "default-encoding", 16)) {
+ arg = getstring(line + 16, " \t", &line); *line = 0;
+ if(mdvi_set_default_encoding(arg) < 0)
+ mdvi_warning(_("%s: could not set as default encoding\n"),
+ arg);
+ } else if(STRNEQ(line, "psfontpath", 10)) {
+ arg = getstring(line + 11, " \t", &line); *line = 0;
+ if(!psinitialized)
+ ps_init_default_paths();
+ if(psfontdir)
+ mdvi_free(psfontdir);
+ psfontdir = kpse_path_expand(arg);
+ } else if(STRNEQ(line, "pslibpath", 9)) {
+ arg = getstring(line + 10, " \t", &line); *line = 0;
+ if(!psinitialized)
+ ps_init_default_paths();
+ if(pslibdir)
+ mdvi_free(pslibdir);
+ pslibdir = kpse_path_expand(arg);
+ } else if(STRNEQ(line, "psfontmap", 9)) {
+ arg = getstring(line + 9, " \t", &line); *line = 0;
+ if(mdvi_ps_read_fontmap(arg) < 0)
+ mdvi_warning("%s: %s: could not read PS fontmap\n",
+ config, arg);
+ }
+ }
+ fclose(in);
+ dstring_reset(&input);
+ fontmaps_loaded = 1;
+ DEBUG((DBG_FMAP, "%d files installed, %d fontmaps\n",
+ count, fontmaps.count));
+ return count;
+}
+
+int mdvi_query_fontmap(DviFontMapInfo *info, const char *fontname)
+{
+ DviFontMapEnt *ent;
+
+ if(!fontmaps_loaded && mdvi_init_fontmaps() < 0)
+ return -1;
+ ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(fontname));
+
+ if(ent == NULL)
+ return -1;
+ info->psname = ent->psname;
+ info->encoding = ent->encoding;
+ info->fontfile = ent->fontfile;
+ info->extend = ent->extend;
+ info->slant = ent->slant;
+ info->fullfile = ent->fullfile;
+
+ return 0;
+}
+
+int mdvi_add_fontmap_file(const char *name, const char *fullpath)
+{
+ DviFontMapEnt *ent;
+
+ if(!fontmaps_loaded && mdvi_init_fontmaps() < 0)
+ return -1;
+ ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(name));
+ if(ent == NULL)
+ return -1;
+ if(ent->fullfile)
+ mdvi_free(ent->fullfile);
+ ent->fullfile = mdvi_strdup(fullpath);
+ return 0;
+}
+
+
+void mdvi_flush_encodings(void)
+{
+ DviEncoding *enc;
+
+ if(enctable.nbucks == 0)
+ return;
+
+ DEBUG((DBG_FMAP, "flushing %d encodings\n", encodings.count));
+ /* asked to remove all encodings */
+ for(; (enc = (DviEncoding *)encodings.head); ) {
+ encodings.head = LIST(enc->next);
+ if((enc != tex_text_encoding && enc->links) || enc->links > 1) {
+ mdvi_warning(_("encoding vector `%s' is in use\n"),
+ enc->name);
+ }
+ destroy_encoding(enc);
+ }
+ /* destroy the static encoding */
+ if(tex_text_encoding->nametab.buckets)
+ mdvi_hash_reset(&tex_text_encoding->nametab, 0);
+ mdvi_hash_reset(&enctable, 0);
+ mdvi_hash_reset(&enctable_file, 0);
+}
+
+void mdvi_flush_fontmaps(void)
+{
+ DviFontMapEnt *ent;
+
+ if(!fontmaps_loaded)
+ return;
+
+ DEBUG((DBG_FMAP, "flushing %d fontmaps\n", fontmaps.count));
+ for(; (ent = (DviFontMapEnt *)fontmaps.head); ) {
+ fontmaps.head = LIST(ent->next);
+ free_ent(ent);
+ }
+ mdvi_hash_reset(&maptable, 0);
+ fontmaps_loaded = 0;
+}
+
+/* reading of PS fontmaps */
+
+void ps_init_default_paths(void)
+{
+ char *kppath;
+ char *kfpath;
+
+ ASSERT(psinitialized == 0);
+
+ kppath = getenv("GS_LIB");
+ kfpath = getenv("GS_FONTPATH");
+
+ if(kppath != NULL)
+ pslibdir = kpse_path_expand(kppath);
+ if(kfpath != NULL)
+ psfontdir = kpse_path_expand(kfpath);
+
+ listh_init(&psfonts);
+ mdvi_hash_create(&pstable, PSMAP_HASH_SIZE);
+ psinitialized = 1;
+}
+
+int mdvi_ps_read_fontmap(const char *name)
+{
+ char *fullname;
+ FILE *in;
+ Dstring dstr;
+ char *line;
+ int count = 0;
+
+ if(!psinitialized)
+ ps_init_default_paths();
+ if(pslibdir)
+ fullname = kpse_path_search(pslibdir, name, 1);
+ else
+ fullname = (char *)name;
+ in = fopen(fullname, "rb");
+ if(in == NULL) {
+ if(fullname != name)
+ mdvi_free(fullname);
+ return -1;
+ }
+ dstring_init(&dstr);
+
+ while((line = dgets(&dstr, in)) != NULL) {
+ char *name;
+ char *mapname;
+ const char *ext;
+ PSFontMap *ps;
+
+ SKIPSP(line);
+ /* we're looking for lines of the form
+ * /FONT-NAME (fontfile)
+ * /FONT-NAME /FONT-ALIAS
+ */
+ if(*line != '/')
+ continue;
+ name = getword(line + 1, " \t", &line);
+ if(*line) *line++ = 0;
+ mapname = getword(line, " \t", &line);
+ if(*line) *line++ = 0;
+
+ if(!name || !mapname || !*name)
+ continue;
+ if(*mapname == '(') {
+ char *end;
+
+ mapname++;
+ for(end = mapname; *end && *end != ')'; end++);
+ *end = 0;
+ }
+ if(!*mapname)
+ continue;
+ /* dont add `.gsf' fonts, which require a full blown
+ * PostScript interpreter */
+ ext = file_extension(mapname);
+ if(ext && STREQ(ext, "gsf")) {
+ DEBUG((DBG_FMAP, "(ps) %s: font `%s' ignored\n",
+ name, mapname));
+ continue;
+ }
+ ps = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(name));
+ if(ps != NULL) {
+ if(STREQ(ps->mapname, mapname))
+ continue;
+ DEBUG((DBG_FMAP,
+ "(ps) replacing font `%s' (%s) by `%s'\n",
+ name, ps->mapname, mapname));
+ mdvi_free(ps->mapname);
+ ps->mapname = mdvi_strdup(mapname);
+ if(ps->fullname) {
+ mdvi_free(ps->fullname);
+ ps->fullname = NULL;
+ }
+ } else {
+ DEBUG((DBG_FMAP, "(ps) adding font `%s' as `%s'\n",
+ name, mapname));
+ ps = xalloc(PSFontMap);
+ ps->psname = mdvi_strdup(name);
+ ps->mapname = mdvi_strdup(mapname);
+ ps->fullname = NULL;
+ listh_append(&psfonts, LIST(ps));
+ mdvi_hash_add(&pstable, MDVI_KEY(ps->psname),
+ ps, MDVI_HASH_UNCHECKED);
+ count++;
+ }
+ }
+ fclose(in);
+ dstring_reset(&dstr);
+
+ DEBUG((DBG_FMAP, "(ps) %s: %d PostScript fonts registered\n",
+ fullname, count));
+ return 0;
+}
+
+void mdvi_ps_flush_fonts(void)
+{
+ PSFontMap *map;
+
+ if(!psinitialized)
+ return;
+ DEBUG((DBG_FMAP, "(ps) flushing PS font map (%d) entries\n",
+ psfonts.count));
+ mdvi_hash_reset(&pstable, 0);
+ for(; (map = (PSFontMap *)psfonts.head); ) {
+ psfonts.head = LIST(map->next);
+ mdvi_free(map->psname);
+ mdvi_free(map->mapname);
+ if(map->fullname)
+ mdvi_free(map->fullname);
+ mdvi_free(map);
+ }
+ listh_init(&psfonts);
+ if(pslibdir) {
+ mdvi_free(pslibdir);
+ pslibdir = NULL;
+ }
+ if(psfontdir) {
+ mdvi_free(psfontdir);
+ psfontdir = NULL;
+ }
+ psinitialized = 0;
+}
+
+char *mdvi_ps_find_font(const char *psname)
+{
+ PSFontMap *map, *smap;
+ char *filename;
+ int recursion_limit = 32;
+
+ DEBUG((DBG_FMAP, "(ps) resolving PS font `%s'\n", psname));
+ if(!psinitialized)
+ return NULL;
+ map = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(psname));
+ if(map == NULL)
+ return NULL;
+ if(map->fullname)
+ return mdvi_strdup(map->fullname);
+
+ /* is it an alias? */
+ smap = map;
+ while(recursion_limit-- > 0 && smap && *smap->mapname == '/')
+ smap = (PSFontMap *)mdvi_hash_lookup(&pstable,
+ MDVI_KEY(smap->mapname + 1));
+ if(smap == NULL) {
+ if(recursion_limit == 0)
+ DEBUG((DBG_FMAP,
+ "(ps) %s: possible loop in PS font map\n",
+ psname));
+ return NULL;
+ }
+
+ if(psfontdir)
+ filename = kpse_path_search(psfontdir, smap->mapname, 1);
+ else if(file_exists(map->mapname))
+ filename = mdvi_strdup(map->mapname);
+ else
+ filename = NULL;
+ if(filename)
+ map->fullname = mdvi_strdup(filename);
+
+ return filename;
+}
+
+/*
+ * To get metric info for a font, we proceed as follows:
+ * - We try to find NAME.<tfm,ofm,afm>.
+ * - We query the fontmap for NAME.
+ * - We get back a PSNAME, and use to find the file in the PS font map.
+ * - We get the PSFONT file name, replace its extension by "afm" and
+ * lookup the file in GS's font search path.
+ * - We finally read the data, transform it as specified in our font map,
+ * and return it to the caller. The new data is left in the font metrics
+ * cache, so the next time it will be found at the first step (when we look
+ * up NAME.afm).
+ *
+ * The name `_ps_' in this function is not meant to imply that it can be
+ * used for Type1 fonts only. It should be usable for TrueType fonts as well.
+ *
+ * The returned metric info is subjected to the same caching mechanism as
+ * all the other metric data, as returned by get_font_metrics(). One should
+ * not modify the returned data at all, and it should be disposed with
+ * free_font_metrics().
+ */
+TFMInfo *mdvi_ps_get_metrics(const char *fontname)
+{
+ TFMInfo *info;
+ DviFontMapInfo map;
+ char buffer[64]; /* to avoid mallocs */
+ char *psfont;
+ char *basefile;
+ char *afmfile;
+ char *ext;
+ int baselen;
+ int nc;
+ TFMChar *ch;
+ double efactor;
+ double sfactor;
+
+ DEBUG((DBG_FMAP, "(ps) %s: looking for metric data\n", fontname));
+ info = get_font_metrics(fontname, DviFontAny, NULL);
+ if(info != NULL)
+ return info;
+
+ /* query the fontmap */
+ if(mdvi_query_fontmap(&map, fontname) < 0 || !map.psname)
+ return NULL;
+
+ /* get the PS font */
+ psfont = mdvi_ps_find_font(map.psname);
+ if(psfont == NULL)
+ return NULL;
+ DEBUG((DBG_FMAP, "(ps) %s: found as PS font `%s'\n",
+ fontname, psfont));
+ /* replace its extension */
+ basefile = strrchr(psfont, '/');
+ if(basefile == NULL)
+ basefile = psfont;
+ baselen = strlen(basefile);
+ ext = strrchr(basefile, '.');
+ if(ext != NULL)
+ *ext = 0;
+ if(baselen + 4 < 64)
+ afmfile = &buffer[0];
+ else
+ afmfile = mdvi_malloc(baselen + 5);
+ strcpy(afmfile, basefile);
+ strcpy(afmfile + baselen, ".afm");
+ /* we don't need this anymore */
+ mdvi_free(psfont);
+ DEBUG((DBG_FMAP, "(ps) %s: looking for `%s'\n",
+ fontname, afmfile));
+ /* lookup the file */
+ psfont = kpse_path_search(psfontdir, afmfile, 1);
+ /* don't need this anymore */
+ if(afmfile != &buffer[0])
+ mdvi_free(afmfile);
+ if(psfont != NULL) {
+ info = get_font_metrics(fontname, DviFontAFM, psfont);
+ mdvi_free(psfont);
+ } else
+ info = NULL;
+ if(info == NULL || (!map.extend && !map.slant))
+ return info;
+
+ /*
+ * transform the data as prescribed -- keep in mind that `info'
+ * points to CACHED data, so we're modifying the metric cache
+ * in place.
+ */
+
+#define DROUND(x) ((x) >= 0 ? floor((x) + 0.5) : ceil((x) - 0.5))
+#define TRANSFORM(x,y) DROUND(efactor * (x) + sfactor * (y))
+
+ efactor = (double)map.extend / 10000.0;
+ sfactor = (double)map.slant / 10000.0;
+ DEBUG((DBG_FMAP, "(ps) %s: applying extend=%f, slant=%f\n",
+ efactor, sfactor));
+
+ nc = info->hic - info->loc + 1;
+ for(ch = info->chars; ch < info->chars + nc; ch++) {
+ /* the AFM bounding box is:
+ * wx = ch->advance
+ * llx = ch->left
+ * lly = -ch->depth
+ * urx = ch->right
+ * ury = ch->height
+ * what we do here is transform wx, llx, and urx by
+ * newX = efactor * oldX + sfactor * oldY
+ * where for `wx' oldY = 0. Also, these numbers are all in
+ * TFM units (i.e. TFM's fix-words, which is just the actual
+ * number times 2^20, no need to do anything to it).
+ */
+ if(ch->present) {
+ ch->advance = TRANSFORM(ch->advance, 0);
+ ch->left = TRANSFORM(ch->left, -ch->depth);
+ ch->right = TRANSFORM(ch->right, ch->height);
+ }
+ }
+
+ return info;
+}
diff --git a/backend/dvi/mdvi-lib/fontmap.h b/backend/dvi/mdvi-lib/fontmap.h
new file mode 100644
index 00000000..bb5a944d
--- /dev/null
+++ b/backend/dvi/mdvi-lib/fontmap.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef _MDVI_FONTMAP_H
+#define _MDVI_FONTMAP_H 1
+
+typedef struct _DviFontMapEnt DviFontMapEnt;
+typedef struct _DviEncoding DviEncoding;
+
+typedef struct {
+ const char *psname;
+ const char *encoding;
+ const char *fontfile;
+ const char *fullfile;
+ const char *fmfile;
+ int fmtype;
+ long extend;
+ long slant;
+} DviFontMapInfo;
+
+struct _DviEncoding {
+ DviEncoding *next;
+ DviEncoding *prev;
+ char *private;
+ char *filename;
+ char *name;
+ char **vector; /* table with exactly 256 strings */
+ int links;
+ long offset;
+ DviHashTable nametab;
+};
+
+struct _DviFontMapEnt {
+ DviFontMapEnt *next;
+ DviFontMapEnt *prev;
+ char *private;
+ char *fontname;
+ char *psname;
+ char *encoding;
+ char *encfile;
+ char *fontfile;
+ char *fullfile;
+ long extend;
+ long slant;
+};
+
+#define MDVI_FMAP_SLANT(x) ((double)(x)->slant / 10000.0)
+#define MDVI_FMAP_EXTEND(x) ((double)(x)->extend / 10000.0)
+
+extern DviEncoding *mdvi_request_encoding __PROTO((const char *));
+extern void mdvi_release_encoding __PROTO((DviEncoding *, int));
+extern int mdvi_encode_glyph __PROTO((DviEncoding *, const char *));
+extern DviFontMapEnt *mdvi_load_fontmap __PROTO((const char *));
+extern void mdvi_install_fontmap __PROTO((DviFontMapEnt *));
+extern int mdvi_load_fontmaps __PROTO((void));
+extern int mdvi_query_fontmap __PROTO((DviFontMapInfo *, const char *));
+extern void mdvi_flush_encodings __PROTO((void));
+extern void mdvi_flush_fontmaps __PROTO((void));
+
+extern int mdvi_add_fontmap_file __PROTO((const char *, const char *));
+
+/* PS font maps */
+extern int mdvi_ps_read_fontmap __PROTO((const char *));
+extern char *mdvi_ps_find_font __PROTO((const char *));
+extern TFMInfo *mdvi_ps_get_metrics __PROTO((const char *));
+extern void mdvi_ps_flush_fonts __PROTO((void));
+
+#endif /* _MDVI_FONTMAP_H */
diff --git a/backend/dvi/mdvi-lib/fontsrch.c b/backend/dvi/mdvi-lib/fontsrch.c
new file mode 100644
index 00000000..8ebaa5ca
--- /dev/null
+++ b/backend/dvi/mdvi-lib/fontsrch.c
@@ -0,0 +1,371 @@
+/* fontsearch.c -- implements the font lookup mechanism in MDVI */
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * How this works:
+ * Fonts are divided into MAX_CLASS priority classes. The first
+ * MAX_CLASS-1 ones correspond to `real' fonts (pk, gf, vf, type1, truetype,
+ * etc). The last one corresponds to `metric' fonts that are used as a last
+ * resort (tfm, afm, ofm, ...). When a font is looked up, it is tried in a
+ * `high' priority class (0 being the highest priority). The priority is
+ * lowered until it reaches MAX_CLASS-1. Then the whole thing is repeated
+ * for the fallback font. When the search reaches MAX_CLASS-1, we lookup the
+ * original font, and then the fallback font. The search can be done
+ * incrementally, with several calls to mdvi_lookup_font(). If this function
+ * is called again to continue a search, the function assumes the previous
+ * font it returned was not valid, and it goes on to the next step.
+ *
+ * Reason for this:
+ * Some font types are quite expensive to load (e.g. Type1), so loading
+ * them is deferred until the last possible moment. This means that a font that
+ * was supposed to exist may have to be discarded. Until now, MDVI had no ability to
+ * "resume" a search, so in this case it would have produced an error, regardless
+ * of whether the offending font existed in other formats.
+ * Also, given the large number of font types supported by MDVI, some mechanism
+ * was necessary to bring some order into the chaos.
+ *
+ * This mechanism fixes these two problems. For the first one, a search can
+ * be "resumed" and all the font formats tried for the missing font, and
+ * again for the fallback font (see above). As for the second, the
+ * hierarchical division in classes gives a lot of flexibility in how the
+ * fonts are configured.
+ */
+
+#include <config.h>
+#include "mdvi.h"
+
+#define HAVE_PROTOTYPES 1
+#include <kpathsea/tex-file.h>
+#include <kpathsea/tex-glyph.h>
+
+struct _DviFontClass {
+ DviFontClass *next;
+ DviFontClass *prev;
+ DviFontInfo info;
+ int links;
+ int id;
+};
+
+char *_mdvi_fallback_font = MDVI_FALLBACK_FONT;
+
+/* this leaves classes 0 and 1 for `real' fonts */
+#define MAX_CLASS 3
+static ListHead font_classes[MAX_CLASS];
+static int initialized = 0;
+
+static void init_font_classes(void)
+{
+ int i;
+
+ for(i = 0; i < MAX_CLASS; i++)
+ listh_init(&font_classes[i]);
+ initialized = 1;
+}
+
+int mdvi_get_font_classes(void)
+{
+ return (MAX_CLASS - 2);
+}
+
+char **mdvi_list_font_class(int klass)
+{
+ char **list;
+ int i, n;
+ DviFontClass *fc;
+
+ if(klass == -1)
+ klass = MAX_CLASS-1;
+ if(klass < 0 || klass >= MAX_CLASS)
+ return NULL;
+ n = font_classes[klass].count;
+ list = xnalloc(char *, n + 1);
+ fc = (DviFontClass *)font_classes[klass].head;
+ for(i = 0; i < n; fc = fc->next, i++) {
+ list[i] = mdvi_strdup(fc->info.name);
+ }
+ list[i] = NULL;
+ return list;
+}
+
+int mdvi_register_font_type(DviFontInfo *info, int klass)
+{
+ DviFontClass *fc;
+
+ if(klass == -1)
+ klass = MAX_CLASS-1;
+ if(klass < 0 || klass >= MAX_CLASS)
+ return -1;
+ if(!initialized)
+ init_font_classes();
+ fc = xalloc(struct _DviFontClass);
+ fc->links = 0;
+ fc->id = klass;
+ fc->info.name = mdvi_strdup(info->name);
+ fc->info.scalable = info->scalable;
+ fc->info.load = info->load;
+ fc->info.getglyph = info->getglyph;
+ fc->info.shrink0 = info->shrink0;
+ fc->info.shrink1 = info->shrink1;
+ fc->info.freedata = info->freedata;
+ fc->info.reset = info->reset;
+ fc->info.lookup = info->lookup;
+ fc->info.kpse_type = info->kpse_type;
+ listh_append(&font_classes[klass], LIST(fc));
+ return 0;
+}
+
+int mdvi_unregister_font_type(const char *name, int klass)
+{
+ DviFontClass *fc;
+ int k;
+
+ if(klass == -1)
+ klass = MAX_CLASS - 1;
+
+ if(klass >= 0 && klass < MAX_CLASS) {
+ k = klass;
+ LIST_FOREACH(fc, DviFontClass, &font_classes[k]) {
+ if(STREQ(fc->info.name, name))
+ break;
+ }
+ } else if(klass < 0) {
+ for(k = 0; k < MAX_CLASS; k++) {
+ LIST_FOREACH(fc, DviFontClass, &font_classes[k]) {
+ if(STREQ(fc->info.name, name))
+ break;
+ }
+ if(fc) break;
+ }
+ } else
+ return -1;
+
+ if(fc == NULL || fc->links)
+ return -1;
+ /* remove it */
+ listh_remove(&font_classes[k], LIST(fc));
+
+ /* and destroy it */
+ mdvi_free(fc->info.name);
+ mdvi_free(fc);
+ return 0;
+}
+
+static char *lookup_font(DviFontClass *ptr, const char *name, Ushort *h, Ushort *v)
+{
+ char *filename;
+
+ /*
+ * If the font type registered a function to do the lookup, use that.
+ * Otherwise we use kpathsea.
+ */
+ if(ptr->info.lookup)
+ filename = ptr->info.lookup(name, h, v);
+ else if(ptr->info.kpse_type <= kpse_any_glyph_format) {
+ kpse_glyph_file_type type;
+
+ filename = kpse_find_glyph(name, Max(*h, *v),
+ ptr->info.kpse_type, &type);
+ /* if kpathsea returned a fallback font, reject it */
+ if(filename && type.source == kpse_glyph_source_fallback) {
+ mdvi_free(filename);
+ filename = NULL;
+ } else if(filename)
+ *h = *v = type.dpi;
+ } else
+ filename = kpse_find_file(name, ptr->info.kpse_type, 1);
+ return filename;
+}
+
+/*
+ * Class MAX_CLASS-1 is special: it consists of `metric' fonts that should
+ * be tried as a last resort
+ */
+char *mdvi_lookup_font(DviFontSearch *search)
+{
+ int kid;
+ int k;
+ DviFontClass *ptr;
+ DviFontClass *last;
+ char *filename = NULL;
+ const char *name;
+ Ushort hdpi, vdpi;
+
+ if(search->id < 0)
+ return NULL;
+
+ if(search->curr == NULL) {
+ /* this is the initial search */
+ name = search->wanted_name;
+ hdpi = search->hdpi;
+ vdpi = search->vdpi;
+ kid = 0;
+ last = NULL;
+ } else {
+ name = search->actual_name;
+ hdpi = search->actual_hdpi;
+ vdpi = search->actual_vdpi;
+ kid = search->id;
+ last = search->curr;
+ }
+
+ ptr = NULL;
+again:
+ /* try all classes except MAX_CLASS-1 */
+ for(k = kid; !filename && k < MAX_CLASS-1; k++) {
+ if(last == NULL)
+ ptr = (DviFontClass *)font_classes[k].head;
+ else
+ ptr = last->next;
+ while(ptr) {
+ DEBUG((DBG_FONTS, "%d: trying `%s' at (%d,%d)dpi as `%s'\n",
+ k, name, hdpi, vdpi, ptr->info.name));
+ /* lookup the font in this class */
+ filename = lookup_font(ptr, name, &hdpi, &vdpi);
+ if(filename)
+ break;
+ ptr = ptr->next;
+ }
+ last = NULL;
+ }
+ if(filename != NULL) {
+ search->id = k-1;
+ search->curr = ptr;
+ search->actual_name = name;
+ search->actual_hdpi = hdpi;
+ search->actual_vdpi = vdpi;
+ search->info = &ptr->info;
+ ptr->links++;
+ return filename;
+ }
+
+ if(kid < MAX_CLASS - 1 && !STREQ(name, _mdvi_fallback_font)) {
+ mdvi_warning("font `%s' at %dx%d not found, trying `%s' instead\n",
+ name, hdpi, vdpi, _mdvi_fallback_font);
+ name = _mdvi_fallback_font;
+ kid = 0;
+ goto again;
+ }
+
+ /* we tried the fallback font, and all the `real' classes. Let's
+ * try the `metric' class now */
+ name = search->wanted_name;
+ hdpi = search->hdpi;
+ vdpi = search->vdpi;
+ if(kid == MAX_CLASS-1) {
+ /* we were looking into this class from the beginning */
+ if(last == NULL) {
+ /* no more fonts to try */
+ return NULL;
+ }
+ ptr = last->next;
+ } else {
+ mdvi_warning("font `%s' not found, trying metric files instead\n",
+ name);
+ ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head;
+ }
+
+metrics:
+ while(ptr) {
+ DEBUG((DBG_FONTS, "metric: trying `%s' at (%d,%d)dpi as `%s'\n",
+ name, hdpi, vdpi, ptr->info.name));
+ filename = lookup_font(ptr, name, &hdpi, &vdpi);
+ if(filename)
+ break;
+ ptr = ptr->next;
+ }
+ if(filename != NULL) {
+ if(STREQ(name, _mdvi_fallback_font))
+ search->id = MAX_CLASS;
+ else
+ search->id = MAX_CLASS - 1;
+ search->curr = ptr;
+ search->actual_name = name;
+ search->actual_hdpi = hdpi;
+ search->actual_vdpi = vdpi;
+ search->info = &ptr->info;
+ ptr->links++;
+ return filename;
+ }
+ if(!STREQ(name, _mdvi_fallback_font)) {
+ mdvi_warning("metric file for `%s' not found, trying `%s' instead\n",
+ name, _mdvi_fallback_font);
+ name = _mdvi_fallback_font;
+ ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head;
+ goto metrics;
+ }
+
+ search->id = -1;
+ search->actual_name = NULL;
+
+ /* tough luck, nothing found */
+ return NULL;
+}
+
+/* called by `font_reference' to do the initial lookup */
+DviFont *mdvi_add_font(const char *name, Int32 sum,
+ int hdpi, int vdpi, Int32 scale)
+{
+ DviFont *font;
+
+ font = xalloc(DviFont);
+ font->fontname = mdvi_strdup(name);
+ SEARCH_INIT(font->search, font->fontname, hdpi, vdpi);
+ font->filename = mdvi_lookup_font(&font->search);
+ if(font->filename == NULL) {
+ /* this answer is final */
+ mdvi_free(font->fontname);
+ mdvi_free(font);
+ return NULL;
+ }
+ font->hdpi = font->search.actual_hdpi;
+ font->vdpi = font->search.actual_vdpi;
+ font->scale = scale;
+ font->design = 0;
+ font->checksum = sum;
+ font->type = 0;
+ font->links = 0;
+ font->loc = 0;
+ font->hic = 0;
+ font->in = NULL;
+ font->chars = NULL;
+ font->subfonts = NULL;
+
+ return font;
+}
+
+int mdvi_font_retry(DviParams *params, DviFont *font)
+{
+ /* try the search again */
+ char *filename;
+
+ ASSERT(font->search.curr != NULL);
+ /* we won't be using this class anymore */
+ font->search.curr->links--;
+
+ filename = mdvi_lookup_font(&font->search);
+ if(filename == NULL)
+ return -1;
+ mdvi_free(font->filename);
+ font->filename = filename;
+ /* copy the new information */
+ font->hdpi = font->search.actual_hdpi;
+ font->vdpi = font->search.actual_vdpi;
+
+ return 0;
+}
diff --git a/backend/dvi/mdvi-lib/gf.c b/backend/dvi/mdvi-lib/gf.c
new file mode 100644
index 00000000..00607ff6
--- /dev/null
+++ b/backend/dvi/mdvi-lib/gf.c
@@ -0,0 +1,395 @@
+/* gf.c - GF font support */
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* functions to read GF fonts */
+
+#include <config.h>
+#include <string.h>
+#include "common.h"
+#include "mdvi.h"
+#include "private.h"
+
+/* opcodes */
+
+#define GF_PAINT0 0
+#define GF_PAINT1 64
+#define GF_PAINT2 65
+#define GF_PAINT3 66
+#define GF_BOC 67
+#define GF_BOC1 68
+#define GF_EOC 69
+#define GF_SKIP0 70
+#define GF_SKIP1 71
+#define GF_SKIP2 72
+#define GF_SKIP3 73
+#define GF_NEW_ROW_0 74
+#define GF_NEW_ROW_1 75
+#define GF_NEW_ROW_MAX 238
+#define GF_XXX1 239
+#define GF_XXX2 240
+#define GF_XXX3 241
+#define GF_XXX4 242
+#define GF_YYY 243
+#define GF_NOOP 244
+#define GF_LOC 245
+#define GF_LOC0 246
+#define GF_PRE 247
+#define GF_POST 248
+#define GF_POST_POST 249
+
+#define GF_ID 131
+#define GF_TRAILER 223
+
+#define BLACK 1
+#define WHITE 0
+
+static int gf_load_font __PROTO((DviParams *, DviFont *));
+static int gf_font_get_glyph __PROTO((DviParams *, DviFont *, int));
+
+/* only symbol exported by this file */
+DviFontInfo gf_font_info = {
+ "GF",
+ 0, /* scaling not supported natively */
+ gf_load_font,
+ gf_font_get_glyph,
+ mdvi_shrink_glyph,
+ mdvi_shrink_glyph_grey,
+ NULL, /* free */
+ NULL, /* reset */
+ NULL, /* lookup */
+ kpse_gf_format,
+ NULL
+};
+
+static int gf_read_bitmap(FILE *p, DviFontChar *ch)
+{
+ int op;
+ int min_n, max_n;
+ int min_m, max_m;
+ int paint_switch;
+ int x, y;
+ int bpl;
+ Int32 par;
+ BmUnit *line;
+ BITMAP *map;
+
+ fseek(p, (long)ch->offset, SEEK_SET);
+ op = fuget1(p);
+ if(op == GF_BOC) {
+ /* skip character code */
+ fuget4(p);
+ /* skip pointer */
+ fuget4(p);
+ min_m = fsget4(p);
+ max_m = fsget4(p);
+ min_n = fsget4(p);
+ max_n = fsget4(p);
+ } else if(op == GF_BOC1) {
+ /* skip character code */
+ fuget1(p);
+ min_m = fuget1(p); /* this is max_m - min_m */
+ max_m = fuget1(p);
+ min_n = fuget1(p); /* this is max_n - min_n */
+ max_n = fuget1(p);
+ min_m = max_m - min_m;
+ min_n = max_n - min_n;
+ } else {
+ mdvi_error(_("GF: invalid opcode %d in character %d\n"),
+ op, ch->code);
+ return -1;
+ }
+
+ ch->x = -min_m;
+ ch->y = max_n;
+ ch->width = max_m - min_m + 1;
+ ch->height = max_n - min_n + 1;
+ map = bitmap_alloc(ch->width, ch->height);
+
+ ch->glyph.data = map;
+ ch->glyph.x = ch->x;
+ ch->glyph.y = ch->y;
+ ch->glyph.w = ch->width;
+ ch->glyph.h = ch->height;
+
+#define COLOR(x) ((x) ? "BLACK" : "WHITE")
+
+ paint_switch = WHITE;
+ x = y = 0;
+ line = map->data;
+ bpl = map->stride;
+ DEBUG((DBG_BITMAPS, "(gf) reading character %d\n", ch->code));
+ while((op = fuget1(p)) != GF_EOC) {
+ Int32 n;
+
+ if(feof(p))
+ break;
+ if(op == GF_PAINT0) {
+ DEBUG((DBG_BITMAPS, "(gf) Paint0 %s -> %s\n",
+ COLOR(paint_switch), COLOR(!paint_switch)));
+ paint_switch = !paint_switch;
+ } else if(op <= GF_PAINT3) {
+ if(op < GF_PAINT1)
+ par = op;
+ else
+ par = fugetn(p, op - GF_PAINT1 + 1);
+ if(y >= ch->height || x + par >= ch->width)
+ goto toobig;
+ /* paint everything between columns x and x + par - 1 */
+ DEBUG((DBG_BITMAPS, "(gf) Paint %d %s from (%d,%d)\n",
+ par, COLOR(paint_switch), x, y));
+ if(paint_switch == BLACK)
+ bitmap_paint_bits(line + (x / BITMAP_BITS),
+ x % BITMAP_BITS, par);
+ paint_switch = !paint_switch;
+ x += par;
+ } else if(op >= GF_NEW_ROW_0 && op <= GF_NEW_ROW_MAX) {
+ y++;
+ line = bm_offset(line, bpl);
+ x = op - GF_NEW_ROW_0;
+ paint_switch = BLACK;
+ DEBUG((DBG_BITMAPS, "(gf) new_row_%d\n", x));
+ } else switch(op) {
+ case GF_SKIP0:
+ y++;
+ line = bm_offset(line, bpl);
+ x = 0;
+ paint_switch = WHITE;
+ DEBUG((DBG_BITMAPS, "(gf) skip_0\n"));
+ break;
+ case GF_SKIP1:
+ case GF_SKIP2:
+ case GF_SKIP3:
+ par = fugetn(p, op - GF_SKIP1 + 1);
+ y += par + 1;
+ line = bm_offset(line, (par + 1) * bpl);
+ x = 0;
+ paint_switch = WHITE;
+ DEBUG((DBG_BITMAPS, "(gf) skip_%d\n", op - GF_SKIP1));
+ break;
+ case GF_XXX1:
+ case GF_XXX2:
+ case GF_XXX3:
+ case GF_XXX4: {
+#ifndef NODEBUG
+ char *s;
+
+ s = read_string(p, op - GF_XXX1 + 1, NULL, 0);
+ DEBUG((DBG_SPECIAL, "(gf) Character %d: Special \"%s\"\n",
+ ch->code, s));
+ mdvi_free(s);
+#else
+ n = fugetn(p, op - GF_XXX1 + 1);
+ fseek(p, (long)n, SEEK_CUR);
+#endif
+ break;
+ }
+ case GF_YYY:
+ n = fuget4(p);
+ DEBUG((DBG_SPECIAL, "(gf) Character %d: MF special %u\n",
+ ch->code, n));
+ break;
+ case GF_NOOP:
+ DEBUG((DBG_BITMAPS, "(gf) no_op\n"));
+ break;
+ default:
+ mdvi_error(_("(gf) Character %d: invalid opcode %d\n"),
+ ch->code, op);
+ goto error;
+ }
+ /* chech that we're still inside the bitmap */
+ if(x > ch->width || y > ch->height)
+ goto toobig;
+ DEBUG((DBG_BITMAPS, "(gf) curr_loc @ (%d,%d)\n", x, y));
+ }
+
+ if(op != GF_EOC)
+ goto error;
+ DEBUG((DBG_BITMAPS, "(gf) end of character %d\n", ch->code));
+ return 0;
+
+toobig:
+ mdvi_error(_("(gf) character %d has an incorrect bounding box\n"),
+ ch->code);
+error:
+ bitmap_destroy(map);
+ ch->glyph.data = NULL;
+ return -1;
+}
+
+static int gf_load_font(DviParams *unused, DviFont *font)
+{
+ int i;
+ int n;
+ int loc;
+ int hic;
+ FILE *p;
+ Int32 word;
+ int op;
+ long alpha, beta, z;
+#ifndef NODEBUG
+ char s[256];
+#endif
+
+ p = font->in;
+
+ /* check preamble */
+ loc = fuget1(p); hic = fuget1(p);
+ if(loc != GF_PRE || hic != GF_ID)
+ goto badgf;
+ loc = fuget1(p);
+#ifndef NODEBUG
+ for(i = 0; i < loc; i++)
+ s[i] = fuget1(p);
+ s[i] = 0;
+ DEBUG((DBG_FONTS, "(gf) %s: %s\n", font->fontname, s));
+#else
+ fseek(p, (long)loc, SEEK_CUR);
+#endif
+ /* now read character locators in postamble */
+ if(fseek(p, (long)-1, SEEK_END) == -1)
+ return -1;
+
+ n = 0;
+ while((op = fuget1(p)) == GF_TRAILER) {
+ if(fseek(p, (long)-2, SEEK_CUR) < 0)
+ break;
+ n++;
+ }
+ if(op != GF_ID || n < 4)
+ goto badgf;
+ /* get the pointer to the postamble */
+ fseek(p, (long)-5, SEEK_CUR);
+ op = fuget4(p);
+ /* jump to it */
+ fseek(p, (long)op, SEEK_SET);
+ if(fuget1(p) != GF_POST)
+ goto badgf;
+ /* skip pointer to last EOC */
+ fuget4(p);
+ /* get the design size */
+ font->design = fuget4(p);
+ /* the checksum */
+ word = fuget4(p);
+ if(word && font->checksum && font->checksum != word) {
+ mdvi_warning(_("%s: bad checksum (expected %u, found %u)\n"),
+ font->fontname, font->checksum, word);
+ } else if(!font->checksum)
+ font->checksum = word;
+ /* skip pixels per point ratio */
+ fuget4(p);
+ fuget4(p);
+ font->chars = xnalloc(DviFontChar, 256);
+ for(loc = 0; loc < 256; loc++)
+ font->chars[loc].offset = 0;
+ /* skip glyph "bounding box" */
+ fseek(p, (long)16, SEEK_CUR);
+ loc = 256;
+ hic = -1;
+ TFMPREPARE(font->scale, z, alpha, beta);
+ while((op = fuget1(p)) != GF_POST_POST) {
+ DviFontChar *ch;
+ int cc;
+
+ /* get the character code */
+ cc = fuget1(p);
+ if(cc < loc)
+ loc = cc;
+ if(cc > hic)
+ hic = cc;
+ ch = &font->chars[cc];
+ switch(op) {
+ case GF_LOC:
+ fsget4(p); /* skip dx */
+ fsget4(p); /* skip dy */
+ break;
+ case GF_LOC0:
+ fuget1(p); /* skip dx */
+ /* dy assumed 0 */
+ break;
+ default:
+ mdvi_error(_("%s: junk in postamble\n"), font->fontname);
+ goto error;
+ }
+ ch->code = cc;
+ ch->tfmwidth = fuget4(p);
+ ch->tfmwidth = TFMSCALE(ch->tfmwidth, z, alpha, beta);
+ ch->offset = fuget4(p);
+ if(ch->offset == -1)
+ ch->offset = 0;
+ /* initialize the rest of the glyph information */
+ ch->x = 0;
+ ch->y = 0;
+ ch->width = 0;
+ ch->height = 0;
+ ch->glyph.data = NULL;
+ ch->shrunk.data = NULL;
+ ch->grey.data = NULL;
+ ch->flags = 0;
+ ch->loaded = 0;
+ }
+
+ if(op != GF_POST_POST)
+ goto badgf;
+
+ if(loc > 0 || hic < 255) {
+ /* shrink to optimal size */
+ memmove(font->chars, font->chars + loc,
+ (hic - loc + 1) * sizeof(DviFontChar));
+ font->chars = xresize(font->chars,
+ DviFontChar, hic - loc + 1);
+ }
+ font->loc = loc;
+ font->hic = hic;
+
+ return 0;
+
+badgf:
+ mdvi_error(_("%s: File corrupted, or not a GF file\n"), font->fontname);
+error:
+ if(font->chars) {
+ mdvi_free(font->chars);
+ font->chars = NULL;
+ }
+ font->loc = font->hic = 0;
+ return -1;
+}
+
+static int gf_font_get_glyph(DviParams *params, DviFont *font, int code)
+{
+ DviFontChar *ch;
+
+ if(code < font->loc || code > font->hic || !font->chars)
+ return -1;
+ ch = &font->chars[code - font->loc];
+
+ if(!ch->loaded) {
+ if(ch->offset == 0)
+ return -1;
+ DEBUG((DBG_GLYPHS, "(gf) %s: loading GF glyph for character %d\n",
+ font->fontname, code));
+ if(font->in == NULL && font_reopen(font) < 0)
+ return -1;
+ if(fseek(font->in, ch->offset, SEEK_SET) == -1)
+ return -1;
+ if(gf_read_bitmap(font->in, ch) < 0)
+ return -1;
+ ch->loaded = 1;
+ }
+ return 0;
+}
diff --git a/backend/dvi/mdvi-lib/hash.c b/backend/dvi/mdvi-lib/hash.c
new file mode 100644
index 00000000..d359e3c8
--- /dev/null
+++ b/backend/dvi/mdvi-lib/hash.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include "mdvi.h"
+
+/* simple hash tables for MDVI */
+
+
+struct _DviHashBucket {
+ DviHashBucket *next;
+ DviHashKey key;
+ Ulong hvalue;
+ void *data;
+};
+
+static Ulong hash_string(DviHashKey key)
+{
+ Uchar *p;
+ Ulong h, g;
+
+ for(h = 0, p = (Uchar *)key; *p; p++) {
+ h = (h << 4UL) + *p;
+ if((g = h & 0xf0000000L) != 0) {
+ h ^= (g >> 24UL);
+ h ^= g;
+ }
+ }
+
+ return h;
+}
+
+static int hash_compare(DviHashKey k1, DviHashKey k2)
+{
+ return strcmp((char *)k1, (char *)k2);
+}
+
+void mdvi_hash_init(DviHashTable *hash)
+{
+ hash->buckets = NULL;
+ hash->nbucks = 0;
+ hash->nkeys = 0;
+ hash->hash_func = NULL;
+ hash->hash_comp = NULL;
+ hash->hash_free = NULL;
+}
+
+void mdvi_hash_create(DviHashTable *hash, int size)
+{
+ int i;
+
+ hash->nbucks = size;
+ hash->buckets = xnalloc(DviHashBucket *, size);
+ for(i = 0; i < size; i++)
+ hash->buckets[i] = NULL;
+ hash->hash_func = hash_string;
+ hash->hash_comp = hash_compare;
+ hash->hash_free = NULL;
+ hash->nkeys = 0;
+}
+
+static DviHashBucket *hash_find(DviHashTable *hash, DviHashKey key)
+{
+ Ulong hval;
+ DviHashBucket *buck;
+
+ hval = (hash->hash_func(key) % hash->nbucks);
+
+ for(buck = hash->buckets[hval]; buck; buck = buck->next)
+ if(hash->hash_comp(buck->key, key) == 0)
+ break;
+ return buck;
+}
+
+/* Neither keys nor data are duplicated */
+int mdvi_hash_add(DviHashTable *hash, DviHashKey key, void *data, int rep)
+{
+ DviHashBucket *buck = NULL;
+ Ulong hval;
+
+ if(rep != MDVI_HASH_UNCHECKED) {
+ buck = hash_find(hash, key);
+ if(buck != NULL) {
+ if(buck->data == data)
+ return 0;
+ if(rep == MDVI_HASH_UNIQUE)
+ return -1;
+ if(hash->hash_free != NULL)
+ hash->hash_free(buck->key, buck->data);
+ }
+ }
+ if(buck == NULL) {
+ buck = xalloc(DviHashBucket);
+ buck->hvalue = hash->hash_func(key);
+ hval = (buck->hvalue % hash->nbucks);
+ buck->next = hash->buckets[hval];
+ hash->buckets[hval] = buck;
+ hash->nkeys++;
+ }
+
+ /* save key and data */
+ buck->key = key;
+ buck->data = data;
+
+ return 0;
+}
+
+void *mdvi_hash_lookup(DviHashTable *hash, DviHashKey key)
+{
+ DviHashBucket *buck = hash_find(hash, key);
+
+ return buck ? buck->data : NULL;
+}
+
+static DviHashBucket *hash_remove(DviHashTable *hash, DviHashKey key)
+{
+ DviHashBucket *buck, *last;
+ Ulong hval;
+
+ hval = hash->hash_func(key);
+ hval %= hash->nbucks;
+
+ for(last = NULL, buck = hash->buckets[hval]; buck; buck = buck->next) {
+ if(hash->hash_comp(buck->key, key) == 0)
+ break;
+ last = buck;
+ }
+ if(buck == NULL)
+ return NULL;
+ if(last)
+ last->next = buck->next;
+ else
+ hash->buckets[hval] = buck->next;
+ hash->nkeys--;
+ return buck;
+}
+
+void *mdvi_hash_remove(DviHashTable *hash, DviHashKey key)
+{
+ DviHashBucket *buck = hash_remove(hash, key);
+ void *data = NULL;
+
+ if(buck) {
+ data = buck->data;
+ mdvi_free(buck);
+ }
+ return data;
+}
+
+void *mdvi_hash_remove_ptr(DviHashTable *hash, DviHashKey key)
+{
+ DviHashBucket *buck, *last;
+ Ulong hval;
+ void *ptr;
+
+ hval = hash->hash_func(key);
+ hval %= hash->nbucks;
+
+ for(last = NULL, buck = hash->buckets[hval]; buck; buck = buck->next) {
+ if(buck->key == key)
+ break;
+ last = buck;
+ }
+ if(buck == NULL)
+ return NULL;
+ if(last)
+ last->next = buck->next;
+ else
+ hash->buckets[hval] = buck->next;
+ hash->nkeys--;
+ /* destroy the bucket */
+ ptr = buck->data;
+ mdvi_free(buck);
+ return ptr;
+}
+
+int mdvi_hash_destroy_key(DviHashTable *hash, DviHashKey key)
+{
+ DviHashBucket *buck = hash_remove(hash, key);
+
+ if(buck == NULL)
+ return -1;
+ if(hash->hash_free)
+ hash->hash_free(buck->key, buck->data);
+ mdvi_free(buck);
+ return 0;
+}
+
+void mdvi_hash_reset(DviHashTable *hash, int reuse)
+{
+ int i;
+ DviHashBucket *buck;
+
+ /* remove all keys in the hash table */
+ for(i = 0; i < hash->nbucks; i++) {
+ for(; (buck = hash->buckets[i]); ) {
+ hash->buckets[i] = buck->next;
+ if(hash->hash_free)
+ hash->hash_free(buck->key, buck->data);
+ mdvi_free(buck);
+ }
+ }
+ hash->nkeys = 0;
+ if(!reuse && hash->buckets) {
+ mdvi_free(hash->buckets);
+ hash->buckets = NULL;
+ hash->nbucks = 0;
+ } /* otherwise, it is left empty, ready to be reused */
+}
diff --git a/backend/dvi/mdvi-lib/hash.h b/backend/dvi/mdvi-lib/hash.h
new file mode 100644
index 00000000..b10afd60
--- /dev/null
+++ b/backend/dvi/mdvi-lib/hash.h
@@ -0,0 +1,49 @@
+#ifndef MDVI_HASH
+#define MDVI_HASH
+
+/* Hash tables */
+
+
+typedef struct _DviHashBucket DviHashBucket;
+typedef struct _DviHashTable DviHashTable;
+
+/*
+ * Hash tables
+ */
+
+typedef Uchar *DviHashKey;
+#define MDVI_KEY(x) ((DviHashKey)(x))
+
+typedef Ulong (*DviHashFunc) __PROTO((DviHashKey key));
+typedef int (*DviHashComp) __PROTO((DviHashKey key1, DviHashKey key2));
+typedef void (*DviHashFree) __PROTO((DviHashKey key, void *data));
+
+
+struct _DviHashTable {
+ DviHashBucket **buckets;
+ int nbucks;
+ int nkeys;
+ DviHashFunc hash_func;
+ DviHashComp hash_comp;
+ DviHashFree hash_free;
+};
+#define MDVI_EMPTY_HASH_TABLE {NULL, 0, 0, NULL, NULL, NULL}
+
+#define MDVI_HASH_REPLACE 0
+#define MDVI_HASH_UNIQUE 1
+#define MDVI_HASH_UNCHECKED 2
+
+extern void mdvi_hash_init __PROTO((DviHashTable *));
+extern void mdvi_hash_create __PROTO((DviHashTable *, int));
+extern int mdvi_hash_add __PROTO((DviHashTable *, DviHashKey, void *, int));
+extern int mdvi_hash_destroy_key __PROTO((DviHashTable *, DviHashKey));
+extern void mdvi_hash_reset __PROTO((DviHashTable *, int));
+extern void *mdvi_hash_lookup __PROTO((DviHashTable *, DviHashKey));
+extern void *mdvi_hash_remove __PROTO((DviHashTable *, DviHashKey));
+extern void *mdvi_hash_remove_ptr __PROTO((DviHashTable *, DviHashKey));
+
+#define mdvi_hash_flush(h) mdvi_hash_reset((h), 1)
+#define mdvi_hash_destroy(h) mdvi_hash_reset((h), 0)
+
+#endif
+
diff --git a/backend/dvi/mdvi-lib/list.c b/backend/dvi/mdvi-lib/list.c
new file mode 100644
index 00000000..eb73a178
--- /dev/null
+++ b/backend/dvi/mdvi-lib/list.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2000, Matias Atria
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include "common.h"
+
+void listh_init(ListHead *head)
+{
+ head->head = head->tail = NULL;
+ head->count = 0;
+}
+
+void listh_prepend(ListHead *head, List *list)
+{
+ list->prev = NULL;
+ list->next = head->head;
+ if(head->head)
+ head->head->prev = list;
+ head->head = list;
+ if(!head->tail)
+ head->tail = list;
+ head->count++;
+}
+
+void listh_append(ListHead *head, List *list)
+{
+ list->next = NULL;
+ list->prev = head->tail;
+ if(head->tail)
+ head->tail->next = list;
+ else
+ head->head = list;
+ head->tail = list;
+ head->count++;
+}
+
+void listh_add_before(ListHead *head, List *at, List *list)
+{
+ if(at == head->head || head->head == NULL)
+ listh_prepend(head, list);
+ else {
+ list->next = at;
+ list->prev = at->prev;
+ at->prev = list;
+ head->count++;
+ }
+}
+
+void listh_add_after(ListHead *head, List *at, List *list)
+{
+ if(at == head->tail || !head->tail)
+ listh_append(head, list);
+ else {
+ list->prev = at;
+ list->next = at->next;
+ at->next = list;
+ head->count++;
+ }
+}
+
+void listh_remove(ListHead *head, List *list)
+{
+ if(list == head->head) {
+ head->head = list->next;
+ if(head->head)
+ head->head->prev = NULL;
+ } else if(list == head->tail) {
+ head->tail = list->prev;
+ if(head->tail)
+ head->tail->next = NULL;
+ } else {
+ list->next->prev = list->prev;
+ list->prev->next = list->next;
+ }
+ if(--head->count == 0)
+ head->head = head->tail = NULL;
+}
+