diff options
author | Karl Berry <karl@freefriends.org> | 2006-01-17 21:41:51 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2006-01-17 21:41:51 +0000 |
commit | 487ca4806cc046076293cf6cc5fbba0db282bac7 (patch) | |
tree | 847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/dvipdfm/pdfdoc.c | |
parent | a3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff) |
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/dvipdfm/pdfdoc.c')
-rw-r--r-- | Build/source/texk/dvipdfm/pdfdoc.c | 1284 |
1 files changed, 1284 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdfm/pdfdoc.c b/Build/source/texk/dvipdfm/pdfdoc.c new file mode 100644 index 00000000000..7aa20f72a02 --- /dev/null +++ b/Build/source/texk/dvipdfm/pdfdoc.c @@ -0,0 +1,1284 @@ +/* $Header$ + + This is dvipdfm, a DVI to PDF translator. + Copyright (C) 1998, 1999 by Mark A. Wicks + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + The author may be contacted via the e-mail address + + mwicks@kettering.edu +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include "system.h" +#include "config.h" +#include "mem.h" +#include "error.h" +#include "mfileio.h" +#include "numbers.h" +#include "dvi.h" +#include "pdflimits.h" +#include "pdfobj.h" +#include "pdfdev.h" +#include "pdfdoc.h" +#include "pdfspecial.h" + +#ifdef HAVE_LIBPNG +#include "thumbnail.h" +#endif + +static pdf_obj *catalog = NULL; +static pdf_obj *docinfo = NULL; +static pdf_obj *page_tree = NULL, *page_tree_ref = NULL; + +int outline_depth=0; +static struct +{ + int kid_count; + pdf_obj *entry; +} outline[MAX_OUTLINE_DEPTH]; + + +static pdf_obj *current_page_resources = NULL; +static pdf_obj *this_page_contents = NULL; +static pdf_obj *glob_page_bop, *glob_page_eop; +static pdf_obj *coord_xform_stream = NULL, *coord_xform_ref = NULL; +static pdf_obj *this_page_bop = NULL; +static pdf_obj *this_page_beads = NULL; +static pdf_obj *this_page_annots = NULL; +static pdf_obj *this_page_xobjects = NULL, *this_page_fonts = NULL; +static pdf_obj *tmp1; + +static unsigned long page_count = 0; +static struct pages { + pdf_obj *page_dict; + pdf_obj *page_ref; +} *pages = NULL; +static unsigned long max_pages = 0; + +static void start_page_tree (void); +static void create_catalog (void); +static void start_current_page_resources (void); +static void finish_page_tree(void); +static void start_name_tree(void); +static void finish_dests_tree(void); +static void finish_pending_xobjects(void); +static void start_articles(void); + +static unsigned char verbose = 0, debug=0; + +void pdf_doc_set_verbose(void) +{ + if (verbose < 255) + verbose += 1; +} + +void pdf_doc_set_debug(void) +{ + debug = 1; +} + +static void resize_pages (unsigned long newsize) +{ + unsigned long i; + if (newsize > max_pages) { + pages = RENEW (pages, newsize, struct pages); + for (i=max_pages; i<newsize; i++) { + pages[i].page_dict = NULL; + pages[i].page_ref = NULL; + } + max_pages = newsize; + } +} + +static pdf_obj *type_name, *page_name, *pages_name, *contents_name, *annots_name, + *resources_name, *bead_name, *count_name, *kids_name, *parent_name, + *mediabox_name, *limits_name, *thumb_name; + +static void make_short_cuts(void) +{ +#ifdef MEM_DEBUG +MEM_START +#endif + /* Define some shorthand for names that will conserve memory (and time) + (similar to the latex \@ne trick */ + type_name = pdf_new_name("Type"); + page_name = pdf_new_name("Page"); + pages_name = pdf_new_name("Pages"); + count_name = pdf_new_name("Count"); + kids_name = pdf_new_name("Kids"); + parent_name = pdf_new_name("Parent"); + contents_name = pdf_new_name("Contents"); + annots_name = pdf_new_name("Annots"); + resources_name = pdf_new_name ("Resources"); + bead_name = pdf_new_name ("B"); + mediabox_name = pdf_new_name ("MediaBox"); + limits_name = pdf_new_name ("Limits"); + thumb_name = pdf_new_name ("Thumb"); +#ifdef MEM_DEBUG +MEM_END +#endif +} +static void release_short_cuts(void) +{ + /* Release those shorthand name we created */ + pdf_release_obj (type_name); + pdf_release_obj (page_name); + pdf_release_obj (pages_name); + pdf_release_obj (count_name); + pdf_release_obj (kids_name); + pdf_release_obj (parent_name); + pdf_release_obj (contents_name); + pdf_release_obj (annots_name); + pdf_release_obj (resources_name); + pdf_release_obj (bead_name); + pdf_release_obj (mediabox_name); + pdf_release_obj (limits_name); + pdf_release_obj (thumb_name); +} + +static void start_page_tree (void) +{ + if (debug) { + fprintf (stderr, "(start_page_tree)"); + } + /* Create empty page tree */ + page_tree = pdf_new_dict(); + page_tree_ref = pdf_ref_obj (page_tree); + /* Both page_tree and page_tree_ref are kept open until the + document is closed. This allows the user to write to page_tree + if he so choses. */ + /* Link /Pages into the catalog + and poing it to indirect reference since we + haven't built the tree yet */ + glob_page_bop = pdf_new_stream(0); + glob_page_eop = pdf_new_stream(0); + coord_xform_stream = pdf_new_stream(0); + coord_xform_ref = pdf_ref_obj (coord_xform_stream); + return; +} + +void pdf_doc_bop (char *string, unsigned length) +{ + if (length > 0) + pdf_add_stream (glob_page_bop, string, length); +} + +void pdf_doc_this_bop (char *string, unsigned length) +{ + if (this_page_bop == NULL) + this_page_bop = pdf_new_stream (STREAM_COMPRESS); + if (length > 0) + pdf_add_stream (this_page_bop, string, length); +} + +void pdf_doc_set_origin (double x, double y) +{ + int len; + static char first = 1; + if (first) { + sprintf (work_buffer, "%g 0 0 %g %g %g cm\n", + dvi_tell_mag()*pdf_dev_scale(), dvi_tell_mag()*pdf_dev_scale(), + x, y); + len = strlen (work_buffer); + pdf_add_stream (coord_xform_stream, work_buffer, len); + first = 0; + } +} + + +void pdf_doc_eop (char *string, unsigned length) +{ + if (length > 0) + pdf_add_stream (glob_page_eop, string, length); +} + +static void start_outline_tree (void) +{ + if (debug) { + fprintf (stderr, "(start_outline_tree)"); + } + /* Create empty outline tree */ + outline[outline_depth].entry = pdf_new_dict(); + outline[outline_depth].kid_count = 0; + return; +} + +static pdf_obj *names_dict; + +static void start_name_tree (void) +{ + if (debug) { + fprintf (stderr, "(start_name_tree)"); + } + names_dict = pdf_new_dict (); +} + +static char *asn_date (void) +{ +#ifndef HAVE_TIMEZONE + #ifdef TM_GM_TOFF + #define timezone (bdtime->gm_toff) + #else + #define timezone 0l +#endif /* TM_GM_TOFF */ +#endif /* HAVE_TIMEZONE */ + static char date_string[24]; + time_t current_time; + struct tm *bd_time; + if (debug) { + fprintf (stderr, "(asn_date)"); + } + time(¤t_time); + bd_time = localtime(¤t_time); + sprintf (date_string, "D:%04d%02d%02d%02d%02d%02d%+03ld'%02ld'", + bd_time -> tm_year+1900, bd_time -> tm_mon+1, bd_time -> tm_mday, + bd_time -> tm_hour, bd_time -> tm_min, bd_time -> tm_sec, + -timezone/3600, timezone%3600); + return date_string; +} + +#define BANNER "dvipdfm %s, Copyright \251 1998, by Mark A. Wicks" +static void create_docinfo (void) +{ + /* Create an empty Info entry and make it + be the root object */ + if (debug) { + fprintf (stderr, "(create_docinfo)"); + } + docinfo = pdf_new_dict (); + pdf_set_info (docinfo); + return; +} + +static void finish_docinfo(void) +{ + char *time_string, *banner; + banner = NEW (strlen(BANNER)+20,char); + sprintf(banner, BANNER, VERSION); + pdf_add_dict (docinfo, + pdf_new_name ("Producer"), + pdf_new_string (banner, strlen (banner))); + RELEASE (banner); + time_string = asn_date(); + pdf_add_dict (docinfo, + pdf_new_name ("CreationDate"), + pdf_new_string (time_string, strlen (time_string))); + pdf_release_obj (docinfo); + return; +} + +void pdf_doc_merge_with_docinfo (pdf_obj *dictionary) +{ + pdf_merge_dict (docinfo, dictionary); +} + +void pdf_doc_merge_with_catalog (pdf_obj *dictionary) +{ + pdf_merge_dict (catalog, dictionary); +} + +static void create_catalog (void) +{ + if (debug) { + fprintf (stderr, "(create_catalog)"); + } + catalog = pdf_new_dict (); + pdf_set_root (catalog); + /* Create /Type attribute */ + pdf_add_dict (catalog, + pdf_link_obj (type_name), + pdf_new_name("Catalog")); + /* Create only those parts of the page tree required for the catalog. + That way, the rest of the page tree can be finished at any time */ + start_page_tree(); + /* Likewise for outline tree */ + start_outline_tree (); + start_name_tree(); + start_articles(); + return; +} + +static void start_current_page_resources (void) +{ + /* work on resources to put in Pages */ + if (debug) { + fprintf (stderr, "(start_current_page_resources)"); + } + current_page_resources = pdf_new_dict (); + tmp1 = pdf_new_array (); + pdf_add_array (tmp1, pdf_new_name ("PDF")); + pdf_add_array (tmp1, pdf_new_name ("Text")); + pdf_add_array (tmp1, pdf_new_name ("ImageC")); + pdf_add_dict (current_page_resources, + pdf_new_name ("ProcSet"), + tmp1); + return; +} + +void pdf_doc_add_to_page_fonts (const char *name, pdf_obj + *resource) +{ +#ifdef MEM_DEBUG + MEM_START; +#endif + + if (debug) { + fprintf (stderr, "(pdf_doc_add_to_page_fonts)"); + } + if (this_page_fonts == NULL) + this_page_fonts = pdf_new_dict(); + pdf_add_dict (this_page_fonts, + pdf_new_name (name), resource); +#ifdef MEM_DEBUG +MEM_END +#endif +} + +void pdf_doc_add_to_page_xobjects (const char *name, pdf_obj + *resource) +{ + if (debug) { + fprintf (stderr, "(pdf_doc_add_to_page_xojects)"); + } + if (this_page_xobjects == NULL) + this_page_xobjects = pdf_new_dict (); + pdf_add_dict (this_page_xobjects, + pdf_new_name (name), + resource); +} + + +void pdf_doc_add_to_page_resources (const char *name, pdf_obj *resource) +{ + if (debug) { + fprintf (stderr, "(pdf_doc_add_to_page_resources)"); + } + pdf_add_dict (current_page_resources, + pdf_new_name (name), + resource); +} + +void pdf_doc_add_to_page_annots (pdf_obj *annot) +{ + if (debug) { + fprintf (stderr, "(pdf_doc_add_to_page_annots)"); + } + if (this_page_annots == NULL) + this_page_annots = pdf_new_array (); + pdf_add_array (this_page_annots, + annot); +} + +static pdf_obj *page_subtree (struct pages *pages, unsigned long npages, + pdf_obj *parent_ref) +{ +#define PAGE_CLUSTER 4 + pdf_obj *self, *self_ref, *kid_array; + self = pdf_new_dict(); + /* This is a slight kludge which allow the subtree + dictionary generated by this routine to be merged with the + real page_tree dictionary, while keeping the indirect + object references right */ + if (parent_ref == NULL) + self_ref = pdf_ref_obj (page_tree); + else + self_ref = pdf_ref_obj (self); + pdf_add_dict (self, pdf_link_obj (type_name), + pdf_link_obj (pages_name)); + pdf_add_dict (self, pdf_link_obj (count_name), + pdf_new_number((double) npages)); + kid_array = pdf_new_array(); + pdf_add_dict (self, pdf_link_obj (kids_name), + kid_array); + if (parent_ref != NULL) { + pdf_add_dict (self, pdf_link_obj(parent_name), + parent_ref); + } + if (npages > 0 && npages <= PAGE_CLUSTER) { + int i; + for (i=0; i<npages; i++) { + pdf_add_array (kid_array, pdf_link_obj(pages[i].page_ref)); + pdf_add_dict (pages[i].page_dict, pdf_link_obj (parent_name), + pdf_link_obj(self_ref)); + pdf_release_obj (pages[i].page_dict); + pdf_release_obj (pages[i].page_ref); + pages[i].page_dict = NULL; + pages[i].page_ref = NULL; + } + } else if (npages > 0) { + int i; + for (i=0; i<PAGE_CLUSTER; i++) { + pdf_obj *subtree; + unsigned long start, end; + start = (i*npages)/PAGE_CLUSTER; + end = ((i+1)*npages)/PAGE_CLUSTER; + if (end-start>1) { + subtree = page_subtree (pages+start, end-start, pdf_link_obj(self_ref)); + pdf_add_array (kid_array, pdf_ref_obj (subtree)); + pdf_release_obj (subtree); + } + else { + pdf_add_array (kid_array, pdf_link_obj(pages[start].page_ref)); + pdf_add_dict (pages[start].page_dict, pdf_link_obj(parent_name), + pdf_link_obj (self_ref)); + pdf_release_obj (pages[start].page_dict); + pdf_release_obj (pages[start].page_ref); + pages[start].page_dict = NULL; + pages[start].page_ref = NULL; + } + } + } + pdf_release_obj (self_ref); + return self; +} + +static void finish_page_tree(void) +{ + pdf_obj *subtree; + if (debug) { + fprintf (stderr, "(finish_page_tree)"); + } + + subtree = page_subtree (pages, page_count, NULL); + pdf_merge_dict (page_tree, subtree); + pdf_release_obj (subtree); + /* Generate media box at root of page tree and let the + other pages inherit it */ + { + tmp1 = pdf_new_array (); + pdf_add_array (tmp1, pdf_new_number (0)); + pdf_add_array (tmp1, pdf_new_number (0)); + pdf_add_array (tmp1, pdf_new_number (ROUND(dev_page_width(),1.0))); + pdf_add_array (tmp1, pdf_new_number (ROUND(dev_page_height(),1.0))); + pdf_add_dict (page_tree, pdf_link_obj (mediabox_name), tmp1); + } + pdf_release_obj (page_tree); + pdf_add_dict (catalog, + pdf_link_obj (pages_name), + pdf_link_obj (page_tree_ref)); + pdf_release_obj (page_tree_ref); + RELEASE (pages); + pdf_add_stream (glob_page_bop, "\n", 1); + pdf_release_obj (glob_page_bop); + pdf_add_stream (glob_page_eop, "\n", 1); + pdf_release_obj (glob_page_eop); + pdf_release_obj (coord_xform_stream); + pdf_release_obj (coord_xform_ref); + return; +} + +void pdf_doc_change_outline_depth(int new_depth) +{ + int i; + if (debug) { + fprintf (stderr, "(change_outline_depth)"); + } + if (outline_depth >= MAX_OUTLINE_DEPTH -1) + ERROR ("Outline is too deep."); + if (new_depth == outline_depth) + /* Nothing to do */ + return; + if (new_depth > outline_depth+1) + ERROR ("Can't increase outline depth by more than one at a time\n"); + if (outline[outline_depth].entry == NULL) + ERROR ("change_outline_depth: Fix me, I'm broke. This shouldn't happen!"); + /* Terminate all entries above this depth */ + for (i=outline_depth-1; i>=new_depth; i--) { + pdf_add_dict (outline[i].entry, + pdf_new_name ("Last"), + pdf_ref_obj (outline[i+1].entry)); + if (i > 0) + tmp1 = pdf_new_number (-outline[i].kid_count); + else + tmp1 = pdf_new_number (outline[i].kid_count); + + pdf_add_dict (outline[i].entry, + pdf_link_obj (count_name), + tmp1); + } + /* Flush out all entries above this depth */ + for (i=new_depth+1; i<=outline_depth; i++) { + pdf_release_obj (outline[i].entry); + outline[i].entry = NULL; + outline[i].kid_count = 0; + } + outline_depth = new_depth; +} + +static void finish_outline(void) +{ + if (debug) + fprintf (stderr, "(finish_outline)"); + /* Link it into the catalog */ + /* Point /Outline attribute to indirect reference */ + pdf_doc_change_outline_depth (0); + pdf_add_dict (catalog, + pdf_new_name ("Outlines"), + pdf_ref_obj(outline[outline_depth].entry)); + pdf_release_obj (outline[0].entry); + outline[0].entry = NULL; +} + + +void pdf_doc_add_outline (pdf_obj *dict) +{ + pdf_obj *new_entry; + if (outline_depth < 1) + ERROR ("Can't add to outline at depth < 1"); + new_entry = pdf_new_dict (); + pdf_merge_dict (new_entry, dict); + /* Caller doesn't know we don't actually use the dictionary, + so he *gave* dict to us. We have to free it */ + pdf_release_obj (dict); + /* Tell it where its parent is */ + pdf_add_dict (new_entry, + pdf_link_obj (parent_name), + pdf_ref_obj (outline[outline_depth-1].entry)); + /* Give mom and dad the good news */ + outline[outline_depth-1].kid_count += 1; + + /* Is this the first entry at this depth? */ + if (outline[outline_depth].entry == NULL) { + /* Is so, tell the parent we are first born */ + pdf_add_dict (outline[outline_depth-1].entry, + pdf_new_name ("First"), + pdf_ref_obj (new_entry)); + } + else { + /* Point us back to sister */ + pdf_add_dict (new_entry, + pdf_new_name ("Prev"), + pdf_ref_obj (outline[outline_depth].entry)); + /* Point our elder sister toward us */ + pdf_add_dict (outline[outline_depth].entry, + pdf_new_name ("Next"), + pdf_ref_obj (new_entry)); + /* Bye-Bye sis */ + pdf_release_obj (outline[outline_depth].entry); + } + outline[outline_depth].entry = new_entry; + /* Just born, so don't have any kids */ + outline[outline_depth].kid_count = 0; +} + +struct dests +{ + char *name; + unsigned length; + pdf_obj *array; +}; +typedef struct dests dest_entry; +static dest_entry *dests = NULL; +unsigned long max_dests = 0; + +static int CDECL cmp_dest (const void *d1, const void *d2) +{ + unsigned length; + int tmp; + length = MIN (((dest_entry *) d1) -> length, ((dest_entry *) d2) -> + length); + if ((tmp = memcmp (((dest_entry *) d1) -> name, ((dest_entry *) d2) + -> name, length)) != 0) + return tmp; + if (((dest_entry *) d1) -> length == ((dest_entry *) d2) -> length) + return 0; + return (((dest_entry *) d1) -> length < ((dest_entry *) d2) -> length ? -1 : 1 ); +} + +static pdf_obj *name_subtree (dest_entry *dests, unsigned long ndests) +{ +#define CLUSTER 4 + pdf_obj *result, *name_array, *limit_array, *kid_array; + result = pdf_new_dict(); + limit_array = pdf_new_array(); + pdf_add_dict (result, pdf_link_obj(limits_name), limit_array); + pdf_add_array (limit_array, pdf_new_string(dests[0].name, + dests[0].length)); + pdf_add_array (limit_array, pdf_new_string(dests[ndests-1].name, + dests[ndests-1].length)); + if (ndests > 0 && ndests <= CLUSTER) { + int i; + name_array = pdf_new_array(); + pdf_add_dict (result, pdf_new_name ("Names"), + name_array); + for (i=0; i<ndests; i++) { + pdf_add_array (name_array, pdf_new_string (dests[i].name, + dests[i].length)); + RELEASE (dests[i].name); + pdf_add_array (name_array, dests[i].array); + } + } else if (ndests > 0) { + int i; + kid_array = pdf_new_array(); + pdf_add_dict (result, pdf_link_obj (kids_name), kid_array); + for (i=0; i<CLUSTER; i++) { + pdf_obj *subtree; + unsigned long start, end; + start = (i*ndests)/CLUSTER; + end = ((i+1)*ndests)/CLUSTER; + subtree = name_subtree (dests+start, end-start); + pdf_add_array (kid_array, pdf_ref_obj (subtree)); + pdf_release_obj (subtree); + } + } + return result; +} + +static unsigned long number_dests = 0; + +static void finish_dests_tree (void) +{ + pdf_obj *kid; + if (number_dests > 0) { + /* Sort before writing any /Dests entries */ + qsort(dests, number_dests, sizeof(dests[0]), cmp_dest); + kid = name_subtree (dests, number_dests); + /* Each entry in dests has been assigned to another object, so + we can free the entire array without freeing the entries. */ + RELEASE (dests); + pdf_add_dict (names_dict, + pdf_new_name ("Dests"), + pdf_ref_obj (kid)); + pdf_release_obj (kid); + } +} + +void pdf_doc_add_dest (char *name, unsigned length, pdf_obj *array_ref) +{ +#ifdef MEM_DEBUG +MEM_START +#endif + if (number_dests >= max_dests) { + max_dests += DESTS_ALLOC_SIZE; + dests = RENEW (dests, max_dests, dest_entry); + } + dests[number_dests].name = NEW (length, char); + memcpy (dests[number_dests].name, name, length); + dests[number_dests].length = length; + dests[number_dests].array = array_ref; + number_dests++; +#ifdef MEM_DEBUG +MEM_END +#endif + return; +} + +struct articles +{ + char *name; + pdf_obj *info; + pdf_obj *first; + pdf_obj *last; + pdf_obj *this; +}; + +typedef struct articles article_entry; +static article_entry articles[MAX_ARTICLES]; +static unsigned long number_articles = 0; + +static pdf_obj *articles_array; +static void start_articles (void) +{ + articles_array = pdf_new_array(); +} + +void pdf_doc_start_article (char *name, pdf_obj *info) +{ + if (number_articles >= MAX_ARTICLES) { + ERROR ("pdf_doc_add_article: Too many articles\n"); + } + if (name == NULL || strlen (name) == 0) + ERROR ("pdf_doc_start_article called null name"); + articles[number_articles].name = NEW (strlen(name)+1, char); + strcpy (articles[number_articles].name, name); + articles[number_articles].info = info; + articles[number_articles].first = NULL; + articles[number_articles].last = NULL; + /* Start dictionary for this article even though we can't finish it + until we get the first bead */ + articles[number_articles].this = pdf_new_dict(); + number_articles++; + return; +} + +void pdf_doc_add_bead (char *article_name, pdf_obj *partial_dict) +{ + /* partial_dict should have P (Page) and R (Rect) already filled in */ + /* See if the specified article exists */ + int i; + for (i=0; i<number_articles; i++) { + if (!strcmp (articles[i].name, article_name)) + break; + } + if (i == number_articles) { + fprintf (stderr, "Bead specified thread that doesn't exist\n"); + return; + } + /* Is this the first bead? */ + if (articles[i].last == NULL) { + articles[i].first = pdf_link_obj (partial_dict); + /* Add pointer to its first object */ + pdf_add_dict (articles[i].this, + pdf_new_name ("F"), + pdf_ref_obj (articles[i].first)); + /* Next add pointer to its Info dictionary */ + pdf_add_dict (articles[i].this, + pdf_new_name ("I"), + pdf_ref_obj (articles[i].info)); + /* Point first bead to parent article */ + pdf_add_dict (partial_dict, + pdf_new_name ("T"), + pdf_ref_obj (articles[i].this)); + /* Ship it out and forget it */ + pdf_add_array (articles_array, pdf_ref_obj (articles[i].this)); + pdf_release_obj (articles[i].this); + articles[i].this = NULL; + } else { + /* Link it in... */ + /* Point last object to this one */ + pdf_add_dict (articles[i].last, + pdf_new_name ("N"), + pdf_ref_obj (partial_dict)); + /* Point this one to last */ + pdf_add_dict (partial_dict, + pdf_new_name ("V"), + pdf_ref_obj (articles[i].last)); + pdf_release_obj (articles[i].last); + } + articles[i].last = partial_dict; + if (this_page_beads == NULL) + this_page_beads = pdf_new_array(); + pdf_add_array (this_page_beads, + pdf_ref_obj (partial_dict)); +} + +void finish_articles(void) +{ + int i; + pdf_add_dict (catalog, + pdf_new_name ("Threads"), + pdf_ref_obj (articles_array)); + pdf_release_obj (articles_array); + for (i=0; i<number_articles; i++) { + if (articles[i].last == NULL) { + fprintf (stderr, "Article started, but no beads\n"); + break; + } + /* Close the loop */ + pdf_add_dict (articles[i].last, + pdf_new_name ("N"), + pdf_ref_obj (articles[i].first)); + pdf_add_dict (articles[i].first, + pdf_new_name ("V"), + pdf_ref_obj (articles[i].last)); + pdf_release_obj (articles[i].first); + pdf_release_obj (articles[i].last); + pdf_release_obj (articles[i].info); + RELEASE (articles[i].name); + } +} + +#ifdef HAVE_LIBPNG +static char thumbnail_opt = 0; +static char *thumb_basename = NULL; + +void pdf_doc_enable_thumbnails(void) +{ + thumbnail_opt = 1; +} + +#endif + +void pdf_doc_finish_page () +{ +#ifdef MEM_DEBUG +MEM_START +#endif + if (debug) { + fprintf (stderr, "(pdf_doc_finish_page)"); + } + finish_pending_xobjects(); + /* Flush this page */ + /* Page_count is the index of the current page, starting at 1 */ + tmp1 = pdf_new_array (); + pdf_add_array (tmp1, pdf_ref_obj (glob_page_bop)); + if (this_page_bop) { + pdf_add_array (tmp1, pdf_ref_obj (this_page_bop)); + } + pdf_add_array (tmp1, pdf_link_obj (coord_xform_ref)); + pdf_add_array (tmp1, pdf_ref_obj (this_page_contents)); + pdf_add_array (tmp1, pdf_ref_obj (glob_page_eop)); + pdf_add_dict (pages[page_count].page_dict, + pdf_link_obj(contents_name), tmp1); + /* We keep .page_dict open because we don't know the parent yet */ + if (this_page_bop != NULL) { + pdf_add_stream (this_page_bop, "\n", 1); + pdf_release_obj (this_page_bop); + this_page_bop = NULL; + } + if (this_page_contents != NULL) { + pdf_add_stream (this_page_contents, "\n", 1); + pdf_release_obj (this_page_contents); + this_page_contents = NULL; + } + if (this_page_annots != NULL) { + pdf_add_dict (pages[page_count].page_dict, + pdf_link_obj(annots_name), + pdf_ref_obj (this_page_annots)); + pdf_release_obj (this_page_annots); + this_page_annots = NULL; + } + if (this_page_beads != NULL) { + pdf_add_dict (pages[page_count].page_dict, + pdf_link_obj (bead_name), + pdf_ref_obj (this_page_beads)); + pdf_release_obj (this_page_beads); + this_page_beads = NULL; + } + if (this_page_fonts != NULL) { + pdf_add_dict (current_page_resources, + pdf_new_name ("Font"), + pdf_ref_obj (this_page_fonts)); + pdf_release_obj (this_page_fonts); + this_page_fonts = NULL; + } + if (this_page_xobjects != NULL) { + pdf_add_dict (current_page_resources, + pdf_new_name ("XObject"), + pdf_ref_obj (this_page_xobjects)); + pdf_release_obj (this_page_xobjects); + this_page_xobjects = NULL; + } + if (current_page_resources != NULL) { + pdf_release_obj (current_page_resources); + current_page_resources = NULL; + } +#ifdef HAVE_LIBPNG + if (thumbnail_opt) { + char *thumb_filename; + pdf_obj *thumbnail; + thumb_filename = NEW (strlen(thumb_basename)+7, char); + sprintf (thumb_filename, "%s.%ld", thumb_basename, + page_count%99999+1L); + thumbnail = do_thumbnail (thumb_filename); + RELEASE (thumb_filename); + if (thumbnail) + pdf_add_dict (pages[page_count].page_dict, + pdf_link_obj (thumb_name), + thumbnail); + } +#endif + page_count += 1; +#ifdef MEM_DEBUG + MEM_END; +#endif +} + +pdf_obj *pdf_doc_current_page_resources (void) +{ + return current_page_resources; +} + + +static int highest_page_ref = 0; +pdf_obj *pdf_doc_ref_page (unsigned long page_no) +{ + if (debug) + fprintf (stderr, "(doc_ref_page:page_no=%ld)", page_no); + if (page_no >= max_pages) { + resize_pages (page_no+PAGES_ALLOC_SIZE); + } + /* Has this page been referenced yet? */ + if (pages[page_no-1].page_dict == NULL) { + /* If not, create it */ + pages[page_no-1].page_dict = pdf_new_dict (); + /* and reference it */ + pages[page_no-1].page_ref = pdf_ref_obj (pages[page_no-1].page_dict); + } + if (page_no > highest_page_ref) + highest_page_ref = page_no; + return pdf_link_obj (pages[page_no-1].page_ref); +} + +pdf_obj *pdf_doc_names (void) +{ + return names_dict; +} + +pdf_obj *pdf_doc_page_tree (void) +{ + return page_tree; +} + +pdf_obj *pdf_doc_catalog (void) +{ + return catalog; +} + +pdf_obj *pdf_doc_this_page (void) +{ + return pages[page_count].page_dict; +} + +pdf_obj *pdf_doc_this_page_ref (void) +{ + return pdf_doc_ref_page(page_count+1); +} + +pdf_obj *pdf_doc_prev_page_ref (void) +{ + if (page_count < 1) { + ERROR ("Reference to previous page, but no pages have been completed yet"); + } + return pdf_doc_ref_page(page_count>0?page_count:1); +} + +pdf_obj *pdf_doc_next_page_ref (void) +{ + return pdf_doc_ref_page(page_count+2); +} + +void pdf_doc_new_page (void) +{ +#ifdef MEM_DEBUG +MEM_START +#endif + if (debug) { + fprintf (stderr, "(pdf_doc_new_page)"); + fprintf (stderr, "page_count=%ld, max_pages=%ld\n", page_count, + max_pages); + } + /* See if we need more pages allocated yet */ + if (page_count >= max_pages) { + resize_pages(max_pages+PAGES_ALLOC_SIZE); + } + /* Was this page already instantiated by a forward reference to it? */ + if (pages[page_count].page_ref == NULL) { + /* If not, create it. */ + pages[page_count].page_dict = pdf_new_dict (); + /* and reference it */ + pages[page_count].page_ref = pdf_ref_obj(pages[page_count].page_dict); + } + pdf_add_dict (pages[page_count].page_dict, + pdf_link_obj(type_name), pdf_link_obj(page_name)); + /* start the contents stream for the new page */ + this_page_contents = pdf_new_stream(STREAM_COMPRESS); + start_current_page_resources(); + pdf_add_dict (pages[page_count].page_dict, + pdf_link_obj (resources_name), + pdf_ref_obj (current_page_resources)); + /* Contents are still available as this_page_contents until next + page is started */ + /* Even though the page is gone, a Reference to this page is kept + until program ends */ +#ifdef MEM_DEBUG +MEM_END +#endif +} + +void pdf_doc_add_to_page (char *buffer, unsigned length) +{ + pdf_add_stream (this_page_contents, buffer, length); +} + +void pdf_doc_init (char *filename) +{ +#ifdef MEM_DEBUG + MEM_START +#endif + if (debug) fprintf (stderr, "pdf_doc_init:\n"); + pdf_out_init (filename); +#ifdef HAVE_LIBPNG + /* Create a default name for thumbnail image files */ + if (thumbnail_opt) { + if (strlen(filename)>4 && !strncmp (".pdf", filename+strlen(filename)-4,4)) { + thumb_basename = NEW (strlen(filename)+1-4, char); + strncpy (thumb_basename, filename, strlen(filename)-4); + thumb_basename[strlen(filename)-4] = 0; + } else { + thumb_basename = NEW (strlen(filename)+1, char); + strcpy (thumb_basename, filename); + } + } +#endif /* HAVE_LIBPNG */ + make_short_cuts(); + create_docinfo (); + create_catalog (); +#ifdef MEM_DEBUG + MEM_END +#endif +} + +void pdf_doc_creator (char *s) +{ + pdf_add_dict (docinfo, pdf_new_name ("Creator"), + pdf_new_string (s, strlen(s))); +} + +void pdf_doc_close () +{ + if (debug) fprintf (stderr, "pdf_doc_finish:\n"); +#ifdef HAVE_LIBPNG + if (thumb_basename) + RELEASE (thumb_basename); +#endif /* HAVE_LIBPNG */ + /* Following things were kept around so user can add dictionary + items */ + finish_docinfo(); + finish_page_tree(); + /* Add names dict to catalog */ + finish_outline(); + finish_dests_tree(); + finish_articles(); + pdf_add_dict (catalog, + pdf_new_name ("Names"), + pdf_ref_obj (names_dict)); + pdf_release_obj (names_dict); + pdf_release_obj (catalog); + /* Do consistency check on forward references to pages */ + if (highest_page_ref > page_count) { + unsigned long i; + fprintf (stderr, "\nWarning: Nonexistent page(s) referenced\n"); + fprintf (stderr, " (PDF file may not work right)\n"); + for (i=page_count; i<highest_page_ref; i++) { + if (pages[i].page_dict) { + pdf_release_obj (pages[i].page_dict); + pdf_release_obj (pages[i].page_ref); + } + } + } + pdf_finish_specials(); + release_short_cuts(); + pdf_out_flush (); +} + +static pdf_obj *build_scale_array (double a, double b, double c, + double d, double e, double f) +{ + pdf_obj *result; + result = pdf_new_array(); + pdf_add_array (result, pdf_new_number (a)); + pdf_add_array (result, pdf_new_number (b)); + pdf_add_array (result, pdf_new_number (c)); + pdf_add_array (result, pdf_new_number (d)); + pdf_add_array (result, pdf_new_number (ROUND(e,0.01))); + pdf_add_array (result, pdf_new_number (ROUND(f,0.01))); + return result; +} + +/* All this routine does is give the form a name + and add a unity scaling matrix. It fills + in required fields. The caller must initialize + the stream */ + + +void doc_make_form_xobj (pdf_obj *this_form_contents, pdf_obj *bbox, + double refptx, double refpty, + double xscale, double yscale, + pdf_obj *resources, char *form_name) +{ + pdf_obj *xobj_dict, *tmp1; + xobj_dict = pdf_stream_dict (this_form_contents); + + pdf_add_dict (xobj_dict, pdf_new_name ("Name"), pdf_new_name(form_name)); + pdf_add_dict (xobj_dict, pdf_link_obj (type_name), + pdf_new_name ("XObject")); + pdf_add_dict (xobj_dict, pdf_new_name ("Subtype"), + pdf_new_name ("Form")); + pdf_add_dict (xobj_dict, pdf_new_name ("BBox"), bbox); + pdf_add_dict (xobj_dict, pdf_new_name ("FormType"), + pdf_new_number(1.0)); + /* The reference point of an Xobject is at the lower left corner + of the bounding box. Since we would like to have an arbitrary + reference point, we use a transformation matrix, translating + the reference point to (0,0) */ + tmp1 = build_scale_array (xscale, 0, 0, yscale, -xscale*refptx, -yscale*refpty); + pdf_add_dict (xobj_dict, pdf_new_name ("Matrix"), tmp1); + pdf_add_dict (xobj_dict, pdf_link_obj (resources_name), resources); + return; +} + +struct resource_stack +{ + pdf_obj *save_page_contents, *save_page_fonts; + pdf_obj *save_page_xobjects, *save_page_resources; + int xform_depth; +} res_stack[4]; + +static int xobjects_pending = 0; + +/* begin_form_xobj creates an xobject with its "origin" at + xpos and ypos that is clipped to the specified bbox. Note + that the origin is not the lower left corner of the bbox */ +pdf_obj *begin_form_xobj (double xpos, double ypos, + double bbllx, double bblly, + double bburx, double bbury, char *res_name) +{ + pdf_obj *bbox; + if (xobjects_pending >= sizeof(res_stack)/sizeof(res_stack[0])) { + fprintf (stderr, "\nForm XObjects nested too deeply. Limit is %d\n", + sizeof(res_stack)/sizeof(res_stack[0])); + return NULL; + } + /* This is a real hack. We basically treat each xobj as a separate mini + page unto itself. Save all the page structures and reinitialize + them when we finish this xobject. */ + res_stack[xobjects_pending].save_page_resources = current_page_resources; + current_page_resources = NULL; + res_stack[xobjects_pending].save_page_xobjects = this_page_xobjects; + this_page_xobjects = NULL; + res_stack[xobjects_pending].save_page_fonts = this_page_fonts; + this_page_fonts = NULL; + res_stack[xobjects_pending].save_page_contents = this_page_contents; + this_page_contents = NULL; + res_stack[xobjects_pending].xform_depth = dev_xform_depth(); + xobjects_pending += 1; + start_current_page_resources(); /* Starts current_page_resources */ + this_page_contents = pdf_new_stream (STREAM_COMPRESS); + /* Make a bounding box for this Xobject */ + /* Translate coordinate system so reference point of object + is at 0 */ + bbox = pdf_new_array (); + pdf_add_array (bbox, pdf_new_number (ROUND(bbllx,0.01))); + pdf_add_array (bbox, pdf_new_number (ROUND(bblly,0.01))); + pdf_add_array (bbox, pdf_new_number (ROUND(bburx,0.01))); + pdf_add_array (bbox, pdf_new_number (ROUND(bbury,0.01))); + /* Resource is already made, so call doc_make_form_xobj() */ + doc_make_form_xobj (this_page_contents, bbox, + xpos, ypos, 1.0, 1.0, + pdf_ref_obj(current_page_resources), res_name); + /* Make sure the object is self-contained by adding the + current font to the object stream */ + dev_reselect_font(); + /* Likewise for color */ + dev_do_color(); + return pdf_link_obj (this_page_contents); +} + +void end_form_xobj (void) +{ + if (xobjects_pending>0) { + xobjects_pending -= 1; + dev_close_all_xforms(res_stack[xobjects_pending].xform_depth); + if (this_page_xobjects) { + pdf_add_dict (current_page_resources, pdf_new_name ("XObject"), + pdf_ref_obj (this_page_xobjects)); + pdf_release_obj (this_page_xobjects); + } + if (this_page_fonts) { + pdf_add_dict (current_page_resources, pdf_new_name ("Font"), + pdf_ref_obj (this_page_fonts)); + pdf_release_obj (this_page_fonts); + } + if (current_page_resources) + pdf_release_obj (current_page_resources); + if (this_page_contents) + pdf_release_obj (this_page_contents); + current_page_resources = res_stack[xobjects_pending].save_page_resources; + this_page_xobjects = res_stack[xobjects_pending].save_page_xobjects; + this_page_fonts = res_stack[xobjects_pending].save_page_fonts; + this_page_contents = res_stack[xobjects_pending].save_page_contents; + /* Must reselect the font again in case there was a font change in + the object */ + dev_reselect_font(); + /* Must reselect color too */ + dev_do_color(); + } else{ + fprintf (stderr, "\nSpecial: exobj: Tried to close a nonexistent xobject\n"); + } + return; +} + +void finish_pending_xobjects (void) +{ + if (xobjects_pending) { + fprintf (stderr, "\nFinishing a pending form XObject at end of page\n"); + while (xobjects_pending--) { + end_form_xobj(); + } + } + return; +} + +static struct +{ + pdf_obj *annot_dict; + unsigned char dirty; + double llx, lly, urx, ury; +} breaking_state = {NULL, 0}; + +void pdf_doc_set_box (void) +{ + breaking_state.llx = dev_page_width(); + breaking_state.urx = 0; + breaking_state.lly = dev_page_height(); + breaking_state.ury = 0; + breaking_state.dirty = 0; + return; +} + +void pdf_doc_begin_annot (pdf_obj *dict) +{ + breaking_state.annot_dict = dict; + pdf_doc_set_box (); + dev_tag_depth (); + return; +} + +void pdf_doc_end_annot (void) +{ + pdf_doc_flush_annot(); + breaking_state.annot_dict = NULL; + dev_untag_depth (); + return; +} + +void pdf_doc_flush_annot (void) +{ + pdf_obj *rectangle, *new_dict; + double grow; + grow = pdf_special_tell_grow (); + if (breaking_state.dirty) { + rectangle = pdf_new_array (); + pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.llx-grow, 0.01))); + pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.lly-grow, 0.01))); + pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.urx+grow, 0.01))); + pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.ury+grow, 0.01))); + new_dict = pdf_new_dict (); + pdf_add_dict (new_dict, pdf_new_name ("Rect"), + rectangle); + pdf_merge_dict (new_dict, breaking_state.annot_dict); + pdf_doc_add_to_page_annots (pdf_ref_obj (new_dict)); + pdf_release_obj (new_dict); + } + pdf_doc_set_box(); + return; +} + +void pdf_doc_expand_box (double llx, double lly, double urx, double + ury) +{ + breaking_state.llx = MIN (breaking_state.llx, llx); + breaking_state.lly = MIN (breaking_state.lly, lly); + breaking_state.urx = MAX (breaking_state.urx, urx); + breaking_state.ury = MAX (breaking_state.ury, ury); + breaking_state.dirty = 1; + return; +} |