Back to index

tetex-bin  3.0
pdfdoc.c
Go to the documentation of this file.
00001 /*  $Header$
00002  
00003     This is dvipdfm, a DVI to PDF translator.
00004     Copyright (C) 1998, 1999 by Mark A. Wicks
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019     
00020     The author may be contacted via the e-mail address
00021 
00022        mwicks@kettering.edu
00023 */
00024 
00025 
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <time.h>
00029 #include "system.h"
00030 #include "config.h"
00031 #include "mem.h"
00032 #include "error.h"
00033 #include "mfileio.h"
00034 #include "numbers.h"
00035 #include "dvi.h"
00036 #include "pdflimits.h"
00037 #include "pdfobj.h"
00038 #include "pdfdev.h"
00039 #include "pdfdoc.h"
00040 #include "pdfspecial.h"
00041 
00042 #ifdef HAVE_LIBPNG
00043 #include "thumbnail.h"
00044 #endif
00045 
00046 static pdf_obj *catalog = NULL;
00047 static pdf_obj *docinfo = NULL;
00048 static pdf_obj *page_tree = NULL, *page_tree_ref = NULL;
00049 
00050 int outline_depth=0;
00051 static struct 
00052 {
00053   int kid_count;
00054   pdf_obj *entry;
00055 } outline[MAX_OUTLINE_DEPTH];
00056 
00057 
00058 static pdf_obj *current_page_resources = NULL;
00059 static pdf_obj *this_page_contents = NULL;
00060 static pdf_obj *glob_page_bop, *glob_page_eop;
00061 static pdf_obj *coord_xform_stream = NULL, *coord_xform_ref = NULL;
00062 static pdf_obj *this_page_bop = NULL;
00063 static pdf_obj *this_page_beads = NULL;
00064 static pdf_obj *this_page_annots = NULL;
00065 static pdf_obj *this_page_xobjects = NULL, *this_page_fonts = NULL;
00066 static pdf_obj *tmp1;
00067 
00068 static unsigned long page_count = 0;
00069 static struct pages {
00070   pdf_obj *page_dict;
00071   pdf_obj *page_ref;
00072 } *pages = NULL;
00073 static unsigned long max_pages = 0;
00074 
00075 static void start_page_tree (void);
00076 static void create_catalog (void);
00077 static void start_current_page_resources (void);
00078 static void finish_page_tree(void);
00079 static void start_name_tree(void);
00080 static void finish_dests_tree(void);
00081 static void finish_pending_xobjects(void);
00082 static void start_articles(void);
00083 
00084 static unsigned char verbose = 0, debug=0;
00085 
00086 void pdf_doc_set_verbose(void)
00087 {
00088   if (verbose < 255) 
00089     verbose += 1;
00090 }
00091 
00092 void pdf_doc_set_debug(void)
00093 {
00094   debug = 1;
00095 }
00096 
00097 static void resize_pages (unsigned long newsize)
00098 {
00099   unsigned long i;
00100   if (newsize > max_pages) {
00101     pages = RENEW (pages, newsize, struct pages);
00102     for (i=max_pages; i<newsize; i++) {
00103       pages[i].page_dict = NULL;
00104       pages[i].page_ref = NULL;
00105     }
00106     max_pages = newsize;
00107   }
00108 }
00109 
00110 static pdf_obj *type_name, *page_name, *pages_name, *contents_name, *annots_name,
00111   *resources_name, *bead_name, *count_name, *kids_name, *parent_name,
00112   *mediabox_name, *limits_name, *thumb_name;
00113 
00114 static void make_short_cuts(void) 
00115 {
00116 #ifdef MEM_DEBUG
00117 MEM_START
00118 #endif
00119   /* Define some shorthand for names that will conserve memory (and time)
00120      (similar to the latex \@ne trick */
00121   type_name = pdf_new_name("Type");
00122   page_name = pdf_new_name("Page");
00123   pages_name = pdf_new_name("Pages");
00124   count_name = pdf_new_name("Count");
00125   kids_name = pdf_new_name("Kids");
00126   parent_name = pdf_new_name("Parent");
00127   contents_name = pdf_new_name("Contents");
00128   annots_name = pdf_new_name("Annots");
00129   resources_name = pdf_new_name ("Resources");
00130   bead_name = pdf_new_name ("B");
00131   mediabox_name = pdf_new_name ("MediaBox");
00132   limits_name = pdf_new_name ("Limits");
00133   thumb_name = pdf_new_name ("Thumb");
00134 #ifdef MEM_DEBUG
00135 MEM_END
00136 #endif
00137 }
00138 static void release_short_cuts(void)
00139 {
00140   /* Release those shorthand name we created */
00141   pdf_release_obj (type_name);
00142   pdf_release_obj (page_name);
00143   pdf_release_obj (pages_name);
00144   pdf_release_obj (count_name);
00145   pdf_release_obj (kids_name);
00146   pdf_release_obj (parent_name);
00147   pdf_release_obj (contents_name);
00148   pdf_release_obj (annots_name);
00149   pdf_release_obj (resources_name);
00150   pdf_release_obj (bead_name);
00151   pdf_release_obj (mediabox_name);
00152   pdf_release_obj (limits_name);
00153   pdf_release_obj (thumb_name);
00154 }
00155 
00156 static void start_page_tree (void)
00157 {
00158   if (debug) {
00159     fprintf (stderr, "(start_page_tree)");
00160   }
00161   /* Create empty page tree */
00162   page_tree = pdf_new_dict();
00163   page_tree_ref = pdf_ref_obj (page_tree);
00164   /* Both page_tree and page_tree_ref are kept open until the
00165      document is closed.  This allows the user to write to page_tree
00166      if he so choses. */
00167   /* Link /Pages into the catalog
00168      and poing it to indirect reference since we
00169      haven't built the tree yet */
00170   glob_page_bop = pdf_new_stream(0);
00171   glob_page_eop = pdf_new_stream(0);
00172   coord_xform_stream = pdf_new_stream(0);
00173   coord_xform_ref = pdf_ref_obj (coord_xform_stream);
00174   return;
00175 }
00176 
00177 void pdf_doc_bop (char *string, unsigned length)
00178 {
00179   if (length > 0)
00180     pdf_add_stream (glob_page_bop, string, length);
00181 }
00182 
00183 void pdf_doc_this_bop (char *string, unsigned length)
00184 {
00185   if (this_page_bop == NULL)
00186     this_page_bop = pdf_new_stream (STREAM_COMPRESS);
00187   if (length > 0)
00188     pdf_add_stream (this_page_bop, string, length);
00189 }
00190 
00191 void pdf_doc_set_origin (double x, double y)
00192 {
00193   int len;
00194   static char first = 1;
00195   if (first) {
00196     sprintf (work_buffer, "%g 0 0 %g %g %g cm\n",
00197             dvi_tell_mag()*pdf_dev_scale(), dvi_tell_mag()*pdf_dev_scale(),
00198             x, y);
00199     len = strlen (work_buffer);
00200     pdf_add_stream (coord_xform_stream, work_buffer, len);
00201     first = 0;
00202   }
00203 }
00204 
00205 
00206 void pdf_doc_eop (char *string, unsigned length)
00207 {
00208   if (length > 0)
00209     pdf_add_stream (glob_page_eop, string, length);
00210 }
00211 
00212 static void start_outline_tree (void)
00213 {
00214   if (debug) {
00215     fprintf (stderr, "(start_outline_tree)");
00216   }
00217   /* Create empty outline tree */
00218   outline[outline_depth].entry = pdf_new_dict();
00219   outline[outline_depth].kid_count = 0;
00220   return;
00221 }
00222 
00223 static pdf_obj *names_dict;
00224 
00225 static void start_name_tree (void)
00226 {
00227   if (debug) {
00228     fprintf (stderr, "(start_name_tree)");
00229   }
00230   names_dict = pdf_new_dict ();
00231 }
00232 
00233 static char *asn_date (void)
00234 {
00235 #ifndef HAVE_TIMEZONE
00236   #ifdef TM_GM_TOFF
00237      #define timezone (bdtime->gm_toff)
00238   #else
00239      #define timezone 0l
00240 #endif /* TM_GM_TOFF */
00241 #endif /* HAVE_TIMEZONE */
00242   static char date_string[24];
00243   time_t current_time;
00244   struct tm *bd_time;
00245   if (debug) {
00246     fprintf (stderr, "(asn_date)");
00247   }
00248   time(&current_time);
00249   bd_time = localtime(&current_time);
00250   sprintf (date_string, "D:%04d%02d%02d%02d%02d%02d%+03ld'%02ld'",
00251           bd_time -> tm_year+1900, bd_time -> tm_mon+1, bd_time -> tm_mday,
00252           bd_time -> tm_hour, bd_time -> tm_min, bd_time -> tm_sec,
00253           -timezone/3600, timezone%3600);
00254   return date_string;
00255 }
00256 
00257 #define BANNER "dvipdfm %s, Copyright \251 1998, by Mark A. Wicks"
00258 static void create_docinfo (void)
00259 {
00260   /* Create an empty Info entry and make it
00261      be the root object */
00262   if (debug) {
00263     fprintf (stderr, "(create_docinfo)");
00264   }
00265   docinfo = pdf_new_dict ();
00266   pdf_set_info (docinfo);
00267   return;
00268 }
00269 
00270 static void finish_docinfo(void)
00271 {
00272   char *time_string, *banner;
00273   banner = NEW (strlen(BANNER)+20,char);
00274   sprintf(banner, BANNER, VERSION);
00275   pdf_add_dict (docinfo, 
00276               pdf_new_name ("Producer"),
00277               pdf_new_string (banner, strlen (banner)));
00278   RELEASE (banner);
00279   time_string = asn_date();
00280   pdf_add_dict (docinfo, 
00281               pdf_new_name ("CreationDate"),
00282               pdf_new_string (time_string, strlen (time_string)));
00283   pdf_release_obj (docinfo);
00284   return;
00285 }
00286 
00287 void pdf_doc_merge_with_docinfo (pdf_obj *dictionary)
00288 {
00289   pdf_merge_dict (docinfo, dictionary);
00290 }
00291 
00292 void pdf_doc_merge_with_catalog (pdf_obj *dictionary)
00293 {
00294   pdf_merge_dict (catalog, dictionary);
00295 }
00296 
00297 static void create_catalog (void)
00298 {
00299   if (debug) {
00300     fprintf (stderr, "(create_catalog)");
00301   }
00302   catalog = pdf_new_dict ();
00303   pdf_set_root (catalog);
00304   /* Create /Type attribute */
00305   pdf_add_dict (catalog,
00306               pdf_link_obj (type_name),
00307               pdf_new_name("Catalog"));
00308  /* Create only those parts of the page tree required for the catalog.
00309     That way, the rest of the page tree can be finished at any time */
00310   start_page_tree(); 
00311   /* Likewise for outline tree */
00312   start_outline_tree ();
00313   start_name_tree();
00314   start_articles();
00315   return;
00316 }
00317 
00318 static void start_current_page_resources (void)
00319 {
00320   /* work on resources to put in Pages */
00321   if (debug) {
00322     fprintf (stderr, "(start_current_page_resources)");
00323   }
00324   current_page_resources = pdf_new_dict ();
00325   tmp1 = pdf_new_array ();
00326   pdf_add_array (tmp1, pdf_new_name ("PDF"));
00327   pdf_add_array (tmp1, pdf_new_name ("Text"));
00328   pdf_add_array (tmp1, pdf_new_name ("ImageC"));
00329   pdf_add_dict (current_page_resources,
00330               pdf_new_name ("ProcSet"),
00331               tmp1);
00332   return;
00333 }
00334 
00335 void pdf_doc_add_to_page_fonts (const char *name, pdf_obj
00336                                *resource)
00337 {
00338 #ifdef MEM_DEBUG
00339   MEM_START;
00340 #endif
00341 
00342   if (debug) {
00343     fprintf (stderr, "(pdf_doc_add_to_page_fonts)");
00344   }
00345   if (this_page_fonts == NULL)
00346     this_page_fonts = pdf_new_dict();
00347   pdf_add_dict (this_page_fonts,
00348               pdf_new_name (name), resource);
00349 #ifdef MEM_DEBUG
00350 MEM_END
00351 #endif
00352 }
00353 
00354 void pdf_doc_add_to_page_xobjects (const char *name, pdf_obj
00355                                *resource)
00356 {
00357   if (debug) {
00358     fprintf (stderr, "(pdf_doc_add_to_page_xojects)");
00359   }
00360   if (this_page_xobjects == NULL)
00361     this_page_xobjects = pdf_new_dict ();
00362   pdf_add_dict (this_page_xobjects,
00363               pdf_new_name (name), 
00364               resource);
00365 }
00366 
00367 
00368 void pdf_doc_add_to_page_resources (const char *name, pdf_obj *resource)
00369 {
00370   if (debug) {
00371     fprintf (stderr, "(pdf_doc_add_to_page_resources)");
00372   }
00373   pdf_add_dict (current_page_resources,
00374               pdf_new_name (name), 
00375               resource);
00376 }
00377 
00378 void pdf_doc_add_to_page_annots (pdf_obj *annot)
00379 {
00380   if (debug) {
00381     fprintf (stderr, "(pdf_doc_add_to_page_annots)");
00382   }
00383   if (this_page_annots == NULL)
00384     this_page_annots = pdf_new_array ();
00385   pdf_add_array (this_page_annots,
00386                annot);
00387 }
00388 
00389 static pdf_obj *page_subtree (struct pages *pages, unsigned long npages,
00390                      pdf_obj *parent_ref)
00391 {
00392 #define PAGE_CLUSTER 4
00393   pdf_obj *self, *self_ref, *kid_array;
00394   self = pdf_new_dict();
00395   /* This is a slight kludge which allow the subtree
00396      dictionary generated by this routine to be merged with the
00397      real page_tree dictionary, while keeping the indirect 
00398      object references right */
00399   if (parent_ref == NULL)
00400     self_ref = pdf_ref_obj (page_tree);
00401   else
00402     self_ref = pdf_ref_obj (self);
00403   pdf_add_dict (self, pdf_link_obj (type_name),
00404               pdf_link_obj (pages_name));
00405   pdf_add_dict (self, pdf_link_obj (count_name),
00406               pdf_new_number((double) npages));
00407   kid_array = pdf_new_array();
00408   pdf_add_dict (self, pdf_link_obj (kids_name), 
00409               kid_array);
00410   if (parent_ref != NULL) {
00411     pdf_add_dict (self, pdf_link_obj(parent_name),
00412                 parent_ref);
00413   }
00414   if (npages > 0 && npages <= PAGE_CLUSTER) {
00415     int i;
00416     for (i=0; i<npages; i++) {
00417       pdf_add_array (kid_array, pdf_link_obj(pages[i].page_ref));
00418       pdf_add_dict (pages[i].page_dict, pdf_link_obj (parent_name),
00419                   pdf_link_obj(self_ref));
00420       pdf_release_obj (pages[i].page_dict);
00421       pdf_release_obj (pages[i].page_ref);
00422       pages[i].page_dict = NULL;
00423       pages[i].page_ref = NULL;
00424     }
00425   } else if (npages > 0) {
00426     int i;
00427     for (i=0; i<PAGE_CLUSTER; i++) {
00428       pdf_obj *subtree;
00429       unsigned long start, end;
00430       start = (i*npages)/PAGE_CLUSTER;
00431       end = ((i+1)*npages)/PAGE_CLUSTER;
00432       if (end-start>1) {
00433        subtree = page_subtree (pages+start, end-start, pdf_link_obj(self_ref));
00434        pdf_add_array (kid_array, pdf_ref_obj (subtree));
00435        pdf_release_obj (subtree);
00436       }
00437       else {
00438        pdf_add_array (kid_array, pdf_link_obj(pages[start].page_ref));
00439        pdf_add_dict (pages[start].page_dict, pdf_link_obj(parent_name),
00440                     pdf_link_obj (self_ref));
00441        pdf_release_obj (pages[start].page_dict);
00442        pdf_release_obj (pages[start].page_ref);
00443        pages[start].page_dict = NULL;
00444        pages[start].page_ref = NULL;
00445       }
00446     }
00447   }
00448   pdf_release_obj (self_ref);
00449   return self;
00450 }
00451 
00452 static void finish_page_tree(void)
00453 {
00454   pdf_obj *subtree;
00455   if (debug) {
00456     fprintf (stderr, "(finish_page_tree)");
00457   }
00458   
00459   subtree = page_subtree (pages, page_count, NULL);
00460   pdf_merge_dict (page_tree, subtree);
00461   pdf_release_obj (subtree);
00462   /* Generate media box at root of page tree and let the
00463      other pages inherit it */
00464   {
00465     tmp1 = pdf_new_array ();
00466     pdf_add_array (tmp1, pdf_new_number (0));
00467     pdf_add_array (tmp1, pdf_new_number (0));
00468     pdf_add_array (tmp1, pdf_new_number (ROUND(dev_page_width(),1.0)));
00469     pdf_add_array (tmp1, pdf_new_number (ROUND(dev_page_height(),1.0)));
00470     pdf_add_dict (page_tree, pdf_link_obj (mediabox_name), tmp1);
00471   }
00472   pdf_release_obj (page_tree);
00473   pdf_add_dict (catalog,
00474               pdf_link_obj (pages_name),
00475               pdf_link_obj (page_tree_ref));
00476   pdf_release_obj (page_tree_ref);
00477   RELEASE (pages);
00478   pdf_add_stream (glob_page_bop, "\n", 1);
00479   pdf_release_obj (glob_page_bop);
00480   pdf_add_stream (glob_page_eop, "\n", 1);
00481   pdf_release_obj (glob_page_eop);
00482   pdf_release_obj (coord_xform_stream);
00483   pdf_release_obj (coord_xform_ref);
00484   return;
00485 }
00486 
00487 void pdf_doc_change_outline_depth(int new_depth)
00488 {
00489   int i;
00490   if (debug) {
00491     fprintf (stderr, "(change_outline_depth)");
00492   }
00493   if (outline_depth >= MAX_OUTLINE_DEPTH -1)
00494     ERROR ("Outline is too deep.");
00495   if (new_depth == outline_depth)
00496     /* Nothing to do */
00497     return;
00498   if (new_depth > outline_depth+1)
00499     ERROR ("Can't increase outline depth by more than one at a time\n");
00500   if (outline[outline_depth].entry == NULL)
00501     ERROR ("change_outline_depth: Fix me, I'm broke. This shouldn't happen!");
00502   /* Terminate all entries above this depth */
00503   for (i=outline_depth-1; i>=new_depth; i--) {
00504     pdf_add_dict (outline[i].entry,
00505                 pdf_new_name ("Last"),
00506                 pdf_ref_obj (outline[i+1].entry));
00507     if (i > 0) 
00508       tmp1 = pdf_new_number (-outline[i].kid_count);
00509     else
00510       tmp1 = pdf_new_number (outline[i].kid_count);
00511 
00512     pdf_add_dict (outline[i].entry,
00513                 pdf_link_obj (count_name),
00514                 tmp1);
00515   }
00516   /* Flush out all entries above this depth */
00517   for (i=new_depth+1; i<=outline_depth; i++) {
00518     pdf_release_obj (outline[i].entry);
00519     outline[i].entry = NULL;
00520     outline[i].kid_count = 0;
00521   }
00522   outline_depth = new_depth;
00523 }
00524 
00525 static void finish_outline(void)
00526 {
00527   if (debug)
00528     fprintf (stderr, "(finish_outline)");
00529   /* Link it into the catalog */
00530   /* Point /Outline attribute to indirect reference */
00531   pdf_doc_change_outline_depth (0);
00532   pdf_add_dict (catalog,
00533               pdf_new_name ("Outlines"),
00534               pdf_ref_obj(outline[outline_depth].entry));
00535   pdf_release_obj (outline[0].entry);
00536   outline[0].entry = NULL;
00537 }
00538 
00539 
00540 void pdf_doc_add_outline (pdf_obj *dict)
00541 {
00542   pdf_obj *new_entry;
00543   if (outline_depth < 1)
00544     ERROR ("Can't add to outline at depth < 1");
00545   new_entry = pdf_new_dict ();
00546   pdf_merge_dict (new_entry, dict);
00547   /* Caller doesn't know we don't actually use the dictionary,
00548      so he *gave* dict to us.  We have to free it */
00549   pdf_release_obj (dict);
00550   /* Tell it where its parent is */
00551   pdf_add_dict (new_entry,
00552               pdf_link_obj (parent_name),
00553               pdf_ref_obj (outline[outline_depth-1].entry));
00554   /* Give mom and dad the good news */
00555   outline[outline_depth-1].kid_count += 1;
00556 
00557   /* Is this the first entry at this depth? */
00558   if (outline[outline_depth].entry == NULL) {
00559     /* Is so, tell the parent we are first born */
00560     pdf_add_dict (outline[outline_depth-1].entry,
00561                 pdf_new_name ("First"),
00562                 pdf_ref_obj (new_entry));
00563   }
00564   else {
00565     /* Point us back to sister */
00566     pdf_add_dict (new_entry,
00567                 pdf_new_name ("Prev"),
00568                 pdf_ref_obj (outline[outline_depth].entry));
00569     /* Point our elder sister toward us */
00570     pdf_add_dict (outline[outline_depth].entry,
00571                 pdf_new_name ("Next"),
00572                 pdf_ref_obj (new_entry));
00573     /* Bye-Bye sis */
00574     pdf_release_obj (outline[outline_depth].entry);
00575   }
00576   outline[outline_depth].entry = new_entry;
00577   /* Just born, so don't have any kids */
00578   outline[outline_depth].kid_count = 0;
00579 }
00580 
00581 struct dests 
00582 {
00583   char *name;
00584   unsigned length;
00585   pdf_obj *array;
00586 };
00587 typedef struct dests dest_entry;
00588 static dest_entry *dests = NULL;
00589 unsigned long max_dests = 0;
00590 
00591 static int CDECL cmp_dest (const void *d1, const void *d2)
00592 {
00593   unsigned length;
00594   int tmp;
00595   length = MIN (((dest_entry *) d1) -> length, ((dest_entry *) d2) ->
00596               length);
00597   if ((tmp = memcmp (((dest_entry *) d1) -> name, ((dest_entry *) d2)
00598                     -> name, length)) != 0)
00599     return tmp;
00600   if (((dest_entry *) d1) -> length == ((dest_entry *) d2) -> length)
00601     return 0;
00602   return (((dest_entry *) d1) -> length < ((dest_entry *) d2) -> length ? -1 : 1 );
00603 }
00604 
00605 static pdf_obj *name_subtree (dest_entry *dests, unsigned long ndests)
00606 {
00607 #define CLUSTER 4
00608   pdf_obj *result, *name_array, *limit_array, *kid_array;
00609   result = pdf_new_dict();
00610   limit_array = pdf_new_array();
00611   pdf_add_dict (result, pdf_link_obj(limits_name), limit_array);
00612   pdf_add_array (limit_array, pdf_new_string(dests[0].name,
00613                                         dests[0].length)); 
00614   pdf_add_array (limit_array, pdf_new_string(dests[ndests-1].name,
00615                                         dests[ndests-1].length));
00616   if (ndests > 0 && ndests <= CLUSTER) {
00617     int i;
00618     name_array = pdf_new_array();
00619     pdf_add_dict (result, pdf_new_name ("Names"),
00620                 name_array);
00621     for (i=0; i<ndests; i++) {
00622       pdf_add_array (name_array, pdf_new_string (dests[i].name,
00623                                            dests[i].length));
00624       RELEASE (dests[i].name);
00625       pdf_add_array (name_array, dests[i].array);
00626     }
00627   } else if (ndests > 0) {
00628     int i;
00629     kid_array = pdf_new_array();
00630     pdf_add_dict (result, pdf_link_obj (kids_name), kid_array);
00631     for (i=0; i<CLUSTER; i++) {
00632       pdf_obj *subtree;
00633       unsigned long start, end;
00634       start = (i*ndests)/CLUSTER;
00635       end = ((i+1)*ndests)/CLUSTER;
00636       subtree = name_subtree (dests+start, end-start);
00637       pdf_add_array (kid_array, pdf_ref_obj (subtree));
00638       pdf_release_obj (subtree);
00639     }
00640   }
00641   return result;
00642 }
00643 
00644 static unsigned long number_dests = 0;
00645 
00646 static void finish_dests_tree (void)
00647 {
00648   pdf_obj *kid;
00649   if (number_dests > 0) {
00650     /* Sort before writing any /Dests entries */
00651     qsort(dests, number_dests, sizeof(dests[0]), cmp_dest);
00652     kid = name_subtree (dests, number_dests);
00653     /* Each entry in dests has been assigned to another object, so
00654        we can free the entire array without freeing the entries. */
00655     RELEASE (dests);
00656     pdf_add_dict (names_dict,
00657                 pdf_new_name ("Dests"),
00658                 pdf_ref_obj (kid));
00659     pdf_release_obj (kid);
00660   }
00661 }
00662 
00663 void pdf_doc_add_dest (char *name, unsigned length, pdf_obj *array_ref)
00664 {
00665 #ifdef MEM_DEBUG
00666 MEM_START
00667 #endif
00668   if (number_dests >= max_dests) {
00669     max_dests += DESTS_ALLOC_SIZE;
00670     dests = RENEW (dests, max_dests, dest_entry);
00671   }
00672   dests[number_dests].name = NEW (length, char);
00673   memcpy (dests[number_dests].name, name, length);
00674   dests[number_dests].length = length;
00675   dests[number_dests].array = array_ref;
00676   number_dests++;
00677 #ifdef MEM_DEBUG
00678 MEM_END
00679 #endif
00680   return;
00681 }
00682 
00683 struct articles
00684 {
00685   char *name;
00686   pdf_obj *info;
00687   pdf_obj *first;
00688   pdf_obj *last;
00689   pdf_obj *this;
00690 };
00691 
00692 typedef struct articles article_entry;
00693 static article_entry articles[MAX_ARTICLES];
00694 static unsigned long number_articles = 0;
00695 
00696 static pdf_obj *articles_array;
00697 static void start_articles (void)
00698 {
00699   articles_array = pdf_new_array();
00700 }
00701 
00702 void pdf_doc_start_article (char *name, pdf_obj *info)
00703 {
00704   if (number_articles >= MAX_ARTICLES) {
00705     ERROR ("pdf_doc_add_article:  Too many articles\n");
00706   }
00707   if (name == NULL || strlen (name) == 0)
00708     ERROR ("pdf_doc_start_article called null name");
00709   articles[number_articles].name = NEW (strlen(name)+1, char);
00710   strcpy (articles[number_articles].name, name);
00711   articles[number_articles].info = info;
00712   articles[number_articles].first = NULL;
00713   articles[number_articles].last = NULL;
00714   /* Start dictionary for this article even though we can't finish it
00715      until we get the first bead */
00716   articles[number_articles].this = pdf_new_dict();
00717   number_articles++;
00718   return;
00719 }
00720 
00721 void pdf_doc_add_bead (char *article_name, pdf_obj *partial_dict)
00722 {
00723   /* partial_dict should have P (Page) and R (Rect) already filled in */
00724   /* See if the specified article exists */
00725   int i;
00726   for (i=0; i<number_articles; i++) {
00727     if (!strcmp (articles[i].name, article_name))
00728       break;
00729   }
00730   if (i == number_articles) {
00731     fprintf (stderr, "Bead specified thread that doesn't exist\n");
00732     return;
00733   }
00734   /* Is this the first bead? */
00735   if (articles[i].last == NULL) {
00736     articles[i].first = pdf_link_obj (partial_dict);
00737     /* Add pointer to its first object */ 
00738     pdf_add_dict (articles[i].this,
00739                 pdf_new_name ("F"),
00740                 pdf_ref_obj (articles[i].first));
00741     /* Next add pointer to its Info dictionary */
00742     pdf_add_dict (articles[i].this,
00743                 pdf_new_name ("I"),
00744                 pdf_ref_obj (articles[i].info));
00745     /* Point first bead to parent article */
00746     pdf_add_dict (partial_dict,
00747                 pdf_new_name ("T"),
00748                 pdf_ref_obj (articles[i].this));
00749     /* Ship it out and forget it */
00750     pdf_add_array (articles_array, pdf_ref_obj (articles[i].this));
00751     pdf_release_obj (articles[i].this);
00752     articles[i].this = NULL;
00753   } else {
00754     /* Link it in... */
00755     /* Point last object to this one */
00756     pdf_add_dict (articles[i].last,
00757                 pdf_new_name ("N"),
00758                 pdf_ref_obj (partial_dict));
00759     /* Point this one to last */
00760     pdf_add_dict (partial_dict,
00761                 pdf_new_name ("V"),
00762                 pdf_ref_obj (articles[i].last));
00763     pdf_release_obj (articles[i].last);
00764   }
00765   articles[i].last = partial_dict;
00766   if (this_page_beads == NULL)
00767     this_page_beads = pdf_new_array();
00768   pdf_add_array (this_page_beads,
00769                pdf_ref_obj (partial_dict));
00770 }
00771 
00772 void finish_articles(void)
00773 {
00774   int i;
00775   pdf_add_dict (catalog,
00776               pdf_new_name ("Threads"),
00777               pdf_ref_obj (articles_array));
00778   pdf_release_obj (articles_array);
00779   for (i=0; i<number_articles; i++) {
00780     if (articles[i].last == NULL) {
00781       fprintf (stderr, "Article started, but no beads\n");
00782       break;
00783     }
00784     /* Close the loop */
00785     pdf_add_dict (articles[i].last,
00786                 pdf_new_name ("N"),
00787                 pdf_ref_obj (articles[i].first));
00788     pdf_add_dict (articles[i].first,
00789                 pdf_new_name ("V"),
00790                 pdf_ref_obj (articles[i].last));
00791     pdf_release_obj (articles[i].first);
00792     pdf_release_obj (articles[i].last);
00793     pdf_release_obj (articles[i].info);
00794     RELEASE (articles[i].name);
00795   }
00796 }
00797 
00798 #ifdef HAVE_LIBPNG
00799 static char thumbnail_opt = 0;
00800 static char *thumb_basename = NULL;
00801 
00802 void pdf_doc_enable_thumbnails(void)
00803 {
00804   thumbnail_opt = 1;
00805 }
00806 
00807 #endif
00808 
00809 void pdf_doc_finish_page ()
00810 {
00811 #ifdef MEM_DEBUG
00812 MEM_START
00813 #endif  
00814   if (debug) {
00815     fprintf (stderr, "(pdf_doc_finish_page)");
00816   }
00817   finish_pending_xobjects();
00818   /* Flush this page */
00819   /* Page_count is the index of the current page, starting at 1 */
00820   tmp1 = pdf_new_array ();
00821   pdf_add_array (tmp1, pdf_ref_obj (glob_page_bop));
00822   if (this_page_bop) {
00823     pdf_add_array (tmp1, pdf_ref_obj (this_page_bop));
00824   }
00825   pdf_add_array (tmp1, pdf_link_obj (coord_xform_ref));
00826   pdf_add_array (tmp1, pdf_ref_obj (this_page_contents));
00827   pdf_add_array (tmp1, pdf_ref_obj (glob_page_eop));
00828   pdf_add_dict (pages[page_count].page_dict,
00829               pdf_link_obj(contents_name), tmp1);
00830   /* We keep .page_dict open because we don't know the parent yet */
00831   if (this_page_bop != NULL) {
00832     pdf_add_stream (this_page_bop, "\n", 1);
00833     pdf_release_obj (this_page_bop);
00834     this_page_bop = NULL;
00835   }
00836   if (this_page_contents != NULL) {
00837     pdf_add_stream (this_page_contents, "\n", 1);
00838     pdf_release_obj (this_page_contents);
00839     this_page_contents = NULL;
00840   }
00841   if (this_page_annots != NULL) {
00842     pdf_add_dict (pages[page_count].page_dict,
00843                 pdf_link_obj(annots_name),
00844                 pdf_ref_obj (this_page_annots));
00845     pdf_release_obj (this_page_annots);
00846     this_page_annots = NULL;
00847   }
00848   if (this_page_beads != NULL) {
00849     pdf_add_dict (pages[page_count].page_dict,
00850                 pdf_link_obj (bead_name),
00851                 pdf_ref_obj (this_page_beads));
00852     pdf_release_obj (this_page_beads);
00853     this_page_beads = NULL;
00854   }
00855   if (this_page_fonts != NULL) {
00856     pdf_add_dict (current_page_resources, 
00857                 pdf_new_name ("Font"),
00858                 pdf_ref_obj (this_page_fonts));
00859     pdf_release_obj (this_page_fonts);
00860     this_page_fonts = NULL;
00861   }
00862   if (this_page_xobjects != NULL) {
00863     pdf_add_dict (current_page_resources,
00864                 pdf_new_name ("XObject"),
00865                 pdf_ref_obj (this_page_xobjects));
00866     pdf_release_obj (this_page_xobjects);
00867     this_page_xobjects = NULL;
00868   }
00869   if (current_page_resources != NULL) {
00870     pdf_release_obj (current_page_resources);
00871     current_page_resources = NULL;
00872   }
00873 #ifdef HAVE_LIBPNG
00874   if (thumbnail_opt) {
00875     char *thumb_filename;
00876     pdf_obj *thumbnail;
00877     thumb_filename = NEW (strlen(thumb_basename)+7, char);
00878     sprintf (thumb_filename, "%s.%ld", thumb_basename,
00879             page_count%99999+1L);
00880     thumbnail = do_thumbnail (thumb_filename);
00881     RELEASE (thumb_filename);
00882     if (thumbnail) 
00883       pdf_add_dict (pages[page_count].page_dict,
00884                   pdf_link_obj (thumb_name),
00885                   thumbnail);
00886   }
00887 #endif
00888   page_count += 1;
00889 #ifdef MEM_DEBUG
00890   MEM_END;
00891 #endif  
00892 }
00893 
00894 pdf_obj *pdf_doc_current_page_resources (void)
00895 {
00896   return current_page_resources;
00897 }
00898 
00899 
00900 static int highest_page_ref = 0;
00901 pdf_obj *pdf_doc_ref_page (unsigned long page_no)
00902 {
00903   if (debug)
00904     fprintf (stderr, "(doc_ref_page:page_no=%ld)", page_no);
00905   if (page_no >= max_pages) {
00906     resize_pages (page_no+PAGES_ALLOC_SIZE);
00907   }
00908   /* Has this page been referenced yet? */ 
00909   if (pages[page_no-1].page_dict == NULL) {
00910     /* If not, create it */
00911     pages[page_no-1].page_dict = pdf_new_dict ();
00912     /* and reference it */
00913     pages[page_no-1].page_ref = pdf_ref_obj (pages[page_no-1].page_dict);
00914   }
00915   if (page_no > highest_page_ref)
00916     highest_page_ref = page_no;
00917   return pdf_link_obj (pages[page_no-1].page_ref);
00918 }
00919 
00920 pdf_obj *pdf_doc_names (void)
00921 {
00922   return names_dict;
00923 }
00924 
00925 pdf_obj *pdf_doc_page_tree (void)
00926 {
00927   return page_tree;
00928 }
00929 
00930 pdf_obj *pdf_doc_catalog (void)
00931 {
00932   return catalog;
00933 }
00934 
00935 pdf_obj *pdf_doc_this_page (void)
00936 {
00937   return pages[page_count].page_dict;
00938 }
00939 
00940 pdf_obj *pdf_doc_this_page_ref (void)
00941 {
00942   return pdf_doc_ref_page(page_count+1);
00943 }
00944 
00945 pdf_obj *pdf_doc_prev_page_ref (void)
00946 {
00947   if (page_count < 1) {
00948     ERROR ("Reference to previous page, but no pages have been completed yet");
00949   }
00950   return pdf_doc_ref_page(page_count>0?page_count:1);
00951 }
00952 
00953 pdf_obj *pdf_doc_next_page_ref (void)
00954 {
00955   return pdf_doc_ref_page(page_count+2);
00956 }
00957 
00958 void pdf_doc_new_page (void)
00959 {
00960 #ifdef MEM_DEBUG
00961 MEM_START
00962 #endif
00963   if (debug) {
00964     fprintf (stderr, "(pdf_doc_new_page)");
00965     fprintf (stderr, "page_count=%ld, max_pages=%ld\n", page_count,
00966             max_pages);
00967   }
00968   /* See if we need more pages allocated yet */
00969   if (page_count >= max_pages) {
00970     resize_pages(max_pages+PAGES_ALLOC_SIZE);
00971   }
00972   /* Was this page already instantiated by a forward reference to it? */
00973   if (pages[page_count].page_ref == NULL) {
00974     /* If not, create it. */
00975     pages[page_count].page_dict = pdf_new_dict ();
00976     /* and reference it */
00977     pages[page_count].page_ref = pdf_ref_obj(pages[page_count].page_dict);
00978   }
00979   pdf_add_dict (pages[page_count].page_dict,
00980               pdf_link_obj(type_name), pdf_link_obj(page_name));
00981   /* start the contents stream for the new page */
00982   this_page_contents = pdf_new_stream(STREAM_COMPRESS);
00983   start_current_page_resources();
00984   pdf_add_dict (pages[page_count].page_dict,
00985               pdf_link_obj (resources_name),
00986               pdf_ref_obj (current_page_resources));
00987   /* Contents are still available as this_page_contents until next
00988      page is started */
00989   /* Even though the page is gone, a Reference to this page is kept
00990      until program ends */
00991 #ifdef MEM_DEBUG
00992 MEM_END
00993 #endif
00994 }
00995 
00996 void pdf_doc_add_to_page (char *buffer, unsigned length)
00997 {
00998   pdf_add_stream (this_page_contents, buffer, length);
00999 }
01000 
01001 void pdf_doc_init (char *filename) 
01002 {
01003 #ifdef MEM_DEBUG
01004   MEM_START
01005 #endif
01006   if (debug) fprintf (stderr, "pdf_doc_init:\n");
01007   pdf_out_init (filename);
01008 #ifdef HAVE_LIBPNG
01009   /* Create a default name for thumbnail image files */
01010   if (thumbnail_opt) {
01011     if (strlen(filename)>4 && !strncmp (".pdf", filename+strlen(filename)-4,4)) {
01012       thumb_basename = NEW (strlen(filename)+1-4, char);
01013       strncpy (thumb_basename, filename, strlen(filename)-4);
01014       thumb_basename[strlen(filename)-4] = 0;
01015     } else {
01016       thumb_basename = NEW (strlen(filename)+1, char);
01017       strcpy (thumb_basename, filename);
01018     }
01019   }
01020 #endif /* HAVE_LIBPNG */
01021   make_short_cuts();
01022   create_docinfo ();
01023   create_catalog ();
01024 #ifdef MEM_DEBUG
01025   MEM_END
01026 #endif
01027 }
01028 
01029 void pdf_doc_creator (char *s)
01030 {
01031   pdf_add_dict (docinfo, pdf_new_name ("Creator"),
01032               pdf_new_string (s, strlen(s)));
01033 }
01034 
01035 void pdf_doc_close ()
01036 {
01037   if (debug) fprintf (stderr, "pdf_doc_finish:\n");
01038 #ifdef HAVE_LIBPNG
01039   if (thumb_basename)
01040     RELEASE (thumb_basename);
01041 #endif /* HAVE_LIBPNG */
01042   /* Following things were kept around so user can add dictionary
01043      items */
01044   finish_docinfo();
01045   finish_page_tree();
01046   /* Add names dict to catalog */
01047   finish_outline();
01048   finish_dests_tree();
01049   finish_articles();
01050   pdf_add_dict (catalog,
01051               pdf_new_name ("Names"),
01052               pdf_ref_obj (names_dict));
01053   pdf_release_obj (names_dict);
01054   pdf_release_obj (catalog);
01055   /* Do consistency check on forward references to pages */
01056   if (highest_page_ref > page_count) {
01057     unsigned long i;
01058     fprintf (stderr, "\nWarning:  Nonexistent page(s) referenced\n");
01059     fprintf (stderr, "          (PDF file may not work right)\n");
01060     for (i=page_count; i<highest_page_ref; i++) {
01061       if (pages[i].page_dict) {
01062        pdf_release_obj (pages[i].page_dict);
01063        pdf_release_obj (pages[i].page_ref);
01064       }
01065     }
01066   }
01067   pdf_finish_specials();
01068   release_short_cuts();
01069   pdf_out_flush ();
01070 }
01071 
01072 static pdf_obj *build_scale_array (double a, double b, double c,
01073                                double d, double e, double f)
01074 {
01075   pdf_obj *result;
01076   result = pdf_new_array();
01077   pdf_add_array (result, pdf_new_number (a));
01078   pdf_add_array (result, pdf_new_number (b));
01079   pdf_add_array (result, pdf_new_number (c));
01080   pdf_add_array (result, pdf_new_number (d));
01081   pdf_add_array (result, pdf_new_number (ROUND(e,0.01)));
01082   pdf_add_array (result, pdf_new_number (ROUND(f,0.01)));
01083   return result;
01084 }
01085 
01086 /* All this routine does is give the form a name
01087    and add a unity scaling matrix. It fills
01088    in required fields.  The caller must initialize
01089    the stream */
01090 
01091 
01092 void doc_make_form_xobj (pdf_obj *this_form_contents, pdf_obj *bbox,
01093                       double refptx, double refpty,
01094                       double xscale, double yscale,
01095                       pdf_obj *resources, char *form_name)
01096 {
01097   pdf_obj *xobj_dict, *tmp1;
01098   xobj_dict = pdf_stream_dict (this_form_contents);
01099   
01100   pdf_add_dict (xobj_dict, pdf_new_name ("Name"), pdf_new_name(form_name));
01101   pdf_add_dict (xobj_dict, pdf_link_obj (type_name),
01102               pdf_new_name ("XObject"));
01103   pdf_add_dict (xobj_dict, pdf_new_name ("Subtype"),
01104               pdf_new_name ("Form"));
01105   pdf_add_dict (xobj_dict, pdf_new_name ("BBox"), bbox);
01106   pdf_add_dict (xobj_dict, pdf_new_name ("FormType"), 
01107               pdf_new_number(1.0));
01108   /* The reference point of an Xobject is at the lower left corner
01109      of the bounding box.  Since we would like to have an arbitrary
01110      reference point, we use a transformation matrix, translating
01111      the reference point to (0,0) */
01112   tmp1 = build_scale_array (xscale, 0, 0, yscale, -xscale*refptx, -yscale*refpty);
01113   pdf_add_dict (xobj_dict, pdf_new_name ("Matrix"), tmp1);
01114   pdf_add_dict (xobj_dict, pdf_link_obj (resources_name), resources);
01115   return;
01116 }
01117 
01118 struct resource_stack 
01119 {
01120   pdf_obj *save_page_contents, *save_page_fonts;
01121   pdf_obj *save_page_xobjects, *save_page_resources;
01122   int xform_depth;
01123 } res_stack[4];
01124 
01125 static int xobjects_pending = 0;
01126 
01127 /* begin_form_xobj creates an xobject with its "origin" at
01128    xpos and ypos that is clipped to the specified bbox. Note
01129    that the origin is not the lower left corner of the bbox */
01130 pdf_obj *begin_form_xobj (double xpos, double ypos,
01131                        double bbllx, double bblly,
01132                        double bburx, double bbury, char *res_name)
01133 {
01134   pdf_obj *bbox;
01135   if (xobjects_pending >= sizeof(res_stack)/sizeof(res_stack[0])) {
01136     fprintf (stderr, "\nForm XObjects nested too deeply.  Limit is %d\n",
01137             sizeof(res_stack)/sizeof(res_stack[0]));
01138     return NULL;
01139   }
01140   /* This is a real hack.  We basically treat each xobj as a separate mini
01141      page unto itself.  Save all the page structures and reinitialize
01142      them when we finish this xobject. */
01143   res_stack[xobjects_pending].save_page_resources = current_page_resources;
01144   current_page_resources = NULL;
01145   res_stack[xobjects_pending].save_page_xobjects = this_page_xobjects;
01146   this_page_xobjects = NULL;
01147   res_stack[xobjects_pending].save_page_fonts = this_page_fonts;
01148   this_page_fonts = NULL;
01149   res_stack[xobjects_pending].save_page_contents = this_page_contents;
01150   this_page_contents = NULL;
01151   res_stack[xobjects_pending].xform_depth = dev_xform_depth();
01152   xobjects_pending += 1;
01153   start_current_page_resources(); /* Starts current_page_resources */
01154   this_page_contents = pdf_new_stream (STREAM_COMPRESS);
01155   /* Make a bounding box for this Xobject */
01156   /* Translate coordinate system so reference point of object 
01157      is at 0 */
01158   bbox = pdf_new_array ();
01159   pdf_add_array (bbox, pdf_new_number (ROUND(bbllx,0.01)));
01160   pdf_add_array (bbox, pdf_new_number (ROUND(bblly,0.01)));
01161   pdf_add_array (bbox, pdf_new_number (ROUND(bburx,0.01)));
01162   pdf_add_array (bbox, pdf_new_number (ROUND(bbury,0.01)));
01163   /* Resource is already made, so call doc_make_form_xobj() */
01164   doc_make_form_xobj (this_page_contents, bbox,
01165                     xpos, ypos, 1.0, 1.0,
01166                     pdf_ref_obj(current_page_resources), res_name);
01167   /* Make sure the object is self-contained by adding the
01168      current font to the object stream */
01169   dev_reselect_font();
01170   /* Likewise for color */
01171   dev_do_color();
01172   return pdf_link_obj (this_page_contents);
01173 }
01174 
01175 void end_form_xobj (void)
01176 {
01177   if (xobjects_pending>0) {
01178     xobjects_pending -= 1;
01179     dev_close_all_xforms(res_stack[xobjects_pending].xform_depth);
01180     if (this_page_xobjects) {
01181       pdf_add_dict (current_page_resources, pdf_new_name ("XObject"),
01182                   pdf_ref_obj (this_page_xobjects));
01183       pdf_release_obj (this_page_xobjects);
01184     }
01185     if (this_page_fonts) {
01186       pdf_add_dict (current_page_resources, pdf_new_name ("Font"),
01187                   pdf_ref_obj (this_page_fonts));
01188       pdf_release_obj (this_page_fonts);
01189     }
01190     if (current_page_resources)
01191       pdf_release_obj (current_page_resources);
01192     if (this_page_contents)
01193       pdf_release_obj (this_page_contents);
01194     current_page_resources = res_stack[xobjects_pending].save_page_resources;
01195     this_page_xobjects = res_stack[xobjects_pending].save_page_xobjects;
01196     this_page_fonts = res_stack[xobjects_pending].save_page_fonts;
01197     this_page_contents = res_stack[xobjects_pending].save_page_contents;
01198     /* Must reselect the font again in case there was a font change in
01199        the object */
01200     dev_reselect_font();
01201     /* Must reselect color too */
01202     dev_do_color();
01203   } else{
01204     fprintf (stderr, "\nSpecial: exobj: Tried to close a nonexistent xobject\n");
01205   }
01206   return;
01207 }
01208 
01209 void finish_pending_xobjects (void)
01210 {
01211   if (xobjects_pending) {
01212     fprintf (stderr, "\nFinishing a pending form XObject at end of page\n"); 
01213     while (xobjects_pending--) {
01214       end_form_xobj();
01215     }
01216   }
01217   return;
01218 }
01219 
01220 static struct
01221 {
01222   pdf_obj *annot_dict;
01223   unsigned char dirty;
01224   double llx, lly, urx, ury;
01225 } breaking_state = {NULL, 0};
01226 
01227 void pdf_doc_set_box (void)
01228 {
01229   breaking_state.llx = dev_page_width();
01230   breaking_state.urx = 0;
01231   breaking_state.lly = dev_page_height();
01232   breaking_state.ury = 0;
01233   breaking_state.dirty = 0;
01234   return;
01235 }
01236 
01237 void pdf_doc_begin_annot (pdf_obj *dict)
01238 {
01239   breaking_state.annot_dict = dict;
01240   pdf_doc_set_box ();
01241   dev_tag_depth ();
01242   return;
01243 }
01244 
01245 void pdf_doc_end_annot (void)
01246 {
01247   pdf_doc_flush_annot();
01248   breaking_state.annot_dict = NULL;
01249   dev_untag_depth ();
01250   return;
01251 }
01252 
01253 void pdf_doc_flush_annot (void)
01254 {
01255   pdf_obj *rectangle, *new_dict;
01256   double grow;
01257   grow = pdf_special_tell_grow ();
01258   if (breaking_state.dirty) {
01259     rectangle = pdf_new_array ();
01260     pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.llx-grow, 0.01)));
01261     pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.lly-grow, 0.01)));
01262     pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.urx+grow, 0.01)));
01263     pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.ury+grow, 0.01)));
01264     new_dict = pdf_new_dict ();
01265     pdf_add_dict (new_dict, pdf_new_name ("Rect"),
01266                 rectangle);
01267     pdf_merge_dict (new_dict, breaking_state.annot_dict);
01268     pdf_doc_add_to_page_annots (pdf_ref_obj (new_dict));
01269     pdf_release_obj (new_dict);
01270   }
01271   pdf_doc_set_box();
01272   return;
01273 }
01274 
01275 void pdf_doc_expand_box (double llx, double lly, double urx, double
01276                       ury)
01277 {
01278   breaking_state.llx = MIN (breaking_state.llx, llx);
01279   breaking_state.lly = MIN (breaking_state.lly, lly);
01280   breaking_state.urx = MAX (breaking_state.urx, urx);
01281   breaking_state.ury = MAX (breaking_state.ury, ury);
01282   breaking_state.dirty = 1;
01283   return;
01284 }