Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/backend
diff options
context:
space:
mode:
authorNickolay V. Shmyrev <nshmyrev@yandex.ru>2007-01-08 12:25:31 (GMT)
committer Nickolay V. Shmyrev <nshmyrev@src.gnome.org>2007-01-08 12:25:31 (GMT)
commit13a06349251874bd35d2f03c3fc93217cee749a2 (patch)
treea681279b008acb19f686ee265aaed2be0da8d9e8 /backend
parentafb550ab779e00918d8fe24742abee3a81ebfe93 (diff)
Reorganize source tree.
2007-01-08 Nickolay V. Shmyrev <nshmyrev@yandex.ru> * Makefile.am: * backend/Makefile.am: * backend/comics/Makefile.am: * backend/djvu/Makefile.am: * backend/dvi/Makefile.am: * backend/ev-async-renderer.c: * backend/ev-async-renderer.h: * backend/ev-attachment.c: * backend/ev-attachment.h: * backend/ev-backend-marshal.c: * backend/ev-document-factory.c: * backend/ev-document-factory.h: * backend/ev-document-find.c: * backend/ev-document-find.h: * backend/ev-document-fonts.c: * backend/ev-document-fonts.h: * backend/ev-document-images.c: * backend/ev-document-images.h: * backend/ev-document-info.h: * backend/ev-document-links.c: * backend/ev-document-links.h: * backend/ev-document-misc.c: * backend/ev-document-misc.h: * backend/ev-document-security.c: * backend/ev-document-security.h: * backend/ev-document-thumbnails.c: * backend/ev-document-thumbnails.h: * backend/ev-document-transition.c: * backend/ev-document-transition.h: * backend/ev-document.c: * backend/ev-document.h: * backend/ev-file-exporter.c: * backend/ev-file-exporter.h: * backend/ev-image.c: * backend/ev-image.h: * backend/ev-link-action.c: * backend/ev-link-action.h: * backend/ev-link-dest.c: * backend/ev-link-dest.h: * backend/ev-link.c: * backend/ev-link.h: * backend/ev-render-context.c: * backend/ev-render-context.h: * backend/ev-selection.c: * backend/ev-selection.h: * backend/impress/Makefile.am: * backend/pdf/Makefile.am: * backend/pixbuf/Makefile.am: * backend/ps/Makefile.am: * backend/ps/ps-document.c: (push_pixbuf), (interpreter_failed), (ps_document_widget_event), (setup_pixmap), (setup_page), (input), (start_interpreter), (stop_interpreter), (document_load), (ps_document_next_page), (render_page): * backend/tiff/Makefile.am: * comics/Makefile.am: * comics/comics-document.c: * comics/comics-document.h: * configure.ac: * cut-n-paste/zoom-control/ephy-zoom-control.c: * djvu/Makefile.am: * djvu/djvu-document-private.h: * djvu/djvu-document.c: * djvu/djvu-document.h: * djvu/djvu-links.c: * djvu/djvu-links.h: * djvu/djvu-text-page.c: * djvu/djvu-text-page.h: * djvu/djvu-text.c: * djvu/djvu-text.h: * dvi/Makefile.am: * dvi/dvi-document.c: * dvi/dvi-document.h: * dvi/fonts.c: * dvi/fonts.h: * dvi/mdvi-lib/Makefile.am: * dvi/mdvi-lib/afmparse.c: * dvi/mdvi-lib/afmparse.h: * dvi/mdvi-lib/bitmap.c: * dvi/mdvi-lib/bitmap.h: * dvi/mdvi-lib/color.c: * dvi/mdvi-lib/color.h: * dvi/mdvi-lib/common.c: * dvi/mdvi-lib/common.h: * dvi/mdvi-lib/defaults.h: * dvi/mdvi-lib/dvimisc.c: * dvi/mdvi-lib/dviopcodes.h: * dvi/mdvi-lib/dviread.c: * dvi/mdvi-lib/files.c: * dvi/mdvi-lib/font.c: * dvi/mdvi-lib/fontmap.c: * dvi/mdvi-lib/fontmap.h: * dvi/mdvi-lib/fontsrch.c: * dvi/mdvi-lib/gf.c: * dvi/mdvi-lib/hash.c: * dvi/mdvi-lib/hash.h: * dvi/mdvi-lib/list.c: * dvi/mdvi-lib/mdvi.h: * dvi/mdvi-lib/pagesel.c: * dvi/mdvi-lib/paper.c: * dvi/mdvi-lib/paper.h: * dvi/mdvi-lib/pk.c: * dvi/mdvi-lib/private.h: * dvi/mdvi-lib/setup.c: * dvi/mdvi-lib/sp-epsf.c: * dvi/mdvi-lib/special.c: * dvi/mdvi-lib/sysdeps.h: * dvi/mdvi-lib/t1.c: * dvi/mdvi-lib/tfm.c: * dvi/mdvi-lib/tfmfile.c: * dvi/mdvi-lib/tt.c: * dvi/mdvi-lib/util.c: * dvi/mdvi-lib/vf.c: * dvi/pixbuf-device.c: * dvi/pixbuf-device.h: * impress/Makefile.am: * impress/common.h: * impress/document.c: * impress/f_oasis.c: * impress/f_oo13.c: * impress/iksemel.c: * impress/iksemel.h: * impress/imposter.h: * impress/impress-document.c: * impress/impress-document.h: * impress/internal.h: * impress/r_back.c: * impress/r_draw.c: * impress/r_geometry.c: * impress/r_gradient.c: * impress/r_style.c: * impress/r_text.c: * impress/render.c: * impress/render.h: * impress/zip.c: * impress/zip.h: * lib/Makefile.am: * lib/ev-debug.c: * lib/ev-debug.h: * lib/ev-file-helpers.c: * lib/ev-file-helpers.h: * lib/ev-gui.c: * lib/ev-gui.h: * lib/ev-tooltip.c: * lib/ev-tooltip.h: * libdocument/Makefile.am: * libdocument/ev-file-helpers.c: * pdf/Makefile.am: * pdf/ev-poppler.cc: * pdf/ev-poppler.h: * pixbuf/Makefile.am: * pixbuf/pixbuf-document.c: * pixbuf/pixbuf-document.h: * properties/Makefile.am: * ps/Makefile.am: * ps/gsdefaults.c: * ps/gsdefaults.h: * ps/gsio.c: * ps/gsio.h: * ps/gstypes.h: * ps/ps-document.c: * ps/ps-document.h: * ps/ps.c: * ps/ps.h: * shell/Makefile.am: * shell/ev-application.h: * shell/ev-sidebar-links.c: * shell/ev-sidebar-links.h: * shell/ev-utils.c: (ev_gui_sanitise_popup_position), (ev_gui_menu_position_tree_selection): * shell/ev-utils.h: * shell/ev-view.c: (ev_view_finalize): * shell/ev-window.c: * shell/main.c: (main): * thumbnailer/Makefile.am: * tiff/Makefile.am: * tiff/tiff-document.c: * tiff/tiff-document.h: * tiff/tiff2ps.c: * tiff/tiff2ps.h: Reorganize source tree. svn path=/trunk/; revision=2197
Diffstat (limited to 'backend')
-rw-r--r--backend/Makefile.am108
-rw-r--r--backend/comics/Makefile.am11
-rw-r--r--backend/comics/comics-document.c509
-rw-r--r--backend/comics/comics-document.h38
-rw-r--r--backend/djvu/Makefile.am22
-rw-r--r--backend/djvu/djvu-document-private.h45
-rw-r--r--backend/djvu/djvu-document.c478
-rw-r--r--backend/djvu/djvu-document.h38
-rw-r--r--backend/djvu/djvu-links.c393
-rw-r--r--backend/djvu/djvu-links.h35
-rw-r--r--backend/djvu/djvu-text-page.c444
-rw-r--r--backend/djvu/djvu-text-page.h59
-rw-r--r--backend/djvu/djvu-text.c298
-rw-r--r--backend/djvu/djvu-text.h47
-rw-r--r--backend/dvi/Makefile.am23
-rw-r--r--backend/dvi/dvi-document.c376
-rw-r--r--backend/dvi/dvi-document.h38
-rw-r--r--backend/dvi/fonts.c57
-rw-r--r--backend/dvi/fonts.h6
-rw-r--r--backend/dvi/mdvi-lib/Makefile.am40
-rw-r--r--backend/dvi/mdvi-lib/afmparse.c1302
-rw-r--r--backend/dvi/mdvi-lib/afmparse.h307
-rw-r--r--backend/dvi/mdvi-lib/bitmap.c1032
-rw-r--r--backend/dvi/mdvi-lib/bitmap.h145
-rw-r--r--backend/dvi/mdvi-lib/color.c135
-rw-r--r--backend/dvi/mdvi-lib/color.h34
-rw-r--r--backend/dvi/mdvi-lib/common.c187
-rw-r--r--backend/dvi/mdvi-lib/common.h283
-rw-r--r--backend/dvi/mdvi-lib/defaults.h71
-rw-r--r--backend/dvi/mdvi-lib/dvimisc.c62
-rw-r--r--backend/dvi/mdvi-lib/dviopcodes.h72
-rw-r--r--backend/dvi/mdvi-lib/dviread.c1584
-rw-r--r--backend/dvi/mdvi-lib/files.c79
-rw-r--r--backend/dvi/mdvi-lib/font.c516
-rw-r--r--backend/dvi/mdvi-lib/fontmap.c1172
-rw-r--r--backend/dvi/mdvi-lib/fontmap.h82
-rw-r--r--backend/dvi/mdvi-lib/fontsrch.c370
-rw-r--r--backend/dvi/mdvi-lib/gf.c394
-rw-r--r--backend/dvi/mdvi-lib/hash.c223
-rw-r--r--backend/dvi/mdvi-lib/hash.h49
-rw-r--r--backend/dvi/mdvi-lib/list.c119
-rw-r--r--backend/dvi/mdvi-lib/mdvi.h618
-rw-r--r--backend/dvi/mdvi-lib/pagesel.c490
-rw-r--r--backend/dvi/mdvi-lib/paper.c170
-rw-r--r--backend/dvi/mdvi-lib/paper.h32
-rw-r--r--backend/dvi/mdvi-lib/pk.c569
-rw-r--r--backend/dvi/mdvi-lib/private.h51
-rw-r--r--backend/dvi/mdvi-lib/setup.c44
-rw-r--r--backend/dvi/mdvi-lib/sp-epsf.c257
-rw-r--r--backend/dvi/mdvi-lib/special.c248
-rw-r--r--backend/dvi/mdvi-lib/sysdeps.h116
-rw-r--r--backend/dvi/mdvi-lib/t1.c638
-rw-r--r--backend/dvi/mdvi-lib/tfm.c213
-rw-r--r--backend/dvi/mdvi-lib/tfmfile.c746
-rw-r--r--backend/dvi/mdvi-lib/tt.c494
-rw-r--r--backend/dvi/mdvi-lib/util.c495
-rw-r--r--backend/dvi/mdvi-lib/vf.c240
-rw-r--r--backend/dvi/pixbuf-device.c220
-rw-r--r--backend/dvi/pixbuf-device.h24
-rw-r--r--backend/ev-async-renderer.c82
-rw-r--r--backend/ev-async-renderer.h61
-rw-r--r--backend/ev-attachment.c409
-rw-r--r--backend/ev-attachment.h72
-rw-r--r--backend/ev-backend-marshal.c2
-rw-r--r--backend/ev-backend-marshalers.list0
-rw-r--r--backend/ev-document-factory.c463
-rw-r--r--backend/ev-document-factory.h46
-rw-r--r--backend/ev-document-find.c126
-rw-r--r--backend/ev-document-find.h98
-rw-r--r--backend/ev-document-fonts.c74
-rw-r--r--backend/ev-document-fonts.h74
-rw-r--r--backend/ev-document-images.c55
-rw-r--r--backend/ev-document-images.h55
-rw-r--r--backend/ev-document-info.h126
-rw-r--r--backend/ev-document-links.c94
-rw-r--r--backend/ev-document-links.h78
-rw-r--r--backend/ev-document-misc.c141
-rw-r--r--backend/ev-document-misc.h47
-rw-r--r--backend/ev-document-security.c63
-rw-r--r--backend/ev-document-security.h63
-rw-r--r--backend/ev-document-thumbnails.c79
-rw-r--r--backend/ev-document-thumbnails.h77
-rw-r--r--backend/ev-document-transition.c55
-rw-r--r--backend/ev-document-transition.h56
-rw-r--r--backend/ev-document.c281
-rw-r--r--backend/ev-document.h140
-rw-r--r--backend/ev-file-exporter.c91
-rw-r--r--backend/ev-file-exporter.h84
-rw-r--r--backend/ev-image.c167
-rw-r--r--backend/ev-image.h74
-rw-r--r--backend/ev-link-action.c360
-rw-r--r--backend/ev-link-action.h72
-rw-r--r--backend/ev-link-dest.c467
-rw-r--r--backend/ev-link-dest.h86
-rw-r--r--backend/ev-link.c226
-rw-r--r--backend/ev-link.h64
-rw-r--r--backend/ev-render-context.c79
-rw-r--r--backend/ev-render-context.h66
-rw-r--r--backend/ev-selection.c93
-rw-r--r--backend/ev-selection.h76
-rw-r--r--backend/impress/Makefile.am38
-rw-r--r--backend/impress/common.h40
-rw-r--r--backend/impress/document.c139
-rw-r--r--backend/impress/f_oasis.c169
-rw-r--r--backend/impress/f_oo13.c180
-rw-r--r--backend/impress/iksemel.c1881
-rw-r--r--backend/impress/iksemel.h402
-rw-r--r--backend/impress/imposter.h84
-rw-r--r--backend/impress/impress-document.c521
-rw-r--r--backend/impress/impress-document.h38
-rw-r--r--backend/impress/internal.h85
-rw-r--r--backend/impress/r_back.c45
-rw-r--r--backend/impress/r_draw.c119
-rw-r--r--backend/impress/r_geometry.c207
-rw-r--r--backend/impress/r_gradient.c386
-rw-r--r--backend/impress/r_style.c110
-rw-r--r--backend/impress/r_text.c385
-rw-r--r--backend/impress/render.c53
-rw-r--r--backend/impress/render.h42
-rw-r--r--backend/impress/zip.c346
-rw-r--r--backend/impress/zip.h18
-rw-r--r--backend/pdf/Makefile.am13
-rw-r--r--backend/pdf/ev-poppler.cc1727
-rw-r--r--backend/pdf/ev-poppler.h39
-rw-r--r--backend/pixbuf/Makefile.am10
-rw-r--r--backend/pixbuf/pixbuf-document.c222
-rw-r--r--backend/pixbuf/pixbuf-document.h38
-rw-r--r--backend/ps/Makefile.am21
-rw-r--r--backend/ps/gsdefaults.c67
-rw-r--r--backend/ps/gsdefaults.h39
-rw-r--r--backend/ps/gsio.c172
-rw-r--r--backend/ps/gsio.h42
-rw-r--r--backend/ps/gstypes.h48
-rw-r--r--backend/ps/ps-document.c1339
-rw-r--r--backend/ps/ps-document.h95
-rw-r--r--backend/ps/ps.c1872
-rw-r--r--backend/ps/ps.h107
-rw-r--r--backend/tiff/Makefile.am12
-rw-r--r--backend/tiff/tiff-document.c437
-rw-r--r--backend/tiff/tiff-document.h38
-rw-r--r--backend/tiff/tiff2ps.c1868
-rw-r--r--backend/tiff/tiff2ps.h30
142 files changed, 30375 insertions, 5019 deletions
diff --git a/backend/Makefile.am b/backend/Makefile.am
index a09734d..f22c2e1 100644
--- a/backend/Makefile.am
+++ b/backend/Makefile.am
@@ -1,121 +1,35 @@
-INCLUDES= \
- -DEVINCE_UIDIR=\"$(pkgdatadir)\" \
- -DGNOMELOCALEDIR=\"$(datadir)/locale\" \
- -I$(top_srcdir)/lib \
- -I$(top_srcdir)/pdf \
- -I$(top_srcdir)/pixbuf \
- -I$(top_srcdir)/tiff \
- -I$(top_srcdir)/ps \
- -I$(top_srcdir)/djvu \
- -I$(top_srcdir)/dvi \
- -I$(top_srcdir)/impress \
- -I$(top_srcdir)/comics \
- $(BACKEND_CFLAGS) \
- $(WARN_CFLAGS) \
- $(DISABLE_DEPRECATED)
+SUBDIRS =
-noinst_LTLIBRARIES = libevbackend.la
-
-libevbackend_la_SOURCES= \
- ev-async-renderer.c \
- ev-async-renderer.h \
- ev-attachment.c \
- ev-attachment.h \
- ev-backend-marshal.c \
- ev-link.c \
- ev-link.h \
- ev-link-action.c \
- ev-link-action.h \
- ev-link-dest.c \
- ev-link-dest.h \
- ev-image.c \
- ev-image.h \
- ev-document.c \
- ev-document.h \
- ev-document-factory.c \
- ev-document-factory.h \
- ev-document-thumbnails.c \
- ev-document-thumbnails.h \
- ev-document-fonts.c \
- ev-document-fonts.h \
- ev-document-links.c \
- ev-document-links.h \
- ev-document-images.c \
- ev-document-images.h \
- ev-document-security.c \
- ev-document-security.h \
- ev-document-find.c \
- ev-document-find.h \
- ev-document-info.h \
- ev-document-transition.h \
- ev-document-transition.c \
- ev-file-exporter.c \
- ev-file-exporter.h \
- ev-render-context.h \
- ev-render-context.c \
- ev-selection.h \
- ev-selection.c \
- ev-document-misc.h \
- ev-document-misc.c
-
-libevbackend_la_LIBADD =
+# Backends
if ENABLE_PDF
-libevbackend_la_LIBADD += \
- $(top_builddir)/pdf/libpdfdocument.la
+SUBDIRS += pdf
endif
-if ENABLE_PS
-libevbackend_la_LIBADD += \
- $(top_builddir)/ps/libpsdocument.la
+if ENABLE_PS
+SUBDIRS += ps
endif
if ENABLE_PIXBUF
-libevbackend_la_LIBADD += \
- $(top_builddir)/pixbuf/libpixbufdocument.la
+SUBDIRS += pixbuf
endif
if ENABLE_DJVU
-libevbackend_la_LIBADD += \
- $(top_builddir)/djvu/libgtkdjvu.la
+SUBDIRS += djvu
endif
if ENABLE_TIFF
-libevbackend_la_LIBADD += \
- $(top_builddir)/tiff/libtiffdocument.la
+SUBDIRS += tiff
endif
if ENABLE_DVI
-libevbackend_la_LIBADD += \
- $(top_builddir)/dvi/libgtkdvi.la
+SUBDIRS += dvi
endif
if ENABLE_COMICS
-libevbackend_la_LIBADD += \
- $(top_builddir)/comics/libcomicsdocument.la
+ SUBDIRS += comics
endif
if ENABLE_IMPRESS
-libevbackend_la_LIBADD += \
- $(top_builddir)/impress/libimpressdocument.la
+ SUBDIRS += impress
endif
-
-BUILT_SOURCES= \
- ev-backend-marshalers.h \
- ev-backend-marshalers.c
-
-CLEANFILES = $(BUILT_SOURCES)
-
-ev-backend-marshalers.h: ev-backend-marshalers.list
- $(GLIB_GENMARSHAL) --prefix=_ev_backend_marshal $(srcdir)/ev-backend-marshalers.list --header > $@
-
-ev-backend-marshalers.c: ev-backend-marshalers.list
- $(GLIB_GENMARSHAL) --prefix=_ev_backend_marshal $(srcdir)/ev-backend-marshalers.list --body > $@
-
-ev-backend-marshal.c: ev-backend-marshalers.h ev-backend-marshalers.c
-
-noinst_HEADERS = \
- ev-backend-marshalers.h
-
-EXTRA_DIST= \
- ev-backend-marshalers.list
diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am
new file mode 100644
index 0000000..8c12139
--- /dev/null
+++ b/backend/comics/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ $(LIB_CFLAGS)
+
+noinst_LTLIBRARIES = libcomicsdocument.la
+
+libcomicsdocument_la_SOURCES = \
+ comics-document.c \
+ comics-document.h
+
diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c
new file mode 100644
index 0000000..7f53f85
--- /dev/null
+++ b/backend/comics/comics-document.c
@@ -0,0 +1,509 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005, Teemu Tervo <teemu.tervo@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+
+#include "comics-document.h"
+#include "ev-document-misc.h"
+#include "ev-document-thumbnails.h"
+
+struct _ComicsDocumentClass
+{
+ GObjectClass parent_class;
+};
+
+struct _ComicsDocument
+{
+ GObject parent_instance;
+
+ gchar *archive;
+ GSList *page_names;
+ int n_pages;
+ char *extract_command;
+ gboolean regex_arg;
+};
+
+typedef struct _ComicsDocumentClass ComicsDocumentClass;
+
+static void comics_document_document_iface_init (EvDocumentIface *iface);
+static void comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *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);
+
+
+G_DEFINE_TYPE_WITH_CODE (
+ ComicsDocument, comics_document, G_TYPE_OBJECT,
+ {
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
+ comics_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+ comics_document_document_thumbnails_iface_init);
+ } );
+
+static char *
+comics_regex_quote (const char *s)
+{
+ char *ret, *d;
+
+ d = ret = g_malloc (strlen (s) * 2 + 3);
+
+ *d++ = '\'';
+
+ for (; *s; s++, d++) {
+ switch (*s) {
+ case '?':
+ case '|':
+ case '[':
+ case ']':
+ case '*':
+ case '\\':
+ case '\'':
+ *d++ = '\\';
+ break;
+ }
+ *d = *s;
+ }
+
+ *d++ = '\'';
+ *d = '\0';
+
+ return ret;
+}
+
+static gboolean
+comics_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+ GSList *supported_extensions;
+ gchar *list_files_command = NULL, *stdout, *quoted_file, *mime_type;
+ gchar **cbr_files;
+ gboolean success;
+ int i, retval;
+
+ comics_document->archive = g_filename_from_uri (uri, NULL, error);
+ g_return_val_if_fail (comics_document->archive != NULL, FALSE);
+
+ quoted_file = g_shell_quote (comics_document->archive);
+ mime_type = gnome_vfs_get_mime_type (uri);
+
+ /* FIXME, use proper cbr/cbz mime types once they're
+ * included in shared-mime-info */
+ if (!strcmp (mime_type, "application/x-cbr")) {
+ comics_document->extract_command =
+ g_strdup ("unrar p -c- -ierr");
+ list_files_command =
+ g_strdup_printf ("unrar vb -c- -- %s", quoted_file);
+ comics_document->regex_arg = FALSE;
+ } else if (!strcmp (mime_type, "application/x-cbz")) {
+ comics_document->extract_command =
+ g_strdup ("unzip -p -C");
+ list_files_command =
+ g_strdup_printf ("zipinfo -1 -- %s", quoted_file);
+ comics_document->regex_arg = TRUE;
+ }
+
+ g_free (quoted_file);
+
+ /* Get list of files in archive */
+ success = g_spawn_command_line_sync (list_files_command,
+ &stdout, NULL, &retval, error);
+ g_free (list_files_command);
+
+ if (!success) {
+ g_free (mime_type);
+ return FALSE;
+ } else if (retval != 0) {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("File corrupted."));
+ g_free (mime_type);
+ return FALSE;
+ }
+
+ cbr_files = g_strsplit (stdout, "\n", 0);
+ supported_extensions = get_supported_image_extensions ();
+ for (i = 0; cbr_files[i] != NULL; i++) {
+ gchar *suffix = g_strrstr (cbr_files[i], ".");
+ if (!suffix)
+ continue;
+ suffix = g_ascii_strdown (suffix + 1, -1);
+
+ if (g_slist_find_custom (supported_extensions, suffix,
+ (GCompareFunc) strcmp) != NULL) {
+ comics_document->page_names =
+ g_slist_insert_sorted (
+ comics_document->page_names,
+ g_strdup (g_strstrip (cbr_files[i])),
+ (GCompareFunc) strcmp);
+ comics_document->n_pages++;
+ }
+
+ g_free (suffix);
+ }
+
+ g_free (stdout);
+ g_free (mime_type);
+ g_strfreev (cbr_files);
+ g_slist_foreach (supported_extensions, (GFunc) g_free, NULL);
+ g_slist_free (supported_extensions);
+
+ if (comics_document->n_pages == 0) {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("No images found in archive %s"),
+ uri);
+ return FALSE;
+ }
+
+ 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)
+{
+ return COMICS_DOCUMENT (document)->n_pages;
+}
+
+static void
+comics_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height)
+{
+ GdkPixbufLoader *loader;
+ char **argv;
+ guchar buf[1024];
+ gboolean success, got_size = FALSE;
+ gint outpipe = -1;
+ GPid child_pid = -1;
+
+ argv = extract_argv (document, page);
+ 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) {
+ gssize 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)) {
+ GdkPixbuf *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);
+}
+
+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 = -1;
+
+ argv = extract_argv (document, rc->page);
+ 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) {
+ gssize 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);
+ return rotated_pixbuf;
+}
+
+static void
+render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
+ gint width,
+ gint height,
+ gpointer data)
+{
+ double *scale = data;
+ int w = width * (*scale);
+ int h = height * (*scale);
+
+ gdk_pixbuf_loader_set_size (loader, w, h);
+}
+
+static void
+comics_document_finalize (GObject *object)
+{
+ ComicsDocument *comics_document = COMICS_DOCUMENT (object);
+
+ if (comics_document->archive)
+ g_free (comics_document->archive);
+
+ if (comics_document->page_names) {
+ g_slist_foreach (comics_document->page_names,
+ (GFunc) g_free, NULL);
+ g_slist_free (comics_document->page_names);
+ }
+
+ if (comics_document->extract_command)
+ g_free (comics_document->extract_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);
+ gobject_class->finalize = comics_document_finalize;
+}
+
+static gboolean
+comics_document_can_get_text (EvDocument *document)
+{
+ return FALSE;
+}
+
+static EvDocumentInfo *
+comics_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+ info = g_new0 (EvDocumentInfo, 1);
+ return info;
+}
+
+static void
+comics_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->load = comics_document_load;
+ iface->save = comics_document_save;
+ iface->can_get_text = comics_document_can_get_text;
+ iface->get_n_pages = comics_document_get_n_pages;
+ iface->get_page_size = comics_document_get_page_size;
+ iface->render_pixbuf = comics_document_render_pixbuf;
+ iface->get_info = comics_document_get_info;
+}
+
+static void
+comics_document_init (ComicsDocument *comics_document)
+{
+ comics_document->archive = NULL;
+ comics_document->page_names = NULL;
+ comics_document->extract_command = NULL;
+ comics_document->n_pages = 0;
+}
+
+/* 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 void
+comics_document_thumbnails_get_geometry (EvDocumentThumbnails *document,
+ gint page,
+ gint suggested_width,
+ gint *width,
+ gint *height,
+ gdouble *scale_factor)
+{
+ gdouble orig_width, orig_height, scale;
+
+ comics_document_get_page_size (EV_DOCUMENT (document), page,
+ &orig_width, &orig_height);
+ scale = suggested_width / orig_width;
+
+ if (width)
+ *width = suggested_width;
+ if (height)
+ *height = orig_height * scale;
+ if (scale_factor)
+ *scale_factor = scale;
+}
+
+static GdkPixbuf *
+comics_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ gint page,
+ gint rotation,
+ gint size,
+ gboolean border)
+{
+ GdkPixbuf *thumbnail, *framed;
+ gint thumb_width, thumb_height;
+ gdouble scale;
+ EvRenderContext *rc;
+
+ comics_document_thumbnails_get_geometry (document, page, size,
+ &thumb_width, &thumb_height,
+ &scale);
+
+ rc = ev_render_context_new (rotation, page, scale);
+ thumbnail = comics_document_render_pixbuf (EV_DOCUMENT (document),
+ rc);
+ g_object_unref (G_OBJECT (rc));
+
+ if (border) {
+ GdkPixbuf *tmp_pixbuf = thumbnail;
+ thumbnail = ev_document_misc_get_thumbnail_frame (-1, -1, 0, tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+ }
+
+ return thumbnail;
+}
+
+static void
+comics_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ gint page,
+ gint suggested_width,
+ gint *width,
+ gint *height)
+{
+ comics_document_thumbnails_get_geometry (document, page,
+ suggested_width,
+ width, height, NULL);
+}
+
+static void
+comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *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;
+
+ quoted_archive = g_shell_quote (comics_document->archive);
+ if (comics_document->regex_arg) {
+ quoted_filename = comics_regex_quote (
+ g_slist_nth_data (comics_document->page_names, page));
+ } else {
+ quoted_filename = g_shell_quote (
+ g_slist_nth_data (comics_document->page_names, 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, 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 0000000..cd5b17b
--- /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 <teemu.tervo@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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;
+
+ComicsDocument *comics_document_new (void);
+
+GType comics_document_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __COMICS_DOCUMENT_H__ */
diff --git a/backend/djvu/Makefile.am b/backend/djvu/Makefile.am
new file mode 100644
index 0000000..fa44bbb
--- /dev/null
+++ b/backend/djvu/Makefile.am
@@ -0,0 +1,22 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ -DGNOMEICONDIR=\""${prefix}/${DATADIRNAME}/pixmaps"\" \
+ $(LIB_CFLAGS) \
+ $(DJVU_CFLAGS)
+
+noinst_LTLIBRARIES = libgtkdjvu.la
+
+libgtkdjvu_la_SOURCES = \
+ djvu-document.c \
+ djvu-document.h \
+ djvu-document-private.h \
+ djvu-links.c \
+ djvu-links.h \
+ djvu-text.c \
+ djvu-text.h \
+ djvu-text-page.c \
+ djvu-text-page.h
+
+
+
diff --git a/backend/djvu/djvu-document-private.h b/backend/djvu/djvu-document-private.h
new file mode 100644
index 0000000..3fa579f
--- /dev/null
+++ b/backend/djvu/djvu-document-private.h
@@ -0,0 +1,45 @@
+/*
+ * Declarations used throughout the djvu classes
+ *
+ * Copyright (C) 2006, Michael Hofmann <mh21@piware.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DJVU_DOCUMENT_INTERNAL_H__
+#define __DJVU_DOCUMENT_INTERNAL_H__
+
+#include "djvu-document.h"
+#include "djvu-text.h"
+
+#include <libdjvu/ddjvuapi.h>
+
+struct _DjvuDocument {
+ GObject parent_instance;
+
+ ddjvu_context_t *d_context;
+ ddjvu_document_t *d_document;
+ ddjvu_format_t *d_format;
+
+ gchar *uri;
+
+ DjvuText *search;
+};
+
+int djvu_document_get_n_pages (EvDocument *document);
+void djvu_handle_events (DjvuDocument *djvu_document,
+ int wait);
+
+#endif /* __DJVU_DOCUMENT_INTERNAL_H__ */
diff --git a/backend/djvu/djvu-document.c b/backend/djvu/djvu-document.c
new file mode 100644
index 0000000..ad18555
--- /dev/null
+++ b/backend/djvu/djvu-document.c
@@ -0,0 +1,478 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005, Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "djvu-document.h"
+#include "djvu-text.h"
+#include "djvu-links.h"
+#include "djvu-document-private.h"
+#include "ev-document-thumbnails.h"
+#include "ev-document-misc.h"
+#include "ev-document-find.h"
+#include "ev-document-links.h"
+
+#include <libdjvu/ddjvuapi.h>
+#include <libdjvu/miniexp.h>
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf-core.h>
+#include <glib/gunicode.h>
+#include <string.h>
+
+#define SCALE_FACTOR 0.2
+
+enum {
+ PROP_0,
+ PROP_TITLE
+};
+
+struct _DjvuDocumentClass
+{
+ GObjectClass parent_class;
+};
+
+typedef struct _DjvuDocumentClass DjvuDocumentClass;
+
+static void djvu_document_document_iface_init (EvDocumentIface *iface);
+static void djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
+static void djvu_document_find_iface_init (EvDocumentFindIface *iface);
+static void djvu_document_document_links_iface_init (EvDocumentLinksIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE
+ (DjvuDocument, djvu_document, G_TYPE_OBJECT,
+ {
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT, djvu_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, djvu_document_document_thumbnails_iface_init)
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND, djvu_document_find_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS, djvu_document_document_links_iface_init);
+ });
+
+
+void
+djvu_handle_events (DjvuDocument *djvu_document, int wait)
+{
+ ddjvu_context_t *ctx = djvu_document->d_context;
+ const ddjvu_message_t *msg;
+ if (!ctx)
+ return;
+ if (wait)
+ msg = ddjvu_message_wait (ctx);
+ while ((msg = ddjvu_message_peek (ctx))) {
+ switch (msg->m_any.tag) {
+ case DDJVU_ERROR:
+ g_warning ("DjvuLibre error: %s",
+ msg->m_error.message);
+ if (msg->m_error.filename)
+ g_warning ("DjvuLibre error: %s:%d",
+ msg->m_error.filename,
+ msg->m_error.lineno);
+ default:
+ break;
+ }
+ 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;
+
+ /* 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) return FALSE;
+
+ if (djvu_document->d_document)
+ ddjvu_document_release (djvu_document->d_document);
+
+ djvu_document->d_document = doc;
+
+ while (!ddjvu_document_decoding_done (djvu_document->d_document))
+ djvu_handle_events(djvu_document, TRUE);
+ g_free (djvu_document->uri);
+ djvu_document->uri = g_strdup (uri);
+
+ 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
+djvu_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ ddjvu_pageinfo_t info;
+ ddjvu_status_t r;
+
+ g_return_if_fail (djvu_document->d_document);
+
+ while ((r = ddjvu_document_get_pageinfo(djvu_document->d_document, page, &info)) < DDJVU_JOB_OK)
+ djvu_handle_events(djvu_document, TRUE);
+
+ if (r >= DDJVU_JOB_FAILED)
+ djvu_handle_events(djvu_document, TRUE);
+
+ *width = info.width * SCALE_FACTOR;
+ *height = info.height * SCALE_FACTOR;
+}
+
+static GdkPixbuf *
+djvu_document_render_pixbuf (EvDocument *document,
+ EvRenderContext *rc)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *rotated_pixbuf;
+
+ ddjvu_rect_t rrect;
+ ddjvu_rect_t prect;
+ ddjvu_page_t *d_page;
+
+ double page_width, page_height;
+
+ d_page = ddjvu_page_create_by_pageno (djvu_document->d_document, rc->page);
+
+ while (!ddjvu_page_decoding_done (d_page))
+ djvu_handle_events(djvu_document, TRUE);
+
+ page_width = ddjvu_page_get_width (d_page) * rc->scale * SCALE_FACTOR;
+ page_height = ddjvu_page_get_height (d_page) * rc->scale * SCALE_FACTOR;
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, page_width, page_height);
+
+ prect.x = 0; prect.y = 0;
+ prect.w = page_width; prect.h = page_height;
+ rrect = prect;
+
+ ddjvu_page_render(d_page, DDJVU_RENDER_COLOR,
+ &prect,
+ &rrect,
+ djvu_document->d_format,
+ gdk_pixbuf_get_rowstride (pixbuf),
+ (gchar *)gdk_pixbuf_get_pixels (pixbuf));
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
+ g_object_unref (pixbuf);
+
+ return rotated_pixbuf;
+}
+
+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);
+
+ ddjvu_context_release (djvu_document->d_context);
+ ddjvu_format_release (djvu_document->d_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);
+
+ gobject_class->finalize = djvu_document_finalize;
+}
+
+static gboolean
+djvu_document_can_get_text (EvDocument *document)
+{
+ return TRUE;
+}
+
+
+static char *
+djvu_document_get_text (EvDocument *document, int page, EvRectangle *rect)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ double width, height;
+ EvRectangle rectangle;
+ char* text;
+
+ djvu_document_get_page_size (document, page, &width, &height);
+ rectangle.x1 = rect->x1 / SCALE_FACTOR;
+ rectangle.y1 = (height - rect->y2) / SCALE_FACTOR;
+ rectangle.x2 = rect->x2 / SCALE_FACTOR;
+ rectangle.y2 = (height - rect->y1) / SCALE_FACTOR;
+
+ text = djvu_text_copy (djvu_document, page, &rectangle);
+
+ if (text == NULL)
+ text = g_strdup ("");
+
+ return text;
+}
+
+static EvDocumentInfo *
+djvu_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+
+ info = g_new0 (EvDocumentInfo, 1);
+
+ return info;
+}
+
+static void
+djvu_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->load = djvu_document_load;
+ iface->save = djvu_document_save;
+ iface->can_get_text = djvu_document_can_get_text;
+ iface->get_text = djvu_document_get_text;
+ iface->get_n_pages = djvu_document_get_n_pages;
+ iface->get_page_size = djvu_document_get_page_size;
+ iface->render_pixbuf = djvu_document_render_pixbuf;
+ iface->get_info = djvu_document_get_info;
+}
+
+static void
+djvu_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ gint page,
+ gint suggested_width,
+ gint *width,
+ gint *height)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ gdouble p_width, p_height;
+ gdouble page_ratio;
+
+ djvu_document_get_page_size (EV_DOCUMENT(djvu_document), page, &p_width, &p_height);
+
+ page_ratio = p_height / p_width;
+ *width = suggested_width;
+ *height = (gint) (suggested_width * page_ratio);
+
+ return;
+}
+
+static GdkPixbuf *
+djvu_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ gint page,
+ gint rotation,
+ gint width,
+ gboolean border)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ GdkPixbuf *pixbuf, *rotated_pixbuf;
+ gint thumb_width, thumb_height;
+
+ guchar *pixels;
+
+ g_return_val_if_fail (djvu_document->d_document, NULL);
+
+ djvu_document_thumbnails_get_dimensions (document, page, width, &thumb_width, &thumb_height);
+
+ 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, page, 1) < DDJVU_JOB_OK)
+ djvu_handle_events(djvu_document, TRUE);
+
+ ddjvu_thumbnail_render (djvu_document->d_document, page,
+ &thumb_width, &thumb_height,
+ djvu_document->d_format,
+ gdk_pixbuf_get_rowstride (pixbuf),
+ (gchar *)pixels);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rotation);
+ g_object_unref (pixbuf);
+
+ if (border) {
+ GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
+ rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, 0, tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+ }
+
+ return rotated_pixbuf;
+}
+
+static void
+djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
+{
+ iface->get_thumbnail = djvu_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = djvu_document_thumbnails_get_dimensions;
+}
+
+static void
+djvu_document_init (DjvuDocument *djvu_document)
+{
+ djvu_document->d_context = ddjvu_context_create ("Evince");
+ djvu_document->d_format = ddjvu_format_create (DDJVU_FORMAT_RGB24, 0, 0);
+ ddjvu_format_set_row_order (djvu_document->d_format,1);
+
+ djvu_document->d_document = NULL;
+}
+
+static void
+djvu_document_find_begin (EvDocumentFind *document,
+ int page,
+ const char *search_string,
+ gboolean case_sensitive)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+
+ if (djvu_document->search &&
+ strcmp (search_string, djvu_text_get_text (djvu_document->search)) == 0)
+ return;
+
+ if (djvu_document->search)
+ djvu_text_free (djvu_document->search);
+
+ djvu_document->search = djvu_text_new (djvu_document,
+ page,
+ case_sensitive,
+ search_string);
+}
+
+static int
+djvu_document_find_get_n_results (EvDocumentFind *document_find, int page)
+{
+ DjvuText *search = DJVU_DOCUMENT (document_find)->search;
+
+ if (search) {
+ return djvu_text_n_results (search, page);
+ } else {
+ return 0;
+ }
+}
+
+static gboolean
+djvu_document_find_get_result (EvDocumentFind *document_find,
+ int page,
+ int n_result,
+ EvRectangle *rectangle)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document_find);
+ DjvuText *search = djvu_document->search;
+ EvRectangle *r;
+ double width, height;
+
+ if (search == NULL)
+ return FALSE;
+
+ r = djvu_text_get_result (search, page, n_result);
+ if (r == NULL)
+ return FALSE;
+
+ djvu_document_get_page_size (EV_DOCUMENT (djvu_document),
+ page, &width, &height);
+ rectangle->x1 = r->x1 * SCALE_FACTOR;
+ rectangle->y1 = height - r->y2 * SCALE_FACTOR;
+ rectangle->x2 = r->x2 * SCALE_FACTOR;
+ rectangle->y2 = height - r->y1 * SCALE_FACTOR;
+
+ return TRUE;
+}
+
+static int
+djvu_document_find_page_has_results (EvDocumentFind *document_find,
+ int page)
+{
+ DjvuText *search = DJVU_DOCUMENT (document_find)->search;
+
+ return search && djvu_text_has_results (search, page);
+}
+
+static double
+djvu_document_find_get_progress (EvDocumentFind *document_find)
+{
+ DjvuText *search = DJVU_DOCUMENT (document_find)->search;
+
+ if (search == NULL) {
+ return 0;
+ }
+
+ return djvu_text_get_progress (search);
+}
+
+static void
+djvu_document_find_cancel (EvDocumentFind *document)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+
+ if (djvu_document->search) {
+ djvu_text_free (djvu_document->search);
+ djvu_document->search = NULL;
+ }
+}
+
+static void
+djvu_document_find_iface_init (EvDocumentFindIface *iface)
+{
+ iface->begin = djvu_document_find_begin;
+ iface->get_n_results = djvu_document_find_get_n_results;
+ iface->get_result = djvu_document_find_get_result;
+ iface->page_has_results = djvu_document_find_page_has_results;
+ iface->get_progress = djvu_document_find_get_progress;
+ iface->cancel = djvu_document_find_cancel;
+}
+
+static GList *
+djvu_document_links_get_links (EvDocumentLinks *document_links,
+ gint page)
+{
+ return djvu_links_get_links (document_links, page, SCALE_FACTOR);
+}
+
+static void
+djvu_document_document_links_iface_init (EvDocumentLinksIface *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 0000000..402f476
--- /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 <nshmyrev@yandex.ru>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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;
+
+DjvuDocument *djvu_document_new (void);
+
+GType djvu_document_get_type (void) G_GNUC_CONST;
+
+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 0000000..38fad0d
--- /dev/null
+++ b/backend/djvu/djvu-links.c
@@ -0,0 +1,393 @@
+/* -*- 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 <pav@iki.fi>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "djvu-document.h"
+#include "djvu-links.h"
+#include "djvu-document-private.h"
+#include "ev-document-links.h"
+
+#include <libdjvu/ddjvuapi.h>
+#include <libdjvu/miniexp.h>
+#include <glib.h>
+#include <string.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;
+ gchar *end_ptr;
+
+ /* #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;
+}
+
+/**
+ * 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 ) {
+ /* 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;
+
+ 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 (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);
+
+ 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,
+ EvLinkMapping *ev_link_mapping)
+{
+ miniexp_t iter;
+ ddjvu_pageinfo_t info;
+
+ 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->x1 = minx;
+ ev_link_mapping->x2 = (minx + width);
+ ev_link_mapping->y1 = (page_info->height - (miny + height));
+ ev_link_mapping->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->x1 = minx;
+ ev_link_mapping->x2 = maxx;
+ ev_link_mapping->y1 = (page_info->height - maxy);
+ ev_link_mapping->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 EvLinkMapping *
+get_djvu_hyperlink_mapping (DjvuDocument *djvu_document,
+ int page,
+ ddjvu_pageinfo_t *page_info,
+ miniexp_t sexp)
+{
+ EvLinkMapping *ev_link_mapping = NULL;
+ EvLinkAction *ev_action = NULL;
+ miniexp_t iter;
+ const char *url, *url_target, *comment;
+
+ ev_link_mapping = g_new (EvLinkMapping, 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->link = 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);
+
+ if (outline) {
+ ddjvu_miniexp_release (djvu_document->d_document, outline);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+GList *
+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;
+ EvLinkMapping *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);
+
+ while (ddjvu_document_get_pageinfo (djvu_document->d_document, page, &page_info) < DDJVU_JOB_OK)
+ djvu_handle_events(djvu_document, TRUE);
+
+ 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->x1 *= scale_factor;
+ ev_link_mapping->x2 *= scale_factor;
+ ev_link_mapping->y1 *= scale_factor;
+ ev_link_mapping->y2 *= scale_factor;
+ retval = g_list_prepend (retval, ev_link_mapping);
+ }
+ }
+ free (hyperlinks);
+ }
+ ddjvu_miniexp_release (djvu_document->d_document, page_annotations);
+ }
+
+ return retval;
+}
+
+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 (document_links), 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);
+
+ if (outline) {
+ model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_BOOLEAN);
+ 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 0000000..fdbfdeb
--- /dev/null
+++ b/backend/djvu/djvu-links.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2006 Pauli Virtanen <pav@iki.fi>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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);
+GList *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 0000000..c19d6f6
--- /dev/null
+++ b/backend/djvu/djvu-text-page.c
@@ -0,0 +1,444 @@
+/*
+ * Implements search and copy functionality for Djvu files.
+ * Copyright (C) 2006 Michael Hofmann <mh21@piware.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "djvu-text-page.h"
+
+#include <libdjvu/miniexp.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)
+{
+ char *token_text;
+
+ 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 = g_new (EvRectangle, 1);
+ 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,
+ 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 0000000..db53326
--- /dev/null
+++ b/backend/djvu/djvu-text-page.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2006 Michael Hofmann <mh21@piware.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DJVU_TEXT_PAGE_H__
+#define __DJVU_TEXT_PAGE_H__
+
+#include "ev-document.h"
+
+#include <string.h>
+#include <glib.h>
+
+#include <libdjvu/ddjvuapi.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,
+ 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/djvu-text.c b/backend/djvu/djvu-text.c
new file mode 100644
index 0000000..beaac6b
--- /dev/null
+++ b/backend/djvu/djvu-text.c
@@ -0,0 +1,298 @@
+/*
+ * Implements search and copy functionality for Djvu files.
+ * Copyright (C) 2006 Michael Hofmann <mh21@piware.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "djvu-document-private.h"
+#include "djvu-document.h"
+#include "djvu-text.h"
+#include "djvu-text-page.h"
+#include "ev-document-find.h"
+#include "ev-document.h"
+
+#include <string.h>
+#include <glib.h>
+
+struct _DjvuText {
+ DjvuDocument *document;
+ gboolean case_sensitive;
+ char *text;
+ GList **pages;
+ guint idle;
+ int start_page;
+ int search_page;
+};
+
+/**
+ * djvu_text_idle_callback:
+ * @data: #DjvuText instance
+ *
+ * Idle callback that processes one page at a time.
+ *
+ * Returns: whether there are more pages to be processed
+ */
+static gboolean
+djvu_text_idle_callback (void *data)
+{
+ DjvuText *djvu_text = (DjvuText *) data;
+ DjvuDocument *djvu_document = djvu_text->document;
+ int n_pages;
+ miniexp_t page_text;
+
+ ev_document_doc_mutex_lock ();
+ while ((page_text =
+ ddjvu_document_get_pagetext (djvu_document->d_document,
+ djvu_text->search_page,
+ "char")) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE);
+
+ if (page_text != miniexp_nil) {
+ DjvuTextPage *page = djvu_text_page_new (page_text);
+ djvu_text_page_prepare_search (page, djvu_text->case_sensitive);
+ if (page->links->len > 0) {
+ djvu_text_page_search (page, djvu_text->text);
+ djvu_text->pages[djvu_text->search_page] = page->results;
+ ev_document_find_changed (EV_DOCUMENT_FIND
+ (djvu_document),
+ djvu_text->search_page);
+ }
+ djvu_text_page_free (page);
+ ddjvu_miniexp_release (djvu_document->d_document,
+ page_text);
+ }
+ ev_document_doc_mutex_unlock ();
+
+ n_pages =
+ djvu_document_get_n_pages (EV_DOCUMENT (djvu_text->document));
+ djvu_text->search_page += 1;
+ if (djvu_text->search_page == n_pages) {
+ /* wrap around */
+ djvu_text->search_page = 0;
+ }
+
+ if (djvu_text->search_page != djvu_text->start_page)
+ return TRUE;
+
+ /* We're done. */
+ djvu_text->idle = 0;
+ /* will return FALSE to remove */
+ return FALSE;
+}
+
+/**
+ * djvu_text_new:
+ * @djvu_document: document to search
+ * @start_page: first page to search
+ * @case_sensitive: uses g_utf8_case_fold() to enable case-insensitive
+ * searching
+ * @text: text to search
+ *
+ * Creates a new #DjvuText instance to enable searching. An idle call
+ * is used to process all pages starting from @start_page.
+ *
+ * Returns: newly created instance
+ */
+DjvuText *
+djvu_text_new (DjvuDocument *djvu_document,
+ int start_page,
+ gboolean case_sensitive,
+ const char *text)
+{
+ DjvuText *djvu_text;
+ int n_pages;
+ int i;
+
+ n_pages = djvu_document_get_n_pages (EV_DOCUMENT (djvu_document));
+
+ djvu_text = g_new0 (DjvuText, 1);
+
+ if (case_sensitive)
+ djvu_text->text = g_strdup (text);
+ else
+ djvu_text->text = g_utf8_casefold (text, -1);
+ djvu_text->pages = g_new0 (GList *, n_pages);
+ for (i = 0; i < n_pages; i++) {
+ djvu_text->pages[i] = NULL;
+ }
+
+ djvu_text->document = djvu_document;
+
+ /* We add at low priority so the progress bar repaints */
+ djvu_text->idle = g_idle_add_full (G_PRIORITY_LOW,
+ djvu_text_idle_callback,
+ djvu_text, NULL);
+
+ djvu_text->case_sensitive = case_sensitive;
+ djvu_text->start_page = start_page;
+ djvu_text->search_page = start_page;
+
+ return djvu_text;
+}
+
+/**
+ * djvu_text_copy:
+ * @djvu_document: document to search
+ * @page: page to search
+ * @rectangle: rectangle to copy
+ *
+ * Copies and returns the text in the given rectangle.
+ *
+ * Returns: newly allocated text or NULL of none is available
+ */
+char *
+djvu_text_copy (DjvuDocument *djvu_document,
+ int page,
+ EvRectangle *rectangle)
+{
+ miniexp_t page_text;
+ char* text = NULL;
+
+ while ((page_text =
+ ddjvu_document_get_pagetext (djvu_document->d_document,
+ page, "char")) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE);
+
+ 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;
+}
+
+/**
+ * djvu_text_free:
+ * @djvu_text: instance to free
+ *
+ * Frees the given #DjvuText instance.
+ */
+void djvu_text_free (DjvuText * djvu_text)
+{
+ DjvuDocument *djvu_document = djvu_text->document;
+ int n_pages;
+ int i;
+
+ if (djvu_text->idle != 0)
+ g_source_remove (djvu_text->idle);
+
+ n_pages = djvu_document_get_n_pages (EV_DOCUMENT (djvu_document));
+ for (i = 0; i < n_pages; i++) {
+ g_list_foreach (djvu_text->pages[i], (GFunc) g_free, NULL);
+ g_list_free (djvu_text->pages[i]);
+ }
+
+ g_free (djvu_text->text);
+}
+
+/**
+ * djvu_text_get_text:
+ * @djvu_text: #DjvuText instance
+ *
+ * Returns the search text. This is mainly to be able to avoid reinstantiation
+ * for the same search text.
+ *
+ * Returns: the text this instance of #DjvuText is looking for
+ */
+const char *
+djvu_text_get_text (DjvuText *djvu_text)
+{
+ return djvu_text->text;
+}
+
+/**
+ * djvu_text_n_results:
+ * @djvu_text: #DjvuText instance
+ * @page: page number
+ *
+ * Returns the number of search results available for the given page.
+ *
+ * Returns: number of search results
+ */
+int
+djvu_text_n_results (DjvuText *djvu_text,
+ int page)
+{
+ return g_list_length (djvu_text->pages[page]);
+}
+
+/**
+ * djvu_text_has_results:
+ * @djvu_text: #DjvuText instance
+ * @page: page number
+ *
+ * Returns whether there are search results available for the given page.
+ * This method executes faster than djvu_text_n_results().
+ *
+ * Returns: whether there are search results
+ */
+int
+djvu_text_has_results (DjvuText *djvu_text,
+ int page)
+{
+ return djvu_text->pages[page] != NULL;
+}
+
+/**
+ * djvu_text_get_result:
+ * @djvu_text: #DjvuText instance
+ * @page: page number
+ * @n_result: result number
+ *
+ * Returns the n-th search result of a given page. The coordinates are
+ * Djvu-specific and need to be processed to be compatible with the Evince
+ * coordinate system. The result may span several lines!
+ *
+ * Returns: the rectangle for the search result
+ */
+EvRectangle *
+djvu_text_get_result (DjvuText *djvu_text,
+ int page,
+ int n_result)
+{
+ return (EvRectangle *) g_list_nth_data (djvu_text->pages[page],
+ n_result);
+}
+
+/**
+ * djvu_text_get_progress:
+ * @djvu_text: #DjvuText instance
+ *
+ * Returns the percentage of pages done searching.
+ *
+ * Returns: the progress as value between 0 and 1
+ */
+double
+djvu_text_get_progress (DjvuText *djvu_text)
+{
+ int pages_done;
+ int n_pages;
+
+ n_pages =
+ djvu_document_get_n_pages (EV_DOCUMENT (djvu_text->document));
+ if (djvu_text->search_page > djvu_text->start_page) {
+ pages_done = djvu_text->search_page - djvu_text->start_page + 1;
+ } else if (djvu_text->search_page == djvu_text->start_page) {
+ pages_done = n_pages;
+ } else {
+ pages_done =
+ n_pages - djvu_text->start_page + djvu_text->search_page;
+ }
+ return pages_done / (double) n_pages;
+}
+
diff --git a/backend/djvu/djvu-text.h b/backend/djvu/djvu-text.h
new file mode 100644
index 0000000..0f99643
--- /dev/null
+++ b/backend/djvu/djvu-text.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2006 Michael Hofmann <mh21@piware.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DJVU_TEXT_H__
+#define __DJVU_TEXT_H__
+
+#include "ev-document.h"
+
+#include <glib.h>
+#include <libdjvu/ddjvuapi.h>
+#include <libdjvu/miniexp.h>
+
+typedef struct _DjvuText DjvuText;
+
+DjvuText *djvu_text_new (DjvuDocument *djvu_document,
+ int start_page,
+ gboolean case_sensitive,
+ const char *text);
+const char *djvu_text_get_text (DjvuText *djvu_text);
+int djvu_text_n_results (DjvuText *djvu_text,
+ int page);
+EvRectangle *djvu_text_get_result (DjvuText *djvu_text,
+ int page,
+ int n_result);
+int djvu_text_has_results (DjvuText *djvu_text,
+ int page);
+double djvu_text_get_progress (DjvuText *djvu_text);
+char *djvu_text_copy (DjvuDocument *djvu_document,
+ int page,
+ EvRectangle *rectangle);
+
+#endif /* __DJVU_TEXT_H__ */
diff --git a/backend/dvi/Makefile.am b/backend/dvi/Makefile.am
new file mode 100644
index 0000000..f23a34b
--- /dev/null
+++ b/backend/dvi/Makefile.am
@@ -0,0 +1,23 @@
+SUBDIRS = mdvi-lib
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ -I$(srcdir)/mdvi-lib \
+ $(LIB_CFLAGS)
+
+noinst_LTLIBRARIES = libgtkdvi.la
+
+libgtkdvi_la_SOURCES = \
+ dvi-document.c \
+ dvi-document.h \
+ pixbuf-device.c \
+ pixbuf-device.h \
+ fonts.c \
+ fonts.h
+
+libgtkdvi_la_LIBADD = mdvi-lib/libmdvi.la
+
+
+
+
diff --git a/backend/dvi/dvi-document.c b/backend/dvi/dvi-document.c
new file mode 100644
index 0000000..4de5e64
--- /dev/null
+++ b/backend/dvi/dvi-document.c
@@ -0,0 +1,376 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005, Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "dvi-document.h"
+#include "ev-document-thumbnails.h"
+#include "ev-document-misc.h"
+
+#include "mdvi.h"
+#include "fonts.h"
+#include "pixbuf-device.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+GMutex *dvi_context_mutex = NULL;
+
+enum {
+ PROP_0,
+ PROP_TITLE
+};
+
+struct _DviDocumentClass
+{
+ GObjectClass parent_class;
+};
+
+struct _DviDocument
+{
+ GObject 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;
+};
+
+typedef struct _DviDocumentClass DviDocumentClass;
+
+static void dvi_document_document_iface_init (EvDocumentIface *iface);
+static void dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
+static void dvi_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height);
+
+G_DEFINE_TYPE_WITH_CODE
+ (DviDocument, dvi_document, G_TYPE_OBJECT,
+ {
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT, dvi_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, dvi_document_document_thumbnails_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) {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("File not available"));
+ 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);
+
+ if (!dvi_document->context) {
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("DVI document has incorrect format"));
+ return FALSE;
+ }
+
+ mdvi_pixbuf_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,
+ int page,
+ double *width,
+ double *height)
+{
+ DviDocument * dvi_document = DVI_DOCUMENT (document);
+
+ *width = dvi_document->base_width;
+ *height = dvi_document->base_height;;
+
+ return;
+}
+
+static GdkPixbuf *
+dvi_document_render_pixbuf (EvDocument *document,
+ EvRenderContext *rc)
+{
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *rotated_pixbuf;
+
+ 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);
+
+ 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;
+ required_height = dvi_document->base_height * rc->scale;
+ 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_pixbuf_device_set_margins (&dvi_document->context->device, xmargin, ymargin);
+
+ mdvi_pixbuf_device_render (dvi_document->context);
+
+ pixbuf = mdvi_pixbuf_device_get_pixbuf (&dvi_document->context->device);
+
+ g_mutex_unlock (dvi_context_mutex);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
+ g_object_unref (pixbuf);
+
+ return rotated_pixbuf;
+}
+
+static void
+dvi_document_finalize (GObject *object)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT(object);
+
+ g_mutex_lock (dvi_context_mutex);
+ if (dvi_document->context)
+ {
+ mdvi_pixbuf_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);
+
+ g_free (dvi_document->uri);
+
+ G_OBJECT_CLASS (dvi_document_parent_class)->finalize (object);
+}
+
+
+static void
+dvi_document_class_init (DviDocumentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = dvi_document_finalize;
+
+ mdvi_init_kpathsea("evince", MDVI_MFMODE, MDVI_FALLBACK_FONT, MDVI_DPI);
+ mdvi_register_fonts ();
+
+ dvi_context_mutex = g_mutex_new ();
+}
+
+static gboolean
+dvi_document_can_get_text (EvDocument *document)
+{
+ return FALSE;
+}
+
+static EvDocumentInfo *
+dvi_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+
+ info = g_new0 (EvDocumentInfo, 1);
+
+ return info;
+}
+
+static void
+dvi_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->load = dvi_document_load;
+ iface->save = dvi_document_save;
+ iface->can_get_text = dvi_document_can_get_text;
+ iface->get_n_pages = dvi_document_get_n_pages;
+ iface->get_page_size = dvi_document_get_page_size;
+ iface->render_pixbuf = dvi_document_render_pixbuf;
+ iface->get_info = dvi_document_get_info;
+}
+
+static void
+dvi_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ gint page,
+ gint suggested_width,
+ gint *width,
+ gint *height)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT (document);
+ gdouble page_ratio;
+
+ page_ratio = dvi_document->base_height / dvi_document->base_width;
+ *width = suggested_width;
+ *height = (gint) (suggested_width * page_ratio);
+
+ return;
+}
+
+static GdkPixbuf *
+dvi_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ gint page,
+ gint rotation,
+ gint width,
+ gboolean border)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT (document);
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *border_pixbuf;
+ GdkPixbuf *rotated_pixbuf;
+ gint thumb_width, thumb_height;
+ gint proposed_width, proposed_height;
+
+ dvi_document_thumbnails_get_dimensions (document, page, width, &thumb_width, &thumb_height);
+
+ g_mutex_lock (dvi_context_mutex);
+
+ mdvi_setpage(dvi_document->context, page);
+
+ 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_pixbuf_device_set_margins (&dvi_document->context->device,
+ MAX (thumb_width - proposed_width, 0) / 2,
+ MAX (thumb_height - proposed_height, 0) / 2);
+ } else {
+ mdvi_pixbuf_device_set_margins (&dvi_document->context->device,
+ MAX (thumb_width - proposed_width - 2, 0) / 2,
+ MAX (thumb_height - proposed_height - 2, 0) / 2);
+ }
+
+
+ mdvi_pixbuf_device_render (dvi_document->context);
+ pixbuf = mdvi_pixbuf_device_get_pixbuf (&dvi_document->context->device);
+
+ g_mutex_unlock (dvi_context_mutex);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rotation);
+ g_object_unref (pixbuf);
+
+ if (border) {
+ GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
+ rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, 0, tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+ }
+
+ return rotated_pixbuf;
+}
+
+static void
+dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
+{
+ iface->get_thumbnail = dvi_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = dvi_document_thumbnails_get_dimensions;
+}
+
+
+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);
+}
diff --git a/backend/dvi/dvi-document.h b/backend/dvi/dvi-document.h
new file mode 100644
index 0000000..d92d474
--- /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 <nshmyrev@yandex.ru>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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;
+
+DviDocument *dvi_document_new (void);
+
+GType dvi_document_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __DVI_DOCUMENT_H__ */
diff --git a/backend/dvi/fonts.c b/backend/dvi/fonts.c
new file mode 100644
index 0000000..99be63c
--- /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 0000000..e55a8dd
--- /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 0000000..73417e1
--- /dev/null
+++ b/backend/dvi/mdvi-lib/Makefile.am
@@ -0,0 +1,40 @@
+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
+
diff --git a/backend/dvi/mdvi-lib/afmparse.c b/backend/dvi/mdvi-lib/afmparse.c
new file mode 100644
index 0000000..5b8ac68
--- /dev/null
+++ b/backend/dvi/mdvi-lib/afmparse.c
@@ -0,0 +1,1302 @@
+/*
+ * (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 "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 0000000..6cce780
--- /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 0000000..9014dba
--- /dev/null
+++ b/backend/dvi/mdvi-lib/bitmap.c
@@ -0,0 +1,1032 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Bitmap manipulation routines */
+
+#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)
+{
+ BITMAP *bm;
+ int i;
+ Uchar *unit;
+ register Uchar *curr;
+ Uchar *end;
+ int bytes;
+
+ DEBUG((DBG_BITMAP_OPS, "convert LSB %dx%d@8 -> 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 += bytes;
+#else
+ int j;
+
+ for(j = 0; j < bytes; curr++, j++)
+ unit[j] = bit_swap[*curr];
+#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)
+{
+ 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];
+#else
+ memcpy(unit, curr, bytes);
+ curr += bytes;
+#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 0000000..4d5f23a
--- /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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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));
+extern BITMAP *bitmap_convert_msb8 __PROTO((Uchar *, 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 0000000..366b0ea
--- /dev/null
+++ b/backend/dvi/mdvi-lib/color.c
@@ -0,0 +1,135 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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 0000000..363776e
--- /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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 0000000..7006682
--- /dev/null
+++ b/backend/dvi/mdvi-lib/common.c
@@ -0,0 +1,187 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static Int32 scaled_width(Int32 fix, int scale);
+
+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;
+}
+
+/* stolen from dvips */
+static Int32 scaled_width(Int32 fix, int scale)
+{
+ Int32 al, bl;
+
+ if(fix < 0)
+ return -scaled_width(-fix, scale);
+ if(scale < 0)
+ return -scaled_width(fix, -scale);
+ al = fix & 32767;
+ bl = scale & 32767;
+ al >>= 15;
+ bl >>= 15;
+
+ return (((al*bl / 32768) + fix*bl + al*scale) / 32 +
+ fix * scale / 1024);
+}
+
+/* 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 0000000..fe4d6f7
--- /dev/null
+++ b/backend/dvi/mdvi-lib/common.h
@@ -0,0 +1,283 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
+
+#ifdef 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 message __PROTO((const char *, ...));
+extern void crash __PROTO((const char *, ...));
+extern void fatal __PROTO((const char *, ...));
+extern void error __PROTO((const char *, ...));
+extern void 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)) crash("%s:%d: Assertion %s failed\n", \
+ __FILE__, __LINE__, #x); \
+ } while(0)
+#define ASSERT_VALUE(x,y) do { \
+ if((x) != (y)) \
+ 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));
+
+/* 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 0000000..46ce6ce
--- /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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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/dvimisc.c b/backend/dvi/mdvi-lib/dvimisc.c
new file mode 100644
index 0000000..06250ca
--- /dev/null
+++ b/backend/dvi/mdvi-lib/dvimisc.c
@@ -0,0 +1,62 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mdvi.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);
+}
+
diff --git a/backend/dvi/mdvi-lib/dviopcodes.h b/backend/dvi/mdvi-lib/dviopcodes.h
new file mode 100644
index 0000000..f99af05
--- /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 0000000..8398c27
--- /dev/null
+++ b/backend/dvi/mdvi-lib/dviread.c
@@ -0,0 +1,1584 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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) {
+ 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 (random() % 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) {
+ 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) {
+ 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, "r");
+ 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) {
+ 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) {
+ 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:
+ 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, "r");
+ if(dvi->in == NULL) {
+ 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) {
+ 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) {
+ 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->params.fg;
+ bg = dvi->params.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 0000000..0ed893b
--- /dev/null
+++ b/backend/dvi/mdvi-lib/files.c
@@ -0,0 +1,79 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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 0000000..fedb7e7
--- /dev/null
+++ b/backend/dvi/mdvi-lib/font.c
@@ -0,0 +1,516 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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, "r")) == 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 &&
+ ch->fg == dvi->curr_fg &&
+ ch->bg == dvi->curr_bg)
+ return ch;
+ if(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) {
+ 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 0000000..cc61064
--- /dev/null
+++ b/backend/dvi/mdvi-lib/fontmap.c
@@ -0,0 +1,1172 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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, "r");
+ 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, "r");
+ 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, "r");
+ else {
+ in = fopen(ptr, "r");
+ 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)) {
+ warning(
+ _("%s: %d: [%s] requested encoding `%s' does not match vector `%s'\n"),
+ file, lineno);
+ } 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, "r");
+ else {
+ in = fopen(file, "r");
+ 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)
+ 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)
+ 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)
+ 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) {
+ 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, "r");
+ 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 0000000..0a901ec
--- /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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 0000000..415ed91
--- /dev/null
+++ b/backend/dvi/mdvi-lib/fontsrch.c
@@ -0,0 +1,370 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "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)) {
+ 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 {
+ 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)) {
+ 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 0000000..2c147ec
--- /dev/null
+++ b/backend/dvi/mdvi-lib/gf.c
@@ -0,0 +1,394 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* functions to read GF fonts */
+
+#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 {
+ 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:
+ 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:
+ 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) {
+ 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:
+ 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:
+ 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 0000000..d030650
--- /dev/null
+++ b/backend/dvi/mdvi-lib/hash.c
@@ -0,0 +1,223 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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 0000000..b10afd6
--- /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 0000000..c434e2b
--- /dev/null
+++ b/backend/dvi/mdvi-lib/list.c
@@ -0,0 +1,119 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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;
+}
+
+void listh_concat(ListHead *h1, ListHead *h2)
+{
+ if(h2->head == NULL)
+ ; /* do nothing */
+ else if(h1->tail == NULL)
+ h1->head = h2->head;
+ else {
+ h1->tail->next = h2->head;
+ h2->head->prev = h1->tail;
+ }
+ h1->tail = h2->tail;
+ h1->count += h2->count;
+}
+
+void listh_catcon(ListHead *h1, ListHead *h2)
+{
+ if(h2->head == NULL)
+ ; /* do nothing */
+ else if(h1->head == NULL)
+ h1->tail = h2->tail;
+ else {
+ h1->head->prev = h2->tail;
+ h2->tail->next = h1->head;
+ }
+ h1->head = h2->head;
+ h1->count += h2->count;
+}
diff --git a/backend/dvi/mdvi-lib/mdvi.h b/backend/dvi/mdvi-lib/mdvi.h
new file mode 100644
index 0000000..961689a
--- /dev/null
+++ b/backend/dvi/mdvi-lib/mdvi.h
@@ -0,0 +1,618 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _MDVI_DVI_H
+#define _MDVI_DVI_H 1
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <math.h>
+
+#include "sysdeps.h"
+#include "bitmap.h"
+#include "common.h"
+#include "defaults.h"
+#include "dviopcodes.h"
+
+typedef struct _DviGlyph DviGlyph;
+typedef struct _DviDevice DviDevice;
+typedef struct _DviFontChar DviFontChar;
+typedef struct _DviFontRef DviFontRef;
+typedef struct _DviFontInfo DviFontInfo;
+typedef struct _DviFont DviFont;
+typedef struct _DviState DviState;
+typedef struct _DviPageSpec *DviPageSpec;
+typedef struct _DviParams DviParams;
+typedef struct _DviBuffer DviBuffer;
+typedef struct _DviContext DviContext;
+typedef struct _DviRange DviRange;
+typedef struct _DviColorPair DviColorPair;
+typedef struct _DviSection DviSection;
+typedef struct _TFMChar TFMChar;
+typedef struct _TFMInfo TFMInfo;
+typedef struct _DviFontSearch DviFontSearch;
+/* this is an opaque type */
+typedef struct _DviFontClass DviFontClass;
+
+typedef void (*DviFreeFunc) __PROTO((void *));
+typedef void (*DviFree2Func) __PROTO((void *, void *));
+
+typedef Ulong DviColor;
+
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+
+typedef enum {
+ FALSE = 0,
+ TRUE = 1
+} DviBool;
+
+#include "hash.h"
+#include "paper.h"
+
+/*
+ * information about a page:
+ * pagenum[0] = offset to BOP
+ * pagenum[1], ..., pagenum[10] = TeX \counters
+ */
+typedef long PageNum[11];
+
+/* this structure contains the platform-specific information
+ * required to interpret a DVI file */
+
+typedef void (*DviGlyphDraw) __PROTO((DviContext *context,
+ DviFontChar *glyph,
+ int x, int y));
+
+typedef void (*DviRuleDraw) __PROTO((DviContext *context,
+ int x, int y,
+ Uint width, Uint height, int fill));
+
+typedef int (*DviColorScale) __PROTO((void *device_data,
+ Ulong *pixels,
+ int npixels,
+ Ulong foreground,
+ Ulong background,
+ double gamma,
+ int density));
+typedef void *(*DviCreateImage) __PROTO((void *device_data,
+ Uint width,
+ Uint height,
+ Uint bpp));
+typedef void (*DviFreeImage) __PROTO((void *image));
+typedef void (*DviPutPixel) __PROTO((void *image, int x, int y, Ulong color));
+typedef void (*DviDevDestroy) __PROTO((void *data));
+typedef void (*DviRefresh) __PROTO((DviContext *dvi, void *device_data));
+typedef void (*DviSetColor) __PROTO((void *device_data, Ulong, Ulong));
+
+struct _DviDevice {
+ DviGlyphDraw draw_glyph;
+ DviRuleDraw draw_rule;
+ DviColorScale alloc_colors;
+ DviCreateImage create_image;
+ DviFreeImage free_image;
+ DviPutPixel put_pixel;
+ DviDevDestroy dev_destroy;
+ DviRefresh refresh;
+ DviSetColor set_color;
+ void * device_data;
+};
+
+/*
+ * Fonts
+ */
+
+#include "fontmap.h"
+
+struct _TFMChar {
+ Int32 present;
+ Int32 advance; /* advance */
+ Int32 height; /* ascent */
+ Int32 depth; /* descent */
+ Int32 left; /* leftSideBearing */
+ Int32 right; /* rightSideBearing */
+};
+
+struct _TFMInfo {
+ int type; /* DviFontAFM, DviFontTFM, DviFontOFM */
+ Uint32 checksum;
+ Uint32 design;
+ int loc;
+ int hic;
+ char coding[64];
+ char family[64];
+ TFMChar *chars;
+};
+
+struct _DviGlyph {
+ short x, y; /* origin */
+ Uint w, h; /* dimensions */
+ void *data; /* bitmap or XImage */
+};
+
+typedef void (*DviFontShrinkFunc)
+ __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *));
+typedef int (*DviFontLoadFunc) __PROTO((DviParams *, DviFont *));
+typedef int (*DviFontGetGlyphFunc) __PROTO((DviParams *, DviFont *, int));
+typedef void (*DviFontFreeFunc) __PROTO((DviFont *));
+typedef void (*DviFontResetFunc) __PROTO((DviFont *));
+typedef char *(*DviFontLookupFunc) __PROTO((const char *, Ushort *, Ushort *));
+typedef int (*DviFontEncodeFunc) __PROTO((DviParams *, DviFont *, DviEncoding *));
+
+struct _DviFontInfo {
+ char *name; /* human-readable format identifying string */
+ int scalable; /* does it support scaling natively? */
+ DviFontLoadFunc load;
+ DviFontGetGlyphFunc getglyph;
+ DviFontShrinkFunc shrink0;
+ DviFontShrinkFunc shrink1;
+ DviFontFreeFunc freedata;
+ DviFontResetFunc reset;
+ DviFontLookupFunc lookup;
+ int kpse_type;
+ void * private;
+};
+
+struct _DviFontChar {
+ Uint32 offset;
+ Int16 code; /* format-dependent, not used by MDVI */
+ Int16 width;
+ Int16 height;
+ Int16 x;
+ Int16 y;
+ Int32 tfmwidth;
+ Ushort flags;
+#ifdef __STRICT_ANSI__
+ Ushort loaded;
+ Ushort missing;
+#else
+ Ushort loaded : 1,
+ missing : 1;
+#endif
+ Ulong fg;
+ Ulong bg;
+ BITMAP *glyph_data;
+ /* data for shrunk bitimaps */
+ DviGlyph glyph;
+ DviGlyph shrunk;
+ DviGlyph grey;
+};
+
+struct _DviFontRef {
+ DviFontRef *next;
+ DviFont *ref;
+ Int32 fontid;
+};
+
+typedef enum {
+ DviFontAny = -1,
+ DviFontPK = 0,
+ DviFontGF = 1,
+ DviFontVF = 2,
+ DviFontTFM = 3,
+ DviFontT1 = 4,
+ DviFontTT = 5,
+ DviFontAFM = 6,
+ DviFontOFM = 7
+} DviFontType;
+
+struct _DviFontSearch {
+ int id;
+ Ushort hdpi;
+ Ushort vdpi;
+ Ushort actual_hdpi;
+ Ushort actual_vdpi;
+ const char *wanted_name;
+ const char *actual_name;
+ DviFontClass *curr;
+ DviFontInfo *info;
+};
+
+/* this is a kludge, I know */
+#define ISVIRTUAL(font) ((font)->search.info->getglyph == NULL)
+#define SEARCH_DONE(s) ((s).id < 0)
+#define SEARCH_INIT(s, name, h, v) do { \
+ (s).id = 0; \
+ (s).curr = NULL; \
+ (s).hdpi = (h); \
+ (s).vdpi = (v); \
+ (s).wanted_name = (name); \
+ (s).actual_name = NULL; \
+ } while(0)
+
+struct _DviFont {
+ DviFont *next;
+ DviFont *prev;
+ int type;
+ Int32 checksum;
+ int hdpi;
+ int vdpi;
+ Int32 scale;
+ Int32 design;
+ FILE *in;
+ char *fontname;
+ char *filename;
+ int links;
+ int loc;
+ int hic;
+ Uint flags;
+ DviFontSearch search;
+ DviFontChar *chars;
+ DviFontRef *subfonts;
+ void *private;
+};
+
+/*
+ * Dvi context
+ */
+
+typedef enum {
+ MDVI_ORIENT_TBLR = 0, /* top to bottom, left to right */
+ MDVI_ORIENT_TBRL = 1, /* top to bottom, right to left */
+ MDVI_ORIENT_BTLR = 2, /* bottom to top, left to right */
+ MDVI_ORIENT_BTRL = 3, /* bottom to top, right to left */
+ MDVI_ORIENT_RP90 = 4, /* rotated +90 degrees (counter-clockwise) */
+ MDVI_ORIENT_RM90 = 5, /* rotated -90 degrees (clockwise) */
+ MDVI_ORIENT_IRP90 = 6, /* flip horizontally, then rotate by +90 */
+ MDVI_ORIENT_IRM90 = 7 /* rotate by -90, then flip horizontally */
+} DviOrientation;
+
+typedef enum {
+ MDVI_PAGE_SORT_UP, /* up, using \counter0 */
+ MDVI_PAGE_SORT_DOWN, /* down, using \counter0 */
+ MDVI_PAGE_SORT_RANDOM, /* randomly */
+ MDVI_PAGE_SORT_DVI_UP, /* up, by location in DVI file */
+ MDVI_PAGE_SORT_DVI_DOWN, /* down, by location in DVI file */
+ MDVI_PAGE_SORT_NONE /* don't sort */
+} DviPageSort;
+
+struct _DviParams {
+ double mag; /* magnification */
+ double conv; /* horizontal DVI -> pixel */
+ double vconv; /* vertical DVI -> pixel */
+ double tfm_conv; /* TFM -> DVI */
+ double gamma; /* gamma correction factor */
+ Uint dpi; /* horizontal resolution */
+ Uint vdpi; /* vertical resolution */
+ int hshrink; /* horizontal shrinking factor */
+ int vshrink; /* vertical shrinking factor */
+ Uint density; /* pixel density */
+ Uint flags; /* flags (see MDVI_PARAM macros) */
+ int hdrift; /* max. horizontal drift */
+ int vdrift; /* max. vertical drift */
+ int vsmallsp; /* small vertical space */
+ int thinsp; /* small horizontal space */
+ int layer; /* visible layer (for layered DVI files) */
+ Ulong fg; /* foreground color */
+ Ulong bg; /* background color */
+ DviOrientation orientation; /* page orientation */
+ int base_x;
+ int base_y;
+};
+
+typedef enum {
+ MDVI_PARAM_LAST = 0,
+ MDVI_SET_DPI = 1,
+ MDVI_SET_XDPI = 2,
+ MDVI_SET_YDPI = 3,
+ MDVI_SET_SHRINK = 4,
+ MDVI_SET_XSHRINK = 5,
+ MDVI_SET_YSHRINK = 6,
+ MDVI_SET_GAMMA = 7,
+ MDVI_SET_DENSITY = 8,
+ MDVI_SET_MAGNIFICATION = 9,
+ MDVI_SET_DRIFT = 10,
+ MDVI_SET_HDRIFT = 11,
+ MDVI_SET_VDRIFT = 12,
+ MDVI_SET_ORIENTATION = 13,
+ MDVI_SET_FOREGROUND = 14,
+ MDVI_SET_BACKGROUND = 15
+} DviParamCode;
+
+struct _DviBuffer {
+ Uchar *data;
+ size_t size; /* allocated size */
+ size_t length; /* amount of data buffered */
+ size_t pos; /* current position in buffer */
+ int frozen; /* can we free this data? */
+};
+
+/* DVI registers */
+struct _DviState {
+ int h;
+ int v;
+ int hh;
+ int vv;
+ int w;
+ int x;
+ int y;
+ int z;
+};
+
+struct _DviColorPair {
+ Ulong fg;
+ Ulong bg;
+};
+
+struct _DviContext {
+ char *filename; /* name of the DVI file */
+ FILE *in; /* from here we read */
+ char *fileid; /* from preamble */
+ int npages; /* number of pages */
+ int currpage; /* currrent page (0 based) */
+ int depth; /* recursion depth */
+ DviBuffer buffer; /* input buffer */
+ DviParams params; /* parameters */
+ DviPaper paper; /* paper type */
+ Int32 num; /* numerator */
+ Int32 den; /* denominator */
+ DviFontRef *fonts; /* fonts used in this file */
+ DviFontRef **fontmap; /* for faster id lookups */
+ DviFontRef *currfont; /* current font */
+ int nfonts; /* # of fonts used in this job */
+ Int32 dvimag; /* original magnification */
+ double dviconv; /* unshrunk scaling factor */
+ double dvivconv; /* unshrunk scaling factor (vertical) */
+ int dvi_page_w; /* unscaled page width */
+ int dvi_page_h; /* unscaled page height */
+ Ulong modtime; /* file modification time */
+ PageNum *pagemap; /* page table */
+ DviState pos; /* registers */
+ DviPageSpec *pagesel; /* page selection data */
+ int curr_layer; /* current layer */
+ DviState *stack; /* DVI stack */
+ int stacksize; /* stack depth */
+ int stacktop; /* stack pointer */
+ DviDevice device; /* device-specific routines */
+ Ulong curr_fg; /* rendering color */
+ Ulong curr_bg;
+
+ DviColorPair *color_stack;
+ int color_top;
+ int color_size;
+
+ DviFontRef *(*findref) __PROTO((DviContext *, Int32));
+ void *user_data; /* client data attached to this context */
+};
+
+typedef enum {
+ MDVI_RANGE_BOUNDED, /* range is finite */
+ MDVI_RANGE_LOWER, /* range has a lower bound */
+ MDVI_RANGE_UPPER, /* range has an upper bound */
+ MDVI_RANGE_UNBOUNDED /* range has no bounds at all */
+} DviRangeType;
+
+struct _DviRange {
+ DviRangeType type; /* one of the above */
+ int from; /* lower bound */
+ int to; /* upper bound */
+ int step; /* step */
+};
+
+
+typedef void (*DviSpecialHandler)
+ __PROTO((DviContext *dvi, const char *prefix, const char *arg));
+
+#define RANGE_HAS_LOWER(x) \
+ ((x) == MDVI_RANGE_BOUNDED || (x) == MDVI_RANGE_LOWER)
+#define RANGE_HAS_UPPER(x) \
+ ((x) == MDVI_RANGE_BOUNDED || (x) == MDVI_RANGE_UPPER)
+
+/*
+ * Macros and prototypes
+ */
+
+#define MDVI_PARAM_ANTIALIASED 1
+#define MDVI_PARAM_MONO 2
+#define MDVI_PARAM_CHARBOXES 4
+#define MDVI_PARAM_SHOWUNDEF 8
+#define MDVI_PARAM_DELAYFONTS 16
+
+/*
+ * The FALLBACK priority class is reserved for font formats that
+ * contain no glyph information and are to be used as a last
+ * resort (e.g. TFM, AFM)
+ */
+#define MDVI_FONTPRIO_FALLBACK -3
+#define MDVI_FONTPRIO_LOWEST -2
+#define MDVI_FONTPRIO_LOW -1
+#define MDVI_FONTPRIO_NORMAL 0
+#define MDVI_FONTPRIO_HIGH 1
+#define MDVI_FONTPRIO_HIGHEST 2
+
+#define MDVI_FONT_ENCODED (1 << 0)
+
+#define MDVI_GLYPH_EMPTY ((void *)1)
+/* does the glyph have a non-empty bitmap/image? */
+#define MDVI_GLYPH_NONEMPTY(x) ((x) && (x) != MDVI_GLYPH_EMPTY)
+/* has the glyph been loaded from disk? */
+#define MDVI_GLYPH_UNSET(x) ((x) == NULL)
+/* do we have only a bounding box for this glyph? */
+#define MDVI_GLYPH_ISEMPTY(x) ((x) == MDVI_GLYPH_EMPTY)
+
+#define MDVI_ENABLED(d,x) ((d)->params.flags & (x))
+#define MDVI_DISABLED(d,x) !MDVI_ENABLED((d), (x))
+
+#define MDVI_LASTPAGE(d) ((d)->npages - 1)
+#define MDVI_NPAGES(d) (d)->npages
+#define MDVI_VALIDPAGE(d,p) ((p) >= 0 && (p) <= MDVI_LASTPAGE(d))
+#define MDVI_FLAGS(d) (d)->params.flags
+#define MDVI_SHRINK_FROM_DPI(d) Max(1, (d) / 75)
+#define MDVI_CURRFG(d) (d)->curr_fg
+#define MDVI_CURRBG(d) (d)->curr_bg
+
+#define pixel_round(d,v) (int)((d)->params.conv * (v) + 0.5)
+#define vpixel_round(d,v) (int)((d)->params.vconv * (v) + 0.5)
+#define rule_round(d,v) (int)((d)->params.conv * (v) + 0.99999) /*9999999)*/
+#define vrule_round(d,v) (int)((d)->params.vconv * (v) + 0.99999)
+
+extern int mdvi_reload __PROTO((DviContext *, DviParams *));
+extern void mdvi_setpage __PROTO((DviContext *, int));
+extern int mdvi_dopage __PROTO((DviContext *, int));
+extern void mdvi_shrink_glyph __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *));
+extern void mdvi_shrink_box __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *));
+extern void mdvi_shrink_glyph_grey __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *));
+extern int mdvi_find_tex_page __PROTO((DviContext *, int));
+extern int mdvi_configure __PROTO((DviContext *, DviParamCode, ...));
+
+extern int get_tfm_chars __PROTO((DviParams *, DviFont *, TFMInfo *, int));
+extern int tfm_load_file __PROTO((const char *, TFMInfo *));
+extern int afm_load_file __PROTO((const char *, TFMInfo *));
+extern TFMInfo *get_font_metrics __PROTO((const char *, int, const char *));
+extern char *lookup_font_metrics __PROTO((const char *, int *));
+extern void free_font_metrics __PROTO((TFMInfo *));
+extern void flush_font_metrics __PROTO((void));
+
+#define get_metrics(name) get_font_metrics((name), DviFontAny, NULL)
+
+extern void mdvi_sort_pages __PROTO((DviContext *, DviPageSort));
+
+extern void mdvi_init_kpathsea __PROTO((const char *, const char *, const char *, int));
+
+extern DviContext* mdvi_init_context __PROTO((DviParams *, DviPageSpec *, const char *));
+extern void mdvi_destroy_context __PROTO((DviContext *));
+
+/* helper macros that call mdvi_configure() */
+#define mdvi_config_one(d,x,y) mdvi_configure((d), (x), (y), MDVI_PARAM_LAST)
+#define mdvi_set_dpi(d,x) mdvi_config_one((d), MDVI_SET_DPI, (x))
+#define mdvi_set_xdpi(d,x) mdvi_config_one((d), MDVI_SET_XDPI, (x))
+#define mdvi_set_ydpi(d,x) mdvi_config_one((d), MDVI_SET_YDPI, (x))
+#define mdvi_set_hshrink(d,h) mdvi_config_one((d), MDVI_SET_XSHRINK, (h))
+#define mdvi_set_vshrink(d,h) mdvi_config_one((d), MDVI_SET_YSHRINK, (h))
+#define mdvi_set_gamma(d,g) mdvi_config_one((d), MDVI_SET_GAMMA, (g))
+#define mdvi_set_density(d,x) mdvi_config_one((d), MDVI_SET_DENSITY, (x))
+#define mdvi_set_drift(d,x) mdvi_config_one((d), MDVI_SET_DRIFT, (x))
+#define mdvi_set_hdrift(d,h) mdvi_config_one((d), MDVI_SET_HDRIFT, (h))
+#define mdvi_set_vdrift(d,v) mdvi_config_one((d), MDVI_SET_VDRIFT, (v))
+#define mdvi_set_mag(d,m) \
+ mdvi_config_one((d), MDVI_SET_MAGNIFICATION, (m))
+#define mdvi_set_foreground(d,x) \
+ mdvi_config_one((d), MDVI_SET_FOREGROUND, (x))
+#define mdvi_set_background(d,x) \
+ mdvi_config_one((d), MDVI_SET_BACKGROUND, (x))
+#define mdvi_set_orientation(d,x) \
+ mdvi_config_one((d), MDVI_SET_ORIENTATION, (x))
+#define mdvi_set_shrink(d,h,v) \
+ mdvi_configure((d), MDVI_SET_XSHRINK, (h), \
+ MDVI_SET_YSHRINK, (v), MDVI_PARAM_LAST)
+
+extern DviRange* mdvi_parse_range __PROTO((const char *, DviRange *, int *, char **));
+extern DviPageSpec* mdvi_parse_page_spec __PROTO((const char *));
+extern void mdvi_free_page_spec __PROTO((DviPageSpec *));
+extern int mdvi_in_range __PROTO((DviRange *, int, int));
+extern int mdvi_range_length __PROTO((DviRange *, int));
+extern int mdvi_page_selected __PROTO((DviPageSpec *, PageNum, int));
+
+/* Specials */
+extern int mdvi_register_special __PROTO((
+ const char *label,
+ const char *prefix,
+ const char *regex,
+ DviSpecialHandler handler,
+ int replace));
+extern int mdvi_unregister_special __PROTO((const char *prefix));
+extern int mdvi_do_special __PROTO((DviContext *dvi, char *dvi_special));
+extern void mdvi_flush_specials __PROTO((void));
+
+/* Fonts */
+
+#define MDVI_FONTSEL_BITMAP (1 << 0)
+#define MDVI_FONTSEL_GREY (1 << 1)
+#define MDVI_FONTSEL_GLYPH (1 << 2)
+
+#define FONTCHAR(font, code) \
+ (((code) < font->loc || (code) > font->hic || !(font)->chars) ? \
+ NULL : &font->chars[(code) - (font)->loc])
+#define FONT_GLYPH_COUNT(font) ((font)->hic - (font)->loc + 1)
+
+#define glyph_present(x) ((x) && (x)->offset)
+
+/* create a reference to a font */
+extern DviFontRef *font_reference __PROTO((DviParams *params,
+ Int32 dvi_id,
+ const char *font_name,
+ Int32 checksum,
+ int xdpi,
+ int ydpi,
+ Int32 scale_factor));
+
+/* drop a reference to a font */
+extern void font_drop_one __PROTO((DviFontRef *));
+
+/* drop a chain of references */
+extern void font_drop_chain __PROTO((DviFontRef *));
+
+/* destroy selected information for a glyph */
+extern void font_reset_one_glyph __PROTO((DviDevice *, DviFontChar *, int));
+
+/* destroy selected information for all glyphs in a font */
+extern void font_reset_font_glyphs __PROTO((DviDevice *, DviFont *, int));
+
+/* same for a chain of font references */
+extern void font_reset_chain_glyphs __PROTO((DviDevice *, DviFontRef *, int));
+
+extern void font_finish_definitions __PROTO((DviContext *));
+
+/* lookup an id # in a reference chain */
+extern DviFontRef* font_find_flat __PROTO((DviContext *, Int32));
+extern DviFontRef* font_find_mapped __PROTO((DviContext *, Int32));
+
+/* called to reopen (or rewind) a font file */
+extern int font_reopen __PROTO((DviFont *));
+
+/* reads a glyph from a font, and makes all necessary transformations */
+extern DviFontChar* font_get_glyph __PROTO((DviContext *, DviFont *, int));
+
+/* transform a glyph according to the given orientation */
+extern void font_transform_glyph __PROTO((DviOrientation, DviGlyph *));
+
+/* destroy all fonts that are not being used, returns number of fonts freed */
+extern int font_free_unused __PROTO((DviDevice *));
+
+#define font_free_glyph(dev, font, code) \
+ font_reset_one_glyph((dev), \
+ FONTCHAR((font), (code)), MDVI_FONTSEL_GLYPH)
+
+extern int mdvi_encode_font __PROTO((DviParams *, DviFont *));
+
+
+/* font lookup functions */
+extern int mdvi_register_font_type __PROTO((DviFontInfo *, int));
+extern char **mdvi_list_font_class __PROTO((int));
+extern int mdvi_get_font_classes __PROTO((void));
+extern int mdvi_unregister_font_type __PROTO((const char *, int));
+extern char *mdvi_lookup_font __PROTO((DviFontSearch *));
+extern DviFont *mdvi_add_font __PROTO((const char *, Int32, int, int, Int32));
+extern int mdvi_font_retry __PROTO((DviParams *, DviFont *));
+
+/* Miscellaneous */
+
+extern int mdvi_set_logfile __PROTO((const char *));
+extern int mdvi_set_logstream __PROTO((FILE *));
+extern int mdvi_set_loglevel __PROTO((int));
+
+#define mdvi_stop_logging(x) mdvi_set_logstream(NULL)
+
+/* this will check the environment and then `texmf.cnf' for
+ * the given name changed to lowercase, and `_' changed to `-' */
+extern char* mdvi_getenv __PROTO((const char *));
+
+#endif /* _MDVI_DVI_H */
diff --git a/backend/dvi/mdvi-lib/pagesel.c b/backend/dvi/mdvi-lib/pagesel.c
new file mode 100644
index 0000000..b24157c
--- /dev/null
+++ b/backend/dvi/mdvi-lib/pagesel.c
@@ -0,0 +1,490 @@
+/* pagesel.c -- Page selection mechanism */
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+char *program_name = "page";
+
+struct _DviPageSpec {
+ DviRange *ranges;
+ int nranges;
+};
+
+DviRange *mdvi_parse_range(const char *format, DviRange *limit, int *nitems, char **endptr)
+{
+ int quoted;
+ int size;
+ int curr;
+ int done;
+ int lower;
+ int upper;
+ int type;
+ char * cp;
+ char * copy;
+ char * text;
+ DviRange one;
+ DviRange *range;
+
+ quoted = (*format == '{');
+ if(quoted) format++;
+
+ size = 0;
+ curr = 0;
+ range = NULL;
+ copy = mdvi_strdup(format);
+ done = 0;
+ lower = 0;
+ upper = 0;
+ type = MDVI_RANGE_UNBOUNDED;
+
+ if(limit) {
+ switch(limit->type) {
+ case MDVI_RANGE_BOUNDED:
+ lower = limit->from;
+ upper = limit->to;
+ break;
+ case MDVI_RANGE_UPPER:
+ lower = INT_MIN;
+ upper = limit->to;
+ break;
+ case MDVI_RANGE_LOWER:
+ lower = limit->from;
+ upper = INT_MAX;
+ break;
+ case MDVI_RANGE_UNBOUNDED:
+ lower = INT_MIN;
+ upper = INT_MAX;
+ break;
+ }
+ type = limit->type;
+ } else {
+ lower = INT_MIN;
+ upper = INT_MAX;
+ type = MDVI_RANGE_UNBOUNDED;
+ }
+ one.type = type;
+ one.from = lower;
+ one.to = upper;
+ one.step = 1;
+ for(cp = text = copy; !done; cp++) {
+ char *p;
+ int f, t, s;
+ int ch;
+ int this_type;
+ int lower_given = 0;
+ int upper_given = 0;
+
+ if(*cp == 0 || *cp == '.' || (*cp == '}' && quoted))
+ done = 1;
+ else if(*cp != ',')
+ continue;
+
+ if(text == cp)
+ continue;
+ ch = *cp;
+ *cp = 0;
+ f = lower;
+ t = upper;
+ s = 1;
+
+ p = strchr(text, ':');
+ if(p) *p++ = 0;
+ if(*text) {
+ lower_given = 1;
+ f = strtol(text, NULL, 0);
+ }
+ if(p == NULL) {
+ if(lower_given) {
+ upper_given = 1;
+ t = f; s = 1;
+ }
+ goto finish;
+ }
+ text = p;
+ p = strchr(text, ':');
+ if(p) *p++ = 0;
+ if(*text) {
+ upper_given = 1;
+ t = strtol(text, NULL, 0);
+ }
+ if(p == NULL)
+ goto finish;
+ text = p;
+ if(*text)
+ s = strtol(text, NULL, 0);
+finish:
+ if(lower_given && upper_given)
+ this_type = MDVI_RANGE_BOUNDED;
+ else if(lower_given) {
+ if(!RANGE_HAS_UPPER(type))
+ this_type = MDVI_RANGE_LOWER;
+ else
+ this_type = MDVI_RANGE_BOUNDED;
+ t = upper;
+ } else if(upper_given) {
+ if(RANGE_HAS_UPPER(one.type)) {
+ one.to++;
+ this_type = MDVI_RANGE_BOUNDED;
+ } else {
+ one.to = lower;
+ if(!RANGE_HAS_LOWER(type))
+ this_type = MDVI_RANGE_UPPER;
+ else
+ this_type = MDVI_RANGE_BOUNDED;
+ }
+ f = one.to;
+ } else {
+ this_type = type;
+ f = lower;
+ t = upper;
+ }
+ one.type = this_type;
+ one.to = t;
+ one.from = f;
+ one.step = s;
+
+ if(curr == size) {
+ size += 8;
+ range = mdvi_realloc(range, size * sizeof(DviRange));
+ }
+ memcpy(&range[curr++], &one, sizeof(DviRange));
+ *cp = ch;
+ text = cp + 1;
+ }
+ if(done)
+ cp--;
+ if(quoted && *cp == '}')
+ cp++;
+ if(endptr)
+ *endptr = (char *)format + (cp - copy);
+ if(curr && curr < size)
+ range = mdvi_realloc(range, curr * sizeof(DviRange));
+ *nitems = curr;
+ mdvi_free(copy);
+ return range;
+}
+
+DviPageSpec *mdvi_parse_page_spec(const char *format)
+{
+ /*
+ * a page specification looks like this:
+ * '{'RANGE_SPEC'}' for a DVI spec
+ * '{'RANGE_SPEC'}' '.' ... for a TeX spec
+ */
+ DviPageSpec *spec;
+ DviRange *range;
+ int count;
+ int i;
+ char *ptr;
+
+ spec = xnalloc(struct _DviPageSpec *, 11);
+ for(i = 0; i < 11; i++)
+ spec[i] = NULL;
+
+ /* check what kind of spec we're parsing */
+ if(*format != '*') {
+ range = mdvi_parse_range(format, NULL, &count, &ptr);
+ if(ptr == format) {
+ if(range) mdvi_free(range);
+ error(_("invalid page specification `%s'\n"), format);
+ return NULL;
+ }
+ } else
+ range = NULL;
+
+ if(*format == 'D' || *format == 'd' || *ptr != '.')
+ i = 0;
+ else
+ i = 1;
+
+ if(range) {
+ spec[i] = xalloc(struct _DviPageSpec);
+ spec[i]->ranges = range;
+ spec[i]->nranges = count;
+ } else
+ spec[i] = NULL;
+
+ if(*ptr != '.') {
+ if(*ptr)
+ warning(_("garbage after DVI page specification ignored\n"));
+ return spec;
+ }
+
+ for(i++; *ptr == '.' && i <= 10; i++) {
+ ptr++;
+ if(*ptr == '*') {
+ ptr++;
+ range = NULL;
+ } else {
+ char *end;
+
+ range = mdvi_parse_range(ptr, NULL, &count, &end);
+ if(end == ptr) {
+ if(range) mdvi_free(range);
+ range = NULL;
+ } else
+ ptr = end;
+ }
+ if(range != NULL) {
+ spec[i] = xalloc(struct _DviPageSpec);
+ spec[i]->ranges = range;
+ spec[i]->nranges = count;
+ } else
+ spec[i] = NULL;
+ }
+
+ if(i > 10)
+ warning(_("more than 10 counters in page specification\n"));
+ else if(*ptr)
+ warning(_("garbage after TeX page specification ignored\n"));
+
+ return spec;
+}
+
+/* returns non-zero if the given page is included by `spec' */
+int mdvi_page_selected(DviPageSpec *spec, PageNum page, int dvipage)
+{
+ int i;
+ int not_found;
+
+ if(spec == NULL)
+ return 1;
+ if(spec[0]) {
+ not_found = mdvi_in_range(spec[0]->ranges,
+ spec[0]->nranges, dvipage);
+ if(not_found < 0)
+ return 0;
+ }
+ for(i = 1; i <= 10; i++) {
+ if(spec[i] == NULL)
+ continue;
+ not_found = mdvi_in_range(spec[i]->ranges,
+ spec[i]->nranges, (int)page[i]);
+ if(not_found < 0)
+ return 0;
+ }
+ return 1;
+}
+
+void mdvi_free_page_spec(DviPageSpec *spec)
+{
+ int i;
+
+ for(i = 0; i < 11; i++)
+ if(spec[i]) {
+ mdvi_free(spec[i]->ranges);
+ mdvi_free(spec[i]);
+ }
+ mdvi_free(spec);
+}
+
+int mdvi_in_range(DviRange *range, int nitems, int value)
+{
+ DviRange *r;
+
+ for(r = range; r < range + nitems; r++) {
+ int cond;
+
+ switch(r->type) {
+ case MDVI_RANGE_BOUNDED:
+ if(value == r->from)
+ return (r - range);
+ if(r->step < 0)
+ cond = (value <= r->from) && (value >= r->to);
+ else
+ cond = (value <= r->to) && (value >= r->from);
+ if(cond && ((value - r->from) % r->step) == 0)
+ return (r - range);
+ break;
+ case MDVI_RANGE_LOWER:
+ if(value == r->from)
+ return (r - range);
+ if(r->step < 0)
+ cond = (value < r->from);
+ else
+ cond = (value > r->from);
+ if(cond && ((value - r->from) % r->step) == 0)
+ return (r - range);
+ break;
+ case MDVI_RANGE_UPPER:
+ if(value == r->to)
+ return (r - range);
+ if(r->step < 0)
+ cond = (value > r->to);
+ else
+ cond = (value < r->to);
+ if(cond && ((value - r->to) % r->step) == 0)
+ return (r - range);
+ break;
+ case MDVI_RANGE_UNBOUNDED:
+ if((value % r->step) == 0)
+ return (r - range);
+ break;
+ }
+ }
+ return -1;
+}
+
+int mdvi_range_length(DviRange *range, int nitems)
+{
+ int count = 0;
+ DviRange *r;
+
+ for(r = range; r < range + nitems; r++) {
+ int n;
+
+ if(r->type != MDVI_RANGE_BOUNDED)
+ return -2;
+ n = (r->to - r->from) / r->step;
+ if(n < 0)
+ n = 0;
+ count += n + 1;
+ }
+ return count;
+}
+
+#ifdef TEST
+
+void print_range(DviRange *range)
+{
+ switch(range->type) {
+ case MDVI_RANGE_BOUNDED:
+ printf("From %d to %d, step %d\n",
+ range->from, range->to, range->step);
+ break;
+ case MDVI_RANGE_LOWER:
+ printf("From %d, step %d\n",
+ range->from, range->step);
+ break;
+ case MDVI_RANGE_UPPER:
+ printf("From %d, step -%d\n",
+ range->to, range->step);
+ break;
+ case MDVI_RANGE_UNBOUNDED:
+ printf("From 0, step %d and %d\n",
+ range->step, -range->step);
+ break;
+ }
+}
+
+int main()
+{
+#if 0
+ char buf[256];
+ DviRange limit;
+
+ limit.from = 0;
+ limit.to = 100;
+ limit.step = 2;
+ limit.type = MDVI_RANGE_UNBOUNDED;
+ while(1) {
+ DviRange *range;
+ char *end;
+ int count;
+ int i;
+
+ printf("Range> "); fflush(stdout);
+ if(fgets(buf, 256, stdin) == NULL)
+ break;
+ if(buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = 0;
+ if(buf[0] == 0)
+ continue;
+ end = NULL;
+ range = mdvi_parse_range(buf, &limit, &count, &end);
+ if(range == NULL) {
+ printf("range is empty\n");
+ continue;
+ }
+
+ for(i = 0; i < count; i++) {
+ printf("Range %d (%d elements):\n",
+ i, mdvi_range_length(&range[i], 1));
+ print_range(&range[i]);
+ }
+ if(end && *end)
+ printf("Tail: [%s]\n", end);
+ printf("range has %d elements\n",
+ mdvi_range_length(range, count));
+#if 1
+ while(1) {
+ int v;
+
+ printf("Value: "); fflush(stdout);
+ if(fgets(buf, 256, stdin) == NULL)
+ break;
+ if(buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = 0;
+ if(buf[0] == 0)
+ break;
+ v = atoi(buf);
+ i = mdvi_in_range(range, count, v);
+ if(i == -1)
+ printf("%d not in range\n", v);
+ else {
+ printf("%d in range: ", v);
+ print_range(&range[i]);
+ }
+ }
+#endif
+ if(range) mdvi_free(range);
+ }
+#else
+ DviPageSpec *spec;
+ char buf[256];
+
+ while(1) {
+ printf("Spec> "); fflush(stdout);
+ if(fgets(buf, 256, stdin) == NULL)
+ break;
+ if(buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = 0;
+ if(buf[0] == 0)
+ continue;
+ spec = mdvi_parse_page_spec(buf);
+ if(spec == NULL)
+ printf("no spec parsed\n");
+ else {
+ int i;
+
+ printf("spec = ");
+ for(i = 0; i < 11; i++) {
+ printf("Counter %d:\n", i);
+ if(spec[i]) {
+ int k;
+
+ for(k = 0; k < spec[i]->nranges; k++)
+ print_range(&spec[i]->ranges[k]);
+ } else
+ printf("\t*\n");
+ }
+ mdvi_free_page_spec(spec);
+ }
+ }
+#endif
+ exit(0);
+
+}
+#endif /* TEST */
diff --git a/backend/dvi/mdvi-lib/paper.c b/backend/dvi/mdvi-lib/paper.c
new file mode 100644
index 0000000..7a7412d
--- /dev/null
+++ b/backend/dvi/mdvi-lib/paper.c
@@ -0,0 +1,170 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+
+#include "common.h"
+#include "mdvi.h"
+#include "private.h"
+
+static const DviPaperSpec papers[] = {
+ {"ISO", 0, 0},
+ {"4A0", "1682mm", "2378mm"},
+ {"2A0", "1189mm", "1682mm"},
+ {"A0", "841mm", "1189mm"},
+ {"A1", "594mm", "841mm"},
+ {"A2", "420mm", "594mm"},
+ {"A3", "297mm", "420mm"},
+ {"A4", "210mm", "297mm"},
+ {"A5", "148mm", "210mm"},
+ {"A6", "105mm", "148mm"},
+ {"A7", "74mm", "105mm"},
+ {"A8", "52mm", "74mm"},
+ {"A9", "37mm", "52mm"},
+ {"A10", "26mm", "37mm"},
+ {"B0", "1000mm", "1414mm"},
+ {"B1", "707mm", "1000mm"},
+ {"B2", "500mm", "707mm"},
+ {"B3", "353mm", "500mm"},
+ {"B4", "250mm", "353mm"},
+ {"B5", "176mm", "250mm"},
+ {"B6", "125mm", "176mm"},
+ {"B7", "88mm", "125mm"},
+ {"B8", "62mm", "88mm"},
+ {"B9", "44mm", "62mm"},
+ {"B10", "31mm", "44mm"},
+ {"C0", "917mm", "1297mm"},
+ {"C1", "648mm", "917mm"},
+ {"C2", "458mm", "648mm"},
+ {"C3", "324mm", "458mm"},
+ {"C4", "229mm", "324mm"},
+ {"C5", "162mm", "229mm"},
+ {"C6", "114mm", "162mm"},
+ {"C7", "81mm", "114mm"},
+ {"C8", "57mm", "81mm"},
+ {"C9", "40mm", "57mm"},
+ {"C10", "28mm", "40mm"},
+ {"US", 0, 0},
+ {"archA", "9in", "12in"},
+ {"archB", "12in", "18in"},
+ {"archC", "18in", "24in"},
+ {"archD", "24in", "36in"},
+ {"archE", "36in", "48in"},
+ {"executive", "7.5in", "10in"},
+ {"flsa", "8.5in", "13in"},
+ {"flse", "8.5in", "13in"},
+ {"halfletter", "5.5in", "8.5in"},
+ {"letter", "8.5in", "11in"},
+ {"legal", "8.5in", "14in"},
+ {"ledger", "17in", "11in"},
+ {"note", "7.5in", "10in"},
+ {"tabloid", "11in", "17in"},
+ {"statement", "5.5in", "8.5in"},
+ {0, 0, 0}
+};
+
+static DviPaperClass str2class(const char *name)
+{
+ if(STRCEQ(name, "ISO"))
+ return MDVI_PAPER_CLASS_ISO;
+ else if(STRCEQ(name, "US"))
+ return MDVI_PAPER_CLASS_US;
+ return MDVI_PAPER_CLASS_CUSTOM;
+}
+
+int mdvi_get_paper_size(const char *name, DviPaper *paper)
+{
+ const DviPaperSpec *sp;
+ double a, b;
+ char c, d, e, f;
+ char buf[32];
+
+ paper->pclass = MDVI_PAPER_CLASS_CUSTOM;
+ if(sscanf(name, "%lfx%lf%c%c", &a, &b, &c, &d) == 4) {
+ sprintf(buf, "%12.16f%c%c", a, c, d);
+ paper->inches_wide = unit2pix_factor(buf);
+ sprintf(buf, "%12.16f%c%c", b, c, d);
+ paper->inches_tall = unit2pix_factor(buf);
+ paper->name = _("custom");
+ return 0;
+ } else if(sscanf(name, "%lf%c%c,%lf%c%c", &a, &c, &d, &b, &e, &f) == 6) {
+ sprintf(buf, "%12.16f%c%c", a, c, d);
+ paper->inches_wide = unit2pix_factor(buf);
+ sprintf(buf, "%12.16f%c%c", b, e, f);
+ paper->inches_tall = unit2pix_factor(buf);
+ paper->name = _("custom");
+ return 0;
+ }
+
+ for(sp = &papers[0]; sp->name; sp++) {
+ if(!sp->width || !sp->height) {
+ paper->pclass = str2class(sp->name);
+ continue;
+ }
+ if(strcasecmp(sp->name, name) == 0) {
+ paper->inches_wide = unit2pix_factor(sp->width);
+ paper->inches_tall = unit2pix_factor(sp->height);
+ paper->name = sp->name;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+DviPaperSpec *mdvi_get_paper_specs(DviPaperClass pclass)
+{
+ int i;
+ int first, count;
+ DviPaperSpec *spec, *ptr;
+
+ first = -1;
+ count = 0;
+ if(pclass == MDVI_PAPER_CLASS_ANY ||
+ pclass == MDVI_PAPER_CLASS_CUSTOM) {
+ first = 0;
+ count = (sizeof(papers) / sizeof(papers[0])) - 3;
+ } else for(i = 0; papers[i].name; i++) {
+ if(papers[i].width == NULL) {
+ if(str2class(papers[i].name) == pclass)
+ first = i;
+ else if(first >= 0)
+ break;
+ } else if(first >= 0)
+ count++;
+ }
+ ptr = spec = xnalloc(DviPaperSpec, count + 1);
+ for(i = first; papers[i].name&& count > 0; i++) {
+ if(papers[i].width) {
+ ptr->name = papers[i].name;
+ ptr->width = papers[i].width;
+ ptr->height = papers[i].height;
+ ptr++;
+ count--;
+ }
+ }
+ ptr->name = NULL;
+ ptr->width = NULL;
+ ptr->height = NULL;
+
+ return spec;
+}
+
+void mdvi_free_paper_specs(DviPaperSpec *spec)
+{
+ mdvi_free(spec);
+}
diff --git a/backend/dvi/mdvi-lib/paper.h b/backend/dvi/mdvi-lib/paper.h
new file mode 100644
index 0000000..d42ee07
--- /dev/null
+++ b/backend/dvi/mdvi-lib/paper.h
@@ -0,0 +1,32 @@
+#ifndef MDVI_PAPER
+#define MDVI_PAPER
+
+typedef struct _DviPaper DviPaper;
+typedef struct _DviPaperSpec DviPaperSpec;
+
+typedef enum {
+ MDVI_PAPER_CLASS_ISO,
+ MDVI_PAPER_CLASS_US,
+ MDVI_PAPER_CLASS_ANY,
+ MDVI_PAPER_CLASS_CUSTOM
+} DviPaperClass;
+
+struct _DviPaper {
+ DviPaperClass pclass;
+ const char *name;
+ double inches_wide;
+ double inches_tall;
+};
+
+struct _DviPaperSpec {
+ const char *name;
+ const char *width;
+ const char *height;
+};
+
+
+extern int mdvi_get_paper_size __PROTO((const char *, DviPaper *));
+extern DviPaperSpec* mdvi_get_paper_specs __PROTO((DviPaperClass));
+extern void mdvi_free_paper_specs __PROTO((DviPaperSpec *));
+
+#endif
diff --git a/backend/dvi/mdvi-lib/pk.c b/backend/dvi/mdvi-lib/pk.c
new file mode 100644
index 0000000..48da008
--- /dev/null
+++ b/backend/dvi/mdvi-lib/pk.c
@@ -0,0 +1,569 @@
+
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * History:
+ *
+ * 11/3/2000:
+ * - First working version
+ * 11/4/2000:
+ * - FIXED: entirely white/black rows were missed.
+ * 11/8/2000:
+ * - TESTED: Glyphs are rendered correctly in different byte orders.
+ * - Made bitmap code much more efficient and compact.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+#define PK_ID 89
+#define PK_CMD_START 240
+#define PK_X1 240
+#define PK_X2 241
+#define PK_X3 242
+#define PK_X4 243
+#define PK_Y 244
+#define PK_POST 245
+#define PK_NOOP 246
+#define PK_PRE 247
+
+#define PK_DYN_F(x) (((x) >> 4) & 0xf)
+#define PK_PACKED(x) (PK_DYN_F(x) != 14)
+
+static int pk_load_font __PROTO((DviParams *, DviFont *));
+static int pk_font_get_glyph __PROTO((DviParams *, DviFont *, int));
+
+static int pk_auto_generate = 1; /* this is ON by default */
+
+typedef struct {
+ char currbyte;
+ char nybpos;
+ int dyn_f;
+} pkread;
+
+static char *pk_lookup __PROTO((const char *, Ushort *, Ushort *));
+static char *pk_lookupn __PROTO((const char *, Ushort *, Ushort *));
+
+/* only symbols exported by this file */
+DviFontInfo pk_font_info = {
+ "PK",
+ 0, /* scaling not supported natively */
+ pk_load_font,
+ pk_font_get_glyph,
+ mdvi_shrink_glyph,
+ mdvi_shrink_glyph_grey,
+ NULL, /* free */
+ NULL, /* reset */
+ pk_lookup, /* lookup */
+ kpse_pk_format,
+ NULL
+};
+
+DviFontInfo pkn_font_info = {
+ "PKN",
+ 0, /* scaling not supported natively */
+ pk_load_font,
+ pk_font_get_glyph,
+ mdvi_shrink_glyph,
+ mdvi_shrink_glyph_grey,
+ NULL, /* free */
+ NULL, /* reset */
+ pk_lookupn, /* lookup */
+ kpse_pk_format,
+ NULL
+};
+
+static char *pk_lookup(const char *name, Ushort *hdpi, Ushort *vdpi)
+{
+ kpse_glyph_file_type type;
+ char *filename;
+
+ if(pk_auto_generate == 0) {
+ kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_cmdline);
+ pk_auto_generate = 1;
+ }
+ filename = kpse_find_glyph(name, Max(*hdpi, *vdpi),
+ kpse_pk_format, &type);
+ if(filename && type.source == kpse_glyph_source_fallback) {
+ mdvi_free(filename);
+ filename = NULL;
+ } else if(filename) {
+ *hdpi = *vdpi = type.dpi;
+ }
+ return filename;
+}
+
+static char *pk_lookupn(const char *name, Ushort *hdpi, Ushort *vdpi)
+{
+ kpse_glyph_file_type type;
+ char *filename;
+
+ if(pk_auto_generate) {
+ kpse_set_program_enabled(kpse_pk_format, 0, kpse_src_cmdline);
+ pk_auto_generate = 0;
+ }
+ filename = kpse_find_glyph(name, Max(*hdpi, *vdpi),
+ kpse_pk_format, &type);
+ if(filename && type.source == kpse_glyph_source_fallback) {
+ mdvi_free(filename);
+ filename = NULL;
+ } else if(filename) {
+ *hdpi = *vdpi = type.dpi;
+ }
+ return filename;
+}
+
+static inline int pk_get_nyb(FILE *p, pkread *pk)
+{
+ unsigned t;
+ int nb;
+ char c;
+
+ t = c = pk->currbyte;
+ nb = pk->nybpos;
+
+ switch(nb) {
+ case 0:
+ c = pk->currbyte = fuget1(p);
+ t = (c >> 4);
+ break;
+ case 1:
+ t = c;
+ break;
+ }
+ pk->nybpos = !nb;
+ return (t & 0xf);
+}
+
+/*
+ * this is a bit cumbersome because we have to pass around
+ * the `pkread' data...
+ */
+static int pk_packed_num(FILE *p, pkread *pkr, int *repeat)
+{
+ int i, j;
+ int dyn_f = pkr->dyn_f;
+
+ i = pk_get_nyb(p, pkr);
+ if(i == 0) {
+ do {
+ j = pk_get_nyb(p, pkr);
+ i++;
+ } while(j == 0);
+ while(i-- > 0)
+ j = (j << 4) + pk_get_nyb(p, pkr);
+ return (j - 15 + ((13 - dyn_f) << 4) +
+ dyn_f);
+ } else if(i <= dyn_f)
+ return i;
+ else if(i < 14)
+ return ((i - dyn_f - 1) << 4) +
+ pk_get_nyb(p, pkr) + dyn_f + 1;
+ else {
+ *repeat = 1;
+ if(i == 14)
+ *repeat = pk_packed_num(p, pkr, repeat);
+ return pk_packed_num(p, pkr, repeat);
+ }
+}
+
+#define ROUND(x,y) (((x) + (y) - 1) / (y))
+
+static BITMAP *get_bitmap(FILE *p, int w, int h, int flags)
+{
+ int i, j;
+ BmUnit *ptr;
+ BITMAP *bm;
+ int bitpos;
+ int currch;
+
+ flags = 0; /* shut up that compiler */
+ bitpos = -1;
+ if((bm = bitmap_alloc(w, h)) == NULL)
+ return NULL;
+ DEBUG((DBG_BITMAPS, "get_bitmap(%d,%d,%d): reading raw bitmap\n",
+ w, h, flags));
+ ptr = bm->data;
+ currch = 0;
+ for(i = 0; i < h; i++) {
+ BmUnit mask;
+
+ mask = FIRSTMASK;
+ for(j = 0; j < w; j++) {
+ if(bitpos < 0) {
+ currch = fuget1(p);
+ bitpos = 7;
+ }
+ if(currch & (1 << bitpos))
+ *ptr |= mask;
+ bitpos--;
+ if(mask == LASTMASK) {
+ ptr++;
+ mask = FIRSTMASK;
+ } else
+ NEXTMASK(mask);
+ }
+ ptr = bm_offset(ptr, bm->stride);
+ }
+ return bm;
+}
+
+static BITMAP *get_packed(FILE *p, int w, int h, int flags)
+{
+ int inrow, count;
+ int row;
+ BITMAP *bm;
+ int repeat_count;
+ int paint;
+ pkread pkr;
+
+ pkr.nybpos = 0;
+ pkr.currbyte = 0;
+ pkr.dyn_f = PK_DYN_F(flags);
+ paint = !!(flags & 0x8);
+
+ repeat_count = 0;
+ row = 0;
+ inrow = w;
+ if((bm = bitmap_alloc(w, h)) == NULL)
+ return NULL;
+ DEBUG((DBG_BITMAPS, "get_packed(%d,%d,%d): reading packed glyph\n",
+ w, h, flags));
+ while(row < h) {
+ int i = 0;
+
+ count = pk_packed_num(p, &pkr, &i);
+ if(i > 0) {
+ if(repeat_count)
+ fprintf(stderr, "second repeat count for this row (had %d and got %d)\n",
+ repeat_count, i);
+ repeat_count = i;
+ }
+
+ if(count >= inrow) {
+ Uchar *r, *t;
+ BmUnit *a, mask;
+
+ /* first finish current row */
+ if(paint)
+ bitmap_set_row(bm, row, w - inrow, inrow, paint);
+ /* now copy it as many times as required */
+ r = (Uchar *)bm->data + row * bm->stride;
+ while(repeat_count-- > 0) {
+ t = r + bm->stride;
+ /* copy entire lines */
+ memcpy(t, r, bm->stride);
+ r = t;
+ row++;
+ }
+ repeat_count = 0;
+ /* count first row we drew */
+ row++;
+ /* update run count */
+ count -= inrow;
+ /* now r points to the beginning of the last row we finished */
+ if(paint)
+ mask = ~((BmUnit)0);
+ else
+ mask = 0;
+ /* goto next row */
+ a = (BmUnit *)(r + bm->stride);
+ /* deal with entirely with/black rows */
+ while(count >= w) {
+ /* count number of atoms in a row */
+ i = ROUND(w, BITMAP_BITS);
+ while(i-- > 0)
+ *a++ = mask;
+ count -= w;
+ row++;
+ }
+ inrow = w;
+ }
+ if(count > 0)
+ bitmap_set_row(bm, row, w - inrow, count, paint);
+ inrow -= count;
+ paint = !paint;
+ }
+ if(row != h || inrow != w) {
+ error(_("Bad PK file: More bits than required\n"));
+ bitmap_destroy(bm);
+ return NULL;
+ }
+ return bm;
+}
+
+static BITMAP *get_char(FILE *p, int w, int h, int flags)
+{
+ /* check if dyn_f == 14 */
+ if(((flags >> 4) & 0xf) == 14)
+ return get_bitmap(p, w, h, flags);
+ else
+ return get_packed(p, w, h, flags);
+}
+
+/* supports any number of characters in a font */
+static int pk_load_font(DviParams *unused, DviFont *font)
+{
+ int i;
+ int flag_byte;
+ int loc, hic, maxch;
+ Int32 checksum;
+ FILE *p;
+#ifndef NODEBUG
+ char s[256];
+#endif
+ long alpha, beta, z;
+
+ font->chars = xnalloc(DviFontChar, 256);
+ p = font->in;
+ memzero(font->chars, 256 * sizeof(DviFontChar));
+ for(i = 0; i < 256; i++)
+ font->chars[i].offset = 0;
+
+ /* check the preamble */
+ loc = fuget1(p); hic = fuget1(p);
+ if(loc != PK_PRE || hic != PK_ID)
+ goto badpk;
+ i = fuget1(p);
+#ifndef NODEBUG
+ for(loc = 0; loc < i; loc++)
+ s[loc] = fuget1(p);
+ s[loc] = 0;
+ DEBUG((DBG_FONTS, "(pk) %s: %s\n", font->fontname, s));
+#else
+ fseek(in, (long)i, SEEK_CUR);
+#endif
+ /* get the design size */
+ font->design = fuget4(p);
+ /* get the checksum */
+ checksum = fuget4(p);
+ if(checksum && font->checksum && font->checksum != checksum) {
+ warning(_("%s: checksum mismatch (expected %u, got %u)\n"),
+ font->fontname, font->checksum, checksum);
+ } else if(!font->checksum)
+ font->checksum = checksum;
+ /* skip pixel per point ratios */
+ fuget4(p);
+ fuget4(p);
+ if(feof(p))
+ goto badpk;
+
+ /* now start reading the font */
+ loc = 256; hic = -1; maxch = 256;
+
+ /* initialize alpha and beta for TFM width computation */
+ TFMPREPARE(font->scale, z, alpha, beta);
+
+ while((flag_byte = fuget1(p)) != PK_POST) {
+ if(feof(p))
+ break;
+ if(flag_byte >= PK_CMD_START) {
+ switch(flag_byte) {
+ case PK_X1:
+ case PK_X2:
+ case PK_X3:
+ case PK_X4: {
+#ifndef NODEBUG
+ char *t;
+ int n;
+
+ i = fugetn(p, flag_byte - PK_X1 + 1);
+ if(i < 256)
+ t = &s[0];
+ else
+ t = mdvi_malloc(i + 1);
+ for(n = 0; n < i; n++)
+ t[n] = fuget1(p);
+ t[n] = 0;
+ DEBUG((DBG_SPECIAL, "(pk) %s: Special \"%s\"\n",
+ font->fontname, t));
+ if(t != &s[0])
+ mdvi_free(t);
+#else
+ i = fugetn(p, flag_byte - PK_X1 + 1);
+ while(i-- > 0)
+ fuget1(p);
+#endif
+ break;
+ }
+ case PK_Y:
+ i = fuget4(p);
+ DEBUG((DBG_SPECIAL, "(pk) %s: MF special %u\n",
+ font->fontname, (unsigned)i));
+ break;
+ case PK_POST:
+ case PK_NOOP:
+ break;
+ case PK_PRE:
+ error(_("%s: unexpected preamble\n"), font->fontname);
+ goto error;
+ }
+ } else {
+ int pl;
+ int cc;
+ int w, h;
+ int x, y;
+ int offset;
+ long tfm;
+
+ switch(flag_byte & 0x7) {
+ case 7:
+ pl = fuget4(p);
+ cc = fuget4(p);
+ offset = ftell(p) + pl;
+ tfm = fuget4(p);
+ fsget4(p); /* skip dx */
+ fsget4(p); /* skip dy */
+ w = fuget4(p);
+ h = fuget4(p);
+ x = fsget4(p);
+ y = fsget4(p);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ pl = (flag_byte % 4) * 65536 + fuget2(p);
+ cc = fuget1(p);
+ offset = ftell(p) + pl;
+ tfm = fuget3(p);
+ fsget2(p); /* skip dx */
+ /* dy assumed 0 */
+ w = fuget2(p);
+ h = fuget2(p);
+ x = fsget2(p);
+ y = fsget2(p);
+ break;
+ default:
+ pl = (flag_byte % 4) * 256 + fuget1(p);
+ cc = fuget1(p);
+ offset = ftell(p) + pl;
+ tfm = fuget3(p);
+ fsget1(p); /* skip dx */
+ /* dy assumed 0 */
+ w = fuget1(p);
+ h = fuget1(p);
+ x = fsget1(p);
+ y = fsget1(p);
+ }
+ if(feof(p))
+ break;
+ if(cc < loc)
+ loc = cc;
+ if(cc > hic)
+ hic = cc;
+ if(cc > maxch) {
+ font->chars = xresize(font->chars,
+ DviFontChar, cc + 16);
+ for(i = maxch; i < cc + 16; i++)
+ font->chars[i].offset = 0;
+ maxch = cc + 16;
+ }
+ font->chars[cc].code = cc;
+ font->chars[cc].flags = flag_byte;
+ font->chars[cc].offset = ftell(p);
+ font->chars[cc].width = w;
+ font->chars[cc].height = h;
+ font->chars[cc].glyph.data = NULL;
+ font->chars[cc].x = x;
+ font->chars[cc].y = y;
+ font->chars[cc].glyph.x = x;
+ font->chars[cc].glyph.y = y;
+ font->chars[cc].glyph.w = w;
+ font->chars[cc].glyph.h = h;
+ font->chars[cc].grey.data = NULL;
+ font->chars[cc].shrunk.data = NULL;
+ font->chars[cc].tfmwidth = TFMSCALE(z, tfm, alpha, beta);
+ font->chars[cc].loaded = 0;
+ fseek(p, (long)offset, SEEK_SET);
+ }
+ }
+ if(flag_byte != PK_POST) {
+ error(_("%s: unexpected end of file (no postamble)\n"),
+ font->fontname);
+ goto error;
+ }
+ while((flag_byte = fuget1(p)) != EOF) {
+ if(flag_byte != PK_NOOP) {
+ error(_("invalid PK file! (junk in postamble)\n"));
+ goto error;
+ }
+ }
+
+ /* resize font char data */
+ if(loc > 0 || hic < maxch-1) {
+ 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;
+
+badpk:
+ error(_("%s: File corrupted, or not a PK file\n"), font->fontname);
+error:
+ mdvi_free(font->chars);
+ font->chars = NULL;
+ font->loc = font->hic = 0;
+ return -1;
+}
+
+static int pk_font_get_glyph(DviParams *params, DviFont *font, int code)
+{
+ DviFontChar *ch;
+
+ if((ch = FONTCHAR(font, code)) == NULL)
+ return -1;
+
+ if(ch->offset == 0)
+ return -1;
+ DEBUG((DBG_GLYPHS, "(pk) loading glyph for character %d (%dx%d) in font `%s'\n",
+ code, ch->width, ch->height, font->fontname));
+ if(font->in == NULL && font_reopen(font) < 0)
+ return -1;
+ if(!ch->width || !ch->height) {
+ /* this happens for ` ' (ASCII 32) in some fonts */
+ ch->glyph.x = ch->x;
+ ch->glyph.y = ch->y;
+ ch->glyph.w = ch->width;
+ ch->glyph.h = ch->height;
+ ch->glyph.data = NULL;
+ return 0;
+ }
+ if(fseek(font->in, ch->offset, SEEK_SET) == -1)
+ return -1;
+ ch->glyph.data = get_char(font->in,
+ ch->width, ch->height, ch->flags);
+ if(ch->glyph.data) {
+ /* restore original settings */
+ ch->glyph.x = ch->x;
+ ch->glyph.y = ch->y;
+ ch->glyph.w = ch->width;
+ ch->glyph.h = ch->height;
+ } else
+ return -1;
+ ch->loaded = 1;
+ return 0;
+}
diff --git a/backend/dvi/mdvi-lib/private.h b/backend/dvi/mdvi-lib/private.h
new file mode 100644
index 0000000..c547cd2
--- /dev/null
+++ b/backend/dvi/mdvi-lib/private.h
@@ -0,0 +1,51 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _MDVI_PRIVATE_H
+#define _MDVI_PRIVATE_H 1
+
+#define HAVE_PROTOTYPES 1
+#include <kpathsea/debug.h>
+#include <kpathsea/tex-file.h>
+#include <kpathsea/tex-glyph.h>
+#include <kpathsea/cnf.h>
+#include <kpathsea/proginit.h>
+#include <kpathsea/progname.h>
+#include <kpathsea/tex-make.h>
+
+#define ISSP(p) (*(p) == ' ' || *(p) == '\t')
+#define SKIPSP(p) while(ISSP(p)) p++
+#define SKIPNSP(p) while(*(p) && !ISSP(p)) p++
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(x) gettext(x)
+#define _G(x) x
+#else
+#define _(x) x
+#define _G(x) x
+#endif /* ENABLE_NLS */
+
+#if defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2
+#define _BREAKPOINT() do { __asm__ __volatile__ ("int $03"); } while(0)
+#elif defined (__alpha__) && defined (__GNUC__) && __GNUC__ >= 2
+#define _BREAKPOINT() do { __asm__ __volatile__ ("bpt"); } while(0)
+#else /* !__i386__ && !__alpha__ */
+#define _BREAKPOINT()
+#endif /* __i386__ */
+
+#endif /* _MDVI_PRIVATE_H */
diff --git a/backend/dvi/mdvi-lib/setup.c b/backend/dvi/mdvi-lib/setup.c
new file mode 100644
index 0000000..dea26e6
--- /dev/null
+++ b/backend/dvi/mdvi-lib/setup.c
@@ -0,0 +1,44 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+void mdvi_init_kpathsea(const char *program,
+ const char *mfmode, const char *font, int dpi)
+{
+ const char *p;
+
+ /* Stop meaningless output generation. */
+ kpse_make_tex_discard_errors = FALSE;
+
+ p = strrchr(program, '/');
+ p = (p ? p + 1 : program);
+ kpse_set_program_name(program, p);
+ kpse_init_prog(p, dpi, mfmode, font);
+ kpse_set_program_enabled(kpse_any_glyph_format, 1, kpse_src_compile);
+ kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_compile);
+ kpse_set_program_enabled(kpse_tfm_format, 1, kpse_src_compile);
+ kpse_set_program_enabled(kpse_ofm_format, 1, kpse_src_compile);
+}
+
diff --git a/backend/dvi/mdvi-lib/sp-epsf.c b/backend/dvi/mdvi-lib/sp-epsf.c
new file mode 100644
index 0000000..ca13c86
--- /dev/null
+++ b/backend/dvi/mdvi-lib/sp-epsf.c
@@ -0,0 +1,257 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* postscript specials */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+typedef struct {
+ double ox;
+ double oy;
+ double bw;
+ double bh;
+ double angle;
+} EpsfBox;
+
+#define LLX 0
+#define LLY 1
+#define URX 2
+#define URY 3
+#define RWI 4
+#define RHI 5
+#define HOFF 6
+#define VOFF 7
+#define HSIZE 8
+#define VSIZE 9
+#define HSCALE 10
+#define VSCALE 11
+#define ANGLE 12
+#define CLIP 13
+
+void epsf_special __PROTO((DviContext *dvi, char *prefix, char *arg));
+
+/* Note: the given strings are modified in place */
+static char *parse_epsf_special(EpsfBox *box, char **ret,
+ char *prefix, char *arg)
+{
+ static struct {
+ char *name;
+ int has_arg;
+ char *value;
+ } keys[] = {
+ {"llx", 1, "0"},
+ {"lly", 1, "0"},
+ {"urx", 1, "0"},
+ {"ury", 1, "0"},
+ {"rwi", 1, "0"},
+ {"rhi", 1, "0"},
+ {"hoffset", 1, "0"},
+ {"voffset", 1, "0"},
+ {"hsize", 1, "612"},
+ {"vsize", 1, "792"},
+ {"hscale", 1, "100"},
+ {"vscale", 1, "100"},
+ {"angle", 1, "0"},
+ {"clip", 0, "0"}
+ };
+#define NKEYS (sizeof(keys) / sizeof(keys[0]))
+ char *ptr;
+ char *filename;
+ int quoted;
+ double value[NKEYS];
+ Uchar present[NKEYS];
+ Buffer buffer;
+ char *name;
+ int i;
+ double originx;
+ double originy;
+ double hsize;
+ double vsize;
+ double hscale;
+ double vscale;
+
+ /* this special has the form
+ * ["]file.ps["] [key=valye]*
+ */
+
+ /* scan the filename */
+ while(*arg == ' ' || *arg == '\t')
+ arg++;
+
+ /* make a copy of the string */
+ ptr = arg;
+
+ if(*ptr == '"')
+ for(name = ++ptr; *ptr && *ptr != '"'; ptr++);
+ else
+ for(name = ptr; *ptr && *ptr != ' ' && *ptr != '\t'; ptr++);
+ if(ptr == name)
+ return NULL;
+ *ptr++ = 0;
+ filename = name;
+
+ /* reset values to defaults */
+ for(i = 0; i < NKEYS; i++) {
+ value[i] = atof(keys[i].value);
+ present[i] = 0;
+ }
+
+ buff_init(&buffer);
+ buff_add(&buffer, "@beginspecial ", 0);
+
+ quoted = 0;
+ while(*ptr) {
+ const char *keyname;
+ char *val;
+ char *p;
+
+ while(*ptr == ' ' || *ptr == '\t')
+ ptr++;
+ keyname = ptr;
+
+ /* get the whole key=value pair */
+ for(; *ptr && *ptr != ' ' && *ptr != '\t'; ptr++);
+
+ if(*ptr) *ptr++ = 0;
+ /* now we shouldn't touch `ptr' anymore */
+
+ /* now work on this pair */
+ p = strchr(keyname, '=');
+ if(p == NULL)
+ val = NULL;
+ else {
+ *p++ = 0;
+ if(*p == '"') {
+ val = ++p;
+ /* skip until closing quote */
+ while(*p && *p != '"')
+ p++;
+ if(*p != '"')
+ warning(
+ _("%s: malformed value for key `%s'\n"),
+ filename, keyname);
+ } else
+ val = p;
+ }
+
+ /* lookup the key */
+ for(i = 0; i < NKEYS; i++)
+ if(STRCEQ(keys[i].name, keyname))
+ break;
+ if(i == NKEYS) {
+ warning(_("%s: unknown key `%s' ignored\n"),
+ filename, keyname);
+ continue;
+ }
+ if(keys[i].has_arg && val == NULL) {
+ warning(_("%s: no argument for key `%s', using defaults\n"),
+ filename, keyname);
+ val = keys[i].value;
+ } else if(!keys[i].has_arg && val) {
+ warning(_("%s: argument `%s' ignored for key `%s'\n"),
+ filename, val, keyname);
+ val = NULL;
+ }
+ if(val)
+ value[i] = atof(val);
+
+ /* put the argument */
+ buff_add(&buffer, val, 0);
+ buff_add(&buffer, " @", 2);
+ buff_add(&buffer, keyname, 0);
+ buff_add(&buffer, " ", 1);
+
+ /* remember that this option was given */
+ present[i] = 0xff;
+ }
+ buff_add(&buffer, " @setspecial", 0);
+
+ /* now compute the bounding box (code comes from dvips) */
+ originx = 0;
+ originy = 0;
+ hscale = 1;
+ vscale = 1;
+ hsize = 0;
+ vsize = 0;
+
+ if(present[HSIZE])
+ hsize = value[HSIZE];
+ if(present[VSIZE])
+ vsize = value[VSIZE];
+ if(present[HOFF])
+ originx = value[HOFF];
+ if(present[VOFF])
+ originy = value[VOFF];
+ if(present[HSCALE])
+ hscale = value[HSCALE] / 100.0;
+ if(present[VSCALE])
+ vscale = value[VSCALE] / 100.0;
+ if(present[URX] && present[LLX])
+ hsize = value[URX] - value[LLX];
+ if(present[URY] && present[LLY])
+ vsize = value[URY] - value[LLY];
+ if(present[RWI] || present[RHI]) {
+ if(present[RWI] && !present[RHI])
+ hscale = vscale = value[RWI] / (10.0 * hsize);
+ else if(present[RHI] && !present[RWI])
+ hscale = vscale = value[RHI] / (10.0 * vsize);
+ else {
+ hscale = value[RWI] / (10.0 * hsize);
+ vscale = value[RHI] / (10.0 * vsize);
+ }
+ }
+
+ box->ox = originx;
+ box->oy = originy;
+ box->bw = hsize * hscale;
+ box->bh = vsize * vscale;
+ box->angle = value[ANGLE];
+
+ *ret = buffer.data;
+
+ return filename;
+}
+
+void epsf_special(DviContext *dvi, char *prefix, char *arg)
+{
+ char *file;
+ char *special;
+ EpsfBox box = {0, 0, 0, 0};
+ int x, y;
+ int w, h;
+ double xf, vf;
+
+ file = parse_epsf_special(&box, &special, prefix, arg);
+ if(file != NULL)
+ mdvi_free(special);
+ /*
+ * draw the bounding box. Notice that it is in PostScript units,
+ * so we have to convert it into pixels
+ */
+ xf = dvi->params.dpi * dvi->params.mag / (72.0 * dvi->params.hshrink);
+ vf = dvi->params.vdpi * dvi->params.mag / (72.0 * dvi->params.vshrink);
+ x = FROUND(box.ox * xf);
+ y = FROUND(box.oy * vf);
+ w = FROUND(box.bw * xf);
+ h = FROUND(box.bh * vf);
+ dvi->device.draw_rule(dvi, dvi->pos.hh + x, dvi->pos.vv + y - h + 1, w, h, 0);
+}
diff --git a/backend/dvi/mdvi-lib/special.c b/backend/dvi/mdvi-lib/special.c
new file mode 100644
index 0000000..23c070e
--- /dev/null
+++ b/backend/dvi/mdvi-lib/special.c
@@ -0,0 +1,248 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+#if defined(WITH_REGEX_SPECIALS) && defined(HAVE_REGEX_H)
+#include <regex.h>
+#endif
+
+typedef struct _DviSpecial {
+ struct _DviSpecial *next;
+ struct _DviSpecial *prev;
+ char *label;
+ char *prefix;
+ size_t plen;
+#ifdef WITH_REGEX_SPECIALS
+ regex_t reg;
+ int has_reg;
+#endif
+ DviSpecialHandler handler;
+} DviSpecial;
+
+static ListHead specials = {NULL, NULL, 0};
+
+#define SPECIAL(x) \
+ void x __PROTO((DviContext *, const char *, const char *))
+
+static SPECIAL(sp_layer);
+extern SPECIAL(epsf_special);
+extern SPECIAL(do_color_special);
+
+static struct {
+ char *label;
+ char *prefix;
+ char *regex;
+ DviSpecialHandler handler;
+} builtins[] = {
+ {"Layers", "layer", NULL, sp_layer},
+ {"EPSF", "psfile", NULL, epsf_special}
+};
+#define NSPECIALS (sizeof(builtins) / sizeof(builtins[0]))
+static int registered_builtins = 0;
+
+static void register_builtin_specials(void)
+{
+ int i;
+
+ ASSERT(registered_builtins == 0);
+ for(i = 0; i < NSPECIALS; i++)
+ mdvi_register_special(
+ builtins[i].label,
+ builtins[i].prefix,
+ builtins[i].regex,
+ builtins[i].handler,
+ 1 /* replace if exists */);
+ registered_builtins = 1;
+}
+
+static DviSpecial *find_special_prefix(const char *prefix)
+{
+ DviSpecial *sp;
+
+ /* should have a hash table here, but I'm so lazy */
+ for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) {
+ if(STRCEQ(sp->prefix, prefix))
+ break;
+ }
+ return sp;
+}
+
+int mdvi_register_special(const char *label, const char *prefix,
+ const char *regex, DviSpecialHandler handler, int replace)
+{
+ DviSpecial *sp;
+ int newsp = 0;
+
+ if(!registered_builtins)
+ register_builtin_specials();
+
+ sp = find_special_prefix(prefix);
+ if(sp == NULL) {
+ sp = xalloc(DviSpecial);
+ sp->prefix = mdvi_strdup(prefix);
+ newsp = 1;
+ } else if(!replace)
+ return -1;
+ else {
+ mdvi_free(sp->label);
+ sp->label = NULL;
+ }
+
+#ifdef WITH_REGEX_SPECIALS
+ if(!newsp && sp->has_reg) {
+ regfree(&sp->reg);
+ sp->has_reg = 0;
+ }
+ if(regex && regcomp(&sp->reg, regex, REG_NOSUB) != 0) {
+ if(newsp) {
+ mdvi_free(sp->prefix);
+ mdvi_free(sp);
+ }
+ return -1;
+ }
+ sp->has_reg = (regex != NULL);
+#endif
+ sp->handler = handler;
+ sp->label = mdvi_strdup(label);
+ sp->plen = strlen(prefix);
+ if(newsp)
+ listh_prepend(&specials, LIST(sp));
+ DEBUG((DBG_SPECIAL,
+ "New \\special handler `%s' with prefix `%s'\n",
+ label, prefix));
+ return 0;
+}
+
+int mdvi_unregister_special(const char *prefix)
+{
+ DviSpecial *sp;
+
+ sp = find_special_prefix(prefix);
+ if(sp == NULL)
+ return -1;
+ mdvi_free(sp->prefix);
+#ifdef WITH_REGEX_SPECIALS
+ if(sp->has_reg)
+ regfree(&sp->reg);
+#endif
+ listh_remove(&specials, LIST(sp));
+ mdvi_free(sp);
+ return 0;
+}
+
+#define IS_PREFIX_DELIMITER(x) (strchr(" \t\n:=", (x)) != NULL)
+
+int mdvi_do_special(DviContext *dvi, char *string)
+{
+ char *prefix;
+ char *ptr;
+ DviSpecial *sp;
+
+ if(!registered_builtins) {
+ }
+
+ if(!string || !*string)
+ return 0;
+
+ /* skip leading spaces */
+ while(*string && isspace(*string))
+ string++;
+
+ DEBUG((DBG_SPECIAL, "Looking for a handler for `%s'\n", string));
+
+ /* now try to find a match */
+ ptr = string;
+ for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) {
+#ifdef WITH_REGEX_SPECIALS
+ if(sp->has_reg && !regexec(&sp->reg, ptr, 0, 0, 0))
+ break;
+#endif
+ /* check the prefix */
+ if(STRNCEQ(sp->prefix, ptr, sp->plen)) {
+ ptr += sp->plen;
+ break;
+ }
+ }
+
+ if(sp == NULL) {
+ DEBUG((DBG_SPECIAL, "None found\n"));
+ return -1;
+ }
+
+ /* extract the prefix */
+ if(ptr == string) {
+ prefix = NULL;
+ DEBUG((DBG_SPECIAL,
+ "REGEX match with `%s' (arg `%s')\n",
+ sp->label, ptr));
+ } else {
+ if(*ptr) *ptr++ = 0;
+ prefix = string;
+ DEBUG((DBG_SPECIAL,
+ "PREFIX match with `%s' (prefix `%s', arg `%s')\n",
+ sp->label, prefix, ptr));
+ }
+
+ /* invoke the handler */
+ sp->handler(dvi, prefix, ptr);
+
+ return 0;
+}
+
+void mdvi_flush_specials(void)
+{
+ DviSpecial *sp, *list;
+
+
+ for(list = (DviSpecial *)specials.head; (sp = list); ) {
+ list = sp->next;
+ if(sp->prefix) mdvi_free(sp->prefix);
+ if(sp->label) mdvi_free(sp->label);
+#ifdef WITH_REGEX_SPECIALS
+ if(sp->has_reg)
+ regfree(&sp->reg);
+#endif
+ mdvi_free(sp);
+ }
+ specials.head = NULL;
+ specials.tail = NULL;
+ specials.count = 0;
+}
+
+/* some builtin specials */
+
+void sp_layer(DviContext *dvi, const char *prefix, const char *arg)
+{
+ if(STREQ("push", arg))
+ dvi->curr_layer++;
+ else if(STREQ("pop", arg)) {
+ if(dvi->curr_layer)
+ dvi->curr_layer--;
+ else
+ warning(_("%s: tried to pop top level layer\n"),
+ dvi->filename);
+ } else if(STREQ("reset", arg))
+ dvi->curr_layer = 0;
+ DEBUG((DBG_SPECIAL, "Layer level: %d\n", dvi->curr_layer));
+}
+
diff --git a/backend/dvi/mdvi-lib/sysdeps.h b/backend/dvi/mdvi-lib/sysdeps.h
new file mode 100644
index 0000000..8f89178
--- /dev/null
+++ b/backend/dvi/mdvi-lib/sysdeps.h
@@ -0,0 +1,116 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _SYSDEP_H
+#define _SYSDEP_H 1
+
+/*
+ * The purpose of this file is to define symbols that describe the
+ * system-dependent features we use. Namely, byte order, native integer
+ * types of various sizes, and safe pointer<->integer conversion.
+ */
+
+#include "config.h"
+
+#ifdef WORDS_BIGENDIAN
+#define WORD_BIG_ENDIAN 1
+#else
+#define WORD_LITTLE_ENDIAN 1
+#endif
+
+typedef unsigned long Ulong;
+typedef unsigned int Uint;
+typedef unsigned short Ushort;
+typedef unsigned char Uchar;
+
+/* this one's easy */
+typedef unsigned char Uint8;
+typedef char Int8;
+
+/* define a datatype for 32bit integers (either int or long) */
+#if SIZEOF_LONG == 4
+typedef unsigned long Uint32;
+typedef long Int32;
+#else /* SIZEOF_LONG != 4 */
+#if SIZEOF_INT == 4
+typedef unsigned int Uint32;
+typedef int Int32;
+#else /* SIZEOF_INT != 4 */
+#ifdef __cplusplus
+#include "No.appropriate.32bit.native.type.found.Fix.sysdeps.h"
+#else
+#error No appropriate 32bit native type found. Fix sysdeps.h
+#endif /* ! __cplusplus */
+#endif /* SIZEOF_INT != 4 */
+#endif /* SIZEOF_LONG != 4 */
+
+/* now 16bit integers (one of long, int or short) */
+#if SIZEOF_SHORT == 2
+typedef unsigned short Uint16;
+typedef short Int16;
+#else /* SIZEOF_SHORT != 2 */
+#if SIZEOF_INT == 2
+typedef unsigned int Uint16;
+typedef short Int16;
+#else /* SIZEOF_INT != 2 */
+#ifdef __cplusplus
+#include "No.appropriate.16bit.native.type.found.Fix.sysdeps.h"
+#else
+#error No appropriate 16bit native type found. Fix sysdeps.h
+#endif /* ! __cplusplus */
+#endif /* SIZEOF_INT != 2 */
+#endif /* SIZEOF_SHORT != 2 */
+
+/*
+ * An integer type to convert to and from pointers safely. All we do here is
+ * look for an integer type with the same size as a pointer.
+ */
+#if SIZEOF_LONG == SIZEOF_VOID_P
+typedef unsigned long UINT;
+typedef long INT;
+#else
+#if SIZEOF_INT == SIZEOF_VOID_P
+typedef unsigned int UINT;
+typedef int INT;
+#else
+#if SIZEOF_SHORT == SIZEOF_VOID_P
+typedef unsigned short UINT;
+typedef short INT;
+#else
+#ifdef __cplusplus
+#include "No.native.pointer-compatible.integer.type.found.Fix.sysdeps.h"
+#else
+#error No native pointer-compatible integer type found. Fix sysdeps.h
+#endif
+#endif
+#endif
+#endif
+
+/* nice, uh? */
+typedef void *Pointer;
+
+/* macros to do the safe pointer <-> integer conversions */
+#define Ptr2Int(x) ((INT)((Pointer)(x)))
+#define Int2Ptr(x) ((Pointer)((INT)(x)))
+
+#ifdef _NO_PROTO
+#define __PROTO(x) ()
+#else
+#define __PROTO(x) x
+#endif
+
+#endif /* _SYSDEP_H */
diff --git a/backend/dvi/mdvi-lib/t1.c b/backend/dvi/mdvi-lib/t1.c
new file mode 100644
index 0000000..acbfa23
--- /dev/null
+++ b/backend/dvi/mdvi-lib/t1.c
@@ -0,0 +1,638 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Type1 font support for MDVI
+ *
+ * We use T1lib only as a rasterizer, not to draw glyphs.
+ */
+
+#include "mdvi.h"
+
+#ifdef WITH_TYPE1_FONTS
+
+#include <stdio.h>
+#include <t1lib.h>
+#include "private.h"
+
+static int t1lib_initialized = 0;
+
+typedef struct t1info {
+ struct t1info *next;
+ struct t1info *prev;
+ char *fontname; /* (short) name of this font */
+ int t1id; /* T1lib's id for this font */
+ int hasmetrics; /* have we processed this font? */
+ TFMInfo *tfminfo; /* TFM data is shared */
+ DviFontMapInfo mapinfo;
+ DviEncoding *encoding;
+} T1Info;
+
+static void t1_font_remove __PROTO((T1Info *));
+static int t1_load_font __PROTO((DviParams *, DviFont *));
+static int t1_font_get_glyph __PROTO((DviParams *, DviFont *, int));
+static void t1_font_shrink_glyph
+ __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *));
+static void t1_free_data __PROTO((DviFont *));
+static void t1_reset_font __PROTO((DviFont *));
+static char *t1_lookup_font __PROTO((const char *, Ushort *, Ushort *));
+
+/* only symbol exported by this file */
+DviFontInfo t1_font_info = {
+ "Type1",
+ 1, /* scaling supported by format */
+ t1_load_font,
+ t1_font_get_glyph,
+ t1_font_shrink_glyph,
+ mdvi_shrink_glyph_grey,
+ t1_free_data,
+ t1_reset_font,
+ t1_lookup_font, /* lookup */
+ kpse_type1_format,
+ NULL
+};
+
+/* this seems good enough for most DVI files */
+#define T1_HASH_SIZE 31
+
+/* If these parameters change, we must delete all size information
+ * in all fonts, and reset the device resolutions in T1lib */
+static int t1lib_xdpi = -1;
+static int t1lib_ydpi = -1;
+
+static ListHead t1fonts = {NULL, NULL, 0};
+static DviHashTable t1hash;
+
+/* Type1 fonts need their own `lookup' function. Here is how it works:
+ * First we try to find the font by its given name. If that fails, we
+ * query the font maps. A typical font map entry may contain the line
+ *
+ * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc <ptmr
+ *
+ * which means: If you're looking for the font `ptmr8rn' load `Times-Roman'
+ * which is in `ptmr' instead, and extend it by 0.82 points, then reencode
+ * it with the vector TeXBase1Encoding from the file `8r.enc'. This will
+ * fail if the entry looks like this:
+ *
+ * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc
+ *
+ * because to deal with this we would need to be able to locate the font file
+ * for the `Times-Roman' font ourselves, and that's beyond the scope of mdvi.
+ * But hey, we tried hard.
+ */
+char *t1_lookup_font(const char *name, Ushort *hdpi, Ushort *vdpi)
+{
+ char *filename;
+ char *newname;
+ const char *ext;
+ DviFontMapInfo info;
+
+ DEBUG((DBG_TYPE1, "(t1) looking for `%s'\n", name));
+
+ /* first let's try the font we were asked for */
+ filename = kpse_find_file(name, kpse_type1_format, 1);
+ if(filename != NULL) {
+ /* we got it */
+ return filename;
+ }
+
+ DEBUG((DBG_TYPE1, "(t1) %s: not found, querying font maps\n", name));
+ /* now query the fontmap */
+ if(mdvi_query_fontmap(&info, name) < 0) {
+ /* it's not there either */
+ return NULL;
+ }
+
+ /* check what we got */
+ if(info.fullfile) {
+ DEBUG((DBG_TYPE1, "(t1) %s: found `%s' (cached)\n",
+ name, info.fullfile));
+ /* this is a cached lookup */
+ return mdvi_strdup(info.fullfile);
+ }
+
+ /* no file associated to this font? */
+ if(info.fontfile == NULL)
+ return info.psname ? mdvi_ps_find_font(info.psname) : NULL;
+
+ /* let's extract the extension */
+ ext = file_extension(info.fontfile);
+ if(ext && !STREQ(ext, "pfa") && !STREQ(ext, "pfb")) {
+ DEBUG((DBG_TYPE1,
+ "(t1) %s: associated name `%s' is not Type1\n",
+ name, info.fontfile));
+ /* it's not a Type1 font */
+ return NULL;
+ }
+
+ /* get the `base' name */
+ if(ext) {
+ newname = mdvi_strdup(name);
+ newname[ext - info.fontfile - 1] = 0;
+ } else
+ newname = (char *)name; /* we don't modify this */
+
+ /* look it up */
+ DEBUG((DBG_TYPE1, "(t1) looking for `%s' on behalf of `%s'\n",
+ newname, name));
+ filename = kpse_find_file(newname, kpse_type1_format, 1);
+
+ /* we don't need this anymore */
+ if(newname != name)
+ mdvi_free(newname);
+ if(filename == NULL) {
+ DEBUG((DBG_TYPE1, "(t1) %s: not found\n", name));
+ return NULL;
+ }
+
+ DEBUG((DBG_TYPE1, "(t1) %s: found as `%s'\n", name, filename));
+ /* got it! let's remember this */
+ mdvi_add_fontmap_file(name, filename);
+ return filename;
+}
+
+static void t1_reset_resolution(int xdpi, int ydpi)
+{
+ int i;
+ int nfonts;
+
+ DEBUG((DBG_TYPE1, "(t1) resetting device resolution (current: (%d,%d))\n",
+ t1lib_xdpi, t1lib_ydpi));
+#if T1LIB_VERSION < 5
+ nfonts = T1_Get_no_fonts();
+#else
+ nfonts = T1_GetNoFonts();
+#endif
+
+ for(i = 0; i < nfonts; i++)
+ T1_DeleteAllSizes(i);
+ /* reset device resolutions */
+ if(T1_SetDeviceResolutions((float)xdpi, (float)ydpi) < 0)
+ warning(_("(t1) failed to reset device resolution\n"));
+ else
+ DEBUG((DBG_TYPE1,
+ "(t1) reset successful, new resolution is (%d, %d)\n",
+ xdpi, ydpi));
+ t1lib_xdpi = xdpi;
+ t1lib_ydpi = ydpi;
+}
+
+static void t1_reset_font(DviFont *font)
+{
+ T1Info *info = (T1Info *)font->private;
+
+ if(info == NULL)
+ return;
+ DEBUG((DBG_FONTS, "(t1) resetting font `%s'\n", font->fontname));
+ /* just mark the font as not having metric info. It will be reset
+ * automatically later */
+ info->hasmetrics = 0;
+}
+
+static void t1_transform_font(T1Info *info)
+{
+ if(!info->hasmetrics && info->encoding != NULL) {
+ DEBUG((DBG_TYPE1, "(t1) %s: encoding with vector `%s'\n",
+ info->fontname, info->encoding->name));
+ T1_DeleteAllSizes(info->t1id);
+ if(T1_ReencodeFont(info->t1id, info->encoding->vector) < 0)
+ warning(_("%s: could not encode font\n"), info->fontname);
+ }
+ if(info->mapinfo.slant) {
+ DEBUG((DBG_TYPE1, "(t1) %s: slanting by %.3f\n",
+ info->fontname,
+ MDVI_FMAP_SLANT(&info->mapinfo)));
+ T1_SlantFont(info->t1id,
+ MDVI_FMAP_SLANT(&info->mapinfo));
+ }
+ if(info->mapinfo.extend) {
+ DEBUG((DBG_TYPE1, "(t1) %s: extending by %.3f\n",
+ info->fontname,
+ MDVI_FMAP_EXTEND(&info->mapinfo)));
+ T1_ExtendFont(info->t1id,
+ MDVI_FMAP_EXTEND(&info->mapinfo));
+ }
+}
+
+/* if this function is called, we really need this font */
+static int t1_really_load_font(DviParams *params, DviFont *font, T1Info *info)
+{
+ int i;
+ T1Info *old;
+ int t1id;
+ int copied;
+ int status;
+
+ DEBUG((DBG_TYPE1, "(t1) really_load_font(%s)\n", info->fontname));
+
+ /* if the parameters changed, reset T1lib */
+ if(t1lib_xdpi != params->dpi || t1lib_ydpi != params->vdpi)
+ t1_reset_resolution(params->dpi, params->vdpi);
+
+ /* if we already have a T1lib id, do nothing */
+ if(info->t1id != -1) {
+ info->hasmetrics = 1;
+ /* apply slant and extend again */
+ t1_transform_font(info);
+ return 0;
+ }
+
+ /* before we even attempt to load the font, make sure we have metric
+ * data for it */
+ info->tfminfo = mdvi_ps_get_metrics(info->fontname);
+ if(info->tfminfo == NULL) {
+ DEBUG((DBG_FONTS,
+ "(t1) %s: no metric data, font ignored\n",
+ info->fontname));
+ goto t1_error;
+ }
+ /* fix this */
+ font->design = info->tfminfo->design;
+
+ /* check if we have a font with this name (maybe at a different size) */
+ old = (T1Info *)mdvi_hash_lookup(&t1hash, (unsigned char *)info->fontname);
+ if(old == info) {
+ /* let's avoid confusion */
+ old = NULL;
+ }
+ if(old && old->t1id != -1) {
+ /* let's take advantage of T1lib's font sharing */
+ t1id = T1_CopyFont(old->t1id);
+ DEBUG((DBG_TYPE1, "(t1) %s -> %d (CopyFont)\n",
+ info->fontname, t1id));
+ copied = 1;
+ } else {
+ t1id = T1_AddFont(font->filename);
+ DEBUG((DBG_TYPE1, "(t1) %s -> %d (AddFont)\n",
+ info->fontname, t1id));
+ copied = 0;
+ }
+ if(t1id < 0)
+ goto t1_error;
+ info->t1id = t1id;
+
+ /*
+ * a minor optimization: If the old font in the hash table has
+ * not been loaded yet, replace it by this one, so we can use
+ * CopyFont later.
+ */
+ if(old && old->t1id == -1) {
+ DEBUG((DBG_TYPE1, "(t1) font `%s' exchanged in hash table\n",
+ info->fontname));
+ mdvi_hash_remove(&t1hash, (unsigned char *)old->fontname);
+ mdvi_hash_add(&t1hash, (unsigned char *)info->fontname,
+ info, MDVI_HASH_UNCHECKED);
+ }
+
+ /* now let T1lib load it */
+ if(!copied && T1_LoadFont(info->t1id) < 0) {
+ DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) failed with error %d\n",
+ info->t1id, T1_errno));
+ goto t1_error;
+ }
+ DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) -> Ok\n", info->t1id));
+
+ /* get information from the fontmap */
+ status = mdvi_query_fontmap(&info->mapinfo, info->fontname);
+ if(!status && info->mapinfo.encoding)
+ info->encoding = mdvi_request_encoding(info->mapinfo.encoding);
+ t1_transform_font(info);
+
+ i = info->tfminfo->hic - info->tfminfo->loc + 1;
+ if(i != font->hic - font->loc + 1) {
+ /* reset to optimal size */
+ font->chars = mdvi_realloc(font->chars, i * sizeof(DviFontChar));
+ }
+
+ /* get the scaled characters metrics */
+ get_tfm_chars(params, font, info->tfminfo, 0);
+ info->hasmetrics = 1;
+
+ DEBUG((DBG_TYPE1, "(t1) font `%s' really-loaded\n", info->fontname));
+ return 0;
+
+t1_error:
+ /* some error does not allows us to use this font. We need to reset
+ * the font structure, so the font system can try to read this
+ * font in a different class */
+
+ /* first destroy the private data */
+ t1_font_remove(info);
+ /* now reset all chars -- this is the important part */
+ mdvi_free(font->chars);
+ font->chars = NULL;
+ font->loc = font->hic = 0;
+ return -1;
+}
+
+static int init_t1lib(DviParams *params)
+{
+ int t1flags;
+
+#ifdef WORD_LITTLE_ENDIAN
+ /* try making T1lib use bitmaps in our format, but if this
+ * fails we'll convert the bitmap ourselves */
+ T1_SetBitmapPad(BITMAP_BITS);
+#endif
+ T1_SetDeviceResolutions((float)params->dpi, (float)params->vdpi);
+ t1flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE|T1_NO_AFM;
+ if(DEBUGGING(TYPE1))
+ t1flags |= LOGFILE;
+ if(T1_InitLib(t1flags) == NULL)
+ return (t1lib_initialized = -1);
+ if(DEBUGGING(TYPE1)) {
+ DEBUG((DBG_TYPE1, "T1lib debugging output saved in t1lib.log\n"));
+ T1_SetLogLevel(T1LOG_DEBUG);
+ }
+ /* initialize our hash table, but don't allocate memory for it
+ * until we use it */
+ mdvi_hash_init(&t1hash);
+ DEBUG((DBG_TYPE1, "(t1) t1lib %s initialized -- resolution is (%d, %d), pad is %d bits\n",
+ T1_GetLibIdent(), params->dpi, params->vdpi, T1_GetBitmapPad()));
+ t1lib_initialized = 1;
+ t1lib_xdpi = params->dpi;
+ t1lib_ydpi = params->vdpi;
+ return 0;
+}
+
+static int t1_load_font(DviParams *params, DviFont *font)
+{
+ T1Info *info;
+ int i;
+
+ if(t1lib_initialized < 0)
+ return -1;
+ else if(t1lib_initialized == 0 && init_t1lib(params) < 0)
+ return -1;
+
+ if(font->in != NULL) {
+ /* we don't need this */
+ fclose(font->in);
+ font->in = NULL;
+ }
+
+ info = xalloc(T1Info);
+
+ /*
+ * mark the font as `unregistered' with T1lib. It will
+ * be added when we actually use it
+ */
+ info->t1id = -1;
+
+ /* add the font to our list */
+ info->fontname = font->fontname;
+ info->hasmetrics = 0;
+ info->encoding = NULL;
+ info->mapinfo.psname = NULL;
+ info->mapinfo.encoding = NULL;
+ info->mapinfo.fontfile = NULL;
+ info->mapinfo.extend = 0;
+ info->mapinfo.slant = 0;
+ info->encoding = NULL;
+
+ /* create the hash table if we have not done so yet */
+ if(t1hash.nbucks == 0)
+ mdvi_hash_create(&t1hash, T1_HASH_SIZE);
+ mdvi_hash_add(&t1hash, (unsigned char *) info->fontname, info, MDVI_HASH_UNIQUE);
+ listh_append(&t1fonts, LIST(info));
+
+ font->private = info;
+
+ /* reset everything */
+ font->chars = xnalloc(DviFontChar, 256);
+ font->loc = 0;
+ font->hic = 255;
+ for(i = 0; i < 256; i++) {
+ font->chars[i].code = i;
+ font->chars[i].offset = 1;
+ font->chars[i].loaded = 0;
+ font->chars[i].glyph.data = NULL;
+ font->chars[i].shrunk.data = NULL;
+ font->chars[i].grey.data = NULL;
+ }
+
+ return 0;
+}
+
+#define GLYPH_WIDTH(g) \
+ ((g)->metrics.rightSideBearing - (g)->metrics.leftSideBearing)
+#define GLYPH_HEIGHT(g) \
+ ((g)->metrics.ascent - (g)->metrics.descent)
+
+static inline BITMAP *t1_glyph_bitmap(GLYPH *glyph)
+{
+ BITMAP *bm;
+ int w, h;
+
+ w = GLYPH_WIDTH(glyph);
+ h = GLYPH_HEIGHT(glyph);
+
+ if(!w || !h)
+ return MDVI_GLYPH_EMPTY;
+ switch(glyph->bpp << 3) {
+ case 8:
+ bm = bitmap_convert_lsb8((unsigned char *)glyph->bits, w, h);
+ break;
+ default:
+ warning(_("(t1) unsupported bitmap pad size %d\n"),
+ glyph->bpp);
+ bm = MDVI_GLYPH_EMPTY;
+ break;
+ }
+ return bm;
+}
+
+static void t1_font_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest)
+{
+ double size;
+ GLYPH *glyph;
+ T1Info *info;
+ T1_TMATRIX matrix;
+
+ info = (T1Info *)font->private;
+ ASSERT(info != NULL);
+
+ DEBUG((DBG_TYPE1, "(t1) shrinking glyph for character %d in `%s' (%d,%d)\n",
+ ch->code, font->fontname, ch->width, ch->height));
+ size = (double)font->scale / (dvi->params.tfm_conv * 0x100000);
+ size = 72.0 * size / 72.27;
+ matrix.cxx = 1.0/(double)dvi->params.hshrink;
+ matrix.cyy = 1.0/(double)dvi->params.vshrink;
+ matrix.cxy = 0.0;
+ matrix.cyx = 0.0;
+ glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix);
+
+ dest->data = t1_glyph_bitmap(glyph);
+ dest->x = -glyph->metrics.leftSideBearing;
+ dest->y = glyph->metrics.ascent;
+ dest->w = GLYPH_WIDTH(glyph);
+ dest->h = GLYPH_HEIGHT(glyph);
+
+#ifndef NODEBUG
+ if(DEBUGGING(BITMAP_DATA)) {
+ DEBUG((DBG_BITMAP_DATA,
+ "(t1) %s: t1_shrink_glyph(%d): (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n",
+ ch->glyph.w, ch->glyph.h, ch->glyph.x, ch->glyph.y,
+ dest->w, dest->h, dest->x, dest->y));
+ bitmap_print(stderr, (BITMAP *)dest->data);
+ }
+#endif
+ /* transform the glyph - we could do this with t1lib, but we do
+ * it ourselves for now */
+ font_transform_glyph(dvi->params.orientation, dest);
+}
+
+static int t1_font_get_glyph(DviParams *params, DviFont *font, int code)
+{
+ T1Info *info = (T1Info *)font->private;
+ GLYPH *glyph;
+ DviFontChar *ch;
+ double size;
+ T1_TMATRIX matrix;
+ int dpi;
+
+ ASSERT(info != NULL);
+ if(!info->hasmetrics && t1_really_load_font(params, font, info) < 0)
+ return -1;
+ ch = FONTCHAR(font, code);
+ if(!ch || !glyph_present(ch))
+ return -1;
+ ch->loaded = 1;
+ if(!ch->width || !ch->height) {
+ ch->glyph.x = ch->x;
+ ch->glyph.y = ch->y;
+ ch->glyph.w = ch->width;
+ ch->glyph.h = ch->height;
+ ch->glyph.data = NULL;
+ return 0;
+ }
+
+ /* load the glyph with T1lib (this is done only once for each glyph) */
+
+ /* get size in TeX points (tfm_conv includes dpi and magnification) */
+ size = (double)font->scale / (params->tfm_conv * 0x100000);
+ /* and transform into PostScript points */
+ size = 72.0 * size / 72.27;
+
+ dpi = Max(font->hdpi, font->vdpi);
+ /* we don't want the glyph to be cached twice (once by us, another by
+ * T1lib), so we use an identity matrix to tell T1lib not to keep the
+ * glyph around */
+ matrix.cxx = (double)font->hdpi / dpi;
+ matrix.cyy = (double)font->vdpi / dpi;
+ matrix.cxy = matrix.cyx = 0.0;
+ glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix);
+ if(glyph == NULL) {
+ ch->glyph.x = ch->x;
+ ch->glyph.y = ch->y;
+ ch->glyph.w = ch->width;
+ ch->glyph.h = ch->height;
+ ch->glyph.data = NULL;
+ ch->missing = 1;
+ return 0;
+ }
+ /* and make it a bitmap */
+ ch->glyph.data = t1_glyph_bitmap(glyph);
+ ch->glyph.x = -glyph->metrics.leftSideBearing;
+ ch->glyph.y = glyph->metrics.ascent;
+ ch->glyph.w = GLYPH_WIDTH(glyph);
+ ch->glyph.h = GLYPH_HEIGHT(glyph);
+
+ /* let's also fix the glyph's origin
+ * (which is not contained in the TFM) */
+ ch->x = ch->glyph.x;
+ ch->y = ch->glyph.y;
+ /* let's fix these too */
+ ch->width = ch->glyph.w;
+ ch->height = ch->glyph.h;
+
+ return 0;
+}
+
+static void t1_font_remove(T1Info *info)
+{
+ T1Info *old;
+
+ /* first remove it from our list */
+ listh_remove(&t1fonts, LIST(info));
+
+ /* it it's in the hash table, we may need to replace this by another font */
+ old = (T1Info *)mdvi_hash_lookup(&t1hash, (unsigned char *)info->fontname);
+ if(old == info) {
+ mdvi_hash_remove(&t1hash, (unsigned char *) info->fontname);
+ /* go through the list and see if there is another
+ * font with this name */
+ for(old = (T1Info *)t1fonts.head; old; old = old->next)
+ if(STREQ(old->fontname, info->fontname))
+ break;
+ if(old != NULL)
+ mdvi_hash_add(&t1hash, (unsigned char *) old->fontname, old,
+ MDVI_HASH_UNCHECKED);
+ }
+ /* release our encoding vector */
+ if(info->encoding) {
+ DEBUG((DBG_TYPE1, "(t1) %s: releasing vector `%s'\n",
+ info->fontname, info->encoding->name));
+ mdvi_release_encoding(info->encoding, 1);
+ }
+
+ /* now get rid of it */
+ if(info->t1id != -1) {
+ DEBUG((DBG_TYPE1, "(t1) %s: T1_DeleteFont(%d)\n",
+ info->fontname, info->t1id));
+ T1_DeleteFont(info->t1id);
+ } else
+ DEBUG((DBG_TYPE1, "(t1) %s: not loaded yet, DeleteFont skipped\n",
+ info->fontname));
+
+ if(info->tfminfo)
+ free_font_metrics(info->tfminfo);
+ /*mdvi_free(info->fontname);*/
+ mdvi_free(info);
+}
+
+static void t1_free_data(DviFont *font)
+{
+ /* called after all the glyphs are destroyed */
+
+ if(font->private == NULL) {
+ /* this is perfectly normal, it just means the font has
+ * not been requested by MDVI yet */
+ return;
+ }
+
+ /* destroy this data */
+
+ t1_font_remove((T1Info *)font->private);
+ font->private = NULL;
+
+ /*
+ * if this is the last T1 font, reset the T1 library
+ * It is important that we do this, because this is will be called
+ * when the resolution or the magnification changes.
+ */
+ if(t1fonts.count == 0) {
+ DEBUG((DBG_TYPE1, "(t1) last font removed -- closing T1lib\n"));
+ T1_CloseLib();
+ t1lib_initialized = 0;
+ t1lib_xdpi = -1;
+ t1lib_ydpi = -1;
+ }
+}
+
+#endif /* WITH_TYPE1_FONTS */
diff --git a/backend/dvi/mdvi-lib/tfm.c b/backend/dvi/mdvi-lib/tfm.c
new file mode 100644
index 0000000..3779c6b
--- /dev/null
+++ b/backend/dvi/mdvi-lib/tfm.c
@@ -0,0 +1,213 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+static int tfm_load_font __PROTO((DviParams *, DviFont *));
+static int tfm_font_get_glyph __PROTO((DviParams *, DviFont *, int));
+
+DviFontInfo tfm_font_info = {
+ "TFM",
+ 0, /* scaling not supported by format */
+ tfm_load_font,
+ tfm_font_get_glyph,
+ mdvi_shrink_box,
+ mdvi_shrink_box,
+ NULL, /* free */
+ NULL, /* reset */
+ NULL, /* lookup */
+ kpse_tfm_format,
+ NULL
+};
+
+DviFontInfo ofm_font_info = {
+ "OFM",
+ 0, /* scaling not supported by format */
+ tfm_load_font,
+ tfm_font_get_glyph,
+ mdvi_shrink_box,
+ mdvi_shrink_box,
+ NULL, /* free */
+ NULL, /* reset */
+ NULL, /* lookup */
+ kpse_ofm_format,
+ NULL
+};
+
+DviFontInfo afm_font_info = {
+ "AFM",
+ 0, /* scaling not supported by format */
+ tfm_load_font,
+ tfm_font_get_glyph,
+ mdvi_shrink_box,
+ mdvi_shrink_box,
+ NULL, /* free */
+ NULL, /* reset */
+ NULL, /* lookup */
+ kpse_afm_format,
+ NULL
+};
+
+#define TYPENAME(font) \
+ ((font)->search.info ? (font)->search.info : "none")
+
+/*
+ * Although it does not seem that way, this conversion is independent of the
+ * shrinking factors, within roundoff (that's because `conv' and `vconv'
+ * have already been scaled by hshrink and vshrink, repsectively). We
+ * should really use `dviconv' and `dvivconv', but I'm not so sure those
+ * should be moved to the DviParams structure.
+ */
+#define XCONV(x) FROUND(params->conv * (x) * params->hshrink)
+#define YCONV(y) FROUND(params->vconv * (y) * params->vshrink)
+
+/* this is used quite often in several places, so I made it standalone */
+int get_tfm_chars(DviParams *params, DviFont *font, TFMInfo *info, int loaded)
+{
+ Int32 z, alpha, beta;
+ int n;
+ DviFontChar *ch;
+ TFMChar *ptr;
+
+ n = info->hic - info->loc + 1;
+ if(n != FONT_GLYPH_COUNT(font)) {
+ font->chars = mdvi_realloc(font->chars,
+ n * sizeof(DviFontChar));
+ }
+ font->loc = info->loc;
+ font->hic = info->hic;
+ ch = font->chars;
+ ptr = info->chars;
+
+ /* Prepare z, alpha and beta for TFM width computation */
+ TFMPREPARE(font->scale, z, alpha, beta);
+
+ /* get the character metrics */
+ for(n = info->loc; n <= info->hic; ch++, ptr++, n++) {
+ int a, b, c, d;
+
+ ch->offset = ptr->present;
+ if(ch->offset == 0)
+ continue;
+ /* this is what we came here for */
+ ch->tfmwidth = TFMSCALE(z, ptr->advance, alpha, beta);
+ /* scale all other TFM units (so they are in DVI units) */
+ a = TFMSCALE(z, ptr->left, alpha, beta);
+ b = TFMSCALE(z, ptr->right, alpha, beta);
+ c = TFMSCALE(z, ptr->height, alpha, beta);
+ d = TFMSCALE(z, ptr->depth, alpha, beta);
+
+ /* now convert to unscaled pixels */
+ ch->width = XCONV(b - a);
+ ch->height = YCONV(c - d);
+ if(ch->height < 0) ch->height = -ch->height;
+ ch->x = XCONV(a);
+ ch->y = YCONV(c);
+ /*
+ * the offset is not used, but we might as well set it to
+ * something meaningful (and it MUST be non-zero)
+ */
+ ch->flags = 0;
+ ch->code = n;
+ ch->glyph.data = NULL;
+ ch->grey.data = NULL;
+ ch->shrunk.data = NULL;
+ ch->loaded = loaded;
+ }
+
+ return 0;
+}
+
+/*
+ * We use this function as a last resort to find the character widths in a
+ * font The DVI rendering code can correctly skip over a glyph if it knows
+ * its TFM width, which is what we try to find here.
+ */
+static int tfm_load_font(DviParams *params, DviFont *font)
+{
+ TFMInfo *tfm;
+ int type;
+
+ switch(font->search.info->kpse_type) {
+ case kpse_tfm_format:
+ type = DviFontTFM;
+ break;
+ case kpse_afm_format:
+ type = DviFontAFM;
+ break;
+ case kpse_ofm_format:
+ type = DviFontOFM;
+ break;
+ default:
+ return -1;
+ }
+
+ /* we don't need this */
+ if(font->in) {
+ fclose(font->in);
+ font->in = NULL;
+ }
+ tfm = get_font_metrics(font->fontname, type, font->filename);
+ if(tfm == NULL)
+ return -1;
+
+ if(tfm->checksum && font->checksum && tfm->checksum != font->checksum) {
+ warning(_("%s: Checksum mismatch (got %u, expected %u)\n"),
+ font->fontname, (unsigned)tfm->checksum,
+ (unsigned)font->checksum);
+ }
+ font->checksum = tfm->checksum;
+ font->design = tfm->design;
+ font->loc = 0;
+ font->hic = 0;
+ font->chars = NULL;
+ get_tfm_chars(params, font, tfm, 1);
+
+ /* free everything */
+ free_font_metrics(tfm);
+
+ return 0;
+}
+
+static int tfm_font_get_glyph(DviParams *params, DviFont *font, int code)
+{
+ DviFontChar *ch;
+
+ ch = FONTCHAR(font, code);
+ if(!glyph_present(ch))
+ return -1;
+ ch->glyph.x = ch->x;
+ ch->glyph.y = ch->y;
+ ch->glyph.w = ch->width;
+ ch->glyph.h = ch->height;
+ /*
+ * This has two purposes: (1) avoid unnecessary calls to this function,
+ * and (2) detect when the glyph data for a TFM font is actually used
+ * (we'll get a SEGV). Any occurrence of that is a bug.
+ */
+ ch->glyph.data = MDVI_GLYPH_EMPTY;
+
+ return 0;
+}
diff --git a/backend/dvi/mdvi-lib/tfmfile.c b/backend/dvi/mdvi-lib/tfmfile.c
new file mode 100644
index 0000000..1ea1b13
--- /dev/null
+++ b/backend/dvi/mdvi-lib/tfmfile.c
@@ -0,0 +1,746 @@
+/* tfmfile.c -- readers for TFM, AFM, OTFM-0 and OTFM-1 files */
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h> /* tex-file.h needs this */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+#ifdef WITH_AFM_FILES
+#undef TRUE
+#undef FALSE
+#include "afmparse.h"
+#endif
+
+typedef struct tfmpool {
+ struct tfmpool *next;
+ struct tfmpool *prev;
+ char *short_name;
+ int links;
+ TFMInfo tfminfo;
+} TFMPool;
+
+static ListHead tfmpool = {NULL, NULL, 0};
+static DviHashTable tfmhash;
+
+#define TFM_HASH_SIZE 31
+
+#ifdef WORD_LITTLE_ENDIAN
+static inline void swap_array(Uint32 *ptr, int n)
+{
+ Uint32 i;
+
+ while(n-- > 0) {
+ i = *ptr;
+ *ptr++ = ((i & 0xff000000) >> 24)
+ | ((i & 0x00ff0000) >> 8)
+ | ((i & 0x0000ff00) << 8)
+ | ((i & 0x000000ff) << 24);
+ }
+}
+#endif
+
+#ifdef WITH_AFM_FILES
+
+static int __PROTO(ofm_load_file(const char *filename, TFMInfo *info));
+
+/* reading of AFM files */
+/* macro to convert between AFM and TFM units */
+#define AFM2TFM(x) FROUND((double)(x) * 0x100000 / 1000)
+int afm_load_file(const char *filename, TFMInfo *info)
+{
+ /* the information we want is:
+ * - tfmwidth
+ * - width and heights
+ * - character origins
+ */
+ FontInfo *fi = NULL;
+ int status;
+ CharMetricInfo *cm;
+ FILE *in;
+
+ in = fopen(filename, "r");
+ if(in == NULL)
+ return -1;
+ status = afm_parse_file(in, &fi, P_GM);
+ fclose(in);
+
+ if(status != ok) {
+ error(_("%s: Error reading AFM data\n"), filename);
+ return -1;
+ }
+
+ /* aim high */
+ info->chars = xnalloc(TFMChar, 256);
+ info->loc = 256;
+ info->hic = 0;
+ info->design = 0xa00000; /* fake -- 10pt */
+ info->checksum = 0; /* no checksum */
+ info->type = DviFontAFM;
+ mdvi_strncpy(info->coding, fi->gfi->encodingScheme, 63);
+ mdvi_strncpy(info->family, fi->gfi->familyName, 63);
+
+ /* now get the data */
+ for(cm = fi->cmi; cm < fi->cmi + fi->numOfChars; cm++) {
+ int code;
+ TFMChar *ch;
+
+ code = cm->code;
+ if(code < 0 || code > 255)
+ continue; /* ignore it */
+ ch = &info->chars[code];
+ ch->present = 1;
+ if(code < info->loc)
+ info->loc = code;
+ if(code > info->hic)
+ info->hic = code;
+ ch->advance = AFM2TFM(cm->wx);
+ /* this is the `leftSideBearing' */
+ ch->left = AFM2TFM(cm->charBBox.llx);
+ /* this is the height (ascent - descent) -- the sign is to follow
+ * TeX conventions, as opposed to Adobe's ones */
+ ch->depth = -AFM2TFM(cm->charBBox.lly);
+ /* this is the width (rightSideBearing - leftSideBearing) */
+ ch->right = AFM2TFM(cm->charBBox.urx);
+ /* this is the `ascent' */
+ ch->height = AFM2TFM(cm->charBBox.ury);
+ }
+
+ /* we don't need this anymore */
+ afm_free_fontinfo(fi);
+
+ /* optimize storage */
+ if(info->loc > 0 || info->hic < 256) {
+ memmove(&info->chars[0],
+ &info->chars[info->loc],
+ (info->hic - info->loc + 1) * sizeof(TFMChar));
+ info->chars = mdvi_realloc(info->chars,
+ (info->hic - info->loc + 1) * sizeof(TFMChar));
+ }
+
+ /* we're done */
+ return 0;
+}
+
+#endif /* WITH_AFM_FILES */
+
+int tfm_load_file(const char *filename, TFMInfo *info)
+{
+ int lf, lh, bc, ec, nw, nh, nd, ne;
+ int i, n;
+ Uchar *tfm;
+ Uchar *ptr;
+ struct stat st;
+ int size;
+ FILE *in;
+ Int32 *cb;
+ Int32 *charinfo;
+ Int32 *widths;
+ Int32 *heights;
+ Int32 *depths;
+ Uint32 checksum;
+
+ in = fopen(filename, "r");
+ if(in == NULL)
+ return -1;
+ tfm = NULL;
+
+ DEBUG((DBG_FONTS, "(mt) reading TFM file `%s'\n",
+ filename));
+ /* We read the entire TFM file into core */
+ if(fstat(fileno(in), &st) < 0)
+ return -1;
+ if(st.st_size == 0)
+ goto bad_tfm;
+
+ /* allocate a word-aligned buffer to hold the file */
+ size = 4 * ROUND(st.st_size, 4);
+ if(size != st.st_size)
+ warning(_("Warning: TFM file `%s' has suspicious size\n"),
+ filename);
+ tfm = (Uchar *)mdvi_malloc(size);
+ if(fread(tfm, st.st_size, 1, in) != 1)
+ goto error;
+ /* we don't need this anymore */
+ fclose(in);
+ in = NULL;
+
+ /* not a checksum, but serves a similar purpose */
+ checksum = 0;
+
+ ptr = tfm;
+ /* get the counters */
+ lf = muget2(ptr);
+ lh = muget2(ptr); checksum += 6 + lh;
+ bc = muget2(ptr);
+ ec = muget2(ptr); checksum += ec - bc + 1;
+ nw = muget2(ptr); checksum += nw;
+ nh = muget2(ptr); checksum += nh;
+ nd = muget2(ptr); checksum += nd;
+ checksum += muget2(ptr); /* skip italics correction count */
+ checksum += muget2(ptr); /* skip lig/kern table size */
+ checksum += muget2(ptr); /* skip kern table size */
+ ne = muget2(ptr); checksum += ne;
+ checksum += muget2(ptr); /* skip # of font parameters */
+
+ size = ec - bc + 1;
+ cb = (Int32 *)tfm; cb += 6 + lh;
+ charinfo = cb; cb += size;
+ widths = cb; cb += nw;
+ heights = cb; cb += nh;
+ depths = cb;
+
+ if(widths[0] || heights[0] || depths[0] ||
+ checksum != lf || bc - 1 > ec || ec > 255 || ne > 256)
+ goto bad_tfm;
+
+ /* from this point on, no error checking is done */
+
+ /* now we're at the header */
+ /* get the checksum */
+ info->checksum = muget4(ptr);
+ /* get the design size */
+ info->design = muget4(ptr);
+ /* get the coding scheme */
+ if(lh > 2) {
+ /* get the coding scheme */
+ i = n = msget1(ptr);
+ if(n < 0 || n > 39) {
+ warning(_("%s: font coding scheme truncated to 40 bytes\n"),
+ filename);
+ n = 39;
+ }
+ memcpy(info->coding, ptr, n);
+ info->coding[n] = 0;
+ ptr += i;
+ } else
+ strcpy(info->coding, "FontSpecific");
+ /* get the font family */
+ if(lh > 12) {
+ n = msget1(ptr);
+ if(n > 0) {
+ i = Max(n, 63);
+ memcpy(info->family, ptr, i);
+ info->family[i] = 0;
+ } else
+ strcpy(info->family, "unspecified");
+ ptr += n;
+ }
+ /* now we don't read from `ptr' anymore */
+
+ info->loc = bc;
+ info->hic = ec;
+ info->type = DviFontTFM;
+
+ /* allocate characters */
+ info->chars = xnalloc(TFMChar, size);
+
+
+#ifdef WORD_LITTLE_ENDIAN
+ /* byte-swap the three arrays at once (they are consecutive in memory) */
+ swap_array((Uint32 *)widths, nw + nh + nd);
+#endif
+
+ /* get the relevant data */
+ ptr = (Uchar *)charinfo;
+ for(i = bc; i <= ec; ptr += 3, i++) {
+ int ndx;
+
+ ndx = (int)*ptr; ptr++;
+ info->chars[i-bc].advance = widths[ndx];
+ /* TFM files lack this information */
+ info->chars[i-bc].left = 0;
+ info->chars[i-bc].right = widths[ndx];
+ info->chars[i-bc].present = (ndx != 0);
+ if(ndx) {
+ ndx = ((*ptr >> 4) & 0xf);
+ info->chars[i-bc].height = heights[ndx];
+ ndx = (*ptr & 0xf);
+ info->chars[i-bc].depth = depths[ndx];
+ }
+ }
+
+ /* free everything */
+ mdvi_free(tfm);
+
+ return 0;
+
+bad_tfm:
+ error(_("%s: File corrupted, or not a TFM file\n"), filename);
+error:
+ if(tfm) mdvi_free(tfm);
+ if(in) fclose(in);
+ return -1;
+}
+
+static int ofm1_load_file(FILE *in, TFMInfo *info)
+{
+ int lf, lh, bc, ec, nw, nh, nd;
+ int nco, ncw, npc;
+ int i;
+ int n;
+ int size;
+ Int32 *tfm;
+ Int32 *widths;
+ Int32 *heights;
+ Int32 *depths;
+ TFMChar *tch;
+ TFMChar *end;
+
+ lf = fuget4(in);
+ lh = fuget4(in);
+ bc = fuget4(in);
+ ec = fuget4(in);
+ nw = fuget4(in);
+ nh = fuget4(in);
+ nd = fuget4(in);
+ fuget4(in); /* italics */
+ fuget4(in); /* lig-kern */
+ fuget4(in); /* kern */
+ fuget4(in); /* extensible recipe */
+ fuget4(in); /* parameters */
+ fuget4(in); /* direction */
+ nco = fuget4(in);
+ ncw = fuget4(in);
+ npc = fuget4(in);
+
+ /* get the checksum */
+ info->checksum = fuget4(in);
+ /* the design size */
+ info->design = fuget4(in);
+ /* get the coding scheme */
+ if(lh > 2) {
+ /* get the coding scheme */
+ i = n = fsget1(in);
+ if(n < 0 || n > 39)
+ n = 39;
+ fread(info->coding, 39, 1, in);
+ info->coding[n] = 0;
+ } else
+ strcpy(info->coding, "FontSpecific");
+ /* get the font family */
+ if(lh > 12) {
+ n = fsget1(in);
+ if(n > 0) {
+ i = Max(n, 63);
+ fread(info->family, i, 1, in);
+ info->family[i] = 0;
+ } else
+ strcpy(info->family, "unspecified");
+ }
+ tfm = NULL;
+
+ /* jump to the beginning of the char-info table */
+ fseek(in, 4L*nco, SEEK_SET);
+
+ size = ec - bc + 1;
+ info->loc = bc;
+ info->hic = ec;
+ info->chars = xnalloc(TFMChar, size);
+ end = info->chars + size;
+
+ for(tch = info->chars, i = 0; i < ncw; i++) {
+ TFMChar ch;
+ int nr;
+
+ /* in the characters we store the actual indices */
+ ch.advance = fuget2(in);
+ ch.height = fuget1(in);
+ ch.depth = fuget1(in);
+ /* skip 2nd word */
+ fuget4(in);
+ /* get # of repeats */
+ nr = fuget2(in);
+ /* skip parameters */
+ fseek(in, (long)npc * 2, SEEK_CUR);
+ /* if npc is odd, skip padding */
+ if(npc & 1) fuget2(in);
+
+ /* now repeat the character */
+ while(nr-- >= 0 && tch < end)
+ memcpy(tch++, &ch, sizeof(TFMChar));
+ if(tch == end)
+ goto bad_tfm;
+ }
+
+ /* I wish we were done, but we aren't */
+
+ /* get the widths, heights and depths */
+ size = nw + nh + nd;
+ tfm = xnalloc(Int32, size);
+ /* read them in one sweep */
+ if(fread(tfm, 4, size, in) != size) {
+ mdvi_free(tfm);
+ goto bad_tfm;
+ }
+
+ /* byte-swap things if necessary */
+#ifdef WORD_LITTLE_ENDIAN
+ swap_array((Uint32 *)tfm, size);
+#endif
+ widths = tfm;
+ heights = widths + nw;
+ depths = heights + nh;
+
+ if(widths[0] || heights[0] || depths[0])
+ goto bad_tfm;
+
+ /* now fix the characters */
+ size = ec - bc + 1;
+ for(tch = info->chars; tch < end; tch++) {
+ tch->present = (tch->advance != 0);
+ tch->advance = widths[tch->advance];
+ tch->height = heights[tch->height];
+ tch->depth = depths[tch->depth];
+ tch->left = 0;
+ tch->right = tch->advance;
+ }
+
+ /* NOW we're done */
+ mdvi_free(tfm);
+ return 0;
+
+bad_tfm:
+ if(tfm) mdvi_free(tfm);
+ return -1;
+}
+
+/* we don't read OFM files into memory, because they can potentially be large */
+static int ofm_load_file(const char *filename, TFMInfo *info)
+{
+ int lf, lh, bc, ec, nw, nh, nd;
+ int i, n;
+ Int32 *tfm;
+ Uchar *ptr;
+ int size;
+ FILE *in;
+ Int32 *cb;
+ Int32 *charinfo;
+ Int32 *widths;
+ Int32 *heights;
+ Int32 *depths;
+ Uint32 checksum;
+ int olevel;
+ int nwords;
+
+ in = fopen(filename, "r");
+ if(in == NULL)
+ return -1;
+
+ /* not a checksum, but serves a similar purpose */
+ checksum = 0;
+
+ /* get the counters */
+ /* get file level */
+ olevel = fsget2(in);
+ if(olevel != 0)
+ goto bad_tfm;
+ olevel = fsget2(in);
+ if(olevel != 0) {
+ DEBUG((DBG_FONTS, "(mt) reading Level-1 OFM file `%s'\n",
+ filename));
+ /* we handle level-1 files separately */
+ if(ofm1_load_file(in, info) < 0)
+ goto bad_tfm;
+ return 0;
+ }
+
+ DEBUG((DBG_FONTS, "(mt) reading Level-0 OFM file `%s'\n", filename));
+ nwords = 14;
+ lf = fuget4(in); checksum = nwords;
+ lh = fuget4(in); checksum += lh;
+ bc = fuget4(in);
+ ec = fuget4(in); checksum += 2 * (ec - bc + 1);
+ nw = fuget4(in); checksum += nw;
+ nh = fuget4(in); checksum += nh;
+ nd = fuget4(in); checksum += nd;
+ checksum += fuget4(in); /* skip italics correction count */
+ checksum += 2*fuget4(in); /* skip lig/kern table size */
+ checksum += fuget4(in); /* skip kern table size */
+ checksum += 2*fuget4(in); /* skip extensible recipe count */
+ checksum += fuget4(in); /* skip # of font parameters */
+
+ /* I have found several .ofm files that seem to have the
+ * font-direction word missing, so we try to detect that here */
+ if(checksum == lf + 1) {
+ DEBUG((DBG_FONTS, "(mt) font direction missing in `%s'\n",
+ filename));
+ checksum--;
+ nwords--;
+ } else {
+ /* skip font direction */
+ fuget4(in);
+ }
+
+ if(checksum != lf || bc > ec + 1 || ec > 65535)
+ goto bad_tfm;
+
+ /* now we're at the header */
+
+ /* get the checksum */
+ info->checksum = fuget4(in);
+ /* get the design size */
+ info->design = fuget4(in);
+
+ /* get the coding scheme */
+ if(lh > 2) {
+ /* get the coding scheme */
+ i = n = fsget1(in);
+ if(n < 0 || n > 39) {
+ warning(_("%s: font coding scheme truncated to 40 bytes\n"),
+ filename);
+ n = 39;
+ }
+ fread(info->coding, 39, 1, in);
+ info->coding[n] = 0;
+ } else
+ strcpy(info->coding, "FontSpecific");
+ /* get the font family */
+ if(lh > 12) {
+ n = fsget1(in);
+ if(n > 0) {
+ i = Max(n, 63);
+ fread(info->family, i, 1, in);
+ info->family[i] = 0;
+ } else
+ strcpy(info->family, "unspecified");
+ }
+
+ /* now skip anything else in the header */
+ fseek(in, 4L*(nwords + lh), SEEK_SET);
+ /* and read everything at once */
+ size = 2*(ec - bc + 1) + nw + nh + nd;
+ tfm = xnalloc(Int32, size * sizeof(Int32));
+ if(fread(tfm, 4, size, in) != size) {
+ mdvi_free(tfm);
+ goto bad_tfm;
+ }
+ /* byte-swap all the tables at once */
+#ifdef WORD_LITTLE_ENDIAN
+ swap_array((Uint32 *)tfm, size);
+#endif
+ cb = tfm;
+ charinfo = cb; cb += 2*(ec - bc + 1);
+ widths = cb; cb += nw;
+ heights = cb; cb += nh;
+ depths = cb;
+
+ if(widths[0] || heights[0] || depths[0]) {
+ mdvi_free(tfm);
+ goto bad_tfm;
+ }
+
+ /* from this point on, no error checking is done */
+
+ /* we don't need this anymore */
+ fclose(in);
+
+ /* now we don't read from `ptr' anymore */
+
+ info->loc = bc;
+ info->hic = ec;
+ info->type = DviFontTFM;
+
+ /* allocate characters */
+ info->chars = xnalloc(TFMChar, size);
+
+ /* get the relevant data */
+ ptr = (Uchar *)charinfo;
+ for(i = bc; i <= ec; ptr += 4, i++) {
+ int ndx;
+
+ ndx = muget2(ptr);
+ info->chars[i-bc].advance = widths[ndx];
+ /* TFM files lack this information */
+ info->chars[i-bc].left = 0;
+ info->chars[i-bc].right = widths[ndx];
+ info->chars[i-bc].present = (ndx != 0);
+ ndx = muget1(ptr);
+ info->chars[i-bc].height = heights[ndx];
+ ndx = muget1(ptr);
+ info->chars[i-bc].depth = depths[ndx];
+ }
+
+ mdvi_free(tfm);
+ return 0;
+
+bad_tfm:
+ error(_("%s: File corrupted, or not a TFM file\n"), filename);
+ fclose(in);
+ return -1;
+}
+
+char *lookup_font_metrics(const char *name, int *type)
+{
+ char *file;
+
+ switch(*type) {
+#ifndef WITH_AFM_FILES
+ case DviFontAny:
+#endif
+ case DviFontTFM:
+ file = kpse_find_tfm(name);
+ *type = DviFontTFM;
+ break;
+ case DviFontOFM: {
+ file = kpse_find_ofm(name);
+ /* we may have gotten a TFM back */
+ if(file != NULL) {
+ const char *ext = file_extension(file);
+ if(ext && STREQ(ext, "tfm"))
+ *type = DviFontTFM;
+ }
+ break;
+ }
+#ifdef WITH_AFM_FILES
+ case DviFontAFM:
+ file = kpse_find_file(name, kpse_afm_format, 0);
+ break;
+ case DviFontAny:
+ file = kpse_find_file(name, kpse_afm_format, 0);
+ *type = DviFontAFM;
+ if(file == NULL) {
+ file = kpse_find_tfm(name);
+ *type = DviFontTFM;
+ }
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ return file;
+}
+
+/*
+ * The next two functions are just wrappers for the font metric loaders,
+ * and use the pool of TFM data
+ */
+
+/* this is how we interpret arguments:
+ * - if filename is NULL, we look for files of the given type,
+ * unless type is DviFontAny, in which case we try all the
+ * types we know of.
+ * - if filename is not NULL, we look at `type' to decide
+ * how to read the file. If type is DviFontAny, we just
+ * return an error.
+ */
+TFMInfo *get_font_metrics(const char *short_name, int type, const char *filename)
+{
+ TFMPool *tfm = NULL;
+ int status;
+ char *file;
+
+ if(tfmpool.count) {
+ tfm = (TFMPool *)mdvi_hash_lookup(&tfmhash,
+ MDVI_KEY(short_name));
+ if(tfm != NULL) {
+ DEBUG((DBG_FONTS, "(mt) reusing metric file `%s' (%d links)\n",
+ short_name, tfm->links));
+ tfm->links++;
+ return &tfm->tfminfo;
+ }
+ }
+
+ file = filename ? (char *)filename : lookup_font_metrics(short_name, &type);
+ if(file == NULL)
+ return NULL;
+
+ tfm = xalloc(TFMPool);
+ DEBUG((DBG_FONTS, "(mt) loading font metric data from `%s'\n", file, file));
+ switch(type) {
+ case DviFontTFM:
+ status = tfm_load_file(file, &tfm->tfminfo);
+ break;
+ case DviFontOFM:
+ status = ofm_load_file(file, &tfm->tfminfo);
+ break;
+#ifdef WITH_AFM_FILES
+ case DviFontAFM:
+ status = afm_load_file(file, &tfm->tfminfo);
+ break;
+#endif
+ default:
+ status = -1;
+ break;
+ }
+ if(file != filename)
+ mdvi_free(file);
+ if(status < 0) {
+ mdvi_free(tfm);
+ return NULL;
+ }
+ tfm->short_name = mdvi_strdup(short_name);
+
+ /* add it to the pool */
+ if(tfmpool.count == 0)
+ mdvi_hash_create(&tfmhash, TFM_HASH_SIZE);
+ mdvi_hash_add(&tfmhash, MDVI_KEY(tfm->short_name),
+ tfm, MDVI_HASH_UNCHECKED);
+ listh_prepend(&tfmpool, LIST(tfm));
+ tfm->links = 1;
+
+ return &tfm->tfminfo;
+}
+
+void free_font_metrics(TFMInfo *info)
+{
+ TFMPool *tfm;
+
+ if(tfmpool.count == 0)
+ return;
+ /* get the entry -- can't use the hash table for this, because
+ * we don't have the short name */
+ for(tfm = (TFMPool *)tfmpool.head; tfm; tfm = tfm->next)
+ if(info == &tfm->tfminfo)
+ break;
+ if(tfm == NULL)
+ return;
+ if(--tfm->links > 0) {
+ DEBUG((DBG_FONTS, "(mt) %s not removed, still in use\n",
+ tfm->short_name));
+ return;
+ }
+ mdvi_hash_remove_ptr(&tfmhash, MDVI_KEY(tfm->short_name));
+
+ DEBUG((DBG_FONTS, "(mt) removing unused TFM data for `%s'\n", tfm->short_name));
+ listh_remove(&tfmpool, LIST(tfm));
+ mdvi_free(tfm->short_name);
+ mdvi_free(tfm->tfminfo.chars);
+ mdvi_free(tfm);
+}
+
+void flush_font_metrics(void)
+{
+ TFMPool *ptr;
+
+ for(; (ptr = (TFMPool *)tfmpool.head); ) {
+ tfmpool.head = LIST(ptr->next);
+
+ mdvi_free(ptr->short_name);
+ mdvi_free(ptr->tfminfo.chars);
+ mdvi_free(ptr);
+ }
+ mdvi_hash_reset(&tfmhash, 0);
+}
diff --git a/backend/dvi/mdvi-lib/tt.c b/backend/dvi/mdvi-lib/tt.c
new file mode 100644
index 0000000..2e69940
--- /dev/null
+++ b/backend/dvi/mdvi-lib/tt.c
@@ -0,0 +1,494 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mdvi.h"
+
+#ifdef WITH_TRUETYPE_FONTS
+
+#include <string.h>
+#include <freetype.h>
+#include <ftxpost.h>
+#include <ftxerr18.h>
+
+#include "private.h"
+
+static TT_Engine tt_handle;
+static int initialized = 0;
+
+typedef struct ftinfo {
+ struct ftinfo *next;
+ struct ftinfo *prev;
+ char *fontname;
+ char *fmfname;
+ TT_Face face;
+ TT_Instance instance;
+ TT_Glyph glyph;
+ int hasmetrics;
+ int loaded;
+ int fmftype;
+ TFMInfo *tfminfo;
+ DviFontMapInfo mapinfo;
+ DviEncoding *encoding;
+} FTInfo;
+
+static int tt_load_font __PROTO((DviParams *, DviFont *));
+static int tt_font_get_glyph __PROTO((DviParams *, DviFont *, int));
+static void tt_free_data __PROTO((DviFont *));
+static void tt_reset_font __PROTO((DviFont *));
+static void tt_shrink_glyph
+ __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *));
+static void tt_font_remove __PROTO((FTInfo *));
+
+DviFontInfo tt_font_info = {
+ "TT",
+ 0,
+ tt_load_font,
+ tt_font_get_glyph,
+ tt_shrink_glyph,
+ mdvi_shrink_glyph_grey,
+ tt_free_data, /* free */
+ tt_reset_font, /* reset */
+ NULL, /* lookup */
+ kpse_truetype_format,
+ NULL
+};
+
+#define FT_HASH_SIZE 31
+
+static ListHead ttfonts = {NULL, NULL, 0};
+
+static int init_freetype(void)
+{
+ TT_Error code;
+
+ ASSERT(initialized == 0);
+ code = TT_Init_FreeType(&tt_handle);
+ if(code) {
+ DEBUG((DBG_TT, "(tt) Init_Freetype: error %d\n", code));
+ return -1;
+ }
+ code = TT_Init_Post_Extension(tt_handle);
+ if(code) {
+ TT_Done_FreeType(tt_handle);
+ return -1;
+ }
+ /* we're on */
+ initialized = 1;
+ return 0;
+}
+
+static void tt_encode_font(DviFont *font, FTInfo *info)
+{
+ TT_Face_Properties prop;
+ int i;
+
+ if(TT_Get_Face_Properties(info->face, &prop))
+ return;
+
+ for(i = 0; i < prop.num_Glyphs; i++) {
+ char *string;
+ int ndx;
+
+ if(TT_Get_PS_Name(info->face, i, &string))
+ continue;
+ ndx = mdvi_encode_glyph(info->encoding, string);
+ if(ndx < font->loc || ndx > font->hic)
+ continue;
+ font->chars[ndx - font->loc].code = i;
+ }
+}
+
+static int tt_really_load_font(DviParams *params, DviFont *font, FTInfo *info)
+{
+ DviFontChar *ch;
+ TFMChar *ptr;
+ Int32 z, alpha, beta;
+ int i;
+ FTInfo *old;
+ TT_Error status;
+ double point_size;
+ static int warned = 0;
+ TT_CharMap cmap;
+ TT_Face_Properties props;
+ int map_found;
+
+ DEBUG((DBG_TT, "(tt) really_load_font(%s)\n", info->fontname));
+
+ /* get the point size */
+ point_size = (double)font->scale / (params->tfm_conv * 0x100000);
+ point_size = 72.0 * point_size / 72.27;
+ if(info->loaded) {
+ /* just reset the size info */
+ TT_Set_Instance_Resolutions(info->instance,
+ params->dpi, params->vdpi);
+ TT_Set_Instance_CharSize(info->instance, FROUND(point_size * 64));
+ /* FIXME: should extend/slant again */
+ info->hasmetrics = 1;
+ return 0;
+ }
+
+ /* load the face */
+ DEBUG((DBG_TT, "(tt) loading new face `%s'\n",
+ info->fontname));
+ status = TT_Open_Face(tt_handle, font->filename, &info->face);
+ if(status) {
+ warning(_("(tt) %s: could not load face: %s\n"),
+ info->fontname, TT_ErrToString18(status));
+ return -1;
+ }
+
+ /* create a new instance of this face */
+ status = TT_New_Instance(info->face, &info->instance);
+ if(status) {
+ warning(_("(tt) %s: could not create face: %s\n"),
+ info->fontname, TT_ErrToString18(status));
+ TT_Close_Face(info->face);
+ return -1;
+ }
+
+ /* create a glyph */
+ status = TT_New_Glyph(info->face, &info->glyph);
+ if(status) {
+ warning(_("(tt) %s: could not create glyph: %s\n"),
+ info->fontname, TT_ErrToString18(status));
+ goto tt_error;
+ }
+
+ /*
+ * We'll try to find a Unicode charmap. It's not that important that we
+ * actually find one, especially if the fontmap files are installed
+ * properly, but it's good to have some predefined behaviour
+ */
+ TT_Get_Face_Properties(info->face, &props);
+
+ map_found = -1;
+ for(i = 0; map_found < 0 && i < props.num_CharMaps; i++) {
+ TT_UShort pid, eid;
+
+ TT_Get_CharMap_ID(info->face, i, &pid, &eid);
+ switch(pid) {
+ case TT_PLATFORM_APPLE_UNICODE:
+ map_found = i;
+ break;
+ case TT_PLATFORM_ISO:
+ if(eid == TT_ISO_ID_7BIT_ASCII ||
+ eid == TT_ISO_ID_8859_1)
+ map_found = 1;
+ break;
+ case TT_PLATFORM_MICROSOFT:
+ if(eid == TT_MS_ID_UNICODE_CS)
+ map_found = 1;
+ break;
+ }
+ }
+ if(map_found < 0) {
+ warning(_("(tt) %s: no acceptable map found, using #0\n"),
+ info->fontname);
+ map_found = 0;
+ }
+ DEBUG((DBG_TT, "(tt) %s: using charmap #%d\n",
+ info->fontname, map_found));
+ TT_Get_CharMap(info->face, map_found, &cmap);
+
+ DEBUG((DBG_TT, "(tt) %s: Set_Char_Size(%.2f, %d, %d)\n",
+ font->fontname, point_size, font->hdpi, font->vdpi));
+ status = TT_Set_Instance_Resolutions(info->instance,
+ params->dpi, params->vdpi);
+ if(status) {
+ error(_("(tt) %s: could not set resolution: %s\n"),
+ info->fontname, TT_ErrToString18(status));
+ goto tt_error;
+ }
+ status = TT_Set_Instance_CharSize(info->instance,
+ FROUND(point_size * 64));
+ if(status) {
+ error(_("(tt) %s: could not set point size: %s\n"),
+ info->fontname, TT_ErrToString18(status));
+ goto tt_error;
+ }
+
+ /* after this point we don't fail */
+
+ /* get information from the fontmap */
+ status = mdvi_query_fontmap(&info->mapinfo, info->fontname);
+ if(!status && info->mapinfo.encoding)
+ info->encoding = mdvi_request_encoding(info->mapinfo.encoding);
+ else
+ info->encoding = NULL;
+
+ if(info->encoding != NULL) {
+ TT_Post post;
+
+ status = TT_Load_PS_Names(info->face, &post);
+ if(status) {
+ warning(_("(tt) %s: could not load PS name table\n"),
+ info->fontname);
+ mdvi_release_encoding(info->encoding, 0);
+ info->encoding = NULL;
+ }
+ }
+
+ /* get the metrics. If this fails, it's not fatal, but certainly bad */
+ info->tfminfo = get_font_metrics(info->fontname,
+ info->fmftype, info->fmfname);
+
+ if(info->tfminfo == NULL) {
+ warning("(tt) %s: no metrics data, font ignored\n",
+ info->fontname);
+ goto tt_error;
+ }
+ /* fix this */
+ font->design = info->tfminfo->design;
+
+ /* get the scaled character metrics */
+ get_tfm_chars(params, font, info->tfminfo, 0);
+
+ if(info->encoding)
+ tt_encode_font(font, info);
+ else {
+ warning(_("%s: no encoding vector found, expect bad output\n"),
+ info->fontname);
+ /* this is better than nothing */
+ for(i = font->loc; i <= font->hic; i++)
+ font->chars[i - font->loc].code = TT_Char_Index(cmap, i);
+ }
+
+ info->loaded = 1;
+ info->hasmetrics = 1;
+ return 0;
+
+tt_error:
+ tt_font_remove(info);
+ mdvi_free(font->chars);
+ font->chars = NULL;
+ font->loc = font->hic = 0;
+ return -1;
+}
+
+static int tt_load_font(DviParams *params, DviFont *font)
+{
+ int i;
+ FTInfo *info;
+
+ if(!initialized && init_freetype() < 0)
+ return -1;
+
+ if(font->in != NULL) {
+ fclose(font->in);
+ font->in = NULL;
+ }
+
+ info = xalloc(FTInfo);
+
+ memzero(info, sizeof(FTInfo));
+ info->fmftype = DviFontAny; /* any metrics type will do */
+ info->fmfname = lookup_font_metrics(font->fontname, &info->fmftype);
+ info->fontname = font->fontname;
+ info->hasmetrics = 0;
+ info->loaded = 0;
+
+ /* these will be obtained from the fontmaps */
+ info->mapinfo.psname = NULL;
+ info->mapinfo.encoding = NULL;
+ info->mapinfo.fontfile = NULL;
+ info->mapinfo.extend = 0;
+ info->mapinfo.slant = 0;
+
+ /* initialize these */
+ font->chars = xnalloc(DviFontChar, 256);
+ font->loc = 0;
+ font->hic = 255;
+ for(i = 0; i < 256; i++) {
+ font->chars[i].offset = 1;
+ font->chars[i].glyph.data = NULL;
+ font->chars[i].shrunk.data = NULL;
+ font->chars[i].grey.data = NULL;
+ }
+
+ if(info->fmfname == NULL)
+ warning(_("(tt) %s: no font metric data\n"), font->fontname);
+
+ listh_append(&ttfonts, LIST(info));
+ font->private = info;
+
+ return 0;
+}
+
+static int tt_get_bitmap(DviParams *params, DviFont *font,
+ int code, double xscale, double yscale, DviGlyph *glyph)
+{
+ TT_Outline outline;
+ TT_Raster_Map raster;
+ TT_BBox bbox;
+ TT_Glyph_Metrics metrics;
+ TT_Matrix mat;
+ FTInfo *info;
+ int error;
+ int have_outline = 0;
+ int w, h;
+
+ info = (FTInfo *)font->private;
+ if(info == NULL)
+ return -1;
+
+ error = TT_Load_Glyph(info->instance, info->glyph,
+ code, TTLOAD_DEFAULT);
+ if(error) goto tt_error;
+ error = TT_Get_Glyph_Outline(info->glyph, &outline);
+ if(error) goto tt_error;
+ have_outline = 1;
+ mat.xx = FROUND(xscale * 65536);
+ mat.yy = FROUND(yscale * 65536);
+ mat.yx = 0;
+ mat.xy = 0;
+ TT_Transform_Outline(&outline, &mat);
+ error = TT_Get_Outline_BBox(&outline, &bbox);
+ if(error) goto tt_error;
+ bbox.xMin &= -64;
+ bbox.yMin &= -64;
+ bbox.xMax = (bbox.xMax + 63) & -64;
+ bbox.yMax = (bbox.yMax + 63) & -64;
+ w = (bbox.xMax - bbox.xMin) / 64;
+ h = (bbox.yMax - bbox.yMin) / 64;
+
+ glyph->w = w;
+ glyph->h = h;
+ glyph->x = -bbox.xMin / 64;
+ glyph->y = bbox.yMax / 64;
+ if(!w || !h)
+ goto tt_error;
+ raster.rows = h;
+ raster.width = w;
+ raster.cols = ROUND(w, 8);
+ raster.size = h * raster.cols;
+ raster.flow = TT_Flow_Down;
+ raster.bitmap = mdvi_calloc(h, raster.cols);
+
+ TT_Translate_Outline(&outline, -bbox.xMin, -bbox.yMin);
+ TT_Get_Outline_Bitmap(tt_handle, &outline, &raster);
+ glyph->data = bitmap_convert_msb8(raster.bitmap, w, h);
+ TT_Done_Outline(&outline);
+ mdvi_free(raster.bitmap);
+
+ return 0;
+tt_error:
+ if(have_outline)
+ TT_Done_Outline(&outline);
+ return -1;
+}
+
+static int tt_font_get_glyph(DviParams *params, DviFont *font, int code)
+{
+ FTInfo *info = (FTInfo *)font->private;
+ DviFontChar *ch;
+ int error;
+ double xs, ys;
+ int dpi;
+
+ ASSERT(info != NULL);
+ if(!info->hasmetrics && tt_really_load_font(params, font, info) < 0)
+ return -1;
+ ch = FONTCHAR(font, code);
+ if(!ch || !glyph_present(ch))
+ return -1;
+ ch->loaded = 1;
+ if(!ch->width || !ch->height)
+ goto blank;
+ if(ch->code == 0) {
+ ch->glyph.data = NULL;
+ goto missing;
+ }
+ /* get the glyph */
+ dpi = Max(font->hdpi, font->vdpi);
+ error = tt_get_bitmap(params, font, ch->code,
+ (double)font->hdpi / dpi,
+ (double)font->vdpi / dpi,
+ &ch->glyph);
+ if(error)
+ goto missing;
+ ch->x = ch->glyph.x;
+ ch->y = ch->glyph.y;
+
+ return 0;
+
+missing:
+ ch->glyph.data = MDVI_GLYPH_EMPTY;
+ ch->missing = 1;
+blank:
+ ch->glyph.w = ch->width;
+ ch->glyph.h = ch->height;
+ ch->glyph.x = ch->x;
+ ch->glyph.y = ch->y;
+ return 0;
+}
+
+static void tt_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest)
+{
+ tt_get_bitmap(&dvi->params, font,
+ ch->code,
+ (double)font->hdpi / (dvi->params.dpi * dvi->params.hshrink),
+ (double)font->vdpi / (dvi->params.vdpi * dvi->params.vshrink),
+ dest);
+ /* transform the glyph for the current orientation */
+ font_transform_glyph(dvi->params.orientation, dest);
+}
+
+static void tt_reset_font(DviFont *font)
+{
+ FTInfo *info = (FTInfo *)font->private;
+
+ if(info == NULL)
+ return;
+ info->hasmetrics = 0;
+}
+
+static void tt_font_remove(FTInfo *info)
+{
+ FTInfo *old;
+
+ if(info->loaded) {
+ /* all fonts in the hash table have called TT_Open_Face */
+ TT_Done_Instance(info->instance);
+ TT_Close_Face(info->face);
+ }
+ listh_remove(&ttfonts, LIST(info));
+ /* release our encodings */
+ if(info->encoding)
+ mdvi_release_encoding(info->encoding, 1);
+ /* and destroy the font */
+ if(info->tfminfo)
+ free_font_metrics(info->tfminfo);
+ if(info->fmfname)
+ mdvi_free(info->fmfname);
+ mdvi_free(info);
+}
+
+static void tt_free_data(DviFont *font)
+{
+ if(font->private == NULL)
+ return;
+
+ tt_font_remove((FTInfo *)font->private);
+ if(initialized && ttfonts.count == 0) {
+ DEBUG((DBG_TT, "(tt) last font removed -- closing FreeType\n"));
+ TT_Done_FreeType(tt_handle);
+ initialized = 0;
+ }
+}
+
+#endif /* WITH_TRUETYPE_FONTS */
diff --git a/backend/dvi/mdvi-lib/util.c b/backend/dvi/mdvi-lib/util.c
new file mode 100644
index 0000000..c1cc649
--- /dev/null
+++ b/backend/dvi/mdvi-lib/util.c
@@ -0,0 +1,495 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "private.h"
+
+static char *const messages[] = {
+ _G("Ooops!"),
+ _G("Aieeeee!!"),
+ _G("Ouch!"),
+ _G("Houston, we have a problem"),
+ _G("3.. 2.. 1.. BOOM!"),
+ _G("I'm history"),
+ _G("I'm going down"),
+ _G("I smell a rat")
+};
+#define NMSGS (sizeof(messages) / sizeof(char *))
+
+static FILE *logfile = NULL;
+static int _mdvi_log_level;
+
+int mdvi_set_logfile(const char *filename);
+int mdvi_set_logstream(FILE *file);
+int mdvi_set_loglevel(int level);
+
+static void vputlog(int level, const char *head, const char *format, va_list ap)
+{
+ if(logfile != NULL && _mdvi_log_level >= level) {
+ if(head != NULL)
+ fprintf(logfile, "%s: ", head);
+ vfprintf(logfile, format, ap);
+ }
+}
+
+int mdvi_set_logfile(const char *filename)
+{
+ FILE *f = NULL;
+
+ if(filename && (f = fopen(filename, "w")) == NULL)
+ return -1;
+ if(logfile != NULL && !isatty(fileno(logfile))) {
+ fclose(logfile);
+ logfile = NULL;
+ }
+ if(filename)
+ logfile = f;
+ return 0;
+}
+
+int mdvi_set_logstream(FILE *file)
+{
+ if(logfile && !isatty(fileno(logfile))) {
+ fclose(logfile);
+ logfile = NULL;
+ }
+ logfile = file;
+ return 0;
+}
+
+int mdvi_set_loglevel(int level)
+{
+ int old = _mdvi_log_level;
+
+ _mdvi_log_level = level;
+ return old;
+}
+
+#ifndef NODEBUG
+Uint32 _mdvi_debug_mask = 0;
+
+void __debug(int mask, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ if(_mdvi_debug_mask & mask) {
+ if(!DEBUGGING(SILENT)) {
+ fprintf(stderr, "Debug: ");
+ vfprintf(stderr, format, ap);
+ fflush(stderr);
+ }
+#ifndef __GNUC__
+ /* let's be portable */
+ va_end(ap);
+ va_start(ap, format);
+#endif
+ vputlog(LOG_DEBUG, "Debug", format, ap);
+ }
+ va_end(ap);
+}
+#endif
+
+void message(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ if(_mdvi_log_level >= LOG_INFO) {
+ fprintf(stderr, "%s: ", program_name);
+ vfprintf(stderr, format, ap);
+#ifndef __GNUC__
+ va_end(ap);
+ va_start(ap, format);
+#endif
+ }
+ vputlog(LOG_INFO, NULL, format, ap);
+ va_end(ap);
+}
+
+void crash(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ fprintf(stderr, "%s: %s: ",
+ program_name,
+ gettext(messages[(int)time(NULL) % NMSGS]));
+ vfprintf(stderr, format, ap);
+#ifndef __GNUC__
+ /* let's be portable */
+ va_end(ap);
+ va_start(ap, format);
+#endif
+ vputlog(LOG_ERROR, _("Crashing"), format, ap);
+ va_end(ap);
+ abort();
+}
+
+void error(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ fprintf(stderr, _("%s: Error: "), program_name);
+ vfprintf(stderr, format, ap);
+#ifndef __GNUC__
+ /* let's be portable */
+ va_end(ap);
+ va_start(ap, format);
+#endif
+ vputlog(LOG_ERROR, _("Error"), format, ap);
+ va_end(ap);
+}
+
+void warning(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ fprintf(stderr, _("%s: Warning: "), program_name);
+ vfprintf(stderr, format, ap);
+#ifndef __GNUC__
+ /* let's be portable */
+ va_end(ap);
+ va_start(ap, format);
+#endif
+ vputlog(LOG_WARN, _("Warning"), format, ap);
+ va_end(ap);
+}
+
+void fatal(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ fprintf(stderr, _("%s: Fatal: "), program_name);
+ vfprintf(stderr, format, ap);
+#ifndef __GNUC__
+ /* let's be portable */
+ va_end(ap);
+ va_start(ap, format);
+#endif
+ vputlog(LOG_ERROR, _("Fatal"), format, ap);
+ va_end(ap);
+#ifndef NODEBUG
+ abort();
+#else
+ exit(EXIT_FAILURE);
+#endif
+}
+
+void *mdvi_malloc(size_t nelems)
+{
+ void *ptr = malloc(nelems);
+
+ if(ptr == NULL)
+ fatal(_("out of memory allocating %u bytes\n"),
+ (unsigned)nelems);
+ return ptr;
+}
+
+void *mdvi_realloc(void *data, size_t newsize)
+{
+ void *ptr;
+
+ if(newsize == 0)
+ crash(_("attempted to reallocate with zero size\n"));
+ ptr = realloc(data, newsize);
+ if(ptr == NULL)
+ fatal(_("failed to reallocate %u bytes\n"), (unsigned)newsize);
+ return ptr;
+}
+
+void *mdvi_calloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ if(nmemb == 0)
+ crash(_("attempted to callocate 0 members\n"));
+ if(size == 0)
+ crash(_("attempted to callocate %u members with size 0\n"),
+ (unsigned)nmemb);
+ ptr = calloc(nmemb, size);
+ if(ptr == 0)
+ fatal(_("failed to allocate %ux%u bytes\n"),
+ (unsigned)nmemb, (unsigned)size);
+ return ptr;
+}
+
+void mdvi_free(void *ptr)
+{
+ if(ptr == NULL)
+ crash(_("attempted to free NULL pointer\n"));
+ free(ptr);
+}
+
+char *mdvi_strdup(const char *string)
+{
+ int length;
+ char *ptr;
+
+ length = strlen(string) + 1;
+ ptr = (char *)mdvi_malloc(length);
+ memcpy(ptr, string, length);
+ return ptr;
+}
+
+/* `to' should have room for length+1 bytes */
+char *mdvi_strncpy(char *to, const char *from, size_t length)
+{
+ strncpy(to, from, length);
+ to[length] = '\0';
+ return to;
+}
+
+char *mdvi_strndup(const char *string, size_t length)
+{
+ int n;
+ char *ptr;
+
+ n = strlen(string);
+ if(n > length)
+ n = length;
+ ptr = (char *)mdvi_malloc(n + 1);
+ memcpy(ptr, string, n);
+ return ptr;
+}
+
+void *mdvi_memdup(const void *data, size_t length)
+{
+ void *ptr = mdvi_malloc(length);
+
+ memcpy(ptr, data, length);
+ return ptr;
+}
+
+double unit2pix_factor(const char *spec)
+{
+ double val;
+ double factor;
+ const char *p, *q;
+ static const char *units = "incmmmmtptpcddccspbpftydcs";
+
+ val = 0.0;
+
+ for(p = spec; *p >= '0' && *p <= '9'; p++)
+ val = 10.0 * val + (double)(*p - '0');
+ if(*p == '.') {
+ p++;
+ factor = 0.1;
+ while(*p && *p >= '0' && *p <= '9') {
+ val += (*p++ - '0') * factor;
+ factor = factor * 0.1;
+ }
+ }
+ factor = 1.0;
+ for(q = units; *q; q += 2) {
+ if(p[0] == q[0] && p[1] == q[1])
+ break;
+ }
+ switch((int)(q - units)) {
+ /*in*/ case 0: factor = 1.0; break;
+ /*cm*/ case 2: factor = 1.0 / 2.54; break;
+ /*mm*/ case 4: factor = 1.0 / 25.4; break;
+ /*mt*/ case 6: factor = 1.0 / 0.0254; break;
+ /*pt*/ case 8: factor = 1.0 / 72.27; break;
+ /*pc*/ case 10: factor = 12.0 / 72.27; break;
+ /*dd*/ case 12: factor = (1238.0 / 1157.0) / 72.27; break;
+ /*cc*/ case 14: factor = 12 * (1238.0 / 1157.0) / 72.27; break;
+ /*sp*/ case 16: factor = 1.0 / (72.27 * 65536); break;
+ /*bp*/ case 18: factor = 1.0 / 72.0; break;
+ /*ft*/ case 20: factor = 12.0; break;
+ /*yd*/ case 22: factor = 36.0; break;
+ /*cs*/ case 24: factor = 1.0 / 72000.0; break;
+ default: factor = 1.0;
+ }
+ return factor * val;
+}
+
+int unit2pix(int dpi, const char *spec)
+{
+ double factor = unit2pix_factor(spec);
+
+ return (int)(factor * dpi + 0.5);
+}
+
+Ulong get_mtime(int fd)
+{
+ struct stat st;
+
+ if(fstat(fd, &st) == 0)
+ return (Ulong)st.st_mtime;
+ return 0;
+}
+
+char *xstradd(char *dest, size_t *size, size_t n, const char *src, size_t m)
+{
+ if(m == 0)
+ m = strlen(src);
+ if(n + m >= *size) {
+ dest = mdvi_realloc(dest, n + m + 1);
+ *size = n + m + 1;
+ }
+ memcpy(dest + n, src, m);
+ dest[n + m] = 0;
+ return dest;
+}
+
+char *getword(char *string, const char *delim, char **end)
+{
+ char *ptr;
+ char *word;
+
+ /* skip leading delimiters */
+ for(ptr = string; *ptr && strchr(delim, *ptr); ptr++);
+
+ if(*ptr == 0)
+ return NULL;
+ word = ptr++;
+ /* skip non-delimiters */
+ while(*ptr && !strchr(delim, *ptr))
+ ptr++;
+ *end = (char *)ptr;
+ return word;
+}
+
+char *getstring(char *string, const char *delim, char **end)
+{
+ char *ptr;
+ char *word;
+ int quoted = 0;
+
+ /* skip leading delimiters */
+ for(ptr = string; *ptr && strchr(delim, *ptr); ptr++);
+
+ if(ptr == NULL)
+ return NULL;
+ quoted = (*ptr == '"');
+ if(quoted)
+ for(word = ++ptr; *ptr && *ptr != '"'; ptr++);
+ else
+ for(word = ptr; *ptr && !strchr(delim, *ptr); ptr++);
+ *end = (char *)ptr;
+ return word;
+}
+
+static long pow2(size_t n)
+{
+ long x = 8; /* don't bother allocating less than this */
+
+ while(x < n)
+ x <<= 1L;
+ return x;
+}
+
+void dstring_init(Dstring *dstr)
+{
+ dstr->data = NULL;
+ dstr->size = 0;
+ dstr->length = 0;
+}
+
+int dstring_append(Dstring *dstr, const char *string, int len)
+{
+ if(len < 0)
+ len = strlen(string);
+ if(len) {
+ if(dstr->length + len >= dstr->size) {
+ dstr->size = pow2(dstr->length + len + 1);
+ dstr->data = mdvi_realloc(dstr->data, dstr->size);
+ }
+ memcpy(dstr->data + dstr->length, string, len);
+ dstr->length += len;
+ dstr->data[dstr->length] = 0;
+ } else if(dstr->size == 0) {
+ ASSERT(dstr->data == NULL);
+ dstr->size = 8;
+ dstr->data = mdvi_malloc(8);
+ dstr->data[0] = 0;
+ }
+
+ return dstr->length;
+}
+
+int dstring_copy(Dstring *dstr, int pos, const char *string, int len)
+{
+ ASSERT(pos >= 0);
+ if(len < 0)
+ len = strlen(string);
+ if(len) {
+ if(pos + len >= dstr->length) {
+ dstr->length = pos;
+ return dstring_append(dstr, string, len);
+ }
+ memcpy(dstr->data + pos, string, len);
+ }
+ return dstr->length;
+}
+
+int dstring_insert(Dstring *dstr, int pos, const char *string, int len)
+{
+ ASSERT(pos >= 0);
+ if(pos == dstr->length)
+ return dstring_append(dstr, string, len);
+ if(len < 0)
+ len = strlen(string);
+ if(len) {
+ if(dstr->length + len >= dstr->size) {
+ dstr->size = pow2(dstr->length + len + 1);
+ dstr->data = mdvi_realloc(dstr->data, dstr->size);
+ }
+ /* make room */
+ memmove(dstr->data + pos, dstr->data + pos + len, len);
+ /* now copy */
+ memcpy(dstr->data + pos, string, len);
+ dstr->length += len;
+ dstr->data[dstr->length] = 0;
+ }
+ return dstr->length;
+}
+
+int dstring_new(Dstring *dstr, const char *string, int len)
+{
+ if(len < 0)
+ len = strlen(string);
+ if(len) {
+ dstr->size = pow2(len + 1);
+ dstr->data = mdvi_malloc(dstr->size * len);
+ memcpy(dstr->data, string, len);
+ } else
+ dstring_init(dstr);
+ return dstr->length;
+}
+
+void dstring_reset(Dstring *dstr)
+{
+ if(dstr->data)
+ mdvi_free(dstr->data);
+ dstring_init(dstr);
+}
+
diff --git a/backend/dvi/mdvi-lib/vf.c b/backend/dvi/mdvi-lib/vf.c
new file mode 100644
index 0000000..e1397fd
--- /dev/null
+++ b/backend/dvi/mdvi-lib/vf.c
@@ -0,0 +1,240 @@
+/* vf.c -- VF 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+
+#include "mdvi.h"
+#include "private.h"
+
+static int vf_load_font __PROTO((DviParams *, DviFont *));
+static void vf_free_macros __PROTO((DviFont *));
+
+/* only symbol exported by this file */
+DviFontInfo vf_font_info = {
+ "VF",
+ 1, /* virtual fonts scale just fine */
+ vf_load_font,
+ NULL, /* get_glyph */
+ NULL, /* shrink0 */
+ NULL, /* shrink1 */
+ vf_free_macros,
+ NULL, /* reset */
+ NULL, /* lookup */
+ kpse_vf_format,
+ NULL
+};
+
+DviFontInfo ovf_font_info = {
+ "OVF",
+ 1, /* virtual fonts scale just fine */
+ vf_load_font,
+ NULL, /* get_glyph */
+ NULL, /* shrink0 */
+ NULL, /* shrink1 */
+ vf_free_macros,
+ NULL, /* reset */
+ NULL, /* lookup */
+ kpse_ovf_format,
+ NULL
+};
+
+static int vf_load_font(DviParams *params, DviFont *font)
+{
+ FILE *p;
+ Uchar *macros;
+ int msize;
+ int mlen;
+ Int32 checksum;
+ long alpha, beta, z;
+ int op;
+ int i;
+ int nchars;
+ int loc, hic;
+ DviFontRef *last;
+
+ macros = NULL;
+ msize = mlen = 0;
+ p = font->in;
+
+ if(fuget1(p) != 247 || fuget1(p) != 202)
+ goto badvf;
+ mlen = fuget1(p);
+ fseek(p, (long)mlen, SEEK_CUR);
+ checksum = fuget4(p);
+ if(checksum && font->checksum && checksum != font->checksum) {
+ warning(_("%s: Checksum mismatch (expected %u, got %u)\n"),
+ font->fontname, font->checksum, checksum);
+ } else if(!font->checksum)
+ font->checksum = checksum;
+ font->design = fuget4(p);
+
+ /* read all the fonts in the preamble */
+ last = NULL;
+
+ /* initialize alpha, beta and z for TFM width computation */
+ TFMPREPARE(font->scale, z, alpha, beta);
+
+ op = fuget1(p);
+ while(op >= DVI_FNT_DEF1 && op <= DVI_FNT_DEF4) {
+ DviFontRef *ref;
+ Int32 scale, design;
+ Uint32 checksum;
+ int id;
+ int n;
+ int hdpi;
+ int vdpi;
+ char *name;
+
+ /* process fnt_def commands */
+
+ id = fugetn(p, op - DVI_FNT_DEF1 + 1);
+ checksum = fuget4(p);
+ scale = fuget4(p);
+ design = fuget4(p);
+
+ /* scale this font according to our parent's scale */
+ scale = TFMSCALE(scale, z, alpha, beta);
+ design = FROUND(params->tfm_conv * design);
+
+ /* compute the resolution */
+ hdpi = FROUND(params->mag * params->dpi * scale / design);
+ vdpi = FROUND(params->mag * params->vdpi * scale / design);
+ n = fuget1(p) + fuget1(p);
+ name = mdvi_malloc(n + 1);
+ fread(name, 1, n, p);
+ name[n] = 0;
+ DEBUG((DBG_FONTS, "(vf) %s: defined font `%s' at %.1fpt (%dx%d dpi)\n",
+ font->fontname, name,
+ (double)scale / (params->tfm_conv * 0x100000), hdpi, vdpi));
+
+ /* get the font */
+ ref = font_reference(params, id, name, checksum, hdpi, vdpi, scale);
+ if(ref == NULL) {
+ error(_("(vf) %s: could not load font `%s'\n"),
+ font->fontname, name);
+ goto error;
+ }
+ mdvi_free(name);
+ if(last == NULL)
+ font->subfonts = last = ref;
+ else
+ last->next = ref;
+ ref->next = NULL;
+ op = fuget1(p);
+ }
+
+ if(op >= DVI_FNT_DEF1 && op <= DVI_FNT_DEF4)
+ goto error;
+
+ /* This function correctly reads both .vf and .ovf files */
+
+ font->chars = xnalloc(DviFontChar, 256);
+ for(i = 0; i < 256; i++)
+ font->chars[i].offset = 0;
+ nchars = 256;
+ loc = -1; hic = -1;
+ /* now read the characters themselves */
+ while(op <= 242) {
+ int pl;
+ Int32 cc;
+ Int32 tfm;
+
+ if(op == 242) {
+ pl = fuget4(p);
+ cc = fuget4(p);
+ tfm = fuget4(p);
+ } else {
+ pl = op;
+ cc = fuget1(p);
+ tfm = fuget3(p);
+ }
+ if(loc < 0 || cc < loc)
+ loc = cc;
+ if(hic < 0 || cc > hic)
+ hic = cc;
+ if(cc >= nchars) {
+ font->chars = xresize(font->chars,
+ DviFontChar, cc + 16);
+ for(i = nchars; i < cc + 16; i++)
+ font->chars[i].offset = 0;
+ nchars = cc + 16;
+ }
+ if(font->chars[cc].offset) {
+ error(_("(vf) %s: character %d redefined\n"),
+ font->fontname, cc);
+ goto error;
+ }
+
+ DEBUG((DBG_GLYPHS, "(vf) %s: defined character %d (macro length %d)\n",
+ font->fontname, cc, pl));
+ font->chars[cc].width = pl + 1;
+ font->chars[cc].code = cc;
+ font->chars[cc].tfmwidth = TFMSCALE(tfm, z, alpha, beta);
+ font->chars[cc].offset = mlen;
+ font->chars[cc].loaded = 1;
+ if(mlen + pl + 1 > msize) {
+ msize = mlen + pl + 256;
+ macros = xresize(macros, Uchar, msize);
+ }
+ if(pl && fread(macros + mlen, 1, pl, p) != pl)
+ break;
+ macros[mlen+pl] = DVI_EOP;
+ mlen += pl + 1;
+ op = fuget1(p);
+ }
+ if(op != 248) {
+ error(_("(vf) %s: no postamble\n"), font->fontname);
+ goto error;
+ }
+
+ /* make macro memory just big enough */
+ if(msize > mlen) {
+ macros = xresize(macros, Uchar, mlen);
+ msize = mlen;
+ }
+
+ DEBUG((DBG_FONTS|DBG_GLYPHS,
+ "(vf) %s: macros use %d bytes\n", font->fontname, msize));
+
+ if(loc > 0 || hic < nchars-1) {
+ 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;
+ font->private = macros;
+
+ return 0;
+
+badvf:
+ error(_("%s: File corrupted, or not a VF file.\n"), font->fontname);
+error:
+ if(font->chars)
+ mdvi_free(font->chars);
+ if(macros)
+ mdvi_free(macros);
+ return -1;
+}
+
+static void vf_free_macros(DviFont *font)
+{
+ mdvi_free(font->private);
+}
diff --git a/backend/dvi/pixbuf-device.c b/backend/dvi/pixbuf-device.c
new file mode 100644
index 0000000..1ef4365
--- /dev/null
+++ b/backend/dvi/pixbuf-device.c
@@ -0,0 +1,220 @@
+#include "pixbuf-device.h"
+#include <gtk/gtk.h>
+
+typedef struct _DviPixbufDevice
+{
+ GdkPixbuf *pixbuf;
+
+ gboolean valid;
+
+ gint xmargin;
+ gint ymargin;
+
+ Ulong fg;
+ Ulong bg;
+
+} DviPixbufDevice;
+
+static void dvi_pixbuf_draw_rule(DviContext *dvi, int x, int y, Uint w, Uint h, int fill);
+
+static void dvi_pixbuf_draw_glyph(DviContext *dvi, DviFontChar *ch, int x0, int y0)
+{
+ DviPixbufDevice *c_device = (DviPixbufDevice *) dvi->device.device_data;
+
+ int x, y, w, h;
+ int isbox;
+ DviGlyph *glyph;
+
+ glyph = &ch->grey;
+
+ isbox = (glyph->data == NULL || (dvi->params.flags & MDVI_PARAM_CHARBOXES));
+
+ x = - glyph->x + x0 + c_device->xmargin;
+ y = - glyph->y + y0 + c_device->ymargin;
+ w = glyph->w;
+ h = glyph->h;
+
+ if (x < 0 || y < 0
+ || x + w > gdk_pixbuf_get_width (c_device->pixbuf)
+ || y + h > gdk_pixbuf_get_height (c_device->pixbuf))
+ return;
+
+ if (isbox) {
+ dvi_pixbuf_draw_rule(dvi, x - c_device->xmargin, y - c_device->ymargin, w, h, FALSE);
+ }
+ else {
+ gdk_pixbuf_copy_area (GDK_PIXBUF (glyph->data),
+ 0, 0,
+ w, h,
+ c_device->pixbuf, x, y);
+ }
+}
+
+static void dvi_pixbuf_draw_rule(DviContext *dvi, int x, int y, Uint w, Uint h, int fill)
+{
+ DviPixbufDevice *c_device = (DviPixbufDevice *) dvi->device.device_data;
+ gint rowstride;
+ guchar *p;
+ gint i, j;
+ gint red, green, blue;
+
+ red = (c_device->fg >> 16) & 0xff;
+ green = (c_device->fg >> 8) & 0xff;
+ blue = c_device->fg & 0xff;
+
+ x += c_device->xmargin; y += c_device->ymargin;
+
+ if (x < 0 || y < 0
+ || x + w > gdk_pixbuf_get_width (c_device->pixbuf)
+ || y + h > gdk_pixbuf_get_height (c_device->pixbuf))
+ return;
+
+ rowstride = gdk_pixbuf_get_rowstride (c_device->pixbuf);
+ p = gdk_pixbuf_get_pixels (c_device->pixbuf) + rowstride * y + 3 * x;
+
+ for (i = 0; i < h; i++) {
+ if (i == 0 || i == h - 1 || fill) {
+ for (j = 0; j < w; j++) {
+ p[j * 3] = red;
+ p[j * 3 + 1] = green;
+ p[j * 3 + 2] = blue;
+ }
+ } else {
+ p[0] = red;
+ p[1] = green;
+ p[2] = blue;
+ p[(w - 1) * 3] = red;
+ p[(w - 1) * 3 + 1] = green;
+ p[(w - 1) * 3 + 2] = blue;
+ }
+ p += rowstride;
+ }
+}
+
+static int dvi_pixbuf_interpolate_colors(void *device_data,
+ Ulong *pixels, int nlevels, Ulong fg, Ulong bg, double g, 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 & 0xff;
+
+ color_fg.red = fg >> 16 & 0xff;
+ color_fg.green = fg >> 8 & 0xff;
+ color_fg.blue = fg & 0xff;
+
+ n = nlevels - 1;
+ for(i = 0; i < nlevels; i++) {
+ if(g > 0)
+ frac = pow((double)i / n, 1 / g);
+ else
+ frac = 1 - pow((double)(n - i) / n, -g);
+ 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 nlevels;
+}
+
+static void *dvi_pixbuf_create_image(void *device_data, Uint w, Uint h, Uint bpp)
+{
+
+ return gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, w, h);
+
+ return NULL;
+}
+
+static void dvi_pixbuf_free_image(void *ptr)
+{
+ g_object_unref (GDK_PIXBUF(ptr));
+}
+
+static void dvi_pixbuf_put_pixel(void *image, int x, int y, Ulong color)
+{
+ guchar *p;
+
+ p = gdk_pixbuf_get_pixels (GDK_PIXBUF(image)) + y * gdk_pixbuf_get_rowstride(GDK_PIXBUF(image)) + x * 3;
+
+ p[0] = (color >> 16) & 0xff;
+ p[1] = (color >> 8) & 0xff;
+ p[2] = color & 0xff;
+}
+
+static void dvi_pixbuf_set_color(void *device_data, Ulong fg, Ulong bg)
+{
+ DviPixbufDevice *c_device = (DviPixbufDevice *) device_data;
+
+ c_device->fg = fg;
+
+ return;
+}
+
+void mdvi_pixbuf_device_init (DviDevice *device)
+{
+ device->device_data =
+ g_new0 (DviPixbufDevice, 1);
+
+ device->draw_glyph = dvi_pixbuf_draw_glyph;
+ device->draw_rule = dvi_pixbuf_draw_rule;
+ device->alloc_colors = dvi_pixbuf_interpolate_colors;
+ device->create_image = dvi_pixbuf_create_image;
+ device->free_image = dvi_pixbuf_free_image;
+ device->put_pixel = dvi_pixbuf_put_pixel;
+ device->set_color = dvi_pixbuf_set_color;
+ device->refresh = NULL;
+
+ return;
+}
+
+void mdvi_pixbuf_device_free (DviDevice *device)
+{
+ DviPixbufDevice *c_device = (DviPixbufDevice *) device->device_data;
+
+ if (c_device->pixbuf)
+ g_object_unref (c_device->pixbuf);
+
+ g_free (c_device);
+}
+
+GdkPixbuf *
+mdvi_pixbuf_device_get_pixbuf (DviDevice *device)
+{
+ DviPixbufDevice *c_device = (DviPixbufDevice *) device->device_data;
+
+ return g_object_ref (c_device->pixbuf);
+}
+
+void
+mdvi_pixbuf_device_render (DviContext * dvi)
+{
+ DviPixbufDevice *c_device = (DviPixbufDevice *) dvi->device.device_data;
+ gint page_width;
+ gint page_height;
+
+ if (c_device->pixbuf)
+ g_object_unref (c_device->pixbuf);
+
+ page_width = dvi->dvi_page_w * dvi->params.conv + 2 * c_device->xmargin;
+ page_height = dvi->dvi_page_h * dvi->params.vconv + 2 * c_device->ymargin;
+
+ c_device->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, page_width, page_height);
+ gdk_pixbuf_fill (c_device->pixbuf, 0xffffffff);
+
+ mdvi_dopage (dvi, dvi->currpage);
+}
+
+
+void
+mdvi_pixbuf_device_set_margins (DviDevice *device, gint xmargin, gint ymargin)
+{
+ DviPixbufDevice *c_device = (DviPixbufDevice *) device->device_data;
+
+ c_device->xmargin = xmargin;
+ c_device->ymargin = ymargin;
+}
diff --git a/backend/dvi/pixbuf-device.h b/backend/dvi/pixbuf-device.h
new file mode 100644
index 0000000..bacae4b
--- /dev/null
+++ b/backend/dvi/pixbuf-device.h
@@ -0,0 +1,24 @@
+#ifndef MDVI_PIXBUF_DEVICE
+#define MDVI_PIXBUF_DEVICE
+
+#include "mdvi.h"
+#include <gtk/gtk.h>
+
+void
+mdvi_pixbuf_device_init (DviDevice *device);
+
+void
+mdvi_pixbuf_device_free (DviDevice *device);
+
+GdkPixbuf *
+mdvi_pixbuf_device_get_pixbuf (DviDevice *device);
+
+void
+mdvi_pixbuf_device_render (DviContext *dvi);
+
+void
+mdvi_pixbuf_device_set_margins (DviDevice *device, gint xmargin, gint ymargin);
+
+#endif /* MDVI_PIXBUF_DEVICE */
+
+
diff --git a/backend/ev-async-renderer.c b/backend/ev-async-renderer.c
deleted file mode 100644
index f0ffce4..0000000
--- a/backend/ev-async-renderer.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2004 Marco Pesenti Gritti
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include "config.h"
-
-#include "ev-async-renderer.h"
-
-static void ev_async_renderer_class_init (gpointer g_class);
-
-enum
-{
- RENDER_FINISHED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-GType
-ev_async_renderer_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0))
- {
- const GTypeInfo our_info =
- {
- sizeof (EvAsyncRendererIface),
- NULL,
- NULL,
- (GClassInitFunc)ev_async_renderer_class_init
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvAsyncRenderer",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-static void
-ev_async_renderer_class_init (gpointer g_class)
-{
- signals[RENDER_FINISHED] =
- g_signal_new ("render_finished",
- EV_TYPE_ASYNC_RENDERER,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EvAsyncRendererIface, render_finished),
- NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE,
- 1,
- GDK_TYPE_PIXBUF);
-}
-
-void
-ev_async_renderer_render_pixbuf (EvAsyncRenderer *async_renderer,
- int page,
- double scale,
- int rotation)
-{
- EvAsyncRendererIface *iface = EV_ASYNC_RENDERER_GET_IFACE (async_renderer);
-
- iface->render_pixbuf (async_renderer, page, scale, rotation);
-}
diff --git a/backend/ev-async-renderer.h b/backend/ev-async-renderer.h
deleted file mode 100644
index 9aa9657..0000000
--- a/backend/ev-async-renderer.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2000-2003 Marco Pesenti Gritti
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef EV_ASYNC_RENDERER_H
-#define EV_ASYNC_RENDERER_H
-
-#include <glib-object.h>
-#include <glib.h>
-#include <gdk/gdkpixbuf.h>
-
-G_BEGIN_DECLS
-
-#define EV_TYPE_ASYNC_RENDERER (ev_async_renderer_get_type ())
-#define EV_ASYNC_RENDERER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_ASYNC_RENDERER, EvAsyncRenderer))
-#define EV_ASYNC_RENDERER_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_ASYNC_RENDERER, EvAsyncRendererIface))
-#define EV_IS_ASYNC_RENDERER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_ASYNC_RENDERER))
-#define EV_IS_ASYNC_RENDERER_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_ASYNC_RENDERER))
-#define EV_ASYNC_RENDERER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_ASYNC_RENDERER, EvAsyncRendererIface))
-
-typedef struct _EvAsyncRenderer EvAsyncRenderer;
-typedef struct _EvAsyncRendererIface EvAsyncRendererIface;
-
-struct _EvAsyncRendererIface
-{
- GTypeInterface base_iface;
-
- void (* render_finished) (EvAsyncRenderer *renderer,
- GdkPixbuf *pixbuf);
-
- void (* render_pixbuf) (EvAsyncRenderer *renderer,
- int page,
- double scale,
- int rotation);
-};
-
-GType ev_async_renderer_get_type (void);
-void ev_async_renderer_render_pixbuf (EvAsyncRenderer *renderer,
- int page,
- double scale,
- int rotation);
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-attachment.c b/backend/ev-attachment.c
deleted file mode 100644
index 7e7ca12..0000000
--- a/backend/ev-attachment.c
+++ /dev/null
@@ -1,409 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-#include <libgnomevfs/gnome-vfs.h>
-#include <libgnomevfs/gnome-vfs-mime-handlers.h>
-#include <libgnomevfs/gnome-vfs-mime-utils.h>
-#include "ev-file-helpers.h"
-#include "ev-attachment.h"
-
-enum
-{
- PROP_0,
- PROP_NAME,
- PROP_DESCRIPTION,
- PROP_MTIME,
- PROP_CTIME,
- PROP_SIZE,
- PROP_DATA
-};
-
-struct _EvAttachmentPrivate {
- gchar *name;
- gchar *description;
- GTime mtime;
- GTime ctime;
- gsize size;
- gchar *data;
- gchar *mime_type;
-
- GnomeVFSMimeApplication *app;
- gchar *tmp_uri;
-};
-
-#define EV_ATTACHMENT_GET_PRIVATE(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_ATTACHMENT, EvAttachmentPrivate))
-
-G_DEFINE_TYPE (EvAttachment, ev_attachment, G_TYPE_OBJECT)
-
-GQuark
-ev_attachment_error_quark (void)
-{
- static GQuark error_quark = 0;
-
- if (error_quark == 0)
- error_quark =
- g_quark_from_static_string ("ev-attachment-error-quark");
-
- return error_quark;
-}
-
-static void
-ev_attachment_finalize (GObject *object)
-{
- EvAttachment *attachment = EV_ATTACHMENT (object);
-
- if (attachment->priv->name) {
- g_free (attachment->priv->name);
- attachment->priv->name = NULL;
- }
-
- if (attachment->priv->description) {
- g_free (attachment->priv->description);
- attachment->priv->description = NULL;
- }
-
- if (attachment->priv->data) {
- g_free (attachment->priv->data);
- attachment->priv->data = NULL;
- }
-
- if (attachment->priv->mime_type) {
- g_free (attachment->priv->mime_type);
- attachment->priv->mime_type = NULL;
- }
-
- if (attachment->priv->app) {
- gnome_vfs_mime_application_free (attachment->priv->app);
- attachment->priv->app = NULL;
- }
-
- if (attachment->priv->tmp_uri) {
- g_unlink (attachment->priv->tmp_uri);
- g_free (attachment->priv->tmp_uri);
- attachment->priv->tmp_uri = NULL;
- }
-
- (* G_OBJECT_CLASS (ev_attachment_parent_class)->finalize) (object);
-}
-
-static void
-ev_attachment_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *param_spec)
-{
- EvAttachment *attachment = EV_ATTACHMENT (object);
-
- switch (prop_id) {
- case PROP_NAME:
- attachment->priv->name = g_value_dup_string (value);
- break;
- case PROP_DESCRIPTION:
- attachment->priv->description = g_value_dup_string (value);
- break;
- case PROP_MTIME:
- attachment->priv->mtime = g_value_get_ulong (value);
- break;
- case PROP_CTIME:
- attachment->priv->ctime = g_value_get_ulong (value);
- break;
- case PROP_SIZE:
- attachment->priv->size = g_value_get_uint (value);
- break;
- case PROP_DATA:
- attachment->priv->data = g_value_get_pointer (value);
- attachment->priv->mime_type =
- g_strdup (gnome_vfs_get_mime_type_for_data (attachment->priv->data,
- attachment->priv->size));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
- prop_id,
- param_spec);
- break;
- }
-}
-
-static void
-ev_attachment_class_init (EvAttachmentClass *klass)
-{
- GObjectClass *g_object_class;
-
- g_object_class = G_OBJECT_CLASS (klass);
-
- g_object_class->set_property = ev_attachment_set_property;
-
- g_type_class_add_private (g_object_class, sizeof (EvAttachmentPrivate));
-
- /* Properties */
- g_object_class_install_property (g_object_class,
- PROP_NAME,
- g_param_spec_string ("name",
- "Name",
- "The attachment name",
- NULL,
- G_PARAM_WRITABLE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_DESCRIPTION,
- g_param_spec_string ("description",
- "Description",
- "The attachment description",
- NULL,
- G_PARAM_WRITABLE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_MTIME,
- g_param_spec_ulong ("mtime",
- "ModifiedTime",
- "The attachment modification date",
- 0, G_MAXULONG, 0,
- G_PARAM_WRITABLE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_CTIME,
- g_param_spec_ulong ("ctime",
- "CreationTime",
- "The attachment creation date",
- 0, G_MAXULONG, 0,
- G_PARAM_WRITABLE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_SIZE,
- g_param_spec_uint ("size",
- "Size",
- "The attachment size",
- 0, G_MAXUINT, 0,
- G_PARAM_WRITABLE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_DATA,
- g_param_spec_pointer ("data",
- "Data",
- "The attachment data",
- G_PARAM_WRITABLE |
- G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class->finalize = ev_attachment_finalize;
-}
-
-static void
-ev_attachment_init (EvAttachment *attachment)
-{
- attachment->priv = EV_ATTACHMENT_GET_PRIVATE (attachment);
-
- attachment->priv->name = NULL;
- attachment->priv->description = NULL;
- attachment->priv->data = NULL;
- attachment->priv->mime_type = NULL;
-
- attachment->priv->tmp_uri = NULL;
-}
-
-EvAttachment *
-ev_attachment_new (const gchar *name,
- const gchar *description,
- GTime mtime,
- GTime ctime,
- gsize size,
- gpointer data)
-{
- EvAttachment *attachment;
-
- attachment = g_object_new (EV_TYPE_ATTACHMENT,
- "name", name,
- "description", description,
- "mtime", mtime,
- "ctime", ctime,
- "size", size,
- "data", data,
- NULL);
-
- return attachment;
-}
-
-const gchar *
-ev_attachment_get_name (EvAttachment *attachment)
-{
- g_return_val_if_fail (EV_IS_ATTACHMENT (attachment), NULL);
-
- return attachment->priv->name;
-}
-
-const gchar *
-ev_attachment_get_description (EvAttachment *attachment)
-{
- g_return_val_if_fail (EV_IS_ATTACHMENT (attachment), NULL);
-
- return attachment->priv->description;
-}
-
-GTime
-ev_attachment_get_modification_date (EvAttachment *attachment)
-{
- g_return_val_if_fail (EV_IS_ATTACHMENT (attachment), 0);
-
- return attachment->priv->mtime;
-}
-
-GTime
-ev_attachment_get_creation_date (EvAttachment *attachment)
-{
- g_return_val_if_fail (EV_IS_ATTACHMENT (attachment), 0);
-
- return attachment->priv->ctime;
-}
-
-const gchar *
-ev_attachment_get_mime_type (EvAttachment *attachment)
-{
- g_return_val_if_fail (EV_IS_ATTACHMENT (attachment), NULL);
-
- return attachment->priv->mime_type;
-}
-
-gboolean
-ev_attachment_save (EvAttachment *attachment,
- const gchar *uri,
- GError **error)
-{
- GnomeVFSHandle *handle = NULL;
- GnomeVFSFileSize written;
- GnomeVFSResult result;
-
- g_return_val_if_fail (EV_IS_ATTACHMENT (attachment), FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
-
- result = gnome_vfs_create (&handle, uri,
- GNOME_VFS_OPEN_WRITE,
- FALSE, 0644);
- if (result != GNOME_VFS_OK) {
- g_set_error (error,
- EV_ATTACHMENT_ERROR,
- (gint) result,
- _("Couldn't save attachment “%s”: %s"),
- uri,
- gnome_vfs_result_to_string (result));
-
- return FALSE;
- }
-
- result = gnome_vfs_write (handle, attachment->priv->data,
- attachment->priv->size, &written);
- if (result != GNOME_VFS_OK || written < attachment->priv->size){
- g_set_error (error,
- EV_ATTACHMENT_ERROR,
- (gint) result,
- _("Couldn't save attachment “%s”: %s"),
- uri,
- gnome_vfs_result_to_string (result));
-
- gnome_vfs_close (handle);
-
- return FALSE;
- }
-
- gnome_vfs_close (handle);
-
- return TRUE;
-}
-
-static gboolean
-ev_attachment_launch_app (EvAttachment *attachment,
- GError **error)
-{
- GnomeVFSResult result;
- GList *uris = NULL;
-
- g_assert (attachment->priv->tmp_uri != NULL);
- g_assert (attachment->priv->app != NULL);
-
- uris = g_list_prepend (uris, attachment->priv->tmp_uri);
- result = gnome_vfs_mime_application_launch (attachment->priv->app,
- uris);
-
- if (result != GNOME_VFS_OK) {
- g_set_error (error,
- EV_ATTACHMENT_ERROR,
- (gint) result,
- _("Couldn't open attachment “%s”: %s"),
- attachment->priv->name,
- gnome_vfs_result_to_string (result));
-
- g_list_free (uris);
-
- return FALSE;
- }
-
- g_list_free (uris);
-
- return TRUE;
-}
-
-gboolean
-ev_attachment_open (EvAttachment *attachment,
- GError **error)
-{
-
- gboolean retval = FALSE;
- GnomeVFSMimeApplication *default_app = NULL;
-
- g_return_val_if_fail (EV_IS_ATTACHMENT (attachment), FALSE);
-
- if (!attachment->priv->app) {
- default_app = gnome_vfs_mime_get_default_application (attachment->priv->mime_type);
- attachment->priv->app = default_app;
- }
-
- if (!attachment->priv->app) {
- g_set_error (error,
- EV_ATTACHMENT_ERROR,
- 0,
- _("Couldn't open attachment “%s”"),
- attachment->priv->name);
-
- return FALSE;
- }
-
- if (attachment->priv->tmp_uri &&
- g_file_test (attachment->priv->tmp_uri, G_FILE_TEST_EXISTS)) {
- retval = ev_attachment_launch_app (attachment, error);
- } else {
- gchar *uri, *filename;
-
- filename = g_build_filename (ev_tmp_dir (), attachment->priv->name, NULL);
- uri = g_filename_to_uri (filename, NULL, NULL);
-
- if (ev_attachment_save (attachment, uri, error)) {
- if (attachment->priv->tmp_uri)
- g_free (attachment->priv->tmp_uri);
- attachment->priv->tmp_uri = g_strdup (filename);
-
- retval = ev_attachment_launch_app (attachment, error);
- }
-
- g_free (filename);
- g_free (uri);
- }
-
- return retval;
-}
diff --git a/backend/ev-attachment.h b/backend/ev-attachment.h
deleted file mode 100644
index 994b654..0000000
--- a/backend/ev-attachment.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __EV_ATTACHMENT_H__
-#define __EV_ATTACHMENT_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef struct _EvAttachment EvAttachment;
-typedef struct _EvAttachmentClass EvAttachmentClass;
-typedef struct _EvAttachmentPrivate EvAttachmentPrivate;
-
-#define EV_TYPE_ATTACHMENT (ev_attachment_get_type())
-#define EV_ATTACHMENT(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_ATTACHMENT, EvAttachment))
-#define EV_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_ATTACHMENT, EvAttachmentClass))
-#define EV_IS_ATTACHMENT(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_ATTACHMENT))
-#define EV_IS_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_ATTACHMENT))
-#define EV_ATTACHMENT_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_ATTACHMENT, EvAttachmentClass))
-
-#define EV_ATTACHMENT_ERROR (ev_attachment_error_quark ())
-
-struct _EvAttachment {
- GObject base_instance;
-
- EvAttachmentPrivate *priv;
-};
-
-struct _EvAttachmentClass {
- GObjectClass base_class;
-};
-
-GType ev_attachment_get_type (void) G_GNUC_CONST;
-GQuark ev_attachment_error_quark (void) G_GNUC_CONST;
-EvAttachment *ev_attachment_new (const gchar *name,
- const gchar *description,
- GTime mtime,
- GTime ctime,
- gsize size,
- gpointer data);
-
-const gchar *ev_attachment_get_name (EvAttachment *attachment);
-const gchar *ev_attachment_get_description (EvAttachment *attachment);
-GTime ev_attachment_get_modification_date (EvAttachment *attachment);
-GTime ev_attachment_get_creation_date (EvAttachment *attachment);
-const gchar *ev_attachment_get_mime_type (EvAttachment *attachment);
-gboolean ev_attachment_save (EvAttachment *attachment,
- const gchar *uri,
- GError **error);
-gboolean ev_attachment_open (EvAttachment *attachment,
- GError **error);
-
-G_END_DECLS
-
-#endif /* __EV_ATTACHMENT_H__ */
diff --git a/backend/ev-backend-marshal.c b/backend/ev-backend-marshal.c
deleted file mode 100644
index 2bc7b6a..0000000
--- a/backend/ev-backend-marshal.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "ev-backend-marshalers.h"
-#include "ev-backend-marshalers.c"
diff --git a/backend/ev-backend-marshalers.list b/backend/ev-backend-marshalers.list
deleted file mode 100644
index e69de29..0000000
--- a/backend/ev-backend-marshalers.list
+++ /dev/null
diff --git a/backend/ev-document-factory.c b/backend/ev-document-factory.c
deleted file mode 100644
index 348cb80..0000000
--- a/backend/ev-document-factory.c
+++ /dev/null
@@ -1,463 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2005, Red Hat, Inc.
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "ev-document-factory.h"
-
-/* The various document type backends: */
-#ifdef ENABLE_PDF
-#include "ev-poppler.h"
-#endif
-#ifdef ENABLE_PS
-#include "ps-document.h"
-#endif
-#ifdef ENABLE_TIFF
-#include "tiff-document.h"
-#endif
-#ifdef ENABLE_DVI
-#include "dvi-document.h"
-#endif
-#ifdef ENABLE_PIXBUF
-#include "pixbuf-document.h"
-#endif
-#ifdef ENABLE_DJVU
-#include "djvu-document.h"
-#endif
-#ifdef ENABLE_COMICS
-#include "comics-document.h"
-#endif
-#ifdef ENABLE_IMPRESS
-#include "impress-document.h"
-#endif
-
-#include <string.h>
-#include <glib/gi18n.h>
-#include <libgnomevfs/gnome-vfs-mime-utils.h>
-#include <libgnomevfs/gnome-vfs-file-info.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
-#include <gtk/gtkfilechooserdialog.h>
-
-typedef struct _EvDocumentType EvDocumentType;
-struct _EvDocumentType
-{
- const char *mime_type;
- EvBackend backend;
- GType (*document_type_factory_callback)();
-};
-
-const EvDocumentType document_types[] = {
-#ifdef ENABLE_PDF
- /* PDF: */
- {"application/pdf", EV_BACKEND_PDF, pdf_document_get_type},
-#endif
-
-#ifdef ENABLE_PS
- /* Postscript: */
- {"application/postscript", EV_BACKEND_PS, ps_document_get_type},
- {"application/x-gzpostscript", EV_BACKEND_PS, ps_document_get_type},
- {"image/x-eps", EV_BACKEND_PS, ps_document_get_type},
-#endif
-
-#ifdef ENABLE_TIFF
- /* Tiff: */
- {"image/tiff", EV_BACKEND_TIFF, tiff_document_get_type},
-#endif
-
-#ifdef ENABLE_DJVU
- /* djvu: */
- {"image/vnd.djvu", EV_BACKEND_DJVU, djvu_document_get_type},
-#endif
-
-#ifdef ENABLE_DVI
- /* dvi: */
- {"application/x-dvi", EV_BACKEND_DVI, dvi_document_get_type},
-#endif
-
-#ifdef ENABLE_COMICS
- /* cbr/cbz: */
- {"application/x-cbr", EV_BACKEND_COMICS, comics_document_get_type},
- {"application/x-cbz", EV_BACKEND_COMICS, comics_document_get_type},
-#endif
-
-#ifdef ENABLE_IMPRESS
- /* Impress slides: */
- {"application/vnd.sun.xml.impress", EV_BACKEND_IMPRESS, impress_document_get_type},
- {"application/vnd.oasis.opendocument.presentation", EV_BACKEND_IMPRESS, impress_document_get_type},
-#endif
-
-};
-
-#ifdef ENABLE_PIXBUF
-
-static GList*
-gdk_pixbuf_mime_type_list ()
-{
- GSList *formats, *list;
- GList *result;
-
- formats = gdk_pixbuf_get_formats ();
- result = NULL;
-
- for (list = formats; list != NULL; list = list->next) {
- GdkPixbufFormat *format = list->data;
- int i;
- gchar **mime_types;
-
- if (gdk_pixbuf_format_is_disabled (format))
- continue;
-
- mime_types = gdk_pixbuf_format_get_mime_types (format);
-
- for (i = 0; mime_types[i] != NULL; i++) {
- result = g_list_append (result, mime_types[i]);
- }
- }
- g_slist_free (formats);
-
- return result;
-}
-
-/* Would be nice to have this in gdk-pixbuf */
-static gboolean
-mime_type_supported_by_gdk_pixbuf (const gchar *mime_type)
-{
- GList *mime_types;
- GList *list;
- gboolean retval = FALSE;
-
- mime_types = gdk_pixbuf_mime_type_list ();
- for (list = mime_types; list; list = list->next) {
- if (strcmp ((char *)list->data, mime_type) == 0) {
- retval = TRUE;
- break;
- }
- }
-
- g_list_foreach (mime_types, (GFunc)g_free, NULL);
- g_list_free (mime_types);
-
- return retval;
-}
-#endif
-
-static EvDocument*
-ev_document_factory_get_from_mime (const char *mime_type)
-{
- int i;
- GType type = G_TYPE_INVALID;
- EvDocument *document = NULL;
-
- g_return_val_if_fail (mime_type, G_TYPE_INVALID);
-
- for (i = 0; i < G_N_ELEMENTS (document_types); i++) {
- if (strcmp (mime_type, document_types[i].mime_type) == 0) {
- g_assert (document_types[i].document_type_factory_callback != NULL);
- type = document_types[i].document_type_factory_callback();
- break;
- }
- }
-#ifdef ENABLE_PIXBUF
- if (type == G_TYPE_INVALID && mime_type_supported_by_gdk_pixbuf (mime_type)) {
- type = pixbuf_document_get_type ();
- }
-#endif
- if (type != G_TYPE_INVALID) {
- document = g_object_new (type, NULL);
- }
-
- return document;
-}
-
-EvBackend
-ev_document_factory_get_backend (EvDocument *document)
-{
- int i;
-
- for (i = 0; i < G_N_ELEMENTS (document_types); i++) {
- GType type = document_types[i].document_type_factory_callback ();
- if (type == G_TYPE_FROM_INSTANCE (document)) {
- return document_types[i].backend;
- }
- }
-
-#ifdef ENABLE_PIXBUF
- if (G_TYPE_FROM_INSTANCE (document) == pixbuf_document_get_type ())
- return EV_BACKEND_PIXBUF;
-#endif
- g_assert_not_reached ();
-
- return 0;
-}
-
-static GList *
-ev_document_factory_get_mime_types (EvBackend backend)
-{
- GList *types = NULL;
- int i;
-
-#ifdef ENABLE_PIXBUF
- if (backend == EV_BACKEND_PIXBUF) {
- return gdk_pixbuf_mime_type_list ();
- }
-#endif
-
- for (i = 0; i < G_N_ELEMENTS (document_types); i++) {
- if (document_types[i].backend == backend) {
- types = g_list_append (types, g_strdup (document_types[i].mime_type));
- }
- }
-
- return types;
-}
-
-static GList *
-ev_document_factory_get_all_mime_types (void)
-{
- GList *types = NULL;
- int i;
-
- for (i = 0; i < G_N_ELEMENTS (document_types); i++) {
- types = g_list_append (types, g_strdup (document_types[i].mime_type));
- }
-
-#ifdef ENABLE_PIXBUF
- types = g_list_concat (types, gdk_pixbuf_mime_type_list ());
-#endif
-
- return types;
-}
-
-static EvDocument *
-get_document_from_uri (const char *uri, gboolean slow, GError **error)
-{
- EvDocument *document = NULL;
-
- GnomeVFSFileInfo *info;
- GnomeVFSResult result;
-
- info = gnome_vfs_file_info_new ();
- result = gnome_vfs_get_file_info (uri, info,
- GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
- GNOME_VFS_FILE_INFO_FOLLOW_LINKS |
- (slow ? GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE : 0));
- if (result != GNOME_VFS_OK) {
- g_set_error (error,
- EV_DOCUMENT_ERROR,
- 0,
- gnome_vfs_result_to_string (result));
- gnome_vfs_file_info_unref (info);
- return NULL;
- }
-
- if (info->mime_type == NULL) {
- g_set_error (error,
- EV_DOCUMENT_ERROR,
- 0,
- _("Unknown MIME Type"));
- gnome_vfs_file_info_unref (info);
- return NULL;
- }
-
- document = ev_document_factory_get_from_mime (info->mime_type);
-
- if (document == NULL) {
- g_set_error (error,
- EV_DOCUMENT_ERROR,
- 0,
- _("Unhandled MIME type: “%s”"), info->mime_type);
- gnome_vfs_file_info_unref (info);
- return NULL;
- }
-
- gnome_vfs_file_info_unref (info);
-
- return document;
-}
-
-EvDocument *
-ev_document_factory_get_document (const char *uri, GError **error)
-{
- EvDocument *document;
- int result;
-
- document = get_document_from_uri (uri, FALSE, error);
-
- if (*error == NULL) {
- result = ev_document_load (document, uri, error);
-
- if (result == FALSE || *error) {
- if (*error &&
- (*error)->domain == EV_DOCUMENT_ERROR &&
- (*error)->code == EV_DOCUMENT_ERROR_ENCRYPTED)
- return document;
- } else {
- return document;
- }
- }
-
- /* Try again with slow mime detection */
- if (document)
- g_object_unref (document);
- document = NULL;
-
- if (*error)
- g_error_free (*error);
- *error = NULL;
-
- document = get_document_from_uri (uri, TRUE, error);
-
- if (*error != NULL) {
- return NULL;
- }
-
- result = ev_document_load (document, uri, error);
-
- if (result == FALSE) {
- if (*error == NULL) {
- g_set_error (error,
- EV_DOCUMENT_ERROR,
- 0,
- _("Unknown MIME Type"));
- } else if ((*error)->domain == EV_DOCUMENT_ERROR &&
- (*error)->code == EV_DOCUMENT_ERROR_ENCRYPTED) {
- return document;
- }
-
- if (document)
- g_object_unref (document);
- document = NULL;
- }
-
- return document;
-}
-
-static void
-file_filter_add_mime_list_and_free (GtkFileFilter *filter, GList *mime_types)
-{
- GList *l;
-
- for (l = mime_types; l != NULL; l = l->next) {
- gtk_file_filter_add_mime_type (filter, l->data);
- }
-
- g_list_foreach (mime_types, (GFunc)g_free, NULL);
- g_list_free (mime_types);
-}
-
-void
-ev_document_factory_add_filters (GtkWidget *chooser, EvDocument *document)
-{
- EvBackend backend = 0;
- GList *mime_types;
- GtkFileFilter *filter;
- GtkFileFilter *default_filter;
- GtkFileFilter *document_filter;
-
- if (document != NULL) {
- backend = ev_document_factory_get_backend (document);
- }
-
- default_filter = document_filter = filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("All Documents"));
- mime_types = ev_document_factory_get_all_mime_types ();
- file_filter_add_mime_list_and_free (filter, mime_types);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
-
-#ifdef ENABLE_PS
- if (document == NULL || backend == EV_BACKEND_PS) {
- default_filter = filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("PostScript Documents"));
- mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PS);
- file_filter_add_mime_list_and_free (filter, mime_types);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
- }
-#endif
-
-#ifdef ENABLE_PDF
- if (document == NULL || backend == EV_BACKEND_PDF) {
- default_filter = filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("PDF Documents"));
- mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PDF);
- file_filter_add_mime_list_and_free (filter, mime_types);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
- }
-#endif
-
-#ifdef ENABLE_PIXBUF
- if (document == NULL || backend == EV_BACKEND_PIXBUF) {
- default_filter = filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Images"));
- mime_types = ev_document_factory_get_mime_types (EV_BACKEND_PIXBUF);
- file_filter_add_mime_list_and_free (filter, mime_types);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
- }
-#endif
-
-#ifdef ENABLE_DVI
- if (document == NULL || backend == EV_BACKEND_DVI) {
- default_filter = filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("DVI Documents"));
- mime_types = ev_document_factory_get_mime_types (EV_BACKEND_DVI);
- file_filter_add_mime_list_and_free (filter, mime_types);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
- }
-#endif
-
-#ifdef ENABLE_DJVU
- if (document == NULL || backend == EV_BACKEND_DJVU) {
- default_filter = filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Djvu Documents"));
- mime_types = ev_document_factory_get_mime_types (EV_BACKEND_DJVU);
- file_filter_add_mime_list_and_free (filter, mime_types);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
- }
-#endif
-
-#ifdef ENABLE_COMICS
- if (document == NULL || backend == EV_BACKEND_COMICS) {
- default_filter = filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Comic Books"));
- mime_types = ev_document_factory_get_mime_types (EV_BACKEND_COMICS);
- file_filter_add_mime_list_and_free (filter, mime_types);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
- }
-#endif
-
-#ifdef ENABLE_IMPRESS
- if (document == NULL || backend == EV_BACKEND_IMPRESS) {
- default_filter = filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("Impress Slides"));
- mime_types = ev_document_factory_get_mime_types (EV_BACKEND_IMPRESS);
- file_filter_add_mime_list_and_free (filter, mime_types);
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
- }
-#endif
-
- filter = gtk_file_filter_new ();
- gtk_file_filter_set_name (filter, _("All Files"));
- gtk_file_filter_add_pattern (filter, "*");
- gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
-
- gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser),
- document == NULL ? document_filter : default_filter);
-}
diff --git a/backend/ev-document-factory.h b/backend/ev-document-factory.h
deleted file mode 100644
index 886be69..0000000
--- a/backend/ev-document-factory.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2005, Red Hat, Inc.
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef EV_DOCUMENT_FACTORY_H
-#define EV_DOCUMENT_FACTORY_H
-
-#include <gtk/gtk.h>
-#include "ev-document.h"
-
-G_BEGIN_DECLS
-
-typedef enum {
- EV_BACKEND_PDF,
- EV_BACKEND_PS,
- EV_BACKEND_TIFF,
- EV_BACKEND_PIXBUF,
- EV_BACKEND_DJVU,
- EV_BACKEND_DVI,
- EV_BACKEND_COMICS,
- EV_BACKEND_IMPRESS
-} EvBackend;
-
-EvDocument* ev_document_factory_get_document (const char *uri, GError **error);
-EvBackend ev_document_factory_get_backend (EvDocument *document);
-void ev_document_factory_add_filters (GtkWidget *chooser, EvDocument *document);
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-document-find.c b/backend/ev-document-find.c
deleted file mode 100644
index 02fbae2..0000000
--- a/backend/ev-document-find.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include "config.h"
-
-#include "ev-document-find.h"
-#include "ev-backend-marshalers.h"
-
-static void ev_document_find_base_init (gpointer g_class);
-
-GType
-ev_document_find_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0))
- {
- const GTypeInfo our_info =
- {
- sizeof (EvDocumentFindIface),
- ev_document_find_base_init,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvDocumentFind",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-static void
-ev_document_find_base_init (gpointer g_class)
-{
- static gboolean initialized = FALSE;
-
- if (!initialized) {
- g_signal_new ("find_changed",
- EV_TYPE_DOCUMENT_FIND,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (EvDocumentFindIface, find_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__INT,
- G_TYPE_NONE, 1,
- G_TYPE_INT);
-
- initialized = TRUE;
- }
-}
-
-void
-ev_document_find_begin (EvDocumentFind *document_find,
- int page,
- const char *search_string,
- gboolean case_sensitive)
-{
- EvDocumentFindIface *iface = EV_DOCUMENT_FIND_GET_IFACE (document_find);
-
- g_return_if_fail (search_string != NULL);
-
- iface->begin (document_find, page, search_string, case_sensitive);
-}
-
-void
-ev_document_find_cancel (EvDocumentFind *document_find)
-{
- EvDocumentFindIface *iface = EV_DOCUMENT_FIND_GET_IFACE (document_find);
- iface->cancel (document_find);
-}
-
-int
-ev_document_find_page_has_results (EvDocumentFind *document_find,
- int page)
-{
- EvDocumentFindIface *iface = EV_DOCUMENT_FIND_GET_IFACE (document_find);
- return iface->page_has_results (document_find, page);
-}
-
-int
-ev_document_find_get_n_results (EvDocumentFind *document_find,
- int page)
-{
- EvDocumentFindIface *iface = EV_DOCUMENT_FIND_GET_IFACE (document_find);
- return iface->get_n_results (document_find, page);
-}
-
-gboolean
-ev_document_find_get_result (EvDocumentFind *document_find,
- int page,
- int n_result,
- EvRectangle *rectangle)
-{
- EvDocumentFindIface *iface = EV_DOCUMENT_FIND_GET_IFACE (document_find);
- return iface->get_result (document_find, page, n_result, rectangle);
-}
-
-double
-ev_document_find_get_progress (EvDocumentFind *document_find)
-{
- EvDocumentFindIface *iface = EV_DOCUMENT_FIND_GET_IFACE (document_find);
- return iface->get_progress (document_find);
-}
-
-void
-ev_document_find_changed (EvDocumentFind *document_find, int page)
-{
- g_signal_emit_by_name (document_find, "find_changed", page);
-}
diff --git a/backend/ev-document-find.h b/backend/ev-document-find.h
deleted file mode 100644
index f365df1..0000000
--- a/backend/ev-document-find.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * $Id$
- */
-
-#ifndef EV_DOCUMENT_FIND_H
-#define EV_DOCUMENT_FIND_H
-
-#include <glib-object.h>
-#include <glib.h>
-#include <gdk/gdk.h>
-
-#include "ev-document.h" /* For EvRectangle */
-
-G_BEGIN_DECLS
-
-#define EV_TYPE_DOCUMENT_FIND (ev_document_find_get_type ())
-#define EV_DOCUMENT_FIND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_FIND, EvDocumentFind))
-#define EV_DOCUMENT_FIND_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_FIND, EvDocumentFindIface))
-#define EV_IS_DOCUMENT_FIND(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_DOCUMENT_FIND))
-#define EV_IS_DOCUMENT_FIND_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT_FIND))
-#define EV_DOCUMENT_FIND_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT_FIND, EvDocumentFindIface))
-
-typedef struct _EvDocumentFind EvDocumentFind;
-typedef struct _EvDocumentFindIface EvDocumentFindIface;
-
-struct _EvDocumentFindIface
-{
- GTypeInterface base_iface;
-
- /* Methods */
-
- void (* begin) (EvDocumentFind *document_find,
- int page,
- const char *search_string,
- gboolean case_sensitive);
- void (* cancel) (EvDocumentFind *document_find);
- int (* page_has_results) (EvDocumentFind *document_find,
- int page);
- int (* get_n_results) (EvDocumentFind *document_find,
- int page);
- gboolean (* get_result) (EvDocumentFind *document_find,
- int page,
- int n_result,
- EvRectangle *rectangle);
- double (* get_progress) (EvDocumentFind *document_find);
-
- /* Signals */
-
- void (* find_changed) (EvDocumentFind *document_find,
- int page);
-};
-
-GType ev_document_find_get_type (void);
-void ev_document_find_begin (EvDocumentFind *document_find,
- int page,
- const char *search_string,
- gboolean case_sensitive);
-void ev_document_find_cancel (EvDocumentFind *document_find);
-int ev_document_find_page_has_results (EvDocumentFind *document_find,
- int page);
-int ev_document_find_get_n_results (EvDocumentFind *document_find,
- int page);
-gboolean ev_document_find_get_result (EvDocumentFind *document_find,
- int page,
- int n_result,
- EvRectangle *rectangle);
-double ev_document_find_get_progress (EvDocumentFind *document_find);
-void ev_document_find_changed (EvDocumentFind *document_find,
- int page);
-
-/* How this interface works:
- *
- * begin() begins a new search, canceling any previous search.
- *
- * cancel() cancels a search if any, otherwise does nothing.
- *
- */
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-document-fonts.c b/backend/ev-document-fonts.c
deleted file mode 100644
index 929c21d..0000000
--- a/backend/ev-document-fonts.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* ev-document-fonts.h
- * this file is part of evince, a gnome document_fonts viewer
- *
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * Author:
- * Marco Pesenti Gritti <mpg@redhat.com>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include "ev-document-fonts.h"
-
-GType
-ev_document_fonts_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0))
- {
- const GTypeInfo our_info =
- {
- sizeof (EvDocumentFontsIface),
- NULL,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvDocumentFonts",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-double
-ev_document_fonts_get_progress (EvDocumentFonts *document_fonts)
-{
- EvDocumentFontsIface *iface = EV_DOCUMENT_FONTS_GET_IFACE (document_fonts);
-
- return iface->get_progress (document_fonts);
-}
-
-gboolean
-ev_document_fonts_scan (EvDocumentFonts *document_fonts,
- int n_pages)
-{
- EvDocumentFontsIface *iface = EV_DOCUMENT_FONTS_GET_IFACE (document_fonts);
-
- return iface->scan (document_fonts, n_pages);
-}
-
-void
-ev_document_fonts_fill_model (EvDocumentFonts *document_fonts,
- GtkTreeModel *model)
-{
- EvDocumentFontsIface *iface = EV_DOCUMENT_FONTS_GET_IFACE (document_fonts);
-
- iface->fill_model (document_fonts, model);
-}
diff --git a/backend/ev-document-fonts.h b/backend/ev-document-fonts.h
deleted file mode 100644
index c9f58f5..0000000
--- a/backend/ev-document-fonts.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* ev-document-fonts.h
- * this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * Author:
- * Marco Pesenti Gritti <mpg@redhat.com>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_DOCUMENT_FONTS_H
-#define EV_DOCUMENT_FONTS_H
-
-#include <glib-object.h>
-#include <glib.h>
-#include <gtk/gtk.h>
-
-#include "ev-document.h"
-#include "ev-link.h"
-
-G_BEGIN_DECLS
-
-
-#define EV_TYPE_DOCUMENT_FONTS (ev_document_fonts_get_type ())
-#define EV_DOCUMENT_FONTS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_FONTS, EvDocumentFonts))
-#define EV_DOCUMENT_FONTS_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_FONTS, EvDocumentFontsIface))
-#define EV_IS_DOCUMENT_FONTS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_DOCUMENT_FONTS))
-#define EV_IS_DOCUMENT_FONTS_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT_FONTS))
-#define EV_DOCUMENT_FONTS_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT_FONTS, EvDocumentFontsIface))
-
-typedef struct _EvDocumentFonts EvDocumentFonts;
-typedef struct _EvDocumentFontsIface EvDocumentFontsIface;
-
-enum {
- EV_DOCUMENT_FONTS_COLUMN_NAME,
- EV_DOCUMENT_FONTS_COLUMN_DETAILS,
- EV_DOCUMENT_FONTS_COLUMN_NUM_COLUMNS
-};
-
-struct _EvDocumentFontsIface
-{
- GTypeInterface base_iface;
-
- /* Methods */
- gboolean (* scan) (EvDocumentFonts *document_fonts,
- int n_pages);
- double (* get_progress) (EvDocumentFonts *document_fonts);
- void (* fill_model) (EvDocumentFonts *document_fonts,
- GtkTreeModel *model);
-};
-
-GType ev_document_fonts_get_type (void);
-gboolean ev_document_fonts_scan (EvDocumentFonts *document_fonts,
- int n_pages);
-double ev_document_fonts_get_progress (EvDocumentFonts *document_fonts);
-void ev_document_fonts_fill_model (EvDocumentFonts *document_fonts,
- GtkTreeModel *model);
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-document-images.c b/backend/ev-document-images.c
deleted file mode 100644
index 117b104..0000000
--- a/backend/ev-document-images.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ev-document-images.c
- * this file is part of evince, a gnome document_links viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "ev-document-images.h"
-
-GType
-ev_document_images_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0)) {
- const GTypeInfo our_info = {
- sizeof (EvDocumentImagesIface),
- NULL,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvDocumentImages",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-GList *
-ev_document_images_get_images (EvDocumentImages *document_images,
- gint page)
-{
- EvDocumentImagesIface *iface = EV_DOCUMENT_IMAGES_GET_IFACE (document_images);
- GList *retval;
-
- retval = iface->get_images (document_images, page);
-
- return retval;
-}
-
-
diff --git a/backend/ev-document-images.h b/backend/ev-document-images.h
deleted file mode 100644
index 28eee46..0000000
--- a/backend/ev-document-images.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ev-document-images.h
- * this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_DOCUMENT_IMAGES_H
-#define EV_DOCUMENT_IMAGES_H
-
-#include <glib-object.h>
-#include <glib.h>
-
-#include "ev-document.h"
-
-G_BEGIN_DECLS
-
-#define EV_TYPE_DOCUMENT_IMAGES (ev_document_images_get_type ())
-#define EV_DOCUMENT_IMAGES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_IMAGES, EvDocumentImages))
-#define EV_DOCUMENT_IMAGES_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_IMAGES, EvDocumentImagesIface))
-#define EV_IS_DOCUMENT_IMAGES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_DOCUMENT_IMAGES))
-#define EV_IS_DOCUMENT_IMAGES_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT_IMAGES))
-#define EV_DOCUMENT_IMAGES_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT_IMAGES, EvDocumentImagesIface))
-
-typedef struct _EvDocumentImages EvDocumentImages;
-typedef struct _EvDocumentImagesIface EvDocumentImagesIface;
-
-struct _EvDocumentImagesIface {
- GTypeInterface base_iface;
-
- /* Methods */
- GList *(* get_images) (EvDocumentImages *document_images,
- gint page);
-};
-
-GType ev_document_images_get_type (void) G_GNUC_CONST;
-GList *ev_document_images_get_images (EvDocumentImages *document_images,
- gint page);
-
-G_END_DECLS
-
-#endif /* EV_DOCUMENT_IMAGES_H */
diff --git a/backend/ev-document-info.h b/backend/ev-document-info.h
deleted file mode 100644
index 0cd1ef8..0000000
--- a/backend/ev-document-info.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2000-2003 Marco Pesenti Gritti
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef EV_DOCUMENT_INFO_H
-#define EV_DOCUMENT_INFO_H
-
-#include <glib-object.h>
-#include <glib.h>
-#include "ev-link.h"
-
-G_BEGIN_DECLS
-
-typedef struct _EvDocumentInfo EvDocumentInfo;
-
-typedef enum
-{
- EV_DOCUMENT_LAYOUT_SINGLE_PAGE,
- EV_DOCUMENT_LAYOUT_ONE_COLUMN,
- EV_DOCUMENT_LAYOUT_TWO_COLUMN_LEFT,
- EV_DOCUMENT_LAYOUT_TWO_COLUMN_RIGHT,
- EV_DOCUMENT_LAYOUT_TWO_PAGE_LEFT,
- EV_DOCUMENT_LAYOUT_TWO_PAGE_RIGHT,
-} EvDocumentLayout;
-
-typedef enum
-{
- EV_DOCUMENT_MODE_NONE,
- EV_DOCUMENT_MODE_USE_OC,
- EV_DOCUMENT_MODE_USE_THUMBS,
- EV_DOCUMENT_MODE_FULL_SCREEN,
- EV_DOCUMENT_MODE_USE_ATTACHMENTS,
- EV_DOCUMENT_MODE_PRESENTATION = EV_DOCUMENT_MODE_FULL_SCREEN /* Will these be different? */
-} EvDocumentMode;
-
-typedef enum
-{
- EV_DOCUMENT_UI_HINT_HIDE_TOOLBAR = 1 << 0,
- EV_DOCUMENT_UI_HINT_HIDE_MENUBAR = 1 << 1,
- EV_DOCUMENT_UI_HINT_HIDE_WINDOWUI = 1 << 2,
- EV_DOCUMENT_UI_HINT_FIT_WINDOW = 1 << 3,
- EV_DOCUMENT_UI_HINT_CENTER_WINDOW = 1 << 4,
- EV_DOCUMENT_UI_HINT_DISPLAY_DOC_TITLE = 1 << 5,
- EV_DOCUMENT_UI_HINT_DIRECTION_RTL = 1 << 6,
-} EvDocumentUIHints;
-
-
-typedef enum
-{
- EV_DOCUMENT_PERMISSIONS_OK_TO_PRINT = 1 << 0,
- EV_DOCUMENT_PERMISSIONS_OK_TO_MODIFY = 1 << 1,
- EV_DOCUMENT_PERMISSIONS_OK_TO_COPY = 1 << 2,
- EV_DOCUMENT_PERMISSIONS_OK_TO_ADD_NOTES = 1 << 3,
- EV_DOCUMENT_PERMISSIONS_FULL = (EV_DOCUMENT_PERMISSIONS_OK_TO_PRINT
- | EV_DOCUMENT_PERMISSIONS_OK_TO_MODIFY
- | EV_DOCUMENT_PERMISSIONS_OK_TO_COPY
- | EV_DOCUMENT_PERMISSIONS_OK_TO_ADD_NOTES),
-} EvDocumentPermissions;
-
-typedef enum
-{
- EV_DOCUMENT_INFO_TITLE = 1 << 0,
- EV_DOCUMENT_INFO_FORMAT = 1 << 1,
- EV_DOCUMENT_INFO_AUTHOR = 1 << 2,
- EV_DOCUMENT_INFO_SUBJECT = 1 << 3,
- EV_DOCUMENT_INFO_KEYWORDS = 1 << 4,
- EV_DOCUMENT_INFO_LAYOUT = 1 << 5,
- EV_DOCUMENT_INFO_CREATOR = 1 << 6,
- EV_DOCUMENT_INFO_PRODUCER = 1 << 7,
- EV_DOCUMENT_INFO_CREATION_DATE = 1 << 8,
- EV_DOCUMENT_INFO_MOD_DATE = 1 << 9,
- EV_DOCUMENT_INFO_LINEARIZED = 1 << 10,
- EV_DOCUMENT_INFO_START_MODE = 1 << 11,
- EV_DOCUMENT_INFO_UI_HINTS = 1 << 12,
- EV_DOCUMENT_INFO_PERMISSIONS = 1 << 13,
- EV_DOCUMENT_INFO_N_PAGES = 1 << 14,
- EV_DOCUMENT_INFO_SECURITY = 1 << 15,
- EV_DOCUMENT_INFO_PAPER_SIZE = 1 << 16
-} EvDocumentInfoFields;
-
-struct _EvDocumentInfo
-{
- char *title;
- char *format; /* eg, "pdf-1.5" */
- char *author;
- char *subject;
- char *keywords;
- char *creator;
- char *producer;
- char *linearized;
- char *security;
- GTime creation_date;
- GTime modified_date;
- EvDocumentLayout layout;
- EvDocumentMode mode;
- guint ui_hints;
- guint permissions;
- int n_pages;
- double paper_height;
- double paper_width;
-
- /* Mask of all the valid fields */
- guint fields_mask;
-};
-
-void ev_document_info_free (EvDocumentInfo *info);
-
-G_END_DECLS
-
-#endif /* EV_DOCUMENT_INFO_H */
diff --git a/backend/ev-document-links.c b/backend/ev-document-links.c
deleted file mode 100644
index c717096..0000000
--- a/backend/ev-document-links.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/* ev-document-links.h
- * this file is part of evince, a gnome document_links viewer
- *
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * Author:
- * Jonathan Blandford <jrb@alum.mit.edu>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include "ev-document-links.h"
-
-GType
-ev_document_links_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0)) {
- const GTypeInfo our_info = {
- sizeof (EvDocumentLinksIface),
- NULL,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvDocumentLinks",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-gboolean
-ev_document_links_has_document_links (EvDocumentLinks *document_links)
-{
- EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links);
- gboolean retval;
-
- retval = iface->has_document_links (document_links);
-
- return retval;
-}
-
-GtkTreeModel *
-ev_document_links_get_links_model (EvDocumentLinks *document_links)
-{
- EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links);
- GtkTreeModel *retval;
-
- retval = iface->get_links_model (document_links);
-
- return retval;
-}
-
-GList *
-ev_document_links_get_links (EvDocumentLinks *document_links,
- gint page)
-{
- EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links);
- GList *retval;
-
- retval = iface->get_links (document_links, page);
-
- return retval;
-}
-
-EvLinkDest *
-ev_document_links_find_link_dest (EvDocumentLinks *document_links,
- const gchar *link_name)
-{
- EvDocumentLinksIface *iface = EV_DOCUMENT_LINKS_GET_IFACE (document_links);
- EvLinkDest *retval;
-
- ev_document_doc_mutex_lock ();
- retval = iface->find_link_dest (document_links, link_name);
- ev_document_doc_mutex_unlock ();
-
- return retval;
-}
diff --git a/backend/ev-document-links.h b/backend/ev-document-links.h
deleted file mode 100644
index 8e8f20a..0000000
--- a/backend/ev-document-links.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* ev-document-links.h
- * this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * Author:
- * Jonathan Blandford <jrb@alum.mit.edu>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_DOCUMENT_LINKS_H
-#define EV_DOCUMENT_LINKS_H
-
-#include <glib-object.h>
-#include <glib.h>
-#include <gtk/gtk.h>
-
-#include "ev-document.h"
-#include "ev-link.h"
-
-G_BEGIN_DECLS
-
-
-#define EV_TYPE_DOCUMENT_LINKS (ev_document_links_get_type ())
-#define EV_DOCUMENT_LINKS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_LINKS, EvDocumentLinks))
-#define EV_DOCUMENT_LINKS_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_LINKS, EvDocumentLinksIface))
-#define EV_IS_DOCUMENT_LINKS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_DOCUMENT_LINKS))
-#define EV_IS_DOCUMENT_LINKS_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT_LINKS))
-#define EV_DOCUMENT_LINKS_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT_LINKS, EvDocumentLinksIface))
-
-typedef struct _EvDocumentLinks EvDocumentLinks;
-typedef struct _EvDocumentLinksIface EvDocumentLinksIface;
-
-enum {
- EV_DOCUMENT_LINKS_COLUMN_MARKUP,
- EV_DOCUMENT_LINKS_COLUMN_LINK,
- EV_DOCUMENT_LINKS_COLUMN_EXPAND,
- EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS
-};
-
-struct _EvDocumentLinksIface
-{
- GTypeInterface base_iface;
-
- /* Methods */
- gboolean (* has_document_links) (EvDocumentLinks *document_links);
- GtkTreeModel *(* get_links_model) (EvDocumentLinks *document_links);
- GList *(* get_links) (EvDocumentLinks *document_links,
- gint page);
- EvLinkDest *(* find_link_dest) (EvDocumentLinks *document_links,
- const gchar *link_name);
-};
-
-GType ev_document_links_get_type (void);
-gboolean ev_document_links_has_document_links (EvDocumentLinks *document_links);
-GtkTreeModel *ev_document_links_get_links_model (EvDocumentLinks *document_links);
-
-GList *ev_document_links_get_links (EvDocumentLinks *document_links,
- gint page);
-EvLinkDest *ev_document_links_find_link_dest (EvDocumentLinks *document_links,
- const gchar *link_name);
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-document-misc.c b/backend/ev-document-misc.c
deleted file mode 100644
index fd6f449..0000000
--- a/backend/ev-document-misc.c
+++ /dev/null
@@ -1,141 +0,0 @@
-
-#include "ev-document-misc.h"
-#include <string.h>
-#include <gtk/gtk.h>
-
-/* Returns a new GdkPixbuf that is suitable for placing in the thumbnail view.
- * It is four pixels wider and taller than the source. If source_pixbuf is not
- * NULL, then it will fill the return pixbuf with the contents of
- * source_pixbuf.
- */
-
-GdkPixbuf *
-ev_document_misc_get_thumbnail_frame (int width,
- int height,
- int rotation,
- GdkPixbuf *source_pixbuf)
-{
- GdkPixbuf *retval;
- guchar *data;
- gint rowstride;
- int i;
- int width_r, height_r;
-
- rotation = rotation % 360;
-
-
- if (source_pixbuf)
- g_return_val_if_fail (GDK_IS_PIXBUF (source_pixbuf), NULL);
-
- if (source_pixbuf) {
- width_r = gdk_pixbuf_get_width (source_pixbuf);
- height_r = gdk_pixbuf_get_height (source_pixbuf);
- } else {
- if (rotation == 0 || rotation == 180) {
- width_r = width;
- height_r = height;
- } else if (rotation == 90 || rotation == 270) {
- width_r = height;
- height_r = width;
- } else {
- g_assert_not_reached ();
- }
- }
-
- /* make sure no one is passing us garbage */
- g_assert (width_r >= 0 && height_r >= 0);
-
- retval = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
- TRUE, 8,
- width_r + 4,
- height_r + 4);
-
- /* make it black and fill in the middle */
- data = gdk_pixbuf_get_pixels (retval);
- rowstride = gdk_pixbuf_get_rowstride (retval);
-
- gdk_pixbuf_fill (retval, 0x000000ff);
- for (i = 1; i < height_r + 1; i++)
- memset (data + (rowstride * i) + 4, 0xffffffff, width_r * 4);
-
- /* copy the source pixbuf */
- if (source_pixbuf)
- gdk_pixbuf_copy_area (source_pixbuf, 0, 0,
- width_r,
- height_r,
- retval,
- 1, 1);
- /* Add the corner */
- data [(width_r + 2) * 4 + 3] = 0;
- data [(width_r + 3) * 4 + 3] = 0;
- data [(width_r + 2) * 4 + (rowstride * 1) + 3] = 0;
- data [(width_r + 3) * 4 + (rowstride * 1) + 3] = 0;
-
- data [(height_r + 2) * rowstride + 3] = 0;
- data [(height_r + 3) * rowstride + 3] = 0;
- data [(height_r + 2) * rowstride + 4 + 3] = 0;
- data [(height_r + 3) * rowstride + 4 + 3] = 0;
-
- return retval;
-}
-
-void
-ev_document_misc_get_page_border_size (gint page_width,
- gint page_height,
- GtkBorder *border)
-{
- g_assert (border);
-
- border->left = 1;
- border->top = 1;
- if (page_width < 100) {
- border->right = 2;
- border->bottom = 2;
- } else if (page_width < 500) {
- border->right = 3;
- border->bottom = 3;
- } else {
- border->right = 4;
- border->bottom = 4;
- }
-}
-
-
-void
-ev_document_misc_paint_one_page (GdkDrawable *drawable,
- GtkWidget *widget,
- GdkRectangle *area,
- GtkBorder *border,
- gboolean highlight)
-{
- gdk_draw_rectangle (drawable,
- highlight ?
- widget->style->text_gc[widget->state] : widget->style->dark_gc[widget->state],
- TRUE,
- area->x,
- area->y,
- area->width,
- area->height);
- gdk_draw_rectangle (drawable,
- widget->style->white_gc,
- TRUE,
- area->x + border->left,
- area->y + border->top,
- area->width - (border->left + border->right),
- area->height - (border->top + border->bottom));
- gdk_draw_rectangle (drawable,
- widget->style->mid_gc[widget->state],
- TRUE,
- area->x,
- area->y + area->height - (border->bottom - border->top),
- border->bottom - border->top,
- border->bottom - border->top);
- gdk_draw_rectangle (drawable,
- widget->style->mid_gc[widget->state],
- TRUE,
- area->x + area->width - (border->right - border->left),
- area->y,
- border->right - border->left,
- border->right - border->left);
-
-}
diff --git a/backend/ev-document-misc.h b/backend/ev-document-misc.h
deleted file mode 100644
index 41f1cae..0000000
--- a/backend/ev-document-misc.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2000-2003 Marco Pesenti Gritti
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * $Id$
- */
-
-#ifndef EV_DOCUMENT_MISC_H
-#define EV_DOCUMENT_MISC_H
-
-
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <gtk/gtkstyle.h>
-
-G_BEGIN_DECLS
-
-
-GdkPixbuf *ev_document_misc_get_thumbnail_frame (int width,
- int height,
- int rotation,
- GdkPixbuf *source_pixbuf);
-void ev_document_misc_get_page_border_size (gint page_width,
- gint page_height,
- GtkBorder *border);
-void ev_document_misc_paint_one_page (GdkDrawable *drawable,
- GtkWidget *widget,
- GdkRectangle *area,
- GtkBorder *border,
- gboolean highlight);
-
-G_END_DECLS
-
-#endif /* EV_DOCUMENT_MISC_H */
diff --git a/backend/ev-document-security.c b/backend/ev-document-security.c
deleted file mode 100644
index 49ded87..0000000
--- a/backend/ev-document-security.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/* ev-document-links.h
- * this file is part of evince, a gnome document_links viewer
- *
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * Author:
- * Jonathan Blandford <jrb@alum.mit.edu>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include "ev-document-security.h"
-
-GType
-ev_document_security_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0))
- {
- const GTypeInfo our_info =
- {
- sizeof (EvDocumentSecurityIface),
- NULL,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvDocumentSecurity",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-gboolean
-ev_document_security_has_document_security (EvDocumentSecurity *document_security)
-{
- EvDocumentSecurityIface *iface = EV_DOCUMENT_SECURITY_GET_IFACE (document_security);
- return iface->has_document_security (document_security);
-}
-
-void
-ev_document_security_set_password (EvDocumentSecurity *document_security,
- const char *password)
-{
- EvDocumentSecurityIface *iface = EV_DOCUMENT_SECURITY_GET_IFACE (document_security);
- iface->set_password (document_security, password);
-}
diff --git a/backend/ev-document-security.h b/backend/ev-document-security.h
deleted file mode 100644
index 154a3b1..0000000
--- a/backend/ev-document-security.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* ev-document-security.h
- * this file is part of evince, a gnome pdf viewer
- *
- * Copyright (C) 2005 Red Hat, Inc.
- *
- * Author:
- * Jonathan Blandford <jrb@alum.mit.edu>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_DOCUMENT_SECURITY_H
-#define EV_DOCUMENT_SECURITY_H
-
-#include <glib-object.h>
-#include <glib.h>
-#include <gdk/gdk.h>
-
-#include "ev-document.h"
-
-G_BEGIN_DECLS
-
-
-#define EV_TYPE_DOCUMENT_SECURITY (ev_document_security_get_type ())
-#define EV_DOCUMENT_SECURITY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_SECURITY, EvDocumentSecurity))
-#define EV_DOCUMENT_SECURITY_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_SECURITY, EvDocumentSecurityIface))
-#define EV_IS_DOCUMENT_SECURITY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_DOCUMENT_SECURITY))
-#define EV_IS_DOCUMENT_SECURITY_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT_SECURITY))
-#define EV_DOCUMENT_SECURITY_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT_SECURITY, EvDocumentSecurityIface))
-
-typedef struct _EvDocumentSecurity EvDocumentSecurity;
-typedef struct _EvDocumentSecurityIface EvDocumentSecurityIface;
-
-struct _EvDocumentSecurityIface
-{
- GTypeInterface base_iface;
-
- /* Methods */
- gboolean (* has_document_security) (EvDocumentSecurity *document_security);
- void (* set_password) (EvDocumentSecurity *document_security,
- const char *password);
-};
-
-GType ev_document_security_get_type (void);
-gboolean ev_document_security_has_document_security (EvDocumentSecurity *document_security);
-void ev_document_security_set_password (EvDocumentSecurity *document_security,
- const char *password);
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-document-thumbnails.c b/backend/ev-document-thumbnails.c
deleted file mode 100644
index c560cec..0000000
--- a/backend/ev-document-thumbnails.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include "config.h"
-
-#include "ev-document-thumbnails.h"
-
-GType
-ev_document_thumbnails_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0))
- {
- const GTypeInfo our_info =
- {
- sizeof (EvDocumentThumbnailsIface),
- NULL,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvDocumentThumbnails",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-GdkPixbuf *
-ev_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
- gint page,
- int rotation,
- gint size,
- gboolean border)
-{
- EvDocumentThumbnailsIface *iface;
-
- g_return_val_if_fail (EV_IS_DOCUMENT_THUMBNAILS (document), NULL);
-
- iface = EV_DOCUMENT_THUMBNAILS_GET_IFACE (document);
-
- return iface->get_thumbnail (document, page, rotation, size, border);
-}
-
-void
-ev_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
- gint page,
- gint suggested_width,
- gint *width,
- gint *height)
-{
- EvDocumentThumbnailsIface *iface;
-
- g_return_if_fail (EV_IS_DOCUMENT_THUMBNAILS (document));
- g_return_if_fail (width != NULL);
- g_return_if_fail (height != NULL);
-
- iface = EV_DOCUMENT_THUMBNAILS_GET_IFACE (document);
- iface->get_dimensions (document, page, suggested_width, width, height);
-}
-
diff --git a/backend/ev-document-thumbnails.h b/backend/ev-document-thumbnails.h
deleted file mode 100644
index 6e15a32..0000000
--- a/backend/ev-document-thumbnails.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2004 Anders Carlsson
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef EV_DOCUMENT_THUMBNAILS_H
-#define EV_DOCUMENT_THUMBNAILS_H
-
-#include <gdk-pixbuf/gdk-pixbuf.h>
-
-#include "ev-render-context.h"
-
-G_BEGIN_DECLS
-
-#define EV_TYPE_DOCUMENT_THUMBNAILS (ev_document_thumbnails_get_type ())
-#define EV_DOCUMENT_THUMBNAILS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_THUMBNAILS, EvDocumentThumbnails))
-#define EV_DOCUMENT_THUMBNAILS_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_THUMBNAILS, EvDocumentThumbnailsIface))
-#define EV_IS_DOCUMENT_THUMBNAILS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_DOCUMENT_THUMBNAILS))
-#define EV_IS_DOCUMENT_THUMBNAILS_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT_THUMBNAILS))
-#define EV_DOCUMENT_THUMBNAILS_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT_THUMBNAILS, EvDocumentThumbnailsIface))
-
-typedef struct _EvDocumentThumbnails EvDocumentThumbnails;
-typedef struct _EvDocumentThumbnailsIface EvDocumentThumbnailsIface;
-
-struct _EvDocumentThumbnailsIface
-{
- GTypeInterface base_iface;
-
- /* Methods */
- GdkPixbuf * (* get_thumbnail) (EvDocumentThumbnails *document,
- gint page,
- gint rotation,
- gint size,
- gboolean border);
- void (* get_dimensions) (EvDocumentThumbnails *document,
- gint page,
- gint suggested_width,
- gint *width,
- gint *height);
-};
-
-GType ev_document_thumbnails_get_type (void);
-
-/* FIXME: This is a little bit busted. We call get_thumbnail w/ a suggested
- * width, but we should call it with a scale so that different sized pages get
- * sized proportionally.
- */
-
-GdkPixbuf *ev_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
- gint page,
- gint rotation,
- gint size,
- gboolean border);
-void ev_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
- gint page,
- gint size,
- gint *width,
- gint *height);
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-document-transition.c b/backend/ev-document-transition.c
deleted file mode 100644
index 274da7c..0000000
--- a/backend/ev-document-transition.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ev-document-transition.c
- * this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "ev-document-transition.h"
-
-GType
-ev_document_transition_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0)) {
- const GTypeInfo our_info = {
- sizeof (EvDocumentTransitionIface),
- NULL,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvDocumentTransition",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-gdouble
-ev_document_transition_get_page_duration (EvDocumentTransition *document_trans,
- gint page)
-{
- EvDocumentTransitionIface *iface = EV_DOCUMENT_TRANSITION_GET_IFACE (document_trans);
-
- if (iface->get_page_duration)
- return iface->get_page_duration (document_trans, page);
-
- return -1;
-}
-
-
diff --git a/backend/ev-document-transition.h b/backend/ev-document-transition.h
deleted file mode 100644
index 3ca55ab..0000000
--- a/backend/ev-document-transition.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* ev-document-transition.h
- * this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_DOCUMENT_TRANSITION_H
-#define EV_DOCUMENT_TRANSITION_H
-
-#include <glib-object.h>
-
-#include "ev-document.h"
-
-G_BEGIN_DECLS
-
-#define EV_TYPE_DOCUMENT_TRANSITION (ev_document_transition_get_type ())
-#define EV_DOCUMENT_TRANSITION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_TRANSITION, EvDocumentTransition))
-#define EV_DOCUMENT_TRANSITION_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_TRANSITION, EvDocumentTransitionIface))
-#define EV_IS_DOCUMENT_TRANSITION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_DOCUMENT_TRANSITION))
-#define EV_IS_DOCUMENT_TRANSITION_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT_TRANSITION))
-#define EV_DOCUMENT_TRANSITION_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT_TRANSITION, EvDocumentTransitionIface))
-
-typedef struct _EvDocumentTransition EvDocumentTransition;
-typedef struct _EvDocumentTransitionIface EvDocumentTransitionIface;
-
-struct _EvDocumentTransitionIface
-{
- GTypeInterface base_iface;
-
- /* Methods */
- gdouble (* get_page_duration) (EvDocumentTransition *document_trans,
- gint page);
- /* TODO: Support page transition effects (page 562 PDF Reference 1.6) */
-};
-
-GType ev_document_transition_get_type (void) G_GNUC_CONST;
-gdouble ev_document_transition_get_page_duration (EvDocumentTransition *document_trans,
- gint page);
-
-G_END_DECLS
-
-#endif /* EV_DOCUMENT_TRANSITION_H */
diff --git a/backend/ev-document.c b/backend/ev-document.c
deleted file mode 100644
index a951bfa..0000000
--- a/backend/ev-document.c
+++ /dev/null
@@ -1,281 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2004 Marco Pesenti Gritti
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include "config.h"
-
-#include "ev-document.h"
-
-#include "ev-backend-marshalers.h"
-
-static void ev_document_class_init (gpointer g_class);
-
-
-GMutex *ev_doc_mutex = NULL;
-GMutex *ev_fc_mutex = NULL;
-
-#define LOG(x)
-GType
-ev_document_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0))
- {
- const GTypeInfo our_info =
- {
- sizeof (EvDocumentIface),
- NULL,
- NULL,
- (GClassInitFunc)ev_document_class_init
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvDocument",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-GQuark
-ev_document_error_quark (void)
-{
- static GQuark q = 0;
- if (q == 0)
- q = g_quark_from_static_string ("ev-document-error-quark");
-
- return q;
-}
-
-static void
-ev_document_class_init (gpointer g_class)
-{
-}
-
-GMutex *
-ev_document_get_doc_mutex (void)
-{
- if (ev_doc_mutex == NULL) {
- ev_doc_mutex = g_mutex_new ();
- }
- return ev_doc_mutex;
-}
-
-void
-ev_document_doc_mutex_lock (void)
-{
- g_mutex_lock (ev_document_get_doc_mutex ());
-}
-
-void
-ev_document_doc_mutex_unlock (void)
-{
- g_mutex_unlock (ev_document_get_doc_mutex ());
-}
-
-GMutex *
-ev_document_get_fc_mutex (void)
-{
- if (ev_fc_mutex == NULL) {
- ev_fc_mutex = g_mutex_new ();
- }
- return ev_fc_mutex;
-}
-
-void
-ev_document_fc_mutex_lock (void)
-{
- g_mutex_lock (ev_document_get_fc_mutex ());
-}
-
-void
-ev_document_fc_mutex_unlock (void)
-{
- g_mutex_unlock (ev_document_get_fc_mutex ());
-}
-
-gboolean
-ev_document_load (EvDocument *document,
- const char *uri,
- GError **error)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
- gboolean retval;
- LOG ("ev_document_load");
- retval = iface->load (document, uri, error);
-
- return retval;
-}
-
-gboolean
-ev_document_save (EvDocument *document,
- const char *uri,
- GError **error)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
- gboolean retval;
-
- LOG ("ev_document_save");
- retval = iface->save (document, uri, error);
-
- return retval;
-}
-
-int
-ev_document_get_n_pages (EvDocument *document)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
- gint retval;
-
- LOG ("ev_document_get_n_pages");
- retval = iface->get_n_pages (document);
-
- return retval;
-}
-
-void
-ev_document_get_page_size (EvDocument *document,
- int page,
- double *width,
- double *height)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
-
- LOG ("ev_document_get_page_size");
- iface->get_page_size (document, page, width, height);
-}
-
-char *
-ev_document_get_page_label(EvDocument *document,
- int page)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
-
- LOG ("ev_document_get_page_label");
- if (iface->get_page_label == NULL)
- return NULL;
-
- return iface->get_page_label (document, page);
-}
-
-gboolean
-ev_document_can_get_text (EvDocument *document)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
-
- return iface->can_get_text (document);
-}
-
-EvDocumentInfo *
-ev_document_get_info (EvDocument *document)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
-
- return iface->get_info (document);
-}
-
-char *
-ev_document_get_text (EvDocument *document,
- int page,
- EvRectangle *rect)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
- char *retval;
-
- LOG ("ev_document_get_text");
- retval = iface->get_text (document, page, rect);
-
- return retval;
-}
-
-gboolean
-ev_document_has_attachments (EvDocument *document)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
-
- if (iface->has_attachments == NULL)
- return FALSE;
-
- return iface->has_attachments (document);
-}
-
-GList *
-ev_document_get_attachments (EvDocument *document)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
- GList *retval;
-
- LOG ("ev_document_get_attachments");
- if (iface->get_attachments == NULL)
- return NULL;
- retval = iface->get_attachments (document);
-
- return retval;
-}
-
-GdkPixbuf *
-ev_document_render_pixbuf (EvDocument *document,
- EvRenderContext *rc)
-{
- EvDocumentIface *iface = EV_DOCUMENT_GET_IFACE (document);
- GdkPixbuf *retval;
-
- LOG ("ev_document_render_pixbuf");
- g_assert (iface->render_pixbuf);
-
- retval = iface->render_pixbuf (document, rc);
-
- return retval;
-}
-
-void
-ev_document_info_free (EvDocumentInfo *info)
-{
- if (info == NULL)
- return;
-
- g_free (info->title);
- g_free (info->format);
- g_free (info->author);
- g_free (info->subject);
- g_free (info->keywords);
- g_free (info->security);
-
- g_free (info);
-}
-
-
-/* Compares two rects. returns 0 if they're equal */
-#define EPSILON 0.0000001
-
-gint
-ev_rect_cmp (EvRectangle *a,
- EvRectangle *b)
-{
- if (a == b)
- return 0;
- if (a == NULL || b == NULL)
- return 1;
-
- return ! ((ABS (a->x1 - b->x1) < EPSILON) &&
- (ABS (a->y1 - b->y1) < EPSILON) &&
- (ABS (a->x2 - b->x2) < EPSILON) &&
- (ABS (a->y2 - b->y2) < EPSILON));
-}
diff --git a/backend/ev-document.h b/backend/ev-document.h
deleted file mode 100644
index 828ca25..0000000
--- a/backend/ev-document.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2000-2003 Marco Pesenti Gritti
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * $Id$
- */
-
-#ifndef EV_DOCUMENT_H
-#define EV_DOCUMENT_H
-
-#include <glib-object.h>
-#include <glib.h>
-#include <gdk/gdk.h>
-
-#include "ev-link.h"
-#include "ev-document-info.h"
-#include "ev-render-context.h"
-
-G_BEGIN_DECLS
-
-#define EV_TYPE_DOCUMENT (ev_document_get_type ())
-#define EV_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT, EvDocument))
-#define EV_DOCUMENT_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT, EvDocumentIface))
-#define EV_IS_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_DOCUMENT))
-#define EV_IS_DOCUMENT_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT))
-#define EV_DOCUMENT_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT, EvDocumentIface))
-
-typedef struct _EvDocument EvDocument;
-typedef struct _EvDocumentIface EvDocumentIface;
-typedef struct _EvPageCache EvPageCache;
-typedef struct _EvPageCacheClass EvPageCacheClass;
-
-#define EV_DOCUMENT_ERROR ev_document_error_quark ()
-#define EV_DOC_MUTEX_LOCK (ev_document_doc_mutex_lock ())
-#define EV_DOC_MUTEX_UNLOCK (ev_document_doc_mutex_unlock ())
-
-typedef enum
-{
- EV_DOCUMENT_ERROR_INVALID,
- EV_DOCUMENT_ERROR_ENCRYPTED
-} EvDocumentError;
-
-typedef struct {
- double x;
- double y;
-} EvPoint;
-
-typedef struct {
- double x1;
- double y1;
- double x2;
- double y2;
-} EvRectangle;
-
-struct _EvDocumentIface
-{
- GTypeInterface base_iface;
-
- /* Methods */
- gboolean (* load) (EvDocument *document,
- const char *uri,
- GError **error);
- gboolean (* save) (EvDocument *document,
- const char *uri,
- GError **error);
- int (* get_n_pages) (EvDocument *document);
- void (* get_page_size) (EvDocument *document,
- int page,
- double *width,
- double *height);
- char * (* get_page_label) (EvDocument *document,
- int page);
- gboolean (* can_get_text) (EvDocument *document);
- char * (* get_text) (EvDocument *document,
- int page,
- EvRectangle *rect);
- gboolean (* has_attachments) (EvDocument *document);
- GList * (* get_attachments) (EvDocument *document);
- GdkPixbuf * (* render_pixbuf) (EvDocument *document,
- EvRenderContext *rc);
- EvDocumentInfo * (* get_info) (EvDocument *document);
-};
-
-GType ev_document_get_type (void);
-GQuark ev_document_error_quark (void);
-
-/* Document mutex */
-GMutex *ev_document_get_doc_mutex (void);
-void ev_document_doc_mutex_lock (void);
-void ev_document_doc_mutex_unlock (void);
-
-/* FontConfig mutex */
-GMutex *ev_document_fc_doc_mutex (void);
-void ev_document_fc_mutex_lock (void);
-void ev_document_fc_mutex_unlock (void);
-
-EvDocumentInfo *ev_document_get_info (EvDocument *document);
-gboolean ev_document_load (EvDocument *document,
- const char *uri,
- GError **error);
-gboolean ev_document_save (EvDocument *document,
- const char *uri,
- GError **error);
-int ev_document_get_n_pages (EvDocument *document);
-void ev_document_get_page_size (EvDocument *document,
- int page,
- double *width,
- double *height);
-char *ev_document_get_page_label (EvDocument *document,
- int page);
-gboolean ev_document_can_get_text (EvDocument *document);
-char *ev_document_get_text (EvDocument *document,
- int page,
- EvRectangle *rect);
-gboolean ev_document_has_attachments (EvDocument *document);
-GList *ev_document_get_attachments (EvDocument *document);
-GdkPixbuf *ev_document_render_pixbuf (EvDocument *document,
- EvRenderContext *rc);
-
-gint ev_rect_cmp (EvRectangle *a,
- EvRectangle *b);
-
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-file-exporter.c b/backend/ev-file-exporter.c
deleted file mode 100644
index 7a4e902..0000000
--- a/backend/ev-file-exporter.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2004 Martin Kretzschmar
- *
- * Author:
- * Martin Kretzschmar <martink@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "ev-file-exporter.h"
-
-GType
-ev_file_exporter_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0)) {
- const GTypeInfo our_info =
- {
- sizeof (EvFileExporterIface),
- NULL,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvFileExporter",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-gboolean
-ev_file_exporter_format_supported (EvFileExporter *exporter,
- EvFileExporterFormat format)
-{
- EvFileExporterIface *iface = EV_FILE_EXPORTER_GET_IFACE (exporter);
-
- if (format < EV_FILE_FORMAT_PS ||
- format > EV_FILE_FORMAT_PDF)
- return FALSE;
-
- return iface->format_supported (exporter, format);
-}
-
-void
-ev_file_exporter_begin (EvFileExporter *exporter,
- EvFileExporterFormat format,
- const gchar *filename,
- gint first_page,
- gint last_page,
- gdouble paper_width,
- gdouble paper_height,
- gboolean duplex)
-{
- EvFileExporterIface *iface = EV_FILE_EXPORTER_GET_IFACE (exporter);
-
- g_return_if_fail (ev_file_exporter_format_supported (exporter, format));
-
- iface->begin (exporter, format, filename, first_page, last_page,
- paper_width, paper_height, duplex);
-}
-
-void
-ev_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
-{
- EvFileExporterIface *iface = EV_FILE_EXPORTER_GET_IFACE (exporter);
-
- iface->do_page (exporter, rc);
-}
-
-void
-ev_file_exporter_end (EvFileExporter *exporter)
-{
- EvFileExporterIface *iface = EV_FILE_EXPORTER_GET_IFACE (exporter);
-
- iface->end (exporter);
-}
diff --git a/backend/ev-file-exporter.h b/backend/ev-file-exporter.h
deleted file mode 100644
index f0e517c..0000000
--- a/backend/ev-file-exporter.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2004 Martin Kretzschmar
- *
- * Author:
- * Martin Kretzschmar <martink@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_FILE_EXPORTER_H
-#define EV_FILE_EXPORTER_H
-
-#include <glib-object.h>
-
-#include "ev-render-context.h"
-
-G_BEGIN_DECLS
-
-typedef enum {
- EV_FILE_FORMAT_PS,
- EV_FILE_FORMAT_PDF,
- EV_FILE_FORMAT_UNKNOWN
-} EvFileExporterFormat;
-
-#define EV_TYPE_FILE_EXPORTER (ev_file_exporter_get_type ())
-#define EV_FILE_EXPORTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_FILE_EXPORTER, EvFileExporter))
-#define EV_FILE_EXPORTER_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_FILE_EXPORTER, EvFileExporterIface))
-#define EV_IS_FILE_EXPORTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_FILE_EXPORTER))
-#define EV_IS_FILE_EXPORTER_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_FILE_EXPORTER))
-#define EV_FILE_EXPORTER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_FILE_EXPORTER, EvFileExporterIface))
-
-typedef struct _EvFileExporter EvFileExporter;
-typedef struct _EvFileExporterIface EvFileExporterIface;
-
-struct _EvFileExporterIface {
- GTypeInterface base_iface;
-
- /* Methods */
- gboolean (* format_supported) (EvFileExporter *exporter,
- EvFileExporterFormat format);
- void (* begin) (EvFileExporter *exporter,
- EvFileExporterFormat format,
- const gchar *filename,
- gint first_page,
- gint last_page,
- gdouble paper_width,
- gdouble paper_height,
- gboolean duplex);
- void (* do_page) (EvFileExporter *exporter,
- EvRenderContext *rc);
- void (* end) (EvFileExporter *exporter);
-};
-
-GType ev_file_exporter_get_type (void) G_GNUC_CONST;
-gboolean ev_file_exporter_format_supported (EvFileExporter *exporter,
- EvFileExporterFormat format);
-void ev_file_exporter_begin (EvFileExporter *exporter,
- EvFileExporterFormat format,
- const gchar *filename,
- gint first_page,
- gint last_page,
- gdouble paper_width,
- gdouble paper_height,
- gboolean duplex);
-void ev_file_exporter_do_page (EvFileExporter *exporter,
- EvRenderContext *rc);
-void ev_file_exporter_end (EvFileExporter *exporter);
-
-G_END_DECLS
-
-#endif
diff --git a/backend/ev-image.c b/backend/ev-image.c
deleted file mode 100644
index f906b00..0000000
--- a/backend/ev-image.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <glib/gstdio.h>
-#include "ev-file-helpers.h"
-#include "ev-image.h"
-
-struct _EvImagePrivate {
- GdkPixbuf *pixbuf;
- gchar *tmp_uri;
-};
-
-#define EV_IMAGE_GET_PRIVATE(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_IMAGE, EvImagePrivate))
-
-G_DEFINE_TYPE (EvImage, ev_image, G_TYPE_OBJECT)
-
-static void
-ev_image_finalize (GObject *object)
-{
- EvImage *image = EV_IMAGE (object);
-
- if (image->priv->pixbuf) {
- g_object_unref (image->priv->pixbuf);
- image->priv->pixbuf = NULL;
- }
-
- if (image->priv->tmp_uri) {
- g_unlink (image->priv->tmp_uri);
- g_free (image->priv->tmp_uri);
- image->priv->tmp_uri = NULL;
- }
-
- (* G_OBJECT_CLASS (ev_image_parent_class)->finalize) (object);
-}
-
-static void
-ev_image_class_init (EvImageClass *klass)
-{
- GObjectClass *g_object_class;
-
- g_object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (g_object_class, sizeof (EvImagePrivate));
-
- g_object_class->finalize = ev_image_finalize;
-}
-
-static void
-ev_image_init (EvImage *image)
-{
- image->priv = EV_IMAGE_GET_PRIVATE (image);
-}
-
-EvImage *
-ev_image_new_from_pixbuf (GdkPixbuf *pixbuf)
-{
- EvImage *image;
-
- g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
-
- image = EV_IMAGE (g_object_new (EV_TYPE_IMAGE, NULL));
- image->priv->pixbuf = g_object_ref (pixbuf);
-
- return image;
-}
-
-GdkPixbuf *
-ev_image_get_pixbuf (EvImage *image)
-{
- g_return_val_if_fail (EV_IS_IMAGE (image), NULL);
- g_return_val_if_fail (GDK_IS_PIXBUF (image->priv->pixbuf), NULL);
-
- return image->priv->pixbuf;
-}
-
-const gchar *
-ev_image_save_tmp (EvImage *image)
-{
- GError *error = NULL;
-
- g_return_val_if_fail (EV_IS_IMAGE (image), NULL);
- g_return_val_if_fail (GDK_IS_PIXBUF (image->priv->pixbuf), NULL);
-
- if (image->priv->tmp_uri)
- return image->priv->tmp_uri;
-
- image->priv->tmp_uri = ev_tmp_filename ("image");
- gdk_pixbuf_save (image->priv->pixbuf,
- image->priv->tmp_uri, "png", &error,
- "compression", "3", NULL);
- if (!error)
- return image->priv->tmp_uri;
-
- /* Erro saving image */
- g_warning (error->message);
- g_error_free (error);
- g_free (image->priv->tmp_uri);
- image->priv->tmp_uri = NULL;
-
- return NULL;
-}
-
-const gchar *
-ev_image_get_tmp_uri (EvImage *image)
-{
- g_return_val_if_fail (EV_IS_IMAGE (image), NULL);
-
- return image->priv->tmp_uri;
-}
-
-/* EvImageMapping */
-static void
-ev_image_mapping_free_foreach (EvImageMapping *mapping)
-{
- g_object_unref (mapping->image);
- g_free (mapping);
-}
-
-void
-ev_image_mapping_free (GList *image_mapping)
-{
- if (!image_mapping)
- return;
-
- g_list_foreach (image_mapping, (GFunc) ev_image_mapping_free_foreach, NULL);
- g_list_free (image_mapping);
-}
-
-EvImage *
-ev_image_mapping_find (GList *image_mapping,
- gdouble x,
- gdouble y)
-{
- GList *list;
-
- for (list = image_mapping; list; list = list->next) {
- EvImageMapping *mapping = list->data;
-
- if ((x >= mapping->x1) &&
- (y >= mapping->y1) &&
- (x <= mapping->x2) &&
- (y <= mapping->y2)) {
- return mapping->image;
- }
- }
-
- return NULL;
-}
-
-
diff --git a/backend/ev-image.h b/backend/ev-image.h
deleted file mode 100644
index 6688e7a..0000000
--- a/backend/ev-image.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __EV_IMAGE_H__
-#define __EV_IMAGE_H__
-
-#include <glib-object.h>
-#include <gdk/gdkpixbuf.h>
-
-G_BEGIN_DECLS
-
-typedef struct _EvImage EvImage;
-typedef struct _EvImageClass EvImageClass;
-typedef struct _EvImagePrivate EvImagePrivate;
-
-#define EV_TYPE_IMAGE (ev_image_get_type())
-#define EV_IMAGE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_IMAGE, EvImage))
-#define EV_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_IMAGE, EvImageClass))
-#define EV_IS_IMAGE(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_IMAGE))
-#define EV_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_IMAGE))
-#define EV_IMAGE_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_IMAGE, EvImageClass))
-
-struct _EvImage {
- GObject base_instance;
-
- EvImagePrivate *priv;
-};
-
-struct _EvImageClass {
- GObjectClass base_class;
-};
-
-GType ev_image_get_type (void) G_GNUC_CONST;
-EvImage *ev_image_new_from_pixbuf (GdkPixbuf *pixbuf);
-
-GdkPixbuf *ev_image_get_pixbuf (EvImage *image);
-const gchar *ev_image_save_tmp (EvImage *image);
-const gchar *ev_image_get_tmp_uri (EvImage *image);
-
-
-/* Image Mapping stuff */
-typedef struct _EvImageMapping EvImageMapping;
-struct _EvImageMapping {
- EvImage *image;
- gdouble x1;
- gdouble y1;
- gdouble x2;
- gdouble y2;
-};
-
-void ev_image_mapping_free (GList *image_mapping);
-EvImage *ev_image_mapping_find (GList *image_mapping,
- gdouble x,
- gdouble y);
-
-G_END_DECLS
-
-#endif /* __EV_IMAGE_H__ */
diff --git a/backend/ev-link-action.c b/backend/ev-link-action.c
deleted file mode 100644
index bc0a8fc..0000000
--- a/backend/ev-link-action.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- * Copyright (C) 2005 Red Hat, Inc.
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "ev-link-action.h"
-
-enum {
- PROP_0,
- PROP_TYPE,
- PROP_DEST,
- PROP_URI,
- PROP_FILENAME,
- PROP_PARAMS,
- PROP_NAME
-};
-
-struct _EvLinkAction {
- GObject base_instance;
-
- EvLinkActionPrivate *priv;
-};
-
-struct _EvLinkActionClass {
- GObjectClass base_class;
-};
-
-struct _EvLinkActionPrivate {
- EvLinkActionType type;
- EvLinkDest *dest;
- gchar *uri;
- gchar *filename;
- gchar *params;
- gchar *name;
-};
-
-G_DEFINE_TYPE (EvLinkAction, ev_link_action, G_TYPE_OBJECT)
-
-#define EV_LINK_ACTION_GET_PRIVATE(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_LINK_ACTION, EvLinkActionPrivate))
-
-GType
-ev_link_action_type_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0)) {
- static const GEnumValue values[] = {
- { EV_LINK_ACTION_TYPE_GOTO_DEST, "EV_LINK_ACTION_TYPE_GOTO_DEST", "goto-dest" },
- { EV_LINK_ACTION_TYPE_GOTO_REMOTE, "EV_LINK_ACTION_TYPE_GOTO_REMOTE", "goto-remote" },
- { EV_LINK_ACTION_TYPE_LAUNCH, "EV_LINK_ACTION_TYPE_LAUNCH", "launch" },
- { EV_LINK_ACTION_TYPE_EXTERNAL_URI, "EV_LINK_ACTION_TYPE_EXTERNAL_URI", "external-uri"},
- { EV_LINK_ACTION_TYPE_NAMED, "EV_LINK_ACTION_TYPE_NAMED", "named"},
- { 0, NULL, NULL }
- };
-
- type = g_enum_register_static ("EvLinkActionType", values);
- }
-
- return type;
-}
-
-EvLinkActionType
-ev_link_action_get_action_type (EvLinkAction *self)
-{
- g_return_val_if_fail (EV_IS_LINK_ACTION (self), 0);
-
- return self->priv->type;
-}
-
-EvLinkDest *
-ev_link_action_get_dest (EvLinkAction *self)
-{
- g_return_val_if_fail (EV_IS_LINK_ACTION (self), NULL);
-
- return self->priv->dest;
-}
-
-const gchar *
-ev_link_action_get_uri (EvLinkAction *self)
-{
- g_return_val_if_fail (EV_IS_LINK_ACTION (self), NULL);
-
- return self->priv->uri;
-}
-
-const gchar *
-ev_link_action_get_filename (EvLinkAction *self)
-{
- g_return_val_if_fail (EV_IS_LINK_ACTION (self), NULL);
-
- return self->priv->filename;
-}
-
-const gchar *
-ev_link_action_get_params (EvLinkAction *self)
-{
- g_return_val_if_fail (EV_IS_LINK_ACTION (self), NULL);
-
- return self->priv->params;
-}
-
-const gchar *
-ev_link_action_get_name (EvLinkAction *self)
-{
- g_return_val_if_fail (EV_IS_LINK_ACTION (self), NULL);
-
- return self->priv->name;
-}
-
-static void
-ev_link_action_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *param_spec)
-{
- EvLinkAction *self;
-
- self = EV_LINK_ACTION (object);
-
- switch (prop_id) {
- case PROP_TYPE:
- g_value_set_enum (value, self->priv->type);
- break;
- case PROP_DEST:
- g_value_set_pointer (value, self->priv->dest);
- break;
- case PROP_URI:
- g_value_set_string (value, self->priv->uri);
- break;
- case PROP_FILENAME:
- g_value_set_string (value, self->priv->filename);
- break;
- case PROP_PARAMS:
- g_value_set_string (value, self->priv->params);
- break;
- case PROP_NAME:
- g_value_set_string (value, self->priv->name);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
- prop_id,
- param_spec);
- break;
- }
-}
-
-static void
-ev_link_action_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *param_spec)
-{
- EvLinkAction *self = EV_LINK_ACTION (object);
-
- switch (prop_id) {
- case PROP_TYPE:
- self->priv->type = g_value_get_enum (value);
- break;
- case PROP_DEST:
- self->priv->dest = g_value_get_pointer (value);
- break;
- case PROP_URI:
- g_free (self->priv->uri);
- self->priv->uri = g_value_dup_string (value);
- break;
- case PROP_FILENAME:
- g_free (self->priv->filename);
- self->priv->filename = g_value_dup_string (value);
- break;
- case PROP_PARAMS:
- g_free (self->priv->params);
- self->priv->params = g_value_dup_string (value);
- break;
- case PROP_NAME:
- g_free (self->priv->name);
- self->priv->name = g_value_dup_string (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
- prop_id,
- param_spec);
- break;
- }
-}
-
-static void
-ev_link_action_finalize (GObject *object)
-{
- EvLinkActionPrivate *priv;
-
- priv = EV_LINK_ACTION (object)->priv;
-
- if (priv->dest) {
- g_object_unref (priv->dest);
- priv->dest = NULL;
- }
-
- if (priv->uri) {
- g_free (priv->uri);
- priv->uri = NULL;
- }
-
- if (priv->filename) {
- g_free (priv->filename);
- priv->filename = NULL;
- }
-
- if (priv->params) {
- g_free (priv->params);
- priv->params = NULL;
- }
-
- if (priv->name) {
- g_free (priv->name);
- priv->name = NULL;
- }
-
- G_OBJECT_CLASS (ev_link_action_parent_class)->finalize (object);
-}
-
-static void
-ev_link_action_init (EvLinkAction *ev_link_action)
-{
- ev_link_action->priv = EV_LINK_ACTION_GET_PRIVATE (ev_link_action);
-
- ev_link_action->priv->dest = NULL;
- ev_link_action->priv->uri = NULL;
- ev_link_action->priv->filename = NULL;
- ev_link_action->priv->params = NULL;
- ev_link_action->priv->name = NULL;
-}
-
-static void
-ev_link_action_class_init (EvLinkActionClass *ev_link_action_class)
-{
- GObjectClass *g_object_class;
-
- g_object_class = G_OBJECT_CLASS (ev_link_action_class);
-
- g_object_class->set_property = ev_link_action_set_property;
- g_object_class->get_property = ev_link_action_get_property;
-
- g_object_class->finalize = ev_link_action_finalize;
-
- g_type_class_add_private (g_object_class, sizeof (EvLinkActionPrivate));
-
- g_object_class_install_property (g_object_class,
- PROP_TYPE,
- g_param_spec_enum ("type",
- "Action Type",
- "The link action type",
- EV_TYPE_LINK_ACTION_TYPE,
- EV_LINK_ACTION_TYPE_GOTO_DEST,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_DEST,
- g_param_spec_pointer ("dest",
- "Action destination",
- "The link action destination",
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_URI,
- g_param_spec_string ("uri",
- "Link Action URI",
- "The link action URI",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_FILENAME,
- g_param_spec_string ("filename",
- "Filename",
- "The link action filename",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_PARAMS,
- g_param_spec_string ("params",
- "Params",
- "The link action params",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_NAME,
- g_param_spec_string ("name",
- "Name",
- "The link action name",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
-}
-
-EvLinkAction *
-ev_link_action_new_dest (EvLinkDest *dest)
-{
- return EV_LINK_ACTION (g_object_new (EV_TYPE_LINK_ACTION,
- "dest", dest,
- "type", EV_LINK_ACTION_TYPE_GOTO_DEST,
- NULL));
-}
-
-EvLinkAction *
-ev_link_action_new_remote (EvLinkDest *dest,
- const gchar *filename)
-{
- return EV_LINK_ACTION (g_object_new (EV_TYPE_LINK_ACTION,
- "dest", dest,
- "filename", filename,
- "type", EV_LINK_ACTION_TYPE_GOTO_REMOTE,
- NULL));
-}
-
-EvLinkAction *
-ev_link_action_new_external_uri (const gchar *uri)
-{
- return EV_LINK_ACTION (g_object_new (EV_TYPE_LINK_ACTION,
- "uri", uri,
- "type", EV_LINK_ACTION_TYPE_EXTERNAL_URI,
- NULL));
-}
-
-EvLinkAction *
-ev_link_action_new_launch (const gchar *filename,
- const gchar *params)
-{
- return EV_LINK_ACTION (g_object_new (EV_TYPE_LINK_ACTION,
- "filename", filename,
- "params", params,
- "type", EV_LINK_ACTION_TYPE_LAUNCH,
- NULL));
-}
-
-EvLinkAction *
-ev_link_action_new_named (const gchar *name)
-{
- return EV_LINK_ACTION (g_object_new (EV_TYPE_LINK_ACTION,
- "name", name,
- "type", EV_LINK_ACTION_TYPE_NAMED,
- NULL));
-}
diff --git a/backend/ev-link-action.h b/backend/ev-link-action.h
deleted file mode 100644
index 32211fe..0000000
--- a/backend/ev-link-action.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- * Copyright (C) 2005 Red Hat, Inc.
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_LINK_ACTION_H
-#define EV_LINK_ACTION_H
-
-#include <glib-object.h>
-#include "ev-link-dest.h"
-
-G_BEGIN_DECLS
-
-typedef struct _EvLinkAction EvLinkAction;
-typedef struct _EvLinkActionClass EvLinkActionClass;
-typedef struct _EvLinkActionPrivate EvLinkActionPrivate;
-
-#define EV_TYPE_LINK_ACTION (ev_link_action_get_type())
-#define EV_LINK_ACTION(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_LINK_ACTION, EvLinkAction))
-#define EV_LINK_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_LINK_ACTION, EvLinkActionClass))
-#define EV_IS_LINK_ACTION(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_LINK_ACTION))
-#define EV_IS_LINK_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_LINK_ACTION))
-#define EV_LINK_ACTION_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_LINK_ACTION, EvLinkActionClass))
-
-#define EV_TYPE_LINK_ACTION_TYPE (ev_link_action_type_get_type ())
-
-typedef enum {
- EV_LINK_ACTION_TYPE_GOTO_DEST,
- EV_LINK_ACTION_TYPE_GOTO_REMOTE,
- EV_LINK_ACTION_TYPE_EXTERNAL_URI,
- EV_LINK_ACTION_TYPE_LAUNCH,
- EV_LINK_ACTION_TYPE_NAMED
- /* We'll probably fill this in more as we support the other types of
- * actions */
-} EvLinkActionType;
-
-GType ev_link_action_type_get_type (void) G_GNUC_CONST;
-GType ev_link_action_get_type (void) G_GNUC_CONST;
-
-EvLinkActionType ev_link_action_get_action_type (EvLinkAction *self);
-EvLinkDest *ev_link_action_get_dest (EvLinkAction *self);
-const gchar *ev_link_action_get_uri (EvLinkAction *self);
-const gchar *ev_link_action_get_filename (EvLinkAction *self);
-const gchar *ev_link_action_get_params (EvLinkAction *self);
-const gchar *ev_link_action_get_name (EvLinkAction *self);
-
-EvLinkAction *ev_link_action_new_dest (EvLinkDest *dest);
-EvLinkAction *ev_link_action_new_remote (EvLinkDest *dest,
- const gchar *filename);
-EvLinkAction *ev_link_action_new_external_uri (const gchar *uri);
-EvLinkAction *ev_link_action_new_launch (const gchar *filename,
- const gchar *params);
-EvLinkAction *ev_link_action_new_named (const gchar *name);
-
-G_END_DECLS
-
-#endif /* EV_LINK_ACTION_H */
diff --git a/backend/ev-link-dest.c b/backend/ev-link-dest.c
deleted file mode 100644
index 09f840a..0000000
--- a/backend/ev-link-dest.c
+++ /dev/null
@@ -1,467 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- * Copyright (C) 2005 Red Hat, Inc.
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "ev-link-dest.h"
-
-enum {
- PROP_0,
- PROP_TYPE,
- PROP_PAGE,
- PROP_LEFT,
- PROP_TOP,
- PROP_BOTTOM,
- PROP_RIGHT,
- PROP_ZOOM,
- PROP_NAMED,
- PROP_PAGE_LABEL
-};
-
-struct _EvLinkDest {
- GObject base_instance;
-
- EvLinkDestPrivate *priv;
-};
-
-struct _EvLinkDestClass {
- GObjectClass base_class;
-};
-
-struct _EvLinkDestPrivate {
- EvLinkDestType type;
- int page;
- double top;
- double left;
- double bottom;
- double right;
- double zoom;
- gchar *named;
- gchar *page_label;
-};
-
-G_DEFINE_TYPE (EvLinkDest, ev_link_dest, G_TYPE_OBJECT)
-
-#define EV_LINK_DEST_GET_PRIVATE(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_LINK_DEST, EvLinkDestPrivate))
-
-GType
-ev_link_dest_type_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0)) {
- static const GEnumValue values[] = {
- { EV_LINK_DEST_TYPE_PAGE, "EV_LINK_DEST_TYPE_PAGE", "page" },
- { EV_LINK_DEST_TYPE_XYZ, "EV_LINK_DEST_TYPE_XYZ", "xyz" },
- { EV_LINK_DEST_TYPE_FIT, "EV_LINK_DEST_TYPE_FIT", "fit" },
- { EV_LINK_DEST_TYPE_FITH, "EV_LINK_DEST_TYPE_FITH", "fith" },
- { EV_LINK_DEST_TYPE_FITV, "EV_LINK_DEST_TYPE_FITV", "fitv" },
- { EV_LINK_DEST_TYPE_FITR, "EV_LINK_DEST_TYPE_FITR", "fitr" },
- { EV_LINK_DEST_TYPE_NAMED, "EV_LINK_DEST_TYPE_NAMED", "named" },
- { EV_LINK_DEST_TYPE_PAGE_LABEL, "EV_LINK_DEST_TYPE_PAGE_LABEL", "page_label" },
- { EV_LINK_DEST_TYPE_UNKNOWN, "EV_LINK_DEST_TYPE_UNKNOWN", "unknown" },
- { 0, NULL, NULL }
- };
-
- type = g_enum_register_static ("EvLinkDestType", values);
- }
-
- return type;
-}
-
-EvLinkDestType
-ev_link_dest_get_dest_type (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), 0);
-
- return self->priv->type;
-}
-
-gint
-ev_link_dest_get_page (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), -1);
-
- return self->priv->page;
-}
-
-gdouble
-ev_link_dest_get_top (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), 0);
-
- return self->priv->top;
-}
-
-gdouble
-ev_link_dest_get_left (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), 0);
-
- return self->priv->left;
-}
-
-gdouble
-ev_link_dest_get_bottom (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), 0);
-
- return self->priv->bottom;
-}
-
-gdouble
-ev_link_dest_get_right (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), 0);
-
- return self->priv->right;
-}
-
-gdouble
-ev_link_dest_get_zoom (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), 0);
-
- return self->priv->zoom;
-}
-
-const gchar *
-ev_link_dest_get_named_dest (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), NULL);
-
- return self->priv->named;
-}
-
-const gchar *
-ev_link_dest_get_page_label (EvLinkDest *self)
-{
- g_return_val_if_fail (EV_IS_LINK_DEST (self), NULL);
-
- return self->priv->page_label;
-}
-
-static void
-ev_link_dest_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *param_spec)
-{
- EvLinkDest *self;
-
- self = EV_LINK_DEST (object);
-
- switch (prop_id) {
- case PROP_TYPE:
- g_value_set_enum (value, self->priv->type);
- break;
- case PROP_PAGE:
- g_value_set_int (value, self->priv->page);
- break;
- case PROP_TOP:
- g_value_set_double (value, self->priv->top);
- break;
- case PROP_LEFT:
- g_value_set_double (value, self->priv->left);
- break;
- case PROP_BOTTOM:
- g_value_set_double (value, self->priv->bottom);
- break;
- case PROP_RIGHT:
- g_value_set_double (value, self->priv->left);
- break;
- case PROP_ZOOM:
- g_value_set_double (value, self->priv->zoom);
- break;
- case PROP_NAMED:
- g_value_set_string (value, self->priv->named);
- break;
- case PROP_PAGE_LABEL:
- g_value_set_string (value, self->priv->page_label);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
- prop_id,
- param_spec);
- break;
- }
-}
-
-static void
-ev_link_dest_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *param_spec)
-{
- EvLinkDest *self = EV_LINK_DEST (object);
-
- switch (prop_id) {
- case PROP_TYPE:
- self->priv->type = g_value_get_enum (value);
- break;
- case PROP_PAGE:
- self->priv->page = g_value_get_int (value);
- break;
- case PROP_TOP:
- self->priv->top = g_value_get_double (value);
- break;
- case PROP_LEFT:
- self->priv->left = g_value_get_double (value);
- break;
- case PROP_BOTTOM:
- self->priv->bottom = g_value_get_double (value);
- break;
- case PROP_RIGHT:
- self->priv->right = g_value_get_double (value);
- break;
- case PROP_ZOOM:
- self->priv->zoom = g_value_get_double (value);
- break;
- case PROP_NAMED:
- self->priv->named = g_value_dup_string (value);
- break;
- case PROP_PAGE_LABEL:
- self->priv->page_label = g_value_dup_string (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
- prop_id,
- param_spec);
- break;
- }
-}
-
-static void
-ev_link_dest_finalize (GObject *object)
-{
- EvLinkDestPrivate *priv;
-
- priv = EV_LINK_DEST (object)->priv;
-
- if (priv->named) {
- g_free (priv->named);
- priv->named = NULL;
- }
- if (priv->page_label) {
- g_free (priv->page_label);
- priv->page_label = NULL;
- }
-
- G_OBJECT_CLASS (ev_link_dest_parent_class)->finalize (object);
-}
-
-static void
-ev_link_dest_init (EvLinkDest *ev_link_dest)
-{
- ev_link_dest->priv = EV_LINK_DEST_GET_PRIVATE (ev_link_dest);
-
- ev_link_dest->priv->named = NULL;
-}
-
-static void
-ev_link_dest_class_init (EvLinkDestClass *ev_link_dest_class)
-{
- GObjectClass *g_object_class;
-
- g_object_class = G_OBJECT_CLASS (ev_link_dest_class);
-
- g_object_class->set_property = ev_link_dest_set_property;
- g_object_class->get_property = ev_link_dest_get_property;
-
- g_object_class->finalize = ev_link_dest_finalize;
-
- g_type_class_add_private (g_object_class, sizeof (EvLinkDestPrivate));
-
- g_object_class_install_property (g_object_class,
- PROP_TYPE,
- g_param_spec_enum ("type",
- "Dest Type",
- "The destination type",
- EV_TYPE_LINK_DEST_TYPE,
- EV_LINK_DEST_TYPE_UNKNOWN,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_PAGE,
- g_param_spec_int ("page",
- "Dest Page",
- "The destination page",
- -1,
- G_MAXINT,
- 0,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_LEFT,
- g_param_spec_double ("left",
- "Left coordinate",
- "The left coordinate",
- -G_MAXDOUBLE,
- G_MAXDOUBLE,
- 0,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_TOP,
- g_param_spec_double ("top",
- "Top coordinate",
- "The top coordinate",
- -G_MAXDOUBLE,
- G_MAXDOUBLE,
- 0,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_BOTTOM,
- g_param_spec_double ("bottom",
- "Bottom coordinate",
- "The bottom coordinate",
- -G_MAXDOUBLE,
- G_MAXDOUBLE,
- 0,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_RIGHT,
- g_param_spec_double ("right",
- "Right coordinate",
- "The right coordinate",
- -G_MAXDOUBLE,
- G_MAXDOUBLE,
- 0,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property (g_object_class,
- PROP_ZOOM,
- g_param_spec_double ("zoom",
- "Zoom",
- "Zoom",
- 0,
- G_MAXDOUBLE,
- 0,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_NAMED,
- g_param_spec_string ("named",
- "Named destination",
- "The named destination",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_PAGE_LABEL,
- g_param_spec_string ("page_label",
- "Label of the page",
- "The label of the destination page",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
-}
-
-EvLinkDest *
-ev_link_dest_new_page (gint page)
-{
- return EV_LINK_DEST (g_object_new (EV_TYPE_LINK_DEST,
- "page", page,
- "type", EV_LINK_DEST_TYPE_PAGE,
- NULL));
-}
-
-EvLinkDest *
-ev_link_dest_new_xyz (gint page,
- gdouble left,
- gdouble top,
- gdouble zoom)
-{
- return EV_LINK_DEST (g_object_new (EV_TYPE_LINK_DEST,
- "page", page,
- "type", EV_LINK_DEST_TYPE_XYZ,
- "left", left,
- "top", top,
- "zoom", zoom,
- NULL));
-}
-
-EvLinkDest *
-ev_link_dest_new_fit (gint page)
-{
- return EV_LINK_DEST (g_object_new (EV_TYPE_LINK_DEST,
- "page", page,
- "type", EV_LINK_DEST_TYPE_FIT,
- NULL));
-}
-
-EvLinkDest *
-ev_link_dest_new_fith (gint page,
- gdouble top)
-{
- return EV_LINK_DEST (g_object_new (EV_TYPE_LINK_DEST,
- "page", page,
- "type", EV_LINK_DEST_TYPE_FITH,
- "top", top,
- NULL));
-}
-
-EvLinkDest *
-ev_link_dest_new_fitv (gint page,
- gdouble left)
-{
- return EV_LINK_DEST (g_object_new (EV_TYPE_LINK_DEST,
- "page", page,
- "type", EV_LINK_DEST_TYPE_FITV,
- "left", left,
- NULL));
-}
-
-EvLinkDest *
-ev_link_dest_new_fitr (gint page,
- gdouble left,
- gdouble bottom,
- gdouble right,
- gdouble top)
-{
- return EV_LINK_DEST (g_object_new (EV_TYPE_LINK_DEST,
- "page", page,
- "type", EV_LINK_DEST_TYPE_FITR,
- "left", left,
- "bottom", bottom,
- "right", right,
- "top", top,
- NULL));
-}
-
-EvLinkDest *
-ev_link_dest_new_named (const gchar *named_dest)
-{
- return EV_LINK_DEST (g_object_new (EV_TYPE_LINK_DEST,
- "named", named_dest,
- "type", EV_LINK_DEST_TYPE_NAMED,
- NULL));
-}
-
-EvLinkDest *
-ev_link_dest_new_page_label (const gchar *page_label)
-{
- return EV_LINK_DEST (g_object_new (EV_TYPE_LINK_DEST,
- "page_label", page_label,
- "type", EV_LINK_DEST_TYPE_PAGE_LABEL,
- NULL));
-}
diff --git a/backend/ev-link-dest.h b/backend/ev-link-dest.h
deleted file mode 100644
index a0c96f5..0000000
--- a/backend/ev-link-dest.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org>
- * Copyright (C) 2005 Red Hat, Inc.
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_LINK_DEST_H
-#define EV_LINK_DEST_H
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef struct _EvLinkDest EvLinkDest;
-typedef struct _EvLinkDestClass EvLinkDestClass;
-typedef struct _EvLinkDestPrivate EvLinkDestPrivate;
-
-#define EV_TYPE_LINK_DEST (ev_link_dest_get_type())
-#define EV_LINK_DEST(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_LINK_DEST, EvLinkDest))
-#define EV_LINK_DEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_LINK_DEST, EvLinkDestClass))
-#define EV_IS_LINK_DEST(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_LINK_DEST))
-#define EV_IS_LINK_DEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_LINK_DEST))
-#define EV_LINK_DEST_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_LINK_DEST, EvLinkDestClass))
-
-#define EV_TYPE_LINK_DEST_TYPE (ev_link_dest_type_get_type ())
-
-typedef enum {
- EV_LINK_DEST_TYPE_PAGE,
- EV_LINK_DEST_TYPE_XYZ,
- EV_LINK_DEST_TYPE_FIT,
- EV_LINK_DEST_TYPE_FITH,
- EV_LINK_DEST_TYPE_FITV,
- EV_LINK_DEST_TYPE_FITR,
- EV_LINK_DEST_TYPE_NAMED,
- EV_LINK_DEST_TYPE_PAGE_LABEL,
- EV_LINK_DEST_TYPE_UNKNOWN
-} EvLinkDestType;
-
-GType ev_link_dest_type_get_type (void) G_GNUC_CONST;
-GType ev_link_dest_get_type (void) G_GNUC_CONST;
-
-EvLinkDestType ev_link_dest_get_dest_type (EvLinkDest *self);
-gint ev_link_dest_get_page (EvLinkDest *self);
-gdouble ev_link_dest_get_top (EvLinkDest *self);
-gdouble ev_link_dest_get_left (EvLinkDest *self);
-gdouble ev_link_dest_get_bottom (EvLinkDest *self);
-gdouble ev_link_dest_get_right (EvLinkDest *self);
-gdouble ev_link_dest_get_zoom (EvLinkDest *self);
-const gchar *ev_link_dest_get_named_dest (EvLinkDest *self);
-const gchar *ev_link_dest_get_page_label (EvLinkDest *self);
-
-EvLinkDest *ev_link_dest_new_page (gint page);
-EvLinkDest *ev_link_dest_new_xyz (gint page,
- gdouble left,
- gdouble top,
- gdouble zoom);
-EvLinkDest *ev_link_dest_new_fit (gint page);
-EvLinkDest *ev_link_dest_new_fith (gint page,
- gdouble top);
-EvLinkDest *ev_link_dest_new_fitv (gint page,
- gdouble left);
-EvLinkDest *ev_link_dest_new_fitr (gint page,
- gdouble left,
- gdouble bottom,
- gdouble right,
- gdouble top);
-EvLinkDest *ev_link_dest_new_named (const gchar *named_dest);
-EvLinkDest *ev_link_dest_new_page_label (const gchar *page_label);
-
-G_END_DECLS
-
-#endif /* EV_LINK_DEST_H */
diff --git a/backend/ev-link.c b/backend/ev-link.c
deleted file mode 100644
index 8561914..0000000
--- a/backend/ev-link.c
+++ /dev/null
@@ -1,226 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2005 Red Hat, Inc.
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "ev-link.h"
-
-enum {
- PROP_0,
- PROP_TITLE,
- PROP_ACTION
-};
-
-struct _EvLink {
- GObject base_instance;
- EvLinkPrivate *priv;
-};
-
-struct _EvLinkClass {
- GObjectClass base_class;
-};
-
-struct _EvLinkPrivate {
- gchar *title;
- EvLinkAction *action;
-};
-
-G_DEFINE_TYPE (EvLink, ev_link, G_TYPE_OBJECT)
-
-#define EV_LINK_GET_PRIVATE(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_LINK, EvLinkPrivate))
-
-const gchar *
-ev_link_get_title (EvLink *self)
-{
- g_return_val_if_fail (EV_IS_LINK (self), NULL);
-
- return self->priv->title;
-}
-
-EvLinkAction *
-ev_link_get_action (EvLink *self)
-{
- g_return_val_if_fail (EV_IS_LINK (self), NULL);
-
- return self->priv->action;
-}
-
-static void
-ev_link_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *param_spec)
-{
- EvLink *self;
-
- self = EV_LINK (object);
-
- switch (prop_id) {
- case PROP_TITLE:
- g_value_set_string (value, self->priv->title);
- break;
- case PROP_ACTION:
- g_value_set_pointer (value, self->priv->action);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
- prop_id,
- param_spec);
- break;
- }
-}
-
-static void
-ev_link_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *param_spec)
-{
- EvLink *self = EV_LINK (object);
-
- switch (prop_id) {
- case PROP_TITLE:
- self->priv->title = g_value_dup_string (value);
- break;
- case PROP_ACTION:
- self->priv->action = g_value_get_pointer (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
- prop_id,
- param_spec);
- break;
- }
-}
-
-static void
-ev_link_finalize (GObject *object)
-{
- EvLinkPrivate *priv;
-
- priv = EV_LINK (object)->priv;
-
- if (priv->title) {
- g_free (priv->title);
- priv->title = NULL;
- }
-
- if (priv->action) {
- g_object_unref (priv->action);
- priv->action = NULL;
- }
-
- G_OBJECT_CLASS (ev_link_parent_class)->finalize (object);
-}
-
-static void
-ev_link_init (EvLink *ev_link)
-{
- ev_link->priv = EV_LINK_GET_PRIVATE (ev_link);
-
- ev_link->priv->title = NULL;
- ev_link->priv->action = NULL;
-}
-
-static void
-ev_link_class_init (EvLinkClass *ev_window_class)
-{
- GObjectClass *g_object_class;
-
- g_object_class = G_OBJECT_CLASS (ev_window_class);
-
- g_object_class->set_property = ev_link_set_property;
- g_object_class->get_property = ev_link_get_property;
-
- g_object_class->finalize = ev_link_finalize;
-
- g_type_class_add_private (g_object_class, sizeof (EvLinkPrivate));
-
- g_object_class_install_property (g_object_class,
- PROP_TITLE,
- g_param_spec_string ("title",
- "Link Title",
- "The link title",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (g_object_class,
- PROP_ACTION,
- g_param_spec_pointer ("action",
- "Link Action",
- "The link action",
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
-}
-
-EvLink *
-ev_link_new (const char *title,
- EvLinkAction *action)
-{
- return EV_LINK (g_object_new (EV_TYPE_LINK,
- "title", title,
- "action", action,
- NULL));
-}
-
-/* Link Mapping stuff */
-static void
-ev_link_mapping_free_foreach (EvLinkMapping *mapping)
-{
- g_object_unref (G_OBJECT (mapping->link));
- g_free (mapping);
-}
-
-void
-ev_link_mapping_free (GList *link_mapping)
-{
- if (link_mapping == NULL)
- return;
-
- g_list_foreach (link_mapping, (GFunc) (ev_link_mapping_free_foreach), NULL);
- g_list_free (link_mapping);
-}
-
-EvLink *
-ev_link_mapping_find (GList *link_mapping,
- gdouble x,
- gdouble y)
-{
- GList *list;
- EvLink *link = NULL;
- int i;
-
- i = 0;
-
- for (list = link_mapping; list; list = list->next) {
- EvLinkMapping *mapping = list->data;
-
- i++;
- if ((x >= mapping->x1) &&
- (y >= mapping->y1) &&
- (x <= mapping->x2) &&
- (y <= mapping->y2)) {
- link = mapping->link;
- break;
- }
- }
-
- return link;
-}
-
diff --git a/backend/ev-link.h b/backend/ev-link.h
deleted file mode 100644
index b7304de..0000000
--- a/backend/ev-link.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2005 Red Hat, Inc.
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_LINK_H
-#define EV_LINK_H
-
-#include <glib-object.h>
-#include "ev-link-action.h"
-
-G_BEGIN_DECLS
-
-typedef struct _EvLink EvLink;
-typedef struct _EvLinkClass EvLinkClass;
-typedef struct _EvLinkPrivate EvLinkPrivate;
-
-#define EV_TYPE_LINK (ev_link_get_type())
-#define EV_LINK(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_LINK, EvLink))
-#define EV_LINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_LINK, EvLinkClass))
-#define EV_IS_LINK(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_LINK))
-#define EV_IS_LINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_LINK))
-#define EV_LINK_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_LINK, EvLinkClass))
-
-GType ev_link_get_type (void) G_GNUC_CONST;
-
-EvLink *ev_link_new (const gchar *title,
- EvLinkAction *action);
-
-const gchar *ev_link_get_title (EvLink *self);
-EvLinkAction *ev_link_get_action (EvLink *self);
-
-/* Link Mapping stuff */
-typedef struct _EvLinkMapping EvLinkMapping;
-struct _EvLinkMapping
-{
- EvLink *link;
- gdouble x1;
- gdouble y1;
- gdouble x2;
- gdouble y2;
-};
-
-void ev_link_mapping_free (GList *link_mapping);
-EvLink *ev_link_mapping_find (GList *link_mapping,
- gdouble x,
- gdouble y);
-G_END_DECLS
-
-#endif /* !EV_LINK_H */
diff --git a/backend/ev-render-context.c b/backend/ev-render-context.c
deleted file mode 100644
index a3969ad..0000000
--- a/backend/ev-render-context.c
+++ /dev/null
@@ -1,79 +0,0 @@
-#include "ev-render-context.h"
-
-static void ev_render_context_init (EvRenderContext *rc);
-static void ev_render_context_class_init (EvRenderContextClass *class);
-
-
-G_DEFINE_TYPE (EvRenderContext, ev_render_context, G_TYPE_OBJECT);
-
-static void ev_render_context_init (EvRenderContext *rc) { /* Do Nothing */ }
-
-static void
-ev_render_context_dispose (GObject *object)
-{
- EvRenderContext *rc;
-
- rc = (EvRenderContext *) object;
-
- if (rc->destroy) {
- (*rc->destroy) (rc->data);
- rc->destroy = NULL;
- }
-
- (* G_OBJECT_CLASS (ev_render_context_parent_class)->dispose) (object);
-}
-
-static void
-ev_render_context_class_init (EvRenderContextClass *class)
-{
- GObjectClass *oclass;
-
- oclass = G_OBJECT_CLASS (class);
-
- oclass->dispose = ev_render_context_dispose;
-}
-
-
-EvRenderContext *
-ev_render_context_new (int rotation,
- gint page,
- gdouble scale)
-{
- EvRenderContext *rc;
-
- rc = (EvRenderContext *) g_object_new (EV_TYPE_RENDER_CONTEXT, NULL);
-
- rc->rotation = rotation;
- rc->page = page;
- rc->scale = scale;
-
- return rc;
-}
-
-void
-ev_render_context_set_page (EvRenderContext *rc,
- gint page)
-{
- g_return_if_fail (rc != NULL);
-
- rc->page = page;
-}
-
-void
-ev_render_context_set_rotation (EvRenderContext *rc,
- int rotation)
-{
- g_return_if_fail (rc != NULL);
-
- rc->rotation = rotation;
-}
-
-void
-ev_render_context_set_scale (EvRenderContext *rc,
- gdouble scale)
-{
- g_return_if_fail (rc != NULL);
-
- rc->scale = scale;
-}
-
diff --git a/backend/ev-render-context.h b/backend/ev-render-context.h
deleted file mode 100644
index 636f02f..0000000
--- a/backend/ev-render-context.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* this file is part of evince, a gnome document viewer
- *
- * Copyright (C) 2005 Jonathan Blandford <jrb@gnome.org>
- *
- * Evince 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.
- *
- * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef EV_RENDER_CONTEXT_H
-#define EV_RENDER_CONTEXT_H
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef struct _EvRenderContext EvRenderContext;
-typedef struct _EvRenderContextClass EvRenderContextClass;
-
-#define EV_TYPE_RENDER_CONTEXT (ev_render_context_get_type())
-#define EV_RENDER_CONTEXT(context) ((EvRenderContext *) (context))
-#define EV_RENDER_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_RENDER_CONTEXT, EvRenderContext))
-#define EV_IS_RENDER_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_RENDER_CONTEXT))
-
-struct _EvRenderContextClass
-{
- GObjectClass klass;
-};
-
-struct _EvRenderContext
-{
- GObject parent;
- int rotation;
- gint page;
- gdouble scale;
-
- gpointer data;
- GDestroyNotify destroy;
-};
-
-
-GType ev_render_context_get_type (void) G_GNUC_CONST;
-EvRenderContext *ev_render_context_new (int rotation,
- gint page,
- gdouble scale);
-void ev_render_context_set_page (EvRenderContext *rc,
- gint page);
-void ev_render_context_set_rotation (EvRenderContext *rc,
- int rotation);
-void ev_render_context_set_scale (EvRenderContext *rc,
- gdouble scale);
-
-
-G_END_DECLS
-
-#endif /* !EV_RENDER_CONTEXT */
diff --git a/backend/ev-selection.c b/backend/ev-selection.c
deleted file mode 100644
index 2aa45a7..0000000
--- a/backend/ev-selection.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2005 Red Hat, Inc.
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include "config.h"
-
-#include "ev-selection.h"
-
-static void ev_selection_base_init (gpointer g_class);
-
-GType
-ev_selection_get_type (void)
-{
- static GType type = 0;
-
- if (G_UNLIKELY (type == 0))
- {
- const GTypeInfo our_info =
- {
- sizeof (EvSelectionIface),
- ev_selection_base_init,
- NULL,
- };
-
- type = g_type_register_static (G_TYPE_INTERFACE,
- "EvSelection",
- &our_info, (GTypeFlags)0);
- }
-
- return type;
-}
-
-static void
-ev_selection_base_init (gpointer g_class)
-{
- static gboolean initialized = FALSE;
-
- if (!initialized) {
- }
-}
-
-
-void
-ev_selection_render_selection (EvSelection *selection,
- EvRenderContext *rc,
- GdkPixbuf **pixbuf,
- EvRectangle *points,
- EvRectangle *old_points,
- GdkColor *text,
- GdkColor *base)
-{
- EvSelectionIface *iface = EV_SELECTION_GET_IFACE (selection);
-
- iface->render_selection (selection, rc,
- pixbuf,
- points, old_points,
- text, base);
-}
-
-GdkRegion *
-ev_selection_get_selection_region (EvSelection *selection,
- EvRenderContext *rc,
- EvRectangle *points)
-{
- EvSelectionIface *iface = EV_SELECTION_GET_IFACE (selection);
-
- return iface->get_selection_region (selection, rc, points);
-}
-
-GdkRegion *
-ev_selection_get_selection_map (EvSelection *selection,
- EvRenderContext *rc)
-{
- EvSelectionIface *iface = EV_SELECTION_GET_IFACE (selection);
-
- return iface->get_selection_map (selection, rc);
-}
diff --git a/backend/ev-selection.h b/backend/ev-selection.h
deleted file mode 100644
index d081604..0000000
--- a/backend/ev-selection.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
-/*
- * Copyright (C) 2000-2003 Marco Pesenti Gritti
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef EV_SELECTION_H
-#define EV_SELECTION_H
-
-#include <glib-object.h>
-#include <glib.h>
-#include <gdk/gdkpixbuf.h>
-#include <gdk/gdk.h>
-#include "ev-document.h"
-
-G_BEGIN_DECLS
-
-#define EV_TYPE_SELECTION (ev_selection_get_type ())
-#define EV_SELECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_SELECTION, EvSelection))
-#define EV_SELECTION_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_SELECTION, EvSelectionIface))
-#define EV_IS_SELECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_SELECTION))
-#define EV_IS_SELECTION_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_SELECTION))
-#define EV_SELECTION_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_SELECTION, EvSelectionIface))
-
-typedef struct _EvSelection EvSelection;
-typedef struct _EvSelectionIface EvSelectionIface;
-
-struct _EvSelectionIface
-{
- GTypeInterface base_iface;
-
- void (* render_selection) (EvSelection *selection,
- EvRenderContext *rc,
- GdkPixbuf **pixbuf,
- EvRectangle *points,
- EvRectangle *old_points,
- GdkColor *text,
- GdkColor *base);
- GdkRegion * (* get_selection_map) (EvSelection *selection,
- EvRenderContext *rc);
- GdkRegion * (* get_selection_region) (EvSelection *selection,
- EvRenderContext *rc,
- EvRectangle *points);
-};
-
-GType ev_selection_get_type (void);
-void ev_selection_render_selection (EvSelection *selection,
- EvRenderContext *rc,
- GdkPixbuf **pixbuf,
- EvRectangle *points,
- EvRectangle *old_points,
- GdkColor *text,
- GdkColor *base);
-GdkRegion *ev_selection_get_selection_map (EvSelection *selection,
- EvRenderContext *rc);
-GdkRegion *ev_selection_get_selection_region (EvSelection *selection,
- EvRenderContext *rc,
- EvRectangle *points);
-
-G_END_DECLS
-
-#endif
diff --git a/backend/impress/Makefile.am b/backend/impress/Makefile.am
new file mode 100644
index 0000000..526839b
--- /dev/null
+++ b/backend/impress/Makefile.am
@@ -0,0 +1,38 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ $(SHELL_CFLAGS) \
+ -DDATADIR=\""$(datadir)"\"
+
+noinst_LTLIBRARIES = libimpressdocument.la
+
+libimpressdocument_la_SOURCES = \
+ $(IMPOSTER_SOURCE_FILES) \
+ $(IMPOSTER_INCLUDE_FILES) \
+ impress-document.c \
+ impress-document.h
+
+IMPOSTER_SOURCE_FILES = \
+ document.c \
+ f_oasis.c \
+ f_oo13.c \
+ iksemel.c \
+ r_back.c \
+ r_draw.c \
+ render.c \
+ r_geometry.c \
+ r_gradient.c \
+ r_style.c \
+ r_text.c \
+ zip.c
+IMPOSTER_INCLUDE_FILES = \
+ common.h \
+ iksemel.h \
+ imposter.h \
+ internal.h \
+ zip.h
+IMPOSTER_RENDER_SOURCE_FILES = \
+ render.c
+IMPOSTER_RENDER_INCLUDE_FILES = \
+ render.h
+
diff --git a/backend/impress/common.h b/backend/impress/common.h
new file mode 100644
index 0000000..73e4ac1
--- /dev/null
+++ b/backend/impress/common.h
@@ -0,0 +1,40 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#ifndef COMMON_H
+#define COMMON_H 1
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#elif HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifndef errno
+extern int errno;
+#endif
+
+#include <iksemel.h>
+#include "imposter.h"
+
+
+#endif /* COMMON_H */
diff --git a/backend/impress/document.c b/backend/impress/document.c
new file mode 100644
index 0000000..8ecbfae
--- /dev/null
+++ b/backend/impress/document.c
@@ -0,0 +1,139 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+
+static iks *
+_imp_load_xml(ImpDoc *doc, const char *xmlfile)
+{
+ int e;
+ iks *x;
+
+ x = zip_load_xml (doc->zfile, xmlfile, &e);
+ return x;
+}
+
+ImpDoc *
+imp_open(const char *filename, int *err)
+{
+ ImpDoc *doc;
+ int e;
+
+ doc = calloc(1, sizeof(ImpDoc));
+ if (!doc) {
+ *err = IMP_NOMEM;
+ return NULL;
+ }
+
+ doc->stack = iks_stack_new(sizeof(ImpPage) * 32, 0);
+ if (!doc->stack) {
+ *err = IMP_NOMEM;
+ imp_close(doc);
+ return NULL;
+ }
+
+ doc->zfile = zip_open(filename, &e);
+ if (e) {
+ *err = IMP_NOTZIP;
+ imp_close(doc);
+ return NULL;
+ }
+
+ doc->content = _imp_load_xml(doc, "content.xml");
+ doc->styles = _imp_load_xml(doc, "styles.xml");
+ doc->meta = _imp_load_xml(doc, "meta.xml");
+
+ if (!doc->content || !doc->styles) {
+ *err = IMP_BADDOC;
+ imp_close(doc);
+ return NULL;
+ }
+
+ e = _imp_oo13_load(doc);
+ if (e && e != IMP_NOTIMP) {
+ *err = e;
+ imp_close(doc);
+ return NULL;
+ }
+
+ if (e == IMP_NOTIMP) {
+ e = _imp_oasis_load(doc);
+ if (e) {
+ *err = e;
+ imp_close(doc);
+ return NULL;
+ }
+ }
+
+ return doc;
+}
+
+int
+imp_nr_pages(ImpDoc *doc)
+{
+ return doc->nr_pages;
+}
+
+ImpPage *
+imp_get_page(ImpDoc *doc, int page_no)
+{
+ if (page_no == IMP_LAST_PAGE) {
+ return doc->last_page;
+ } else {
+ ImpPage *page;
+ if (page_no < 0 || page_no > doc->nr_pages) return NULL;
+ for (page = doc->pages; page_no; --page_no) {
+ page = page->next;
+ }
+ return page;
+ }
+}
+
+ImpPage *
+imp_next_page(ImpPage *page)
+{
+ return page->next;
+}
+
+ImpPage *
+imp_prev_page(ImpPage *page)
+{
+ return page->prev;
+}
+
+int
+imp_get_page_no(ImpPage *page)
+{
+ return page->nr;
+}
+
+const char *
+imp_get_page_name(ImpPage *page)
+{
+ return page->name;
+}
+
+void *
+imp_get_xml(ImpDoc *doc, const char *filename)
+{
+ if (strcmp(filename, "content.xml") == 0)
+ return doc->content;
+ else if (strcmp(filename, "styles.xml") == 0)
+ return doc->styles;
+ else if (strcmp(filename, "meta.xml") == 0)
+ return doc->meta;
+ else
+ return NULL;
+}
+
+void
+imp_close(ImpDoc *doc)
+{
+ if (doc->stack) iks_stack_delete(doc->stack);
+ if (doc->zfile) zip_close(doc->zfile);
+ free(doc);
+}
diff --git a/backend/impress/f_oasis.c b/backend/impress/f_oasis.c
new file mode 100644
index 0000000..193cef8
--- /dev/null
+++ b/backend/impress/f_oasis.c
@@ -0,0 +1,169 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+
+static void
+render_object(ImpRenderCtx *ctx, void *drw_data, iks *node)
+{
+ char *tag, *t;
+ ImpColor fg;
+
+ tag = iks_name(node);
+ if (strcmp(tag, "draw:g") == 0) {
+ iks *x;
+ for (x = iks_first_tag(node); x; x = iks_next_tag(x)) {
+ render_object(ctx, drw_data, x);
+ }
+ } else if (strcmp(tag, "draw:frame") == 0) {
+ iks *x;
+ for (x = iks_first_tag(node); x; x = iks_next_tag(x)) {
+ render_object(ctx, drw_data, x);
+ }
+ } else if (strcmp(tag, "draw:line") == 0) {
+ r_get_color(ctx, node, "svg:stroke-color", &fg);
+ ctx->drw->set_fg_color(drw_data, &fg);
+ ctx->drw->draw_line(drw_data,
+ r_get_x(ctx, node, "svg:x1"), r_get_y(ctx, node, "svg:y1"),
+ r_get_x(ctx, node, "svg:x2"), r_get_y(ctx, node, "svg:y2")
+ );
+ } else if (strcmp(tag, "draw:rect") == 0) {
+ int x, y, w, h, r = 0;
+ char *t;
+ x = r_get_x(ctx, node, "svg:x");
+ y = r_get_y(ctx, node, "svg:y");
+ w = r_get_x(ctx, node, "svg:width");
+ h = r_get_y(ctx, node, "svg:height");
+ t = r_get_style(ctx, node, "draw:corner-radius");
+ if (t) r = atof(t) * ctx->fact_x;
+ if (r_get_style(ctx, node, "draw:fill")) {
+ r_get_color(ctx, node, "draw:fill-color", &fg);
+ ctx->drw->set_fg_color(drw_data, &fg);
+ _imp_draw_rect(ctx, drw_data, 1, x, y, w, h, r);
+ }
+ r_get_color(ctx, node, "svg:stroke-color", &fg);
+ ctx->drw->set_fg_color(drw_data, &fg);
+ _imp_draw_rect(ctx, drw_data, 0, x, y, w, h, r);
+ r_text(ctx, drw_data, node);
+ } else if (strcmp(tag, "draw:ellipse") == 0 || strcmp(tag, "draw:circle") == 0) {
+ int sa, ea, fill = 0;
+ r_get_color(ctx, node, "svg:stroke-color", &fg);
+ sa = r_get_angle(node, "draw:start-angle", 0);
+ ea = r_get_angle(node, "draw:end-angle", 360);
+ if (ea > sa) ea = ea - sa; else ea = 360 + ea - sa;
+ t = r_get_style(ctx, node, "draw:fill");
+ if (t) fill = 1;
+ ctx->drw->set_fg_color(drw_data, &fg);
+ ctx->drw->draw_arc(drw_data,
+ fill,
+ r_get_x(ctx, node, "svg:x"), r_get_y(ctx, node, "svg:y"),
+ r_get_x(ctx, node, "svg:width"), r_get_y(ctx, node, "svg:height"),
+ sa, ea
+ );
+ } else if (strcmp(tag, "draw:polygon") == 0) {
+ // FIXME:
+ r_polygon(ctx, drw_data, node);
+ } else if (strcmp(tag, "draw:text-box") == 0) {
+ // FIXME:
+ r_text(ctx, drw_data, node);
+ } else if (strcmp(tag, "draw:image") == 0) {
+ char *name;
+
+ name = iks_find_attrib(node, "xlink:href");
+ if (!name) return;
+ if (name[0] == '#') ++name;
+
+ _imp_draw_image(ctx, drw_data,
+ name,
+ r_get_x(ctx, node, "svg:x"),
+ r_get_y(ctx, node, "svg:y"),
+ r_get_x(ctx, node, "svg:width"),
+ r_get_y(ctx, node, "svg:height")
+ );
+ } else {
+ printf("Unknown element: %s\n", tag);
+ }
+}
+
+static void
+render_page(ImpRenderCtx *ctx, void *drw_data)
+{
+ iks *x;
+ char *element;
+ int i;
+
+ i = _imp_fill_back(ctx, drw_data, ctx->page->page);
+ element = iks_find_attrib(ctx->page->page, "draw:master-page-name");
+ if (element) {
+ x = iks_find_with_attrib(
+ iks_find(ctx->page->doc->styles, "office:master-styles"),
+ "style:master-page", "style:name", element
+ );
+ if (x) {
+ if (i == 0) _imp_fill_back(ctx, drw_data, x);
+ for (x = iks_first_tag(x); x; x = iks_next_tag(x)) {
+ if (iks_find_attrib(x, "presentation:class"))
+ continue;
+ render_object(ctx, drw_data, x);
+ }
+ }
+ }
+ for (x = iks_first_tag(ctx->page->page); x; x = iks_next_tag(x)) {
+ render_object(ctx, drw_data, x);
+ }
+}
+
+static void
+get_geometry(ImpRenderCtx *ctx)
+{
+ char *tmp;
+ iks *x, *y;
+
+ tmp = iks_find_attrib(ctx->page->page, "draw:master-page-name");
+ x = iks_find(ctx->page->doc->styles, "office:master-styles");
+ y = iks_find_with_attrib(x, "style:master-page", "style:name", tmp);
+ x = iks_find(ctx->page->doc->styles, "office:automatic-styles");
+ y = iks_find_with_attrib(x, "style:page-layout", "style:name",
+ iks_find_attrib(y, "style:page-layout-name"));
+ ctx->cm_w = atof(iks_find_attrib(iks_find(y, "style:page-layout-properties"), "fo:page-width"));
+ ctx->cm_h = atof(iks_find_attrib(iks_find(y, "style:page-layout-properties"), "fo:page-height"));
+}
+
+int
+_imp_oasis_load(ImpDoc *doc)
+{
+ ImpPage *page;
+ iks *x, *pres;
+ int i;
+
+ pres = iks_find(iks_find(doc->content, "office:body"), "office:presentation");
+ if (!pres) return IMP_NOTIMP;
+
+ x = iks_find(pres, "draw:page");
+ if (!x) return IMP_NOTIMP;
+ i = 0;
+ for (; x; x = iks_next_tag(x)) {
+ if (strcmp(iks_name(x), "draw:page") == 0) {
+ page = iks_stack_alloc(doc->stack, sizeof(ImpPage));
+ if (!page) return IMP_NOMEM;
+ memset(page, 0, sizeof(ImpPage));
+ page->page = x;
+ page->nr = ++i;
+ page->name = iks_find_attrib(x, "draw:name");
+ page->doc = doc;
+ if (!doc->pages) doc->pages = page;
+ page->prev = doc->last_page;
+ if (doc->last_page) doc->last_page->next = page;
+ doc->last_page = page;
+ }
+ }
+ doc->nr_pages = i;
+ doc->get_geometry = get_geometry;
+ doc->render_page = render_page;
+
+ return 0;
+}
diff --git a/backend/impress/f_oo13.c b/backend/impress/f_oo13.c
new file mode 100644
index 0000000..ce84132
--- /dev/null
+++ b/backend/impress/f_oo13.c
@@ -0,0 +1,180 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+
+// { "draw:text-box", r_text },
+// { "draw:connector", r_line },
+// { "draw:polyline", r_polyline },
+// { "draw:polygon", r_polygon },
+// { "draw:path", r_path },
+
+static void
+render_object(ImpRenderCtx *ctx, void *drw_data, iks *node)
+{
+ char *tag, *t;
+ ImpColor fg;
+
+ tag = iks_name(node);
+ if (strcmp(tag, "draw:g") == 0) {
+ iks *x;
+ for (x = iks_first_tag(node); x; x = iks_next_tag(x)) {
+ render_object(ctx, drw_data, x);
+ }
+ } else if (strcmp(tag, "draw:line") == 0) {
+ int x1, y1, x2, y2;
+ r_get_color(ctx, node, "svg:stroke-color", &fg);
+ ctx->drw->set_fg_color(drw_data, &fg);
+ x1 = r_get_x(ctx, node, "svg:x1");
+ y1 = r_get_y(ctx, node, "svg:y1");
+ x2 = r_get_x(ctx, node, "svg:x2");
+ y2 = r_get_y(ctx, node, "svg:y2");
+ ctx->drw->draw_line(drw_data, x1, y1, x2, y2);
+ if (r_get_style(ctx, node, "draw:marker-start")) {
+ _imp_draw_line_end(ctx, drw_data, 0, 0, x2, y2, x1, y1);
+ }
+ if (r_get_style(ctx, node, "draw:marker-end")) {
+ _imp_draw_line_end(ctx, drw_data, 0, 0, x1, y1, x2, y2);
+ }
+ } else if (strcmp(tag, "draw:rect") == 0) {
+ int x, y, w, h, r = 0;
+ char *t;
+ x = r_get_x(ctx, node, "svg:x");
+ y = r_get_y(ctx, node, "svg:y");
+ w = r_get_x(ctx, node, "svg:width");
+ h = r_get_y(ctx, node, "svg:height");
+ t = r_get_style(ctx, node, "draw:corner-radius");
+ if (t) r = atof(t) * ctx->fact_x;
+ t = r_get_style(ctx, node, "draw:fill");
+ if (t && strcmp(t, "none") != 0) {
+ r_get_color(ctx, node, "draw:fill-color", &fg);
+ ctx->drw->set_fg_color(drw_data, &fg);
+ _imp_draw_rect(ctx, drw_data, 1, x, y, w, h, r);
+ }
+ r_get_color(ctx, node, "svg:stroke-color", &fg);
+ ctx->drw->set_fg_color(drw_data, &fg);
+ _imp_draw_rect(ctx, drw_data, 0, x, y, w, h, r);
+ r_text(ctx, drw_data, node);
+ } else if (strcmp(tag, "draw:ellipse") == 0 || strcmp(tag, "draw:circle") == 0) {
+ int sa, ea, fill = 0;
+ r_get_color(ctx, node, "svg:stroke-color", &fg);
+ sa = r_get_angle(node, "draw:start-angle", 0);
+ ea = r_get_angle(node, "draw:end-angle", 360);
+ if (ea > sa) ea = ea - sa; else ea = 360 + ea - sa;
+ t = r_get_style(ctx, node, "draw:fill");
+ if (t) fill = 1;
+ ctx->drw->set_fg_color(drw_data, &fg);
+ ctx->drw->draw_arc(drw_data,
+ fill,
+ r_get_x(ctx, node, "svg:x"), r_get_y(ctx, node, "svg:y"),
+ r_get_x(ctx, node, "svg:width"), r_get_y(ctx, node, "svg:height"),
+ sa, ea
+ );
+ } else if (strcmp(tag, "draw:polygon") == 0) {
+ // FIXME:
+ r_polygon(ctx, drw_data, node);
+ } else if (strcmp(tag, "draw:text-box") == 0) {
+ // FIXME:
+ r_text(ctx, drw_data, node);
+ } else if (strcmp(tag, "draw:image") == 0) {
+ char *name;
+
+ name = iks_find_attrib(node, "xlink:href");
+ if (!name) return;
+ if (name[0] == '#') ++name;
+
+ _imp_draw_image(ctx, drw_data,
+ name,
+ r_get_x(ctx, node, "svg:x"),
+ r_get_y(ctx, node, "svg:y"),
+ r_get_x(ctx, node, "svg:width"),
+ r_get_y(ctx, node, "svg:height")
+ );
+ } else {
+ printf("Unknown element: %s\n", tag);
+ }
+}
+
+static void
+render_page(ImpRenderCtx *ctx, void *drw_data)
+{
+ iks *x;
+ char *element;
+ int i;
+
+ i = _imp_fill_back(ctx, drw_data, ctx->page->page);
+ element = iks_find_attrib(ctx->page->page, "draw:master-page-name");
+ if (element) {
+ x = iks_find_with_attrib(
+ iks_find(ctx->page->doc->styles, "office:master-styles"),
+ "style:master-page", "style:name", element
+ );
+ if (x) {
+ if (i == 0) _imp_fill_back(ctx, drw_data, x);
+ for (x = iks_first_tag(x); x; x = iks_next_tag(x)) {
+ if (iks_find_attrib(x, "presentation:class"))
+ continue;
+ render_object(ctx, drw_data, x);
+ }
+ }
+ }
+ for (x = iks_first_tag(ctx->page->page); x; x = iks_next_tag(x)) {
+ render_object(ctx, drw_data, x);
+ }
+}
+
+static void
+get_geometry(ImpRenderCtx *ctx)
+{
+ char *tmp;
+ iks *x, *y;
+
+ tmp = iks_find_attrib(ctx->page->page, "draw:master-page-name");
+ x = iks_find(ctx->page->doc->styles, "office:master-styles");
+ y = iks_find_with_attrib(x, "style:master-page", "style:name", tmp);
+ x = iks_find(ctx->page->doc->styles, "office:automatic-styles");
+ y = iks_find_with_attrib(x, "style:page-master", "style:name",
+ iks_find_attrib(y, "style:page-master-name"));
+ ctx->cm_w = atof(iks_find_attrib(iks_find(y, "style:properties"), "fo:page-width"));
+ ctx->cm_h = atof(iks_find_attrib(iks_find(y, "style:properties"), "fo:page-height"));
+}
+
+int
+_imp_oo13_load(ImpDoc *doc)
+{
+ ImpPage *page;
+ char *class;
+ iks *x;
+ int i;
+
+ class = iks_find_attrib(doc->content, "office:class");
+ if (iks_strcmp(class, "presentation") != 0) return IMP_NOTIMP;
+
+ x = iks_find(iks_find(doc->content, "office:body"), "draw:page");
+ if (!x) return IMP_NOTIMP;
+ i = 0;
+ for (; x; x = iks_next_tag(x)) {
+ if (strcmp(iks_name(x), "draw:page") == 0) {
+ page = iks_stack_alloc(doc->stack, sizeof(ImpPage));
+ if (!page) return IMP_NOMEM;
+ memset(page, 0, sizeof(ImpPage));
+ page->page = x;
+ page->nr = ++i;
+ page->name = iks_find_attrib(x, "draw:name");
+ page->doc = doc;
+ if (!doc->pages) doc->pages = page;
+ page->prev = doc->last_page;
+ if (doc->last_page) doc->last_page->next = page;
+ doc->last_page = page;
+ }
+ }
+ doc->nr_pages = i;
+ doc->get_geometry = get_geometry;
+ doc->render_page = render_page;
+
+ return 0;
+}
diff --git a/backend/impress/iksemel.c b/backend/impress/iksemel.c
new file mode 100644
index 0000000..6d24d43
--- /dev/null
+++ b/backend/impress/iksemel.c
@@ -0,0 +1,1881 @@
+/* iksemel (XML parser for Jabber)
+** Copyright (C) 2000-2003 Gurer Ozen <madcat@e-kolay.net>
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU Lesser General Public License.
+*/
+
+/* minimum sax buffer size */
+#define SAX_BUFFER_MIN_SIZE 128
+
+/* sax parser structure plus extra data of dom parser */
+#define DEFAULT_DOM_CHUNK_SIZE 256
+
+/* sax parser structure plus extra data of stream parser */
+#define DEFAULT_STREAM_CHUNK_SIZE 256
+
+/* iks structure, its data, child iks structures, for stream parsing */
+#define DEFAULT_IKS_CHUNK_SIZE 1024
+
+/* iks structure, its data, child iks structures, for file parsing */
+#define DEFAULT_DOM_IKS_CHUNK_SIZE 2048
+
+/* rule structure and from/to/id/ns strings */
+#define DEFAULT_RULE_CHUNK_SIZE 128
+
+/* file is read by blocks with this size */
+#define FILE_IO_BUF_SIZE 4096
+
+/* network receive buffer */
+#define NET_IO_BUF_SIZE 4096
+/* iksemel (XML parser for Jabber)
+** Copyright (C) 2000-2003 Gurer Ozen <madcat@e-kolay.net>
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU Lesser General Public License.
+*/
+
+#include <errno.h>
+
+#include "common.h"
+#include "iksemel.h"
+
+/***** malloc wrapper *****/
+
+static void *(*my_malloc_func)(size_t size);
+static void (*my_free_func)(void *ptr);
+
+void *
+iks_malloc (size_t size)
+{
+ if (my_malloc_func)
+ return my_malloc_func (size);
+ else
+ return malloc (size);
+}
+
+void
+iks_free (void *ptr)
+{
+ if (my_free_func)
+ my_free_func (ptr);
+ else
+ free (ptr);
+}
+
+void
+iks_set_mem_funcs (void *(*malloc_func)(size_t size), void (*free_func)(void *ptr))
+{
+ my_malloc_func = malloc_func;
+ my_free_func = free_func;
+}
+
+/***** NULL-safe Functions *****/
+
+char *
+iks_strdup (const char *src)
+{
+ if (src) return strdup(src);
+ return NULL;
+}
+
+char *
+iks_strcat (char *dest, const char *src)
+{
+ size_t len;
+
+ if (!src) return dest;
+
+ len = strlen (src);
+ memcpy (dest, src, len);
+ dest[len] = '\0';
+ return dest + len;
+}
+
+int
+iks_strcmp (const char *a, const char *b)
+{
+ if (!a || !b) return -1;
+ return strcmp (a, b);
+}
+
+int
+iks_strcasecmp (const char *a, const char *b)
+{
+ if (!a || !b) return -1;
+ return strcasecmp (a, b);
+}
+
+int
+iks_strncmp (const char *a, const char *b, size_t n)
+{
+ if (!a || !b) return -1;
+ return strncmp (a, b, n);
+}
+
+int
+iks_strncasecmp (const char *a, const char *b, size_t n)
+{
+ if (!a || !b) return -1;
+ return strncasecmp (a, b, n);
+}
+
+size_t
+iks_strlen (const char *src)
+{
+ if (!src) return 0;
+ return strlen (src);
+}
+
+/***** XML Escaping *****/
+
+char *
+iks_escape (ikstack *s, char *src, size_t len)
+{
+ char *ret;
+ int i, j, nlen;
+
+ if (!src || !s) return NULL;
+ if (len == -1) len = strlen (src);
+
+ nlen = len;
+ for (i=0; i<len; i++) {
+ switch (src[i]) {
+ case '&': nlen += 4; break;
+ case '<': nlen += 3; break;
+ case '>': nlen += 3; break;
+ case '\'': nlen += 5; break;
+ case '"': nlen += 5; break;
+ }
+ }
+ if (len == nlen) return src;
+
+ ret = iks_stack_alloc (s, nlen + 1);
+ if (!ret) return NULL;
+
+ for (i=j=0; i<len; i++) {
+ switch (src[i]) {
+ case '&': memcpy (&ret[j], "&amp;", 5); j += 5; break;
+ case '\'': memcpy (&ret[j], "&apos;", 6); j += 6; break;
+ case '"': memcpy (&ret[j], "&quot;", 6); j += 6; break;
+ case '<': memcpy (&ret[j], "&lt;", 4); j += 4; break;
+ case '>': memcpy (&ret[j], "&gt;", 4); j += 4; break;
+ default: ret[j++] = src[i];
+ }
+ }
+ ret[j] = '\0';
+
+ return ret;
+}
+
+char *
+iks_unescape (ikstack *s, char *src, size_t len)
+{
+ int i,j;
+ char *ret;
+
+ if (!s || !src) return NULL;
+ if (!strchr (src, '&')) return src;
+ if (len == -1) len = strlen (src);
+
+ ret = iks_stack_alloc (s, len + 1);
+ if (!ret) return NULL;
+
+ for (i=j=0; i<len; i++) {
+ if (src[i] == '&') {
+ i++;
+ if (strncmp (&src[i], "amp;", 4) == 0) {
+ ret[j] = '&';
+ i += 3;
+ } else if (strncmp (&src[i], "quot;", 5) == 0) {
+ ret[j] = '"';
+ i += 4;
+ } else if (strncmp (&src[i], "apos;", 5) == 0) {
+ ret[j] = '\'';
+ i += 4;
+ } else if (strncmp (&src[i], "lt;", 3) == 0) {
+ ret[j] = '<';
+ i += 2;
+ } else if (strncmp (&src[i], "gt;", 3) == 0) {
+ ret[j] = '>';
+ i += 2;
+ } else {
+ ret[j] = src[--i];
+ }
+ } else {
+ ret[j] = src[i];
+ }
+ j++;
+ }
+ ret[j] = '\0';
+
+ return ret;
+}
+/* iksemel (XML parser for Jabber)
+** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU Lesser General Public License.
+*/
+
+#include "common.h"
+#include "iksemel.h"
+
+struct align_test { char a; double b; };
+#define DEFAULT_ALIGNMENT ((size_t) ((char *) &((struct align_test *) 0)->b - (char *) 0))
+#define ALIGN_MASK ( DEFAULT_ALIGNMENT - 1 )
+#define MIN_CHUNK_SIZE ( DEFAULT_ALIGNMENT * 8 )
+#define MIN_ALLOC_SIZE DEFAULT_ALIGNMENT
+#define ALIGN(x) ( (x) + (DEFAULT_ALIGNMENT - ( (x) & ALIGN_MASK)) )
+
+typedef struct ikschunk_struct {
+ struct ikschunk_struct *next;
+ size_t size;
+ size_t used;
+ size_t last;
+ char data[4];
+} ikschunk;
+
+struct ikstack_struct {
+ size_t allocated;
+ ikschunk *meta;
+ ikschunk *data;
+};
+
+static ikschunk *
+find_space (ikstack *s, ikschunk *c, size_t size)
+{
+ /* FIXME: dont use *2 after over allocated chunks */
+ while (1) {
+ if (c->size - c->used >= size) return c;
+ if (!c->next) {
+ if ((c->size * 2) > size) size = c->size * 2;
+ c->next = iks_malloc (sizeof (ikschunk) + size);
+ if (!c->next) return NULL;
+ s->allocated += sizeof (ikschunk) + size;
+ c = c->next;
+ c->next = NULL;
+ c->size = size;
+ c->used = 0;
+ c->last = (size_t) -1;
+ return c;
+ }
+ c = c->next;
+ }
+ return NULL;
+}
+
+ikstack *
+iks_stack_new (size_t meta_chunk, size_t data_chunk)
+{
+ ikstack *s;
+ size_t len;
+
+ if (meta_chunk < MIN_CHUNK_SIZE) meta_chunk = MIN_CHUNK_SIZE;
+ if (meta_chunk & ALIGN_MASK) meta_chunk = ALIGN (meta_chunk);
+ if (data_chunk < MIN_CHUNK_SIZE) data_chunk = MIN_CHUNK_SIZE;
+ if (data_chunk & ALIGN_MASK) data_chunk = ALIGN (data_chunk);
+
+ len = sizeof (ikstack) + meta_chunk + data_chunk + (sizeof (ikschunk) * 2);
+ s = iks_malloc (len);
+ if (!s) return NULL;
+ s->allocated = len;
+ s->meta = (ikschunk *) ((char *) s + sizeof (ikstack));
+ s->meta->next = NULL;
+ s->meta->size = meta_chunk;
+ s->meta->used = 0;
+ s->meta->last = (size_t) -1;
+ s->data = (ikschunk *) ((char *) s + sizeof (ikstack) + sizeof (ikschunk) + meta_chunk);
+ s->data->next = NULL;
+ s->data->size = data_chunk;
+ s->data->used = 0;
+ s->data->last = (size_t) -1;
+ return s;
+}
+
+void *
+iks_stack_alloc (ikstack *s, size_t size)
+{
+ ikschunk *c;
+ void *mem;
+
+ if (size < MIN_ALLOC_SIZE) size = MIN_ALLOC_SIZE;
+ if (size & ALIGN_MASK) size = ALIGN (size);
+
+ c = find_space (s, s->meta, size);
+ if (!c) return NULL;
+ mem = c->data + c->used;
+ c->used += size;
+ return mem;
+}
+
+char *
+iks_stack_strdup (ikstack *s, const char *src, size_t len)
+{
+ ikschunk *c;
+ char *dest;
+
+ if (!src) return NULL;
+ if (0 == len) len = strlen (src);
+
+ c = find_space (s, s->data, len + 1);
+ if (!c) return NULL;
+ dest = c->data + c->used;
+ c->last = c->used;
+ c->used += len + 1;
+ memcpy (dest, src, len);
+ dest[len] = '\0';
+ return dest;
+}
+
+char *
+iks_stack_strcat (ikstack *s, char *old, size_t old_len, const char *src, size_t src_len)
+{
+ char *ret;
+ ikschunk *c;
+
+ if (!old) {
+ return iks_stack_strdup (s, src, src_len);
+ }
+ if (0 == old_len) old_len = strlen (old);
+ if (0 == src_len) src_len = strlen (src);
+
+ for (c = s->data; c; c = c->next) {
+ if (c->data + c->last == old) break;
+ }
+ if (!c) {
+ c = find_space (s, s->data, old_len + src_len + 1);
+ if (!c) return NULL;
+ ret = c->data + c->used;
+ c->last = c->used;
+ c->used += old_len + src_len + 1;
+ memcpy (ret, old, old_len);
+ memcpy (ret + old_len, src, src_len);
+ ret[old_len + src_len] = '\0';
+ return ret;
+ }
+
+ if (c->size - c->used > src_len) {
+ ret = c->data + c->last;
+ memcpy (ret + old_len, src, src_len);
+ c->used += src_len;
+ ret[old_len + src_len] = '\0';
+ } else {
+ /* FIXME: decrease c->used before moving string to new place */
+ c = find_space (s, s->data, old_len + src_len + 1);
+ if (!c) return NULL;
+ c->last = c->used;
+ ret = c->data + c->used;
+ memcpy (ret, old, old_len);
+ c->used += old_len;
+ memcpy (c->data + c->used, src, src_len);
+ c->used += src_len;
+ c->data[c->used] = '\0';
+ c->used++;
+ }
+ return ret;
+}
+
+void
+iks_stack_stat (ikstack *s, size_t *allocated, size_t *used)
+{
+ ikschunk *c;
+
+ if (allocated) {
+ *allocated = s->allocated;
+ }
+ if (used) {
+ *used = 0;
+ for (c = s->meta; c; c = c->next) {
+ (*used) += c->used;
+ }
+ for (c = s->data; c; c = c->next) {
+ (*used) += c->used;
+ }
+ }
+}
+
+void
+iks_stack_delete (ikstack *s)
+{
+ ikschunk *c, *tmp;
+
+ c = s->meta->next;
+ while (c) {
+ tmp = c->next;
+ iks_free (c);
+ c = tmp;
+ }
+ c = s->data->next;
+ while (c) {
+ tmp = c->next;
+ iks_free (c);
+ c = tmp;
+ }
+ iks_free (s);
+}
+/* iksemel (XML parser for Jabber)
+** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU Lesser General Public License.
+*/
+
+#include "common.h"
+#include "iksemel.h"
+
+enum cons_e {
+ C_CDATA = 0,
+ C_TAG_START,
+ C_TAG,
+ C_TAG_END,
+ C_ATTRIBUTE,
+ C_ATTRIBUTE_1,
+ C_ATTRIBUTE_2,
+ C_VALUE,
+ C_VALUE_APOS,
+ C_VALUE_QUOT,
+ C_WHITESPACE,
+ C_ENTITY,
+ C_COMMENT,
+ C_COMMENT_1,
+ C_COMMENT_2,
+ C_COMMENT_3,
+ C_MARKUP,
+ C_MARKUP_1,
+ C_SECT,
+ C_SECT_CDATA,
+ C_SECT_CDATA_1,
+ C_SECT_CDATA_2,
+ C_SECT_CDATA_3,
+ C_SECT_CDATA_4,
+ C_SECT_CDATA_C,
+ C_SECT_CDATA_E,
+ C_SECT_CDATA_E2,
+ C_PI
+};
+
+/* if you add a variable here, dont forget changing iks_parser_reset */
+struct iksparser_struct {
+ ikstack *s;
+ void *user_data;
+ iksTagHook *tagHook;
+ iksCDataHook *cdataHook;
+ iksDeleteHook *deleteHook;
+ /* parser context */
+ char *stack;
+ size_t stack_pos;
+ size_t stack_max;
+
+ enum cons_e context;
+ enum cons_e oldcontext;
+
+ char *tag_name;
+ enum ikstagtype tagtype;
+
+ unsigned int attmax;
+ unsigned int attcur;
+ int attflag;
+ char **atts;
+ int valflag;
+
+ unsigned int entpos;
+ char entity[8];
+
+ unsigned long nr_bytes;
+ unsigned long nr_lines;
+
+ int uni_max;
+ int uni_len;
+};
+
+iksparser *
+iks_sax_new (void *user_data, iksTagHook *tagHook, iksCDataHook *cdataHook)
+{
+ iksparser *prs;
+
+ prs = iks_malloc (sizeof (iksparser));
+ if (NULL == prs) return NULL;
+ memset (prs, 0, sizeof (iksparser));
+ prs->user_data = user_data;
+ prs->tagHook = tagHook;
+ prs->cdataHook = cdataHook;
+ return prs;
+}
+
+iksparser *
+iks_sax_extend (ikstack *s, void *user_data, iksTagHook *tagHook, iksCDataHook *cdataHook, iksDeleteHook *deleteHook)
+{
+ iksparser *prs;
+
+ prs = iks_stack_alloc (s, sizeof (iksparser));
+ if (NULL == prs) return NULL;
+ memset (prs, 0, sizeof (iksparser));
+ prs->s = s;
+ prs->user_data = user_data;
+ prs->tagHook = tagHook;
+ prs->cdataHook = cdataHook;
+ prs->deleteHook = deleteHook;
+ return prs;
+}
+
+ikstack *
+iks_parser_stack (iksparser *prs)
+{
+ return prs->s;
+}
+
+void *
+iks_user_data (iksparser *prs)
+{
+ return prs->user_data;
+}
+
+unsigned long
+iks_nr_bytes (iksparser *prs)
+{
+ return prs->nr_bytes;
+}
+
+unsigned long
+iks_nr_lines (iksparser *prs)
+{
+ return prs->nr_lines;
+}
+
+#define IS_WHITESPACE(x) ' ' == (x) || '\t' == (x) || '\r' == (x) || '\n' == (x)
+#define NOT_WHITESPACE(x) ' ' != (x) && '\t' != (x) && '\r' != (x) && '\n' != (x)
+
+static int
+stack_init (iksparser *prs)
+{
+ prs->stack = iks_malloc (128);
+ if (!prs->stack) return 0;
+ prs->stack_max = 128;
+ prs->stack_pos = 0;
+ return 1;
+}
+
+static int
+stack_expand (iksparser *prs, int len)
+{
+ size_t need;
+ off_t diff;
+ char *tmp;
+ need = len - (prs->stack_max - prs->stack_pos);
+ if (need < prs->stack_max) {
+ need = prs->stack_max * 2;
+ } else {
+ need = prs->stack_max + (need * 1.2);
+ }
+ tmp = iks_malloc (need);
+ if (!tmp) return 0;
+ diff = tmp - prs->stack;
+ memcpy (tmp, prs->stack, prs->stack_max);
+ iks_free (prs->stack);
+ prs->stack = tmp;
+ prs->stack_max = need;
+ prs->tag_name += diff;
+ if (prs->attflag != 0) {
+ int i = 0;
+ while (i < (prs->attmax * 2)) {
+ if (prs->atts[i]) prs->atts[i] += diff;
+ i++;
+ }
+ }
+ return 1;
+}
+
+#define STACK_INIT \
+ if (NULL == prs->stack && 0 == stack_init (prs)) return IKS_NOMEM
+
+#define STACK_PUSH_START (prs->stack + prs->stack_pos)
+
+#define STACK_PUSH(buf,len) \
+{ \
+ char *sbuf = (buf); \
+ size_t slen = (len); \
+ if (prs->stack_max - prs->stack_pos <= slen) { \
+ if (0 == stack_expand (prs, slen)) return IKS_NOMEM; \
+ } \
+ memcpy (prs->stack + prs->stack_pos, sbuf, slen); \
+ prs->stack_pos += slen; \
+}
+
+#define STACK_PUSH_END \
+{ \
+ if (prs->stack_pos >= prs->stack_max) { \
+ if (0 == stack_expand (prs, 1)) return IKS_NOMEM; \
+ } \
+ prs->stack[prs->stack_pos] = '\0'; \
+ prs->stack_pos++; \
+}
+
+static enum ikserror
+sax_core (iksparser *prs, char *buf, int len)
+{
+ enum ikserror err;
+ int pos = 0, old = 0, re, stack_old = -1;
+ unsigned char c;
+
+ while (pos < len) {
+ re = 0;
+ c = buf[pos];
+ if (0 == c || 0xFE == c || 0xFF == c) return IKS_BADXML;
+ if (prs->uni_max) {
+ if ((c & 0xC0) != 0x80) return IKS_BADXML;
+ prs->uni_len++;
+ if (prs->uni_len == prs->uni_max) prs->uni_max = 0;
+ goto cont;
+ } else {
+ if (c & 0x80) {
+ unsigned char mask;
+ if ((c & 0x60) == 0x40) {
+ prs->uni_max = 2;
+ mask = 0x1F;
+ } else if ((c & 0x70) == 0x60) {
+ prs->uni_max = 3;
+ mask = 0x0F;
+ } else if ((c & 0x78) == 0x70) {
+ prs->uni_max = 4;
+ mask = 0x07;
+ } else if ((c & 0x7C) == 0x78) {
+ prs->uni_max = 5;
+ mask = 0x03;
+ } else if ((c & 0x7E) == 0x7C) {
+ prs->uni_max = 6;
+ mask = 0x01;
+ } else {
+ return IKS_BADXML;
+ }
+ if ((c & mask) == 0) return IKS_BADXML;
+ prs->uni_len = 1;
+ if (stack_old == -1) stack_old = pos;
+ goto cont;
+ }
+ }
+
+ switch (prs->context) {
+ case C_CDATA:
+ if ('&' == c) {
+ if (old < pos && prs->cdataHook) {
+ err = prs->cdataHook (prs->user_data, &buf[old], pos - old);
+ if (IKS_OK != err) return err;
+ }
+ prs->context = C_ENTITY;
+ prs->entpos = 0;
+ break;
+ }
+ if ('<' == c) {
+ if (old < pos && prs->cdataHook) {
+ err = prs->cdataHook (prs->user_data, &buf[old], pos - old);
+ if (IKS_OK != err) return err;
+ }
+ STACK_INIT;
+ prs->tag_name = STACK_PUSH_START;
+ if (!prs->tag_name) return IKS_NOMEM;
+ prs->context = C_TAG_START;
+ }
+ break;
+
+ case C_TAG_START:
+ prs->context = C_TAG;
+ if ('/' == c) {
+ prs->tagtype = IKS_CLOSE;
+ break;
+ }
+ if ('?' == c) {
+ prs->context = C_PI;
+ break;
+ }
+ if ('!' == c) {
+ prs->context = C_MARKUP;
+ break;
+ }
+ prs->tagtype = IKS_OPEN;
+ stack_old = pos;
+ break;
+
+ case C_TAG:
+ if (IS_WHITESPACE(c)) {
+ if (IKS_CLOSE == prs->tagtype)
+ prs->oldcontext = C_TAG_END;
+ else
+ prs->oldcontext = C_ATTRIBUTE;
+ prs->context = C_WHITESPACE;
+ if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
+ stack_old = -1;
+ STACK_PUSH_END;
+ break;
+ }
+ if ('/' == c) {
+ if (IKS_CLOSE == prs->tagtype) return IKS_BADXML;
+ prs->tagtype = IKS_SINGLE;
+ prs->context = C_TAG_END;
+ if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
+ stack_old = -1;
+ STACK_PUSH_END;
+ break;
+ }
+ if ('>' == c) {
+ prs->context = C_TAG_END;
+ if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
+ stack_old = -1;
+ STACK_PUSH_END;
+ re = 1;
+ }
+ if (stack_old == -1) stack_old = pos;
+ break;
+
+ case C_TAG_END:
+ if (c != '>') return IKS_BADXML;
+ if (prs->tagHook) {
+ char **tmp;
+ if (prs->attcur == 0) tmp = NULL; else tmp = prs->atts;
+ err = prs->tagHook (prs->user_data, prs->tag_name, tmp, prs->tagtype);
+ if (IKS_OK != err) return err;
+ }
+ prs->stack_pos = 0;
+ stack_old = -1;
+ prs->attcur = 0;
+ prs->attflag = 0;
+ prs->context = C_CDATA;
+ old = pos + 1;
+ break;
+
+ case C_ATTRIBUTE:
+ if ('/' == c) {
+ prs->tagtype = IKS_SINGLE;
+ prs->context = C_TAG_END;
+ break;
+ }
+ if ('>' == c) {
+ prs->context = C_TAG_END;
+ re = 1;
+ break;
+ }
+ if (!prs->atts) {
+ prs->attmax = 12;
+ prs->atts = iks_malloc (sizeof(char *) * 2 * 12);
+ if (!prs->atts) return IKS_NOMEM;
+ memset (prs->atts, 0, sizeof(char *) * 2 * 12);
+ prs->attcur = 0;
+ } else {
+ if (prs->attcur >= (prs->attmax * 2)) {
+ void *tmp;
+ prs->attmax += 12;
+ tmp = iks_malloc (sizeof(char *) * 2 * prs->attmax);
+ if (!tmp) return IKS_NOMEM;
+ memset (tmp, 0, sizeof(char *) * 2 * prs->attmax);
+ memcpy (tmp, prs->atts, sizeof(char *) * prs->attcur);
+ free (prs->atts);
+ prs->atts = tmp;
+ }
+ }
+ prs->attflag = 1;
+ prs->atts[prs->attcur] = STACK_PUSH_START;
+ stack_old = pos;
+ prs->context = C_ATTRIBUTE_1;
+ break;
+
+ case C_ATTRIBUTE_1:
+ if ('=' == c) {
+ if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
+ stack_old = -1;
+ STACK_PUSH_END;
+ prs->context = C_VALUE;
+ break;
+ }
+ if (stack_old == -1) stack_old = pos;
+ break;
+
+ case C_ATTRIBUTE_2:
+ if ('/' == c) {
+ prs->tagtype = IKS_SINGLE;
+ prs->atts[prs->attcur] = NULL;
+ prs->context = C_TAG_END;
+ break;
+ }
+ if ('>' == c) {
+ prs->atts[prs->attcur] = NULL;
+ prs->context = C_TAG_END;
+ re = 1;
+ break;
+ }
+ prs->context = C_ATTRIBUTE;
+ re = 1;
+ break;
+
+ case C_VALUE:
+ prs->atts[prs->attcur + 1] = STACK_PUSH_START;
+ if ('\'' == c) {
+ prs->context = C_VALUE_APOS;
+ break;
+ }
+ if ('"' == c) {
+ prs->context = C_VALUE_QUOT;
+ break;
+ }
+ return IKS_BADXML;
+
+ case C_VALUE_APOS:
+ if ('\'' == c) {
+ if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
+ stack_old = -1;
+ STACK_PUSH_END;
+ prs->oldcontext = C_ATTRIBUTE_2;
+ prs->context = C_WHITESPACE;
+ prs->attcur += 2;
+ }
+ if (stack_old == -1) stack_old = pos;
+ break;
+
+ case C_VALUE_QUOT:
+ if ('"' == c) {
+ if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
+ stack_old = -1;
+ STACK_PUSH_END;
+ prs->oldcontext = C_ATTRIBUTE_2;
+ prs->context = C_WHITESPACE;
+ prs->attcur += 2;
+ }
+ if (stack_old == -1) stack_old = pos;
+ break;
+
+ case C_WHITESPACE:
+ if (NOT_WHITESPACE(c)) {
+ prs->context = prs->oldcontext;
+ re = 1;
+ }
+ break;
+
+ case C_ENTITY:
+ if (';' == c) {
+ char hede[2];
+ char t = '?';
+ prs->entity[prs->entpos] = '\0';
+ if (strcmp(prs->entity, "amp") == 0)
+ t = '&';
+ else if (strcmp(prs->entity, "quot") == 0)
+ t = '"';
+ else if (strcmp(prs->entity, "apos") == 0)
+ t = '\'';
+ else if (strcmp(prs->entity, "lt") == 0)
+ t = '<';
+ else if (strcmp(prs->entity, "gt") == 0)
+ t = '>';
+ old = pos + 1;
+ hede[0] = t;
+ if (prs->cdataHook) {
+ err = prs->cdataHook (prs->user_data, &hede[0], 1);
+ if (IKS_OK != err) return err;
+ }
+ prs->context = C_CDATA;
+ } else {
+ prs->entity[prs->entpos++] = buf[pos];
+ if (prs->entpos > 7) return IKS_BADXML;
+ }
+ break;
+
+ case C_COMMENT:
+ if ('-' != c) return IKS_BADXML;
+ prs->context = C_COMMENT_1;
+ break;
+
+ case C_COMMENT_1:
+ if ('-' == c) prs->context = C_COMMENT_2;
+ break;
+
+ case C_COMMENT_2:
+ if ('-' == c)
+ prs->context = C_COMMENT_3;
+ else
+ prs->context = C_COMMENT_1;
+ break;
+
+ case C_COMMENT_3:
+ if ('>' != c) return IKS_BADXML;
+ prs->context = C_CDATA;
+ old = pos + 1;
+ break;
+
+ case C_MARKUP:
+ if ('[' == c) {
+ prs->context = C_SECT;
+ break;
+ }
+ if ('-' == c) {
+ prs->context = C_COMMENT;
+ break;
+ }
+ prs->context = C_MARKUP_1;
+
+ case C_MARKUP_1:
+ if ('>' == c) {
+ old = pos + 1;
+ prs->context = C_CDATA;
+ }
+ break;
+
+ case C_SECT:
+ if ('C' == c) {
+ prs->context = C_SECT_CDATA;
+ break;
+ }
+ return IKS_BADXML;
+
+ case C_SECT_CDATA:
+ if ('D' != c) return IKS_BADXML;
+ prs->context = C_SECT_CDATA_1;
+ break;
+
+ case C_SECT_CDATA_1:
+ if ('A' != c) return IKS_BADXML;
+ prs->context = C_SECT_CDATA_2;
+ break;
+
+ case C_SECT_CDATA_2:
+ if ('T' != c) return IKS_BADXML;
+ prs->context = C_SECT_CDATA_3;
+ break;
+
+ case C_SECT_CDATA_3:
+ if ('A' != c) return IKS_BADXML;
+ prs->context = C_SECT_CDATA_4;
+ break;
+
+ case C_SECT_CDATA_4:
+ if ('[' != c) return IKS_BADXML;
+ old = pos + 1;
+ prs->context = C_SECT_CDATA_C;
+ break;
+
+ case C_SECT_CDATA_C:
+ if (']' == c) {
+ prs->context = C_SECT_CDATA_E;
+ if (prs->cdataHook && old < pos) {
+ err = prs->cdataHook (prs->user_data, &buf[old], pos - old);
+ if (IKS_OK != err) return err;
+ }
+ }
+ break;
+
+ case C_SECT_CDATA_E:
+ if (']' == c) {
+ prs->context = C_SECT_CDATA_E2;
+ } else {
+ if (prs->cdataHook) {
+ err = prs->cdataHook (prs->user_data, "]", 1);
+ if (IKS_OK != err) return err;
+ }
+ old = pos;
+ prs->context = C_SECT_CDATA_C;
+ }
+ break;
+
+ case C_SECT_CDATA_E2:
+ if ('>' == c) {
+ old = pos + 1;
+ prs->context = C_CDATA;
+ } else {
+ if (prs->cdataHook) {
+ err = prs->cdataHook (prs->user_data, "]]", 2);
+ if (IKS_OK != err) return err;
+ }
+ old = pos;
+ prs->context = C_SECT_CDATA_C;
+ }
+ break;
+
+ case C_PI:
+ old = pos + 1;
+ if ('>' == c) prs->context = C_CDATA;
+ break;
+ }
+cont:
+ if (0 == re) {
+ pos++;
+ prs->nr_bytes++;
+ if ('\n' == c) prs->nr_lines++;
+ }
+ }
+
+ if (stack_old != -1)
+ STACK_PUSH (buf + stack_old, pos - stack_old);
+
+ err = IKS_OK;
+ if (prs->cdataHook && (prs->context == C_CDATA || prs->context == C_SECT_CDATA_C) && old < pos)
+ err = prs->cdataHook (prs->user_data, &buf[old], pos - old);
+ return err;
+}
+
+int
+iks_parse (iksparser *prs, const char *data, size_t len, int finish)
+{
+ if (!data) return IKS_OK;
+ if (len == 0) len = strlen (data);
+ return sax_core (prs, (char *) data, len);
+}
+
+void
+iks_parser_reset (iksparser *prs)
+{
+ if (prs->deleteHook) prs->deleteHook (prs->user_data);
+ prs->stack_pos = 0;
+ prs->context = 0;
+ prs->oldcontext = 0;
+ prs->tagtype = 0;
+ prs->attcur = 0;
+ prs->attflag = 0;
+ prs->valflag = 0;
+ prs->entpos = 0;
+ prs->nr_bytes = 0;
+ prs->nr_lines = 0;
+ prs->uni_max = 0;
+ prs->uni_len = 0;
+}
+
+void
+iks_parser_delete (iksparser *prs)
+{
+ if (prs->deleteHook) prs->deleteHook (prs->user_data);
+ if (prs->stack) iks_free (prs->stack);
+ if (prs->atts) iks_free (prs->atts);
+ if (prs->s) iks_stack_delete (prs->s); else iks_free (prs);
+}
+/* iksemel (XML parser for Jabber)
+** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU Lesser General Public License.
+*/
+
+#include "common.h"
+#include "iksemel.h"
+
+#define IKS_COMMON \
+ struct iks_struct *next, *prev; \
+ struct iks_struct *parent; \
+ enum ikstype type; \
+ ikstack *s
+
+struct iks_struct {
+ IKS_COMMON;
+};
+
+struct iks_tag {
+ IKS_COMMON;
+ struct iks_struct *children, *last_child;
+ struct iks_struct *attribs, *last_attrib;
+ char *name;
+};
+
+#define IKS_TAG_NAME(x) ((struct iks_tag *) (x) )->name
+#define IKS_TAG_CHILDREN(x) ((struct iks_tag *) (x) )->children
+#define IKS_TAG_LAST_CHILD(x) ((struct iks_tag *) (x) )->last_child
+#define IKS_TAG_ATTRIBS(x) ((struct iks_tag *) (x) )->attribs
+#define IKS_TAG_LAST_ATTRIB(x) ((struct iks_tag *) (x) )->last_attrib
+
+struct iks_cdata {
+ IKS_COMMON;
+ char *cdata;
+ size_t len;
+};
+
+#define IKS_CDATA_CDATA(x) ((struct iks_cdata *) (x) )->cdata
+#define IKS_CDATA_LEN(x) ((struct iks_cdata *) (x) )->len
+
+struct iks_attrib {
+ IKS_COMMON;
+ char *name;
+ char *value;
+};
+
+#define IKS_ATTRIB_NAME(x) ((struct iks_attrib *) (x) )->name
+#define IKS_ATTRIB_VALUE(x) ((struct iks_attrib *) (x) )->value
+
+/***** Node Creating & Deleting *****/
+
+iks *
+iks_new (const char *name)
+{
+ ikstack *s;
+ iks *x;
+
+ s = iks_stack_new (sizeof (struct iks_tag) * 6, 256);
+ if (!s) return NULL;
+ x = iks_new_within (name, s);
+ if (!x) {
+ iks_stack_delete (s);
+ return NULL;
+ }
+ return x;
+}
+
+iks *
+iks_new_within (const char *name, ikstack *s)
+{
+ iks *x;
+ size_t len;
+
+ if (name) len = sizeof (struct iks_tag); else len = sizeof (struct iks_cdata);
+ x = iks_stack_alloc (s, len);
+ if (!x) return NULL;
+ memset (x, 0, len);
+ x->s = s;
+ x->type = IKS_TAG;
+ if (name) {
+ IKS_TAG_NAME (x) = iks_stack_strdup (s, name, 0);
+ if (!IKS_TAG_NAME (x)) return NULL;
+ }
+ return x;
+}
+
+iks *
+iks_insert (iks *x, const char *name)
+{
+ iks *y;
+
+ if (!x) return NULL;
+
+ y = iks_new_within (name, x->s);
+ if (!y) return NULL;
+ y->parent = x;
+ if (!IKS_TAG_CHILDREN (x)) IKS_TAG_CHILDREN (x) = y;
+ if (IKS_TAG_LAST_CHILD (x)) {
+ IKS_TAG_LAST_CHILD (x)->next = y;
+ y->prev = IKS_TAG_LAST_CHILD (x);
+ }
+ IKS_TAG_LAST_CHILD (x) = y;
+ return y;
+}
+
+iks *
+iks_insert_cdata (iks *x, const char *data, size_t len)
+{
+ iks *y;
+
+ if(!x || !data) return NULL;
+ if(len == 0) len = strlen (data);
+
+ y = IKS_TAG_LAST_CHILD (x);
+ if (y && y->type == IKS_CDATA) {
+ IKS_CDATA_CDATA (y) = iks_stack_strcat (x->s, IKS_CDATA_CDATA (y), IKS_CDATA_LEN (y), data, len);
+ IKS_CDATA_LEN (y) += len;
+ } else {
+ y = iks_insert (x, NULL);
+ if (!y) return NULL;
+ y->type = IKS_CDATA;
+ IKS_CDATA_CDATA (y) = iks_stack_strdup (x->s, data, len);
+ if (!IKS_CDATA_CDATA (y)) return NULL;
+ IKS_CDATA_LEN (y) = len;
+ }
+ return y;
+}
+
+iks *
+iks_insert_attrib (iks *x, const char *name, const char *value)
+{
+ iks *y;
+ size_t len;
+
+ if (!x) return NULL;
+
+ y = IKS_TAG_ATTRIBS (x);
+ while (y) {
+ if (strcmp (name, IKS_ATTRIB_NAME (y)) == 0) break;
+ y = y->next;
+ }
+ if (NULL == y) {
+ if (!value) return NULL;
+ y = iks_stack_alloc (x->s, sizeof (struct iks_attrib));
+ if (!y) return NULL;
+ memset (y, 0, sizeof (struct iks_attrib));
+ y->type = IKS_ATTRIBUTE;
+ IKS_ATTRIB_NAME (y) = iks_stack_strdup (x->s, name, 0);
+ y->parent = x;
+ if (!IKS_TAG_ATTRIBS (x)) IKS_TAG_ATTRIBS (x) = y;
+ if (IKS_TAG_LAST_ATTRIB (x)) {
+ IKS_TAG_LAST_ATTRIB (x)->next = y;
+ y->prev = IKS_TAG_LAST_ATTRIB (x);
+ }
+ IKS_TAG_LAST_ATTRIB (x) = y;
+ }
+
+ if (value) {
+ len = strlen (value);
+ IKS_ATTRIB_VALUE (y) = iks_stack_strdup (x->s, value, len);
+ if (!IKS_ATTRIB_VALUE (y)) return NULL;
+ } else {
+ if (y->next) y->next->prev = y->prev;
+ if (y->prev) y->prev->next = y->next;
+ if (IKS_TAG_ATTRIBS (x) == y) IKS_TAG_ATTRIBS (x) = y->next;
+ if (IKS_TAG_LAST_ATTRIB (x) == y) IKS_TAG_LAST_ATTRIB (x) = y->prev;
+ }
+
+ return y;
+}
+
+iks *
+iks_insert_node (iks *x, iks *y)
+{
+ y->parent = x;
+ if (!IKS_TAG_CHILDREN (x)) IKS_TAG_CHILDREN (x) = y;
+ if (IKS_TAG_LAST_CHILD (x)) {
+ IKS_TAG_LAST_CHILD (x)->next = y;
+ y->prev = IKS_TAG_LAST_CHILD (x);
+ }
+ IKS_TAG_LAST_CHILD (x) = y;
+ return y;
+}
+
+void
+iks_hide (iks *x)
+{
+ iks *y;
+
+ if (!x) return;
+
+ if (x->prev) x->prev->next = x->next;
+ if (x->next) x->next->prev = x->prev;
+ y = x->parent;
+ if (y) {
+ if (IKS_TAG_CHILDREN (y) == x) IKS_TAG_CHILDREN (y) = x->next;
+ if (IKS_TAG_LAST_CHILD (y) == x) IKS_TAG_LAST_CHILD (y) = x->prev;
+ }
+}
+
+void
+iks_delete (iks *x)
+{
+ if (x) iks_stack_delete (x->s);
+}
+
+/***** Node Traversing *****/
+
+iks *
+iks_next (iks *x)
+{
+ if (x) return x->next;
+ return NULL;
+}
+
+iks *
+iks_next_tag (iks *x)
+{
+ if (x) {
+ while (1) {
+ x = x->next;
+ if (NULL == x) break;
+ if (IKS_TAG == x->type) return x;
+ }
+ }
+ return NULL;
+}
+
+iks *
+iks_prev (iks *x)
+{
+ if (x) return x->prev;
+ return NULL;
+}
+
+iks *
+iks_prev_tag (iks *x)
+{
+ if (x) {
+ while (1) {
+ x = x->prev;
+ if (NULL == x) break;
+ if (IKS_TAG == x->type) return x;
+ }
+ }
+ return NULL;
+}
+
+iks *
+iks_parent (iks *x)
+{
+ if (x) return x->parent;
+ return NULL;
+}
+
+iks *
+iks_root (iks *x)
+{
+ if (x) {
+ while (x->parent)
+ x = x->parent;
+ }
+ return x;
+}
+
+iks *
+iks_child (iks *x)
+{
+ if (x) return IKS_TAG_CHILDREN (x);
+ return NULL;
+}
+
+iks *
+iks_first_tag (iks *x)
+{
+ if (x) {
+ x = IKS_TAG_CHILDREN (x);
+ while (x) {
+ if (IKS_TAG == x->type) return x;
+ x = x->next;
+ }
+ }
+ return NULL;
+}
+
+iks *
+iks_attrib (iks *x)
+{
+ if (x) return IKS_TAG_ATTRIBS (x);
+ return NULL;
+}
+
+iks *
+iks_find (iks *x, const char *name)
+{
+ iks *y;
+
+ if (!x) return NULL;
+ y = IKS_TAG_CHILDREN (x);
+ while (y) {
+ if (IKS_TAG == y->type && IKS_TAG_NAME (y) && strcmp (IKS_TAG_NAME (y), name) == 0) return y;
+ y = y->next;
+ }
+ return NULL;
+}
+
+char *
+iks_find_cdata (iks *x, const char *name)
+{
+ iks *y;
+
+ y = iks_find (x, name);
+ if (!y) return NULL;
+ y = IKS_TAG_CHILDREN (y);
+ if (!y || IKS_CDATA != y->type) return NULL;
+ return IKS_CDATA_CDATA (y);
+}
+
+char *
+iks_find_attrib (iks *x, const char *name)
+{
+ iks *y;
+
+ if (!x) return NULL;
+
+ y = IKS_TAG_ATTRIBS (x);
+ while (y) {
+ if (IKS_ATTRIB_NAME (y) && strcmp (IKS_ATTRIB_NAME (y), name) == 0)
+ return IKS_ATTRIB_VALUE (y);
+ y = y->next;
+ }
+ return NULL;
+}
+
+iks *
+iks_find_with_attrib (iks *x, const char *tagname, const char *attrname, const char *value)
+{
+ iks *y;
+
+ if (NULL == x) return NULL;
+
+ if (tagname) {
+ for (y = IKS_TAG_CHILDREN (x); y; y = y->next) {
+ if (IKS_TAG == y->type
+ && strcmp (IKS_TAG_NAME (y), tagname) == 0
+ && iks_strcmp (iks_find_attrib (y, attrname), value) == 0) {
+ return y;
+ }
+ }
+ } else {
+ for (y = IKS_TAG_CHILDREN (x); y; y = y->next) {
+ if (IKS_TAG == y->type
+ && iks_strcmp (iks_find_attrib (y, attrname), value) == 0) {
+ return y;
+ }
+ }
+ }
+ return NULL;
+}
+
+/***** Node Information *****/
+
+ikstack *
+iks_stack (iks *x)
+{
+ if (x) return x->s;
+ return NULL;
+}
+
+enum ikstype
+iks_type (iks *x)
+{
+ if (x) return x->type;
+ return IKS_NONE;
+}
+
+char *
+iks_name (iks *x)
+{
+ if (x) {
+ if (IKS_TAG == x->type)
+ return IKS_TAG_NAME (x);
+ else
+ return IKS_ATTRIB_NAME (x);
+ }
+ return NULL;
+}
+
+char *
+iks_cdata (iks *x)
+{
+ if (x) {
+ if (IKS_CDATA == x->type)
+ return IKS_CDATA_CDATA (x);
+ else
+ return IKS_ATTRIB_VALUE (x);
+ }
+ return NULL;
+}
+
+size_t
+iks_cdata_size (iks *x)
+{
+ if (x) return IKS_CDATA_LEN (x);
+ return 0;
+}
+
+int
+iks_has_children (iks *x)
+{
+ if (x && IKS_TAG == x->type && IKS_TAG_CHILDREN (x)) return 1;
+ return 0;
+}
+
+int
+iks_has_attribs (iks *x)
+{
+ if (x && IKS_TAG == x->type && IKS_TAG_ATTRIBS (x)) return 1;
+ return 0;
+}
+
+/***** Serializing *****/
+
+static size_t
+escape_size (char *src, size_t len)
+{
+ size_t sz;
+ char c;
+ int i;
+
+ sz = 0;
+ for (i = 0; i < len; i++) {
+ c = src[i];
+ switch (c) {
+ case '&': sz += 5; break;
+ case '\'': sz += 6; break;
+ case '"': sz += 6; break;
+ case '<': sz += 4; break;
+ case '>': sz += 4; break;
+ default: sz++; break;
+ }
+ }
+ return sz;
+}
+
+static char *
+my_strcat (char *dest, char *src, size_t len)
+{
+ if (0 == len) len = strlen (src);
+ memcpy (dest, src, len);
+ return dest + len;
+}
+
+static char *
+escape (char *dest, char *src, size_t len)
+{
+ char c;
+ int i;
+ int j = 0;
+
+ for (i = 0; i < len; i++) {
+ c = src[i];
+ if ('&' == c || '<' == c || '>' == c || '\'' == c || '"' == c) {
+ if (i - j > 0) dest = my_strcat (dest, src + j, i - j);
+ j = i + 1;
+ switch (c) {
+ case '&': dest = my_strcat (dest, "&amp;", 5); break;
+ case '\'': dest = my_strcat (dest, "&apos;", 6); break;
+ case '"': dest = my_strcat (dest, "&quot;", 6); break;
+ case '<': dest = my_strcat (dest, "&lt;", 4); break;
+ case '>': dest = my_strcat (dest, "&gt;", 4); break;
+ }
+ }
+ }
+ if (i - j > 0) dest = my_strcat (dest, src + j, i - j);
+ return dest;
+}
+
+char *
+iks_string (ikstack *s, iks *x)
+{
+ size_t size;
+ int level, dir;
+ iks *y, *z;
+ char *ret, *t;
+
+ if (!x) return NULL;
+
+ if (x->type == IKS_CDATA) {
+ if (s) {
+ return iks_stack_strdup (s, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
+ } else {
+ ret = iks_malloc (IKS_CDATA_LEN (x));
+ memcpy (ret, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
+ return ret;
+ }
+ }
+
+ size = 0;
+ level = 0;
+ dir = 0;
+ y = x;
+ while (1) {
+ if (dir==0) {
+ if (y->type == IKS_TAG) {
+ size++;
+ size += strlen (IKS_TAG_NAME (y));
+ for (z = IKS_TAG_ATTRIBS (y); z; z = z->next) {
+ size += 4 + strlen (IKS_ATTRIB_NAME (z))
+ + escape_size (IKS_ATTRIB_VALUE (z), strlen (IKS_ATTRIB_VALUE (z)));
+ }
+ if (IKS_TAG_CHILDREN (y)) {
+ size++;
+ y = IKS_TAG_CHILDREN (y);
+ level++;
+ continue;
+ } else {
+ size += 2;
+ }
+ } else {
+ size += escape_size (IKS_CDATA_CDATA (y), IKS_CDATA_LEN (y));
+ }
+ }
+ z = y->next;
+ if (z) {
+ if (0 == level) {
+ if (IKS_TAG_CHILDREN (y)) size += 3 + strlen (IKS_TAG_NAME (y));
+ break;
+ }
+ y = z;
+ dir = 0;
+ } else {
+ y = y->parent;
+ level--;
+ if (level >= 0) size += 3 + strlen (IKS_TAG_NAME (y));
+ if (level < 1) break;
+ dir = 1;
+ }
+ }
+
+ if (s) ret = iks_stack_alloc (s, size + 1);
+ else ret = iks_malloc (size + 1);
+
+ if (!ret) return NULL;
+
+ t = ret;
+ level = 0;
+ dir = 0;
+ while (1) {
+ if (dir==0) {
+ if (x->type == IKS_TAG) {
+ *t++ = '<';
+ t = my_strcat (t, IKS_TAG_NAME (x), 0);
+ y = IKS_TAG_ATTRIBS (x);
+ while (y) {
+ *t++ = ' ';
+ t = my_strcat (t, IKS_ATTRIB_NAME (y), 0);
+ *t++ = '=';
+ *t++ = '\'';
+ t = escape (t, IKS_ATTRIB_VALUE (y), strlen (IKS_ATTRIB_VALUE (y)));
+ *t++ = '\'';
+ y = y->next;
+ }
+ if (IKS_TAG_CHILDREN (x)) {
+ *t++ = '>';
+ x = IKS_TAG_CHILDREN (x);
+ level++;
+ continue;
+ } else {
+ *t++ = '/';
+ *t++ = '>';
+ }
+ } else {
+ t = escape (t, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
+ }
+ }
+ y = x->next;
+ if (y) {
+ if (0 == level) {
+ if (IKS_TAG_CHILDREN (x)) {
+ *t++ = '<';
+ *t++ = '/';
+ t = my_strcat (t, IKS_TAG_NAME (x), 0);
+ *t++ = '>';
+ }
+ break;
+ }
+ x = y;
+ dir = 0;
+ } else {
+ x = x->parent;
+ level--;
+ if (level >= 0) {
+ *t++ = '<';
+ *t++ = '/';
+ t = my_strcat (t, IKS_TAG_NAME (x), 0);
+ *t++ = '>';
+ }
+ if (level < 1) break;
+ dir = 1;
+ }
+ }
+ *t = '\0';
+
+ return ret;
+}
+
+/***** Copying *****/
+
+iks *
+iks_copy_within (iks *x, ikstack *s)
+{
+ int level=0, dir=0;
+ iks *copy = NULL;
+ iks *cur = NULL;
+ iks *y;
+
+ while (1) {
+ if (dir == 0) {
+ if (x->type == IKS_TAG) {
+ if (copy == NULL) {
+ copy = iks_new_within (IKS_TAG_NAME (x), s);
+ cur = copy;
+ } else {
+ cur = iks_insert (cur, IKS_TAG_NAME (x));
+ }
+ for (y = IKS_TAG_ATTRIBS (x); y; y = y->next) {
+ iks_insert_attrib (cur, IKS_ATTRIB_NAME (y), IKS_ATTRIB_VALUE (y));
+ }
+ if (IKS_TAG_CHILDREN (x)) {
+ x = IKS_TAG_CHILDREN (x);
+ level++;
+ continue;
+ } else {
+ cur = cur->parent;
+ }
+ } else {
+ iks_insert_cdata (cur, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
+ }
+ }
+ y = x->next;
+ if (y) {
+ if (0 == level) break;
+ x = y;
+ dir = 0;
+ } else {
+ if (level < 2) break;
+ level--;
+ x = x->parent;
+ cur = cur->parent;
+ dir = 1;
+ }
+ }
+ return copy;
+}
+
+iks *
+iks_copy (iks *x)
+{
+ return iks_copy_within (x, iks_stack_new (sizeof (struct iks_tag) * 6, 256));
+}
+/* iksemel (XML parser for Jabber)
+** Copyright (C) 2000-2003 Gurer Ozen <madcat@e-kolay.net>
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU Lesser General Public License.
+*/
+
+#include "common.h"
+#include "iksemel.h"
+
+struct dom_data {
+ iks **iksptr;
+ iks *current;
+ size_t chunk_size;
+};
+
+static int
+tagHook (struct dom_data *data, char *name, char **atts, int type)
+{
+ iks *x;
+
+ if (IKS_OPEN == type || IKS_SINGLE == type) {
+ if (data->current) {
+ x = iks_insert (data->current, name);
+ } else {
+ ikstack *s;
+ s = iks_stack_new (data->chunk_size, data->chunk_size);
+ x = iks_new_within (name, s);
+ }
+ if (atts) {
+ int i=0;
+ while (atts[i]) {
+ iks_insert_attrib (x, atts[i], atts[i+1]);
+ i += 2;
+ }
+ }
+ data->current = x;
+ }
+ if (IKS_CLOSE == type || IKS_SINGLE == type) {
+ x = iks_parent (data->current);
+ if (x)
+ data->current = x;
+ else {
+ *(data->iksptr) = data->current;
+ data->current = NULL;
+ }
+ }
+ return IKS_OK;
+}
+
+static int
+cdataHook (struct dom_data *data, char *cdata, size_t len)
+{
+ if (data->current) iks_insert_cdata (data->current, cdata, len);
+ return IKS_OK;
+}
+
+static void
+deleteHook (struct dom_data *data)
+{
+ if (data->current) iks_delete (data->current);
+ data->current = NULL;
+}
+
+iksparser *
+iks_dom_new (iks **iksptr)
+{
+ ikstack *s;
+ struct dom_data *data;
+
+ *iksptr = NULL;
+ s = iks_stack_new (DEFAULT_DOM_CHUNK_SIZE, 0);
+ if (!s) return NULL;
+ data = iks_stack_alloc (s, sizeof (struct dom_data));
+ data->iksptr = iksptr;
+ data->current = NULL;
+ data->chunk_size = DEFAULT_DOM_IKS_CHUNK_SIZE;
+ return iks_sax_extend (s, data, (iksTagHook *) tagHook, (iksCDataHook *) cdataHook, (iksDeleteHook *) deleteHook);
+}
+
+void
+iks_set_size_hint (iksparser *prs, size_t approx_size)
+{
+ size_t cs;
+ struct dom_data *data = iks_user_data (prs);
+
+ cs = approx_size / 10;
+ if (cs < DEFAULT_DOM_IKS_CHUNK_SIZE) cs = DEFAULT_DOM_IKS_CHUNK_SIZE;
+ data->chunk_size = cs;
+}
+
+iks *
+iks_tree (const char *xml_str, size_t len, int *err)
+{
+ iksparser *prs;
+ iks *x;
+ int e;
+
+ if (0 == len) len = strlen (xml_str);
+ prs = iks_dom_new (&x);
+ if (!prs) {
+ if (err) *err = IKS_NOMEM;
+ return NULL;
+ }
+ e = iks_parse (prs, xml_str, len, 1);
+ if (err) *err = e;
+ iks_parser_delete (prs);
+ return x;
+}
+
+int
+iks_load (const char *fname, iks **xptr)
+{
+ iksparser *prs;
+ char *buf;
+ FILE *f;
+ int len, done = 0;
+ int ret;
+
+ *xptr = NULL;
+
+ buf = iks_malloc (FILE_IO_BUF_SIZE);
+ if (!buf) return IKS_NOMEM;
+ ret = IKS_NOMEM;
+ prs = iks_dom_new (xptr);
+ if (prs) {
+ f = fopen (fname, "r");
+ if (f) {
+ while (0 == done) {
+ len = fread (buf, 1, FILE_IO_BUF_SIZE, f);
+ if (len < FILE_IO_BUF_SIZE) {
+ if (0 == feof (f)) {
+ ret = IKS_FILE_RWERR;
+ len = 0;
+ }
+ done = 1;
+ }
+ if (len > 0) {
+ int e;
+ e = iks_parse (prs, buf, len, done);
+ if (IKS_OK != e) {
+ ret = e;
+ break;
+ }
+ if (done) ret = IKS_OK;
+ }
+ }
+ fclose (f);
+ } else {
+ if (ENOENT == errno) ret = IKS_FILE_NOFILE;
+ else ret = IKS_FILE_NOACCESS;
+ }
+ iks_parser_delete (prs);
+ }
+ iks_free (buf);
+ return ret;
+}
+
+int
+iks_save (const char *fname, iks *x)
+{
+ FILE *f;
+ char *data;
+ int ret;
+
+ ret = IKS_NOMEM;
+ data = iks_string (NULL, x);
+ if (data) {
+ ret = IKS_FILE_NOACCESS;
+ f = fopen (fname, "w");
+ if (f) {
+ ret = IKS_FILE_RWERR;
+ if (fputs (data, f) >= 0) ret = IKS_OK;
+ fclose (f);
+ }
+ iks_free (data);
+ }
+ return ret;
+}
diff --git a/backend/impress/iksemel.h b/backend/impress/iksemel.h
new file mode 100644
index 0000000..66c87d6
--- /dev/null
+++ b/backend/impress/iksemel.h
@@ -0,0 +1,402 @@
+/* iksemel (XML parser for Jabber)
+** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU Lesser General Public License.
+*/
+
+#ifndef IKSEMEL_H
+#define IKSEMEL_H 1
+
+#ifdef __cplusplus
+#include <cstddef> /* size_t for C++ */
+extern "C" {
+#else
+#include <stddef.h> /* size_t for C */
+#endif
+
+/***** object stack *****/
+
+struct ikstack_struct;
+typedef struct ikstack_struct ikstack;
+
+ikstack *iks_stack_new (size_t meta_chunk, size_t data_chunk);
+void *iks_stack_alloc (ikstack *s, size_t size);
+char *iks_stack_strdup (ikstack *s, const char *src, size_t len);
+char *iks_stack_strcat (ikstack *s, char *old, size_t old_len, const char *src, size_t src_len);
+void iks_stack_stat (ikstack *s, size_t *allocated, size_t *used);
+void iks_stack_delete (ikstack *s);
+
+/***** utilities *****/
+
+void *iks_malloc (size_t size);
+void iks_free (void *ptr);
+void iks_set_mem_funcs (void *(*malloc_func)(size_t size), void (*free_func)(void *ptr));
+
+char *iks_strdup (const char *src);
+char *iks_strcat (char *dest, const char *src);
+int iks_strcmp (const char *a, const char *b);
+int iks_strcasecmp (const char *a, const char *b);
+int iks_strncmp (const char *a, const char *b, size_t n);
+int iks_strncasecmp (const char *a, const char *b, size_t n);
+size_t iks_strlen (const char *src);
+char *iks_escape (ikstack *s, char *src, size_t len);
+char *iks_unescape (ikstack *s, char *src, size_t len);
+
+/***** dom tree *****/
+
+enum ikstype {
+ IKS_NONE = 0,
+ IKS_TAG,
+ IKS_ATTRIBUTE,
+ IKS_CDATA
+};
+
+struct iks_struct;
+typedef struct iks_struct iks;
+
+iks *iks_new (const char *name);
+iks *iks_new_within (const char *name, ikstack *s);
+iks *iks_insert (iks *x, const char *name);
+iks *iks_insert_cdata (iks *x, const char *data, size_t len);
+iks *iks_insert_attrib (iks *x, const char *name, const char *value);
+iks *iks_insert_node (iks *x, iks *y);
+void iks_hide (iks *x);
+void iks_delete (iks *x);
+iks *iks_next (iks *x);
+iks *iks_next_tag (iks *x);
+iks *iks_prev (iks *x);
+iks *iks_prev_tag (iks *x);
+iks *iks_parent (iks *x);
+iks *iks_root (iks *x);
+iks *iks_child (iks *x);
+iks *iks_first_tag (iks *x);
+iks *iks_attrib (iks *x);
+iks *iks_find (iks *x, const char *name);
+char *iks_find_cdata (iks *x, const char *name);
+char *iks_find_attrib (iks *x, const char *name);
+iks *iks_find_with_attrib (iks *x, const char *tagname, const char *attrname, const char *value);
+ikstack *iks_stack (iks *x);
+enum ikstype iks_type (iks *x);
+char *iks_name (iks *x);
+char *iks_cdata (iks *x);
+size_t iks_cdata_size (iks *x);
+int iks_has_children (iks *x);
+int iks_has_attribs (iks *x);
+char *iks_string (ikstack *s, iks *x);
+iks *iks_copy (iks *x);
+iks *iks_copy_within (iks *x, ikstack *s);
+
+/***** sax parser *****/
+
+enum ikserror {
+ IKS_OK = 0,
+ IKS_NOMEM,
+ IKS_BADXML,
+ IKS_HOOK
+};
+
+enum ikstagtype {
+ IKS_OPEN,
+ IKS_CLOSE,
+ IKS_SINGLE
+};
+
+typedef int (iksTagHook)(void *user_data, char *name, char **atts, int type);
+typedef int (iksCDataHook)(void *user_data, char *data, size_t len);
+typedef void (iksDeleteHook)(void *user_data);
+
+struct iksparser_struct;
+typedef struct iksparser_struct iksparser;
+
+iksparser *iks_sax_new (void *user_data, iksTagHook *tagHook, iksCDataHook *cdataHook);
+iksparser *iks_sax_extend (ikstack *s, void *user_data, iksTagHook *tagHook, iksCDataHook *cdataHook, iksDeleteHook *deleteHook);
+ikstack *iks_parser_stack (iksparser *prs);
+void *iks_user_data (iksparser *prs);
+unsigned long iks_nr_bytes (iksparser *prs);
+unsigned long iks_nr_lines (iksparser *prs);
+int iks_parse (iksparser *prs, const char *data, size_t len, int finish);
+void iks_parser_reset (iksparser *prs);
+void iks_parser_delete (iksparser *prs);
+
+/***** dom parser *****/
+
+enum iksfileerror {
+ IKS_FILE_NOFILE = 4,
+ IKS_FILE_NOACCESS,
+ IKS_FILE_RWERR
+};
+
+iksparser *iks_dom_new (iks **iksptr);
+void iks_set_size_hint (iksparser *prs, size_t approx_size);
+iks *iks_tree (const char *xml_str, size_t len, int *err);
+int iks_load (const char *fname, iks **xptr);
+int iks_save (const char *fname, iks *x);
+
+/***** transport layer *****/
+
+typedef void (iksTClose)(void *socket);
+typedef int (iksTConnect)(iksparser *prs, void **socketptr, const char *server, int port);
+typedef int (iksTSend)(void *socket, const char *data, size_t len);
+typedef int (iksTRecv)(void *socket, char *buffer, size_t buf_len, int timeout);
+typedef int (iksTConnectFD)(iksparser *prs, void **socketptr, void *fd);
+typedef void *(iksTGetFD)(void *socket);
+
+enum iksasyncevents {
+ IKS_ASYNC_RESOLVED,
+ IKS_ASYNC_CONNECTED,
+ IKS_ASYNC_WRITE,
+ IKS_ASYNC_WRITTEN,
+ IKS_ASYNC_READ,
+ IKS_ASYNC_CLOSED,
+ IKS_ASYNC_ERROR
+};
+
+typedef int (iksAsyncNotify)(void *user_data, int event, void *event_data);
+typedef int (iksTConnectAsync)(iksparser *prs, void **socketptr, const char *server, int port, void *notify_data, iksAsyncNotify *notify_func);
+
+typedef struct ikstransport_struct {
+ /* basic api, connect can be NULL if one of the other connect funcs are used */
+ iksTConnect *connect;
+ iksTSend *send;
+ iksTRecv *recv;
+ iksTClose *close;
+ /* optional fd api */
+ iksTConnectFD *connect_fd;
+ iksTGetFD *get_fd;
+ /* optional async api */
+ iksTConnectAsync *connect_async;
+} ikstransport;
+
+extern ikstransport iks_default_transport;
+
+/***** stream parser *****/
+
+enum iksneterror {
+ IKS_NET_NODNS = 4,
+ IKS_NET_NOSOCK,
+ IKS_NET_NOCONN,
+ IKS_NET_RWERR,
+ IKS_NET_NOTSUPP,
+ IKS_NET_TLSFAIL
+};
+
+enum iksnodetype {
+ IKS_NODE_START,
+ IKS_NODE_NORMAL,
+ IKS_NODE_ERROR,
+ IKS_NODE_STOP
+};
+
+enum ikssasltype {
+ IKS_SASL_PLAIN,
+ IKS_SASL_DIGEST_MD5
+};
+
+#define IKS_JABBER_PORT 5222
+
+typedef int (iksStreamHook)(void *user_data, int type, iks *node);
+typedef void (iksLogHook)(void *user_data, const char *data, size_t size, int is_incoming);
+
+iksparser *iks_stream_new (char *name_space, void *user_data, iksStreamHook *streamHook);
+void *iks_stream_user_data (iksparser *prs);
+void iks_set_log_hook (iksparser *prs, iksLogHook *logHook);
+int iks_connect_tcp (iksparser *prs, const char *server, int port);
+int iks_connect_fd (iksparser *prs, int fd);
+int iks_connect_via (iksparser *prs, const char *server, int port, const char *server_name);
+int iks_connect_with (iksparser *prs, const char *server, int port, const char *server_name, ikstransport *trans);
+int iks_connect_async (iksparser *prs, const char *server, int port, void *notify_data, iksAsyncNotify *notify_func);
+int iks_connect_async_with (iksparser *prs, const char *server, int port, const char *server_name, ikstransport *trans, void *notify_data, iksAsyncNotify *notify_func);
+int iks_fd (iksparser *prs);
+int iks_recv (iksparser *prs, int timeout);
+int iks_send_header (iksparser *prs, const char *to);
+int iks_send (iksparser *prs, iks *x);
+int iks_send_raw (iksparser *prs, const char *xmlstr);
+void iks_disconnect (iksparser *prs);
+int iks_has_tls (void);
+int iks_is_secure (iksparser *prs);
+int iks_start_tls (iksparser *prs);
+int iks_start_sasl (iksparser *prs, enum ikssasltype type, char *username, char *pass);
+
+/***** jabber *****/
+
+#define IKS_NS_CLIENT "jabber:client"
+#define IKS_NS_SERVER "jabber:server"
+#define IKS_NS_AUTH "jabber:iq:auth"
+#define IKS_NS_AUTH_0K "jabber:iq:auth:0k"
+#define IKS_NS_REGISTER "jabber:iq:register"
+#define IKS_NS_ROSTER "jabber:iq:roster"
+#define IKS_NS_XROSTER "jabber:x:roster"
+#define IKS_NS_OFFLINE "jabber:x:offline"
+#define IKS_NS_AGENT "jabber:iq:agent"
+#define IKS_NS_AGENTS "jabber:iq:agents"
+#define IKS_NS_BROWSE "jabber:iq:browse"
+#define IKS_NS_CONFERENCE "jabber:iq:conference"
+#define IKS_NS_DELAY "jabber:x:delay"
+#define IKS_NS_VERSION "jabber:iq:version"
+#define IKS_NS_TIME "jabber:iq:time"
+#define IKS_NS_VCARD "vcard-temp"
+#define IKS_NS_PRIVATE "jabber:iq:private"
+#define IKS_NS_SEARCH "jabber:iq:search"
+#define IKS_NS_OOB "jabber:iq:oob"
+#define IKS_NS_XOOB "jabber:x:oob"
+#define IKS_NS_ADMIN "jabber:iq:admin"
+#define IKS_NS_FILTER "jabber:iq:filter"
+#define IKS_NS_GATEWAY "jabber:iq:gateway"
+#define IKS_NS_LAST "jabber:iq:last"
+#define IKS_NS_SIGNED "jabber:x:signed"
+#define IKS_NS_ENCRYPTED "jabber:x:encrypted"
+#define IKS_NS_ENVELOPE "jabber:x:envelope"
+#define IKS_NS_EVENT "jabber:x:event"
+#define IKS_NS_EXPIRE "jabber:x:expire"
+#define IKS_NS_XHTML "http://www.w3.org/1999/xhtml"
+#define IKS_NS_XMPP_SASL "urn:ietf:params:xml:ns:xmpp-sasl"
+#define IKS_NS_XMPP_BIND "urn:ietf:params:xml:ns:xmpp-bind"
+#define IKS_NS_XMPP_SESSION "urn:ietf:params:xml:ns:xmpp-session"
+
+#define IKS_ID_USER 1
+#define IKS_ID_SERVER 2
+#define IKS_ID_RESOURCE 4
+#define IKS_ID_PARTIAL IKS_ID_USER | IKS_ID_SERVER
+#define IKS_ID_FULL IKS_ID_USER | IKS_ID_SERVER | IKS_ID_RESOURCE
+
+#define IKS_STREAM_STARTTLS 1
+#define IKS_STREAM_SESSION 2
+#define IKS_STREAM_BIND 4
+#define IKS_STREAM_SASL_PLAIN 8
+#define IKS_STREAM_SASL_MD5 16
+
+typedef struct iksid_struct {
+ char *user;
+ char *server;
+ char *resource;
+ char *partial;
+ char *full;
+} iksid;
+
+iksid *iks_id_new (ikstack *s, const char *jid);
+int iks_id_cmp (iksid *a, iksid *b, int parts);
+
+enum ikspaktype {
+ IKS_PAK_NONE = 0,
+ IKS_PAK_MESSAGE,
+ IKS_PAK_PRESENCE,
+ IKS_PAK_IQ,
+ IKS_PAK_S10N
+};
+
+enum iksubtype {
+ IKS_TYPE_NONE = 0,
+ IKS_TYPE_ERROR,
+
+ IKS_TYPE_CHAT,
+ IKS_TYPE_GROUPCHAT,
+ IKS_TYPE_HEADLINE,
+
+ IKS_TYPE_GET,
+ IKS_TYPE_SET,
+ IKS_TYPE_RESULT,
+
+ IKS_TYPE_SUBSCRIBE,
+ IKS_TYPE_SUBSCRIBED,
+ IKS_TYPE_UNSUBSCRIBE,
+ IKS_TYPE_UNSUBSCRIBED,
+ IKS_TYPE_PROBE,
+ IKS_TYPE_AVAILABLE,
+ IKS_TYPE_UNAVAILABLE
+};
+
+enum ikshowtype {
+ IKS_SHOW_UNAVAILABLE = 0,
+ IKS_SHOW_AVAILABLE,
+ IKS_SHOW_CHAT,
+ IKS_SHOW_AWAY,
+ IKS_SHOW_XA,
+ IKS_SHOW_DND
+};
+
+typedef struct ikspak_struct {
+ iks *x;
+ iksid *from;
+ iks *query;
+ char *ns;
+ char *id;
+ enum ikspaktype type;
+ enum iksubtype subtype;
+ enum ikshowtype show;
+} ikspak;
+
+ikspak *iks_packet (iks *x);
+
+iks *iks_make_auth (iksid *id, const char *pass, const char *sid);
+iks *iks_make_msg (enum iksubtype type, const char *to, const char *body);
+iks *iks_make_s10n (enum iksubtype type, const char *to, const char *msg);
+iks *iks_make_pres (enum ikshowtype show, const char *status);
+iks *iks_make_iq (enum iksubtype type, const char *xmlns);
+iks *iks_make_resource_bind(iksid *id);
+iks *iks_make_session(void);
+int iks_stream_features(iks *x);
+
+/***** jabber packet filter *****/
+
+#define IKS_RULE_DONE 0
+#define IKS_RULE_ID 1
+#define IKS_RULE_TYPE 2
+#define IKS_RULE_SUBTYPE 4
+#define IKS_RULE_FROM 8
+#define IKS_RULE_FROM_PARTIAL 16
+#define IKS_RULE_NS 32
+
+enum iksfilterret {
+ IKS_FILTER_PASS,
+ IKS_FILTER_EAT
+};
+
+typedef int (iksFilterHook)(void *user_data, ikspak *pak);
+
+struct iksfilter_struct;
+typedef struct iksfilter_struct iksfilter;
+struct iksrule_struct;
+typedef struct iksrule_struct iksrule;
+
+iksfilter *iks_filter_new (void);
+iksrule *iks_filter_add_rule (iksfilter *f, iksFilterHook *filterHook, void *user_data, ...);
+void iks_filter_remove_rule (iksfilter *f, iksrule *rule);
+void iks_filter_remove_hook (iksfilter *f, iksFilterHook *filterHook);
+void iks_filter_packet (iksfilter *f, ikspak *pak);
+void iks_filter_delete (iksfilter *f);
+
+/***** sha1 *****/
+
+struct iksha_struct;
+typedef struct iksha_struct iksha;
+
+iksha *iks_sha_new (void);
+void iks_sha_reset (iksha *sha);
+void iks_sha_hash (iksha *sha, const unsigned char *data, size_t len, int finish);
+void iks_sha_print (iksha *sha, char *hash);
+void iks_sha_delete (iksha *sha);
+void iks_sha (const char *data, char *hash);
+
+/***** md5 *****/
+
+struct ikmd5_struct;
+typedef struct iksmd5_struct iksmd5;
+
+iksmd5 *iks_md5_new(void);
+void iks_md5_reset(iksmd5 *md5);
+void iks_md5_hash(iksmd5 *md5, const unsigned char *data, size_t slen, int finish);
+void iks_md5_delete(iksmd5 *md5);
+void iks_md5_print(iksmd5 *md5, char *buf);
+void iks_md5_digest(iksmd5 *md5, unsigned char *digest);
+void iks_md5(const char *data, char *buf);
+
+/***** base64 *****/
+
+char *iks_base64_decode(const char *buf);
+char *iks_base64_encode(const char *buf, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IKSEMEL_H */
diff --git a/backend/impress/imposter.h b/backend/impress/imposter.h
new file mode 100644
index 0000000..50c87f2
--- /dev/null
+++ b/backend/impress/imposter.h
@@ -0,0 +1,84 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#ifndef IMPOSTER_H
+#define IMPOSTER_H
+
+#include <sys/types.h>
+
+enum {
+ IMP_OK = 0,
+ IMP_NOMEM,
+ IMP_NOTZIP,
+ IMP_BADZIP,
+ IMP_BADDOC,
+ IMP_NOTIMP
+};
+
+struct ImpDoc_struct;
+typedef struct ImpDoc_struct ImpDoc;
+
+struct ImpPage_struct;
+typedef struct ImpPage_struct ImpPage;
+
+typedef struct ImpPointStruct {
+ int x;
+ int y;
+} ImpPoint;
+
+typedef struct ImpColorStruct {
+ int red;
+ int green;
+ int blue;
+} ImpColor;
+
+#define IMP_NORMAL 0
+#define IMP_BOLD 1
+#define IMP_ITALIC 2
+#define IMP_UNDERLINE 4
+
+typedef struct ImpDrawer_struct {
+ void (*get_size)(void *drw_data, int *w, int *h);
+ void (*set_fg_color)(void *drw_data, ImpColor *color);
+ void (*draw_line)(void *drw_data, int x1, int y1, int x2, int y2);
+ void (*draw_rect)(void *drw_data, int fill, int x, int y, int w, int h);
+ void (*draw_polygon)(void *drw_data, int fill, ImpPoint *pts, int nr_pts);
+ void (*draw_arc)(void *drw_data, int fill, int x, int y, int w, int h, int sa, int ea);
+ void (*draw_bezier)(void *drw_data, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3);
+ void *(*open_image)(void *drw_data, const unsigned char *pix, size_t size);
+ void (*get_image_size)(void *drw_data, void *img_data, int *w, int *h);
+ void *(*scale_image)(void *drw_data, void *img_data, int w, int h);
+ void (*draw_image)(void *drw_data, void *img_data, int x, int y, int w, int h);
+ void (*close_image)(void *drw_data, void *img_data);
+ void (*get_text_size)(void *drw_data, const char *text, size_t len, int size, int styles, int *w, int *h);
+ void (*draw_text)(void *drw_data, int x, int y, const char *text, size_t len, int size, int styles);
+} ImpDrawer;
+
+struct ImpRenderCtx_struct;
+typedef struct ImpRenderCtx_struct ImpRenderCtx;
+
+#define IMP_LAST_PAGE -1
+
+ImpDoc *imp_open(const char *filename, int *err);
+int imp_nr_pages(ImpDoc *doc);
+ImpPage *imp_get_page(ImpDoc *doc, int page_no);
+void imp_close(ImpDoc *doc);
+
+void *imp_get_xml(ImpDoc *doc, const char *filename);
+
+ImpPage *imp_next_page(ImpPage *page);
+ImpPage *imp_prev_page(ImpPage *page);
+int imp_get_page_no(ImpPage *page);
+const char *imp_get_page_name(ImpPage *page);
+
+ImpRenderCtx *imp_create_context(const ImpDrawer *drw);
+void imp_context_set_page(ImpRenderCtx *ctx, ImpPage *page);
+void imp_context_set_step(ImpRenderCtx *ctx, int step);
+void imp_render(ImpRenderCtx *ctx, void *drw_data);
+void imp_delete_context(ImpRenderCtx *ctx);
+
+
+#endif /* IMPOSTER_H */
diff --git a/backend/impress/impress-document.c b/backend/impress/impress-document.c
new file mode 100644
index 0000000..3ea993d
--- /dev/null
+++ b/backend/impress/impress-document.c
@@ -0,0 +1,521 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005, Jonathan Blandford <jrb@gnome.org>
+ * Copyright (C) 2005, Bastien Nocera <hadess@hadess.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include "imposter.h"
+#include "impress-document.h"
+#include "ev-document-thumbnails.h"
+#include "ev-document-misc.h"
+
+struct _ImpressDocumentClass
+{
+ GObjectClass parent_class;
+};
+
+struct _ImpressDocument
+{
+ GObject parent_instance;
+
+ ImpDoc *imp;
+ ImpRenderCtx *ctx;
+
+ GMutex *mutex;
+ GdkPixmap *pixmap;
+ GdkGC *gc;
+ PangoContext *pango_ctx;
+
+ /* Only used while rendering inside the mainloop */
+ int pagenum;
+ GdkPixbuf *pixbuf;
+ GCond *cond;
+};
+
+#define PAGE_WIDTH 1024
+#define PAGE_HEIGHT 768
+
+typedef struct _ImpressDocumentClass ImpressDocumentClass;
+
+static void impress_document_document_iface_init (EvDocumentIface *iface);
+static void impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ImpressDocument, impress_document, G_TYPE_OBJECT,
+ { G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
+ impress_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+ impress_document_document_thumbnails_iface_init);
+ });
+
+/* Renderer */
+static void
+imp_render_draw_bezier_real (GdkDrawable *d, GdkGC *gc, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
+{
+ int x, y, nx, ny;
+ int ax, bx, cx, ay, by, cy;
+ double t, t2, t3;
+
+ x = x0;
+ y = y0;
+
+ cx = 3 * (x1 - x0);
+ bx = 3 * (x2 - x1) - cx;
+ ax = x3 - x0 - cx - bx;
+ cy = 3 * (y1 - y0);
+ by = 3 * (y2 - y1) - cy;
+ ay = y3 - y0 - cy - by;
+
+ for (t = 0; t < 1; t += 0.01) {
+ t2 = t * t;
+ t3 = t2 * t;
+ nx = ax * t3 + bx * t2 + cx * t + x0;
+ ny = ay * t3 + by * t2 + cy * t + y0;
+ gdk_draw_line (d, gc, x, y, nx, ny);
+ x = nx;
+ y = ny;
+ }
+}
+
+static void
+imp_render_get_size(void *drw_data, int *w, int *h)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+
+ gdk_drawable_get_size(impress_document->pixmap, w, h);
+}
+
+static void
+imp_render_set_fg_color(void *drw_data, ImpColor *color)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+ GdkColor c;
+
+ c.red = color->red;
+ c.green = color->green;
+ c.blue = color->blue;
+ gdk_gc_set_rgb_fg_color(impress_document->gc, &c);
+}
+
+static void
+imp_render_draw_line(void *drw_data, int x1, int y1, int x2, int y2)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+
+ gdk_draw_line(impress_document->pixmap, impress_document->gc, x1, y1, x2, y2);
+}
+
+static void
+imp_render_draw_rect(void *drw_data, int fill, int x, int y, int w, int h)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+
+ gdk_draw_rectangle(impress_document->pixmap, impress_document->gc, fill, x, y, w, h);
+}
+
+static void
+imp_render_draw_polygon(void *drw_data, int fill, ImpPoint *pts, int nr_pts)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+
+ gdk_draw_polygon(impress_document->pixmap, impress_document->gc, fill, (GdkPoint *)pts, nr_pts);
+}
+
+static void
+imp_render_draw_arc(void *drw_data, int fill, int x, int y, int w, int h, int sa, int ea)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+
+ gdk_draw_arc(impress_document->pixmap, impress_document->gc, fill, x, y, w, h, sa * 64, ea * 64);
+}
+
+static void
+imp_render_draw_bezier(void *drw_data, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+
+ imp_render_draw_bezier_real (impress_document->pixmap, impress_document->gc, x0, y0, x1, y1, x2, y2, x3, y3);
+}
+
+static void *
+imp_render_open_image(void *drw_data, const unsigned char *pix, size_t size)
+{
+ GdkPixbufLoader *gpl;
+ GdkPixbuf *pb;
+
+ gpl = gdk_pixbuf_loader_new();
+ gdk_pixbuf_loader_write(gpl, pix, size, NULL);
+ gdk_pixbuf_loader_close(gpl, NULL);
+ pb = gdk_pixbuf_loader_get_pixbuf(gpl);
+ return pb;
+}
+
+static void
+imp_render_get_image_size(void *drw_data, void *img_data, int *w, int *h)
+{
+ GdkPixbuf *pb = (GdkPixbuf *) img_data;
+
+ *w = gdk_pixbuf_get_width(pb);
+ *h = gdk_pixbuf_get_height(pb);
+}
+
+static void *
+imp_render_scale_image(void *drw_data, void *img_data, int w, int h)
+{
+ GdkPixbuf *pb = (GdkPixbuf *) img_data;
+
+ return gdk_pixbuf_scale_simple(pb, w, h, GDK_INTERP_BILINEAR);
+}
+
+static void
+imp_render_draw_image(void *drw_data, void *img_data, int x, int y, int w, int h)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+ GdkPixbuf *pb = (GdkPixbuf *) img_data;
+
+ gdk_draw_pixbuf(impress_document->pixmap, impress_document->gc, pb, 0, 0, x, y, w, h, GDK_RGB_DITHER_NONE, 0, 0);
+}
+
+static void
+imp_render_close_image(void *drw_data, void *img_data)
+{
+ GdkPixbuf *pb = (GdkPixbuf *) img_data;
+
+ g_object_unref(G_OBJECT(pb));
+}
+
+static char *
+imp_render_markup(const char *text, size_t len, int styles, int size)
+{
+ double scr_mm, scr_px, dpi;
+ char *esc;
+ char *ret;
+ int sz;
+
+ scr_mm = gdk_screen_get_height_mm(gdk_screen_get_default());
+ scr_px = gdk_screen_get_height(gdk_screen_get_default());
+ dpi = (scr_px / scr_mm) * 25.4;
+ sz = (int) ((double) size * 72.0 * PANGO_SCALE / dpi);
+ esc = g_markup_escape_text(text, len);
+ ret = g_strdup_printf("<span size ='%d'>%s</span>", sz, esc);
+ g_free(esc);
+ return ret;
+}
+
+static void
+imp_render_get_text_size(void *drw_data, const char *text, size_t len, int size, int styles, int *w, int *h)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+ PangoLayout *lay;
+ int pw, ph;
+ char *m;
+
+ g_return_if_fail (impress_document->pango_ctx != NULL);
+
+ lay = pango_layout_new(impress_document->pango_ctx);
+ m = imp_render_markup(text, len, styles, size);
+ pango_layout_set_markup(lay, m, strlen(m));
+ pango_layout_get_size(lay, &pw, &ph);
+ g_object_unref(lay);
+ g_free(m);
+ *w = pw / PANGO_SCALE;
+ *h = ph / PANGO_SCALE;
+}
+
+static void
+imp_render_draw_text(void *drw_data, int x, int y, const char *text, size_t len, int size, int styles)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data);
+ PangoLayout *lay;
+ char *m;
+
+ g_return_if_fail (impress_document->pango_ctx != NULL);
+
+ lay = pango_layout_new(impress_document->pango_ctx);
+ m = imp_render_markup(text, len, styles, size);
+ pango_layout_set_markup(lay, m, strlen(m));
+ gdk_draw_layout(impress_document->pixmap, impress_document->gc, x, y, lay);
+ g_object_unref(lay);
+ g_free(m);
+}
+
+static const ImpDrawer imp_render_functions = {
+ imp_render_get_size,
+ imp_render_set_fg_color,
+ imp_render_draw_line,
+ imp_render_draw_rect,
+ imp_render_draw_polygon,
+ imp_render_draw_arc,
+ imp_render_draw_bezier,
+ imp_render_open_image,
+ imp_render_get_image_size,
+ imp_render_scale_image,
+ imp_render_draw_image,
+ imp_render_close_image,
+ imp_render_get_text_size,
+ imp_render_draw_text
+};
+
+/* Document interface */
+static gboolean
+impress_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
+ gchar *filename;
+ ImpDoc *imp;
+ int err;
+
+ /* FIXME: Could we actually load uris ? */
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ {
+ //FIXME
+ //g_error_set ();
+ return FALSE;
+ }
+
+ imp = imp_open (filename, &err);
+
+ if (!imp)
+ {
+ //FIXME translate the err, set error
+ g_free (filename);
+ return FALSE;
+ }
+ impress_document->imp = imp;
+
+ return TRUE;
+}
+
+static gboolean
+impress_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ return FALSE;
+}
+
+static int
+impress_document_get_n_pages (EvDocument *document)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
+
+ g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), 0);
+ g_return_val_if_fail (impress_document->imp != NULL, 0);
+
+ return imp_nr_pages (impress_document->imp);
+}
+
+static void
+impress_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
+
+ g_return_if_fail (IMPRESS_IS_DOCUMENT (document));
+ g_return_if_fail (impress_document->imp != NULL);
+
+ //FIXME
+ *width = PAGE_WIDTH;
+ *height = PAGE_HEIGHT;
+}
+
+static gboolean
+imp_render_get_from_drawable (ImpressDocument *impress_document)
+{
+ ImpPage *page;
+
+ page = imp_get_page (impress_document->imp, impress_document->pagenum);
+
+ g_return_val_if_fail (page != NULL, FALSE);
+
+ imp_context_set_page (impress_document->ctx, page);
+ imp_render (impress_document->ctx, impress_document);
+
+ impress_document->pixbuf = gdk_pixbuf_get_from_drawable (NULL,
+ GDK_DRAWABLE (impress_document->pixmap),
+ NULL,
+ 0, 0,
+ 0, 0,
+ PAGE_WIDTH, PAGE_HEIGHT);
+ g_cond_broadcast (impress_document->cond);
+ return FALSE;
+}
+
+static GdkPixbuf *
+impress_document_render_pixbuf (EvDocument *document,
+ EvRenderContext *rc)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (document);
+ GdkPixbuf *scaled_pixbuf;
+
+ g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), 0);
+ g_return_val_if_fail (impress_document->imp != NULL, 0);
+
+ impress_document->pagenum = rc->page;
+
+ g_mutex_lock (impress_document->mutex);
+ impress_document->cond = g_cond_new ();
+
+ g_idle_add ((GSourceFunc) imp_render_get_from_drawable, impress_document);
+
+ g_cond_wait (impress_document->cond, impress_document->mutex);
+ g_cond_free (impress_document->cond);
+ g_mutex_unlock (impress_document->mutex);
+
+ scaled_pixbuf = gdk_pixbuf_scale_simple (impress_document->pixbuf,
+ PAGE_WIDTH * rc->scale,
+ PAGE_HEIGHT * rc->scale,
+ GDK_INTERP_BILINEAR);
+ gdk_pixbuf_unref (impress_document->pixbuf);
+ impress_document->pixbuf = NULL;
+
+ return scaled_pixbuf;
+}
+
+static void
+impress_document_finalize (GObject *object)
+{
+ ImpressDocument *impress_document = IMPRESS_DOCUMENT (object);
+
+ g_mutex_free (impress_document->mutex);
+
+ imp_close (impress_document->imp);
+ imp_delete_context (impress_document->ctx);
+ g_free (impress_document->pango_ctx);
+ g_object_unref (G_OBJECT (impress_document->pixmap));
+ g_object_unref (impress_document->gc);
+
+ G_OBJECT_CLASS (impress_document_parent_class)->finalize (object);
+}
+
+static void
+impress_document_class_init (ImpressDocumentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = impress_document_finalize;
+}
+
+static gboolean
+impress_document_can_get_text (EvDocument *document)
+{
+ return FALSE;
+}
+
+static EvDocumentInfo *
+impress_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+
+ info = g_new0 (EvDocumentInfo, 1);
+ info->fields_mask = 0;
+
+ return info;
+}
+
+static void
+impress_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->load = impress_document_load;
+ iface->save = impress_document_save;
+ iface->can_get_text = impress_document_can_get_text;
+ iface->get_n_pages = impress_document_get_n_pages;
+ iface->get_page_size = impress_document_get_page_size;
+ iface->render_pixbuf = impress_document_render_pixbuf;
+ iface->get_info = impress_document_get_info;
+}
+
+static GdkPixbuf *
+impress_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ gint page,
+ gint rotation,
+ gint size,
+ gboolean border)
+{
+ GdkPixbuf *pixbuf = NULL;
+ gdouble w, h;
+ EvRenderContext *rc;
+
+ impress_document_get_page_size (EV_DOCUMENT (document),
+ page,
+ &w, &h);
+
+ rc = ev_render_context_new (rotation, page, size/w);
+ pixbuf = impress_document_render_pixbuf (EV_DOCUMENT (document), rc);
+ g_object_unref (G_OBJECT (rc));
+
+ if (border)
+ {
+ GdkPixbuf *tmp_pixbuf = pixbuf;
+ pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, 0, tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+ }
+
+ return pixbuf;
+}
+
+static void
+impress_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ gint page,
+ gint suggested_width,
+ gint *width,
+ gint *height)
+{
+ gdouble page_ratio;
+ gdouble w, h;
+
+ impress_document_get_page_size (EV_DOCUMENT (document),
+ page,
+ &w, &h);
+ g_return_if_fail (w > 0);
+ page_ratio = h/w;
+ *width = suggested_width;
+ *height = (gint) (suggested_width * page_ratio);
+}
+
+static void
+impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
+{
+ iface->get_thumbnail = impress_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = impress_document_thumbnails_get_dimensions;
+}
+
+static void
+impress_document_init (ImpressDocument *impress_document)
+{
+ GdkWindow *window;
+
+ impress_document->mutex = g_mutex_new ();
+ impress_document->ctx = imp_create_context(&imp_render_functions);
+
+ window = gdk_screen_get_root_window (gdk_screen_get_default ());
+
+ impress_document->pixmap = gdk_pixmap_new (window,
+ PAGE_WIDTH, PAGE_HEIGHT, -1);
+ impress_document->gc = gdk_gc_new (impress_document->pixmap);
+ impress_document->pango_ctx = gdk_pango_context_get ();
+}
+
+/*
+ * vim: sw=2 ts=8 cindent noai bs=2
+ */
diff --git a/backend/impress/impress-document.h b/backend/impress/impress-document.h
new file mode 100644
index 0000000..7698e98
--- /dev/null
+++ b/backend/impress/impress-document.h
@@ -0,0 +1,38 @@
+
+/* pdfdocument.h: Implementation of EvDocument for impresss
+ * Copyright (C) 2005, Jonathan Blandford <jrb@gnome.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __IMPRESS_DOCUMENT_H__
+#define __IMPRESS_DOCUMENT_H__
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+#define IMPRESS_TYPE_DOCUMENT (impress_document_get_type ())
+#define IMPRESS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IMPRESS_TYPE_DOCUMENT, ImpressDocument))
+#define IMPRESS_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IMPRESS_TYPE_DOCUMENT))
+
+typedef struct _ImpressDocument ImpressDocument;
+
+ImpressDocument *impress_document_new (void);
+GType impress_document_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __IMPRESS_DOCUMENT_H__ */
diff --git a/backend/impress/internal.h b/backend/impress/internal.h
new file mode 100644
index 0000000..eb99c3e
--- /dev/null
+++ b/backend/impress/internal.h
@@ -0,0 +1,85 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "zip.h"
+
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+struct ImpDoc_struct {
+ ikstack *stack;
+ zip *zfile;
+ iks *content;
+ iks *styles;
+ iks *meta;
+ ImpPage *pages;
+ ImpPage *last_page;
+ int nr_pages;
+ void (*get_geometry)(ImpRenderCtx *ctx);
+ void (*render_page)(ImpRenderCtx *ctx, void *drw_data);
+};
+
+struct ImpPage_struct {
+ struct ImpPage_struct *next;
+ struct ImpPage_struct *prev;
+ ImpDoc *doc;
+ iks *page;
+ const char *name;
+ int nr;
+};
+
+struct ImpRenderCtx_struct {
+ const ImpDrawer *drw;
+ ImpPage *page;
+ iks *content;
+ iks *styles;
+ iks *last_element;
+ int step;
+ int pix_w, pix_h;
+ double cm_w, cm_h;
+ double fact_x, fact_y;
+};
+
+char *r_get_style (ImpRenderCtx *ctx, iks *node, char *attr);
+int r_get_color(ImpRenderCtx *ctx, iks *node, char *name, ImpColor *ic);
+void r_parse_color(const char *color, ImpColor *ic);
+int r_get_x (ImpRenderCtx *ctx, iks *node, char *name);
+int r_get_y (ImpRenderCtx *ctx, iks *node, char *name);
+int r_get_angle (iks *node, char *name, int def);
+
+enum {
+ IMP_LE_NONE = 0,
+ IMP_LE_ARROW,
+ IMP_LE_SQUARE,
+ IMP_LE_DIMENSION,
+ IMP_LE_DOUBLE_ARROW,
+ IMP_LE_SMALL_ARROW,
+ IMP_LE_ROUND_ARROW,
+ IMP_LE_SYM_ARROW,
+ IMP_LE_LINE_ARROW,
+ IMP_LE_ROUND_LARGE_ARROW,
+ IMP_LE_CIRCLE,
+ IMP_LE_SQUARE_45,
+ IMP_LE_CONCAVE_ARROW
+};
+
+void _imp_draw_rect(ImpRenderCtx *ctx, void *drw_data, int fill, int x, int y, int w, int h, int round);
+void _imp_draw_line_end(ImpRenderCtx *ctx, void *drw_data, int type, int size, int x, int y, int x2, int y2);
+void _imp_draw_image(ImpRenderCtx *ctx, void *drw_data, const char *name, int x, int y, int w, int h);
+void _imp_tile_image(ImpRenderCtx *ctx, void *drw_data, const char *name, int x, int y, int w, int h);
+
+int _imp_fill_back(ImpRenderCtx *ctx, void *drw_data, iks *node);
+void r_text(ImpRenderCtx *ctx, void *drw_data, iks *node);
+void r_polygon(ImpRenderCtx *ctx, void *drw_data, iks *node);
+void r_circle(ImpRenderCtx *ctx, void *drw_data, iks *node);
+void r_polyline(ImpRenderCtx *ctx, void *drw_data, iks *node);
+void r_draw_gradient (ImpRenderCtx *ctx, void *drw_data, iks *node);
+
+int _imp_oo13_load(ImpDoc *doc);
+int _imp_oasis_load(ImpDoc *doc);
+
+
+#endif /* INTERNAL_H */
diff --git a/backend/impress/r_back.c b/backend/impress/r_back.c
new file mode 100644
index 0000000..29a30bb
--- /dev/null
+++ b/backend/impress/r_back.c
@@ -0,0 +1,45 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+
+int
+_imp_fill_back(ImpRenderCtx *ctx, void *drw_data, iks *node)
+{
+ ImpColor col;
+ char *type;
+ char *stil, *gfx;
+ iks *x;
+
+ type = r_get_style(ctx, node, "draw:fill");
+ if (type == 0) return 0;
+
+ if (strcmp(type, "solid") == 0) {
+ if (r_get_color(ctx, node, "draw:fill-color", &col)) {
+ ctx->drw->set_fg_color(drw_data, &col);
+ }
+ ctx->drw->draw_rect(drw_data, 1, 0, 0, ctx->pix_w, ctx->pix_h);
+ } else if (strcmp (type, "bitmap") == 0) {
+ stil = r_get_style(ctx, node, "draw:fill-image-name");
+ x = iks_find_with_attrib(iks_find(ctx->styles, "office:styles"),
+ "draw:fill-image", "draw:name", stil
+ );
+ gfx = iks_find_attrib(x, "xlink:href");
+ if (gfx) {
+ if (iks_strcmp(r_get_style(ctx, node, "style:repeat"), "stretch") == 0) {
+ _imp_draw_image(ctx, drw_data, gfx, 0, 0, ctx->pix_w, ctx->pix_h);
+ } else {
+ _imp_tile_image(ctx, drw_data, gfx, 0, 0, ctx->pix_w, ctx->pix_h);
+ }
+ }
+ } else if (strcmp(type, "gradient") == 0) {
+ r_draw_gradient(ctx, drw_data, node);
+ } else {
+ return 0;
+ }
+ return 1;
+}
diff --git a/backend/impress/r_draw.c b/backend/impress/r_draw.c
new file mode 100644
index 0000000..746afbd
--- /dev/null
+++ b/backend/impress/r_draw.c
@@ -0,0 +1,119 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+#include <math.h>
+
+void
+_imp_draw_rect(ImpRenderCtx *ctx, void *drw_data, int fill, int x, int y, int w, int h, int round)
+{
+ int a;
+
+ if (0 == round) {
+ ctx->drw->draw_rect(drw_data, fill, x, y, w, h);
+ return;
+ }
+
+ ctx->drw->draw_arc(drw_data, fill,
+ x, y, round, round, 90, 90);
+ ctx->drw->draw_arc(drw_data, fill,
+ x + w - round, y, round, round, 0, 90);
+ ctx->drw->draw_arc(drw_data, fill,
+ x + w - round, y + h - round, round, round, 270, 90);
+ ctx->drw->draw_arc(drw_data, fill,
+ x, y + h - round, round, round, 180, 90);
+
+ a = round / 2;
+ if (fill) {
+ ctx->drw->draw_rect(drw_data, 1, x + a, y, w - a - a, h);
+ ctx->drw->draw_rect(drw_data, 1, x, y + a, w, h - a - a);
+ return;
+ }
+ ctx->drw->draw_line(drw_data, x + a, y, x + w - a, y);
+ ctx->drw->draw_line(drw_data, x + a, y + h, x + w - a, y + h);
+ ctx->drw->draw_line(drw_data, x, y + a, x, y + h - a);
+ ctx->drw->draw_line(drw_data, x + w, y + a, x + w, y + h - a);
+}
+
+void
+_imp_draw_line_end(ImpRenderCtx *ctx, void *drw_data, int type, int size, int x, int y, int x2, int y2)
+{
+ ImpPoint pts[4];
+ double ia, a;
+
+ // FIXME: different types and sizes
+
+ pts[0].x = x2;
+ pts[0].y = y2;
+
+ ia = 20 * 3.14 * 2 / 360;
+
+ if (x2-x == 0) {
+ if (y < y2) a = 3.14 + (3.14 / 2); else a = (3.14 / 2);
+ } else if (y2-y == 0) {
+ if (x < x2) a = 3.14; else a = 0;
+ } else
+ a = atan ((y2-y) / (x2-x)) - 3.14;
+
+ pts[1].x = x2 + 0.3 * ctx->fact_x * cos (a - ia);
+ pts[1].y = y2 + 0.3 * ctx->fact_y * sin (a - ia);
+
+ pts[2].x = x2 + 0.3 * ctx->fact_x * cos (a + ia);
+ pts[2].y = y2 + 0.3 * ctx->fact_y * sin (a + ia);
+
+ ctx->drw->draw_polygon(drw_data, 1, pts, 3);
+}
+
+void
+_imp_draw_image(ImpRenderCtx *ctx, void *drw_data, const char *name, int x, int y, int w, int h)
+{
+ void *img1, *img2;
+ char *pix;
+ size_t len;
+
+ len = zip_get_size(ctx->page->doc->zfile, name);
+ pix = malloc(len);
+ if (!pix) return;
+ zip_load(ctx->page->doc->zfile, name, pix);
+
+ img1 = ctx->drw->open_image(drw_data, pix, len);
+ free(pix);
+ if (!img1) return;
+ img2 = ctx->drw->scale_image(drw_data, img1, w, h);
+ if (img2) {
+ ctx->drw->draw_image(drw_data, img2, x, y, w, h);
+ ctx->drw->close_image(drw_data, img2);
+ }
+ ctx->drw->close_image(drw_data, img1);
+}
+
+void
+_imp_tile_image(ImpRenderCtx *ctx, void *drw_data, const char *name, int x, int y, int w, int h)
+{
+ void *img1;
+ char *pix;
+ size_t len;
+ int gx, gy, gw, gh;
+
+ len = zip_get_size(ctx->page->doc->zfile, name);
+ pix = malloc(len);
+ if (!pix) return;
+ zip_load(ctx->page->doc->zfile, name, pix);
+
+ img1 = ctx->drw->open_image(drw_data, pix, len);
+ free(pix);
+ if (!img1) return;
+
+ ctx->drw->get_image_size(drw_data, img1, &gw, &gh);
+ for (gx = x; gx < w; gx += gw) {
+ for (gy = y; gy < h; gy += gh) {
+ ctx->drw->draw_image(drw_data, img1, gx, gy, gw, gh);
+ }
+ }
+
+ ctx->drw->close_image(drw_data, img1);
+}
diff --git a/backend/impress/r_geometry.c b/backend/impress/r_geometry.c
new file mode 100644
index 0000000..6662de6
--- /dev/null
+++ b/backend/impress/r_geometry.c
@@ -0,0 +1,207 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+#include <math.h>
+
+void
+r_parse_color(const char *color, ImpColor *ic)
+{
+ unsigned int cval;
+
+ if (1 != sscanf(color, "#%X", &cval)) return;
+
+ ic->red = (cval & 0xFF0000) >> 8;
+ ic->green = cval & 0x00FF00;
+ ic->blue = (cval & 0xFF) << 8;
+}
+
+int
+r_get_color(ImpRenderCtx *ctx, iks *node, char *name, ImpColor *ic)
+{
+ char *color;
+
+ color = r_get_style(ctx, node, name);
+ if (!color) return 0;
+ r_parse_color(color, ic);
+
+ return 1;
+}
+
+static void
+fg_color(ImpRenderCtx *ctx, void *drw_data, iks *node, char *name)
+{
+ ImpColor ic;
+
+ if (r_get_color(ctx, node, name, &ic)) {
+ ctx->drw->set_fg_color(drw_data, &ic);
+ }
+}
+
+int
+r_get_x (ImpRenderCtx *ctx, iks *node, char *name)
+{
+ char *val;
+
+ val = iks_find_attrib (node, name);
+ if (!val) return 0;
+ return atof (val) * ctx->fact_x;
+}
+
+int
+r_get_y (ImpRenderCtx *ctx, iks *node, char *name)
+{
+ char *val;
+
+ val = iks_find_attrib (node, name);
+ if (!val) return 0;
+ return atof (val) * ctx->fact_y;
+}
+
+int
+r_get_angle (iks *node, char *name, int def)
+{
+ char *tmp;
+
+ tmp = iks_find_attrib (node, name);
+ if (!tmp) return def;
+ return atof (tmp);
+}
+
+static int x, y, w, h;
+static int px, py, pw, ph;
+
+static void
+r_get_viewbox (iks *node)
+{
+ char *tmp;
+
+ tmp = iks_find_attrib (node, "svg:viewBox");
+ if (!tmp) return;
+ sscanf (tmp, "%d %d %d %d", &px, &py, &pw, &ph);
+}
+
+void
+r_polygon(ImpRenderCtx *ctx, void *drw_data, iks *node)
+{
+ char *data;
+ ImpPoint *points;
+ int i, cnt, j;
+ int num;
+ int fill = 1;
+
+ data = r_get_style (ctx, node, "draw:fill");
+ if (!data || strcmp (data, "solid") != 0) fill = 0;
+
+ x = r_get_x (ctx, node, "svg:x");
+ y = r_get_y (ctx, node, "svg:y");
+ w = r_get_x (ctx, node, "svg:width");
+ h = r_get_y (ctx, node, "svg:height");
+ r_get_viewbox (node);
+
+ data = iks_find_attrib (node, "draw:points");
+ points = malloc (sizeof (ImpPoint) * strlen (data) / 4);
+
+ cnt = 0;
+ j = 0;
+ num = -1;
+ for (i = 0; data[i]; i++) {
+ if (data[i] >= '0' && data[i] <= '9') {
+ if (num == -1) num = i;
+ } else {
+ if (num != -1) {
+ if (j == 0) {
+ points[cnt].x = atoi (data + num);
+ j = 1;
+ } else {
+ points[cnt++].y = atoi (data + num);
+ j = 0;
+ }
+ num = -1;
+ }
+ }
+ }
+ if (num != -1) {
+ if (j == 0) {
+ points[cnt].x = atoi (data + num);
+ } else {
+ points[cnt++].y = atoi (data + num);
+ }
+ }
+ for (i = 0; i < cnt; i++) {
+ points[i].x = x + points[i].x * w / pw;
+ points[i].y = y + points[i].y * h / ph;
+ }
+
+ if (fill) {
+ fg_color(ctx, drw_data, node, "draw:fill-color");
+ ctx->drw->draw_polygon(drw_data, 1, points, cnt);
+ }
+ fg_color(ctx, drw_data, node, "svg:stroke-color");
+ ctx->drw->draw_polygon(drw_data, 0, points, cnt);
+
+ free (points);
+}
+
+void
+r_polyline(ImpRenderCtx *ctx, void *drw_data, iks *node)
+{
+ char *data;
+ ImpPoint *points;
+ int i, cnt, j;
+ int num;
+ int pen_x, pen_y;
+
+ x = r_get_x (ctx, node, "svg:x");
+ y = r_get_y (ctx, node, "svg:y");
+ w = r_get_x (ctx, node, "svg:width");
+ h = r_get_y (ctx, node, "svg:height");
+ r_get_viewbox (node);
+
+ data = iks_find_attrib (node, "draw:points");
+ points = malloc (sizeof (ImpPoint) * strlen (data) / 4);
+
+ cnt = 0;
+ j = 0;
+ num = -1;
+ for (i = 0; data[i]; i++) {
+ if (data[i] >= '0' && data[i] <= '9') {
+ if (num == -1) num = i;
+ } else {
+ if (num != -1) {
+ if (j == 0) {
+ points[cnt].x = atoi (data + num);
+ j = 1;
+ } else {
+ points[cnt++].y = atoi (data + num);
+ j = 0;
+ }
+ num = -1;
+ }
+ }
+ }
+ if (num != -1) {
+ if (j == 0) {
+ points[cnt].x = atoi (data + num);
+ } else {
+ points[cnt++].y = atoi (data + num);
+ }
+ }
+
+ pen_x = x + points[0].x * w /pw;
+ pen_y = y + points[0].y * h / ph;
+ fg_color(ctx, drw_data, node, "svg:stroke-color");
+ for (i = 1; i < cnt; i++) {
+ int tx, ty;
+ tx = x + points[i].x * w / pw;
+ ty = y + points[i].y * h / ph;
+ ctx->drw->draw_line(drw_data, pen_x, pen_y, tx, ty);
+ pen_x = tx;
+ pen_y = ty;
+ }
+ free (points);
+}
diff --git a/backend/impress/r_gradient.c b/backend/impress/r_gradient.c
new file mode 100644
index 0000000..f6b9af2
--- /dev/null
+++ b/backend/impress/r_gradient.c
@@ -0,0 +1,386 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+#include <math.h>
+
+#define GRAD_LINEAR 0
+#define GRAD_AXIAL 1
+#define GRAD_SQUARE 2
+#define GRAD_RECTANGULAR 3
+#define GRAD_RADIAL 4
+#define GRAD_ELLIPTICAL 5
+
+typedef struct Gradient_s {
+ int type;
+ ImpColor start;
+ int start_intensity;
+ ImpColor end;
+ int end_intensity;
+ int angle;
+ int border;
+ int steps;
+ int offset_x;
+ int offset_y;
+} Gradient;
+
+typedef struct Rectangle_s {
+ int Left;
+ int Top;
+ int Right;
+ int Bottom;
+} Rectangle;
+
+static void
+poly_rotate (ImpPoint *poly, int n, int cx, int cy, double fAngle)
+{
+ int i;
+ long nX, nY;
+
+ for (i = 0; i < n; i++) {
+ nX = poly->x - cx;
+ nY = poly->y - cy;
+ poly->x = (cos(fAngle) * nX + sin(fAngle) * nY) + cx;
+ poly->y = - (sin(fAngle)* nX - cos(fAngle) * nY) + cy;
+ poly++;
+ }
+}
+
+static void
+r_draw_gradient_simple (ImpRenderCtx *ctx, void *drw_data, Gradient *grad)
+{
+ Rectangle rRect = { 0, 0, ctx->pix_w - 1, ctx->pix_h - 1 };
+ Rectangle aRect, aFullRect;
+ ImpPoint poly[4], tempoly[2];
+ ImpColor gcol;
+ double fW, fH, fDX, fDY, fAngle;
+ double fScanLine, fScanInc;
+ long redSteps, greenSteps, blueSteps;
+ long nBorder;
+ int i, nSteps, nSteps2;
+ int cx, cy;
+
+ cx = rRect.Left + (rRect.Right - rRect.Left) / 2;
+ cy = rRect.Top + (rRect.Bottom - rRect.Top) / 2;
+
+ aRect = rRect;
+ aRect.Top--; aRect.Left--; aRect.Bottom++; aRect.Right++;
+ fW = rRect.Right - rRect.Left;
+ fH = rRect.Bottom - rRect.Top;
+ fAngle = (((double) grad->angle) * 3.14 / 1800.0);
+ fDX = fW * fabs (cos (fAngle)) + fH * fabs (sin (fAngle));
+ fDY = fH * fabs (cos (fAngle)) + fW * fabs (sin (fAngle));
+ fDX = (fDX - fW) * 0.5 - 0.5;
+ fDY = (fDY - fH) * 0.5 - 0.5;
+ aRect.Left -= fDX;
+ aRect.Right += fDX;
+ aRect.Top -= fDY;
+ aRect.Bottom += fDY;
+ aFullRect = aRect;
+
+ nBorder = grad->border * (aRect.Bottom - aRect.Top) / 100;
+ if (grad->type == GRAD_LINEAR) {
+ aRect.Top += nBorder;
+ } else {
+ nBorder >>= 1;
+ aRect.Top += nBorder;
+ aRect.Bottom -= nBorder;
+ }
+
+ if (aRect.Top > (aRect.Bottom - 1))
+ aRect.Top = aRect.Bottom - 1;
+
+ poly[0].x = aFullRect.Left;
+ poly[0].y = aFullRect.Top;
+ poly[1].x = aFullRect.Right;
+ poly[1].y = aFullRect.Top;
+ poly[2].x = aRect.Right;
+ poly[2].y = aRect.Top;
+ poly[3].x = aRect.Left;
+ poly[3].y = aRect.Top;
+ poly_rotate (&poly[0], 4, cx, cy, fAngle);
+
+ redSteps = grad->end.red - grad->start.red;
+ greenSteps = grad->end.green - grad->start.green;
+ blueSteps = grad->end.blue - grad->start.blue;
+ nSteps = grad->steps;
+ if (nSteps == 0) {
+ long mr;
+ mr = aRect.Bottom - aRect.Top;
+ if (mr < 50)
+ nSteps = mr / 2;
+ else
+ nSteps = mr / 4;
+ mr = abs(redSteps);
+ if (abs(greenSteps) > mr) mr = abs(greenSteps);
+ if (abs(blueSteps) > mr) mr = abs(blueSteps);
+ if (mr < nSteps) nSteps = mr;
+ }
+
+ if (grad->type == GRAD_AXIAL) {
+ if (nSteps & 1) nSteps++;
+ nSteps2 = nSteps + 2;
+ gcol = grad->end;
+ redSteps <<= 1;
+ greenSteps <<= 1;
+ blueSteps <<= 1;
+ } else {
+ nSteps2 = nSteps + 1;
+ gcol = grad->start;
+ }
+
+ fScanLine = aRect.Top;
+ fScanInc = (double)(aRect.Bottom - aRect.Top) / (double)nSteps;
+
+ for (i = 0; i < nSteps2; i++) {
+ // draw polygon
+ ctx->drw->set_fg_color(drw_data, &gcol);
+ ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4);
+ // calc next polygon
+ aRect.Top = (long)(fScanLine += fScanInc);
+ if (i == nSteps) {
+ tempoly[0].x = aFullRect.Left;
+ tempoly[0].y = aFullRect.Bottom;
+ tempoly[1].x = aFullRect.Right;
+ tempoly[1].y = aFullRect.Bottom;
+ } else {
+ tempoly[0].x = aRect.Left;
+ tempoly[0].y = aRect.Top;
+ tempoly[1].x = aRect.Right;
+ tempoly[1].y = aRect.Top;
+ }
+ poly_rotate (&tempoly[0], 2, cx, cy, fAngle);
+ poly[0] = poly[3];
+ poly[1] = poly[2];
+ poly[2] = tempoly[1];
+ poly[3] = tempoly[0];
+ // calc next color
+ if (grad->type == GRAD_LINEAR) {
+ gcol.red = grad->start.red + ((redSteps * i) / nSteps2);
+ gcol.green = grad->start.green + ((greenSteps * i) / nSteps2);
+ gcol.blue = grad->start.blue + ((blueSteps * i) / nSteps2);
+ } else {
+ if (i >= nSteps) {
+ gcol.red = grad->end.red;
+ gcol.green = grad->end.green;
+ gcol.blue = grad->end.blue;
+ } else {
+ if (i <= (nSteps / 2)) {
+ gcol.red = grad->end.red - ((redSteps * i) / nSteps2);
+ gcol.green = grad->end.green - ((greenSteps * i) / nSteps2);
+ gcol.blue = grad->end.blue - ((blueSteps * i) / nSteps2);
+ } else {
+ int i2 = i - nSteps / 2;
+ gcol.red = grad->start.red + ((redSteps * i2) / nSteps2);
+ gcol.green = grad->start.green + ((greenSteps * i2) / nSteps2);
+ gcol.blue = grad->start.blue + ((blueSteps * i2) / nSteps2);
+ }
+ }
+ }
+ }
+}
+
+static void
+r_draw_gradient_complex (ImpRenderCtx *ctx, void *drw_data, Gradient *grad)
+{
+ Rectangle rRect = { 0, 0, ctx->pix_w - 1, ctx->pix_h - 1 };
+ Rectangle aRect = rRect;
+ ImpColor gcol;
+ ImpPoint poly[4];
+ double fAngle = (((double) grad->angle) * 3.14 / 1800.0);
+ long redSteps, greenSteps, blueSteps;
+ long nZW, nZH;
+ long bX, bY;
+ long sW, sH;
+ long cx, cy;
+ int i;
+ long nSteps;
+ double sTop, sLeft, sRight, sBottom, sInc;
+ int minRect;
+
+ redSteps = grad->end.red - grad->start.red;
+ greenSteps = grad->end.green - grad->start.green;
+ blueSteps = grad->end.blue - grad->start.blue;
+
+ if (grad->type == GRAD_SQUARE || grad->type == GRAD_RECTANGULAR) {
+ double fW = aRect.Right - aRect.Left;
+ double fH = aRect.Bottom - aRect.Top;
+ double fDX = fW * fabs (cos (fAngle)) + fH * fabs (sin (fAngle));
+ double fDY = fH * fabs (cos (fAngle)) + fW * fabs (sin (fAngle));
+ fDX = (fDX - fW) * 0.5 - 0.5;
+ fDY = (fDY - fH) * 0.5 - 0.5;
+ aRect.Left -= fDX;
+ aRect.Right += fDX;
+ aRect.Top -= fDY;
+ aRect.Bottom += fDY;
+ }
+
+ sW = aRect.Right - aRect.Left;
+ sH = aRect.Bottom - aRect.Top;
+
+ if (grad->type == GRAD_SQUARE) {
+ if (sW > sH) sH = sW; else sW = sH;
+ } else if (grad->type == GRAD_RADIAL) {
+ sW = 0.5 + sqrt ((double)sW*(double)sW + (double)sH*(double)sH);
+ sH = sW;
+ } else if (grad->type == GRAD_ELLIPTICAL) {
+ sW = 0.5 + (double)sW * 1.4142;
+ sH = 0.5 + (double)sH * 1.4142;
+ }
+
+ nZW = (aRect.Right - aRect.Left) * grad->offset_x / 100;
+ nZH = (aRect.Bottom - aRect.Top) * grad->offset_y / 100;
+ bX = grad->border * sW / 100;
+ bY = grad->border * sH / 100;
+ cx = aRect.Left + nZW;
+ cy = aRect.Top + nZH;
+
+ sW -= bX;
+ sH -= bY;
+
+ aRect.Left = cx - ((aRect.Right - aRect.Left) >> 1);
+ aRect.Top = cy - ((aRect.Bottom - aRect.Top) >> 1);
+
+ nSteps = grad->steps;
+ minRect = aRect.Right - aRect.Left;
+ if (aRect.Bottom - aRect.Top < minRect) minRect = aRect.Bottom - aRect.Top;
+ if (nSteps == 0) {
+ long mr;
+ if (minRect < 50)
+ nSteps = minRect / 2;
+ else
+ nSteps = minRect / 4;
+ mr = abs(redSteps);
+ if (abs(greenSteps) > mr) mr = abs(greenSteps);
+ if (abs(blueSteps) > mr) mr = abs(blueSteps);
+ if (mr < nSteps) nSteps = mr;
+ }
+
+ sLeft = aRect.Left;
+ sTop = aRect.Top;
+ sRight = aRect.Right;
+ sBottom = aRect.Bottom;
+ sInc = (double) minRect / (double) nSteps * 0.5;
+
+ gcol = grad->start;
+ poly[0].x = rRect.Left;
+ poly[0].y = rRect.Top;
+ poly[1].x = rRect.Right;
+ poly[1].y = rRect.Top;
+ poly[2].x = rRect.Right;
+ poly[2].y = rRect.Bottom;
+ poly[3].x = rRect.Left;
+ poly[3].y = rRect.Bottom;
+ ctx->drw->set_fg_color(drw_data, &gcol);
+ ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4);
+
+ for (i = 0; i < nSteps; i++) {
+ aRect.Left = (long) (sLeft += sInc);
+ aRect.Top = (long) (sTop += sInc);
+ aRect.Right = (long) (sRight -= sInc);
+ aRect.Bottom = (long) (sBottom -= sInc);
+ if (aRect.Bottom - aRect.Top < 2 || aRect.Right - aRect.Left < 2)
+ break;
+
+ gcol.red = grad->start.red + (redSteps * (i+1) / nSteps);
+ gcol.green = grad->start.green + (greenSteps * (i+1) / nSteps);
+ gcol.blue = grad->start.blue + (blueSteps * (i+1) / nSteps);
+ ctx->drw->set_fg_color(drw_data, &gcol);
+
+ if (grad->type == GRAD_RADIAL || grad->type == GRAD_ELLIPTICAL) {
+ ctx->drw->draw_arc(drw_data, 1, aRect.Left, aRect.Top,
+ aRect.Right - aRect.Left, aRect.Bottom - aRect.Top,
+ 0, 360);
+ } else {
+ poly[0].x = aRect.Left;
+ poly[0].y = aRect.Top;
+ poly[1].x = aRect.Right;
+ poly[1].y = aRect.Top;
+ poly[2].x = aRect.Right;
+ poly[2].y = aRect.Bottom;
+ poly[3].x = aRect.Left;
+ poly[3].y = aRect.Bottom;
+ poly_rotate (&poly[0], 4, cx, cy, fAngle);
+ ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4);
+ }
+ }
+}
+
+void
+r_draw_gradient (ImpRenderCtx *ctx, void *drw_data, iks *node)
+{
+// GdkGC *gc;
+ Gradient grad;
+ char *stil, *tmp;
+ iks *x;
+
+ stil = r_get_style (ctx, node, "draw:fill-gradient-name");
+ x = iks_find_with_attrib (iks_find (ctx->styles, "office:styles"),
+ "draw:gradient", "draw:name", stil);
+ if (x) {
+ memset (&grad, 0, sizeof (Gradient));
+ grad.type = -1;
+ grad.offset_x = 50;
+ grad.offset_y = 50;
+
+ tmp = iks_find_attrib (x, "draw:start-color");
+ if (tmp) r_parse_color (tmp, &grad.start);
+ tmp = iks_find_attrib (x, "draw:start-intensity");
+ if (tmp) {
+ int val = atoi (tmp);
+ grad.start.red = grad.start.red * val / 100;
+ grad.start.green = grad.start.green * val / 100;
+ grad.start.blue = grad.start.blue * val / 100;
+ }
+ tmp = iks_find_attrib (x, "draw:end-color");
+ if (tmp) r_parse_color (tmp, &grad.end);
+ tmp = iks_find_attrib (x, "draw:end-intensity");
+ if (tmp) {
+ int val = atoi (tmp);
+ grad.end.red = grad.end.red * val / 100;
+ grad.end.green = grad.end.green * val / 100;
+ grad.end.blue = grad.end.blue * val / 100;
+ }
+ tmp = iks_find_attrib (x, "draw:angle");
+ if (tmp) grad.angle = atoi(tmp) % 3600;
+ tmp = iks_find_attrib (x, "draw:border");
+ if (tmp) grad.border = atoi(tmp);
+ tmp = r_get_style (ctx, node, "draw:gradient-step-count");
+ if (tmp) grad.steps = atoi (tmp);
+ tmp = iks_find_attrib (x, "draw:cx");
+ if (tmp) grad.offset_x = atoi (tmp);
+ tmp = iks_find_attrib (x, "draw:cy");
+ if (tmp) grad.offset_y = atoi (tmp);
+ tmp = iks_find_attrib (x, "draw:style");
+ if (iks_strcmp (tmp, "linear") == 0)
+ grad.type = GRAD_LINEAR;
+ else if (iks_strcmp (tmp, "axial") == 0)
+ grad.type = GRAD_AXIAL;
+ else if (iks_strcmp (tmp, "radial") == 0)
+ grad.type = GRAD_RADIAL;
+ else if (iks_strcmp (tmp, "rectangular") == 0)
+ grad.type = GRAD_RECTANGULAR;
+ else if (iks_strcmp (tmp, "ellipsoid") == 0)
+ grad.type = GRAD_ELLIPTICAL;
+ else if (iks_strcmp (tmp, "square") == 0)
+ grad.type = GRAD_SQUARE;
+
+ if (grad.type == -1) return;
+
+// gc = ctx->gc;
+// ctx->gc = gdk_gc_new (ctx->d);
+// gdk_gc_copy (ctx->gc, gc);
+
+ if (grad.type == GRAD_LINEAR || grad.type == GRAD_AXIAL)
+ r_draw_gradient_simple (ctx, drw_data, &grad);
+ else
+ r_draw_gradient_complex (ctx, drw_data, &grad);
+
+// gdk_gc_unref (ctx->gc);
+// ctx->gc = gc;
+ }
+}
diff --git a/backend/impress/r_style.c b/backend/impress/r_style.c
new file mode 100644
index 0000000..570c000
--- /dev/null
+++ b/backend/impress/r_style.c
@@ -0,0 +1,110 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+
+static char *
+get_style(ImpRenderCtx *ctx, iks *node, char *style, char *attr)
+{
+ char *ret;
+ iks *x;
+
+ if (!style) return NULL;
+
+ if (iks_root (node) == ctx->content) {
+ x = iks_find_with_attrib (iks_find (ctx->content, "office:automatic-styles"),
+ "style:style", "style:name", style);
+ } else {
+ x = iks_find_with_attrib (iks_find (ctx->styles, "office:automatic-styles"),
+ "style:style", "style:name", style);
+ }
+ if (!x) return NULL;
+
+ while (x) {
+ ret = iks_find_attrib (iks_find (x, "style:properties"), attr);
+ if (ret) return ret;
+ ret = iks_find_attrib (iks_find (x, "style:text-properties"), attr);
+ if (ret) return ret;
+ ret = iks_find_attrib (iks_find (x, "style:paragraph-properties"), attr);
+ if (ret) return ret;
+ ret = iks_find_attrib (iks_find (x, "style:graphic-properties"), attr);
+ if (ret) return ret;
+ ret = iks_find_attrib (iks_find (x, "style:drawing-page-properties"), attr);
+ if (ret) return ret;
+
+ style = iks_find_attrib (x, "style:parent-style-name");
+ if (!style) return NULL;
+
+ x = iks_find_with_attrib (iks_find (ctx->styles, "office:styles"),
+ "style:style", "style:name", style);
+
+ }
+ return NULL;
+}
+
+char *
+r_get_style (ImpRenderCtx *ctx, iks *node, char *attr)
+{
+ char *ret, *s;
+ iks *x;
+
+ ret = iks_find_attrib (node, attr);
+ if (ret) return ret;
+
+ for (x = node; x; x = iks_parent (x)) {
+ s = iks_find_attrib (x, "text:style-name");
+ ret = get_style (ctx, node, s, attr);
+ if (ret) return ret;
+ s = iks_find_attrib (x, "presentation:style-name");
+ ret = get_style (ctx, node, s, attr);
+ if (ret) return ret;
+ s = iks_find_attrib (x, "draw:style-name");
+ ret = get_style (ctx, node, s, attr);
+ if (ret) return ret;
+ }
+ return NULL;
+}
+
+#if 0
+static iks *
+get_style_x (ImpRenderCtx *ctx, iks *node, char *style, char *attr)
+{
+ iks *x;
+
+ if (!style) return NULL;
+
+ if (iks_root (node) == ctx->content) {
+ x = iks_find_with_attrib (iks_find (ctx->content, "office:automatic-styles"),
+ "text:list-style", "style:name", style);
+ } else {
+ x = iks_find_with_attrib (iks_find (ctx->styles, "office:automatic-styles"),
+ "text:list-style", "style:name", style);
+ }
+ return x;
+}
+
+static iks *
+r_get_bullet (ImpRenderCtx *ctx, iks *node, char *attr)
+{
+ iks *ret;
+ char *s;
+ iks *x;
+
+ for (x = node; x; x = iks_parent (x)) {
+ s = iks_find_attrib (x, "text:style-name");
+ ret = get_style_x (ctx, node, s, attr);
+ if (ret) return ret;
+ s = iks_find_attrib (x, "presentation:style-name");
+ ret = get_style_x (ctx, node, s, attr);
+ if (ret) return ret;
+ s = iks_find_attrib (x, "draw:style-name");
+ ret = get_style_x (ctx, node, s, attr);
+ if (ret) return ret;
+ }
+ return NULL;
+}
+#endif
diff --git a/backend/impress/r_text.c b/backend/impress/r_text.c
new file mode 100644
index 0000000..e08fd15
--- /dev/null
+++ b/backend/impress/r_text.c
@@ -0,0 +1,385 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+
+struct Span {
+ struct Span *next;
+ int x, y;
+ int w, h;
+ char *text;
+ int len;
+ int size;
+ int styles;
+ ImpColor fg;
+};
+
+struct Line {
+ struct Line *next;
+ struct Span *spans;
+ struct Span *last_span;
+ int x, y;
+ int w, h;
+};
+
+struct Layout {
+ ikstack *s;
+ int x, y, w, h;
+ int tw, th;
+ struct Line *lines;
+ struct Line *last_line;
+ char spaces[128];
+};
+
+static struct Line *
+add_line(struct Layout *lay)
+{
+ struct Line *line;
+
+ line = iks_stack_alloc(lay->s, sizeof(struct Line));
+ memset(line, 0, sizeof(struct Line));
+
+ if (!lay->lines) lay->lines = line;
+ if (lay->last_line) lay->last_line->next = line;
+ lay->last_line = line;
+
+ return line;
+}
+
+static struct Span *
+add_span(struct Layout *lay, char *text, int len, int size, int styles)
+{
+ struct Line *line;
+ struct Span *span;
+
+ span = iks_stack_alloc(lay->s, sizeof(struct Span));
+ memset(span, 0, sizeof(struct Span));
+ span->text = text;
+ span->len = len;
+ span->size = size;
+ span->styles = styles;
+
+ line = lay->last_line;
+ if (!line) line = add_line(lay);
+ if (line->spans) {
+ span->x = line->last_span->x + line->last_span->w;
+ span->y = line->last_span->y;
+ } else {
+ span->x = line->x;
+ span->y = line->y;
+ }
+
+ if (!line->spans) line->spans = span;
+ if (line->last_span) line->last_span->next = span;
+ line->last_span = span;
+
+ return span;
+}
+
+static void
+calc_sizes(ImpRenderCtx *ctx, void *drw_data, struct Layout *lay)
+{
+ struct Line *line;
+ struct Span *span;
+
+ for (line = lay->lines; line; line = line->next) {
+ for (span = line->spans; span; span = span->next) {
+ ctx->drw->get_text_size(drw_data,
+ span->text, span->len,
+ span->size, span->styles,
+ &span->w, &span->h
+ );
+ line->w += span->w;
+ if (span->h > line->h) line->h = span->h;
+ }
+ if (line->w > lay->tw) lay->tw = line->w;
+ lay->th += line->h;
+ }
+}
+
+static void
+calc_pos(ImpRenderCtx *ctx, struct Layout *lay)
+{
+ struct Line *line;
+ struct Span *span;
+ int x, y, x2;
+
+ x = lay->x;
+ y = lay->y;
+ for (line = lay->lines; line; line = line->next) {
+ line->x = x;
+ line->y = y;
+ y += line->h;
+ x2 = x;
+ for (span = line->spans; span; span = span->next) {
+ span->x = x2;
+ span->y = y;
+ x2 += span->w;
+ }
+ }
+}
+
+static void
+_imp_draw_layout(ImpRenderCtx *ctx, void *drw_data, struct Layout *lay)
+{
+ struct Line *line;
+ struct Span *span;
+
+ for (line = lay->lines; line; line = line->next) {
+ for (span = line->spans; span; span = span->next) {
+ ctx->drw->set_fg_color(drw_data, &span->fg);
+ ctx->drw->draw_text(drw_data,
+ span->x, span->y,
+ span->text, span->len,
+ span->size,
+ span->styles
+ );
+ }
+ }
+}
+
+static void
+text_span(ImpRenderCtx *ctx, struct Layout *lay, iks *node, char *text, size_t len)
+{
+ struct Span *span;
+ double cm;
+ char *attr, *t, *s;
+ int px = 0, cont = 1;
+ int styles = IMP_NORMAL;
+
+ attr = r_get_style(ctx, node, "fo:font-size");
+ if (attr) {
+ cm = atof(attr);
+ if (strstr(attr, "pt")) cm = cm * 2.54 / 102;
+ px = cm * ctx->fact_y;
+ }
+ attr = r_get_style(ctx, node, "fo:font-weight");
+ if (attr && strcmp(attr, "bold") == 0) styles |= IMP_BOLD;
+ attr = r_get_style(ctx, node, "style:text-underline");
+ if (attr && strcmp(attr, "single") == 0) styles |= IMP_UNDERLINE;
+ attr = r_get_style(ctx, node, "fo:font-style");
+ if (attr && strcmp(attr, "italic") == 0) styles |= IMP_ITALIC;
+
+ t = text;
+ while (cont) {
+ s = strchr(t, '\n');
+ if (s) {
+ int len2 = s - t;
+ span = add_span(lay, t, len2, px, styles);
+ t = s + 1;
+ len -= len2;
+ add_line(lay);
+ } else {
+ span = add_span(lay, text, len, px, styles);
+ cont = 0;
+ }
+ r_get_color(ctx, node, "fo:color", &span->fg);
+ }
+}
+
+static void
+text_p(ImpRenderCtx *ctx, struct Layout *lay, iks *node)
+{
+ iks *n, *n2;
+
+ add_line(lay);
+ for (n = iks_child(node); n; n = iks_next(n)) {
+ if (iks_type(n) == IKS_CDATA) {
+ text_span(ctx, lay, node, iks_cdata(n), iks_cdata_size(n));
+ } else if (iks_strcmp(iks_name(n), "text:span") == 0) {
+ for (n2 = iks_child(n); n2; n2 = iks_next(n2)) {
+ if (iks_type(n2) == IKS_CDATA) {
+ text_span(ctx, lay, n2, iks_cdata(n2), iks_cdata_size(n2));
+ } else if (iks_strcmp(iks_name(n2), "text:s") == 0) {
+ char *attr;
+ int c = 1;
+ attr = iks_find_attrib(n2, "text:c");
+ if (attr) c = atoi(attr);
+ if (c > 127) {
+ c = 127;
+ puts("bork bork");
+ }
+ text_span(ctx, lay, n, lay->spaces, c);
+ } else if (iks_strcmp(iks_name(n2), "text:a") == 0) {
+ text_span(ctx, lay, n, iks_cdata(iks_child(n2)), iks_cdata_size(iks_child(n2)));
+ } else if (iks_strcmp(iks_name(n2), "text:tab-stop") == 0) {
+ text_span(ctx, lay, n, "\t", 1);
+ } else if (iks_strcmp(iks_name(n2), "text:page-number") == 0) {
+ char buf[8];
+ sprintf(buf, "%d", ctx->page->nr);
+ text_span(ctx, lay, n, iks_stack_strdup(lay->s, buf, 0), strlen(buf));
+ }
+ }
+ } else if (iks_strcmp(iks_name(n), "text:line-break") == 0) {
+ add_line(lay);
+ } else if (iks_strcmp(iks_name(n), "text:a") == 0) {
+ text_span(ctx, lay, n, iks_cdata(iks_child(n)), iks_cdata_size(iks_child(n)));
+ } else if (iks_strcmp(iks_name(n), "text:page-number") == 0) {
+ char buf[8];
+ sprintf(buf, "%d", ctx->page->nr);
+ text_span(ctx, lay, n, iks_stack_strdup(lay->s, buf, 0), strlen(buf));
+ }
+ }
+}
+
+static void
+text_list(ImpRenderCtx *ctx, struct Layout *lay, iks *node)
+{
+ iks *n, *n2;
+
+ for (n = iks_first_tag(node); n; n = iks_next_tag(n)) {
+ for (n2 = iks_first_tag(n); n2; n2 = iks_next_tag(n2)) {
+ if (strcmp(iks_name(n2), "text:p") == 0) {
+ text_p(ctx, lay, n2);
+ } else if (strcmp(iks_name(n2), "text:ordered-list") == 0) {
+ text_list(ctx, lay, n2);
+ } else if (strcmp(iks_name(n2), "text:unordered-list") == 0) {
+ text_list(ctx, lay, n2);
+ } else if (strcmp(iks_name(n2), "text:list") == 0) {
+ text_list(ctx, lay, n2);
+ }
+ }
+ }
+}
+
+void
+r_text(ImpRenderCtx *ctx, void *drw_data, iks *node)
+{
+ struct Layout lay;
+ iks *n;
+
+ memset(&lay, 0, sizeof(struct Layout));
+ memset(&lay.spaces, ' ', 128);
+ lay.s = iks_stack_new(sizeof(struct Span) * 16, 0);
+ lay.x = r_get_x(ctx, node, "svg:x");
+ lay.y = r_get_y(ctx, node, "svg:y");
+ lay.w = r_get_y(ctx, node, "svg:width");
+ lay.h = r_get_y(ctx, node, "svg:height");
+
+ for (n = iks_first_tag(node); n; n = iks_next_tag(n)) {
+ if (strcmp(iks_name(n), "text:p") == 0) {
+ text_p(ctx, &lay, n);
+ } else if (strcmp(iks_name(n), "text:ordered-list") == 0) {
+ text_list(ctx, &lay, n);
+ } else if (strcmp(iks_name(n), "text:unordered-list") == 0) {
+ text_list(ctx, &lay, n);
+ } else if (strcmp(iks_name(n), "text:list") == 0) {
+ text_list(ctx, &lay, n);
+ }
+ }
+
+ calc_sizes(ctx, drw_data, &lay);
+ calc_pos(ctx, &lay);
+ _imp_draw_layout(ctx, drw_data, &lay);
+
+ iks_stack_delete(lay.s);
+}
+/*
+static void
+text_span (render_ctx *ctx, text_ctx *tc, struct layout_s *lout, iks *node, char *text, int len)
+{
+ if (tc->bullet_flag && tc->bullet_sz) size = tc->bullet_sz; else size = r_get_font_size (ctx, tc, node);
+}
+
+static int
+is_animated (render_ctx *ctx, text_ctx *tc, iks *node)
+{
+ if (!ctx->step_mode) return 0;
+ if (!tc->id) return 0;
+ while (strcmp (iks_name (node), "draw:page") != 0
+ && strcmp (iks_name (node), "style:master-page") != 0)
+ node = iks_parent (node);
+ node = iks_find (node, "presentation:animations");
+ if (!node) return 0;
+ if (iks_find_with_attrib (node, "presentation:show-text", "draw:shape-id", tc->id)) return 1;
+ return 0;
+}
+
+static void
+text_p (render_ctx *ctx, text_ctx *tc, iks *node)
+{
+ if (is_animated (ctx, tc, node) && ctx->step_cnt >= ctx->step) lout->flag = 0;
+ ctx->step_cnt++;
+
+ attr = r_get_style (ctx, node, "text:enable-numbering");
+ if (attr && strcmp (attr, "true") == 0) {
+ if (iks_child (node) && tc->bullet) {
+ tc->bullet_flag = 1;
+ text_span (ctx, tc, lout, node, tc->bullet, strlen (tc->bullet));
+ text_span (ctx, tc, lout, node, " ", 1);
+ tc->bullet_flag = 0;
+ }
+ }
+
+ if (!lout->text) {
+lout->h = 0;
+attr = r_get_style (ctx, node, "fo:line-height");
+if (attr) {
+ int ratio = atoi (attr);
+ lout->lh = ratio;
+} else {
+ lout->lh = 100;
+}
+tc->layouts = g_list_append (tc->layouts, lout);
+// g_object_unref (lout->play);
+// iks_stack_delete (s);
+ return;
+ }
+
+ attr = r_get_style (ctx, node, "fo:text-align");
+ if (attr) {
+ if (strcmp (attr, "center") == 0)
+ pango_layout_set_alignment (lout->play, PANGO_ALIGN_CENTER);
+ else if (strcmp (attr, "end") == 0)
+ pango_layout_set_alignment (lout->play, PANGO_ALIGN_RIGHT);
+ }
+ pango_layout_set_width (lout->play, tc->w * PANGO_SCALE);
+ pango_layout_set_markup (lout->play, lout->text, lout->text_len);
+ pango_layout_get_pixel_size (lout->play, &lout->w, &lout->h);
+ attr = r_get_style (ctx, node, "fo:line-height");
+ if (attr) {
+ int ratio = atoi (attr);
+ lout->lh = ratio;
+ } else {
+ lout->lh = 100;
+ }
+ tc->layouts = g_list_append (tc->layouts, lout);
+}
+
+static void
+find_bullet (render_ctx *ctx, text_ctx *tc, iks *node)
+{
+ iks *x;
+ char *t;
+ x = r_get_bullet (ctx, node, "text:list-level-style-bullet");
+ x = iks_find (x, "text:list-level-style-bullet");
+ t = iks_find_attrib (x, "text:bullet-char");
+ if (t) tc->bullet = t; else tc->bullet = "*";
+ x = iks_find (x, "style:properties");
+ t = iks_find_attrib (x, "fo:font-size");
+ if (t) tc->bullet_sz = tc->last_sz * atoi (t) / 100;
+ else tc->bullet_sz = 0;
+}
+
+void
+r_text (render_ctx *ctx, iks *node)
+{
+ tc.id = iks_find_attrib (node, "draw:id");
+ ctx->step_cnt = 0;
+ for (n = iks_first_tag (node); n; n = iks_next_tag (n)) {
+ if (strcmp (iks_name (n), "text:p") == 0) {
+ text_p (ctx, &tc, n);
+ } else if (strcmp (iks_name (n), "text:ordered-list") == 0) {
+ text_list (ctx, &tc, n);
+ } else if (strcmp (iks_name (n), "text:unordered-list") == 0) {
+ find_bullet (ctx, &tc, n);
+ text_list (ctx, &tc, n);
+ tc.bullet = 0;
+ }
+ }
+
+*/
diff --git a/backend/impress/render.c b/backend/impress/render.c
new file mode 100644
index 0000000..0338600
--- /dev/null
+++ b/backend/impress/render.c
@@ -0,0 +1,53 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "internal.h"
+
+ImpRenderCtx *
+imp_create_context(const ImpDrawer *drw)
+{
+ ImpRenderCtx *ctx;
+
+ ctx = calloc(1, sizeof(ImpRenderCtx));
+ if (!ctx) return NULL;
+ ctx->drw = drw;
+ return ctx;
+}
+
+void
+imp_context_set_page(ImpRenderCtx *ctx, ImpPage *page)
+{
+ ctx->page = page;
+ ctx->content = page->doc->content;
+ ctx->styles = page->doc->styles;
+}
+
+void
+imp_context_set_step(ImpRenderCtx *ctx, int step)
+{
+ ctx->step = step;
+}
+
+void
+imp_render(ImpRenderCtx *ctx, void *drw_data)
+{
+ // find drawing area size
+ ctx->drw->get_size(drw_data, &ctx->pix_w, &ctx->pix_h);
+ // find page size
+ ctx->page->doc->get_geometry(ctx);
+ // calculate ratio
+ ctx->fact_x = ctx->pix_w / ctx->cm_w;
+ ctx->fact_y = ctx->pix_h / ctx->cm_h;
+ // call renderer
+ ctx->page->doc->render_page(ctx, drw_data);
+}
+
+void
+imp_delete_context(ImpRenderCtx *ctx)
+{
+ free(ctx);
+}
diff --git a/backend/impress/render.h b/backend/impress/render.h
new file mode 100644
index 0000000..64c994c
--- /dev/null
+++ b/backend/impress/render.h
@@ -0,0 +1,42 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "imposter.h"
+
+#ifndef OO_RENDER_H
+#define OO_RENDER_H
+
+#include <gtk/gtkdrawingarea.h>
+
+#define OO_TYPE_RENDER (oo_render_get_type())
+#define OO_RENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), OO_TYPE_RENDER , OORender))
+#define OO_RENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), OO_TYPE_RENDER , OORenderClass))
+
+typedef struct _OORender OORender;
+typedef struct _OORenderClass OORenderClass;
+
+struct _OORender {
+ GtkDrawingArea area;
+ ImpRenderCtx *ctx;
+ ImpPage *page;
+ int step;
+ int step_mode;
+};
+
+struct _OORenderClass {
+ GtkDrawingAreaClass parent_class;
+ void (*page_changed)(OORender *obj);
+};
+
+GType oo_render_get_type(void);
+GtkWidget *oo_render_new(void);
+void oo_render_set_page(OORender *obj, ImpPage *page);
+ImpPage *oo_render_get_page(OORender *obj);
+void oo_render_step(OORender *obj);
+void oo_render_step_mode(OORender *obj, int mode);
+
+
+#endif /* OO_RENDER_H */
diff --git a/backend/impress/zip.c b/backend/impress/zip.c
new file mode 100644
index 0000000..4b179b5
--- /dev/null
+++ b/backend/impress/zip.c
@@ -0,0 +1,346 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+#include "common.h"
+#include "zip.h"
+#include <zlib.h>
+#define _(x) x
+
+enum {
+ ZIP_OK = 0,
+ ZIP_NOMEM,
+ ZIP_NOSIG,
+ ZIP_BADZIP,
+ ZIP_NOMULTI,
+ ZIP_EOPEN,
+ ZIP_EREAD,
+ ZIP_NOFILE
+};
+
+struct zipfile {
+ struct zipfile *next;
+ char *name;
+ ulong crc;
+ ulong zip_size;
+ ulong real_size;
+ ulong pos;
+};
+
+struct zip_struct {
+ FILE *f;
+ struct zipfile *files;
+ ulong cd_pos;
+ ulong cd_size;
+ ulong cd_offset;
+ ulong head_size;
+ ulong rem_size;
+ ulong nr_files;
+};
+
+char *
+zip_error (int err)
+{
+ char *ret;
+
+ switch (err) {
+ case ZIP_OK:
+ ret = _("No error");
+ break;
+ case ZIP_NOMEM:
+ ret = _("Not enough memory");
+ break;
+ case ZIP_NOSIG:
+ ret = _("Cannot find zip signature");
+ break;
+ case ZIP_BADZIP:
+ ret = _("Invalid zip file");
+ break;
+ case ZIP_NOMULTI:
+ ret = _("Multi file zips are not supported");
+ break;
+ case ZIP_EOPEN:
+ ret = _("Cannot open the file");
+ break;
+ case ZIP_EREAD:
+ ret = _("Cannot read data from file");
+ break;
+ case ZIP_NOFILE:
+ ret = _("Cannot find file in the zip archive");
+ break;
+ default:
+ ret = _("Unknown error");
+ break;
+ }
+ return ret;
+}
+
+static int
+find_cd (zip *z)
+{
+ FILE *f;
+ char *buf;
+ ulong size, pos, i, flag;
+
+ f = z->f;
+ if (fseek (f, 0, SEEK_END) != 0) return 1;
+ size = ftell (f);
+ if (size < 0xffff) pos = 0; else pos = size - 0xffff;
+ buf = malloc (size - pos + 1);
+ if (!buf) return 1;
+ if (fseek (f, pos, SEEK_SET) != 0) {
+ free (buf);
+ return 1;
+ }
+ if (fread (buf, size - pos, 1, f) != 1) {
+ free (buf);
+ return 1;
+ }
+ flag = 0;
+ for (i = size - pos - 3; i > 0; i--) {
+ if (buf[i] == 0x50 && buf[i+1] == 0x4b && buf[i+2] == 0x05 && buf[i+3] == 0x06) {
+ z->cd_pos = i + pos;
+ flag = 1;
+ break;
+ }
+ }
+ free (buf);
+ if (flag != 1) return 1;
+ return 0;
+}
+
+static unsigned long
+get_long (unsigned char *buf)
+{
+ return buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
+}
+
+static unsigned long
+get_word (unsigned char *buf)
+{
+ return buf[0] + (buf[1] << 8);
+}
+
+static int
+list_files (zip *z)
+{
+ unsigned char buf[46];
+ struct zipfile *zfile;
+ ulong pat, fn_size;
+ int nr = 0;
+
+ pat = z->cd_offset;
+ while (nr < z->nr_files) {
+ fseek (z->f, pat + z->head_size, SEEK_SET);
+
+ if (fread (buf, 46, 1, z->f) != 1) return ZIP_EREAD;
+ if (get_long (buf) != 0x02014b50) return ZIP_BADZIP;
+
+ zfile = malloc (sizeof (struct zipfile));
+ if (!zfile) return ZIP_NOMEM;
+ memset (zfile, 0, sizeof (struct zipfile));
+
+ zfile->crc = get_long (buf + 16);
+ zfile->zip_size = get_long (buf + 20);
+ zfile->real_size = get_long (buf + 24);
+ fn_size = get_word (buf + 28);
+ zfile->pos = get_long (buf + 42);
+
+ zfile->name = malloc (fn_size + 1);
+ if (!zfile->name) {
+ free (zfile);
+ return ZIP_NOMEM;
+ }
+ fread (zfile->name, fn_size, 1, z->f);
+ zfile->name[fn_size] = '\0';
+
+ zfile->next = z->files;
+ z->files = zfile;
+
+ pat += 0x2e + fn_size + get_word (buf + 30) + get_word (buf + 32);
+ nr++;
+ }
+ return ZIP_OK;
+}
+
+zip *
+zip_open (const char *fname, int *err)
+{
+ unsigned char buf[22];
+ zip *z;
+ FILE *f;
+
+ f = fopen (fname, "rb");
+ if (NULL == f) {
+ *err = ZIP_EOPEN;
+ return NULL;
+ }
+
+ z = malloc (sizeof (zip));
+ memset (z, 0, sizeof (zip));
+ z->f = f;
+
+ if (find_cd (z)) {
+ zip_close (z);
+ *err = ZIP_NOSIG;
+ return NULL;
+ }
+
+ fseek (f, z->cd_pos, SEEK_SET);
+ if (fread (buf, 22, 1, f) != 1) {
+ zip_close (z);
+ *err = ZIP_EREAD;
+ return NULL;
+ }
+ z->nr_files = get_word (buf + 10);
+ if (get_word (buf + 8) != z->nr_files) {
+ zip_close (z);
+ *err = ZIP_NOMULTI;
+ return NULL;
+ }
+ z->cd_size = get_long (buf + 12);
+ z->cd_offset = get_long (buf + 16);
+ z->rem_size = get_word (buf + 20);
+ z->head_size = z->cd_pos - (z->cd_offset + z->cd_size);
+
+ *err = list_files (z);
+ if (*err != ZIP_OK) {
+ zip_close (z);
+ return NULL;
+ }
+
+ *err = ZIP_OK;
+ return z;
+}
+
+void
+zip_close (zip *z)
+{
+ struct zipfile *zfile, *tmp;
+
+ zfile = z->files;
+ while (zfile) {
+ tmp = zfile->next;
+ if (zfile->name) free (zfile->name);
+ free (zfile);
+ zfile = tmp;
+ }
+ z->files = NULL;
+ if (z->f) fclose (z->f);
+ z->f = NULL;
+}
+
+static struct zipfile *
+find_file (zip *z, const char *name)
+{
+ struct zipfile *zfile;
+
+ zfile = z->files;
+ while (zfile) {
+ if (strcmp (zfile->name, name) == 0) return zfile;
+ zfile = zfile->next;
+ }
+ return NULL;
+}
+
+static int
+seek_file (zip *z, struct zipfile *zfile)
+{
+ unsigned char buf[30];
+
+ fseek (z->f, zfile->pos + z->head_size, SEEK_SET);
+ if (fread (buf, 30, 1, z->f) != 1) return ZIP_EREAD;
+ if (get_long (buf) != 0x04034b50) return ZIP_BADZIP;
+ fseek (z->f, get_word (buf + 26) + get_word (buf + 28), SEEK_CUR);
+ return ZIP_OK;
+}
+
+iks *
+zip_load_xml (zip *z, const char *name, int *err)
+{
+ iksparser *prs;
+ char *real_buf;
+ iks *x;
+ struct zipfile *zfile;
+
+ *err = ZIP_OK;
+
+ zfile = find_file (z, name);
+ if (!zfile) {
+ *err = ZIP_NOFILE;
+ return NULL;
+ }
+
+ seek_file (z, zfile);
+
+ real_buf = malloc (zfile->real_size + 1);
+ if (zfile->zip_size < zfile->real_size) {
+ char *zip_buf;
+ z_stream zs;
+ zs.zalloc = NULL;
+ zs.zfree = NULL;
+ zs.opaque = NULL;
+ zip_buf = malloc (zfile->zip_size);
+ fread (zip_buf, zfile->zip_size, 1, z->f);
+ zs.next_in = zip_buf;
+ zs.avail_in = zfile->zip_size;
+ zs.next_out = real_buf;
+ zs.avail_out = zfile->real_size;
+ inflateInit2 (&zs, -MAX_WBITS);
+ inflate (&zs, Z_FINISH);
+ inflateEnd (&zs);
+ free (zip_buf);
+ } else {
+ fread (real_buf, zfile->real_size, 1, z->f);
+ }
+
+ real_buf[zfile->real_size] = '\0';
+ prs = iks_dom_new (&x);
+ iks_parse (prs, real_buf, zfile->real_size, 1);
+ iks_parser_delete (prs);
+ free (real_buf);
+ return x;
+}
+
+unsigned long zip_get_size (zip *z, const char *name)
+{
+ struct zipfile *zf;
+
+ zf = find_file (z, name);
+ if (!zf) return 0;
+ return zf->real_size;
+}
+
+int zip_load (zip *z, const char *name, char *buf)
+{
+ struct zipfile *zfile;
+
+ zfile = find_file (z, name);
+ if (!zfile) return ZIP_NOFILE;
+
+ seek_file (z, zfile);
+
+ if (zfile->zip_size < zfile->real_size) {
+ char *zip_buf;
+ z_stream zs;
+ zs.zalloc = NULL;
+ zs.zfree = NULL;
+ zs.opaque = NULL;
+ zip_buf = malloc (zfile->zip_size);
+ fread (zip_buf, zfile->zip_size, 1, z->f);
+ zs.next_in = zip_buf;
+ zs.avail_in = zfile->zip_size;
+ zs.next_out = buf;
+ zs.avail_out = zfile->real_size;
+ inflateInit2 (&zs, -MAX_WBITS);
+ inflate (&zs, Z_FINISH);
+ inflateEnd (&zs);
+ free (zip_buf);
+ } else {
+ fread (buf, zfile->real_size, 1, z->f);
+ }
+
+ return ZIP_OK;
+}
diff --git a/backend/impress/zip.h b/backend/impress/zip.h
new file mode 100644
index 0000000..23ff363
--- /dev/null
+++ b/backend/impress/zip.h
@@ -0,0 +1,18 @@
+/* imposter (OO.org Impress viewer)
+** Copyright (C) 2003-2005 Gurer Ozen
+** This code is free software; you can redistribute it and/or
+** modify it under the terms of GNU General Public License.
+*/
+
+struct zip_struct;
+typedef struct zip_struct zip;
+
+char *zip_error (int err);
+
+zip *zip_open (const char *fname, int *err);
+void zip_close (zip *z);
+
+iks *zip_load_xml (zip *z, const char *name, int *err);
+
+unsigned long zip_get_size (zip *z, const char *name);
+int zip_load (zip *z, const char *name, char *buf);
diff --git a/backend/pdf/Makefile.am b/backend/pdf/Makefile.am
new file mode 100644
index 0000000..38d0831
--- /dev/null
+++ b/backend/pdf/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ $(BACKEND_CFLAGS) \
+ $(POPPLER_CFLAGS) \
+ $(WARN_CXXFLAGS) \
+ $(DISABLE_DEPRECATED)
+
+noinst_LTLIBRARIES = libpdfdocument.la
+
+libpdfdocument_la_SOURCES = \
+ ev-poppler.cc \
+ ev-poppler.h
diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc
new file mode 100644
index 0000000..2068998
--- /dev/null
+++ b/backend/pdf/ev-poppler.cc
@@ -0,0 +1,1727 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* pdfdocument.h: Implementation of EvDocument for PDF
+ * Copyright (C) 2004, Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <poppler.h>
+#include <poppler-document.h>
+#include <poppler-page.h>
+#ifdef HAVE_CAIRO_PDF
+#include <cairo-pdf.h>
+#endif
+#include <glib/gi18n.h>
+
+#include "ev-poppler.h"
+#include "ev-file-exporter.h"
+#include "ev-document-find.h"
+#include "ev-document-misc.h"
+#include "ev-document-links.h"
+#include "ev-document-images.h"
+#include "ev-document-fonts.h"
+#include "ev-document-security.h"
+#include "ev-document-thumbnails.h"
+#include "ev-document-transition.h"
+#include "ev-selection.h"
+#include "ev-attachment.h"
+#include "ev-image.h"
+
+typedef struct {
+ PdfDocument *document;
+ char *text;
+ GList **pages;
+ guint idle;
+ int start_page;
+ int search_page;
+} PdfDocumentSearch;
+
+typedef struct {
+ EvFileExporterFormat format;
+ PopplerPSFile *ps_file;
+#ifdef HAVE_CAIRO_PDF
+ cairo_t *pdf_cairo;
+#endif
+} PdfPrintContext;
+
+struct _PdfDocumentClass
+{
+ GObjectClass parent_class;
+};
+
+struct _PdfDocument
+{
+ GObject parent_instance;
+
+ PopplerDocument *document;
+ gchar *password;
+
+ PopplerFontInfo *font_info;
+ PopplerFontsIter *fonts_iter;
+ int fonts_scanned_pages;
+
+ PdfDocumentSearch *search;
+ PdfPrintContext *print_ctx;
+};
+
+static void pdf_document_document_iface_init (EvDocumentIface *iface);
+static void pdf_document_security_iface_init (EvDocumentSecurityIface *iface);
+static void pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
+static void pdf_document_document_links_iface_init (EvDocumentLinksIface *iface);
+static void pdf_document_document_images_iface_init (EvDocumentImagesIface *iface);
+static void pdf_document_document_fonts_iface_init (EvDocumentFontsIface *iface);
+static void pdf_document_find_iface_init (EvDocumentFindIface *iface);
+static void pdf_document_file_exporter_iface_init (EvFileExporterIface *iface);
+static void pdf_selection_iface_init (EvSelectionIface *iface);
+static void pdf_document_page_transition_iface_init (EvDocumentTransitionIface *iface);
+static void pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails,
+ gint page,
+ gint size,
+ gint *width,
+ gint *height);
+static int pdf_document_get_n_pages (EvDocument *document);
+
+static EvLinkDest *ev_link_dest_from_dest (PdfDocument *pdf_document,
+ PopplerDest *dest);
+static EvLink *ev_link_from_action (PdfDocument *pdf_document,
+ PopplerAction *action);
+static void pdf_document_search_free (PdfDocumentSearch *search);
+static void pdf_print_context_free (PdfPrintContext *ctx);
+
+
+G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
+ {
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
+ pdf_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_SECURITY,
+ pdf_document_security_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+ pdf_document_document_thumbnails_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS,
+ pdf_document_document_links_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_IMAGES,
+ pdf_document_document_images_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FONTS,
+ pdf_document_document_fonts_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND,
+ pdf_document_find_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
+ pdf_document_file_exporter_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION,
+ pdf_selection_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_TRANSITION,
+ pdf_document_page_transition_iface_init);
+ });
+
+
+static void
+set_rc_data (PdfDocument *pdf_document,
+ EvRenderContext *rc)
+{
+ if (rc->data == NULL) {
+ rc->data = poppler_document_get_page (pdf_document->document,
+ rc->page);
+ rc->destroy = g_object_unref;
+ } else {
+ g_assert (rc->page == poppler_page_get_index (POPPLER_PAGE (rc->data)));
+ }
+}
+
+static void
+pdf_document_search_free (PdfDocumentSearch *search)
+{
+ PdfDocument *pdf_document = search->document;
+ int n_pages;
+ int i;
+
+ if (search->idle != 0)
+ g_source_remove (search->idle);
+
+ n_pages = pdf_document_get_n_pages (EV_DOCUMENT (pdf_document));
+ for (i = 0; i < n_pages; i++) {
+ g_list_foreach (search->pages[i], (GFunc) g_free, NULL);
+ g_list_free (search->pages[i]);
+ }
+ g_free (search->pages);
+
+ g_free (search->text);
+ g_free (search);
+}
+
+static void
+pdf_document_dispose (GObject *object)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT(object);
+
+ if (pdf_document->print_ctx) {
+ pdf_print_context_free (pdf_document->print_ctx);
+ pdf_document->print_ctx = NULL;
+ }
+
+ if (pdf_document->search) {
+ pdf_document_search_free (pdf_document->search);
+ pdf_document->search = NULL;
+ }
+
+ if (pdf_document->document) {
+ g_object_unref (pdf_document->document);
+ }
+
+ if (pdf_document->font_info) {
+ poppler_font_info_free (pdf_document->font_info);
+ }
+
+ if (pdf_document->fonts_iter) {
+ poppler_fonts_iter_free (pdf_document->fonts_iter);
+ }
+}
+
+static void
+pdf_document_class_init (PdfDocumentClass *klass)
+{
+ GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
+
+ g_object_class->dispose = pdf_document_dispose;
+}
+
+static void
+pdf_document_init (PdfDocument *pdf_document)
+{
+ pdf_document->password = NULL;
+}
+
+static void
+convert_error (GError *poppler_error,
+ GError **error)
+{
+ if (poppler_error == NULL)
+ return;
+
+ if (poppler_error->domain == POPPLER_ERROR) {
+ /* convert poppler errors into EvDocument errors */
+ gint code = EV_DOCUMENT_ERROR_INVALID;
+ if (poppler_error->code == POPPLER_ERROR_INVALID)
+ code = EV_DOCUMENT_ERROR_INVALID;
+ else if (poppler_error->code == POPPLER_ERROR_ENCRYPTED)
+ code = EV_DOCUMENT_ERROR_ENCRYPTED;
+
+
+ g_set_error (error,
+ EV_DOCUMENT_ERROR,
+ code,
+ poppler_error->message,
+ NULL);
+ } else {
+ g_propagate_error (error, poppler_error);
+ }
+}
+
+
+/* EvDocument */
+static gboolean
+pdf_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ gboolean retval;
+ GError *poppler_error = NULL;
+
+ retval = poppler_document_save (PDF_DOCUMENT (document)->document,
+ uri,
+ &poppler_error);
+ if (! retval)
+ convert_error (poppler_error, error);
+
+ return retval;
+}
+
+static gboolean
+pdf_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ GError *poppler_error = NULL;
+ PdfDocument *pdf_document = PDF_DOCUMENT (document);
+
+ pdf_document->document =
+ poppler_document_new_from_file (uri, pdf_document->password, &poppler_error);
+
+ if (pdf_document->document == NULL) {
+ convert_error (poppler_error, error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+pdf_document_get_n_pages (EvDocument *document)
+{
+ return poppler_document_get_n_pages (PDF_DOCUMENT (document)->document);
+}
+
+static void
+pdf_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document);
+ PopplerPage *poppler_page;
+
+ poppler_page = poppler_document_get_page (pdf_document->document, page);
+ poppler_page_get_size (poppler_page, width, height);
+ g_object_unref (poppler_page);
+}
+
+static char *
+pdf_document_get_page_label (EvDocument *document,
+ int page)
+{
+ PopplerPage *poppler_page;
+ char *label = NULL;
+
+ poppler_page = poppler_document_get_page (PDF_DOCUMENT (document)->document,
+ page);
+
+ g_object_get (G_OBJECT (poppler_page),
+ "label", &label,
+ NULL);
+ g_object_unref (poppler_page);
+
+ return label;
+}
+
+static gboolean
+pdf_document_has_attachments (EvDocument *document)
+{
+ PdfDocument *pdf_document;
+
+ pdf_document = PDF_DOCUMENT (document);
+
+ return poppler_document_has_attachments (pdf_document->document);
+}
+
+struct SaveToBufferData {
+ gchar *buffer;
+ gsize len, max;
+};
+
+static gboolean
+attachment_save_to_buffer_callback (const gchar *buf,
+ gsize count,
+ gpointer user_data,
+ GError **error)
+{
+ struct SaveToBufferData *sdata = (SaveToBufferData *)user_data;
+ gchar *new_buffer;
+ gsize new_max;
+
+ if (sdata->len + count > sdata->max) {
+ new_max = MAX (sdata->max * 2, sdata->len + count);
+ new_buffer = (gchar *)g_realloc (sdata->buffer, new_max);
+
+ sdata->buffer = new_buffer;
+ sdata->max = new_max;
+ }
+
+ memcpy (sdata->buffer + sdata->len, buf, count);
+ sdata->len += count;
+
+ return TRUE;
+}
+
+static gboolean
+attachment_save_to_buffer (PopplerAttachment *attachment,
+ gchar **buffer,
+ gsize *buffer_size,
+ GError **error)
+{
+ static const gint initial_max = 1024;
+ struct SaveToBufferData sdata;
+
+ *buffer = NULL;
+ *buffer_size = 0;
+
+ sdata.buffer = (gchar *) g_malloc (initial_max);
+ sdata.max = initial_max;
+ sdata.len = 0;
+
+ if (! poppler_attachment_save_to_callback (attachment,
+ attachment_save_to_buffer_callback,
+ &sdata,
+ error)) {
+ g_free (sdata.buffer);
+ return FALSE;
+ }
+
+ *buffer = sdata.buffer;
+ *buffer_size = sdata.len;
+
+ return TRUE;
+}
+
+static GList *
+pdf_document_get_attachments (EvDocument *document)
+{
+ PdfDocument *pdf_document;
+ GList *attachments;
+ GList *list;
+ GList *retval = NULL;
+
+ pdf_document = PDF_DOCUMENT (document);
+
+ if (!pdf_document_has_attachments (document))
+ return NULL;
+
+ attachments = poppler_document_get_attachments (pdf_document->document);
+
+ for (list = attachments; list; list = list->next) {
+ PopplerAttachment *attachment;
+ EvAttachment *ev_attachment;
+ gchar *data = NULL;
+ gsize size;
+ GError *error = NULL;
+
+ attachment = (PopplerAttachment *) list->data;
+
+ if (attachment_save_to_buffer (attachment, &data, &size, &error)) {
+ ev_attachment = ev_attachment_new (attachment->name,
+ attachment->description,
+ attachment->mtime,
+ attachment->ctime,
+ size, data);
+
+ retval = g_list_prepend (retval, ev_attachment);
+ } else {
+ if (error) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+
+ g_free (data);
+ }
+ }
+
+ g_object_unref (attachment);
+ }
+
+ return g_list_reverse (retval);
+}
+
+static GdkPixbuf *
+pdf_document_render_pixbuf (EvDocument *document,
+ EvRenderContext *rc)
+{
+ PdfDocument *pdf_document;
+ GdkPixbuf *pixbuf;
+ double width_points, height_points;
+ gint width, height;
+
+ pdf_document = PDF_DOCUMENT (document);
+
+ set_rc_data (pdf_document, rc);
+
+ poppler_page_get_size (POPPLER_PAGE (rc->data), &width_points, &height_points);
+
+ if (rc->rotation == 90 || rc->rotation == 270) {
+ width = (int) ((height_points * rc->scale) + 0.5);
+ height = (int) ((width_points * rc->scale) + 0.5);
+ } else {
+ width = (int) ((width_points * rc->scale) + 0.5);
+ height = (int) ((height_points * rc->scale) + 0.5);
+ }
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ FALSE, 8,
+ width, height);
+
+ poppler_page_render_to_pixbuf (POPPLER_PAGE (rc->data),
+ 0, 0,
+ width, height,
+ rc->scale,
+ rc->rotation,
+ pixbuf);
+
+
+ return pixbuf;
+}
+
+/* EvDocumentSecurity */
+
+static gboolean
+pdf_document_has_document_security (EvDocumentSecurity *document_security)
+{
+ /* FIXME: do we really need to have this? */
+ return FALSE;
+}
+
+static void
+pdf_document_set_password (EvDocumentSecurity *document_security,
+ const char *password)
+{
+ PdfDocument *document = PDF_DOCUMENT (document_security);
+
+ if (document->password)
+ g_free (document->password);
+
+ document->password = g_strdup (password);
+}
+
+static gboolean
+pdf_document_can_get_text (EvDocument *document)
+{
+ return TRUE;
+}
+
+static EvDocumentInfo *
+pdf_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+ PopplerPageLayout layout;
+ PopplerPageMode mode;
+ PopplerViewerPreferences view_prefs;
+ PopplerPermissions permissions;
+
+ info = g_new0 (EvDocumentInfo, 1);
+
+ info->fields_mask = EV_DOCUMENT_INFO_TITLE |
+ EV_DOCUMENT_INFO_FORMAT |
+ EV_DOCUMENT_INFO_AUTHOR |
+ EV_DOCUMENT_INFO_SUBJECT |
+ EV_DOCUMENT_INFO_KEYWORDS |
+ EV_DOCUMENT_INFO_LAYOUT |
+ EV_DOCUMENT_INFO_START_MODE |
+ EV_DOCUMENT_INFO_PERMISSIONS |
+ EV_DOCUMENT_INFO_UI_HINTS |
+ EV_DOCUMENT_INFO_CREATOR |
+ EV_DOCUMENT_INFO_PRODUCER |
+ EV_DOCUMENT_INFO_CREATION_DATE |
+ EV_DOCUMENT_INFO_MOD_DATE |
+ EV_DOCUMENT_INFO_LINEARIZED |
+ EV_DOCUMENT_INFO_N_PAGES |
+ EV_DOCUMENT_INFO_SECURITY |
+ EV_DOCUMENT_INFO_PAPER_SIZE;
+
+ g_object_get (PDF_DOCUMENT (document)->document,
+ "title", &(info->title),
+ "format", &(info->format),
+ "author", &(info->author),
+ "subject", &(info->subject),
+ "keywords", &(info->keywords),
+ "page-mode", &mode,
+ "page-layout", &layout,
+ "viewer-preferences", &view_prefs,
+ "permissions", &permissions,
+ "creator", &(info->creator),
+ "producer", &(info->producer),
+ "creation-date", &(info->creation_date),
+ "mod-date", &(info->modified_date),
+ "linearized", &(info->linearized),
+ NULL);
+
+ pdf_document_get_page_size(document, 0,
+ &(info->paper_width),
+ &(info->paper_height));
+
+ // Convert to mm.
+ info->paper_width = info->paper_width / 72.0f * 25.4f;
+ info->paper_height = info->paper_height / 72.0f * 25.4f;
+
+ switch (layout) {
+ case POPPLER_PAGE_LAYOUT_SINGLE_PAGE:
+ info->layout = EV_DOCUMENT_LAYOUT_SINGLE_PAGE;
+ break;
+ case POPPLER_PAGE_LAYOUT_ONE_COLUMN:
+ info->layout = EV_DOCUMENT_LAYOUT_ONE_COLUMN;
+ break;
+ case POPPLER_PAGE_LAYOUT_TWO_COLUMN_LEFT:
+ info->layout = EV_DOCUMENT_LAYOUT_TWO_COLUMN_LEFT;
+ break;
+ case POPPLER_PAGE_LAYOUT_TWO_COLUMN_RIGHT:
+ info->layout = EV_DOCUMENT_LAYOUT_TWO_COLUMN_RIGHT;
+ case POPPLER_PAGE_LAYOUT_TWO_PAGE_LEFT:
+ info->layout = EV_DOCUMENT_LAYOUT_TWO_PAGE_LEFT;
+ break;
+ case POPPLER_PAGE_LAYOUT_TWO_PAGE_RIGHT:
+ info->layout = EV_DOCUMENT_LAYOUT_TWO_PAGE_RIGHT;
+ break;
+ default:
+ break;
+ }
+
+ switch (mode) {
+ case POPPLER_PAGE_MODE_NONE:
+ info->mode = EV_DOCUMENT_MODE_NONE;
+ break;
+ case POPPLER_PAGE_MODE_USE_THUMBS:
+ info->mode = EV_DOCUMENT_MODE_USE_THUMBS;
+ break;
+ case POPPLER_PAGE_MODE_USE_OC:
+ info->mode = EV_DOCUMENT_MODE_USE_OC;
+ break;
+ case POPPLER_PAGE_MODE_FULL_SCREEN:
+ info->mode = EV_DOCUMENT_MODE_FULL_SCREEN;
+ break;
+ case POPPLER_PAGE_MODE_USE_ATTACHMENTS:
+ info->mode = EV_DOCUMENT_MODE_USE_ATTACHMENTS;
+ default:
+ break;
+ }
+
+ info->ui_hints = 0;
+ if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_TOOLBAR) {
+ info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_TOOLBAR;
+ }
+ if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_MENUBAR) {
+ info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_MENUBAR;
+ }
+ if (view_prefs & POPPLER_VIEWER_PREFERENCES_HIDE_WINDOWUI) {
+ info->ui_hints |= EV_DOCUMENT_UI_HINT_HIDE_WINDOWUI;
+ }
+ if (view_prefs & POPPLER_VIEWER_PREFERENCES_FIT_WINDOW) {
+ info->ui_hints |= EV_DOCUMENT_UI_HINT_FIT_WINDOW;
+ }
+ if (view_prefs & POPPLER_VIEWER_PREFERENCES_CENTER_WINDOW) {
+ info->ui_hints |= EV_DOCUMENT_UI_HINT_CENTER_WINDOW;
+ }
+ if (view_prefs & POPPLER_VIEWER_PREFERENCES_DISPLAY_DOC_TITLE) {
+ info->ui_hints |= EV_DOCUMENT_UI_HINT_DISPLAY_DOC_TITLE;
+ }
+ if (view_prefs & POPPLER_VIEWER_PREFERENCES_DIRECTION_RTL) {
+ info->ui_hints |= EV_DOCUMENT_UI_HINT_DIRECTION_RTL;
+ }
+
+ info->permissions = 0;
+ if (permissions & POPPLER_PERMISSIONS_OK_TO_PRINT) {
+ info->permissions |= EV_DOCUMENT_PERMISSIONS_OK_TO_PRINT;
+ }
+ if (permissions & POPPLER_PERMISSIONS_OK_TO_MODIFY) {
+ info->permissions |= EV_DOCUMENT_PERMISSIONS_OK_TO_MODIFY;
+ }
+ if (permissions & POPPLER_PERMISSIONS_OK_TO_COPY) {
+ info->permissions |= EV_DOCUMENT_PERMISSIONS_OK_TO_COPY;
+ }
+ if (permissions & POPPLER_PERMISSIONS_OK_TO_ADD_NOTES) {
+ info->permissions |= EV_DOCUMENT_PERMISSIONS_OK_TO_ADD_NOTES;
+ }
+
+ info->n_pages = ev_document_get_n_pages (document);
+
+ if (ev_document_security_has_document_security (EV_DOCUMENT_SECURITY (document))) {
+ /* translators: this is the document security state */
+ info->security = g_strdup (_("Yes"));
+ } else {
+ /* translators: this is the document security state */
+ info->security = g_strdup (_("No"));
+ }
+
+ return info;
+}
+
+static char *
+pdf_document_get_text (EvDocument *document, int page, EvRectangle *rect)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document);
+ PopplerPage *poppler_page;
+ PopplerRectangle r;
+ double height;
+ char *text;
+
+ poppler_page = poppler_document_get_page (pdf_document->document, page);
+ g_return_val_if_fail (poppler_page != NULL, NULL);
+
+ poppler_page_get_size (poppler_page, NULL, &height);
+ r.x1 = rect->x1;
+ r.y1 = height - rect->y2;
+ r.x2 = rect->x2;
+ r.y2 = height - rect->y1;
+
+ text = poppler_page_get_text (poppler_page, &r);
+
+ g_object_unref (poppler_page);
+
+ return text;
+}
+
+static void
+pdf_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->save = pdf_document_save;
+ iface->load = pdf_document_load;
+ iface->get_n_pages = pdf_document_get_n_pages;
+ iface->get_page_size = pdf_document_get_page_size;
+ iface->get_page_label = pdf_document_get_page_label;
+ iface->has_attachments = pdf_document_has_attachments;
+ iface->get_attachments = pdf_document_get_attachments;
+ iface->render_pixbuf = pdf_document_render_pixbuf;
+ iface->get_text = pdf_document_get_text;
+ iface->can_get_text = pdf_document_can_get_text;
+ iface->get_info = pdf_document_get_info;
+};
+
+static void
+pdf_document_security_iface_init (EvDocumentSecurityIface *iface)
+{
+ iface->has_document_security = pdf_document_has_document_security;
+ iface->set_password = pdf_document_set_password;
+}
+
+static gdouble
+pdf_document_fonts_get_progress (EvDocumentFonts *document_fonts)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document_fonts);
+ int n_pages;
+
+ n_pages = pdf_document_get_n_pages (EV_DOCUMENT (pdf_document));
+
+ return (double)pdf_document->fonts_scanned_pages / (double)n_pages;
+}
+
+static gboolean
+pdf_document_fonts_scan (EvDocumentFonts *document_fonts,
+ int n_pages)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document_fonts);
+ gboolean result;
+
+ g_return_val_if_fail (PDF_IS_DOCUMENT (document_fonts), FALSE);
+
+ if (pdf_document->font_info == NULL) {
+ pdf_document->font_info = poppler_font_info_new (pdf_document->document);
+ }
+
+ if (pdf_document->fonts_iter) {
+ poppler_fonts_iter_free (pdf_document->fonts_iter);
+ }
+
+ pdf_document->fonts_scanned_pages += n_pages;
+
+ result = poppler_font_info_scan (pdf_document->font_info, n_pages,
+ &pdf_document->fonts_iter);
+ if (!result) {
+ pdf_document->fonts_scanned_pages = 0;
+ poppler_font_info_free (pdf_document->font_info);
+ pdf_document->font_info = NULL;
+ }
+
+ return result;
+}
+
+static const char *
+font_type_to_string (PopplerFontType type)
+{
+ switch (type) {
+ case POPPLER_FONT_TYPE_TYPE1:
+ return _("Type 1");
+ case POPPLER_FONT_TYPE_TYPE1C:
+ return _("Type 1C");
+ case POPPLER_FONT_TYPE_TYPE3:
+ return _("Type 3");
+ case POPPLER_FONT_TYPE_TRUETYPE:
+ return _("TrueType");
+ case POPPLER_FONT_TYPE_CID_TYPE0:
+ return _("Type 1 (CID)");
+ case POPPLER_FONT_TYPE_CID_TYPE0C:
+ return _("Type 1C (CID)");
+ case POPPLER_FONT_TYPE_CID_TYPE2:
+ return _("TrueType (CID)");
+ default:
+ return _("Unknown font type");
+ }
+}
+
+static void
+pdf_document_fonts_fill_model (EvDocumentFonts *document_fonts,
+ GtkTreeModel *model)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document_fonts);
+ PopplerFontsIter *iter = pdf_document->fonts_iter;
+
+ g_return_if_fail (PDF_IS_DOCUMENT (document_fonts));
+
+ if (!iter)
+ return;
+
+ do {
+ GtkTreeIter list_iter;
+ const char *name;
+ const char *type;
+ const char *embedded;
+ char *details;
+
+ name = poppler_fonts_iter_get_name (iter);
+
+ if (name == NULL) {
+ name = _("No name");
+ }
+
+ type = font_type_to_string (
+ poppler_fonts_iter_get_font_type (iter));
+
+ if (poppler_fonts_iter_is_embedded (iter)) {
+ if (poppler_fonts_iter_is_subset (iter))
+ embedded = _("Embedded subset");
+ else
+ embedded = _("Embedded");
+ } else {
+ embedded = _("Not embedded");
+ }
+
+ details = g_markup_printf_escaped ("%s\n%s", type, embedded);
+
+ gtk_list_store_append (GTK_LIST_STORE (model), &list_iter);
+ gtk_list_store_set (GTK_LIST_STORE (model), &list_iter,
+ EV_DOCUMENT_FONTS_COLUMN_NAME, name,
+ EV_DOCUMENT_FONTS_COLUMN_DETAILS, details,
+ -1);
+
+ g_free (details);
+ } while (poppler_fonts_iter_next (iter));
+}
+
+static void
+pdf_document_document_fonts_iface_init (EvDocumentFontsIface *iface)
+{
+ iface->fill_model = pdf_document_fonts_fill_model;
+ iface->scan = pdf_document_fonts_scan;
+ iface->get_progress = pdf_document_fonts_get_progress;
+}
+
+static gboolean
+pdf_document_links_has_document_links (EvDocumentLinks *document_links)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
+ PopplerIndexIter *iter;
+
+ g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), FALSE);
+
+ iter = poppler_index_iter_new (pdf_document->document);
+ if (iter == NULL)
+ return FALSE;
+ poppler_index_iter_free (iter);
+
+ return TRUE;
+}
+
+static EvLinkDest *
+ev_link_dest_from_dest (PdfDocument *pdf_document,
+ PopplerDest *dest)
+{
+ EvLinkDest *ev_dest = NULL;
+ const char *unimplemented_dest = NULL;
+
+ g_assert (dest != NULL);
+
+ switch (dest->type) {
+ case POPPLER_DEST_XYZ: {
+ PopplerPage *poppler_page;
+ double height;
+
+ poppler_page = poppler_document_get_page (pdf_document->document,
+ MAX (0, dest->page_num - 1));
+ poppler_page_get_size (poppler_page, NULL, &height);
+ ev_dest = ev_link_dest_new_xyz (dest->page_num - 1,
+ dest->left,
+ height - dest->top,
+ dest->zoom);
+ g_object_unref (poppler_page);
+ }
+ break;
+ case POPPLER_DEST_FIT:
+ ev_dest = ev_link_dest_new_fit (dest->page_num - 1);
+ break;
+ case POPPLER_DEST_FITH: {
+ PopplerPage *poppler_page;
+ double height;
+
+ poppler_page = poppler_document_get_page (pdf_document->document,
+ MAX (0, dest->page_num - 1));
+ poppler_page_get_size (poppler_page, NULL, &height);
+ ev_dest = ev_link_dest_new_fith (dest->page_num - 1,
+ height - dest->top);
+ g_object_unref (poppler_page);
+ }
+ break;
+ case POPPLER_DEST_FITV:
+ ev_dest = ev_link_dest_new_fitv (dest->page_num - 1,
+ dest->left);
+ break;
+ case POPPLER_DEST_FITR: {
+ PopplerPage *poppler_page;
+ double height;
+
+ poppler_page = poppler_document_get_page (pdf_document->document,
+ MAX (0, dest->page_num - 1));
+ poppler_page_get_size (poppler_page, NULL, &height);
+ ev_dest = ev_link_dest_new_fitr (dest->page_num - 1,
+ dest->left,
+ height - dest->bottom,
+ dest->right,
+ height - dest->top);
+ g_object_unref (poppler_page);
+ }
+ break;
+ case POPPLER_DEST_FITB:
+ unimplemented_dest = "POPPLER_DEST_FITB";
+ break;
+ case POPPLER_DEST_FITBH:
+ unimplemented_dest = "POPPLER_DEST_FITBH";
+ break;
+ case POPPLER_DEST_FITBV:
+ unimplemented_dest = "POPPLER_DEST_FITBV";
+ break;
+ case POPPLER_DEST_NAMED:
+ ev_dest = ev_link_dest_new_named (dest->named_dest);
+ break;
+ case POPPLER_DEST_UNKNOWN:
+ unimplemented_dest = "POPPLER_DEST_UNKNOWN";
+ break;
+ }
+
+ if (unimplemented_dest) {
+ g_warning ("Unimplemented named action: %s, please post a "
+ "bug report in Evince bugzilla "
+ "(http://bugzilla.gnome.org) with a testcase.",
+ unimplemented_dest);
+ }
+
+ if (!ev_dest)
+ ev_dest = ev_link_dest_new_page (dest->page_num - 1);
+
+ return ev_dest;
+}
+
+static EvLink *
+ev_link_from_action (PdfDocument *pdf_document,
+ PopplerAction *action)
+{
+ EvLink *link = NULL;
+ EvLinkAction *ev_action = NULL;
+ const char *unimplemented_action = NULL;
+
+ switch (action->type) {
+ case POPPLER_ACTION_GOTO_DEST: {
+ EvLinkDest *dest;
+
+ dest = ev_link_dest_from_dest (pdf_document, action->goto_dest.dest);
+ ev_action = ev_link_action_new_dest (dest);
+ }
+ break;
+ case POPPLER_ACTION_GOTO_REMOTE: {
+ EvLinkDest *dest;
+
+ dest = ev_link_dest_from_dest (pdf_document, action->goto_remote.dest);
+ ev_action = ev_link_action_new_remote (dest,
+ action->goto_remote.file_name);
+
+ }
+ break;
+ case POPPLER_ACTION_LAUNCH:
+ ev_action = ev_link_action_new_launch (action->launch.file_name,
+ action->launch.params);
+ break;
+ case POPPLER_ACTION_URI:
+ ev_action = ev_link_action_new_external_uri (action->uri.uri);
+ break;
+ case POPPLER_ACTION_NAMED:
+ ev_action = ev_link_action_new_named (action->named.named_dest);
+ break;
+ case POPPLER_ACTION_MOVIE:
+ unimplemented_action = "POPPLER_ACTION_MOVIE";
+ break;
+ case POPPLER_ACTION_UNKNOWN:
+ unimplemented_action = "POPPLER_ACTION_UNKNOWN";
+ }
+
+ if (unimplemented_action) {
+ g_warning ("Unimplemented action: %s, please post a bug report with a testcase.",
+ unimplemented_action);
+ }
+
+ link = ev_link_new (action->any.title, ev_action);
+
+ return link;
+}
+
+static void
+build_tree (PdfDocument *pdf_document,
+ GtkTreeModel *model,
+ GtkTreeIter *parent,
+ PopplerIndexIter *iter)
+{
+
+ do {
+ GtkTreeIter tree_iter;
+ PopplerIndexIter *child;
+ PopplerAction *action;
+ EvLink *link = NULL;
+ gboolean expand;
+ char *title_markup;
+
+ action = poppler_index_iter_get_action (iter);
+ expand = poppler_index_iter_is_open (iter);
+
+ if (!action)
+ continue;
+
+ switch (action->type) {
+ case POPPLER_ACTION_GOTO_DEST: {
+ /* For bookmarks, solve named destinations */
+ if (action->goto_dest.dest->type == POPPLER_DEST_NAMED) {
+ PopplerDest *dest;
+ EvLinkDest *ev_dest = NULL;
+ EvLinkAction *ev_action;
+
+ dest = poppler_document_find_dest (pdf_document->document,
+ action->goto_dest.dest->named_dest);
+ if (!dest) {
+ link = ev_link_from_action (pdf_document, action);
+ break;
+ }
+
+ ev_dest = ev_link_dest_from_dest (pdf_document, dest);
+ poppler_dest_free (dest);
+
+ ev_action = ev_link_action_new_dest (ev_dest);
+ link = ev_link_new (action->any.title, ev_action);
+ } else {
+ link = ev_link_from_action (pdf_document, action);
+ }
+ }
+ break;
+ default:
+ link = ev_link_from_action (pdf_document, action);
+ break;
+ }
+
+ if (!link) {
+ poppler_action_free (action);
+ continue;
+ }
+
+ gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent);
+ title_markup = g_markup_escape_text (ev_link_get_title (link), -1);
+
+ gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter,
+ EV_DOCUMENT_LINKS_COLUMN_MARKUP, title_markup,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, link,
+ EV_DOCUMENT_LINKS_COLUMN_EXPAND, expand,
+ -1);
+
+ g_free (title_markup);
+ g_object_unref (link);
+
+ child = poppler_index_iter_get_child (iter);
+ if (child)
+ build_tree (pdf_document, model, &tree_iter, child);
+ poppler_index_iter_free (child);
+ poppler_action_free (action);
+
+ } while (poppler_index_iter_next (iter));
+}
+
+static GtkTreeModel *
+pdf_document_links_get_links_model (EvDocumentLinks *document_links)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document_links);
+ GtkTreeModel *model = NULL;
+ PopplerIndexIter *iter;
+
+ g_return_val_if_fail (PDF_IS_DOCUMENT (document_links), NULL);
+
+ iter = poppler_index_iter_new (pdf_document->document);
+ /* Create the model if we have items*/
+ if (iter != NULL) {
+ model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_BOOLEAN);
+ build_tree (pdf_document, model, NULL, iter);
+ poppler_index_iter_free (iter);
+ }
+
+ return model;
+}
+
+static GList *
+pdf_document_links_get_links (EvDocumentLinks *document_links,
+ gint page)
+{
+ PdfDocument *pdf_document;
+ PopplerPage *poppler_page;
+ GList *retval = NULL;
+ GList *mapping_list;
+ GList *list;
+ double height;
+
+ pdf_document = PDF_DOCUMENT (document_links);
+ poppler_page = poppler_document_get_page (pdf_document->document,
+ page);
+ mapping_list = poppler_page_get_link_mapping (poppler_page);
+ poppler_page_get_size (poppler_page, NULL, &height);
+
+ for (list = mapping_list; list; list = list->next) {
+ PopplerLinkMapping *link_mapping;
+ EvLinkMapping *ev_link_mapping;
+
+ link_mapping = (PopplerLinkMapping *)list->data;
+ ev_link_mapping = g_new (EvLinkMapping, 1);
+ ev_link_mapping->link = ev_link_from_action (pdf_document,
+ link_mapping->action);
+ ev_link_mapping->x1 = link_mapping->area.x1;
+ ev_link_mapping->x2 = link_mapping->area.x2;
+ /* Invert this for X-style coordinates */
+ ev_link_mapping->y1 = height - link_mapping->area.y2;
+ ev_link_mapping->y2 = height - link_mapping->area.y1;
+
+ retval = g_list_prepend (retval, ev_link_mapping);
+ }
+
+ poppler_page_free_link_mapping (mapping_list);
+ g_object_unref (poppler_page);
+
+ return g_list_reverse (retval);
+}
+
+static EvLinkDest *
+pdf_document_links_find_link_dest (EvDocumentLinks *document_links,
+ const gchar *link_name)
+{
+ PdfDocument *pdf_document;
+ PopplerDest *dest;
+ EvLinkDest *ev_dest = NULL;
+
+ pdf_document = PDF_DOCUMENT (document_links);
+ dest = poppler_document_find_dest (pdf_document->document,
+ link_name);
+ if (dest) {
+ ev_dest = ev_link_dest_from_dest (pdf_document, dest);
+ poppler_dest_free (dest);
+ }
+
+ return ev_dest;
+}
+
+static void
+pdf_document_document_links_iface_init (EvDocumentLinksIface *iface)
+{
+ iface->has_document_links = pdf_document_links_has_document_links;
+ iface->get_links_model = pdf_document_links_get_links_model;
+ iface->get_links = pdf_document_links_get_links;
+ iface->find_link_dest = pdf_document_links_find_link_dest;
+}
+
+static GList *
+pdf_document_images_get_images (EvDocumentImages *document_images,
+ gint page)
+{
+ GList *retval = NULL;
+#ifdef HAVE_POPPLER_PAGE_GET_IMAGE_MAPPING
+ PdfDocument *pdf_document;
+ PopplerPage *poppler_page;
+ GList *mapping_list;
+ GList *list;
+
+ pdf_document = PDF_DOCUMENT (document_images);
+ poppler_page = poppler_document_get_page (pdf_document->document, page);
+ mapping_list = poppler_page_get_image_mapping (poppler_page);
+
+ for (list = mapping_list; list; list = list->next) {
+ PopplerImageMapping *image_mapping;
+ EvImageMapping *ev_image_mapping;
+
+ image_mapping = (PopplerImageMapping *)list->data;
+
+ ev_image_mapping = g_new (EvImageMapping, 1);
+
+ ev_image_mapping->image = ev_image_new_from_pixbuf (image_mapping->image);
+ ev_image_mapping->x1 = image_mapping->area.x1;
+ ev_image_mapping->x2 = image_mapping->area.x2;
+ ev_image_mapping->y1 = image_mapping->area.y1;
+ ev_image_mapping->y2 = image_mapping->area.y2;
+
+ retval = g_list_prepend (retval, ev_image_mapping);
+ }
+
+ poppler_page_free_image_mapping (mapping_list);
+ g_object_unref (poppler_page);
+#endif /* HAVE_POPPLER_PAGE_GET_IMAGE_MAPPING */
+ return retval;
+}
+
+static void
+pdf_document_document_images_iface_init (EvDocumentImagesIface *iface)
+{
+ iface->get_images = pdf_document_images_get_images;
+}
+
+static GdkPixbuf *
+make_thumbnail_for_size (PdfDocument *pdf_document,
+ gint page,
+ int rotation,
+ gint size)
+{
+ PopplerPage *poppler_page;
+ GdkPixbuf *pixbuf;
+ int width, height;
+ double scale;
+ gdouble unscaled_width, unscaled_height;
+
+ poppler_page = poppler_document_get_page (pdf_document->document, page);
+ g_return_val_if_fail (poppler_page != NULL, NULL);
+
+ pdf_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (pdf_document), page,
+ size, &width, &height);
+ poppler_page_get_size (poppler_page, &unscaled_width, &unscaled_height);
+ scale = width / unscaled_width;
+
+ /* rotate */
+ if (rotation == 90 || rotation == 270) {
+ int temp;
+ temp = width;
+ width = height;
+ height = temp;
+ }
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
+ width, height);
+ gdk_pixbuf_fill (pixbuf, 0xffffffff);
+
+ ev_document_fc_mutex_lock ();
+ poppler_page_render_to_pixbuf (poppler_page, 0, 0,
+ width, height,
+ scale, rotation, pixbuf);
+ ev_document_fc_mutex_unlock ();
+
+
+ g_object_unref (poppler_page);
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails,
+ gint page,
+ gint rotation,
+ gint size,
+ gboolean border)
+{
+ PdfDocument *pdf_document;
+ PopplerPage *poppler_page;
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *border_pixbuf;
+
+ pdf_document = PDF_DOCUMENT (document_thumbnails);
+
+ poppler_page = poppler_document_get_page (pdf_document->document, page);
+ g_return_val_if_fail (poppler_page != NULL, NULL);
+
+ pixbuf = poppler_page_get_thumbnail (poppler_page);
+
+ if (pixbuf == NULL) {
+ /* There is no provided thumbnail. We need to make one. */
+ pixbuf = make_thumbnail_for_size (pdf_document, page, rotation, size);
+ }
+
+ if (border) {
+ border_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, rotation, pixbuf);
+ g_object_unref (pixbuf);
+ pixbuf = border_pixbuf;
+ }
+
+ g_object_unref (poppler_page);
+
+ return pixbuf;
+}
+
+static void
+pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails,
+ gint page,
+ gint size,
+ gint *width,
+ gint *height)
+{
+ PdfDocument *pdf_document;
+ PopplerPage *poppler_page;
+ gint has_thumb;
+
+ pdf_document = PDF_DOCUMENT (document_thumbnails);
+ poppler_page = poppler_document_get_page (pdf_document->document, page);
+
+ g_return_if_fail (width != NULL);
+ g_return_if_fail (height != NULL);
+ g_return_if_fail (poppler_page != NULL);
+
+ has_thumb = poppler_page_get_thumbnail_size (poppler_page, width, height);
+
+ if (!has_thumb) {
+ double page_width, page_height;
+
+ poppler_page_get_size (poppler_page, &page_width, &page_height);
+ *width = size;
+ *height = (int) (size * page_height / page_width);
+ }
+ g_object_unref (poppler_page);
+}
+
+static void
+pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
+{
+ iface->get_thumbnail = pdf_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = pdf_document_thumbnails_get_dimensions;
+}
+
+
+static gboolean
+pdf_document_search_idle_callback (void *data)
+{
+ PdfDocumentSearch *search = (PdfDocumentSearch*) data;
+ PdfDocument *pdf_document = search->document;
+ int n_pages;
+ GList *matches;
+ PopplerPage *page;
+
+ page = poppler_document_get_page (search->document->document,
+ search->search_page);
+
+ ev_document_doc_mutex_lock ();
+ matches = poppler_page_find_text (page, search->text);
+ ev_document_doc_mutex_unlock ();
+
+ g_object_unref (page);
+
+ search->pages[search->search_page] = matches;
+ ev_document_find_changed (EV_DOCUMENT_FIND (pdf_document),
+ search->search_page);
+
+ n_pages = pdf_document_get_n_pages (EV_DOCUMENT (search->document));
+ search->search_page += 1;
+ if (search->search_page == n_pages) {
+ /* wrap around */
+ search->search_page = 0;
+ }
+
+ if (search->search_page != search->start_page) {
+ return TRUE;
+ }
+
+ /* We're done. */
+ search->idle = 0; /* will return FALSE to remove */
+ return FALSE;
+}
+
+
+static PdfDocumentSearch *
+pdf_document_search_new (PdfDocument *pdf_document,
+ int start_page,
+ const char *text)
+{
+ PdfDocumentSearch *search;
+ int n_pages;
+ int i;
+
+ n_pages = pdf_document_get_n_pages (EV_DOCUMENT (pdf_document));
+
+ search = g_new0 (PdfDocumentSearch, 1);
+
+ search->text = g_strdup (text);
+ search->pages = g_new0 (GList *, n_pages);
+ search->document = pdf_document;
+
+ /* We add at low priority so the progress bar repaints */
+ search->idle = g_idle_add_full (G_PRIORITY_LOW,
+ pdf_document_search_idle_callback,
+ search,
+ NULL);
+
+ search->start_page = start_page;
+ search->search_page = start_page;
+
+ return search;
+}
+
+static void
+pdf_document_find_begin (EvDocumentFind *document,
+ int page,
+ const char *search_string,
+ gboolean case_sensitive)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document);
+
+ /* FIXME handle case_sensitive (right now XPDF
+ * code is always case insensitive for ASCII
+ * and case sensitive for all other languaages)
+ */
+
+ if (pdf_document->search &&
+ strcmp (search_string, pdf_document->search->text) == 0)
+ return;
+
+ if (pdf_document->search)
+ pdf_document_search_free (pdf_document->search);
+
+ pdf_document->search = pdf_document_search_new (pdf_document,
+ page,
+ search_string);
+}
+
+static int
+pdf_document_find_get_n_results (EvDocumentFind *document_find, int page)
+{
+ PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
+
+ if (search) {
+ return g_list_length (search->pages[page]);
+ } else {
+ return 0;
+ }
+}
+
+static gboolean
+pdf_document_find_get_result (EvDocumentFind *document_find,
+ int page,
+ int n_result,
+ EvRectangle *rectangle)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document_find);
+ PdfDocumentSearch *search = pdf_document->search;
+ PopplerPage *poppler_page;
+ PopplerRectangle *r;
+ double height;
+
+ if (search == NULL)
+ return FALSE;
+
+ r = (PopplerRectangle *) g_list_nth_data (search->pages[page],
+ n_result);
+ if (r == NULL)
+ return FALSE;
+
+ poppler_page = poppler_document_get_page (pdf_document->document, page);
+ poppler_page_get_size (poppler_page, NULL, &height);
+ rectangle->x1 = r->x1;
+ rectangle->y1 = height - r->y2;
+ rectangle->x2 = r->x2;
+ rectangle->y2 = height - r->y1;
+ g_object_unref (poppler_page);
+
+ return TRUE;
+}
+
+static int
+pdf_document_find_page_has_results (EvDocumentFind *document_find,
+ int page)
+{
+ PdfDocumentSearch *search = PDF_DOCUMENT (document_find)->search;
+
+ return search && search->pages[page] != NULL;
+}
+
+static double
+pdf_document_find_get_progress (EvDocumentFind *document_find)
+{
+ PdfDocumentSearch *search;
+ int n_pages, pages_done;
+
+ search = PDF_DOCUMENT (document_find)->search;
+
+ if (search == NULL) {
+ return 0;
+ }
+
+ n_pages = pdf_document_get_n_pages (EV_DOCUMENT (document_find));
+ if (search->search_page > search->start_page) {
+ pages_done = search->search_page - search->start_page + 1;
+ } else if (search->search_page == search->start_page) {
+ pages_done = n_pages;
+ } else {
+ pages_done = n_pages - search->start_page + search->search_page;
+ }
+
+ return pages_done / (double) n_pages;
+}
+
+static void
+pdf_document_find_cancel (EvDocumentFind *document)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document);
+
+ if (pdf_document->search) {
+ pdf_document_search_free (pdf_document->search);
+ pdf_document->search = NULL;
+ }
+}
+
+static void
+pdf_document_find_iface_init (EvDocumentFindIface *iface)
+{
+ iface->begin = pdf_document_find_begin;
+ iface->get_n_results = pdf_document_find_get_n_results;
+ iface->get_result = pdf_document_find_get_result;
+ iface->page_has_results = pdf_document_find_page_has_results;
+ iface->get_progress = pdf_document_find_get_progress;
+ iface->cancel = pdf_document_find_cancel;
+}
+
+static const gboolean supported_formats[] = {
+ TRUE, /* EV_FILE_FORMAT_PS */
+#ifdef HAVE_CAIRO_PDF
+#ifdef HAVE_POPPLER_PAGE_RENDER
+ TRUE, /* EV_FILE_FORMAT_PDF */
+#else
+ FALSE, /* EV_FILE_FORMAT_PDF */
+#endif
+#endif
+};
+
+static void
+pdf_print_context_free (PdfPrintContext *ctx)
+{
+ if (!ctx)
+ return;
+
+ if (ctx->ps_file) {
+ poppler_ps_file_free (ctx->ps_file);
+ ctx->ps_file = NULL;
+ }
+#ifdef HAVE_CAIRO_PDF
+ if (ctx->pdf_cairo) {
+ cairo_destroy (ctx->pdf_cairo);
+ ctx->pdf_cairo = NULL;
+ }
+#endif
+ g_free (ctx);
+}
+
+static gboolean
+pdf_document_file_exporter_format_supported (EvFileExporter *exporter,
+ EvFileExporterFormat format)
+{
+ return supported_formats[format];
+}
+
+static void
+pdf_document_file_exporter_begin (EvFileExporter *exporter,
+ EvFileExporterFormat format,
+ const char *filename,
+ int first_page,
+ int last_page,
+ double width,
+ double height,
+ gboolean duplex)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
+ PdfPrintContext *ctx;
+
+ if (pdf_document->print_ctx)
+ pdf_print_context_free (pdf_document->print_ctx);
+ pdf_document->print_ctx = g_new0 (PdfPrintContext, 1);
+ ctx = pdf_document->print_ctx;
+ ctx->format = format;
+
+ switch (format) {
+ case EV_FILE_FORMAT_PS:
+ ctx->ps_file = poppler_ps_file_new (pdf_document->document,
+ filename, first_page,
+ last_page - first_page + 1);
+ poppler_ps_file_set_paper_size (ctx->ps_file, width, height);
+ poppler_ps_file_set_duplex (ctx->ps_file, duplex);
+
+ break;
+ case EV_FILE_FORMAT_PDF: {
+#ifdef HAVE_CAIRO_PDF
+ cairo_surface_t *surface;
+
+ surface = cairo_pdf_surface_create (filename, width, height);
+ ctx->pdf_cairo = cairo_create (surface);
+ cairo_surface_destroy (surface);
+#endif
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+pdf_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
+ PdfPrintContext *ctx = pdf_document->print_ctx;
+ PopplerPage *poppler_page;
+
+ g_return_if_fail (pdf_document->print_ctx != NULL);
+
+ poppler_page = poppler_document_get_page (pdf_document->document, rc->page);
+
+ switch (ctx->format) {
+ case EV_FILE_FORMAT_PS:
+ poppler_page_render_to_ps (poppler_page, ctx->ps_file);
+ break;
+ case EV_FILE_FORMAT_PDF:
+#ifdef HAVE_POPPLER_PAGE_RENDER
+ poppler_page_render (poppler_page, ctx->pdf_cairo);
+#endif
+#ifdef HAVE_CAIRO_PDF
+ cairo_show_page (ctx->pdf_cairo);
+#endif
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_object_unref (poppler_page);
+}
+
+static void
+pdf_document_file_exporter_end (EvFileExporter *exporter)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (exporter);
+
+ pdf_print_context_free (pdf_document->print_ctx);
+ pdf_document->print_ctx = NULL;
+}
+
+static void
+pdf_document_file_exporter_iface_init (EvFileExporterIface *iface)
+{
+ iface->format_supported = pdf_document_file_exporter_format_supported;
+ iface->begin = pdf_document_file_exporter_begin;
+ iface->do_page = pdf_document_file_exporter_do_page;
+ iface->end = pdf_document_file_exporter_end;
+}
+
+static void
+pdf_selection_render_selection (EvSelection *selection,
+ EvRenderContext *rc,
+ GdkPixbuf **pixbuf,
+ EvRectangle *points,
+ EvRectangle *old_points,
+ GdkColor *text,
+ GdkColor *base)
+{
+ PdfDocument *pdf_document;
+ double width_points, height_points;
+ gint width, height;
+
+ pdf_document = PDF_DOCUMENT (selection);
+ set_rc_data (pdf_document, rc);
+
+ poppler_page_get_size (POPPLER_PAGE (rc->data), &width_points, &height_points);
+ width = (int) ((width_points * rc->scale) + 0.5);
+ height = (int) ((height_points * rc->scale) + 0.5);
+
+ if (*pixbuf == NULL) {
+ * pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ TRUE, 8,
+ width, height);
+ }
+
+ poppler_page_render_selection (POPPLER_PAGE (rc->data),
+ rc->scale, rc->rotation, *pixbuf,
+ (PopplerRectangle *)points,
+ (PopplerRectangle *)old_points,
+ text,
+ base);
+}
+
+
+static GdkRegion *
+pdf_selection_get_selection_region (EvSelection *selection,
+ EvRenderContext *rc,
+ EvRectangle *points)
+{
+ PdfDocument *pdf_document;
+ GdkRegion *retval;
+
+ pdf_document = PDF_DOCUMENT (selection);
+
+ set_rc_data (pdf_document, rc);
+
+ retval = poppler_page_get_selection_region ((PopplerPage *)rc->data, rc->scale, (PopplerRectangle *) points);
+
+ return retval;
+}
+
+static GdkRegion *
+pdf_selection_get_selection_map (EvSelection *selection,
+ EvRenderContext *rc)
+{
+ PdfDocument *pdf_document;
+ PopplerPage *poppler_page;
+ PopplerRectangle points;
+ GdkRegion *retval;
+
+ pdf_document = PDF_DOCUMENT (selection);
+ poppler_page = poppler_document_get_page (pdf_document->document,
+ rc->page);
+
+ points.x1 = 0.0;
+ points.y1 = 0.0;
+ poppler_page_get_size (poppler_page, &(points.x2), &(points.y2));
+ retval = poppler_page_get_selection_region (poppler_page, 1.0, &points);
+ g_object_unref (poppler_page);
+
+ return retval;
+}
+
+static void
+pdf_selection_iface_init (EvSelectionIface *iface)
+{
+ iface->render_selection = pdf_selection_render_selection;
+ iface->get_selection_region = pdf_selection_get_selection_region;
+ iface->get_selection_map = pdf_selection_get_selection_map;
+}
+
+/* Page Transitions */
+static gdouble
+pdf_document_get_page_duration (EvDocumentTransition *trans,
+ gint page)
+{
+#ifdef HAVE_POPPLER_PAGE_GET_DURATION
+ PdfDocument *pdf_document;
+ PopplerPage *poppler_page;
+ gdouble duration = -1;
+
+ pdf_document = PDF_DOCUMENT (trans);
+ poppler_page = poppler_document_get_page (pdf_document->document, page);
+ if (!poppler_page)
+ return -1;
+
+ duration = poppler_page_get_duration (poppler_page);
+ g_object_unref (poppler_page);
+
+ return duration;
+#else
+ return -1;
+#endif /* HAVE_POPPLER_PAGE_GET_DURATION */
+}
+
+static void
+pdf_document_page_transition_iface_init (EvDocumentTransitionIface *iface)
+{
+ iface->get_page_duration = pdf_document_get_page_duration;
+}
+
+PdfDocument *
+pdf_document_new (void)
+{
+ return PDF_DOCUMENT (g_object_new (PDF_TYPE_DOCUMENT, NULL));
+}
diff --git a/backend/pdf/ev-poppler.h b/backend/pdf/ev-poppler.h
new file mode 100644
index 0000000..8cc65d1
--- /dev/null
+++ b/backend/pdf/ev-poppler.h
@@ -0,0 +1,39 @@
+/* pdfdocument.h: Implementation of EvDocument for PDF
+ * Copyright (C) 2004, Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PDF_DOCUMENT_H__
+#define __PDF_DOCUMENT_H__
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+#define PDF_TYPE_DOCUMENT (pdf_document_get_type ())
+#define PDF_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PDF_TYPE_DOCUMENT, PdfDocument))
+#define PDF_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PDF_TYPE_DOCUMENT))
+
+typedef struct _PdfDocument PdfDocument;
+typedef struct _PdfDocumentClass PdfDocumentClass;
+
+PdfDocument *pdf_document_new (void);
+GType pdf_document_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __PDF_DOCUMENT_H__ */
diff --git a/backend/pixbuf/Makefile.am b/backend/pixbuf/Makefile.am
new file mode 100644
index 0000000..b81e1c8
--- /dev/null
+++ b/backend/pixbuf/Makefile.am
@@ -0,0 +1,10 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ $(BACKEND_CFLAGS)
+
+noinst_LTLIBRARIES = libpixbufdocument.la
+
+libpixbufdocument_la_SOURCES = \
+ pixbuf-document.c \
+ pixbuf-document.h
diff --git a/backend/pixbuf/pixbuf-document.c b/backend/pixbuf/pixbuf-document.c
new file mode 100644
index 0000000..e3d064d
--- /dev/null
+++ b/backend/pixbuf/pixbuf-document.c
@@ -0,0 +1,222 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2004, Anders Carlsson <andersca@gnome.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "pixbuf-document.h"
+#include "ev-document-thumbnails.h"
+
+struct _PixbufDocumentClass
+{
+ GObjectClass parent_class;
+};
+
+struct _PixbufDocument
+{
+ GObject parent_instance;
+
+ GdkPixbuf *pixbuf;
+
+ gchar *uri;
+};
+
+typedef struct _PixbufDocumentClass PixbufDocumentClass;
+
+static void pixbuf_document_document_iface_init (EvDocumentIface *iface);
+static void pixbuf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (PixbufDocument, pixbuf_document, G_TYPE_OBJECT,
+ { G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
+ pixbuf_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+ pixbuf_document_document_thumbnails_iface_init)
+ });
+
+static gboolean
+pixbuf_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document);
+
+ gchar *filename;
+ GdkPixbuf *pixbuf;
+
+ /* FIXME: We could actually load uris */
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ return FALSE;
+
+ pixbuf = gdk_pixbuf_new_from_file (filename, error);
+
+ if (!pixbuf)
+ return FALSE;
+
+ pixbuf_document->pixbuf = pixbuf;
+ g_free (pixbuf_document->uri);
+ pixbuf_document->uri = g_strdup (uri);
+
+ return TRUE;
+}
+
+static gboolean
+pixbuf_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document);
+
+ return ev_xfer_uri_simple (pixbuf_document->uri, uri, error);
+}
+
+static int
+pixbuf_document_get_n_pages (EvDocument *document)
+{
+ return 1;
+}
+
+static void
+pixbuf_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height)
+{
+ PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document);
+
+ *width = gdk_pixbuf_get_width (pixbuf_document->pixbuf);
+ *height = gdk_pixbuf_get_height (pixbuf_document->pixbuf);
+}
+
+static GdkPixbuf*
+pixbuf_document_render_pixbuf (EvDocument *document,
+ EvRenderContext *rc)
+{
+ PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document);
+ GdkPixbuf *scaled_pixbuf, *rotated_pixbuf;
+
+ scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf_document->pixbuf,
+ gdk_pixbuf_get_width (pixbuf_document->pixbuf) * rc->scale,
+ gdk_pixbuf_get_height (pixbuf_document->pixbuf) * rc->scale,
+ GDK_INTERP_BILINEAR);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf, 360 - rc->rotation);
+ g_object_unref (scaled_pixbuf);
+
+ return rotated_pixbuf;
+}
+
+static void
+pixbuf_document_finalize (GObject *object)
+{
+ PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (object);
+
+ g_object_unref (pixbuf_document->pixbuf);
+ g_free (pixbuf_document->uri);
+
+ G_OBJECT_CLASS (pixbuf_document_parent_class)->finalize (object);
+}
+
+static void
+pixbuf_document_class_init (PixbufDocumentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = pixbuf_document_finalize;
+}
+
+static gboolean
+pixbuf_document_can_get_text (EvDocument *document)
+{
+ return FALSE;
+}
+
+static EvDocumentInfo *
+pixbuf_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+
+ info = g_new0 (EvDocumentInfo, 1);
+ info->fields_mask = 0;
+
+ return info;
+}
+
+static void
+pixbuf_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->load = pixbuf_document_load;
+ iface->save = pixbuf_document_save;
+ iface->can_get_text = pixbuf_document_can_get_text;
+ iface->get_n_pages = pixbuf_document_get_n_pages;
+ iface->get_page_size = pixbuf_document_get_page_size;
+ iface->render_pixbuf = pixbuf_document_render_pixbuf;
+ iface->get_info = pixbuf_document_get_info;
+}
+
+static GdkPixbuf *
+pixbuf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ gint page,
+ gint rotation,
+ gint size,
+ gboolean border)
+{
+ PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document);
+ GdkPixbuf *pixbuf, *rotated_pixbuf;
+ gdouble scale_factor;
+ gint height;
+
+ scale_factor = (gdouble)size / gdk_pixbuf_get_width (pixbuf_document->pixbuf);
+
+ height = gdk_pixbuf_get_height (pixbuf_document->pixbuf) * scale_factor;
+
+ pixbuf = gdk_pixbuf_scale_simple (pixbuf_document->pixbuf, size, height,
+ GDK_INTERP_BILINEAR);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rotation);
+ g_object_unref (pixbuf);
+
+ return rotated_pixbuf;
+}
+
+static void
+pixbuf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ gint page,
+ gint suggested_width,
+ gint *width,
+ gint *height)
+{
+ PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document);
+ gdouble page_ratio;
+
+ page_ratio = ((double)gdk_pixbuf_get_height (pixbuf_document->pixbuf)) /
+ gdk_pixbuf_get_width (pixbuf_document->pixbuf);
+ *width = suggested_width;
+ *height = (gint) (suggested_width * page_ratio);
+}
+
+static void
+pixbuf_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
+{
+ iface->get_thumbnail = pixbuf_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = pixbuf_document_thumbnails_get_dimensions;
+}
+
+
+static void
+pixbuf_document_init (PixbufDocument *pixbuf_document)
+{
+}
diff --git a/backend/pixbuf/pixbuf-document.h b/backend/pixbuf/pixbuf-document.h
new file mode 100644
index 0000000..bddf94a
--- /dev/null
+++ b/backend/pixbuf/pixbuf-document.h
@@ -0,0 +1,38 @@
+/* pdfdocument.h: Implementation of EvDocument for pixbufs
+ * Copyright (C) 2004, Anders Carlsson <andersca@gnome.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PIXBUF_DOCUMENT_H__
+#define __PIXBUF_DOCUMENT_H__
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+#define PIXBUF_TYPE_DOCUMENT (pixbuf_document_get_type ())
+#define PIXBUF_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIXBUF_TYPE_DOCUMENT, PixbufDocument))
+#define PIXBUF_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIXBUF_TYPE_DOCUMENT))
+
+typedef struct _PixbufDocument PixbufDocument;
+
+PixbufDocument *pixbuf_document_new (void);
+
+GType pixbuf_document_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __PIXBUF_DOCUMENT_H__ */
diff --git a/backend/ps/Makefile.am b/backend/ps/Makefile.am
new file mode 100644
index 0000000..499eee3
--- /dev/null
+++ b/backend/ps/Makefile.am
@@ -0,0 +1,21 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ $(BACKEND_CFLAGS) \
+ $(WARN_CFLAGS)
+
+# $(DISABLE_DEPRECATED)
+
+noinst_LTLIBRARIES = libpsdocument.la
+
+libpsdocument_la_SOURCES = \
+ gsio.c \
+ gsio.h \
+ gstypes.h \
+ ps.c \
+ ps.h \
+ ps-document.c \
+ ps-document.h \
+ gsdefaults.c \
+ gsdefaults.h
+
diff --git a/backend/ps/gsdefaults.c b/backend/ps/gsdefaults.c
new file mode 100644
index 0000000..2263fca
--- /dev/null
+++ b/backend/ps/gsdefaults.c
@@ -0,0 +1,67 @@
+/*
+ * gsdefaults.c: default settings for the GtkGS widget
+ *
+ * Copyright 2002 - 2005 the Free Software Foundation
+ *
+ * Author: Jaka Mocnik <jaka@gnu.org>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+
+#include "gsdefaults.h"
+
+GtkGSPaperSize gtk_gs_paper_sizes[] = {
+ {N_("BBox"), 0, 0},
+ {N_("Letter"), 612, 792,},
+ {N_("Tabloid"), 792, 1224,},
+ {N_("Ledger"), 1224, 792,},
+ {N_("Legal"), 612, 1008,},
+ {N_("Statement"), 396, 612,},
+ {N_("Executive"), 540, 720,},
+ {N_("A0"), 2380, 3368,},
+ {N_("A1"), 1684, 2380,},
+ {N_("A2"), 1190, 1684,},
+ {N_("A3"), 842, 1190,},
+ {N_("A4"), 595, 842,},
+ {N_("A5"), 420, 595,},
+ {N_("B4"), 729, 1032,},
+ {N_("B5"), 516, 729,},
+ {N_("Folio"), 612, 936,},
+ {N_("Quarto"), 610, 780,},
+ {N_("10x14"), 720, 1008,},
+ {NULL, 0, 0}
+};
+
+GtkGSPaperSize *
+gtk_gs_defaults_get_paper_sizes()
+{
+ return gtk_gs_paper_sizes;
+}
+
+const gchar *
+gtk_gs_defaults_get_ungzip_cmd (void)
+{
+ return "gzip -cd";
+}
+
+const gchar *
+gtk_gs_defaults_get_unbzip2_cmd (void)
+{
+ return "bzip2 -cd";
+}
diff --git a/backend/ps/gsdefaults.h b/backend/ps/gsdefaults.h
new file mode 100644
index 0000000..2f50f6f
--- /dev/null
+++ b/backend/ps/gsdefaults.h
@@ -0,0 +1,39 @@
+/*
+ * gsdefaults.h: default settings of a GtkGS widget.
+ *
+ * Copyright 2002 - 2005 the Free Software Foundation
+ *
+ * Author: Jaka Mocnik <jaka@gnu.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef __GS_DEFAULTS_H__
+#define __GS_DEFAULTS_H__
+
+#include <glib.h>
+
+#include "gstypes.h"
+
+G_BEGIN_DECLS
+
+/* defaults accessors */
+
+GtkGSPaperSize *gtk_gs_defaults_get_paper_sizes(void);
+const gchar *gtk_gs_defaults_get_ungzip_cmd(void);
+const gchar *gtk_gs_defaults_get_unbzip2_cmd(void);
+
+G_END_DECLS
+
+#endif /* __GS_DEFAULTS_H__ */
diff --git a/backend/ps/gsio.c b/backend/ps/gsio.c
new file mode 100644
index 0000000..15c7f38
--- /dev/null
+++ b/backend/ps/gsio.c
@@ -0,0 +1,172 @@
+/*
+ * gsio.c: an IO abstraction
+ *
+ * Copyright 2002 - 2005 the Free Software Foundation
+ *
+ * Author: Jaka Mocnik <jaka@gnu.org>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+
+#include <gsio.h>
+
+#define CHUNK_SIZE 32768
+
+typedef struct _GtkGSDocChunk GtkGSDocChunk;
+struct _GtkGSDocChunk {
+ gchar *buf, *ptr;
+ guint len, max_len;
+};
+
+struct _GtkGSDocSink {
+ GSList *chunks;
+ GtkGSDocChunk *tail;
+};
+
+static GtkGSDocChunk *
+gtk_gs_doc_chunk_new(guint size)
+{
+ GtkGSDocChunk *c;
+
+ c = g_new0(GtkGSDocChunk, 1);
+ if((c->buf = g_malloc(sizeof(gchar) * size)) == NULL) {
+ g_free(c);
+ return NULL;
+ }
+ c->ptr = c->buf;
+ *c->ptr = '\0';
+ c->max_len = size;
+ c->len = 0;
+ return c;
+}
+
+static void
+gtk_gs_doc_chunk_free(GtkGSDocChunk * c)
+{
+ if(c->buf)
+ g_free(c->buf);
+ g_free(c);
+}
+
+GtkGSDocSink *
+gtk_gs_doc_sink_new()
+{
+ GtkGSDocSink *sink;
+
+ sink = g_new0(GtkGSDocSink, 1);
+ return sink;
+}
+
+void
+gtk_gs_doc_sink_free(GtkGSDocSink * sink)
+{
+ GSList *node;
+
+ node = sink->chunks;
+ while(node) {
+ gtk_gs_doc_chunk_free((GtkGSDocChunk *) node->data);
+ node = node->next;
+ }
+ g_slist_free(sink->chunks);
+}
+
+void
+gtk_gs_doc_sink_write(GtkGSDocSink * sink, const gchar * buf, int len)
+{
+ gint real_len;
+
+ if(sink->tail == NULL) {
+ sink->tail = gtk_gs_doc_chunk_new(CHUNK_SIZE);
+ sink->chunks = g_slist_append(sink->chunks, sink->tail);
+ }
+
+ real_len = MIN(sink->tail->max_len - sink->tail->len, len);
+ if(real_len > 0) {
+ strncpy(sink->tail->ptr, buf, real_len);
+ sink->tail->ptr += real_len;
+ sink->tail->len += real_len;
+ }
+ len -= real_len;
+ if(len > 0) {
+ sink->tail = NULL;
+ gtk_gs_doc_sink_write(sink, buf + real_len, len);
+ }
+}
+
+void
+gtk_gs_doc_sink_printf_v(GtkGSDocSink * sink, const gchar * fmt, va_list ap)
+{
+ gint max_len, len;
+
+ if(sink->tail == NULL) {
+ sink->tail = gtk_gs_doc_chunk_new(CHUNK_SIZE);
+ sink->chunks = g_slist_append(sink->chunks, sink->tail);
+ }
+
+ max_len = sink->tail->max_len - sink->tail->len;
+ if(max_len > 0) {
+ len = g_vsnprintf(sink->tail->ptr, max_len, fmt, ap);
+ if(len >= max_len - 1) {
+ /* force printf in the next chunk later on */
+ max_len = 0;
+ sink->tail = NULL;
+ }
+ else {
+ sink->tail->ptr += len;
+ sink->tail->len += len;
+ }
+ }
+ if(max_len <= 0) {
+ gtk_gs_doc_sink_printf(sink, fmt, ap);
+ }
+}
+
+void
+gtk_gs_doc_sink_printf(GtkGSDocSink * sink, const gchar * fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ gtk_gs_doc_sink_printf_v(sink, fmt, ap);
+ va_end(ap);
+}
+
+gchar *
+gtk_gs_doc_sink_get_buffer(GtkGSDocSink * sink)
+{
+ guint total;
+ GSList *node;
+
+ for(total = 0, node = sink->chunks; node; node = node->next) {
+ total += ((GtkGSDocChunk *) node->data)->len;
+ }
+ if(total) {
+ gchar *buf = g_malloc(sizeof(gchar) * (total + 1)), *ptr;
+ if(!buf)
+ return NULL;
+ for(ptr = buf, node = sink->chunks; node; node = node->next) {
+ memcpy(ptr,
+ ((GtkGSDocChunk *) node->data)->buf,
+ ((GtkGSDocChunk *) node->data)->len);
+ ptr += ((GtkGSDocChunk *) node->data)->len;
+ }
+ buf[total] = '\0';
+ return buf;
+ }
+ else
+ return NULL;
+}
diff --git a/backend/ps/gsio.h b/backend/ps/gsio.h
new file mode 100644
index 0000000..3f83cb0
--- /dev/null
+++ b/backend/ps/gsio.h
@@ -0,0 +1,42 @@
+/*
+ * gsio.h: an IO abstraction
+ *
+ * Copyright 2002 - 2005 The Free Software Foundation
+ *
+ * Author: jaKa Mocnik <jaka@gnu.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GS_IO_H__
+#define __GS_IO_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkGSDocSink GtkGSDocSink;
+
+GtkGSDocSink *gtk_gs_doc_sink_new(void);
+void gtk_gs_doc_sink_free(GtkGSDocSink * sink);
+void gtk_gs_doc_sink_write(GtkGSDocSink * sink, const gchar * buf, int len);
+void gtk_gs_doc_sink_printf_v(GtkGSDocSink * sink, const gchar * fmt,
+ va_list ap);
+void gtk_gs_doc_sink_printf(GtkGSDocSink * sink, const gchar * fmt, ...);
+gchar *gtk_gs_doc_sink_get_buffer(GtkGSDocSink * sink);
+
+G_END_DECLS
+
+#endif /* __GS_IO_H__ */
diff --git a/backend/ps/gstypes.h b/backend/ps/gstypes.h
new file mode 100644
index 0000000..f1f5cb2
--- /dev/null
+++ b/backend/ps/gstypes.h
@@ -0,0 +1,48 @@
+/*
+ * Ghostscript widget for GTK/GNOME
+ *
+ * Copyright 1998 - 2005 The Free Software Foundation
+ *
+ * Authors: Jaka Mocnik, Federico Mena (Quartic), Szekeres Istvan (Pista)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSTYPES_H__
+#define __GSTYPES_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkGSPaperSize GtkGSPaperSize;
+
+typedef enum {
+ GTK_GS_ORIENTATION_NONE = -1,
+ GTK_GS_ORIENTATION_PORTRAIT = 0,
+ GTK_GS_ORIENTATION_SEASCAPE = 3,
+ GTK_GS_ORIENTATION_UPSIDEDOWN = 2,
+ GTK_GS_ORIENTATION_LANDSCAPE = 1
+} GtkGSOrientation;
+
+struct _GtkGSPaperSize {
+ gchar *name;
+ gint width, height;
+};
+
+G_END_DECLS
+
+#endif /* __GSTYPES_H__ */
diff --git a/backend/ps/ps-document.c b/backend/ps/ps-document.c
new file mode 100644
index 0000000..ee5d449
--- /dev/null
+++ b/backend/ps/ps-document.c
@@ -0,0 +1,1339 @@
+/* Ghostscript widget for GTK/GNOME
+ *
+ * Copyright (C) 1998 - 2005 the Free Software Foundation
+ *
+ * Authors: Jonathan Blandford, Jaka Mocnik
+ *
+ * Based on code by: Federico Mena (Quartic), Szekeres Istvan (Pista)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkobject.h>
+#include <gdk/gdkprivate.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+#include <X11/Intrinsic.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "ps-document.h"
+#include "gsdefaults.h"
+#include "ev-file-exporter.h"
+#include "ev-async-renderer.h"
+
+#define MAX_BUFSIZE 1024
+
+#define PS_DOCUMENT_IS_COMPRESSED(gs) (PS_DOCUMENT(gs)->gs_filename_unc != NULL)
+#define PS_DOCUMENT_GET_PS_FILE(gs) (PS_DOCUMENT_IS_COMPRESSED(gs) ? \
+ PS_DOCUMENT(gs)->gs_filename_unc : \
+ PS_DOCUMENT(gs)->gs_filename)
+
+/* structure to describe section of file to send to ghostscript */
+struct record_list
+{
+ FILE *fp;
+ long begin;
+ guint len;
+ gboolean seek_needed;
+ gboolean close;
+ struct record_list *next;
+};
+
+static gboolean broken_pipe = FALSE;
+
+/* Forward declarations */
+static void ps_document_init (PSDocument *gs);
+static void ps_document_class_init (PSDocumentClass *klass);
+static void send_ps (PSDocument *gs,
+ long begin,
+ unsigned int len,
+ gboolean close);
+static void output (gpointer data,
+ gint source,
+ GdkInputCondition condition);
+static void input (gpointer data,
+ gint source,
+ GdkInputCondition condition);
+static void stop_interpreter (PSDocument *gs);
+static gint start_interpreter (PSDocument *gs);
+static void ps_document_document_iface_init (EvDocumentIface *iface);
+static void ps_document_file_exporter_iface_init (EvFileExporterIface *iface);
+static void ps_async_renderer_iface_init (EvAsyncRendererIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (PSDocument, ps_document, G_TYPE_OBJECT,
+ {
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
+ ps_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
+ ps_document_file_exporter_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_ASYNC_RENDERER,
+ ps_async_renderer_iface_init);
+ });
+
+static GObjectClass *parent_class = NULL;
+static PSDocumentClass *gs_class = NULL;
+
+static void
+ps_document_init (PSDocument *gs)
+{
+ gs->bpixmap = NULL;
+
+ gs->interpreter_pid = -1;
+
+ gs->busy = FALSE;
+ gs->gs_filename = 0;
+ gs->gs_filename_unc = 0;
+
+ broken_pipe = FALSE;
+
+ gs->structured_doc = FALSE;
+ gs->reading_from_pipe = FALSE;
+ gs->send_filename_to_gs = FALSE;
+
+ gs->doc = NULL;
+
+ gs->interpreter_input = -1;
+ gs->interpreter_output = -1;
+ gs->interpreter_err = -1;
+ gs->interpreter_input_id = 0;
+ gs->interpreter_output_id = 0;
+ gs->interpreter_error_id = 0;
+
+ gs->ps_input = NULL;
+ gs->input_buffer = NULL;
+ gs->input_buffer_ptr = NULL;
+ gs->bytes_left = 0;
+ gs->buffer_bytes_left = 0;
+
+ gs->gs_status = _("No document loaded.");
+
+ gs->ps_export_pagelist = NULL;
+ gs->ps_export_filename = NULL;
+}
+
+static void
+ps_document_dispose (GObject *object)
+{
+ PSDocument *gs = PS_DOCUMENT (object);
+
+ g_return_if_fail (gs != NULL);
+
+ if (gs->gs_psfile) {
+ fclose (gs->gs_psfile);
+ gs->gs_psfile = NULL;
+ }
+
+ if (gs->gs_filename) {
+ g_free (gs->gs_filename);
+ gs->gs_filename = NULL;
+ }
+
+ if (gs->doc) {
+ psfree (gs->doc);
+ gs->doc = NULL;
+ }
+
+ if (gs->gs_filename_unc) {
+ unlink(gs->gs_filename_unc);
+ g_free(gs->gs_filename_unc);
+ gs->gs_filename_unc = NULL;
+ }
+
+ if (gs->bpixmap) {
+ gdk_drawable_unref (gs->bpixmap);
+ }
+
+ if(gs->input_buffer) {
+ g_free(gs->input_buffer);
+ gs->input_buffer = NULL;
+ }
+
+ if (gs->target_window) {
+ gtk_widget_destroy (gs->target_window);
+ gs->target_window = NULL;
+ gs->pstarget = NULL;
+ }
+
+ stop_interpreter (gs);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+ps_document_class_init(PSDocumentClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) klass;
+ parent_class = g_type_class_peek_parent (klass);
+ gs_class = klass;
+
+ object_class->dispose = ps_document_dispose;
+
+ klass->gs_atom = gdk_atom_intern ("GHOSTVIEW", FALSE);
+ klass->next_atom = gdk_atom_intern ("NEXT", FALSE);
+ klass->page_atom = gdk_atom_intern ("PAGE", FALSE);
+ klass->string_atom = gdk_atom_intern ("STRING", FALSE);
+}
+
+static void
+push_pixbuf (PSDocument *gs)
+{
+ GdkColormap *cmap;
+ GdkPixbuf *pixbuf;
+ int width, height;
+
+ if (gs->pstarget == NULL)
+ return;
+
+ cmap = gdk_window_get_colormap (gs->pstarget);
+ gdk_drawable_get_size (gs->bpixmap, &width, &height);
+ pixbuf = gdk_pixbuf_get_from_drawable (NULL, gs->bpixmap, cmap,
+ 0, 0, 0, 0,
+ width, height);
+ g_signal_emit_by_name (gs, "render_finished", pixbuf);
+ g_object_unref (pixbuf);
+}
+
+static void
+interpreter_failed (PSDocument *gs, char *msg)
+{
+ push_pixbuf (gs);
+
+ stop_interpreter (gs);
+}
+
+static gboolean
+ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ PSDocument *gs = (PSDocument *) data;
+
+ if(event->type != GDK_CLIENT_EVENT)
+ return FALSE;
+
+ gs->message_window = event->client.data.l[0];
+
+ if (event->client.message_type == gs_class->page_atom) {
+ gs->busy = FALSE;
+
+ push_pixbuf (gs);
+ }
+
+ return TRUE;
+}
+
+static void
+send_ps (PSDocument *gs, long begin, unsigned int len, gboolean close)
+{
+ struct record_list *ps_new;
+
+ if (gs->interpreter_input < 0) {
+ g_critical("No pipe to gs: error in send_ps().");
+ return;
+ }
+
+ ps_new = g_new0 (struct record_list, 1);
+ ps_new->fp = gs->gs_psfile;
+ ps_new->begin = begin;
+ ps_new->len = len;
+ ps_new->seek_needed = TRUE;
+ ps_new->close = close;
+ ps_new->next = NULL;
+
+ if (gs->input_buffer == NULL) {
+ gs->input_buffer = g_malloc(MAX_BUFSIZE);
+ }
+
+ if (gs->ps_input == NULL) {
+ gs->input_buffer_ptr = gs->input_buffer;
+ gs->bytes_left = len;
+ gs->buffer_bytes_left = 0;
+ gs->ps_input = ps_new;
+ gs->interpreter_input_id = gdk_input_add
+ (gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
+ } else {
+ struct record_list *p = gs->ps_input;
+ while (p->next != NULL) {
+ p = p->next;
+ }
+ p->next = ps_new;
+ }
+}
+
+static void
+setup_pixmap (PSDocument *gs, int page, double scale, int rotation)
+{
+ GdkGC *fill;
+ GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
+ GdkColormap *colormap;
+ double width, height;
+ int pixmap_width, pixmap_height;
+
+ if (gs->pstarget == NULL)
+ return;
+
+ ev_document_get_page_size (EV_DOCUMENT (gs), page, &width, &height);
+
+ if (rotation == 90 || rotation == 270) {
+ pixmap_height = width * scale + 0.5;
+ pixmap_width = height * scale + 0.5;
+ } else {
+ pixmap_width = width * scale + 0.5;
+ pixmap_height = height * scale + 0.5;
+ }
+
+ if(gs->bpixmap) {
+ int w, h;
+
+ gdk_drawable_get_size (gs->bpixmap, &w, &h);
+
+ if (pixmap_width != w || h != pixmap_height) {
+ gdk_drawable_unref (gs->bpixmap);
+ gs->bpixmap = NULL;
+ stop_interpreter (gs);
+ }
+ }
+
+ if (!gs->bpixmap) {
+
+ fill = gdk_gc_new (gs->pstarget);
+ colormap = gdk_drawable_get_colormap (gs->pstarget);
+ gdk_color_alloc (colormap, &white);
+ gdk_gc_set_foreground (fill, &white);
+ gs->bpixmap = gdk_pixmap_new (gs->pstarget, pixmap_width,
+ pixmap_height, -1);
+ gdk_draw_rectangle (gs->bpixmap, fill, TRUE,
+ 0, 0, pixmap_width, pixmap_height);
+ }
+}
+
+#define DEFAULT_PAGE_SIZE 1
+
+static void
+get_page_box (PSDocument *gs, int page, int *urx, int *ury, int *llx, int *lly)
+{
+ gint new_llx = 0;
+ gint new_lly = 0;
+ gint new_urx = 0;
+ gint new_ury = 0;
+ GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes ();
+ int new_pagesize = -1;
+
+ g_return_if_fail (PS_IS_DOCUMENT (gs));
+
+ if (new_pagesize == -1) {
+ new_pagesize = DEFAULT_PAGE_SIZE;
+ if (gs->doc) {
+ /* If we have a document:
+ * We use -- the page size (if specified)
+ * or the doc. size (if specified)
+ * or the page bbox (if specified)
+ * or the bounding box
+ */
+ if ((page >= 0) && (gs->doc->numpages > page) &&
+ (gs->doc->pages) && (gs->doc->pages[page].size)) {
+ new_pagesize = gs->doc->pages[page].size - gs->doc->size;
+ } else if (gs->doc->default_page_size != NULL) {
+ new_pagesize = gs->doc->default_page_size - gs->doc->size;
+ } else if ((page >= 0) &&
+ (gs->doc->numpages > page) &&
+ (gs->doc->pages) &&
+ (gs->doc->pages[page].boundingbox[URX] >
+ gs->doc->pages[page].boundingbox[LLX]) &&
+ (gs->doc->pages[page].boundingbox[URY] >
+ gs->doc->pages[page].boundingbox[LLY])) {
+ new_pagesize = -1;
+ } else if ((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
+ (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
+ new_pagesize = -1;
+ }
+ }
+ }
+
+ /* Compute bounding box */
+ if (gs->doc && (gs->doc->epsf || new_pagesize == -1)) { /* epsf or bbox */
+ if ((page >= 0) &&
+ (gs->doc->pages) &&
+ (gs->doc->pages[page].boundingbox[URX] >
+ gs->doc->pages[page].boundingbox[LLX]) &&
+ (gs->doc->pages[page].boundingbox[URY] >
+ gs->doc->pages[page].boundingbox[LLY])) {
+ /* use page bbox */
+ new_llx = gs->doc->pages[page].boundingbox[LLX];
+ new_lly = gs->doc->pages[page].boundingbox[LLY];
+ new_urx = gs->doc->pages[page].boundingbox[URX];
+ new_ury = gs->doc->pages[page].boundingbox[URY];
+ } else if ((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
+ (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
+ /* use doc bbox */
+ new_llx = gs->doc->boundingbox[LLX];
+ new_lly = gs->doc->boundingbox[LLY];
+ new_urx = gs->doc->boundingbox[URX];
+ new_ury = gs->doc->boundingbox[URY];
+ }
+ } else {
+ if (new_pagesize < 0)
+ new_pagesize = DEFAULT_PAGE_SIZE;
+ new_llx = new_lly = 0;
+ if (gs->doc && gs->doc->size &&
+ (new_pagesize < gs->doc->numsizes)) {
+ new_urx = gs->doc->size[new_pagesize].width;
+ new_ury = gs->doc->size[new_pagesize].height;
+ } else {
+ new_urx = papersizes[new_pagesize].width;
+ new_ury = papersizes[new_pagesize].height;
+ }
+ }
+
+ if (new_urx <= new_llx)
+ new_urx = papersizes[12].width;
+ if (new_ury <= new_lly)
+ new_ury = papersizes[12].height;
+
+ *urx = new_urx;
+ *ury = new_ury;
+ *llx = new_llx;
+ *lly = new_lly;
+}
+
+static void
+setup_page (PSDocument *gs, int page, double scale, int rotation)
+{
+ gchar *buf;
+ char scaled_dpi[G_ASCII_DTOSTR_BUF_SIZE];
+ int urx, ury, llx, lly;
+
+ get_page_box (gs, page, &urx, &ury, &llx, &lly);
+ g_ascii_dtostr (scaled_dpi, G_ASCII_DTOSTR_BUF_SIZE, 72.0 * scale);
+
+ buf = g_strdup_printf ("%ld %d %d %d %d %d %s %s %d %d %d %d",
+ 0L, rotation, llx, lly, urx, ury,
+ scaled_dpi, scaled_dpi,
+ 0, 0, 0, 0);
+
+ gdk_property_change (gs->pstarget, gs_class->gs_atom, gs_class->string_atom,
+ 8, GDK_PROP_MODE_REPLACE, (guchar *)buf, strlen(buf));
+ g_free (buf);
+
+ gdk_flush ();
+}
+
+static void
+close_pipe (int p[2])
+{
+ if (p[0] != -1) {
+ close (p[0]);
+ }
+ if (p[1] != -1) {
+ close (p[1]);
+ }
+}
+
+static gboolean
+is_interpreter_ready (PSDocument *gs)
+{
+ return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
+}
+
+static void
+output (gpointer data, gint source, GdkInputCondition condition)
+{
+ char buf[MAX_BUFSIZE + 1];
+ guint bytes = 0;
+ PSDocument *gs = PS_DOCUMENT(data);
+
+ if (source == gs->interpreter_output) {
+ bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
+ if (bytes == 0) { /* EOF occurred */
+ close (gs->interpreter_output);
+ gs->interpreter_output = -1;
+ gdk_input_remove (gs->interpreter_output_id);
+ return;
+ } else if (bytes == -1) {
+ /* trouble... */
+ interpreter_failed (gs, NULL);
+ return;
+ }
+ if (gs->interpreter_err == -1) {
+ interpreter_failed (gs, NULL);
+ }
+ } else if (source == gs->interpreter_err) {
+ bytes = read (gs->interpreter_err, buf, MAX_BUFSIZE);
+ if (bytes == 0) { /* EOF occurred */
+ close (gs->interpreter_err);
+ gs->interpreter_err = -1;
+ gdk_input_remove (gs->interpreter_error_id);
+ return;
+ } else if (bytes == -1) {
+ /* trouble... */
+ interpreter_failed (gs, NULL);
+ return;
+ }
+ if (gs->interpreter_output == -1) {
+ interpreter_failed(gs, NULL);
+ }
+ }
+
+ if (bytes > 0) {
+ buf[bytes] = '\0';
+ printf ("%s", buf);
+ }
+}
+
+static void
+catchPipe (int i)
+{
+ broken_pipe = True;
+}
+
+static void
+input(gpointer data, gint source, GdkInputCondition condition)
+{
+ PSDocument *gs = PS_DOCUMENT(data);
+ int bytes_written;
+ void (*oldsig) (int);
+ oldsig = signal(SIGPIPE, catchPipe);
+
+ do {
+ if (gs->buffer_bytes_left == 0) {
+ /* Get a new section if required */
+ if (gs->ps_input && gs->bytes_left == 0) {
+ struct record_list *ps_old = gs->ps_input;
+ gs->ps_input = ps_old->next;
+ if (ps_old->close && NULL != ps_old->fp)
+ fclose (ps_old->fp);
+ g_free (ps_old);
+ }
+
+ /* Have to seek at the beginning of each section */
+ if (gs->ps_input && gs->ps_input->seek_needed) {
+ fseek (gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
+ gs->ps_input->seek_needed = FALSE;
+ gs->bytes_left = gs->ps_input->len;
+ }
+
+ if (gs->bytes_left > MAX_BUFSIZE) {
+ gs->buffer_bytes_left = fread (gs->input_buffer, sizeof(char),
+ MAX_BUFSIZE, gs->ps_input->fp);
+ } else if (gs->bytes_left > 0) {
+ gs->buffer_bytes_left = fread (gs->input_buffer, sizeof(char),
+ gs->bytes_left, gs->ps_input->fp);
+ } else {
+ gs->buffer_bytes_left = 0;
+ }
+ if (gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
+ interpreter_failed (gs, NULL); /* Error occurred */
+ }
+ gs->input_buffer_ptr = gs->input_buffer;
+ gs->bytes_left -= gs->buffer_bytes_left;
+ }
+
+ if (gs->buffer_bytes_left > 0) {
+ bytes_written = write (gs->interpreter_input,
+ gs->input_buffer_ptr, gs->buffer_bytes_left);
+
+ if (broken_pipe) {
+ interpreter_failed (gs, g_strdup(_("Broken pipe.")));
+ broken_pipe = FALSE;
+ interpreter_failed (gs, NULL);
+ } else if (bytes_written == -1) {
+ if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
+ interpreter_failed (gs, NULL); /* Something bad happened */
+ }
+ } else {
+ gs->buffer_bytes_left -= bytes_written;
+ gs->input_buffer_ptr += bytes_written;
+ }
+ }
+ } while (gs->ps_input && gs->buffer_bytes_left == 0);
+
+ signal (SIGPIPE, oldsig);
+
+ if (gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
+ if (gs->interpreter_input_id != 0) {
+ gdk_input_remove (gs->interpreter_input_id);
+ gs->interpreter_input_id = 0;
+ }
+ }
+}
+
+static int
+start_interpreter (PSDocument *gs)
+{
+ int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
+ int std_out[2]; /* pipe from interp stdout */
+ int std_err[2]; /* pipe from interp stderr */
+
+#define NUM_ARGS 100
+#define NUM_GS_ARGS (NUM_ARGS - 20)
+#define NUM_ALPHA_ARGS 10
+
+ char *argv[NUM_ARGS], *dir, *gv_env, *gs_path;
+ char **gs_args, **alpha_args = NULL;
+ char **gv_env_vars = NULL;
+ int argc = 0, i;
+
+ if(!gs->gs_filename)
+ return 0;
+
+ stop_interpreter(gs);
+
+ /* set up the args... */
+ gs_path = g_find_program_in_path ("gs");
+ gs_args = g_strsplit (gs_path, " ", NUM_GS_ARGS);
+ g_free (gs_path);
+ for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++) {
+ argv[argc] = gs_args[i];
+ }
+
+ alpha_args = g_strsplit (ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
+ for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++) {
+ argv[argc] = alpha_args[i];
+ }
+
+ argv[argc++] = "-dNOPAUSE";
+ argv[argc++] = "-dQUIET";
+ argv[argc++] = "-dSAFER";
+
+ /* set up the pipes */
+ if (gs->send_filename_to_gs) {
+ argv[argc++] = PS_DOCUMENT_GET_PS_FILE (gs);
+ argv[argc++] = "-c";
+ argv[argc++] = "quit";
+ } else {
+ argv[argc++] = "-";
+ }
+
+ argv[argc++] = NULL;
+
+ if (!gs->reading_from_pipe && !gs->send_filename_to_gs) {
+ if (pipe (std_in) == -1) {
+ g_critical ("Unable to open pipe to Ghostscript.");
+ return -1;
+ }
+ }
+
+ if (pipe (std_out) == -1) {
+ close_pipe (std_in);
+ return -1;
+ }
+
+ if (pipe(std_err) == -1) {
+ close_pipe (std_in);
+ close_pipe (std_out);
+ return -1;
+ }
+
+ gv_env = g_strdup_printf ("GHOSTVIEW=%ld %ld;DISPLAY=%s",
+ gdk_x11_drawable_get_xid (gs->pstarget),
+ gdk_x11_drawable_get_xid (gs->bpixmap),
+ gdk_display_get_name (gdk_drawable_get_display (gs->pstarget)));
+
+ gs->interpreter_pid = fork ();
+ switch (gs->interpreter_pid) {
+ case -1: /* error */
+ close_pipe (std_in);
+ close_pipe (std_out);
+ close_pipe (std_err);
+ return -2;
+ break;
+ case 0: /* child */
+ close (std_out[0]);
+ dup2 (std_out[1], 1);
+ close (std_out[1]);
+
+ close (std_err[0]);
+ dup2 (std_err[1], 2);
+ close (std_err[1]);
+
+ if (!gs->reading_from_pipe) {
+ if (gs->send_filename_to_gs) {
+ int stdinfd;
+ /* just in case gs tries to read from stdin */
+ stdinfd = open("/dev/null", O_RDONLY);
+ if (stdinfd != 0) {
+ dup2(stdinfd, 0);
+ close(stdinfd);
+ }
+ } else {
+ close (std_in[1]);
+ dup2 (std_in[0], 0);
+ close (std_in[0]);
+ }
+ }
+
+ gv_env_vars = g_strsplit (gv_env, ";", -1);
+ g_free (gv_env);
+ for (i = 0; gv_env_vars[i]; i++) {
+ putenv (gv_env_vars[i]);
+ }
+
+ /* change to directory where the input file is. This helps
+ * with postscript-files which include other files using
+ * a relative path */
+ dir = g_path_get_dirname (gs->gs_filename);
+ chdir (dir);
+ g_free (dir);
+
+ execvp (argv[0], argv);
+
+ /* Notify error */
+ g_critical ("Unable to execute [%s]\n", argv[0]);
+ g_strfreev (gs_args);
+ g_strfreev (alpha_args);
+ g_strfreev (gv_env_vars);
+ _exit (1);
+ break;
+ default: /* parent */
+ if (!gs->send_filename_to_gs && !gs->reading_from_pipe) {
+ int result;
+ close (std_in[0]);
+ /* use non-blocking IO for pipe to ghostscript */
+ result = fcntl (std_in[1], F_GETFL, 0);
+ fcntl (std_in[1], F_SETFL, result | O_NONBLOCK);
+ gs->interpreter_input = std_in[1];
+ } else {
+ gs->interpreter_input = -1;
+ }
+ close (std_out[1]);
+
+ gs->interpreter_output = std_out[0];
+ close (std_err[1]);
+ gs->interpreter_err = std_err[0];
+ gs->interpreter_output_id =
+ gdk_input_add (std_out[0], GDK_INPUT_READ, output, gs);
+ gs->interpreter_error_id =
+ gdk_input_add (std_err[0], GDK_INPUT_READ, output, gs);
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+stop_interpreter(PSDocument * gs)
+{
+ if (gs->interpreter_pid > 0) {
+ int status = 0;
+ kill (gs->interpreter_pid, SIGTERM);
+ while ((wait(&status) == -1) && (errno == EINTR));
+ gs->interpreter_pid = -1;
+ if (status == 1) {
+ gs->gs_status = _("Interpreter failed.");
+ }
+ }
+
+ if (gs->interpreter_input >= 0) {
+ close (gs->interpreter_input);
+ gs->interpreter_input = -1;
+ if (gs->interpreter_input_id != 0) {
+ gdk_input_remove(gs->interpreter_input_id);
+ gs->interpreter_input_id = 0;
+ }
+ while (gs->ps_input) {
+ struct record_list *ps_old = gs->ps_input;
+ gs->ps_input = gs->ps_input->next;
+ if (ps_old->close && NULL != ps_old->fp)
+ fclose (ps_old->fp);
+ g_free (ps_old);
+ }
+ }
+
+ if (gs->interpreter_output >= 0) {
+ close (gs->interpreter_output);
+ gs->interpreter_output = -1;
+ if (gs->interpreter_output_id) {
+ gdk_input_remove (gs->interpreter_output_id);
+ gs->interpreter_output_id = 0;
+ }
+ }
+
+ if (gs->interpreter_err >= 0) {
+ close (gs->interpreter_err);
+ gs->interpreter_err = -1;
+ if (gs->interpreter_error_id) {
+ gdk_input_remove (gs->interpreter_error_id);
+ gs->interpreter_error_id = 0;
+ }
+ }
+
+ gs->busy = FALSE;
+}
+
+/* If file exists and is a regular file then return its length, else -1 */
+static gint
+file_length (const gchar * filename)
+{
+ struct stat stat_rec;
+
+ if (filename && (stat (filename, &stat_rec) == 0) && S_ISREG (stat_rec.st_mode))
+ return stat_rec.st_size;
+ else
+ return -1;
+}
+
+/* Test if file exists, is a regular file and its length is > 0 */
+static gboolean
+file_readable(const char *filename)
+{
+ return (file_length (filename) > 0);
+}
+
+/*
+ * Decompress gs->gs_filename if necessary
+ * Set gs->filename_unc to the name of the uncompressed file or NULL.
+ * Error reporting via signal 'interpreter_message'
+ * Return name of input file to use or NULL on error..
+ */
+static gchar *
+check_filecompressed (PSDocument * gs)
+{
+ FILE *file;
+ gchar buf[1024];
+ gchar *filename, *filename_unc, *filename_err, *cmdline;
+ const gchar *cmd;
+ int fd;
+
+ cmd = NULL;
+
+ if ((file = fopen(gs->gs_filename, "r")) &&
+ (fread (buf, sizeof(gchar), 3, file) == 3)) {
+ if ((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
+ /* file is gzipped or compressed */
+ cmd = gtk_gs_defaults_get_ungzip_cmd ();
+ } else if (strncmp (buf, "BZh", 3) == 0) {
+ /* file is compressed with bzip2 */
+ cmd = gtk_gs_defaults_get_unbzip2_cmd ();
+ }
+ }
+
+ if (NULL != file)
+ fclose(file);
+
+ if (!cmd)
+ return gs->gs_filename;
+
+ /* do the decompression */
+ filename = g_shell_quote (gs->gs_filename);
+ filename_unc = g_strconcat (g_get_tmp_dir (), "/evinceXXXXXX", NULL);
+ if ((fd = mkstemp (filename_unc)) < 0) {
+ g_free (filename_unc);
+ g_free (filename);
+ return NULL;
+ }
+ close (fd);
+
+ filename_err = g_strconcat (g_get_tmp_dir (), "/evinceXXXXXX", NULL);
+ if ((fd = mkstemp(filename_err)) < 0) {
+ g_free (filename_err);
+ g_free (filename_unc);
+ g_free (filename);
+ return NULL;
+ }
+ close (fd);
+
+ cmdline = g_strdup_printf ("%s %s >%s 2>%s", cmd,
+ filename, filename_unc, filename_err);
+ if (system (cmdline) == 0 &&
+ file_readable (filename_unc) &&
+ file_length (filename_err) == 0) {
+ /* sucessfully uncompressed file */
+ gs->gs_filename_unc = filename_unc;
+ } else {
+ gchar *filename_dsp;
+ gchar *msg;
+
+ /* report error */
+ filename_dsp = g_filename_display_name (gs->gs_filename);
+ msg = g_strdup_printf (_("Error while decompressing file “%s”:\n"), filename_dsp);
+ g_free (filename_dsp);
+
+ interpreter_failed (gs, msg);
+ g_free (msg);
+ unlink (filename_unc);
+ g_free (filename_unc);
+ filename_unc = NULL;
+ }
+
+ unlink (filename_err);
+ g_free (filename_err);
+ g_free (cmdline);
+ g_free (filename);
+
+ return filename_unc;
+}
+
+static gint
+ps_document_enable_interpreter(PSDocument *gs)
+{
+ g_return_val_if_fail (PS_IS_DOCUMENT (gs), FALSE);
+
+ if (!gs->gs_filename)
+ return 0;
+
+ return start_interpreter (gs);
+}
+
+static gboolean
+document_load (PSDocument *gs, const gchar *fname)
+{
+ g_return_val_if_fail (PS_IS_DOCUMENT(gs), FALSE);
+
+ if (fname == NULL) {
+ gs->gs_status = "";
+ return FALSE;
+ }
+
+ /* prepare this document */
+ gs->structured_doc = FALSE;
+ gs->send_filename_to_gs = TRUE;
+ gs->gs_filename = g_strdup (fname);
+
+ if ((gs->reading_from_pipe = (strcmp (fname, "-") == 0))) {
+ gs->send_filename_to_gs = FALSE;
+ } else {
+ /*
+ * We need to make sure that the file is loadable/exists!
+ * otherwise we want to exit without loading new stuff...
+ */
+ gchar *filename = NULL;
+
+ if (!file_readable(fname)) {
+ gchar *filename_dsp;
+ gchar *msg;
+
+ filename_dsp = g_filename_display_name (fname);
+ msg = g_strdup_printf (_("Cannot open file “%s”.\n"), filename_dsp);
+ g_free (filename_dsp);
+
+ interpreter_failed (gs, msg);
+ g_free (msg);
+ gs->gs_status = _("File is not readable.");
+ } else {
+ filename = check_filecompressed(gs);
+ }
+
+ if (!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
+ interpreter_failed (gs, NULL);
+ return FALSE;
+ }
+
+ /* we grab the vital statistics!!! */
+ gs->doc = psscan(gs->gs_psfile, TRUE, filename);
+
+ if ((!gs->doc->epsf && gs->doc->numpages > 0) ||
+ (gs->doc->epsf && gs->doc->numpages > 1)) {
+ gs->structured_doc = TRUE;
+ gs->send_filename_to_gs = FALSE;
+ }
+ }
+
+ gs->gs_status = _("Document loaded.");
+
+ return TRUE;
+}
+
+static gboolean
+ps_document_next_page (PSDocument *gs)
+{
+ XEvent event;
+ GdkScreen *screen;
+ GdkDisplay *display;
+ Display *dpy;
+
+ g_return_val_if_fail (PS_IS_DOCUMENT(gs), FALSE);
+ g_return_val_if_fail (gs->interpreter_pid != 0, FALSE);
+ g_return_val_if_fail (gs->busy != TRUE, FALSE);
+
+ gs->busy = TRUE;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (gs->target_window));
+ display = gdk_screen_get_display (screen);
+ dpy = gdk_x11_display_get_xdisplay (display);
+
+ event.xclient.type = ClientMessage;
+ event.xclient.display = dpy;
+ event.xclient.window = gs->message_window;
+ event.xclient.message_type =
+ gdk_x11_atom_to_xatom_for_display (display,
+ gs_class->next_atom);
+ event.xclient.format = 32;
+
+ gdk_error_trap_push ();
+ XSendEvent (dpy, gs->message_window, FALSE, 0, &event);
+ gdk_flush ();
+ gdk_error_trap_pop ();
+
+ return TRUE;
+}
+
+static gboolean
+render_page (PSDocument *gs, int page)
+{
+ g_return_val_if_fail(gs != NULL, FALSE);
+ g_return_val_if_fail(PS_IS_DOCUMENT(gs), FALSE);
+
+ if(!gs->gs_filename) {
+ return FALSE;
+ }
+
+ if (gs->structured_doc && gs->doc) {
+
+ if (is_interpreter_ready (gs)) {
+ ps_document_next_page (gs);
+ } else {
+ ps_document_enable_interpreter (gs);
+ send_ps (gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
+ send_ps (gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
+ }
+
+ send_ps (gs, gs->doc->pages[page].begin,
+ gs->doc->pages[page].len, FALSE);
+ } else {
+ /* Unstructured document
+ *
+ * In the case of non structured documents,
+ * GS read the PS from the actual file (via command
+ * line. Hence, ggv only send a signal next page.
+ * If ghostview is not running it is usually because
+ * the last page of the file was displayed. In that
+ * case, ggv restarts GS again and the first page is displayed.
+ */
+
+ if (!is_interpreter_ready (gs)) {
+ ps_document_enable_interpreter(gs);
+ }
+ ps_document_next_page(gs);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ps_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ char *filename;
+ char *gs_path;
+ gboolean result;
+
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ return FALSE;
+
+ gs_path = g_find_program_in_path ("gs");
+ if (!gs_path) {
+ gchar *filename_dsp;
+ filename_dsp = g_filename_display_name (filename);
+ g_set_error(error,
+ G_FILE_ERROR,
+ G_FILE_ERROR_NOENT,
+ _("Failed to load document “%s”. Ghostscript interpreter was not found in path"),
+ filename);
+ g_free (filename_dsp);
+ result = FALSE;
+ } else {
+ result = document_load (PS_DOCUMENT (document), filename);
+ if (!result) {
+ gchar *filename_dsp;
+ filename_dsp = g_filename_display_name (filename);
+
+ g_set_error (error, G_FILE_ERROR,
+ G_FILE_ERROR_FAILED,
+ _("Failed to load document “%s”"),
+ filename_dsp);
+ g_free (filename_dsp);
+ }
+ g_free (gs_path);
+ }
+ g_free (filename);
+
+ return result;
+}
+
+static gboolean
+save_document (PSDocument *document, const char *filename)
+{
+ gboolean result = TRUE;
+ GtkGSDocSink *sink = gtk_gs_doc_sink_new ();
+ FILE *f, *src_file;
+ gchar *buf;
+
+ src_file = fopen (PS_DOCUMENT_GET_PS_FILE(document), "r");
+ if (src_file) {
+ struct stat stat_rec;
+
+ if (stat (PS_DOCUMENT_GET_PS_FILE(document), &stat_rec) == 0) {
+ pscopy (src_file, sink, 0, stat_rec.st_size - 1);
+ }
+
+ fclose (src_file);
+ }
+
+ buf = gtk_gs_doc_sink_get_buffer (sink);
+ if (buf == NULL) {
+ return FALSE;
+ }
+
+ f = fopen (filename, "w");
+ if (f) {
+ fputs (buf, f);
+ fclose (f);
+ } else {
+ result = FALSE;
+ }
+
+ g_free (buf);
+ gtk_gs_doc_sink_free (sink);
+ g_free (sink);
+
+ return result;
+}
+
+static gboolean
+save_page_list (PSDocument *document, int *page_list, const char *filename)
+{
+ gboolean result = TRUE;
+ GtkGSDocSink *sink = gtk_gs_doc_sink_new ();
+ FILE *f;
+ gchar *buf;
+
+ pscopydoc (sink, PS_DOCUMENT_GET_PS_FILE(document),
+ document->doc, page_list);
+
+ buf = gtk_gs_doc_sink_get_buffer (sink);
+
+ f = fopen (filename, "w");
+ if (f) {
+ fputs (buf, f);
+ fclose (f);
+ } else {
+ result = FALSE;
+ }
+
+ g_free (buf);
+ gtk_gs_doc_sink_free (sink);
+ g_free (sink);
+
+ return result;
+}
+
+static gboolean
+ps_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ PSDocument *ps = PS_DOCUMENT (document);
+ gboolean result;
+ char *filename;
+
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ return FALSE;
+
+ result = save_document (ps, filename);
+
+ g_free (filename);
+
+ return result;
+}
+
+static int
+ps_document_get_n_pages (EvDocument *document)
+{
+ PSDocument *ps = PS_DOCUMENT (document);
+
+ g_return_val_if_fail (ps != NULL, -1);
+
+ if (!ps->gs_filename || !ps->doc) {
+ return -1;
+ }
+
+ return ps->structured_doc ? ps->doc->numpages : 1;
+}
+
+static void
+ps_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height)
+{
+ PSDocument *gs = PS_DOCUMENT (document);
+ int urx, ury, llx, lly;
+
+ get_page_box (gs, page, &urx, &ury, &llx, &lly);
+
+ if (width) {
+ *width = (urx - llx) + 0.5;
+ }
+
+ if (height) {
+ *height = (ury - lly) + 0.5;
+ }
+}
+
+static gboolean
+ps_document_can_get_text (EvDocument *document)
+{
+ return FALSE;
+}
+
+static void
+ps_async_renderer_render_pixbuf (EvAsyncRenderer *renderer, int page, double scale, int rotation)
+{
+ PSDocument *gs = PS_DOCUMENT (renderer);
+
+ if (gs->pstarget == NULL) {
+ gs->target_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_widget_realize (gs->target_window);
+ gs->pstarget = gs->target_window->window;
+
+ g_assert (gs->pstarget != NULL);
+
+ g_signal_connect (gs->target_window, "event",
+ G_CALLBACK (ps_document_widget_event),
+ gs);
+ }
+
+ setup_pixmap (gs, page, scale, rotation);
+ setup_page (gs, page, scale, rotation);
+
+ render_page (gs, page);
+}
+
+static EvDocumentInfo *
+ps_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+ PSDocument *ps = PS_DOCUMENT (document);
+ int urx, ury, llx, lly;
+
+ info = g_new0 (EvDocumentInfo, 1);
+ info->fields_mask = EV_DOCUMENT_INFO_TITLE |
+ EV_DOCUMENT_INFO_FORMAT |
+ EV_DOCUMENT_INFO_CREATOR |
+ EV_DOCUMENT_INFO_N_PAGES |
+ EV_DOCUMENT_INFO_PAPER_SIZE;
+
+ info->title = g_strdup (ps->doc->title);
+ info->format = ps->doc->epsf ? g_strdup (_("Encapsulated PostScript"))
+ : g_strdup (_("PostScript"));
+ info->creator = g_strdup (ps->doc->creator);
+ info->n_pages = ev_document_get_n_pages (document);
+
+ get_page_box (PS_DOCUMENT (document), 0, &urx, &ury, &llx, &lly);
+
+ info->paper_width = (urx - llx) / 72.0f * 25.4f;
+ info->paper_height = (ury - lly) / 72.0f * 25.4f;
+
+ return info;
+}
+
+static void
+ps_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->load = ps_document_load;
+ iface->save = ps_document_save;
+ iface->can_get_text = ps_document_can_get_text;
+ iface->get_n_pages = ps_document_get_n_pages;
+ iface->get_page_size = ps_document_get_page_size;
+ iface->get_info = ps_document_get_info;
+}
+
+static void
+ps_async_renderer_iface_init (EvAsyncRendererIface *iface)
+{
+ iface->render_pixbuf = ps_async_renderer_render_pixbuf;
+}
+
+static gboolean
+ps_document_file_exporter_format_supported (EvFileExporter *exporter,
+ EvFileExporterFormat format)
+{
+ return (format == EV_FILE_FORMAT_PS);
+}
+
+static void
+ps_document_file_exporter_begin (EvFileExporter *exporter,
+ EvFileExporterFormat format,
+ const char *filename,
+ int first_page,
+ int last_page,
+ double width,
+ double height,
+ gboolean duplex)
+{
+ PSDocument *document = PS_DOCUMENT (exporter);
+
+ if (document->structured_doc) {
+ g_free (document->ps_export_pagelist);
+
+ document->ps_export_pagelist = g_new0 (int, document->doc->numpages);
+ }
+
+ document->ps_export_filename = g_strdup (filename);
+}
+
+static void
+ps_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
+{
+ PSDocument *document = PS_DOCUMENT (exporter);
+
+ if (document->structured_doc) {
+ document->ps_export_pagelist[rc->page] = 1;
+ }
+}
+
+static void
+ps_document_file_exporter_end (EvFileExporter *exporter)
+{
+ PSDocument *document = PS_DOCUMENT (exporter);
+
+ if (!document->structured_doc) {
+ save_document (document, document->ps_export_filename);
+ } else {
+ save_page_list (document, document->ps_export_pagelist,
+ document->ps_export_filename);
+ g_free (document->ps_export_pagelist);
+ g_free (document->ps_export_filename);
+ document->ps_export_pagelist = NULL;
+ document->ps_export_filename = NULL;
+ }
+}
+
+static void
+ps_document_file_exporter_iface_init (EvFileExporterIface *iface)
+{
+ iface->format_supported = ps_document_file_exporter_format_supported;
+ iface->begin = ps_document_file_exporter_begin;
+ iface->do_page = ps_document_file_exporter_do_page;
+ iface->end = ps_document_file_exporter_end;
+}
diff --git a/backend/ps/ps-document.h b/backend/ps/ps-document.h
new file mode 100644
index 0000000..c8d19db
--- /dev/null
+++ b/backend/ps/ps-document.h
@@ -0,0 +1,95 @@
+/*
+ * Ghostscript widget for GTK/GNOME
+ *
+ * Copyright 1998 - 2005 The Free Software Foundation
+ *
+ * Authors: Jaka Mocnik, Federico Mena (Quartic), Szekeres Istvan (Pista)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PS_DOCUMENT_H__
+#define __PS_DOCUMENT_H__
+
+#include <sys/types.h>
+#include <gtk/gtkwidget.h>
+
+#include "ev-document.h"
+#include "ps.h"
+#include "gstypes.h"
+
+G_BEGIN_DECLS
+
+#define PS_TYPE_DOCUMENT (ps_document_get_type())
+#define PS_DOCUMENT(obj) GTK_CHECK_CAST (obj, ps_document_get_type (), PSDocument)
+#define PS_DOCUMENT_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, ps_document_get_type (), PSDocumentClass)
+#define PS_IS_DOCUMENT(obj) GTK_CHECK_TYPE (obj, ps_document_get_type())
+
+typedef struct _PSDocument PSDocument;
+typedef struct _PSDocumentClass PSDocumentClass;
+
+struct _PSDocument {
+ GObject object;
+
+ GtkWidget *target_window;
+ GdkWindow *pstarget;
+ GdkPixmap *bpixmap;
+ long message_window; /* Used by ghostview to receive messages from app */
+
+ pid_t interpreter_pid; /* PID of interpreter, -1 if none */
+ int interpreter_input; /* stdin of interpreter */
+ int interpreter_output; /* stdout of interpreter */
+ int interpreter_err; /* stderr of interpreter */
+ guint interpreter_input_id;
+ guint interpreter_output_id;
+ guint interpreter_error_id;
+
+ gboolean busy; /* Is gs busy drawing? */
+ gboolean structured_doc;
+
+ struct record_list *ps_input;
+ gchar *input_buffer_ptr;
+ guint bytes_left;
+ guint buffer_bytes_left;
+
+ FILE *gs_psfile; /* the currently loaded FILE */
+ gchar *gs_filename; /* the currently loaded filename */
+ gchar *gs_filename_unc; /* Uncompressed file */
+ gchar *input_buffer;
+ gboolean send_filename_to_gs; /* True if gs should read from file directly */
+ gboolean reading_from_pipe; /* True if ggv is reading input from pipe */
+ struct document *doc;
+
+ int *ps_export_pagelist;
+ char *ps_export_filename;
+
+ const gchar *gs_status; /* PSDocument status */
+};
+
+struct _PSDocumentClass {
+ GObjectClass parent_class;
+
+ GdkAtom gs_atom;
+ GdkAtom next_atom;
+ GdkAtom page_atom;
+ GdkAtom string_atom;
+};
+
+GType ps_document_get_type(void);
+
+G_END_DECLS
+
+#endif /* __PS_DOCUMENT_H__ */
diff --git a/backend/ps/ps.c b/backend/ps/ps.c
new file mode 100644
index 0000000..b4b54e0
--- /dev/null
+++ b/backend/ps/ps.c
@@ -0,0 +1,1872 @@
+/*
+ * ps.c -- Postscript scanning and copying routines.
+ * Copyright (C) 1992, 1998 Timothy O. Theisen
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Tim Theisen Systems Programmer
+ * Internet: tim@cs.wisc.edu Department of Computer Sciences
+ * UUCP: uwvax!tim University of Wisconsin-Madison
+ * Phone: (608)262-0438 1210 West Dayton Street
+ * FAX: (608)262-9777 Madison, WI 53706
+ */
+
+/* 18/3/98 Jake Hamby patch */
+
+/*
+ * 98/03/17: Jake Hamby (jehamby@lightside.com):
+ * Added support for compressed/gzipped Postscript and PDF files.
+ * Compressed files are gunzipped to a temporary file, and PDF files are
+ * scanned by calling Ghostscript to generate a fake DSC file.
+ * This is based on code from GV 3.5.8, which is available at:
+ * http://wwwthep.physik.uni-mainz.de/~plass/gv/
+ */
+
+/* GV by Johannes Plass
+ * Department of Physics
+ * Johannes Gutenberg University
+ * Mainz, Germany
+ *
+ * <plass@thep.physik.uni-mainz.de>
+ */
+
+/* end of patch */
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef BUFSIZ
+# define BUFSIZ 1024
+#endif
+#include <ctype.h>
+#include <X11/Xos.h> /* #includes the appropriate <string.h> */
+#include "gstypes.h"
+#include "gsdefaults.h"
+#include "ps.h"
+#include "gsio.h"
+
+#include <glib.h>
+
+/* length calculates string length at compile time */
+/* can only be used with character constants */
+#define length(a) (sizeof(a)-1)
+#define iscomment(a, b) (strncmp(a, b, length(b)) == 0)
+#define DSCcomment(a) (a[0] == '%' && a[1] == '%')
+
+ /* list of standard paper sizes from Adobe's PPD. */
+
+/*--------------------------------------------------*/
+/* Declarations for ps_io_*() routines. */
+
+typedef struct FileDataStruct_ *FileData;
+
+typedef struct FileDataStruct_ {
+ FILE *file; /* file */
+ int file_desc; /* file descriptor corresponding to file */
+ int filepos; /* file position corresponding to the start of the line */
+ char *buf; /* buffer */
+ int buf_size; /* size of buffer */
+ int buf_end; /* last char in buffer given as offset to buf */
+ int line_begin; /* start of the line given as offset to buf */
+ int line_end; /* end of the line given as offset to buf */
+ int line_len; /* length of line, i.e. (line_end-line_begin) */
+ char line_termchar; /* char exchanged for a '\0' at end of line */
+ int status; /* 0 = okay, 1 = failed */
+} FileDataStruct;
+
+static FileData ps_io_init (FILE *file);
+static void ps_io_exit (FileData data);
+static char *ps_io_fgetchars (FileData data, int offset);
+
+static char *skipped_line = "% ps_io_fgetchars: skipped line";
+static char *empty_string = "";
+
+static char *readline (FileData fd, char **lineP, long *positionP, unsigned int *line_lenP);
+static char *gettextline(char *line);
+static char *get_next_text(char *line, char **next_char);
+static int blank(char *line);
+
+static struct page *
+pages_new(struct page *pages, int current, int maxpages)
+{
+ struct page *oldpages = pages;
+ if(!oldpages)
+ pages = g_new0(struct page, maxpages);
+ else
+ pages = g_renew(struct page, oldpages, maxpages);
+ for(; current < maxpages; current++) {
+ memset(&(pages[current]), 0x00, sizeof(struct page));
+ pages[current].orientation = GTK_GS_ORIENTATION_NONE;
+ }
+ return pages;
+}
+
+/*
+ * psscan -- scan the PostScript file for document structuring comments.
+ *
+ * This scanner is designed to retrieve the information necessary for
+ * the ghostview previewer. It will scan files that conform to any
+ * version (1.0, 2.0, 2.1, or 3.0) of the document structuring conventions.
+ * It does not really care which version of comments the file contains.
+ * (The comments are largely upward compatible.) It will scan a number
+ * of non-conforming documents. (You could have part of the document
+ * conform to V2.0 and the rest conform to V3.0. It would be similar
+ * to the DC-2 1/2+, it would look funny but it can still fly.)
+ *
+ * This routine returns a pointer to the document structure.
+ * The structure contains the information relevant to previewing.
+ * These include EPSF flag (to tell if the file is a encapsulated figure),
+ * Page Size (for the Page Size), Bounding Box (to minimize backing
+ * pixmap size or determine window size for encapsulated PostScript),
+ * Orientation of Paper (for default transformation matrix), and
+ * Page Order. The Title, Creator, and CreationDate are also retrieved to
+ * help identify the document.
+ *
+ * The following comments are examined:
+ *
+ * Header section:
+ * Must start with %!PS-Adobe-. Version numbers ignored.
+ * Also allowed to be just %!PS, many files seem to have that.
+ *
+ * %!PS-Adobe-* [EPSF-*]
+ * %%BoundingBox: <int> <int> <int> <int>|(atend)
+ * %%Creator: <textline>
+ * %%CreationDate: <textline>
+ * %%Orientation: Portrait|Landscape|(atend)
+ * %%Pages: <uint> [<int>]|(atend)
+ * %%PageOrder: Ascend|Descend|Special|(atend)
+ * %%Title: <textline>
+ * %%DocumentMedia: <text> <real> <real> <real> <text> <text>
+ * %%DocumentPaperSizes: <text>
+ * %%EndComments
+ *
+ * Note: Either the 3.0 or 2.0 syntax for %%Pages is accepted.
+ * Also either the 2.0 %%DocumentPaperSizes or the 3.0
+ * %%DocumentMedia comments are accepted as well.
+ *
+ * The header section ends either explicitly with %%EndComments or
+ * implicitly with any line that does not begin with %X where X is
+ * a not whitespace character.
+ *
+ * If the file is encapsulated PostScript the optional Preview section
+ * is next:
+ *
+ * %%BeginPreview
+ * %%EndPreview
+ *
+ * This section explicitly begins and ends with the above comments.
+ *
+ * Next the Defaults section for version 3 page defaults:
+ *
+ * %%BeginDefaults
+ * %%PageBoundingBox: <int> <int> <int> <int>
+ * %%PageOrientation: Portrait|Landscape
+ * %%PageMedia: <text>
+ * %%EndDefaults
+ *
+ * This section explicitly begins and ends with the above comments.
+ *
+ * The prolog section either explicitly starts with %%BeginProlog or
+ * implicitly with any nonblank line.
+ *
+ * %%BeginProlog
+ * %%EndProlog
+ *
+ * The Prolog should end with %%EndProlog, however the proglog implicitly
+ * ends when %%BeginSetup, %%Page, %%Trailer or %%EOF are encountered.
+ *
+ * The Setup section is where the version 2 page defaults are found.
+ * This section either explicitly begins with %%BeginSetup or implicitly
+ * with any nonblank line after the Prolog.
+ *
+ * %%BeginSetup
+ * %%PageBoundingBox: <int> <int> <int> <int>
+ * %%PageOrientation: Portrait|Landscape
+ * %%PaperSize: <text>
+ * %%EndSetup
+ *
+ * The Setup should end with %%EndSetup, however the setup implicitly
+ * ends when %%Page, %%Trailer or %%EOF are encountered.
+ *
+ * Next each page starts explicitly with %%Page and ends implicitly with
+ * %%Page or %%Trailer or %%EOF. The following comments are recognized:
+ *
+ * %%Page: <text> <uint>
+ * %%PageBoundingBox: <int> <int> <int> <int>|(atend)
+ * %%PageOrientation: Portrait|Landscape
+ * %%PageMedia: <text>
+ * %%PaperSize: <text>
+ *
+ * The tralier section start explicitly with %%Trailer and end with %%EOF.
+ * The following comment are examined with the proper (atend) notation
+ * was used in the header:
+ *
+ * %%Trailer
+ * %%BoundingBox: <int> <int> <int> <int>|(atend)
+ * %%Orientation: Portrait|Landscape|(atend)
+ * %%Pages: <uint> [<int>]|(atend)
+ * %%PageOrder: Ascend|Descend|Special|(atend)
+ * %%EOF
+ *
+ *
+ * + A DC-3 received severe damage to one of its wings. The wing was a total
+ * loss. There was no replacement readily available, so the mechanic
+ * installed a wing from a DC-2.
+ */
+
+#include <glib.h>
+
+struct document *
+psscan(FILE * file, int respect_eof, const gchar * fname)
+{
+ struct document *doc;
+ int bb_set = NONE;
+ int pages_set = NONE;
+ int page_order_set = NONE;
+ int orientation_set = NONE;
+ int page_bb_set = NONE;
+ int page_size_set = NONE;
+ int preread; /* flag which tells the readline isn't needed */
+ int i;
+ unsigned int maxpages = 0;
+ unsigned int nextpage = 1; /* Next expected page */
+ unsigned int thispage;
+ int ignore = 0; /* whether to ignore page ordinals */
+ char *label;
+ char *line;
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ long position; /* Position of the current line */
+ long beginsection; /* Position of the beginning of the section */
+ unsigned int line_len; /* Length of the current line */
+ unsigned int section_len; /* Place to accumulate the section length */
+ char *next_char; /* 1st char after text returned by get_next_text() */
+ char *cp;
+ GtkGSPaperSize *dmp;
+ GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
+ FileData fd;
+
+ if(!file)
+ return NULL;
+
+ rewind(file);
+
+ fd = ps_io_init(file);
+ if (!readline(fd, &line, &position, &line_len)) {
+ fprintf(stderr, "Warning: empty file.\n");
+ ps_io_exit(fd);
+ return(NULL);
+ }
+
+ /* HP printer job language data follows. Some printer drivers add pjl
+ * commands to switch a pjl printer to postscript mode. If no PS header
+ * follows, this seems to be a real pjl file. */
+ if(iscomment(line, "\033%-12345X@PJL")) {
+ /* read until first DSC comment */
+ while(readline(fd, &line, &position, &line_len)
+ && (line[0] != '%')) ;
+ if(line[0] != '%') {
+ g_print("psscan error: input files seems to be a PJL file.\n");
+ return (NULL);
+ }
+ }
+
+ /* Header comments */
+
+ /* Header should start with "%!PS-Adobe-", but some programms omit
+ * parts of this or add a ^D at the beginning. */
+ if(iscomment(line, "%!PS") || iscomment(line, "\004%!PS")) {
+ doc = g_new0(struct document, 1);
+ doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
+ doc->orientation = GTK_GS_ORIENTATION_NONE;
+
+ /* ignore possible leading ^D */
+ if(*line == '\004') {
+ position++;
+ line_len--;
+ }
+
+/* Jake Hamby patch 18/3/98 */
+
+ text[0] = '\0';
+ sscanf(line, "%*s %256s", text);
+ /*doc->epsf = iscomment(text, "EPSF-"); */
+ doc->epsf = iscomment(text, "EPSF"); /* Hamby - This line changed */
+ doc->beginheader = position;
+ section_len = line_len;
+ }
+ else {
+ /* There are postscript documents that do not have
+ %PS at the beginning, usually unstructured. We should GS decide
+ For instance, the tech reports at this university:
+
+ http://svrc.it.uq.edu.au/Bibliography/svrc-tr.html?94-45
+
+ add ugly PostScript before the actual document.
+
+ GS and gv is
+ able to display them correctly as unstructured PS.
+
+ In a way, this makes sense, a program PostScript does not need
+ the !PS at the beginning.
+ */
+ doc = g_new0(struct document, 1);
+ doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
+ doc->orientation = GTK_GS_ORIENTATION_NONE;
+ return (doc);
+ }
+
+ preread = 0;
+ while(preread || readline(fd, &line, &position, &line_len)) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ if(line[0] != '%' ||
+ iscomment(line + 1, "%EndComments") ||
+ line[1] == ' ' || line[1] == '\t' || line[1] == '\n' ||
+ !isprint(line[1])) {
+ break;
+ }
+ else if(line[1] != '%') {
+ /* Do nothing */
+ }
+ else if(doc->title == NULL && iscomment(line + 2, "Title:")) {
+ doc->title = gettextline(line + length("%%Title:"));
+ }
+ else if(doc->date == NULL && iscomment(line + 2, "CreationDate:")) {
+ doc->date = gettextline(line + length("%%CreationDate:"));
+ }
+ else if(doc->creator == NULL && iscomment(line + 2, "Creator:")) {
+ doc->creator = gettextline(line + length("%%Creator:"));
+ }
+ else if(bb_set == NONE && iscomment(line + 2, "BoundingBox:")) {
+ sscanf(line + length("%%BoundingBox:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ bb_set = ATEND;
+ }
+ else {
+ if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
+ &(doc->boundingbox[LLX]),
+ &(doc->boundingbox[LLY]),
+ &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) == 4)
+ bb_set = 1;
+ else {
+ float fllx, flly, furx, fury;
+ if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
+ &fllx, &flly, &furx, &fury) == 4) {
+ bb_set = 1;
+ doc->boundingbox[LLX] = fllx;
+ doc->boundingbox[LLY] = flly;
+ doc->boundingbox[URX] = furx;
+ doc->boundingbox[URY] = fury;
+ if(fllx < doc->boundingbox[LLX])
+ doc->boundingbox[LLX]--;
+ if(flly < doc->boundingbox[LLY])
+ doc->boundingbox[LLY]--;
+ if(furx > doc->boundingbox[URX])
+ doc->boundingbox[URX]++;
+ if(fury > doc->boundingbox[URY])
+ doc->boundingbox[URY]++;
+ }
+ }
+ }
+ }
+ else if(orientation_set == NONE && iscomment(line + 2, "Orientation:")) {
+ sscanf(line + length("%%Orientation:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ orientation_set = ATEND;
+ }
+ else if(strcmp(text, "Portrait") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ orientation_set = 1;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ orientation_set = 1;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ orientation_set = 1;
+ }
+ }
+ else if(page_order_set == NONE && iscomment(line + 2, "PageOrder:")) {
+ sscanf(line + length("%%PageOrder:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ page_order_set = ATEND;
+ }
+ else if(strcmp(text, "Ascend") == 0) {
+ doc->pageorder = ASCEND;
+ page_order_set = 1;
+ }
+ else if(strcmp(text, "Descend") == 0) {
+ doc->pageorder = DESCEND;
+ page_order_set = 1;
+ }
+ else if(strcmp(text, "Special") == 0) {
+ doc->pageorder = SPECIAL;
+ page_order_set = 1;
+ }
+ }
+ else if(pages_set == NONE && iscomment(line + 2, "Pages:")) {
+ sscanf(line + length("%%Pages:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ pages_set = ATEND;
+ }
+ else {
+ switch (sscanf(line + length("%%Pages:"), "%d %d", &maxpages, &i)) {
+ case 2:
+ if(page_order_set == NONE) {
+ if(i == -1) {
+ doc->pageorder = DESCEND;
+ page_order_set = 1;
+ }
+ else if(i == 0) {
+ doc->pageorder = SPECIAL;
+ page_order_set = 1;
+ }
+ else if(i == 1) {
+ doc->pageorder = ASCEND;
+ page_order_set = 1;
+ }
+ }
+ case 1:
+ if(maxpages > 0)
+ doc->pages = pages_new(NULL, 0, maxpages);
+ }
+ }
+ }
+ else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentMedia:")) {
+ float w, h;
+ doc->size = g_new0(GtkGSPaperSize, 1);
+ doc->size[0].name =
+ get_next_text(line + length("%%DocumentMedia:"), &next_char);
+ if(doc->size[0].name != NULL) {
+ if(sscanf(next_char, "%f %f", &w, &h) == 2) {
+ doc->size[0].width = w + 0.5;
+ doc->size[0].height = h + 0.5;
+ }
+ if(doc->size[0].width != 0 && doc->size[0].height != 0)
+ doc->numsizes = 1;
+ else
+ g_free(doc->size[0].name);
+ }
+ preread = 1;
+ while(readline(fd, &line, &position, &line_len) &&
+ DSCcomment(line) && iscomment(line + 2, "+")) {
+ section_len += line_len;
+ doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
+ doc->size[doc->numsizes].name =
+ get_next_text(line + length("%%+"), &next_char);
+ if(doc->size[doc->numsizes].name != NULL) {
+ if(sscanf(next_char, "%f %f", &w, &h) == 2) {
+ doc->size[doc->numsizes].width = w + 0.5;
+ doc->size[doc->numsizes].height = h + 0.5;
+ }
+ if(doc->size[doc->numsizes].width != 0 &&
+ doc->size[doc->numsizes].height != 0)
+ doc->numsizes++;
+ else
+ g_free(doc->size[doc->numsizes].name);
+ }
+ }
+ section_len += line_len;
+ if(doc->numsizes != 0)
+ doc->default_page_size = doc->size;
+ }
+ else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentPaperSizes:")) {
+
+ doc->size = g_new0(GtkGSPaperSize, 1);
+ doc->size[0].name =
+ get_next_text(line + length("%%DocumentPaperSizes:"), &next_char);
+ if(doc->size[0].name != NULL) {
+ doc->size[0].width = 0;
+ doc->size[0].height = 0;
+ for(dmp = papersizes; dmp->name != NULL; dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(doc->size[0].name, dmp->name) == 0) {
+ g_free(doc->size[0].name);
+ doc->size[0].name = g_strdup(dmp->name);
+ doc->size[0].width = dmp->width;
+ doc->size[0].height = dmp->height;
+ break;
+ }
+ }
+ if(doc->size[0].width != 0 && doc->size[0].height != 0)
+ doc->numsizes = 1;
+ else
+ g_free(doc->size[0].name);
+ }
+ while((cp = get_next_text(next_char, &next_char))) {
+ doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
+ doc->size[doc->numsizes].name = cp;
+ doc->size[doc->numsizes].width = 0;
+ doc->size[doc->numsizes].height = 0;
+ for(dmp = papersizes; dmp->name != NULL; dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
+ g_free(doc->size[doc->numsizes].name);
+ doc->size[doc->numsizes].name = g_strdup(dmp->name);
+ doc->size[doc->numsizes].name = dmp->name;
+ doc->size[doc->numsizes].width = dmp->width;
+ doc->size[doc->numsizes].height = dmp->height;
+ break;
+ }
+ }
+ if(doc->size[doc->numsizes].width != 0 &&
+ doc->size[doc->numsizes].height != 0)
+ doc->numsizes++;
+ else
+ g_free(doc->size[doc->numsizes].name);
+ }
+ preread = 1;
+ while(readline(fd, &line, &position, &line_len) &&
+ DSCcomment(line) && iscomment(line + 2, "+")) {
+ section_len += line_len;
+ next_char = line + length("%%+");
+ while((cp = get_next_text(next_char, &next_char))) {
+ doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
+ doc->size[doc->numsizes].name = cp;
+ doc->size[doc->numsizes].width = 0;
+ doc->size[doc->numsizes].height = 0;
+ for(dmp = papersizes; dmp->name != NULL; dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
+ doc->size[doc->numsizes].width = dmp->width;
+ doc->size[doc->numsizes].height = dmp->height;
+ break;
+ }
+ }
+ if(doc->size[doc->numsizes].width != 0 &&
+ doc->size[doc->numsizes].height != 0)
+ doc->numsizes++;
+ else
+ g_free(doc->size[doc->numsizes].name);
+ }
+ }
+ section_len += line_len;
+ if(doc->numsizes != 0)
+ doc->default_page_size = doc->size;
+ }
+ }
+
+ if(DSCcomment(line) && iscomment(line + 2, "EndComments")) {
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ }
+ doc->endheader = position;
+ doc->lenheader = section_len - line_len;
+
+ /* Optional Preview comments for encapsulated PostScript files */
+
+ beginsection = position;
+ section_len = line_len;
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+ if(doc->epsf && DSCcomment(line) && iscomment(line + 2, "BeginPreview")) {
+ doc->beginpreview = beginsection;
+ beginsection = 0;
+ while(readline(fd, &line, &position, &line_len) &&
+ !(DSCcomment(line) && iscomment(line + 2, "EndPreview"))) {
+ section_len += line_len;
+ }
+ section_len += line_len;
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ doc->endpreview = position;
+ doc->lenpreview = section_len - line_len;
+ }
+
+ /* Page Defaults for Version 3.0 files */
+
+ if(beginsection == 0) {
+ beginsection = position;
+ section_len = line_len;
+ }
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+ if(DSCcomment(line) && iscomment(line + 2, "BeginDefaults")) {
+ doc->begindefaults = beginsection;
+ beginsection = 0;
+ while(readline(fd, &line, &position, &line_len) &&
+ !(DSCcomment(line) && iscomment(line + 2, "EndDefaults"))) {
+ section_len += line_len;
+ if(!DSCcomment(line)) {
+ /* Do nothing */
+ }
+ else if(doc->default_page_orientation == GTK_GS_ORIENTATION_NONE &&
+ iscomment(line + 2, "PageOrientation:")) {
+ sscanf(line + length("%%PageOrientation:"), "%256s", text);
+ if(strcmp(text, "Portrait") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ }
+ }
+ else if(page_size_set == NONE && iscomment(line + 2, "PageMedia:")) {
+ cp = get_next_text(line + length("%%PageMedia:"), NULL);
+ for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
+ if(strcmp(cp, dmp->name) == 0) {
+ doc->default_page_size = dmp;
+ page_size_set = 1;
+ break;
+ }
+ }
+ g_free(cp);
+ }
+ else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
+ if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
+ &(doc->default_page_boundingbox[LLX]),
+ &(doc->default_page_boundingbox[LLY]),
+ &(doc->default_page_boundingbox[URX]),
+ &(doc->default_page_boundingbox[URY])) == 4)
+ page_bb_set = 1;
+ else {
+ float fllx, flly, furx, fury;
+ if(sscanf
+ (line + length("%%PageBoundingBox:"), "%f %f %f %f",
+ &fllx, &flly, &furx, &fury) == 4) {
+ page_bb_set = 1;
+ doc->default_page_boundingbox[LLX] = fllx;
+ doc->default_page_boundingbox[LLY] = flly;
+ doc->default_page_boundingbox[URX] = furx;
+ doc->default_page_boundingbox[URY] = fury;
+ if(fllx < doc->default_page_boundingbox[LLX])
+ doc->default_page_boundingbox[LLX]--;
+ if(flly < doc->default_page_boundingbox[LLY])
+ doc->default_page_boundingbox[LLY]--;
+ if(furx > doc->default_page_boundingbox[URX])
+ doc->default_page_boundingbox[URX]++;
+ if(fury > doc->default_page_boundingbox[URY])
+ doc->default_page_boundingbox[URY]++;
+ }
+ }
+ }
+ }
+ section_len += line_len;
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ doc->enddefaults = position;
+ doc->lendefaults = section_len - line_len;
+ }
+
+ /* Document Prolog */
+
+ if(beginsection == 0) {
+ beginsection = position;
+ section_len = line_len;
+ }
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+ if(!(DSCcomment(line) &&
+ (iscomment(line + 2, "BeginSetup") ||
+ iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
+ doc->beginprolog = beginsection;
+ beginsection = 0;
+ preread = 1;
+
+ while((preread ||
+ readline(fd, &line, &position, &line_len)) &&
+ !(DSCcomment(line) &&
+ (iscomment(line + 2, "EndProlog") ||
+ iscomment(line + 2, "BeginSetup") ||
+ iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ }
+ section_len += line_len;
+ if(DSCcomment(line) && iscomment(line + 2, "EndProlog")) {
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ }
+ doc->endprolog = position;
+ doc->lenprolog = section_len - line_len;
+ }
+
+ /* Document Setup, Page Defaults found here for Version 2 files */
+
+ if(beginsection == 0) {
+ beginsection = position;
+ section_len = line_len;
+ }
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+ if(!(DSCcomment(line) &&
+ (iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") ||
+ (respect_eof && iscomment(line + 2, "EOF"))))) {
+ doc->beginsetup = beginsection;
+ beginsection = 0;
+ preread = 1;
+ while((preread ||
+ readline(fd, &line, &position, &line_len)) &&
+ !(DSCcomment(line) &&
+ (iscomment(line + 2, "EndSetup") ||
+ iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") ||
+ (respect_eof && iscomment(line + 2, "EOF"))))) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ if(!DSCcomment(line)) {
+ /* Do nothing */
+ }
+ else if(doc->default_page_orientation == GTK_GS_ORIENTATION_NONE &&
+ iscomment(line + 2, "PageOrientation:")) {
+ sscanf(line + length("%%PageOrientation:"), "%256s", text);
+ if(strcmp(text, "Portrait") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ }
+ }
+ else if(page_size_set == NONE && iscomment(line + 2, "PaperSize:")) {
+ cp = get_next_text(line + length("%%PaperSize:"), NULL);
+ for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(cp, dmp->name) == 0) {
+ doc->default_page_size = dmp;
+ page_size_set = 1;
+ break;
+ }
+ }
+ g_free(cp);
+ }
+ else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
+ if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
+ &(doc->default_page_boundingbox[LLX]),
+ &(doc->default_page_boundingbox[LLY]),
+ &(doc->default_page_boundingbox[URX]),
+ &(doc->default_page_boundingbox[URY])) == 4)
+ page_bb_set = 1;
+ else {
+ float fllx, flly, furx, fury;
+ if(sscanf
+ (line + length("%%PageBoundingBox:"), "%f %f %f %f",
+ &fllx, &flly, &furx, &fury) == 4) {
+ page_bb_set = 1;
+ doc->default_page_boundingbox[LLX] = fllx;
+ doc->default_page_boundingbox[LLY] = flly;
+ doc->default_page_boundingbox[URX] = furx;
+ doc->default_page_boundingbox[URY] = fury;
+ if(fllx < doc->default_page_boundingbox[LLX])
+ doc->default_page_boundingbox[LLX]--;
+ if(flly < doc->default_page_boundingbox[LLY])
+ doc->default_page_boundingbox[LLY]--;
+ if(furx > doc->default_page_boundingbox[URX])
+ doc->default_page_boundingbox[URX]++;
+ if(fury > doc->default_page_boundingbox[URY])
+ doc->default_page_boundingbox[URY]++;
+ }
+ }
+ }
+ }
+ section_len += line_len;
+ if(DSCcomment(line) && iscomment(line + 2, "EndSetup")) {
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ }
+ doc->endsetup = position;
+ doc->lensetup = section_len - line_len;
+ }
+
+ /* HACK: Mozilla 1.8 Workaround.
+
+ It seems that Mozilla 1.8 generates important postscript code
+ after the '%%EndProlog' and before the first page comment '%%Page: x y'.
+ See comment below also.
+ */
+
+ if(doc->beginprolog && !doc->beginsetup) {
+ doc->lenprolog += section_len - line_len;
+ doc->endprolog = position;
+ }
+
+ /* HACK: Windows NT Workaround
+
+ Mark Pfeifer (pfeiferm%ppddev@comet.cmis.abbott.com) noticed
+ about problems when viewing Windows NT 3.51 generated postscript
+ files with gv. He found that the relevant postscript files
+ show important postscript code after the '%%EndSetup' and before
+ the first page comment '%%Page: x y'.
+ */
+ if(doc->beginsetup) {
+ while(!(DSCcomment(line) &&
+ (iscomment(line + 2, "EndSetup") ||
+ (iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") ||
+ (respect_eof && iscomment(line + 2, "EOF"))))) &&
+ (readline(fd, &line, &position, &line_len))) {
+ section_len += line_len;
+ doc->lensetup = section_len - line_len;
+ doc->endsetup = position;
+ }
+ }
+
+ /* Individual Pages */
+
+ if(beginsection == 0) {
+ beginsection = position;
+ section_len = line_len;
+ }
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+
+newpage:
+ while(DSCcomment(line) && iscomment(line + 2, "Page:")) {
+ if(maxpages == 0) {
+ maxpages = 1;
+ doc->pages = pages_new(NULL, 0, maxpages);
+ }
+ label = get_next_text(line + length("%%Page:"), &next_char);
+ if(sscanf(next_char, "%d", &thispage) != 1)
+ thispage = 0;
+ if(nextpage == 1) {
+ ignore = thispage != 1;
+ }
+ if(!ignore && thispage != nextpage) {
+ g_free(label);
+ doc->numpages--;
+ goto continuepage;
+ }
+ nextpage++;
+ if(doc->numpages == maxpages) {
+ maxpages++;
+ doc->pages = pages_new(doc->pages, maxpages - 1, maxpages);
+ }
+ page_bb_set = NONE;
+ doc->pages[doc->numpages].label = label;
+ if(beginsection) {
+ doc->pages[doc->numpages].begin = beginsection;
+ beginsection = 0;
+ }
+ else {
+ doc->pages[doc->numpages].begin = position;
+ section_len = line_len;
+ }
+ continuepage:
+ while(readline(fd, &line, &position, &line_len) &&
+ !(DSCcomment(line) &&
+ (iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") ||
+ (respect_eof && iscomment(line + 2, "EOF"))))) {
+ section_len += line_len;
+ if(!DSCcomment(line)) {
+ /* Do nothing */
+ }
+ else if(doc->pages[doc->numpages].orientation == NONE &&
+ iscomment(line + 2, "PageOrientation:")) {
+ sscanf(line + length("%%PageOrientation:"), "%256s", text);
+ if(strcmp(text, "Portrait") == 0) {
+ doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ }
+ }
+ else if(doc->pages[doc->numpages].size == NULL &&
+ iscomment(line + 2, "PageMedia:")) {
+ cp = get_next_text(line + length("%%PageMedia:"), NULL);
+ for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
+ if(strcmp(cp, dmp->name) == 0) {
+ doc->pages[doc->numpages].size = dmp;
+ break;
+ }
+ }
+ g_free(cp);
+ }
+ else if(doc->pages[doc->numpages].size == NULL &&
+ iscomment(line + 2, "PaperSize:")) {
+ cp = get_next_text(line + length("%%PaperSize:"), NULL);
+ for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(cp, dmp->name) == 0) {
+ doc->pages[doc->numpages].size = dmp;
+ break;
+ }
+ }
+ g_free(cp);
+ }
+ else if((page_bb_set == NONE || page_bb_set == ATEND) &&
+ iscomment(line + 2, "PageBoundingBox:")) {
+ sscanf(line + length("%%PageBoundingBox:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ page_bb_set = ATEND;
+ }
+ else {
+ if(sscanf
+ (line + length("%%PageBoundingBox:"), "%d %d %d %d",
+ &(doc->pages[doc->numpages].boundingbox[LLX]),
+ &(doc->pages[doc->numpages].boundingbox[LLY]),
+ &(doc->pages[doc->numpages].boundingbox[URX]),
+ &(doc->pages[doc->numpages].boundingbox[URY])) == 4) {
+ if(page_bb_set == NONE)
+ page_bb_set = 1;
+ }
+ else {
+ float fllx, flly, furx, fury;
+ if(sscanf(line + length("%%PageBoundingBox:"),
+ "%f %f %f %f", &fllx, &flly, &furx, &fury) == 4) {
+ if(page_bb_set == NONE)
+ page_bb_set = 1;
+ doc->pages[doc->numpages].boundingbox[LLX] = fllx;
+ doc->pages[doc->numpages].boundingbox[LLY] = flly;
+ doc->pages[doc->numpages].boundingbox[URX] = furx;
+ doc->pages[doc->numpages].boundingbox[URY] = fury;
+ if(fllx < doc->pages[doc->numpages].boundingbox[LLX])
+ doc->pages[doc->numpages].boundingbox[LLX]--;
+ if(flly < doc->pages[doc->numpages].boundingbox[LLY])
+ doc->pages[doc->numpages].boundingbox[LLY]--;
+ if(furx > doc->pages[doc->numpages].boundingbox[URX])
+ doc->pages[doc->numpages].boundingbox[URX]++;
+ if(fury > doc->pages[doc->numpages].boundingbox[URY])
+ doc->pages[doc->numpages].boundingbox[URY]++;
+ }
+ }
+ }
+ }
+ }
+ section_len += line_len;
+ doc->pages[doc->numpages].end = position;
+ doc->pages[doc->numpages].len = section_len - line_len;
+ doc->numpages++;
+ }
+
+ /* Document Trailer */
+
+ if(beginsection) {
+ doc->begintrailer = beginsection;
+ beginsection = 0;
+ }
+ else {
+ doc->begintrailer = position;
+ section_len = line_len;
+ }
+
+ preread = 1;
+ while((preread ||
+ readline(fd, &line, &position, &line_len)) &&
+ !(respect_eof && DSCcomment(line) && iscomment(line + 2, "EOF"))) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ if(!DSCcomment(line)) {
+ /* Do nothing */
+ }
+ else if(iscomment(line + 2, "Page:")) {
+ g_free(get_next_text(line + length("%%Page:"), &next_char));
+ if(sscanf(next_char, "%d", &thispage) != 1)
+ thispage = 0;
+ if(!ignore && thispage == nextpage) {
+ if(doc->numpages > 0) {
+ doc->pages[doc->numpages - 1].end = position;
+ doc->pages[doc->numpages - 1].len += section_len - line_len;
+ }
+ else {
+ if(doc->endsetup) {
+ doc->endsetup = position;
+ doc->endsetup += section_len - line_len;
+ }
+ else if(doc->endprolog) {
+ doc->endprolog = position;
+ doc->endprolog += section_len - line_len;
+ }
+ }
+ goto newpage;
+ }
+ }
+ else if(!respect_eof && iscomment(line + 2, "Trailer")) {
+ /* What we thought was the start of the trailer was really */
+ /* the trailer of an EPS on the page. */
+ /* Set the end of the page to this trailer and keep scanning. */
+ if(doc->numpages > 0) {
+ doc->pages[doc->numpages - 1].end = position;
+ doc->pages[doc->numpages - 1].len += section_len - line_len;
+ }
+ doc->begintrailer = position;
+ section_len = line_len;
+ }
+ else if(bb_set == ATEND && iscomment(line + 2, "BoundingBox:")) {
+ if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
+ &(doc->boundingbox[LLX]),
+ &(doc->boundingbox[LLY]),
+ &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) != 4) {
+ float fllx, flly, furx, fury;
+ if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
+ &fllx, &flly, &furx, &fury) == 4) {
+ doc->boundingbox[LLX] = fllx;
+ doc->boundingbox[LLY] = flly;
+ doc->boundingbox[URX] = furx;
+ doc->boundingbox[URY] = fury;
+ if(fllx < doc->boundingbox[LLX])
+ doc->boundingbox[LLX]--;
+ if(flly < doc->boundingbox[LLY])
+ doc->boundingbox[LLY]--;
+ if(furx > doc->boundingbox[URX])
+ doc->boundingbox[URX]++;
+ if(fury > doc->boundingbox[URY])
+ doc->boundingbox[URY]++;
+ }
+ }
+ }
+ else if(orientation_set == ATEND && iscomment(line + 2, "Orientation:")) {
+ sscanf(line + length("%%Orientation:"), "%256s", text);
+ if(strcmp(text, "Portrait") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ }
+ }
+ else if(page_order_set == ATEND && iscomment(line + 2, "PageOrder:")) {
+ sscanf(line + length("%%PageOrder:"), "%256s", text);
+ if(strcmp(text, "Ascend") == 0) {
+ doc->pageorder = ASCEND;
+ }
+ else if(strcmp(text, "Descend") == 0) {
+ doc->pageorder = DESCEND;
+ }
+ else if(strcmp(text, "Special") == 0) {
+ doc->pageorder = SPECIAL;
+ }
+ }
+ else if(pages_set == ATEND && iscomment(line + 2, "Pages:")) {
+ if(sscanf(line + length("%%Pages:"), "%*u %d", &i) == 1) {
+ if(page_order_set == NONE) {
+ if(i == -1)
+ doc->pageorder = DESCEND;
+ else if(i == 0)
+ doc->pageorder = SPECIAL;
+ else if(i == 1)
+ doc->pageorder = ASCEND;
+ }
+ }
+ }
+ }
+ section_len += line_len;
+ if(DSCcomment(line) && iscomment(line + 2, "EOF")) {
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ }
+ doc->endtrailer = position;
+ doc->lentrailer = section_len - line_len;
+
+#if 0
+ section_len = line_len;
+ preread = 1;
+ while(preread || readline(line, sizeof line, file, &position, &line_len)) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ if(DSCcomment(line) && iscomment(line + 2, "Page:")) {
+ g_free(get_next_text(line + length("%%Page:"), &next_char));
+ if(sscanf(next_char, "%d", &thispage) != 1)
+ thispage = 0;
+ if(!ignore && thispage == nextpage) {
+ if(doc->numpages > 0) {
+ doc->pages[doc->numpages - 1].end = position;
+ doc->pages[doc->numpages - 1].len += doc->lentrailer +
+ section_len - line_len;
+ }
+ else {
+ if(doc->endsetup) {
+ doc->endsetup = position;
+ doc->endsetup += doc->lentrailer + section_len - line_len;
+ }
+ else if(doc->endprolog) {
+ doc->endprolog = position;
+ doc->endprolog += doc->lentrailer + section_len - line_len;
+ }
+ }
+ goto newpage;
+ }
+ }
+ }
+#endif
+ return doc;
+}
+
+/*
+ * psfree -- free dynamic storage associated with document structure.
+ */
+
+void
+psfree(doc)
+ struct document *doc;
+{
+ int i;
+
+ if(doc) {
+ /*
+ printf("This document exists\n");
+ */
+ for(i = 0; i < doc->numpages; i++) {
+ if(doc->pages[i].label)
+ g_free(doc->pages[i].label);
+ }
+ for(i = 0; i < doc->numsizes; i++) {
+ if(doc->size[i].name)
+ g_free(doc->size[i].name);
+ }
+ if(doc->title)
+ g_free(doc->title);
+ if(doc->date)
+ g_free(doc->date);
+ if(doc->creator)
+ g_free(doc->creator);
+ if(doc->pages)
+ g_free(doc->pages);
+ if(doc->size)
+ g_free(doc->size);
+ g_free(doc);
+ }
+}
+
+/*
+ * gettextine -- skip over white space and return the rest of the line.
+ * If the text begins with '(' return the text string
+ * using get_next_text().
+ */
+
+static char *
+gettextline(char *line)
+{
+ char *cp;
+
+ while(*line && (*line == ' ' || *line == '\t'))
+ line++;
+ if(*line == '(') {
+ return get_next_text(line, NULL);
+ }
+ else {
+ if(strlen(line) == 0)
+ return NULL;
+
+ cp = g_strdup(line);
+
+ /* Remove end of line */
+ if(cp[strlen(line) - 2] == '\r' && cp[strlen(line) - 1] == '\n')
+ /* Handle DOS \r\n */
+ cp[strlen(line) - 2] = '\0';
+ else if(cp[strlen(line) - 1] == '\n' || cp[strlen(line) - 1] == '\r')
+ /* Handle mac and unix */
+ cp[strlen(line) - 1] = '\0';
+
+ return cp;
+ }
+}
+
+/*
+ * get_next_text -- return the next text string on the line.
+ * return NULL if nothing is present.
+ */
+
+static char *
+get_next_text(line, next_char)
+ char *line;
+ char **next_char;
+{
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ char *cp;
+ int quoted = 0;
+
+ while(*line && (*line == ' ' || *line == '\t'))
+ line++;
+ cp = text;
+ if(*line == '(') {
+ int level = 0;
+ quoted = 1;
+ line++;
+ while(*line && !(*line == ')' && level == 0)
+ && (cp - text) < PSLINELENGTH - 1) {
+ if(*line == '\\') {
+ if(*(line + 1) == 'n') {
+ *cp++ = '\n';
+ line += 2;
+ }
+ else if(*(line + 1) == 'r') {
+ *cp++ = '\r';
+ line += 2;
+ }
+ else if(*(line + 1) == 't') {
+ *cp++ = '\t';
+ line += 2;
+ }
+ else if(*(line + 1) == 'b') {
+ *cp++ = '\b';
+ line += 2;
+ }
+ else if(*(line + 1) == 'f') {
+ *cp++ = '\f';
+ line += 2;
+ }
+ else if(*(line + 1) == '\\') {
+ *cp++ = '\\';
+ line += 2;
+ }
+ else if(*(line + 1) == '(') {
+ *cp++ = '(';
+ line += 2;
+ }
+ else if(*(line + 1) == ')') {
+ *cp++ = ')';
+ line += 2;
+ }
+ else if(*(line + 1) >= '0' && *(line + 1) <= '9') {
+ if(*(line + 2) >= '0' && *(line + 2) <= '9') {
+ if(*(line + 3) >= '0' && *(line + 3) <= '9') {
+ *cp++ =
+ ((*(line + 1) - '0') * 8 + *(line + 2) -
+ '0') * 8 + *(line + 3) - '0';
+ line += 4;
+ }
+ else {
+ *cp++ = (*(line + 1) - '0') * 8 + *(line + 2) - '0';
+ line += 3;
+ }
+ }
+ else {
+ *cp++ = *(line + 1) - '0';
+ line += 2;
+ }
+ }
+ else {
+ line++;
+ *cp++ = *line++;
+ }
+ }
+ else if(*line == '(') {
+ level++;
+ *cp++ = *line++;
+ }
+ else if(*line == ')') {
+ level--;
+ *cp++ = *line++;
+ }
+ else {
+ *cp++ = *line++;
+ }
+ }
+ }
+ else {
+ while(*line && !(*line == ' ' || *line == '\t' || *line == '\n')
+ && (cp - text) < PSLINELENGTH - 1)
+ *cp++ = *line++;
+ }
+ *cp = '\0';
+ if(next_char)
+ *next_char = line;
+ if(!quoted && strlen(text) == 0)
+ return NULL;
+ return g_strdup(text);
+}
+
+/*
+ * pscopy -- copy lines of Postscript from a section of one file
+ * to another file.
+ * Automatically switch to binary copying whenever
+ * %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
+ * comments are encountered.
+ */
+
+void
+pscopy(from, to, begin, end)
+ FILE *from;
+ GtkGSDocSink *to;
+ long begin; /* set negative to avoid initial seek */
+ long end;
+{
+ char line[PSLINELENGTH]; /* 255 characters + 1 newline + 1 NULL */
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ unsigned int num;
+ int i;
+ char buf[BUFSIZ];
+
+ if(begin >= 0)
+ fseek(from, begin, SEEK_SET);
+ while(ftell(from) < end) {
+ fgets(line, sizeof line, from);
+ gtk_gs_doc_sink_write(to, line, strlen(line));
+
+ if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
+ /* Do nothing */
+ }
+ else if(iscomment(line + 7, "Data:")) {
+ text[0] = '\0';
+ if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
+ if(strcmp(text, "Lines") == 0) {
+ for(i = 0; i < num; i++) {
+ fgets(line, sizeof(line), from);
+ gtk_gs_doc_sink_write(to, line, strlen(line));
+ }
+ }
+ else {
+ while(num > BUFSIZ) {
+ fread(buf, sizeof(char), BUFSIZ, from);
+ gtk_gs_doc_sink_write(to, buf, BUFSIZ);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof(char), num, from);
+ gtk_gs_doc_sink_write(to, buf, num);
+ }
+ }
+ }
+ else if(iscomment(line + 7, "Binary:")) {
+ if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
+ while(num > BUFSIZ) {
+ fread(buf, sizeof(char), BUFSIZ, from);
+ gtk_gs_doc_sink_write(to, buf, BUFSIZ);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof(char), num, from);
+ gtk_gs_doc_sink_write(to, buf, num);
+ }
+ }
+ }
+}
+
+/*
+ * pscopyuntil -- copy lines of Postscript from a section of one file
+ * to another file until a particular comment is reached.
+ * Automatically switch to binary copying whenever
+ * %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
+ * comments are encountered.
+ */
+
+char *
+pscopyuntil(FILE * from, GtkGSDocSink * to, long begin, long end,
+ const char *comment)
+{
+ char line[PSLINELENGTH]; /* 255 characters + 1 newline + 1 NULL */
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ unsigned int num;
+ int comment_length;
+ int i;
+ char buf[BUFSIZ];
+
+ if(comment != NULL)
+ comment_length = strlen(comment);
+ else
+ comment_length = 0;
+ if(begin >= 0)
+ fseek(from, begin, SEEK_SET);
+
+ while(ftell(from) < end && !feof(from)) {
+ fgets(line, sizeof line, from);
+
+ /* iscomment cannot be used here,
+ * because comment_length is not known at compile time. */
+ if(comment != NULL && strncmp(line, comment, comment_length) == 0) {
+ return g_strdup(line);
+ }
+ gtk_gs_doc_sink_write(to, line, strlen(line));
+ if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
+ /* Do nothing */
+ }
+ else if(iscomment(line + 7, "Data:")) {
+ text[0] = '\0';
+ if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
+ if(strcmp(text, "Lines") == 0) {
+ for(i = 0; i < num; i++) {
+ fgets(line, sizeof line, from);
+ gtk_gs_doc_sink_write(to, line, strlen(line));
+ }
+ }
+ else {
+ while(num > BUFSIZ) {
+ fread(buf, sizeof(char), BUFSIZ, from);
+ gtk_gs_doc_sink_write(to, buf, BUFSIZ);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof(char), num, from);
+ gtk_gs_doc_sink_write(to, buf, num);
+ }
+ }
+ }
+ else if(iscomment(line + 7, "Binary:")) {
+ if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
+ while(num > BUFSIZ) {
+ fread(buf, sizeof(char), BUFSIZ, from);
+ gtk_gs_doc_sink_write(to, buf, BUFSIZ);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof(char), num, from);
+ gtk_gs_doc_sink_write(to, buf, num);
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * blank -- determine whether the line contains nothing but whitespace.
+ */
+
+static int
+blank(char *line)
+{
+ char *cp = line;
+
+ while(*cp == ' ' || *cp == '\t')
+ cp++;
+ return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
+}
+
+/*##########################################################*/
+/* pscopydoc */
+/* Copy the headers, marked pages, and trailer to fp */
+/*##########################################################*/
+
+void
+pscopydoc(GtkGSDocSink * dest,
+ char *src_filename, struct document *d, gint * pagelist)
+{
+ FILE *src_file;
+ char text[PSLINELENGTH];
+ char *comment;
+ gboolean pages_written = FALSE;
+ gboolean pages_atend = FALSE;
+ int pages;
+ int page = 1;
+ int i, j;
+ int here;
+
+ src_file = fopen(src_filename, "r");
+ i = 0;
+ pages = 0;
+ for(i = 0; i < d->numpages; i++) {
+ if(pagelist[i])
+ pages++;
+ }
+
+ here = d->beginheader;
+
+ while((comment = pscopyuntil(src_file, dest, here, d->endheader, "%%Pages:"))) {
+ here = ftell(src_file);
+ if(pages_written || pages_atend) {
+ g_free(comment);
+ continue;
+ }
+ sscanf(comment + length("%%Pages:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ gtk_gs_doc_sink_write(dest, comment, strlen(comment));
+ pages_atend = TRUE;
+ }
+ else {
+ switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
+ case 1:
+ gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
+ break;
+ default:
+ gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
+ break;
+ }
+ pages_written = TRUE;
+ }
+ g_free(comment);
+ }
+ pscopyuntil(src_file, dest, d->beginpreview, d->endpreview, NULL);
+ pscopyuntil(src_file, dest, d->begindefaults, d->enddefaults, NULL);
+ pscopyuntil(src_file, dest, d->beginprolog, d->endprolog, NULL);
+ pscopyuntil(src_file, dest, d->beginsetup, d->endsetup, NULL);
+
+ for(i = 0; i < d->numpages; i++) {
+ if(d->pageorder == DESCEND)
+ j = (d->numpages - 1) - i;
+ else
+ j = i;
+ j = i;
+ if(pagelist[j]) {
+ comment = pscopyuntil(src_file, dest,
+ d->pages[i].begin, d->pages[i].end, "%%Page:");
+ gtk_gs_doc_sink_printf(dest, "%%%%Page: %s %d\n",
+ d->pages[i].label, page++);
+ g_free(comment);
+ pscopyuntil(src_file, dest, -1, d->pages[i].end, NULL);
+ }
+ }
+
+ here = d->begintrailer;
+ while((comment = pscopyuntil(src_file, dest, here, d->endtrailer,
+ "%%Pages:"))) {
+ here = ftell(src_file);
+ if(pages_written) {
+ g_free(comment);
+ continue;
+ }
+ switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
+ case 1:
+ gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
+ break;
+ default:
+ gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
+ break;
+ }
+ pages_written = TRUE;
+ g_free(comment);
+ }
+
+ fclose(src_file);
+}
+
+/*----------------------------------------------------------*/
+/* ps_io_init */
+/*----------------------------------------------------------*/
+
+#define FD_FILE (fd->file)
+#define FD_FILE_DESC (fd->file_desc)
+#define FD_FILEPOS (fd->filepos)
+#define FD_LINE_BEGIN (fd->line_begin)
+#define FD_LINE_END (fd->line_end)
+#define FD_LINE_LEN (fd->line_len)
+#define FD_LINE_TERMCHAR (fd->line_termchar)
+#define FD_BUF (fd->buf)
+#define FD_BUF_END (fd->buf_end)
+#define FD_BUF_SIZE (fd->buf_size)
+#define FD_STATUS (fd->status)
+
+#define FD_STATUS_OKAY 0
+#define FD_STATUS_BUFTOOLARGE 1
+#define FD_STATUS_NOMORECHARS 2
+
+#define LINE_CHUNK_SIZE 4096
+#define MAX_PS_IO_FGETCHARS_BUF_SIZE 57344
+#define BREAK_PS_IO_FGETCHARS_BUF_SIZE 49152
+
+static FileData ps_io_init(file)
+ FILE *file;
+{
+ FileData fd;
+ size_t size = sizeof(FileDataStruct);
+
+ fd = (FileData) g_malloc(size);
+ memset((void*) fd ,0,(size_t)size);
+
+ rewind(file);
+ FD_FILE = file;
+ FD_FILE_DESC = fileno(file);
+ FD_FILEPOS = ftell(file);
+ FD_BUF_SIZE = (2*LINE_CHUNK_SIZE)+1;
+ FD_BUF = g_malloc(FD_BUF_SIZE);
+ FD_BUF[0] = '\0';
+ return(fd);
+}
+
+/*----------------------------------------------------------*/
+/* ps_io_exit */
+/*----------------------------------------------------------*/
+
+static void
+ps_io_exit(fd)
+ FileData fd;
+{
+ g_free(FD_BUF);
+ g_free(fd);
+}
+
+/*----------------------------------------------------------*/
+/* ps_io_fseek */
+/*----------------------------------------------------------*/
+
+/*static int
+ps_io_fseek(fd,offset)
+ FileData fd;
+ int offset;
+{
+ int status;
+ status=fseek(FD_FILE,(long)offset,SEEK_SET);
+ FD_BUF_END = FD_LINE_BEGIN = FD_LINE_END = FD_LINE_LEN = 0;
+ FD_FILEPOS = offset;
+ FD_STATUS = FD_STATUS_OKAY;
+ return(status);
+}*/
+
+/*----------------------------------------------------------*/
+/* ps_io_ftell */
+/*----------------------------------------------------------*/
+
+/*static int
+ps_io_ftell(fd)
+ FileData fd;
+{
+ return(FD_FILEPOS);
+}*/
+
+/*----------------------------------------------------------*/
+/* ps_io_fgetchars */
+/*----------------------------------------------------------*/
+
+#ifdef USE_MEMMOVE_CODE
+static void ps_memmove (d, s, l)
+ char *d;
+ const char *s;
+ unsigned l;
+{
+ if (s < d) for (s += l, d += l; l; --l) *--d = *--s;
+ else if (s != d) for (; l; --l) *d++ = *s++;
+}
+#else
+# define ps_memmove memmove
+#endif
+
+static char * ps_io_fgetchars(fd,num)
+ FileData fd;
+ int num;
+{
+ char *eol=NULL,*tmp;
+ size_t size_of_char = sizeof(char);
+
+ if (FD_STATUS != FD_STATUS_OKAY) {
+ return(NULL);
+ }
+
+ FD_BUF[FD_LINE_END] = FD_LINE_TERMCHAR; /* restoring char previously exchanged against '\0' */
+ FD_LINE_BEGIN = FD_LINE_END;
+
+ do {
+ if (num<0) { /* reading whole line */
+ if (FD_BUF_END-FD_LINE_END) {
+ /* strpbrk is faster but fails on lines with embedded NULLs
+ eol = strpbrk(FD_BUF+FD_LINE_END,"\n\r");
+ */
+ tmp = FD_BUF + FD_BUF_END;
+ eol = FD_BUF + FD_LINE_END;
+ while (eol < tmp && *eol != '\n' && *eol != '\r') eol++;
+ if (eol >= tmp) eol = NULL;
+ if (eol) {
+ if (*eol=='\r' && *(eol+1)=='\n') eol += 2;
+ else eol++;
+ break;
+ }
+ }
+ } else { /* reading specified num of chars */
+ if (FD_BUF_END >= FD_LINE_BEGIN+num) {
+ eol = FD_BUF+FD_LINE_BEGIN+num;
+ break;
+ }
+ }
+
+ if (FD_BUF_END - FD_LINE_BEGIN > BREAK_PS_IO_FGETCHARS_BUF_SIZE) {
+ eol = FD_BUF + FD_BUF_END - 1;
+ break;
+ }
+
+ while (FD_BUF_SIZE < FD_BUF_END+LINE_CHUNK_SIZE+1) {
+ if (FD_BUF_SIZE > MAX_PS_IO_FGETCHARS_BUF_SIZE) {
+ /* we should never get here, since the line is broken
+ artificially after BREAK_PS_IO_FGETCHARS_BUF_SIZE bytes. */
+ fprintf(stderr, "gv: ps_io_fgetchars: Fatal Error: buffer became too large.\n");
+ exit(-1);
+ }
+ if (FD_LINE_BEGIN) {
+ ps_memmove((void*)FD_BUF,(void*)(FD_BUF+FD_LINE_BEGIN),
+ ((size_t)(FD_BUF_END-FD_LINE_BEGIN+1))*size_of_char);
+ FD_BUF_END -= FD_LINE_BEGIN;
+ FD_LINE_BEGIN = 0;
+ } else {
+ FD_BUF_SIZE = FD_BUF_SIZE+LINE_CHUNK_SIZE+1;
+ FD_BUF = g_realloc(FD_BUF,FD_BUF_SIZE);
+ }
+ }
+
+ FD_LINE_END = FD_BUF_END;
+#ifdef VMS
+ /* different existing VMS file formats require that we use read here ###jp###,10/12/96 */
+ if (num<0) FD_BUF_END += read(FD_FILE_DESC,FD_BUF+FD_BUF_END,LINE_CHUNK_SIZE);
+ else FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
+#else
+ /* read() seems to fail sometimes (? ? ?) so we always use fread ###jp###,07/31/96*/
+ FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
+#endif
+
+ FD_BUF[FD_BUF_END] = '\0';
+ if (FD_BUF_END-FD_LINE_END == 0) {
+ FD_STATUS = FD_STATUS_NOMORECHARS;
+ return(NULL);
+ }
+ }
+ while (1);
+
+ FD_LINE_END = eol - FD_BUF;
+ FD_LINE_LEN = FD_LINE_END - FD_LINE_BEGIN;
+ FD_LINE_TERMCHAR = FD_BUF[FD_LINE_END];
+ FD_BUF[FD_LINE_END] = '\0';
+#ifdef USE_FTELL_FOR_FILEPOS
+ if (FD_LINE_END==FD_BUF_END) {
+ /*
+ For VMS we cannot assume that the record is FD_LINE_LEN bytes long
+ on the disk. For stream_lf and stream_cr that is true, but not for
+ other formats, since VAXC/DECC converts the formatting into a single \n.
+ eg. variable format files have a 2-byte length and padding to an even
+ number of characters. So, we use ftell for each record.
+ This still will not work if we need to fseek to a \n or \r inside a
+ variable record (ftell always returns the start of the record in this
+ case).
+ (Tim Adye, adye@v2.rl.ac.uk)
+ */
+ FD_FILEPOS = ftell(FD_FILE);
+ } else
+#endif /* USE_FTELL_FOR_FILEPOS */
+ FD_FILEPOS += FD_LINE_LEN;
+
+ return(FD_BUF+FD_LINE_BEGIN);
+}
+
+/*----------------------------------------------------------*/
+/*
+ readline()
+ Read the next line in the postscript file.
+ Automatically skip over data (as indicated by
+ %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
+ comments.)
+ Also, skip over included documents (as indicated by
+ %%BeginDocument/%%EndDocument comments.)
+*/
+/*----------------------------------------------------------*/
+
+static char *readline (fd, lineP, positionP, line_lenP)
+ FileData fd;
+ char **lineP;
+ long *positionP;
+ unsigned int *line_lenP;
+{
+ unsigned int nbytes=0;
+ int skipped=0;
+ char *line;
+
+ if (positionP) *positionP = FD_FILEPOS;
+ line = ps_io_fgetchars(fd,-1);
+ if (!line) {
+ *line_lenP = 0;
+ *lineP = empty_string;
+ return(NULL);
+ }
+
+ *line_lenP = FD_LINE_LEN;
+
+#define IS_COMMENT(comment) \
+ (DSCcomment(line) && iscomment(line+2,(comment)))
+#define IS_BEGIN(comment) \
+ (iscomment(line+7,(comment)))
+#define SKIP_WHILE(cond) \
+ while (readline(fd, &line, NULL, &nbytes) && (cond)) *line_lenP += nbytes;\
+ skipped=1;
+#define SKIP_UNTIL_1(comment) { \
+ SKIP_WHILE((!IS_COMMENT(comment))) \
+ }
+#define SKIP_UNTIL_2(comment1,comment2) { \
+ SKIP_WHILE((!IS_COMMENT(comment1) && !IS_COMMENT(comment2)))\
+ }
+
+ if (!IS_COMMENT("Begin")) {} /* Do nothing */
+ else if IS_BEGIN("Document:") SKIP_UNTIL_1("EndDocument")
+ else if IS_BEGIN("Feature:") SKIP_UNTIL_1("EndFeature")
+#ifdef USE_ACROREAD_WORKAROUND
+ else if IS_BEGIN("File") SKIP_UNTIL_2("EndFile","EOF")
+#else
+ else if IS_BEGIN("File") SKIP_UNTIL_1("EndFile")
+#endif
+ else if IS_BEGIN("Font") SKIP_UNTIL_1("EndFont")
+ else if IS_BEGIN("ProcSet") SKIP_UNTIL_1("EndProcSet")
+ else if IS_BEGIN("Resource") SKIP_UNTIL_1("EndResource")
+ else if IS_BEGIN("Data:") {
+ int num;
+ char text[101];
+ if (FD_LINE_LEN > 100) FD_BUF[100] = '\0';
+ text[0] = '\0';
+ if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
+ if (strcmp(text, "Lines") == 0) {
+ while (num) {
+ line = ps_io_fgetchars(fd,-1);
+ if (line) *line_lenP += FD_LINE_LEN;
+ num--;
+ }
+ } else {
+ int read_chunk_size = LINE_CHUNK_SIZE;
+ while (num>0) {
+ if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
+ line = ps_io_fgetchars(fd,read_chunk_size);
+ if (line) *line_lenP += FD_LINE_LEN;
+ num -= read_chunk_size;
+ }
+ }
+ }
+ SKIP_UNTIL_1("EndData")
+ }
+ else if IS_BEGIN("Binary:") {
+ int num;
+ if (sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
+ int read_chunk_size = LINE_CHUNK_SIZE;
+ while (num>0) {
+ if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
+ line = ps_io_fgetchars(fd,read_chunk_size);
+ if (line) *line_lenP += FD_LINE_LEN;
+ num -= read_chunk_size;
+ }
+ SKIP_UNTIL_1("EndBinary")
+ }
+ }
+
+ if (skipped) {
+ *line_lenP += nbytes;
+ *lineP = skipped_line;
+ } else {
+ *lineP = FD_BUF+FD_LINE_BEGIN;
+ }
+
+ return(FD_BUF+FD_LINE_BEGIN);
+}
diff --git a/backend/ps/ps.h b/backend/ps/ps.h
new file mode 100644
index 0000000..5e27618
--- /dev/null
+++ b/backend/ps/ps.h
@@ -0,0 +1,107 @@
+/*
+ * ps.h -- Include file for PostScript routines.
+ * Copyright (C) 1992 Timothy O. Theisen
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Tim Theisen Systems Programmer
+ * Internet: tim@cs.wisc.edu Department of Computer Sciences
+ * UUCP: uwvax!tim University of Wisconsin-Madison
+ * Phone: (608)262-0438 1210 West Dayton Street
+ * FAX: (608)262-9777 Madison, WI 53706
+ */
+#ifndef __GGV_PS_H__
+#define __GGV_PS_H__
+
+#include <stdio.h>
+
+#include <gsio.h>
+#include <gstypes.h>
+
+G_BEGIN_DECLS
+
+/* Constants used to index into the bounding box array. */
+#define LLX 0
+#define LLY 1
+#define URX 2
+#define URY 3
+
+/* Constants used to store keywords that are scanned. */
+/* NONE is not a keyword, it tells when a field was not set */
+
+enum { ATEND = -1, NONE = 0, ASCEND, DESCEND, SPECIAL };
+
+#define PSLINELENGTH 257 /* 255 characters + 1 newline + 1 NULL */
+
+struct document {
+ int epsf; /* Encapsulated PostScript flag. */
+ char *title; /* Title of document. */
+ char *date; /* Creation date. */
+ char *creator; /* Program that created the file */
+ int pageorder; /* ASCEND, DESCEND, SPECIAL */
+ long beginheader, endheader; /* offsets into file */
+ unsigned int lenheader;
+ long beginpreview, endpreview;
+ unsigned int lenpreview;
+ long begindefaults, enddefaults;
+ unsigned int lendefaults;
+ long beginprolog, endprolog;
+ unsigned int lenprolog;
+ long beginsetup, endsetup;
+ unsigned int lensetup;
+ long begintrailer, endtrailer;
+ unsigned int lentrailer;
+ int boundingbox[4];
+ int default_page_boundingbox[4];
+ int orientation; /* GTK_GS_ORIENTATION_PORTRAIT, GTK_GS_ORIENTATION_LANDSCAPE */
+ int default_page_orientation; /* GTK_GS_ORIENTATION_PORTRAIT, GTK_GS_ORIENTATION_LANDSCAPE */
+ unsigned int numsizes;
+ GtkGSPaperSize *size;
+ GtkGSPaperSize *default_page_size;
+ unsigned int numpages;
+ struct page *pages;
+};
+
+struct page {
+ char *label;
+ int boundingbox[4];
+ GtkGSPaperSize *size;
+ int orientation; /* GTK_GS_ORIENTATION_PORTRAIT, GTK_GS_ORIENTATION_LANDSCAPE */
+ long begin, end; /* offsets into file */
+ unsigned int len;
+};
+
+/* scans a PostScript file and return a pointer to the document
+ structure. Returns NULL if file does not Conform to commenting
+ conventions . */
+struct document *psscan(FILE * fileP, int respect_eof, const gchar * fname);
+
+/* free data structure malloc'ed by psscan */
+void psfree(struct document *);
+
+/* Copy a portion of the PostScript file */
+void pscopy(FILE * from, GtkGSDocSink * to, long begin, long end);
+
+/* Copy a portion of the PostScript file upto a comment */
+char *pscopyuntil(FILE * from, GtkGSDocSink * to, long begin, long end,
+ const char *comment);
+
+/* Copy the headers, marked pages, and trailer to fp */
+void pscopydoc(GtkGSDocSink * dest_file, char *src_filename,
+ struct document *d, int *pagelist);
+
+G_END_DECLS
+
+#endif /* __GGV_PS_H__ */
diff --git a/backend/tiff/Makefile.am b/backend/tiff/Makefile.am
new file mode 100644
index 0000000..f89e9d9
--- /dev/null
+++ b/backend/tiff/Makefile.am
@@ -0,0 +1,12 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ $(BACKEND_CFLAGS)
+
+noinst_LTLIBRARIES = libtiffdocument.la
+
+libtiffdocument_la_SOURCES = \
+ tiff-document.c \
+ tiff-document.h \
+ tiff2ps.c \
+ tiff2ps.h
diff --git a/backend/tiff/tiff-document.c b/backend/tiff/tiff-document.c
new file mode 100644
index 0000000..24af05f
--- /dev/null
+++ b/backend/tiff/tiff-document.c
@@ -0,0 +1,437 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005, Jonathan Blandford <jrb@gnome.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* FIXME: Should probably buffer calls to libtiff with TIFFSetWarningHandler
+ */
+
+#include <stdio.h>
+#include <glib.h>
+
+#include "tiffio.h"
+#include "tiff2ps.h"
+#include "tiff-document.h"
+#include "ev-document-misc.h"
+#include "ev-document-thumbnails.h"
+#include "ev-file-exporter.h"
+
+struct _TiffDocumentClass
+{
+ GObjectClass parent_class;
+};
+
+struct _TiffDocument
+{
+ GObject parent_instance;
+
+ TIFF *tiff;
+ gint n_pages;
+ TIFF2PSContext *ps_export_ctx;
+
+ gchar *uri;
+};
+
+typedef struct _TiffDocumentClass TiffDocumentClass;
+
+static void tiff_document_document_iface_init (EvDocumentIface *iface);
+static void tiff_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
+static void tiff_document_document_file_exporter_iface_init (EvFileExporterIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (TiffDocument, tiff_document, G_TYPE_OBJECT,
+ { G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
+ tiff_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+ tiff_document_document_thumbnails_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
+ tiff_document_document_file_exporter_iface_init);
+ });
+
+static TIFFErrorHandler orig_error_handler = NULL;
+static TIFFErrorHandler orig_warning_handler = NULL;
+
+static void
+push_handlers (void)
+{
+ orig_error_handler = TIFFSetErrorHandler (NULL);
+ orig_warning_handler = TIFFSetWarningHandler (NULL);
+}
+
+static void
+pop_handlers (void)
+{
+ TIFFSetErrorHandler (orig_error_handler);
+ TIFFSetWarningHandler (orig_warning_handler);
+}
+
+static gboolean
+tiff_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ TiffDocument *tiff_document = TIFF_DOCUMENT (document);
+ gchar *filename;
+ TIFF *tiff;
+
+ push_handlers ();
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ {
+ pop_handlers ();
+ return FALSE;
+ }
+
+ tiff = TIFFOpen (filename, "r");
+ if (tiff)
+ {
+ guint32 w, h;
+ /* FIXME: unused data? why bother here */
+ TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &w);
+ TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &h);
+ }
+ if (!tiff)
+ {
+ pop_handlers ();
+ return FALSE;
+ }
+ tiff_document->tiff = tiff;
+ g_free (tiff_document->uri);
+ g_free (filename);
+ tiff_document->uri = g_strdup (uri);
+
+ pop_handlers ();
+ return TRUE;
+}
+
+static gboolean
+tiff_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ TiffDocument *tiff_document = TIFF_DOCUMENT (document);
+
+ return ev_xfer_uri_simple (tiff_document->uri, uri, error);
+}
+
+static int
+tiff_document_get_n_pages (EvDocument *document)
+{
+ TiffDocument *tiff_document = TIFF_DOCUMENT (document);
+
+ g_return_val_if_fail (TIFF_IS_DOCUMENT (document), 0);
+ g_return_val_if_fail (tiff_document->tiff != NULL, 0);
+
+ if (tiff_document->n_pages == -1)
+ {
+ push_handlers ();
+ tiff_document->n_pages = 0;
+ do
+ {
+ tiff_document->n_pages ++;
+ }
+ while (TIFFReadDirectory (tiff_document->tiff));
+ pop_handlers ();
+ }
+
+ return tiff_document->n_pages;
+}
+
+static void
+tiff_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height)
+{
+ guint32 w, h;
+ gfloat x_res, y_res;
+ TiffDocument *tiff_document = TIFF_DOCUMENT (document);
+
+ g_return_if_fail (TIFF_IS_DOCUMENT (document));
+ g_return_if_fail (tiff_document->tiff != NULL);
+
+ push_handlers ();
+ if (TIFFSetDirectory (tiff_document->tiff, page) != 1)
+ {
+ pop_handlers ();
+ return;
+ }
+
+ TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &w);
+ TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &h);
+ TIFFGetField (tiff_document->tiff, TIFFTAG_XRESOLUTION, &x_res);
+ TIFFGetField (tiff_document->tiff, TIFFTAG_YRESOLUTION, &y_res);
+ h = h * (x_res / y_res);
+
+ *width = w;
+ *height = h;
+
+ pop_handlers ();
+}
+
+static GdkPixbuf *
+tiff_document_render_pixbuf (EvDocument *document,
+ EvRenderContext *rc)
+{
+ TiffDocument *tiff_document = TIFF_DOCUMENT (document);
+ int width, height;
+ float x_res, y_res;
+ gint rowstride, bytes;
+ guchar *pixels = NULL;
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *scaled_pixbuf;
+ GdkPixbuf *rotated_pixbuf;
+
+ g_return_val_if_fail (TIFF_IS_DOCUMENT (document), 0);
+ g_return_val_if_fail (tiff_document->tiff != NULL, 0);
+
+ push_handlers ();
+ if (TIFFSetDirectory (tiff_document->tiff, rc->page) != 1)
+ {
+ pop_handlers ();
+ return NULL;
+ }
+
+ if (!TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &width))
+ {
+ pop_handlers ();
+ return NULL;
+ }
+
+ if (! TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &height))
+ {
+ pop_handlers ();
+ return NULL;
+ }
+
+ if (!TIFFGetField (tiff_document->tiff, TIFFTAG_XRESOLUTION, &x_res))
+ {
+ pop_handlers ();
+ return NULL;
+ }
+
+ if (! TIFFGetField (tiff_document->tiff, TIFFTAG_YRESOLUTION, &y_res))
+ {
+ pop_handlers ();
+ return NULL;
+ }
+
+ pop_handlers ();
+
+ /* Sanity check the doc */
+ if (width <= 0 || height <= 0)
+ return NULL;
+
+ rowstride = width * 4;
+ if (rowstride / 4 != width)
+ /* overflow */
+ return NULL;
+
+ bytes = height * rowstride;
+ if (bytes / rowstride != height)
+ /* overflow */
+ return NULL;
+
+ pixels = g_try_malloc (bytes);
+ if (!pixels)
+ return NULL;
+
+ pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
+ width, height, rowstride,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+ TIFFReadRGBAImageOriented (tiff_document->tiff, width, height, (uint32 *)gdk_pixbuf_get_pixels (pixbuf), ORIENTATION_TOPLEFT, 1);
+ pop_handlers ();
+
+ scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
+ width * rc->scale,
+ height * rc->scale * (x_res/y_res),
+ GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf, 360 - rc->rotation);
+ g_object_unref (scaled_pixbuf);
+
+ return rotated_pixbuf;
+}
+
+static void
+tiff_document_finalize (GObject *object)
+{
+ TiffDocument *tiff_document = TIFF_DOCUMENT (object);
+
+ if (tiff_document->tiff)
+ TIFFClose (tiff_document->tiff);
+ if (tiff_document->uri)
+ g_free (tiff_document->uri);
+
+ G_OBJECT_CLASS (tiff_document_parent_class)->finalize (object);
+}
+
+static void
+tiff_document_class_init (TiffDocumentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = tiff_document_finalize;
+}
+
+static gboolean
+tiff_document_can_get_text (EvDocument *document)
+{
+ return FALSE;
+}
+
+static EvDocumentInfo *
+tiff_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+
+ info = g_new0 (EvDocumentInfo, 1);
+ info->fields_mask = 0;
+
+ return info;
+}
+
+static void
+tiff_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->load = tiff_document_load;
+ iface->save = tiff_document_save;
+ iface->can_get_text = tiff_document_can_get_text;
+ iface->get_n_pages = tiff_document_get_n_pages;
+ iface->get_page_size = tiff_document_get_page_size;
+ iface->render_pixbuf = tiff_document_render_pixbuf;
+ iface->get_info = tiff_document_get_info;
+}
+
+static GdkPixbuf *
+tiff_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
+ gint page,
+ gint rotation,
+ gint size,
+ gboolean border)
+{
+ EvRenderContext *rc;
+ GdkPixbuf *pixbuf;
+ gdouble w, h;
+
+ tiff_document_get_page_size (EV_DOCUMENT (document),
+ page,
+ &w, &h);
+
+ rc = ev_render_context_new (rotation, page, size/w);
+ pixbuf = tiff_document_render_pixbuf (EV_DOCUMENT (document), rc);
+ g_object_unref (G_OBJECT (rc));
+
+ if (border)
+ {
+ GdkPixbuf *tmp_pixbuf = pixbuf;
+ pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, 0, tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+ }
+
+ return pixbuf;
+}
+
+static void
+tiff_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
+ gint page,
+ gint suggested_width,
+ gint *width,
+ gint *height)
+{
+ gdouble page_ratio;
+ gdouble w, h;
+
+ tiff_document_get_page_size (EV_DOCUMENT (document),
+ page,
+ &w, &h);
+ g_return_if_fail (w > 0);
+ page_ratio = h/w;
+ *width = suggested_width;
+ *height = (gint) (suggested_width * page_ratio);
+}
+
+static void
+tiff_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
+{
+ iface->get_thumbnail = tiff_document_thumbnails_get_thumbnail;
+ iface->get_dimensions = tiff_document_thumbnails_get_dimensions;
+}
+
+/* postscript exporter implementation */
+
+static gboolean
+tiff_document_file_exporter_format_supported (EvFileExporter *exporter,
+ EvFileExporterFormat format)
+{
+ return (format == EV_FILE_FORMAT_PS);
+}
+
+static void
+tiff_document_file_exporter_begin (EvFileExporter *exporter,
+ EvFileExporterFormat format,
+ const char *filename,
+ int first_page,
+ int last_page,
+ double width,
+ double height,
+ gboolean duplex)
+{
+ TiffDocument *document = TIFF_DOCUMENT (exporter);
+
+ document->ps_export_ctx = tiff2ps_context_new(filename);
+}
+
+static void
+tiff_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
+{
+ TiffDocument *document = TIFF_DOCUMENT (exporter);
+
+ if (document->ps_export_ctx == NULL)
+ return;
+ if (TIFFSetDirectory (document->tiff, rc->page) != 1)
+ return;
+ tiff2ps_process_page (document->ps_export_ctx, document->tiff,
+ 0, 0, 0, 0, 0);
+}
+
+static void
+tiff_document_file_exporter_end (EvFileExporter *exporter)
+{
+ TiffDocument *document = TIFF_DOCUMENT (exporter);
+
+ if (document->ps_export_ctx == NULL)
+ return;
+ tiff2ps_context_finalize(document->ps_export_ctx);
+}
+
+static void
+tiff_document_document_file_exporter_iface_init (EvFileExporterIface *iface)
+{
+ iface->format_supported = tiff_document_file_exporter_format_supported;
+ iface->begin = tiff_document_file_exporter_begin;
+ iface->do_page = tiff_document_file_exporter_do_page;
+ iface->end = tiff_document_file_exporter_end;
+}
+
+static void
+tiff_document_init (TiffDocument *tiff_document)
+{
+ tiff_document->n_pages = -1;
+}
diff --git a/backend/tiff/tiff-document.h b/backend/tiff/tiff-document.h
new file mode 100644
index 0000000..bdf0e29
--- /dev/null
+++ b/backend/tiff/tiff-document.h
@@ -0,0 +1,38 @@
+
+/* pdfdocument.h: Implementation of EvDocument for tiffs
+ * Copyright (C) 2005, Jonathan Blandford <jrb@gnome.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TIFF_DOCUMENT_H__
+#define __TIFF_DOCUMENT_H__
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+#define TIFF_TYPE_DOCUMENT (tiff_document_get_type ())
+#define TIFF_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIFF_TYPE_DOCUMENT, TiffDocument))
+#define TIFF_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIFF_TYPE_DOCUMENT))
+
+typedef struct _TiffDocument TiffDocument;
+
+TiffDocument *tiff_document_new (void);
+GType tiff_document_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __TIFF_DOCUMENT_H__ */
diff --git a/backend/tiff/tiff2ps.c b/backend/tiff/tiff2ps.c
new file mode 100644
index 0000000..632169b
--- /dev/null
+++ b/backend/tiff/tiff2ps.c
@@ -0,0 +1,1868 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* $Id$ */
+
+/*
+ * Copyright (c) 1988-1997 Sam Leffler
+ * Copyright (c) 1991-1997 Silicon Graphics, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+/*
+ * Modified for use as Evince TIFF ps exporter by
+ * Matthew S. Wilson <msw@rpath.com>
+ * Modifications Copyright (C) 2005 rpath, Inc.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h> /* for atof */
+#include <math.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "tiff2ps.h"
+
+/*
+ * Revision history
+ *
+ * 2001-Mar-21
+ * I (Bruce A. Mallett) added this revision history comment ;)
+ *
+ * Fixed PS_Lvl2page() code which outputs non-ASCII85 raw
+ * data. Moved test for when to output a line break to
+ * *after* the output of a character. This just serves
+ * to fix an eye-nuisance where the first line of raw
+ * data was one character shorter than subsequent lines.
+ *
+ * Added an experimental ASCII85 encoder which can be used
+ * only when there is a single buffer of bytes to be encoded.
+ * This version is much faster at encoding a straight-line
+ * buffer of data because it can avoid alot of the loop
+ * overhead of the byte-by-bye version. To use this version
+ * you need to define EXP_ASCII85ENCODER (experimental ...).
+ *
+ * Added bug fix given by Michael Schmidt to PS_Lvl2page()
+ * in which an end-of-data marker ('>') was not being output
+ * when producing non-ASCII85 encoded PostScript Level 2
+ * data.
+ *
+ * Fixed PS_Lvl2colorspace() so that it no longer assumes that
+ * a TIFF having more than 2 planes is a CMYK. This routine
+ * no longer looks at the samples per pixel but instead looks
+ * at the "photometric" value. This change allows support of
+ * CMYK TIFFs.
+ *
+ * Modified the PostScript L2 imaging loop so as to test if
+ * the input stream is still open before attempting to do a
+ * flushfile on it. This was done because some RIPs close
+ * the stream after doing the image operation.
+ *
+ * Got rid of the realloc() being done inside a loop in the
+ * PSRawDataBW() routine. The code now walks through the
+ * byte-size array outside the loop to determine the largest
+ * size memory block that will be needed.
+ *
+ * Added "-m" switch to ask tiff2ps to, where possible, use the
+ * "imagemask" operator instead of the "image" operator.
+ *
+ * Added the "-i #" switch to allow interpolation to be disabled.
+ *
+ * Unrolled a loop or two to improve performance.
+ */
+
+/*
+ * Define EXP_ASCII85ENCODER if you want to use an experimental
+ * version of the ASCII85 encoding routine. The advantage of
+ * using this routine is that tiff2ps will convert to ASCII85
+ * encoding at between 3 and 4 times the speed as compared to
+ * using the old (non-experimental) encoder. The disadvantage
+ * is that you will be using a new (and unproven) encoding
+ * routine. So user beware, you have been warned!
+ */
+
+#define EXP_ASCII85ENCODER
+
+/*
+ * NB: this code assumes uint32 works with printf's %l[ud].
+ */
+
+struct _TIFF2PSContext
+{
+ char *filename; /* input filename */
+ FILE *fd; /* output file stream */
+ int ascii85; /* use ASCII85 encoding */
+ int interpolate; /* interpolate level2 image */
+ int level2; /* generate PostScript level 2 */
+ int level3; /* generate PostScript level 3 */
+ int generateEPSF; /* generate Encapsulated PostScript */
+ int PSduplex; /* enable duplex printing */
+ int PStumble; /* enable top edge binding */
+ int PSavoiddeadzone; /* enable avoiding printer deadzone */
+ double maxPageHeight; /* maximum size to fit on page */
+ double splitOverlap; /* amount for split pages to overlag */
+ int rotate; /* rotate image by 180 degrees */
+ int useImagemask; /* Use imagemask instead of image operator */
+ uint16 res_unit; /* Resolution units: 2 - inches, 3 - cm */
+ int npages; /* number of pages processed */
+
+ tsize_t tf_bytesperrow;
+ tsize_t ps_bytesperrow;
+ tsize_t tf_rowsperstrip;
+ tsize_t tf_numberstrips;
+
+ /*
+ * ASCII85 Encoding Support.
+ */
+ unsigned char ascii85buf[10];
+ int ascii85count;
+ int ascii85breaklen;
+ uint16 samplesperpixel;
+ uint16 bitspersample;
+ uint16 planarconfiguration;
+ uint16 photometric;
+ uint16 compression;
+ uint16 extrasamples;
+ int alpha;
+};
+
+static void PSpage(TIFF2PSContext*, TIFF*, uint32, uint32);
+static void PSColorContigPreamble(TIFF2PSContext*, uint32, uint32, int);
+static void PSColorSeparatePreamble(TIFF2PSContext*, uint32, uint32, int);
+static void PSDataColorContig(TIFF2PSContext*, TIFF*, uint32, uint32, int);
+static void PSDataColorSeparate(TIFF2PSContext*, TIFF*, uint32, uint32, int);
+static void PSDataPalette(TIFF2PSContext*, TIFF*, uint32, uint32);
+static void PSDataBW(TIFF2PSContext*, TIFF*, uint32, uint32);
+static void Ascii85Init(TIFF2PSContext*);
+static void Ascii85Put(TIFF2PSContext*, unsigned char);
+static void Ascii85Flush(TIFF2PSContext*);
+static void PSHead(TIFF2PSContext*, TIFF*, uint32, uint32,
+ double, double, double, double);
+static void PSTail(TIFF2PSContext*);
+
+#if defined( EXP_ASCII85ENCODER )
+static int Ascii85EncodeBlock(TIFF2PSContext*, uint8 * ascii85_p,
+ unsigned f_eod, const uint8 * raw_p, int raw_l);
+#endif
+
+TIFF2PSContext* tiff2ps_context_new(const gchar *filename) {
+ TIFF2PSContext* ctx;
+
+ ctx = g_new0(TIFF2PSContext, 1);
+ ctx->filename = g_strdup(filename);
+ ctx->fd = g_fopen(ctx->filename, "w");
+ if (ctx->fd == NULL)
+ return NULL;
+ ctx->interpolate = TRUE; /* interpolate level2 image */
+ ctx->PSavoiddeadzone = TRUE; /* enable avoiding printer deadzone */
+ return ctx;
+}
+
+void tiff2ps_context_finalize(TIFF2PSContext *ctx) {
+ PSTail(ctx);
+ fclose(ctx->fd);
+ g_free(ctx->filename);
+ g_free(ctx);
+}
+
+static int
+checkImage(TIFF2PSContext *ctx, TIFF* tif)
+{
+ switch (ctx->photometric) {
+ case PHOTOMETRIC_YCBCR:
+ if ((ctx->compression == COMPRESSION_JPEG
+ || ctx->compression == COMPRESSION_OJPEG)
+ && ctx->planarconfiguration == PLANARCONFIG_CONTIG) {
+ /* can rely on libjpeg to convert to RGB */
+ TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE,
+ JPEGCOLORMODE_RGB);
+ ctx->photometric = PHOTOMETRIC_RGB;
+ } else {
+ if (ctx->level2 || ctx->level3)
+ break;
+ TIFFError(ctx->filename, "Can not handle image with %s",
+ "Ctx->PhotometricInterpretation=YCbCr");
+ return (0);
+ }
+ /* fall thru... */
+ case PHOTOMETRIC_RGB:
+ if (ctx->alpha && ctx->bitspersample != 8) {
+ TIFFError(ctx->filename,
+ "Can not handle %d-bit/sample RGB image with ctx->alpha",
+ ctx->bitspersample);
+ return (0);
+ }
+ /* fall thru... */
+ case PHOTOMETRIC_SEPARATED:
+ case PHOTOMETRIC_PALETTE:
+ case PHOTOMETRIC_MINISBLACK:
+ case PHOTOMETRIC_MINISWHITE:
+ break;
+ case PHOTOMETRIC_LOGL:
+ case PHOTOMETRIC_LOGLUV:
+ if (ctx->compression != COMPRESSION_SGILOG &&
+ ctx->compression != COMPRESSION_SGILOG24) {
+ TIFFError(ctx->filename,
+ "Can not handle %s data with ctx->compression other than SGILog",
+ (ctx->photometric == PHOTOMETRIC_LOGL) ?
+ "LogL" : "LogLuv"
+ );
+ return (0);
+ }
+ /* rely on library to convert to RGB/greyscale */
+ TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT);
+ ctx->photometric = (ctx->photometric == PHOTOMETRIC_LOGL) ?
+ PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB;
+ ctx->bitspersample = 8;
+ break;
+ case PHOTOMETRIC_CIELAB:
+ /* fall thru... */
+ default:
+ TIFFError(ctx->filename,
+ "Can not handle image with Ctx->PhotometricInterpretation=%d",
+ ctx->photometric);
+ return (0);
+ }
+ switch (ctx->bitspersample) {
+ case 1: case 2:
+ case 4: case 8:
+ break;
+ default:
+ TIFFError(ctx->filename, "Can not handle %d-bit/sample image",
+ ctx->bitspersample);
+ return (0);
+ }
+ if (ctx->planarconfiguration == PLANARCONFIG_SEPARATE &&
+ ctx->extrasamples > 0)
+ TIFFWarning(ctx->filename, "Ignoring extra samples");
+ return (1);
+}
+
+#define PS_UNIT_SIZE 72.0F
+#define PSUNITS(npix,res) ((npix) * (PS_UNIT_SIZE / (res)))
+
+static char RGBcolorimage[] = "\
+/bwproc {\n\
+ rgbproc\n\
+ dup length 3 idiv string 0 3 0\n\
+ 5 -1 roll {\n\
+ add 2 1 roll 1 sub dup 0 eq {\n\
+ pop 3 idiv\n\
+ 3 -1 roll\n\
+ dup 4 -1 roll\n\
+ dup 3 1 roll\n\
+ 5 -1 roll put\n\
+ 1 add 3 0\n\
+ } { 2 1 roll } ifelse\n\
+ } forall\n\
+ pop pop pop\n\
+} def\n\
+/colorimage where {pop} {\n\
+ /colorimage {pop pop /rgbproc exch def {bwproc} image} bind def\n\
+} ifelse\n\
+";
+
+/*
+ * Adobe Photoshop requires a comment line of the form:
+ *
+ * %ImageData: <cols> <rows> <depth> <main channels> <pad channels>
+ * <block size> <1 for binary|2 for hex> "data start"
+ *
+ * It is claimed to be part of some future revision of the EPS spec.
+ */
+static void
+PhotoshopBanner(TIFF2PSContext* ctx, uint32 w, uint32 h, int bs, int nc,
+ char* startline)
+{
+ fprintf(ctx->fd, "%%ImageData: %ld %ld %d %d 0 %d 2 \"",
+ (long) w, (long) h, ctx->bitspersample, nc, bs);
+ fprintf(ctx->fd, startline, nc);
+ fprintf(ctx->fd, "\"\n");
+}
+
+/*
+ * pw : image width in pixels
+ * ph : image height in pixels
+ * pprw : image width in PS units (72 dpi)
+ * pprh : image height in PS units (72 dpi)
+ */
+static void
+setupPageState(TIFF2PSContext *ctx, TIFF* tif, uint32* pw, uint32* ph,
+ double* pprw, double* pprh)
+{
+ float xres = 0.0F, yres = 0.0F;
+
+ TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, pw);
+ TIFFGetField(tif, TIFFTAG_IMAGELENGTH, ph);
+ if (ctx->res_unit == 0)
+ TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &ctx->res_unit);
+ /*
+ * Calculate printable area.
+ */
+ if (!TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres)
+ || fabs(xres) < 0.0000001)
+ xres = PS_UNIT_SIZE;
+ if (!TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres)
+ || fabs(yres) < 0.0000001)
+ yres = PS_UNIT_SIZE;
+ switch (ctx->res_unit) {
+ case RESUNIT_CENTIMETER:
+ xres *= 2.54F, yres *= 2.54F;
+ break;
+ case RESUNIT_INCH:
+ break;
+ case RESUNIT_NONE:
+ default:
+ xres *= PS_UNIT_SIZE, yres *= PS_UNIT_SIZE;
+ break;
+ }
+ *pprh = PSUNITS(*ph, yres);
+ *pprw = PSUNITS(*pw, xres);
+}
+
+static int
+isCCITTCompression(TIFF* tif)
+{
+ uint16 compress;
+ TIFFGetField(tif, TIFFTAG_COMPRESSION, &compress);
+ return (compress == COMPRESSION_CCITTFAX3 ||
+ compress == COMPRESSION_CCITTFAX4 ||
+ compress == COMPRESSION_CCITTRLE ||
+ compress == COMPRESSION_CCITTRLEW);
+}
+
+static char *hex = "0123456789abcdef";
+
+/*
+ * imagewidth & imageheight are 1/72 inches
+ * pagewidth & pageheight are inches
+ */
+static int
+PlaceImage(TIFF2PSContext *ctx, double pagewidth, double pageheight,
+ double imagewidth, double imageheight, int splitpage,
+ double lm, double bm, int cnt)
+{
+ double xtran = 0;
+ double ytran = 0;
+ double xscale = 1;
+ double yscale = 1;
+ double left_offset = lm * PS_UNIT_SIZE;
+ double bottom_offset = bm * PS_UNIT_SIZE;
+ double subimageheight;
+ double splitheight;
+ double overlap;
+ /* buffers for locale-insitive number formatting */
+ gchar buf[2][G_ASCII_DTOSTR_BUF_SIZE];
+
+ pagewidth *= PS_UNIT_SIZE;
+ pageheight *= PS_UNIT_SIZE;
+
+ if (ctx->maxPageHeight==0)
+ splitheight = 0;
+ else
+ splitheight = ctx->maxPageHeight * PS_UNIT_SIZE;
+ overlap = ctx->splitOverlap * PS_UNIT_SIZE;
+
+ /*
+ * WIDTH:
+ * if too wide, scrunch to fit
+ * else leave it alone
+ */
+ if (imagewidth <= pagewidth) {
+ xscale = imagewidth;
+ } else {
+ xscale = pagewidth;
+ }
+
+ /* HEIGHT:
+ * if too long, scrunch to fit
+ * if too short, move to top of page
+ */
+ if (imageheight <= pageheight) {
+ yscale = imageheight;
+ ytran = pageheight - imageheight;
+ } else if (imageheight > pageheight &&
+ (splitheight == 0 || imageheight <= splitheight)) {
+ yscale = pageheight;
+ } else /* imageheight > splitheight */ {
+ subimageheight = imageheight - (pageheight-overlap)*splitpage;
+ if (subimageheight <= pageheight) {
+ yscale = imageheight;
+ ytran = pageheight - subimageheight;
+ splitpage = 0;
+ } else if ( subimageheight > pageheight && subimageheight <= splitheight) {
+ yscale = imageheight * pageheight / subimageheight;
+ ytran = 0;
+ splitpage = 0;
+ } else /* sumimageheight > splitheight */ {
+ yscale = imageheight;
+ ytran = pageheight - subimageheight;
+ splitpage++;
+ }
+ }
+
+ bottom_offset += ytran / (cnt?2:1);
+ if (cnt)
+ left_offset += xtran / 2;
+
+ fprintf(ctx->fd, "%s %s translate\n",
+ g_ascii_dtostr(buf[0], sizeof(buf[0]), left_offset),
+ g_ascii_dtostr(buf[1], sizeof(buf[1]), bottom_offset));
+ fprintf(ctx->fd, "%s %s scale\n",
+ g_ascii_dtostr(buf[0], sizeof(buf[0]), xscale),
+ g_ascii_dtostr(buf[1], sizeof(buf[1]), yscale));
+ if (ctx->rotate)
+ fputs ("1 1 translate 180 ctx->rotate\n", ctx->fd);
+
+ return splitpage;
+}
+
+
+void
+tiff2ps_process_page(TIFF2PSContext* ctx, TIFF* tif, double pw, double ph,
+ double lm, double bm, gboolean cnt)
+{
+ uint32 w, h;
+ float ox, oy;
+ double prw, prh;
+ double scale = 1.0;
+ double left_offset = lm * PS_UNIT_SIZE;
+ double bottom_offset = bm * PS_UNIT_SIZE;
+ uint16* sampleinfo;
+ int split;
+ /* buffers for locale-insitive number formatting */
+ gchar buf[2][G_ASCII_DTOSTR_BUF_SIZE];
+
+ if (!TIFFGetField(tif, TIFFTAG_XPOSITION, &ox))
+ ox = 0;
+ if (!TIFFGetField(tif, TIFFTAG_YPOSITION, &oy))
+ oy = 0;
+ setupPageState(ctx, tif, &w, &h, &prw, &prh);
+
+ ctx->tf_numberstrips = TIFFNumberOfStrips(tif);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP,
+ &ctx->tf_rowsperstrip);
+ setupPageState(ctx, tif, &w, &h, &prw, &prh);
+ if (!ctx->npages)
+ PSHead(ctx, tif, w, h, prw, prh, ox, oy);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE,
+ &ctx->bitspersample);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL,
+ &ctx->samplesperpixel);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG,
+ &ctx->planarconfiguration);
+ TIFFGetField(tif, TIFFTAG_COMPRESSION, &ctx->compression);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
+ &ctx->extrasamples, &sampleinfo);
+ ctx->alpha = (ctx->extrasamples == 1 &&
+ sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA);
+ if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &ctx->photometric)) {
+ switch (ctx->samplesperpixel - ctx->extrasamples) {
+ case 1:
+ if (isCCITTCompression(tif))
+ ctx->photometric = PHOTOMETRIC_MINISWHITE;
+ else
+ ctx->photometric = PHOTOMETRIC_MINISBLACK;
+ break;
+ case 3:
+ ctx->photometric = PHOTOMETRIC_RGB;
+ break;
+ case 4:
+ ctx->photometric = PHOTOMETRIC_SEPARATED;
+ break;
+ }
+ }
+ if (checkImage(ctx, tif)) {
+ ctx->tf_bytesperrow = TIFFScanlineSize(tif);
+ ctx->npages++;
+ fprintf(ctx->fd, "%%%%Page: %d %d\n", ctx->npages,
+ ctx->npages);
+ if (!ctx->generateEPSF && ( ctx->level2 || ctx->level3 )) {
+ double psw = 0.0, psh = 0.0;
+ if (psw != 0.0) {
+ psw = pw * PS_UNIT_SIZE;
+ if (ctx->res_unit == RESUNIT_CENTIMETER)
+ psw *= 2.54F;
+ } else
+ psw=ctx->rotate ? prh:prw;
+ if (psh != 0.0) {
+ psh = ph * PS_UNIT_SIZE;
+ if (ctx->res_unit == RESUNIT_CENTIMETER)
+ psh *= 2.54F;
+ } else
+ psh=ctx->rotate ? prw:prh;
+ fprintf(ctx->fd,
+ "1 dict begin /PageSize [ %s %s ] def currentdict end setpagedevice\n",
+ g_ascii_dtostr(buf[0], sizeof(buf[0]), psw),
+ g_ascii_dtostr(buf[1], sizeof(buf[1]), psh));
+ fputs(
+ "<<\n /Policies <<\n /PageSize 3\n >>\n>> setpagedevice\n",
+ ctx->fd);
+ }
+ fprintf(ctx->fd, "gsave\n");
+ fprintf(ctx->fd, "100 dict begin\n");
+ if (pw != 0 || ph != 0) {
+ if (!pw)
+ pw = prw;
+ if (!ph)
+ ph = prh;
+ if (ctx->maxPageHeight) { /* used -H option */
+ split = PlaceImage(ctx,pw,ph,prw,prh,
+ 0,lm,bm,cnt);
+ while( split ) {
+ PSpage(ctx, tif, w, h);
+ fprintf(ctx->fd, "end\n");
+ fprintf(ctx->fd, "grestore\n");
+ fprintf(ctx->fd, "showpage\n");
+ ctx->npages++;
+ fprintf(ctx->fd, "%%%%Page: %d %d\n",
+ ctx->npages, ctx->npages);
+ fprintf(ctx->fd, "gsave\n");
+ fprintf(ctx->fd, "100 dict begin\n");
+ split = PlaceImage(ctx,pw,ph,prw,prh,
+ split,lm,bm,cnt);
+ }
+ } else {
+ pw *= PS_UNIT_SIZE;
+ ph *= PS_UNIT_SIZE;
+
+ /* NB: maintain image aspect ratio */
+ scale = pw/prw < ph/prh ?
+ pw/prw : ph/prh;
+ if (scale > 1.0)
+ scale = 1.0;
+ if (cnt) {
+ bottom_offset +=
+ (ph - prh * scale) / 2;
+ left_offset +=
+ (pw - prw * scale) / 2;
+ }
+ fprintf(ctx->fd, "%s %s translate\n",
+ g_ascii_dtostr(buf[0], sizeof(buf[0]), left_offset),
+ g_ascii_dtostr(buf[1], sizeof(buf[1]), bottom_offset));
+ fprintf(ctx->fd, "%s %s scale\n",
+ g_ascii_dtostr(buf[0], sizeof(buf[0]), prw * scale),
+ g_ascii_dtostr(buf[1], sizeof(buf[1]), prh * scale));
+ if (ctx->rotate)
+ fputs ("1 1 translate 180 ctx->rotate\n", ctx->fd);
+ }
+ } else {
+ fprintf(ctx->fd, "%s %s scale\n",
+ g_ascii_dtostr(buf[0], sizeof(buf[0]), prw),
+ g_ascii_dtostr(buf[1], sizeof(buf[1]), prh));
+ if (ctx->rotate)
+ fputs ("1 1 translate 180 ctx->rotate\n", ctx->fd);
+ }
+ PSpage(ctx, tif, w, h);
+ fprintf(ctx->fd, "end\n");
+ fprintf(ctx->fd, "grestore\n");
+ fprintf(ctx->fd, "showpage\n");
+ }
+}
+
+
+static char DuplexPreamble[] = "\
+%%BeginFeature: *Duplex True\n\
+systemdict begin\n\
+ /languagelevel where { pop languagelevel } { 1 } ifelse\n\
+ 2 ge { 1 dict dup /Duplex true put setpagedevice }\n\
+ { statusdict /setduplex known { statusdict begin setduplex true end } if\n\
+ } ifelse\n\
+end\n\
+%%EndFeature\n\
+";
+
+static char TumblePreamble[] = "\
+%%BeginFeature: *Tumble True\n\
+systemdict begin\n\
+ /languagelevel where { pop languagelevel } { 1 } ifelse\n\
+ 2 ge { 1 dict dup /Tumble true put setpagedevice }\n\
+ { statusdict /settumble known { statusdict begin true settumble end } if\n\
+ } ifelse\n\
+end\n\
+%%EndFeature\n\
+";
+
+static char AvoidDeadZonePreamble[] = "\
+gsave newpath clippath pathbbox grestore\n\
+ 4 2 roll 2 copy translate\n\
+ exch 3 1 roll sub 3 1 roll sub exch\n\
+ currentpagedevice /PageSize get aload pop\n\
+ exch 3 1 roll div 3 1 roll div abs exch abs\n\
+ 2 copy gt { exch } if pop\n\
+ dup 1 lt { dup scale } { pop } ifelse\n\
+";
+
+void
+PSHead(TIFF2PSContext *ctx, TIFF *tif, uint32 w, uint32 h,
+ double pw, double ph, double ox, double oy)
+{
+ time_t t;
+
+ (void) tif; (void) w; (void) h;
+ t = time(0);
+ fprintf(ctx->fd, "%%!PS-Adobe-3.0%s\n",
+ ctx->generateEPSF ? " EPSF-3.0" : "");
+ fprintf(ctx->fd, "%%%%Creator: Evince\n");
+ fprintf(ctx->fd, "%%%%CreationDate: %s", ctime(&t));
+ fprintf(ctx->fd, "%%%%DocumentData: Clean7Bit\n");
+ fprintf(ctx->fd, "%%%%Origin: %ld %ld\n", (long) ox, (long) oy);
+ /* NB: should use PageBoundingBox */
+ fprintf(ctx->fd, "%%%%BoundingBox: 0 0 %ld %ld\n",
+ (long) ceil(pw), (long) ceil(ph));
+ fprintf(ctx->fd, "%%%%LanguageLevel: %d\n",
+ (ctx->level3 ? 3 : (ctx->level2 ? 2 : 1)));
+ fprintf(ctx->fd, "%%%%Pages: (atend)\n");
+ fprintf(ctx->fd, "%%%%EndComments\n");
+ fprintf(ctx->fd, "%%%%BeginSetup\n");
+ if (ctx->PSduplex)
+ fprintf(ctx->fd, "%s", DuplexPreamble);
+ if (ctx->PStumble)
+ fprintf(ctx->fd, "%s", TumblePreamble);
+ if (ctx->PSavoiddeadzone && (ctx->level2 || ctx->level3))
+ fprintf(ctx->fd, "%s", AvoidDeadZonePreamble);
+ fprintf(ctx->fd, "%%%%EndSetup\n");
+}
+
+static void
+PSTail(TIFF2PSContext *ctx)
+{
+ if (!ctx->npages)
+ return;
+ fprintf(ctx->fd, "%%%%Trailer\n");
+ fprintf(ctx->fd, "%%%%Pages: %d\n", ctx->npages);
+ fprintf(ctx->fd, "%%%%EOF\n");
+}
+
+static int
+checkcmap(TIFF2PSContext* ctx, TIFF* tif, int n,
+ uint16* r, uint16* g, uint16* b)
+{
+ (void) tif;
+ while (n-- > 0)
+ if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256)
+ return (16);
+ TIFFWarning(ctx->filename, "Assuming 8-bit colormap");
+ return (8);
+}
+
+static void
+PS_Lvl2colorspace(TIFF2PSContext* ctx, TIFF* tif)
+{
+ uint16 *rmap, *gmap, *bmap;
+ int i, num_colors;
+ const char * colorspace_p;
+
+ switch ( ctx->photometric )
+ {
+ case PHOTOMETRIC_SEPARATED:
+ colorspace_p = "CMYK";
+ break;
+
+ case PHOTOMETRIC_RGB:
+ colorspace_p = "RGB";
+ break;
+
+ default:
+ colorspace_p = "Gray";
+ }
+
+ /*
+ * Set up PostScript Level 2 colorspace according to
+ * section 4.8 in the PostScript refenence manual.
+ */
+ fputs("% PostScript Level 2 only.\n", ctx->fd);
+ if (ctx->photometric != PHOTOMETRIC_PALETTE) {
+ if (ctx->photometric == PHOTOMETRIC_YCBCR) {
+ /* MORE CODE HERE */
+ }
+ fprintf(ctx->fd, "/Device%s setcolorspace\n", colorspace_p );
+ return;
+ }
+
+ /*
+ * Set up an indexed/palette colorspace
+ */
+ num_colors = (1 << ctx->bitspersample);
+ if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap)) {
+ TIFFError(ctx->filename,
+ "Palette image w/o \"Colormap\" tag");
+ return;
+ }
+ if (checkcmap(ctx, tif, num_colors, rmap, gmap, bmap) == 16) {
+ /*
+ * Convert colormap to 8-bits values.
+ */
+#define CVT(x) (((x) * 255) / ((1L<<16)-1))
+ for (i = 0; i < num_colors; i++) {
+ rmap[i] = CVT(rmap[i]);
+ gmap[i] = CVT(gmap[i]);
+ bmap[i] = CVT(bmap[i]);
+ }
+#undef CVT
+ }
+ fprintf(ctx->fd, "[ /Indexed /DeviceRGB %d", num_colors - 1);
+ if (ctx->ascii85) {
+ Ascii85Init(ctx);
+ fputs("\n<~", ctx->fd);
+ ctx->ascii85breaklen -= 2;
+ } else
+ fputs(" <", ctx->fd);
+ for (i = 0; i < num_colors; i++) {
+ if (ctx->ascii85) {
+ Ascii85Put(ctx, (unsigned char)rmap[i]);
+ Ascii85Put(ctx, (unsigned char)gmap[i]);
+ Ascii85Put(ctx, (unsigned char)bmap[i]);
+ } else {
+ fputs((i % 8) ? " " : "\n ", ctx->fd);
+ fprintf(ctx->fd, "%02x%02x%02x",
+ rmap[i], gmap[i], bmap[i]);
+ }
+ }
+ if (ctx->ascii85)
+ Ascii85Flush(ctx);
+ else
+ fputs(">\n", ctx->fd);
+ fputs("] setcolorspace\n", ctx->fd);
+}
+
+static int
+PS_Lvl2ImageDict(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h)
+{
+ int use_rawdata;
+ uint32 tile_width, tile_height;
+ uint16 predictor, minsamplevalue, maxsamplevalue;
+ int repeat_count;
+ char im_h[64], im_x[64], im_y[64];
+ char * imageOp = "image";
+
+ if ( ctx->useImagemask && (ctx->bitspersample == 1) )
+ imageOp = "imagemask";
+
+ (void)strcpy(im_x, "0");
+ (void)sprintf(im_y, "%lu", (long) h);
+ (void)sprintf(im_h, "%lu", (long) h);
+ tile_width = w;
+ tile_height = h;
+ if (TIFFIsTiled(tif)) {
+ repeat_count = TIFFNumberOfTiles(tif);
+ TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
+ if (tile_width > w || tile_height > h ||
+ (w % tile_width) != 0 || (h % tile_height != 0)) {
+ /*
+ * The tiles does not fit image width and height.
+ * Set up a clip rectangle for the image unit square.
+ */
+ fputs("0 0 1 1 rectclip\n", ctx->fd);
+ }
+ if (tile_width < w) {
+ fputs("/im_x 0 def\n", ctx->fd);
+ (void)strcpy(im_x, "im_x neg");
+ }
+ if (tile_height < h) {
+ fputs("/im_y 0 def\n", ctx->fd);
+ (void)sprintf(im_y, "%lu im_y sub", (unsigned long) h);
+ }
+ } else {
+ repeat_count = ctx->tf_numberstrips;
+ tile_height = ctx->tf_rowsperstrip;
+ if (tile_height > h)
+ tile_height = h;
+ if (repeat_count > 1) {
+ fputs("/im_y 0 def\n", ctx->fd);
+ fprintf(ctx->fd, "/im_h %lu def\n",
+ (unsigned long) tile_height);
+ (void)strcpy(im_h, "im_h");
+ (void)sprintf(im_y, "%lu im_y sub", (unsigned long) h);
+ }
+ }
+
+ /*
+ * Output start of exec block
+ */
+ fputs("{ % exec\n", ctx->fd);
+
+ if (repeat_count > 1)
+ fprintf(ctx->fd, "%d { %% repeat\n", repeat_count);
+
+ /*
+ * Output filter options and image dictionary.
+ */
+ if (ctx->ascii85)
+ fputs(" /im_stream currentfile /ASCII85Decode filter def\n",
+ ctx->fd);
+ fputs(" <<\n", ctx->fd);
+ fputs(" /ImageType 1\n", ctx->fd);
+ fprintf(ctx->fd, " /Width %lu\n", (unsigned long) tile_width);
+ /*
+ * Workaround for some software that may crash when last strip
+ * of image contains fewer number of scanlines than specified
+ * by the `/Height' variable. So for stripped images with multiple
+ * strips we will set `/Height' as `im_h', because one is
+ * recalculated for each strip - including the (smaller) final strip.
+ * For tiled images and images with only one strip `/Height' will
+ * contain number of scanlines in tile (or image height in case of
+ * one-stripped image).
+ */
+ if (TIFFIsTiled(tif) || ctx->tf_numberstrips == 1)
+ fprintf(ctx->fd, " /Height %lu\n", (unsigned long) tile_height);
+ else
+ fprintf(ctx->fd, " /Height im_h\n");
+
+ if (ctx->planarconfiguration == PLANARCONFIG_SEPARATE && ctx->samplesperpixel > 1)
+ fputs(" /MultipleDataSources true\n", ctx->fd);
+ fprintf(ctx->fd, " /ImageMatrix [ %lu 0 0 %ld %s %s ]\n",
+ (unsigned long) w, - (long)h, im_x, im_y);
+ fprintf(ctx->fd, " /BitsPerComponent %d\n", ctx->bitspersample);
+ fprintf(ctx->fd, " /Ctx->Interpolate %s\n", ctx->interpolate ? "true" : "false");
+
+ switch (ctx->samplesperpixel - ctx->extrasamples) {
+ case 1:
+ switch (ctx->photometric) {
+ case PHOTOMETRIC_MINISBLACK:
+ fputs(" /Decode [0 1]\n", ctx->fd);
+ break;
+ case PHOTOMETRIC_MINISWHITE:
+ switch (ctx->compression) {
+ case COMPRESSION_CCITTRLE:
+ case COMPRESSION_CCITTRLEW:
+ case COMPRESSION_CCITTFAX3:
+ case COMPRESSION_CCITTFAX4:
+ /*
+ * Manage inverting with /Blackis1 flag
+ * since there migth be uncompressed parts
+ */
+ fputs(" /Decode [0 1]\n", ctx->fd);
+ break;
+ default:
+ /*
+ * ERROR...
+ */
+ fputs(" /Decode [1 0]\n", ctx->fd);
+ break;
+ }
+ break;
+ case PHOTOMETRIC_PALETTE:
+ TIFFGetFieldDefaulted(tif, TIFFTAG_MINSAMPLEVALUE,
+ &minsamplevalue);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_MAXSAMPLEVALUE,
+ &maxsamplevalue);
+ fprintf(ctx->fd, " /Decode [%u %u]\n",
+ minsamplevalue, maxsamplevalue);
+ break;
+ default:
+ /*
+ * ERROR ?
+ */
+ fputs(" /Decode [0 1]\n", ctx->fd);
+ break;
+ }
+ break;
+ case 3:
+ switch (ctx->photometric) {
+ case PHOTOMETRIC_RGB:
+ fputs(" /Decode [0 1 0 1 0 1]\n", ctx->fd);
+ break;
+ case PHOTOMETRIC_MINISWHITE:
+ case PHOTOMETRIC_MINISBLACK:
+ default:
+ /*
+ * ERROR??
+ */
+ fputs(" /Decode [0 1 0 1 0 1]\n", ctx->fd);
+ break;
+ }
+ break;
+ case 4:
+ /*
+ * ERROR??
+ */
+ fputs(" /Decode [0 1 0 1 0 1 0 1]\n", ctx->fd);
+ break;
+ }
+ fputs(" /DataSource", ctx->fd);
+ if (ctx->planarconfiguration == PLANARCONFIG_SEPARATE &&
+ ctx->samplesperpixel > 1)
+ fputs(" [", ctx->fd);
+ if (ctx->ascii85)
+ fputs(" im_stream", ctx->fd);
+ else
+ fputs(" currentfile /ASCIIHexDecode filter", ctx->fd);
+
+ use_rawdata = TRUE;
+ switch (ctx->compression) {
+ case COMPRESSION_NONE: /* 1: uncompressed */
+ break;
+ case COMPRESSION_CCITTRLE: /* 2: CCITT modified Huffman RLE */
+ case COMPRESSION_CCITTRLEW: /* 32771: #1 w/ word alignment */
+ case COMPRESSION_CCITTFAX3: /* 3: CCITT Group 3 fax encoding */
+ case COMPRESSION_CCITTFAX4: /* 4: CCITT Group 4 fax encoding */
+ fputs("\n\t<<\n", ctx->fd);
+ if (ctx->compression == COMPRESSION_CCITTFAX3) {
+ uint32 g3_options;
+
+ fputs("\t /EndOfLine true\n", ctx->fd);
+ fputs("\t /EndOfBlock false\n", ctx->fd);
+ if (!TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS,
+ &g3_options))
+ g3_options = 0;
+ if (g3_options & GROUP3OPT_2DENCODING)
+ fprintf(ctx->fd, "\t /K %s\n", im_h);
+ if (g3_options & GROUP3OPT_UNCOMPRESSED)
+ fputs("\t /Uncompressed true\n", ctx->fd);
+ if (g3_options & GROUP3OPT_FILLBITS)
+ fputs("\t /EncodedByteAlign true\n", ctx->fd);
+ }
+ if (ctx->compression == COMPRESSION_CCITTFAX4) {
+ uint32 g4_options;
+
+ fputs("\t /K -1\n", ctx->fd);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_GROUP4OPTIONS,
+ &g4_options);
+ if (g4_options & GROUP4OPT_UNCOMPRESSED)
+ fputs("\t /Uncompressed true\n", ctx->fd);
+ }
+ if (!(tile_width == w && w == 1728U))
+ fprintf(ctx->fd, "\t /Columns %lu\n",
+ (unsigned long) tile_width);
+ fprintf(ctx->fd, "\t /Rows %s\n", im_h);
+ if (ctx->compression == COMPRESSION_CCITTRLE ||
+ ctx->compression == COMPRESSION_CCITTRLEW) {
+ fputs("\t /EncodedByteAlign true\n", ctx->fd);
+ fputs("\t /EndOfBlock false\n", ctx->fd);
+ }
+ if (ctx->photometric == PHOTOMETRIC_MINISBLACK)
+ fputs("\t /BlackIs1 true\n", ctx->fd);
+ fprintf(ctx->fd, "\t>> /CCITTFaxDecode filter");
+ break;
+ case COMPRESSION_LZW: /* 5: Lempel-Ziv & Welch */
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PREDICTOR, &predictor);
+ if (predictor == 2) {
+ fputs("\n\t<<\n", ctx->fd);
+ fprintf(ctx->fd, "\t /Predictor %u\n", predictor);
+ fprintf(ctx->fd, "\t /Columns %lu\n",
+ (unsigned long) tile_width);
+ fprintf(ctx->fd, "\t /Colors %u\n", ctx->samplesperpixel);
+ fprintf(ctx->fd, "\t /BitsPerComponent %u\n",
+ ctx->bitspersample);
+ fputs("\t>>", ctx->fd);
+ }
+ fputs(" /LZWDecode filter", ctx->fd);
+ break;
+ case COMPRESSION_DEFLATE: /* 5: ZIP */
+ case COMPRESSION_ADOBE_DEFLATE:
+ if ( ctx->level3 ) {
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PREDICTOR, &predictor);
+ if (predictor > 1) {
+ fprintf(ctx->fd, "\t %% PostScript Level 3 only.");
+ fputs("\n\t<<\n", ctx->fd);
+ fprintf(ctx->fd, "\t /Predictor %u\n", predictor);
+ fprintf(ctx->fd, "\t /Columns %lu\n",
+ (unsigned long) tile_width);
+ fprintf(ctx->fd, "\t /Colors %u\n", ctx->samplesperpixel);
+ fprintf(ctx->fd, "\t /BitsPerComponent %u\n",
+ ctx->bitspersample);
+ fputs("\t>>", ctx->fd);
+ }
+ fputs(" /FlateDecode filter", ctx->fd);
+ } else {
+ use_rawdata = FALSE ;
+ }
+ break;
+ case COMPRESSION_PACKBITS: /* 32773: Macintosh RLE */
+ fputs(" /RunLengthDecode filter", ctx->fd);
+ use_rawdata = TRUE;
+ break;
+ case COMPRESSION_OJPEG: /* 6: !6.0 JPEG */
+ case COMPRESSION_JPEG: /* 7: %JPEG DCT ctx->compression */
+#ifdef notdef
+ /*
+ * Code not tested yet
+ */
+ fputs(" /DCTDecode filter", ctx->fd);
+ use_rawdata = TRUE;
+#else
+ use_rawdata = FALSE;
+#endif
+ break;
+ case COMPRESSION_NEXT: /* 32766: NeXT 2-bit RLE */
+ case COMPRESSION_THUNDERSCAN: /* 32809: ThunderScan RLE */
+ case COMPRESSION_PIXARFILM: /* 32908: Pixar companded 10bit LZW */
+ case COMPRESSION_JBIG: /* 34661: ISO JBIG */
+ use_rawdata = FALSE;
+ break;
+ case COMPRESSION_SGILOG: /* 34676: SGI LogL or LogLuv */
+ case COMPRESSION_SGILOG24: /* 34677: SGI 24-bit LogLuv */
+ use_rawdata = FALSE;
+ break;
+ default:
+ /*
+ * ERROR...
+ */
+ use_rawdata = FALSE;
+ break;
+ }
+ if (ctx->planarconfiguration == PLANARCONFIG_SEPARATE &&
+ ctx->samplesperpixel > 1) {
+ uint16 i;
+
+ /*
+ * NOTE: This code does not work yet...
+ */
+ for (i = 1; i < ctx->samplesperpixel; i++)
+ fputs(" dup", ctx->fd);
+ fputs(" ]", ctx->fd);
+ }
+
+ fprintf( ctx->fd, "\n >> %s\n", imageOp );
+ if (ctx->ascii85)
+ fputs(" im_stream status { im_stream flushfile } if\n", ctx->fd);
+ if (repeat_count > 1) {
+ if (tile_width < w) {
+ fprintf(ctx->fd, " /im_x im_x %lu add def\n",
+ (unsigned long) tile_width);
+ if (tile_height < h) {
+ fprintf(ctx->fd, " im_x %lu ge {\n",
+ (unsigned long) w);
+ fputs(" /im_x 0 def\n", ctx->fd);
+ fprintf(ctx->fd, " /im_y im_y %lu add def\n",
+ (unsigned long) tile_height);
+ fputs(" } if\n", ctx->fd);
+ }
+ }
+ if (tile_height < h) {
+ if (tile_width >= w) {
+ fprintf(ctx->fd, " /im_y im_y %lu add def\n",
+ (unsigned long) tile_height);
+ if (!TIFFIsTiled(tif)) {
+ fprintf(ctx->fd, " /im_h %lu im_y sub",
+ (unsigned long) h);
+ fprintf(ctx->fd, " dup %lu gt { pop",
+ (unsigned long) tile_height);
+ fprintf(ctx->fd, " %lu } if def\n",
+ (unsigned long) tile_height);
+ }
+ }
+ }
+ fputs("} repeat\n", ctx->fd);
+ }
+ /*
+ * End of exec function
+ */
+ fputs("}\n", ctx->fd);
+
+ return(use_rawdata);
+}
+
+#define MAXLINE 36
+
+static int
+PS_Lvl2page(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h)
+{
+ uint16 fillorder;
+ int use_rawdata, tiled_image, breaklen = MAXLINE;
+ uint32 chunk_no, num_chunks, *bc;
+ unsigned char *buf_data, *cp;
+ tsize_t chunk_size, byte_count;
+
+#if defined( EXP_ASCII85ENCODER )
+ int ascii85_l; /* Length, in bytes, of ascii85_p[] data */
+ uint8 * ascii85_p = 0; /* Holds ASCII85 encoded data */
+#endif
+
+ PS_Lvl2colorspace(ctx, tif);
+ use_rawdata = PS_Lvl2ImageDict(ctx, tif, w, h);
+
+/* See http://bugzilla.remotesensing.org/show_bug.cgi?id=80 */
+#ifdef ENABLE_BROKEN_BEGINENDDATA
+ fputs("%%BeginData:\n", ctx->fd);
+#endif
+ fputs("exec\n", ctx->fd);
+
+ tiled_image = TIFFIsTiled(tif);
+ if (tiled_image) {
+ num_chunks = TIFFNumberOfTiles(tif);
+ TIFFGetField(tif, TIFFTAG_TILEBYTECOUNTS, &bc);
+ } else {
+ num_chunks = TIFFNumberOfStrips(tif);
+ TIFFGetField(tif, TIFFTAG_STRIPBYTECOUNTS, &bc);
+ }
+
+ if (use_rawdata) {
+ chunk_size = (tsize_t) bc[0];
+ for (chunk_no = 1; chunk_no < num_chunks; chunk_no++)
+ if ((tsize_t) bc[chunk_no] > chunk_size)
+ chunk_size = (tsize_t) bc[chunk_no];
+ } else {
+ if (tiled_image)
+ chunk_size = TIFFTileSize(tif);
+ else
+ chunk_size = TIFFStripSize(tif);
+ }
+ buf_data = (unsigned char *)_TIFFmalloc(chunk_size);
+ if (!buf_data) {
+ TIFFError(ctx->filename, "Can't alloc %u bytes for %s.",
+ chunk_size, tiled_image ? "tiles" : "strips");
+ return(FALSE);
+ }
+
+#if defined( EXP_ASCII85ENCODER )
+ if ( ctx->ascii85 ) {
+ /*
+ * Allocate a buffer to hold the ASCII85 encoded data. Note
+ * that it is allocated with sufficient room to hold the
+ * encoded data (5*chunk_size/4) plus the EOD marker (+8)
+ * and formatting line breaks. The line breaks are more
+ * than taken care of by using 6*chunk_size/4 rather than
+ * 5*chunk_size/4.
+ */
+
+ ascii85_p = _TIFFmalloc( (chunk_size+(chunk_size/2)) + 8 );
+
+ if ( !ascii85_p ) {
+ _TIFFfree( buf_data );
+
+ TIFFError( ctx->filename,
+ "Cannot allocate ASCII85 encoding buffer." );
+ return ( FALSE );
+ }
+ }
+#endif
+
+ TIFFGetFieldDefaulted(tif, TIFFTAG_FILLORDER, &fillorder);
+ for (chunk_no = 0; chunk_no < num_chunks; chunk_no++) {
+ if (ctx->ascii85)
+ Ascii85Init(ctx);
+ else
+ breaklen = MAXLINE;
+ if (use_rawdata) {
+ if (tiled_image)
+ byte_count = TIFFReadRawTile(tif, chunk_no,
+ buf_data, chunk_size);
+ else
+ byte_count = TIFFReadRawStrip(tif, chunk_no,
+ buf_data, chunk_size);
+ if (fillorder == FILLORDER_LSB2MSB)
+ TIFFReverseBits(buf_data, byte_count);
+ } else {
+ if (tiled_image)
+ byte_count = TIFFReadEncodedTile(tif,
+ chunk_no, buf_data,
+ chunk_size);
+ else
+ byte_count = TIFFReadEncodedStrip(tif,
+ chunk_no, buf_data,
+ chunk_size);
+ }
+ if (byte_count < 0) {
+ TIFFError(ctx->filename, "Can't read %s %d.",
+ tiled_image ? "tile" : "strip", chunk_no);
+ if (ctx->ascii85)
+ Ascii85Put(ctx, '\0');
+ }
+ /*
+ * For images with ctx->alpha, matte against a white background;
+ * i.e. Cback * (1 - Aimage) where Cback = 1. We will fill the
+ * lower part of the buffer with the modified values.
+ *
+ * XXX: needs better solution
+ */
+ if (ctx->alpha) {
+ int adjust, i, j = 0;
+ int ncomps = ctx->samplesperpixel - ctx->extrasamples;
+ for (i = 0; i < byte_count; i+=ctx->samplesperpixel) {
+ adjust = 255 - buf_data[i + ncomps];
+ switch (ncomps) {
+ case 1:
+ buf_data[j++] = buf_data[i] + adjust;
+ break;
+ case 2:
+ buf_data[j++] = buf_data[i] + adjust;
+ buf_data[j++] = buf_data[i+1] + adjust;
+ break;
+ case 3:
+ buf_data[j++] = buf_data[i] + adjust;
+ buf_data[j++] = buf_data[i+1] + adjust;
+ buf_data[j++] = buf_data[i+2] + adjust;
+ break;
+ }
+ }
+ byte_count -= j;
+ }
+
+ if (ctx->ascii85) {
+#if defined( EXP_ASCII85ENCODER )
+ ascii85_l = Ascii85EncodeBlock(ctx, ascii85_p, 1,
+ buf_data, byte_count);
+
+ if ( ascii85_l > 0 )
+ fwrite( ascii85_p, ascii85_l, 1, ctx->fd );
+#else
+ for (cp = buf_data; byte_count > 0; byte_count--)
+ Ascii85Put(ctx, *cp++);
+#endif
+ }
+ else
+ {
+ for (cp = buf_data; byte_count > 0; byte_count--) {
+ putc(hex[((*cp)>>4)&0xf], ctx->fd);
+ putc(hex[(*cp)&0xf], ctx->fd);
+ cp++;
+
+ if (--breaklen <= 0) {
+ putc('\n', ctx->fd);
+ breaklen = MAXLINE;
+ }
+ }
+ }
+
+ if ( !ctx->ascii85 ) {
+ if ( ctx->level2 || ctx->level3 )
+ putc( '>', ctx->fd );
+ putc('\n', ctx->fd);
+ }
+#if !defined( EXP_ASCII85ENCODER )
+ else
+ Ascii85Flush(ctx);
+#endif
+ }
+
+#if defined( EXP_ASCII85ENCODER )
+ if ( ascii85_p )
+ _TIFFfree( ascii85_p );
+#endif
+ _TIFFfree(buf_data);
+#ifdef ENABLE_BROKEN_BEGINENDDATA
+ fputs("%%EndData\n", ctx->fd);
+#endif
+ return(TRUE);
+}
+
+void
+PSpage(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h)
+{
+ char *imageOp = "image";
+
+ if ( ctx->useImagemask && (ctx->bitspersample == 1) )
+ imageOp = "imagemask";
+
+ if ((ctx->level2 || ctx->level3) && PS_Lvl2page(ctx, tif, w, h))
+ return;
+ ctx->ps_bytesperrow = ctx->tf_bytesperrow - (ctx->extrasamples * ctx->bitspersample / 8)*w;
+ switch (ctx->photometric) {
+ case PHOTOMETRIC_RGB:
+ if (ctx->planarconfiguration == PLANARCONFIG_CONTIG) {
+ fprintf(ctx->fd, "%s", RGBcolorimage);
+ PSColorContigPreamble(ctx, w, h, 3);
+ PSDataColorContig(ctx, tif, w, h, 3);
+ } else {
+ PSColorSeparatePreamble(ctx, w, h, 3);
+ PSDataColorSeparate(ctx, tif, w, h, 3);
+ }
+ break;
+ case PHOTOMETRIC_SEPARATED:
+ /* XXX should emit CMYKcolorimage */
+ if (ctx->planarconfiguration == PLANARCONFIG_CONTIG) {
+ PSColorContigPreamble(ctx, w, h, 4);
+ PSDataColorContig(ctx, tif, w, h, 4);
+ } else {
+ PSColorSeparatePreamble(ctx, w, h, 4);
+ PSDataColorSeparate(ctx, tif, w, h, 4);
+ }
+ break;
+ case PHOTOMETRIC_PALETTE:
+ fprintf(ctx->fd, "%s", RGBcolorimage);
+ PhotoshopBanner(ctx, w, h, 1, 3, "false 3 colorimage");
+ fprintf(ctx->fd, "/scanLine %ld string def\n",
+ (long) ctx->ps_bytesperrow * 3L);
+ fprintf(ctx->fd, "%lu %lu 8\n",
+ (unsigned long) w, (unsigned long) h);
+ fprintf(ctx->fd, "[%lu 0 0 -%lu 0 %lu]\n",
+ (unsigned long) w, (unsigned long) h,
+ (unsigned long) h);
+ fprintf(ctx->fd,
+ "{currentfile scanLine readhexstring pop} bind\n");
+ fprintf(ctx->fd, "false 3 colorimage\n");
+ PSDataPalette(ctx, tif, w, h);
+ break;
+ case PHOTOMETRIC_MINISBLACK:
+ case PHOTOMETRIC_MINISWHITE:
+ PhotoshopBanner(ctx, w, h, 1, 1, imageOp);
+ fprintf(ctx->fd, "/scanLine %ld string def\n",
+ (long) ctx->ps_bytesperrow);
+ fprintf(ctx->fd, "%lu %lu %d\n",
+ (unsigned long) w, (unsigned long) h, ctx->bitspersample);
+ fprintf(ctx->fd, "[%lu 0 0 -%lu 0 %lu]\n",
+ (unsigned long) w, (unsigned long) h, (unsigned long) h);
+ fprintf(ctx->fd,
+ "{currentfile scanLine readhexstring pop} bind\n");
+ fprintf(ctx->fd, "%s\n", imageOp);
+ PSDataBW(ctx, tif, w, h);
+ break;
+ }
+ putc('\n', ctx->fd);
+}
+
+void
+PSColorContigPreamble(TIFF2PSContext* ctx, uint32 w, uint32 h, int nc)
+{
+ ctx->ps_bytesperrow = nc * (ctx->tf_bytesperrow / ctx->samplesperpixel);
+ PhotoshopBanner(ctx, w, h, 1, nc, "false %d colorimage");
+ fprintf(ctx->fd, "/line %ld string def\n", (long) ctx->ps_bytesperrow);
+ fprintf(ctx->fd, "%lu %lu %d\n",
+ (unsigned long) w, (unsigned long) h, ctx->bitspersample);
+ fprintf(ctx->fd, "[%lu 0 0 -%lu 0 %lu]\n",
+ (unsigned long) w, (unsigned long) h, (unsigned long) h);
+ fprintf(ctx->fd, "{currentfile line readhexstring pop} bind\n");
+ fprintf(ctx->fd, "false %d colorimage\n", nc);
+}
+
+void
+PSColorSeparatePreamble(TIFF2PSContext* ctx, uint32 w, uint32 h, int nc)
+{
+ int i;
+
+ PhotoshopBanner(ctx, w, h, ctx->ps_bytesperrow, nc, "true %d colorimage");
+ for (i = 0; i < nc; i++)
+ fprintf(ctx->fd, "/line%d %ld string def\n",
+ i, (long) ctx->ps_bytesperrow);
+ fprintf(ctx->fd, "%lu %lu %d\n",
+ (unsigned long) w, (unsigned long) h, ctx->bitspersample);
+ fprintf(ctx->fd, "[%lu 0 0 -%lu 0 %lu] \n",
+ (unsigned long) w, (unsigned long) h, (unsigned long) h);
+ for (i = 0; i < nc; i++)
+ fprintf(ctx->fd, "{currentfile line%d readhexstring pop}bind\n", i);
+ fprintf(ctx->fd, "true %d colorimage\n", nc);
+}
+
+#define DOBREAK(len, howmany, fd) \
+ if (((len) -= (howmany)) <= 0) { \
+ putc('\n', fd); \
+ (len) = MAXLINE-(howmany); \
+ }
+#define PUTHEX(c,fd) putc(hex[((c)>>4)&0xf],fd); putc(hex[(c)&0xf],fd)
+
+void
+PSDataColorContig(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h, int nc)
+{
+ uint32 row;
+ int breaklen = MAXLINE, cc, es = ctx->samplesperpixel - nc;
+ unsigned char *tf_buf;
+ unsigned char *cp, c;
+
+ (void) w;
+ tf_buf = (unsigned char *) _TIFFmalloc(ctx->tf_bytesperrow);
+ if (tf_buf == NULL) {
+ TIFFError(ctx->filename, "No space for scanline buffer");
+ return;
+ }
+ for (row = 0; row < h; row++) {
+ if (TIFFReadScanline(tif, tf_buf, row, 0) < 0)
+ break;
+ cp = tf_buf;
+ if (ctx->alpha) {
+ int adjust;
+ cc = 0;
+ for (; cc < ctx->tf_bytesperrow; cc += ctx->samplesperpixel) {
+ DOBREAK(breaklen, nc, ctx->fd);
+ /*
+ * For images with ctx->alpha, matte against
+ * a white background; i.e.
+ * Cback * (1 - Aimage)
+ * where Cback = 1.
+ */
+ adjust = 255 - cp[nc];
+ switch (nc) {
+ case 4: c = *cp++ + adjust; PUTHEX(c,ctx->fd);
+ case 3: c = *cp++ + adjust; PUTHEX(c,ctx->fd);
+ case 2: c = *cp++ + adjust; PUTHEX(c,ctx->fd);
+ case 1: c = *cp++ + adjust; PUTHEX(c,ctx->fd);
+ }
+ cp += es;
+ }
+ } else {
+ cc = 0;
+ for (; cc < ctx->tf_bytesperrow; cc += ctx->samplesperpixel) {
+ DOBREAK(breaklen, nc, ctx->fd);
+ switch (nc) {
+ case 4: c = *cp++; PUTHEX(c,ctx->fd);
+ case 3: c = *cp++; PUTHEX(c,ctx->fd);
+ case 2: c = *cp++; PUTHEX(c,ctx->fd);
+ case 1: c = *cp++; PUTHEX(c,ctx->fd);
+ }
+ cp += es;
+ }
+ }
+ }
+ _TIFFfree((char *) tf_buf);
+}
+
+void
+PSDataColorSeparate(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h, int nc)
+{
+ uint32 row;
+ int breaklen = MAXLINE, cc;
+ tsample_t s, maxs;
+ unsigned char *tf_buf;
+ unsigned char *cp, c;
+
+ (void) w;
+ tf_buf = (unsigned char *) _TIFFmalloc(ctx->tf_bytesperrow);
+ if (tf_buf == NULL) {
+ TIFFError(ctx->filename, "No space for scanline buffer");
+ return;
+ }
+ maxs = (ctx->samplesperpixel > nc ? nc : ctx->samplesperpixel);
+ for (row = 0; row < h; row++) {
+ for (s = 0; s < maxs; s++) {
+ if (TIFFReadScanline(tif, tf_buf, row, s) < 0)
+ break;
+ for (cp = tf_buf, cc = 0; cc < ctx->tf_bytesperrow; cc++) {
+ DOBREAK(breaklen, 1, ctx->fd);
+ c = *cp++;
+ PUTHEX(c,ctx->fd);
+ }
+ }
+ }
+ _TIFFfree((char *) tf_buf);
+}
+
+#define PUTRGBHEX(c,fd) \
+ PUTHEX(rmap[c],fd); PUTHEX(gmap[c],fd); PUTHEX(bmap[c],fd)
+
+void
+PSDataPalette(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h)
+{
+ uint16 *rmap, *gmap, *bmap;
+ uint32 row;
+ int breaklen = MAXLINE, cc, nc;
+ unsigned char *tf_buf;
+ unsigned char *cp, c;
+
+ (void) w;
+ if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap)) {
+ TIFFError(ctx->filename, "Palette image w/o \"Colormap\" tag");
+ return;
+ }
+ switch (ctx->bitspersample) {
+ case 8: case 4: case 2: case 1:
+ break;
+ default:
+ TIFFError(ctx->filename, "Depth %d not supported", ctx->bitspersample);
+ return;
+ }
+ nc = 3 * (8 / ctx->bitspersample);
+ tf_buf = (unsigned char *) _TIFFmalloc(ctx->tf_bytesperrow);
+ if (tf_buf == NULL) {
+ TIFFError(ctx->filename, "No space for scanline buffer");
+ return;
+ }
+ if (checkcmap(ctx, tif, 1<<ctx->bitspersample, rmap, gmap, bmap) == 16) {
+ int i;
+#define CVT(x) ((unsigned short) (((x) * 255) / ((1U<<16)-1)))
+ for (i = (1<<ctx->bitspersample)-1; i >= 0; i--) {
+ rmap[i] = CVT(rmap[i]);
+ gmap[i] = CVT(gmap[i]);
+ bmap[i] = CVT(bmap[i]);
+ }
+#undef CVT
+ }
+ for (row = 0; row < h; row++) {
+ if (TIFFReadScanline(tif, tf_buf, row, 0) < 0)
+ break;
+ for (cp = tf_buf, cc = 0; cc < ctx->tf_bytesperrow; cc++) {
+ DOBREAK(breaklen, nc, ctx->fd);
+ switch (ctx->bitspersample) {
+ case 8:
+ c = *cp++; PUTRGBHEX(c, ctx->fd);
+ break;
+ case 4:
+ c = *cp++; PUTRGBHEX(c&0xf, ctx->fd);
+ c >>= 4; PUTRGBHEX(c, ctx->fd);
+ break;
+ case 2:
+ c = *cp++; PUTRGBHEX(c&0x3, ctx->fd);
+ c >>= 2; PUTRGBHEX(c&0x3, ctx->fd);
+ c >>= 2; PUTRGBHEX(c&0x3, ctx->fd);
+ c >>= 2; PUTRGBHEX(c, ctx->fd);
+ break;
+ case 1:
+ c = *cp++; PUTRGBHEX(c&0x1, ctx->fd);
+ c >>= 1; PUTRGBHEX(c&0x1, ctx->fd);
+ c >>= 1; PUTRGBHEX(c&0x1, ctx->fd);
+ c >>= 1; PUTRGBHEX(c&0x1, ctx->fd);
+ c >>= 1; PUTRGBHEX(c&0x1, ctx->fd);
+ c >>= 1; PUTRGBHEX(c&0x1, ctx->fd);
+ c >>= 1; PUTRGBHEX(c&0x1, ctx->fd);
+ c >>= 1; PUTRGBHEX(c, ctx->fd);
+ break;
+ }
+ }
+ }
+ _TIFFfree((char *) tf_buf);
+}
+
+void
+PSDataBW(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h)
+{
+ int breaklen = MAXLINE;
+ unsigned char* tf_buf;
+ unsigned char* cp;
+ tsize_t stripsize = TIFFStripSize(tif);
+ tstrip_t s;
+
+#if defined( EXP_ASCII85ENCODER )
+ int ascii85_l; /* Length, in bytes, of ascii85_p[] data */
+ uint8 *ascii85_p = 0; /* Holds ASCII85 encoded data */
+#endif
+
+ (void) w; (void) h;
+ tf_buf = (unsigned char *) _TIFFmalloc(stripsize);
+ memset(tf_buf, 0, stripsize);
+ if (tf_buf == NULL) {
+ TIFFError(ctx->filename, "No space for scanline buffer");
+ return;
+ }
+
+#if defined( EXP_ASCII85ENCODER )
+ if ( ctx->ascii85 ) {
+ /*
+ * Allocate a buffer to hold the ASCII85 encoded data. Note
+ * that it is allocated with sufficient room to hold the
+ * encoded data (5*stripsize/4) plus the EOD marker (+8)
+ * and formatting line breaks. The line breaks are more
+ * than taken care of by using 6*stripsize/4 rather than
+ * 5*stripsize/4.
+ */
+
+ ascii85_p = _TIFFmalloc( (stripsize+(stripsize/2)) + 8 );
+
+ if ( !ascii85_p ) {
+ _TIFFfree( tf_buf );
+
+ TIFFError( ctx->filename,
+ "Cannot allocate ASCII85 encoding buffer." );
+ return;
+ }
+ }
+#endif
+
+ if (ctx->ascii85)
+ Ascii85Init(ctx);
+
+ for (s = 0; s < TIFFNumberOfStrips(tif); s++) {
+ int cc = TIFFReadEncodedStrip(tif, s, tf_buf, stripsize);
+ if (cc < 0) {
+ TIFFError(ctx->filename, "Can't read strip");
+ break;
+ }
+ cp = tf_buf;
+ if (ctx->photometric == PHOTOMETRIC_MINISWHITE) {
+ for (cp += cc; --cp >= tf_buf;)
+ *cp = ~*cp;
+ cp++;
+ }
+ if (ctx->ascii85) {
+#if defined( EXP_ASCII85ENCODER )
+ if (ctx->alpha) {
+ int adjust, i;
+ for (i = 0; i < cc; i+=2) {
+ adjust = 255 - cp[i + 1];
+ cp[i / 2] = cp[i] + adjust;
+ }
+ cc /= 2;
+ }
+
+ ascii85_l = Ascii85EncodeBlock(ctx, ascii85_p, 1, cp,
+ cc);
+
+ if ( ascii85_l > 0 )
+ fwrite( ascii85_p, ascii85_l, 1, ctx->fd );
+#else
+ while (cc-- > 0)
+ Ascii85Put(ctx, *cp++);
+#endif /* EXP_ASCII85_ENCODER */
+ } else {
+ unsigned char c;
+
+ if (ctx->alpha) {
+ int adjust;
+ while (cc-- > 0) {
+ DOBREAK(breaklen, 1, ctx->fd);
+ /*
+ * For images with ctx->alpha, matte against
+ * a white background; i.e.
+ * Cback * (1 - Aimage)
+ * where Cback = 1.
+ */
+ adjust = 255 - cp[1];
+ c = *cp++ + adjust; PUTHEX(c,ctx->fd);
+ cp++, cc--;
+ }
+ } else {
+ while (cc-- > 0) {
+ c = *cp++;
+ DOBREAK(breaklen, 1, ctx->fd);
+ PUTHEX(c, ctx->fd);
+ }
+ }
+ }
+ }
+
+ if ( !ctx->ascii85 )
+ {
+ if ( ctx->level2 || ctx->level3)
+ fputs(">\n", ctx->fd);
+ }
+#if !defined( EXP_ASCII85ENCODER )
+ else
+ Ascii85Flush(ctx);
+#else
+ if ( ascii85_p )
+ _TIFFfree( ascii85_p );
+#endif
+
+ _TIFFfree(tf_buf);
+}
+
+static void
+Ascii85Init(TIFF2PSContext *ctx)
+{
+ ctx->ascii85breaklen = 2*MAXLINE;
+ ctx->ascii85count = 0;
+}
+
+static void
+Ascii85Encode(unsigned char* raw, char *buf)
+{
+ uint32 word;
+
+ word = (((raw[0]<<8)+raw[1])<<16) + (raw[2]<<8) + raw[3];
+ if (word != 0L) {
+ uint32 q;
+ uint16 w1;
+
+ q = word / (85L*85*85*85); /* actually only a byte */
+ buf[0] = (char) (q + '!');
+
+ word -= q * (85L*85*85*85); q = word / (85L*85*85);
+ buf[1] = (char) (q + '!');
+
+ word -= q * (85L*85*85); q = word / (85*85);
+ buf[2] = (char) (q + '!');
+
+ w1 = (uint16) (word - q*(85L*85));
+ buf[3] = (char) ((w1 / 85) + '!');
+ buf[4] = (char) ((w1 % 85) + '!');
+ buf[5] = '\0';
+ } else
+ buf[0] = 'z', buf[1] = '\0';
+}
+
+void
+Ascii85Put(TIFF2PSContext *ctx, unsigned char code)
+{
+ ctx->ascii85buf[ctx->ascii85count++] = code;
+ if (ctx->ascii85count >= 4) {
+ unsigned char* p;
+ int n;
+ char buf[6];
+
+ for (n = ctx->ascii85count, p = ctx->ascii85buf;
+ n >= 4; n -= 4, p += 4) {
+ char* cp;
+ Ascii85Encode(p, buf);
+ for (cp = buf; *cp; cp++) {
+ putc(*cp, ctx->fd);
+ if (--ctx->ascii85breaklen == 0) {
+ putc('\n', ctx->fd);
+ ctx->ascii85breaklen = 2*MAXLINE;
+ }
+ }
+ }
+ _TIFFmemcpy(ctx->ascii85buf, p, n);
+ ctx->ascii85count = n;
+ }
+}
+
+void
+Ascii85Flush(TIFF2PSContext* ctx)
+{
+ if (ctx->ascii85count > 0) {
+ char res[6];
+ _TIFFmemset(&ctx->ascii85buf[ctx->ascii85count], 0, 3);
+ Ascii85Encode(ctx->ascii85buf, res);
+ fwrite(res[0] == 'z' ? "!!!!" : res, ctx->ascii85count + 1, 1, ctx->fd);
+ }
+ fputs("~>\n", ctx->fd);
+}
+#if defined( EXP_ASCII85ENCODER)
+
+#define A85BREAKCNTR ctx->ascii85breaklen
+#define A85BREAKLEN (2*MAXLINE)
+
+/*****************************************************************************
+*
+* Name: Ascii85EncodeBlock( ascii85_p, f_eod, raw_p, raw_l )
+*
+* Description: This routine will encode the raw data in the buffer described
+* by raw_p and raw_l into ASCII85 format and store the encoding
+* in the buffer given by ascii85_p.
+*
+* Parameters: ctx - TIFF2PS context
+* ascii85_p - A buffer supplied by the caller which will
+* contain the encoded ASCII85 data.
+* f_eod - Flag: Nz means to end the encoded buffer with
+* an End-Of-Data marker.
+* raw_p - Pointer to the buffer of data to be encoded
+* raw_l - Number of bytes in raw_p[] to be encoded
+*
+* Returns: (int) < 0 Error, see errno
+* >= 0 Number of bytes written to ascii85_p[].
+*
+* Notes: An external variable given by A85BREAKCNTR is used to
+* determine when to insert newline characters into the
+* encoded data. As each byte is placed into ascii85_p this
+* external is decremented. If the variable is decrement to
+* or past zero then a newline is inserted into ascii85_p
+* and the A85BREAKCNTR is then reset to A85BREAKLEN.
+* Note: for efficiency reasons the A85BREAKCNTR variable
+* is not actually checked on *every* character
+* placed into ascii85_p but often only for every
+* 5 characters.
+*
+* THE CALLER IS RESPONSIBLE FOR ENSURING THAT ASCII85_P[] IS
+* SUFFICIENTLY LARGE TO THE ENCODED DATA!
+* You will need at least 5 * (raw_l/4) bytes plus space for
+* newline characters and space for an EOD marker (if
+* requested). A safe calculation is to use 6*(raw_l/4) + 8
+* to size ascii85_p.
+*
+*****************************************************************************/
+
+int Ascii85EncodeBlock( TIFF2PSContext *ctx, uint8 * ascii85_p,
+ unsigned f_eod, const uint8 * raw_p, int raw_l )
+
+{
+ char ascii85[5]; /* Encoded 5 tuple */
+ int ascii85_l; /* Number of bytes written to ascii85_p[] */
+ int rc; /* Return code */
+ uint32 val32; /* Unencoded 4 tuple */
+
+ ascii85_l = 0; /* Nothing written yet */
+
+ if ( raw_p )
+ {
+ --raw_p; /* Prepare for pre-increment fetches */
+
+ for ( ; raw_l > 3; raw_l -= 4 )
+ {
+ val32 = *(++raw_p) << 24;
+ val32 += *(++raw_p) << 16;
+ val32 += *(++raw_p) << 8;
+ val32 += *(++raw_p);
+
+ if ( val32 == 0 ) /* Special case */
+ {
+ ascii85_p[ascii85_l] = 'z';
+ rc = 1;
+ }
+
+ else
+ {
+ ascii85[4] = (char) ((val32 % 85) + 33);
+ val32 /= 85;
+
+ ascii85[3] = (char) ((val32 % 85) + 33);
+ val32 /= 85;
+
+ ascii85[2] = (char) ((val32 % 85) + 33);
+ val32 /= 85;
+
+ ascii85[1] = (char) ((val32 % 85) + 33);
+ ascii85[0] = (char) ((val32 / 85) + 33);
+
+ _TIFFmemcpy( &ascii85_p[ascii85_l], ascii85, sizeof(ascii85) );
+ rc = sizeof(ascii85);
+ }
+
+ ascii85_l += rc;
+
+ if ( (A85BREAKCNTR -= rc) <= 0 )
+ {
+ ascii85_p[ascii85_l] = '\n';
+ ++ascii85_l;
+ A85BREAKCNTR = A85BREAKLEN;
+ }
+ }
+
+ /*
+ * Output any straggler bytes:
+ */
+
+ if ( raw_l > 0 )
+ {
+ int len; /* Output this many bytes */
+
+ len = raw_l + 1;
+ val32 = *++raw_p << 24; /* Prime the pump */
+
+ if ( --raw_l > 0 ) val32 += *(++raw_p) << 16;
+ if ( --raw_l > 0 ) val32 += *(++raw_p) << 8;
+
+ val32 /= 85;
+
+ ascii85[3] = (char) ((val32 % 85) + 33);
+ val32 /= 85;
+
+ ascii85[2] = (char) ((val32 % 85) + 33);
+ val32 /= 85;
+
+ ascii85[1] = (char) ((val32 % 85) + 33);
+ ascii85[0] = (char) ((val32 / 85) + 33);
+
+ _TIFFmemcpy( &ascii85_p[ascii85_l], ascii85, len );
+ ascii85_l += len;
+ }
+ }
+
+ /*
+ * If requested add an ASCII85 End Of Data marker:
+ */
+
+ if ( f_eod )
+ {
+ ascii85_p[ascii85_l++] = '~';
+ ascii85_p[ascii85_l++] = '>';
+ ascii85_p[ascii85_l++] = '\n';
+ }
+
+ return ( ascii85_l );
+
+} /* Ascii85EncodeBlock() */
+
+#endif /* EXP_ASCII85ENCODER */
+
+/* vim: set ts=8 sts=8 sw=8 noet: */
diff --git a/backend/tiff/tiff2ps.h b/backend/tiff/tiff2ps.h
new file mode 100644
index 0000000..1944aac
--- /dev/null
+++ b/backend/tiff/tiff2ps.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2005 rpath, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include "tiffio.h"
+
+typedef struct _TIFF2PSContext TIFF2PSContext;
+
+TIFF2PSContext *tiff2ps_context_new(const gchar *filename);
+void tiff2ps_process_page(TIFF2PSContext* ctx, TIFF* tif,
+ double pagewidth, double pageheight,
+ double leftmargin, double bottommargin,
+ gboolean center);
+void tiff2ps_context_finalize(TIFF2PSContext* ctx);