From 13a06349251874bd35d2f03c3fc93217cee749a2 Mon Sep 17 00:00:00 2001 From: Nickolay V. Shmyrev Date: Mon, 08 Jan 2007 12:25:31 +0000 Subject: Reorganize source tree. 2007-01-08 Nickolay V. Shmyrev * 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 --- (limited to 'backend') 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 + * + * 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 +#include +#include +#include + +#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 + * + * 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 + * + * 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 + +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 + * + * 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 +#include +#include +#include +#include +#include + +#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 + * + * 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 + * + * 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 +#include +#include +#include + +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 + * + * 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 + +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 + * + * 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 + +/** + * 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 + * + * 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 +#include + +#include + +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 + * + * 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 +#include + +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 + * + * 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 +#include +#include + +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 + * + * 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 + +#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 +#include + +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 + * + * 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 /* added for MDVI */ +#include /* added for MDVI */ + +#include +#include +#include +#include +#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 + +/* 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 + +#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 +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 +#include + +#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 +#include +#include + +#include "sysdeps.h" + +#if STDC_HEADERS +# include +#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 +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +#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 + +#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 +#include +#include +#include +#include + +#include "mdvi.h" +#include "private.h" + +#include +#include + +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 . 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.. + * - 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 +#include + +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 +#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 +#include +#include + +#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 +#include +#include +#include + +#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 + +#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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#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 +#include +#include +#include + +#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 +#include + +#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 +#include + +#include "mdvi.h" +#include "private.h" + +#if defined(WITH_REGEX_SPECIALS) && defined(HAVE_REGEX_H) +#include +#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 +#include +#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 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 +#include +#include +#include +#include + +#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 /* tex-file.h needs this */ +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include + +#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 + +#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 + +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 + +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 -#include -#include - -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 - * - * 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 -#include -#include -#include -#include -#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 - * - * 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 - -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 -#include -#include -#include -#include -#include - -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 -#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 -#include -#include - -#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 - * - * 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 - * - * 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 -#include -#include - -#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 - * - * 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 - * - * 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 -#include - -#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 -#include -#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 - * - * 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 - * - * 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 -#include -#include - -#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 -#include - -/* 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 -#include - -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 - * - * 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 - * - * 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 -#include -#include - -#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 - * - * 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 - -#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 - * - * 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 - * - * 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 - -#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 -#include -#include - -#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 - * - * 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 - * - * 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 - -#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 - * - * 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 -#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 - * - * 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 -#include - -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 - * 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 - * 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 -#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 - * 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 - * 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 - -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 -#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 - * - * 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 - -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 -#include -#include -#include -#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 +#include + +#ifdef STDC_HEADERS +#include +#include +#include +#elif HAVE_STRINGS_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifndef errno +extern int errno; +#endif + +#include +#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 +** 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 +** This code is free software; you can redistribute it and/or +** modify it under the terms of GNU Lesser General Public License. +*/ + +#include + +#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': 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': memcpy (&ret[j], ">", 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 +** 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 +** 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 +** 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, "&", 5); break; + case '\'': dest = my_strcat (dest, "'", 6); break; + case '"': dest = my_strcat (dest, """, 6); break; + case '<': dest = my_strcat (dest, "<", 4); break; + case '>': dest = my_strcat (dest, ">", 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 +** 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 +** 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 /* size_t for C++ */ +extern "C" { +#else +#include /* 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 + +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 + * Copyright (C) 2005, Bastien Nocera + * + * 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 +#include +#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("%s", 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 + * + * 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 + +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 + +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 + +#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 + +#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 +#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 +#include +#include +#include +#include +#include +#ifdef HAVE_CAIRO_PDF +#include +#endif +#include + +#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 + * + * 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 + * + * 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 + * + * 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 + +#include + +#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 + * + * 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 + +#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 + * + * 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 + +#include + +#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 + * + * 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 + +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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +#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 + * + * + */ + +/* end of patch */ + +#include +#include +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif +#ifndef BUFSIZ +# define BUFSIZ 1024 +#endif +#include +#include /* #includes the appropriate */ +#include "gstypes.h" +#include "gsdefaults.h" +#include "ps.h" +#include "gsio.h" + +#include + +/* 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: |(atend) + * %%Creator: + * %%CreationDate: + * %%Orientation: Portrait|Landscape|(atend) + * %%Pages: []|(atend) + * %%PageOrder: Ascend|Descend|Special|(atend) + * %%Title: + * %%DocumentMedia: + * %%DocumentPaperSizes: + * %%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: + * %%PageOrientation: Portrait|Landscape + * %%PageMedia: + * %%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: + * %%PageOrientation: Portrait|Landscape + * %%PaperSize: + * %%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: + * %%PageBoundingBox: |(atend) + * %%PageOrientation: Portrait|Landscape + * %%PageMedia: + * %%PaperSize: + * + * 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: |(atend) + * %%Orientation: Portrait|Landscape|(atend) + * %%Pages: []|(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 + +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 + +#include +#include + +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 + * + * 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 +#include + +#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 + * + * 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 + * Modifications Copyright (C) 2005 rpath, Inc. + * + */ + +#include +#include /* for atof */ +#include +#include +#include +#include + +#include +#include + +#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:
+ * <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<bitspersample, rmap, gmap, bmap) == 16) { + int i; +#define CVT(x) ((unsigned short) (((x) * 255) / ((1U<<16)-1))) + for (i = (1<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 +#include +#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); -- cgit v0.9.1