diff options
author | Perberos <[email protected]> | 2011-11-09 18:17:43 -0300 |
---|---|---|
committer | Perberos <[email protected]> | 2011-11-09 18:17:43 -0300 |
commit | f6ce926719943751cf65cacde7fae050593eb2d6 (patch) | |
tree | 9224d1751678cf2d1fbd0431f128b711311c0287 /backend | |
download | atril-f6ce926719943751cf65cacde7fae050593eb2d6.tar.bz2 atril-f6ce926719943751cf65cacde7fae050593eb2d6.tar.xz |
inicial
Diffstat (limited to 'backend')
101 files changed, 29912 insertions, 0 deletions
diff --git a/backend/Makefile.am b/backend/Makefile.am new file mode 100644 index 00000000..a6dc6e58 --- /dev/null +++ b/backend/Makefile.am @@ -0,0 +1,40 @@ +SUBDIRS = + +# Backends + +if ENABLE_PDF +SUBDIRS += pdf +endif + +if ENABLE_PS +SUBDIRS += ps +endif + +if ENABLE_PIXBUF +SUBDIRS += pixbuf +endif + +if ENABLE_DJVU +SUBDIRS += djvu +endif + +if ENABLE_TIFF +SUBDIRS += tiff +endif + +if ENABLE_DVI +SUBDIRS += dvi +endif + +if ENABLE_COMICS + SUBDIRS += comics +endif + +if ENABLE_IMPRESS + SUBDIRS += impress +endif + +EXTRA_DIST = \ + backend.symbols + +-include $(top_srcdir)/git.mk diff --git a/backend/backend.symbols b/backend/backend.symbols new file mode 100644 index 00000000..6c41cf8f --- /dev/null +++ b/backend/backend.symbols @@ -0,0 +1 @@ +register_evince_backend diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am new file mode 100644 index 00000000..0b06ecbc --- /dev/null +++ b/backend/comics/Makefile.am @@ -0,0 +1,32 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCE_COMPILATION \ + $(BACKEND_CFLAGS) \ + $(LIB_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_LTLIBRARIES = libcomicsdocument.la + +libcomicsdocument_la_SOURCES = \ + comics-document.c \ + comics-document.h + +libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libcomicsdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument.la \ + $(BACKEND_LIBS) \ + $(LIB_LIBS) + +backend_in_files = comicsdocument.evince-backend.in +backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_EVINCE_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c new file mode 100644 index 00000000..4d74385a --- /dev/null +++ b/backend/comics/comics-document.c @@ -0,0 +1,936 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Copyright (C) 2009-2010 Juanjo Marín <[email protected]> + * Copyright (C) 2005, Teemu Tervo <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <glib/gstdio.h> +#include <gio/gio.h> + +#ifdef G_OS_WIN32 +# define WIFEXITED(x) ((x) != 3) +# define WEXITSTATUS(x) (x) +#else +# include <sys/wait.h> +#endif + +#include "comics-document.h" +#include "ev-document-misc.h" +#include "ev-document-thumbnails.h" +#include "ev-file-helpers.h" + +#ifdef G_OS_WIN32 +/* On windows g_spawn_command_line_sync reads stdout in O_BINARY mode, not in O_TEXT mode. + * As a consequence, newlines are in a platform dependent representation (\r\n). This + * might be considered a bug in glib. + */ +#define EV_EOL "\r\n" +#else +#define EV_EOL "\n" +#endif + +typedef enum +{ + RARLABS, + GNAUNRAR, + UNZIP, + P7ZIP, + TAR +} ComicBookDecompressType; + +typedef struct _ComicsDocumentClass ComicsDocumentClass; + +struct _ComicsDocumentClass +{ + EvDocumentClass parent_class; +}; + +struct _ComicsDocument +{ + EvDocument parent_instance; + + gchar *archive, *dir; + GPtrArray *page_names; + gchar *selected_command, *alternative_command; + gchar *extract_command, *list_command, *decompress_tmp; + gboolean regex_arg; + gint offset; + ComicBookDecompressType command_usage; +}; + +#define OFFSET_7Z 53 +#define OFFSET_ZIP 2 +#define NO_OFFSET 0 + +/* For perfomance reasons of 7z* we've choosen to decompress on the temporary + * directory instead of decompressing on the stdout */ + +/** + * @extract: command line arguments to pass to extract a file from the archive + * to stdout. + * @list: command line arguments to list the archive contents + * @decompress_tmp: command line arguments to pass to extract the archive + * into a directory. + * @regex_arg: whether the command can accept regex expressions + * @offset: the position offset of the filename on each line in the output of + * running the @list command + */ +typedef struct { + char *extract; + char *list; + char *decompress_tmp; + gboolean regex_arg; + gint offset; +} ComicBookDecompressCommand; + +static const ComicBookDecompressCommand command_usage_def[] = { + /* RARLABS unrar */ + {"%s p -c- -ierr --", "%s vb -c- -- %s", NULL , FALSE, NO_OFFSET}, + + /* GNA! unrar */ + {NULL , "%s t %s" , "%s -xf %s %s" , FALSE, NO_OFFSET}, + + /* unzip */ + {"%s -p -C --" , "%s %s" , NULL , TRUE , OFFSET_ZIP}, + + /* 7zip */ + {NULL , "%s l -- %s" , "%s x -y %s -o%s", FALSE, OFFSET_7Z}, + + /* tar */ + {"%s -xOf" , "%s -tf %s" , NULL , FALSE, NO_OFFSET} +}; + +static void comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); + +static GSList* get_supported_image_extensions (void); +static void get_page_size_area_prepared_cb (GdkPixbufLoader *loader, + gpointer data); +static void render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, + gint width, + gint height, + gpointer data); +static char** extract_argv (EvDocument *document, + gint page); + + +EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document, + { + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, + comics_document_document_thumbnails_iface_init); + } ); + +/** + * comics_regex_quote: + * @unquoted_string: a literal string + * + * Quotes a string so unzip will not interpret the regex expressions of + * @unquoted_string. Basically, this functions uses [] to disable regex + * expressions. The return value must be freed with * g_free() + * + * Return value: quoted and disabled-regex string + **/ +static gchar * +comics_regex_quote (const gchar *unquoted_string) +{ + const gchar *p; + GString *dest; + + dest = g_string_new ("'"); + + p = unquoted_string; + + while (*p) { + switch (*p) { + /* * matches a sequence of 0 or more characters */ + case ('*'): + /* ? matches exactly 1 charactere */ + case ('?'): + /* [...] matches any single character found inside + * the brackets. Disabling the first bracket is enough. + */ + case ('['): + g_string_append (dest, "["); + g_string_append_c (dest, *p); + g_string_append (dest, "]"); + break; + /* Because \ escapes regex expressions that we are + * disabling for unzip, we need to disable \ too */ + case ('\\'): + g_string_append (dest, "[\\\\]"); + break; + /* Escape single quote inside the string */ + case ('\''): + g_string_append (dest, "'\\''"); + break; + default: + g_string_append_c (dest, *p); + break; + } + ++p; + } + g_string_append_c (dest, '\''); + return g_string_free (dest, FALSE); +} + + +/* This function manages the command for decompressing a comic book */ +static gboolean +comics_decompress_temp_dir (const gchar *command_decompress_tmp, + const gchar *command, + GError **error) +{ + gboolean success; + gchar *std_out, *basename; + GError *err = NULL; + gint retval; + + success = g_spawn_command_line_sync (command_decompress_tmp, &std_out, + NULL, &retval, &err); + basename = g_path_get_basename (command); + if (!success) { + g_set_error (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("Error launching the command “%s” in order to " + "decompress the comic book: %s"), + basename, + err->message); + g_error_free (err); + } else if (WIFEXITED (retval)) { + if (WEXITSTATUS (retval) == EXIT_SUCCESS) { + g_free (std_out); + g_free (basename); + return TRUE; + } else { + g_set_error (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("The command “%s” failed at " + "decompressing the comic book."), + basename); + g_free (std_out); + } + } else { + g_set_error (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("The command “%s” did not end normally."), + basename); + g_free (std_out); + } + g_free (basename); + return FALSE; +} + +/* This function shows how to use the choosen command for decompressing a + * comic book file. It modifies fields of the ComicsDocument struct with + * this information */ +static gboolean +comics_generate_command_lines (ComicsDocument *comics_document, + GError **error) +{ + gchar *quoted_file, *quoted_file_aux; + gchar *quoted_command; + ComicBookDecompressType type; + + type = comics_document->command_usage; + comics_document->regex_arg = command_usage_def[type].regex_arg; + quoted_command = g_shell_quote (comics_document->selected_command); + if (comics_document->regex_arg) { + quoted_file = comics_regex_quote (comics_document->archive); + quoted_file_aux = g_shell_quote (comics_document->archive); + comics_document->list_command = + g_strdup_printf (command_usage_def[type].list, + comics_document->alternative_command, + quoted_file_aux); + g_free (quoted_file_aux); + } else { + quoted_file = g_shell_quote (comics_document->archive); + comics_document->list_command = + g_strdup_printf (command_usage_def[type].list, + quoted_command, quoted_file); + } + comics_document->extract_command = + g_strdup_printf (command_usage_def[type].extract, + quoted_command); + comics_document->offset = command_usage_def[type].offset; + if (command_usage_def[type].decompress_tmp) { + comics_document->dir = ev_mkdtemp ("evince-comics-XXXXXX", error); + if (comics_document->dir == NULL) + return FALSE; + + /* unrar-free can't create directories, but ev_mkdtemp already created the dir */ + + comics_document->decompress_tmp = + g_strdup_printf (command_usage_def[type].decompress_tmp, + quoted_command, quoted_file, + comics_document->dir); + g_free (quoted_file); + g_free (quoted_command); + + if (!comics_decompress_temp_dir (comics_document->decompress_tmp, + comics_document->selected_command, error)) + return FALSE; + else + return TRUE; + } else { + g_free (quoted_file); + g_free (quoted_command); + return TRUE; + } + +} + +/* This function chooses an external command for decompressing a comic + * book based on its mime tipe. */ +static gboolean +comics_check_decompress_command (gchar *mime_type, + ComicsDocument *comics_document, + GError **error) +{ + gboolean success; + gchar *std_out, *std_err; + gint retval; + GError *err = NULL; + + /* FIXME, use proper cbr/cbz mime types once they're + * included in shared-mime-info */ + + if (!strcmp (mime_type, "application/x-cbr") || + !strcmp (mime_type, "application/x-rar")) { + /* The RARLAB provides a no-charge proprietary (freeware) + * decompress-only client for Linux called unrar. Another + * option is a GPLv2-licensed command-line tool developed by + * the Gna! project. Confusingly enough, the free software RAR + * decoder is also named unrar. For this reason we need to add + * some lines for disambiguation. Sorry for the added the + * complexity but it's life :) + * Finally, some distributions, like Debian, rename this free + * option as unrar-free. + * */ + comics_document->selected_command = + g_find_program_in_path ("unrar"); + if (comics_document->selected_command) { + /* We only use std_err to avoid printing useless error + * messages on the terminal */ + success = + g_spawn_command_line_sync ( + comics_document->selected_command, + &std_out, &std_err, + &retval, &err); + if (!success) { + g_propagate_error (error, err); + g_error_free (err); + return FALSE; + /* I don't check retval status because RARLAB unrar + * doesn't have a way to return 0 without involving an + * operation with a file*/ + } else if (WIFEXITED (retval)) { + if (g_strrstr (std_out,"freeware") != NULL) + /* The RARLAB freeware client */ + comics_document->command_usage = RARLABS; + else + /* The Gna! free software client */ + comics_document->command_usage = GNAUNRAR; + + g_free (std_out); + g_free (std_err); + return TRUE; + } + } + /* The Gna! free software client with Debian naming convention */ + comics_document->selected_command = + g_find_program_in_path ("unrar-free"); + if (comics_document->selected_command) { + comics_document->command_usage = GNAUNRAR; + return TRUE; + } + + } else if (!strcmp (mime_type, "application/x-cbz") || + !strcmp (mime_type, "application/zip")) { + /* InfoZIP's unzip program */ + comics_document->selected_command = + g_find_program_in_path ("unzip"); + comics_document->alternative_command = + g_find_program_in_path ("zipnote"); + if (comics_document->selected_command && + comics_document->alternative_command) { + comics_document->command_usage = UNZIP; + return TRUE; + } + + } else if (!strcmp (mime_type, "application/x-cb7") || + !strcmp (mime_type, "application/x-7z-compressed")) { + /* 7zr, 7za and 7z are the commands from the p7zip project able + * to decompress .7z files */ + comics_document->selected_command = + g_find_program_in_path ("7zr"); + if (comics_document->selected_command) { + comics_document->command_usage = P7ZIP; + return TRUE; + } + comics_document->selected_command = + g_find_program_in_path ("7za"); + if (comics_document->selected_command) { + comics_document->command_usage = P7ZIP; + return TRUE; + } + comics_document->selected_command = + g_find_program_in_path ("7z"); + if (comics_document->selected_command) { + comics_document->command_usage = P7ZIP; + return TRUE; + } + } else if (!strcmp (mime_type, "application/x-cbt") || + !strcmp (mime_type, "application/x-tar")) { + /* tar utility (Tape ARchive) */ + comics_document->selected_command = + g_find_program_in_path ("tar"); + if (comics_document->selected_command) { + comics_document->command_usage = TAR; + return TRUE; + } + } else { + g_set_error (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("Not a comic book MIME type: %s"), + mime_type); + return FALSE; + } + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("Can't find an appropriate command to " + "decompress this type of comic book")); + return FALSE; +} + +static int +sort_page_names (gconstpointer a, + gconstpointer b) +{ + return strcmp (* (const char **) a, * (const char **) b); +} + +static gboolean +comics_document_load (EvDocument *document, + const char *uri, + GError **error) +{ + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + GSList *supported_extensions; + gchar *std_out; + gchar *mime_type; + gchar **cb_files, *cb_file; + gboolean success; + int i, retval; + GError *err = NULL; + + comics_document->archive = g_filename_from_uri (uri, NULL, error); + if (!comics_document->archive) + return FALSE; + + mime_type = ev_file_get_mime_type (uri, FALSE, &err); + if (!mime_type) { + if (err) { + g_propagate_error (error, err); + } else { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("Unknown MIME Type")); + } + + return FALSE; + } + + if (!comics_check_decompress_command (mime_type, comics_document, + error)) { + g_free (mime_type); + return FALSE; + } else if (!comics_generate_command_lines (comics_document, error)) { + g_free (mime_type); + return FALSE; + } + + g_free (mime_type); + + /* Get list of files in archive */ + success = g_spawn_command_line_sync (comics_document->list_command, + &std_out, NULL, &retval, error); + + if (!success) { + return FALSE; + } else if (!WIFEXITED(retval) || WEXITSTATUS(retval) != EXIT_SUCCESS) { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("File corrupted")); + return FALSE; + } + + /* FIXME: is this safe against filenames containing \n in the archive ? */ + cb_files = g_strsplit (std_out, EV_EOL, 0); + + g_free (std_out); + + if (!cb_files) { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("No files in archive")); + return FALSE; + } + + comics_document->page_names = g_ptr_array_sized_new (64); + + supported_extensions = get_supported_image_extensions (); + for (i = 0; cb_files[i] != NULL; i++) { + if (comics_document->offset != NO_OFFSET) { + if (g_utf8_strlen (cb_files[i],-1) > + comics_document->offset) { + cb_file = + g_utf8_offset_to_pointer (cb_files[i], + comics_document->offset); + } else { + continue; + } + } else { + cb_file = cb_files[i]; + } + gchar *suffix = g_strrstr (cb_file, "."); + if (!suffix) + continue; + suffix = g_ascii_strdown (suffix + 1, -1); + if (g_slist_find_custom (supported_extensions, suffix, + (GCompareFunc) strcmp) != NULL) { + g_ptr_array_add (comics_document->page_names, + g_strstrip (g_strdup (cb_file))); + } + g_free (suffix); + } + g_strfreev (cb_files); + g_slist_foreach (supported_extensions, (GFunc) g_free, NULL); + g_slist_free (supported_extensions); + + if (comics_document->page_names->len == 0) { + g_set_error (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("No images found in archive %s"), + uri); + return FALSE; + } + + /* Now sort the pages */ + g_ptr_array_sort (comics_document->page_names, sort_page_names); + + return TRUE; +} + + +static gboolean +comics_document_save (EvDocument *document, + const char *uri, + GError **error) +{ + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + + return ev_xfer_uri_simple (comics_document->archive, uri, error); +} + +static int +comics_document_get_n_pages (EvDocument *document) +{ + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + + if (comics_document->page_names == NULL) + return 0; + + return comics_document->page_names->len; +} + +static void +comics_document_get_page_size (EvDocument *document, + EvPage *page, + double *width, + double *height) +{ + GdkPixbufLoader *loader; + char **argv; + guchar buf[1024]; + gboolean success, got_size = FALSE; + gint outpipe = -1; + GPid child_pid; + gssize bytes; + GdkPixbuf *pixbuf; + gchar *filename; + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + + if (!comics_document->decompress_tmp) { + argv = extract_argv (document, page->index); + success = g_spawn_async_with_pipes (NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | + G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, + &child_pid, + NULL, &outpipe, NULL, NULL); + g_strfreev (argv); + g_return_if_fail (success == TRUE); + + loader = gdk_pixbuf_loader_new (); + g_signal_connect (loader, "area-prepared", + G_CALLBACK (get_page_size_area_prepared_cb), + &got_size); + + while (outpipe >= 0) { + bytes = read (outpipe, buf, 1024); + + if (bytes > 0) + gdk_pixbuf_loader_write (loader, buf, bytes, NULL); + if (bytes <= 0 || got_size) { + close (outpipe); + outpipe = -1; + gdk_pixbuf_loader_close (loader, NULL); + } + } + + if (gdk_pixbuf_loader_get_pixbuf (loader)) { + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (width) + *width = gdk_pixbuf_get_width (pixbuf); + if (height) + *height = gdk_pixbuf_get_height (pixbuf); + } + + g_spawn_close_pid (child_pid); + g_object_unref (loader); + } else { + filename = g_build_filename (comics_document->dir, + (char *) comics_document->page_names->pdata[page->index], + NULL); + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + g_free (filename); + if (width) + *width = gdk_pixbuf_get_width (pixbuf); + if (height) + *height = gdk_pixbuf_get_height (pixbuf); + } +} + +static void +get_page_size_area_prepared_cb (GdkPixbufLoader *loader, + gpointer data) +{ + gboolean *got_size = data; + *got_size = TRUE; +} + +static GdkPixbuf * +comics_document_render_pixbuf (EvDocument *document, + EvRenderContext *rc) +{ + GdkPixbufLoader *loader; + GdkPixbuf *rotated_pixbuf; + char **argv; + guchar buf[4096]; + gboolean success; + gint outpipe = -1; + GPid child_pid; + gssize bytes; + gint width, height; + gchar *filename; + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + + if (!comics_document->decompress_tmp) { + argv = extract_argv (document, rc->page->index); + success = g_spawn_async_with_pipes (NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | + G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, + &child_pid, + NULL, &outpipe, NULL, NULL); + g_strfreev (argv); + g_return_val_if_fail (success == TRUE, NULL); + + loader = gdk_pixbuf_loader_new (); + g_signal_connect (loader, "size-prepared", + G_CALLBACK (render_pixbuf_size_prepared_cb), + &rc->scale); + + while (outpipe >= 0) { + bytes = read (outpipe, buf, 4096); + + if (bytes > 0) { + gdk_pixbuf_loader_write (loader, buf, bytes, + NULL); + } else if (bytes <= 0) { + close (outpipe); + gdk_pixbuf_loader_close (loader, NULL); + outpipe = -1; + } + } + + rotated_pixbuf = gdk_pixbuf_rotate_simple ( + gdk_pixbuf_loader_get_pixbuf (loader), + 360 - rc->rotation); + g_spawn_close_pid (child_pid); + g_object_unref (loader); + } else { + filename = + g_build_filename (comics_document->dir, + (char *) comics_document->page_names->pdata[rc->page->index], + NULL); + + gdk_pixbuf_get_file_info (filename, &width, &height); + + rotated_pixbuf = + gdk_pixbuf_rotate_simple (gdk_pixbuf_new_from_file_at_size ( + filename, width * (rc->scale) + 0.5, + height * (rc->scale) + 0.5, NULL), + 360 - rc->rotation); + g_free (filename); + + } + return rotated_pixbuf; +} + +static cairo_surface_t * +comics_document_render (EvDocument *document, + EvRenderContext *rc) +{ + GdkPixbuf *pixbuf; + cairo_surface_t *surface; + + pixbuf = comics_document_render_pixbuf (document, rc); + surface = ev_document_misc_surface_from_pixbuf (pixbuf); + g_object_unref (pixbuf); + + return surface; +} + +static void +render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader, + gint width, + gint height, + gpointer data) +{ + double *scale = data; + int w = (width * (*scale) + 0.5); + int h = (height * (*scale) + 0.5); + + gdk_pixbuf_loader_set_size (loader, w, h); +} + +/** + * comics_remove_dir: Removes a directory recursively. + * Returns: + * 0 if it was successfully deleted, + * -1 if an error occurred + */ +static int +comics_remove_dir (gchar *path_name) +{ + GDir *content_dir; + const gchar *filename; + gchar *filename_with_path; + + if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) { + content_dir = g_dir_open (path_name, 0, NULL); + filename = g_dir_read_name (content_dir); + while (filename) { + filename_with_path = + g_build_filename (path_name, + filename, NULL); + comics_remove_dir (filename_with_path); + g_free (filename_with_path); + filename = g_dir_read_name (content_dir); + } + g_dir_close (content_dir); + } + /* Note from g_remove() documentation: on Windows, it is in general not + * possible to remove a file that is open to some process, or mapped + * into memory.*/ + return (g_remove (path_name)); +} + +static void +comics_document_finalize (GObject *object) +{ + ComicsDocument *comics_document = COMICS_DOCUMENT (object); + + if (comics_document->decompress_tmp) { + if (comics_remove_dir (comics_document->dir) == -1) + g_warning (_("There was an error deleting “%s”."), + comics_document->dir); + g_free (comics_document->dir); + } + + if (comics_document->page_names) { + g_ptr_array_foreach (comics_document->page_names, (GFunc) g_free, NULL); + g_ptr_array_free (comics_document->page_names, TRUE); + } + + g_free (comics_document->archive); + g_free (comics_document->selected_command); + g_free (comics_document->alternative_command); + g_free (comics_document->extract_command); + g_free (comics_document->list_command); + + G_OBJECT_CLASS (comics_document_parent_class)->finalize (object); +} + +static void +comics_document_class_init (ComicsDocumentClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + gobject_class->finalize = comics_document_finalize; + + ev_document_class->load = comics_document_load; + ev_document_class->save = comics_document_save; + ev_document_class->get_n_pages = comics_document_get_n_pages; + ev_document_class->get_page_size = comics_document_get_page_size; + ev_document_class->render = comics_document_render; +} + +static void +comics_document_init (ComicsDocument *comics_document) +{ + comics_document->archive = NULL; + comics_document->page_names = NULL; + comics_document->extract_command = NULL; +} + +/* Returns a list of file extensions supported by gdk-pixbuf */ +static GSList* +get_supported_image_extensions() +{ + GSList *extensions = NULL; + GSList *formats = gdk_pixbuf_get_formats (); + GSList *l; + + for (l = formats; l != NULL; l = l->next) { + int i; + gchar **ext = gdk_pixbuf_format_get_extensions (l->data); + + for (i = 0; ext[i] != NULL; i++) { + extensions = g_slist_append (extensions, + g_strdup (ext[i])); + } + + g_strfreev (ext); + } + + g_slist_free (formats); + return extensions; +} + +static GdkPixbuf * +comics_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, + EvRenderContext *rc, + gboolean border) +{ + GdkPixbuf *thumbnail; + + thumbnail = comics_document_render_pixbuf (EV_DOCUMENT (document), rc); + + if (border) { + GdkPixbuf *tmp_pixbuf = thumbnail; + + thumbnail = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf); + g_object_unref (tmp_pixbuf); + } + + return thumbnail; +} + +static void +comics_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, + EvRenderContext *rc, + gint *width, + gint *height) +{ + gdouble page_width, page_height; + + comics_document_get_page_size (EV_DOCUMENT (document), rc->page, + &page_width, &page_height); + + if (rc->rotation == 90 || rc->rotation == 270) { + *width = (gint) (page_height * rc->scale); + *height = (gint) (page_width * rc->scale); + } else { + *width = (gint) (page_width * rc->scale); + *height = (gint) (page_height * rc->scale); + } +} + +static void +comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface) +{ + iface->get_thumbnail = comics_document_thumbnails_get_thumbnail; + iface->get_dimensions = comics_document_thumbnails_get_dimensions; +} + +static char** +extract_argv (EvDocument *document, gint page) +{ + ComicsDocument *comics_document = COMICS_DOCUMENT (document); + char **argv; + char *command_line, *quoted_archive, *quoted_filename; + GError *err = NULL; + + if (page >= comics_document->page_names->len) + return NULL; + + if (comics_document->regex_arg) { + quoted_archive = comics_regex_quote (comics_document->archive); + quoted_filename = + comics_regex_quote (comics_document->page_names->pdata[page]); + } else { + quoted_archive = g_shell_quote (comics_document->archive); + quoted_filename = g_shell_quote (comics_document->page_names->pdata[page]); + } + + command_line = g_strdup_printf ("%s %s %s", + comics_document->extract_command, + quoted_archive, + quoted_filename); + g_shell_parse_argv (command_line, NULL, &argv, &err); + + if (err) { + g_warning (_("Error %s"), err->message); + g_error_free (err); + return NULL; + } + + g_free (command_line); + g_free (quoted_archive); + g_free (quoted_filename); + return argv; +} diff --git a/backend/comics/comics-document.h b/backend/comics/comics-document.h new file mode 100644 index 00000000..76feef05 --- /dev/null +++ b/backend/comics/comics-document.h @@ -0,0 +1,38 @@ +/* comics-document.h: Implementation of EvDocument for comic book archives + * Copyright (C) 2005, Teemu Tervo <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __COMICS_DOCUMENT_H__ +#define __COMICS_DOCUMENT_H__ + +#include "ev-document.h" + +G_BEGIN_DECLS + +#define COMICS_TYPE_DOCUMENT (comics_document_get_type ()) +#define COMICS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COMICS_TYPE_DOCUMENT, ComicsDocument)) +#define COMICS_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COMICS_TYPE_DOCUMENT)) + +typedef struct _ComicsDocument ComicsDocument; + +GType comics_document_get_type (void) G_GNUC_CONST; + +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + +G_END_DECLS + +#endif /* __COMICS_DOCUMENT_H__ */ diff --git a/backend/comics/comicsdocument.evince-backend.in b/backend/comics/comicsdocument.evince-backend.in new file mode 100644 index 00000000..90d4c01b --- /dev/null +++ b/backend/comics/comicsdocument.evince-backend.in @@ -0,0 +1,4 @@ +[Evince Backend] +Module=comicsdocument +_TypeDescription=Comic Books +MimeType=application/x-cbr;application/x-cbz;application/x-cb7;application/x-cbt; diff --git a/backend/djvu/Makefile.am b/backend/djvu/Makefile.am new file mode 100644 index 00000000..6bc9d06e --- /dev/null +++ b/backend/djvu/Makefile.am @@ -0,0 +1,38 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -DMATEICONDIR=\""${prefix}/${DATADIRNAME}/pixmaps"\" \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCE_COMPILATION \ + $(BACKEND_CFLAGS) \ + $(DJVU_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_LTLIBRARIES = libdjvudocument.la + +libdjvudocument_la_SOURCES = \ + djvu-document.c \ + djvu-document.h \ + djvu-document-private.h \ + djvu-links.c \ + djvu-links.h \ + djvu-text-page.c \ + djvu-text-page.h + +libdjvudocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libdjvudocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument.la \ + $(BACKEND_LIBS) \ + $(DJVU_LIBS) + +backend_in_files = djvudocument.evince-backend.in +backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_EVINCE_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/djvu/djvu-document-private.h b/backend/djvu/djvu-document-private.h new file mode 100644 index 00000000..2ec9b67a --- /dev/null +++ b/backend/djvu/djvu-document-private.h @@ -0,0 +1,48 @@ +/* + * Declarations used throughout the djvu classes + * + * Copyright (C) 2006, Michael Hofmann <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __DJVU_DOCUMENT_INTERNAL_H__ +#define __DJVU_DOCUMENT_INTERNAL_H__ + +#include "djvu-document.h" + +#include <libdjvu/ddjvuapi.h> + +struct _DjvuDocument { + EvDocument parent_instance; + + ddjvu_context_t *d_context; + ddjvu_document_t *d_document; + ddjvu_format_t *d_format; + ddjvu_format_t *thumbs_format; + + gchar *uri; + + /* PS exporter */ + gchar *ps_filename; + GString *opts; +}; + +int djvu_document_get_n_pages (EvDocument *document); +void djvu_handle_events (DjvuDocument *djvu_document, + int wait, + GError **error); + +#endif /* __DJVU_DOCUMENT_INTERNAL_H__ */ diff --git a/backend/djvu/djvu-document.c b/backend/djvu/djvu-document.c new file mode 100644 index 00000000..aa0e595d --- /dev/null +++ b/backend/djvu/djvu-document.c @@ -0,0 +1,706 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Copyright (C) 2005, Nickolay V. Shmyrev <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <config.h> +#include "djvu-document.h" +#include "djvu-text-page.h" +#include "djvu-links.h" +#include "djvu-document-private.h" +#include "ev-document-thumbnails.h" +#include "ev-file-exporter.h" +#include "ev-document-misc.h" +#include "ev-document-find.h" +#include "ev-document-links.h" +#include "ev-selection.h" +#include "ev-file-helpers.h" + +#include <glib.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <glib/gi18n-lib.h> +#include <string.h> + +#define SCALE_FACTOR 0.2 + +enum { + PROP_0, + PROP_TITLE +}; + +struct _DjvuDocumentClass +{ + EvDocumentClass parent_class; +}; + +typedef struct _DjvuDocumentClass DjvuDocumentClass; + +static void djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); +static void djvu_document_file_exporter_iface_init (EvFileExporterInterface *iface); +static void djvu_document_find_iface_init (EvDocumentFindInterface *iface); +static void djvu_document_document_links_iface_init (EvDocumentLinksInterface *iface); +static void djvu_selection_iface_init (EvSelectionInterface *iface); + +EV_BACKEND_REGISTER_WITH_CODE (DjvuDocument, djvu_document, + { + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, djvu_document_document_thumbnails_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, djvu_document_file_exporter_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND, djvu_document_find_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS, djvu_document_document_links_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION, djvu_selection_iface_init); + }); + + +#define EV_DJVU_ERROR ev_djvu_error_quark () + +static GQuark +ev_djvu_error_quark (void) +{ + static GQuark q = 0; + if (q == 0) + q = g_quark_from_string ("ev-djvu-quark"); + + return q; +} + +static void +handle_message (const ddjvu_message_t *msg, GError **error) +{ + switch (msg->m_any.tag) { + case DDJVU_ERROR: { + gchar *error_str; + + if (msg->m_error.filename) { + error_str = g_strdup_printf ("DjvuLibre error: %s:%d", + msg->m_error.filename, + msg->m_error.lineno); + } else { + error_str = g_strdup_printf ("DjvuLibre error: %s", + msg->m_error.message); + } + + if (error) { + g_set_error_literal (error, EV_DJVU_ERROR, 0, error_str); + } else { + g_warning ("%s", error_str); + } + + g_free (error_str); + return; + } + break; + default: + break; + } +} + +void +djvu_handle_events (DjvuDocument *djvu_document, int wait, GError **error) +{ + ddjvu_context_t *ctx = djvu_document->d_context; + const ddjvu_message_t *msg; + + if (!ctx) + return; + + if (wait) + ddjvu_message_wait (ctx); + + while ((msg = ddjvu_message_peek (ctx))) { + handle_message (msg, error); + ddjvu_message_pop (ctx); + if (error && *error) + return; + } +} + +static void +djvu_wait_for_message (DjvuDocument *djvu_document, ddjvu_message_tag_t message, GError **error) +{ + ddjvu_context_t *ctx = djvu_document->d_context; + const ddjvu_message_t *msg; + + ddjvu_message_wait (ctx); + while ((msg = ddjvu_message_peek (ctx)) && (msg->m_any.tag != message)) { + handle_message (msg, error); + ddjvu_message_pop (ctx); + if (error && *error) + return; + } + if (msg && msg->m_any.tag == message) + ddjvu_message_pop (ctx); +} + +static gboolean +djvu_document_load (EvDocument *document, + const char *uri, + GError **error) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document); + ddjvu_document_t *doc; + gchar *filename; + gboolean missing_files = FALSE; + GError *djvu_error = NULL; + + /* FIXME: We could actually load uris */ + filename = g_filename_from_uri (uri, NULL, error); + if (!filename) + return FALSE; + + doc = ddjvu_document_create_by_filename (djvu_document->d_context, filename, TRUE); + + if (!doc) { + g_free (filename); + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("DjVu document has incorrect format")); + return FALSE; + } + + if (djvu_document->d_document) + ddjvu_document_release (djvu_document->d_document); + + djvu_document->d_document = doc; + + djvu_wait_for_message (djvu_document, DDJVU_DOCINFO, &djvu_error); + if (djvu_error) { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + djvu_error->message); + g_error_free (djvu_error); + g_free (filename); + ddjvu_document_release (djvu_document->d_document); + djvu_document->d_document = NULL; + + return FALSE; + } + + if (ddjvu_document_decoding_error (djvu_document->d_document)) + djvu_handle_events (djvu_document, TRUE, &djvu_error); + + if (djvu_error) { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + djvu_error->message); + g_error_free (djvu_error); + g_free (filename); + ddjvu_document_release (djvu_document->d_document); + djvu_document->d_document = NULL; + + return FALSE; + } + + g_free (djvu_document->uri); + djvu_document->uri = g_strdup (uri); + + if (ddjvu_document_get_type (djvu_document->d_document) == DDJVU_DOCTYPE_INDIRECT) { + gint n_files; + gint i; + gchar *base; + + base = g_path_get_dirname (filename); + + n_files = ddjvu_document_get_filenum (djvu_document->d_document); + for (i = 0; i < n_files; i++) { + struct ddjvu_fileinfo_s fileinfo; + gchar *file; + + ddjvu_document_get_fileinfo (djvu_document->d_document, + i, &fileinfo); + + if (fileinfo.type != 'P') + continue; + + file = g_build_filename (base, fileinfo.id, NULL); + if (!g_file_test (file, G_FILE_TEST_EXISTS)) { + missing_files = TRUE; + g_free (file); + + break; + } + g_free (file); + } + g_free (base); + } + g_free (filename); + + if (missing_files) { + g_set_error_literal (error, + G_FILE_ERROR, + G_FILE_ERROR_EXIST, + _("The document is composed of several files. " + "One or more of these files cannot be accessed.")); + + return FALSE; + } + + return TRUE; +} + + +static gboolean +djvu_document_save (EvDocument *document, + const char *uri, + GError **error) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document); + + return ev_xfer_uri_simple (djvu_document->uri, uri, error); +} + +int +djvu_document_get_n_pages (EvDocument *document) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document); + + g_return_val_if_fail (djvu_document->d_document, 0); + + return ddjvu_document_get_pagenum (djvu_document->d_document); +} + +static void +document_get_page_size (DjvuDocument *djvu_document, + gint page, + double *width, + double *height) +{ + ddjvu_pageinfo_t info; + ddjvu_status_t r; + + while ((r = ddjvu_document_get_pageinfo(djvu_document->d_document, page, &info)) < DDJVU_JOB_OK) + djvu_handle_events(djvu_document, TRUE, NULL); + + if (r >= DDJVU_JOB_FAILED) + djvu_handle_events(djvu_document, TRUE, NULL); + + *width = info.width * SCALE_FACTOR; + *height = info.height * SCALE_FACTOR; +} + +static void +djvu_document_get_page_size (EvDocument *document, + EvPage *page, + double *width, + double *height) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document); + + g_return_if_fail (djvu_document->d_document); + + document_get_page_size (djvu_document, page->index, + width, height); +} + +static cairo_surface_t * +djvu_document_render (EvDocument *document, + EvRenderContext *rc) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document); + cairo_surface_t *surface; + gchar *pixels; + gint rowstride; + ddjvu_rect_t rrect; + ddjvu_rect_t prect; + ddjvu_page_t *d_page; + ddjvu_page_rotation_t rotation; + double page_width, page_height, tmp; + + d_page = ddjvu_page_create_by_pageno (djvu_document->d_document, rc->page->index); + + while (!ddjvu_page_decoding_done (d_page)) + djvu_handle_events(djvu_document, TRUE, NULL); + + page_width = ddjvu_page_get_width (d_page) * rc->scale * SCALE_FACTOR + 0.5; + page_height = ddjvu_page_get_height (d_page) * rc->scale * SCALE_FACTOR + 0.5; + + switch (rc->rotation) { + case 90: + rotation = DDJVU_ROTATE_90; + tmp = page_height; + page_height = page_width; + page_width = tmp; + + break; + case 180: + rotation = DDJVU_ROTATE_180; + + break; + case 270: + rotation = DDJVU_ROTATE_270; + tmp = page_height; + page_height = page_width; + page_width = tmp; + + break; + default: + rotation = DDJVU_ROTATE_0; + } + + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + page_width, page_height); + rowstride = cairo_image_surface_get_stride (surface); + pixels = (gchar *)cairo_image_surface_get_data (surface); + + prect.x = 0; + prect.y = 0; + prect.w = page_width; + prect.h = page_height; + rrect = prect; + + ddjvu_page_set_rotation (d_page, rotation); + + ddjvu_page_render (d_page, DDJVU_RENDER_COLOR, + &prect, + &rrect, + djvu_document->d_format, + rowstride, + pixels); + + cairo_surface_mark_dirty (surface); + + return surface; +} + +static void +djvu_document_finalize (GObject *object) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (object); + + if (djvu_document->d_document) + ddjvu_document_release (djvu_document->d_document); + + if (djvu_document->opts) + g_string_free (djvu_document->opts, TRUE); + + if (djvu_document->ps_filename) + g_free (djvu_document->ps_filename); + + ddjvu_context_release (djvu_document->d_context); + ddjvu_format_release (djvu_document->d_format); + ddjvu_format_release (djvu_document->thumbs_format); + g_free (djvu_document->uri); + + G_OBJECT_CLASS (djvu_document_parent_class)->finalize (object); +} + +static void +djvu_document_class_init (DjvuDocumentClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + gobject_class->finalize = djvu_document_finalize; + + ev_document_class->load = djvu_document_load; + ev_document_class->save = djvu_document_save; + ev_document_class->get_n_pages = djvu_document_get_n_pages; + ev_document_class->get_page_size = djvu_document_get_page_size; + ev_document_class->render = djvu_document_render; +} + +static gchar * +djvu_text_copy (DjvuDocument *djvu_document, + gint page, + EvRectangle *rectangle) +{ + miniexp_t page_text; + gchar *text = NULL; + + while ((page_text = + ddjvu_document_get_pagetext (djvu_document->d_document, + page, "char")) == miniexp_dummy) + djvu_handle_events (djvu_document, TRUE, NULL); + + if (page_text != miniexp_nil) { + DjvuTextPage *page = djvu_text_page_new (page_text); + + text = djvu_text_page_copy (page, rectangle); + djvu_text_page_free (page); + ddjvu_miniexp_release (djvu_document->d_document, page_text); + } + + return text; +} + +static gchar * +djvu_selection_get_selected_text (EvSelection *selection, + EvPage *page, + EvSelectionStyle style, + EvRectangle *points) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (selection); + double width, height; + EvRectangle rectangle; + gchar *text; + + djvu_document_get_page_size (EV_DOCUMENT (djvu_document), + page, &width, &height); + rectangle.x1 = points->x1 / SCALE_FACTOR; + rectangle.y1 = (height - points->y2) / SCALE_FACTOR; + rectangle.x2 = points->x2 / SCALE_FACTOR; + rectangle.y2 = (height - points->y1) / SCALE_FACTOR; + + text = djvu_text_copy (djvu_document, page->index, &rectangle); + + if (text == NULL) + text = g_strdup (""); + + return text; +} + +static void +djvu_selection_iface_init (EvSelectionInterface *iface) +{ + iface->get_selected_text = djvu_selection_get_selected_text; +} + +static void +djvu_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, + EvRenderContext *rc, + gint *width, + gint *height) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document); + gdouble page_width, page_height; + + djvu_document_get_page_size (EV_DOCUMENT(djvu_document), rc->page, + &page_width, &page_height); + + if (rc->rotation == 90 || rc->rotation == 270) { + *width = (gint) (page_height * rc->scale); + *height = (gint) (page_width * rc->scale); + } else { + *width = (gint) (page_width * rc->scale); + *height = (gint) (page_height * rc->scale); + } +} + +static GdkPixbuf * +djvu_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, + EvRenderContext *rc, + gboolean border) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document); + GdkPixbuf *pixbuf, *rotated_pixbuf; + gdouble page_width, page_height; + gint thumb_width, thumb_height; + guchar *pixels; + + g_return_val_if_fail (djvu_document->d_document, NULL); + + djvu_document_get_page_size (EV_DOCUMENT(djvu_document), rc->page, + &page_width, &page_height); + + thumb_width = (gint) (page_width * rc->scale); + thumb_height = (gint) (page_height * rc->scale); + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, + thumb_width, thumb_height); + gdk_pixbuf_fill (pixbuf, 0xffffffff); + pixels = gdk_pixbuf_get_pixels (pixbuf); + + while (ddjvu_thumbnail_status (djvu_document->d_document, rc->page->index, 1) < DDJVU_JOB_OK) + djvu_handle_events(djvu_document, TRUE, NULL); + + ddjvu_thumbnail_render (djvu_document->d_document, rc->page->index, + &thumb_width, &thumb_height, + djvu_document->thumbs_format, + gdk_pixbuf_get_rowstride (pixbuf), + (gchar *)pixels); + + rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation); + g_object_unref (pixbuf); + + if (border) { + GdkPixbuf *tmp_pixbuf = rotated_pixbuf; + + rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf); + g_object_unref (tmp_pixbuf); + } + + return rotated_pixbuf; +} + +static void +djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface) +{ + iface->get_thumbnail = djvu_document_thumbnails_get_thumbnail; + iface->get_dimensions = djvu_document_thumbnails_get_dimensions; +} + +/* EvFileExporterIface */ +static void +djvu_document_file_exporter_begin (EvFileExporter *exporter, + EvFileExporterContext *fc) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (exporter); + + if (djvu_document->ps_filename) + g_free (djvu_document->ps_filename); + djvu_document->ps_filename = g_strdup (fc->filename); + + g_string_assign (djvu_document->opts, "-page="); +} + +static void +djvu_document_file_exporter_do_page (EvFileExporter *exporter, + EvRenderContext *rc) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (exporter); + + g_string_append_printf (djvu_document->opts, "%d,", (rc->page->index) + 1); +} + +static void +djvu_document_file_exporter_end (EvFileExporter *exporter) +{ + int d_optc = 1; + const char *d_optv[d_optc]; + + DjvuDocument *djvu_document = DJVU_DOCUMENT (exporter); + + FILE *fn = fopen (djvu_document->ps_filename, "w"); + if (fn == NULL) { + g_warning ("Cannot open file “%s”.", djvu_document->ps_filename); + return; + } + + d_optv[0] = djvu_document->opts->str; + + ddjvu_job_t * job = ddjvu_document_print(djvu_document->d_document, fn, d_optc, d_optv); + while (!ddjvu_job_done(job)) { + djvu_handle_events (djvu_document, TRUE, NULL); + } + + fclose(fn); +} + +static EvFileExporterCapabilities +djvu_document_file_exporter_get_capabilities (EvFileExporter *exporter) +{ + return EV_FILE_EXPORTER_CAN_PAGE_SET | + EV_FILE_EXPORTER_CAN_COPIES | + EV_FILE_EXPORTER_CAN_COLLATE | + EV_FILE_EXPORTER_CAN_REVERSE | + EV_FILE_EXPORTER_CAN_GENERATE_PS; +} + +static void +djvu_document_file_exporter_iface_init (EvFileExporterInterface *iface) +{ + iface->begin = djvu_document_file_exporter_begin; + iface->do_page = djvu_document_file_exporter_do_page; + iface->end = djvu_document_file_exporter_end; + iface->get_capabilities = djvu_document_file_exporter_get_capabilities; +} + +static void +djvu_document_init (DjvuDocument *djvu_document) +{ + guint masks[4] = { 0xff0000, 0xff00, 0xff, 0xff000000 }; + + djvu_document->d_context = ddjvu_context_create ("Evince"); + djvu_document->d_format = ddjvu_format_create (DDJVU_FORMAT_RGBMASK32, 4, masks); + ddjvu_format_set_row_order (djvu_document->d_format, 1); + + djvu_document->thumbs_format = ddjvu_format_create (DDJVU_FORMAT_RGB24, 0, 0); + ddjvu_format_set_row_order (djvu_document->thumbs_format, 1); + + djvu_document->ps_filename = NULL; + djvu_document->opts = g_string_new (""); + + djvu_document->d_document = NULL; +} + +static GList * +djvu_document_find_find_text (EvDocumentFind *document, + EvPage *page, + const char *text, + gboolean case_sensitive) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document); + miniexp_t page_text; + gdouble width, height; + GList *matches = NULL, *l; + + g_return_val_if_fail (text != NULL, NULL); + + while ((page_text = ddjvu_document_get_pagetext (djvu_document->d_document, + page->index, + "char")) == miniexp_dummy) + djvu_handle_events (djvu_document, TRUE, NULL); + + if (page_text != miniexp_nil) { + DjvuTextPage *tpage = djvu_text_page_new (page_text); + + djvu_text_page_prepare_search (tpage, case_sensitive); + if (tpage->links->len > 0) { + djvu_text_page_search (tpage, text); + matches = tpage->results; + } + djvu_text_page_free (tpage); + ddjvu_miniexp_release (djvu_document->d_document, page_text); + } + + if (!matches) + return NULL; + + document_get_page_size (djvu_document, page->index, &width, &height); + for (l = matches; l && l->data; l = g_list_next (l)) { + EvRectangle *r = (EvRectangle *)l->data; + gdouble tmp; + + tmp = r->y1; + + r->x1 *= SCALE_FACTOR; + r->x2 *= SCALE_FACTOR; + + tmp = r->y1; + r->y1 = height - r->y2 * SCALE_FACTOR; + r->y2 = height - tmp * SCALE_FACTOR; + } + + + return matches; +} + +static void +djvu_document_find_iface_init (EvDocumentFindInterface *iface) +{ + iface->find_text = djvu_document_find_find_text; +} + +static EvMappingList * +djvu_document_links_get_links (EvDocumentLinks *document_links, + EvPage *page) +{ + return djvu_links_get_links (document_links, page->index, SCALE_FACTOR); +} + +static void +djvu_document_document_links_iface_init (EvDocumentLinksInterface *iface) +{ + iface->has_document_links = djvu_links_has_document_links; + iface->get_links_model = djvu_links_get_links_model; + iface->get_links = djvu_document_links_get_links; + iface->find_link_dest = djvu_links_find_link_dest; +} diff --git a/backend/djvu/djvu-document.h b/backend/djvu/djvu-document.h new file mode 100644 index 00000000..bcb916af --- /dev/null +++ b/backend/djvu/djvu-document.h @@ -0,0 +1,38 @@ +/* djvu-document.h: Implementation of EvDocument for djvu documents + * Copyright (C) 2005, Nickolay V. Shmyrev <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __DJVU_DOCUMENT_H__ +#define __DJVU_DOCUMENT_H__ + +#include "ev-document.h" + +G_BEGIN_DECLS + +#define DJVU_TYPE_DOCUMENT (djvu_document_get_type ()) +#define DJVU_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DJVU_TYPE_DOCUMENT, DjvuDocument)) +#define DJVU_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DJVU_TYPE_DOCUMENT)) + +typedef struct _DjvuDocument DjvuDocument; + +GType djvu_document_get_type (void) G_GNUC_CONST; + +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + +G_END_DECLS + +#endif /* __DJVU_DOCUMENT_H__ */ diff --git a/backend/djvu/djvu-links.c b/backend/djvu/djvu-links.c new file mode 100644 index 00000000..d13af0be --- /dev/null +++ b/backend/djvu/djvu-links.c @@ -0,0 +1,434 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Implements hyperlink functionality for Djvu files. + * Copyright (C) 2006 Pauli Virtanen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <string.h> +#include <glib.h> +#include <libdjvu/miniexp.h> +#include "djvu-document.h" +#include "djvu-links.h" +#include "djvu-document-private.h" +#include "ev-document-links.h" +#include "ev-mapping-list.h" + +static gboolean number_from_miniexp(miniexp_t sexp, int *number) +{ + if (miniexp_numberp (sexp)) { + *number = miniexp_to_int (sexp); + return TRUE; + } else { + return FALSE; + } +} + +static gboolean string_from_miniexp(miniexp_t sexp, const char **str) +{ + if (miniexp_stringp (sexp)) { + *str = miniexp_to_str (sexp); + return TRUE; + } else { + return FALSE; + } +} + +static gboolean number_from_string_10(const gchar *str, guint64 *number) +{ + gchar *end_ptr; + + *number = g_ascii_strtoull(str, &end_ptr, 10); + if (*end_ptr == '\0') { + return TRUE; + } else { + return FALSE; + } +} + +static EvLinkDest * +get_djvu_link_dest (const DjvuDocument *djvu_document, const gchar *link_name, int base_page) +{ + guint64 page_num = 0; + + /* #pagenum, #+pageoffset, #-pageoffset */ + if (g_str_has_prefix (link_name, "#")) { + if (base_page > 0 && g_str_has_prefix (link_name+1, "+")) { + if (number_from_string_10 (link_name + 2, &page_num)) { + return ev_link_dest_new_page (base_page + page_num); + } + } else if (base_page > 0 && g_str_has_prefix (link_name+1, "-")) { + if (number_from_string_10 (link_name + 2, &page_num)) { + return ev_link_dest_new_page (base_page - page_num); + } + } else { + if (number_from_string_10 (link_name + 1, &page_num)) { + return ev_link_dest_new_page (page_num - 1); + } + } + } else { + /* FIXME: component file identifiers */ + } + + return NULL; +} + +static EvLinkAction * +get_djvu_link_action (const DjvuDocument *djvu_document, const gchar *link_name, int base_page) +{ + EvLinkDest *ev_dest = NULL; + EvLinkAction *ev_action = NULL; + + ev_dest = get_djvu_link_dest (djvu_document, link_name, base_page); + + if (ev_dest) { + ev_action = ev_link_action_new_dest (ev_dest); + } else if (strstr(link_name, "://") != NULL) { + /* It's probably an URI */ + ev_action = ev_link_action_new_external_uri (link_name); + } else { + /* FIXME: component file identifiers */ + } + + return ev_action; +} + +static gchar * +str_to_utf8 (const gchar *text) +{ + static const gchar *encodings_to_try[2]; + static gint n_encodings_to_try = 0; + gchar *utf8_text = NULL; + gint i; + + if (n_encodings_to_try == 0) { + const gchar *charset; + gboolean charset_is_utf8; + + charset_is_utf8 = g_get_charset (&charset); + if (!charset_is_utf8) { + encodings_to_try[n_encodings_to_try++] = charset; + } + + if (g_ascii_strcasecmp (charset, "ISO-8859-1") != 0) { + encodings_to_try[n_encodings_to_try++] = "ISO-8859-1"; + } + } + + for (i = 0; i < n_encodings_to_try; i++) { + utf8_text = g_convert (text, -1, "UTF-8", + encodings_to_try[i], + NULL, NULL, NULL); + if (utf8_text) + break; + } + + return utf8_text; +} + +/** + * Builds the index GtkTreeModel from DjVu s-expr + * + * (bookmarks + * ("title1" "dest1" + * ("title12" "dest12" + * ... ) + * ... ) + * ("title2" "dest2" + * ... ) + * ... ) + */ +static void +build_tree (const DjvuDocument *djvu_document, + GtkTreeModel *model, + GtkTreeIter *parent, + miniexp_t iter) +{ + const char *title, *link_dest; + char *title_markup; + + EvLinkAction *ev_action = NULL; + EvLink *ev_link = NULL; + GtkTreeIter tree_iter; + + if (miniexp_car (iter) == miniexp_symbol ("bookmarks")) { + /* The (bookmarks) cons */ + iter = miniexp_cdr (iter); + } else if ( miniexp_length (iter) >= 2 ) { + gchar *utf8_title = NULL; + + /* An entry */ + if (!string_from_miniexp (miniexp_car (iter), &title)) goto unknown_entry; + if (!string_from_miniexp (miniexp_cadr (iter), &link_dest)) goto unknown_entry; + + + if (!g_utf8_validate (title, -1, NULL)) { + utf8_title = str_to_utf8 (title); + title_markup = g_markup_escape_text (utf8_title, -1); + } else { + title_markup = g_markup_escape_text (title, -1); + } + + ev_action = get_djvu_link_action (djvu_document, link_dest, -1); + + if (g_str_has_suffix (link_dest, ".djvu")) { + /* FIXME: component file identifiers */ + } else if (ev_action) { + ev_link = ev_link_new (utf8_title ? utf8_title : title, ev_action); + gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent); + gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter, + EV_DOCUMENT_LINKS_COLUMN_MARKUP, title_markup, + EV_DOCUMENT_LINKS_COLUMN_LINK, ev_link, + EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE, + -1); + g_object_unref (ev_link); + } else { + gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent); + gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter, + EV_DOCUMENT_LINKS_COLUMN_MARKUP, title_markup, + EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE, + -1); + } + + g_free (title_markup); + g_free (utf8_title); + iter = miniexp_cddr (iter); + parent = &tree_iter; + } else { + goto unknown_entry; + } + + for (; iter != miniexp_nil; iter = miniexp_cdr (iter)) { + build_tree (djvu_document, model, parent, miniexp_car (iter)); + } + return; + + unknown_entry: + g_warning ("DjvuLibre error: Unknown entry in bookmarks"); + return; +} + +static gboolean +get_djvu_hyperlink_area (ddjvu_pageinfo_t *page_info, + miniexp_t sexp, + EvMapping *ev_link_mapping) +{ + miniexp_t iter; + + iter = sexp; + + if ((miniexp_car (iter) == miniexp_symbol ("rect") || miniexp_car (iter) == miniexp_symbol ("oval")) + && miniexp_length (iter) == 5) { + /* FIXME: get bounding box for (oval) since Evince doesn't support shaped links */ + int minx, miny, width, height; + + iter = miniexp_cdr (iter); + if (!number_from_miniexp (miniexp_car (iter), &minx)) goto unknown_link; + iter = miniexp_cdr (iter); + if (!number_from_miniexp (miniexp_car (iter), &miny)) goto unknown_link; + iter = miniexp_cdr (iter); + if (!number_from_miniexp (miniexp_car (iter), &width)) goto unknown_link; + iter = miniexp_cdr (iter); + if (!number_from_miniexp (miniexp_car (iter), &height)) goto unknown_link; + + ev_link_mapping->area.x1 = minx; + ev_link_mapping->area.x2 = (minx + width); + ev_link_mapping->area.y1 = (page_info->height - (miny + height)); + ev_link_mapping->area.y2 = (page_info->height - miny); + } else if (miniexp_car (iter) == miniexp_symbol ("poly") + && miniexp_length (iter) >= 5 && miniexp_length (iter) % 2 == 1) { + + /* FIXME: get bounding box since Evince doesn't support shaped links */ + int minx = G_MAXINT, miny = G_MAXINT; + int maxx = G_MININT, maxy = G_MININT; + + iter = miniexp_cdr(iter); + while (iter != miniexp_nil) { + int x, y; + + if (!number_from_miniexp (miniexp_car(iter), &x)) goto unknown_link; + iter = miniexp_cdr (iter); + if (!number_from_miniexp (miniexp_car(iter), &y)) goto unknown_link; + iter = miniexp_cdr (iter); + + minx = MIN (minx, x); + miny = MIN (miny, y); + maxx = MAX (maxx, x); + maxy = MAX (maxy, y); + } + + ev_link_mapping->area.x1 = minx; + ev_link_mapping->area.x2 = maxx; + ev_link_mapping->area.y1 = (page_info->height - maxy); + ev_link_mapping->area.y2 = (page_info->height - miny); + } else { + /* unknown */ + goto unknown_link; + } + + return TRUE; + + unknown_link: + g_warning("DjvuLibre error: Unknown hyperlink area %s", miniexp_to_name(miniexp_car(sexp))); + return FALSE; +} + +static EvMapping * +get_djvu_hyperlink_mapping (DjvuDocument *djvu_document, + int page, + ddjvu_pageinfo_t *page_info, + miniexp_t sexp) +{ + EvMapping *ev_link_mapping = NULL; + EvLinkAction *ev_action = NULL; + miniexp_t iter; + const char *url, *url_target, *comment; + + ev_link_mapping = g_new (EvMapping, 1); + + iter = sexp; + + if (miniexp_car (iter) != miniexp_symbol ("maparea")) goto unknown_mapping; + + iter = miniexp_cdr(iter); + + if (miniexp_caar(iter) == miniexp_symbol("url")) { + if (!string_from_miniexp (miniexp_cadr (miniexp_car (iter)), &url)) goto unknown_mapping; + if (!string_from_miniexp (miniexp_caddr (miniexp_car (iter)), &url_target)) goto unknown_mapping; + } else { + if (!string_from_miniexp (miniexp_car(iter), &url)) goto unknown_mapping; + url_target = NULL; + } + + iter = miniexp_cdr (iter); + if (!string_from_miniexp (miniexp_car(iter), &comment)) goto unknown_mapping; + + iter = miniexp_cdr (iter); + if (!get_djvu_hyperlink_area (page_info, miniexp_car(iter), ev_link_mapping)) goto unknown_mapping; + + iter = miniexp_cdr (iter); + /* FIXME: DjVu hyperlink attributes are ignored */ + + ev_action = get_djvu_link_action (djvu_document, url, page); + if (!ev_action) goto unknown_mapping; + + ev_link_mapping->data = ev_link_new (comment, ev_action); + + return ev_link_mapping; + + unknown_mapping: + if (ev_link_mapping) g_free(ev_link_mapping); + g_warning("DjvuLibre error: Unknown hyperlink %s", miniexp_to_name(miniexp_car(sexp))); + return NULL; +} + + +gboolean +djvu_links_has_document_links (EvDocumentLinks *document_links) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document_links); + miniexp_t outline; + + while ((outline = ddjvu_document_get_outline (djvu_document->d_document)) == miniexp_dummy) + djvu_handle_events (djvu_document, TRUE, NULL); + + if (outline) { + ddjvu_miniexp_release (djvu_document->d_document, outline); + return TRUE; + } + + return FALSE; +} + +EvMappingList * +djvu_links_get_links (EvDocumentLinks *document_links, + gint page, + double scale_factor) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document_links); + GList *retval = NULL; + miniexp_t page_annotations = miniexp_nil; + miniexp_t *hyperlinks = NULL, *iter = NULL; + EvMapping *ev_link_mapping; + ddjvu_pageinfo_t page_info; + + while ((page_annotations = ddjvu_document_get_pageanno (djvu_document->d_document, page)) == miniexp_dummy) + djvu_handle_events (djvu_document, TRUE, NULL); + + while (ddjvu_document_get_pageinfo (djvu_document->d_document, page, &page_info) < DDJVU_JOB_OK) + djvu_handle_events(djvu_document, TRUE, NULL); + + if (page_annotations) { + hyperlinks = ddjvu_anno_get_hyperlinks (page_annotations); + if (hyperlinks) { + for (iter = hyperlinks; *iter; ++iter) { + ev_link_mapping = get_djvu_hyperlink_mapping (djvu_document, page, &page_info, *iter); + if (ev_link_mapping) { + ev_link_mapping->area.x1 *= scale_factor; + ev_link_mapping->area.x2 *= scale_factor; + ev_link_mapping->area.y1 *= scale_factor; + ev_link_mapping->area.y2 *= scale_factor; + retval = g_list_prepend (retval, ev_link_mapping); + } + } + free (hyperlinks); + } + ddjvu_miniexp_release (djvu_document->d_document, page_annotations); + } + + return ev_mapping_list_new (page, retval, (GDestroyNotify)g_object_unref); +} + +EvLinkDest * +djvu_links_find_link_dest (EvDocumentLinks *document_links, + const gchar *link_name) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document_links); + EvLinkDest *ev_dest = NULL; + + ev_dest = get_djvu_link_dest (djvu_document, link_name, -1); + + if (!ev_dest) { + g_warning ("DjvuLibre error: unknown link destination %s", link_name); + } + + return ev_dest; +} + +GtkTreeModel * +djvu_links_get_links_model (EvDocumentLinks *document_links) +{ + DjvuDocument *djvu_document = DJVU_DOCUMENT (document_links); + GtkTreeModel *model = NULL; + miniexp_t outline = miniexp_nil; + + while ((outline = ddjvu_document_get_outline (djvu_document->d_document)) == miniexp_dummy) + djvu_handle_events (djvu_document, TRUE, NULL); + + if (outline) { + model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS, + G_TYPE_STRING, + G_TYPE_OBJECT, + G_TYPE_BOOLEAN, + G_TYPE_STRING); + build_tree (djvu_document, model, NULL, outline); + + ddjvu_miniexp_release (djvu_document->d_document, outline); + } + + return model; +} diff --git a/backend/djvu/djvu-links.h b/backend/djvu/djvu-links.h new file mode 100644 index 00000000..76d9072c --- /dev/null +++ b/backend/djvu/djvu-links.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2006 Pauli Virtanen <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __DJVU_LINK_H__ +#define __DJVU_LINK_H__ + +#include "ev-document-links.h" +#include "djvu-document.h" + +#include <glib.h> + +GtkTreeModel *djvu_links_get_links_model (EvDocumentLinks *document_links); +EvMappingList *djvu_links_get_links (EvDocumentLinks *document_links, + gint page, + double scale_factor); +EvLinkDest *djvu_links_find_link_dest (EvDocumentLinks *document_links, + const gchar *link_name); +gboolean djvu_links_has_document_links (EvDocumentLinks *document_links); + +#endif /* __DJVU_LINK_H__ */ diff --git a/backend/djvu/djvu-text-page.c b/backend/djvu/djvu-text-page.c new file mode 100644 index 00000000..3f171d1e --- /dev/null +++ b/backend/djvu/djvu-text-page.c @@ -0,0 +1,445 @@ +/* + * Implements search and copy functionality for Djvu files. + * Copyright (C) 2006 Michael Hofmann <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <string.h> +#include <glib.h> +#include <libdjvu/miniexp.h> +#include "djvu-text-page.h" + + +/** + * djvu_text_page_selection_process: + * @page: #DjvuTextPage instance + * @p: s-expression to append + * @delimit: character/word/... delimiter + * + * Appends the string in @p to the page text. + * + * Returns: whether the end was not reached in this s-expression + */ +static gboolean +djvu_text_page_selection_process (DjvuTextPage *page, + miniexp_t p, + int delimit) +{ + if (page->text || p == page->start) { + char *token_text = (char *) miniexp_to_str (miniexp_nth (5, p)); + if (page->text) { + char *new_text = + g_strjoin (delimit & 2 ? "\n" : + delimit & 1 ? " " : NULL, + page->text, token_text, + NULL); + g_free (page->text); + page->text = new_text; + } else + page->text = g_strdup (token_text); + if (p == page->end) + return FALSE; + } + return TRUE; +} + +/** + * djvu_text_page_selection: + * @page: #DjvuTextPage instance + * @p: tree to append + * @delimit: character/word/... delimiter + * + * Walks the tree in @p and appends the text with + * djvu_text_page_selection_process() for all s-expressions + * between the start and end fields. + * + * Returns: whether the end was not reached in this subtree + */ +static gboolean +djvu_text_page_selection (DjvuTextPage *page, + miniexp_t p, + int delimit) +{ + g_return_val_if_fail (miniexp_consp (p) && miniexp_symbolp + (miniexp_car (p)), FALSE); + + if (miniexp_car (p) != page->char_symbol) + delimit |= miniexp_car (p) == page->word_symbol ? 1 : 2; + + miniexp_t deeper = miniexp_cddr (miniexp_cdddr (p)); + while (deeper != miniexp_nil) { + miniexp_t str = miniexp_car (deeper); + if (miniexp_stringp (str)) { + if (!djvu_text_page_selection_process + (page, p, delimit)) + return FALSE; + } else { + if (!djvu_text_page_selection + (page, str, delimit)) + return FALSE; + } + delimit = 0; + deeper = miniexp_cdr (deeper); + } + return TRUE; +} + +static void +djvu_text_page_limits_process (DjvuTextPage *page, + miniexp_t p, + EvRectangle *rect) +{ + EvRectangle current; + + current.x1 = miniexp_to_int (miniexp_nth (1, p)); + current.y1 = miniexp_to_int (miniexp_nth (2, p)); + current.x2 = miniexp_to_int (miniexp_nth (3, p)); + current.y2 = miniexp_to_int (miniexp_nth (4, p)); + if (current.x2 >= rect->x1 && current.y1 <= rect->y2 && + current.x1 <= rect->x2 && current.y2 >= rect->y1) { + if (page->start == miniexp_nil) + page->start = p; + page->end = p; + } +} + + +static void +djvu_text_page_limits (DjvuTextPage *page, + miniexp_t p, + EvRectangle *rect) +{ + g_return_if_fail (miniexp_consp (p) && + miniexp_symbolp (miniexp_car (p))); + + miniexp_t deeper = miniexp_cddr (miniexp_cdddr (p)); + while (deeper != miniexp_nil) { + miniexp_t str = miniexp_car (deeper); + if (miniexp_stringp (str)) + djvu_text_page_limits_process (page, p, rect); + else + djvu_text_page_limits (page, str, rect); + + deeper = miniexp_cdr (deeper); + } +} + +char * +djvu_text_page_copy (DjvuTextPage *page, + EvRectangle *rectangle) +{ + char* text; + + page->start = miniexp_nil; + page->end = miniexp_nil; + djvu_text_page_limits (page, page->text_structure, rectangle); + djvu_text_page_selection (page, page->text_structure, 0); + + /* Do not free the string */ + text = page->text; + page->text = NULL; + + return text; +} + +/** + * djvu_text_page_position: + * @page: #DjvuTextPage instance + * @position: index in the page text + * + * Returns the closest s-expression that contains the given position in + * the page text. + * + * Returns: closest s-expression + */ +static miniexp_t +djvu_text_page_position (DjvuTextPage *page, + int position) +{ + GArray *links = page->links; + int low = 0; + int hi = links->len - 1; + int mid = 0; + + g_return_val_if_fail (hi >= 0, miniexp_nil); + + /* Shamelessly copied from GNU classpath */ + while (low <= hi) { + mid = (low + hi) >> 1; + DjvuTextLink *link = + &g_array_index (links, DjvuTextLink, mid); + if (link->position == position) + break; + else if (link->position > position) + hi = --mid; + else + low = mid + 1; + } + + return g_array_index (page->links, DjvuTextLink, mid).pair; +} + +/** + * djvu_text_page_union: + * @target: first rectangle and result + * @source: second rectangle + * + * Calculates the bounding box of two rectangles and stores the reuslt + * in the first. + */ +static void +djvu_text_page_union (EvRectangle *target, + EvRectangle *source) +{ + if (source->x1 < target->x1) + target->x1 = source->x1; + if (source->x2 > target->x2) + target->x2 = source->x2; + if (source->y1 < target->y1) + target->y1 = source->y1; + if (source->y2 > target->y2) + target->y2 = source->y2; +} + +/** + * djvu_text_page_sexpr_process: + * @page: #DjvuTextPage instance + * @p: s-expression to append + * @start: first s-expression in the selection + * @end: last s-expression in the selection + * + * Appends the rectangle defined by @p to the internal bounding box rectangle. + * + * Returns: whether the end was not reached in this s-expression + */ +static gboolean +djvu_text_page_sexpr_process (DjvuTextPage *page, + miniexp_t p, + miniexp_t start, + miniexp_t end) +{ + if (page->bounding_box || p == start) { + EvRectangle *new_rectangle = ev_rectangle_new (); + new_rectangle->x1 = miniexp_to_int (miniexp_nth (1, p)); + new_rectangle->y1 = miniexp_to_int (miniexp_nth (2, p)); + new_rectangle->x2 = miniexp_to_int (miniexp_nth (3, p)); + new_rectangle->y2 = miniexp_to_int (miniexp_nth (4, p)); + if (page->bounding_box) { + djvu_text_page_union (page->bounding_box, + new_rectangle); + g_free (new_rectangle); + } else + page->bounding_box = new_rectangle; + if (p == end) + return FALSE; + } + return TRUE; +} + +/** + * djvu_text_page_sexpr: + * @page: #DjvuTextPage instance + * @p: tree to append + * @start: first s-expression in the selection + * @end: last s-expression in the selection + * + * Walks the tree in @p and extends the rectangle with + * djvu_text_page_process() for all s-expressions between @start and @end. + * + * Returns: whether the end was not reached in this subtree + */ +static gboolean +djvu_text_page_sexpr (DjvuTextPage *page, + miniexp_t p, + miniexp_t start, + miniexp_t end) +{ + g_return_val_if_fail (miniexp_consp (p) && miniexp_symbolp + (miniexp_car (p)), FALSE); + + miniexp_t deeper = miniexp_cddr (miniexp_cdddr (p)); + while (deeper != miniexp_nil) { + miniexp_t str = miniexp_car (deeper); + if (miniexp_stringp (str)) { + if (!djvu_text_page_sexpr_process + (page, p, start, end)) + return FALSE; + } else { + if (!djvu_text_page_sexpr + (page, str, start, end)) + return FALSE; + } + deeper = miniexp_cdr (deeper); + } + return TRUE; +} + +/** + * djvu_text_page_box: + * @page: #DjvuTextPage instance + * @start: first s-expression in the selection + * @end: last s-expression in the selection + * + * Builds a rectangle that contains all s-expressions in the given range. + */ +static EvRectangle * +djvu_text_page_box (DjvuTextPage *page, + miniexp_t start, + miniexp_t end) +{ + page->bounding_box = NULL; + djvu_text_page_sexpr (page, page->text_structure, start, end); + return page->bounding_box; +} + +/** + * djvu_text_page_append_search: + * @page: #DjvuTextPage instance + * @p: tree to append + * @case_sensitive: do not ignore case + * @delimit: insert spaces because of higher (sentence/paragraph/...) break + * + * Appends the tree in @p to the internal text string. + */ +static void +djvu_text_page_append_text (DjvuTextPage *page, + miniexp_t p, + gboolean case_sensitive, + gboolean delimit) +{ + char *token_text; + + g_return_if_fail (miniexp_consp (p) && + miniexp_symbolp (miniexp_car (p))); + + delimit |= page->char_symbol != miniexp_car (p); + + miniexp_t deeper = miniexp_cddr (miniexp_cdddr (p)); + while (deeper != miniexp_nil) { + miniexp_t data = miniexp_car (deeper); + if (miniexp_stringp (data)) { + DjvuTextLink link; + link.position = page->text == NULL ? 0 : + strlen (page->text); + link.pair = p; + g_array_append_val (page->links, link); + + token_text = (char *) miniexp_to_str (data); + if (!case_sensitive) + token_text = g_utf8_casefold (token_text, -1); + if (page->text == NULL) + page->text = g_strdup (token_text); + else { + char *new_text = + g_strjoin (delimit ? " " : NULL, + page->text, token_text, + NULL); + g_free (page->text); + page->text = new_text; + } + if (!case_sensitive) + g_free (token_text); + } else + djvu_text_page_append_text (page, data, + case_sensitive, delimit); + delimit = FALSE; + deeper = miniexp_cdr (deeper); + } +} + +/** + * djvu_text_page_search: + * @page: #DjvuTextPage instance + * @text: text to search + * + * Searches the page for the given text. The results list has to be + * externally freed afterwards. + */ +void +djvu_text_page_search (DjvuTextPage *page, + const char *text) +{ + char *haystack = page->text; + int search_len; + EvRectangle *result; + if (page->links->len == 0) + return; + + search_len = strlen (text); + while ((haystack = strstr (haystack, text)) != NULL) { + int start_p = haystack - page->text; + miniexp_t start = djvu_text_page_position (page, start_p); + int end_p = start_p + search_len - 1; + miniexp_t end = djvu_text_page_position (page, end_p); + result = djvu_text_page_box (page, start, end); + g_assert (result); + page->results = g_list_prepend (page->results, result); + haystack = haystack + search_len; + } + page->results = g_list_reverse (page->results); +} + + +/** + * djvu_text_page_prepare_search: + * @page: #DjvuTextPage instance + * @case_sensitive: do not ignore case + * + * Indexes the page text and prepares the page for subsequent searches. + */ +void +djvu_text_page_prepare_search (DjvuTextPage *page, + gboolean case_sensitive) +{ + djvu_text_page_append_text (page, page->text_structure, + case_sensitive, FALSE); +} + +/** + * djvu_text_page_new: + * @text: S-expression of the page text + * + * Creates a new page to search. + * + * Returns: new #DjvuTextPage instance + */ +DjvuTextPage * +djvu_text_page_new (miniexp_t text) +{ + DjvuTextPage *page; + + page = g_new0 (DjvuTextPage, 1); + page->links = g_array_new (FALSE, FALSE, sizeof (DjvuTextLink)); + page->char_symbol = miniexp_symbol ("char"); + page->word_symbol = miniexp_symbol ("word"); + page->text_structure = text; + return page; +} + +/** + * djvu_text_page_free: + * @page: #DjvuTextPage instance + * + * Frees the given #DjvuTextPage instance. + */ +void +djvu_text_page_free (DjvuTextPage *page) +{ + g_free (page->text); + g_array_free (page->links, TRUE); + g_free (page); +} diff --git a/backend/djvu/djvu-text-page.h b/backend/djvu/djvu-text-page.h new file mode 100644 index 00000000..6e16f259 --- /dev/null +++ b/backend/djvu/djvu-text-page.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2006 Michael Hofmann <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __DJVU_TEXT_PAGE_H__ +#define __DJVU_TEXT_PAGE_H__ + +#include "ev-document.h" + +#include <string.h> +#include <glib.h> +#include <libdjvu/miniexp.h> + + +typedef struct _DjvuTextPage DjvuTextPage; +typedef struct _DjvuTextLink DjvuTextLink; + +struct _DjvuTextPage { + char *text; + GArray *links; + GList *results; + miniexp_t char_symbol; + miniexp_t word_symbol; + EvRectangle *bounding_box; + miniexp_t text_structure; + miniexp_t start; + miniexp_t end; +}; + +struct _DjvuTextLink { + int position; + miniexp_t pair; +}; + +char * djvu_text_page_copy (DjvuTextPage *page, + EvRectangle *rectangle); +void djvu_text_page_prepare_search (DjvuTextPage *page, + gboolean case_sensitive); +void djvu_text_page_search (DjvuTextPage *page, + const char *text); +DjvuTextPage* djvu_text_page_new (miniexp_t text); +void djvu_text_page_free (DjvuTextPage *page); + +#endif /* __DJVU_TEXT_PAGE_H__ */ + diff --git a/backend/djvu/djvudocument.evince-backend.in b/backend/djvu/djvudocument.evince-backend.in new file mode 100644 index 00000000..485af118 --- /dev/null +++ b/backend/djvu/djvudocument.evince-backend.in @@ -0,0 +1,4 @@ +[Evince Backend] +Module=djvudocument +_TypeDescription=DjVu Documents +MimeType=image/vnd.djvu diff --git a/backend/dvi/Makefile.am b/backend/dvi/Makefile.am new file mode 100644 index 00000000..6dcd50ed --- /dev/null +++ b/backend/dvi/Makefile.am @@ -0,0 +1,47 @@ +SUBDIRS = mdvi-lib + +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -I$(srcdir)/mdvi-lib \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCE_COMPILATION \ + $(WARN_CFLAGS) \ + $(BACKEND_CFLAGS) \ + $(SPECTRE_CFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_LTLIBRARIES = libdvidocument.la + +libdvidocument_la_SOURCES = \ + dvi-document.c \ + dvi-document.h \ + cairo-device.c \ + cairo-device.h \ + texmfcnf.c \ + texmfcnf.h \ + fonts.c \ + fonts.h + +libdvidocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libdvidocument_la_LIBADD = \ + mdvi-lib/libmdvi.la \ + -lkpathsea \ + $(top_builddir)/libdocument/libevdocument.la \ + $(BACKEND_LIBS) \ + $(SPECTRE_LIBS) + +if WITH_TYPE1_FONTS +libdvidocument_la_LIBADD += -lt1 +endif + +backend_in_files = dvidocument.evince-backend.in +backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_EVINCE_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/dvi/cairo-device.c b/backend/dvi/cairo-device.c new file mode 100644 index 00000000..47425cad --- /dev/null +++ b/backend/dvi/cairo-device.c @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2007 Carlos Garcia Campos <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> + +#include <stdlib.h> +#include <gdk/gdk.h> +#ifdef HAVE_SPECTRE +#include <libspectre/spectre.h> +#endif + +#include "cairo-device.h" + +typedef struct { + cairo_t *cr; + + gint xmargin; + gint ymargin; + + gdouble scale; + + Ulong fg; + Ulong bg; + +} DviCairoDevice; + +static void +dvi_cairo_draw_glyph (DviContext *dvi, + DviFontChar *ch, + int x0, + int y0) +{ + DviCairoDevice *cairo_device; + int x, y, w, h; + gboolean isbox; + DviGlyph *glyph; + cairo_surface_t *surface; + + cairo_device = (DviCairoDevice *) dvi->device.device_data; + + glyph = &ch->grey; + + isbox = (glyph->data == NULL || + (dvi->params.flags & MDVI_PARAM_CHARBOXES) || + MDVI_GLYPH_ISEMPTY (glyph->data)); + + x = - glyph->x + x0 + cairo_device->xmargin; + y = - glyph->y + y0 + cairo_device->ymargin; + w = glyph->w; + h = glyph->h; + + surface = cairo_get_target (cairo_device->cr); + if (x < 0 || y < 0 + || x + w > cairo_image_surface_get_width (surface) + || y + h > cairo_image_surface_get_height (surface)) + return; + + cairo_save (cairo_device->cr); + if (isbox) { + cairo_rectangle (cairo_device->cr, + x - cairo_device->xmargin, + y - cairo_device->ymargin, + w, h); + cairo_stroke (cairo_device->cr); + } else { + cairo_translate (cairo_device->cr, x, y); + cairo_set_source_surface (cairo_device->cr, + (cairo_surface_t *) glyph->data, + 0, 0); + cairo_paint (cairo_device->cr); + } + + cairo_restore (cairo_device->cr); +} + +static void +dvi_cairo_draw_rule (DviContext *dvi, + int x, + int y, + Uint width, + Uint height, + int fill) +{ + DviCairoDevice *cairo_device; + Ulong color; + + cairo_device = (DviCairoDevice *) dvi->device.device_data; + + color = cairo_device->fg; + + cairo_save (cairo_device->cr); + + cairo_set_line_width (cairo_device->cr, + cairo_get_line_width (cairo_device->cr) * cairo_device->scale); + cairo_set_source_rgb (cairo_device->cr, + ((color >> 16) & 0xff) / 255., + ((color >> 8) & 0xff) / 255., + ((color >> 0) & 0xff) / 255.); + + cairo_rectangle (cairo_device->cr, + x + cairo_device->xmargin, + y + cairo_device->ymargin, + width, height); + if (fill == 0) { + cairo_stroke (cairo_device->cr); + } else { + cairo_fill (cairo_device->cr); + } + + cairo_restore (cairo_device->cr); +} + +#ifdef HAVE_SPECTRE +static void +dvi_cairo_draw_ps (DviContext *dvi, + const char *filename, + int x, + int y, + Uint width, + Uint height) +{ + DviCairoDevice *cairo_device; + unsigned char *data = NULL; + int row_length; + SpectreDocument *psdoc; + SpectreRenderContext *rc; + int w, h; + SpectreStatus status; + cairo_surface_t *image; + + cairo_device = (DviCairoDevice *) dvi->device.device_data; + + psdoc = spectre_document_new (); + spectre_document_load (psdoc, filename); + if (spectre_document_status (psdoc)) { + spectre_document_free (psdoc); + return; + } + + spectre_document_get_page_size (psdoc, &w, &h); + + rc = spectre_render_context_new (); + spectre_render_context_set_scale (rc, + (double)width / w, + (double)height / h); + spectre_document_render_full (psdoc, rc, &data, &row_length); + status = spectre_document_status (psdoc); + + spectre_render_context_free (rc); + spectre_document_free (psdoc); + + if (status) { + g_warning ("Error rendering PS document %s: %s\n", + filename, spectre_status_to_string (status)); + free (data); + + return; + } + + image = cairo_image_surface_create_for_data ((unsigned char *)data, + CAIRO_FORMAT_RGB24, + width, height, + row_length); + + cairo_save (cairo_device->cr); + + cairo_translate (cairo_device->cr, + x + cairo_device->xmargin, + y + cairo_device->ymargin); + cairo_set_source_surface (cairo_device->cr, image, 0, 0); + cairo_paint (cairo_device->cr); + + cairo_restore (cairo_device->cr); + + cairo_surface_destroy (image); + free (data); +} +#endif /* HAVE_SPECTRE */ + +static int +dvi_cairo_alloc_colors (void *device_data, + Ulong *pixels, + int npixels, + Ulong fg, + Ulong bg, + double gamma, + int density) +{ + double frac; + GdkColor color, color_fg, color_bg; + int i, n; + + color_bg.red = (bg >> 16) & 0xff; + color_bg.green = (bg >> 8) & 0xff; + color_bg.blue = (bg >> 0) & 0xff; + + color_fg.red = (fg >> 16) & 0xff; + color_fg.green = (fg >> 8) & 0xff; + color_fg.blue = (fg >> 0) & 0xff; + + n = npixels - 1; + for (i = 0; i < npixels; i++) { + frac = (gamma > 0) ? + pow ((double)i / n, 1 / gamma) : + 1 - pow ((double)(n - i) / n, -gamma); + + color.red = frac * ((double)color_fg.red - color_bg.red) + color_bg.red; + color.green = frac * ((double)color_fg.green - color_bg.green) + color_bg.green; + color.blue = frac * ((double)color_fg.blue - color_bg.blue) + color_bg.blue; + + pixels[i] = (color.red << 16) + (color.green << 8) + color.blue + 0xff000000; + } + + return npixels; +} + +static void * +dvi_cairo_create_image (void *device_data, + Uint width, + Uint height, + Uint bpp) +{ + return cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); +} + +static void +dvi_cairo_free_image (void *ptr) +{ + cairo_surface_destroy ((cairo_surface_t *)ptr); +} + +static void +dvi_cairo_put_pixel (void *image, int x, int y, Ulong color) +{ + cairo_surface_t *surface; + gint rowstride; + guint32 *p; + + surface = (cairo_surface_t *) image; + + rowstride = cairo_image_surface_get_stride (surface); + p = (guint32*) (cairo_image_surface_get_data (surface) + y * rowstride + x * 4); + + *p = color; +} + +static void +dvi_cairo_set_color (void *device_data, Ulong fg, Ulong bg) +{ + DviCairoDevice *cairo_device = (DviCairoDevice *) device_data; + + cairo_device->fg = fg; + cairo_device->bg = bg; +} + +/* Public methods */ +void +mdvi_cairo_device_init (DviDevice *device) +{ + device->device_data = g_new0 (DviCairoDevice, 1); + + device->draw_glyph = dvi_cairo_draw_glyph; + device->draw_rule = dvi_cairo_draw_rule; + device->alloc_colors = dvi_cairo_alloc_colors; + device->create_image = dvi_cairo_create_image; + device->free_image = dvi_cairo_free_image; + device->put_pixel = dvi_cairo_put_pixel; + device->set_color = dvi_cairo_set_color; +#ifdef HAVE_SPECTRE + device->draw_ps = dvi_cairo_draw_ps; +#else + device->draw_ps = NULL; +#endif + device->refresh = NULL; +} + +void +mdvi_cairo_device_free (DviDevice *device) +{ + DviCairoDevice *cairo_device; + + cairo_device = (DviCairoDevice *) device->device_data; + + if (cairo_device->cr) + cairo_destroy (cairo_device->cr); + + g_free (cairo_device); +} + +cairo_surface_t * +mdvi_cairo_device_get_surface (DviDevice *device) +{ + DviCairoDevice *cairo_device; + + cairo_device = (DviCairoDevice *) device->device_data; + + return cairo_surface_reference (cairo_get_target (cairo_device->cr)); +} + +void +mdvi_cairo_device_render (DviContext* dvi) +{ + DviCairoDevice *cairo_device; + gint page_width; + gint page_height; + cairo_surface_t *surface; + guchar *pixels; + gint rowstride; + static const cairo_user_data_key_t key; + + cairo_device = (DviCairoDevice *) dvi->device.device_data; + + if (cairo_device->cr) + cairo_destroy (cairo_device->cr); + + page_width = dvi->dvi_page_w * dvi->params.conv + 2 * cairo_device->xmargin; + page_height = dvi->dvi_page_h * dvi->params.vconv + 2 * cairo_device->ymargin; + + rowstride = page_width * 4; + pixels = (guchar *) g_malloc (page_height * rowstride); + memset (pixels, 0xff, page_height * rowstride); + + surface = cairo_image_surface_create_for_data (pixels, + CAIRO_FORMAT_RGB24, + page_width, page_height, + rowstride); + cairo_surface_set_user_data (surface, &key, + pixels, (cairo_destroy_func_t)g_free); + + cairo_device->cr = cairo_create (surface); + cairo_surface_destroy (surface); + + mdvi_dopage (dvi, dvi->currpage); +} + +void +mdvi_cairo_device_set_margins (DviDevice *device, + gint xmargin, + gint ymargin) +{ + DviCairoDevice *cairo_device; + + cairo_device = (DviCairoDevice *) device->device_data; + + cairo_device->xmargin = xmargin; + cairo_device->ymargin = ymargin; +} + +void +mdvi_cairo_device_set_scale (DviDevice *device, + gdouble scale) +{ + DviCairoDevice *cairo_device; + + cairo_device = (DviCairoDevice *) device->device_data; + + cairo_device->scale = scale; +} diff --git a/backend/dvi/cairo-device.h b/backend/dvi/cairo-device.h new file mode 100644 index 00000000..bf76d2af --- /dev/null +++ b/backend/dvi/cairo-device.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 Carlos Garcia Campos <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef MDVI_CAIRO_DEVICE +#define MDVI_CAIRO_DEVICE + +#include <glib.h> +#include <cairo.h> + +#include "mdvi.h" + +G_BEGIN_DECLS + +void mdvi_cairo_device_init (DviDevice *device); +void mdvi_cairo_device_free (DviDevice *device); +cairo_surface_t *mdvi_cairo_device_get_surface (DviDevice *device); +void mdvi_cairo_device_render (DviContext* dvi); +void mdvi_cairo_device_set_margins (DviDevice *device, + gint xmargin, + gint ymargin); +void mdvi_cairo_device_set_scale (DviDevice *device, + gdouble scale); + +G_END_DECLS + +#endif /* MDVI_CAIRO_DEVICE */ diff --git a/backend/dvi/dvi-document.c b/backend/dvi/dvi-document.c new file mode 100644 index 00000000..a4a3dc6d --- /dev/null +++ b/backend/dvi/dvi-document.c @@ -0,0 +1,610 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Copyright (C) 2005, Nickolay V. Shmyrev <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "dvi-document.h" +#include "texmfcnf.h" +#include "ev-document-thumbnails.h" +#include "ev-document-misc.h" +#include "ev-file-exporter.h" +#include "ev-file-helpers.h" + +#include "mdvi.h" +#include "fonts.h" +#include "color.h" +#include "cairo-device.h" + +#include <glib/gi18n-lib.h> +#include <ctype.h> +#ifdef G_OS_WIN32 +# define WIFEXITED(x) ((x) != 3) +# define WEXITSTATUS(x) (x) +#else +# include <sys/wait.h> +#endif +#include <stdlib.h> + +GMutex *dvi_context_mutex = NULL; + +enum { + PROP_0, + PROP_TITLE +}; + +struct _DviDocumentClass +{ + EvDocumentClass parent_class; +}; + +struct _DviDocument +{ + EvDocument parent_instance; + + DviContext *context; + DviPageSpec *spec; + DviParams *params; + + /* To let document scale we should remember width and height */ + double base_width; + double base_height; + + gchar *uri; + + /* PDF exporter */ + gchar *exporter_filename; + GString *exporter_opts; +}; + +typedef struct _DviDocumentClass DviDocumentClass; + +static void dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); +static void dvi_document_file_exporter_iface_init (EvFileExporterInterface *iface); +static void dvi_document_do_color_special (DviContext *dvi, + const char *prefix, + const char *arg); + +EV_BACKEND_REGISTER_WITH_CODE (DviDocument, dvi_document, + { + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, dvi_document_document_thumbnails_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, dvi_document_file_exporter_iface_init); + }); + +static gboolean +dvi_document_load (EvDocument *document, + const char *uri, + GError **error) +{ + gchar *filename; + DviDocument *dvi_document = DVI_DOCUMENT(document); + + filename = g_filename_from_uri (uri, NULL, error); + if (!filename) + return FALSE; + + g_mutex_lock (dvi_context_mutex); + if (dvi_document->context) + mdvi_destroy_context (dvi_document->context); + + dvi_document->context = mdvi_init_context(dvi_document->params, dvi_document->spec, filename); + g_mutex_unlock (dvi_context_mutex); + g_free (filename); + + if (!dvi_document->context) { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("DVI document has incorrect format")); + return FALSE; + } + + mdvi_cairo_device_init (&dvi_document->context->device); + + + dvi_document->base_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv + + 2 * unit2pix(dvi_document->params->dpi, MDVI_HMARGIN) / dvi_document->params->hshrink; + + dvi_document->base_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv + + 2 * unit2pix(dvi_document->params->vdpi, MDVI_VMARGIN) / dvi_document->params->vshrink; + + g_free (dvi_document->uri); + dvi_document->uri = g_strdup (uri); + + return TRUE; +} + + +static gboolean +dvi_document_save (EvDocument *document, + const char *uri, + GError **error) +{ + DviDocument *dvi_document = DVI_DOCUMENT (document); + + return ev_xfer_uri_simple (dvi_document->uri, uri, error); +} + +static int +dvi_document_get_n_pages (EvDocument *document) +{ + DviDocument *dvi_document = DVI_DOCUMENT (document); + + return dvi_document->context->npages; +} + +static void +dvi_document_get_page_size (EvDocument *document, + EvPage *page, + double *width, + double *height) +{ + DviDocument *dvi_document = DVI_DOCUMENT (document); + + *width = dvi_document->base_width; + *height = dvi_document->base_height;; +} + +static cairo_surface_t * +dvi_document_render (EvDocument *document, + EvRenderContext *rc) +{ + cairo_surface_t *surface; + cairo_surface_t *rotated_surface; + DviDocument *dvi_document = DVI_DOCUMENT(document); + gint required_width, required_height; + gint proposed_width, proposed_height; + gint xmargin = 0, ymargin = 0; + + /* We should protect our context since it's not + * thread safe. The work to the future - + * let context render page independently + */ + g_mutex_lock (dvi_context_mutex); + + mdvi_setpage (dvi_document->context, rc->page->index); + + mdvi_set_shrink (dvi_document->context, + (int)((dvi_document->params->hshrink - 1) / rc->scale) + 1, + (int)((dvi_document->params->vshrink - 1) / rc->scale) + 1); + + required_width = dvi_document->base_width * rc->scale + 0.5; + required_height = dvi_document->base_height * rc->scale + 0.5; + proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv; + proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv; + + if (required_width >= proposed_width) + xmargin = (required_width - proposed_width) / 2; + if (required_height >= proposed_height) + ymargin = (required_height - proposed_height) / 2; + + mdvi_cairo_device_set_margins (&dvi_document->context->device, xmargin, ymargin); + mdvi_cairo_device_set_scale (&dvi_document->context->device, rc->scale); + mdvi_cairo_device_render (dvi_document->context); + surface = mdvi_cairo_device_get_surface (&dvi_document->context->device); + + g_mutex_unlock (dvi_context_mutex); + + rotated_surface = ev_document_misc_surface_rotate_and_scale (surface, + required_width, + required_height, + rc->rotation); + cairo_surface_destroy (surface); + + return rotated_surface; +} + +static void +dvi_document_finalize (GObject *object) +{ + DviDocument *dvi_document = DVI_DOCUMENT(object); + + g_mutex_lock (dvi_context_mutex); + if (dvi_document->context) { + mdvi_cairo_device_free (&dvi_document->context->device); + mdvi_destroy_context (dvi_document->context); + } + g_mutex_unlock (dvi_context_mutex); + + if (dvi_document->params) + g_free (dvi_document->params); + + if (dvi_document->exporter_filename) + g_free (dvi_document->exporter_filename); + + if (dvi_document->exporter_opts) + g_string_free (dvi_document->exporter_opts, TRUE); + + g_free (dvi_document->uri); + + G_OBJECT_CLASS (dvi_document_parent_class)->finalize (object); +} + +static gboolean +dvi_document_support_synctex (EvDocument *document) +{ + return TRUE; +} + +static void +dvi_document_class_init (DviDocumentClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + gchar *texmfcnf; + + gobject_class->finalize = dvi_document_finalize; + + texmfcnf = get_texmfcnf(); + mdvi_init_kpathsea ("evince", MDVI_MFMODE, MDVI_FALLBACK_FONT, MDVI_DPI, texmfcnf); + g_free(texmfcnf); + + mdvi_register_special ("Color", "color", NULL, dvi_document_do_color_special, 1); + mdvi_register_fonts (); + + dvi_context_mutex = g_mutex_new (); + + ev_document_class->load = dvi_document_load; + ev_document_class->save = dvi_document_save; + ev_document_class->get_n_pages = dvi_document_get_n_pages; + ev_document_class->get_page_size = dvi_document_get_page_size; + ev_document_class->render = dvi_document_render; + ev_document_class->support_synctex = dvi_document_support_synctex; +} + +static void +dvi_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, + EvRenderContext *rc, + gint *width, + gint *height) +{ + DviDocument *dvi_document = DVI_DOCUMENT (document); + gdouble page_width = dvi_document->base_width; + gdouble page_height = dvi_document->base_height; + + if (rc->rotation == 90 || rc->rotation == 270) { + *width = (gint) (page_height * rc->scale); + *height = (gint) (page_width * rc->scale); + } else { + *width = (gint) (page_width * rc->scale); + *height = (gint) (page_height * rc->scale); + } +} + +static GdkPixbuf * +dvi_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, + EvRenderContext *rc, + gboolean border) +{ + DviDocument *dvi_document = DVI_DOCUMENT (document); + GdkPixbuf *pixbuf; + GdkPixbuf *rotated_pixbuf; + cairo_surface_t *surface; + gint thumb_width, thumb_height; + gint proposed_width, proposed_height; + + thumb_width = (gint) (dvi_document->base_width * rc->scale); + thumb_height = (gint) (dvi_document->base_height * rc->scale); + + g_mutex_lock (dvi_context_mutex); + + mdvi_setpage (dvi_document->context, rc->page->index); + + mdvi_set_shrink (dvi_document->context, + (int)dvi_document->base_width * dvi_document->params->hshrink / thumb_width, + (int)dvi_document->base_height * dvi_document->params->vshrink / thumb_height); + + proposed_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv; + proposed_height = dvi_document->context->dvi_page_h * dvi_document->context->params.vconv; + + if (border) { + mdvi_cairo_device_set_margins (&dvi_document->context->device, + MAX (thumb_width - proposed_width, 0) / 2, + MAX (thumb_height - proposed_height, 0) / 2); + } else { + mdvi_cairo_device_set_margins (&dvi_document->context->device, + MAX (thumb_width - proposed_width - 2, 0) / 2, + MAX (thumb_height - proposed_height - 2, 0) / 2); + } + + mdvi_cairo_device_set_scale (&dvi_document->context->device, rc->scale); + mdvi_cairo_device_render (dvi_document->context); + surface = mdvi_cairo_device_get_surface (&dvi_document->context->device); + g_mutex_unlock (dvi_context_mutex); + + pixbuf = ev_document_misc_pixbuf_from_surface (surface); + cairo_surface_destroy (surface); + + rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation); + g_object_unref (pixbuf); + + if (border) { + GdkPixbuf *tmp_pixbuf = rotated_pixbuf; + + rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf); + g_object_unref (tmp_pixbuf); + } + + return rotated_pixbuf; +} + +static void +dvi_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface) +{ + iface->get_thumbnail = dvi_document_thumbnails_get_thumbnail; + iface->get_dimensions = dvi_document_thumbnails_get_dimensions; +} + +/* EvFileExporterIface */ +static void +dvi_document_file_exporter_begin (EvFileExporter *exporter, + EvFileExporterContext *fc) +{ + DviDocument *dvi_document = DVI_DOCUMENT(exporter); + + if (dvi_document->exporter_filename) + g_free (dvi_document->exporter_filename); + dvi_document->exporter_filename = g_strdup (fc->filename); + + if (dvi_document->exporter_opts) { + g_string_free (dvi_document->exporter_opts, TRUE); + } + dvi_document->exporter_opts = g_string_new ("-s "); +} + +static void +dvi_document_file_exporter_do_page (EvFileExporter *exporter, + EvRenderContext *rc) +{ + DviDocument *dvi_document = DVI_DOCUMENT(exporter); + + g_string_append_printf (dvi_document->exporter_opts, "%d,", (rc->page->index) + 1); +} + +static void +dvi_document_file_exporter_end (EvFileExporter *exporter) +{ + gchar *command_line; + gint exit_stat; + GError *err = NULL; + gboolean success; + + DviDocument *dvi_document = DVI_DOCUMENT(exporter); + + command_line = g_strdup_printf ("dvipdfm %s -o %s \"%s\"", /* dvipdfm -s 1,2,.., -o exporter_filename dvi_filename */ + dvi_document->exporter_opts->str, + dvi_document->exporter_filename, + dvi_document->context->filename); + + success = g_spawn_command_line_sync (command_line, + NULL, + NULL, + &exit_stat, + &err); + + g_free (command_line); + + if (success == FALSE) { + g_warning ("Error: %s", err->message); + } else if (!WIFEXITED(exit_stat) || WEXITSTATUS(exit_stat) != EXIT_SUCCESS){ + g_warning ("Error: dvipdfm does not end normally or exit with a failure status."); + } + + if (err) + g_error_free (err); +} + +static EvFileExporterCapabilities +dvi_document_file_exporter_get_capabilities (EvFileExporter *exporter) +{ + return EV_FILE_EXPORTER_CAN_PAGE_SET | + EV_FILE_EXPORTER_CAN_COPIES | + EV_FILE_EXPORTER_CAN_COLLATE | + EV_FILE_EXPORTER_CAN_REVERSE | + EV_FILE_EXPORTER_CAN_GENERATE_PDF; +} + +static void +dvi_document_file_exporter_iface_init (EvFileExporterInterface *iface) +{ + iface->begin = dvi_document_file_exporter_begin; + iface->do_page = dvi_document_file_exporter_do_page; + iface->end = dvi_document_file_exporter_end; + iface->get_capabilities = dvi_document_file_exporter_get_capabilities; +} + +#define RGB2ULONG(r,g,b) ((0xFF<<24)|(r<<16)|(g<<8)|(b)) + +static gboolean +hsb2rgb (float h, float s, float v, guchar *red, guchar *green, guchar *blue) +{ + float i, f, p, q, t, r, g, b; + + if (h == 360) + h = 0; + else if ((h > 360) || (h < 0)) + return FALSE; + + s /= 100; + v /= 100; + h /= 60; + i = floor (h); + f = h - i; + p = v * (1 - s); + q = v * (1 - (s * f)); + t = v * (1 - (s * (1 - f))); + + if (i == 0) { + r = v; + g = t; + b = p; + } else if (i == 1) { + r = q; + g = v; + b = p; + } else if (i == 2) { + r = p; + g = v; + b = t; + } else if (i == 3) { + r = p; + g = q; + b = v; + } else if (i == 4) { + r = t; + g = p; + b = v; + } else if (i == 5) { + r = v; + g = p; + b = q; + } + + *red = (guchar)floor(r * 255.0); + *green = (guchar)floor(g * 255.0); + *blue = (guchar)floor(b * 255.0); + + return TRUE; +} + +static void +parse_color (const gchar *ptr, + gdouble *color, + gint n_color) +{ + gchar *p = (gchar *)ptr; + gint i; + + for (i = 0; i < n_color; i++) { + while (isspace (*p)) p++; + color[i] = g_ascii_strtod (p, NULL); + while (!isspace (*p) && *p != '\0') p++; + if (*p == '\0') + break; + } +} + +static void +dvi_document_do_color_special (DviContext *dvi, const char *prefix, const char *arg) +{ + if (strncmp (arg, "pop", 3) == 0) { + mdvi_pop_color (dvi); + } else if (strncmp (arg, "push", 4) == 0) { + /* Find color source: Named, CMYK or RGB */ + const char *tmp = arg + 4; + + while (isspace (*tmp)) tmp++; + + if (!strncmp ("rgb", tmp, 3)) { + gdouble rgb[3]; + guchar red, green, blue; + + parse_color (tmp + 4, rgb, 3); + + red = 255 * rgb[0]; + green = 255 * rgb[1]; + blue = 255 * rgb[2]; + + mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF); + } else if (!strncmp ("hsb", tmp, 4)) { + gdouble hsb[3]; + guchar red, green, blue; + + parse_color (tmp + 4, hsb, 3); + + if (hsb2rgb (hsb[0], hsb[1], hsb[2], &red, &green, &blue)) + mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF); + } else if (!strncmp ("cmyk", tmp, 4)) { + gdouble cmyk[4]; + double r, g, b; + guchar red, green, blue; + + parse_color (tmp + 5, cmyk, 4); + + r = 1.0 - cmyk[0] - cmyk[3]; + if (r < 0.0) + r = 0.0; + g = 1.0 - cmyk[1] - cmyk[3]; + if (g < 0.0) + g = 0.0; + b = 1.0 - cmyk[2] - cmyk[3]; + if (b < 0.0) + b = 0.0; + + red = r * 255 + 0.5; + green = g * 255 + 0.5; + blue = b * 255 + 0.5; + + mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF); + } else if (!strncmp ("gray ", tmp, 5)) { + gdouble gray; + guchar rgb; + + parse_color (tmp + 5, &gray, 1); + + rgb = gray * 255 + 0.5; + + mdvi_push_color (dvi, RGB2ULONG (rgb, rgb, rgb), 0xFFFFFFFF); + } else { + GdkColor color; + + if (gdk_color_parse (tmp, &color)) { + guchar red, green, blue; + + red = color.red * 255 / 65535.; + green = color.green * 255 / 65535.; + blue = color.blue * 255 / 65535.; + + mdvi_push_color (dvi, RGB2ULONG (red, green, blue), 0xFFFFFFFF); + } + } + } +} + +static void +dvi_document_init_params (DviDocument *dvi_document) +{ + dvi_document->params = g_new0 (DviParams, 1); + + dvi_document->params->dpi = MDVI_DPI; + dvi_document->params->vdpi = MDVI_VDPI; + dvi_document->params->mag = MDVI_MAGNIFICATION; + dvi_document->params->density = MDVI_DEFAULT_DENSITY; + dvi_document->params->gamma = MDVI_DEFAULT_GAMMA; + dvi_document->params->flags = MDVI_PARAM_ANTIALIASED; + dvi_document->params->hdrift = 0; + dvi_document->params->vdrift = 0; + dvi_document->params->hshrink = MDVI_SHRINK_FROM_DPI(dvi_document->params->dpi); + dvi_document->params->vshrink = MDVI_SHRINK_FROM_DPI(dvi_document->params->vdpi); + dvi_document->params->orientation = MDVI_ORIENT_TBLR; + + dvi_document->spec = NULL; + + dvi_document->params->bg = 0xffffffff; + dvi_document->params->fg = 0xff000000; +} + +static void +dvi_document_init (DviDocument *dvi_document) +{ + dvi_document->context = NULL; + dvi_document_init_params (dvi_document); + + dvi_document->exporter_filename = NULL; + dvi_document->exporter_opts = NULL; +} diff --git a/backend/dvi/dvi-document.h b/backend/dvi/dvi-document.h new file mode 100644 index 00000000..845301b6 --- /dev/null +++ b/backend/dvi/dvi-document.h @@ -0,0 +1,38 @@ +/* dvi-document.h: Implementation of EvDocument for dvi documents + * Copyright (C) 2005, Nickolay V. Shmyrev <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __DVI_DOCUMENT_H__ +#define __DVI_DOCUMENT_H__ + +#include "ev-document.h" + +G_BEGIN_DECLS + +#define DVI_TYPE_DOCUMENT (dvi_document_get_type ()) +#define DVI_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DVI_TYPE_DOCUMENT, DviDocument)) +#define DVI_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DVI_TYPE_DOCUMENT)) + +typedef struct _DviDocument DviDocument; + +GType dvi_document_get_type (void) G_GNUC_CONST; + +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + +G_END_DECLS + +#endif /* __DVI_DOCUMENT_H__ */ diff --git a/backend/dvi/dvidocument.evince-backend.in b/backend/dvi/dvidocument.evince-backend.in new file mode 100644 index 00000000..cb5fe6ed --- /dev/null +++ b/backend/dvi/dvidocument.evince-backend.in @@ -0,0 +1,4 @@ +[Evince Backend] +Module=dvidocument +_TypeDescription=DVI Documents +MimeType=application/x-dvi;application/x-bzdvi;application/x-gzdvi diff --git a/backend/dvi/fonts.c b/backend/dvi/fonts.c new file mode 100644 index 00000000..99be63ce --- /dev/null +++ b/backend/dvi/fonts.c @@ -0,0 +1,57 @@ +#include "config.h" +#include "fonts.h" +#include "mdvi.h" + +static int registered = 0; + +extern DviFontInfo pk_font_info; +extern DviFontInfo pkn_font_info; +extern DviFontInfo gf_font_info; +extern DviFontInfo vf_font_info; +extern DviFontInfo ovf_font_info; +#if 0 +extern DviFontInfo tt_font_info; +#endif +#ifdef WITH_TYPE1_FONTS +extern DviFontInfo t1_font_info; +#endif +extern DviFontInfo afm_font_info; +extern DviFontInfo tfm_font_info; +extern DviFontInfo ofm_font_info; + +static struct fontinfo { + DviFontInfo *info; + char *desc; + int klass; +} known_fonts[] = { + {&vf_font_info, "Virtual fonts", 0}, + {&ovf_font_info, "Omega's virtual fonts", 0}, +#if 0 + {&tt_font_info, "TrueType fonts", 0}, +#endif +#ifdef WITH_TYPE1_FONTS + {&t1_font_info, "Type1 PostScript fonts", 0}, +#endif + {&pk_font_info, "Packed bitmap (auto-generated)", 1}, + {&pkn_font_info, "Packed bitmap", -2}, + {&gf_font_info, "Metafont's generic font format", 1}, + {&ofm_font_info, "Omega font metrics", -1}, + {&tfm_font_info, "TeX font metrics", -1}, + {&afm_font_info, "Adobe font metrics", -1}, + {0, 0} +}; + +void mdvi_register_fonts (void) +{ + struct fontinfo *type; + + if (!registered) { + for(type = known_fonts; type->info; type++) { + mdvi_register_font_type(type->info, type->klass); + } + registered = 1; + } + return; +} + + diff --git a/backend/dvi/fonts.h b/backend/dvi/fonts.h new file mode 100644 index 00000000..e55a8ddf --- /dev/null +++ b/backend/dvi/fonts.h @@ -0,0 +1,6 @@ +#ifndef MDVI_FONTS_REGISTRATION_H +#define MDVI_FONTS_REGISTRATION_H + +void mdvi_register_fonts (void); + +#endif /* MDVI_FONTS_REGISTRATION_H */ diff --git a/backend/dvi/mdvi-lib/Makefile.am b/backend/dvi/mdvi-lib/Makefile.am new file mode 100644 index 00000000..e976aa60 --- /dev/null +++ b/backend/dvi/mdvi-lib/Makefile.am @@ -0,0 +1,43 @@ +INCLUDES = $(WARN_CFLAGS) +noinst_LTLIBRARIES = libmdvi.la + +libmdvi_la_SOURCES = \ + afmparse.c \ + afmparse.h \ + bitmap.c \ + bitmap.h \ + color.c \ + color.h \ + common.c \ + common.h \ + defaults.h \ + dviopcodes.h \ + dviread.c \ + files.c \ + font.c \ + fontmap.c \ + fontmap.h \ + fontsrch.c \ + gf.c \ + hash.c \ + hash.h \ + list.c \ + mdvi.h \ + pagesel.c \ + paper.c \ + paper.h \ + pk.c \ + private.h \ + setup.c \ + special.c \ + sp-epsf.c \ + sysdeps.h \ + t1.c \ + tfm.c \ + tfmfile.c \ + tt.c \ + util.c \ + vf.c + + +-include $(top_srcdir)/git.mk diff --git a/backend/dvi/mdvi-lib/afmparse.c b/backend/dvi/mdvi-lib/afmparse.c new file mode 100644 index 00000000..164366b0 --- /dev/null +++ b/backend/dvi/mdvi-lib/afmparse.c @@ -0,0 +1,1303 @@ +/* + * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved. + * + * This file may be freely copied and redistributed as long as: + * 1) This entire notice continues to be included in the file, + * 2) If the file has been modified in any way, a notice of such + * modification is conspicuously indicated. + * + * PostScript, Display PostScript, and Adobe are registered trademarks of + * Adobe Systems Incorporated. + * + * ************************************************************************ + * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT + * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS + * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR + * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY + * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, + * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * ************************************************************************ + */ + +/* + * modified for MDVI: + * - some names changed to avoid conflicts with T1lib + * - changed to ANSI C prototypes, as used by MDVI + * mal - 3/01 + */ + +/* parseAFM.c + * + * This file is used in conjuction with the parseAFM.h header file. + * This file contains several procedures that are used to parse AFM + * files. It is intended to work with an application program that needs + * font metric information. The program can be used as is by making a + * procedure call to "parseFile" (passing in the expected parameters) + * and having it fill in a data structure with the data from the + * AFM file, or an application developer may wish to customize this + * code. + * + * There is also a file, parseAFMclient.c, that is a sample application + * showing how to call the "parseFile" procedure and how to use the data + * after "parseFile" has returned. + * + * Please read the comments in parseAFM.h and parseAFMclient.c. + * + * History: + * original: DSM Thu Oct 20 17:39:59 PDT 1988 + * modified: DSM Mon Jul 3 14:17:50 PDT 1989 + * - added 'storageProblem' return code + * - fixed bug of not allocating extra byte for string duplication + * - fixed typos + * modified: DSM Tue Apr 3 11:18:34 PDT 1990 + * - added free(ident) at end of parseFile routine + * modified: DSM Tue Jun 19 10:16:29 PDT 1990 + * - changed (width == 250) to (width = 250) in initializeArray + */ + +#include <config.h> +#include "sysdeps.h" + +#ifdef WITH_AFM_FILES + +#include <stdlib.h> /* added for MDVI */ +#include <string.h> /* added for MDVI */ + +#include <stdio.h> +#include <errno.h> +#include <sys/file.h> +#include <math.h> +#include "afmparse.h" +#undef VERSION + +#define lineterm EOL /* line terminating character */ +#define normalEOF 1 /* return code from parsing routines used only */ + /* in this module */ +#define Space "space" /* used in string comparison to look for the width */ + /* of the space character to init the widths array */ +#define False "false" /* used in string comparison to check the value of */ + /* boolean keys (e.g. IsFixedPitch) */ + +#define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0) + + + +/*************************** GLOBALS ***********************/ + +static char *ident = NULL; /* storage buffer for keywords */ + + +/* "shorts" for fast case statement + * The values of each of these enumerated items correspond to an entry in the + * table of strings defined below. Therefore, if you add a new string as + * new keyword into the keyStrings table, you must also add a corresponding + * parseKey AND it MUST be in the same position! + * + * IMPORTANT: since the sorting algorithm is a binary search, the strings of + * keywords must be placed in lexicographical order, below. [Therefore, the + * enumerated items are not necessarily in lexicographical order, depending + * on the name chosen. BUT, they must be placed in the same position as the + * corresponding key string.] The NOPE shall remain in the last position, + * since it does not correspond to any key string, and it is used in the + * "recognize" procedure to calculate how many possible keys there are. + */ + +enum parseKey { + ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT, + DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, + ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN, + FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISFIXEDPITCH, + ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME, + NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, + STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS, + STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION, + UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT, + NOPE } ; + +/* keywords for the system: + * This a table of all of the current strings that are vaild AFM keys. + * Each entry can be referenced by the appropriate parseKey value (an + * enumerated data type defined above). If you add a new keyword here, + * a corresponding parseKey MUST be added to the enumerated data type + * defined above, AND it MUST be added in the same position as the + * string is in this table. + * + * IMPORTANT: since the sorting algorithm is a binary search, the keywords + * must be placed in lexicographical order. And, NULL should remain at the + * end. + */ + +static char *keyStrings[] = { + "Ascender", "B", "C", "CC", "CapHeight", "Comment", + "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites", + "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern", + "FamilyName", "FontBBox", "FontName", "FullName", "IsFixedPitch", + "ItalicAngle", "KP", "KPX", "L", "N", + "Notice", "PCC", "StartCharMetrics", "StartComposites", + "StartFontMetrics", "StartKernData", "StartKernPairs", + "StartTrackKern", "TrackKern", "UnderlinePosition", + "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight", + NULL }; + +/*************************** PARSING ROUTINES **************/ + +/*************************** token *************************/ + +/* A "AFM File Conventions" tokenizer. That means that it will + * return the next token delimited by white space. See also + * the `linetoken' routine, which does a similar thing but + * reads all tokens until the next end-of-line. + */ + +static char *token(FILE *stream) +{ + int ch, idx; + + /* skip over white space */ + while ((ch = fgetc(stream)) == ' ' || ch == lineterm || + ch == ',' || ch == '\t' || ch == ';'); + + idx = 0; + while (ch != EOF && ch != ' ' && ch != lineterm + && ch != '\t' && ch != ':' && ch != ';') + { + ident[idx++] = ch; + ch = fgetc(stream); + } /* while */ + + if (ch == EOF && idx < 1) return ((char *)NULL); + if (idx >= 1 && ch != ':' ) ungetc(ch, stream); + if (idx < 1 ) ident[idx++] = ch; /* single-character token */ + ident[idx] = 0; + + return(ident); /* returns pointer to the token */ + +} /* token */ + + +/*************************** linetoken *************************/ + +/* "linetoken" will get read all tokens until the EOL character from + * the given stream. This is used to get any arguments that can be + * more than one word (like Comment lines and FullName). + */ + +static char *linetoken(FILE *stream) +{ + int ch, idx; + + while ((ch = fgetc(stream)) == ' ' || ch == '\t' ); + + idx = 0; + while (ch != EOF && ch != lineterm) + { + ident[idx++] = ch; + ch = fgetc(stream); + } /* while */ + + ungetc(ch, stream); + ident[idx] = 0; + + return(ident); /* returns pointer to the token */ + +} /* linetoken */ + + +/*************************** recognize *************************/ + +/* This function tries to match a string to a known list of + * valid AFM entries (check the keyStrings array above). + * "ident" contains everything from white space through the + * next space, tab, or ":" character. + * + * The algorithm is a standard Knuth binary search. + */ + +static enum parseKey recognize(char *ident) +{ + int lower = 0, upper = (int) NOPE, midpoint, cmpvalue; + BOOL found = FALSE; + + while ((upper >= lower) && !found) + { + midpoint = (lower + upper)/2; + if (keyStrings[midpoint] == NULL) break; + cmpvalue = strncmp(ident, keyStrings[midpoint], MAX_NAME); + if (cmpvalue == 0) found = TRUE; + else if (cmpvalue < 0) upper = midpoint - 1; + else lower = midpoint + 1; + } /* while */ + + if (found) return (enum parseKey) midpoint; + else return NOPE; + +} /* recognize */ + + +/************************* parseGlobals *****************************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "StartCharMetrics" keyword, which essentially marks the + * end of the Global Font Information and the beginning of the character + * metrics information. + * + * If the caller of "parseFile" specified that it wanted the Global + * Font Information (as defined by the "AFM File Specification" + * document), then that information will be stored in the returned + * data structure. + * + * Any Global Font Information entries that are not found in a + * given file, will have the usual default initialization value + * for its type (i.e. entries of type int will be 0, etc). + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static BOOL parseGlobals(FILE *fp, GlobalFontInfo *gfi) +{ + BOOL cont = TRUE, save = (gfi != NULL); + int error = ok; + register char *keyword; + + while (cont) + { + keyword = token(fp); + + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Global Font info section */ + /* without saving any of the data */ + switch (recognize(keyword)) + { + case STARTCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire global font info section, */ + /* saving the data */ + switch(recognize(keyword)) + { + case STARTFONTMETRICS: + keyword = token(fp); + gfi->afmVersion = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->afmVersion, keyword); + break; + case COMMENT: + keyword = linetoken(fp); + break; + case FONTNAME: + keyword = token(fp); + gfi->fontName = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->fontName, keyword); + break; + case ENCODINGSCHEME: + keyword = token(fp); + gfi->encodingScheme = (char *) + malloc(strlen(keyword) + 1); + strcpy(gfi->encodingScheme, keyword); + break; + case FULLNAME: + keyword = linetoken(fp); + gfi->fullName = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->fullName, keyword); + break; + case FAMILYNAME: + keyword = linetoken(fp); + gfi->familyName = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->familyName, keyword); + break; + case WEIGHT: + keyword = token(fp); + gfi->weight = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->weight, keyword); + break; + case ITALICANGLE: + keyword = token(fp); + gfi->italicAngle = atof(keyword); + if (errno == ERANGE) error = parseError; + break; + case ISFIXEDPITCH: + keyword = token(fp); + if (MATCH(keyword, False)) + gfi->isFixedPitch = 0; + else + gfi->isFixedPitch = 1; + break; + case UNDERLINEPOSITION: + keyword = token(fp); + gfi->underlinePosition = atoi(keyword); + break; + case UNDERLINETHICKNESS: + keyword = token(fp); + gfi->underlineThickness = atoi(keyword); + break; + case VERSION: + keyword = token(fp); + gfi->version = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->version, keyword); + break; + case NOTICE: + keyword = linetoken(fp); + gfi->notice = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->notice, keyword); + break; + case FONTBBOX: + keyword = token(fp); + gfi->fontBBox.llx = atoi(keyword); + keyword = token(fp); + gfi->fontBBox.lly = atoi(keyword); + keyword = token(fp); + gfi->fontBBox.urx = atoi(keyword); + keyword = token(fp); + gfi->fontBBox.ury = atoi(keyword); + break; + case CAPHEIGHT: + keyword = token(fp); + gfi->capHeight = atoi(keyword); + break; + case XHEIGHT: + keyword = token(fp); + gfi->xHeight = atoi(keyword); + break; + case DESCENDER: + keyword = token(fp); + gfi->descender = atoi(keyword); + break; + case ASCENDER: + keyword = token(fp); + gfi->ascender = atoi(keyword); + break; + case STARTCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + return(error); + +} /* parseGlobals */ + + + +#if 0 /* this function does not seem to be used anywhere */ +/************************* initializeArray ************************/ + +/* Unmapped character codes are (at Adobe Systems) assigned the + * width of the space character (if one exists) else they get the + * value of 250 ems. This function initializes all entries in the + * char widths array to have this value. Then any mapped character + * codes will be replaced with the width of the appropriate character + * when parsing the character metric section. + + * This function parses the Character Metrics Section looking + * for a space character (by comparing character names). If found, + * the width of the space character will be used to initialize the + * values in the array of character widths. + * + * Before returning, the position of the read/write pointer of the + * file is reset to be where it was upon entering this function. + */ + +static int initializeArray(FILE *fp, int *cwi) +{ + BOOL cont = TRUE, found = FALSE; + long opos = ftell(fp); + int code = 0, width = 0, i = 0, error = 0; + register char *keyword; + + while (cont) + { + keyword = token(fp); + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + code = atoi(token(fp)); + break; + case XWIDTH: + width = atoi(token(fp)); + break; + case CHARNAME: + keyword = token(fp); + if (MATCH(keyword, Space)) + { + cont = FALSE; + found = TRUE; + } + break; + case ENDCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (!found) + width = 250; + + for (i = 0; i < 256; ++i) + cwi[i] = width; + + fseek(fp, opos, 0); + + return(error); + +} /* initializeArray */ +#endif /* unused */ + +/************************* parseCharWidths **************************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "EndCharMetrics" keyword. It will save the character + * width info (as opposed to all of the character metric information) + * if requested by the caller of parseFile. Otherwise, it will just + * parse through the section without saving any information. + * + * If data is to be saved, parseCharWidths is passed in a pointer + * to an array of widths that has already been initialized by the + * standard value for unmapped character codes. This function parses + * the Character Metrics section only storing the width information + * for the encoded characters into the array using the character code + * as the index into that array. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCharWidths(FILE *fp, int *cwi) +{ + BOOL cont = TRUE, save = (cwi != NULL); + int pos = 0, error = ok; + register char *keyword; + + while (cont) + { + keyword = token(fp); + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Char Metrics section without */ + /* saving any of the data*/ + switch (recognize(keyword)) + { + case ENDCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire char metrics section, saving */ + /* only the char x-width info */ + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + keyword = token(fp); + pos = atoi(keyword); + break; + case XYWIDTH: + /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */ + keyword = token(fp); keyword = token(fp); /* eat values */ + error = parseError; + break; + case XWIDTH: + keyword = token(fp); + if (pos >= 0) /* ignore unmapped chars */ + cwi[pos] = atoi(keyword); + break; + case ENDCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case CHARNAME: /* eat values (so doesn't cause parseError) */ + keyword = token(fp); + break; + case CHARBBOX: + keyword = token(fp); keyword = token(fp); + keyword = token(fp); keyword = token(fp); + break; + case LIGATURE: + keyword = token(fp); keyword = token(fp); + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + return(error); + +} /* parseCharWidths */ + + +/************************* parseCharMetrics ************************/ + +/* This function is called by parseFile if the caller of parseFile + * requested that all character metric information be saved + * (as opposed to only the character width information). + * + * parseCharMetrics is passed in a pointer to an array of records + * to hold information on a per character basis. This function + * parses the Character Metrics section storing all character + * metric information for the ALL characters (mapped and unmapped) + * into the array. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCharMetrics(FILE *fp, FontInfo *fi) +{ + BOOL cont = TRUE, firstTime = TRUE; + int error = ok, count = 0; + register CharMetricInfo *temp = fi->cmi; + register char *keyword; + + while (cont) + { + keyword = token(fp); + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + if (count < fi->numOfChars) + { + if (firstTime) firstTime = FALSE; + else temp++; + temp->code = atoi(token(fp)); + count++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case XYWIDTH: + temp->wx = atoi(token(fp)); + temp->wy = atoi(token(fp)); + break; + case XWIDTH: + temp->wx = atoi(token(fp)); + break; + case CHARNAME: + keyword = token(fp); + temp->name = (char *) malloc(strlen(keyword) + 1); + strcpy(temp->name, keyword); + break; + case CHARBBOX: + temp->charBBox.llx = atoi(token(fp)); + temp->charBBox.lly = atoi(token(fp)); + temp->charBBox.urx = atoi(token(fp)); + temp->charBBox.ury = atoi(token(fp)); + break; + case LIGATURE: { + Ligature **tail = &(temp->ligs); + Ligature *node = *tail; + + if (*tail != NULL) + { + while (node->next != NULL) + node = node->next; + tail = &(node->next); + } + + *tail = (Ligature *) calloc(1, sizeof(Ligature)); + keyword = token(fp); + (*tail)->succ = (char *) malloc(strlen(keyword) + 1); + strcpy((*tail)->succ, keyword); + keyword = token(fp); + (*tail)->lig = (char *) malloc(strlen(keyword) + 1); + strcpy((*tail)->lig, keyword); + break; } + case ENDCHARMETRICS: + cont = FALSE;; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if ((error == ok) && (count != fi->numOfChars)) + error = parseError; + + return(error); + +} /* parseCharMetrics */ + + + +/************************* parseTrackKernData ***********************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "EndTrackKern" or "EndKernData" keywords. It will save the + * track kerning data if requested by the caller of parseFile. + * + * parseTrackKernData is passed in a pointer to the FontInfo record. + * If data is to be saved, the FontInfo record will already contain + * a valid pointer to storage for the track kerning data. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseTrackKernData(FILE *fp, FontInfo *fi) +{ + BOOL cont = TRUE, save = (fi->tkd != NULL); + int pos = 0, error = ok, tcount = 0; + register char *keyword; + + while (cont) + { + keyword = token(fp); + + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Track Kerning Data */ + /* section without saving any of the data */ + switch(recognize(keyword)) + { + case ENDTRACKKERN: + case ENDKERNDATA: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Track Kerning Data section, */ + /* saving the data */ + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case TRACKKERN: + if (tcount < fi->numOfTracks) + { + keyword = token(fp); + fi->tkd[pos].degree = atoi(keyword); + keyword = token(fp); + fi->tkd[pos].minPtSize = atof(keyword); + if (errno == ERANGE) error = parseError; + keyword = token(fp); + fi->tkd[pos].minKernAmt = atof(keyword); + if (errno == ERANGE) error = parseError; + keyword = token(fp); + fi->tkd[pos].maxPtSize = atof(keyword); + if (errno == ERANGE) error = parseError; + keyword = token(fp); + fi->tkd[pos++].maxKernAmt = atof(keyword); + if (errno == ERANGE) error = parseError; + tcount++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case ENDTRACKKERN: + case ENDKERNDATA: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && tcount != fi->numOfTracks) + error = parseError; + + return(error); + +} /* parseTrackKernData */ + + +/************************* parsePairKernData ************************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "EndKernPairs" or "EndKernData" keywords. It will save + * the pair kerning data if requested by the caller of parseFile. + * + * parsePairKernData is passed in a pointer to the FontInfo record. + * If data is to be saved, the FontInfo record will already contain + * a valid pointer to storage for the pair kerning data. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parsePairKernData(FILE *fp, FontInfo *fi) +{ + BOOL cont = TRUE, save = (fi->pkd != NULL); + int pos = 0, error = ok, pcount = 0; + register char *keyword; + + while (cont) + { + keyword = token(fp); + + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Pair Kerning Data */ + /* section without saving any of the data */ + switch(recognize(keyword)) + { + case ENDKERNPAIRS: + case ENDKERNDATA: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Pair Kerning Data section, */ + /* saving the data */ + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case KERNPAIR: + if (pcount < fi->numOfPairs) + { + keyword = token(fp); + fi->pkd[pos].name1 = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->pkd[pos].name1, keyword); + keyword = token(fp); + fi->pkd[pos].name2 = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->pkd[pos].name2, keyword); + keyword = token(fp); + fi->pkd[pos].xamt = atoi(keyword); + keyword = token(fp); + fi->pkd[pos++].yamt = atoi(keyword); + pcount++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case KERNPAIRXAMT: + if (pcount < fi->numOfPairs) + { + keyword = token(fp); + fi->pkd[pos].name1 = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->pkd[pos].name1, keyword); + keyword = token(fp); + fi->pkd[pos].name2 = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->pkd[pos].name2, keyword); + keyword = token(fp); + fi->pkd[pos++].xamt = atoi(keyword); + pcount++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case ENDKERNPAIRS: + case ENDKERNDATA: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && pcount != fi->numOfPairs) + error = parseError; + + return(error); + +} /* parsePairKernData */ + + +/************************* parseCompCharData **************************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "EndComposites" keyword. It will save the composite + * character data if requested by the caller of parseFile. + * + * parseCompCharData is passed in a pointer to the FontInfo record, and + * a boolean representing if the data should be saved. + * + * This function will create the appropriate amount of storage for + * the composite character data and store a pointer to the storage + * in the FontInfo record. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCompCharData(FILE *fp, FontInfo *fi) +{ + BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL); + int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0; + register char *keyword; + + while (cont) + { + keyword = token(fp); + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + error = earlyEOF; + break; /* get out of loop */ + } + if (ccount > fi->numOfComps) + { + error = parseError; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Composite Character info */ + /* section without saving any of the data */ + switch(recognize(keyword)) + { + case ENDCOMPOSITES: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Composite Character info section, */ + /* saving the data */ + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case COMPCHAR: + if (ccount < fi->numOfComps) + { + keyword = token(fp); + if (pcount != fi->ccd[pos].numOfPieces) + error = parseError; + pcount = 0; + if (firstTime) firstTime = FALSE; + else pos++; + fi->ccd[pos].ccName = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->ccd[pos].ccName, keyword); + keyword = token(fp); + fi->ccd[pos].numOfPieces = atoi(keyword); + fi->ccd[pos].pieces = (Pcc *) + calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc)); + j = 0; + ccount++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case COMPCHARPIECE: + if (pcount < fi->ccd[pos].numOfPieces) + { + keyword = token(fp); + fi->ccd[pos].pieces[j].pccName = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->ccd[pos].pieces[j].pccName, keyword); + keyword = token(fp); + fi->ccd[pos].pieces[j].deltax = atoi(keyword); + keyword = token(fp); + fi->ccd[pos].pieces[j++].deltay = atoi(keyword); + pcount++; + } + else + error = parseError; + break; + case ENDCOMPOSITES: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && ccount != fi->numOfComps) + error = parseError; + + return(error); + +} /* parseCompCharData */ + + + + +/*************************** 'PUBLIC' FUNCTION ********************/ + + +/*************************** parseFile *****************************/ + +/* parseFile is the only 'public' procedure available. It is called + * from an application wishing to get information from an AFM file. + * The caller of this function is responsible for locating and opening + * an AFM file and handling all errors associated with that task. + * + * parseFile expects 3 parameters: a vaild file pointer, a pointer + * to a (FontInfo *) variable (for which storage will be allocated and + * the data requested filled in), and a mask specifying which + * data from the AFM File should be saved in the FontInfo structure. + * + * The file will be parsed and the requested data will be stored in + * a record of type FontInfo (refer to ParseAFM.h). + * + * parseFile returns an error code as defined in parseAFM.h. + * + * The position of the read/write pointer associated with the file + * pointer upon return of this function is undefined. + */ + +extern int afm_parse_file(FILE *fp, FontInfo **fi, FLAGS flags) +{ + + int code = ok; /* return code from each of the parsing routines */ + int error = ok; /* used as the return code from this function */ + + register char *keyword; /* used to store a token */ + + + /* storage data for the global variable ident */ + ident = (char *) calloc(MAX_NAME, sizeof(char)); + if (ident == NULL) {error = storageProblem; return(error);} + + (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo)); + if ((*fi) == NULL) {error = storageProblem; return(error);} + + if (flags & P_G) + { + (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo)); + if ((*fi)->gfi == NULL) {error = storageProblem; return(error);} + } + + /* The AFM File begins with Global Font Information. This section */ + /* will be parsed whether or not information should be saved. */ + code = parseGlobals(fp, (*fi)->gfi); + + if (code < 0) error = code; + + /* The Global Font Information is followed by the Character Metrics */ + /* section. Which procedure is used to parse this section depends on */ + /* how much information should be saved. If all of the metrics info */ + /* is wanted, parseCharMetrics is called. If only the character widths */ + /* is wanted, parseCharWidths is called. parseCharWidths will also */ + /* be called in the case that no character data is to be saved, just */ + /* to parse through the section. */ + + if ((code != normalEOF) && (code != earlyEOF)) + { + (*fi)->numOfChars = atoi(token(fp)); + if (flags & (P_M ^ P_W)) + { + (*fi)->cmi = (CharMetricInfo *) + calloc((*fi)->numOfChars, sizeof(CharMetricInfo)); + if ((*fi)->cmi == NULL) {error = storageProblem; return(error);} + code = parseCharMetrics(fp, *fi); + } + else + { + if (flags & P_W) + { + (*fi)->cwi = (int *) calloc(256, sizeof(int)); + if ((*fi)->cwi == NULL) + { + error = storageProblem; + return(error); + } + } + /* parse section regardless */ + code = parseCharWidths(fp, (*fi)->cwi); + } /* else */ + } /* if */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + /* The remaining sections of the AFM are optional. This code will */ + /* look at the next keyword in the file to determine what section */ + /* is next, and then allocate the appropriate amount of storage */ + /* for the data (if the data is to be saved) and call the */ + /* appropriate parsing routine to parse the section. */ + + while ((code != normalEOF) && (code != earlyEOF)) + { + keyword = token(fp); + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + code = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword)) + { + case STARTKERNDATA: + break; + case ENDKERNDATA: + break; + case STARTTRACKKERN: + keyword = token(fp); + if (flags & P_T) + { + (*fi)->numOfTracks = atoi(keyword); + (*fi)->tkd = (TrackKernData *) + calloc((*fi)->numOfTracks, sizeof(TrackKernData)); + if ((*fi)->tkd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parseTrackKernData(fp, *fi); + break; + case STARTKERNPAIRS: + keyword = token(fp); + if (flags & P_P) + { + (*fi)->numOfPairs = atoi(keyword); + (*fi)->pkd = (PairKernData *) + calloc((*fi)->numOfPairs, sizeof(PairKernData)); + if ((*fi)->pkd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parsePairKernData(fp, *fi); + break; + case STARTCOMPOSITES: + keyword = token(fp); + if (flags & P_C) + { + (*fi)->numOfComps = atoi(keyword); + (*fi)->ccd = (CompCharData *) + calloc((*fi)->numOfComps, sizeof(CompCharData)); + if ((*fi)->ccd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parseCompCharData(fp, *fi); + break; + case ENDFONTMETRICS: + code = normalEOF; + break; + case NOPE: + default: + code = parseError; + break; + } /* switch */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + } /* while */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + if (ident != NULL) { free(ident); ident = NULL; } + + return(error); + +} /* parseFile */ + +/* added for MDVI: this function was copied from `parseAFMclient.c' */ + +void afm_free_fontinfo(FontInfo *fi) +{ + if (fi != NULL) + { + if (fi->gfi != NULL) + { + free(fi->gfi->afmVersion); fi->gfi->afmVersion = NULL; + free(fi->gfi->fontName); fi->gfi->fontName = NULL; + free(fi->gfi->fullName); fi->gfi->fullName = NULL; + free(fi->gfi->familyName); fi->gfi->familyName = NULL; + free(fi->gfi->weight); fi->gfi->weight = NULL; + free(fi->gfi->version); fi->gfi->version = NULL; + free(fi->gfi->notice); fi->gfi->notice = NULL; + free(fi->gfi->encodingScheme); fi->gfi->encodingScheme = NULL; + free(fi->gfi); fi->gfi = NULL; + } + + if (fi->cwi != NULL) + { free(fi->cwi); fi->cwi = NULL; } + + if (fi->cmi != NULL) + { + int i = 0; + CharMetricInfo *temp = fi->cmi; + Ligature *node = temp->ligs; + + for (i = 0; i < fi->numOfChars; ++i) + { + for (node = temp->ligs; node != NULL; node = node->next) + { + free(node->succ); node->succ = NULL; + free(node->lig); node->lig = NULL; + } + + free(temp->name); temp->name = NULL; + temp++; + } + + free(fi->cmi); fi->cmi = NULL; + } + + if (fi->tkd != NULL) + { free(fi->tkd); fi->tkd = NULL; } + + if (fi->pkd != NULL) + { + free(fi->pkd->name1); fi->pkd->name1 = NULL; + free(fi->pkd->name2); fi->pkd->name2 = NULL; + free(fi->pkd); fi->pkd = NULL; + } + + if (fi->ccd != NULL) + { + int i = 0, j = 0; + CompCharData *ccd = fi->ccd; + + for (i = 0; i < fi->numOfComps; ++i) + { + for (j = 0; j < ccd[i].numOfPieces; ++j) + { + free(ccd[i].pieces[j].pccName); + ccd[i].pieces[j].pccName = NULL; + } + + free(ccd[i].ccName); ccd[i].ccName = NULL; + } + + free(fi->ccd); fi->ccd = NULL; + } + + free(fi); + + } /* if */ + +} /* afm_free_fontinfo */ + +#endif /* WITH_AFM_FILES */ diff --git a/backend/dvi/mdvi-lib/afmparse.h b/backend/dvi/mdvi-lib/afmparse.h new file mode 100644 index 00000000..6cce780c --- /dev/null +++ b/backend/dvi/mdvi-lib/afmparse.h @@ -0,0 +1,307 @@ +/* modified for MDVI -- some names changed to avoid conflicts with T1lib */ +/* + * (C) 1988, 1989 by Adobe Systems Incorporated. All rights reserved. + * + * This file may be freely copied and redistributed as long as: + * 1) This entire notice continues to be included in the file, + * 2) If the file has been modified in any way, a notice of such + * modification is conspicuously indicated. + * + * PostScript, Display PostScript, and Adobe are registered trademarks of + * Adobe Systems Incorporated. + * + * ************************************************************************ + * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT + * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS + * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR + * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY + * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, + * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * ************************************************************************ + */ + +/* ParseAFM.h + * + * This header file is used in conjuction with the parseAFM.c file. + * Together these files provide the functionality to parse Adobe Font + * Metrics files and store the information in predefined data structures. + * It is intended to work with an application program that needs font metric + * information. The program can be used as is by making a procedure call to + * parse an AFM file and have the data stored, or an application developer + * may wish to customize the code. + * + * This header file defines the data structures used as well as the key + * strings that are currently recognized by this version of the AFM parser. + * This program is based on the document "Adobe Font Metrics Files, + * Specification Version 2.0". + * + * AFM files are separated into distinct sections of different data. Because + * of this, the parseAFM program can parse a specified file to only save + * certain sections of information based on the application's needs. A record + * containing the requested information will be returned to the application. + * + * AFM files are divided into five sections of data: + * 1) The Global Font Information + * 2) The Character Metrics Information + * 3) The Track Kerning Data + * 4) The Pair-Wise Kerning Data + * 5) The Composite Character Data + * + * Basically, the application can request any of these sections independent + * of what other sections are requested. In addition, in recognizing that + * many applications will want ONLY the x-width of characters and not all + * of the other character metrics information, there is a way to receive + * only the width information so as not to pay the storage cost for the + * unwanted data. An application should never request both the + * "quick and dirty" char metrics (widths only) and the Character Metrics + * Information since the Character Metrics Information will contain all + * of the character widths as well. + * + * There is a procedure in parseAFM.c, called parseFile, that can be + * called from any application wishing to get information from the AFM File. + * This procedure expects 3 parameters: a vaild file descriptor, a pointer + * to a (FontInfo *) variable (for which space will be allocated and then + * will be filled in with the data requested), and a mask specifying + * which data from the AFM File should be saved in the FontInfo structure. + * + * The flags that can be used to set the appropriate mask are defined below. + * In addition, several commonly used masks have already been defined. + * + * History: + * original: DSM Thu Oct 20 17:39:59 PDT 1988 + * modified: DSM Mon Jul 3 14:17:50 PDT 1989 + * - added 'storageProblem' return code + * - fixed typos + */ +#ifndef _MDVI_PARSEAFM_H +#define _MDVI_PARSEAFM_H 1 + +#include "sysdeps.h" + +#include <stdio.h> + +/* your basic constants */ +#define TRUE 1 +#define FALSE 0 +#define EOL '\n' /* end-of-line indicator */ +#define MAX_NAME 4096 /* max length for identifiers */ +#define BOOL int +#define FLAGS int + +/* Flags that can be AND'ed together to specify exactly what + * information from the AFM file should be saved. */ +#define P_G 0x01 /* 0000 0001 */ /* Global Font Info */ +#define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */ +#define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */ +#define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */ +#define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */ +#define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */ + +/* Commonly used flags */ +#define P_GW (P_G | P_W) +#define P_GM (P_G | P_M) +#define P_GMP (P_G | P_M | P_P) +#define P_GMK (P_G | P_M | P_P | P_T) +#define P_ALL (P_G | P_M | P_P | P_T | P_C) + +/* Possible return codes from the parseFile procedure. + * + * ok means there were no problems parsing the file. + * + * parseError means that there was some kind of parsing error, but the + * parser went on. This could include problems like the count for any given + * section does not add up to how many entries there actually were, or + * there was a key that was not recognized. The return record may contain + * vaild data or it may not. + * + * earlyEOF means that an End of File was encountered before expected. This + * may mean that the AFM file had been truncated, or improperly formed. + * + * storageProblem means that there were problems allocating storage for + * the data structures that would have contained the AFM data. + */ +#define ok 0 +#define parseError -1 +#define earlyEOF -2 +#define storageProblem -3 + +/************************* TYPES *********************************/ +/* Below are all of the data structure definitions. These structures + * try to map as closely as possible to grouping and naming of data + * in the AFM Files. + */ + +/* Bounding box definition. Used for the Font BBox as well as the + * Character BBox. + */ +typedef struct +{ + int llx; /* lower left x-position */ + int lly; /* lower left y-position */ + int urx; /* upper right x-position */ + int ury; /* upper right y-position */ +} BBox; + +/* Global Font information. + * The key that each field is associated with is in comments. For an + * explanation about each key and its value please refer to the AFM + * documentation (full title & version given above). + */ +typedef struct +{ + char *afmVersion; /* key: StartFontMetrics */ + char *fontName; /* key: FontName */ + char *fullName; /* key: FullName */ + char *familyName; /* key: FamilyName */ + char *weight; /* key: Weight */ + float italicAngle; /* key: ItalicAngle */ + BOOL isFixedPitch; /* key: IsFixedPitch */ + BBox fontBBox; /* key: FontBBox */ + int underlinePosition; /* key: UnderlinePosition */ + int underlineThickness; /* key: UnderlineThickness */ + char *version; /* key: Version */ + char *notice; /* key: Notice */ + char *encodingScheme; /* key: EncodingScheme */ + int capHeight; /* key: CapHeight */ + int xHeight; /* key: XHeight */ + int ascender; /* key: Ascender */ + int descender; /* key: Descender */ +} GlobalFontInfo; + +/* Ligature definition is a linked list since any character can have + * any number of ligatures. + */ +typedef struct _t_ligature +{ + char *succ, *lig; + struct _t_ligature *next; +} Ligature; + +/* Character Metric Information. This structure is used only if ALL + * character metric information is requested. If only the character + * widths is requested, then only an array of the character x-widths + * is returned. + * + * The key that each field is associated with is in comments. For an + * explanation about each key and its value please refer to the + * Character Metrics section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + int code, /* key: C */ + wx, /* key: WX */ + wy; /* together wx and wy are associated with key: W */ + char *name; /* key: N */ + BBox charBBox; /* key: B */ + Ligature *ligs; /* key: L (linked list; not a fixed number of Ls */ +} CharMetricInfo; + +/* Track kerning data structure. + * The fields of this record are the five values associated with every + * TrackKern entry. + * + * For an explanation about each value please refer to the + * Track Kerning section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + int degree; + float minPtSize, + minKernAmt, + maxPtSize, + maxKernAmt; +} TrackKernData; + +/* Pair Kerning data structure. + * The fields of this record are the four values associated with every + * KP entry. For KPX entries, the yamt will be zero. + * + * For an explanation about each value please refer to the + * Pair Kerning section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *name1; + char *name2; + int xamt, + yamt; +} PairKernData; + +/* PCC is a piece of a composite character. This is a sub structure of a + * compCharData described below. + * These fields will be filled in with the values from the key PCC. + * + * For an explanation about each key and its value please refer to the + * Composite Character section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *pccName; + int deltax, + deltay; +} Pcc; + +/* Composite Character Information data structure. + * The fields ccName and numOfPieces are filled with the values associated + * with the key CC. The field pieces points to an array (size = numOfPieces) + * of information about each of the parts of the composite character. That + * array is filled in with the values from the key PCC. + * + * For an explanation about each key and its value please refer to the + * Composite Character section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *ccName; + int numOfPieces; + Pcc *pieces; +} CompCharData; + +/* FontInfo + * Record type containing pointers to all of the other data + * structures containing information about a font. + * A a record of this type is filled with data by the + * parseFile function. + */ +typedef struct +{ + GlobalFontInfo *gfi; /* ptr to a GlobalFontInfo record */ + int *cwi; /* ptr to 256 element array of just char widths */ + int numOfChars; /* number of entries in char metrics array */ + CharMetricInfo *cmi; /* ptr to char metrics array */ + int numOfTracks; /* number to entries in track kerning array */ + TrackKernData *tkd; /* ptr to track kerning array */ + int numOfPairs; /* number to entries in pair kerning array */ + PairKernData *pkd; /* ptr to pair kerning array */ + int numOfComps; /* number to entries in comp char array */ + CompCharData *ccd; /* ptr to comp char array */ +} FontInfo; + +/************************* PROCEDURES ****************************/ + +/* Call this procedure to do the grunt work of parsing an AFM file. + * + * "fp" should be a valid file pointer to an AFM file. + * + * "fi" is a pointer to a pointer to a FontInfo record sturcture + * (defined above). Storage for the FontInfo structure will be + * allocated in parseFile and the structure will be filled in + * with the requested data from the AFM File. + * + * "flags" is a mask with bits set representing what data should + * be saved. Defined above are valid flags that can be used to set + * the mask, as well as a few commonly used masks. + * + * The possible return codes from parseFile are defined above. + */ + +extern int afm_parse_file __PROTO((FILE *, FontInfo **, FLAGS)); +extern void afm_free_fontinfo __PROTO((FontInfo *)); + +#endif /* _MDVI_PARSEAFM_H */ diff --git a/backend/dvi/mdvi-lib/bitmap.c b/backend/dvi/mdvi-lib/bitmap.c new file mode 100644 index 00000000..53f21207 --- /dev/null +++ b/backend/dvi/mdvi-lib/bitmap.c @@ -0,0 +1,1035 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* Bitmap manipulation routines */ + +#include <config.h> +#include <stdlib.h> + +#include "mdvi.h" +#include "color.h" + +/* bit_masks[n] contains a BmUnit with `n' contiguous bits */ + +static BmUnit bit_masks[] = { + 0x0, 0x1, 0x3, 0x7, + 0xf, 0x1f, 0x3f, 0x7f, + 0xff, +#if BITMAP_BYTES > 1 + 0x1ff, 0x3ff, 0x7ff, + 0xfff, 0x1fff, 0x3fff, 0x7fff, + 0xffff, +#if BITMAP_BYTES > 2 + 0x1ffff, 0x3ffff, 0x7ffff, + 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, + 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, + 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff +#endif /* BITMAP_BYTES > 2 */ +#endif /* BITMAP_BYTES > 1 */ +}; + +#ifndef NODEBUG +#define SHOW_OP_DATA (DEBUGGING(BITMAP_OPS) && DEBUGGING(BITMAP_DATA)) +#endif + +/* + * Some useful macros to manipulate bitmap data + * SEGMENT(m,n) = bit mask for a segment of `m' contiguous bits + * starting at column `n'. These macros assume that + * m + n <= BITMAP_BITS, 0 <= m, n. + */ +#ifdef WORD_BIG_ENDIAN +#define SEGMENT(m,n) (bit_masks[m] << (BITMAP_BITS - (m) - (n))) +#else +#define SEGMENT(m,n) (bit_masks[m] << (n)) +#endif + +/* sampling and shrinking routines shamelessly stolen from xdvi */ + +static int sample_count[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +/* bit_swap[j] = j with all bits inverted (i.e. msb -> lsb) */ +static Uchar bit_swap[] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + + +/* + * next we have three bitmap functions to convert bitmaps in LSB bit order + * with 8, 16 and 32 bits per unit, to our internal format. The differences + * are minimal, but writing a generic function to handle all unit sizes is + * hopelessly slow. + */ + +BITMAP *bitmap_convert_lsb8(Uchar *bits, int w, int h, int stride) +{ + BITMAP *bm; + int i; + Uchar *unit; + register Uchar *curr; + Uchar *end; + int bytes; + + DEBUG((DBG_BITMAP_OPS, "convert LSB %dx%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 += stride; +#else + int j; + + for(j = 0; j < bytes; curr++, j++) + unit[j] = bit_swap[*curr]; + cur += stride - bytes; +#endif + memzero(unit + bytes, bm->stride - bytes); + unit += bm->stride; + } + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); + return bm; +} + +BITMAP *bitmap_convert_msb8(Uchar *data, int w, int h, int stride) +{ + BITMAP *bm; + Uchar *unit; + Uchar *curr; + int i; + int bytes; + + bm = bitmap_alloc(w, h); + bytes = ROUND(w, 8); + unit = (Uchar *)bm->data; + curr = data; + for(i = 0; i < h; i++) { +#ifdef WORD_LITTLE_ENDIAN + int j; + + for(j = 0; j < bytes; curr++, j++) + unit[j] = bit_swap[*curr]; + curr += stride - bytes; +#else + memcpy(unit, curr, bytes); + curr += stride; +#endif + memzero(unit + bytes, bm->stride - bytes); + unit += bm->stride; + } + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); + return bm; +} + + +BITMAP *bitmap_copy(BITMAP *bm) +{ + BITMAP *nb = bitmap_alloc(bm->width, bm->height); + + DEBUG((DBG_BITMAP_OPS, "copy %dx%d\n", bm->width, bm->height)); + memcpy(nb->data, bm->data, bm->height * bm->stride); + return nb; +} + +BITMAP *bitmap_alloc(int w, int h) +{ + BITMAP *bm; + + bm = xalloc(BITMAP); + bm->width = w; + bm->height = h; + bm->stride = BM_BYTES_PER_LINE(bm); + if(h && bm->stride) + bm->data = (BmUnit *)mdvi_calloc(h, bm->stride); + else + bm->data = NULL; + + return bm; +} + +BITMAP *bitmap_alloc_raw(int w, int h) +{ + BITMAP *bm; + + bm = xalloc(BITMAP); + bm->width = w; + bm->height = h; + bm->stride = BM_BYTES_PER_LINE(bm); + if(h && bm->stride) + bm->data = (BmUnit *)mdvi_malloc(h * bm->stride); + else + bm->data = NULL; + + return bm; +} + +void bitmap_destroy(BITMAP *bm) +{ + if(bm->data) + free(bm->data); + free(bm); +} + +void bitmap_print(FILE *out, BITMAP *bm) +{ + int i, j; + BmUnit *a, mask; + static const char labels[] = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' + }; + int sub; + + a = bm->data; + fprintf(out, " "); + if(bm->width > 10) { + putchar('0'); + sub = 0; + for(j = 2; j <= bm->width; j++) + if((j %10) == 0) { + if((j % 100) == 0) { + fprintf(out, "*"); + sub += 100; + } else + fprintf(out, "%d", (j - sub)/10); + } else + putc(' ', out); + fprintf(out, "\n "); + } + for(j = 0; j < bm->width; j++) + putc(labels[j % 10], out); + putchar('\n'); + for(i = 0; i < bm->height; i++) { + mask = FIRSTMASK; + a = (BmUnit *)((char *)bm->data + i * bm->stride); + fprintf(out, "%3d ", i+1); + for(j = 0; j < bm->width; j++) { + if(*a & mask) + putc('#', out); + else + putc('.', out); + if(mask == LASTMASK) { + a++; + mask = FIRSTMASK; + } else + NEXTMASK(mask); + } + putchar('\n'); + } +} + +void bitmap_set_col(BITMAP *bm, int row, int col, int count, int state) +{ + BmUnit *ptr; + BmUnit mask; + + ptr = __bm_unit_ptr(bm, col, row); + mask = FIRSTMASKAT(col); + + while(count-- > 0) { + if(state) + *ptr |= mask; + else + *ptr &= ~mask; + /* move to next row */ + ptr = bm_offset(ptr, bm->stride); + } +} + +/* + * to use this function you should first make sure that + * there is room for `count' bits in the scanline + * + * A general-purpose (but not very efficient) function to paint `n' pixels + * on a bitmap, starting at position (x, y) would be: + * + * bitmap_paint_bits(__bm_unit_ptr(bitmap, x, y), x % BITMAP_BITS, n) + * + */ +void bitmap_paint_bits(BmUnit *ptr, int n, int count) +{ + /* paint the head */ + if(n + count > BITMAP_BITS) { + *ptr |= SEGMENT(BITMAP_BITS - n, n); + count -= BITMAP_BITS - n; + ptr++; + } else { + *ptr |= SEGMENT(count, n); + return; + } + + /* paint the middle */ + for(; count >= BITMAP_BITS; count -= BITMAP_BITS) + *ptr++ = bit_masks[BITMAP_BITS]; + + /* paint the tail */ + if(count > 0) + *ptr |= SEGMENT(count, 0); +} + +/* + * same as paint_bits but clears pixels instead of painting them. Written + * as a separate function for efficiency reasons. + */ +void bitmap_clear_bits(BmUnit *ptr, int n, int count) +{ + if(n + count > BITMAP_BITS) { + *ptr &= ~SEGMENT(BITMAP_BITS - n, n); + count -= BITMAP_BITS; + ptr++; + } else { + *ptr &= ~SEGMENT(count, n); + return; + } + + for(; count >= BITMAP_BITS; count -= BITMAP_BITS) + *ptr++ = 0; + + if(count > 0) + *ptr &= ~SEGMENT(count, 0); +} + +/* the general function to paint rows. Still used by the PK reader, but that + * will change soon (The GF reader already uses bitmap_paint_bits()). + */ +void bitmap_set_row(BITMAP *bm, int row, int col, int count, int state) +{ + BmUnit *ptr; + + ptr = __bm_unit_ptr(bm, col, row); + if(state) + bitmap_paint_bits(ptr, col & (BITMAP_BITS-1), count); + else + bitmap_clear_bits(ptr, col & (BITMAP_BITS-1), count); +} + +/* + * Now several `flipping' operations + */ + +void bitmap_flip_horizontally(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->width; + nb.height = bm->height; + nb.stride = bm->stride; + nb.data = mdvi_calloc(bm->height, bm->stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, nb.width-1, 0); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fline = fptr; + tline = tptr; + fmask = FIRSTMASK; + tmask = FIRSTMASKAT(nb.width-1); + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + if(tmask == FIRSTMASK) { + tmask = LASTMASK; + tline--; + } else + PREVMASK(tmask); + } + fptr = bm_offset(fptr, bm->stride); + tptr = bm_offset(tptr, bm->stride); + } + DEBUG((DBG_BITMAP_OPS, "flip_horizontally (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_flip_vertically(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask; + int w, h; + + nb.width = bm->width; + nb.height = bm->height; + nb.stride = bm->stride; + nb.data = mdvi_calloc(bm->height, bm->stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, 0, nb.height-1); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fline = fptr; + tline = tptr; + fmask = FIRSTMASK; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= fmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + tline++; + } else + NEXTMASK(fmask); + } + fptr = bm_offset(fptr, bm->stride); + tptr = (BmUnit *)((char *)tptr - bm->stride); + } + DEBUG((DBG_BITMAP_OPS, "flip_vertically (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_flip_diagonally(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->width; + nb.height = bm->height; + nb.stride = bm->stride; + nb.data = mdvi_calloc(bm->height, bm->stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, nb.width-1, nb.height-1); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fline = fptr; + tline = tptr; + fmask = FIRSTMASK; + tmask = FIRSTMASKAT(nb.width-1); + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + if(tmask == FIRSTMASK) { + tmask = LASTMASK; + tline--; + } else + PREVMASK(tmask); + } + fptr = bm_offset(fptr, bm->stride); + tptr = bm_offset(tptr, -nb.stride); + } + DEBUG((DBG_BITMAP_OPS, "flip_diagonally (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_rotate_clockwise(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->height; + nb.height = bm->width; + nb.stride = BM_BYTES_PER_LINE(&nb); + nb.data = mdvi_calloc(nb.height, nb.stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, nb.width - 1, 0); + + tmask = FIRSTMASKAT(nb.width-1); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fmask = FIRSTMASK; + fline = fptr; + tline = tptr; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + /* go to next row */ + tline = bm_offset(tline, nb.stride); + } + fptr = bm_offset(fptr, bm->stride); + if(tmask == FIRSTMASK) { + tmask = LASTMASK; + tptr--; + } else + PREVMASK(tmask); + } + + DEBUG((DBG_BITMAP_OPS, "rotate_clockwise (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + bm->width = nb.width; + bm->height = nb.height; + bm->stride = nb.stride; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_rotate_counter_clockwise(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->height; + nb.height = bm->width; + nb.stride = BM_BYTES_PER_LINE(&nb); + nb.data = mdvi_calloc(nb.height, nb.stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, 0, nb.height - 1); + + tmask = FIRSTMASK; + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fmask = FIRSTMASK; + fline = fptr; + tline = tptr; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + /* go to previous row */ + tline = bm_offset(tline, -nb.stride); + } + fptr = bm_offset(fptr, bm->stride); + if(tmask == LASTMASK) { + tmask = FIRSTMASK; + tptr++; + } else + NEXTMASK(tmask); + } + + DEBUG((DBG_BITMAP_OPS, "rotate_counter_clockwise (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + bm->width = nb.width; + bm->height = nb.height; + bm->stride = nb.stride; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_flip_rotate_clockwise(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->height; + nb.height = bm->width; + nb.stride = BM_BYTES_PER_LINE(&nb); + nb.data = mdvi_calloc(nb.height, nb.stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, nb.width-1, nb.height-1); + + tmask = FIRSTMASKAT(nb.width-1); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fmask = FIRSTMASK; + fline = fptr; + tline = tptr; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + /* go to previous line */ + tline = bm_offset(tline, -nb.stride); + } + fptr = bm_offset(fptr, bm->stride); + if(tmask == FIRSTMASK) { + tmask = LASTMASK; + tptr--; + } else + PREVMASK(tmask); + } + DEBUG((DBG_BITMAP_OPS, "flip_rotate_clockwise (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + bm->width = nb.width; + bm->height = nb.height; + bm->stride = nb.stride; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_flip_rotate_counter_clockwise(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->height; + nb.height = bm->width; + nb.stride = BM_BYTES_PER_LINE(&nb); + nb.data = mdvi_calloc(nb.height, nb.stride); + + fptr = bm->data; + tptr = nb.data; + tmask = FIRSTMASK; + + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fmask = FIRSTMASK; + fline = fptr; + tline = tptr; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + /* go to next line */ + tline = bm_offset(tline, nb.stride); + } + fptr = bm_offset(fptr, bm->stride); + if(tmask == LASTMASK) { + tmask = FIRSTMASK; + tptr++; + } else + NEXTMASK(tmask); + } + + DEBUG((DBG_BITMAP_OPS, "flip_rotate_counter_clockwise (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + bm->width = nb.width; + bm->height = nb.height; + bm->stride = nb.stride; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +#if 0 +void bitmap_transform(BITMAP *map, DviOrientation orient) +{ + switch(orient) { + case MDVI_ORIENT_TBLR: + break; + case MDVI_ORIENT_TBRL: + bitmap_flip_horizontally(map); + break; + case MDVI_ORIENT_BTLR: + bitmap_flip_vertically(map); + break; + case MDVI_ORIENT_BTRL: + bitmap_flip_diagonally(map); + break; + case MDVI_ORIENT_RP90: + bitmap_rotate_counter_clockwise(map); + break; + case MDVI_ORIENT_RM90: + bitmap_rotate_clockwise(map); + break; + case MDVI_ORIENT_IRP90: + bitmap_flip_rotate_counter_clockwise(map); + break; + case MDVI_ORIENT_IRM90: + bitmap_flip_rotate_clockwise(map); + break; + } +} +#endif + +/* + * Count the number of non-zero bits in a box of dimensions w x h, starting + * at column `step' in row `data'. + * + * Shamelessly stolen from xdvi. + */ +static int do_sample(BmUnit *data, int stride, int step, int w, int h) +{ + BmUnit *ptr, *end, *cp; + int shift, n; + int bits_left; + int wid; + + ptr = data + step / BITMAP_BITS; + end = bm_offset(data, h * stride); + shift = FIRSTSHIFTAT(step); + bits_left = w; + n = 0; + while(bits_left) { +#ifndef WORD_BIG_ENDIAN + wid = BITMAP_BITS - shift; +#else + wid = shift; +#endif + if(wid > bits_left) + wid = bits_left; + if(wid > 8) + wid = 8; +#ifdef WORD_BIG_ENDIAN + shift -= wid; +#endif + for(cp = ptr; cp < end; cp = bm_offset(cp, stride)) + n += sample_count[(*cp >> shift) & bit_masks[wid]]; +#ifndef WORD_BIG_ENDIAN + shift += wid; +#endif +#ifdef WORD_BIG_ENDIAN + if(shift == 0) { + shift = BITMAP_BITS; + ptr++; + } +#else + if(shift == BITMAP_BITS) { + shift = 0; + ptr++; + } +#endif + bits_left -= wid; + } + return n; +} + +void mdvi_shrink_box(DviContext *dvi, DviFont *font, + DviFontChar *pk, DviGlyph *dest) +{ + int x, y, z; + DviGlyph *glyph; + int hs, vs; + + hs = dvi->params.hshrink; + vs = dvi->params.vshrink; + glyph = &pk->glyph; + + x = (int)glyph->x / hs; + if((int)glyph->x - x * hs > 0) + x++; + dest->w = x + ROUND((int)glyph->w - glyph->x, hs); + + z = (int)glyph->y + 1; + y = z / vs; + if(z - y * vs <= 0) + y--; + dest->h = y + ROUND((int)glyph->h - z, vs) + 1; + dest->x = x; + dest->y = glyph->y / vs; + dest->data = MDVI_GLYPH_EMPTY; + DEBUG((DBG_BITMAPS, "shrink_box: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n", + glyph->w, glyph->h, glyph->x, glyph->y, + dest->w, dest->h, dest->x, dest->y)); +} + +void mdvi_shrink_glyph(DviContext *dvi, DviFont *font, + DviFontChar *pk, DviGlyph *dest) +{ + int rows_left, rows, init_cols; + int cols_left, cols; + BmUnit *old_ptr, *new_ptr; + BITMAP *oldmap, *newmap; + BmUnit m, *cp; + DviGlyph *glyph; + int sample, min_sample; + int old_stride; + int new_stride; + int x, y; + int w, h; + int hs, vs; + + hs = dvi->params.hshrink; + vs = dvi->params.vshrink; + + min_sample = vs * hs * dvi->params.density / 100; + + glyph = &pk->glyph; + oldmap = (BITMAP *)glyph->data; + + x = (int)glyph->x / hs; + init_cols = (int)glyph->x - x * hs; + if(init_cols <= 0) + init_cols += hs; + else + x++; + w = x + ROUND((int)glyph->w - glyph->x, hs); + + cols = (int)glyph->y + 1; + y = cols / vs; + rows = cols - y * vs; + if(rows <= 0) { + rows += vs; + y--; + } + h = y + ROUND((int)glyph->h - cols, vs) + 1; + + /* create the new glyph */ + newmap = bitmap_alloc(w, h); + dest->data = newmap; + dest->x = x; + dest->y = glyph->y / vs; + dest->w = w; + dest->h = h; + + old_ptr = oldmap->data; + old_stride = oldmap->stride; + new_ptr = newmap->data; + new_stride = newmap->stride; + rows_left = glyph->h; + + while(rows_left) { + if(rows > rows_left) + rows = rows_left; + cols_left = glyph->w; + m = FIRSTMASK; + cp = new_ptr; + cols = init_cols; + while(cols_left > 0) { + if(cols > cols_left) + cols = cols_left; + sample = do_sample(old_ptr, old_stride, + glyph->w - cols_left, cols, rows); + if(sample >= min_sample) + *cp |= m; + if(m == LASTMASK) { + m = FIRSTMASK; + cp++; + } else + NEXTMASK(m); + cols_left -= cols; + cols = hs; + } + new_ptr = bm_offset(new_ptr, new_stride); + old_ptr = bm_offset(old_ptr, rows * old_stride); + rows_left -= rows; + rows = vs; + } + DEBUG((DBG_BITMAPS, "shrink_glyph: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n", + glyph->w, glyph->h, glyph->x, glyph->y, + dest->w, dest->h, dest->x, dest->y)); + if(DEBUGGING(BITMAP_DATA)) + bitmap_print(stderr, newmap); +} + +void mdvi_shrink_glyph_grey(DviContext *dvi, DviFont *font, + DviFontChar *pk, DviGlyph *dest) +{ + int rows_left, rows; + int cols_left, cols, init_cols; + long sampleval, samplemax; + BmUnit *old_ptr; + void *image; + int w, h; + int x, y; + DviGlyph *glyph; + BITMAP *map; + Ulong *pixels; + int npixels; + Ulong colortab[2]; + int hs, vs; + DviDevice *dev; + + hs = dvi->params.hshrink; + vs = dvi->params.vshrink; + dev = &dvi->device; + + glyph = &pk->glyph; + map = (BITMAP *)glyph->data; + + x = (int)glyph->x / hs; + init_cols = (int)glyph->x - x * hs; + if(init_cols <= 0) + init_cols += hs; + else + x++; + w = x + ROUND((int)glyph->w - glyph->x, hs); + + cols = (int)glyph->y + 1; + y = cols / vs; + rows = cols - y * vs; + if(rows <= 0) { + rows += vs; + y--; + } + h = y + ROUND((int)glyph->h - cols, vs) + 1; + ASSERT(w && h); + + /* before touching anything, do this */ + image = dev->create_image(dev->device_data, w, h, BITMAP_BITS); + if(image == NULL) { + mdvi_shrink_glyph(dvi, font, pk, dest); + return; + } + + /* save these colors */ + pk->fg = MDVI_CURRFG(dvi); + pk->bg = MDVI_CURRBG(dvi); + + samplemax = vs * hs; + npixels = samplemax + 1; + pixels = get_color_table(&dvi->device, npixels, pk->fg, pk->bg, + dvi->params.gamma, dvi->params.density); + if(pixels == NULL) { + npixels = 2; + colortab[0] = pk->fg; + colortab[1] = pk->bg; + pixels = &colortab[0]; + } + + /* setup the new glyph */ + dest->data = image; + dest->x = x; + dest->y = glyph->y / vs; + dest->w = w; + dest->h = h; + + y = 0; + old_ptr = map->data; + rows_left = glyph->h; + + while(rows_left && y < h) { + x = 0; + if(rows > rows_left) + rows = rows_left; + cols_left = glyph->w; + cols = init_cols; + while(cols_left && x < w) { + if(cols > cols_left) + cols = cols_left; + sampleval = do_sample(old_ptr, map->stride, + glyph->w - cols_left, cols, rows); + /* scale the sample value by the number of grey levels */ + if(npixels - 1 != samplemax) + sampleval = ((npixels-1) * sampleval) / samplemax; + ASSERT(sampleval < npixels); + dev->put_pixel(image, x, y, pixels[sampleval]); + cols_left -= cols; + cols = hs; + x++; + } + for(; x < w; x++) + dev->put_pixel(image, x, y, pixels[0]); + old_ptr = bm_offset(old_ptr, rows * map->stride); + rows_left -= rows; + rows = vs; + y++; + } + + for(; y < h; y++) { + for(x = 0; x < w; x++) + dev->put_pixel(image, x, y, pixels[0]); + } + DEBUG((DBG_BITMAPS, "shrink_glyph_grey: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n", + glyph->w, glyph->h, glyph->x, glyph->y, + dest->w, dest->h, dest->x, dest->y)); +} + diff --git a/backend/dvi/mdvi-lib/bitmap.h b/backend/dvi/mdvi-lib/bitmap.h new file mode 100644 index 00000000..4d98fecd --- /dev/null +++ b/backend/dvi/mdvi-lib/bitmap.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _BITMAP_H +#define _BITMAP_H 1 + +#include "sysdeps.h" + +/* Structures and functions to manipulate bitmaps */ + +/* bitmap unit (as in X's docs) */ +typedef Uint32 BmUnit; + +/* size (in bytes) of a bitmap atom */ +#define BITMAP_BYTES 4 + +/* size (in bits) of a bitmap atom */ +#define BITMAP_BITS (BITMAP_BYTES << 3) + +typedef struct { + int width; + int height; + int stride; + BmUnit *data; +} BITMAP; + +#define BM_BYTES_PER_LINE(b) \ + (ROUND((b)->width, BITMAP_BITS) * BITMAP_BYTES) +#define BM_WIDTH(b) (((BITMAP *)(b))->width) +#define BM_HEIGHT(b) (((BITMAP *)(b))->height) + +#define BMBIT(n) ((BmUnit)1 << (n)) + +/* Macros to manipulate individual pixels in a bitmap + * (they are slow, don't use them) + */ + +#define bm_offset(b,o) (BmUnit *)((Uchar *)(b) + (o)) + +#define __bm_unit_ptr(b,x,y) \ + bm_offset((b)->data, (y) * (b)->stride + \ + ((x) / BITMAP_BITS) * BITMAP_BYTES) + +#define __bm_unit(b,x,y) __bm_unit_ptr((b), (x), (y))[0] + +#define BM_GETPIXEL(b,x,y) __bm_unit((b), (x), (y)) +#define BM_SETPIXEL(b,x,y) (__bm_unit((b), (x), (y)) |= FIRSTMASKAT(x)) +#define BM_CLRPIXEL(b,x,y) (__bm_unit((b), (x), (y)) &= ~FIRSTMASKAT(x)) + +/* + * These macros are used to access pixels in a bitmap. They are supposed + * to be used like this: + */ +#if 0 + BmUnit *row, mask; + + mask = FIRSTMASK; + + /* position `unit' at coordinates (column_number, row_number) */ + unit = (BmUnit *)((char *)bitmap->data + row_number * bitmap->stride + + (column_number / BITMAP_BITS); + /* loop over all pixels IN THE SAME ROW */ + for(i = 0; i < number_of_pixels; i++) { + /* to test if a pixel is set */ + if(*unit & mask) { + /* yes, it is, do something with it */ + } + /* to set/clear a pixel */ + if(painting) + *unit |= mask; /* now you see it */ + else + *unit &= ~mask; /* now you don't */ + /* move to next pixel */ + if(mask == LASTMASK) { + unit++; + UPDATEMASK(mask); + } + } +/* end of sample code */ +#endif + +/* bitmaps are stored in native byte order */ +#ifdef WORD_BIG_ENDIAN +#define FIRSTSHIFT (BITMAP_BITS - 1) +#define LASTSHIFT 0 +#define NEXTMASK(m) ((m) >>= 1) +#define PREVMASK(m) ((m) <<= 1) +#define FIRSTSHIFTAT(c) (BITMAP_BITS - ((c) % BITMAP_BITS) - 1) +#else +#define FIRSTSHIFT 0 +#define LASTSHIFT (BITMAP_BITS - 1) +#define NEXTMASK(m) ((m) <<= 1) +#define PREVMASK(m) ((m) >>= 1) +#define FIRSTSHIFTAT(c) ((c) % BITMAP_BITS) +#endif + +#define FIRSTMASK BMBIT(FIRSTSHIFT) +#define FIRSTMASKAT(c) BMBIT(FIRSTSHIFTAT(c)) +#define LASTMASK BMBIT(LASTSHIFT) + +extern BITMAP *bitmap_alloc __PROTO((int, int)); +extern BITMAP *bitmap_alloc_raw __PROTO((int, int)); +extern void bitmap_destroy __PROTO((BITMAP *)); + +/* + * set_row(bm, row, col, count, state): + * sets `count' pixels to state `onoff', starting from pixel + * at position (col, row). All pixels must lie in the same + * row. + */ +extern void bitmap_set_col __PROTO((BITMAP *, int, int, int, int)); +extern void bitmap_set_row __PROTO((BITMAP *, int, int, int, int)); + +extern void bitmap_paint_bits __PROTO((BmUnit *, int, int)); +extern void bitmap_clear_bits __PROTO((BmUnit *, int, int)); + +extern BITMAP *bitmap_copy __PROTO((BITMAP *)); +extern void bitmap_flip_horizontally __PROTO((BITMAP *)); +extern void bitmap_flip_vertically __PROTO((BITMAP *)); +extern void bitmap_flip_diagonally __PROTO((BITMAP *)); +extern void bitmap_rotate_clockwise __PROTO((BITMAP *)); +extern void bitmap_rotate_counter_clockwise __PROTO((BITMAP *)); +extern void bitmap_flip_rotate_clockwise __PROTO((BITMAP *)); +extern void bitmap_flip_rotate_counter_clockwise __PROTO((BITMAP *)); +extern BITMAP *bitmap_convert_lsb8 __PROTO((Uchar *, int, int, int)); +extern BITMAP *bitmap_convert_msb8 __PROTO((Uchar *, int, int, int)); + +#include <stdio.h> +extern void bitmap_print __PROTO((FILE *, BITMAP *)); + +#endif /* _BITMAP_H */ diff --git a/backend/dvi/mdvi-lib/color.c b/backend/dvi/mdvi-lib/color.c new file mode 100644 index 00000000..c28107fd --- /dev/null +++ b/backend/dvi/mdvi-lib/color.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "mdvi.h" +#include "color.h" + +void mdvi_set_color(DviContext *dvi, Ulong fg, Ulong bg) +{ + if(dvi->curr_fg != fg || dvi->curr_bg != bg) { + DEBUG((DBG_DEVICE, "setting color to (%lu,%lu)\n", fg, bg)); + if(dvi->device.set_color) + dvi->device.set_color(dvi->device.device_data, fg, bg); + dvi->curr_fg = fg; + dvi->curr_bg = bg; + } +} + +void mdvi_push_color(DviContext *dvi, Ulong fg, Ulong bg) +{ + if(dvi->color_top == dvi->color_size) { + dvi->color_size += 32; + dvi->color_stack = mdvi_realloc(dvi->color_stack, + dvi->color_size * sizeof(DviColorPair)); + } + dvi->color_stack[dvi->color_top].fg = dvi->curr_fg; + dvi->color_stack[dvi->color_top].bg = dvi->curr_bg; + dvi->color_top++; + mdvi_set_color(dvi, fg, bg); +} + +void mdvi_pop_color(DviContext *dvi) +{ + Ulong fg, bg; + + if(dvi->color_top == 0) + return; + dvi->color_top--; + fg = dvi->color_stack[dvi->color_top].fg; + bg = dvi->color_stack[dvi->color_top].bg; + mdvi_set_color(dvi, fg, bg); +} + +void mdvi_reset_color(DviContext *dvi) +{ + dvi->color_top = 0; + mdvi_set_color(dvi, dvi->params.fg, dvi->params.bg); +} + +/* cache for color tables, to avoid creating them for every glyph */ +typedef struct { + Ulong fg; + Ulong bg; + Uint nlevels; + Ulong *pixels; + int density; + double gamma; + Uint hits; +} ColorCache; + +#define CCSIZE 256 +static ColorCache color_cache[CCSIZE]; +static int cc_entries; + +#define GAMMA_DIFF 0.005 + + +/* create a color table */ +Ulong *get_color_table(DviDevice *dev, + int nlevels, Ulong fg, Ulong bg, double gamma, int density) +{ + ColorCache *cc, *tofree; + int lohits; + Ulong *pixels; + int status; + + lohits = color_cache[0].hits; + tofree = &color_cache[0]; + /* look in the cache and see if we have one that matches this request */ + for(cc = &color_cache[0]; cc < &color_cache[cc_entries]; cc++) { + if(cc->hits < lohits) { + lohits = cc->hits; + tofree = cc; + } + if(cc->fg == fg && cc->bg == bg && cc->density == density && + cc->nlevels == nlevels && fabs(cc->gamma - gamma) <= GAMMA_DIFF) + break; + } + + if(cc < &color_cache[cc_entries]) { + cc->hits++; + return cc->pixels; + } + + DEBUG((DBG_DEVICE, "Adding color table to cache (fg=%lu, bg=%lu, n=%d)\n", + fg, bg, nlevels)); + + /* no entry was found in the cache, create a new one */ + if(cc_entries < CCSIZE) { + cc = &color_cache[cc_entries++]; + cc->pixels = NULL; + } else { + cc = tofree; + mdvi_free(cc->pixels); + } + pixels = xnalloc(Ulong, nlevels); + status = dev->alloc_colors(dev->device_data, + pixels, nlevels, fg, bg, gamma, density); + if(status < 0) { + mdvi_free(pixels); + return NULL; + } + cc->fg = fg; + cc->bg = bg; + cc->gamma = gamma; + cc->density = density; + cc->nlevels = nlevels; + cc->pixels = pixels; + cc->hits = 1; + return pixels; +} diff --git a/backend/dvi/mdvi-lib/color.h b/backend/dvi/mdvi-lib/color.h new file mode 100644 index 00000000..35b2f923 --- /dev/null +++ b/backend/dvi/mdvi-lib/color.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _COLOR_H_ +#define _COLOR_H_ + +#include "common.h" + +extern Ulong *get_color_table(DviDevice *dev, + int nlevels, Ulong fg, Ulong bg, double gamma, int density); + +extern void mdvi_set_color __PROTO((DviContext *, Ulong, Ulong)); +extern void mdvi_push_color __PROTO((DviContext *, Ulong, Ulong)); +extern void mdvi_pop_color __PROTO((DviContext *)); +extern void mdvi_reset_color __PROTO((DviContext *)); + +#endif /* _COLOR_H_ */ + diff --git a/backend/dvi/mdvi-lib/common.c b/backend/dvi/mdvi-lib/common.c new file mode 100644 index 00000000..97b34b56 --- /dev/null +++ b/backend/dvi/mdvi-lib/common.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> + +#include "common.h" + +long fsgetn(FILE *p, size_t n) +{ + long v; + + v = fgetbyte(p); + if(v & 0x80) + v -= 0x100; + while(--n > 0) + v = (v << 8) | fgetbyte(p); + return v; +} + +Ulong fugetn(FILE *p, size_t n) +{ + Ulong v; + + v = fgetbyte(p); + while(--n > 0) + v = (v << 8) | fgetbyte(p); + return v; +} + +long msgetn(const Uchar *p, size_t n) +{ + long v = (long)*p++; + + if(v & 0x80) + v -= 0x100; + while(--n > 0) + v = (v << 8) | *p++; + return v; +} + +Ulong mugetn(const Uchar *p, size_t n) +{ + Ulong v = (Ulong)*p++; + + while(--n > 0) + v = (v << 8) | *p++; + return v; +} + +char *read_string(FILE *in, int s, char *buffer, size_t len) +{ + int n; + char *str; + + n = fugetn(in, s ? s : 1); + if((str = buffer) == NULL || n + 1 > len) + str = mdvi_malloc(n + 1); + if(fread(str, 1, n, in) != n) { + if(str != buffer) mdvi_free(str); + return NULL; + } + str[n] = 0; + return str; +} + +size_t read_bcpl(FILE *in, char *buffer, size_t maxlen, size_t wanted) +{ + size_t i; + + i = (int)fuget1(in); + if(maxlen && i > maxlen) + i = maxlen; + if(fread(buffer, i, 1, in) != 1) + return -1; + buffer[i] = '\0'; + while(wanted-- > i) + (void)fgetc(in); + return i; +} + +char *read_alloc_bcpl(FILE *in, size_t maxlen, size_t *size) +{ + size_t i; + char *buffer; + + i = (size_t)fuget1(in); + if(maxlen && i > maxlen) + i = maxlen; + buffer = (char *)malloc(i + 1); + if(buffer == NULL) + return NULL; + if(fread(buffer, i, 1, in) != 1) { + free(buffer); + return NULL; + } + buffer[i] = '\0'; + if(size) *size = i; + return buffer; +} + +/* buffers */ + +void buff_free(Buffer *buf) +{ + if(buf->data) + mdvi_free(buf->data); + buff_init(buf); +} + +void buff_init(Buffer *buf) +{ + buf->data = NULL; + buf->size = 0; + buf->length = 0; +} + +size_t buff_add(Buffer *buf, const char *data, size_t len) +{ + if(!len && data) + len = strlen(data); + if(buf->length + len + 1 > buf->size) { + buf->size = buf->length + len + 256; + buf->data = mdvi_realloc(buf->data, buf->size); + } + memcpy(buf->data + buf->length, data, len); + buf->length += len; + return buf->length; +} + +char *buff_gets(Buffer *buf, size_t *length) +{ + char *ptr; + char *ret; + size_t len; + + ptr = strchr(buf->data, '\n'); + if(ptr == NULL) + return NULL; + ptr++; /* include newline */ + len = ptr - buf->data; + ret = mdvi_malloc(len + 1); + if(len > 0) { + memcpy(ret, buf->data, len); + memmove(buf->data, buf->data + len, buf->length - len); + buf->length -= len; + } + ret[len] = 0; + if(length) *length = len; + return ret; +} + diff --git a/backend/dvi/mdvi-lib/common.h b/backend/dvi/mdvi-lib/common.h new file mode 100644 index 00000000..27a7d8f9 --- /dev/null +++ b/backend/dvi/mdvi-lib/common.h @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_COMMON_H +#define _MDVI_COMMON_H 1 + +#include <stdio.h> +#include <sys/types.h> +#include <math.h> + +#include "sysdeps.h" + +#if STDC_HEADERS +# include <string.h> +#endif + +#if !defined(STDC_HEADERS) || defined(__STRICT_ANSI__) +# ifndef HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +# ifndef HAVE_MEMCPY +# define memcpy(a,b,n) bcopy((b), (a), (n)) +# define memmove(a,b,n) bcopy((b), (a), (n)) +# endif +#endif + +#if defined(STDC_HEADERS) || defined(HAVE_MEMCPY) +#define memzero(a,n) memset((a), 0, (n)) +#else +#define memzero(a,n) bzero((a), (n)) +#endif + +typedef struct _List { + struct _List *next; + struct _List *prev; +} List; +#define LIST(x) ((List *)(x)) + +typedef struct { + char *data; + size_t size; + size_t length; +} Buffer; + +typedef struct { + List *head; + List *tail; + int count; +} ListHead; +#define MDVI_EMPTY_LIST_HEAD {NULL, NULL, 0} + +typedef struct { + char *data; + size_t size; + size_t length; +} Dstring; + +/* Functions to read numbers from streams and memory */ + +#define fgetbyte(p) ((unsigned)getc(p)) + +extern char *program_name; + +extern Ulong fugetn __PROTO((FILE *, size_t)); +extern long fsgetn __PROTO((FILE *, size_t)); +extern Ulong mugetn __PROTO((const Uchar *, size_t)); +extern long msgetn __PROTO((const Uchar *, size_t)); + +/* To read from a stream (fu: unsigned, fs: signed) */ +#define fuget4(p) fugetn((p), 4) +#define fuget3(p) fugetn((p), 3) +#define fuget2(p) fugetn((p), 2) +#define fuget1(p) fgetbyte(p) +#define fsget4(p) fsgetn((p), 4) +#define fsget3(p) fsgetn((p), 3) +#define fsget2(p) fsgetn((p), 2) +#define fsget1(p) fsgetn((p), 1) + +/* To read from memory (mu: unsigned, ms: signed) */ +#define MUGETN(p,n) ((p) += (n), mugetn((p)-(n), (n))) +#define MSGETN(p,n) ((p) += (n), msgetn((p)-(n), (n))) +#define muget4(p) MUGETN((p), 4) +#define muget3(p) MUGETN((p), 3) +#define muget2(p) MUGETN((p), 2) +#define muget1(p) MUGETN((p), 1) +#define msget4(p) MSGETN((p), 4) +#define msget3(p) MSGETN((p), 3) +#define msget2(p) MSGETN((p), 2) +#define msget1(p) MSGETN((p), 1) + +#define ROUND(x,y) (((x) + (y) - 1) / (y)) +#define FROUND(x) (int)((x) + 0.5) +#define SFROUND(x) (int)((x) >= 0 ? floor((x) + 0.5) : ceil((x) + 0.5)) + +#define Max(a,b) (((a) > (b)) ? (a) : (b)) +#define Min(a,b) (((a) < (b)) ? (a) : (b)) + +/* make 2byte number from 2 8bit quantities */ +#define HALFWORD(a,b) ((((a) << 8) & 0xf) | (b)) +#define FULLWORD(a,b,c,d) \ + ((((Int8)(a) << 24) & 0xff000000) | \ + (((Uint8)(b) << 16) & 0x00ff0000) | \ + (((Uint8)(c) << 8) & 0x0000ff00) | \ + ((Uint8)(d) & 0xff)) + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define SWAPINT(a,b) \ + ({ int _s = a; a = b; b = _s; }) +#else +#define SWAPINT(a,b) do { int _s = a; a = b; b = _s; } while(0) +#endif + +#define STREQ(a,b) (strcmp((a), (b)) == 0) +#define STRNEQ(a,b,n) (strncmp((a), (b), (n)) == 0) +#define STRCEQ(a,b) (strcasecmp((a), (b)) == 0) +#define STRNCEQ(a,b,n) (strncasecmp((a), (b), (n)) == 0) + +extern char *read_string __PROTO((FILE *, int, char *, size_t)); +extern size_t read_bcpl __PROTO((FILE *, char *, size_t, size_t)); +extern char *read_alloc_bcpl __PROTO((FILE *, size_t, size_t *)); + +/* miscellaneous */ + +extern void mdvi_message __PROTO((const char *, ...)); +extern void mdvi_crash __PROTO((const char *, ...)); +extern void mdvi_fatal __PROTO((const char *, ...)); +extern void mdvi_error __PROTO((const char *, ...)); +extern void mdvi_warning __PROTO((const char *, ...)); +extern int unit2pix __PROTO((int, const char *)); +extern double unit2pix_factor __PROTO((const char *)); + +#define LOG_NONE -1 +#define LOG_INFO 0 +#define LOG_WARN 1 +#define LOG_ERROR 2 +#define LOG_DEBUG 3 + +#define DBG_OPCODE (1 << 0) +#define DBG_FONTS (1 << 1) +#define DBG_FILES (1 << 2) +#define DBG_DVI (1 << 3) +#define DBG_PARAMS (1 << 4) +#define DBG_SPECIAL (1 << 5) +#define DBG_DEVICE (1 << 6) +#define DBG_GLYPHS (1 << 7) +#define DBG_BITMAPS (1 << 8) +#define DBG_PATHS (1 << 9) +#define DBG_SEARCH (1 << 10) +#define DBG_VARS (1 << 11) +#define DBG_BITMAP_OPS (1 << 12) +#define DBG_BITMAP_DATA (1 << 13) +#define DBG_TYPE1 (1 << 14) +#define DBG_TT (1 << 15) +#define DBG_FT2 (1 << 16) +#define DBG_FMAP (1 << 17) + +#define DBG_SILENT (1 << 31) + +#ifdef NODEBUG +#define DEBUGGING(x) 0 +#else +#define DEBUGGING(x) (_mdvi_debug_mask & DBG_##x) +#endif + +#ifndef NODEBUG +extern Uint32 _mdvi_debug_mask; +extern void __debug __PROTO((int, const char *, ...)); +#define DEBUG(x) __debug x +#define ASSERT(x) do { \ + if(!(x)) mdvi_crash("%s:%d: Assertion %s failed\n", \ + __FILE__, __LINE__, #x); \ + } while(0) +#define ASSERT_VALUE(x,y) do { \ + if((x) != (y)) \ + mdvi_crash("%s:%d: Assertion failed (%d = %s != %s)\n", \ + __FILE__, __LINE__, (x), #x, #x); \ + } while(0) +#else +#define DEBUG(x) do { } while(0) +#define ASSERT(x) do { } while(0) +#define ASSERT_VALUE(x,y) do { } while(0) +#endif + +#define set_debug_mask(m) (_mdvi_debug_mask = (Uint32)(m)) +#define add_debug_mask(m) (_mdvi_debug_mask |= (Uint32)(m)) +#define get_debug_mask() _mdvi_debug_mask + +/* memory allocation */ + +extern void mdvi_free __PROTO((void *)); +extern void *mdvi_malloc __PROTO((size_t)); +extern void *mdvi_realloc __PROTO((void *, size_t)); +extern void *mdvi_calloc __PROTO((size_t, size_t)); +extern char *mdvi_strncpy __PROTO((char *, const char *, size_t)); +extern char *mdvi_strdup __PROTO((const char *)); +extern char *mdvi_strndup __PROTO((const char *, size_t)); +extern void *mdvi_memdup __PROTO((const void *, size_t)); +extern char *mdvi_build_path_from_cwd __PROTO((const char *)); +extern char *mdvi_strrstr __PROTO((const char *, const char *)); + +/* macros to make memory allocation nicer */ +#define xalloc(t) (t *)mdvi_malloc(sizeof(t)) +#define xnalloc(t,n) (t *)mdvi_calloc((n), sizeof(t)) +#define xresize(p,t,n) (t *)mdvi_realloc((p), (n) * sizeof(t)) + +extern char *xstradd __PROTO((char *, size_t *, size_t, const char *, size_t)); + +extern Ulong get_mtime __PROTO((int)); + +/* lists */ +extern void listh_init __PROTO((ListHead *)); +extern void listh_prepend __PROTO((ListHead *, List *)); +extern void listh_append __PROTO((ListHead *, List *)); +extern void listh_add_before __PROTO((ListHead *, List *, List *)); +extern void listh_add_after __PROTO((ListHead *, List *, List *)); +extern void listh_remove __PROTO((ListHead *, List *)); +extern void listh_concat __PROTO((ListHead *, ListHead *)); +extern void listh_catcon __PROTO((ListHead *, ListHead *)); + +extern void buff_init __PROTO((Buffer *)); +extern size_t buff_add __PROTO((Buffer *, const char *, size_t)); +extern char *buff_gets __PROTO((Buffer *, size_t *)); +extern void buff_free __PROTO((Buffer *)); + +extern char *getword __PROTO((char *, const char *, char **)); +extern char *getstring __PROTO((char *, const char *, char **)); + +extern void dstring_init __PROTO((Dstring *)); +extern int dstring_new __PROTO((Dstring *, const char *, int)); +extern int dstring_append __PROTO((Dstring *, const char *, int)); +extern int dstring_copy __PROTO((Dstring *, int, const char *, int)); +extern int dstring_insert __PROTO((Dstring *, int, const char *, int)); +extern void dstring_reset __PROTO((Dstring *)); + +#define dstring_length(d) ((d)->length) +#define dstring_strcat(d,s) dstring_append((d), (s), -1) + +extern char *dgets __PROTO((Dstring *, FILE *)); +extern int file_readable __PROTO((const char *)); +extern int file_exists __PROTO((const char *)); +extern const char *file_basename __PROTO((const char *)); +extern const char *file_extension __PROTO((const char *)); + +/* + * Miscellaneous macros + */ + +#define LIST_FOREACH(ptr, type, list) \ + for(ptr = (type *)(list)->head; ptr; ptr = (ptr)->next) + +#define Size(x) (sizeof(x) / sizeof((x)[0])) + +/* multiply a fix_word by a 32bit number */ +#define B0(x) ((x) & 0xff) +#define B1(x) B0((x) >> 8) +#define B2(x) B0((x) >> 16) +#define B3(x) B0((x) >> 24) +#define __tfm_mul(z,t) \ + (((((B0(t) * (z)) >> 8) + (B1(t) * (z))) >> 8) + B2(t) * (z)) +#define TFMSCALE(z,t,a,b) \ + ((B3(t) == 255) ? \ + __tfm_mul((z), (t)) / (b) - (a) : \ + __tfm_mul((z), (t)) / (b)) +#define TFMPREPARE(x,z,a,b) do { \ + a = 16; z = (x); \ + while(z > 040000000L) { z >>= 1; a <<= 1; } \ + b = 256 / a; a *= z; \ + } while(0) + +#endif /* _MDVI_COMMON_H */ diff --git a/backend/dvi/mdvi-lib/defaults.h b/backend/dvi/mdvi-lib/defaults.h new file mode 100644 index 00000000..7e63f81e --- /dev/null +++ b/backend/dvi/mdvi-lib/defaults.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_DEFAULTS_H +#define _MDVI_DEFAULTS_H 1 + +/* resolution */ +#define MDVI_DPI 600 +#define MDVI_VDPI MDVI_DPI + +/* horizontal margins */ +#define MDVI_HMARGIN "1in" + +/* vertical margins */ +#define MDVI_VMARGIN "1in" + +/* rulers */ +#define MDVI_HRUNITS "1in" +#define MDVI_VRUNITS "1in" + +/* paper */ +#define MDVI_PAPERNAME "letter" + +/* magnification */ +#define MDVI_MAGNIFICATION 1.0 + +/* fallback font */ +#define MDVI_FALLBACK_FONT "cmr10" + +/* metafont mode */ +#define MDVI_MFMODE NULL + +/* default shrinking factor */ +#define MDVI_DEFAULT_SHRINKING -1 /* based on resolution */ + +/* default pixel density */ +#define MDVI_DEFAULT_DENSITY 50 + +/* default gamma correction */ +#define MDVI_DEFAULT_GAMMA 1.0 + +/* default window geometry */ +#define MDVI_GEOMETRY NULL + +/* default orientation */ +#define MDVI_ORIENTATION "tblr" + +/* colors */ +#define MDVI_FOREGROUND "black" +#define MDVI_BACKGROUND "white" + +/* flags */ +#define MDVI_DEFAULT_FLAGS MDVI_ANTIALIASED + +#define MDVI_DEFAULT_CONFIG "mdvi.conf" + +#endif /* _MDVI_DEAFAULTS_H */ diff --git a/backend/dvi/mdvi-lib/dviopcodes.h b/backend/dvi/mdvi-lib/dviopcodes.h new file mode 100644 index 00000000..f99af05e --- /dev/null +++ b/backend/dvi/mdvi-lib/dviopcodes.h @@ -0,0 +1,72 @@ +#ifndef _MDVI_DVIOPCODES_H +#define _MDVI_DVIOPCODES_H 1 + +#define DVI_SET_CHAR0 0 +#define DVI_SET_CHAR1 1 +#define DVI_SET_CHAR_MAX 127 +#define DVI_SET1 128 +#define DVI_SET2 129 +#define DVI_SET3 130 +#define DVI_SET4 131 +#define DVI_SET_RULE 132 +#define DVI_PUT1 133 +#define DVI_PUT2 134 +#define DVI_PUT3 135 +#define DVI_PUT4 136 +#define DVI_PUT_RULE 137 +#define DVI_NOOP 138 +#define DVI_BOP 139 +#define DVI_EOP 140 +#define DVI_PUSH 141 +#define DVI_POP 142 +#define DVI_RIGHT1 143 +#define DVI_RIGHT2 144 +#define DVI_RIGHT3 145 +#define DVI_RIGHT4 146 +#define DVI_W0 147 +#define DVI_W1 148 +#define DVI_W2 149 +#define DVI_W3 150 +#define DVI_W4 151 +#define DVI_X0 152 +#define DVI_X1 153 +#define DVI_X2 154 +#define DVI_X3 155 +#define DVI_X4 156 +#define DVI_DOWN1 157 +#define DVI_DOWN2 158 +#define DVI_DOWN3 159 +#define DVI_DOWN4 160 +#define DVI_Y0 161 +#define DVI_Y1 162 +#define DVI_Y2 163 +#define DVI_Y3 164 +#define DVI_Y4 165 +#define DVI_Z0 166 +#define DVI_Z1 167 +#define DVI_Z2 168 +#define DVI_Z3 169 +#define DVI_Z4 170 +#define DVI_FNT_NUM0 171 +#define DVI_FNT_NUM1 172 +#define DVI_FNT_NUM_MAX 234 +#define DVI_FNT1 235 +#define DVI_FNT2 236 +#define DVI_FNT3 237 +#define DVI_FNT4 238 +#define DVI_XXX1 239 +#define DVI_XXX2 240 +#define DVI_XXX3 241 +#define DVI_XXX4 242 +#define DVI_FNT_DEF1 243 +#define DVI_FNT_DEF2 244 +#define DVI_FNT_DEF3 245 +#define DVI_FNT_DEF4 246 +#define DVI_PRE 247 +#define DVI_POST 248 +#define DVI_POST_POST 249 + +#define DVI_ID 2 +#define DVI_TRAILER 223 + +#endif /* _MDVI_DVIOPCODES_H */ diff --git a/backend/dvi/mdvi-lib/dviread.c b/backend/dvi/mdvi-lib/dviread.c new file mode 100644 index 00000000..97b7b844 --- /dev/null +++ b/backend/dvi/mdvi-lib/dviread.c @@ -0,0 +1,1585 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include "mdvi.h" +#include "private.h" +#include "color.h" + +typedef int (*DviCommand) __PROTO((DviContext *, int)); + +#define DVICMDDEF(x) static int x __PROTO((DviContext *, int)) + +DVICMDDEF(set_char); +DVICMDDEF(set_rule); +DVICMDDEF(no_op); +DVICMDDEF(push); +DVICMDDEF(pop); +DVICMDDEF(move_right); +DVICMDDEF(move_down); +DVICMDDEF(move_w); +DVICMDDEF(move_x); +DVICMDDEF(move_y); +DVICMDDEF(move_z); +DVICMDDEF(sel_font); +DVICMDDEF(sel_fontn); +DVICMDDEF(special); +DVICMDDEF(def_font); +DVICMDDEF(undefined); +DVICMDDEF(unexpected); + +static const DviCommand dvi_commands[] = { + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, /* 0 - 127 */ + set_char, set_char, set_char, set_char, /* 128 - 131 */ + set_rule, /* 132 */ + set_char, set_char, set_char, set_char, /* 133 - 136 */ + set_rule, /* 137 */ + no_op, /* 138 */ + unexpected, /* 139 (BOP) */ + unexpected, /* 140 (EOP) */ + push, /* 141 */ + pop, /* 142 */ + move_right, move_right, move_right, move_right, /* 143 - 146 */ + move_w, move_w, move_w, move_w, move_w, /* 147 - 151 */ + move_x, move_x, move_x, move_x, move_x, /* 152 - 156 */ + move_down, move_down, move_down, move_down, /* 157 - 160 */ + move_y, move_y, move_y, move_y, move_y, /* 161 - 165 */ + move_z, move_z, move_z, move_z, move_z, /* 166 - 170 */ + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, /* 171 - 234 */ + sel_fontn, sel_fontn, sel_fontn, sel_fontn, /* 235 - 238 */ + special, special, special, special, /* 239 - 242 */ + def_font, def_font, def_font, def_font, /* 243 - 246 */ + unexpected, /* 247 (PRE) */ + unexpected, /* 248 (POST) */ + unexpected, /* 249 (POST_POST) */ + undefined, undefined, undefined, + undefined, undefined, undefined /* 250 - 255 */ +}; + +#define DVI_BUFLEN 4096 + +static int mdvi_run_macro(DviContext *dvi, Uchar *macro, size_t len); + +static void dummy_draw_glyph(DviContext *dvi, DviFontChar *ch, int x, int y) +{ +} + +static void dummy_draw_rule(DviContext *dvi, int x, int y, Uint w, Uint h, int f) +{ +} + +static int dummy_alloc_colors(void *a, Ulong *b, int c, Ulong d, Ulong e, double f, int g) +{ + return -1; +} + +static void *dummy_create_image(void *a, Uint b, Uint c, Uint d) +{ + return NULL; +} + +static void dummy_free_image(void *a) +{ +} + +static void dummy_dev_destroy(void *a) +{ +} + +static void dummy_dev_putpixel(void *a, int x, int y, Ulong c) +{ +} + +static void dummy_dev_refresh(DviContext *a, void *b) +{ +} + +static void dummy_dev_set_color(void *a, Ulong b, Ulong c) +{ +} + +/* functions to report errors */ +static void dvierr(DviContext *dvi, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s[%d]: Error: ", + dvi->filename, dvi->currpage); + vfprintf(stderr, format, ap); + va_end(ap); +} + +static void dviwarn(DviContext *dvi, const char *format, ...) +{ + va_list ap; + + fprintf(stderr, "%s[%d]: Warning: ", + dvi->filename, dvi->currpage); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} + +#define NEEDBYTES(d,n) \ + ((d)->buffer.pos + (n) > (d)->buffer.length) + +static int get_bytes(DviContext *dvi, size_t n) +{ + /* + * caller wants to read `n' bytes from dvi->buffer + dvi->pos. + * Make sure there is enough data to satisfy the request + */ + if(NEEDBYTES(dvi, n)) { + size_t required; + int newlen; + + if(dvi->buffer.frozen || dvi->in == NULL || feof(dvi->in)) { + /* this is EOF */ + dviwarn(dvi, _("unexpected EOF\n")); + return -1; + } + /* get more data */ + if(dvi->buffer.data == NULL) { + /* first allocation */ + dvi->buffer.size = Max(DVI_BUFLEN, n); + dvi->buffer.data = (Uchar *)mdvi_malloc(dvi->buffer.size); + dvi->buffer.length = 0; + dvi->buffer.frozen = 0; + } else if(dvi->buffer.pos < dvi->buffer.length) { + /* move whatever we want to keep */ + dvi->buffer.length -= dvi->buffer.pos; + memmove(dvi->buffer.data, + dvi->buffer.data + dvi->buffer.pos, + dvi->buffer.length); + } else { + /* we can discard all the data in this buffer */ + dvi->buffer.length = 0; + } + + required = n - dvi->buffer.length; + if(required > dvi->buffer.size - dvi->buffer.length) { + /* need to allocate more memory */ + dvi->buffer.size = dvi->buffer.length + required + 128; + dvi->buffer.data = (Uchar *)xresize(dvi->buffer.data, + char, dvi->buffer.size); + } + /* now read into the buffer */ + newlen = fread(dvi->buffer.data + dvi->buffer.length, + 1, dvi->buffer.size - dvi->buffer.length, dvi->in); + if(newlen == -1) { + mdvi_error("%s: %s\n", dvi->filename, strerror(errno)); + return -1; + } + dvi->buffer.length += newlen; + dvi->buffer.pos = 0; + } + return 0; +} + +/* only relative forward seeks are supported by this function */ +static int dskip(DviContext *dvi, long offset) +{ + ASSERT(offset > 0); + + if(NEEDBYTES(dvi, offset) && get_bytes(dvi, offset) == -1) + return -1; + dvi->buffer.pos += offset; + return 0; +} + +/* DVI I/O functions (note: here `n' must be <= 4) */ +static long dsgetn(DviContext *dvi, size_t n) +{ + long val; + + if(NEEDBYTES(dvi, n) && get_bytes(dvi, n) == -1) + return -1; + val = msgetn(dvi->buffer.data + dvi->buffer.pos, n); + dvi->buffer.pos += n; + return val; +} + +static int dread(DviContext *dvi, char *buffer, size_t len) +{ + if(NEEDBYTES(dvi, len) && get_bytes(dvi, len) == -1) + return -1; + memcpy(buffer, dvi->buffer.data + dvi->buffer.pos, len); + dvi->buffer.pos += len; + return 0; +} + +static long dugetn(DviContext *dvi, size_t n) +{ + long val; + + if(NEEDBYTES(dvi, n) && get_bytes(dvi, n) == -1) + return -1; + val = mugetn(dvi->buffer.data + dvi->buffer.pos, n); + dvi->buffer.pos += n; + return val; +} + +static long dtell(DviContext *dvi) +{ + return dvi->depth ? + dvi->buffer.pos : + ftell(dvi->in) - dvi->buffer.length + dvi->buffer.pos; +} + +static void dreset(DviContext *dvi) +{ + if(!dvi->buffer.frozen && dvi->buffer.data) + mdvi_free(dvi->buffer.data); + dvi->buffer.data = NULL; + dvi->buffer.size = 0; + dvi->buffer.length = 0; + dvi->buffer.pos = 0; +} + +#define dsget1(d) dsgetn((d), 1) +#define dsget2(d) dsgetn((d), 2) +#define dsget3(d) dsgetn((d), 3) +#define dsget4(d) dsgetn((d), 4) +#define duget1(d) dugetn((d), 1) +#define duget2(d) dugetn((d), 2) +#define duget3(d) dugetn((d), 3) +#define duget4(d) dugetn((d), 4) + +#ifndef NODEBUG +static void dviprint(DviContext *dvi, const char *command, int sub, const char *fmt, ...) +{ + int i; + va_list ap; + + printf("%s: ", dvi->filename); + for(i = 0; i < dvi->depth; i++) + printf(" "); + printf("%4lu: %s", dtell(dvi), command); + if(sub >= 0) printf("%d", sub); + if(*fmt) printf(": "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} +#define SHOWCMD(x) \ + if(_mdvi_debug_mask & DBG_OPCODE) do { dviprint x; } while(0) +#else +#define SHOWCMD(x) do { } while(0) +#endif + +int mdvi_find_tex_page(DviContext *dvi, int tex_page) +{ + int i; + + for(i = 0; i < dvi->npages; i++) + if(dvi->pagemap[i][1] == tex_page) + return i; + return -1; +} + +/* page sorting functions */ +static int sort_up(const void *p1, const void *p2) +{ + return ((long *)p1)[1] - ((long *)p2)[1]; +} +static int sort_down(const void *p1, const void *p2) +{ + return ((long *)p2)[1] - ((long *)p1)[1]; +} +static int sort_random(const void *p1, const void *p2) +{ + return (rand() % 1) ? -1 : 1; +} +static int sort_dvi_up(const void *p1, const void *p2) +{ + return ((long *)p1)[0] - ((long *)p2)[0]; +} +static int sort_dvi_down(const void *p1, const void *p2) +{ + return ((long *)p1)[0] - ((long *)p2)[0]; +} + +void mdvi_sort_pages(DviContext *dvi, DviPageSort type) +{ + int (*sortfunc) __PROTO((const void *, const void *)); + + switch(type) { + case MDVI_PAGE_SORT_UP: + sortfunc = sort_up; + break; + case MDVI_PAGE_SORT_DOWN: + sortfunc = sort_down; + break; + case MDVI_PAGE_SORT_RANDOM: + sortfunc = sort_random; + break; + case MDVI_PAGE_SORT_DVI_UP: + sortfunc = sort_dvi_up; + break; + case MDVI_PAGE_SORT_DVI_DOWN: + sortfunc = sort_dvi_down; + break; + case MDVI_PAGE_SORT_NONE: + default: + sortfunc = NULL; + break; + } + + if(sortfunc) + qsort(dvi->pagemap, dvi->npages, sizeof(PageNum), sortfunc); +} + +static DviFontRef *define_font(DviContext *dvi, int op) +{ + Int32 arg; + Int32 scale; + Int32 dsize; + Int32 checksum; + int hdpi; + int vdpi; + int n; + char *name; + DviFontRef *ref; + + arg = dugetn(dvi, op - DVI_FNT_DEF1 + 1); + checksum = duget4(dvi); + scale = duget4(dvi); + dsize = duget4(dvi); + hdpi = FROUND(dvi->params.mag * dvi->params.dpi * scale / dsize); + vdpi = FROUND(dvi->params.mag * dvi->params.vdpi * scale / dsize); + n = duget1(dvi) + duget1(dvi); + name = mdvi_malloc(n + 1); + dread(dvi, name, n); + name[n] = 0; + DEBUG((DBG_FONTS, "requesting font %d = `%s' at %.1fpt (%dx%d dpi)\n", + arg, name, (double)scale / (dvi->params.tfm_conv * 0x100000), + hdpi, vdpi)); + ref = font_reference(&dvi->params, arg, name, checksum, hdpi, vdpi, scale); + if(ref == NULL) { + mdvi_error(_("could not load font `%s'\n"), name); + mdvi_free(name); + return NULL; + } + mdvi_free(name); + return ref; +} + +static char *opendvi(const char *name) +{ + int len; + char *file; + + len = strlen(name); + /* if file ends with .dvi and it exists, that's it */ + if(len >= 4 && STREQ(name+len-4, ".dvi")) { + DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", name)); + if(access(name, R_OK) == 0) + return mdvi_strdup(name); + } + + /* try appending .dvi */ + file = mdvi_malloc(len + 5); + strcpy(file, name); + strcpy(file+len, ".dvi"); + DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", file)); + if(access(file, R_OK) == 0) + return file; + /* try the given name */ + file[len] = 0; + DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", file)); + if(access(file, R_OK) == 0) + return file; + mdvi_free(file); + return NULL; +} + +int mdvi_reload(DviContext *dvi, DviParams *np) +{ + DviContext *newdvi; + DviParams *pars; + + /* close our file */ + if(dvi->in) { + fclose(dvi->in); + dvi->in = NULL; + } + + pars = np ? np : &dvi->params; + DEBUG((DBG_DVI, "%s: reloading\n", dvi->filename)); + + /* load it again */ + newdvi = mdvi_init_context(pars, dvi->pagesel, dvi->filename); + if(newdvi == NULL) { + mdvi_warning(_("could not reload `%s'\n"), dvi->filename); + return -1; + } + + /* drop all our font references */ + font_drop_chain(dvi->fonts); + /* destroy our font map */ + if(dvi->fontmap) + mdvi_free(dvi->fontmap); + dvi->currfont = NULL; + + /* and use the ones we just loaded */ + dvi->fonts = newdvi->fonts; + dvi->fontmap = newdvi->fontmap; + dvi->nfonts = newdvi->nfonts; + + /* copy the new information */ + dvi->params = newdvi->params; + dvi->num = newdvi->num; + dvi->den = newdvi->den; + dvi->dvimag = newdvi->dvimag; + dvi->dviconv = newdvi->dviconv; + dvi->dvivconv = newdvi->dvivconv; + dvi->modtime = newdvi->modtime; + + if(dvi->fileid) mdvi_free(dvi->fileid); + dvi->fileid = newdvi->fileid; + + dvi->dvi_page_w = newdvi->dvi_page_w; + dvi->dvi_page_h = newdvi->dvi_page_h; + + mdvi_free(dvi->pagemap); + dvi->pagemap = newdvi->pagemap; + dvi->npages = newdvi->npages; + if(dvi->currpage > dvi->npages-1) + dvi->currpage = 0; + + mdvi_free(dvi->stack); + dvi->stack = newdvi->stack; + dvi->stacksize = newdvi->stacksize; + + /* remove fonts that are not being used anymore */ + font_free_unused(&dvi->device); + + mdvi_free(newdvi->filename); + mdvi_free(newdvi); + + DEBUG((DBG_DVI, "%s: reload successful\n", dvi->filename)); + if(dvi->device.refresh) + dvi->device.refresh(dvi, dvi->device.device_data); + + return 0; +} + +/* function to change parameters ia DVI context + * The DVI context is modified ONLY if this function is successful */ +int mdvi_configure(DviContext *dvi, DviParamCode option, ...) +{ + va_list ap; + int reset_all; + int reset_font; + DviParams np; + + va_start(ap, option); + + reset_font = 0; + reset_all = 0; + np = dvi->params; /* structure copy */ + while(option != MDVI_PARAM_LAST) { + switch(option) { + case MDVI_SET_DPI: + np.dpi = np.vdpi = va_arg(ap, Uint); + reset_all = 1; + break; + case MDVI_SET_XDPI: + np.dpi = va_arg(ap, Uint); + reset_all = 1; + break; + case MDVI_SET_YDPI: + np.vdpi = va_arg(ap, Uint); + break; + case MDVI_SET_SHRINK: + np.hshrink = np.vshrink = va_arg(ap, Uint); + reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP; + break; + case MDVI_SET_XSHRINK: + np.hshrink = va_arg(ap, Uint); + reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP; + break; + case MDVI_SET_YSHRINK: + np.vshrink = va_arg(ap, Uint); + reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP; + break; + case MDVI_SET_ORIENTATION: + np.orientation = va_arg(ap, DviOrientation); + reset_font = MDVI_FONTSEL_GLYPH; + break; + case MDVI_SET_GAMMA: + np.gamma = va_arg(ap, double); + reset_font = MDVI_FONTSEL_GREY; + break; + case MDVI_SET_DENSITY: + np.density = va_arg(ap, Uint); + reset_font = MDVI_FONTSEL_BITMAP; + break; + case MDVI_SET_MAGNIFICATION: + np.mag = va_arg(ap, double); + reset_all = 1; + break; + case MDVI_SET_DRIFT: + np.hdrift = np.vdrift = va_arg(ap, int); + break; + case MDVI_SET_HDRIFT: + np.hdrift = va_arg(ap, int); + break; + case MDVI_SET_VDRIFT: + np.vdrift = va_arg(ap, int); + break; + case MDVI_SET_FOREGROUND: + np.fg = va_arg(ap, Ulong); + reset_font = MDVI_FONTSEL_GREY; + break; + case MDVI_SET_BACKGROUND: + np.bg = va_arg(ap, Ulong); + reset_font = MDVI_FONTSEL_GREY; + break; + default: + break; + } + option = va_arg(ap, DviParamCode); + } + va_end(ap); + + /* check that all values make sense */ + if(np.dpi <= 0 || np.vdpi <= 0) + return -1; + if(np.mag <= 0.0) + return -1; + if(np.density < 0) + return -1; + if(np.hshrink < 1 || np.vshrink < 1) + return -1; + if(np.hdrift < 0 || np.vdrift < 0) + return -1; + if(np.fg == np.bg) + return -1; + + /* + * If the dpi or the magnification change, we basically have to reload + * the DVI file again from scratch. + */ + + if(reset_all) + return (mdvi_reload(dvi, &np) == 0); + + if(np.hshrink != dvi->params.hshrink) { + np.conv = dvi->dviconv; + if(np.hshrink) + np.conv /= np.hshrink; + } + if(np.vshrink != dvi->params.vshrink) { + np.vconv = dvi->dvivconv; + if(np.vshrink) + np.vconv /= np.vshrink; + } + + if(reset_font) { + font_reset_chain_glyphs(&dvi->device, dvi->fonts, reset_font); + } + dvi->params = np; + if((reset_font & MDVI_FONTSEL_GLYPH) && dvi->device.refresh) { + dvi->device.refresh(dvi, dvi->device.device_data); + return 0; + } + + return 1; +} +/* + * Read the initial data from the DVI file. If something is wrong with the + * file, we just spit out an error message and refuse to load the file, + * without giving any details. This makes sense because DVI files are ok + * 99.99% of the time, and dvitype(1) can be used to check the other 0.01%. + */ +DviContext *mdvi_init_context(DviParams *par, DviPageSpec *spec, const char *file) +{ + FILE *p; + Int32 arg; + int op; + long offset; + int n; + DviContext *dvi; + char *filename; + int pagecount; + + /* + * 1. Open the file and initialize the DVI context + */ + + filename = opendvi(file); + if(filename == NULL) { + perror(file); + return NULL; + } + p = fopen(filename, "rb"); + if(p == NULL) { + perror(file); + mdvi_free(filename); + return NULL; + } + dvi = xalloc(DviContext); + memzero(dvi, sizeof(DviContext)); + dvi->pagemap = NULL; + dvi->filename = filename; + dvi->stack = NULL; + dvi->modtime = get_mtime(fileno(p)); + dvi->buffer.data = NULL; + dvi->pagesel = spec; + dvi->in = p; /* now we can use the dget*() functions */ + + /* + * 2. Read the preamble, extract scaling information, and + * setup the DVI parameters. + */ + + if(fuget1(p) != DVI_PRE) + goto bad_dvi; + if((arg = fuget1(p)) != DVI_ID) { + mdvi_error(_("%s: unsupported DVI format (version %u)\n"), + file, arg); + goto error; /* jump to the end of this routine, + * where we handle errors */ + } + /* get dimensions */ + dvi->num = fuget4(p); + dvi->den = fuget4(p); + dvi->dvimag = fuget4(p); + + /* check that these numbers make sense */ + if(!dvi->num || !dvi->den || !dvi->dvimag) + goto bad_dvi; + + dvi->params.mag = + (par->mag > 0 ? par->mag : (double)dvi->dvimag / 1000.0); + dvi->params.hdrift = par->hdrift; + dvi->params.vdrift = par->vdrift; + dvi->params.dpi = par->dpi ? par->dpi : MDVI_DPI; + dvi->params.vdpi = par->vdpi ? par->vdpi : par->dpi; + dvi->params.hshrink = par->hshrink; + dvi->params.vshrink = par->vshrink; + dvi->params.density = par->density; + dvi->params.gamma = par->gamma; + dvi->params.conv = (double)dvi->num / dvi->den; + dvi->params.conv *= (dvi->params.dpi / 254000.0) * dvi->params.mag; + dvi->params.vconv = (double)dvi->num / dvi->den; + dvi->params.vconv *= (dvi->params.vdpi / 254000.0) * dvi->params.mag; + dvi->params.tfm_conv = (25400000.0 / dvi->num) * + ((double)dvi->den / 473628672) / 16.0; + dvi->params.flags = par->flags; + dvi->params.orientation = par->orientation; + dvi->params.fg = par->fg; + dvi->params.bg = par->bg; + + /* initialize colors */ + dvi->curr_fg = par->fg; + dvi->curr_bg = par->bg; + dvi->color_stack = NULL; + dvi->color_top = 0; + dvi->color_size = 0; + + /* pixel conversion factors */ + dvi->dviconv = dvi->params.conv; + dvi->dvivconv = dvi->params.vconv; + if(dvi->params.hshrink) + dvi->params.conv /= dvi->params.hshrink; + if(dvi->params.vshrink) + dvi->params.vconv /= dvi->params.vshrink; + + /* get the comment from the preamble */ + n = fuget1(p); + dvi->fileid = mdvi_malloc(n + 1); + fread(dvi->fileid, 1, n, p); + dvi->fileid[n] = 0; + DEBUG((DBG_DVI, "%s: %s\n", filename, dvi->fileid)); + + /* + * 3. Read postamble, extract page information (number of + * pages, dimensions) and stack depth. + */ + + /* jump to the end of the file */ + if(fseek(p, (long)-1, SEEK_END) == -1) + goto error; + for(n = 0; (op = fuget1(p)) == DVI_TRAILER; n++) + if(fseek(p, (long)-2, SEEK_CUR) < 0) + break; + if(op != arg || n < 4) + goto bad_dvi; + /* get the pointer to postamble */ + fseek(p, (long)-5, SEEK_CUR); + arg = fuget4(p); + /* jump to it */ + fseek(p, (long)arg, SEEK_SET); + if(fuget1(p) != DVI_POST) + goto bad_dvi; + offset = fuget4(p); + if(dvi->num != fuget4(p) || dvi->den != fuget4(p) || + dvi->dvimag != fuget4(p)) + goto bad_dvi; + dvi->dvi_page_h = fuget4(p); + dvi->dvi_page_w = fuget4(p); + dvi->stacksize = fuget2(p); + dvi->npages = fuget2(p); + DEBUG((DBG_DVI, "%s: from postamble: stack depth %d, %d page%s\n", + filename, dvi->stacksize, dvi->npages, dvi->npages > 1 ? "s" : "")); + + /* + * 4. Process font definitions. + */ + + /* process font definitions */ + dvi->nfonts = 0; + dvi->fontmap = NULL; + /* + * CAREFUL: here we need to use the dvi->buffer, but it might leave the + * the file cursor in the wrong position after reading fonts (because of + * buffering). It's ok, though, because after the font definitions we read + * the page offsets, and we fseek() to the relevant part of the file with + * SEEK_SET. Nothing is read after the page offsets. + */ + while((op = duget1(dvi)) != DVI_POST_POST) { + DviFontRef *ref; + + if(op == DVI_NOOP) + continue; + else if(op < DVI_FNT_DEF1 || op > DVI_FNT_DEF4) + goto error; + ref = define_font(dvi, op); + if(ref == NULL) + goto error; + ref->next = dvi->fonts; + dvi->fonts = ref; + dvi->nfonts++; + } + /* we don't need the buffer anymore */ + dreset(dvi); + + if(op != DVI_POST_POST) + goto bad_dvi; + font_finish_definitions(dvi); + DEBUG((DBG_DVI, "%s: %d font%s required by this job\n", + filename, dvi->nfonts, dvi->nfonts > 1 ? "s" : "")); + dvi->findref = font_find_mapped; + + /* + * 5. Build the page map. + */ + + dvi->pagemap = xnalloc(PageNum, dvi->npages); + memzero(dvi->pagemap, sizeof(PageNum) * dvi->npages); + + n = dvi->npages - 1; + pagecount = n; + while(offset != -1) { + int i; + PageNum page; + + fseek(p, offset, SEEK_SET); + op = fuget1(p); + if(op != DVI_BOP || n < 0) + goto bad_dvi; + for(i = 1; i <= 10; i++) + page[i] = fsget4(p); + page[0] = offset; + offset = fsget4(p); + /* check if the page is selected */ + if(spec && mdvi_page_selected(spec, page, n) == 0) { + DEBUG((DBG_DVI, "Page %d (%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld) ignored by request\n", + n, page[1], page[2], page[3], page[4], page[5], + page[6], page[7], page[8], page[9], page[10])); + } else { + memcpy(&dvi->pagemap[pagecount], page, sizeof(PageNum)); + pagecount--; + } + n--; + } + pagecount++; + if(pagecount >= dvi->npages) { + mdvi_error(_("no pages selected\n")); + goto error; + } + if(pagecount) { + DEBUG((DBG_DVI, "%d of %d pages selected\n", + dvi->npages - pagecount, dvi->npages)); + dvi->npages -= pagecount; + memmove(dvi->pagemap, &dvi->pagemap[pagecount], + dvi->npages * sizeof(PageNum)); + } + + /* + * 6. Setup stack, initialize device functions + */ + + dvi->curr_layer = 0; + dvi->stack = xnalloc(DviState, dvi->stacksize + 8); + + dvi->device.draw_glyph = dummy_draw_glyph; + dvi->device.draw_rule = dummy_draw_rule; + dvi->device.alloc_colors = dummy_alloc_colors; + dvi->device.create_image = dummy_create_image; + dvi->device.free_image = dummy_free_image; + dvi->device.dev_destroy = dummy_dev_destroy; + dvi->device.put_pixel = dummy_dev_putpixel; + dvi->device.refresh = dummy_dev_refresh; + dvi->device.set_color = dummy_dev_set_color; + dvi->device.device_data = NULL; + + DEBUG((DBG_DVI, "%s read successfully\n", filename)); + return dvi; + +bad_dvi: + mdvi_error(_("%s: File corrupted, or not a DVI file\n"), file); +error: + /* if we came from the font definitions, this will be non-trivial */ + dreset(dvi); + mdvi_destroy_context(dvi); + return NULL; +} + +void mdvi_destroy_context(DviContext *dvi) +{ + if(dvi->device.dev_destroy) + dvi->device.dev_destroy(dvi->device.device_data); + /* release all fonts */ + if(dvi->fonts) { + font_drop_chain(dvi->fonts); + font_free_unused(&dvi->device); + } + if(dvi->fontmap) + mdvi_free(dvi->fontmap); + if(dvi->filename) + mdvi_free(dvi->filename); + if(dvi->stack) + mdvi_free(dvi->stack); + if(dvi->pagemap) + mdvi_free(dvi->pagemap); + if(dvi->fileid) + mdvi_free(dvi->fileid); + if(dvi->in) + fclose(dvi->in); + if(dvi->buffer.data && !dvi->buffer.frozen) + mdvi_free(dvi->buffer.data); + if(dvi->color_stack) + mdvi_free(dvi->color_stack); + + mdvi_free(dvi); +} + +void mdvi_setpage(DviContext *dvi, int pageno) +{ + if(pageno < 0) + pageno = 0; + if(pageno > dvi->npages-1) + pageno = dvi->npages - 1; + dvi->currpage = pageno; +} + +static int mdvi_run_macro(DviContext *dvi, Uchar *macro, size_t len) +{ + DviFontRef *curr, *fonts; + DviBuffer saved_buffer; + FILE *saved_file; + int opcode; + int oldtop; + + dvi->depth++; + push(dvi, DVI_PUSH); + dvi->pos.w = 0; + dvi->pos.x = 0; + dvi->pos.y = 0; + dvi->pos.z = 0; + + /* save our state */ + curr = dvi->currfont; + fonts = dvi->fonts; + saved_buffer = dvi->buffer; + saved_file = dvi->in; + dvi->currfont = curr->ref->subfonts; + dvi->fonts = curr->ref->subfonts; + dvi->buffer.data = macro; + dvi->buffer.pos = 0; + dvi->buffer.length = len; + dvi->buffer.frozen = 1; + dvi->in = NULL; + oldtop = dvi->stacktop; + + /* execute commands */ + while((opcode = duget1(dvi)) != DVI_EOP) { + if(dvi_commands[opcode](dvi, opcode) < 0) + break; + } + if(opcode != DVI_EOP) + dviwarn(dvi, _("%s: vf macro had errors\n"), + curr->ref->fontname); + if(dvi->stacktop != oldtop) + dviwarn(dvi, _("%s: stack not empty after vf macro\n"), + curr->ref->fontname); + + /* restore things */ + pop(dvi, DVI_POP); + dvi->currfont = curr; + dvi->fonts = fonts; + dvi->buffer = saved_buffer; + dvi->in = saved_file; + dvi->depth--; + + return (opcode != DVI_EOP ? -1 : 0); +} + +int mdvi_dopage(DviContext *dvi, int pageno) +{ + int op; + int ppi; + int reloaded = 0; + +again: + if(dvi->in == NULL) { + /* try reopening the file */ + dvi->in = fopen(dvi->filename, "rb"); + if(dvi->in == NULL) { + mdvi_warning(_("%s: could not reopen file (%s)\n"), + dvi->filename, + strerror(errno)); + return -1; + } + DEBUG((DBG_FILES, "reopen(%s) -> Ok\n", dvi->filename)); + } + + /* check if we need to reload the file */ + if(!reloaded && get_mtime(fileno(dvi->in)) > dvi->modtime) { + mdvi_reload(dvi, &dvi->params); + /* we have to reopen the file, again */ + reloaded = 1; + goto again; + } + + if(pageno < 0 || pageno > dvi->npages-1) { + mdvi_error(_("%s: page %d out of range\n"), + dvi->filename, pageno); + return -1; + } + + fseek(dvi->in, (long)dvi->pagemap[pageno][0], SEEK_SET); + if((op = fuget1(dvi->in)) != DVI_BOP) { + mdvi_error(_("%s: bad offset at page %d\n"), + dvi->filename, pageno+1); + return -1; + } + + /* skip bop */ + fseek(dvi->in, (long)44, SEEK_CUR); + + /* reset state */ + dvi->currfont = NULL; + memzero(&dvi->pos, sizeof(DviState)); + dvi->stacktop = 0; + dvi->currpage = pageno; + dvi->curr_layer = 0; + + if(dvi->buffer.data && !dvi->buffer.frozen) + mdvi_free(dvi->buffer.data); + + /* reset our buffer */ + dvi->buffer.data = NULL; + dvi->buffer.length = 0; + dvi->buffer.pos = 0; + dvi->buffer.frozen = 0; + +#if 0 /* make colors survive page breaks */ + /* reset color stack */ + mdvi_reset_color(dvi); +#endif + + /* set max horizontal and vertical drift (from dvips) */ + if(dvi->params.hdrift < 0) { + ppi = dvi->params.dpi / dvi->params.hshrink; /* shrunk pixels per inch */ + if(ppi < 600) + dvi->params.hdrift = ppi / 100; + else if(ppi < 1200) + dvi->params.hdrift = ppi / 200; + else + dvi->params.hdrift = ppi / 400; + } + if(dvi->params.vdrift < 0) { + ppi = dvi->params.vdpi / dvi->params.vshrink; /* shrunk pixels per inch */ + if(ppi < 600) + dvi->params.vdrift = ppi / 100; + else if(ppi < 1200) + dvi->params.vdrift = ppi / 200; + else + dvi->params.vdrift = ppi / 400; + } + + dvi->params.thinsp = FROUND(0.025 * dvi->params.dpi / dvi->params.conv); + dvi->params.vsmallsp = FROUND(0.025 * dvi->params.vdpi / dvi->params.vconv); + + /* execute all the commands in the page */ + while((op = duget1(dvi)) != DVI_EOP) { + if(dvi_commands[op](dvi, op) < 0) + break; + } + + fflush(stdout); + fflush(stderr); + if(op != DVI_EOP) + return -1; + if(dvi->stacktop) + dviwarn(dvi, _("stack not empty at end of page\n")); + return 0; +} + +static int inline move_vertical(DviContext *dvi, int amount) +{ + int rvv; + + dvi->pos.v += amount; + rvv = vpixel_round(dvi, dvi->pos.v); + if(!dvi->params.vdrift) + return rvv; + if(amount > dvi->params.vsmallsp || amount <= -dvi->params.vsmallsp) + return rvv; + else { + int newvv; + + newvv = dvi->pos.vv + vpixel_round(dvi, amount); + if(rvv - newvv > dvi->params.vdrift) + return rvv - dvi->params.vdrift; + else if(newvv - rvv > dvi->params.vdrift) + return rvv + dvi->params.vdrift; + else + return newvv; + } +} + +static int inline move_horizontal(DviContext *dvi, int amount) +{ + int rhh; + + dvi->pos.h += amount; + rhh = pixel_round(dvi, dvi->pos.h); + if(!dvi->params.hdrift) + return rhh; + else if(amount > dvi->params.thinsp || amount <= -6 * dvi->params.thinsp) + return rhh; + else { + int newhh; + + newhh = dvi->pos.hh + pixel_round(dvi, amount); + if(rhh - newhh > dvi->params.hdrift) + return rhh - dvi->params.hdrift; + else if(newhh - rhh > dvi->params.hdrift) + return rhh + dvi->params.hdrift; + else + return newhh; + } +} + +static void inline fix_after_horizontal(DviContext *dvi) +{ + int rhh; + + rhh = pixel_round(dvi, dvi->pos.h); + if(!dvi->params.hdrift) + dvi->pos.hh = rhh; + else if(rhh - dvi->pos.hh > dvi->params.hdrift) + dvi->pos.hh = rhh - dvi->params.hdrift; + else if(dvi->pos.hh - rhh > dvi->params.hdrift) + dvi->pos.hh = rhh + dvi->params.hdrift; +} + +/* commands */ + +#define DBGSUM(a,b,c) \ + (a), (b) > 0 ? '+' : '-', \ + (b) > 0 ? (b) : -(b), (c) + +/* + * Draw rules with some sort of antialias support. Usefult for high-rate + * scale factors. + */ + +static void draw_shrink_rule (DviContext *dvi, int x, int y, Uint w, Uint h, int f) +{ + int hs, vs, npixels; + Ulong fg, bg; + Ulong *pixels; + + hs = dvi->params.hshrink; + vs = dvi->params.vshrink; + fg = dvi->curr_fg; + bg = dvi->curr_bg; + + if (MDVI_ENABLED(dvi, MDVI_PARAM_ANTIALIASED)) { + npixels = vs * hs + 1; + pixels = get_color_table(&dvi->device, npixels, bg, fg, + dvi->params.gamma, dvi->params.density); + + if (pixels) { + int color; + + /* Lines with width 1 should be perfectly visible + * in shrink about 15. That is the reason of constant + */ + + color = (pow (vs / h * hs, 2) + pow (hs / w * vs, 2)) / 225; + if (color < npixels) { + fg = pixels[color]; + } else { + fg = pixels[npixels - 1]; + } + } + } + + mdvi_push_color (dvi, fg, bg); + dvi->device.draw_rule(dvi, x, y, w, h, f); + mdvi_pop_color (dvi); + + return; +} + +/* + * The only commands that actually draw something are: + * set_char, set_rule + */ + +static void draw_box(DviContext *dvi, DviFontChar *ch) +{ + DviGlyph *glyph = NULL; + int x, y, w, h; + + if(!MDVI_GLYPH_UNSET(ch->shrunk.data)) + glyph = &ch->shrunk; + else if(!MDVI_GLYPH_UNSET(ch->grey.data)) + glyph = &ch->grey; + else if(!MDVI_GLYPH_UNSET(ch->glyph.data)) + glyph = &ch->glyph; + if(glyph == NULL) + return; + x = glyph->x; + y = glyph->y; + w = glyph->w; + h = glyph->h; + /* this is bad -- we have to undo the orientation */ + switch(dvi->params.orientation) { + case MDVI_ORIENT_TBLR: + break; + case MDVI_ORIENT_TBRL: + x = w - x; + break; + case MDVI_ORIENT_BTLR: + y = h - y; + break; + case MDVI_ORIENT_BTRL: + x = w - x; + y = h - y; + break; + case MDVI_ORIENT_RP90: + SWAPINT(w, h); + SWAPINT(x, y); + x = w - x; + break; + case MDVI_ORIENT_RM90: + SWAPINT(w, h); + SWAPINT(x, y); + y = h - y; + break; + case MDVI_ORIENT_IRP90: + SWAPINT(w, h); + SWAPINT(x, y); + break; + case MDVI_ORIENT_IRM90: + SWAPINT(w, h); + SWAPINT(x, y); + x = w - x; + y = h - y; + break; + } + + draw_shrink_rule(dvi, dvi->pos.hh - x, dvi->pos.vv - y, w, h, 1); +} + +int set_char(DviContext *dvi, int opcode) +{ + int num; + int h; + int hh; + DviFontChar *ch; + DviFont *font; + + if(opcode < 128) + num = opcode; + else + num = dugetn(dvi, opcode - DVI_SET1 + 1); + if(dvi->currfont == NULL) { + dvierr(dvi, _("no default font set yet\n")); + return -1; + } + font = dvi->currfont->ref; + ch = font_get_glyph(dvi, font, num); + if(ch == NULL || ch->missing) { + /* try to display something anyway */ + ch = FONTCHAR(font, num); + if(!glyph_present(ch)) { + dviwarn(dvi, + _("requested character %d does not exist in `%s'\n"), + num, font->fontname); + return 0; + } + draw_box(dvi, ch); + } else if(dvi->curr_layer <= dvi->params.layer) { + if(ISVIRTUAL(font)) + mdvi_run_macro(dvi, (Uchar *)font->private + + ch->offset, ch->width); + else if(ch->width && ch->height) + dvi->device.draw_glyph(dvi, ch, + dvi->pos.hh, dvi->pos.vv); + } + if(opcode >= DVI_PUT1 && opcode <= DVI_PUT4) { + SHOWCMD((dvi, "putchar", opcode - DVI_PUT1 + 1, + "char %d (%s)\n", + num, dvi->currfont->ref->fontname)); + } else { + h = dvi->pos.h + ch->tfmwidth; + hh = dvi->pos.hh + pixel_round(dvi, ch->tfmwidth); + SHOWCMD((dvi, "setchar", num, "(%d,%d) h:=%d%c%d=%d, hh:=%d (%s)\n", + dvi->pos.hh, dvi->pos.vv, + DBGSUM(dvi->pos.h, ch->tfmwidth, h), hh, + font->fontname)); + dvi->pos.h = h; + dvi->pos.hh = hh; + fix_after_horizontal(dvi); + } + + return 0; +} + +int set_rule(DviContext *dvi, int opcode) +{ + Int32 a, b; + int h, w; + + a = dsget4(dvi); + b = dsget4(dvi); w = rule_round(dvi, b); + if(a > 0 && b > 0) { + h = vrule_round(dvi, a); + SHOWCMD((dvi, opcode == DVI_SET_RULE ? "setrule" : "putrule", -1, + "width %d, height %d (%dx%d pixels)\n", + b, a, w, h)); + /* the `draw' functions expect the origin to be at the top left + * corner of the rule, not the bottom left, as in DVI files */ + if(dvi->curr_layer <= dvi->params.layer) { + draw_shrink_rule(dvi, + dvi->pos.hh, dvi->pos.vv - h + 1, w, h, 1); + } + } else { + SHOWCMD((dvi, opcode == DVI_SET_RULE ? "setrule" : "putrule", -1, + "(moving left only, by %d)\n", b)); + } + + if(opcode == DVI_SET_RULE) { + dvi->pos.h += b; + dvi->pos.hh += w; + fix_after_horizontal(dvi); + } + return 0; +} + +int no_op(DviContext *dvi, int opcode) +{ + SHOWCMD((dvi, "noop", -1, "")); + return 0; +} + +int push(DviContext *dvi, int opcode) +{ + if(dvi->stacktop == dvi->stacksize) { + if(!dvi->depth) + dviwarn(dvi, _("enlarging stack\n")); + dvi->stacksize += 8; + dvi->stack = xresize(dvi->stack, + DviState, dvi->stacksize); + } + memcpy(&dvi->stack[dvi->stacktop], &dvi->pos, sizeof(DviState)); + SHOWCMD((dvi, "push", -1, + "level %d: (h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=%d,vv=%d)\n", + dvi->stacktop, + dvi->pos.h, dvi->pos.v, dvi->pos.w, dvi->pos.x, + dvi->pos.y, dvi->pos.z, dvi->pos.hh, dvi->pos.vv)); + dvi->stacktop++; + return 0; +} + +int pop(DviContext *dvi, int opcode) +{ + if(dvi->stacktop == 0) { + dvierr(dvi, _("stack underflow\n")); + return -1; + } + memcpy(&dvi->pos, &dvi->stack[dvi->stacktop-1], sizeof(DviState)); + SHOWCMD((dvi, "pop", -1, + "level %d: (h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=%d,vv=%d)\n", + dvi->stacktop, + dvi->pos.h, dvi->pos.v, dvi->pos.w, dvi->pos.x, + dvi->pos.y, dvi->pos.z, dvi->pos.hh, dvi->pos.vv)); + dvi->stacktop--; + return 0; +} + +int move_right(DviContext *dvi, int opcode) +{ + Int32 arg; + int h, hh; + + arg = dsgetn(dvi, opcode - DVI_RIGHT1 + 1); + h = dvi->pos.h; + hh = move_horizontal(dvi, arg); + SHOWCMD((dvi, "right", opcode - DVI_RIGHT1 + 1, + "%d h:=%d%c%d=%d, hh:=%d\n", + arg, DBGSUM(h, arg, dvi->pos.h), hh)); + dvi->pos.hh = hh; + return 0; +} + +int move_down(DviContext *dvi, int opcode) +{ + Int32 arg; + int v, vv; + + arg = dsgetn(dvi, opcode - DVI_DOWN1 + 1); + v = dvi->pos.v; + vv = move_vertical(dvi, arg); + SHOWCMD((dvi, "down", opcode - DVI_DOWN1 + 1, + "%d v:=%d%c%d=%d, vv:=%d\n", + arg, DBGSUM(v, arg, dvi->pos.v), vv)); + dvi->pos.vv = vv; + return 0; +} + +int move_w(DviContext *dvi, int opcode) +{ + int h, hh; + + if(opcode != DVI_W0) + dvi->pos.w = dsgetn(dvi, opcode - DVI_W0); + h = dvi->pos.h; + hh = move_horizontal(dvi, dvi->pos.w); + SHOWCMD((dvi, "w", opcode - DVI_W0, + "%d h:=%d%c%d=%d, hh:=%d\n", + dvi->pos.w, DBGSUM(h, dvi->pos.w, dvi->pos.h), hh)); + dvi->pos.hh = hh; + return 0; +} + +int move_x(DviContext *dvi, int opcode) +{ + int h, hh; + + if(opcode != DVI_X0) + dvi->pos.x = dsgetn(dvi, opcode - DVI_X0); + h = dvi->pos.h; + hh = move_horizontal(dvi, dvi->pos.x); + SHOWCMD((dvi, "x", opcode - DVI_X0, + "%d h:=%d%c%d=%d, hh:=%d\n", + dvi->pos.x, DBGSUM(h, dvi->pos.x, dvi->pos.h), hh)); + dvi->pos.hh = hh; + return 0; +} + +int move_y(DviContext *dvi, int opcode) +{ + int v, vv; + + if(opcode != DVI_Y0) + dvi->pos.y = dsgetn(dvi, opcode - DVI_Y0); + v = dvi->pos.v; + vv = move_vertical(dvi, dvi->pos.y); + SHOWCMD((dvi, "y", opcode - DVI_Y0, + "%d h:=%d%c%d=%d, hh:=%d\n", + dvi->pos.y, DBGSUM(v, dvi->pos.y, dvi->pos.v), vv)); + dvi->pos.vv = vv; + return 0; +} + +int move_z(DviContext *dvi, int opcode) +{ + int v, vv; + + if(opcode != DVI_Z0) + dvi->pos.z = dsgetn(dvi, opcode - DVI_Z0); + v = dvi->pos.v; + vv = move_vertical(dvi, dvi->pos.z); + SHOWCMD((dvi, "z", opcode - DVI_Z0, + "%d h:=%d%c%d=%d, hh:=%d\n", + dvi->pos.z, DBGSUM(v, dvi->pos.z, dvi->pos.v), vv)); + dvi->pos.vv = vv; + return 0; +} + +int sel_font(DviContext *dvi, int opcode) +{ + DviFontRef *ref; + int ndx; + + ndx = opcode - DVI_FNT_NUM0; + if(dvi->depth) + ref = font_find_flat(dvi, ndx); + else + ref = dvi->findref(dvi, ndx); + if(ref == NULL) { + dvierr(dvi, _("font %d is not defined\n"), + opcode - DVI_FNT_NUM0); + return -1; + } + SHOWCMD((dvi, "fntnum", opcode - DVI_FNT_NUM0, + "current font is %s\n", + ref->ref->fontname)); + dvi->currfont = ref; + return 0; +} + +int sel_fontn(DviContext *dvi, int opcode) +{ + Int32 arg; + DviFontRef *ref; + + arg = dugetn(dvi, opcode - DVI_FNT1 + 1); + if(dvi->depth) + ref = font_find_flat(dvi, arg); + else + ref = dvi->findref(dvi, arg); + if(ref == NULL) { + dvierr(dvi, _("font %d is not defined\n"), arg); + return -1; + } + SHOWCMD((dvi, "fnt", opcode - DVI_FNT1 + 1, + "current font is %s (id %d)\n", + ref->ref->fontname, arg)); + dvi->currfont = ref; + return 0; +} + +int special(DviContext *dvi, int opcode) +{ + char *s; + Int32 arg; + + arg = dugetn(dvi, opcode - DVI_XXX1 + 1); + s = mdvi_malloc(arg + 1); + dread(dvi, s, arg); + s[arg] = 0; + mdvi_do_special(dvi, s); + SHOWCMD((dvi, "XXXX", opcode - DVI_XXX1 + 1, + "[%s]", s)); + mdvi_free(s); + return 0; +} + +int def_font(DviContext *dvi, int opcode) +{ + DviFontRef *ref; + Int32 arg; + + arg = dugetn(dvi, opcode - DVI_FNT_DEF1 + 1); + if(dvi->depth) + ref = font_find_flat(dvi, arg); + else + ref = dvi->findref(dvi, arg); + /* skip the rest */ + dskip(dvi, 12); + dskip(dvi, duget1(dvi) + duget1(dvi)); + if(ref == NULL) { + dvierr(dvi, _("font %d is not defined in postamble\n"), arg); + return -1; + } + SHOWCMD((dvi, "fntdef", opcode - DVI_FNT_DEF1 + 1, + "%d -> %s (%d links)\n", + ref->fontid, ref->ref->fontname, + ref->ref->links)); + return 0; +} + +int unexpected(DviContext *dvi, int opcode) +{ + dvierr(dvi, _("unexpected opcode %d\n"), opcode); + return -1; +} + +int undefined(DviContext *dvi, int opcode) +{ + dvierr(dvi, _("undefined opcode %d\n"), opcode); + return -1; +} + diff --git a/backend/dvi/mdvi-lib/files.c b/backend/dvi/mdvi-lib/files.c new file mode 100644 index 00000000..b7065068 --- /dev/null +++ b/backend/dvi/mdvi-lib/files.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include "common.h" + +char *dgets(Dstring *dstr, FILE *in) +{ + char buffer[256]; + + dstr->length = 0; + if(feof(in)) + return NULL; + while(fgets(buffer, 256, in) != NULL) { + int len = strlen(buffer); + + if(buffer[len-1] == '\n') { + dstring_append(dstr, buffer, len - 1); + break; + } + dstring_append(dstr, buffer, len); + } + if(dstr->data) + dstr->data[dstr->length] = 0; + return dstr->data; +} + +/* some simple helper functions to manipulate file names */ + +const char *file_basename(const char *filename) +{ + const char *ptr = strrchr(filename, '/'); + + return (ptr ? ptr + 1 : filename); +} + +const char *file_extension(const char *filename) +{ + const char *ptr = strchr(file_basename(filename), '.'); + + return (ptr ? ptr + 1 : NULL); +} + +int file_readable(const char *filename) +{ + int status = (access(filename, R_OK) == 0); + + DEBUG((DBG_FILES, "file_redable(%s) -> %s\n", + filename, status ? "Yes" : "No")); + return status; +} + +int file_exists(const char *filename) +{ + int status = (access(filename, F_OK) == 0); + + DEBUG((DBG_FILES, "file_exists(%s) -> %s\n", + filename, status ? "Yes" : "No")); + return status; +} + diff --git a/backend/dvi/mdvi-lib/font.c b/backend/dvi/mdvi-lib/font.c new file mode 100644 index 00000000..2f655df0 --- /dev/null +++ b/backend/dvi/mdvi-lib/font.c @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> + +#include "mdvi.h" +#include "private.h" + +static ListHead fontlist; + +extern char *_mdvi_fallback_font; + +extern void vf_free_macros(DviFont *); + +#define finfo search.info +#define TYPENAME(font) \ + ((font)->finfo ? (font)->finfo->name : "none") + +int font_reopen(DviFont *font) +{ + if(font->in) + fseek(font->in, (long)0, SEEK_SET); + else if((font->in = fopen(font->filename, "rb")) == NULL) { + DEBUG((DBG_FILES, "reopen(%s) -> Error\n", font->filename)); + return -1; + } + DEBUG((DBG_FILES, "reopen(%s) -> Ok.\n", font->filename)); + return 0; +} + +/* used from context: params and device */ +static int load_font_file(DviParams *params, DviFont *font) +{ + int status; + + if(SEARCH_DONE(font->search)) + return -1; + if(font->in == NULL && font_reopen(font) < 0) + return -1; + DEBUG((DBG_FONTS, "%s: loading %s font from `%s'\n", + font->fontname, + font->finfo->name, font->filename)); + do { + status = font->finfo->load(params, font); + } while(status < 0 && mdvi_font_retry(params, font) == 0); + if(status < 0) + return -1; + if(font->in) { + fclose(font->in); + font->in = NULL; + } + DEBUG((DBG_FONTS, "reload_font(%s) -> %s\n", + font->fontname, status < 0 ? "Error" : "Ok")); + return 0; +} + +void font_drop_one(DviFontRef *ref) +{ + DviFont *font; + + font = ref->ref; + mdvi_free(ref); + /* drop all children */ + for(ref = font->subfonts; ref; ref = ref->next) { + /* just adjust the reference counts */ + ref->ref->links--; + } + if(--font->links == 0) { + /* + * this font doesn't have any more references, but + * we still keep it around in case a virtual font + * requests it. + */ + if(font->in) { + fclose(font->in); + font->in = NULL; + } + if(LIST(font) != fontlist.tail) { + /* move it to the end of the list */ + listh_remove(&fontlist, LIST(font)); + listh_append(&fontlist, LIST(font)); + } + } + DEBUG((DBG_FONTS, "%s: reference dropped, %d more left\n", + font->fontname, font->links)); +} + +void font_drop_chain(DviFontRef *head) +{ + DviFontRef *ptr; + + for(; (ptr = head); ) { + head = ptr->next; + font_drop_one(ptr); + } +} + +int font_free_unused(DviDevice *dev) +{ + DviFont *font, *next; + int count = 0; + + DEBUG((DBG_FONTS, "destroying unused fonts\n")); + for(font = (DviFont *)fontlist.head; font; font = next) { + DviFontRef *ref; + + next = font->next; + if(font->links) + continue; + count++; + DEBUG((DBG_FONTS, "removing unused %s font `%s'\n", + TYPENAME(font), font->fontname)); + listh_remove(&fontlist, LIST(font)); + if(font->in) + fclose(font->in); + /* get rid of subfonts (but can't use `drop_chain' here) */ + for(; (ref = font->subfonts); ) { + font->subfonts = ref->next; + mdvi_free(ref); + } + /* remove this font */ + font_reset_font_glyphs(dev, font, MDVI_FONTSEL_GLYPH); + /* let the font destroy its private data */ + if(font->finfo->freedata) + font->finfo->freedata(font); + /* destroy characters */ + if(font->chars) + mdvi_free(font->chars); + mdvi_free(font->fontname); + mdvi_free(font->filename); + mdvi_free(font); + } + DEBUG((DBG_FONTS, "%d unused fonts removed\n", count)); + return count; +} + +/* used from context: params and device */ +DviFontRef * +font_reference( + DviParams *params, /* rendering parameters */ + Int32 id, /* external id number */ + const char *name, /* font name */ + Int32 sum, /* checksum (from DVI of VF) */ + int hdpi, /* resolution */ + int vdpi, + Int32 scale) /* scaling factor (from DVI or VF) */ +{ + DviFont *font; + DviFontRef *ref; + DviFontRef *subfont_ref; + + /* see if there is a font with the same characteristics */ + for(font = (DviFont *)fontlist.head; font; font = font->next) { + if(strcmp(name, font->fontname) == 0 + && (!sum || !font->checksum || font->checksum == sum) + && font->hdpi == hdpi + && font->vdpi == vdpi + && font->scale == scale) + break; + } + /* try to load the font */ + if(font == NULL) { + font = mdvi_add_font(name, sum, hdpi, vdpi, scale); + if(font == NULL) + return NULL; + listh_append(&fontlist, LIST(font)); + } + if(!font->links && !font->chars && load_font_file(params, font) < 0) { + DEBUG((DBG_FONTS, "font_reference(%s) -> Error\n", name)); + return NULL; + } + ref = xalloc(DviFontRef); + ref->ref = font; + + font->links++; + for(subfont_ref = font->subfonts; subfont_ref; subfont_ref = subfont_ref->next) { + /* just adjust the reference counts */ + subfont_ref->ref->links++; + } + + ref->fontid = id; + + if(LIST(font) != fontlist.head) { + listh_remove(&fontlist, LIST(font)); + listh_prepend(&fontlist, LIST(font)); + } + + DEBUG((DBG_FONTS, "font_reference(%s) -> %d links\n", + font->fontname, font->links)); + return ref; +} + +void font_transform_glyph(DviOrientation orient, DviGlyph *g) +{ + BITMAP *map; + int x, y; + + map = (BITMAP *)g->data; + if(MDVI_GLYPH_ISEMPTY(map)) + map = NULL; + + /* put the glyph in the right orientation */ + switch(orient) { + case MDVI_ORIENT_TBLR: + break; + case MDVI_ORIENT_TBRL: + g->x = g->w - g->x; + if(map) bitmap_flip_horizontally(map); + break; + case MDVI_ORIENT_BTLR: + g->y = g->h - g->y; + if(map) bitmap_flip_vertically(map); + break; + case MDVI_ORIENT_BTRL: + g->x = g->w - g->x; + g->y = g->h - g->y; + if(map) bitmap_flip_diagonally(map); + break; + case MDVI_ORIENT_RP90: + if(map) bitmap_rotate_counter_clockwise(map); + y = g->y; + x = g->w - g->x; + g->x = y; + g->y = x; + SWAPINT(g->w, g->h); + break; + case MDVI_ORIENT_RM90: + if(map) bitmap_rotate_clockwise(map); + y = g->h - g->y; + x = g->x; + g->x = y; + g->y = x; + SWAPINT(g->w, g->h); + break; + case MDVI_ORIENT_IRP90: + if(map) bitmap_flip_rotate_counter_clockwise(map); + y = g->y; + x = g->x; + g->x = y; + g->y = x; + SWAPINT(g->w, g->h); + break; + case MDVI_ORIENT_IRM90: + if(map) bitmap_flip_rotate_clockwise(map); + y = g->h - g->y; + x = g->w - g->x; + g->x = y; + g->y = x; + SWAPINT(g->w, g->h); + break; + } +} + +static int load_one_glyph(DviContext *dvi, DviFont *font, int code) +{ + BITMAP *map; + DviFontChar *ch; + int status; + +#ifndef NODEBUG + ch = FONTCHAR(font, code); + DEBUG((DBG_GLYPHS, "loading glyph code %d in %s (at %u)\n", + code, font->fontname, ch->offset)); +#endif + if(font->finfo->getglyph == NULL) { + /* font type does not need to load glyphs (e.g. vf) */ + return 0; + } + + status = font->finfo->getglyph(&dvi->params, font, code); + if(status < 0) + return -1; + /* get the glyph again (font->chars may have changed) */ + ch = FONTCHAR(font, code); +#ifndef NODEBUG + map = (BITMAP *)ch->glyph.data; + if(DEBUGGING(BITMAP_DATA)) { + DEBUG((DBG_BITMAP_DATA, + "%s: new %s bitmap for character %d:\n", + font->fontname, TYPENAME(font), code)); + if(MDVI_GLYPH_ISEMPTY(map)) + DEBUG((DBG_BITMAP_DATA, "blank bitmap\n")); + else + bitmap_print(stderr, map); + } +#endif + /* check if we have to scale it */ + if(!font->finfo->scalable && font->hdpi != font->vdpi) { + int hs, vs, d; + + /* we scale it ourselves */ + d = Max(font->hdpi, font->vdpi); + hs = d / font->hdpi; + vs = d / font->vdpi; + if(ch->width && ch->height && (hs > 1 || vs > 1)) { + int h, v; + DviGlyph glyph; + + DEBUG((DBG_FONTS, + "%s: scaling glyph %d to resolution %dx%d\n", + font->fontname, code, font->hdpi, font->vdpi)); + h = dvi->params.hshrink; + v = dvi->params.vshrink; + d = dvi->params.density; + dvi->params.hshrink = hs; + dvi->params.vshrink = vs; + dvi->params.density = 50; + /* shrink it */ + font->finfo->shrink0(dvi, font, ch, &glyph); + /* restore parameters */ + dvi->params.hshrink = h; + dvi->params.vshrink = v; + dvi->params.density = d; + /* update glyph data */ + if(!MDVI_GLYPH_ISEMPTY(ch->glyph.data)) + bitmap_destroy((BITMAP *)ch->glyph.data); + ch->glyph.data = glyph.data; + ch->glyph.x = glyph.x; + ch->glyph.y = glyph.y; + ch->glyph.w = glyph.w; + ch->glyph.h = glyph.h; + } + + } + font_transform_glyph(dvi->params.orientation, &ch->glyph); + + return 0; +} + +DviFontChar *font_get_glyph(DviContext *dvi, DviFont *font, int code) +{ + DviFontChar *ch; + +again: + /* if we have not loaded the font yet, do so now */ + if(!font->chars && load_font_file(&dvi->params, font) < 0) + return NULL; + + /* get the unscaled glyph, maybe loading it from disk */ + ch = FONTCHAR(font, code); + if(!ch || !glyph_present(ch)) + return NULL; + if(!ch->loaded && load_one_glyph(dvi, font, code) == -1) { + if(font->chars == NULL) { + /* we need to try another font class */ + goto again; + } + return NULL; + } + /* yes, we have to do this again */ + ch = FONTCHAR(font, code); + + /* Got the glyph. If we also have the right scaled glyph, do no more */ + if(!ch->width || !ch->height || + font->finfo->getglyph == NULL || + (dvi->params.hshrink == 1 && dvi->params.vshrink == 1)) + return ch; + + /* If the glyph is empty, we just need to shrink the box */ + if(ch->missing || MDVI_GLYPH_ISEMPTY(ch->glyph.data)) { + if(MDVI_GLYPH_UNSET(ch->shrunk.data)) + mdvi_shrink_box(dvi, font, ch, &ch->shrunk); + return ch; + } else if(MDVI_ENABLED(dvi, MDVI_PARAM_ANTIALIASED)) { + if(ch->grey.data && + !MDVI_GLYPH_ISEMPTY(ch->grey.data) && + ch->fg == dvi->curr_fg && + ch->bg == dvi->curr_bg) + return ch; + if(ch->grey.data && + !MDVI_GLYPH_ISEMPTY(ch->grey.data)) { + if(dvi->device.free_image) + dvi->device.free_image(ch->grey.data); + ch->grey.data = NULL; + } + font->finfo->shrink1(dvi, font, ch, &ch->grey); + } else if(!ch->shrunk.data) + font->finfo->shrink0(dvi, font, ch, &ch->shrunk); + + return ch; +} + +void font_reset_one_glyph(DviDevice *dev, DviFontChar *ch, int what) +{ + if(!glyph_present(ch)) + return; + if(what & MDVI_FONTSEL_BITMAP) { + if(MDVI_GLYPH_NONEMPTY(ch->shrunk.data)) + bitmap_destroy((BITMAP *)ch->shrunk.data); + ch->shrunk.data = NULL; + } + if(what & MDVI_FONTSEL_GREY) { + if(MDVI_GLYPH_NONEMPTY(ch->grey.data)) { + if(dev->free_image) + dev->free_image(ch->grey.data); + } + ch->grey.data = NULL; + } + if(what & MDVI_FONTSEL_GLYPH) { + if(MDVI_GLYPH_NONEMPTY(ch->glyph.data)) + bitmap_destroy((BITMAP *)ch->glyph.data); + ch->glyph.data = NULL; + ch->loaded = 0; + } +} + +void font_reset_font_glyphs(DviDevice *dev, DviFont *font, int what) +{ + int i; + DviFontChar *ch; + + if(what & MDVI_FONTSEL_GLYPH) + what |= MDVI_FONTSEL_BITMAP|MDVI_FONTSEL_GREY; + if(font->subfonts) { + DviFontRef *ref; + + for(ref = font->subfonts; ref; ref = ref->next) + font_reset_font_glyphs(dev, ref->ref, what); + } + if(font->in) { + DEBUG((DBG_FILES, "close(%s)\n", font->filename)); + fclose(font->in); + font->in = NULL; + } + if(font->finfo->getglyph == NULL) + return; + DEBUG((DBG_FONTS, "resetting glyphs in font `%s'\n", font->fontname)); + for(ch = font->chars, i = font->loc; i <= font->hic; ch++, i++) { + if(glyph_present(ch)) + font_reset_one_glyph(dev, ch, what); + } + if((what & MDVI_FONTSEL_GLYPH) && font->finfo->reset) + font->finfo->reset(font); +} + +void font_reset_chain_glyphs(DviDevice *dev, DviFontRef *head, int what) +{ + DviFontRef *ref; + + for(ref = head; ref; ref = ref->next) + font_reset_font_glyphs(dev, ref->ref, what); +} + +static int compare_refs(const void *p1, const void *p2) +{ + return ((*(DviFontRef **)p1)->fontid - (*(DviFontRef **)p2)->fontid); +} + +void font_finish_definitions(DviContext *dvi) +{ + int count; + DviFontRef **map, *ref; + + /* first get rid of unused fonts */ + font_free_unused(&dvi->device); + + if(dvi->fonts == NULL) { + mdvi_warning(_("%s: no fonts defined\n"), dvi->filename); + return; + } + map = xnalloc(DviFontRef *, dvi->nfonts); + for(count = 0, ref = dvi->fonts; ref; ref = ref->next) + map[count++] = ref; + /* sort the array by font id */ + qsort(map, dvi->nfonts, sizeof(DviFontRef *), compare_refs); + dvi->fontmap = map; +} + +DviFontRef *font_find_flat(DviContext *dvi, Int32 id) +{ + DviFontRef *ref; + + for(ref = dvi->fonts; ref; ref = ref->next) + if(ref->fontid == id) + break; + return ref; +} + +DviFontRef *font_find_mapped(DviContext *dvi, Int32 id) +{ + int lo, hi, n; + DviFontRef **map; + + /* do a binary search */ + lo = 0; hi = dvi->nfonts; + map = dvi->fontmap; + while(lo < hi) { + int sign; + + n = (hi + lo) >> 1; + sign = (map[n]->fontid - id); + if(sign == 0) + break; + else if(sign < 0) + lo = n; + else + hi = n; + } + if(lo >= hi) + return NULL; + return map[n]; +} + diff --git a/backend/dvi/mdvi-lib/fontmap.c b/backend/dvi/mdvi-lib/fontmap.c new file mode 100644 index 00000000..c3c3a8d3 --- /dev/null +++ b/backend/dvi/mdvi-lib/fontmap.c @@ -0,0 +1,1174 @@ +/* encoding.c - functions to manipulate encodings and fontmaps */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/stat.h> + +#include "mdvi.h" +#include "private.h" + +#include <kpathsea/expand.h> +#include <kpathsea/pathsearch.h> + +typedef struct _DviFontMap DviFontMap; + +struct _DviFontMap { + ListHead entries; + DviHashTable fonts; +}; + +typedef struct _PSFontMap { + struct _PSFontMap *next; + struct _PSFontMap *prev; + char *psname; + char *mapname; + char *fullname; +} PSFontMap; + +/* these variables control PS font maps */ +static char *pslibdir = NULL; /* path where we look for PS font maps */ +static char *psfontdir = NULL; /* PS font search path */ +static int psinitialized = 0; /* did we expand the path already? */ + +static ListHead psfonts = MDVI_EMPTY_LIST_HEAD; +static DviHashTable pstable = MDVI_EMPTY_HASH_TABLE; + +static ListHead fontmaps; +static DviHashTable maptable; +static int fontmaps_loaded = 0; + +#define MAP_HASH_SIZE 57 +#define ENC_HASH_SIZE 31 +#define PSMAP_HASH_SIZE 57 + +/* this hash table should be big enough to + * hold (ideally) one glyph name per bucket */ +#define ENCNAME_HASH_SIZE 131 /* most TeX fonts have 128 glyphs */ + +static ListHead encodings = MDVI_EMPTY_LIST_HEAD; +static DviEncoding *tex_text_encoding = NULL; +static DviEncoding *default_encoding = NULL; + +/* we keep two hash tables for encodings: one for their base files (e.g. + * "8r.enc"), and another one for their names (e.g. "TeXBase1Encoding") */ +static DviHashTable enctable = MDVI_EMPTY_HASH_TABLE; +static DviHashTable enctable_file = MDVI_EMPTY_HASH_TABLE; + +/* the TeX text encoding, from dvips */ +static char *tex_text_vector[256] = { + "Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon", + "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle", + "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave", + "acute", "caron", "breve", "macron", "ring", "cedilla", + "germandbls", "ae", "oe", "oslash", "AE", "OE", "Oslash", "space", + "exclam", "quotedbl", "numbersign", "dollar", "percent", + "ampersand", "quoteright", "parenleft", "parenright", "asterisk", + "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", + "three", "four", "five", "six", "seven", "eight", "nine", "colon", + "semicolon", "less", "equal", "greater", "question", "at", "A", "B", + "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "bracketleft", "backslash", "bracketright", "circumflex", + "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", + "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", + "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "tilde", + "dieresis", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void ps_init_default_paths __PROTO((void)); +static int mdvi_set_default_encoding __PROTO((const char *name)); +static int mdvi_init_fontmaps __PROTO((void)); + +/* + * What we do here is allocate one block large enough to hold the entire + * file (these files are small) minus the leading comments. This is much + * better than allocating up to 256 tiny strings per encoding vector. */ +static int read_encoding(DviEncoding *enc) +{ + FILE *in; + int curr; + char *line; + char *name; + char *next; + struct stat st; + + ASSERT(enc->private == NULL); + + in = fopen(enc->filename, "rb"); + if(in == NULL) { + DEBUG((DBG_FMAP, "%s: could not read `%s' (%s)\n", + enc->name, enc->filename, strerror(errno))); + return -1; + } + if(fstat(fileno(in), &st) < 0) { + /* should not happen */ + fclose(in); + return -1; + } + st.st_size -= enc->offset; + + /* this will be one big string */ + enc->private = (char *)malloc(st.st_size + 1); + /* setup the hash table */ + mdvi_hash_create(&enc->nametab, ENCNAME_HASH_SIZE); + /* setup the encoding vector */ + enc->vector = (char **)mdvi_malloc(256 * sizeof(char *)); + + /* jump to the beginning of the interesting part */ + fseek(in, enc->offset, SEEK_SET); + /* and read everything */ + if(fread(enc->private, st.st_size, 1, in) != 1) { + fclose(in); + mdvi_free(enc->private); + enc->private = NULL; + return -1; + } + /* we don't need this anymore */ + fclose(in); + curr = 0; + + next = name = NULL; + DEBUG((DBG_FMAP, "%s: reading encoding vector\n", enc->name)); + for(line = enc->private; *line && curr < 256; line = next) { + SKIPSP(line); + if(*line == ']') { + line++; SKIPSP(line); + if(STRNEQ(line, "def", 3)) + break; + } + name = getword(line, " \t\n", &next); + if(name == NULL) + break; + /* next > line */ + if(*name < ' ') + continue; + if(*name == '%') { + while(*next && *next != '\n') + next++; + if(*next) next++; /* skip \n */ + continue; + } + + /* got a name */ + if(*next) *next++ = 0; + + if(*name == '/') + name++; + enc->vector[curr] = name; + /* add it to the hash table */ + if(!STREQ(name, ".notdef")) { + mdvi_hash_add(&enc->nametab, MDVI_KEY(name), + Int2Ptr(curr + 1), MDVI_HASH_REPLACE); + } + curr++; + } + if(curr == 0) { + mdvi_hash_reset(&enc->nametab, 0); + mdvi_free(enc->private); + mdvi_free(enc); + return -1; + } + while(curr < 256) + enc->vector[curr++] = NULL; + return 0; +} + +static DviEncoding *find_encoding(const char *name) +{ + return (DviEncoding *)(encodings.count ? + mdvi_hash_lookup(&enctable, MDVI_KEY(name)) : NULL); +} + +static void destroy_encoding(DviEncoding *enc) +{ + if(enc == default_encoding) { + default_encoding = tex_text_encoding; + /* now we use reference counts again */ + mdvi_release_encoding(enc, 1); + } + if(enc != tex_text_encoding) { + mdvi_hash_reset(&enc->nametab, 0); + if(enc->private) { + mdvi_free(enc->private); + mdvi_free(enc->vector); + } + if(enc->name) + mdvi_free(enc->name); + if(enc->filename) + mdvi_free(enc->filename); + mdvi_free(enc); + } +} + +/* this is used for the `enctable_file' hash table */ +static void file_hash_free(DviHashKey key, void *data) +{ + mdvi_free(key); +} + +static DviEncoding *register_encoding(const char *basefile, int replace) +{ + DviEncoding *enc; + FILE *in; + char *filename; + char *name; + Dstring input; + char *line; + long offset; + + DEBUG((DBG_FMAP, "register_encoding(%s)\n", basefile)); + + if(encodings.count) { + enc = mdvi_hash_lookup(&enctable_file, MDVI_KEY(basefile)); + if(enc != NULL) { + DEBUG((DBG_FMAP, "%s: already there\n", basefile)); + return enc; /* no error */ + } + } + + /* try our own files first */ + filename = kpse_find_file(basefile, + kpse_program_text_format, 0); + + /* then try the system-wide ones */ + if(filename == NULL) + filename = kpse_find_file(basefile, + kpse_tex_ps_header_format, 0); + if(filename == NULL) + filename = kpse_find_file(basefile, + kpse_dvips_config_format, 0); + + /* finally try the given name */ + if(filename == NULL) + filename = mdvi_strdup(basefile); + + in = fopen(filename, "rb"); + if(in == NULL) { + mdvi_free(filename); + return NULL; + } + + /* just lookup the name of the encoding */ + name = NULL; + dstring_init(&input); + while((line = dgets(&input, in)) != NULL) { + if(STRNEQ(line, "Encoding=", 9)) { + name = getword(line + 9, " \t", &line); + if(*line) *line++ = 0; + break; + } else if(*line == '/') { + char *label = getword(line + 1, " \t", &line); + if(*line) { + *line++ = 0; + SKIPSP(line); + if(*line == '[') { + *line = 0; + name = label; + break; + } + } + } + } + offset = ftell(in); + fclose(in); + if(name == NULL || *name == 0) { + DEBUG((DBG_FMAP, + "%s: could not determine name of encoding\n", + basefile)); + mdvi_free(filename); + return NULL; + } + + /* check if the encoding is already there */ + enc = find_encoding(name); + if(enc == tex_text_encoding) { + /* A special case: if the vector we found is the static one, + * allow the user to override it with an external file */ + listh_remove(&encodings, LIST(enc)); + mdvi_hash_remove(&enctable, MDVI_KEY(enc->name)); + if(enc == default_encoding) + default_encoding = NULL; + } else if(enc) { + /* if the encoding is being used, refuse to remove it */ + if(enc->links) { + mdvi_free(filename); + dstring_reset(&input); + return NULL; + } + if(replace) { + mdvi_hash_remove(&enctable, MDVI_KEY(name)); + mdvi_hash_remove(&enctable_file, MDVI_KEY(basefile)); + listh_remove(&encodings, LIST(enc)); + if(enc == default_encoding) { + default_encoding = NULL; + mdvi_release_encoding(enc, 1); + } + DEBUG((DBG_FMAP, "%s: overriding encoding\n", name)); + destroy_encoding(enc); + } else { + mdvi_free(filename); + dstring_reset(&input); + return enc; /* no error */ + } + } + enc = xalloc(DviEncoding); + enc->name = mdvi_strdup(name); + enc->filename = filename; + enc->links = 0; + enc->offset = offset; + enc->private = NULL; + enc->vector = NULL; + mdvi_hash_init(&enc->nametab); + dstring_reset(&input); + if(default_encoding == NULL) + default_encoding = enc; + mdvi_hash_add(&enctable, MDVI_KEY(enc->name), + enc, MDVI_HASH_UNCHECKED); + mdvi_hash_add(&enctable_file, MDVI_KEY(mdvi_strdup(basefile)), + enc, MDVI_HASH_REPLACE); + listh_prepend(&encodings, LIST(enc)); + DEBUG((DBG_FMAP, "%s: encoding `%s' registered\n", + basefile, enc->name)); + return enc; +} + +DviEncoding *mdvi_request_encoding(const char *name) +{ + DviEncoding *enc = find_encoding(name); + + if(enc == NULL) { + DEBUG((DBG_FMAP, "%s: encoding not found, returning default `%s'\n", + name, default_encoding->name)); + return default_encoding; + } + /* we don't keep reference counts for this */ + if(enc == tex_text_encoding) + return enc; + if(!enc->private && read_encoding(enc) < 0) + return NULL; + enc->links++; + + /* if the hash table is empty, rebuild it */ + if(enc->nametab.nkeys == 0) { + int i; + + DEBUG((DBG_FMAP, "%s: rehashing\n", enc->name)); + for(i = 0; i < 256; i++) { + if(enc->vector[i] == NULL) + continue; + mdvi_hash_add(&enc->nametab, + MDVI_KEY(enc->vector[i]), + (DviHashKey)Int2Ptr(i), + MDVI_HASH_REPLACE); + } + } + return enc; +} + +void mdvi_release_encoding(DviEncoding *enc, int should_free) +{ + /* ignore our static encoding */ + if(enc == tex_text_encoding) + return; + if(!enc->links || --enc->links > 0 || !should_free) + return; + DEBUG((DBG_FMAP, "%s: resetting encoding vector\n", enc->name)); + mdvi_hash_reset(&enc->nametab, 1); /* we'll reuse it */ +} + +int mdvi_encode_glyph(DviEncoding *enc, const char *name) +{ + void *data; + + data = mdvi_hash_lookup(&enc->nametab, MDVI_KEY(name)); + if(data == NULL) + return -1; + /* we added +1 to the hashed index just to distinguish + * a failed lookup from a zero index. Adjust it now. */ + return (Ptr2Int(data) - 1); +} + +/**************** + * Fontmaps * + ****************/ + +static void parse_spec(DviFontMapEnt *ent, char *spec) +{ + char *arg, *command; + + /* this is a ridiculously simple parser, and recognizes only + * things of the form <argument> <command>. Of these, only + * command=SlantFont, ExtendFont and ReEncodeFont are handled */ + while(*spec) { + arg = getword(spec, " \t", &spec); + if(*spec) *spec++ = 0; + command = getword(spec, " \t", &spec); + if(*spec) *spec++ = 0; + if(!arg || !command) + continue; + if(STREQ(command, "SlantFont")) { + double x = 10000 * strtod(arg, 0); + + /* SFROUND evaluates arguments twice */ + ent->slant = SFROUND(x); + } else if(STREQ(command, "ExtendFont")) { + double x = 10000 * strtod(arg, 0); + + ent->extend = SFROUND(x); + } else if(STREQ(command, "ReEncodeFont")) { + if(ent->encoding) + mdvi_free(ent->encoding); + ent->encoding = mdvi_strdup(arg); + } + } +} + +#if 0 +static void print_ent(DviFontMapEnt *ent) +{ + printf("Entry for `%s':\n", ent->fontname); + printf(" PS name: %s\n", ent->psname ? ent->psname : "(none)"); + printf(" Encoding: %s\n", ent->encoding ? ent->encoding : "(default)"); + printf(" EncFile: %s\n", ent->encfile ? ent->encfile : "(none)"); + printf(" FontFile: %s\n", ent->fontfile ? ent->fontfile : "(same)"); + printf(" Extend: %ld\n", ent->extend); + printf(" Slant: %ld\n", ent->slant); +} +#endif + +DviFontMapEnt *mdvi_load_fontmap(const char *file) +{ + char *ptr; + FILE *in; + int lineno = 1; + Dstring input; + ListHead list; + DviFontMapEnt *ent; + DviEncoding *last_encoding; + char *last_encfile; + + ptr = kpse_find_file(file, kpse_program_text_format, 0); + if(ptr == NULL) + ptr = kpse_find_file(file, kpse_tex_ps_header_format, 0); + if(ptr == NULL) + ptr = kpse_find_file(file, kpse_dvips_config_format, 0); + if(ptr == NULL) + in = fopen(file, "rb"); + else { + in = fopen(ptr, "rb"); + mdvi_free(ptr); + } + if(in == NULL) + return NULL; + + ent = NULL; + listh_init(&list); + dstring_init(&input); + last_encoding = NULL; + last_encfile = NULL; + + while((ptr = dgets(&input, in)) != NULL) { + char *font_file; + char *tex_name; + char *ps_name; + char *vec_name; + int is_encoding; + DviEncoding *enc; + + lineno++; + SKIPSP(ptr); + + /* we skip what dvips does */ + if(*ptr <= ' ' || *ptr == '*' || *ptr == '#' || + *ptr == ';' || *ptr == '%') + continue; + + font_file = NULL; + tex_name = NULL; + ps_name = NULL; + vec_name = NULL; + is_encoding = 0; + + if(ent == NULL) { + ent = xalloc(DviFontMapEnt); + ent->encoding = NULL; + ent->slant = 0; + ent->extend = 0; + } + while(*ptr) { + char *hdr_name = NULL; + + while(*ptr && *ptr <= ' ') + ptr++; + if(*ptr == 0) + break; + if(*ptr == '"') { + char *str; + + str = getstring(ptr, " \t", &ptr); + if(*ptr) *ptr++ = 0; + parse_spec(ent, str); + continue; + } else if(*ptr == '<') { + ptr++; + if(*ptr == '<') + ptr++; + else if(*ptr == '[') { + is_encoding = 1; + ptr++; + } + SKIPSP(ptr); + hdr_name = ptr; + } else if(!tex_name) + tex_name = ptr; + else if(!ps_name) + ps_name = ptr; + else + hdr_name = ptr; + + /* get next word */ + getword(ptr, " \t", &ptr); + if(*ptr) *ptr++ = 0; + + if(hdr_name) { + const char *ext = file_extension(hdr_name); + + if(is_encoding || (ext && STRCEQ(ext, "enc"))) + vec_name = hdr_name; + else + font_file = hdr_name; + } + } + + if(tex_name == NULL) + continue; + ent->fontname = mdvi_strdup(tex_name); + ent->psname = ps_name ? mdvi_strdup(ps_name) : NULL; + ent->fontfile = font_file ? mdvi_strdup(font_file) : NULL; + ent->encfile = vec_name ? mdvi_strdup(vec_name) : NULL; + ent->fullfile = NULL; + enc = NULL; /* we don't have this yet */ + + /* if we have an encoding file, register it */ + if(ent->encfile) { + /* register_encoding is smart enough not to load the + * same file twice */ + if(!last_encfile || !STREQ(last_encfile, ent->encfile)) { + last_encfile = ent->encfile; + last_encoding = register_encoding(ent->encfile, 1); + } + enc = last_encoding; + } + if(ent->encfile && enc){ + if(ent->encoding && !STREQ(ent->encoding, enc->name)) { + mdvi_warning( + _("%s: %d: [%s] requested encoding `%s' does not match vector `%s'\n"), + file, lineno, ent->encfile, + ent->encoding, enc->name); + } else if(!ent->encoding) + ent->encoding = mdvi_strdup(enc->name); + } + + /* add it to the list */ + /*print_ent(ent);*/ + listh_append(&list, LIST(ent)); + ent = NULL; + } + dstring_reset(&input); + fclose(in); + + return (DviFontMapEnt *)list.head; +} + +static void free_ent(DviFontMapEnt *ent) +{ + ASSERT(ent->fontname != NULL); + mdvi_free(ent->fontname); + if(ent->psname) + mdvi_free(ent->psname); + if(ent->fontfile) + mdvi_free(ent->fontfile); + if(ent->encoding) + mdvi_free(ent->encoding); + if(ent->encfile) + mdvi_free(ent->encfile); + if(ent->fullfile) + mdvi_free(ent->fullfile); + mdvi_free(ent); +} + +void mdvi_install_fontmap(DviFontMapEnt *head) +{ + DviFontMapEnt *ent, *next; + + for(ent = head; ent; ent = next) { + /* add all the entries, overriding old ones */ + DviFontMapEnt *old; + + old = (DviFontMapEnt *) + mdvi_hash_remove(&maptable, MDVI_KEY(ent->fontname)); + if(old != NULL) { + DEBUG((DBG_FMAP, "%s: overriding fontmap entry\n", + old->fontname)); + listh_remove(&fontmaps, LIST(old)); + free_ent(old); + } + next = ent->next; + mdvi_hash_add(&maptable, MDVI_KEY(ent->fontname), + ent, MDVI_HASH_UNCHECKED); + listh_append(&fontmaps, LIST(ent)); + } +} + +static void init_static_encoding() +{ + DviEncoding *encoding; + int i; + + DEBUG((DBG_FMAP, "installing static TeX text encoding\n")); + encoding = xalloc(DviEncoding); + encoding->private = ""; + encoding->filename = ""; + encoding->name = "TeXTextEncoding"; + encoding->vector = tex_text_vector; + encoding->links = 1; + encoding->offset = 0; + mdvi_hash_create(&encoding->nametab, ENCNAME_HASH_SIZE); + for(i = 0; i < 256; i++) { + if(encoding->vector[i]) { + mdvi_hash_add(&encoding->nametab, + MDVI_KEY(encoding->vector[i]), + (DviHashKey)Int2Ptr(i), + MDVI_HASH_UNCHECKED); + } + } + ASSERT_VALUE(encodings.count, 0); + mdvi_hash_create(&enctable, ENC_HASH_SIZE); + mdvi_hash_create(&enctable_file, ENC_HASH_SIZE); + enctable_file.hash_free = file_hash_free; + mdvi_hash_add(&enctable, MDVI_KEY(encoding->name), + encoding, MDVI_HASH_UNCHECKED); + listh_prepend(&encodings, LIST(encoding)); + tex_text_encoding = encoding; + default_encoding = tex_text_encoding; +} + +static int mdvi_set_default_encoding(const char *name) +{ + DviEncoding *enc, *old; + + enc = find_encoding(name); + if(enc == NULL) + return -1; + if(enc == default_encoding) + return 0; + /* this will read it from file if necessary, + * but it can fail if the file is corrupted */ + enc = mdvi_request_encoding(name); + if(enc == NULL) + return -1; + old = default_encoding; + default_encoding = enc; + if(old != tex_text_encoding) + mdvi_release_encoding(old, 1); + return 0; +} + +static int mdvi_init_fontmaps(void) +{ + char *file; + char *line; + FILE *in; + Dstring input; + int count = 0; + char *config; + + if(fontmaps_loaded) + return 0; + /* we will only try this once */ + fontmaps_loaded = 1; + + DEBUG((DBG_FMAP, "reading fontmaps\n")); + + /* make sure the static encoding is there */ + init_static_encoding(); + + /* create the fontmap hash table */ + mdvi_hash_create(&maptable, MAP_HASH_SIZE); + + /* get the name of our configuration file */ + config = kpse_cnf_get("mdvi-config"); + if(config == NULL) + config = MDVI_DEFAULT_CONFIG; + /* let's ask kpathsea for the file first */ + file = kpse_find_file(config, kpse_program_text_format, 0); + if(file == NULL) + in = fopen(config, "rb"); + else { + in = fopen(file, "rb"); + mdvi_free(file); + } + if(in == NULL) + return -1; + dstring_init(&input); + while((line = dgets(&input, in)) != NULL) { + char *arg; + + SKIPSP(line); + if(*line < ' ' || *line == '#' || *line == '%') + continue; + if(STRNEQ(line, "fontmap", 7)) { + DviFontMapEnt *ent; + + arg = getstring(line + 7, " \t", &line); *line = 0; + DEBUG((DBG_FMAP, "%s: loading fontmap\n", arg)); + ent = mdvi_load_fontmap(arg); + if(ent == NULL) + mdvi_warning(_("%s: could not load fontmap\n"), arg); + else { + DEBUG((DBG_FMAP, + "%s: installing fontmap\n", arg)); + mdvi_install_fontmap(ent); + count++; + } + } else if(STRNEQ(line, "encoding", 8)) { + arg = getstring(line + 8, " \t", &line); *line = 0; + if(arg && *arg) + register_encoding(arg, 1); + } else if(STRNEQ(line, "default-encoding", 16)) { + arg = getstring(line + 16, " \t", &line); *line = 0; + if(mdvi_set_default_encoding(arg) < 0) + mdvi_warning(_("%s: could not set as default encoding\n"), + arg); + } else if(STRNEQ(line, "psfontpath", 10)) { + arg = getstring(line + 11, " \t", &line); *line = 0; + if(!psinitialized) + ps_init_default_paths(); + if(psfontdir) + mdvi_free(psfontdir); + psfontdir = kpse_path_expand(arg); + } else if(STRNEQ(line, "pslibpath", 9)) { + arg = getstring(line + 10, " \t", &line); *line = 0; + if(!psinitialized) + ps_init_default_paths(); + if(pslibdir) + mdvi_free(pslibdir); + pslibdir = kpse_path_expand(arg); + } else if(STRNEQ(line, "psfontmap", 9)) { + arg = getstring(line + 9, " \t", &line); *line = 0; + if(mdvi_ps_read_fontmap(arg) < 0) + mdvi_warning("%s: %s: could not read PS fontmap\n", + config, arg); + } + } + fclose(in); + dstring_reset(&input); + fontmaps_loaded = 1; + DEBUG((DBG_FMAP, "%d files installed, %d fontmaps\n", + count, fontmaps.count)); + return count; +} + +int mdvi_query_fontmap(DviFontMapInfo *info, const char *fontname) +{ + DviFontMapEnt *ent; + + if(!fontmaps_loaded && mdvi_init_fontmaps() < 0) + return -1; + ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(fontname)); + + if(ent == NULL) + return -1; + info->psname = ent->psname; + info->encoding = ent->encoding; + info->fontfile = ent->fontfile; + info->extend = ent->extend; + info->slant = ent->slant; + info->fullfile = ent->fullfile; + + return 0; +} + +int mdvi_add_fontmap_file(const char *name, const char *fullpath) +{ + DviFontMapEnt *ent; + + if(!fontmaps_loaded && mdvi_init_fontmaps() < 0) + return -1; + ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(name)); + if(ent == NULL) + return -1; + if(ent->fullfile) + mdvi_free(ent->fullfile); + ent->fullfile = mdvi_strdup(fullpath); + return 0; +} + + +void mdvi_flush_encodings(void) +{ + DviEncoding *enc; + + if(enctable.nbucks == 0) + return; + + DEBUG((DBG_FMAP, "flushing %d encodings\n", encodings.count)); + /* asked to remove all encodings */ + for(; (enc = (DviEncoding *)encodings.head); ) { + encodings.head = LIST(enc->next); + if((enc != tex_text_encoding && enc->links) || enc->links > 1) { + mdvi_warning(_("encoding vector `%s' is in use\n"), + enc->name); + } + destroy_encoding(enc); + } + /* destroy the static encoding */ + if(tex_text_encoding->nametab.buckets) + mdvi_hash_reset(&tex_text_encoding->nametab, 0); + mdvi_hash_reset(&enctable, 0); + mdvi_hash_reset(&enctable_file, 0); +} + +void mdvi_flush_fontmaps(void) +{ + DviFontMapEnt *ent; + + if(!fontmaps_loaded) + return; + + DEBUG((DBG_FMAP, "flushing %d fontmaps\n", fontmaps.count)); + for(; (ent = (DviFontMapEnt *)fontmaps.head); ) { + fontmaps.head = LIST(ent->next); + free_ent(ent); + } + mdvi_hash_reset(&maptable, 0); + fontmaps_loaded = 0; +} + +/* reading of PS fontmaps */ + +void ps_init_default_paths(void) +{ + char *kppath; + char *kfpath; + + ASSERT(psinitialized == 0); + + kppath = getenv("GS_LIB"); + kfpath = getenv("GS_FONTPATH"); + + if(kppath != NULL) + pslibdir = kpse_path_expand(kppath); + if(kfpath != NULL) + psfontdir = kpse_path_expand(kfpath); + + listh_init(&psfonts); + mdvi_hash_create(&pstable, PSMAP_HASH_SIZE); + psinitialized = 1; +} + +int mdvi_ps_read_fontmap(const char *name) +{ + char *fullname; + FILE *in; + Dstring dstr; + char *line; + int count = 0; + + if(!psinitialized) + ps_init_default_paths(); + if(pslibdir) + fullname = kpse_path_search(pslibdir, name, 1); + else + fullname = (char *)name; + in = fopen(fullname, "rb"); + if(in == NULL) { + if(fullname != name) + mdvi_free(fullname); + return -1; + } + dstring_init(&dstr); + + while((line = dgets(&dstr, in)) != NULL) { + char *name; + char *mapname; + const char *ext; + PSFontMap *ps; + + SKIPSP(line); + /* we're looking for lines of the form + * /FONT-NAME (fontfile) + * /FONT-NAME /FONT-ALIAS + */ + if(*line != '/') + continue; + name = getword(line + 1, " \t", &line); + if(*line) *line++ = 0; + mapname = getword(line, " \t", &line); + if(*line) *line++ = 0; + + if(!name || !mapname || !*name) + continue; + if(*mapname == '(') { + char *end; + + mapname++; + for(end = mapname; *end && *end != ')'; end++); + *end = 0; + } + if(!*mapname) + continue; + /* dont add `.gsf' fonts, which require a full blown + * PostScript interpreter */ + ext = file_extension(mapname); + if(ext && STREQ(ext, "gsf")) { + DEBUG((DBG_FMAP, "(ps) %s: font `%s' ignored\n", + name, mapname)); + continue; + } + ps = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(name)); + if(ps != NULL) { + if(STREQ(ps->mapname, mapname)) + continue; + DEBUG((DBG_FMAP, + "(ps) replacing font `%s' (%s) by `%s'\n", + name, ps->mapname, mapname)); + mdvi_free(ps->mapname); + ps->mapname = mdvi_strdup(mapname); + if(ps->fullname) { + mdvi_free(ps->fullname); + ps->fullname = NULL; + } + } else { + DEBUG((DBG_FMAP, "(ps) adding font `%s' as `%s'\n", + name, mapname)); + ps = xalloc(PSFontMap); + ps->psname = mdvi_strdup(name); + ps->mapname = mdvi_strdup(mapname); + ps->fullname = NULL; + listh_append(&psfonts, LIST(ps)); + mdvi_hash_add(&pstable, MDVI_KEY(ps->psname), + ps, MDVI_HASH_UNCHECKED); + count++; + } + } + fclose(in); + dstring_reset(&dstr); + + DEBUG((DBG_FMAP, "(ps) %s: %d PostScript fonts registered\n", + fullname, count)); + return 0; +} + +void mdvi_ps_flush_fonts(void) +{ + PSFontMap *map; + + if(!psinitialized) + return; + DEBUG((DBG_FMAP, "(ps) flushing PS font map (%d) entries\n", + psfonts.count)); + mdvi_hash_reset(&pstable, 0); + for(; (map = (PSFontMap *)psfonts.head); ) { + psfonts.head = LIST(map->next); + mdvi_free(map->psname); + mdvi_free(map->mapname); + if(map->fullname) + mdvi_free(map->fullname); + mdvi_free(map); + } + listh_init(&psfonts); + if(pslibdir) { + mdvi_free(pslibdir); + pslibdir = NULL; + } + if(psfontdir) { + mdvi_free(psfontdir); + psfontdir = NULL; + } + psinitialized = 0; +} + +char *mdvi_ps_find_font(const char *psname) +{ + PSFontMap *map, *smap; + char *filename; + int recursion_limit = 32; + + DEBUG((DBG_FMAP, "(ps) resolving PS font `%s'\n", psname)); + if(!psinitialized) + return NULL; + map = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(psname)); + if(map == NULL) + return NULL; + if(map->fullname) + return mdvi_strdup(map->fullname); + + /* is it an alias? */ + smap = map; + while(recursion_limit-- > 0 && smap && *smap->mapname == '/') + smap = (PSFontMap *)mdvi_hash_lookup(&pstable, + MDVI_KEY(smap->mapname + 1)); + if(smap == NULL) { + if(recursion_limit == 0) + DEBUG((DBG_FMAP, + "(ps) %s: possible loop in PS font map\n", + psname)); + return NULL; + } + + if(psfontdir) + filename = kpse_path_search(psfontdir, smap->mapname, 1); + else if(file_exists(map->mapname)) + filename = mdvi_strdup(map->mapname); + else + filename = NULL; + if(filename) + map->fullname = mdvi_strdup(filename); + + return filename; +} + +/* + * To get metric info for a font, we proceed as follows: + * - We try to find NAME.<tfm,ofm,afm>. + * - We query the fontmap for NAME. + * - We get back a PSNAME, and use to find the file in the PS font map. + * - We get the PSFONT file name, replace its extension by "afm" and + * lookup the file in GS's font search path. + * - We finally read the data, transform it as specified in our font map, + * and return it to the caller. The new data is left in the font metrics + * cache, so the next time it will be found at the first step (when we look + * up NAME.afm). + * + * The name `_ps_' in this function is not meant to imply that it can be + * used for Type1 fonts only. It should be usable for TrueType fonts as well. + * + * The returned metric info is subjected to the same caching mechanism as + * all the other metric data, as returned by get_font_metrics(). One should + * not modify the returned data at all, and it should be disposed with + * free_font_metrics(). + */ +TFMInfo *mdvi_ps_get_metrics(const char *fontname) +{ + TFMInfo *info; + DviFontMapInfo map; + char buffer[64]; /* to avoid mallocs */ + char *psfont; + char *basefile; + char *afmfile; + char *ext; + int baselen; + int nc; + TFMChar *ch; + double efactor; + double sfactor; + + DEBUG((DBG_FMAP, "(ps) %s: looking for metric data\n", fontname)); + info = get_font_metrics(fontname, DviFontAny, NULL); + if(info != NULL) + return info; + + /* query the fontmap */ + if(mdvi_query_fontmap(&map, fontname) < 0 || !map.psname) + return NULL; + + /* get the PS font */ + psfont = mdvi_ps_find_font(map.psname); + if(psfont == NULL) + return NULL; + DEBUG((DBG_FMAP, "(ps) %s: found as PS font `%s'\n", + fontname, psfont)); + /* replace its extension */ + basefile = strrchr(psfont, '/'); + if(basefile == NULL) + basefile = psfont; + baselen = strlen(basefile); + ext = strrchr(basefile, '.'); + if(ext != NULL) + *ext = 0; + if(baselen + 4 < 64) + afmfile = &buffer[0]; + else + afmfile = mdvi_malloc(baselen + 5); + strcpy(afmfile, basefile); + strcpy(afmfile + baselen, ".afm"); + /* we don't need this anymore */ + mdvi_free(psfont); + DEBUG((DBG_FMAP, "(ps) %s: looking for `%s'\n", + fontname, afmfile)); + /* lookup the file */ + psfont = kpse_path_search(psfontdir, afmfile, 1); + /* don't need this anymore */ + if(afmfile != &buffer[0]) + mdvi_free(afmfile); + if(psfont != NULL) { + info = get_font_metrics(fontname, DviFontAFM, psfont); + mdvi_free(psfont); + } else + info = NULL; + if(info == NULL || (!map.extend && !map.slant)) + return info; + + /* + * transform the data as prescribed -- keep in mind that `info' + * points to CACHED data, so we're modifying the metric cache + * in place. + */ + +#define DROUND(x) ((x) >= 0 ? floor((x) + 0.5) : ceil((x) - 0.5)) +#define TRANSFORM(x,y) DROUND(efactor * (x) + sfactor * (y)) + + efactor = (double)map.extend / 10000.0; + sfactor = (double)map.slant / 10000.0; + DEBUG((DBG_FMAP, "(ps) %s: applying extend=%f, slant=%f\n", + efactor, sfactor)); + + nc = info->hic - info->loc + 1; + for(ch = info->chars; ch < info->chars + nc; ch++) { + /* the AFM bounding box is: + * wx = ch->advance + * llx = ch->left + * lly = -ch->depth + * urx = ch->right + * ury = ch->height + * what we do here is transform wx, llx, and urx by + * newX = efactor * oldX + sfactor * oldY + * where for `wx' oldY = 0. Also, these numbers are all in + * TFM units (i.e. TFM's fix-words, which is just the actual + * number times 2^20, no need to do anything to it). + */ + if(ch->present) { + ch->advance = TRANSFORM(ch->advance, 0); + ch->left = TRANSFORM(ch->left, -ch->depth); + ch->right = TRANSFORM(ch->right, ch->height); + } + } + + return info; +} diff --git a/backend/dvi/mdvi-lib/fontmap.h b/backend/dvi/mdvi-lib/fontmap.h new file mode 100644 index 00000000..bb5a944d --- /dev/null +++ b/backend/dvi/mdvi-lib/fontmap.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_FONTMAP_H +#define _MDVI_FONTMAP_H 1 + +typedef struct _DviFontMapEnt DviFontMapEnt; +typedef struct _DviEncoding DviEncoding; + +typedef struct { + const char *psname; + const char *encoding; + const char *fontfile; + const char *fullfile; + const char *fmfile; + int fmtype; + long extend; + long slant; +} DviFontMapInfo; + +struct _DviEncoding { + DviEncoding *next; + DviEncoding *prev; + char *private; + char *filename; + char *name; + char **vector; /* table with exactly 256 strings */ + int links; + long offset; + DviHashTable nametab; +}; + +struct _DviFontMapEnt { + DviFontMapEnt *next; + DviFontMapEnt *prev; + char *private; + char *fontname; + char *psname; + char *encoding; + char *encfile; + char *fontfile; + char *fullfile; + long extend; + long slant; +}; + +#define MDVI_FMAP_SLANT(x) ((double)(x)->slant / 10000.0) +#define MDVI_FMAP_EXTEND(x) ((double)(x)->extend / 10000.0) + +extern DviEncoding *mdvi_request_encoding __PROTO((const char *)); +extern void mdvi_release_encoding __PROTO((DviEncoding *, int)); +extern int mdvi_encode_glyph __PROTO((DviEncoding *, const char *)); +extern DviFontMapEnt *mdvi_load_fontmap __PROTO((const char *)); +extern void mdvi_install_fontmap __PROTO((DviFontMapEnt *)); +extern int mdvi_load_fontmaps __PROTO((void)); +extern int mdvi_query_fontmap __PROTO((DviFontMapInfo *, const char *)); +extern void mdvi_flush_encodings __PROTO((void)); +extern void mdvi_flush_fontmaps __PROTO((void)); + +extern int mdvi_add_fontmap_file __PROTO((const char *, const char *)); + +/* PS font maps */ +extern int mdvi_ps_read_fontmap __PROTO((const char *)); +extern char *mdvi_ps_find_font __PROTO((const char *)); +extern TFMInfo *mdvi_ps_get_metrics __PROTO((const char *)); +extern void mdvi_ps_flush_fonts __PROTO((void)); + +#endif /* _MDVI_FONTMAP_H */ diff --git a/backend/dvi/mdvi-lib/fontsrch.c b/backend/dvi/mdvi-lib/fontsrch.c new file mode 100644 index 00000000..8ebaa5ca --- /dev/null +++ b/backend/dvi/mdvi-lib/fontsrch.c @@ -0,0 +1,371 @@ +/* fontsearch.c -- implements the font lookup mechanism in MDVI */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * How this works: + * Fonts are divided into MAX_CLASS priority classes. The first + * MAX_CLASS-1 ones correspond to `real' fonts (pk, gf, vf, type1, truetype, + * etc). The last one corresponds to `metric' fonts that are used as a last + * resort (tfm, afm, ofm, ...). When a font is looked up, it is tried in a + * `high' priority class (0 being the highest priority). The priority is + * lowered until it reaches MAX_CLASS-1. Then the whole thing is repeated + * for the fallback font. When the search reaches MAX_CLASS-1, we lookup the + * original font, and then the fallback font. The search can be done + * incrementally, with several calls to mdvi_lookup_font(). If this function + * is called again to continue a search, the function assumes the previous + * font it returned was not valid, and it goes on to the next step. + * + * Reason for this: + * Some font types are quite expensive to load (e.g. Type1), so loading + * them is deferred until the last possible moment. This means that a font that + * was supposed to exist may have to be discarded. Until now, MDVI had no ability to + * "resume" a search, so in this case it would have produced an error, regardless + * of whether the offending font existed in other formats. + * Also, given the large number of font types supported by MDVI, some mechanism + * was necessary to bring some order into the chaos. + * + * This mechanism fixes these two problems. For the first one, a search can + * be "resumed" and all the font formats tried for the missing font, and + * again for the fallback font (see above). As for the second, the + * hierarchical division in classes gives a lot of flexibility in how the + * fonts are configured. + */ + +#include <config.h> +#include "mdvi.h" + +#define HAVE_PROTOTYPES 1 +#include <kpathsea/tex-file.h> +#include <kpathsea/tex-glyph.h> + +struct _DviFontClass { + DviFontClass *next; + DviFontClass *prev; + DviFontInfo info; + int links; + int id; +}; + +char *_mdvi_fallback_font = MDVI_FALLBACK_FONT; + +/* this leaves classes 0 and 1 for `real' fonts */ +#define MAX_CLASS 3 +static ListHead font_classes[MAX_CLASS]; +static int initialized = 0; + +static void init_font_classes(void) +{ + int i; + + for(i = 0; i < MAX_CLASS; i++) + listh_init(&font_classes[i]); + initialized = 1; +} + +int mdvi_get_font_classes(void) +{ + return (MAX_CLASS - 2); +} + +char **mdvi_list_font_class(int klass) +{ + char **list; + int i, n; + DviFontClass *fc; + + if(klass == -1) + klass = MAX_CLASS-1; + if(klass < 0 || klass >= MAX_CLASS) + return NULL; + n = font_classes[klass].count; + list = xnalloc(char *, n + 1); + fc = (DviFontClass *)font_classes[klass].head; + for(i = 0; i < n; fc = fc->next, i++) { + list[i] = mdvi_strdup(fc->info.name); + } + list[i] = NULL; + return list; +} + +int mdvi_register_font_type(DviFontInfo *info, int klass) +{ + DviFontClass *fc; + + if(klass == -1) + klass = MAX_CLASS-1; + if(klass < 0 || klass >= MAX_CLASS) + return -1; + if(!initialized) + init_font_classes(); + fc = xalloc(struct _DviFontClass); + fc->links = 0; + fc->id = klass; + fc->info.name = mdvi_strdup(info->name); + fc->info.scalable = info->scalable; + fc->info.load = info->load; + fc->info.getglyph = info->getglyph; + fc->info.shrink0 = info->shrink0; + fc->info.shrink1 = info->shrink1; + fc->info.freedata = info->freedata; + fc->info.reset = info->reset; + fc->info.lookup = info->lookup; + fc->info.kpse_type = info->kpse_type; + listh_append(&font_classes[klass], LIST(fc)); + return 0; +} + +int mdvi_unregister_font_type(const char *name, int klass) +{ + DviFontClass *fc; + int k; + + if(klass == -1) + klass = MAX_CLASS - 1; + + if(klass >= 0 && klass < MAX_CLASS) { + k = klass; + LIST_FOREACH(fc, DviFontClass, &font_classes[k]) { + if(STREQ(fc->info.name, name)) + break; + } + } else if(klass < 0) { + for(k = 0; k < MAX_CLASS; k++) { + LIST_FOREACH(fc, DviFontClass, &font_classes[k]) { + if(STREQ(fc->info.name, name)) + break; + } + if(fc) break; + } + } else + return -1; + + if(fc == NULL || fc->links) + return -1; + /* remove it */ + listh_remove(&font_classes[k], LIST(fc)); + + /* and destroy it */ + mdvi_free(fc->info.name); + mdvi_free(fc); + return 0; +} + +static char *lookup_font(DviFontClass *ptr, const char *name, Ushort *h, Ushort *v) +{ + char *filename; + + /* + * If the font type registered a function to do the lookup, use that. + * Otherwise we use kpathsea. + */ + if(ptr->info.lookup) + filename = ptr->info.lookup(name, h, v); + else if(ptr->info.kpse_type <= kpse_any_glyph_format) { + kpse_glyph_file_type type; + + filename = kpse_find_glyph(name, Max(*h, *v), + ptr->info.kpse_type, &type); + /* if kpathsea returned a fallback font, reject it */ + if(filename && type.source == kpse_glyph_source_fallback) { + mdvi_free(filename); + filename = NULL; + } else if(filename) + *h = *v = type.dpi; + } else + filename = kpse_find_file(name, ptr->info.kpse_type, 1); + return filename; +} + +/* + * Class MAX_CLASS-1 is special: it consists of `metric' fonts that should + * be tried as a last resort + */ +char *mdvi_lookup_font(DviFontSearch *search) +{ + int kid; + int k; + DviFontClass *ptr; + DviFontClass *last; + char *filename = NULL; + const char *name; + Ushort hdpi, vdpi; + + if(search->id < 0) + return NULL; + + if(search->curr == NULL) { + /* this is the initial search */ + name = search->wanted_name; + hdpi = search->hdpi; + vdpi = search->vdpi; + kid = 0; + last = NULL; + } else { + name = search->actual_name; + hdpi = search->actual_hdpi; + vdpi = search->actual_vdpi; + kid = search->id; + last = search->curr; + } + + ptr = NULL; +again: + /* try all classes except MAX_CLASS-1 */ + for(k = kid; !filename && k < MAX_CLASS-1; k++) { + if(last == NULL) + ptr = (DviFontClass *)font_classes[k].head; + else + ptr = last->next; + while(ptr) { + DEBUG((DBG_FONTS, "%d: trying `%s' at (%d,%d)dpi as `%s'\n", + k, name, hdpi, vdpi, ptr->info.name)); + /* lookup the font in this class */ + filename = lookup_font(ptr, name, &hdpi, &vdpi); + if(filename) + break; + ptr = ptr->next; + } + last = NULL; + } + if(filename != NULL) { + search->id = k-1; + search->curr = ptr; + search->actual_name = name; + search->actual_hdpi = hdpi; + search->actual_vdpi = vdpi; + search->info = &ptr->info; + ptr->links++; + return filename; + } + + if(kid < MAX_CLASS - 1 && !STREQ(name, _mdvi_fallback_font)) { + mdvi_warning("font `%s' at %dx%d not found, trying `%s' instead\n", + name, hdpi, vdpi, _mdvi_fallback_font); + name = _mdvi_fallback_font; + kid = 0; + goto again; + } + + /* we tried the fallback font, and all the `real' classes. Let's + * try the `metric' class now */ + name = search->wanted_name; + hdpi = search->hdpi; + vdpi = search->vdpi; + if(kid == MAX_CLASS-1) { + /* we were looking into this class from the beginning */ + if(last == NULL) { + /* no more fonts to try */ + return NULL; + } + ptr = last->next; + } else { + mdvi_warning("font `%s' not found, trying metric files instead\n", + name); + ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head; + } + +metrics: + while(ptr) { + DEBUG((DBG_FONTS, "metric: trying `%s' at (%d,%d)dpi as `%s'\n", + name, hdpi, vdpi, ptr->info.name)); + filename = lookup_font(ptr, name, &hdpi, &vdpi); + if(filename) + break; + ptr = ptr->next; + } + if(filename != NULL) { + if(STREQ(name, _mdvi_fallback_font)) + search->id = MAX_CLASS; + else + search->id = MAX_CLASS - 1; + search->curr = ptr; + search->actual_name = name; + search->actual_hdpi = hdpi; + search->actual_vdpi = vdpi; + search->info = &ptr->info; + ptr->links++; + return filename; + } + if(!STREQ(name, _mdvi_fallback_font)) { + mdvi_warning("metric file for `%s' not found, trying `%s' instead\n", + name, _mdvi_fallback_font); + name = _mdvi_fallback_font; + ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head; + goto metrics; + } + + search->id = -1; + search->actual_name = NULL; + + /* tough luck, nothing found */ + return NULL; +} + +/* called by `font_reference' to do the initial lookup */ +DviFont *mdvi_add_font(const char *name, Int32 sum, + int hdpi, int vdpi, Int32 scale) +{ + DviFont *font; + + font = xalloc(DviFont); + font->fontname = mdvi_strdup(name); + SEARCH_INIT(font->search, font->fontname, hdpi, vdpi); + font->filename = mdvi_lookup_font(&font->search); + if(font->filename == NULL) { + /* this answer is final */ + mdvi_free(font->fontname); + mdvi_free(font); + return NULL; + } + font->hdpi = font->search.actual_hdpi; + font->vdpi = font->search.actual_vdpi; + font->scale = scale; + font->design = 0; + font->checksum = sum; + font->type = 0; + font->links = 0; + font->loc = 0; + font->hic = 0; + font->in = NULL; + font->chars = NULL; + font->subfonts = NULL; + + return font; +} + +int mdvi_font_retry(DviParams *params, DviFont *font) +{ + /* try the search again */ + char *filename; + + ASSERT(font->search.curr != NULL); + /* we won't be using this class anymore */ + font->search.curr->links--; + + filename = mdvi_lookup_font(&font->search); + if(filename == NULL) + return -1; + mdvi_free(font->filename); + font->filename = filename; + /* copy the new information */ + font->hdpi = font->search.actual_hdpi; + font->vdpi = font->search.actual_vdpi; + + return 0; +} diff --git a/backend/dvi/mdvi-lib/gf.c b/backend/dvi/mdvi-lib/gf.c new file mode 100644 index 00000000..00607ff6 --- /dev/null +++ b/backend/dvi/mdvi-lib/gf.c @@ -0,0 +1,395 @@ +/* gf.c - GF font support */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* functions to read GF fonts */ + +#include <config.h> +#include <string.h> +#include "common.h" +#include "mdvi.h" +#include "private.h" + +/* opcodes */ + +#define GF_PAINT0 0 +#define GF_PAINT1 64 +#define GF_PAINT2 65 +#define GF_PAINT3 66 +#define GF_BOC 67 +#define GF_BOC1 68 +#define GF_EOC 69 +#define GF_SKIP0 70 +#define GF_SKIP1 71 +#define GF_SKIP2 72 +#define GF_SKIP3 73 +#define GF_NEW_ROW_0 74 +#define GF_NEW_ROW_1 75 +#define GF_NEW_ROW_MAX 238 +#define GF_XXX1 239 +#define GF_XXX2 240 +#define GF_XXX3 241 +#define GF_XXX4 242 +#define GF_YYY 243 +#define GF_NOOP 244 +#define GF_LOC 245 +#define GF_LOC0 246 +#define GF_PRE 247 +#define GF_POST 248 +#define GF_POST_POST 249 + +#define GF_ID 131 +#define GF_TRAILER 223 + +#define BLACK 1 +#define WHITE 0 + +static int gf_load_font __PROTO((DviParams *, DviFont *)); +static int gf_font_get_glyph __PROTO((DviParams *, DviFont *, int)); + +/* only symbol exported by this file */ +DviFontInfo gf_font_info = { + "GF", + 0, /* scaling not supported natively */ + gf_load_font, + gf_font_get_glyph, + mdvi_shrink_glyph, + mdvi_shrink_glyph_grey, + NULL, /* free */ + NULL, /* reset */ + NULL, /* lookup */ + kpse_gf_format, + NULL +}; + +static int gf_read_bitmap(FILE *p, DviFontChar *ch) +{ + int op; + int min_n, max_n; + int min_m, max_m; + int paint_switch; + int x, y; + int bpl; + Int32 par; + BmUnit *line; + BITMAP *map; + + fseek(p, (long)ch->offset, SEEK_SET); + op = fuget1(p); + if(op == GF_BOC) { + /* skip character code */ + fuget4(p); + /* skip pointer */ + fuget4(p); + min_m = fsget4(p); + max_m = fsget4(p); + min_n = fsget4(p); + max_n = fsget4(p); + } else if(op == GF_BOC1) { + /* skip character code */ + fuget1(p); + min_m = fuget1(p); /* this is max_m - min_m */ + max_m = fuget1(p); + min_n = fuget1(p); /* this is max_n - min_n */ + max_n = fuget1(p); + min_m = max_m - min_m; + min_n = max_n - min_n; + } else { + mdvi_error(_("GF: invalid opcode %d in character %d\n"), + op, ch->code); + return -1; + } + + ch->x = -min_m; + ch->y = max_n; + ch->width = max_m - min_m + 1; + ch->height = max_n - min_n + 1; + map = bitmap_alloc(ch->width, ch->height); + + ch->glyph.data = map; + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + +#define COLOR(x) ((x) ? "BLACK" : "WHITE") + + paint_switch = WHITE; + x = y = 0; + line = map->data; + bpl = map->stride; + DEBUG((DBG_BITMAPS, "(gf) reading character %d\n", ch->code)); + while((op = fuget1(p)) != GF_EOC) { + Int32 n; + + if(feof(p)) + break; + if(op == GF_PAINT0) { + DEBUG((DBG_BITMAPS, "(gf) Paint0 %s -> %s\n", + COLOR(paint_switch), COLOR(!paint_switch))); + paint_switch = !paint_switch; + } else if(op <= GF_PAINT3) { + if(op < GF_PAINT1) + par = op; + else + par = fugetn(p, op - GF_PAINT1 + 1); + if(y >= ch->height || x + par >= ch->width) + goto toobig; + /* paint everything between columns x and x + par - 1 */ + DEBUG((DBG_BITMAPS, "(gf) Paint %d %s from (%d,%d)\n", + par, COLOR(paint_switch), x, y)); + if(paint_switch == BLACK) + bitmap_paint_bits(line + (x / BITMAP_BITS), + x % BITMAP_BITS, par); + paint_switch = !paint_switch; + x += par; + } else if(op >= GF_NEW_ROW_0 && op <= GF_NEW_ROW_MAX) { + y++; + line = bm_offset(line, bpl); + x = op - GF_NEW_ROW_0; + paint_switch = BLACK; + DEBUG((DBG_BITMAPS, "(gf) new_row_%d\n", x)); + } else switch(op) { + case GF_SKIP0: + y++; + line = bm_offset(line, bpl); + x = 0; + paint_switch = WHITE; + DEBUG((DBG_BITMAPS, "(gf) skip_0\n")); + break; + case GF_SKIP1: + case GF_SKIP2: + case GF_SKIP3: + par = fugetn(p, op - GF_SKIP1 + 1); + y += par + 1; + line = bm_offset(line, (par + 1) * bpl); + x = 0; + paint_switch = WHITE; + DEBUG((DBG_BITMAPS, "(gf) skip_%d\n", op - GF_SKIP1)); + break; + case GF_XXX1: + case GF_XXX2: + case GF_XXX3: + case GF_XXX4: { +#ifndef NODEBUG + char *s; + + s = read_string(p, op - GF_XXX1 + 1, NULL, 0); + DEBUG((DBG_SPECIAL, "(gf) Character %d: Special \"%s\"\n", + ch->code, s)); + mdvi_free(s); +#else + n = fugetn(p, op - GF_XXX1 + 1); + fseek(p, (long)n, SEEK_CUR); +#endif + break; + } + case GF_YYY: + n = fuget4(p); + DEBUG((DBG_SPECIAL, "(gf) Character %d: MF special %u\n", + ch->code, n)); + break; + case GF_NOOP: + DEBUG((DBG_BITMAPS, "(gf) no_op\n")); + break; + default: + mdvi_error(_("(gf) Character %d: invalid opcode %d\n"), + ch->code, op); + goto error; + } + /* chech that we're still inside the bitmap */ + if(x > ch->width || y > ch->height) + goto toobig; + DEBUG((DBG_BITMAPS, "(gf) curr_loc @ (%d,%d)\n", x, y)); + } + + if(op != GF_EOC) + goto error; + DEBUG((DBG_BITMAPS, "(gf) end of character %d\n", ch->code)); + return 0; + +toobig: + mdvi_error(_("(gf) character %d has an incorrect bounding box\n"), + ch->code); +error: + bitmap_destroy(map); + ch->glyph.data = NULL; + return -1; +} + +static int gf_load_font(DviParams *unused, DviFont *font) +{ + int i; + int n; + int loc; + int hic; + FILE *p; + Int32 word; + int op; + long alpha, beta, z; +#ifndef NODEBUG + char s[256]; +#endif + + p = font->in; + + /* check preamble */ + loc = fuget1(p); hic = fuget1(p); + if(loc != GF_PRE || hic != GF_ID) + goto badgf; + loc = fuget1(p); +#ifndef NODEBUG + for(i = 0; i < loc; i++) + s[i] = fuget1(p); + s[i] = 0; + DEBUG((DBG_FONTS, "(gf) %s: %s\n", font->fontname, s)); +#else + fseek(p, (long)loc, SEEK_CUR); +#endif + /* now read character locators in postamble */ + if(fseek(p, (long)-1, SEEK_END) == -1) + return -1; + + n = 0; + while((op = fuget1(p)) == GF_TRAILER) { + if(fseek(p, (long)-2, SEEK_CUR) < 0) + break; + n++; + } + if(op != GF_ID || n < 4) + goto badgf; + /* get the pointer to the postamble */ + fseek(p, (long)-5, SEEK_CUR); + op = fuget4(p); + /* jump to it */ + fseek(p, (long)op, SEEK_SET); + if(fuget1(p) != GF_POST) + goto badgf; + /* skip pointer to last EOC */ + fuget4(p); + /* get the design size */ + font->design = fuget4(p); + /* the checksum */ + word = fuget4(p); + if(word && font->checksum && font->checksum != word) { + mdvi_warning(_("%s: bad checksum (expected %u, found %u)\n"), + font->fontname, font->checksum, word); + } else if(!font->checksum) + font->checksum = word; + /* skip pixels per point ratio */ + fuget4(p); + fuget4(p); + font->chars = xnalloc(DviFontChar, 256); + for(loc = 0; loc < 256; loc++) + font->chars[loc].offset = 0; + /* skip glyph "bounding box" */ + fseek(p, (long)16, SEEK_CUR); + loc = 256; + hic = -1; + TFMPREPARE(font->scale, z, alpha, beta); + while((op = fuget1(p)) != GF_POST_POST) { + DviFontChar *ch; + int cc; + + /* get the character code */ + cc = fuget1(p); + if(cc < loc) + loc = cc; + if(cc > hic) + hic = cc; + ch = &font->chars[cc]; + switch(op) { + case GF_LOC: + fsget4(p); /* skip dx */ + fsget4(p); /* skip dy */ + break; + case GF_LOC0: + fuget1(p); /* skip dx */ + /* dy assumed 0 */ + break; + default: + mdvi_error(_("%s: junk in postamble\n"), font->fontname); + goto error; + } + ch->code = cc; + ch->tfmwidth = fuget4(p); + ch->tfmwidth = TFMSCALE(ch->tfmwidth, z, alpha, beta); + ch->offset = fuget4(p); + if(ch->offset == -1) + ch->offset = 0; + /* initialize the rest of the glyph information */ + ch->x = 0; + ch->y = 0; + ch->width = 0; + ch->height = 0; + ch->glyph.data = NULL; + ch->shrunk.data = NULL; + ch->grey.data = NULL; + ch->flags = 0; + ch->loaded = 0; + } + + if(op != GF_POST_POST) + goto badgf; + + if(loc > 0 || hic < 255) { + /* shrink to optimal size */ + memmove(font->chars, font->chars + loc, + (hic - loc + 1) * sizeof(DviFontChar)); + font->chars = xresize(font->chars, + DviFontChar, hic - loc + 1); + } + font->loc = loc; + font->hic = hic; + + return 0; + +badgf: + mdvi_error(_("%s: File corrupted, or not a GF file\n"), font->fontname); +error: + if(font->chars) { + mdvi_free(font->chars); + font->chars = NULL; + } + font->loc = font->hic = 0; + return -1; +} + +static int gf_font_get_glyph(DviParams *params, DviFont *font, int code) +{ + DviFontChar *ch; + + if(code < font->loc || code > font->hic || !font->chars) + return -1; + ch = &font->chars[code - font->loc]; + + if(!ch->loaded) { + if(ch->offset == 0) + return -1; + DEBUG((DBG_GLYPHS, "(gf) %s: loading GF glyph for character %d\n", + font->fontname, code)); + if(font->in == NULL && font_reopen(font) < 0) + return -1; + if(fseek(font->in, ch->offset, SEEK_SET) == -1) + return -1; + if(gf_read_bitmap(font->in, ch) < 0) + return -1; + ch->loaded = 1; + } + return 0; +} diff --git a/backend/dvi/mdvi-lib/hash.c b/backend/dvi/mdvi-lib/hash.c new file mode 100644 index 00000000..d359e3c8 --- /dev/null +++ b/backend/dvi/mdvi-lib/hash.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "mdvi.h" + +/* simple hash tables for MDVI */ + + +struct _DviHashBucket { + DviHashBucket *next; + DviHashKey key; + Ulong hvalue; + void *data; +}; + +static Ulong hash_string(DviHashKey key) +{ + Uchar *p; + Ulong h, g; + + for(h = 0, p = (Uchar *)key; *p; p++) { + h = (h << 4UL) + *p; + if((g = h & 0xf0000000L) != 0) { + h ^= (g >> 24UL); + h ^= g; + } + } + + return h; +} + +static int hash_compare(DviHashKey k1, DviHashKey k2) +{ + return strcmp((char *)k1, (char *)k2); +} + +void mdvi_hash_init(DviHashTable *hash) +{ + hash->buckets = NULL; + hash->nbucks = 0; + hash->nkeys = 0; + hash->hash_func = NULL; + hash->hash_comp = NULL; + hash->hash_free = NULL; +} + +void mdvi_hash_create(DviHashTable *hash, int size) +{ + int i; + + hash->nbucks = size; + hash->buckets = xnalloc(DviHashBucket *, size); + for(i = 0; i < size; i++) + hash->buckets[i] = NULL; + hash->hash_func = hash_string; + hash->hash_comp = hash_compare; + hash->hash_free = NULL; + hash->nkeys = 0; +} + +static DviHashBucket *hash_find(DviHashTable *hash, DviHashKey key) +{ + Ulong hval; + DviHashBucket *buck; + + hval = (hash->hash_func(key) % hash->nbucks); + + for(buck = hash->buckets[hval]; buck; buck = buck->next) + if(hash->hash_comp(buck->key, key) == 0) + break; + return buck; +} + +/* Neither keys nor data are duplicated */ +int mdvi_hash_add(DviHashTable *hash, DviHashKey key, void *data, int rep) +{ + DviHashBucket *buck = NULL; + Ulong hval; + + if(rep != MDVI_HASH_UNCHECKED) { + buck = hash_find(hash, key); + if(buck != NULL) { + if(buck->data == data) + return 0; + if(rep == MDVI_HASH_UNIQUE) + return -1; + if(hash->hash_free != NULL) + hash->hash_free(buck->key, buck->data); + } + } + if(buck == NULL) { + buck = xalloc(DviHashBucket); + buck->hvalue = hash->hash_func(key); + hval = (buck->hvalue % hash->nbucks); + buck->next = hash->buckets[hval]; + hash->buckets[hval] = buck; + hash->nkeys++; + } + + /* save key and data */ + buck->key = key; + buck->data = data; + + return 0; +} + +void *mdvi_hash_lookup(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck = hash_find(hash, key); + + return buck ? buck->data : NULL; +} + +static DviHashBucket *hash_remove(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck, *last; + Ulong hval; + + hval = hash->hash_func(key); + hval %= hash->nbucks; + + for(last = NULL, buck = hash->buckets[hval]; buck; buck = buck->next) { + if(hash->hash_comp(buck->key, key) == 0) + break; + last = buck; + } + if(buck == NULL) + return NULL; + if(last) + last->next = buck->next; + else + hash->buckets[hval] = buck->next; + hash->nkeys--; + return buck; +} + +void *mdvi_hash_remove(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck = hash_remove(hash, key); + void *data = NULL; + + if(buck) { + data = buck->data; + mdvi_free(buck); + } + return data; +} + +void *mdvi_hash_remove_ptr(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck, *last; + Ulong hval; + void *ptr; + + hval = hash->hash_func(key); + hval %= hash->nbucks; + + for(last = NULL, buck = hash->buckets[hval]; buck; buck = buck->next) { + if(buck->key == key) + break; + last = buck; + } + if(buck == NULL) + return NULL; + if(last) + last->next = buck->next; + else + hash->buckets[hval] = buck->next; + hash->nkeys--; + /* destroy the bucket */ + ptr = buck->data; + mdvi_free(buck); + return ptr; +} + +int mdvi_hash_destroy_key(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck = hash_remove(hash, key); + + if(buck == NULL) + return -1; + if(hash->hash_free) + hash->hash_free(buck->key, buck->data); + mdvi_free(buck); + return 0; +} + +void mdvi_hash_reset(DviHashTable *hash, int reuse) +{ + int i; + DviHashBucket *buck; + + /* remove all keys in the hash table */ + for(i = 0; i < hash->nbucks; i++) { + for(; (buck = hash->buckets[i]); ) { + hash->buckets[i] = buck->next; + if(hash->hash_free) + hash->hash_free(buck->key, buck->data); + mdvi_free(buck); + } + } + hash->nkeys = 0; + if(!reuse && hash->buckets) { + mdvi_free(hash->buckets); + hash->buckets = NULL; + hash->nbucks = 0; + } /* otherwise, it is left empty, ready to be reused */ +} diff --git a/backend/dvi/mdvi-lib/hash.h b/backend/dvi/mdvi-lib/hash.h new file mode 100644 index 00000000..b10afd60 --- /dev/null +++ b/backend/dvi/mdvi-lib/hash.h @@ -0,0 +1,49 @@ +#ifndef MDVI_HASH +#define MDVI_HASH + +/* Hash tables */ + + +typedef struct _DviHashBucket DviHashBucket; +typedef struct _DviHashTable DviHashTable; + +/* + * Hash tables + */ + +typedef Uchar *DviHashKey; +#define MDVI_KEY(x) ((DviHashKey)(x)) + +typedef Ulong (*DviHashFunc) __PROTO((DviHashKey key)); +typedef int (*DviHashComp) __PROTO((DviHashKey key1, DviHashKey key2)); +typedef void (*DviHashFree) __PROTO((DviHashKey key, void *data)); + + +struct _DviHashTable { + DviHashBucket **buckets; + int nbucks; + int nkeys; + DviHashFunc hash_func; + DviHashComp hash_comp; + DviHashFree hash_free; +}; +#define MDVI_EMPTY_HASH_TABLE {NULL, 0, 0, NULL, NULL, NULL} + +#define MDVI_HASH_REPLACE 0 +#define MDVI_HASH_UNIQUE 1 +#define MDVI_HASH_UNCHECKED 2 + +extern void mdvi_hash_init __PROTO((DviHashTable *)); +extern void mdvi_hash_create __PROTO((DviHashTable *, int)); +extern int mdvi_hash_add __PROTO((DviHashTable *, DviHashKey, void *, int)); +extern int mdvi_hash_destroy_key __PROTO((DviHashTable *, DviHashKey)); +extern void mdvi_hash_reset __PROTO((DviHashTable *, int)); +extern void *mdvi_hash_lookup __PROTO((DviHashTable *, DviHashKey)); +extern void *mdvi_hash_remove __PROTO((DviHashTable *, DviHashKey)); +extern void *mdvi_hash_remove_ptr __PROTO((DviHashTable *, DviHashKey)); + +#define mdvi_hash_flush(h) mdvi_hash_reset((h), 1) +#define mdvi_hash_destroy(h) mdvi_hash_reset((h), 0) + +#endif + diff --git a/backend/dvi/mdvi-lib/list.c b/backend/dvi/mdvi-lib/list.c new file mode 100644 index 00000000..eb73a178 --- /dev/null +++ b/backend/dvi/mdvi-lib/list.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "common.h" + +void listh_init(ListHead *head) +{ + head->head = head->tail = NULL; + head->count = 0; +} + +void listh_prepend(ListHead *head, List *list) +{ + list->prev = NULL; + list->next = head->head; + if(head->head) + head->head->prev = list; + head->head = list; + if(!head->tail) + head->tail = list; + head->count++; +} + +void listh_append(ListHead *head, List *list) +{ + list->next = NULL; + list->prev = head->tail; + if(head->tail) + head->tail->next = list; + else + head->head = list; + head->tail = list; + head->count++; +} + +void listh_add_before(ListHead *head, List *at, List *list) +{ + if(at == head->head || head->head == NULL) + listh_prepend(head, list); + else { + list->next = at; + list->prev = at->prev; + at->prev = list; + head->count++; + } +} + +void listh_add_after(ListHead *head, List *at, List *list) +{ + if(at == head->tail || !head->tail) + listh_append(head, list); + else { + list->prev = at; + list->next = at->next; + at->next = list; + head->count++; + } +} + +void listh_remove(ListHead *head, List *list) +{ + if(list == head->head) { + head->head = list->next; + if(head->head) + head->head->prev = NULL; + } else if(list == head->tail) { + head->tail = list->prev; + if(head->tail) + head->tail->next = NULL; + } else { + list->next->prev = list->prev; + list->prev->next = list->next; + } + if(--head->count == 0) + head->head = head->tail = NULL; +} + +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 00000000..327e61fe --- /dev/null +++ b/backend/dvi/mdvi-lib/mdvi.h @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_DVI_H +#define _MDVI_DVI_H 1 + +#include <stdio.h> +#include <sys/types.h> +#include <math.h> + +#include "sysdeps.h" +#include "bitmap.h" +#include "common.h" +#include "defaults.h" +#include "dviopcodes.h" + +typedef struct _DviGlyph DviGlyph; +typedef struct _DviDevice DviDevice; +typedef struct _DviFontChar DviFontChar; +typedef struct _DviFontRef DviFontRef; +typedef struct _DviFontInfo DviFontInfo; +typedef struct _DviFont DviFont; +typedef struct _DviState DviState; +typedef struct _DviPageSpec *DviPageSpec; +typedef struct _DviParams DviParams; +typedef struct _DviBuffer DviBuffer; +typedef struct _DviContext DviContext; +typedef struct _DviRange DviRange; +typedef struct _DviColorPair DviColorPair; +typedef struct _DviSection DviSection; +typedef struct _TFMChar TFMChar; +typedef struct _TFMInfo TFMInfo; +typedef struct _DviFontSearch DviFontSearch; +/* this is an opaque type */ +typedef struct _DviFontClass DviFontClass; + +typedef void (*DviFreeFunc) __PROTO((void *)); +typedef void (*DviFree2Func) __PROTO((void *, void *)); + +typedef Ulong DviColor; + +#ifdef TRUE +#undef TRUE +#endif +#ifdef FALSE +#undef FALSE +#endif + +typedef enum { + FALSE = 0, + TRUE = 1 +} DviBool; + +#include "hash.h" +#include "paper.h" + +/* + * information about a page: + * pagenum[0] = offset to BOP + * pagenum[1], ..., pagenum[10] = TeX \counters + */ +typedef long PageNum[11]; + +/* this structure contains the platform-specific information + * required to interpret a DVI file */ + +typedef void (*DviGlyphDraw) __PROTO((DviContext *context, + DviFontChar *glyph, + int x, int y)); + +typedef void (*DviRuleDraw) __PROTO((DviContext *context, + int x, int y, + Uint width, Uint height, int fill)); + +typedef int (*DviColorScale) __PROTO((void *device_data, + Ulong *pixels, + int npixels, + Ulong foreground, + Ulong background, + double gamma, + int density)); +typedef void *(*DviCreateImage) __PROTO((void *device_data, + Uint width, + Uint height, + Uint bpp)); +typedef void (*DviFreeImage) __PROTO((void *image)); +typedef void (*DviPutPixel) __PROTO((void *image, int x, int y, Ulong color)); +typedef void (*DviDevDestroy) __PROTO((void *data)); +typedef void (*DviRefresh) __PROTO((DviContext *dvi, void *device_data)); +typedef void (*DviSetColor) __PROTO((void *device_data, Ulong, Ulong)); +typedef void (*DviPSDraw) __PROTO((DviContext *context, + const char *filename, + int x, int y, + Uint width, Uint height)); + +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; + DviPSDraw draw_ps; + 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, const char *)); + +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 00000000..5a153955 --- /dev/null +++ b/backend/dvi/mdvi-lib/pagesel.c @@ -0,0 +1,491 @@ +/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> + +#include "mdvi.h" +#include "private.h" + +char *program_name = "page"; + +struct _DviPageSpec { + DviRange *ranges; + int nranges; +}; + +DviRange *mdvi_parse_range(const char *format, DviRange *limit, int *nitems, char **endptr) +{ + int quoted; + int size; + int curr; + int done; + int lower; + int upper; + int type; + char * cp; + char * copy; + char * text; + DviRange one; + DviRange *range; + + quoted = (*format == '{'); + if(quoted) format++; + + size = 0; + curr = 0; + range = NULL; + copy = mdvi_strdup(format); + done = 0; + lower = 0; + upper = 0; + type = MDVI_RANGE_UNBOUNDED; + + if(limit) { + switch(limit->type) { + case MDVI_RANGE_BOUNDED: + lower = limit->from; + upper = limit->to; + break; + case MDVI_RANGE_UPPER: + lower = INT_MIN; + upper = limit->to; + break; + case MDVI_RANGE_LOWER: + lower = limit->from; + upper = INT_MAX; + break; + case MDVI_RANGE_UNBOUNDED: + lower = INT_MIN; + upper = INT_MAX; + break; + } + type = limit->type; + } else { + lower = INT_MIN; + upper = INT_MAX; + type = MDVI_RANGE_UNBOUNDED; + } + one.type = type; + one.from = lower; + one.to = upper; + one.step = 1; + for(cp = text = copy; !done; cp++) { + char *p; + int f, t, s; + int ch; + int this_type; + int lower_given = 0; + int upper_given = 0; + + if(*cp == 0 || *cp == '.' || (*cp == '}' && quoted)) + done = 1; + else if(*cp != ',') + continue; + + if(text == cp) + continue; + ch = *cp; + *cp = 0; + f = lower; + t = upper; + s = 1; + + p = strchr(text, ':'); + if(p) *p++ = 0; + if(*text) { + lower_given = 1; + f = strtol(text, NULL, 0); + } + if(p == NULL) { + if(lower_given) { + upper_given = 1; + t = f; s = 1; + } + goto finish; + } + text = p; + p = strchr(text, ':'); + if(p) *p++ = 0; + if(*text) { + upper_given = 1; + t = strtol(text, NULL, 0); + } + if(p == NULL) + goto finish; + text = p; + if(*text) + s = strtol(text, NULL, 0); +finish: + if(lower_given && upper_given) + this_type = MDVI_RANGE_BOUNDED; + else if(lower_given) { + if(!RANGE_HAS_UPPER(type)) + this_type = MDVI_RANGE_LOWER; + else + this_type = MDVI_RANGE_BOUNDED; + t = upper; + } else if(upper_given) { + if(RANGE_HAS_UPPER(one.type)) { + one.to++; + this_type = MDVI_RANGE_BOUNDED; + } else { + one.to = lower; + if(!RANGE_HAS_LOWER(type)) + this_type = MDVI_RANGE_UPPER; + else + this_type = MDVI_RANGE_BOUNDED; + } + f = one.to; + } else { + this_type = type; + f = lower; + t = upper; + } + one.type = this_type; + one.to = t; + one.from = f; + one.step = s; + + if(curr == size) { + size += 8; + range = mdvi_realloc(range, size * sizeof(DviRange)); + } + memcpy(&range[curr++], &one, sizeof(DviRange)); + *cp = ch; + text = cp + 1; + } + if(done) + cp--; + if(quoted && *cp == '}') + cp++; + if(endptr) + *endptr = (char *)format + (cp - copy); + if(curr && curr < size) + range = mdvi_realloc(range, curr * sizeof(DviRange)); + *nitems = curr; + mdvi_free(copy); + return range; +} + +DviPageSpec *mdvi_parse_page_spec(const char *format) +{ + /* + * a page specification looks like this: + * '{'RANGE_SPEC'}' for a DVI spec + * '{'RANGE_SPEC'}' '.' ... for a TeX spec + */ + DviPageSpec *spec; + DviRange *range; + int count; + int i; + char *ptr; + + spec = xnalloc(struct _DviPageSpec *, 11); + for(i = 0; i < 11; i++) + spec[i] = NULL; + + /* check what kind of spec we're parsing */ + if(*format != '*') { + range = mdvi_parse_range(format, NULL, &count, &ptr); + if(ptr == format) { + if(range) mdvi_free(range); + mdvi_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) + mdvi_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) + mdvi_warning(_("more than 10 counters in page specification\n")); + else if(*ptr) + mdvi_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 00000000..42cbcd3f --- /dev/null +++ b/backend/dvi/mdvi-lib/paper.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <string.h> + +#include "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 00000000..d42ee079 --- /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 00000000..a5791869 --- /dev/null +++ b/backend/dvi/mdvi-lib/pk.c @@ -0,0 +1,570 @@ + +/* Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * 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 <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <math.h> + +#include "mdvi.h" +#include "private.h" + +#define PK_ID 89 +#define PK_CMD_START 240 +#define PK_X1 240 +#define PK_X2 241 +#define PK_X3 242 +#define PK_X4 243 +#define PK_Y 244 +#define PK_POST 245 +#define PK_NOOP 246 +#define PK_PRE 247 + +#define PK_DYN_F(x) (((x) >> 4) & 0xf) +#define PK_PACKED(x) (PK_DYN_F(x) != 14) + +static int pk_load_font __PROTO((DviParams *, DviFont *)); +static int pk_font_get_glyph __PROTO((DviParams *, DviFont *, int)); + +static int pk_auto_generate = 1; /* this is ON by default */ + +typedef struct { + char currbyte; + char nybpos; + int dyn_f; +} pkread; + +static char *pk_lookup __PROTO((const char *, Ushort *, Ushort *)); +static char *pk_lookupn __PROTO((const char *, Ushort *, Ushort *)); + +/* only symbols exported by this file */ +DviFontInfo pk_font_info = { + "PK", + 0, /* scaling not supported natively */ + pk_load_font, + pk_font_get_glyph, + mdvi_shrink_glyph, + mdvi_shrink_glyph_grey, + NULL, /* free */ + NULL, /* reset */ + pk_lookup, /* lookup */ + kpse_pk_format, + NULL +}; + +DviFontInfo pkn_font_info = { + "PKN", + 0, /* scaling not supported natively */ + pk_load_font, + pk_font_get_glyph, + mdvi_shrink_glyph, + mdvi_shrink_glyph_grey, + NULL, /* free */ + NULL, /* reset */ + pk_lookupn, /* lookup */ + kpse_pk_format, + NULL +}; + +static char *pk_lookup(const char *name, Ushort *hdpi, Ushort *vdpi) +{ + kpse_glyph_file_type type; + char *filename; + + if(pk_auto_generate == 0) { + kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_cmdline); + pk_auto_generate = 1; + } + filename = kpse_find_glyph(name, Max(*hdpi, *vdpi), + kpse_pk_format, &type); + if(filename && type.source == kpse_glyph_source_fallback) { + mdvi_free(filename); + filename = NULL; + } else if(filename) { + *hdpi = *vdpi = type.dpi; + } + return filename; +} + +static char *pk_lookupn(const char *name, Ushort *hdpi, Ushort *vdpi) +{ + kpse_glyph_file_type type; + char *filename; + + if(pk_auto_generate) { + kpse_set_program_enabled(kpse_pk_format, 0, kpse_src_cmdline); + pk_auto_generate = 0; + } + filename = kpse_find_glyph(name, Max(*hdpi, *vdpi), + kpse_pk_format, &type); + if(filename && type.source == kpse_glyph_source_fallback) { + mdvi_free(filename); + filename = NULL; + } else if(filename) { + *hdpi = *vdpi = type.dpi; + } + return filename; +} + +static inline int pk_get_nyb(FILE *p, pkread *pk) +{ + unsigned t; + int nb; + char c; + + t = c = pk->currbyte; + nb = pk->nybpos; + + switch(nb) { + case 0: + c = pk->currbyte = fuget1(p); + t = (c >> 4); + break; + case 1: + t = c; + break; + } + pk->nybpos = !nb; + return (t & 0xf); +} + +/* + * this is a bit cumbersome because we have to pass around + * the `pkread' data... + */ +static int pk_packed_num(FILE *p, pkread *pkr, int *repeat) +{ + int i, j; + int dyn_f = pkr->dyn_f; + + i = pk_get_nyb(p, pkr); + if(i == 0) { + do { + j = pk_get_nyb(p, pkr); + i++; + } while(j == 0); + while(i-- > 0) + j = (j << 4) + pk_get_nyb(p, pkr); + return (j - 15 + ((13 - dyn_f) << 4) + + dyn_f); + } else if(i <= dyn_f) + return i; + else if(i < 14) + return ((i - dyn_f - 1) << 4) + + pk_get_nyb(p, pkr) + dyn_f + 1; + else { + *repeat = 1; + if(i == 14) + *repeat = pk_packed_num(p, pkr, repeat); + return pk_packed_num(p, pkr, repeat); + } +} + +#define ROUND(x,y) (((x) + (y) - 1) / (y)) + +static BITMAP *get_bitmap(FILE *p, int w, int h, int flags) +{ + int i, j; + BmUnit *ptr; + BITMAP *bm; + int bitpos; + int currch; + + flags = 0; /* shut up that compiler */ + bitpos = -1; + if((bm = bitmap_alloc(w, h)) == NULL) + return NULL; + DEBUG((DBG_BITMAPS, "get_bitmap(%d,%d,%d): reading raw bitmap\n", + w, h, flags)); + ptr = bm->data; + currch = 0; + for(i = 0; i < h; i++) { + BmUnit mask; + + mask = FIRSTMASK; + for(j = 0; j < w; j++) { + if(bitpos < 0) { + currch = fuget1(p); + bitpos = 7; + } + if(currch & (1 << bitpos)) + *ptr |= mask; + bitpos--; + if(mask == LASTMASK) { + ptr++; + mask = FIRSTMASK; + } else + NEXTMASK(mask); + } + ptr = bm_offset(ptr, bm->stride); + } + return bm; +} + +static BITMAP *get_packed(FILE *p, int w, int h, int flags) +{ + int inrow, count; + int row; + BITMAP *bm; + int repeat_count; + int paint; + pkread pkr; + + pkr.nybpos = 0; + pkr.currbyte = 0; + pkr.dyn_f = PK_DYN_F(flags); + paint = !!(flags & 0x8); + + repeat_count = 0; + row = 0; + inrow = w; + if((bm = bitmap_alloc(w, h)) == NULL) + return NULL; + DEBUG((DBG_BITMAPS, "get_packed(%d,%d,%d): reading packed glyph\n", + w, h, flags)); + while(row < h) { + int i = 0; + + count = pk_packed_num(p, &pkr, &i); + if(i > 0) { + if(repeat_count) + fprintf(stderr, "second repeat count for this row (had %d and got %d)\n", + repeat_count, i); + repeat_count = i; + } + + if(count >= inrow) { + Uchar *r, *t; + BmUnit *a, mask; + + /* first finish current row */ + if(paint) + bitmap_set_row(bm, row, w - inrow, inrow, paint); + /* now copy it as many times as required */ + r = (Uchar *)bm->data + row * bm->stride; + while(repeat_count-- > 0) { + t = r + bm->stride; + /* copy entire lines */ + memcpy(t, r, bm->stride); + r = t; + row++; + } + repeat_count = 0; + /* count first row we drew */ + row++; + /* update run count */ + count -= inrow; + /* now r points to the beginning of the last row we finished */ + if(paint) + mask = ~((BmUnit)0); + else + mask = 0; + /* goto next row */ + a = (BmUnit *)(r + bm->stride); + /* deal with entirely with/black rows */ + while(count >= w) { + /* count number of atoms in a row */ + i = ROUND(w, BITMAP_BITS); + while(i-- > 0) + *a++ = mask; + count -= w; + row++; + } + inrow = w; + } + if(count > 0) + bitmap_set_row(bm, row, w - inrow, count, paint); + inrow -= count; + paint = !paint; + } + if(row != h || inrow != w) { + mdvi_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) { + mdvi_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: + mdvi_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) { + mdvi_error(_("%s: unexpected end of file (no postamble)\n"), + font->fontname); + goto error; + } + while((flag_byte = fuget1(p)) != EOF) { + if(flag_byte != PK_NOOP) { + mdvi_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: + mdvi_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 00000000..9f89dc70 --- /dev/null +++ b/backend/dvi/mdvi-lib/private.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_PRIVATE_H +#define _MDVI_PRIVATE_H 1 + +#define HAVE_PROTOTYPES 1 + +#if STDC_HEADERS +# /* kpathsea's headers (wrongly!) redefine strchr() and strrchr() to +# non ANSI C functions if HAVE_STRCHR and HAVE_STRRCHR are not defined. +# */ +# ifndef HAVE_STRCHR +# define HAVE_STRCHR +# endif +# ifndef HAVE_STRRCHR +# define HAVE_STRRCHR +# endif +#endif + +#include <kpathsea/debug.h> +#include <kpathsea/tex-file.h> +#include <kpathsea/tex-glyph.h> +#include <kpathsea/cnf.h> +#include <kpathsea/proginit.h> +#include <kpathsea/progname.h> +#include <kpathsea/tex-make.h> +#include <kpathsea/lib.h> + +#define ISSP(p) (*(p) == ' ' || *(p) == '\t') +#define SKIPSP(p) while(ISSP(p)) p++ +#define SKIPNSP(p) while(*(p) && !ISSP(p)) p++ + +#ifdef ENABLE_NLS +#include <libintl.h> +#define _(x) gettext(x) +#define _G(x) x +#else +#define _(x) x +#define _G(x) x +#endif /* ENABLE_NLS */ + +#if defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2 +#define _BREAKPOINT() do { __asm__ __volatile__ ("int $03"); } while(0) +#elif defined (__alpha__) && defined (__GNUC__) && __GNUC__ >= 2 +#define _BREAKPOINT() do { __asm__ __volatile__ ("bpt"); } while(0) +#else /* !__i386__ && !__alpha__ */ +#define _BREAKPOINT() +#endif /* __i386__ */ + +#endif /* _MDVI_PRIVATE_H */ diff --git a/backend/dvi/mdvi-lib/setup.c b/backend/dvi/mdvi-lib/setup.c new file mode 100644 index 00000000..ba0c545d --- /dev/null +++ b/backend/dvi/mdvi-lib/setup.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "mdvi.h" +#include "private.h" + +void mdvi_init_kpathsea(const char *program, + const char *mfmode, const char *font, int dpi, + const char *texmfcnf) +{ + 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); + if (texmfcnf != NULL) + xputenv("TEXMFCNF", texmfcnf); +} + diff --git a/backend/dvi/mdvi-lib/sp-epsf.c b/backend/dvi/mdvi-lib/sp-epsf.c new file mode 100644 index 00000000..703a9c8c --- /dev/null +++ b/backend/dvi/mdvi-lib/sp-epsf.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* postscript specials */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mdvi.h" +#include "private.h" + +typedef struct { + double ox; + double oy; + double bw; + double bh; + double angle; +} EpsfBox; + +#define LLX 0 +#define LLY 1 +#define URX 2 +#define URY 3 +#define RWI 4 +#define RHI 5 +#define HOFF 6 +#define VOFF 7 +#define HSIZE 8 +#define VSIZE 9 +#define HSCALE 10 +#define VSCALE 11 +#define ANGLE 12 +#define CLIP 13 + +void epsf_special __PROTO((DviContext *dvi, char *prefix, char *arg)); + +/* Note: the given strings are modified in place */ +static char *parse_epsf_special(EpsfBox *box, char **ret, + char *prefix, char *arg) +{ + static struct { + char *name; + int has_arg; + char *value; + } keys[] = { + {"llx", 1, "0"}, + {"lly", 1, "0"}, + {"urx", 1, "0"}, + {"ury", 1, "0"}, + {"rwi", 1, "0"}, + {"rhi", 1, "0"}, + {"hoffset", 1, "0"}, + {"voffset", 1, "0"}, + {"hsize", 1, "612"}, + {"vsize", 1, "792"}, + {"hscale", 1, "100"}, + {"vscale", 1, "100"}, + {"angle", 1, "0"}, + {"clip", 0, "0"} + }; +#define NKEYS (sizeof(keys) / sizeof(keys[0])) + char *ptr; + char *filename; + int quoted; + double value[NKEYS]; + Uchar present[NKEYS]; + Buffer buffer; + char *name; + int i; + double originx; + double originy; + double hsize; + double vsize; + double hscale; + double vscale; + + /* this special has the form + * ["]file.ps["] [key=valye]* + */ + + /* scan the filename */ + while(*arg == ' ' || *arg == '\t') + arg++; + + /* make a copy of the string */ + ptr = arg; + + if(*ptr == '"') + for(name = ++ptr; *ptr && *ptr != '"'; ptr++); + else + for(name = ptr; *ptr && *ptr != ' ' && *ptr != '\t'; ptr++); + if(ptr == name) + return NULL; + *ptr++ = 0; + filename = name; + + /* reset values to defaults */ + for(i = 0; i < NKEYS; i++) { + value[i] = atof(keys[i].value); + present[i] = 0; + } + + buff_init(&buffer); + buff_add(&buffer, "@beginspecial ", 0); + + quoted = 0; + while(*ptr) { + const char *keyname; + char *val; + char *p; + + while(*ptr == ' ' || *ptr == '\t') + ptr++; + keyname = ptr; + + /* get the whole key=value pair */ + for(; *ptr && *ptr != ' ' && *ptr != '\t'; ptr++); + + if(*ptr) *ptr++ = 0; + /* now we shouldn't touch `ptr' anymore */ + + /* now work on this pair */ + p = strchr(keyname, '='); + if(p == NULL) + val = NULL; + else { + *p++ = 0; + if(*p == '"') { + val = ++p; + /* skip until closing quote */ + while(*p && *p != '"') + p++; + if(*p != '"') + mdvi_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) { + mdvi_warning(_("%s: unknown key `%s' ignored\n"), + filename, keyname); + continue; + } + if(keys[i].has_arg && val == NULL) { + mdvi_warning(_("%s: no argument for key `%s', using defaults\n"), + filename, keyname); + val = keys[i].value; + } else if(!keys[i].has_arg && val) { + mdvi_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; + char *psfile; + char *tmp; + EpsfBox box = {0, 0, 0, 0}; + int x, y; + int w, h; + double xf, vf; + struct stat buf; + + file = parse_epsf_special(&box, &special, prefix, arg); + if (file != NULL) + mdvi_free (special); + + xf = dvi->params.dpi * dvi->params.mag / (72.0 * dvi->params.hshrink); + vf = dvi->params.vdpi * dvi->params.mag / (72.0 * dvi->params.vshrink); + w = FROUND(box.bw * xf); + h = FROUND(box.bh * vf); + x = FROUND(box.ox * xf) + dvi->pos.hh; + y = FROUND(box.oy * vf) + dvi->pos.vv - h + 1; + + if (!file || !dvi->device.draw_ps) { + dvi->device.draw_rule (dvi, x, y, w, h, 0); + return; + } + + if (file[0] == '/') { /* Absolute path */ + if (stat (file, &buf) == 0) + dvi->device.draw_ps (dvi, file, x, y, w, h); + else + dvi->device.draw_rule (dvi, x, y, w, h, 0); + return; + } + + tmp = mdvi_strrstr (dvi->filename, "/"); + if (tmp) { /* Document directory */ + int path_len = strlen (dvi->filename) - strlen (tmp + 1); + int file_len = strlen (file); + + psfile = mdvi_malloc (path_len + file_len + 1); + psfile[0] = '\0'; + strncat (psfile, dvi->filename, path_len); + strncat (psfile, file, file_len); + + if (stat (psfile, &buf) == 0) { + dvi->device.draw_ps (dvi, psfile, x, y, w, h); + mdvi_free (psfile); + + return; + } + + mdvi_free (psfile); + } + + psfile = mdvi_build_path_from_cwd (file); + if (stat (psfile, &buf) == 0) { /* Current working dir */ + dvi->device.draw_ps (dvi, psfile, x, y, w, h); + mdvi_free (psfile); + + return; + } + + mdvi_free (psfile); + + psfile = kpse_find_pict (file); + if (psfile) { /* kpse */ + dvi->device.draw_ps (dvi, psfile, x, y, w, h); + } else { + dvi->device.draw_rule(dvi, x, y, w, h, 0); + } + + free (psfile); +} diff --git a/backend/dvi/mdvi-lib/special.c b/backend/dvi/mdvi-lib/special.c new file mode 100644 index 00000000..e4832544 --- /dev/null +++ b/backend/dvi/mdvi-lib/special.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <ctype.h> +#include <string.h> + +#include "mdvi.h" +#include "private.h" + +#if defined(WITH_REGEX_SPECIALS) && defined(HAVE_REGEX_H) +#include <regex.h> +#endif + +typedef struct _DviSpecial { + struct _DviSpecial *next; + struct _DviSpecial *prev; + char *label; + char *prefix; + size_t plen; +#ifdef WITH_REGEX_SPECIALS + regex_t reg; + int has_reg; +#endif + DviSpecialHandler handler; +} DviSpecial; + +static ListHead specials = {NULL, NULL, 0}; + +#define SPECIAL(x) \ + void x __PROTO((DviContext *, const char *, const char *)) + +static SPECIAL(sp_layer); +extern SPECIAL(epsf_special); +extern SPECIAL(do_color_special); + +static struct { + char *label; + char *prefix; + char *regex; + DviSpecialHandler handler; +} builtins[] = { + {"Layers", "layer", NULL, sp_layer}, + {"EPSF", "psfile", NULL, epsf_special} +}; +#define NSPECIALS (sizeof(builtins) / sizeof(builtins[0])) +static int registered_builtins = 0; + +static void register_builtin_specials(void) +{ + int i; + + ASSERT(registered_builtins == 0); + registered_builtins = 1; + 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 */); +} + +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 + mdvi_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 00000000..c77d7651 --- /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 00000000..bc910ba6 --- /dev/null +++ b/backend/dvi/mdvi-lib/t1.c @@ -0,0 +1,630 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Type1 font support for MDVI + * + * We use T1lib only as a rasterizer, not to draw glyphs. + */ + +#include <config.h> +#include "mdvi.h" + +#ifdef WITH_TYPE1_FONTS + +#include <stdio.h> +#include <t1lib.h> +#include "private.h" + +static int t1lib_initialized = 0; + +typedef struct t1info { + struct t1info *next; + struct t1info *prev; + char *fontname; /* (short) name of this font */ + int t1id; /* T1lib's id for this font */ + int hasmetrics; /* have we processed this font? */ + TFMInfo *tfminfo; /* TFM data is shared */ + DviFontMapInfo mapinfo; + DviEncoding *encoding; +} T1Info; + +static void t1_font_remove __PROTO((T1Info *)); +static int t1_load_font __PROTO((DviParams *, DviFont *)); +static int t1_font_get_glyph __PROTO((DviParams *, DviFont *, int)); +static void t1_font_shrink_glyph + __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +static void t1_free_data __PROTO((DviFont *)); +static void t1_reset_font __PROTO((DviFont *)); +static char *t1_lookup_font __PROTO((const char *, Ushort *, Ushort *)); + +/* only symbol exported by this file */ +DviFontInfo t1_font_info = { + "Type1", + 1, /* scaling supported by format */ + t1_load_font, + t1_font_get_glyph, + t1_font_shrink_glyph, + mdvi_shrink_glyph_grey, + t1_free_data, + t1_reset_font, + t1_lookup_font, /* lookup */ + kpse_type1_format, + NULL +}; + +/* this seems good enough for most DVI files */ +#define T1_HASH_SIZE 31 + +/* If these parameters change, we must delete all size information + * in all fonts, and reset the device resolutions in T1lib */ +static int t1lib_xdpi = -1; +static int t1lib_ydpi = -1; + +static ListHead t1fonts = {NULL, NULL, 0}; +static DviHashTable t1hash; + +/* Type1 fonts need their own `lookup' function. Here is how it works: + * First we try to find the font by its given name. If that fails, we + * query the font maps. A typical font map entry may contain the line + * + * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc <ptmr + * + * which means: If you're looking for the font `ptmr8rn' load `Times-Roman' + * which is in `ptmr' instead, and extend it by 0.82 points, then reencode + * it with the vector TeXBase1Encoding from the file `8r.enc'. This will + * fail if the entry looks like this: + * + * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc + * + * because to deal with this we would need to be able to locate the font file + * for the `Times-Roman' font ourselves, and that's beyond the scope of mdvi. + * But hey, we tried hard. + */ +char *t1_lookup_font(const char *name, Ushort *hdpi, Ushort *vdpi) +{ + char *filename; + char *newname; + const char *ext; + DviFontMapInfo info; + + DEBUG((DBG_TYPE1, "(t1) looking for `%s'\n", name)); + + /* first let's try the font we were asked for */ + filename = kpse_find_file(name, kpse_type1_format, 1); + if(filename != NULL) { + /* we got it */ + return filename; + } + + DEBUG((DBG_TYPE1, "(t1) %s: not found, querying font maps\n", name)); + /* now query the fontmap */ + if(mdvi_query_fontmap(&info, name) < 0) { + /* it's not there either */ + return NULL; + } + + /* check what we got */ + if(info.fullfile) { + DEBUG((DBG_TYPE1, "(t1) %s: found `%s' (cached)\n", + name, info.fullfile)); + /* this is a cached lookup */ + return mdvi_strdup(info.fullfile); + } + + /* no file associated to this font? */ + if(info.fontfile == NULL) + return info.psname ? mdvi_ps_find_font(info.psname) : NULL; + + /* let's extract the extension */ + ext = file_extension(info.fontfile); + if(ext && !STREQ(ext, "pfa") && !STREQ(ext, "pfb")) { + DEBUG((DBG_TYPE1, + "(t1) %s: associated name `%s' is not Type1\n", + name, info.fontfile)); + /* it's not a Type1 font */ + return NULL; + } + + /* get the `base' name */ + if(ext) { + newname = mdvi_strdup(name); + newname[ext - info.fontfile - 1] = 0; + } else + newname = (char *)name; /* we don't modify this */ + + /* look it up */ + DEBUG((DBG_TYPE1, "(t1) looking for `%s' on behalf of `%s'\n", + newname, name)); + filename = kpse_find_file(newname, kpse_type1_format, 1); + + /* we don't need this anymore */ + if(newname != name) + mdvi_free(newname); + if(filename == NULL) { + DEBUG((DBG_TYPE1, "(t1) %s: not found\n", name)); + return NULL; + } + + DEBUG((DBG_TYPE1, "(t1) %s: found as `%s'\n", name, filename)); + /* got it! let's remember this */ + mdvi_add_fontmap_file(name, filename); + return filename; +} + +static void t1_reset_resolution(int xdpi, int ydpi) +{ + int i; + int nfonts; + + DEBUG((DBG_TYPE1, "(t1) resetting device resolution (current: (%d,%d))\n", + t1lib_xdpi, t1lib_ydpi)); +#if T1LIB_VERSION < 5 + nfonts = T1_Get_no_fonts(); +#else + nfonts = T1_GetNoFonts(); +#endif + + for(i = 0; i < nfonts; i++) + T1_DeleteAllSizes(i); + /* reset device resolutions */ + if(T1_SetDeviceResolutions((float)xdpi, (float)ydpi) < 0) + mdvi_warning(_("(t1) failed to reset device resolution\n")); + else + DEBUG((DBG_TYPE1, + "(t1) reset successful, new resolution is (%d, %d)\n", + xdpi, ydpi)); + t1lib_xdpi = xdpi; + t1lib_ydpi = ydpi; +} + +static void t1_reset_font(DviFont *font) +{ + T1Info *info = (T1Info *)font->private; + + if(info == NULL) + return; + DEBUG((DBG_FONTS, "(t1) resetting font `%s'\n", font->fontname)); + /* just mark the font as not having metric info. It will be reset + * automatically later */ + info->hasmetrics = 0; +} + +static void t1_transform_font(T1Info *info) +{ + if(!info->hasmetrics && info->encoding != NULL) { + DEBUG((DBG_TYPE1, "(t1) %s: encoding with vector `%s'\n", + info->fontname, info->encoding->name)); + T1_DeleteAllSizes(info->t1id); + if(T1_ReencodeFont(info->t1id, info->encoding->vector) < 0) + mdvi_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) +{ + int w, h, pad; + + w = GLYPH_WIDTH(glyph); + h = GLYPH_HEIGHT(glyph); + + if(!w || !h) + return MDVI_GLYPH_EMPTY; + + pad = T1_GetBitmapPad(); + return bitmap_convert_lsb8((unsigned char *)glyph->bits, w, h, ROUND(w, pad) * (pad >> 3)); +} + +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 00000000..f37de0be --- /dev/null +++ b/backend/dvi/mdvi-lib/tfm.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mdvi.h" +#include "private.h" + +static int tfm_load_font __PROTO((DviParams *, DviFont *)); +static int tfm_font_get_glyph __PROTO((DviParams *, DviFont *, int)); + +DviFontInfo tfm_font_info = { + "TFM", + 0, /* scaling not supported by format */ + tfm_load_font, + tfm_font_get_glyph, + mdvi_shrink_box, + mdvi_shrink_box, + NULL, /* free */ + NULL, /* reset */ + NULL, /* lookup */ + kpse_tfm_format, + NULL +}; + +DviFontInfo ofm_font_info = { + "OFM", + 0, /* scaling not supported by format */ + tfm_load_font, + tfm_font_get_glyph, + mdvi_shrink_box, + mdvi_shrink_box, + NULL, /* free */ + NULL, /* reset */ + NULL, /* lookup */ + kpse_ofm_format, + NULL +}; + +DviFontInfo afm_font_info = { + "AFM", + 0, /* scaling not supported by format */ + tfm_load_font, + tfm_font_get_glyph, + mdvi_shrink_box, + mdvi_shrink_box, + NULL, /* free */ + NULL, /* reset */ + NULL, /* lookup */ + kpse_afm_format, + NULL +}; + +#define TYPENAME(font) \ + ((font)->search.info ? (font)->search.info : "none") + +/* + * Although it does not seem that way, this conversion is independent of the + * shrinking factors, within roundoff (that's because `conv' and `vconv' + * have already been scaled by hshrink and vshrink, repsectively). We + * should really use `dviconv' and `dvivconv', but I'm not so sure those + * should be moved to the DviParams structure. + */ +#define XCONV(x) FROUND(params->conv * (x) * params->hshrink) +#define YCONV(y) FROUND(params->vconv * (y) * params->vshrink) + +/* this is used quite often in several places, so I made it standalone */ +int get_tfm_chars(DviParams *params, DviFont *font, TFMInfo *info, int loaded) +{ + Int32 z, alpha, beta; + int n; + DviFontChar *ch; + TFMChar *ptr; + + n = info->hic - info->loc + 1; + if(n != FONT_GLYPH_COUNT(font)) { + font->chars = mdvi_realloc(font->chars, + n * sizeof(DviFontChar)); + } + font->loc = info->loc; + font->hic = info->hic; + ch = font->chars; + ptr = info->chars; + + /* Prepare z, alpha and beta for TFM width computation */ + TFMPREPARE(font->scale, z, alpha, beta); + + /* get the character metrics */ + for(n = info->loc; n <= info->hic; ch++, ptr++, n++) { + int a, b, c, d; + + ch->offset = ptr->present; + if(ch->offset == 0) + continue; + /* this is what we came here for */ + ch->tfmwidth = TFMSCALE(z, ptr->advance, alpha, beta); + /* scale all other TFM units (so they are in DVI units) */ + a = TFMSCALE(z, ptr->left, alpha, beta); + b = TFMSCALE(z, ptr->right, alpha, beta); + c = TFMSCALE(z, ptr->height, alpha, beta); + d = TFMSCALE(z, ptr->depth, alpha, beta); + + /* now convert to unscaled pixels */ + ch->width = XCONV(b - a); + ch->height = YCONV(c - d); + if(ch->height < 0) ch->height = -ch->height; + ch->x = XCONV(a); + ch->y = YCONV(c); + /* + * the offset is not used, but we might as well set it to + * something meaningful (and it MUST be non-zero) + */ + ch->flags = 0; + ch->code = n; + ch->glyph.data = NULL; + ch->grey.data = NULL; + ch->shrunk.data = NULL; + ch->loaded = loaded; + } + + return 0; +} + +/* + * We use this function as a last resort to find the character widths in a + * font The DVI rendering code can correctly skip over a glyph if it knows + * its TFM width, which is what we try to find here. + */ +static int tfm_load_font(DviParams *params, DviFont *font) +{ + TFMInfo *tfm; + int type; + + switch(font->search.info->kpse_type) { + case kpse_tfm_format: + type = DviFontTFM; + break; + case kpse_afm_format: + type = DviFontAFM; + break; + case kpse_ofm_format: + type = DviFontOFM; + break; + default: + return -1; + } + + /* we don't need this */ + if(font->in) { + fclose(font->in); + font->in = NULL; + } + tfm = get_font_metrics(font->fontname, type, font->filename); + if(tfm == NULL) + return -1; + + if(tfm->checksum && font->checksum && tfm->checksum != font->checksum) { + mdvi_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 00000000..73ebf26a --- /dev/null +++ b/backend/dvi/mdvi-lib/tfmfile.c @@ -0,0 +1,747 @@ +/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdio.h> /* tex-file.h needs this */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mdvi.h" +#include "private.h" + +#ifdef WITH_AFM_FILES +#undef TRUE +#undef FALSE +#include "afmparse.h" +#endif + +typedef struct tfmpool { + struct tfmpool *next; + struct tfmpool *prev; + char *short_name; + int links; + TFMInfo tfminfo; +} TFMPool; + +static ListHead tfmpool = {NULL, NULL, 0}; +static DviHashTable tfmhash; + +#define TFM_HASH_SIZE 31 + +#ifdef WORD_LITTLE_ENDIAN +static inline void swap_array(Uint32 *ptr, int n) +{ + Uint32 i; + + while(n-- > 0) { + i = *ptr; + *ptr++ = ((i & 0xff000000) >> 24) + | ((i & 0x00ff0000) >> 8) + | ((i & 0x0000ff00) << 8) + | ((i & 0x000000ff) << 24); + } +} +#endif + +#ifdef WITH_AFM_FILES + +static int __PROTO(ofm_load_file(const char *filename, TFMInfo *info)); + +/* reading of AFM files */ +/* macro to convert between AFM and TFM units */ +#define AFM2TFM(x) FROUND((double)(x) * 0x100000 / 1000) +int afm_load_file(const char *filename, TFMInfo *info) +{ + /* the information we want is: + * - tfmwidth + * - width and heights + * - character origins + */ + FontInfo *fi = NULL; + int status; + CharMetricInfo *cm; + FILE *in; + + in = fopen(filename, "rb"); + if(in == NULL) + return -1; + status = afm_parse_file(in, &fi, P_GM); + fclose(in); + + if(status != ok) { + mdvi_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, "rb"); + 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) + mdvi_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) { + mdvi_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: + mdvi_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, "rb"); + 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) { + mdvi_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: + mdvi_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 00000000..e85d8e70 --- /dev/null +++ b/backend/dvi/mdvi-lib/tt.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "mdvi.h" + +#ifdef WITH_TRUETYPE_FONTS + +#include <string.h> +#include <freetype.h> +#include <ftxpost.h> +#include <ftxerr18.h> + +#include "private.h" + +static TT_Engine tt_handle; +static int initialized = 0; + +typedef struct ftinfo { + struct ftinfo *next; + struct ftinfo *prev; + char *fontname; + char *fmfname; + TT_Face face; + TT_Instance instance; + TT_Glyph glyph; + int hasmetrics; + int loaded; + int fmftype; + TFMInfo *tfminfo; + DviFontMapInfo mapinfo; + DviEncoding *encoding; +} FTInfo; + +static int tt_load_font __PROTO((DviParams *, DviFont *)); +static int tt_font_get_glyph __PROTO((DviParams *, DviFont *, int)); +static void tt_free_data __PROTO((DviFont *)); +static void tt_reset_font __PROTO((DviFont *)); +static void tt_shrink_glyph + __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +static void tt_font_remove __PROTO((FTInfo *)); + +DviFontInfo tt_font_info = { + "TT", + 0, + tt_load_font, + tt_font_get_glyph, + tt_shrink_glyph, + mdvi_shrink_glyph_grey, + tt_free_data, /* free */ + tt_reset_font, /* reset */ + NULL, /* lookup */ + kpse_truetype_format, + NULL +}; + +#define FT_HASH_SIZE 31 + +static ListHead ttfonts = {NULL, NULL, 0}; + +static int init_freetype(void) +{ + TT_Error code; + + ASSERT(initialized == 0); + code = TT_Init_FreeType(&tt_handle); + if(code) { + DEBUG((DBG_TT, "(tt) Init_Freetype: error %d\n", code)); + return -1; + } + code = TT_Init_Post_Extension(tt_handle); + if(code) { + TT_Done_FreeType(tt_handle); + return -1; + } + /* we're on */ + initialized = 1; + return 0; +} + +static void tt_encode_font(DviFont *font, FTInfo *info) +{ + TT_Face_Properties prop; + int i; + + if(TT_Get_Face_Properties(info->face, &prop)) + return; + + for(i = 0; i < prop.num_Glyphs; i++) { + char *string; + int ndx; + + if(TT_Get_PS_Name(info->face, i, &string)) + continue; + ndx = mdvi_encode_glyph(info->encoding, string); + if(ndx < font->loc || ndx > font->hic) + continue; + font->chars[ndx - font->loc].code = i; + } +} + +static int tt_really_load_font(DviParams *params, DviFont *font, FTInfo *info) +{ + DviFontChar *ch; + TFMChar *ptr; + Int32 z, alpha, beta; + int i; + FTInfo *old; + TT_Error status; + double point_size; + static int warned = 0; + TT_CharMap cmap; + TT_Face_Properties props; + int map_found; + + DEBUG((DBG_TT, "(tt) really_load_font(%s)\n", info->fontname)); + + /* get the point size */ + point_size = (double)font->scale / (params->tfm_conv * 0x100000); + point_size = 72.0 * point_size / 72.27; + if(info->loaded) { + /* just reset the size info */ + TT_Set_Instance_Resolutions(info->instance, + params->dpi, params->vdpi); + TT_Set_Instance_CharSize(info->instance, FROUND(point_size * 64)); + /* FIXME: should extend/slant again */ + info->hasmetrics = 1; + return 0; + } + + /* load the face */ + DEBUG((DBG_TT, "(tt) loading new face `%s'\n", + info->fontname)); + status = TT_Open_Face(tt_handle, font->filename, &info->face); + if(status) { + mdvi_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) { + mdvi_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) { + mdvi_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) { + mdvi_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) { + mdvi_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) { + mdvi_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 { + mdvi_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) + mdvi_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, ROUND(w, 8)); + 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 00000000..349d273a --- /dev/null +++ b/backend/dvi/mdvi-lib/util.c @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> +#include <sys/stat.h> +#include <errno.h> +#include <unistd.h> + +#include "common.h" +#include "private.h" + +static char *const messages[] = { + _G("Ooops!"), + _G("Aieeeee!!"), + _G("Ouch!"), + _G("Houston, we have a problem"), + _G("3.. 2.. 1.. BOOM!"), + _G("I'm history"), + _G("I'm going down"), + _G("I smell a rat") +}; +#define NMSGS (sizeof(messages) / sizeof(char *)) + +static FILE *logfile = NULL; +static int _mdvi_log_level; + +int mdvi_set_logfile(const char *filename); +int mdvi_set_logstream(FILE *file); +int mdvi_set_loglevel(int level); + +static void vputlog(int level, const char *head, const char *format, va_list ap) +{ + if(logfile != NULL && _mdvi_log_level >= level) { + if(head != NULL) + fprintf(logfile, "%s: ", head); + vfprintf(logfile, format, ap); + } +} + +int mdvi_set_logfile(const char *filename) +{ + FILE *f = NULL; + + if(filename && (f = fopen(filename, "w")) == NULL) + return -1; + if(logfile != NULL && !isatty(fileno(logfile))) { + fclose(logfile); + logfile = NULL; + } + if(filename) + logfile = f; + return 0; +} + +int mdvi_set_logstream(FILE *file) +{ + if(logfile && !isatty(fileno(logfile))) { + fclose(logfile); + logfile = NULL; + } + logfile = file; + return 0; +} + +int mdvi_set_loglevel(int level) +{ + int old = _mdvi_log_level; + + _mdvi_log_level = level; + return old; +} + +#ifndef NODEBUG +Uint32 _mdvi_debug_mask = 0; + +void __debug(int mask, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + if(_mdvi_debug_mask & mask) { + if(!DEBUGGING(SILENT)) { + fprintf(stderr, "Debug: "); + vfprintf(stderr, format, ap); + fflush(stderr); + } +#ifndef __GNUC__ + /* let's be portable */ + va_end(ap); + va_start(ap, format); +#endif + vputlog(LOG_DEBUG, "Debug", format, ap); + } + va_end(ap); +} +#endif + +void mdvi_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 mdvi_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 mdvi_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 mdvi_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 mdvi_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) + mdvi_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) + mdvi_crash(_("attempted to reallocate with zero size\n")); + ptr = realloc(data, newsize); + if(ptr == NULL) + mdvi_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) + mdvi_crash(_("attempted to callocate 0 members\n")); + if(size == 0) + mdvi_crash(_("attempted to callocate %u members with size 0\n"), + (unsigned)nmemb); + ptr = calloc(nmemb, size); + if(ptr == 0) + mdvi_fatal(_("failed to allocate %ux%u bytes\n"), + (unsigned)nmemb, (unsigned)size); + return ptr; +} + +void mdvi_free(void *ptr) +{ + if(ptr == NULL) + mdvi_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; +} + +char *mdvi_strrstr (const char *haystack, const char *needle) +{ + size_t i; + size_t needle_len; + size_t haystack_len; + const char *p; + + needle_len = strlen (needle); + haystack_len = strlen (haystack); + + if (needle_len == 0) + return NULL; + + if (haystack_len < needle_len) + return (char *)haystack; + + p = haystack + haystack_len - needle_len; + while (p >= haystack) { + for (i = 0; i < needle_len; i++) + if (p[i] != needle[i]) + goto next; + + return (char *)p; + + next: + p--; + } + + return NULL; +} + +char *mdvi_build_path_from_cwd (const char *path) +{ + char *ptr; + char *buf = NULL; + size_t buf_size = 512; + + while (1) { + buf = mdvi_realloc (buf, buf_size); + if ((ptr = getcwd (buf, buf_size)) == NULL && errno == ERANGE) { + buf_size *= 2; + } else { + buf = ptr; + break; + } + } + + buf = mdvi_realloc (buf, strlen (buf) + strlen (path) + 2); + strcat (buf, "/"); + strncat (buf, path, strlen (path)); + + return buf; +} + +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 00000000..fb498476 --- /dev/null +++ b/backend/dvi/mdvi-lib/vf.c @@ -0,0 +1,241 @@ +/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <string.h> + +#include "mdvi.h" +#include "private.h" + +static int vf_load_font __PROTO((DviParams *, DviFont *)); +static void vf_free_macros __PROTO((DviFont *)); + +/* only symbol exported by this file */ +DviFontInfo vf_font_info = { + "VF", + 1, /* virtual fonts scale just fine */ + vf_load_font, + NULL, /* get_glyph */ + NULL, /* shrink0 */ + NULL, /* shrink1 */ + vf_free_macros, + NULL, /* reset */ + NULL, /* lookup */ + kpse_vf_format, + NULL +}; + +DviFontInfo ovf_font_info = { + "OVF", + 1, /* virtual fonts scale just fine */ + vf_load_font, + NULL, /* get_glyph */ + NULL, /* shrink0 */ + NULL, /* shrink1 */ + vf_free_macros, + NULL, /* reset */ + NULL, /* lookup */ + kpse_ovf_format, + NULL +}; + +static int vf_load_font(DviParams *params, DviFont *font) +{ + FILE *p; + Uchar *macros; + int msize; + int mlen; + Int32 checksum; + long alpha, beta, z; + int op; + int i; + int nchars; + int loc, hic; + DviFontRef *last; + + macros = NULL; + msize = mlen = 0; + p = font->in; + + if(fuget1(p) != 247 || fuget1(p) != 202) + goto badvf; + mlen = fuget1(p); + fseek(p, (long)mlen, SEEK_CUR); + checksum = fuget4(p); + if(checksum && font->checksum && checksum != font->checksum) { + mdvi_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) { + mdvi_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) { + mdvi_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) { + mdvi_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: + mdvi_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/texmfcnf.c b/backend/dvi/texmfcnf.c new file mode 100644 index 00000000..9ca3125f --- /dev/null +++ b/backend/dvi/texmfcnf.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010, Hib Eris <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "texmfcnf.h" + +#include <glib.h> +#include <stdlib.h> +#ifdef G_OS_WIN32 +#include <windows.h> +#endif + +gchar * +get_texmfcnf(void) +{ + char *env = getenv("TEXMFCNF"); + if (env) + return g_strdup(env); + +#ifdef G_OS_WIN32 + gchar *texmfcnf = NULL; + TCHAR path[_MAX_PATH]; + + if (SearchPath(NULL, "mktexpk", ".exe", _MAX_PATH, path, NULL)) + { + gchar *sdir, *sdir_parent, *sdir_grandparent; + const gchar *texmfcnf_fmt = "{%s,%s,%s}{,{/share,}/texmf{-local,}/web2c}"; + + sdir = g_path_get_dirname(path); + sdir_parent = g_path_get_dirname(sdir); + sdir_grandparent = g_path_get_dirname(sdir_parent); + + texmfcnf = g_strdup_printf(texmfcnf_fmt, + sdir, sdir_parent, sdir_grandparent); + + g_free(sdir); + g_free(sdir_parent); + g_free(sdir_grandparent); + } + return texmfcnf; +#else + return NULL; +#endif +} + + + + diff --git a/backend/dvi/texmfcnf.h b/backend/dvi/texmfcnf.h new file mode 100644 index 00000000..11cb9b3d --- /dev/null +++ b/backend/dvi/texmfcnf.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010, Hib Eris <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TEXMFCNF_H +#define TEXMFCNF_H + +#include <glib.h> + +G_BEGIN_DECLS + +gchar *get_texmfcnf(void); + +G_END_DECLS + +#endif /* TEXMFCNF_H */ + + + + diff --git a/backend/impress/Makefile.am b/backend/impress/Makefile.am new file mode 100644 index 00000000..a7b65705 --- /dev/null +++ b/backend/impress/Makefile.am @@ -0,0 +1,57 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCEDATADIR=\""$(datadir)"\" \ + -DEVINCE_COMPILATION \ + $(BACKEND_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_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 + +libimpressdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libimpressdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument.la \ + $(BACKEND_LIBS) -lz + +backend_in_files = impressdocument.evince-backend.in +backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_EVINCE_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/impress/common.h b/backend/impress/common.h new file mode 100644 index 00000000..73e4ac19 --- /dev/null +++ b/backend/impress/common.h @@ -0,0 +1,40 @@ +/* imposter (OO.org Impress viewer) +** Copyright (C) 2003-2005 Gurer Ozen +** This code is free software; you can redistribute it and/or +** modify it under the terms of GNU General Public License. +*/ + +#ifndef COMMON_H +#define COMMON_H 1 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <stdio.h> + +#ifdef STDC_HEADERS +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#elif HAVE_STRINGS_H +#include <strings.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifndef errno +extern int errno; +#endif + +#include <iksemel.h> +#include "imposter.h" + + +#endif /* COMMON_H */ diff --git a/backend/impress/document.c b/backend/impress/document.c new file mode 100644 index 00000000..b01c0e18 --- /dev/null +++ b/backend/impress/document.c @@ -0,0 +1,140 @@ +/* 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 <config.h> +#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 00000000..8fe6ee1a --- /dev/null +++ b/backend/impress/f_oasis.c @@ -0,0 +1,170 @@ +/* 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 <config.h> +#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 00000000..7bfeeb53 --- /dev/null +++ b/backend/impress/f_oo13.c @@ -0,0 +1,181 @@ +/* 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 <config.h> +#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 00000000..9908e132 --- /dev/null +++ b/backend/impress/iksemel.c @@ -0,0 +1,1882 @@ +/* iksemel (XML parser for Jabber) +** Copyright (C) 2000-2003 Gurer Ozen <[email protected]> +** 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 <[email protected]> +** This code is free software; you can redistribute it and/or +** modify it under the terms of GNU Lesser General Public License. +*/ + +#include <config.h> +#include <errno.h> + +#include "common.h" +#include "iksemel.h" + +/***** malloc wrapper *****/ + +static void *(*my_malloc_func)(size_t size); +static void (*my_free_func)(void *ptr); + +void * +iks_malloc (size_t size) +{ + if (my_malloc_func) + return my_malloc_func (size); + else + return malloc (size); +} + +void +iks_free (void *ptr) +{ + if (my_free_func) + my_free_func (ptr); + else + free (ptr); +} + +void +iks_set_mem_funcs (void *(*malloc_func)(size_t size), void (*free_func)(void *ptr)) +{ + my_malloc_func = malloc_func; + my_free_func = free_func; +} + +/***** NULL-safe Functions *****/ + +char * +iks_strdup (const char *src) +{ + if (src) return strdup(src); + return NULL; +} + +char * +iks_strcat (char *dest, const char *src) +{ + size_t len; + + if (!src) return dest; + + len = strlen (src); + memcpy (dest, src, len); + dest[len] = '\0'; + return dest + len; +} + +int +iks_strcmp (const char *a, const char *b) +{ + if (!a || !b) return -1; + return strcmp (a, b); +} + +int +iks_strcasecmp (const char *a, const char *b) +{ + if (!a || !b) return -1; + return strcasecmp (a, b); +} + +int +iks_strncmp (const char *a, const char *b, size_t n) +{ + if (!a || !b) return -1; + return strncmp (a, b, n); +} + +int +iks_strncasecmp (const char *a, const char *b, size_t n) +{ + if (!a || !b) return -1; + return strncasecmp (a, b, n); +} + +size_t +iks_strlen (const char *src) +{ + if (!src) return 0; + return strlen (src); +} + +/***** XML Escaping *****/ + +char * +iks_escape (ikstack *s, char *src, size_t len) +{ + char *ret; + int i, j, nlen; + + if (!src || !s) return NULL; + if (len == -1) len = strlen (src); + + nlen = len; + for (i=0; i<len; i++) { + switch (src[i]) { + case '&': nlen += 4; break; + case '<': nlen += 3; break; + case '>': nlen += 3; break; + case '\'': nlen += 5; break; + case '"': nlen += 5; break; + } + } + if (len == nlen) return src; + + ret = iks_stack_alloc (s, nlen + 1); + if (!ret) return NULL; + + for (i=j=0; i<len; i++) { + switch (src[i]) { + case '&': memcpy (&ret[j], "&", 5); j += 5; break; + case '\'': memcpy (&ret[j], "'", 6); j += 6; break; + case '"': memcpy (&ret[j], """, 6); j += 6; break; + case '<': memcpy (&ret[j], "<", 4); j += 4; break; + case '>': 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<len; i++) { + if (src[i] == '&') { + i++; + if (strncmp (&src[i], "amp;", 4) == 0) { + ret[j] = '&'; + i += 3; + } else if (strncmp (&src[i], "quot;", 5) == 0) { + ret[j] = '"'; + i += 4; + } else if (strncmp (&src[i], "apos;", 5) == 0) { + ret[j] = '\''; + i += 4; + } else if (strncmp (&src[i], "lt;", 3) == 0) { + ret[j] = '<'; + i += 2; + } else if (strncmp (&src[i], "gt;", 3) == 0) { + ret[j] = '>'; + i += 2; + } else { + ret[j] = src[--i]; + } + } else { + ret[j] = src[i]; + } + j++; + } + ret[j] = '\0'; + + return ret; +} +/* iksemel (XML parser for Jabber) +** Copyright (C) 2000-2004 Gurer Ozen <[email protected]> +** 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 <[email protected]> +** 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 + 1)); + if (!tmp) return IKS_NOMEM; + memset (tmp, 0, sizeof(char *) * (2 * prs->attmax + 1)); + memcpy (tmp, prs->atts, sizeof(char *) * prs->attcur); + iks_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 <[email protected]> +** 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 <[email protected]> +** 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 00000000..66c87d61 --- /dev/null +++ b/backend/impress/iksemel.h @@ -0,0 +1,402 @@ +/* iksemel (XML parser for Jabber) +** Copyright (C) 2000-2004 Gurer Ozen <[email protected]> +** This code is free software; you can redistribute it and/or +** modify it under the terms of GNU Lesser General Public License. +*/ + +#ifndef IKSEMEL_H +#define IKSEMEL_H 1 + +#ifdef __cplusplus +#include <cstddef> /* size_t for C++ */ +extern "C" { +#else +#include <stddef.h> /* size_t for C */ +#endif + +/***** object stack *****/ + +struct ikstack_struct; +typedef struct ikstack_struct ikstack; + +ikstack *iks_stack_new (size_t meta_chunk, size_t data_chunk); +void *iks_stack_alloc (ikstack *s, size_t size); +char *iks_stack_strdup (ikstack *s, const char *src, size_t len); +char *iks_stack_strcat (ikstack *s, char *old, size_t old_len, const char *src, size_t src_len); +void iks_stack_stat (ikstack *s, size_t *allocated, size_t *used); +void iks_stack_delete (ikstack *s); + +/***** utilities *****/ + +void *iks_malloc (size_t size); +void iks_free (void *ptr); +void iks_set_mem_funcs (void *(*malloc_func)(size_t size), void (*free_func)(void *ptr)); + +char *iks_strdup (const char *src); +char *iks_strcat (char *dest, const char *src); +int iks_strcmp (const char *a, const char *b); +int iks_strcasecmp (const char *a, const char *b); +int iks_strncmp (const char *a, const char *b, size_t n); +int iks_strncasecmp (const char *a, const char *b, size_t n); +size_t iks_strlen (const char *src); +char *iks_escape (ikstack *s, char *src, size_t len); +char *iks_unescape (ikstack *s, char *src, size_t len); + +/***** dom tree *****/ + +enum ikstype { + IKS_NONE = 0, + IKS_TAG, + IKS_ATTRIBUTE, + IKS_CDATA +}; + +struct iks_struct; +typedef struct iks_struct iks; + +iks *iks_new (const char *name); +iks *iks_new_within (const char *name, ikstack *s); +iks *iks_insert (iks *x, const char *name); +iks *iks_insert_cdata (iks *x, const char *data, size_t len); +iks *iks_insert_attrib (iks *x, const char *name, const char *value); +iks *iks_insert_node (iks *x, iks *y); +void iks_hide (iks *x); +void iks_delete (iks *x); +iks *iks_next (iks *x); +iks *iks_next_tag (iks *x); +iks *iks_prev (iks *x); +iks *iks_prev_tag (iks *x); +iks *iks_parent (iks *x); +iks *iks_root (iks *x); +iks *iks_child (iks *x); +iks *iks_first_tag (iks *x); +iks *iks_attrib (iks *x); +iks *iks_find (iks *x, const char *name); +char *iks_find_cdata (iks *x, const char *name); +char *iks_find_attrib (iks *x, const char *name); +iks *iks_find_with_attrib (iks *x, const char *tagname, const char *attrname, const char *value); +ikstack *iks_stack (iks *x); +enum ikstype iks_type (iks *x); +char *iks_name (iks *x); +char *iks_cdata (iks *x); +size_t iks_cdata_size (iks *x); +int iks_has_children (iks *x); +int iks_has_attribs (iks *x); +char *iks_string (ikstack *s, iks *x); +iks *iks_copy (iks *x); +iks *iks_copy_within (iks *x, ikstack *s); + +/***** sax parser *****/ + +enum ikserror { + IKS_OK = 0, + IKS_NOMEM, + IKS_BADXML, + IKS_HOOK +}; + +enum ikstagtype { + IKS_OPEN, + IKS_CLOSE, + IKS_SINGLE +}; + +typedef int (iksTagHook)(void *user_data, char *name, char **atts, int type); +typedef int (iksCDataHook)(void *user_data, char *data, size_t len); +typedef void (iksDeleteHook)(void *user_data); + +struct iksparser_struct; +typedef struct iksparser_struct iksparser; + +iksparser *iks_sax_new (void *user_data, iksTagHook *tagHook, iksCDataHook *cdataHook); +iksparser *iks_sax_extend (ikstack *s, void *user_data, iksTagHook *tagHook, iksCDataHook *cdataHook, iksDeleteHook *deleteHook); +ikstack *iks_parser_stack (iksparser *prs); +void *iks_user_data (iksparser *prs); +unsigned long iks_nr_bytes (iksparser *prs); +unsigned long iks_nr_lines (iksparser *prs); +int iks_parse (iksparser *prs, const char *data, size_t len, int finish); +void iks_parser_reset (iksparser *prs); +void iks_parser_delete (iksparser *prs); + +/***** dom parser *****/ + +enum iksfileerror { + IKS_FILE_NOFILE = 4, + IKS_FILE_NOACCESS, + IKS_FILE_RWERR +}; + +iksparser *iks_dom_new (iks **iksptr); +void iks_set_size_hint (iksparser *prs, size_t approx_size); +iks *iks_tree (const char *xml_str, size_t len, int *err); +int iks_load (const char *fname, iks **xptr); +int iks_save (const char *fname, iks *x); + +/***** transport layer *****/ + +typedef void (iksTClose)(void *socket); +typedef int (iksTConnect)(iksparser *prs, void **socketptr, const char *server, int port); +typedef int (iksTSend)(void *socket, const char *data, size_t len); +typedef int (iksTRecv)(void *socket, char *buffer, size_t buf_len, int timeout); +typedef int (iksTConnectFD)(iksparser *prs, void **socketptr, void *fd); +typedef void *(iksTGetFD)(void *socket); + +enum iksasyncevents { + IKS_ASYNC_RESOLVED, + IKS_ASYNC_CONNECTED, + IKS_ASYNC_WRITE, + IKS_ASYNC_WRITTEN, + IKS_ASYNC_READ, + IKS_ASYNC_CLOSED, + IKS_ASYNC_ERROR +}; + +typedef int (iksAsyncNotify)(void *user_data, int event, void *event_data); +typedef int (iksTConnectAsync)(iksparser *prs, void **socketptr, const char *server, int port, void *notify_data, iksAsyncNotify *notify_func); + +typedef struct ikstransport_struct { + /* basic api, connect can be NULL if one of the other connect funcs are used */ + iksTConnect *connect; + iksTSend *send; + iksTRecv *recv; + iksTClose *close; + /* optional fd api */ + iksTConnectFD *connect_fd; + iksTGetFD *get_fd; + /* optional async api */ + iksTConnectAsync *connect_async; +} ikstransport; + +extern ikstransport iks_default_transport; + +/***** stream parser *****/ + +enum iksneterror { + IKS_NET_NODNS = 4, + IKS_NET_NOSOCK, + IKS_NET_NOCONN, + IKS_NET_RWERR, + IKS_NET_NOTSUPP, + IKS_NET_TLSFAIL +}; + +enum iksnodetype { + IKS_NODE_START, + IKS_NODE_NORMAL, + IKS_NODE_ERROR, + IKS_NODE_STOP +}; + +enum ikssasltype { + IKS_SASL_PLAIN, + IKS_SASL_DIGEST_MD5 +}; + +#define IKS_JABBER_PORT 5222 + +typedef int (iksStreamHook)(void *user_data, int type, iks *node); +typedef void (iksLogHook)(void *user_data, const char *data, size_t size, int is_incoming); + +iksparser *iks_stream_new (char *name_space, void *user_data, iksStreamHook *streamHook); +void *iks_stream_user_data (iksparser *prs); +void iks_set_log_hook (iksparser *prs, iksLogHook *logHook); +int iks_connect_tcp (iksparser *prs, const char *server, int port); +int iks_connect_fd (iksparser *prs, int fd); +int iks_connect_via (iksparser *prs, const char *server, int port, const char *server_name); +int iks_connect_with (iksparser *prs, const char *server, int port, const char *server_name, ikstransport *trans); +int iks_connect_async (iksparser *prs, const char *server, int port, void *notify_data, iksAsyncNotify *notify_func); +int iks_connect_async_with (iksparser *prs, const char *server, int port, const char *server_name, ikstransport *trans, void *notify_data, iksAsyncNotify *notify_func); +int iks_fd (iksparser *prs); +int iks_recv (iksparser *prs, int timeout); +int iks_send_header (iksparser *prs, const char *to); +int iks_send (iksparser *prs, iks *x); +int iks_send_raw (iksparser *prs, const char *xmlstr); +void iks_disconnect (iksparser *prs); +int iks_has_tls (void); +int iks_is_secure (iksparser *prs); +int iks_start_tls (iksparser *prs); +int iks_start_sasl (iksparser *prs, enum ikssasltype type, char *username, char *pass); + +/***** jabber *****/ + +#define IKS_NS_CLIENT "jabber:client" +#define IKS_NS_SERVER "jabber:server" +#define IKS_NS_AUTH "jabber:iq:auth" +#define IKS_NS_AUTH_0K "jabber:iq:auth:0k" +#define IKS_NS_REGISTER "jabber:iq:register" +#define IKS_NS_ROSTER "jabber:iq:roster" +#define IKS_NS_XROSTER "jabber:x:roster" +#define IKS_NS_OFFLINE "jabber:x:offline" +#define IKS_NS_AGENT "jabber:iq:agent" +#define IKS_NS_AGENTS "jabber:iq:agents" +#define IKS_NS_BROWSE "jabber:iq:browse" +#define IKS_NS_CONFERENCE "jabber:iq:conference" +#define IKS_NS_DELAY "jabber:x:delay" +#define IKS_NS_VERSION "jabber:iq:version" +#define IKS_NS_TIME "jabber:iq:time" +#define IKS_NS_VCARD "vcard-temp" +#define IKS_NS_PRIVATE "jabber:iq:private" +#define IKS_NS_SEARCH "jabber:iq:search" +#define IKS_NS_OOB "jabber:iq:oob" +#define IKS_NS_XOOB "jabber:x:oob" +#define IKS_NS_ADMIN "jabber:iq:admin" +#define IKS_NS_FILTER "jabber:iq:filter" +#define IKS_NS_GATEWAY "jabber:iq:gateway" +#define IKS_NS_LAST "jabber:iq:last" +#define IKS_NS_SIGNED "jabber:x:signed" +#define IKS_NS_ENCRYPTED "jabber:x:encrypted" +#define IKS_NS_ENVELOPE "jabber:x:envelope" +#define IKS_NS_EVENT "jabber:x:event" +#define IKS_NS_EXPIRE "jabber:x:expire" +#define IKS_NS_XHTML "http://www.w3.org/1999/xhtml" +#define IKS_NS_XMPP_SASL "urn:ietf:params:xml:ns:xmpp-sasl" +#define IKS_NS_XMPP_BIND "urn:ietf:params:xml:ns:xmpp-bind" +#define IKS_NS_XMPP_SESSION "urn:ietf:params:xml:ns:xmpp-session" + +#define IKS_ID_USER 1 +#define IKS_ID_SERVER 2 +#define IKS_ID_RESOURCE 4 +#define IKS_ID_PARTIAL IKS_ID_USER | IKS_ID_SERVER +#define IKS_ID_FULL IKS_ID_USER | IKS_ID_SERVER | IKS_ID_RESOURCE + +#define IKS_STREAM_STARTTLS 1 +#define IKS_STREAM_SESSION 2 +#define IKS_STREAM_BIND 4 +#define IKS_STREAM_SASL_PLAIN 8 +#define IKS_STREAM_SASL_MD5 16 + +typedef struct iksid_struct { + char *user; + char *server; + char *resource; + char *partial; + char *full; +} iksid; + +iksid *iks_id_new (ikstack *s, const char *jid); +int iks_id_cmp (iksid *a, iksid *b, int parts); + +enum ikspaktype { + IKS_PAK_NONE = 0, + IKS_PAK_MESSAGE, + IKS_PAK_PRESENCE, + IKS_PAK_IQ, + IKS_PAK_S10N +}; + +enum iksubtype { + IKS_TYPE_NONE = 0, + IKS_TYPE_ERROR, + + IKS_TYPE_CHAT, + IKS_TYPE_GROUPCHAT, + IKS_TYPE_HEADLINE, + + IKS_TYPE_GET, + IKS_TYPE_SET, + IKS_TYPE_RESULT, + + IKS_TYPE_SUBSCRIBE, + IKS_TYPE_SUBSCRIBED, + IKS_TYPE_UNSUBSCRIBE, + IKS_TYPE_UNSUBSCRIBED, + IKS_TYPE_PROBE, + IKS_TYPE_AVAILABLE, + IKS_TYPE_UNAVAILABLE +}; + +enum ikshowtype { + IKS_SHOW_UNAVAILABLE = 0, + IKS_SHOW_AVAILABLE, + IKS_SHOW_CHAT, + IKS_SHOW_AWAY, + IKS_SHOW_XA, + IKS_SHOW_DND +}; + +typedef struct ikspak_struct { + iks *x; + iksid *from; + iks *query; + char *ns; + char *id; + enum ikspaktype type; + enum iksubtype subtype; + enum ikshowtype show; +} ikspak; + +ikspak *iks_packet (iks *x); + +iks *iks_make_auth (iksid *id, const char *pass, const char *sid); +iks *iks_make_msg (enum iksubtype type, const char *to, const char *body); +iks *iks_make_s10n (enum iksubtype type, const char *to, const char *msg); +iks *iks_make_pres (enum ikshowtype show, const char *status); +iks *iks_make_iq (enum iksubtype type, const char *xmlns); +iks *iks_make_resource_bind(iksid *id); +iks *iks_make_session(void); +int iks_stream_features(iks *x); + +/***** jabber packet filter *****/ + +#define IKS_RULE_DONE 0 +#define IKS_RULE_ID 1 +#define IKS_RULE_TYPE 2 +#define IKS_RULE_SUBTYPE 4 +#define IKS_RULE_FROM 8 +#define IKS_RULE_FROM_PARTIAL 16 +#define IKS_RULE_NS 32 + +enum iksfilterret { + IKS_FILTER_PASS, + IKS_FILTER_EAT +}; + +typedef int (iksFilterHook)(void *user_data, ikspak *pak); + +struct iksfilter_struct; +typedef struct iksfilter_struct iksfilter; +struct iksrule_struct; +typedef struct iksrule_struct iksrule; + +iksfilter *iks_filter_new (void); +iksrule *iks_filter_add_rule (iksfilter *f, iksFilterHook *filterHook, void *user_data, ...); +void iks_filter_remove_rule (iksfilter *f, iksrule *rule); +void iks_filter_remove_hook (iksfilter *f, iksFilterHook *filterHook); +void iks_filter_packet (iksfilter *f, ikspak *pak); +void iks_filter_delete (iksfilter *f); + +/***** sha1 *****/ + +struct iksha_struct; +typedef struct iksha_struct iksha; + +iksha *iks_sha_new (void); +void iks_sha_reset (iksha *sha); +void iks_sha_hash (iksha *sha, const unsigned char *data, size_t len, int finish); +void iks_sha_print (iksha *sha, char *hash); +void iks_sha_delete (iksha *sha); +void iks_sha (const char *data, char *hash); + +/***** md5 *****/ + +struct ikmd5_struct; +typedef struct iksmd5_struct iksmd5; + +iksmd5 *iks_md5_new(void); +void iks_md5_reset(iksmd5 *md5); +void iks_md5_hash(iksmd5 *md5, const unsigned char *data, size_t slen, int finish); +void iks_md5_delete(iksmd5 *md5); +void iks_md5_print(iksmd5 *md5, char *buf); +void iks_md5_digest(iksmd5 *md5, unsigned char *digest); +void iks_md5(const char *data, char *buf); + +/***** base64 *****/ + +char *iks_base64_decode(const char *buf); +char *iks_base64_encode(const char *buf, int len); + +#ifdef __cplusplus +} +#endif + +#endif /* IKSEMEL_H */ diff --git a/backend/impress/imposter.h b/backend/impress/imposter.h new file mode 100644 index 00000000..50c87f2c --- /dev/null +++ b/backend/impress/imposter.h @@ -0,0 +1,84 @@ +/* imposter (OO.org Impress viewer) +** Copyright (C) 2003-2005 Gurer Ozen +** This code is free software; you can redistribute it and/or +** modify it under the terms of GNU General Public License. +*/ + +#ifndef IMPOSTER_H +#define IMPOSTER_H + +#include <sys/types.h> + +enum { + IMP_OK = 0, + IMP_NOMEM, + IMP_NOTZIP, + IMP_BADZIP, + IMP_BADDOC, + IMP_NOTIMP +}; + +struct ImpDoc_struct; +typedef struct ImpDoc_struct ImpDoc; + +struct ImpPage_struct; +typedef struct ImpPage_struct ImpPage; + +typedef struct ImpPointStruct { + int x; + int y; +} ImpPoint; + +typedef struct ImpColorStruct { + int red; + int green; + int blue; +} ImpColor; + +#define IMP_NORMAL 0 +#define IMP_BOLD 1 +#define IMP_ITALIC 2 +#define IMP_UNDERLINE 4 + +typedef struct ImpDrawer_struct { + void (*get_size)(void *drw_data, int *w, int *h); + void (*set_fg_color)(void *drw_data, ImpColor *color); + void (*draw_line)(void *drw_data, int x1, int y1, int x2, int y2); + void (*draw_rect)(void *drw_data, int fill, int x, int y, int w, int h); + void (*draw_polygon)(void *drw_data, int fill, ImpPoint *pts, int nr_pts); + void (*draw_arc)(void *drw_data, int fill, int x, int y, int w, int h, int sa, int ea); + void (*draw_bezier)(void *drw_data, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3); + void *(*open_image)(void *drw_data, const unsigned char *pix, size_t size); + void (*get_image_size)(void *drw_data, void *img_data, int *w, int *h); + void *(*scale_image)(void *drw_data, void *img_data, int w, int h); + void (*draw_image)(void *drw_data, void *img_data, int x, int y, int w, int h); + void (*close_image)(void *drw_data, void *img_data); + void (*get_text_size)(void *drw_data, const char *text, size_t len, int size, int styles, int *w, int *h); + void (*draw_text)(void *drw_data, int x, int y, const char *text, size_t len, int size, int styles); +} ImpDrawer; + +struct ImpRenderCtx_struct; +typedef struct ImpRenderCtx_struct ImpRenderCtx; + +#define IMP_LAST_PAGE -1 + +ImpDoc *imp_open(const char *filename, int *err); +int imp_nr_pages(ImpDoc *doc); +ImpPage *imp_get_page(ImpDoc *doc, int page_no); +void imp_close(ImpDoc *doc); + +void *imp_get_xml(ImpDoc *doc, const char *filename); + +ImpPage *imp_next_page(ImpPage *page); +ImpPage *imp_prev_page(ImpPage *page); +int imp_get_page_no(ImpPage *page); +const char *imp_get_page_name(ImpPage *page); + +ImpRenderCtx *imp_create_context(const ImpDrawer *drw); +void imp_context_set_page(ImpRenderCtx *ctx, ImpPage *page); +void imp_context_set_step(ImpRenderCtx *ctx, int step); +void imp_render(ImpRenderCtx *ctx, void *drw_data); +void imp_delete_context(ImpRenderCtx *ctx); + + +#endif /* IMPOSTER_H */ diff --git a/backend/impress/impress-document.c b/backend/impress/impress-document.c new file mode 100644 index 00000000..5254a881 --- /dev/null +++ b/backend/impress/impress-document.c @@ -0,0 +1,548 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Copyright (C) 2005, Jonathan Blandford <[email protected]> + * Copyright (C) 2005, Bastien Nocera <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n-lib.h> +#include <gtk/gtk.h> + +#include "imposter.h" +#include "impress-document.h" + +#include "ev-document-misc.h" +#include "ev-document-thumbnails.h" + +struct _ImpressDocumentClass +{ + EvDocumentClass parent_class; +}; + +struct _ImpressDocument +{ + EvDocument 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_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); + +EV_BACKEND_REGISTER_WITH_CODE (ImpressDocument, impress_document, + { + EV_BACKEND_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); + + #if GTK_CHECK_VERSION(3, 0, 0) + *w = gdk_window_get_width(impress_document->pixmap); + *h = gdk_window_get_height(impress_document->pixmap); + #else + gdk_drawable_get_size(impress_document->pixmap, w, h); + #endif +} + +static void +imp_render_set_fg_color(void *drw_data, ImpColor *color) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + GdkColor c; + + c.red = color->red; + c.green = color->green; + c.blue = color->blue; + gdk_gc_set_rgb_fg_color(impress_document->gc, &c); +} + +static void +imp_render_draw_line(void *drw_data, int x1, int y1, int x2, int y2) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + + gdk_draw_line(impress_document->pixmap, impress_document->gc, x1, y1, x2, y2); +} + +static void +imp_render_draw_rect(void *drw_data, int fill, int x, int y, int w, int h) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + + gdk_draw_rectangle(impress_document->pixmap, impress_document->gc, fill, x, y, w, h); +} + +static void +imp_render_draw_polygon(void *drw_data, int fill, ImpPoint *pts, int nr_pts) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + + gdk_draw_polygon(impress_document->pixmap, impress_document->gc, fill, (GdkPoint *)pts, nr_pts); +} + +static void +imp_render_draw_arc(void *drw_data, int fill, int x, int y, int w, int h, int sa, int ea) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + + gdk_draw_arc(impress_document->pixmap, impress_document->gc, fill, x, y, w, h, sa * 64, ea * 64); +} + +static void +imp_render_draw_bezier(void *drw_data, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + + imp_render_draw_bezier_real (impress_document->pixmap, impress_document->gc, x0, y0, x1, y1, x2, y2, x3, y3); +} + +static void * +imp_render_open_image(void *drw_data, const unsigned char *pix, size_t size) +{ + GdkPixbufLoader *gpl; + GdkPixbuf *pb; + + gpl = gdk_pixbuf_loader_new(); + gdk_pixbuf_loader_write(gpl, pix, size, NULL); + gdk_pixbuf_loader_close(gpl, NULL); + pb = gdk_pixbuf_loader_get_pixbuf(gpl); + return pb; +} + +static void +imp_render_get_image_size(void *drw_data, void *img_data, int *w, int *h) +{ + GdkPixbuf *pb = (GdkPixbuf *) img_data; + + *w = gdk_pixbuf_get_width(pb); + *h = gdk_pixbuf_get_height(pb); +} + +static void * +imp_render_scale_image(void *drw_data, void *img_data, int w, int h) +{ + GdkPixbuf *pb = (GdkPixbuf *) img_data; + + return gdk_pixbuf_scale_simple(pb, w, h, GDK_INTERP_BILINEAR); +} + +static void +imp_render_draw_image(void *drw_data, void *img_data, int x, int y, int w, int h) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + GdkPixbuf *pb = (GdkPixbuf *) img_data; + + gdk_draw_pixbuf(impress_document->pixmap, impress_document->gc, pb, 0, 0, x, y, w, h, GDK_RGB_DITHER_NONE, 0, 0); +} + +static void +imp_render_close_image(void *drw_data, void *img_data) +{ + GdkPixbuf *pb = (GdkPixbuf *) img_data; + + g_object_unref(G_OBJECT(pb)); +} + +static char * +imp_render_markup(const char *text, size_t len, int styles, int size) +{ + double scr_mm, scr_px, dpi; + char *esc; + char *ret; + int sz; + + scr_mm = gdk_screen_get_height_mm(gdk_screen_get_default()); + scr_px = gdk_screen_get_height(gdk_screen_get_default()); + dpi = (scr_px / scr_mm) * 25.4; + sz = (int) ((double) size * 72.0 * PANGO_SCALE / dpi); + esc = g_markup_escape_text(text, len); + ret = g_strdup_printf("<span size ='%d'>%s</span>", sz, esc); + g_free(esc); + return ret; +} + +static void +imp_render_get_text_size(void *drw_data, const char *text, size_t len, int size, int styles, int *w, int *h) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + PangoLayout *lay; + int pw, ph; + char *m; + + g_return_if_fail (impress_document->pango_ctx != NULL); + + lay = pango_layout_new(impress_document->pango_ctx); + m = imp_render_markup(text, len, styles, size); + pango_layout_set_markup(lay, m, strlen(m)); + pango_layout_get_size(lay, &pw, &ph); + g_object_unref(lay); + g_free(m); + *w = pw / PANGO_SCALE; + *h = ph / PANGO_SCALE; +} + +static void +imp_render_draw_text(void *drw_data, int x, int y, const char *text, size_t len, int size, int styles) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (drw_data); + PangoLayout *lay; + char *m; + + g_return_if_fail (impress_document->pango_ctx != NULL); + + lay = pango_layout_new(impress_document->pango_ctx); + m = imp_render_markup(text, len, styles, size); + pango_layout_set_markup(lay, m, strlen(m)); + gdk_draw_layout(impress_document->pixmap, impress_document->gc, x, y, lay); + g_object_unref(lay); + g_free(m); +} + +static const ImpDrawer imp_render_functions = { + imp_render_get_size, + imp_render_set_fg_color, + imp_render_draw_line, + imp_render_draw_rect, + imp_render_draw_polygon, + imp_render_draw_arc, + imp_render_draw_bezier, + imp_render_open_image, + imp_render_get_image_size, + imp_render_scale_image, + imp_render_draw_image, + imp_render_close_image, + imp_render_get_text_size, + imp_render_draw_text +}; + +/* Document interface */ +static gboolean +impress_document_load (EvDocument *document, + const char *uri, + GError **error) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (document); + gchar *filename; + ImpDoc *imp; + int err; + + /* FIXME: Could we actually load uris ? */ + filename = g_filename_from_uri (uri, NULL, error); + if (!filename) + return FALSE; + + imp = imp_open (filename, &err); + g_free (filename); + + if (!imp) + { + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("Invalid document")); + return FALSE; + } + impress_document->imp = imp; + + return TRUE; +} + +static gboolean +impress_document_save (EvDocument *document, + const char *uri, + GError **error) +{ + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + "Not supported"); + 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, + EvPage *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); + + ev_document_doc_mutex_lock (); + imp_context_set_page (impress_document->ctx, page); + imp_render (impress_document->ctx, impress_document); + ev_document_doc_mutex_unlock (); + + 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 *pixbuf; + + g_return_val_if_fail (IMPRESS_IS_DOCUMENT (document), NULL); + g_return_val_if_fail (impress_document->imp != NULL, NULL); + + impress_document->pagenum = rc->page->index; + + g_mutex_lock (impress_document->mutex); + impress_document->cond = g_cond_new (); + + ev_document_fc_mutex_unlock (); + ev_document_doc_mutex_unlock (); + 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); + ev_document_doc_mutex_lock (); + ev_document_fc_mutex_lock (); + + g_mutex_unlock (impress_document->mutex); + + pixbuf = impress_document->pixbuf; + impress_document->pixbuf = NULL; + + return pixbuf; +} + +static cairo_surface_t * +impress_document_render (EvDocument *document, + EvRenderContext *rc) +{ + GdkPixbuf *pixbuf; + cairo_surface_t *surface, *scaled_surface; + + pixbuf = impress_document_render_pixbuf (document, rc); + + /* FIXME: impress backend should be ported to cairo */ + surface = ev_document_misc_surface_from_pixbuf (pixbuf); + g_object_unref (pixbuf); + + scaled_surface = ev_document_misc_surface_rotate_and_scale (surface, + (PAGE_WIDTH * rc->scale) + 0.5, + (PAGE_HEIGHT * rc->scale) + 0.5, + rc->rotation); + cairo_surface_destroy (surface); + + return scaled_surface; +} + +static void +impress_document_finalize (GObject *object) +{ + ImpressDocument *impress_document = IMPRESS_DOCUMENT (object); + + if (impress_document->mutex) + g_mutex_free (impress_document->mutex); + + if (impress_document->imp) + imp_close (impress_document->imp); + + if (impress_document->ctx) + imp_delete_context (impress_document->ctx); + + if (impress_document->pango_ctx) + g_object_unref (impress_document->pango_ctx); + + if (impress_document->pixmap) + g_object_unref (G_OBJECT (impress_document->pixmap)); + + if (impress_document->gc) + 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); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + gobject_class->finalize = impress_document_finalize; + + ev_document_class->load = impress_document_load; + ev_document_class->save = impress_document_save; + ev_document_class->get_n_pages = impress_document_get_n_pages; + ev_document_class->get_page_size = impress_document_get_page_size; + ev_document_class->render = impress_document_render; +} + +static GdkPixbuf * +impress_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, + EvRenderContext *rc, + gboolean border) +{ + GdkPixbuf *pixbuf; + GdkPixbuf *scaled_pixbuf; + + pixbuf = impress_document_render_pixbuf (EV_DOCUMENT (document), rc); + scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf, + (PAGE_WIDTH * rc->scale), + (PAGE_HEIGHT * rc->scale), + GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + + if (border) + { + GdkPixbuf *tmp_pixbuf = scaled_pixbuf; + + scaled_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf); + g_object_unref (tmp_pixbuf); + } + + return scaled_pixbuf; +} + +static void +impress_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, + EvRenderContext *rc, + gint *width, + gint *height) +{ + gdouble page_width, page_height; + + impress_document_get_page_size (EV_DOCUMENT (document), + rc->page, + &page_width, &page_height); + + if (rc->rotation == 90 || rc->rotation == 270) + { + *width = (gint) (page_height * rc->scale); + *height = (gint) (page_width * rc->scale); + } + else + { + *width = (gint) (page_width * rc->scale); + *height = (gint) (page_height * rc->scale); + } +} + +static void +impress_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *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_for_screen (gdk_screen_get_default ()); +} + +/* + * 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 00000000..17a3c19a --- /dev/null +++ b/backend/impress/impress-document.h @@ -0,0 +1,39 @@ + +/* pdfdocument.h: Implementation of EvDocument for impresss + * Copyright (C) 2005, Jonathan Blandford <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __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; + +GType impress_document_get_type (void) G_GNUC_CONST; + +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + +G_END_DECLS + +#endif /* __IMPRESS_DOCUMENT_H__ */ diff --git a/backend/impress/impressdocument.evince-backend.in b/backend/impress/impressdocument.evince-backend.in new file mode 100644 index 00000000..61de77ab --- /dev/null +++ b/backend/impress/impressdocument.evince-backend.in @@ -0,0 +1,4 @@ +[Evince Backend] +Module=impressdocument +_TypeDescription=Impress Slides +MimeType=application/vnd.sun.xml.impress;application/vnd.oasis.opendocument.presentation; diff --git a/backend/impress/internal.h b/backend/impress/internal.h new file mode 100644 index 00000000..eb99c3ec --- /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 00000000..3f050d9e --- /dev/null +++ b/backend/impress/r_back.c @@ -0,0 +1,46 @@ +/* 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 <config.h> +#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 00000000..aad1655e --- /dev/null +++ b/backend/impress/r_draw.c @@ -0,0 +1,120 @@ +/* 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 <config.h> +#include "common.h" +#include "internal.h" +#include <math.h> + +void +_imp_draw_rect(ImpRenderCtx *ctx, void *drw_data, int fill, int x, int y, int w, int h, int round) +{ + int a; + + if (0 == round) { + ctx->drw->draw_rect(drw_data, fill, x, y, w, h); + return; + } + + ctx->drw->draw_arc(drw_data, fill, + x, y, round, round, 90, 90); + ctx->drw->draw_arc(drw_data, fill, + x + w - round, y, round, round, 0, 90); + ctx->drw->draw_arc(drw_data, fill, + x + w - round, y + h - round, round, round, 270, 90); + ctx->drw->draw_arc(drw_data, fill, + x, y + h - round, round, round, 180, 90); + + a = round / 2; + if (fill) { + ctx->drw->draw_rect(drw_data, 1, x + a, y, w - a - a, h); + ctx->drw->draw_rect(drw_data, 1, x, y + a, w, h - a - a); + return; + } + ctx->drw->draw_line(drw_data, x + a, y, x + w - a, y); + ctx->drw->draw_line(drw_data, x + a, y + h, x + w - a, y + h); + ctx->drw->draw_line(drw_data, x, y + a, x, y + h - a); + ctx->drw->draw_line(drw_data, x + w, y + a, x + w, y + h - a); +} + +void +_imp_draw_line_end(ImpRenderCtx *ctx, void *drw_data, int type, int size, int x, int y, int x2, int y2) +{ + ImpPoint pts[4]; + double ia, a; + + // FIXME: different types and sizes + + pts[0].x = x2; + pts[0].y = y2; + + ia = 20 * 3.14 * 2 / 360; + + if (x2-x == 0) { + if (y < y2) a = 3.14 + (3.14 / 2); else a = (3.14 / 2); + } else if (y2-y == 0) { + if (x < x2) a = 3.14; else a = 0; + } else + a = atan ((y2-y) / (x2-x)) - 3.14; + + pts[1].x = x2 + 0.3 * ctx->fact_x * cos (a - ia); + pts[1].y = y2 + 0.3 * ctx->fact_y * sin (a - ia); + + pts[2].x = x2 + 0.3 * ctx->fact_x * cos (a + ia); + pts[2].y = y2 + 0.3 * ctx->fact_y * sin (a + ia); + + ctx->drw->draw_polygon(drw_data, 1, pts, 3); +} + +void +_imp_draw_image(ImpRenderCtx *ctx, void *drw_data, const char *name, int x, int y, int w, int h) +{ + void *img1, *img2; + char *pix; + size_t len; + + len = zip_get_size(ctx->page->doc->zfile, name); + pix = malloc(len); + if (!pix) return; + zip_load(ctx->page->doc->zfile, name, pix); + + img1 = ctx->drw->open_image(drw_data, pix, len); + free(pix); + if (!img1) return; + img2 = ctx->drw->scale_image(drw_data, img1, w, h); + if (img2) { + ctx->drw->draw_image(drw_data, img2, x, y, w, h); + ctx->drw->close_image(drw_data, img2); + } + ctx->drw->close_image(drw_data, img1); +} + +void +_imp_tile_image(ImpRenderCtx *ctx, void *drw_data, const char *name, int x, int y, int w, int h) +{ + void *img1; + char *pix; + size_t len; + int gx, gy, gw, gh; + + len = zip_get_size(ctx->page->doc->zfile, name); + pix = malloc(len); + if (!pix) return; + zip_load(ctx->page->doc->zfile, name, pix); + + img1 = ctx->drw->open_image(drw_data, pix, len); + free(pix); + if (!img1) return; + + ctx->drw->get_image_size(drw_data, img1, &gw, &gh); + for (gx = x; gx < w; gx += gw) { + for (gy = y; gy < h; gy += gh) { + ctx->drw->draw_image(drw_data, img1, gx, gy, gw, gh); + } + } + + ctx->drw->close_image(drw_data, img1); +} diff --git a/backend/impress/r_geometry.c b/backend/impress/r_geometry.c new file mode 100644 index 00000000..4819821a --- /dev/null +++ b/backend/impress/r_geometry.c @@ -0,0 +1,208 @@ +/* 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 <config.h> +#include "common.h" +#include "internal.h" +#include <math.h> + +void +r_parse_color(const char *color, ImpColor *ic) +{ + unsigned int cval; + + if (1 != sscanf(color, "#%X", &cval)) return; + + ic->red = (cval & 0xFF0000) >> 8; + ic->green = cval & 0x00FF00; + ic->blue = (cval & 0xFF) << 8; +} + +int +r_get_color(ImpRenderCtx *ctx, iks *node, char *name, ImpColor *ic) +{ + char *color; + + color = r_get_style(ctx, node, name); + if (!color) return 0; + r_parse_color(color, ic); + + return 1; +} + +static void +fg_color(ImpRenderCtx *ctx, void *drw_data, iks *node, char *name) +{ + ImpColor ic; + + if (r_get_color(ctx, node, name, &ic)) { + ctx->drw->set_fg_color(drw_data, &ic); + } +} + +int +r_get_x (ImpRenderCtx *ctx, iks *node, char *name) +{ + char *val; + + val = iks_find_attrib (node, name); + if (!val) return 0; + return atof (val) * ctx->fact_x; +} + +int +r_get_y (ImpRenderCtx *ctx, iks *node, char *name) +{ + char *val; + + val = iks_find_attrib (node, name); + if (!val) return 0; + return atof (val) * ctx->fact_y; +} + +int +r_get_angle (iks *node, char *name, int def) +{ + char *tmp; + + tmp = iks_find_attrib (node, name); + if (!tmp) return def; + return atof (tmp); +} + +static int x, y, w, h; +static int px, py, pw, ph; + +static void +r_get_viewbox (iks *node) +{ + char *tmp; + + tmp = iks_find_attrib (node, "svg:viewBox"); + if (!tmp) return; + sscanf (tmp, "%d %d %d %d", &px, &py, &pw, &ph); +} + +void +r_polygon(ImpRenderCtx *ctx, void *drw_data, iks *node) +{ + char *data; + ImpPoint *points; + int i, cnt, j; + int num; + int fill = 1; + + data = r_get_style (ctx, node, "draw:fill"); + if (!data || strcmp (data, "solid") != 0) fill = 0; + + x = r_get_x (ctx, node, "svg:x"); + y = r_get_y (ctx, node, "svg:y"); + w = r_get_x (ctx, node, "svg:width"); + h = r_get_y (ctx, node, "svg:height"); + r_get_viewbox (node); + + data = iks_find_attrib (node, "draw:points"); + points = malloc (sizeof (ImpPoint) * strlen (data) / 4); + + cnt = 0; + j = 0; + num = -1; + for (i = 0; data[i]; i++) { + if (data[i] >= '0' && data[i] <= '9') { + if (num == -1) num = i; + } else { + if (num != -1) { + if (j == 0) { + points[cnt].x = atoi (data + num); + j = 1; + } else { + points[cnt++].y = atoi (data + num); + j = 0; + } + num = -1; + } + } + } + if (num != -1) { + if (j == 0) { + points[cnt].x = atoi (data + num); + } else { + points[cnt++].y = atoi (data + num); + } + } + for (i = 0; i < cnt; i++) { + points[i].x = x + points[i].x * w / pw; + points[i].y = y + points[i].y * h / ph; + } + + if (fill) { + fg_color(ctx, drw_data, node, "draw:fill-color"); + ctx->drw->draw_polygon(drw_data, 1, points, cnt); + } + fg_color(ctx, drw_data, node, "svg:stroke-color"); + ctx->drw->draw_polygon(drw_data, 0, points, cnt); + + free (points); +} + +void +r_polyline(ImpRenderCtx *ctx, void *drw_data, iks *node) +{ + char *data; + ImpPoint *points; + int i, cnt, j; + int num; + int pen_x, pen_y; + + x = r_get_x (ctx, node, "svg:x"); + y = r_get_y (ctx, node, "svg:y"); + w = r_get_x (ctx, node, "svg:width"); + h = r_get_y (ctx, node, "svg:height"); + r_get_viewbox (node); + + data = iks_find_attrib (node, "draw:points"); + points = malloc (sizeof (ImpPoint) * strlen (data) / 4); + + cnt = 0; + j = 0; + num = -1; + for (i = 0; data[i]; i++) { + if (data[i] >= '0' && data[i] <= '9') { + if (num == -1) num = i; + } else { + if (num != -1) { + if (j == 0) { + points[cnt].x = atoi (data + num); + j = 1; + } else { + points[cnt++].y = atoi (data + num); + j = 0; + } + num = -1; + } + } + } + if (num != -1) { + if (j == 0) { + points[cnt].x = atoi (data + num); + } else { + points[cnt++].y = atoi (data + num); + } + } + + pen_x = x + points[0].x * w /pw; + pen_y = y + points[0].y * h / ph; + fg_color(ctx, drw_data, node, "svg:stroke-color"); + for (i = 1; i < cnt; i++) { + int tx, ty; + tx = x + points[i].x * w / pw; + ty = y + points[i].y * h / ph; + ctx->drw->draw_line(drw_data, pen_x, pen_y, tx, ty); + pen_x = tx; + pen_y = ty; + } + free (points); +} diff --git a/backend/impress/r_gradient.c b/backend/impress/r_gradient.c new file mode 100644 index 00000000..79636fe2 --- /dev/null +++ b/backend/impress/r_gradient.c @@ -0,0 +1,387 @@ +/* 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 <config.h> +#include "common.h" +#include "internal.h" +#include <math.h> + +#define GRAD_LINEAR 0 +#define GRAD_AXIAL 1 +#define GRAD_SQUARE 2 +#define GRAD_RECTANGULAR 3 +#define GRAD_RADIAL 4 +#define GRAD_ELLIPTICAL 5 + +typedef struct Gradient_s { + int type; + ImpColor start; + int start_intensity; + ImpColor end; + int end_intensity; + int angle; + int border; + int steps; + int offset_x; + int offset_y; +} Gradient; + +typedef struct Rectangle_s { + int Left; + int Top; + int Right; + int Bottom; +} Rectangle; + +static void +poly_rotate (ImpPoint *poly, int n, int cx, int cy, double fAngle) +{ + int i; + long nX, nY; + + for (i = 0; i < n; i++) { + nX = poly->x - cx; + nY = poly->y - cy; + poly->x = (cos(fAngle) * nX + sin(fAngle) * nY) + cx; + poly->y = - (sin(fAngle)* nX - cos(fAngle) * nY) + cy; + poly++; + } +} + +static void +r_draw_gradient_simple (ImpRenderCtx *ctx, void *drw_data, Gradient *grad) +{ + Rectangle rRect = { 0, 0, ctx->pix_w - 1, ctx->pix_h - 1 }; + Rectangle aRect, aFullRect; + ImpPoint poly[4], tempoly[2]; + ImpColor gcol; + double fW, fH, fDX, fDY, fAngle; + double fScanLine, fScanInc; + long redSteps, greenSteps, blueSteps; + long nBorder; + int i, nSteps, nSteps2; + int cx, cy; + + cx = rRect.Left + (rRect.Right - rRect.Left) / 2; + cy = rRect.Top + (rRect.Bottom - rRect.Top) / 2; + + aRect = rRect; + aRect.Top--; aRect.Left--; aRect.Bottom++; aRect.Right++; + fW = rRect.Right - rRect.Left; + fH = rRect.Bottom - rRect.Top; + fAngle = (((double) grad->angle) * 3.14 / 1800.0); + fDX = fW * fabs (cos (fAngle)) + fH * fabs (sin (fAngle)); + fDY = fH * fabs (cos (fAngle)) + fW * fabs (sin (fAngle)); + fDX = (fDX - fW) * 0.5 - 0.5; + fDY = (fDY - fH) * 0.5 - 0.5; + aRect.Left -= fDX; + aRect.Right += fDX; + aRect.Top -= fDY; + aRect.Bottom += fDY; + aFullRect = aRect; + + nBorder = grad->border * (aRect.Bottom - aRect.Top) / 100; + if (grad->type == GRAD_LINEAR) { + aRect.Top += nBorder; + } else { + nBorder >>= 1; + aRect.Top += nBorder; + aRect.Bottom -= nBorder; + } + + if (aRect.Top > (aRect.Bottom - 1)) + aRect.Top = aRect.Bottom - 1; + + poly[0].x = aFullRect.Left; + poly[0].y = aFullRect.Top; + poly[1].x = aFullRect.Right; + poly[1].y = aFullRect.Top; + poly[2].x = aRect.Right; + poly[2].y = aRect.Top; + poly[3].x = aRect.Left; + poly[3].y = aRect.Top; + poly_rotate (&poly[0], 4, cx, cy, fAngle); + + redSteps = grad->end.red - grad->start.red; + greenSteps = grad->end.green - grad->start.green; + blueSteps = grad->end.blue - grad->start.blue; + nSteps = grad->steps; + if (nSteps == 0) { + long mr; + mr = aRect.Bottom - aRect.Top; + if (mr < 50) + nSteps = mr / 2; + else + nSteps = mr / 4; + mr = abs(redSteps); + if (abs(greenSteps) > mr) mr = abs(greenSteps); + if (abs(blueSteps) > mr) mr = abs(blueSteps); + if (mr < nSteps) nSteps = mr; + } + + if (grad->type == GRAD_AXIAL) { + if (nSteps & 1) nSteps++; + nSteps2 = nSteps + 2; + gcol = grad->end; + redSteps <<= 1; + greenSteps <<= 1; + blueSteps <<= 1; + } else { + nSteps2 = nSteps + 1; + gcol = grad->start; + } + + fScanLine = aRect.Top; + fScanInc = (double)(aRect.Bottom - aRect.Top) / (double)nSteps; + + for (i = 0; i < nSteps2; i++) { + // draw polygon + ctx->drw->set_fg_color(drw_data, &gcol); + ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4); + // calc next polygon + aRect.Top = (long)(fScanLine += fScanInc); + if (i == nSteps) { + tempoly[0].x = aFullRect.Left; + tempoly[0].y = aFullRect.Bottom; + tempoly[1].x = aFullRect.Right; + tempoly[1].y = aFullRect.Bottom; + } else { + tempoly[0].x = aRect.Left; + tempoly[0].y = aRect.Top; + tempoly[1].x = aRect.Right; + tempoly[1].y = aRect.Top; + } + poly_rotate (&tempoly[0], 2, cx, cy, fAngle); + poly[0] = poly[3]; + poly[1] = poly[2]; + poly[2] = tempoly[1]; + poly[3] = tempoly[0]; + // calc next color + if (grad->type == GRAD_LINEAR) { + gcol.red = grad->start.red + ((redSteps * i) / nSteps2); + gcol.green = grad->start.green + ((greenSteps * i) / nSteps2); + gcol.blue = grad->start.blue + ((blueSteps * i) / nSteps2); + } else { + if (i >= nSteps) { + gcol.red = grad->end.red; + gcol.green = grad->end.green; + gcol.blue = grad->end.blue; + } else { + if (i <= (nSteps / 2)) { + gcol.red = grad->end.red - ((redSteps * i) / nSteps2); + gcol.green = grad->end.green - ((greenSteps * i) / nSteps2); + gcol.blue = grad->end.blue - ((blueSteps * i) / nSteps2); + } else { + int i2 = i - nSteps / 2; + gcol.red = grad->start.red + ((redSteps * i2) / nSteps2); + gcol.green = grad->start.green + ((greenSteps * i2) / nSteps2); + gcol.blue = grad->start.blue + ((blueSteps * i2) / nSteps2); + } + } + } + } +} + +static void +r_draw_gradient_complex (ImpRenderCtx *ctx, void *drw_data, Gradient *grad) +{ + Rectangle rRect = { 0, 0, ctx->pix_w - 1, ctx->pix_h - 1 }; + Rectangle aRect = rRect; + ImpColor gcol; + ImpPoint poly[4]; + double fAngle = (((double) grad->angle) * 3.14 / 1800.0); + long redSteps, greenSteps, blueSteps; + long nZW, nZH; + long bX, bY; + long sW, sH; + long cx, cy; + int i; + long nSteps; + double sTop, sLeft, sRight, sBottom, sInc; + int minRect; + + redSteps = grad->end.red - grad->start.red; + greenSteps = grad->end.green - grad->start.green; + blueSteps = grad->end.blue - grad->start.blue; + + if (grad->type == GRAD_SQUARE || grad->type == GRAD_RECTANGULAR) { + double fW = aRect.Right - aRect.Left; + double fH = aRect.Bottom - aRect.Top; + double fDX = fW * fabs (cos (fAngle)) + fH * fabs (sin (fAngle)); + double fDY = fH * fabs (cos (fAngle)) + fW * fabs (sin (fAngle)); + fDX = (fDX - fW) * 0.5 - 0.5; + fDY = (fDY - fH) * 0.5 - 0.5; + aRect.Left -= fDX; + aRect.Right += fDX; + aRect.Top -= fDY; + aRect.Bottom += fDY; + } + + sW = aRect.Right - aRect.Left; + sH = aRect.Bottom - aRect.Top; + + if (grad->type == GRAD_SQUARE) { + if (sW > sH) sH = sW; else sW = sH; + } else if (grad->type == GRAD_RADIAL) { + sW = 0.5 + sqrt ((double)sW*(double)sW + (double)sH*(double)sH); + sH = sW; + } else if (grad->type == GRAD_ELLIPTICAL) { + sW = 0.5 + (double)sW * 1.4142; + sH = 0.5 + (double)sH * 1.4142; + } + + nZW = (aRect.Right - aRect.Left) * grad->offset_x / 100; + nZH = (aRect.Bottom - aRect.Top) * grad->offset_y / 100; + bX = grad->border * sW / 100; + bY = grad->border * sH / 100; + cx = aRect.Left + nZW; + cy = aRect.Top + nZH; + + sW -= bX; + sH -= bY; + + aRect.Left = cx - ((aRect.Right - aRect.Left) >> 1); + aRect.Top = cy - ((aRect.Bottom - aRect.Top) >> 1); + + nSteps = grad->steps; + minRect = aRect.Right - aRect.Left; + if (aRect.Bottom - aRect.Top < minRect) minRect = aRect.Bottom - aRect.Top; + if (nSteps == 0) { + long mr; + if (minRect < 50) + nSteps = minRect / 2; + else + nSteps = minRect / 4; + mr = abs(redSteps); + if (abs(greenSteps) > mr) mr = abs(greenSteps); + if (abs(blueSteps) > mr) mr = abs(blueSteps); + if (mr < nSteps) nSteps = mr; + } + + sLeft = aRect.Left; + sTop = aRect.Top; + sRight = aRect.Right; + sBottom = aRect.Bottom; + sInc = (double) minRect / (double) nSteps * 0.5; + + gcol = grad->start; + poly[0].x = rRect.Left; + poly[0].y = rRect.Top; + poly[1].x = rRect.Right; + poly[1].y = rRect.Top; + poly[2].x = rRect.Right; + poly[2].y = rRect.Bottom; + poly[3].x = rRect.Left; + poly[3].y = rRect.Bottom; + ctx->drw->set_fg_color(drw_data, &gcol); + ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4); + + for (i = 0; i < nSteps; i++) { + aRect.Left = (long) (sLeft += sInc); + aRect.Top = (long) (sTop += sInc); + aRect.Right = (long) (sRight -= sInc); + aRect.Bottom = (long) (sBottom -= sInc); + if (aRect.Bottom - aRect.Top < 2 || aRect.Right - aRect.Left < 2) + break; + + gcol.red = grad->start.red + (redSteps * (i+1) / nSteps); + gcol.green = grad->start.green + (greenSteps * (i+1) / nSteps); + gcol.blue = grad->start.blue + (blueSteps * (i+1) / nSteps); + ctx->drw->set_fg_color(drw_data, &gcol); + + if (grad->type == GRAD_RADIAL || grad->type == GRAD_ELLIPTICAL) { + ctx->drw->draw_arc(drw_data, 1, aRect.Left, aRect.Top, + aRect.Right - aRect.Left, aRect.Bottom - aRect.Top, + 0, 360); + } else { + poly[0].x = aRect.Left; + poly[0].y = aRect.Top; + poly[1].x = aRect.Right; + poly[1].y = aRect.Top; + poly[2].x = aRect.Right; + poly[2].y = aRect.Bottom; + poly[3].x = aRect.Left; + poly[3].y = aRect.Bottom; + poly_rotate (&poly[0], 4, cx, cy, fAngle); + ctx->drw->draw_polygon(drw_data, 1, &poly[0], 4); + } + } +} + +void +r_draw_gradient (ImpRenderCtx *ctx, void *drw_data, iks *node) +{ +// GdkGC *gc; + Gradient grad; + char *stil, *tmp; + iks *x; + + stil = r_get_style (ctx, node, "draw:fill-gradient-name"); + x = iks_find_with_attrib (iks_find (ctx->styles, "office:styles"), + "draw:gradient", "draw:name", stil); + if (x) { + memset (&grad, 0, sizeof (Gradient)); + grad.type = -1; + grad.offset_x = 50; + grad.offset_y = 50; + + tmp = iks_find_attrib (x, "draw:start-color"); + if (tmp) r_parse_color (tmp, &grad.start); + tmp = iks_find_attrib (x, "draw:start-intensity"); + if (tmp) { + int val = atoi (tmp); + grad.start.red = grad.start.red * val / 100; + grad.start.green = grad.start.green * val / 100; + grad.start.blue = grad.start.blue * val / 100; + } + tmp = iks_find_attrib (x, "draw:end-color"); + if (tmp) r_parse_color (tmp, &grad.end); + tmp = iks_find_attrib (x, "draw:end-intensity"); + if (tmp) { + int val = atoi (tmp); + grad.end.red = grad.end.red * val / 100; + grad.end.green = grad.end.green * val / 100; + grad.end.blue = grad.end.blue * val / 100; + } + tmp = iks_find_attrib (x, "draw:angle"); + if (tmp) grad.angle = atoi(tmp) % 3600; + tmp = iks_find_attrib (x, "draw:border"); + if (tmp) grad.border = atoi(tmp); + tmp = r_get_style (ctx, node, "draw:gradient-step-count"); + if (tmp) grad.steps = atoi (tmp); + tmp = iks_find_attrib (x, "draw:cx"); + if (tmp) grad.offset_x = atoi (tmp); + tmp = iks_find_attrib (x, "draw:cy"); + if (tmp) grad.offset_y = atoi (tmp); + tmp = iks_find_attrib (x, "draw:style"); + if (iks_strcmp (tmp, "linear") == 0) + grad.type = GRAD_LINEAR; + else if (iks_strcmp (tmp, "axial") == 0) + grad.type = GRAD_AXIAL; + else if (iks_strcmp (tmp, "radial") == 0) + grad.type = GRAD_RADIAL; + else if (iks_strcmp (tmp, "rectangular") == 0) + grad.type = GRAD_RECTANGULAR; + else if (iks_strcmp (tmp, "ellipsoid") == 0) + grad.type = GRAD_ELLIPTICAL; + else if (iks_strcmp (tmp, "square") == 0) + grad.type = GRAD_SQUARE; + + if (grad.type == -1) return; + +// gc = ctx->gc; +// ctx->gc = gdk_gc_new (ctx->d); +// gdk_gc_copy (ctx->gc, gc); + + if (grad.type == GRAD_LINEAR || grad.type == GRAD_AXIAL) + r_draw_gradient_simple (ctx, drw_data, &grad); + else + r_draw_gradient_complex (ctx, drw_data, &grad); + +// g_object_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 00000000..39ee9c62 --- /dev/null +++ b/backend/impress/r_style.c @@ -0,0 +1,111 @@ +/* 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 <config.h> +#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 00000000..4b89bcad --- /dev/null +++ b/backend/impress/r_text.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 <config.h> +#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 00000000..67b80756 --- /dev/null +++ b/backend/impress/render.c @@ -0,0 +1,54 @@ +/* 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 <config.h> +#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/zip.c b/backend/impress/zip.c new file mode 100644 index 00000000..b1f25c8c --- /dev/null +++ b/backend/impress/zip.c @@ -0,0 +1,349 @@ +/* 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 <config.h> +#include "common.h" +#include "zip.h" +#include <zlib.h> +#define _(x) x + +typedef unsigned long ulong; + +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 00000000..23ff3631 --- /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 00000000..725a512c --- /dev/null +++ b/backend/pdf/Makefile.am @@ -0,0 +1,34 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCE_COMPILATION \ + $(BACKEND_CFLAGS) \ + $(POPPLER_CFLAGS) \ + $(WARN_CXXFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_LTLIBRARIES = libpdfdocument.la + +libpdfdocument_la_SOURCES = \ + ev-poppler.cc \ + ev-poppler.h + +libpdfdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libpdfdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument.la \ + $(BACKEND_LIBS) \ + $(POPPLER_LIBS) \ + $(CAIRO_PDF_LIBS) \ + $(CAIRO_PS_LIBS) + +backend_in_files = pdfdocument.evince-backend.in +backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_EVINCE_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc new file mode 100644 index 00000000..e2abb55d --- /dev/null +++ b/backend/pdf/ev-poppler.cc @@ -0,0 +1,3290 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* this file is part of evince, a mate document viewer + * + * Copyright (C) 2009, Juanjo Marín <[email protected]> + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <math.h> +#include <string.h> +#include <gtk/gtk.h> +#include <poppler.h> +#include <poppler-document.h> +#include <poppler-page.h> +#ifdef HAVE_CAIRO_PDF +#include <cairo-pdf.h> +#endif +#ifdef HAVE_CAIRO_PS +#include <cairo-ps.h> +#endif +#include <glib/gi18n-lib.h> + +#include "ev-poppler.h" +#include "ev-file-exporter.h" +#include "ev-document-find.h" +#include "ev-document-misc.h" +#include "ev-document-links.h" +#include "ev-document-images.h" +#include "ev-document-fonts.h" +#include "ev-document-security.h" +#include "ev-document-thumbnails.h" +#include "ev-document-transition.h" +#include "ev-document-forms.h" +#include "ev-document-layers.h" +#include "ev-document-print.h" +#include "ev-document-annotations.h" +#include "ev-document-attachments.h" +#include "ev-document-text.h" +#include "ev-selection.h" +#include "ev-transition-effect.h" +#include "ev-attachment.h" +#include "ev-image.h" + +#include <libxml/tree.h> +#include <libxml/parser.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> + +#if (defined (HAVE_CAIRO_PDF) || defined (HAVE_CAIRO_PS)) +#define HAVE_CAIRO_PRINT +#endif + +/* fields from the XMP Rights Management Schema, XMP Specification Sept 2005, pag. 45 */ +#define LICENSE_MARKED "/x:xmpmeta/rdf:RDF/rdf:Description/xmpRights:Marked" +#define LICENSE_TEXT "/x:xmpmeta/rdf:RDF/rdf:Description/dc:rights/rdf:Alt/rdf:li[lang('%s')]" +#define LICENSE_WEB_STATEMENT "/x:xmpmeta/rdf:RDF/rdf:Description/xmpRights:WebStatement" +/* license field from Creative Commons schema, http://creativecommons.org/ns */ +#define LICENSE_URI "/x:xmpmeta/rdf:RDF/rdf:Description/cc:license/@rdf:resource" + +typedef struct { + EvFileExporterFormat format; + + /* Pages per sheet */ + gint pages_per_sheet; + gint pages_printed; + gint pages_x; + gint pages_y; + gdouble paper_width; + gdouble paper_height; + +#ifdef HAVE_CAIRO_PRINT + cairo_t *cr; +#else + PopplerPSFile *ps_file; +#endif +} PdfPrintContext; + +struct _PdfDocumentClass +{ + EvDocumentClass parent_class; +}; + +struct _PdfDocument +{ + EvDocument parent_instance; + + PopplerDocument *document; + gchar *password; + gboolean forms_modified; + gboolean annots_modified; + + PopplerFontInfo *font_info; + PopplerFontsIter *fonts_iter; + int fonts_scanned_pages; + + PdfPrintContext *print_ctx; + + GList *layers; + GHashTable *annots; +}; + +static void pdf_document_security_iface_init (EvDocumentSecurityInterface *iface); +static void pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); +static void pdf_document_document_links_iface_init (EvDocumentLinksInterface *iface); +static void pdf_document_document_images_iface_init (EvDocumentImagesInterface *iface); +static void pdf_document_document_forms_iface_init (EvDocumentFormsInterface *iface); +static void pdf_document_document_fonts_iface_init (EvDocumentFontsInterface *iface); +static void pdf_document_document_layers_iface_init (EvDocumentLayersInterface *iface); +static void pdf_document_document_print_iface_init (EvDocumentPrintInterface *iface); +static void pdf_document_document_annotations_iface_init (EvDocumentAnnotationsInterface *iface); +static void pdf_document_document_attachments_iface_init (EvDocumentAttachmentsInterface *iface); +static void pdf_document_find_iface_init (EvDocumentFindInterface *iface); +static void pdf_document_file_exporter_iface_init (EvFileExporterInterface *iface); +static void pdf_selection_iface_init (EvSelectionInterface *iface); +static void pdf_document_page_transition_iface_init (EvDocumentTransitionInterface *iface); +static void pdf_document_text_iface_init (EvDocumentTextInterface *iface); +static void pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails, + EvRenderContext *rc, + 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_print_context_free (PdfPrintContext *ctx); +static gboolean attachment_save_to_buffer (PopplerAttachment *attachment, + gchar **buffer, + gsize *buffer_size, + GError **error); + +EV_BACKEND_REGISTER_WITH_CODE (PdfDocument, pdf_document, + { + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_SECURITY, + pdf_document_security_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, + pdf_document_document_thumbnails_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS, + pdf_document_document_links_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_IMAGES, + pdf_document_document_images_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FORMS, + pdf_document_document_forms_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FONTS, + pdf_document_document_fonts_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LAYERS, + pdf_document_document_layers_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_PRINT, + pdf_document_document_print_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_ANNOTATIONS, + pdf_document_document_annotations_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_ATTACHMENTS, + pdf_document_document_attachments_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND, + pdf_document_find_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, + pdf_document_file_exporter_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION, + pdf_selection_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_TRANSITION, + pdf_document_page_transition_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_TEXT, + pdf_document_text_iface_init); + }); + +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->annots) { + g_hash_table_destroy (pdf_document->annots); + pdf_document->annots = 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); + } + + if (pdf_document->layers) { + g_list_foreach (pdf_document->layers, (GFunc)g_object_unref, NULL); + g_list_free (pdf_document->layers); + } + + G_OBJECT_CLASS (pdf_document_parent_class)->dispose (object); +} + +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_literal (error, + EV_DOCUMENT_ERROR, + code, + poppler_error->message); + + g_error_free (poppler_error); + } else { + g_propagate_error (error, poppler_error); + } +} + + +/* EvDocument */ +static gboolean +pdf_document_save (EvDocument *document, + const char *uri, + GError **error) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + gboolean retval; + GError *poppler_error = NULL; + + if (pdf_document->forms_modified || pdf_document->annots_modified) { + retval = poppler_document_save (pdf_document->document, + uri, &poppler_error); + if (retval) { + pdf_document->forms_modified = FALSE; + pdf_document->annots_modified = FALSE; + } + } else { + retval = poppler_document_save_a_copy (pdf_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 EvPage * +pdf_document_get_page (EvDocument *document, + gint index) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + PopplerPage *poppler_page; + EvPage *page; + + poppler_page = poppler_document_get_page (pdf_document->document, index); + page = ev_page_new (index); + page->backend_page = (EvBackendPage)g_object_ref (poppler_page); + page->backend_destroy_func = (EvBackendPageDestroyFunc)g_object_unref; + g_object_unref (poppler_page); + + return page; +} + +static void +pdf_document_get_page_size (EvDocument *document, + EvPage *page, + double *width, + double *height) +{ + g_return_if_fail (POPPLER_IS_PAGE (page->backend_page)); + + poppler_page_get_size (POPPLER_PAGE (page->backend_page), width, height); +} + +static char * +pdf_document_get_page_label (EvDocument *document, + EvPage *page) +{ + char *label = NULL; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + g_object_get (G_OBJECT (page->backend_page), + "label", &label, + NULL); + return label; +} + +static cairo_surface_t * +pdf_page_render (PopplerPage *page, + gint width, + gint height, + EvRenderContext *rc) +{ + cairo_surface_t *surface; + cairo_t *cr; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + cr = cairo_create (surface); + + switch (rc->rotation) { + case 90: + cairo_translate (cr, width, 0); + break; + case 180: + cairo_translate (cr, width, height); + break; + case 270: + cairo_translate (cr, 0, height); + break; + default: + cairo_translate (cr, 0, 0); + } + cairo_scale (cr, rc->scale, rc->scale); + cairo_rotate (cr, rc->rotation * G_PI / 180.0); + poppler_page_render (page, cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_paint (cr); + + cairo_destroy (cr); + + return surface; +} + +static cairo_surface_t * +pdf_document_render (EvDocument *document, + EvRenderContext *rc) +{ + PdfDocument *pdf_document; + PopplerPage *poppler_page; + double width_points, height_points; + gint width, height; + + poppler_page = POPPLER_PAGE (rc->page->backend_page); + + poppler_page_get_size (poppler_page, + &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); + } + + return pdf_page_render (poppler_page, + width, height, rc); +} + +/* reference: +http://www.pdfa.org/lib/exe/fetch.php?id=pdfa%3Aen%3Atechdoc&cache=cache&media=pdfa:techdoc:tn0001_pdfa-1_and_namespaces_2008-03-18.pdf */ +static char * +pdf_document_get_format_from_metadata (xmlDocPtr doc, + xmlXPathContextPtr xpathCtx) +{ + xmlXPathObjectPtr xpathObj; + xmlChar *part = NULL; + xmlChar *conf = NULL; + char *result = NULL; + int i; + + /* add pdf/a namespaces */ + xmlXPathRegisterNs (xpathCtx, BAD_CAST "x", BAD_CAST "adobe:ns:meta/"); + xmlXPathRegisterNs (xpathCtx, BAD_CAST "rdf", BAD_CAST "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + xmlXPathRegisterNs (xpathCtx, BAD_CAST "pdfaid", BAD_CAST "http://www.aiim.org/pdfa/ns/id/"); + + /* reads pdf/a part */ + /* first syntax: child node */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "/x:xmpmeta/rdf:RDF/rdf:Description/pdfaid:part", xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr != 0) + part = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject (xpathObj); + } + if (part == NULL) { + /* second syntax: attribute */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "/x:xmpmeta/rdf:RDF/rdf:Description/@pdfaid:part", xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr != 0) + part = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject (xpathObj); + } + } + + /* reads pdf/a conformance */ + /* first syntax: child node */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "/x:xmpmeta/rdf:RDF/rdf:Description/pdfaid:conformance", xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr != 0) + conf = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject (xpathObj); + } + if (conf == NULL) { + /* second syntax: attribute */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "/x:xmpmeta/rdf:RDF/rdf:Description/@pdfaid:conformance", xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && xpathObj->nodesetval->nodeNr != 0) + conf = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + + xmlXPathFreeObject (xpathObj); + } + } + + if (part != NULL && conf != NULL) { + /* makes conf lowercase */ + for (i = 0; conf[i]; i++) + conf[i] = g_ascii_tolower (conf[i]); + + /* return buffer */ + result = g_strdup_printf ("PDF/A - %s%s", part, conf); + } + + /* Cleanup */ + xmlFree (part); + xmlFree (conf); + + return result; +} + +static EvDocumentLicense * +pdf_document_get_license_from_metadata (xmlDocPtr doc, + xmlXPathContextPtr xpathCtx) +{ + xmlXPathObjectPtr xpathObj; + xmlChar *marked = NULL; + const char *language_string; + char *aux; + gchar **tags; + gchar *tag, *tag_aux; + int i, j; + EvDocumentLicense *license; + + /* register namespaces */ + xmlXPathRegisterNs (xpathCtx, BAD_CAST "x", BAD_CAST "adobe:ns:meta/"); + xmlXPathRegisterNs (xpathCtx, BAD_CAST "rdf", BAD_CAST "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + xmlXPathRegisterNs (xpathCtx, BAD_CAST "dc", BAD_CAST "http://purl.org/dc/elements/1.1/"); + /* XMP Rights Management Schema */ + xmlXPathRegisterNs (xpathCtx, BAD_CAST "xmpRights", BAD_CAST "http://ns.adobe.com/xap/1.0/rights/"); + /* Creative Commons Schema */ + xmlXPathRegisterNs (xpathCtx, BAD_CAST "cc", BAD_CAST "http://creativecommons.org/ns#"); + + /* checking if the document has been marked as defined on the XMP Rights + * Management Schema */ + xpathObj = xmlXPathEvalExpression (BAD_CAST LICENSE_MARKED, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + marked = xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + + /* a) Not marked => No XMP Rights information */ + if (!marked) { + xmlFree (marked); + return NULL; + } + + license = ev_document_license_new (); + + /* b) Marked False => Public Domain, no copyrighted material and no + * license needed */ + if (g_strrstr ((char *) marked, "False") != NULL) { + license->text = g_strdup (_("This work is in the Public Domain")); + /* c) Marked True => Copyrighted material */ + } else { + /* Checking usage terms as defined by the XMP Rights Management + * Schema. This field is recomended to be checked by Creative + * Commons */ + /* 1) checking for a suitable localized string */ + language_string = pango_language_to_string (gtk_get_default_language ()); + tags = g_strsplit (language_string, "-", -1); + i = g_strv_length (tags); + while (i-- && !license->text) { + tag = g_strdup (tags[0]); + for (j = 1; j <= i; j++) { + tag_aux = g_strdup_printf ("%s-%s", tag, tags[j]); + g_free (tag); + tag = tag_aux; + } + aux = g_strdup_printf (LICENSE_TEXT, tag); + xpathObj = xmlXPathEvalExpression (BAD_CAST aux, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + license->text = (gchar *)xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + g_free (tag); + g_free (aux); + } + g_strfreev(tags); + + /* 2) if not, use the default string */ + if (!license->text) { + aux = g_strdup_printf (LICENSE_TEXT, "x-default"); + xpathObj = xmlXPathEvalExpression (BAD_CAST aux, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + license->text = (gchar *)xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + g_free (aux); + } + + /* Checking the license URI as defined by the Creative Commons + * Schema. This field is recomended to be checked by Creative + * Commons */ + xpathObj = xmlXPathEvalExpression (BAD_CAST LICENSE_URI, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + license->uri = (gchar *)xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + + /* Checking the web statement as defined by the XMP Rights + * Management Schema. Checking it out is a sort of above-and-beyond + * the basic recommendations by Creative Commons. It can be + * considered as a "reinforcement" approach to add certainty. */ + xpathObj = xmlXPathEvalExpression (BAD_CAST LICENSE_WEB_STATEMENT, xpathCtx); + if (xpathObj != NULL) { + if (xpathObj->nodesetval != NULL && + xpathObj->nodesetval->nodeNr != 0) + license->web_statement = (gchar *)xmlNodeGetContent (xpathObj->nodesetval->nodeTab[0]); + xmlXPathFreeObject (xpathObj); + } + } + xmlFree (marked); + + if (!license->text && !license->uri && !license->web_statement) { + ev_document_license_free (license); + return NULL; + } + + return license; +} + +static void +pdf_document_parse_metadata (const gchar *metadata, + EvDocumentInfo *info) +{ + xmlDocPtr doc; + xmlXPathContextPtr xpathCtx; + gchar *fmt; + + doc = xmlParseMemory (metadata, strlen (metadata)); + if (doc == NULL) + return; /* invalid xml metadata */ + + xpathCtx = xmlXPathNewContext (doc); + if (xpathCtx == NULL) { + xmlFreeDoc (doc); + return; /* invalid xpath context */ + } + + fmt = pdf_document_get_format_from_metadata (doc, xpathCtx); + if (fmt != NULL) { + g_free (info->format); + info->format = fmt; + } + + info->license = pdf_document_get_license_from_metadata (doc, xpathCtx); + + xmlXPathFreeContext (xpathCtx); + xmlFreeDoc (doc); +} + + +static EvDocumentInfo * +pdf_document_get_info (EvDocument *document) +{ + EvDocumentInfo *info; + PopplerPageLayout layout; + PopplerPageMode mode; + PopplerViewerPreferences view_prefs; + PopplerPermissions permissions; + EvPage *page; + char *metadata; + + 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 | + EV_DOCUMENT_INFO_LICENSE; + + 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), + "metadata", &metadata, + NULL); + + if (metadata != NULL) { + pdf_document_parse_metadata (metadata, info); + g_free (metadata); + } + + info->n_pages = ev_document_get_n_pages (document); + + if (info->n_pages > 0) { + ev_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; + } + + 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 gboolean +pdf_document_get_backend_info (EvDocument *document, EvDocumentBackendInfo *info) +{ + PopplerBackend backend; + + backend = poppler_get_backend (); + switch (backend) { + case POPPLER_BACKEND_CAIRO: + info->name = "poppler/cairo"; + break; + case POPPLER_BACKEND_SPLASH: + info->name = "poppler/splash"; + break; + default: + info->name = "poppler/unknown"; + break; + } + + info->version = poppler_get_version (); + + return TRUE; +} + +static gboolean +pdf_document_support_synctex (EvDocument *document) +{ + return TRUE; +} + +static void +pdf_document_class_init (PdfDocumentClass *klass) +{ + GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + g_object_class->dispose = pdf_document_dispose; + + ev_document_class->save = pdf_document_save; + ev_document_class->load = pdf_document_load; + ev_document_class->get_n_pages = pdf_document_get_n_pages; + ev_document_class->get_page = pdf_document_get_page; + ev_document_class->get_page_size = pdf_document_get_page_size; + ev_document_class->get_page_label = pdf_document_get_page_label; + ev_document_class->render = pdf_document_render; + ev_document_class->get_info = pdf_document_get_info; + ev_document_class->get_backend_info = pdf_document_get_backend_info; + ev_document_class->support_synctex = pdf_document_support_synctex; +} + +/* 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 void +pdf_document_security_iface_init (EvDocumentSecurityInterface *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 (EvDocumentFontsInterface *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 - MIN (height, dest->top), + dest->zoom, + dest->change_left, + dest->change_top, + dest->change_zoom); + g_object_unref (poppler_page); + } + break; + case POPPLER_DEST_FITB: + case POPPLER_DEST_FIT: + ev_dest = ev_link_dest_new_fit (dest->page_num - 1); + break; + case POPPLER_DEST_FITBH: + 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 - MIN (height, dest->top), + dest->change_top); + g_object_unref (poppler_page); + } + break; + case POPPLER_DEST_FITBV: + case POPPLER_DEST_FITV: + ev_dest = ev_link_dest_new_fitv (dest->page_num - 1, + dest->left, + dest->change_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 - MIN (height, dest->bottom), + dest->right, + height - MIN (height, dest->top)); + g_object_unref (poppler_page); + } + 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 destination: %s, please post a " + "bug report in Evince bugzilla " + "(http://bugzilla.mate.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_NONE: + break; + 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; +#if POPPLER_CHECK_VERSION (0, 13, 2) + case POPPLER_ACTION_RENDITION: + unimplemented_action = "POPPLER_ACTION_RENDITION"; + break; + case POPPLER_ACTION_OCG_STATE: + unimplemented_action = "POPPLER_ACTION_OCG_STATE"; + break; +#endif + case POPPLER_ACTION_UNKNOWN: + unimplemented_action = "POPPLER_ACTION_UNKNOWN"; + } + + if (unimplemented_action) { + g_warning ("Unimplemented action: %s, please post a bug report " + "in Evince bugzilla (http://bugzilla.mate.org) " + "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 || strlen (ev_link_get_title (link)) <= 0) { + poppler_action_free (action); + if (link) + g_object_unref (link); + + 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, + G_TYPE_STRING); + build_tree (pdf_document, model, NULL, iter); + poppler_index_iter_free (iter); + } + + return model; +} + +static EvMappingList * +pdf_document_links_get_links (EvDocumentLinks *document_links, + EvPage *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_PAGE (page->backend_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; + EvMapping *ev_link_mapping; + + link_mapping = (PopplerLinkMapping *)list->data; + ev_link_mapping = g_new (EvMapping, 1); + ev_link_mapping->data = ev_link_from_action (pdf_document, + link_mapping->action); + ev_link_mapping->area.x1 = link_mapping->area.x1; + ev_link_mapping->area.x2 = link_mapping->area.x2; + /* Invert this for X-style coordinates */ + ev_link_mapping->area.y1 = height - link_mapping->area.y2; + ev_link_mapping->area.y2 = height - link_mapping->area.y1; + + retval = g_list_prepend (retval, ev_link_mapping); + } + + poppler_page_free_link_mapping (mapping_list); + + return ev_mapping_list_new (page->index, g_list_reverse (retval), (GDestroyNotify)g_object_unref); +} + +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 (EvDocumentLinksInterface *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 EvMappingList * +pdf_document_images_get_image_mapping (EvDocumentImages *document_images, + EvPage *page) +{ + GList *retval = NULL; + PdfDocument *pdf_document; + PopplerPage *poppler_page; + GList *mapping_list; + GList *list; + + pdf_document = PDF_DOCUMENT (document_images); + poppler_page = POPPLER_PAGE (page->backend_page); + mapping_list = poppler_page_get_image_mapping (poppler_page); + + for (list = mapping_list; list; list = list->next) { + PopplerImageMapping *image_mapping; + EvMapping *ev_image_mapping; + + image_mapping = (PopplerImageMapping *)list->data; + + ev_image_mapping = g_new (EvMapping, 1); + + ev_image_mapping->data = ev_image_new (page->index, image_mapping->image_id); + ev_image_mapping->area.x1 = image_mapping->area.x1; + ev_image_mapping->area.y1 = image_mapping->area.y1; + ev_image_mapping->area.x2 = image_mapping->area.x2; + ev_image_mapping->area.y2 = image_mapping->area.y2; + + retval = g_list_prepend (retval, ev_image_mapping); + } + + poppler_page_free_image_mapping (mapping_list); + + return ev_mapping_list_new (page->index, g_list_reverse (retval), (GDestroyNotify)g_object_unref); +} + +GdkPixbuf * +pdf_document_images_get_image (EvDocumentImages *document_images, + EvImage *image) +{ + GdkPixbuf *retval = NULL; + PdfDocument *pdf_document; + PopplerPage *poppler_page; + cairo_surface_t *surface; + + pdf_document = PDF_DOCUMENT (document_images); + poppler_page = poppler_document_get_page (pdf_document->document, + ev_image_get_page (image)); + + surface = poppler_page_get_image (poppler_page, ev_image_get_id (image)); + if (surface) { + retval = ev_document_misc_pixbuf_from_surface (surface); + cairo_surface_destroy (surface); + } + + g_object_unref (poppler_page); + + return retval; +} + +static void +pdf_document_document_images_iface_init (EvDocumentImagesInterface *iface) +{ + iface->get_image_mapping = pdf_document_images_get_image_mapping; + iface->get_image = pdf_document_images_get_image; +} + +static GdkPixbuf * +make_thumbnail_for_page (PopplerPage *poppler_page, + EvRenderContext *rc, + gint width, + gint height) +{ + GdkPixbuf *pixbuf; + +#ifdef POPPLER_WITH_GDK + 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, + rc->scale, rc->rotation, pixbuf); + ev_document_fc_mutex_unlock (); +#else + cairo_surface_t *surface; + + ev_document_fc_mutex_lock (); + surface = pdf_page_render (poppler_page, width, height, rc); + ev_document_fc_mutex_unlock (); + + pixbuf = ev_document_misc_pixbuf_from_surface (surface); + cairo_surface_destroy (surface); +#endif /* POPPLER_WITH_GDK */ + + return pixbuf; +} + +static GdkPixbuf * +pdf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails, + EvRenderContext *rc, + gboolean border) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document_thumbnails); + PopplerPage *poppler_page; + GdkPixbuf *pixbuf = NULL; + GdkPixbuf *border_pixbuf; + gint width, height; + + poppler_page = POPPLER_PAGE (rc->page->backend_page); + + pdf_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (pdf_document), + rc, &width, &height); + +#ifdef POPPLER_WITH_GDK + pixbuf = poppler_page_get_thumbnail_pixbuf (poppler_page); +#else + cairo_surface_t *surface; + + surface = poppler_page_get_thumbnail (poppler_page); + if (surface) { + pixbuf = ev_document_misc_pixbuf_from_surface (surface); + cairo_surface_destroy (surface); + } +#endif /* POPPLER_WITH_GDK */ + + if (pixbuf != NULL) { + int thumb_width = (rc->rotation == 90 || rc->rotation == 270) ? + gdk_pixbuf_get_height (pixbuf) : + gdk_pixbuf_get_width (pixbuf); + + if (thumb_width == width) { + GdkPixbuf *rotated_pixbuf; + + rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, + (GdkPixbufRotation) (360 - rc->rotation)); + g_object_unref (pixbuf); + pixbuf = rotated_pixbuf; + } else { + /* The provided thumbnail has a different size */ + g_object_unref (pixbuf); + pixbuf = make_thumbnail_for_page (poppler_page, rc, width, height); + } + } else { + /* There is no provided thumbnail. We need to make one. */ + pixbuf = make_thumbnail_for_page (poppler_page, rc, width, height); + } + + if (border && pixbuf) { + border_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf); + g_object_unref (pixbuf); + pixbuf = border_pixbuf; + } + + return pixbuf; +} + +static void +pdf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails, + EvRenderContext *rc, + gint *width, + gint *height) +{ + double page_width, page_height; + + poppler_page_get_size (POPPLER_PAGE (rc->page->backend_page), + &page_width, &page_height); + + *width = MAX ((gint)(page_width * rc->scale + 0.5), 1); + *height = MAX ((gint)(page_height * rc->scale + 0.5), 1); + + if (rc->rotation == 90 || rc->rotation == 270) { + gint temp; + + temp = *width; + *width = *height; + *height = temp; + } +} + +static void +pdf_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface) +{ + iface->get_thumbnail = pdf_document_thumbnails_get_thumbnail; + iface->get_dimensions = pdf_document_thumbnails_get_dimensions; +} + + +static GList * +pdf_document_find_find_text (EvDocumentFind *document_find, + EvPage *page, + const gchar *text, + gboolean case_sensitive) +{ + GList *matches, *l; + PopplerPage *poppler_page; + gdouble height; + GList *retval = NULL; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + g_return_val_if_fail (text != NULL, NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); + + matches = poppler_page_find_text (poppler_page, text); + if (!matches) + return NULL; + + poppler_page_get_size (poppler_page, NULL, &height); + for (l = matches; l && l->data; l = g_list_next (l)) { + PopplerRectangle *rect = (PopplerRectangle *)l->data; + EvRectangle *ev_rect; + + ev_rect = ev_rectangle_new (); + ev_rect->x1 = rect->x1; + ev_rect->x2 = rect->x2; + /* Invert this for X-style coordinates */ + ev_rect->y1 = height - rect->y2; + ev_rect->y2 = height - rect->y1; + + retval = g_list_prepend (retval, ev_rect); + } + + g_list_foreach (matches, (GFunc)poppler_rectangle_free, NULL); + g_list_free (matches); + + return g_list_reverse (retval); +} + +static void +pdf_document_find_iface_init (EvDocumentFindInterface *iface) +{ + iface->find_text = pdf_document_find_find_text; +} + +static void +pdf_print_context_free (PdfPrintContext *ctx) +{ + if (!ctx) + return; + +#ifdef HAVE_CAIRO_PRINT + if (ctx->cr) { + cairo_destroy (ctx->cr); + ctx->cr = NULL; + } +#else + if (ctx->ps_file) { + poppler_ps_file_free (ctx->ps_file); + ctx->ps_file = NULL; + } +#endif + g_free (ctx); +} + +static void +pdf_document_file_exporter_begin (EvFileExporter *exporter, + EvFileExporterContext *fc) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (exporter); + PdfPrintContext *ctx; +#ifdef HAVE_CAIRO_PRINT + gdouble width, height; + cairo_surface_t *surface = NULL; +#endif + + 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 = fc->format; + +#ifdef HAVE_CAIRO_PRINT + ctx->pages_per_sheet = CLAMP (fc->pages_per_sheet, 1, 16); + + ctx->paper_width = fc->paper_width; + ctx->paper_height = fc->paper_height; + + switch (fc->pages_per_sheet) { + default: + case 1: + ctx->pages_x = 1; + ctx->pages_y = 1; + break; + case 2: + ctx->pages_x = 1; + ctx->pages_y = 2; + break; + case 4: + ctx->pages_x = 2; + ctx->pages_y = 2; + break; + case 6: + ctx->pages_x = 2; + ctx->pages_y = 3; + break; + case 9: + ctx->pages_x = 3; + ctx->pages_y = 3; + break; + case 16: + ctx->pages_x = 4; + ctx->pages_y = 4; + break; + } + + ctx->pages_printed = 0; + + switch (fc->format) { + case EV_FILE_FORMAT_PS: +#ifdef HAVE_CAIRO_PS + surface = cairo_ps_surface_create (fc->filename, fc->paper_width, fc->paper_height); +#endif + break; + case EV_FILE_FORMAT_PDF: +#ifdef HAVE_CAIRO_PDF + surface = cairo_pdf_surface_create (fc->filename, fc->paper_width, fc->paper_height); +#endif + break; + default: + g_assert_not_reached (); + } + + ctx->cr = cairo_create (surface); + cairo_surface_destroy (surface); + +#else /* HAVE_CAIRO_PRINT */ + if (ctx->format == EV_FILE_FORMAT_PS) { + ctx->ps_file = poppler_ps_file_new (pdf_document->document, + fc->filename, fc->first_page, + fc->last_page - fc->first_page + 1); + poppler_ps_file_set_paper_size (ctx->ps_file, fc->paper_width, fc->paper_height); + poppler_ps_file_set_duplex (ctx->ps_file, fc->duplex); + } +#endif /* HAVE_CAIRO_PRINT */ +} + +static void +pdf_document_file_exporter_begin_page (EvFileExporter *exporter) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (exporter); + PdfPrintContext *ctx = pdf_document->print_ctx; + + g_return_if_fail (pdf_document->print_ctx != NULL); + + ctx->pages_printed = 0; + +#ifdef HAVE_CAIRO_PRINT + if (ctx->paper_width > ctx->paper_height) { + if (ctx->format == EV_FILE_FORMAT_PS) { + cairo_ps_surface_set_size (cairo_get_target (ctx->cr), + ctx->paper_height, + ctx->paper_width); + } else if (ctx->format == EV_FILE_FORMAT_PDF) { + cairo_pdf_surface_set_size (cairo_get_target (ctx->cr), + ctx->paper_height, + ctx->paper_width); + } + } +#endif /* HAVE_CAIRO_PRINT */ +} + +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; +#ifdef HAVE_CAIRO_PRINT + gdouble page_width, page_height; + gint x, y; + gboolean rotate; + gdouble width, height; + gdouble pwidth, pheight; + gdouble xscale, yscale; +#endif + + g_return_if_fail (pdf_document->print_ctx != NULL); + + poppler_page = POPPLER_PAGE (rc->page->backend_page); + +#ifdef HAVE_CAIRO_PRINT + x = (ctx->pages_printed % ctx->pages_per_sheet) % ctx->pages_x; + y = (ctx->pages_printed % ctx->pages_per_sheet) / ctx->pages_x; + poppler_page_get_size (poppler_page, &page_width, &page_height); + + if (page_width > page_height && page_width > ctx->paper_width) { + rotate = TRUE; + } else { + rotate = FALSE; + } + + /* Use always portrait mode and rotate when necessary */ + if (ctx->paper_width > ctx->paper_height) { + width = ctx->paper_height; + height = ctx->paper_width; + rotate = !rotate; + } else { + width = ctx->paper_width; + height = ctx->paper_height; + } + + if (ctx->pages_per_sheet == 2 || ctx->pages_per_sheet == 6) { + rotate = !rotate; + } + + if (rotate) { + gint tmp1; + gdouble tmp2; + + tmp1 = x; + x = y; + y = tmp1; + + tmp2 = page_width; + page_width = page_height; + page_height = tmp2; + } + + pwidth = width / ctx->pages_x; + pheight = height / ctx->pages_y; + + if ((page_width > pwidth || page_height > pheight) || + (page_width < pwidth && page_height < pheight)) { + xscale = pwidth / page_width; + yscale = pheight / page_height; + + if (yscale < xscale) { + xscale = yscale; + } else { + yscale = xscale; + } + + } else { + xscale = yscale = 1; + } + + /* TODO: center */ + + cairo_save (ctx->cr); + if (rotate) { + cairo_matrix_t matrix; + + cairo_translate (ctx->cr, (2 * y + 1) * pwidth, 0); + cairo_matrix_init (&matrix, + 0, 1, + -1, 0, + 0, 0); + cairo_transform (ctx->cr, &matrix); + } + + cairo_translate (ctx->cr, + x * (rotate ? pheight : pwidth), + y * (rotate ? pwidth : pheight)); + cairo_scale (ctx->cr, xscale, yscale); + + poppler_page_render_for_printing (poppler_page, ctx->cr); + + ctx->pages_printed++; + + cairo_restore (ctx->cr); +#else /* HAVE_CAIRO_PRINT */ + if (ctx->format == EV_FILE_FORMAT_PS) + poppler_page_render_to_ps (poppler_page, ctx->ps_file); +#endif /* HAVE_CAIRO_PRINT */ +} + +static void +pdf_document_file_exporter_end_page (EvFileExporter *exporter) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (exporter); + PdfPrintContext *ctx = pdf_document->print_ctx; + + g_return_if_fail (pdf_document->print_ctx != NULL); + +#ifdef HAVE_CAIRO_PRINT + cairo_show_page (ctx->cr); +#endif +} + +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 EvFileExporterCapabilities +pdf_document_file_exporter_get_capabilities (EvFileExporter *exporter) +{ + return (EvFileExporterCapabilities) ( + EV_FILE_EXPORTER_CAN_PAGE_SET | + EV_FILE_EXPORTER_CAN_COPIES | + EV_FILE_EXPORTER_CAN_COLLATE | + EV_FILE_EXPORTER_CAN_REVERSE | + EV_FILE_EXPORTER_CAN_SCALE | +#ifdef HAVE_CAIRO_PRINT + EV_FILE_EXPORTER_CAN_NUMBER_UP | +#endif + +#ifdef HAVE_CAIRO_PDF + EV_FILE_EXPORTER_CAN_GENERATE_PDF | +#endif + EV_FILE_EXPORTER_CAN_GENERATE_PS); +} + +static void +pdf_document_file_exporter_iface_init (EvFileExporterInterface *iface) +{ + iface->begin = pdf_document_file_exporter_begin; + iface->begin_page = pdf_document_file_exporter_begin_page; + iface->do_page = pdf_document_file_exporter_do_page; + iface->end_page = pdf_document_file_exporter_end_page; + iface->end = pdf_document_file_exporter_end; + iface->get_capabilities = pdf_document_file_exporter_get_capabilities; +} + +/* EvDocumentPrint */ +static void +pdf_document_print_print_page (EvDocumentPrint *document, + EvPage *page, + cairo_t *cr) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + + poppler_page_render_for_printing (POPPLER_PAGE (page->backend_page), cr); +} + +static void +pdf_document_document_print_iface_init (EvDocumentPrintInterface *iface) +{ + iface->print_page = pdf_document_print_print_page; +} + +static void +pdf_selection_render_selection (EvSelection *selection, + EvRenderContext *rc, + cairo_surface_t **surface, + EvRectangle *points, + EvRectangle *old_points, + EvSelectionStyle style, + GdkColor *text, + GdkColor *base) +{ + PopplerPage *poppler_page; + cairo_t *cr; + PopplerColor text_color, base_color; + double width_points, height_points; + gint width, height; + + poppler_page = POPPLER_PAGE (rc->page->backend_page); + + poppler_page_get_size (poppler_page, + &width_points, &height_points); + width = (int) ((width_points * rc->scale) + 0.5); + height = (int) ((height_points * rc->scale) + 0.5); + + text_color.red = text->red; + text_color.green = text->green; + text_color.blue = text->blue; + + base_color.red = base->red; + base_color.green = base->green; + base_color.blue = base->blue; + + if (*surface == NULL) { + *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + + } + + cr = cairo_create (*surface); + cairo_scale (cr, rc->scale, rc->scale); + cairo_surface_set_device_offset (*surface, 0, 0); + memset (cairo_image_surface_get_data (*surface), 0x00, + cairo_image_surface_get_height (*surface) * + cairo_image_surface_get_stride (*surface)); + poppler_page_render_selection (poppler_page, + cr, + (PopplerRectangle *)points, + (PopplerRectangle *)old_points, + (PopplerSelectionStyle)style, + &text_color, + &base_color); + cairo_destroy (cr); +} + +static gchar * +pdf_selection_get_selected_text (EvSelection *selection, + EvPage *page, + EvSelectionStyle style, + EvRectangle *points) +{ + PopplerPage *poppler_page; + char *retval; + + poppler_page = POPPLER_PAGE (page->backend_page); + +#ifdef HAVE_POPPLER_PAGE_GET_SELECTED_TEXT + retval = poppler_page_get_selected_text (poppler_page, + (PopplerSelectionStyle)style, + (PopplerRectangle *)points); +#else + PopplerRectangle r; + double height; + + poppler_page_get_size (poppler_page, NULL, &height); + r.x1 = points->x1; + r.y1 = height - points->y2; + r.x2 = points->x2; + r.y2 = height - points->y1; + + retval = poppler_page_get_text (poppler_page, + (PopplerSelectionStyle)style, + &r); +#endif /* HAVE_POPPLER_PAGE_GET_SELECTED_TEXT */ + + return retval; +} + +static cairo_region_t * +create_region_from_poppler_region (GList *region, gdouble scale) +{ + GList *l; + cairo_region_t *retval; + + retval = cairo_region_create (); + + for (l = region; l; l = g_list_next (l)) { + PopplerRectangle *rectangle; + cairo_rectangle_int_t rect; + + rectangle = (PopplerRectangle *)l->data; + + rect.x = (gint) ((rectangle->x1 * scale) + 0.5); + rect.y = (gint) ((rectangle->y1 * scale) + 0.5); + rect.width = (gint) (((rectangle->x2 - rectangle->x1) * scale) + 0.5); + rect.height = (gint) (((rectangle->y2 - rectangle->y1) * scale) + 0.5); + cairo_region_union_rectangle (retval, &rect); + + poppler_rectangle_free (rectangle); + } + + return retval; +} + +static cairo_region_t * +pdf_selection_get_selection_region (EvSelection *selection, + EvRenderContext *rc, + EvSelectionStyle style, + EvRectangle *points) +{ + PopplerPage *poppler_page; + cairo_region_t *retval; + GList *region; + + poppler_page = POPPLER_PAGE (rc->page->backend_page); + region = poppler_page_get_selection_region (poppler_page, + 1.0, + (PopplerSelectionStyle)style, + (PopplerRectangle *) points); + retval = create_region_from_poppler_region (region, rc->scale); + g_list_free (region); + + return retval; +} + +static void +pdf_selection_iface_init (EvSelectionInterface *iface) +{ + iface->render_selection = pdf_selection_render_selection; + iface->get_selected_text = pdf_selection_get_selected_text; + iface->get_selection_region = pdf_selection_get_selection_region; +} + + +/* EvDocumentText */ +static cairo_region_t * +pdf_document_text_get_text_mapping (EvDocumentText *document_text, + EvPage *page) +{ + PopplerPage *poppler_page; + PopplerRectangle points; + GList *region; + cairo_region_t *retval; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); + + points.x1 = 0.0; + points.y1 = 0.0; + poppler_page_get_size (poppler_page, &(points.x2), &(points.y2)); + + region = poppler_page_get_selection_region (poppler_page, 1.0, + POPPLER_SELECTION_GLYPH, + &points); + retval = create_region_from_poppler_region (region, 1.0); + g_list_free (region); + + return retval; +} + +#ifdef HAVE_POPPLER_PAGE_GET_SELECTED_TEXT +static gchar * +pdf_document_text_get_text (EvDocumentText *selection, + EvPage *page) +{ + PopplerPage *poppler_page; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); + + return poppler_page_get_text (poppler_page); +} +#else +static gchar * +pdf_document_text_get_text (EvDocumentText *selection, + EvPage *page) +{ + PopplerPage *poppler_page; + PopplerRectangle r; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); + + r.x1 = 0; + r.y1 = 0; + poppler_page_get_size (poppler_page, &(r.x2), &(r.y2)); + + return poppler_page_get_text (poppler_page, + POPPLER_SELECTION_WORD, + &r); +} +#endif /* HAVE_POPPLER_PAGE_GET_SELECTED_TEXT */ + +#ifdef HAVE_POPPLER_PAGE_GET_TEXT_LAYOUT +static gboolean +pdf_document_text_get_text_layout (EvDocumentText *selection, + EvPage *page, + EvRectangle **areas, + guint *n_areas) +{ + PopplerPage *poppler_page; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); + + return poppler_page_get_text_layout (poppler_page, (PopplerRectangle **)areas, n_areas); +} +#endif + +static void +pdf_document_text_iface_init (EvDocumentTextInterface *iface) +{ + iface->get_text_mapping = pdf_document_text_get_text_mapping; + iface->get_text = pdf_document_text_get_text; +#ifdef HAVE_POPPLER_PAGE_GET_TEXT_LAYOUT + iface->get_text_layout = pdf_document_text_get_text_layout; +#endif +} + +/* Page Transitions */ +static gdouble +pdf_document_get_page_duration (EvDocumentTransition *trans, + gint page) +{ + 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; +} + +static EvTransitionEffect * +pdf_document_get_effect (EvDocumentTransition *trans, + gint page) +{ + PdfDocument *pdf_document; + PopplerPage *poppler_page; + PopplerPageTransition *page_transition; + EvTransitionEffect *effect; + + pdf_document = PDF_DOCUMENT (trans); + poppler_page = poppler_document_get_page (pdf_document->document, page); + + if (!poppler_page) + return NULL; + + page_transition = poppler_page_get_transition (poppler_page); + + if (!page_transition) { + g_object_unref (poppler_page); + return NULL; + } + + /* enums in PopplerPageTransition match the EvTransitionEffect ones */ + effect = ev_transition_effect_new ((EvTransitionEffectType) page_transition->type, + "alignment", page_transition->alignment, + "direction", page_transition->direction, + "duration", page_transition->duration, + "angle", page_transition->angle, + "scale", page_transition->scale, + "rectangular", page_transition->rectangular, + NULL); + + poppler_page_transition_free (page_transition); + g_object_unref (poppler_page); + + return effect; +} + +static void +pdf_document_page_transition_iface_init (EvDocumentTransitionInterface *iface) +{ + iface->get_page_duration = pdf_document_get_page_duration; + iface->get_effect = pdf_document_get_effect; +} + +/* Forms */ +static void +pdf_document_get_crop_box (EvDocument *document, + int page, + EvRectangle *rect) +{ + PdfDocument *pdf_document; + PopplerPage *poppler_page; + PopplerRectangle poppler_rect; + + pdf_document = PDF_DOCUMENT (document); + poppler_page = poppler_document_get_page (pdf_document->document, page); + poppler_page_get_crop_box (poppler_page, &poppler_rect); + rect->x1 = poppler_rect.x1; + rect->x2 = poppler_rect.x2; + rect->y1 = poppler_rect.y1; + rect->y2 = poppler_rect.y2; +} + +static EvFormField * +ev_form_field_from_poppler_field (PopplerFormField *poppler_field) +{ + EvFormField *ev_field = NULL; + gint id; + gdouble font_size; + gboolean is_read_only; + + id = poppler_form_field_get_id (poppler_field); + font_size = poppler_form_field_get_font_size (poppler_field); + is_read_only = poppler_form_field_is_read_only (poppler_field); + + switch (poppler_form_field_get_field_type (poppler_field)) { + case POPPLER_FORM_FIELD_TEXT: { + EvFormFieldText *field_text; + EvFormFieldTextType ev_text_type = EV_FORM_FIELD_TEXT_NORMAL; + + switch (poppler_form_field_text_get_text_type (poppler_field)) { + case POPPLER_FORM_TEXT_NORMAL: + ev_text_type = EV_FORM_FIELD_TEXT_NORMAL; + break; + case POPPLER_FORM_TEXT_MULTILINE: + ev_text_type = EV_FORM_FIELD_TEXT_MULTILINE; + break; + case POPPLER_FORM_TEXT_FILE_SELECT: + ev_text_type = EV_FORM_FIELD_TEXT_FILE_SELECT; + break; + } + + ev_field = ev_form_field_text_new (id, ev_text_type); + field_text = EV_FORM_FIELD_TEXT (ev_field); + + field_text->do_spell_check = poppler_form_field_text_do_spell_check (poppler_field); + field_text->do_scroll = poppler_form_field_text_do_scroll (poppler_field); + field_text->is_rich_text = poppler_form_field_text_is_rich_text (poppler_field); + field_text->is_password = poppler_form_field_text_is_password (poppler_field); + field_text->max_len = poppler_form_field_text_get_max_len (poppler_field); + field_text->text = poppler_form_field_text_get_text (poppler_field); + + } + break; + case POPPLER_FORM_FIELD_BUTTON: { + EvFormFieldButton *field_button; + EvFormFieldButtonType ev_button_type = EV_FORM_FIELD_BUTTON_PUSH; + + switch (poppler_form_field_button_get_button_type (poppler_field)) { + case POPPLER_FORM_BUTTON_PUSH: + ev_button_type = EV_FORM_FIELD_BUTTON_PUSH; + break; + case POPPLER_FORM_BUTTON_CHECK: + ev_button_type = EV_FORM_FIELD_BUTTON_CHECK; + break; + case POPPLER_FORM_BUTTON_RADIO: + ev_button_type = EV_FORM_FIELD_BUTTON_RADIO; + break; + } + + ev_field = ev_form_field_button_new (id, ev_button_type); + field_button = EV_FORM_FIELD_BUTTON (ev_field); + + field_button->state = poppler_form_field_button_get_state (poppler_field); + } + break; + case POPPLER_FORM_FIELD_CHOICE: { + EvFormFieldChoice *field_choice; + EvFormFieldChoiceType ev_choice_type = EV_FORM_FIELD_CHOICE_COMBO; + + switch (poppler_form_field_choice_get_choice_type (poppler_field)) { + case POPPLER_FORM_CHOICE_COMBO: + ev_choice_type = EV_FORM_FIELD_CHOICE_COMBO; + break; + case EV_FORM_FIELD_CHOICE_LIST: + ev_choice_type = EV_FORM_FIELD_CHOICE_LIST; + break; + } + + ev_field = ev_form_field_choice_new (id, ev_choice_type); + field_choice = EV_FORM_FIELD_CHOICE (ev_field); + + field_choice->is_editable = poppler_form_field_choice_is_editable (poppler_field); + field_choice->multi_select = poppler_form_field_choice_can_select_multiple (poppler_field); + field_choice->do_spell_check = poppler_form_field_choice_do_spell_check (poppler_field); + field_choice->commit_on_sel_change = poppler_form_field_choice_commit_on_change (poppler_field); + + /* TODO: we need poppler_form_field_choice_get_selected_items in poppler + field_choice->selected_items = poppler_form_field_choice_get_selected_items (poppler_field);*/ + if (field_choice->is_editable) + field_choice->text = poppler_form_field_choice_get_text (poppler_field); + } + break; + case POPPLER_FORM_FIELD_SIGNATURE: + /* TODO */ + ev_field = ev_form_field_signature_new (id); + break; + case POPPLER_FORM_FIELD_UNKNOWN: + return NULL; + } + + ev_field->font_size = font_size; + ev_field->is_read_only = is_read_only; + + return ev_field; +} + +static EvMappingList * +pdf_document_forms_get_form_fields (EvDocumentForms *document, + EvPage *page) +{ + PopplerPage *poppler_page; + GList *retval = NULL; + GList *fields; + GList *list; + double height; + + g_return_val_if_fail (POPPLER_IS_PAGE (page->backend_page), NULL); + + poppler_page = POPPLER_PAGE (page->backend_page); + fields = poppler_page_get_form_field_mapping (poppler_page); + poppler_page_get_size (poppler_page, NULL, &height); + + for (list = fields; list; list = list->next) { + PopplerFormFieldMapping *mapping; + EvMapping *field_mapping; + EvFormField *ev_field; + + mapping = (PopplerFormFieldMapping *)list->data; + + ev_field = ev_form_field_from_poppler_field (mapping->field); + if (!ev_field) + continue; + + field_mapping = g_new0 (EvMapping, 1); + field_mapping->area.x1 = mapping->area.x1; + field_mapping->area.x2 = mapping->area.x2; + field_mapping->area.y1 = height - mapping->area.y2; + field_mapping->area.y2 = height - mapping->area.y1; + field_mapping->data = ev_field; + ev_field->page = EV_PAGE (g_object_ref (page)); + + g_object_set_data_full (G_OBJECT (ev_field), + "poppler-field", + g_object_ref (mapping->field), + (GDestroyNotify) g_object_unref); + + retval = g_list_prepend (retval, field_mapping); + } + + poppler_page_free_form_field_mapping (fields); + + return retval ? ev_mapping_list_new (page->index, + g_list_reverse (retval), + (GDestroyNotify)g_object_unref) : NULL; +} + +static gboolean +pdf_document_forms_document_is_modified (EvDocumentForms *document) +{ + return PDF_DOCUMENT (document)->forms_modified; +} + +static gchar * +pdf_document_forms_form_field_text_get_text (EvDocumentForms *document, + EvFormField *field) + +{ + PopplerFormField *poppler_field; + gchar *text; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return NULL; + + text = poppler_form_field_text_get_text (poppler_field); + + return text; +} + +static void +pdf_document_forms_form_field_text_set_text (EvDocumentForms *document, + EvFormField *field, + const gchar *text) +{ + PopplerFormField *poppler_field; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return; + + poppler_form_field_text_set_text (poppler_field, text); + PDF_DOCUMENT (document)->forms_modified = TRUE; +} + +static void +pdf_document_forms_form_field_button_set_state (EvDocumentForms *document, + EvFormField *field, + gboolean state) +{ + PopplerFormField *poppler_field; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return; + + poppler_form_field_button_set_state (poppler_field, state); + PDF_DOCUMENT (document)->forms_modified = TRUE; +} + +static gboolean +pdf_document_forms_form_field_button_get_state (EvDocumentForms *document, + EvFormField *field) +{ + PopplerFormField *poppler_field; + gboolean state; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return FALSE; + + state = poppler_form_field_button_get_state (poppler_field); + + return state; +} + +static gchar * +pdf_document_forms_form_field_choice_get_item (EvDocumentForms *document, + EvFormField *field, + gint index) +{ + PopplerFormField *poppler_field; + gchar *text; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return NULL; + + text = poppler_form_field_choice_get_item (poppler_field, index); + + return text; +} + +static int +pdf_document_forms_form_field_choice_get_n_items (EvDocumentForms *document, + EvFormField *field) +{ + PopplerFormField *poppler_field; + gint n_items; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return -1; + + n_items = poppler_form_field_choice_get_n_items (poppler_field); + + return n_items; +} + +static gboolean +pdf_document_forms_form_field_choice_is_item_selected (EvDocumentForms *document, + EvFormField *field, + gint index) +{ + PopplerFormField *poppler_field; + gboolean selected; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return FALSE; + + selected = poppler_form_field_choice_is_item_selected (poppler_field, index); + + return selected; +} + +static void +pdf_document_forms_form_field_choice_select_item (EvDocumentForms *document, + EvFormField *field, + gint index) +{ + PopplerFormField *poppler_field; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return; + + poppler_form_field_choice_select_item (poppler_field, index); + PDF_DOCUMENT (document)->forms_modified = TRUE; +} + +static void +pdf_document_forms_form_field_choice_toggle_item (EvDocumentForms *document, + EvFormField *field, + gint index) +{ + PopplerFormField *poppler_field; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return; + + poppler_form_field_choice_toggle_item (poppler_field, index); + PDF_DOCUMENT (document)->forms_modified = TRUE; +} + +static void +pdf_document_forms_form_field_choice_unselect_all (EvDocumentForms *document, + EvFormField *field) +{ + PopplerFormField *poppler_field; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return; + + poppler_form_field_choice_unselect_all (poppler_field); + PDF_DOCUMENT (document)->forms_modified = TRUE; +} + +static void +pdf_document_forms_form_field_choice_set_text (EvDocumentForms *document, + EvFormField *field, + const gchar *text) +{ + PopplerFormField *poppler_field; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return; + + poppler_form_field_choice_set_text (poppler_field, text); + PDF_DOCUMENT (document)->forms_modified = TRUE; +} + +static gchar * +pdf_document_forms_form_field_choice_get_text (EvDocumentForms *document, + EvFormField *field) +{ + PopplerFormField *poppler_field; + gchar *text; + + poppler_field = POPPLER_FORM_FIELD (g_object_get_data (G_OBJECT (field), "poppler-field")); + if (!poppler_field) + return NULL; + + text = poppler_form_field_choice_get_text (poppler_field); + + return text; +} + +static void +pdf_document_document_forms_iface_init (EvDocumentFormsInterface *iface) +{ + iface->get_form_fields = pdf_document_forms_get_form_fields; + iface->document_is_modified = pdf_document_forms_document_is_modified; + iface->form_field_text_get_text = pdf_document_forms_form_field_text_get_text; + iface->form_field_text_set_text = pdf_document_forms_form_field_text_set_text; + iface->form_field_button_set_state = pdf_document_forms_form_field_button_set_state; + iface->form_field_button_get_state = pdf_document_forms_form_field_button_get_state; + iface->form_field_choice_get_item = pdf_document_forms_form_field_choice_get_item; + iface->form_field_choice_get_n_items = pdf_document_forms_form_field_choice_get_n_items; + iface->form_field_choice_is_item_selected = pdf_document_forms_form_field_choice_is_item_selected; + iface->form_field_choice_select_item = pdf_document_forms_form_field_choice_select_item; + iface->form_field_choice_toggle_item = pdf_document_forms_form_field_choice_toggle_item; + iface->form_field_choice_unselect_all = pdf_document_forms_form_field_choice_unselect_all; + iface->form_field_choice_set_text = pdf_document_forms_form_field_choice_set_text; + iface->form_field_choice_get_text = pdf_document_forms_form_field_choice_get_text; +} + +/* Annotations */ +static void +poppler_annot_color_to_gdk_color (PopplerAnnot *poppler_annot, + GdkColor *color) +{ + PopplerColor *poppler_color; + + poppler_color = poppler_annot_get_color (poppler_annot); + if (poppler_color) { + color->red = poppler_color->red; + color->green = poppler_color->green; + color->blue = poppler_color->blue; + + g_free (poppler_color); + } /* TODO: else use a default color */ +} + +static EvAnnotationTextIcon +get_annot_text_icon (PopplerAnnotText *poppler_annot) +{ +#ifdef HAVE_POPPLER_PAGE_ADD_ANNOT + gchar *icon = poppler_annot_text_get_icon (poppler_annot); + EvAnnotationTextIcon retval; + + if (!icon) + return EV_ANNOTATION_TEXT_ICON_UNKNOWN; + + if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_NOTE) == 0) + retval = EV_ANNOTATION_TEXT_ICON_NOTE; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_COMMENT) == 0) + retval = EV_ANNOTATION_TEXT_ICON_COMMENT; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_KEY) == 0) + retval = EV_ANNOTATION_TEXT_ICON_KEY; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_HELP) == 0) + retval = EV_ANNOTATION_TEXT_ICON_HELP; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_NEW_PARAGRAPH) == 0) + retval = EV_ANNOTATION_TEXT_ICON_NEW_PARAGRAPH; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_PARAGRAPH) == 0) + retval = EV_ANNOTATION_TEXT_ICON_PARAGRAPH; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_INSERT) == 0) + retval = EV_ANNOTATION_TEXT_ICON_INSERT; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_CROSS) == 0) + retval = EV_ANNOTATION_TEXT_ICON_CROSS; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_CIRCLE) == 0) + retval = EV_ANNOTATION_TEXT_ICON_CIRCLE; + else + retval = EV_ANNOTATION_TEXT_ICON_UNKNOWN; + + g_free (icon); + + return retval; +#else + return EV_ANNOTATION_TEXT_ICON_UNKNOWN; +#endif +} + +static const gchar * +get_poppler_annot_text_icon (EvAnnotationTextIcon icon) +{ +#ifdef HAVE_POPPLER_PAGE_ADD_ANNOT + switch (icon) { + case EV_ANNOTATION_TEXT_ICON_NOTE: + return POPPLER_ANNOT_TEXT_ICON_NOTE; + case EV_ANNOTATION_TEXT_ICON_COMMENT: + return POPPLER_ANNOT_TEXT_ICON_COMMENT; + case EV_ANNOTATION_TEXT_ICON_KEY: + return POPPLER_ANNOT_TEXT_ICON_KEY; + case EV_ANNOTATION_TEXT_ICON_HELP: + return POPPLER_ANNOT_TEXT_ICON_HELP; + case EV_ANNOTATION_TEXT_ICON_NEW_PARAGRAPH: + return POPPLER_ANNOT_TEXT_ICON_NEW_PARAGRAPH; + case EV_ANNOTATION_TEXT_ICON_PARAGRAPH: + return POPPLER_ANNOT_TEXT_ICON_PARAGRAPH; + case EV_ANNOTATION_TEXT_ICON_INSERT: + return POPPLER_ANNOT_TEXT_ICON_INSERT; + case EV_ANNOTATION_TEXT_ICON_CROSS: + return POPPLER_ANNOT_TEXT_ICON_CROSS; + case EV_ANNOTATION_TEXT_ICON_CIRCLE: + return POPPLER_ANNOT_TEXT_ICON_CIRCLE; + case EV_ANNOTATION_TEXT_ICON_UNKNOWN: + default: + return POPPLER_ANNOT_TEXT_ICON_NOTE; + } +#else + return "Note"; +#endif +} + +static EvAnnotation * +ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, + EvPage *page) +{ + EvAnnotation *ev_annot = NULL; + const gchar *unimplemented_annot = NULL; + + switch (poppler_annot_get_annot_type (poppler_annot)) { + case POPPLER_ANNOT_TEXT: { + PopplerAnnotText *poppler_text; + EvAnnotationText *ev_annot_text; + + poppler_text = POPPLER_ANNOT_TEXT (poppler_annot); + + ev_annot = ev_annotation_text_new (page); + + ev_annot_text = EV_ANNOTATION_TEXT (ev_annot); + ev_annotation_text_set_is_open (ev_annot_text, + poppler_annot_text_get_is_open (poppler_text)); + ev_annotation_text_set_icon (ev_annot_text, get_annot_text_icon (poppler_text)); + } + break; + case POPPLER_ANNOT_FILE_ATTACHMENT: { + PopplerAnnotFileAttachment *poppler_annot_attachment; + EvAnnotationAttachment *ev_annot_attachment; + PopplerAttachment *poppler_attachment; + gchar *data = NULL; + gsize size; + GError *error = NULL; + + poppler_annot_attachment = POPPLER_ANNOT_FILE_ATTACHMENT (poppler_annot); + poppler_attachment = poppler_annot_file_attachment_get_attachment (poppler_annot_attachment); + + if (poppler_attachment && + attachment_save_to_buffer (poppler_attachment, &data, &size, &error)) { + EvAttachment *ev_attachment; + + ev_attachment = ev_attachment_new (poppler_attachment->name, + poppler_attachment->description, + poppler_attachment->mtime, + poppler_attachment->ctime, + size, data); + ev_annot = ev_annotation_attachment_new (page, ev_attachment); + g_object_unref (ev_attachment); + } else if (error) { + g_warning ("%s", error->message); + g_error_free (error); + } + + if (poppler_attachment) + g_object_unref (poppler_attachment); + } + break; + case POPPLER_ANNOT_LINK: + case POPPLER_ANNOT_WIDGET: + /* Ignore link and widgets annots since they are already handled */ + break; + default: { + GEnumValue *enum_value; + + enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (POPPLER_TYPE_ANNOT_TYPE), + poppler_annot_get_annot_type (poppler_annot)); + unimplemented_annot = enum_value ? enum_value->value_name : "Unknown annotation"; + } + } + + if (unimplemented_annot) { + g_warning ("Unimplemented annotation: %s, please post a " + "bug report in Evince bugzilla " + "(http://bugzilla.mate.org) with a testcase.", + unimplemented_annot); + } + + if (ev_annot) { + time_t utime; + gchar *modified; + gchar *contents; + gchar *name; + GdkColor color; + + contents = poppler_annot_get_contents (poppler_annot); + if (contents) { + ev_annotation_set_contents (ev_annot, contents); + g_free (contents); + } + + name = poppler_annot_get_name (poppler_annot); + if (name) { + ev_annotation_set_name (ev_annot, name); + g_free (name); + } + + modified = poppler_annot_get_modified (poppler_annot); + if (poppler_date_parse (modified, &utime)) { + ev_annotation_set_modified_from_time (ev_annot, utime); + } else { + ev_annotation_set_modified (ev_annot, modified); + } + g_free (modified); + + poppler_annot_color_to_gdk_color (poppler_annot, &color); + ev_annotation_set_color (ev_annot, &color); + + if (POPPLER_IS_ANNOT_MARKUP (poppler_annot)) { + PopplerAnnotMarkup *markup; + gchar *label; + gdouble opacity; + PopplerRectangle poppler_rect; + + markup = POPPLER_ANNOT_MARKUP (poppler_annot); + + if (poppler_annot_markup_get_popup_rectangle (markup, &poppler_rect)) { + EvRectangle ev_rect; + gboolean is_open; + gdouble height; + + poppler_page_get_size (POPPLER_PAGE (page->backend_page), + NULL, &height); + ev_rect.x1 = poppler_rect.x1; + ev_rect.x2 = poppler_rect.x2; + ev_rect.y1 = height - poppler_rect.y2; + ev_rect.y2 = height - poppler_rect.y1; + + is_open = poppler_annot_markup_get_popup_is_open (markup); + + g_object_set (ev_annot, + "rectangle", &ev_rect, + "popup_is_open", is_open, + "has_popup", TRUE, + NULL); + } else { + g_object_set (ev_annot, + "has_popup", FALSE, + NULL); + } + + label = poppler_annot_markup_get_label (markup); + opacity = poppler_annot_markup_get_opacity (markup); + + g_object_set (ev_annot, + "label", label, + "opacity", opacity, + NULL); + + g_free (label); + } + } + + return ev_annot; +} + +static EvMappingList * +pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annotations, + EvPage *page) +{ + GList *retval = NULL; + PdfDocument *pdf_document; + PopplerPage *poppler_page; + EvMappingList *mapping_list; + GList *annots; + GList *list; + gdouble height; + gint i = 0; + + pdf_document = PDF_DOCUMENT (document_annotations); + poppler_page = POPPLER_PAGE (page->backend_page); + + if (pdf_document->annots) { + mapping_list = (EvMappingList *)g_hash_table_lookup (pdf_document->annots, + GINT_TO_POINTER (page->index)); + if (mapping_list) + return ev_mapping_list_ref (mapping_list); + } + + annots = poppler_page_get_annot_mapping (poppler_page); + poppler_page_get_size (poppler_page, NULL, &height); + + for (list = annots; list; list = list->next) { + PopplerAnnotMapping *mapping; + EvMapping *annot_mapping; + EvAnnotation *ev_annot; + + mapping = (PopplerAnnotMapping *)list->data; + + ev_annot = ev_annot_from_poppler_annot (mapping->annot, page); + if (!ev_annot) + continue; + + i++; + + /* Make sure annot has a unique name */ + if (!ev_annotation_get_name (ev_annot)) { + gchar *name = g_strdup_printf ("annot-%d-%d", page->index, i); + + ev_annotation_set_name (ev_annot, name); + g_free (name); + } + + annot_mapping = g_new (EvMapping, 1); + annot_mapping->area.x1 = mapping->area.x1; + annot_mapping->area.x2 = mapping->area.x2; + annot_mapping->area.y1 = height - mapping->area.y2; + annot_mapping->area.y2 = height - mapping->area.y1; + annot_mapping->data = ev_annot; + + g_object_set_data_full (G_OBJECT (ev_annot), + "poppler-annot", + g_object_ref (mapping->annot), + (GDestroyNotify) g_object_unref); + + retval = g_list_prepend (retval, annot_mapping); + } + + poppler_page_free_annot_mapping (annots); + + if (!retval) + return NULL; + + if (!pdf_document->annots) { + pdf_document->annots = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + (GDestroyNotify)NULL, + (GDestroyNotify)ev_mapping_list_unref); + } + + mapping_list = ev_mapping_list_new (page->index, g_list_reverse (retval), (GDestroyNotify)g_object_unref); + g_hash_table_insert (pdf_document->annots, + GINT_TO_POINTER (page->index), + ev_mapping_list_ref (mapping_list)); + + return mapping_list; +} + +static gboolean +pdf_document_annotations_document_is_modified (EvDocumentAnnotations *document_annotations) +{ + return PDF_DOCUMENT (document_annotations)->annots_modified; +} + +#ifdef HAVE_POPPLER_PAGE_ADD_ANNOT +static void +pdf_document_annotations_add_annotation (EvDocumentAnnotations *document_annotations, + EvAnnotation *annot, + EvRectangle *rect) +{ + PopplerAnnot *poppler_annot; + PdfDocument *pdf_document; + EvPage *page; + PopplerPage *poppler_page; + GList *list = NULL; + EvMappingList *mapping_list; + EvMapping *annot_mapping; + PopplerRectangle poppler_rect; + gdouble height; + PopplerColor poppler_color; + GdkColor color; + time_t utime; + gchar *modified; + gchar *name; + + pdf_document = PDF_DOCUMENT (document_annotations); + page = ev_annotation_get_page (annot); + poppler_page = POPPLER_PAGE (page->backend_page); + + poppler_page_get_size (poppler_page, NULL, &height); + poppler_rect.x1 = rect->x1; + poppler_rect.x2 = rect->x2; + poppler_rect.y1 = height - rect->y2; + poppler_rect.y2 = height - rect->y1; + poppler_annot = poppler_annot_text_new (pdf_document->document, &poppler_rect); + + ev_annotation_get_color (annot, &color); + poppler_color.red = color.red; + poppler_color.green = color.green; + poppler_color.blue = color.blue; + poppler_annot_set_color (poppler_annot, &poppler_color); + + if (EV_IS_ANNOTATION_MARKUP (annot)) { + EvAnnotationMarkup *markup = EV_ANNOTATION_MARKUP (annot); + const gchar *label; + + if (ev_annotation_markup_has_popup (markup)) { + EvRectangle popup_rect; + + ev_annotation_markup_get_rectangle (markup, &popup_rect); + poppler_rect.x1 = popup_rect.x1; + poppler_rect.x2 = popup_rect.x2; + poppler_rect.y1 = height - popup_rect.y2; + poppler_rect.y2 = height - popup_rect.y1; + poppler_annot_markup_set_popup (POPPLER_ANNOT_MARKUP (poppler_annot), &poppler_rect); + poppler_annot_markup_set_popup_is_open (POPPLER_ANNOT_MARKUP (poppler_annot), + ev_annotation_markup_get_popup_is_open (markup)); + } + + label = ev_annotation_markup_get_label (markup); + if (label) + poppler_annot_markup_set_label (POPPLER_ANNOT_MARKUP (poppler_annot), label); + } + + if (EV_IS_ANNOTATION_TEXT (annot)) { + EvAnnotationText *text = EV_ANNOTATION_TEXT (annot); + EvAnnotationTextIcon icon; + + icon = ev_annotation_text_get_icon (text); + poppler_annot_text_set_icon (POPPLER_ANNOT_TEXT (poppler_annot), + get_poppler_annot_text_icon (icon)); + } + poppler_page_add_annot (poppler_page, poppler_annot); + + annot_mapping = g_new (EvMapping, 1); + annot_mapping->area = *rect; + annot_mapping->data = annot; + g_object_set_data_full (G_OBJECT (annot), + "poppler-annot", + g_object_ref (poppler_annot), + (GDestroyNotify) g_object_unref); + + if (pdf_document->annots) { + mapping_list = (EvMappingList *)g_hash_table_lookup (pdf_document->annots, + GINT_TO_POINTER (page->index)); + list = ev_mapping_list_get_list (mapping_list); + name = g_strdup_printf ("annot-%d-%d", page->index, g_list_length (list) + 1); + ev_annotation_set_name (annot, name); + g_free (name); + list = g_list_append (list, annot_mapping); + } else { + pdf_document->annots = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + (GDestroyNotify)NULL, + (GDestroyNotify)ev_mapping_list_unref); + name = g_strdup_printf ("annot-%d-0", page->index); + ev_annotation_set_name (annot, name); + g_free (name); + list = g_list_append (list, annot_mapping); + mapping_list = ev_mapping_list_new (page->index, list, (GDestroyNotify)g_object_unref); + g_hash_table_insert (pdf_document->annots, + GINT_TO_POINTER (page->index), + ev_mapping_list_ref (mapping_list)); + } + + pdf_document->annots_modified = TRUE; +} +#endif /* HAVE_POPPLER_PAGE_ADD_ANNOT */ + +static void +pdf_document_annotations_save_annotation (EvDocumentAnnotations *document_annotations, + EvAnnotation *annot, + EvAnnotationsSaveMask mask) +{ + PopplerAnnot *poppler_annot; + + poppler_annot = POPPLER_ANNOT (g_object_get_data (G_OBJECT (annot), "poppler-annot")); + if (!poppler_annot) + return; + + if (mask & EV_ANNOTATIONS_SAVE_CONTENTS) + poppler_annot_set_contents (poppler_annot, + ev_annotation_get_contents (annot)); + +#ifdef HAVE_POPPLER_PAGE_ADD_ANNOT + if (mask & EV_ANNOTATIONS_SAVE_COLOR) { + PopplerColor color; + GdkColor ev_color; + + ev_annotation_get_color (annot, &ev_color); + color.red = ev_color.red; + color.green = ev_color.green; + color.blue = ev_color.blue; + poppler_annot_set_color (poppler_annot, &color); + } + + if (EV_IS_ANNOTATION_MARKUP (annot)) { + EvAnnotationMarkup *ev_markup = EV_ANNOTATION_MARKUP (annot); + PopplerAnnotMarkup *markup = POPPLER_ANNOT_MARKUP (poppler_annot); + + if (mask & EV_ANNOTATIONS_SAVE_LABEL) + poppler_annot_markup_set_label (markup, ev_annotation_markup_get_label (ev_markup)); + if (mask & EV_ANNOTATIONS_SAVE_OPACITY) + poppler_annot_markup_set_opacity (markup, ev_annotation_markup_get_opacity (ev_markup)); + if (mask & EV_ANNOTATIONS_SAVE_POPUP_IS_OPEN) + poppler_annot_markup_set_popup_is_open (markup, ev_annotation_markup_get_popup_is_open (ev_markup)); + } + + if (EV_IS_ANNOTATION_TEXT (annot)) { + EvAnnotationText *ev_text = EV_ANNOTATION_TEXT (annot); + PopplerAnnotText *text = POPPLER_ANNOT_TEXT (poppler_annot); + + if (mask & EV_ANNOTATIONS_SAVE_TEXT_IS_OPEN) { + poppler_annot_text_set_is_open (text, + ev_annotation_text_get_is_open (ev_text)); + } + if (mask & EV_ANNOTATIONS_SAVE_TEXT_ICON) { + EvAnnotationTextIcon icon; + + icon = ev_annotation_text_get_icon (ev_text); + poppler_annot_text_set_icon (text, get_poppler_annot_text_icon (icon)); + } + } +#endif /* HAVE_POPPLER_PAGE_ADD_ANNOT */ + PDF_DOCUMENT (document_annotations)->annots_modified = TRUE; +} + +static void +pdf_document_document_annotations_iface_init (EvDocumentAnnotationsInterface *iface) +{ + iface->get_annotations = pdf_document_annotations_get_annotations; + iface->document_is_modified = pdf_document_annotations_document_is_modified; +#ifdef HAVE_POPPLER_PAGE_ADD_ANNOT + iface->add_annotation = pdf_document_annotations_add_annotation; +#endif + iface->save_annotation = pdf_document_annotations_save_annotation; +} + +/* Attachments */ +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_attachments_get_attachments (EvDocumentAttachments *document) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + GList *attachments; + GList *list; + GList *retval = 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 gboolean +pdf_document_attachments_has_attachments (EvDocumentAttachments *document) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + + return poppler_document_has_attachments (pdf_document->document); +} + +static void +pdf_document_document_attachments_iface_init (EvDocumentAttachmentsInterface *iface) +{ + iface->has_attachments = pdf_document_attachments_has_attachments; + iface->get_attachments = pdf_document_attachments_get_attachments; +} + +/* Layers */ +static gboolean +pdf_document_layers_has_layers (EvDocumentLayers *document) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + PopplerLayersIter *iter; + + iter = poppler_layers_iter_new (pdf_document->document); + if (!iter) + return FALSE; + poppler_layers_iter_free (iter); + + return TRUE; +} + +static void +build_layers_tree (PdfDocument *pdf_document, + GtkTreeModel *model, + GtkTreeIter *parent, + PopplerLayersIter *iter) +{ + do { + GtkTreeIter tree_iter; + PopplerLayersIter *child; + PopplerLayer *layer; + EvLayer *ev_layer = NULL; + gboolean visible; + gchar *markup; + gint rb_group = 0; + + layer = poppler_layers_iter_get_layer (iter); + if (layer) { + markup = g_markup_escape_text (poppler_layer_get_title (layer), -1); + visible = poppler_layer_is_visible (layer); + rb_group = poppler_layer_get_radio_button_group_id (layer); + pdf_document->layers = g_list_append (pdf_document->layers, + g_object_ref (layer)); + ev_layer = ev_layer_new (g_list_length (pdf_document->layers) - 1, + poppler_layer_is_parent (layer), + rb_group); + } else { + gchar *title; + + title = poppler_layers_iter_get_title (iter); + markup = g_markup_escape_text (title, -1); + g_free (title); + + visible = FALSE; + layer = NULL; + } + + gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent); + gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter, + EV_DOCUMENT_LAYERS_COLUMN_TITLE, markup, + EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, visible, + EV_DOCUMENT_LAYERS_COLUMN_ENABLED, TRUE, /* FIXME */ + EV_DOCUMENT_LAYERS_COLUMN_SHOWTOGGLE, (layer != NULL), + EV_DOCUMENT_LAYERS_COLUMN_RBGROUP, rb_group, + EV_DOCUMENT_LAYERS_COLUMN_LAYER, ev_layer, + -1); + if (ev_layer) + g_object_unref (ev_layer); + g_free (markup); + + child = poppler_layers_iter_get_child (iter); + if (child) + build_layers_tree (pdf_document, model, &tree_iter, child); + poppler_layers_iter_free (child); + } while (poppler_layers_iter_next (iter)); +} + +static GtkTreeModel * +pdf_document_layers_get_layers (EvDocumentLayers *document) +{ + GtkTreeModel *model = NULL; + PdfDocument *pdf_document = PDF_DOCUMENT (document); + PopplerLayersIter *iter; + + iter = poppler_layers_iter_new (pdf_document->document); + if (iter) { + model = (GtkTreeModel *) gtk_tree_store_new (EV_DOCUMENT_LAYERS_N_COLUMNS, + G_TYPE_STRING, /* TITLE */ + G_TYPE_OBJECT, /* LAYER */ + G_TYPE_BOOLEAN, /* VISIBLE */ + G_TYPE_BOOLEAN, /* ENABLED */ + G_TYPE_BOOLEAN, /* SHOWTOGGLE */ + G_TYPE_INT); /* RBGROUP */ + build_layers_tree (pdf_document, model, NULL, iter); + poppler_layers_iter_free (iter); + } + return model; +} + +static void +pdf_document_layers_show_layer (EvDocumentLayers *document, + EvLayer *layer) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + guint layer_id = ev_layer_get_id (layer); + + poppler_layer_show (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); +} + +static void +pdf_document_layers_hide_layer (EvDocumentLayers *document, + EvLayer *layer) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + guint layer_id = ev_layer_get_id (layer); + + poppler_layer_hide (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); +} + +static gboolean +pdf_document_layers_layer_is_visible (EvDocumentLayers *document, + EvLayer *layer) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + guint layer_id = ev_layer_get_id (layer); + + return poppler_layer_is_visible (POPPLER_LAYER (g_list_nth_data (pdf_document->layers, layer_id))); +} + +static void +pdf_document_document_layers_iface_init (EvDocumentLayersInterface *iface) +{ + iface->has_layers = pdf_document_layers_has_layers; + iface->get_layers = pdf_document_layers_get_layers; + iface->show_layer = pdf_document_layers_show_layer; + iface->hide_layer = pdf_document_layers_hide_layer; + iface->layer_is_visible = pdf_document_layers_layer_is_visible; +} diff --git a/backend/pdf/ev-poppler.h b/backend/pdf/ev-poppler.h new file mode 100644 index 00000000..f37f2268 --- /dev/null +++ b/backend/pdf/ev-poppler.h @@ -0,0 +1,40 @@ +/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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; + +GType pdf_document_get_type (void) G_GNUC_CONST; + +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + + +G_END_DECLS + +#endif /* __PDF_DOCUMENT_H__ */ diff --git a/backend/pdf/pdfdocument.evince-backend.in b/backend/pdf/pdfdocument.evince-backend.in new file mode 100644 index 00000000..d2b55ddc --- /dev/null +++ b/backend/pdf/pdfdocument.evince-backend.in @@ -0,0 +1,6 @@ +[Evince Backend] +Module=pdfdocument +Resident=true +_TypeDescription=PDF Documents +MimeType=application/pdf;application/x-bzpdf;application/x-gzpdf;application/x-ext-pdf + diff --git a/backend/pixbuf/Makefile.am b/backend/pixbuf/Makefile.am new file mode 100644 index 00000000..7a28e05b --- /dev/null +++ b/backend/pixbuf/Makefile.am @@ -0,0 +1,30 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCE_COMPILATION \ + $(BACKEND_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_LTLIBRARIES = libpixbufdocument.la + +libpixbufdocument_la_SOURCES = \ + pixbuf-document.c \ + pixbuf-document.h + +libpixbufdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libpixbufdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument.la \ + $(BACKEND_LIBS) + +backend_in_files = pixbufdocument.evince-backend.in +backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_EVINCE_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/pixbuf/pixbuf-document.c b/backend/pixbuf/pixbuf-document.c new file mode 100644 index 00000000..065fe498 --- /dev/null +++ b/backend/pixbuf/pixbuf-document.c @@ -0,0 +1,208 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Copyright (C) 2004, Anders Carlsson <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "pixbuf-document.h" +#include "ev-document-thumbnails.h" +#include "ev-document-misc.h" +#include "ev-file-helpers.h" + +struct _PixbufDocumentClass +{ + EvDocumentClass parent_class; +}; + +struct _PixbufDocument +{ + EvDocument parent_instance; + + GdkPixbuf *pixbuf; + + gchar *uri; +}; + +typedef struct _PixbufDocumentClass PixbufDocumentClass; + +static void pixbuf_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); + +EV_BACKEND_REGISTER_WITH_CODE (PixbufDocument, pixbuf_document, + { + EV_BACKEND_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, + EvPage *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 cairo_surface_t * +pixbuf_document_render (EvDocument *document, + EvRenderContext *rc) +{ + PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document); + GdkPixbuf *scaled_pixbuf, *rotated_pixbuf; + cairo_surface_t *surface; + + scaled_pixbuf = gdk_pixbuf_scale_simple ( + pixbuf_document->pixbuf, + (gdk_pixbuf_get_width (pixbuf_document->pixbuf) * rc->scale) + 0.5, + (gdk_pixbuf_get_height (pixbuf_document->pixbuf) * rc->scale) + 0.5, + GDK_INTERP_BILINEAR); + + rotated_pixbuf = gdk_pixbuf_rotate_simple (scaled_pixbuf, 360 - rc->rotation); + g_object_unref (scaled_pixbuf); + + surface = ev_document_misc_surface_from_pixbuf (rotated_pixbuf); + g_object_unref (rotated_pixbuf); + + return surface; +} + +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); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + gobject_class->finalize = pixbuf_document_finalize; + + ev_document_class->load = pixbuf_document_load; + ev_document_class->save = pixbuf_document_save; + ev_document_class->get_n_pages = pixbuf_document_get_n_pages; + ev_document_class->get_page_size = pixbuf_document_get_page_size; + ev_document_class->render = pixbuf_document_render; +} + +static GdkPixbuf * +pixbuf_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, + EvRenderContext *rc, + gboolean border) +{ + PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document); + GdkPixbuf *pixbuf, *rotated_pixbuf; + gint width, height; + + width = (gint) (gdk_pixbuf_get_width (pixbuf_document->pixbuf) * rc->scale); + height = (gint) (gdk_pixbuf_get_height (pixbuf_document->pixbuf) * rc->scale); + + pixbuf = gdk_pixbuf_scale_simple (pixbuf_document->pixbuf, + width, height, + GDK_INTERP_BILINEAR); + + rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation); + g_object_unref (pixbuf); + + return rotated_pixbuf; +} + +static void +pixbuf_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, + EvRenderContext *rc, + gint *width, + gint *height) +{ + PixbufDocument *pixbuf_document = PIXBUF_DOCUMENT (document); + gint p_width = gdk_pixbuf_get_width (pixbuf_document->pixbuf); + gint p_height = gdk_pixbuf_get_height (pixbuf_document->pixbuf); + + if (rc->rotation == 90 || rc->rotation == 270) { + *width = (gint) (p_height * rc->scale); + *height = (gint) (p_width * rc->scale); + } else { + *width = (gint) (p_width * rc->scale); + *height = (gint) (p_height * rc->scale); + } +} + +static void +pixbuf_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *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 00000000..6f34372d --- /dev/null +++ b/backend/pixbuf/pixbuf-document.h @@ -0,0 +1,38 @@ +/* pdfdocument.h: Implementation of EvDocument for pixbufs + * Copyright (C) 2004, Anders Carlsson <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __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; + +GType pixbuf_document_get_type (void) G_GNUC_CONST; + +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + +G_END_DECLS + +#endif /* __PIXBUF_DOCUMENT_H__ */ diff --git a/backend/pixbuf/pixbufdocument.evince-backend.in b/backend/pixbuf/pixbufdocument.evince-backend.in new file mode 100644 index 00000000..9beb526b --- /dev/null +++ b/backend/pixbuf/pixbufdocument.evince-backend.in @@ -0,0 +1,4 @@ +[Evince Backend] +Module=pixbufdocument +_TypeDescription=Images +MimeType=image/*; diff --git a/backend/ps/Makefile.am b/backend/ps/Makefile.am new file mode 100644 index 00000000..3f059de1 --- /dev/null +++ b/backend/ps/Makefile.am @@ -0,0 +1,32 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCE_COMPILATION \ + $(BACKEND_CFLAGS) \ + $(SPECTRE_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_LTLIBRARIES = libpsdocument.la + +libpsdocument_la_SOURCES = \ + ev-spectre.c \ + ev-spectre.h + +libpsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libpsdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument.la \ + $(BACKEND_LIBS) \ + $(SPECTRE_LIBS) + +backend_in_files = psdocument.evince-backend.in +backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_EVINCE_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/ps/ev-spectre.c b/backend/ps/ev-spectre.c new file mode 100644 index 00000000..e1877a7b --- /dev/null +++ b/backend/ps/ev-spectre.c @@ -0,0 +1,473 @@ +/* this file is part of evince, a mate document viewer + * + * Copyright (C) 2007 Carlos Garcia Campos <[email protected]> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> + +#include <config.h> +#include <glib/gi18n-lib.h> +#include <stdlib.h> +#include <libspectre/spectre.h> + +#include "ev-spectre.h" + +#include "ev-file-exporter.h" +#include "ev-document-thumbnails.h" +#include "ev-document-misc.h" + +struct _PSDocument { + EvDocument object; + + SpectreDocument *doc; + SpectreExporter *exporter; +}; + +struct _PSDocumentClass { + EvDocumentClass parent_class; +}; + +static void ps_document_file_exporter_iface_init (EvFileExporterInterface *iface); +static void ps_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); + +EV_BACKEND_REGISTER_WITH_CODE (PSDocument, ps_document, + { + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, + ps_document_document_thumbnails_iface_init); + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, + ps_document_file_exporter_iface_init); + }); + +/* PSDocument */ +static void +ps_document_init (PSDocument *ps_document) +{ +} + +static void +ps_document_dispose (GObject *object) +{ + PSDocument *ps = PS_DOCUMENT (object); + + if (ps->doc) { + spectre_document_free (ps->doc); + ps->doc = NULL; + } + + if (ps->exporter) { + spectre_exporter_free (ps->exporter); + ps->exporter = NULL; + } + + G_OBJECT_CLASS (ps_document_parent_class)->dispose (object); +} + +/* EvDocumentIface */ +static gboolean +ps_document_load (EvDocument *document, + const char *uri, + GError **error) +{ + PSDocument *ps = PS_DOCUMENT (document); + gchar *filename; + + filename = g_filename_from_uri (uri, NULL, error); + if (!filename) + return FALSE; + + ps->doc = spectre_document_new (); + + spectre_document_load (ps->doc, filename); + if (spectre_document_status (ps->doc)) { + 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 (filename); + + return FALSE; + } + + g_free (filename); + + return TRUE; +} + +static gboolean +ps_document_save (EvDocument *document, + const char *uri, + GError **error) +{ + PSDocument *ps = PS_DOCUMENT (document); + gchar *filename; + + filename = g_filename_from_uri (uri, NULL, error); + if (!filename) + return FALSE; + + spectre_document_save (ps->doc, filename); + if (spectre_document_status (ps->doc)) { + gchar *filename_dsp; + + filename_dsp = g_filename_display_name (filename); + g_set_error (error, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + _("Failed to save document “%s”"), + filename_dsp); + g_free (filename_dsp); + g_free (filename); + + return FALSE; + } + + g_free (filename); + + return TRUE; +} + +static int +ps_document_get_n_pages (EvDocument *document) +{ + PSDocument *ps = PS_DOCUMENT (document); + + return spectre_document_get_n_pages (ps->doc); +} + +static EvPage * +ps_document_get_page (EvDocument *document, + gint index) +{ + PSDocument *ps = PS_DOCUMENT (document); + SpectrePage *ps_page; + EvPage *page; + + ps_page = spectre_document_get_page (ps->doc, index); + page = ev_page_new (index); + page->backend_page = (EvBackendPage)ps_page; + page->backend_destroy_func = (EvBackendPageDestroyFunc)spectre_page_free; + + return page; +} + +static gint +get_page_rotation (SpectrePage *page) +{ + switch (spectre_page_get_orientation (page)) { + default: + case SPECTRE_ORIENTATION_PORTRAIT: + return 0; + case SPECTRE_ORIENTATION_LANDSCAPE: + return 90; + case SPECTRE_ORIENTATION_REVERSE_PORTRAIT: + return 180; + case SPECTRE_ORIENTATION_REVERSE_LANDSCAPE: + return 270; + } + + return 0; +} + +static void +ps_document_get_page_size (EvDocument *document, + EvPage *page, + double *width, + double *height) +{ + SpectrePage *ps_page; + gdouble page_width, page_height; + gint pwidth, pheight; + gint rotate; + + ps_page = (SpectrePage *)page->backend_page; + + spectre_page_get_size (ps_page, &pwidth, &pheight); + + rotate = get_page_rotation (ps_page); + if (rotate == 90 || rotate == 270) { + page_height = pwidth; + page_width = pheight; + } else { + page_width = pwidth; + page_height = pheight; + } + + if (width) { + *width = page_width; + } + + if (height) { + *height = page_height; + } +} + +static char * +ps_document_get_page_label (EvDocument *document, + EvPage *page) +{ + return g_strdup (spectre_page_get_label ((SpectrePage *)page->backend_page)); +} + +static EvDocumentInfo * +ps_document_get_info (EvDocument *document) +{ + PSDocument *ps = PS_DOCUMENT (document); + EvDocumentInfo *info; + const gchar *creator; + SpectrePage *ps_page; + gint width, height; + + 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; + + creator = spectre_document_get_creator (ps->doc); + + ps_page = spectre_document_get_page (ps->doc, 0); + spectre_page_get_size (ps_page, &width, &height); + spectre_page_free (ps_page); + + info->title = g_strdup (spectre_document_get_title (ps->doc)); + info->format = g_strdup (spectre_document_get_format (ps->doc)); + info->creator = g_strdup (creator ? creator : spectre_document_get_for (ps->doc)); + info->n_pages = spectre_document_get_n_pages (ps->doc); + info->paper_width = width / 72.0f * 25.4f; + info->paper_height = height / 72.0f * 25.4f; + + return info; +} + +static gboolean +ps_document_get_backend_info (EvDocument *document, + EvDocumentBackendInfo *info) +{ + info->name = "libspectre"; + info->version = SPECTRE_VERSION_STRING; + + return TRUE; +} + +static cairo_surface_t * +ps_document_render (EvDocument *document, + EvRenderContext *rc) +{ + SpectrePage *ps_page; + SpectreRenderContext *src; + gint width_points; + gint height_points; + gint width, height; + gint swidth, sheight; + guchar *data = NULL; + gint stride; + gint rotation; + cairo_surface_t *surface; + static const cairo_user_data_key_t key; + + ps_page = (SpectrePage *)rc->page->backend_page; + + spectre_page_get_size (ps_page, &width_points, &height_points); + + width = (gint) ((width_points * rc->scale) + 0.5); + height = (gint) ((height_points * rc->scale) + 0.5); + rotation = (rc->rotation + get_page_rotation (ps_page)) % 360; + + src = spectre_render_context_new (); + spectre_render_context_set_scale (src, + (gdouble)width / width_points, + (gdouble)height / height_points); + spectre_render_context_set_rotation (src, rotation); + spectre_page_render (ps_page, src, &data, &stride); + spectre_render_context_free (src); + + if (!data) { + return NULL; + } + + if (spectre_page_status (ps_page)) { + g_warning ("%s", spectre_status_to_string (spectre_page_status (ps_page))); + g_free (data); + + return NULL; + } + + if (rotation == 90 || rotation == 270) { + swidth = height; + sheight = width; + } else { + swidth = width; + sheight = height; + } + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_RGB24, + swidth, sheight, + stride); + cairo_surface_set_user_data (surface, &key, + data, (cairo_destroy_func_t)g_free); + return surface; +} + +static void +ps_document_class_init (PSDocumentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + object_class->dispose = ps_document_dispose; + + ev_document_class->load = ps_document_load; + ev_document_class->save = ps_document_save; + ev_document_class->get_n_pages = ps_document_get_n_pages; + ev_document_class->get_page = ps_document_get_page; + ev_document_class->get_page_size = ps_document_get_page_size; + ev_document_class->get_page_label = ps_document_get_page_label; + ev_document_class->get_info = ps_document_get_info; + ev_document_class->get_backend_info = ps_document_get_backend_info; + ev_document_class->render = ps_document_render; +} + +/* EvDocumentThumbnailsIface */ +static GdkPixbuf * +ps_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails, + EvRenderContext *rc, + gboolean border) +{ + PSDocument *ps = PS_DOCUMENT (document_thumbnails); + cairo_surface_t *surface; + GdkPixbuf *pixbuf = NULL; + + surface = ps_document_render (EV_DOCUMENT (ps), rc); + if (!surface) { + g_warning ("Error rendering thumbnail"); + return NULL; + } + + pixbuf = ev_document_misc_pixbuf_from_surface (surface); + cairo_surface_destroy (surface); + + if (border) { + GdkPixbuf *border_pixbuf; + + border_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf); + g_object_unref (pixbuf); + pixbuf = border_pixbuf; + } + + return pixbuf; +} + +static void +ps_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails, + EvRenderContext *rc, + gint *width, + gint *height) +{ + PSDocument *ps = PS_DOCUMENT (document_thumbnails); + gdouble page_width, page_height; + + ps_document_get_page_size (EV_DOCUMENT (ps), + rc->page, + &page_width, &page_height); + + if (rc->rotation == 90 || rc->rotation == 270) { + *width = (gint) (page_height * rc->scale); + *height = (gint) (page_width * rc->scale); + } else { + *width = (gint) (page_width * rc->scale); + *height = (gint) (page_height * rc->scale); + } +} + +static void +ps_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface) +{ + iface->get_thumbnail = ps_document_thumbnails_get_thumbnail; + iface->get_dimensions = ps_document_thumbnails_get_dimensions; +} + +/* EvFileExporterIface */ +static void +ps_document_file_exporter_begin (EvFileExporter *exporter, + EvFileExporterContext *fc) +{ + PSDocument *ps = PS_DOCUMENT (exporter); + + if (ps->exporter) + spectre_exporter_free (ps->exporter); + + switch (fc->format) { + case EV_FILE_FORMAT_PS: + ps->exporter = + spectre_exporter_new (ps->doc, + SPECTRE_EXPORTER_FORMAT_PS); + break; + case EV_FILE_FORMAT_PDF: + ps->exporter = + spectre_exporter_new (ps->doc, + SPECTRE_EXPORTER_FORMAT_PDF); + break; + default: + g_assert_not_reached (); + } + + spectre_exporter_begin (ps->exporter, fc->filename); +} + +static void +ps_document_file_exporter_do_page (EvFileExporter *exporter, + EvRenderContext *rc) +{ + PSDocument *ps = PS_DOCUMENT (exporter); + + spectre_exporter_do_page (ps->exporter, rc->page->index); +} + +static void +ps_document_file_exporter_end (EvFileExporter *exporter) +{ + PSDocument *ps = PS_DOCUMENT (exporter); + + spectre_exporter_end (ps->exporter); +} + +static EvFileExporterCapabilities +ps_document_file_exporter_get_capabilities (EvFileExporter *exporter) +{ + return EV_FILE_EXPORTER_CAN_PAGE_SET | + EV_FILE_EXPORTER_CAN_COPIES | + EV_FILE_EXPORTER_CAN_COLLATE | + EV_FILE_EXPORTER_CAN_REVERSE | + EV_FILE_EXPORTER_CAN_GENERATE_PS | + EV_FILE_EXPORTER_CAN_GENERATE_PDF; +} + +static void +ps_document_file_exporter_iface_init (EvFileExporterInterface *iface) +{ + iface->begin = ps_document_file_exporter_begin; + iface->do_page = ps_document_file_exporter_do_page; + iface->end = ps_document_file_exporter_end; + iface->get_capabilities = ps_document_file_exporter_get_capabilities; +} diff --git a/backend/ps/ev-spectre.h b/backend/ps/ev-spectre.h new file mode 100644 index 00000000..17b18db0 --- /dev/null +++ b/backend/ps/ev-spectre.h @@ -0,0 +1,48 @@ +/* + * Ghostscript widget for GTK/MATE + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PS_DOCUMENT_H__ +#define __PS_DOCUMENT_H__ + +#include <glib-object.h> + +#include "ev-document.h" + +G_BEGIN_DECLS + +#define PS_TYPE_DOCUMENT (ps_document_get_type()) +#define PS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PS_TYPE_DOCUMENT, PSDocument)) +#define PS_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PS_TYPE_DOCUMENT, PSDocumentClass)) +#define PS_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PS_TYPE_DOCUMENT)) +#define PS_DOCUMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PS_TYPE_DOCUMENT, PSDocumentClass)) + +typedef struct _PSDocument PSDocument; +typedef struct _PSDocumentClass PSDocumentClass; + +GType ps_document_get_type (void) G_GNUC_CONST; + +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + +G_END_DECLS + +#endif /* __PS_DOCUMENT_H__ */ diff --git a/backend/ps/psdocument.evince-backend.in b/backend/ps/psdocument.evince-backend.in new file mode 100644 index 00000000..af24be61 --- /dev/null +++ b/backend/ps/psdocument.evince-backend.in @@ -0,0 +1,5 @@ +[Evince Backend] +Module=psdocument +Resident=true +_TypeDescription=PostScript Documents +MimeType=application/postscript;application/x-bzpostscript;application/x-gzpostscript;image/x-eps;image/x-bzeps;image/x-gzeps diff --git a/backend/tiff/Makefile.am b/backend/tiff/Makefile.am new file mode 100644 index 00000000..9f1a00e6 --- /dev/null +++ b/backend/tiff/Makefile.am @@ -0,0 +1,33 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libdocument \ + -DMATELOCALEDIR=\"$(datadir)/locale\" \ + -DEVINCE_COMPILATION \ + $(BACKEND_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED) + +backend_LTLIBRARIES = libtiffdocument.la + +libtiffdocument_la_SOURCES = \ + tiff-document.c \ + tiff-document.h \ + tiff2ps.c \ + tiff2ps.h + +libtiffdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS) +libtiffdocument_la_LIBADD = \ + $(top_builddir)/libdocument/libevdocument.la \ + $(BACKEND_LIBS) \ + -ltiff + +backend_in_files = tiffdocument.evince-backend.in +backend_DATA = $(backend_in_files:.evince-backend.in=.evince-backend) + +EXTRA_DIST = $(backend_in_files) + +CLEANFILES = $(backend_DATA) + +@EV_INTLTOOL_EVINCE_BACKEND_RULE@ + +-include $(top_srcdir)/git.mk diff --git a/backend/tiff/tiff-document.c b/backend/tiff/tiff-document.c new file mode 100644 index 00000000..9c113b49 --- /dev/null +++ b/backend/tiff/tiff-document.c @@ -0,0 +1,533 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* + * Copyright (C) 2005, Jonathan Blandford <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* FIXME: Should probably buffer calls to libtiff with TIFFSetWarningHandler + */ + +#include "config.h" + +#include <config.h> +#include <stdio.h> +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "tiffio.h" +#include "tiff2ps.h" +#include "tiff-document.h" +#include "ev-document-misc.h" +#include "ev-document-thumbnails.h" +#include "ev-file-exporter.h" +#include "ev-file-helpers.h" + +struct _TiffDocumentClass +{ + EvDocumentClass parent_class; +}; + +struct _TiffDocument +{ + EvDocument parent_instance; + + TIFF *tiff; + gint n_pages; + TIFF2PSContext *ps_export_ctx; + + gchar *uri; +}; + +typedef struct _TiffDocumentClass TiffDocumentClass; + +static void tiff_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface); +static void tiff_document_document_file_exporter_iface_init (EvFileExporterInterface *iface); + +EV_BACKEND_REGISTER_WITH_CODE (TiffDocument, tiff_document, + { + EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, + tiff_document_document_thumbnails_iface_init); + EV_BACKEND_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; + + filename = g_filename_from_uri (uri, NULL, error); + if (!filename) + return FALSE; + + push_handlers (); + 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 (); + + g_set_error_literal (error, + EV_DOCUMENT_ERROR, + EV_DOCUMENT_ERROR_INVALID, + _("Invalid document")); + + g_free (filename); + 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_resolution (TiffDocument *tiff_document, + gfloat *x_res, + gfloat *y_res) +{ + gfloat x = 72.0, y = 72.0; + gushort unit; + + if (TIFFGetField (tiff_document->tiff, TIFFTAG_XRESOLUTION, &x) && + TIFFGetField (tiff_document->tiff, TIFFTAG_YRESOLUTION, &y)) { + if (TIFFGetFieldDefaulted (tiff_document->tiff, TIFFTAG_RESOLUTIONUNIT, &unit)) { + if (unit == RESUNIT_CENTIMETER) { + x *= 2.54; + y *= 2.54; + } + } + } + + *x_res = x; + *y_res = y; +} + +static void +tiff_document_get_page_size (EvDocument *document, + EvPage *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->index) != 1) { + pop_handlers (); + return; + } + + TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGEWIDTH, &w); + TIFFGetField (tiff_document->tiff, TIFFTAG_IMAGELENGTH, &h); + tiff_document_get_resolution (tiff_document, &x_res, &y_res); + h = h * (x_res / y_res); + + *width = w; + *height = h; + + pop_handlers (); +} + +static cairo_surface_t * +tiff_document_render (EvDocument *document, + EvRenderContext *rc) +{ + TiffDocument *tiff_document = TIFF_DOCUMENT (document); + int width, height; + float x_res, y_res; + gint rowstride, bytes; + guchar *pixels = NULL; + guchar *p; + int orientation; + cairo_surface_t *surface; + cairo_surface_t *rotated_surface; + static const cairo_user_data_key_t key; + + g_return_val_if_fail (TIFF_IS_DOCUMENT (document), NULL); + g_return_val_if_fail (tiff_document->tiff != NULL, NULL); + + push_handlers (); + if (TIFFSetDirectory (tiff_document->tiff, rc->page->index) != 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_ORIENTATION, &orientation)) { + orientation = ORIENTATION_TOPLEFT; + } + + tiff_document_get_resolution (tiff_document, &x_res, &y_res); + + pop_handlers (); + + /* Sanity check the doc */ + if (width <= 0 || height <= 0) + return NULL; + +#ifdef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH + rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width); +#else + rowstride = width * 4; +#endif + if (rowstride / 4 != width) + /* overflow, or cairo was changed in an unsupported way */ + return NULL; + + bytes = height * rowstride; + if (bytes / rowstride != height) + /* overflow */ + return NULL; + + pixels = g_try_malloc (bytes); + if (!pixels) + return NULL; + + surface = cairo_image_surface_create_for_data (pixels, + CAIRO_FORMAT_RGB24, + width, height, + rowstride); + cairo_surface_set_user_data (surface, &key, + pixels, (cairo_destroy_func_t)g_free); + + TIFFReadRGBAImageOriented (tiff_document->tiff, + width, height, + (uint32 *)pixels, + orientation, 1); + pop_handlers (); + + /* Convert the format returned by libtiff to + * what cairo expects + */ + p = pixels; + while (p < pixels + bytes) { + guint32 *pixel = (guint32*)p; + guint8 r = TIFFGetR(*pixel); + guint8 g = TIFFGetG(*pixel); + guint8 b = TIFFGetB(*pixel); + guint8 a = TIFFGetA(*pixel); + + *pixel = (a << 24) | (r << 16) | (g << 8) | b; + + p += 4; + } + + rotated_surface = ev_document_misc_surface_rotate_and_scale (surface, + (width * rc->scale) + 0.5, + (height * rc->scale * (x_res / y_res)) + 0.5, + rc->rotation); + cairo_surface_destroy (surface); + + return rotated_surface; +} + +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; + + push_handlers (); + if (TIFFSetDirectory (tiff_document->tiff, rc->page->index) != 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; + } + + tiff_document_get_resolution (tiff_document, &x_res, &y_res); + + 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); + TIFFReadRGBAImageOriented (tiff_document->tiff, + width, height, + (uint32 *)pixels, + 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 gchar * +tiff_document_get_page_label (EvDocument *document, + EvPage *page) +{ + TiffDocument *tiff_document = TIFF_DOCUMENT (document); + static gchar *label; + + if (TIFFGetField (tiff_document->tiff, TIFFTAG_PAGENAME, &label) && + g_utf8_validate (label, -1, NULL)) { + return g_strdup (label); + } + + return NULL; +} + +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); + EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass); + + gobject_class->finalize = tiff_document_finalize; + + ev_document_class->load = tiff_document_load; + ev_document_class->save = tiff_document_save; + ev_document_class->get_n_pages = tiff_document_get_n_pages; + ev_document_class->get_page_size = tiff_document_get_page_size; + ev_document_class->render = tiff_document_render; + ev_document_class->get_page_label = tiff_document_get_page_label; +} + +static GdkPixbuf * +tiff_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document, + EvRenderContext *rc, + gboolean border) +{ + GdkPixbuf *pixbuf; + + pixbuf = tiff_document_render_pixbuf (EV_DOCUMENT (document), rc); + + if (border) { + GdkPixbuf *tmp_pixbuf = pixbuf; + + pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf); + g_object_unref (tmp_pixbuf); + } + + return pixbuf; +} + +static void +tiff_document_thumbnails_get_dimensions (EvDocumentThumbnails *document, + EvRenderContext *rc, + gint *width, + gint *height) +{ + gdouble page_width, page_height; + + tiff_document_get_page_size (EV_DOCUMENT (document), + rc->page, + &page_width, &page_height); + + if (rc->rotation == 90 || rc->rotation == 270) { + *width = (gint) (page_height * rc->scale); + *height = (gint) (page_width * rc->scale); + } else { + *width = (gint) (page_width * rc->scale); + *height = (gint) (page_height * rc->scale); + } +} + +static void +tiff_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface) +{ + iface->get_thumbnail = tiff_document_thumbnails_get_thumbnail; + iface->get_dimensions = tiff_document_thumbnails_get_dimensions; +} + +/* postscript exporter implementation */ +static void +tiff_document_file_exporter_begin (EvFileExporter *exporter, + EvFileExporterContext *fc) +{ + TiffDocument *document = TIFF_DOCUMENT (exporter); + + document->ps_export_ctx = tiff2ps_context_new(fc->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->index) != 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 EvFileExporterCapabilities +tiff_document_file_exporter_get_capabilities (EvFileExporter *exporter) +{ + return EV_FILE_EXPORTER_CAN_PAGE_SET | + EV_FILE_EXPORTER_CAN_COPIES | + EV_FILE_EXPORTER_CAN_COLLATE | + EV_FILE_EXPORTER_CAN_REVERSE | + EV_FILE_EXPORTER_CAN_GENERATE_PS; +} + +static void +tiff_document_document_file_exporter_iface_init (EvFileExporterInterface *iface) +{ + iface->begin = tiff_document_file_exporter_begin; + iface->do_page = tiff_document_file_exporter_do_page; + iface->end = tiff_document_file_exporter_end; + iface->get_capabilities = tiff_document_file_exporter_get_capabilities; +} + +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 00000000..a188f143 --- /dev/null +++ b/backend/tiff/tiff-document.h @@ -0,0 +1,38 @@ + +/* pdfdocument.h: Implementation of EvDocument for tiffs + * Copyright (C) 2005, Jonathan Blandford <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __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; + +GType tiff_document_get_type (void) G_GNUC_CONST; +G_MODULE_EXPORT GType register_evince_backend (GTypeModule *module); + +G_END_DECLS + +#endif /* __TIFF_DOCUMENT_H__ */ diff --git a/backend/tiff/tiff2ps.c b/backend/tiff/tiff2ps.c new file mode 100644 index 00000000..43a6cb32 --- /dev/null +++ b/backend/tiff/tiff2ps.c @@ -0,0 +1,1872 @@ +/* -*- 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 <[email protected]> + * Modifications Copyright (C) 2005 rpath, Inc. + * + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> /* for atof */ +#include <math.h> +#include <time.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "tiff2ps.h" + +/* + * Revision history + * + * 2001-Mar-21 + * I (Bruce A. Mallett) added this revision history comment ;) + * + * Fixed PS_Lvl2page() code which outputs non-ASCII85 raw + * data. Moved test for when to output a line break to + * *after* the output of a character. This just serves + * to fix an eye-nuisance where the first line of raw + * data was one character shorter than subsequent lines. + * + * Added an experimental ASCII85 encoder which can be used + * only when there is a single buffer of bytes to be encoded. + * This version is much faster at encoding a straight-line + * buffer of data because it can avoid alot of the loop + * overhead of the byte-by-bye version. To use this version + * you need to define EXP_ASCII85ENCODER (experimental ...). + * + * Added bug fix given by Michael Schmidt to PS_Lvl2page() + * in which an end-of-data marker ('>') was not being output + * when producing non-ASCII85 encoded PostScript Level 2 + * data. + * + * Fixed PS_Lvl2colorspace() so that it no longer assumes that + * a TIFF having more than 2 planes is a CMYK. This routine + * no longer looks at the samples per pixel but instead looks + * at the "photometric" value. This change allows support of + * CMYK TIFFs. + * + * Modified the PostScript L2 imaging loop so as to test if + * the input stream is still open before attempting to do a + * flushfile on it. This was done because some RIPs close + * the stream after doing the image operation. + * + * Got rid of the realloc() being done inside a loop in the + * PSRawDataBW() routine. The code now walks through the + * byte-size array outside the loop to determine the largest + * size memory block that will be needed. + * + * Added "-m" switch to ask tiff2ps to, where possible, use the + * "imagemask" operator instead of the "image" operator. + * + * Added the "-i #" switch to allow interpolation to be disabled. + * + * Unrolled a loop or two to improve performance. + */ + +/* + * Define EXP_ASCII85ENCODER if you want to use an experimental + * version of the ASCII85 encoding routine. The advantage of + * using this routine is that tiff2ps will convert to ASCII85 + * encoding at between 3 and 4 times the speed as compared to + * using the old (non-experimental) encoder. The disadvantage + * is that you will be using a new (and unproven) encoding + * routine. So user beware, you have been warned! + */ + +#define EXP_ASCII85ENCODER + +/* + * NB: this code assumes uint32 works with printf's %l[ud]. + */ + +struct _TIFF2PSContext +{ + char *filename; /* input filename */ + FILE *fd; /* output file stream */ + int ascii85; /* use ASCII85 encoding */ + int interpolate; /* interpolate level2 image */ + int level2; /* generate PostScript level 2 */ + int level3; /* generate PostScript level 3 */ + int generateEPSF; /* generate Encapsulated PostScript */ + int PSduplex; /* enable duplex printing */ + int PStumble; /* enable top edge binding */ + int PSavoiddeadzone; /* enable avoiding printer deadzone */ + double maxPageHeight; /* maximum size to fit on page */ + double splitOverlap; /* amount for split pages to overlag */ + int rotate; /* rotate image by 180 degrees */ + int useImagemask; /* Use imagemask instead of image operator */ + uint16 res_unit; /* Resolution units: 2 - inches, 3 - cm */ + int npages; /* number of pages processed */ + + tsize_t tf_bytesperrow; + tsize_t ps_bytesperrow; + tsize_t tf_rowsperstrip; + tsize_t tf_numberstrips; + + /* + * ASCII85 Encoding Support. + */ + unsigned char ascii85buf[10]; + int ascii85count; + int ascii85breaklen; + uint16 samplesperpixel; + uint16 bitspersample; + uint16 planarconfiguration; + uint16 photometric; + uint16 compression; + uint16 extrasamples; + int alpha; +}; + +static void PSpage(TIFF2PSContext*, TIFF*, uint32, uint32); +static void PSColorContigPreamble(TIFF2PSContext*, uint32, uint32, int); +static void PSColorSeparatePreamble(TIFF2PSContext*, uint32, uint32, int); +static void PSDataColorContig(TIFF2PSContext*, TIFF*, uint32, uint32, int); +static void PSDataColorSeparate(TIFF2PSContext*, TIFF*, uint32, uint32, int); +static void PSDataPalette(TIFF2PSContext*, TIFF*, uint32, uint32); +static void PSDataBW(TIFF2PSContext*, TIFF*, uint32, uint32); +static void Ascii85Init(TIFF2PSContext*); +static void Ascii85Put(TIFF2PSContext*, unsigned char); +static void Ascii85Flush(TIFF2PSContext*); +static void PSHead(TIFF2PSContext*, TIFF*, uint32, uint32, + double, double, double, double); +static void PSTail(TIFF2PSContext*); + +#if defined( EXP_ASCII85ENCODER ) +static int Ascii85EncodeBlock(TIFF2PSContext*, uint8 * ascii85_p, + unsigned f_eod, const uint8 * raw_p, int raw_l); +#endif + +TIFF2PSContext* tiff2ps_context_new(const gchar *filename) { + TIFF2PSContext* ctx; + + ctx = g_new0(TIFF2PSContext, 1); + ctx->filename = g_strdup(filename); + ctx->fd = g_fopen(ctx->filename, "w"); + if (ctx->fd == NULL) { + g_free (ctx->filename); + g_free (ctx); + return NULL; + } + ctx->interpolate = TRUE; /* interpolate level2 image */ + ctx->PSavoiddeadzone = TRUE; /* enable avoiding printer deadzone */ + return ctx; +} + +void tiff2ps_context_finalize(TIFF2PSContext *ctx) { + PSTail(ctx); + fclose(ctx->fd); + g_free(ctx->filename); + g_free(ctx); +} + +static int +checkImage(TIFF2PSContext *ctx, TIFF* tif) +{ + switch (ctx->photometric) { + case PHOTOMETRIC_YCBCR: + if ((ctx->compression == COMPRESSION_JPEG + || ctx->compression == COMPRESSION_OJPEG) + && ctx->planarconfiguration == PLANARCONFIG_CONTIG) { + /* can rely on libjpeg to convert to RGB */ + TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, + JPEGCOLORMODE_RGB); + ctx->photometric = PHOTOMETRIC_RGB; + } else { + if (ctx->level2 || ctx->level3) + break; + TIFFError(ctx->filename, "Can not handle image with %s", + "Ctx->PhotometricInterpretation=YCbCr"); + return (0); + } + /* fall thru... */ + case PHOTOMETRIC_RGB: + if (ctx->alpha && ctx->bitspersample != 8) { + TIFFError(ctx->filename, + "Can not handle %d-bit/sample RGB image with ctx->alpha", + ctx->bitspersample); + return (0); + } + /* fall thru... */ + case PHOTOMETRIC_SEPARATED: + case PHOTOMETRIC_PALETTE: + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: + break; + case PHOTOMETRIC_LOGL: + case PHOTOMETRIC_LOGLUV: + if (ctx->compression != COMPRESSION_SGILOG && + ctx->compression != COMPRESSION_SGILOG24) { + TIFFError(ctx->filename, + "Can not handle %s data with ctx->compression other than SGILog", + (ctx->photometric == PHOTOMETRIC_LOGL) ? + "LogL" : "LogLuv" + ); + return (0); + } + /* rely on library to convert to RGB/greyscale */ + TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT); + ctx->photometric = (ctx->photometric == PHOTOMETRIC_LOGL) ? + PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB; + ctx->bitspersample = 8; + break; + case PHOTOMETRIC_CIELAB: + /* fall thru... */ + default: + TIFFError(ctx->filename, + "Can not handle image with Ctx->PhotometricInterpretation=%d", + ctx->photometric); + return (0); + } + switch (ctx->bitspersample) { + case 1: case 2: + case 4: case 8: + break; + default: + TIFFError(ctx->filename, "Can not handle %d-bit/sample image", + ctx->bitspersample); + return (0); + } + if (ctx->planarconfiguration == PLANARCONFIG_SEPARATE && + ctx->extrasamples > 0) + TIFFWarning(ctx->filename, "Ignoring extra samples"); + return (1); +} + +#define PS_UNIT_SIZE 72.0F +#define PSUNITS(npix,res) ((npix) * (PS_UNIT_SIZE / (res))) + +static char RGBcolorimage[] = "\ +/bwproc {\n\ + rgbproc\n\ + dup length 3 idiv string 0 3 0\n\ + 5 -1 roll {\n\ + add 2 1 roll 1 sub dup 0 eq {\n\ + pop 3 idiv\n\ + 3 -1 roll\n\ + dup 4 -1 roll\n\ + dup 3 1 roll\n\ + 5 -1 roll put\n\ + 1 add 3 0\n\ + } { 2 1 roll } ifelse\n\ + } forall\n\ + pop pop pop\n\ +} def\n\ +/colorimage where {pop} {\n\ + /colorimage {pop pop /rgbproc exch def {bwproc} image} bind def\n\ +} ifelse\n\ +"; + +/* + * Adobe Photoshop requires a comment line of the form: + * + * %ImageData: <cols> <rows> <depth> <main channels> <pad channels> + * <block size> <1 for binary|2 for hex> "data start" + * + * It is claimed to be part of some future revision of the EPS spec. + */ +static void +PhotoshopBanner(TIFF2PSContext* ctx, uint32 w, uint32 h, int bs, int nc, + char* startline) +{ + fprintf(ctx->fd, "%%ImageData: %ld %ld %d %d 0 %d 2 \"", + (long) w, (long) h, ctx->bitspersample, nc, bs); + fprintf(ctx->fd, startline, nc); + fprintf(ctx->fd, "\"\n"); +} + +/* + * pw : image width in pixels + * ph : image height in pixels + * pprw : image width in PS units (72 dpi) + * pprh : image height in PS units (72 dpi) + */ +static void +setupPageState(TIFF2PSContext *ctx, TIFF* tif, uint32* pw, uint32* ph, + double* pprw, double* pprh) +{ + float xres = 0.0F, yres = 0.0F; + + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, pw); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, ph); + if (ctx->res_unit == 0) + TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &ctx->res_unit); + /* + * Calculate printable area. + */ + if (!TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) + || fabs(xres) < 0.0000001) + xres = PS_UNIT_SIZE; + if (!TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) + || fabs(yres) < 0.0000001) + yres = PS_UNIT_SIZE; + switch (ctx->res_unit) { + case RESUNIT_CENTIMETER: + xres *= 2.54F, yres *= 2.54F; + break; + case RESUNIT_INCH: + break; + case RESUNIT_NONE: + default: + xres *= PS_UNIT_SIZE, yres *= PS_UNIT_SIZE; + break; + } + *pprh = PSUNITS(*ph, yres); + *pprw = PSUNITS(*pw, xres); +} + +static int +isCCITTCompression(TIFF* tif) +{ + uint16 compress; + TIFFGetField(tif, TIFFTAG_COMPRESSION, &compress); + return (compress == COMPRESSION_CCITTFAX3 || + compress == COMPRESSION_CCITTFAX4 || + compress == COMPRESSION_CCITTRLE || + compress == COMPRESSION_CCITTRLEW); +} + +static char *hex = "0123456789abcdef"; + +/* + * imagewidth & imageheight are 1/72 inches + * pagewidth & pageheight are inches + */ +static int +PlaceImage(TIFF2PSContext *ctx, double pagewidth, double pageheight, + double imagewidth, double imageheight, int splitpage, + double lm, double bm, int cnt) +{ + double xtran = 0; + double ytran = 0; + double xscale = 1; + double yscale = 1; + double left_offset = lm * PS_UNIT_SIZE; + double bottom_offset = bm * PS_UNIT_SIZE; + double subimageheight; + double splitheight; + double overlap; + /* buffers for locale-insitive number formatting */ + gchar buf[2][G_ASCII_DTOSTR_BUF_SIZE]; + + pagewidth *= PS_UNIT_SIZE; + pageheight *= PS_UNIT_SIZE; + + if (ctx->maxPageHeight==0) + splitheight = 0; + else + splitheight = ctx->maxPageHeight * PS_UNIT_SIZE; + overlap = ctx->splitOverlap * PS_UNIT_SIZE; + + /* + * WIDTH: + * if too wide, scrunch to fit + * else leave it alone + */ + if (imagewidth <= pagewidth) { + xscale = imagewidth; + } else { + xscale = pagewidth; + } + + /* HEIGHT: + * if too long, scrunch to fit + * if too short, move to top of page + */ + if (imageheight <= pageheight) { + yscale = imageheight; + ytran = pageheight - imageheight; + } else if (imageheight > pageheight && + (splitheight == 0 || imageheight <= splitheight)) { + yscale = pageheight; + } else /* imageheight > splitheight */ { + subimageheight = imageheight - (pageheight-overlap)*splitpage; + if (subimageheight <= pageheight) { + yscale = imageheight; + ytran = pageheight - subimageheight; + splitpage = 0; + } else if ( subimageheight > pageheight && subimageheight <= splitheight) { + yscale = imageheight * pageheight / subimageheight; + ytran = 0; + splitpage = 0; + } else /* sumimageheight > splitheight */ { + yscale = imageheight; + ytran = pageheight - subimageheight; + splitpage++; + } + } + + bottom_offset += ytran / (cnt?2:1); + if (cnt) + left_offset += xtran / 2; + + fprintf(ctx->fd, "%s %s translate\n", + g_ascii_dtostr(buf[0], sizeof(buf[0]), left_offset), + g_ascii_dtostr(buf[1], sizeof(buf[1]), bottom_offset)); + fprintf(ctx->fd, "%s %s scale\n", + g_ascii_dtostr(buf[0], sizeof(buf[0]), xscale), + g_ascii_dtostr(buf[1], sizeof(buf[1]), yscale)); + if (ctx->rotate) + fputs ("1 1 translate 180 ctx->rotate\n", ctx->fd); + + return splitpage; +} + + +void +tiff2ps_process_page(TIFF2PSContext* ctx, TIFF* tif, double pw, double ph, + double lm, double bm, gboolean cnt) +{ + uint32 w, h; + float ox, oy; + double prw, prh; + double scale = 1.0; + double left_offset = lm * PS_UNIT_SIZE; + double bottom_offset = bm * PS_UNIT_SIZE; + uint16* sampleinfo; + int split; + /* buffers for locale-insitive number formatting */ + gchar buf[2][G_ASCII_DTOSTR_BUF_SIZE]; + + if (!TIFFGetField(tif, TIFFTAG_XPOSITION, &ox)) + ox = 0; + if (!TIFFGetField(tif, TIFFTAG_YPOSITION, &oy)) + oy = 0; + setupPageState(ctx, tif, &w, &h, &prw, &prh); + + ctx->tf_numberstrips = TIFFNumberOfStrips(tif); + TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, + &ctx->tf_rowsperstrip); + setupPageState(ctx, tif, &w, &h, &prw, &prh); + if (!ctx->npages) + PSHead(ctx, tif, w, h, prw, prh, ox, oy); + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, + &ctx->bitspersample); + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, + &ctx->samplesperpixel); + TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, + &ctx->planarconfiguration); + TIFFGetField(tif, TIFFTAG_COMPRESSION, &ctx->compression); + TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES, + &ctx->extrasamples, &sampleinfo); + ctx->alpha = (ctx->extrasamples == 1 && + sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA); + if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &ctx->photometric)) { + switch (ctx->samplesperpixel - ctx->extrasamples) { + case 1: + if (isCCITTCompression(tif)) + ctx->photometric = PHOTOMETRIC_MINISWHITE; + else + ctx->photometric = PHOTOMETRIC_MINISBLACK; + break; + case 3: + ctx->photometric = PHOTOMETRIC_RGB; + break; + case 4: + ctx->photometric = PHOTOMETRIC_SEPARATED; + break; + } + } + if (checkImage(ctx, tif)) { + ctx->tf_bytesperrow = TIFFScanlineSize(tif); + ctx->npages++; + fprintf(ctx->fd, "%%%%Page: %d %d\n", ctx->npages, + ctx->npages); + if (!ctx->generateEPSF && ( ctx->level2 || ctx->level3 )) { + double psw = 0.0, psh = 0.0; + if (psw != 0.0) { + psw = pw * PS_UNIT_SIZE; + if (ctx->res_unit == RESUNIT_CENTIMETER) + psw *= 2.54F; + } else + psw=ctx->rotate ? prh:prw; + if (psh != 0.0) { + psh = ph * PS_UNIT_SIZE; + if (ctx->res_unit == RESUNIT_CENTIMETER) + psh *= 2.54F; + } else + psh=ctx->rotate ? prw:prh; + fprintf(ctx->fd, + "1 dict begin /PageSize [ %s %s ] def currentdict end setpagedevice\n", + g_ascii_dtostr(buf[0], sizeof(buf[0]), psw), + g_ascii_dtostr(buf[1], sizeof(buf[1]), psh)); + fputs( + "<<\n /Policies <<\n /PageSize 3\n >>\n>> setpagedevice\n", + ctx->fd); + } + fprintf(ctx->fd, "gsave\n"); + fprintf(ctx->fd, "100 dict begin\n"); + if (pw != 0 || ph != 0) { + if (!pw) + pw = prw; + if (!ph) + ph = prh; + if (ctx->maxPageHeight) { /* used -H option */ + split = PlaceImage(ctx,pw,ph,prw,prh, + 0,lm,bm,cnt); + while( split ) { + PSpage(ctx, tif, w, h); + fprintf(ctx->fd, "end\n"); + fprintf(ctx->fd, "grestore\n"); + fprintf(ctx->fd, "showpage\n"); + ctx->npages++; + fprintf(ctx->fd, "%%%%Page: %d %d\n", + ctx->npages, ctx->npages); + fprintf(ctx->fd, "gsave\n"); + fprintf(ctx->fd, "100 dict begin\n"); + split = PlaceImage(ctx,pw,ph,prw,prh, + split,lm,bm,cnt); + } + } else { + pw *= PS_UNIT_SIZE; + ph *= PS_UNIT_SIZE; + + /* NB: maintain image aspect ratio */ + scale = pw/prw < ph/prh ? + pw/prw : ph/prh; + if (scale > 1.0) + scale = 1.0; + if (cnt) { + bottom_offset += + (ph - prh * scale) / 2; + left_offset += + (pw - prw * scale) / 2; + } + fprintf(ctx->fd, "%s %s translate\n", + g_ascii_dtostr(buf[0], sizeof(buf[0]), left_offset), + g_ascii_dtostr(buf[1], sizeof(buf[1]), bottom_offset)); + fprintf(ctx->fd, "%s %s scale\n", + g_ascii_dtostr(buf[0], sizeof(buf[0]), prw * scale), + g_ascii_dtostr(buf[1], sizeof(buf[1]), prh * scale)); + if (ctx->rotate) + fputs ("1 1 translate 180 ctx->rotate\n", ctx->fd); + } + } else { + fprintf(ctx->fd, "%s %s scale\n", + g_ascii_dtostr(buf[0], sizeof(buf[0]), prw), + g_ascii_dtostr(buf[1], sizeof(buf[1]), prh)); + if (ctx->rotate) + fputs ("1 1 translate 180 ctx->rotate\n", ctx->fd); + } + PSpage(ctx, tif, w, h); + fprintf(ctx->fd, "end\n"); + fprintf(ctx->fd, "grestore\n"); + fprintf(ctx->fd, "showpage\n"); + } +} + + +static char DuplexPreamble[] = "\ +%%BeginFeature: *Duplex True\n\ +systemdict begin\n\ + /languagelevel where { pop languagelevel } { 1 } ifelse\n\ + 2 ge { 1 dict dup /Duplex true put setpagedevice }\n\ + { statusdict /setduplex known { statusdict begin setduplex true end } if\n\ + } ifelse\n\ +end\n\ +%%EndFeature\n\ +"; + +static char TumblePreamble[] = "\ +%%BeginFeature: *Tumble True\n\ +systemdict begin\n\ + /languagelevel where { pop languagelevel } { 1 } ifelse\n\ + 2 ge { 1 dict dup /Tumble true put setpagedevice }\n\ + { statusdict /settumble known { statusdict begin true settumble end } if\n\ + } ifelse\n\ +end\n\ +%%EndFeature\n\ +"; + +static char AvoidDeadZonePreamble[] = "\ +gsave newpath clippath pathbbox grestore\n\ + 4 2 roll 2 copy translate\n\ + exch 3 1 roll sub 3 1 roll sub exch\n\ + currentpagedevice /PageSize get aload pop\n\ + exch 3 1 roll div 3 1 roll div abs exch abs\n\ + 2 copy gt { exch } if pop\n\ + dup 1 lt { dup scale } { pop } ifelse\n\ +"; + +void +PSHead(TIFF2PSContext *ctx, TIFF *tif, uint32 w, uint32 h, + double pw, double ph, double ox, double oy) +{ + time_t t; + + (void) tif; (void) w; (void) h; + t = time(0); + fprintf(ctx->fd, "%%!PS-Adobe-3.0%s\n", + ctx->generateEPSF ? " EPSF-3.0" : ""); + fprintf(ctx->fd, "%%%%Creator: Evince\n"); + fprintf(ctx->fd, "%%%%CreationDate: %s", ctime(&t)); + fprintf(ctx->fd, "%%%%DocumentData: Clean7Bit\n"); + fprintf(ctx->fd, "%%%%Origin: %ld %ld\n", (long) ox, (long) oy); + /* NB: should use PageBoundingBox */ + fprintf(ctx->fd, "%%%%BoundingBox: 0 0 %ld %ld\n", + (long) ceil(pw), (long) ceil(ph)); + fprintf(ctx->fd, "%%%%LanguageLevel: %d\n", + (ctx->level3 ? 3 : (ctx->level2 ? 2 : 1))); + fprintf(ctx->fd, "%%%%Pages: (atend)\n"); + fprintf(ctx->fd, "%%%%EndComments\n"); + fprintf(ctx->fd, "%%%%BeginSetup\n"); + if (ctx->PSduplex) + fprintf(ctx->fd, "%s", DuplexPreamble); + if (ctx->PStumble) + fprintf(ctx->fd, "%s", TumblePreamble); + if (ctx->PSavoiddeadzone && (ctx->level2 || ctx->level3)) + fprintf(ctx->fd, "%s", AvoidDeadZonePreamble); + fprintf(ctx->fd, "%%%%EndSetup\n"); +} + +static void +PSTail(TIFF2PSContext *ctx) +{ + if (!ctx->npages) + return; + fprintf(ctx->fd, "%%%%Trailer\n"); + fprintf(ctx->fd, "%%%%Pages: %d\n", ctx->npages); + fprintf(ctx->fd, "%%%%EOF\n"); +} + +static int +checkcmap(TIFF2PSContext* ctx, TIFF* tif, int n, + uint16* r, uint16* g, uint16* b) +{ + (void) tif; + while (n-- > 0) + if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256) + return (16); + TIFFWarning(ctx->filename, "Assuming 8-bit colormap"); + return (8); +} + +static void +PS_Lvl2colorspace(TIFF2PSContext* ctx, TIFF* tif) +{ + uint16 *rmap, *gmap, *bmap; + int i, num_colors; + const char * colorspace_p; + + switch ( ctx->photometric ) + { + case PHOTOMETRIC_SEPARATED: + colorspace_p = "CMYK"; + break; + + case PHOTOMETRIC_RGB: + colorspace_p = "RGB"; + break; + + default: + colorspace_p = "Gray"; + } + + /* + * Set up PostScript Level 2 colorspace according to + * section 4.8 in the PostScript refenence manual. + */ + fputs("% PostScript Level 2 only.\n", ctx->fd); + if (ctx->photometric != PHOTOMETRIC_PALETTE) { + if (ctx->photometric == PHOTOMETRIC_YCBCR) { + /* MORE CODE HERE */ + } + fprintf(ctx->fd, "/Device%s setcolorspace\n", colorspace_p ); + return; + } + + /* + * Set up an indexed/palette colorspace + */ + num_colors = (1 << ctx->bitspersample); + if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap)) { + TIFFError(ctx->filename, + "Palette image w/o \"Colormap\" tag"); + return; + } + if (checkcmap(ctx, tif, num_colors, rmap, gmap, bmap) == 16) { + /* + * Convert colormap to 8-bits values. + */ +#define CVT(x) (((x) * 255) / ((1L<<16)-1)) + for (i = 0; i < num_colors; i++) { + rmap[i] = CVT(rmap[i]); + gmap[i] = CVT(gmap[i]); + bmap[i] = CVT(bmap[i]); + } +#undef CVT + } + fprintf(ctx->fd, "[ /Indexed /DeviceRGB %d", num_colors - 1); + if (ctx->ascii85) { + Ascii85Init(ctx); + fputs("\n<~", ctx->fd); + ctx->ascii85breaklen -= 2; + } else + fputs(" <", ctx->fd); + for (i = 0; i < num_colors; i++) { + if (ctx->ascii85) { + Ascii85Put(ctx, (unsigned char)rmap[i]); + Ascii85Put(ctx, (unsigned char)gmap[i]); + Ascii85Put(ctx, (unsigned char)bmap[i]); + } else { + fputs((i % 8) ? " " : "\n ", ctx->fd); + fprintf(ctx->fd, "%02x%02x%02x", + rmap[i], gmap[i], bmap[i]); + } + } + if (ctx->ascii85) + Ascii85Flush(ctx); + else + fputs(">\n", ctx->fd); + fputs("] setcolorspace\n", ctx->fd); +} + +static int +PS_Lvl2ImageDict(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h) +{ + int use_rawdata; + uint32 tile_width, tile_height; + uint16 predictor, minsamplevalue, maxsamplevalue; + int repeat_count; + char im_h[64], im_x[64], im_y[64]; + char * imageOp = "image"; + + if ( ctx->useImagemask && (ctx->bitspersample == 1) ) + imageOp = "imagemask"; + + (void)strcpy(im_x, "0"); + (void)sprintf(im_y, "%lu", (long) h); + (void)sprintf(im_h, "%lu", (long) h); + tile_width = w; + tile_height = h; + if (TIFFIsTiled(tif)) { + repeat_count = TIFFNumberOfTiles(tif); + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width); + TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height); + if (tile_width > w || tile_height > h || + (w % tile_width) != 0 || (h % tile_height != 0)) { + /* + * The tiles does not fit image width and height. + * Set up a clip rectangle for the image unit square. + */ + fputs("0 0 1 1 rectclip\n", ctx->fd); + } + if (tile_width < w) { + fputs("/im_x 0 def\n", ctx->fd); + (void)strcpy(im_x, "im_x neg"); + } + if (tile_height < h) { + fputs("/im_y 0 def\n", ctx->fd); + (void)sprintf(im_y, "%lu im_y sub", (unsigned long) h); + } + } else { + repeat_count = ctx->tf_numberstrips; + tile_height = ctx->tf_rowsperstrip; + if (tile_height > h) + tile_height = h; + if (repeat_count > 1) { + fputs("/im_y 0 def\n", ctx->fd); + fprintf(ctx->fd, "/im_h %lu def\n", + (unsigned long) tile_height); + (void)strcpy(im_h, "im_h"); + (void)sprintf(im_y, "%lu im_y sub", (unsigned long) h); + } + } + + /* + * Output start of exec block + */ + fputs("{ % exec\n", ctx->fd); + + if (repeat_count > 1) + fprintf(ctx->fd, "%d { %% repeat\n", repeat_count); + + /* + * Output filter options and image dictionary. + */ + if (ctx->ascii85) + fputs(" /im_stream currentfile /ASCII85Decode filter def\n", + ctx->fd); + fputs(" <<\n", ctx->fd); + fputs(" /ImageType 1\n", ctx->fd); + fprintf(ctx->fd, " /Width %lu\n", (unsigned long) tile_width); + /* + * Workaround for some software that may crash when last strip + * of image contains fewer number of scanlines than specified + * by the `/Height' variable. So for stripped images with multiple + * strips we will set `/Height' as `im_h', because one is + * recalculated for each strip - including the (smaller) final strip. + * For tiled images and images with only one strip `/Height' will + * contain number of scanlines in tile (or image height in case of + * one-stripped image). + */ + if (TIFFIsTiled(tif) || ctx->tf_numberstrips == 1) + fprintf(ctx->fd, " /Height %lu\n", (unsigned long) tile_height); + else + fprintf(ctx->fd, " /Height im_h\n"); + + if (ctx->planarconfiguration == PLANARCONFIG_SEPARATE && ctx->samplesperpixel > 1) + fputs(" /MultipleDataSources true\n", ctx->fd); + fprintf(ctx->fd, " /ImageMatrix [ %lu 0 0 %ld %s %s ]\n", + (unsigned long) w, - (long)h, im_x, im_y); + fprintf(ctx->fd, " /BitsPerComponent %d\n", ctx->bitspersample); + fprintf(ctx->fd, " /Ctx->Interpolate %s\n", ctx->interpolate ? "true" : "false"); + + switch (ctx->samplesperpixel - ctx->extrasamples) { + case 1: + switch (ctx->photometric) { + case PHOTOMETRIC_MINISBLACK: + fputs(" /Decode [0 1]\n", ctx->fd); + break; + case PHOTOMETRIC_MINISWHITE: + switch (ctx->compression) { + case COMPRESSION_CCITTRLE: + case COMPRESSION_CCITTRLEW: + case COMPRESSION_CCITTFAX3: + case COMPRESSION_CCITTFAX4: + /* + * Manage inverting with /Blackis1 flag + * since there migth be uncompressed parts + */ + fputs(" /Decode [0 1]\n", ctx->fd); + break; + default: + /* + * ERROR... + */ + fputs(" /Decode [1 0]\n", ctx->fd); + break; + } + break; + case PHOTOMETRIC_PALETTE: + TIFFGetFieldDefaulted(tif, TIFFTAG_MINSAMPLEVALUE, + &minsamplevalue); + TIFFGetFieldDefaulted(tif, TIFFTAG_MAXSAMPLEVALUE, + &maxsamplevalue); + fprintf(ctx->fd, " /Decode [%u %u]\n", + minsamplevalue, maxsamplevalue); + break; + default: + /* + * ERROR ? + */ + fputs(" /Decode [0 1]\n", ctx->fd); + break; + } + break; + case 3: + switch (ctx->photometric) { + case PHOTOMETRIC_RGB: + fputs(" /Decode [0 1 0 1 0 1]\n", ctx->fd); + break; + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + default: + /* + * ERROR?? + */ + fputs(" /Decode [0 1 0 1 0 1]\n", ctx->fd); + break; + } + break; + case 4: + /* + * ERROR?? + */ + fputs(" /Decode [0 1 0 1 0 1 0 1]\n", ctx->fd); + break; + } + fputs(" /DataSource", ctx->fd); + if (ctx->planarconfiguration == PLANARCONFIG_SEPARATE && + ctx->samplesperpixel > 1) + fputs(" [", ctx->fd); + if (ctx->ascii85) + fputs(" im_stream", ctx->fd); + else + fputs(" currentfile /ASCIIHexDecode filter", ctx->fd); + + use_rawdata = TRUE; + switch (ctx->compression) { + case COMPRESSION_NONE: /* 1: uncompressed */ + break; + case COMPRESSION_CCITTRLE: /* 2: CCITT modified Huffman RLE */ + case COMPRESSION_CCITTRLEW: /* 32771: #1 w/ word alignment */ + case COMPRESSION_CCITTFAX3: /* 3: CCITT Group 3 fax encoding */ + case COMPRESSION_CCITTFAX4: /* 4: CCITT Group 4 fax encoding */ + fputs("\n\t<<\n", ctx->fd); + if (ctx->compression == COMPRESSION_CCITTFAX3) { + uint32 g3_options; + + fputs("\t /EndOfLine true\n", ctx->fd); + fputs("\t /EndOfBlock false\n", ctx->fd); + if (!TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, + &g3_options)) + g3_options = 0; + if (g3_options & GROUP3OPT_2DENCODING) + fprintf(ctx->fd, "\t /K %s\n", im_h); + if (g3_options & GROUP3OPT_UNCOMPRESSED) + fputs("\t /Uncompressed true\n", ctx->fd); + if (g3_options & GROUP3OPT_FILLBITS) + fputs("\t /EncodedByteAlign true\n", ctx->fd); + } + if (ctx->compression == COMPRESSION_CCITTFAX4) { + uint32 g4_options; + + fputs("\t /K -1\n", ctx->fd); + TIFFGetFieldDefaulted(tif, TIFFTAG_GROUP4OPTIONS, + &g4_options); + if (g4_options & GROUP4OPT_UNCOMPRESSED) + fputs("\t /Uncompressed true\n", ctx->fd); + } + if (!(tile_width == w && w == 1728U)) + fprintf(ctx->fd, "\t /Columns %lu\n", + (unsigned long) tile_width); + fprintf(ctx->fd, "\t /Rows %s\n", im_h); + if (ctx->compression == COMPRESSION_CCITTRLE || + ctx->compression == COMPRESSION_CCITTRLEW) { + fputs("\t /EncodedByteAlign true\n", ctx->fd); + fputs("\t /EndOfBlock false\n", ctx->fd); + } + if (ctx->photometric == PHOTOMETRIC_MINISBLACK) + fputs("\t /BlackIs1 true\n", ctx->fd); + fprintf(ctx->fd, "\t>> /CCITTFaxDecode filter"); + break; + case COMPRESSION_LZW: /* 5: Lempel-Ziv & Welch */ + TIFFGetFieldDefaulted(tif, TIFFTAG_PREDICTOR, &predictor); + if (predictor == 2) { + fputs("\n\t<<\n", ctx->fd); + fprintf(ctx->fd, "\t /Predictor %u\n", predictor); + fprintf(ctx->fd, "\t /Columns %lu\n", + (unsigned long) tile_width); + fprintf(ctx->fd, "\t /Colors %u\n", ctx->samplesperpixel); + fprintf(ctx->fd, "\t /BitsPerComponent %u\n", + ctx->bitspersample); + fputs("\t>>", ctx->fd); + } + fputs(" /LZWDecode filter", ctx->fd); + break; + case COMPRESSION_DEFLATE: /* 5: ZIP */ + case COMPRESSION_ADOBE_DEFLATE: + if ( ctx->level3 ) { + TIFFGetFieldDefaulted(tif, TIFFTAG_PREDICTOR, &predictor); + if (predictor > 1) { + fprintf(ctx->fd, "\t %% PostScript Level 3 only."); + fputs("\n\t<<\n", ctx->fd); + fprintf(ctx->fd, "\t /Predictor %u\n", predictor); + fprintf(ctx->fd, "\t /Columns %lu\n", + (unsigned long) tile_width); + fprintf(ctx->fd, "\t /Colors %u\n", ctx->samplesperpixel); + fprintf(ctx->fd, "\t /BitsPerComponent %u\n", + ctx->bitspersample); + fputs("\t>>", ctx->fd); + } + fputs(" /FlateDecode filter", ctx->fd); + } else { + use_rawdata = FALSE ; + } + break; + case COMPRESSION_PACKBITS: /* 32773: Macintosh RLE */ + fputs(" /RunLengthDecode filter", ctx->fd); + use_rawdata = TRUE; + break; + case COMPRESSION_OJPEG: /* 6: !6.0 JPEG */ + case COMPRESSION_JPEG: /* 7: %JPEG DCT ctx->compression */ +#ifdef notdef + /* + * Code not tested yet + */ + fputs(" /DCTDecode filter", ctx->fd); + use_rawdata = TRUE; +#else + use_rawdata = FALSE; +#endif + break; + case COMPRESSION_NEXT: /* 32766: NeXT 2-bit RLE */ + case COMPRESSION_THUNDERSCAN: /* 32809: ThunderScan RLE */ + case COMPRESSION_PIXARFILM: /* 32908: Pixar companded 10bit LZW */ + case COMPRESSION_JBIG: /* 34661: ISO JBIG */ + use_rawdata = FALSE; + break; + case COMPRESSION_SGILOG: /* 34676: SGI LogL or LogLuv */ + case COMPRESSION_SGILOG24: /* 34677: SGI 24-bit LogLuv */ + use_rawdata = FALSE; + break; + default: + /* + * ERROR... + */ + use_rawdata = FALSE; + break; + } + if (ctx->planarconfiguration == PLANARCONFIG_SEPARATE && + ctx->samplesperpixel > 1) { + uint16 i; + + /* + * NOTE: This code does not work yet... + */ + for (i = 1; i < ctx->samplesperpixel; i++) + fputs(" dup", ctx->fd); + fputs(" ]", ctx->fd); + } + + fprintf( ctx->fd, "\n >> %s\n", imageOp ); + if (ctx->ascii85) + fputs(" im_stream status { im_stream flushfile } if\n", ctx->fd); + if (repeat_count > 1) { + if (tile_width < w) { + fprintf(ctx->fd, " /im_x im_x %lu add def\n", + (unsigned long) tile_width); + if (tile_height < h) { + fprintf(ctx->fd, " im_x %lu ge {\n", + (unsigned long) w); + fputs(" /im_x 0 def\n", ctx->fd); + fprintf(ctx->fd, " /im_y im_y %lu add def\n", + (unsigned long) tile_height); + fputs(" } if\n", ctx->fd); + } + } + if (tile_height < h) { + if (tile_width >= w) { + fprintf(ctx->fd, " /im_y im_y %lu add def\n", + (unsigned long) tile_height); + if (!TIFFIsTiled(tif)) { + fprintf(ctx->fd, " /im_h %lu im_y sub", + (unsigned long) h); + fprintf(ctx->fd, " dup %lu gt { pop", + (unsigned long) tile_height); + fprintf(ctx->fd, " %lu } if def\n", + (unsigned long) tile_height); + } + } + } + fputs("} repeat\n", ctx->fd); + } + /* + * End of exec function + */ + fputs("}\n", ctx->fd); + + return(use_rawdata); +} + +#define MAXLINE 36 + +static int +PS_Lvl2page(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h) +{ + uint16 fillorder; + int use_rawdata, tiled_image, breaklen = MAXLINE; + uint32 chunk_no, num_chunks, *bc; + unsigned char *buf_data, *cp; + tsize_t chunk_size, byte_count; + +#if defined( EXP_ASCII85ENCODER ) + int ascii85_l; /* Length, in bytes, of ascii85_p[] data */ + uint8 * ascii85_p = 0; /* Holds ASCII85 encoded data */ +#endif + + PS_Lvl2colorspace(ctx, tif); + use_rawdata = PS_Lvl2ImageDict(ctx, tif, w, h); + +/* See http://bugzilla.remotesensing.org/show_bug.cgi?id=80 */ +#ifdef ENABLE_BROKEN_BEGINENDDATA + fputs("%%BeginData:\n", ctx->fd); +#endif + fputs("exec\n", ctx->fd); + + tiled_image = TIFFIsTiled(tif); + if (tiled_image) { + num_chunks = TIFFNumberOfTiles(tif); + TIFFGetField(tif, TIFFTAG_TILEBYTECOUNTS, &bc); + } else { + num_chunks = TIFFNumberOfStrips(tif); + TIFFGetField(tif, TIFFTAG_STRIPBYTECOUNTS, &bc); + } + + if (use_rawdata) { + chunk_size = (tsize_t) bc[0]; + for (chunk_no = 1; chunk_no < num_chunks; chunk_no++) + if ((tsize_t) bc[chunk_no] > chunk_size) + chunk_size = (tsize_t) bc[chunk_no]; + } else { + if (tiled_image) + chunk_size = TIFFTileSize(tif); + else + chunk_size = TIFFStripSize(tif); + } + buf_data = (unsigned char *)_TIFFmalloc(chunk_size); + if (!buf_data) { + TIFFError(ctx->filename, "Can't alloc %u bytes for %s.", + chunk_size, tiled_image ? "tiles" : "strips"); + return(FALSE); + } + +#if defined( EXP_ASCII85ENCODER ) + if ( ctx->ascii85 ) { + /* + * Allocate a buffer to hold the ASCII85 encoded data. Note + * that it is allocated with sufficient room to hold the + * encoded data (5*chunk_size/4) plus the EOD marker (+8) + * and formatting line breaks. The line breaks are more + * than taken care of by using 6*chunk_size/4 rather than + * 5*chunk_size/4. + */ + + ascii85_p = _TIFFmalloc( (chunk_size+(chunk_size/2)) + 8 ); + + if ( !ascii85_p ) { + _TIFFfree( buf_data ); + + TIFFError( ctx->filename, + "Cannot allocate ASCII85 encoding buffer." ); + return ( FALSE ); + } + } +#endif + + TIFFGetFieldDefaulted(tif, TIFFTAG_FILLORDER, &fillorder); + for (chunk_no = 0; chunk_no < num_chunks; chunk_no++) { + if (ctx->ascii85) + Ascii85Init(ctx); + else + breaklen = MAXLINE; + if (use_rawdata) { + if (tiled_image) + byte_count = TIFFReadRawTile(tif, chunk_no, + buf_data, chunk_size); + else + byte_count = TIFFReadRawStrip(tif, chunk_no, + buf_data, chunk_size); + if (fillorder == FILLORDER_LSB2MSB) + TIFFReverseBits(buf_data, byte_count); + } else { + if (tiled_image) + byte_count = TIFFReadEncodedTile(tif, + chunk_no, buf_data, + chunk_size); + else + byte_count = TIFFReadEncodedStrip(tif, + chunk_no, buf_data, + chunk_size); + } + if (byte_count < 0) { + TIFFError(ctx->filename, "Can't read %s %d.", + tiled_image ? "tile" : "strip", chunk_no); + if (ctx->ascii85) + Ascii85Put(ctx, '\0'); + } + /* + * For images with ctx->alpha, matte against a white background; + * i.e. Cback * (1 - Aimage) where Cback = 1. We will fill the + * lower part of the buffer with the modified values. + * + * XXX: needs better solution + */ + if (ctx->alpha) { + int adjust, i, j = 0; + int ncomps = ctx->samplesperpixel - ctx->extrasamples; + for (i = 0; i < byte_count; i+=ctx->samplesperpixel) { + adjust = 255 - buf_data[i + ncomps]; + switch (ncomps) { + case 1: + buf_data[j++] = buf_data[i] + adjust; + break; + case 2: + buf_data[j++] = buf_data[i] + adjust; + buf_data[j++] = buf_data[i+1] + adjust; + break; + case 3: + buf_data[j++] = buf_data[i] + adjust; + buf_data[j++] = buf_data[i+1] + adjust; + buf_data[j++] = buf_data[i+2] + adjust; + break; + } + } + byte_count -= j; + } + + if (ctx->ascii85) { +#if defined( EXP_ASCII85ENCODER ) + ascii85_l = Ascii85EncodeBlock(ctx, ascii85_p, 1, + buf_data, byte_count); + + if ( ascii85_l > 0 ) + fwrite( ascii85_p, ascii85_l, 1, ctx->fd ); +#else + for (cp = buf_data; byte_count > 0; byte_count--) + Ascii85Put(ctx, *cp++); +#endif + } + else + { + for (cp = buf_data; byte_count > 0; byte_count--) { + putc(hex[((*cp)>>4)&0xf], ctx->fd); + putc(hex[(*cp)&0xf], ctx->fd); + cp++; + + if (--breaklen <= 0) { + putc('\n', ctx->fd); + breaklen = MAXLINE; + } + } + } + + if ( !ctx->ascii85 ) { + if ( ctx->level2 || ctx->level3 ) + putc( '>', ctx->fd ); + putc('\n', ctx->fd); + } +#if !defined( EXP_ASCII85ENCODER ) + else + Ascii85Flush(ctx); +#endif + } + +#if defined( EXP_ASCII85ENCODER ) + if ( ascii85_p ) + _TIFFfree( ascii85_p ); +#endif + _TIFFfree(buf_data); +#ifdef ENABLE_BROKEN_BEGINENDDATA + fputs("%%EndData\n", ctx->fd); +#endif + return(TRUE); +} + +void +PSpage(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h) +{ + char *imageOp = "image"; + + if ( ctx->useImagemask && (ctx->bitspersample == 1) ) + imageOp = "imagemask"; + + if ((ctx->level2 || ctx->level3) && PS_Lvl2page(ctx, tif, w, h)) + return; + ctx->ps_bytesperrow = ctx->tf_bytesperrow - (ctx->extrasamples * ctx->bitspersample / 8)*w; + switch (ctx->photometric) { + case PHOTOMETRIC_RGB: + if (ctx->planarconfiguration == PLANARCONFIG_CONTIG) { + fprintf(ctx->fd, "%s", RGBcolorimage); + PSColorContigPreamble(ctx, w, h, 3); + PSDataColorContig(ctx, tif, w, h, 3); + } else { + PSColorSeparatePreamble(ctx, w, h, 3); + PSDataColorSeparate(ctx, tif, w, h, 3); + } + break; + case PHOTOMETRIC_SEPARATED: + /* XXX should emit CMYKcolorimage */ + if (ctx->planarconfiguration == PLANARCONFIG_CONTIG) { + PSColorContigPreamble(ctx, w, h, 4); + PSDataColorContig(ctx, tif, w, h, 4); + } else { + PSColorSeparatePreamble(ctx, w, h, 4); + PSDataColorSeparate(ctx, tif, w, h, 4); + } + break; + case PHOTOMETRIC_PALETTE: + fprintf(ctx->fd, "%s", RGBcolorimage); + PhotoshopBanner(ctx, w, h, 1, 3, "false 3 colorimage"); + fprintf(ctx->fd, "/scanLine %ld string def\n", + (long) ctx->ps_bytesperrow * 3L); + fprintf(ctx->fd, "%lu %lu 8\n", + (unsigned long) w, (unsigned long) h); + fprintf(ctx->fd, "[%lu 0 0 -%lu 0 %lu]\n", + (unsigned long) w, (unsigned long) h, + (unsigned long) h); + fprintf(ctx->fd, + "{currentfile scanLine readhexstring pop} bind\n"); + fprintf(ctx->fd, "false 3 colorimage\n"); + PSDataPalette(ctx, tif, w, h); + break; + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: + PhotoshopBanner(ctx, w, h, 1, 1, imageOp); + fprintf(ctx->fd, "/scanLine %ld string def\n", + (long) ctx->ps_bytesperrow); + fprintf(ctx->fd, "%lu %lu %d\n", + (unsigned long) w, (unsigned long) h, ctx->bitspersample); + fprintf(ctx->fd, "[%lu 0 0 -%lu 0 %lu]\n", + (unsigned long) w, (unsigned long) h, (unsigned long) h); + fprintf(ctx->fd, + "{currentfile scanLine readhexstring pop} bind\n"); + fprintf(ctx->fd, "%s\n", imageOp); + PSDataBW(ctx, tif, w, h); + break; + } + putc('\n', ctx->fd); +} + +void +PSColorContigPreamble(TIFF2PSContext* ctx, uint32 w, uint32 h, int nc) +{ + ctx->ps_bytesperrow = nc * (ctx->tf_bytesperrow / ctx->samplesperpixel); + PhotoshopBanner(ctx, w, h, 1, nc, "false %d colorimage"); + fprintf(ctx->fd, "/line %ld string def\n", (long) ctx->ps_bytesperrow); + fprintf(ctx->fd, "%lu %lu %d\n", + (unsigned long) w, (unsigned long) h, ctx->bitspersample); + fprintf(ctx->fd, "[%lu 0 0 -%lu 0 %lu]\n", + (unsigned long) w, (unsigned long) h, (unsigned long) h); + fprintf(ctx->fd, "{currentfile line readhexstring pop} bind\n"); + fprintf(ctx->fd, "false %d colorimage\n", nc); +} + +void +PSColorSeparatePreamble(TIFF2PSContext* ctx, uint32 w, uint32 h, int nc) +{ + int i; + + PhotoshopBanner(ctx, w, h, ctx->ps_bytesperrow, nc, "true %d colorimage"); + for (i = 0; i < nc; i++) + fprintf(ctx->fd, "/line%d %ld string def\n", + i, (long) ctx->ps_bytesperrow); + fprintf(ctx->fd, "%lu %lu %d\n", + (unsigned long) w, (unsigned long) h, ctx->bitspersample); + fprintf(ctx->fd, "[%lu 0 0 -%lu 0 %lu] \n", + (unsigned long) w, (unsigned long) h, (unsigned long) h); + for (i = 0; i < nc; i++) + fprintf(ctx->fd, "{currentfile line%d readhexstring pop}bind\n", i); + fprintf(ctx->fd, "true %d colorimage\n", nc); +} + +#define DOBREAK(len, howmany, fd) \ + if (((len) -= (howmany)) <= 0) { \ + putc('\n', fd); \ + (len) = MAXLINE-(howmany); \ + } +#define PUTHEX(c,fd) putc(hex[((c)>>4)&0xf],fd); putc(hex[(c)&0xf],fd) + +void +PSDataColorContig(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h, int nc) +{ + uint32 row; + int breaklen = MAXLINE, cc, es = ctx->samplesperpixel - nc; + unsigned char *tf_buf; + unsigned char *cp, c; + + (void) w; + tf_buf = (unsigned char *) _TIFFmalloc(ctx->tf_bytesperrow); + if (tf_buf == NULL) { + TIFFError(ctx->filename, "No space for scanline buffer"); + return; + } + for (row = 0; row < h; row++) { + if (TIFFReadScanline(tif, tf_buf, row, 0) < 0) + break; + cp = tf_buf; + if (ctx->alpha) { + int adjust; + cc = 0; + for (; cc < ctx->tf_bytesperrow; cc += ctx->samplesperpixel) { + DOBREAK(breaklen, nc, ctx->fd); + /* + * For images with ctx->alpha, matte against + * a white background; i.e. + * Cback * (1 - Aimage) + * where Cback = 1. + */ + adjust = 255 - cp[nc]; + switch (nc) { + case 4: c = *cp++ + adjust; PUTHEX(c,ctx->fd); + case 3: c = *cp++ + adjust; PUTHEX(c,ctx->fd); + case 2: c = *cp++ + adjust; PUTHEX(c,ctx->fd); + case 1: c = *cp++ + adjust; PUTHEX(c,ctx->fd); + } + cp += es; + } + } else { + cc = 0; + for (; cc < ctx->tf_bytesperrow; cc += ctx->samplesperpixel) { + DOBREAK(breaklen, nc, ctx->fd); + switch (nc) { + case 4: c = *cp++; PUTHEX(c,ctx->fd); + case 3: c = *cp++; PUTHEX(c,ctx->fd); + case 2: c = *cp++; PUTHEX(c,ctx->fd); + case 1: c = *cp++; PUTHEX(c,ctx->fd); + } + cp += es; + } + } + } + _TIFFfree((char *) tf_buf); +} + +void +PSDataColorSeparate(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h, int nc) +{ + uint32 row; + int breaklen = MAXLINE, cc; + tsample_t s, maxs; + unsigned char *tf_buf; + unsigned char *cp, c; + + (void) w; + tf_buf = (unsigned char *) _TIFFmalloc(ctx->tf_bytesperrow); + if (tf_buf == NULL) { + TIFFError(ctx->filename, "No space for scanline buffer"); + return; + } + maxs = (ctx->samplesperpixel > nc ? nc : ctx->samplesperpixel); + for (row = 0; row < h; row++) { + for (s = 0; s < maxs; s++) { + if (TIFFReadScanline(tif, tf_buf, row, s) < 0) + break; + for (cp = tf_buf, cc = 0; cc < ctx->tf_bytesperrow; cc++) { + DOBREAK(breaklen, 1, ctx->fd); + c = *cp++; + PUTHEX(c,ctx->fd); + } + } + } + _TIFFfree((char *) tf_buf); +} + +#define PUTRGBHEX(c,fd) \ + PUTHEX(rmap[c],fd); PUTHEX(gmap[c],fd); PUTHEX(bmap[c],fd) + +void +PSDataPalette(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h) +{ + uint16 *rmap, *gmap, *bmap; + uint32 row; + int breaklen = MAXLINE, cc, nc; + unsigned char *tf_buf; + unsigned char *cp, c; + + (void) w; + if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap)) { + TIFFError(ctx->filename, "Palette image w/o \"Colormap\" tag"); + return; + } + switch (ctx->bitspersample) { + case 8: case 4: case 2: case 1: + break; + default: + TIFFError(ctx->filename, "Depth %d not supported", ctx->bitspersample); + return; + } + nc = 3 * (8 / ctx->bitspersample); + tf_buf = (unsigned char *) _TIFFmalloc(ctx->tf_bytesperrow); + if (tf_buf == NULL) { + TIFFError(ctx->filename, "No space for scanline buffer"); + return; + } + if (checkcmap(ctx, tif, 1<<ctx->bitspersample, rmap, gmap, bmap) == 16) { + int i; +#define CVT(x) ((unsigned short) (((x) * 255) / ((1U<<16)-1))) + for (i = (1<<ctx->bitspersample)-1; i >= 0; i--) { + rmap[i] = CVT(rmap[i]); + gmap[i] = CVT(gmap[i]); + bmap[i] = CVT(bmap[i]); + } +#undef CVT + } + for (row = 0; row < h; row++) { + if (TIFFReadScanline(tif, tf_buf, row, 0) < 0) + break; + for (cp = tf_buf, cc = 0; cc < ctx->tf_bytesperrow; cc++) { + DOBREAK(breaklen, nc, ctx->fd); + switch (ctx->bitspersample) { + case 8: + c = *cp++; PUTRGBHEX(c, ctx->fd); + break; + case 4: + c = *cp++; PUTRGBHEX(c&0xf, ctx->fd); + c >>= 4; PUTRGBHEX(c, ctx->fd); + break; + case 2: + c = *cp++; PUTRGBHEX(c&0x3, ctx->fd); + c >>= 2; PUTRGBHEX(c&0x3, ctx->fd); + c >>= 2; PUTRGBHEX(c&0x3, ctx->fd); + c >>= 2; PUTRGBHEX(c, ctx->fd); + break; + case 1: + c = *cp++; PUTRGBHEX(c&0x1, ctx->fd); + c >>= 1; PUTRGBHEX(c&0x1, ctx->fd); + c >>= 1; PUTRGBHEX(c&0x1, ctx->fd); + c >>= 1; PUTRGBHEX(c&0x1, ctx->fd); + c >>= 1; PUTRGBHEX(c&0x1, ctx->fd); + c >>= 1; PUTRGBHEX(c&0x1, ctx->fd); + c >>= 1; PUTRGBHEX(c&0x1, ctx->fd); + c >>= 1; PUTRGBHEX(c, ctx->fd); + break; + } + } + } + _TIFFfree((char *) tf_buf); +} + +void +PSDataBW(TIFF2PSContext* ctx, TIFF* tif, uint32 w, uint32 h) +{ + int breaklen = MAXLINE; + unsigned char* tf_buf; + unsigned char* cp; + tsize_t stripsize = TIFFStripSize(tif); + tstrip_t s; + +#if defined( EXP_ASCII85ENCODER ) + int ascii85_l; /* Length, in bytes, of ascii85_p[] data */ + uint8 *ascii85_p = 0; /* Holds ASCII85 encoded data */ +#endif + + (void) w; (void) h; + tf_buf = (unsigned char *) _TIFFmalloc(stripsize); + memset(tf_buf, 0, stripsize); + if (tf_buf == NULL) { + TIFFError(ctx->filename, "No space for scanline buffer"); + return; + } + +#if defined( EXP_ASCII85ENCODER ) + if ( ctx->ascii85 ) { + /* + * Allocate a buffer to hold the ASCII85 encoded data. Note + * that it is allocated with sufficient room to hold the + * encoded data (5*stripsize/4) plus the EOD marker (+8) + * and formatting line breaks. The line breaks are more + * than taken care of by using 6*stripsize/4 rather than + * 5*stripsize/4. + */ + + ascii85_p = _TIFFmalloc( (stripsize+(stripsize/2)) + 8 ); + + if ( !ascii85_p ) { + _TIFFfree( tf_buf ); + + TIFFError( ctx->filename, + "Cannot allocate ASCII85 encoding buffer." ); + return; + } + } +#endif + + if (ctx->ascii85) + Ascii85Init(ctx); + + for (s = 0; s < TIFFNumberOfStrips(tif); s++) { + int cc = TIFFReadEncodedStrip(tif, s, tf_buf, stripsize); + if (cc < 0) { + TIFFError(ctx->filename, "Can't read strip"); + break; + } + cp = tf_buf; + if (ctx->photometric == PHOTOMETRIC_MINISWHITE) { + for (cp += cc; --cp >= tf_buf;) + *cp = ~*cp; + cp++; + } + if (ctx->ascii85) { +#if defined( EXP_ASCII85ENCODER ) + if (ctx->alpha) { + int adjust, i; + for (i = 0; i < cc; i+=2) { + adjust = 255 - cp[i + 1]; + cp[i / 2] = cp[i] + adjust; + } + cc /= 2; + } + + ascii85_l = Ascii85EncodeBlock(ctx, ascii85_p, 1, cp, + cc); + + if ( ascii85_l > 0 ) + fwrite( ascii85_p, ascii85_l, 1, ctx->fd ); +#else + while (cc-- > 0) + Ascii85Put(ctx, *cp++); +#endif /* EXP_ASCII85_ENCODER */ + } else { + unsigned char c; + + if (ctx->alpha) { + int adjust; + while (cc-- > 0) { + DOBREAK(breaklen, 1, ctx->fd); + /* + * For images with ctx->alpha, matte against + * a white background; i.e. + * Cback * (1 - Aimage) + * where Cback = 1. + */ + adjust = 255 - cp[1]; + c = *cp++ + adjust; PUTHEX(c,ctx->fd); + cp++, cc--; + } + } else { + while (cc-- > 0) { + c = *cp++; + DOBREAK(breaklen, 1, ctx->fd); + PUTHEX(c, ctx->fd); + } + } + } + } + + if ( !ctx->ascii85 ) + { + if ( ctx->level2 || ctx->level3) + fputs(">\n", ctx->fd); + } +#if !defined( EXP_ASCII85ENCODER ) + else + Ascii85Flush(ctx); +#else + if ( ascii85_p ) + _TIFFfree( ascii85_p ); +#endif + + _TIFFfree(tf_buf); +} + +static void +Ascii85Init(TIFF2PSContext *ctx) +{ + ctx->ascii85breaklen = 2*MAXLINE; + ctx->ascii85count = 0; +} + +static void +Ascii85Encode(unsigned char* raw, char *buf) +{ + uint32 word; + + word = (((raw[0]<<8)+raw[1])<<16) + (raw[2]<<8) + raw[3]; + if (word != 0L) { + uint32 q; + uint16 w1; + + q = word / (85L*85*85*85); /* actually only a byte */ + buf[0] = (char) (q + '!'); + + word -= q * (85L*85*85*85); q = word / (85L*85*85); + buf[1] = (char) (q + '!'); + + word -= q * (85L*85*85); q = word / (85*85); + buf[2] = (char) (q + '!'); + + w1 = (uint16) (word - q*(85L*85)); + buf[3] = (char) ((w1 / 85) + '!'); + buf[4] = (char) ((w1 % 85) + '!'); + buf[5] = '\0'; + } else + buf[0] = 'z', buf[1] = '\0'; +} + +void +Ascii85Put(TIFF2PSContext *ctx, unsigned char code) +{ + ctx->ascii85buf[ctx->ascii85count++] = code; + if (ctx->ascii85count >= 4) { + unsigned char* p; + int n; + char buf[6]; + + for (n = ctx->ascii85count, p = ctx->ascii85buf; + n >= 4; n -= 4, p += 4) { + char* cp; + Ascii85Encode(p, buf); + for (cp = buf; *cp; cp++) { + putc(*cp, ctx->fd); + if (--ctx->ascii85breaklen == 0) { + putc('\n', ctx->fd); + ctx->ascii85breaklen = 2*MAXLINE; + } + } + } + _TIFFmemcpy(ctx->ascii85buf, p, n); + ctx->ascii85count = n; + } +} + +void +Ascii85Flush(TIFF2PSContext* ctx) +{ + if (ctx->ascii85count > 0) { + char res[6]; + _TIFFmemset(&ctx->ascii85buf[ctx->ascii85count], 0, 3); + Ascii85Encode(ctx->ascii85buf, res); + fwrite(res[0] == 'z' ? "!!!!" : res, ctx->ascii85count + 1, 1, ctx->fd); + } + fputs("~>\n", ctx->fd); +} +#if defined( EXP_ASCII85ENCODER) + +#define A85BREAKCNTR ctx->ascii85breaklen +#define A85BREAKLEN (2*MAXLINE) + +/***************************************************************************** +* +* Name: Ascii85EncodeBlock( ascii85_p, f_eod, raw_p, raw_l ) +* +* Description: This routine will encode the raw data in the buffer described +* by raw_p and raw_l into ASCII85 format and store the encoding +* in the buffer given by ascii85_p. +* +* Parameters: ctx - TIFF2PS context +* ascii85_p - A buffer supplied by the caller which will +* contain the encoded ASCII85 data. +* f_eod - Flag: Nz means to end the encoded buffer with +* an End-Of-Data marker. +* raw_p - Pointer to the buffer of data to be encoded +* raw_l - Number of bytes in raw_p[] to be encoded +* +* Returns: (int) < 0 Error, see errno +* >= 0 Number of bytes written to ascii85_p[]. +* +* Notes: An external variable given by A85BREAKCNTR is used to +* determine when to insert newline characters into the +* encoded data. As each byte is placed into ascii85_p this +* external is decremented. If the variable is decrement to +* or past zero then a newline is inserted into ascii85_p +* and the A85BREAKCNTR is then reset to A85BREAKLEN. +* Note: for efficiency reasons the A85BREAKCNTR variable +* is not actually checked on *every* character +* placed into ascii85_p but often only for every +* 5 characters. +* +* THE CALLER IS RESPONSIBLE FOR ENSURING THAT ASCII85_P[] IS +* SUFFICIENTLY LARGE TO THE ENCODED DATA! +* You will need at least 5 * (raw_l/4) bytes plus space for +* newline characters and space for an EOD marker (if +* requested). A safe calculation is to use 6*(raw_l/4) + 8 +* to size ascii85_p. +* +*****************************************************************************/ + +int Ascii85EncodeBlock( TIFF2PSContext *ctx, uint8 * ascii85_p, + unsigned f_eod, const uint8 * raw_p, int raw_l ) + +{ + char ascii85[5]; /* Encoded 5 tuple */ + int ascii85_l; /* Number of bytes written to ascii85_p[] */ + int rc; /* Return code */ + uint32 val32; /* Unencoded 4 tuple */ + + ascii85_l = 0; /* Nothing written yet */ + + if ( raw_p ) + { + --raw_p; /* Prepare for pre-increment fetches */ + + for ( ; raw_l > 3; raw_l -= 4 ) + { + val32 = *(++raw_p) << 24; + val32 += *(++raw_p) << 16; + val32 += *(++raw_p) << 8; + val32 += *(++raw_p); + + if ( val32 == 0 ) /* Special case */ + { + ascii85_p[ascii85_l] = 'z'; + rc = 1; + } + + else + { + ascii85[4] = (char) ((val32 % 85) + 33); + val32 /= 85; + + ascii85[3] = (char) ((val32 % 85) + 33); + val32 /= 85; + + ascii85[2] = (char) ((val32 % 85) + 33); + val32 /= 85; + + ascii85[1] = (char) ((val32 % 85) + 33); + ascii85[0] = (char) ((val32 / 85) + 33); + + _TIFFmemcpy( &ascii85_p[ascii85_l], ascii85, sizeof(ascii85) ); + rc = sizeof(ascii85); + } + + ascii85_l += rc; + + if ( (A85BREAKCNTR -= rc) <= 0 ) + { + ascii85_p[ascii85_l] = '\n'; + ++ascii85_l; + A85BREAKCNTR = A85BREAKLEN; + } + } + + /* + * Output any straggler bytes: + */ + + if ( raw_l > 0 ) + { + int len; /* Output this many bytes */ + + len = raw_l + 1; + val32 = *++raw_p << 24; /* Prime the pump */ + + if ( --raw_l > 0 ) val32 += *(++raw_p) << 16; + if ( --raw_l > 0 ) val32 += *(++raw_p) << 8; + + val32 /= 85; + + ascii85[3] = (char) ((val32 % 85) + 33); + val32 /= 85; + + ascii85[2] = (char) ((val32 % 85) + 33); + val32 /= 85; + + ascii85[1] = (char) ((val32 % 85) + 33); + ascii85[0] = (char) ((val32 / 85) + 33); + + _TIFFmemcpy( &ascii85_p[ascii85_l], ascii85, len ); + ascii85_l += len; + } + } + + /* + * If requested add an ASCII85 End Of Data marker: + */ + + if ( f_eod ) + { + ascii85_p[ascii85_l++] = '~'; + ascii85_p[ascii85_l++] = '>'; + ascii85_p[ascii85_l++] = '\n'; + } + + return ( ascii85_l ); + +} /* Ascii85EncodeBlock() */ + +#endif /* EXP_ASCII85ENCODER */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ diff --git a/backend/tiff/tiff2ps.h b/backend/tiff/tiff2ps.h new file mode 100644 index 00000000..575b881b --- /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <glib.h> +#include <stdio.h> +#include "tiffio.h" + +typedef struct _TIFF2PSContext TIFF2PSContext; + +TIFF2PSContext *tiff2ps_context_new(const gchar *filename); +void tiff2ps_process_page(TIFF2PSContext* ctx, TIFF* tif, + double pagewidth, double pageheight, + double leftmargin, double bottommargin, + gboolean center); +void tiff2ps_context_finalize(TIFF2PSContext* ctx); diff --git a/backend/tiff/tiffdocument.evince-backend.in b/backend/tiff/tiffdocument.evince-backend.in new file mode 100644 index 00000000..daaa7fd9 --- /dev/null +++ b/backend/tiff/tiffdocument.evince-backend.in @@ -0,0 +1,4 @@ +[Evince Backend] +Module=tiffdocument +_TypeDescription=Tiff Documents +MimeType=image/tiff |