Back to index

tetex-bin  3.0
pdfspecial.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 <ctype.h>
00029 #include <math.h>
00030 #include <string.h>
00031 #include "system.h"
00032 #include "mem.h"
00033 #include "mfileio.h"
00034 #include "numbers.h"
00035 #include "dvi.h"
00036 #include "pdflimits.h"
00037 #include "pdfspecial.h"
00038 #include "pdfobj.h"
00039 #include "pdfdoc.h"
00040 #include "pdfdev.h"
00041 #include "pdfparse.h"
00042 #include "jpeg.h"
00043 #include "epdf.h"
00044 #include "mpost.h"
00045 #include "psimage.h"
00046 
00047 #include "config.h"
00048 
00049 #ifdef HAVE_LIBPNG
00050 #include <png.h>
00051 #include "pngimage.h"
00052 #endif /* HAVE_LIBPNG */
00053 
00054 #define verbose 0
00055 #define debug 0
00056 
00057 static void add_reference (char *name, pdf_obj *object, char *res_name);
00058 static void release_reference (char *name);
00059 static pdf_obj *lookup_reference(char *name);
00060 static char *lookup_ref_res_name (char *name);
00061 static pdf_obj *lookup_object(char *name);
00062 static void do_content (char **start, char *end, double x_user, double
00063                      y_user);
00064 static void do_image(char **start, char *end, double x_user, double y_user);
00065 static pdf_obj *jpeg_start_image (FILE *file);
00066 static void finish_image (pdf_obj *image_res, struct xform_info *p,
00067                        char *res_name);
00068 static void do_bxobj (char **start, char *end,
00069                     double x_user, double y_user);
00070 static void do_exobj (void);
00071 static void do_uxobj (char **start, char *end, double x_user, double
00072                     y_user);
00073 
00074 static char ignore_colors = 0;
00075 static double annot_grow = 0.0;
00076 
00077 void pdf_special_set_grow (double g)
00078 {
00079   annot_grow = g;
00080 }
00081 double pdf_special_tell_grow (void)
00082 {
00083   return annot_grow;
00084 }
00085 
00086 void pdf_special_ignore_colors(void)
00087 {
00088   ignore_colors = 1;
00089 }
00090 
00091 static void do_bop(char **start, char *end)
00092 {
00093 #ifdef MEM_DEBUG
00094   MEM_START
00095 #endif
00096   if (*start < end)
00097     pdf_doc_bop (*start, end - *start);
00098   *start = end;
00099 #ifdef MEM_DEBUG
00100   MEM_END
00101 #endif
00102   return;
00103 }
00104 
00105 static void do_eop(char **start, char *end)
00106 {
00107 #ifdef MEM_DEBUG
00108   MEM_START
00109 #endif
00110   if (*start < end)
00111     pdf_doc_eop (*start, end - *start);
00112   *start = end;
00113 #ifdef MEM_DEBUG
00114   MEM_END
00115 #endif
00116 }
00117 
00118 pdf_obj *get_reference(char **start, char *end)
00119 {
00120   char *name, *save = *start;
00121   pdf_obj *result = NULL;
00122 #ifdef MEM_DEBUG
00123 MEM_START
00124 #endif
00125   skip_white (start, end);
00126   if ((name = parse_pdf_reference(start, end)) != NULL) {
00127     if ((result = lookup_reference (name)) == NULL) {
00128       fprintf (stderr, "\nNamed reference (@%s) doesn't exist.\n", name);
00129       *start = save;
00130       dump(*start, end);
00131     }
00132     RELEASE (name);
00133   }
00134 #ifdef MEM_DEBUG
00135 MEM_END
00136 #endif
00137   return result;
00138 }
00139 
00140 pdf_obj *get_reference_lvalue(char **start, char *end)
00141 {
00142   char *name, *save = *start;
00143   pdf_obj *result = NULL;
00144 #ifdef MEM_DEBUG
00145 MEM_START
00146 #endif
00147   skip_white (start, end);
00148   if ((name = parse_pdf_reference(start, end)) != NULL) {
00149     result = lookup_object (name);
00150     if (result == NULL) {
00151       fprintf (stderr, "\nNamed object (@%s) doesn't exist.\n", name);
00152       *start = save;
00153       dump(*start, end);
00154     }
00155     RELEASE (name);
00156   }
00157 #ifdef MEM_DEBUG
00158 MEM_END
00159 #endif
00160   return result;
00161 }
00162 
00163 static void do_put(char **start, char *end)
00164 {
00165   pdf_obj *result = NULL, *data = NULL;
00166   char *save = *start;
00167   skip_white(start, end);
00168   if ((result = get_reference_lvalue(start, end)) != NULL) {
00169     skip_white (start, end);
00170     save = *start;
00171     switch (result -> type) {
00172     case PDF_DICT:
00173       if ((data = parse_pdf_dict (start, end)) != NULL &&
00174          data -> type == PDF_DICT) {
00175        pdf_merge_dict (result, data);
00176       } else{
00177        fprintf (stderr, "\nSpecial put:  Expecting a dictionary\n");
00178        *start = save;
00179        dump(*start, end);
00180       }
00181       if (data != NULL)
00182        pdf_release_obj (data);
00183       break;
00184     case PDF_ARRAY:
00185       while (*start < end && 
00186             (data = parse_pdf_object (start, end)) != NULL) {
00187        pdf_add_array (result, data);
00188        skip_white(start, end);
00189       }
00190       if (*start < end) {
00191        fprintf (stderr, "\nSpecial: put: invalid object.  Rest of line ignored.\n");
00192        *start = save;
00193        dump(*start, end);
00194       }
00195       break;
00196     default:
00197       fprintf (stderr, "\nSpecial put:  Invalid destination object type\n");
00198       break;
00199     }
00200   }
00201   else {
00202     fprintf (stderr, "\nSpecial put:  Nonexistent object reference\n");
00203   }
00204   return;
00205 }
00206 
00207 /* The following must be consecutive and
00208    starting at 0.  Order must match
00209    dimensions array below */
00210 
00211 #define WIDTH 0
00212 #define HEIGHT 1
00213 #define DEPTH 2
00214 #define SCALE 3
00215 #define XSCALE 4
00216 #define YSCALE 5
00217 #define ROTATE 6
00218 #define BBOX 7
00219 
00220 struct {
00221   char *s;
00222   int key;
00223   int hasunits;
00224 } dimensions[] = {
00225   {"width", WIDTH, 1},
00226   {"height", HEIGHT, 1},
00227   {"depth", DEPTH, 1},
00228   {"scale", SCALE, 0},
00229   {"xscale", XSCALE, 0},
00230   {"yscale", YSCALE, 0},
00231   {"rotate", ROTATE, 0},
00232   {"bbox", BBOX, 0}
00233 };
00234 
00235 struct xform_info *new_xform_info (void)
00236 {
00237   struct xform_info *result;
00238   result = NEW (1, struct xform_info);
00239   result -> width = 0.0;
00240   result -> height = 0.0;
00241   result -> depth = 0.0;
00242   result -> scale = 0.0;
00243   result -> xscale = 0.0;
00244   result -> yscale = 0.0;
00245   result -> rotate = 0.0;
00246   result -> user_bbox = 0;
00247   result -> clip = 0;
00248  /* These next two must be initialized be cause
00249     they represent the reference point even
00250     if the user doesn't specify one.  We must
00251     have a reference point */
00252   result -> u_llx = 0.0;
00253   result -> u_lly = 0.0;
00254   return result;
00255 }
00256 
00257 void release_xform_info (struct xform_info *p)
00258 {
00259   RELEASE (p);
00260   return;
00261 }
00262 
00263 int validate_image_xform_info (struct xform_info *p)
00264 {
00265   int result = 1;
00266   if (p->width != 0.0)
00267     if (p->scale !=0.0 || p->xscale != 0.0) {
00268       fprintf (stderr, "\nCan't supply both width and scale\n");
00269       result = 0;
00270     }
00271   if (p->height != 0.0) 
00272     if (p->scale !=0.0 || p->yscale != 0.0) {
00273       fprintf (stderr, "\nCan't supply both height and scale\n");
00274       result = 0;
00275     }
00276   if (p->scale != 0.0)
00277     if (p->xscale != 0.0 || p->yscale != 0.0) {
00278       fprintf (stderr, "\nCan't supply overall scale along with axis scales");
00279       result = 0;
00280     }
00281   return result;
00282 }
00283 
00284 static int parse_one_dim_word (char **start, char *end)
00285 {
00286   int i;
00287   char *dimension_string;
00288   char *save = *start;
00289   int result = -1;
00290   skip_white(start, end);
00291   if ((dimension_string = parse_ident(start, end)) != NULL) {
00292     for (i=0; i<sizeof(dimensions)/sizeof(dimensions[0]); i++) {
00293       if (!strcmp (dimensions[i].s, dimension_string))
00294        break;
00295     }
00296     if (i != sizeof(dimensions)/sizeof(dimensions[0])) {
00297       result =  dimensions[i].key;
00298     } else {
00299       fprintf (stderr, "\n%s: Invalid keyword\n", dimension_string);
00300     }
00301     RELEASE (dimension_string);
00302   }
00303   if (result < 0) {
00304     *start = save;
00305     fprintf (stderr, "\nExpecting a keyword here, e.g., height, width, etc.\n");
00306     dump(*start, end);
00307   }
00308   return result;
00309 }
00310 
00311 struct {
00312   char *s;
00313   double units;
00314   int is_true_unit;
00315 } units[] = {
00316   {"pt", (72.0/72.27), 0},
00317   {"in", (72.0), 0},
00318   {"cm", (72.0/2.54), 0},
00319   {"mm", (72.0/25.4), 0},
00320   {"truept", (72.0/72.27), 1},
00321   {"truein", (72.0), 1},
00322   {"truecm", (72.0/2.54), 1},
00323   {"truemm", (72.0/25.4), 1}
00324 };
00325   
00326 double parse_one_unit (char **start, char *end)
00327 {
00328   int i;
00329   char *unit_string = NULL, *save = *start;
00330   double result = -1.0;
00331   int errors = 0;
00332   skip_white(start, end);
00333   if ((unit_string = parse_c_ident(start, end)) != NULL) {
00334     for (i=0; i<sizeof(units)/sizeof(units[0]); i++) {
00335       if (!strcmp (units[i].s, unit_string))
00336        break;
00337     }
00338     if (i == sizeof(units)/sizeof(units[0])) {
00339       fprintf (stderr,
00340               "\n%s: Invalid unit of measurement (should be in, cm, pt, etc.)\n", unit_string);
00341       errors = 1;
00342     }
00343     if (i != sizeof(units)/sizeof(units[0]) && !units[i].is_true_unit)
00344       result = units[i].units;
00345     /* If these are "true" units, we must pre-shrink since the entire
00346        document is magnified */
00347     if (i != sizeof(units)/sizeof(units[0]) && units[i].is_true_unit)
00348       result = units[i].units/dvi_tell_mag();
00349     RELEASE (unit_string);
00350   }
00351   if (!unit_string || errors) {
00352     fprintf (stderr, "\nExpecting a unit here (e.g., in, cm, pt)\n");
00353     *start = save; 
00354     dump(*start, end);
00355   }
00356   return result;
00357 }
00358 
00359 static int parse_dimension (char **start, char *end,
00360                          struct xform_info *p)
00361 {
00362   char *number_string = NULL;
00363   char *save = NULL;
00364   double units = -1.0;
00365   int key, error = 0;
00366   skip_white(start, end);
00367   while (*start < end && isalpha (**start)) {
00368     save = *start;
00369     if ((key = parse_one_dim_word(start, end)) >= 0) {
00370       skip_white(start, end);
00371     } else {
00372       fprintf (stderr,
00373               "\nExpecting a dimension/transformation keyword (e.g., width, height) here:\n");
00374       error = 1;
00375       break;
00376     }
00377     if (key != BBOX) { /* BBOX is handled somewhat differently than
00378                        all the others  */
00379       number_string = parse_number(start, end);
00380       if (key >= 0 && number_string == NULL) {
00381        fprintf (stderr, "Expecting a number following dimension/transformation keyword\n");
00382        error = 1;
00383        break;
00384       }
00385       /* If we got a key and a number, see if we need a dimension also */
00386       if (key >= 0 && number_string != NULL && dimensions[key].hasunits) {
00387        skip_white(start, end);
00388        if ((units = parse_one_unit(start, end)) < 0.0) {
00389          fprintf (stderr, "\nExpecting a dimension unit\n");
00390          error = 1;
00391        }
00392       }
00393       if (!error && key >= 0 && number_string != NULL) {
00394        switch (key) {
00395        case WIDTH:
00396          if (p->width != 0.0)
00397            fprintf (stderr, "\nDuplicate width specified: %s\n", number_string);
00398          p->width = atof (number_string)*units;
00399          break;
00400        case HEIGHT:
00401          if (p->height != 0.0)
00402            fprintf (stderr, "\nDuplicate height specified: %s\n", number_string);
00403          p->height = atof (number_string)*units;
00404          break;
00405        case DEPTH:
00406          if (p->depth != 0.0)
00407            fprintf (stderr, "\nDuplicate depth specified: %s\n", number_string);
00408          p->depth = atof (number_string)*units;
00409          break;
00410        case SCALE:
00411          if (p->scale != 0.0)
00412            fprintf (stderr, "\nDuplicate depth specified: %s\n", number_string);
00413          p->scale = atof (number_string);
00414          break;
00415        case XSCALE:
00416          if (p->xscale != 0.0)
00417            fprintf (stderr, "\nDuplicate xscale specified: %s\n", number_string);
00418          p->xscale = atof (number_string);
00419          break;
00420        case YSCALE:
00421          if (p->yscale != 0.0)
00422            fprintf (stderr, "\nDuplicate yscale specified: %s\n", number_string);
00423          p->yscale = atof (number_string);
00424          break;
00425        case ROTATE:
00426          if (p->rotate != 0)
00427            fprintf (stderr, "\nDuplicate rotation specified: %s\n", number_string);
00428          p->rotate = atof (number_string) * M_PI / 180.0;
00429          break;
00430        default:
00431          ERROR ("parse_dimension: Invalid key");
00432        }
00433        if (number_string != NULL) {
00434          RELEASE (number_string);
00435          number_string = NULL;
00436        }
00437       }
00438     } else { /* BBox case handled here */
00439       char *llx = NULL, *lly = NULL, *urx = NULL, *ury = NULL;
00440       if ((llx = parse_number (start, end)) &&
00441          (lly = parse_number (start, end)) &&
00442          (urx = parse_number (start, end)) &&
00443          (ury = parse_number (start, end))) {
00444        p->u_llx = atof (llx);
00445        p->u_lly = atof (lly);
00446        p->u_urx = atof (urx);
00447        p->u_ury = atof (ury);
00448        p->user_bbox = 1; /* Flag to indicate that user specified a bbox */
00449       } else {
00450        fprintf (stderr, "\nExpecting four numbers following \"bbox\" specification.\n");
00451        error = 1; /* Flag error, but don't break until we get a
00452                     chance to free structures */
00453       }
00454       if (llx) RELEASE (llx);
00455       if (lly) RELEASE (lly);
00456       if (urx) RELEASE (urx);
00457       if (ury) RELEASE (ury);
00458       if (error) break;
00459     }
00460     skip_white(start, end);
00461   }
00462   if (error && save)
00463     dump (save, end);
00464   return !error;
00465 }
00466 
00467 static void do_pagesize(char **start, char *end)
00468 {
00469   struct xform_info *p;
00470   int error = 0;
00471   p = new_xform_info();
00472   skip_white(start, end);
00473   if (parse_dimension(start, end, p)) {
00474     if (p->scale != 0.0 || p->xscale != 0.0 || p->yscale != 0.0) {
00475       fprintf (stderr, "\nScale meaningless for pagesize\n");
00476       error = 1;
00477     }
00478     if (p->width == 0.0 || p->depth + p->height == 0.0) {
00479       fprintf (stderr, "\nPage cannot have a zero dimension\n");
00480       error = 1;
00481     }
00482   } else {
00483     fprintf (stderr, "\nSpecial: pagesize: Failed to find a valid set of dimensions\n");
00484     dump (*start, end);
00485     error = 1;
00486   }
00487   if (!error)
00488     /* Since these are physical dimensions, they need to be scaled by
00489        mag.  "Virtual" dimensions are scaled by a transformation
00490        matrix.  Physical dimensions (e.g, page size and annotations)
00491        cannot */
00492     dev_set_page_size (dvi_tell_mag()*p->width, dvi_tell_mag()*(p->depth + p->height));
00493   release_xform_info (p);
00494   return;
00495 }
00496 
00497 
00498 static void do_ann(char **start, char *end)
00499 {
00500   pdf_obj *result = NULL, *rectangle = NULL;
00501   char *name = NULL;
00502   int error = 0;
00503   struct xform_info *p;
00504 #ifdef MEM_DEBUG
00505 MEM_START
00506 #endif
00507   p = new_xform_info();
00508   skip_white(start, end);
00509   name = parse_opt_ident(start, end);
00510   skip_white(start, end);
00511   if (parse_dimension(start, end, p)) {
00512     if (p->scale != 0.0 || p->xscale != 0.0 || p->yscale != 0.0) {
00513       fprintf (stderr, "\nScale meaningless for annotations\n");
00514       error = 1;
00515     }
00516     if (p->width == 0.0 || p->depth + p->height == 0.0) {
00517       fprintf (stderr, "Special ann: Rectangle has a zero dimension\n");
00518       fprintf (stderr, "Special ann: Annotations require both horizontal and vertical dimensions\n");
00519       error = 1;
00520     }
00521   }
00522   if (!error && (result = parse_pdf_dict(start, end)) != NULL) {
00523     rectangle = pdf_new_array();
00524     pdf_add_array (rectangle, pdf_new_number(ROUND(dev_phys_x()-annot_grow,0.01)));
00525     pdf_add_array (rectangle, pdf_new_number(ROUND(dev_phys_y()-dvi_tell_mag()*p->depth-annot_grow,0.01)));
00526     pdf_add_array (rectangle, pdf_new_number(ROUND(dev_phys_x()+dvi_tell_mag()*p->width+annot_grow,0.01)));
00527     pdf_add_array (rectangle, pdf_new_number(ROUND(dev_phys_y()+dvi_tell_mag()*p->height+annot_grow,0.01)));
00528     pdf_add_dict (result, pdf_new_name ("Rect"),
00529                 rectangle);
00530     pdf_doc_add_to_page_annots (pdf_ref_obj (result));
00531     /* If this object has a named reference, we file it away for
00532        later.  Otherwise we release it */
00533     if (name != NULL) {
00534       add_reference (name, result, NULL);
00535       /* An annotation is treated differently from a cos object.
00536         cos objects are kept open for the user.  We forcibly
00537          "close" the annotation by calling release_reference */
00538       release_reference (name);
00539     } else
00540       pdf_release_obj (result);
00541   } else {
00542     fprintf (stderr, "Ignoring annotation with invalid dictionary\n");
00543     error = 1;
00544   }
00545   release_xform_info (p);
00546   if (name)
00547     RELEASE (name);
00548 #ifdef MEM_DEBUG
00549 MEM_END
00550 #endif
00551   return;
00552 }
00553 
00554 pdf_obj *pending_annot_dict = NULL;
00555 static void do_bann(char **start, char *end)
00556 {
00557   int error = 0;
00558 #ifdef MEM_DEBUG
00559 MEM_START
00560 #endif
00561   if (!pending_annot_dict) {
00562     skip_white(start, end);
00563     if ((pending_annot_dict = parse_pdf_dict(start, end)) != NULL) {
00564       pdf_doc_begin_annot (pending_annot_dict);
00565       /* If this object has a named reference, we file it away for
00566         later.  Otherwise we release it */
00567     } else {
00568       fprintf (stderr, "Ignoring annotation with invalid dictionary\n");
00569       error = 1;
00570     }
00571   } else {
00572     fprintf (stderr, "\nCan't begin an annotation when one is pending.\n");
00573   }
00574 #ifdef MEM_DEBUG
00575 MEM_END
00576 #endif
00577   return;
00578 }
00579 
00580 static void do_eann(char **start, char *end)
00581 {
00582 #ifdef MEM_DEBUG
00583 MEM_START
00584 #endif
00585   if (pending_annot_dict) {
00586     pdf_doc_end_annot ();
00587     pdf_release_obj (pending_annot_dict);
00588     pending_annot_dict = NULL;
00589   } else {
00590     fprintf (stderr, "\nTried to end an annotation without starting one!\n");
00591   }
00592 #ifdef MEM_DEBUG
00593 MEM_END
00594 #endif
00595   return;
00596 }
00597 
00598 static void do_bgcolor(char **start, char *end)
00599 {
00600   char *save = *start;
00601   pdf_obj *color;
00602   int error = 0;
00603   skip_white(start, end);
00604   if ((color = parse_pdf_object(start, end)) != NULL &&
00605       (color -> type == PDF_ARRAY ||
00606        color -> type == PDF_NUMBER )) {
00607     switch (color -> type) {
00608       int i;
00609     case PDF_ARRAY:
00610       for (i=0; i<5; i++) {
00611        if (pdf_get_array (color, i) == NULL ||
00612            pdf_get_array (color, i) -> type != PDF_NUMBER)
00613          break;
00614       }
00615       switch (i) {
00616       case 3:
00617        dev_bg_rgb_color (pdf_number_value (pdf_get_array (color,0)),
00618                        pdf_number_value (pdf_get_array (color,1)),
00619                        pdf_number_value (pdf_get_array (color,2)));
00620        break;
00621       case 4:
00622        dev_bg_cmyk_color (pdf_number_value (pdf_get_array (color,0)),
00623                         pdf_number_value (pdf_get_array (color,1)),
00624                         pdf_number_value (pdf_get_array (color,2)),
00625                         pdf_number_value (pdf_get_array (color,3)));
00626        break;
00627       default:
00628        fprintf (stderr, "\nSpecial: begincolor: Expecting either RGB or CMYK color array\n");
00629        error = 1;
00630       }
00631       break;
00632     case PDF_NUMBER:
00633       dev_bg_gray (pdf_number_value (color));
00634     }
00635   } else {
00636     fprintf (stderr, "\nSpecial: background color: Expecting color specified by an array or number\n");
00637     error = 1;
00638   }
00639   if (error) {
00640     *start = save;
00641     dump (*start, end);
00642   }
00643   if (color)
00644     pdf_release_obj (color);
00645   return;
00646 }
00647 
00648 static void do_bcolor(char **start, char *end)
00649 {
00650   char *save = *start;
00651   pdf_obj *color;
00652   int error = 0;
00653 #ifdef MEM_DEBUG
00654   MEM_START
00655 #endif /* MEM_DEBUG */
00656   skip_white(start, end);
00657   if ((color = parse_pdf_object(start, end)) != NULL &&
00658       (color -> type == PDF_ARRAY ||
00659        color -> type == PDF_NUMBER )) {
00660     switch (color -> type) {
00661       int i;
00662     case PDF_ARRAY:
00663       for (i=0; i<5; i++) {
00664        if (pdf_get_array (color, i) == NULL ||
00665            pdf_get_array (color, i) -> type != PDF_NUMBER)
00666          break;
00667       }
00668       switch (i) {
00669       case 3:
00670        dev_begin_rgb_color (pdf_number_value (pdf_get_array (color,0)),
00671                           pdf_number_value (pdf_get_array (color,1)),
00672                           pdf_number_value (pdf_get_array (color,2)));
00673        break;
00674       case 4:
00675        dev_begin_cmyk_color (pdf_number_value (pdf_get_array (color,0)),
00676                            pdf_number_value (pdf_get_array (color,1)),
00677                            pdf_number_value (pdf_get_array (color,2)),
00678                            pdf_number_value (pdf_get_array (color,3)));
00679        break;
00680       default:
00681        fprintf (stderr, "\nSpecial: begincolor: Expecting either RGB or CMYK color array\n");
00682        error = 1;
00683       }
00684       break;
00685     case PDF_NUMBER:
00686       dev_begin_gray (pdf_number_value (color));
00687     }
00688   } else {
00689     fprintf (stderr, "\nSpecial: Begincolor: Expecting color specified by an array or number\n");
00690     error = 1;
00691   }
00692   if (error) {
00693     *start = save;
00694     dump (*start, end);
00695   }
00696   if (color)
00697     pdf_release_obj (color);
00698 #ifdef MEM_DEBUG
00699   MEM_END
00700 #endif /* MEM_DEBUG */
00701   return;
00702 }
00703 
00704 static void do_scolor(char **start, char *end)
00705 {
00706   char *save = *start;
00707   pdf_obj *color;
00708   int error = 0;
00709 #ifdef MEM_DEBUG
00710   MEM_START
00711 #endif /* MEM_DEBUG */
00712   skip_white(start, end);
00713   if ((color = parse_pdf_object(start, end)) != NULL &&
00714       (color -> type == PDF_ARRAY ||
00715        color -> type == PDF_NUMBER )) {
00716     switch (color -> type) {
00717       int i;
00718     case PDF_ARRAY:
00719       for (i=0; i<5; i++) {
00720        if (pdf_get_array (color, i) == NULL ||
00721            pdf_get_array (color, i) -> type != PDF_NUMBER)
00722          break;
00723       }
00724       switch (i) {
00725       case 3:
00726        dev_set_def_rgb_color (pdf_number_value (pdf_get_array (color,0)),
00727                             pdf_number_value (pdf_get_array (color,1)),
00728                             pdf_number_value (pdf_get_array (color,2)));
00729        break;
00730       case 4:
00731        dev_set_def_cmyk_color (pdf_number_value (pdf_get_array (color,0)),
00732                             pdf_number_value (pdf_get_array (color,1)),
00733                             pdf_number_value (pdf_get_array (color,2)),
00734                             pdf_number_value (pdf_get_array (color,3)));
00735        break;
00736       default:
00737        fprintf (stderr, "\nSpecial: begincolor: Expecting either RGB or CMYK color array\n");
00738        error = 1;
00739       }
00740       break;
00741     case PDF_NUMBER:
00742       dev_set_def_gray (pdf_number_value (color));
00743     }
00744   } else {
00745     fprintf (stderr, "\nSpecial: Begincolor: Expecting color specified by an array or number\n");
00746     error = 1;
00747   }
00748   if (error) {
00749     *start = save;
00750     dump (*start, end);
00751   }
00752   if (color)
00753     pdf_release_obj (color);
00754 #ifdef MEM_DEBUG
00755   MEM_END
00756 #endif /* MEM_DEBUG */
00757   return;
00758 }
00759 
00760 static void do_bgray(char **start, char *end)
00761 {
00762   char *number_string;
00763   skip_white(start, end);
00764   if ((number_string = parse_number (start, end)) != NULL) {
00765     dev_begin_gray (atof (number_string));
00766     RELEASE (number_string);
00767   } else {
00768     fprintf (stderr, "\nSpecial: begingray: Expecting a numerical grayscale specification\n");
00769   }
00770   return;
00771 }
00772 
00773 static void do_ecolor(void)
00774 {
00775   dev_end_color();
00776 }
00777 
00778 static void do_egray(void)
00779 {
00780   dev_end_color();
00781 }
00782 
00783 static void do_bxform (char **start, char *end, double x_user, double y_user)
00784 {
00785   int error = 0;
00786   char *save = *start;
00787   struct xform_info *p;
00788   p = new_xform_info ();
00789 #ifdef MEM_DEBUG
00790   MEM_START
00791 #endif
00792   skip_white (start, end);
00793   if (parse_dimension (start, end, p)) {
00794     if (!validate_image_xform_info (p)) {
00795       fprintf (stderr, "\nSpecified dimensions are inconsistent\n");
00796       fprintf (stderr, "\nSpecial will be ignored\n");
00797       error = 1;
00798     }
00799     if (p -> width != 0.0 || p -> height != 0.0 || p -> depth != 0.0) {
00800       fprintf (stderr, "Special: bt: width, height, and depth are meaningless\n");
00801       fprintf (stderr, "Special: bt: These will be ignored\n");
00802       /* This isn't really a fatal error */
00803     }
00804     if (p -> scale != 0.0) {
00805       p->xscale = p->scale;
00806       p->yscale = p->scale;
00807     }
00808     if (p -> xscale == 0.0)
00809       p->xscale = 1.0;
00810     if (p -> yscale == 0.0)
00811       p->yscale = 1.0;
00812   } else {
00813     fprintf (stderr, "\nError in transformation parameters\n");
00814     error = 1;
00815   }
00816   if (!error) {
00817     dev_begin_xform (p->xscale, p->yscale, p->rotate, x_user, y_user);
00818   } else {
00819     *start = save;
00820     dump (*start, end);
00821   }
00822   release_xform_info (p);
00823 #ifdef MEM_DEBUG
00824 MEM_END
00825 #endif
00826   return;
00827 }
00828 
00829 static void do_exform(void)
00830 {
00831 #ifdef MEM_DEBUG
00832 MEM_START
00833 #endif
00834   dev_end_xform();
00835 #ifdef MEM_DEBUG
00836 MEM_END
00837 #endif
00838 }
00839 
00840 static void do_outline(char **start, char *end)
00841 {
00842   pdf_obj *level = NULL, *result;
00843   int error = 0;
00844   char *save; 
00845   static int lowest_level = 255;
00846   skip_white(start, end);
00847   save = *start; 
00848   if ((level = parse_pdf_object(start, end)) != NULL &&
00849       level -> type == PDF_NUMBER) {
00850     /* Make sure we know where the starting level is */
00851     if ( (int) pdf_number_value (level) < lowest_level)
00852       lowest_level = (int) pdf_number_value (level);
00853 
00854     if ((result = parse_pdf_dict(start, end)) != NULL) {
00855       pdf_doc_change_outline_depth
00856        ((int)pdf_number_value(level)-lowest_level+1);
00857       pdf_doc_add_outline (result);
00858     } else {
00859       fprintf (stderr, "\nIgnoring invalid dictionary\n");
00860       error = 1;
00861     }
00862   } else {
00863     fprintf (stderr, "\nSpecial: outline: Expecting number for object level.\n");
00864     *start = save;
00865     error = 1;
00866   }
00867   if (error)
00868     dump (*start, end);
00869   if (level)
00870     pdf_release_obj (level);
00871   return;
00872 }
00873 
00874 static void do_article(char **start, char *end)
00875 {
00876   char *name = NULL, *save = *start;
00877   int error = 0;
00878   pdf_obj *info_dict = NULL;
00879   skip_white (start, end);
00880   if (*((*start)++) != '@' || (name = parse_ident(start, end)) == NULL) {
00881     fprintf (stderr, "Article name expected.\n");
00882     *start = save;
00883     dump (*start, end);
00884     error = 1;
00885   }
00886   if (!error && (info_dict = parse_pdf_dict(start, end)) == NULL) {
00887     fprintf (stderr, "Ignoring invalid dictionary\n");
00888     error = 1;
00889   }
00890   if (!error) {
00891     pdf_doc_start_article (name, pdf_link_obj(info_dict));
00892     add_reference (name, info_dict, NULL);
00893   }
00894   if (name)
00895     RELEASE (name);
00896   return;
00897 }
00898 
00899 static void do_bead(char **start, char *end)
00900 {
00901   pdf_obj *bead_dict, *rectangle, *article, *info_dict = NULL;
00902   int error = 0;
00903   char *name = NULL, *save = *start;
00904   struct xform_info *p;
00905   p = new_xform_info();
00906   skip_white(start, end);
00907   if (*((*start)++) != '@' || (name = parse_ident(start, end)) == NULL) {
00908     fprintf (stderr, "Article reference expected.\nWhich article does this go with?\n");
00909     error = 1;
00910   }
00911   /* If okay so far, try to get a bounding box */
00912   if (!error) {
00913     skip_white(start, end);
00914     if (!parse_dimension(start, end, p)) {
00915       fprintf (stderr, "\nSpecial: thread: Error in bounding box specification for this bead\n");
00916       error = 1;
00917     }
00918     if (p->scale != 0.0 || p->xscale != 0.0 || p->yscale != 0.0) {
00919       fprintf (stderr, "\nScale meaningless for annotations\n");
00920       error = 1;
00921     }
00922     if (p->width == 0.0 || p->depth + p->height == 0.0) {
00923       fprintf (stderr, "\nSpecial thread: Rectangle has a zero dimension\n");
00924       error = 1;
00925     }
00926   }
00927   if (!error) {
00928     skip_white (start, end);
00929     if (**start == '<') {
00930       if ((info_dict = parse_pdf_dict (start, end)) ==
00931        NULL) {
00932        fprintf (stderr, "\nSpecial: thread: Error in dictionary\n");
00933        error = 1;
00934       }
00935     } else
00936       info_dict = pdf_new_dict();
00937   }
00938   if (!error && name && info_dict) {
00939     /* Does this article exist yet */
00940     if ((article = lookup_object (name)) == NULL) {
00941       pdf_doc_start_article (name, pdf_link_obj (info_dict));
00942       add_reference (name, info_dict, NULL);
00943     } else {
00944       pdf_merge_dict (article, info_dict);
00945       pdf_release_obj (info_dict);
00946       info_dict = NULL;
00947     }
00948     bead_dict = pdf_new_dict ();
00949     rectangle = pdf_new_array();
00950     pdf_add_array (rectangle, pdf_new_number(ROUND(dev_phys_x(),0.01)));
00951     pdf_add_array (rectangle, pdf_new_number(ROUND(dev_phys_y()-p->depth,0.01)));
00952     pdf_add_array (rectangle, pdf_new_number(ROUND(dev_phys_x()+p->width,0.01)));
00953     pdf_add_array (rectangle, pdf_new_number(ROUND(dev_phys_y()+p->height,0.01)));
00954     pdf_add_dict (bead_dict, pdf_new_name ("R"),
00955                 rectangle);
00956     pdf_add_dict (bead_dict, pdf_new_name ("P"),
00957                 pdf_doc_this_page_ref());
00958     pdf_doc_add_bead (name, bead_dict);
00959   }
00960   release_xform_info(p);
00961   if (name != NULL) {
00962     RELEASE (name);
00963   }
00964   if (error) {
00965     *start = save;
00966     dump (*start, end);
00967   }
00968   return;
00969 }
00970 
00971 pdf_obj *embed_image (char *filename, struct xform_info *p,
00972                    double x_user, double y_user, char *objname) 
00973 {
00974   pdf_obj *result = NULL;
00975   char *kpse_file_name;
00976   FILE *image_file;
00977   static char res_name[16];
00978   static long next_image = 1;
00979   sprintf (res_name, "Im%ld", next_image);
00980   if ((kpse_file_name = kpse_find_pict (filename)) &&
00981       (image_file = MFOPEN (kpse_file_name, FOPEN_RBIN_MODE))) {
00982     fprintf (stderr, "(%s", kpse_file_name);
00983     if (check_for_jpeg(image_file)) {
00984       result = jpeg_start_image(image_file);
00985       if (result)
00986        finish_image (result, p, res_name);
00987     }
00988 #ifdef HAVE_LIBPNG
00989     else if (check_for_png(image_file)) {
00990       fprintf (stderr, "<PNG>");
00991       result = start_png_image (image_file, NULL);
00992       if (result)
00993        finish_image (result, p, res_name);
00994     }
00995 #endif
00996     else if (check_for_pdf (image_file)) {
00997       fprintf (stderr, "<PDF>");
00998       result = pdf_include_page (image_file, p, res_name);
00999     }
01000     else if (check_for_mp (image_file)) {
01001       fprintf (stderr, "<MPOST>");
01002       result = mp_include (image_file, p, res_name, x_user, y_user);
01003     }
01004     /* Make sure we check for PS *after* checking for MP since
01005        MP is a special case of PS */
01006     else if (check_for_ps (image_file)) {
01007       fprintf (stderr, "<PS>");
01008       result = ps_include (kpse_file_name, p,
01009                         res_name, x_user, y_user);
01010     }
01011     else{
01012       fprintf (stderr, "<?>");
01013       result = ps_include (kpse_file_name, p,
01014                         res_name, x_user, y_user);
01015     }
01016     MFCLOSE (image_file);
01017     fprintf (stderr, ")");
01018   } else {
01019       fprintf (stderr, "\nError locating or opening file (%s)\n", filename);
01020   }
01021   if (result) { /* Put reference to object on page */
01022     next_image += 1;
01023     pdf_doc_add_to_page_xobjects (res_name, pdf_ref_obj(result));
01024     pdf_doc_add_to_page (" q", 2);
01025     /* Handle the conversion to PDF stream units here */
01026     add_xform_matrix (x_user, y_user,
01027                     p->xscale/pdf_dev_scale(), p->yscale/pdf_dev_scale(), p->rotate);
01028     if (p->depth != 0.0)
01029       add_xform_matrix (0.0, -p->depth, 1.0, 1.0, 0.0);
01030     sprintf (work_buffer, " /%s Do Q", res_name);
01031     pdf_doc_add_to_page (work_buffer, strlen(work_buffer));
01032   } else {
01033     fprintf (stderr, "\npdf: image inclusion failed for (%s).\n", filename);
01034   }
01035   if (objname != NULL && result != NULL) {
01036     add_reference (objname, pdf_link_obj (result), res_name);
01037     /* Read the explanation for the next line in do_ann() */
01038     release_reference (objname);
01039   }
01040   return result;
01041 }
01042 
01043 static void do_image (char **start, char *end, double x_user, double y_user)
01044 {
01045   char *filename = NULL, *objname = NULL, *save;
01046   pdf_obj *filestring = NULL, *result = NULL;
01047   int error = 0;
01048   struct xform_info *p;
01049 #ifdef MEM_DEBUG
01050 MEM_START
01051 #endif
01052   skip_white(start, end);
01053   objname = parse_opt_ident(start, end);
01054   p = new_xform_info();
01055   skip_white(start, end);
01056   save = *start;
01057   if (!parse_dimension(start, end, p)) {
01058     fprintf (stderr, "\nError in dimensions of encapsulated image\n");
01059     error = 1;
01060   }
01061   skip_white(start, end);
01062   if (!error && (filestring = parse_pdf_string(start, end)) == NULL) {
01063     fprintf (stderr, "\nMissing filename\n");
01064     error = 1;
01065   }
01066   if (!error) {
01067     filename = pdf_string_value(filestring);
01068   }
01069   if (!error && !validate_image_xform_info (p)) {
01070     fprintf (stderr, "\nSpecified dimensions are inconsistent\n");
01071     dump (save, end);
01072     error = 1;
01073   }
01074   if (!error)
01075     result = embed_image (filename, p, x_user, y_user, objname);
01076   else
01077     dump (save, end);
01078   if (p)
01079     release_xform_info (p);
01080   if (objname)
01081     RELEASE (objname);
01082   if (filestring)
01083     pdf_release_obj (filestring);
01084   if (error || !result)
01085     fprintf (stderr, "Image special ignored.\n");
01086   if (result)
01087     pdf_release_obj (result);
01088   return;
01089 #ifdef MEM_DEBUG
01090 MEM_END
01091 #endif
01092 }
01093 
01094 static void do_dest(char **start, char *end)
01095 {
01096   pdf_obj *name;
01097   pdf_obj *array;
01098 #ifdef MEM_DEBUG
01099 MEM_START
01100 #endif
01101   skip_white(start, end);
01102   if ((name = parse_pdf_string(start, end)) == NULL) {
01103     fprintf (stderr, "\nPDF string expected and not found.\n");
01104     fprintf (stderr, "Special dest: ignored\n");
01105     dump(*start, end);
01106     return;
01107   }
01108   if ((array = parse_pdf_array(start, end)) == NULL) {
01109     pdf_release_obj (name);
01110     return;
01111   }
01112   pdf_doc_add_dest (pdf_obj_string_value(name),
01113                   pdf_obj_string_length(name),
01114                   pdf_ref_obj (array));
01115   pdf_release_obj (name);
01116   pdf_release_obj (array);
01117 #ifdef MEM_DEBUG
01118 MEM_END
01119 #endif
01120 }
01121 
01122 static void do_docinfo(char **start, char *end)
01123 {
01124   pdf_obj *result;
01125   if ((result = parse_pdf_dict(start, end)) != NULL) {
01126     pdf_doc_merge_with_docinfo (result);
01127   } else {
01128     fprintf (stderr, "\nSpecial: docinfo: Dictionary expected and not found\n");
01129     dump (*start, end);
01130   }
01131   pdf_release_obj (result);
01132   return;
01133 }
01134 
01135 static void do_docview(char **start, char *end)
01136 {
01137   pdf_obj *result;
01138   if ((result = parse_pdf_dict(start, end)) != NULL) {
01139     pdf_doc_merge_with_catalog (result);
01140   } else {
01141     fprintf (stderr, "\nSpecial: docview: Dictionary expected and not found\n");
01142     dump (*start, end);
01143   }
01144   pdf_release_obj (result);
01145   return;
01146 }
01147 
01148 
01149 static void do_close(char **start, char *end)
01150 {
01151   char *name;
01152   skip_white(start, end);
01153   if ((name = parse_pdf_reference(start, end)) != NULL) {
01154     release_reference (name);
01155     RELEASE (name);
01156   }
01157   return;
01158 }
01159 
01160 static void do_obj(char **start, char *end)
01161 {
01162   pdf_obj *result;
01163   char *name;
01164   skip_white(start, end);
01165   name = parse_opt_ident(start, end);
01166   if ((result = parse_pdf_object(start, end)) == NULL) {
01167     fprintf (stderr, "Special object: Ignored.\n");
01168     return;
01169   };
01170   if (name != NULL) {
01171     add_reference (name, result, NULL);
01172     RELEASE (name);
01173   }
01174   return;
01175 }
01176 
01177 static void do_content(char **start, char *end, double x_user, double y_user)
01178 {
01179   int len;
01180   if (*start < end) {
01181     sprintf (work_buffer, " q 1 0 0 1 %.2f %.2f cm ", x_user, y_user);
01182     len = strlen(work_buffer);
01183     pdf_doc_add_to_page (work_buffer, len);
01184     pdf_doc_add_to_page (*start, end-*start);
01185     pdf_doc_add_to_page (" Q", 2);
01186   }
01187   *start = end;
01188   return;
01189 }
01190 
01191 
01192 static int is_pdf_special (char **start, char *end)
01193 {
01194   skip_white(start, end);
01195   if (end-*start >= strlen ("pdf:") &&
01196       !strncmp (*start, "pdf:", strlen("pdf:"))) {
01197     *start += strlen("pdf:");
01198     return 1;
01199   }
01200   return 0;
01201 }
01202 
01203 #define ANN 1
01204 #define OUTLINE 2
01205 #define ARTICLE 3
01206 #define DEST 4
01207 #define DOCINFO 7
01208 #define DOCVIEW 8
01209 #define OBJ 9
01210 #define CONTENT 10
01211 #define PUT 11
01212 #define CLOSE 12
01213 #define BOP 13
01214 #define EOP 14
01215 #define BEAD 15
01216 #define EPDF 16
01217 #define IMAGE 17
01218 #define BCOLOR 18
01219 #define ECOLOR 19
01220 #define BGRAY  20
01221 #define EGRAY  21
01222 #define BGCOLOR 22
01223 #define BXFORM 23
01224 #define EXFORM 24
01225 #define DVIPDFM_PAGE_SIZE 25
01226 #define BXOBJ 26
01227 #define EXOBJ 27
01228 #define UXOBJ 28
01229 #define SCOLOR 29
01230 #define BANN   30
01231 #define EANN   31
01232 #define LINK_ANNOT 32
01233 #define NOLINK_ANNOT 33
01234 
01235 struct pdfmark
01236 {
01237   char *string;
01238   int value;
01239 } pdfmarks[] = {
01240   {"ann", ANN},
01241   {"annot", ANN},
01242   {"annotate", ANN},
01243   {"annotation", ANN},
01244   {"out", OUTLINE},
01245   {"outline", OUTLINE},
01246   {"art", ARTICLE},
01247   {"article", ARTICLE},
01248   {"bead", BEAD},
01249   {"thread", BEAD},
01250   {"dest", DEST},
01251   {"docinfo", DOCINFO},
01252   {"docview", DOCVIEW},
01253   {"obj", OBJ},
01254   {"object", OBJ},
01255   {"content", CONTENT},
01256   {"put", PUT},
01257   {"close", CLOSE},
01258   {"bop", BOP},
01259   {"eop", EOP},
01260   {"epdf", EPDF},
01261   {"image", IMAGE},
01262   {"img", IMAGE},
01263   {"bc", BCOLOR},
01264   {"bcolor", BCOLOR},
01265   {"begincolor", BCOLOR},
01266   {"link", LINK_ANNOT},
01267   {"nolink", NOLINK_ANNOT},
01268   {"sc", SCOLOR},
01269   {"scolor", SCOLOR},
01270   {"setcolor", SCOLOR},
01271   {"ec", ECOLOR},
01272   {"ecolor", ECOLOR},
01273   {"endcolor", ECOLOR},
01274   {"bg", BGRAY},
01275   {"bgray", BGRAY},
01276   {"begingray", BGRAY},
01277   {"eg", EGRAY},
01278   {"egray", EGRAY},
01279   {"endgray", EGRAY},
01280   {"bgcolor", BGCOLOR},
01281   {"bgc", BGCOLOR},
01282   {"bbc", BGCOLOR},
01283   {"bbg", BGCOLOR},
01284   {"pagesize", DVIPDFM_PAGE_SIZE},
01285   {"beginann", BANN},
01286   {"bann", BANN},
01287   {"bannot", BANN},
01288   {"eann", EANN},
01289   {"endann", EANN},
01290   {"eannot", EANN},
01291   {"begintransform", BXFORM},
01292   {"begintrans", BXFORM},
01293   {"btrans", BXFORM},
01294   {"bt", BXFORM},
01295   {"endtransform", EXFORM},
01296   {"endtrans", EXFORM},
01297   {"etrans", EXFORM},
01298   {"et", EXFORM},
01299   {"beginxobj", BXOBJ},
01300   {"bxobj", BXOBJ},
01301   {"endxobj", EXOBJ},
01302   {"exobj", EXOBJ},
01303   {"usexobj", UXOBJ},
01304   {"uxobj", UXOBJ}
01305 };
01306 
01307 static int parse_pdfmark (char **start, char *end)
01308 {
01309   char *save;
01310   int i;
01311   if (verbose) {
01312     fprintf (stderr, "\nparse_pdfmark:");
01313     dump (*start, end);
01314   }
01315   skip_white(start, end);
01316   if (*start >= end) {
01317     fprintf (stderr, "Special ignored...no pdfmark found\n");
01318     return -1;
01319   }
01320   
01321   save = *start;
01322   while (*start < end && isalpha (**start))
01323     (*start)++;
01324   for (i=0; i<sizeof(pdfmarks)/sizeof(struct pdfmark); i++) {
01325     if (*start-save == strlen (pdfmarks[i].string) &&
01326        !strncmp (save, pdfmarks[i].string,
01327                 strlen(pdfmarks[i].string)))
01328       return pdfmarks[i].value;
01329   }
01330   *start = save;
01331   fprintf (stderr, "\nExpecting pdfmark (and didn't find one)\n");
01332   dump(*start, end);
01333   return -1;
01334 }
01335 
01336 struct named_reference 
01337 {
01338   char *name;
01339   char *res_name;
01340   pdf_obj *object_ref;
01341   pdf_obj *object;
01342 } *named_references = NULL;
01343 static unsigned long number_named_references = 0, max_named_objects = 0;
01344 
01345 static void add_reference (char *name, pdf_obj *object, char *res_name)
01346 {
01347   int i;
01348   if (number_named_references >= max_named_objects) {
01349     max_named_objects += NAMED_OBJ_ALLOC_SIZE;
01350     named_references = RENEW (named_references, max_named_objects,
01351                            struct named_reference);
01352   }
01353   for (i=0; i<number_named_references; i++) {
01354     if (!strcmp (named_references[i].name, name)) {
01355       break;
01356     }
01357   }
01358   if (i != number_named_references) {
01359     fprintf (stderr, "\nWarning: @%s: Duplicate named reference ignored\n", name);
01360   }
01361   named_references[number_named_references].name = NEW (strlen
01362                                                  (name)+1,
01363                                                  char);
01364   strcpy (named_references[number_named_references].name, name);
01365   if (res_name != NULL && strlen(res_name) != 0) {
01366     named_references[number_named_references].res_name=NEW(strlen(name)+1, char);
01367     strcpy (named_references[number_named_references].res_name,
01368            res_name);
01369   } else {
01370     named_references[number_named_references].res_name = NULL;
01371   }
01372   named_references[number_named_references].object_ref = pdf_ref_obj(object);
01373   named_references[number_named_references].object = object;
01374   number_named_references+=1;
01375 }
01376 /* The following routine returns copies, not the original object */
01377 static pdf_obj *lookup_reference(char *name)
01378 {
01379   int i;
01380   /* First check for builtins first */
01381   if (!strcmp (name, "ypos")) {
01382     return pdf_new_number(ROUND(dev_phys_y(),0.01));
01383   }
01384   if (!strcmp (name, "xpos")) {
01385     return pdf_new_number(ROUND(dev_phys_x(),0.01));
01386   }
01387   if (!strcmp (name, "thispage")) {
01388     return pdf_doc_this_page_ref();
01389   }
01390   if (!strcmp (name, "prevpage")) {
01391     return pdf_doc_prev_page_ref();
01392   }
01393   if (!strcmp (name, "nextpage")) {
01394     return pdf_doc_next_page_ref();
01395   }
01396   if (!strcmp (name, "pages")) {
01397     return pdf_ref_obj (pdf_doc_page_tree());
01398   }
01399   if (!strcmp (name, "names")) {
01400     return pdf_ref_obj (pdf_doc_names());
01401   }
01402   if (!strcmp (name, "resources")) {
01403     return pdf_ref_obj (pdf_doc_current_page_resources());
01404   }
01405   if (!strcmp (name, "catalog")) {
01406     return pdf_ref_obj (pdf_doc_catalog());
01407   }
01408 
01409   if (strlen (name) > 4 &&
01410       !strncmp (name, "page", 4) &&
01411       is_an_int (name+4)) {
01412     return pdf_doc_ref_page(atoi (name+4));
01413   }
01414   for (i=0; i<number_named_references; i++) {
01415     if (!strcmp (named_references[i].name, name)) {
01416       break;
01417     }
01418   }
01419   if (i == number_named_references)
01420     return NULL;
01421   return (pdf_link_obj (named_references[i].object_ref));
01422 }
01423 
01424 static pdf_obj *lookup_object(char *name)
01425 {
01426   int i;
01427   if (!strcmp (name, "thispage")) {
01428     return pdf_doc_this_page();
01429   }
01430   if (!strcmp (name, "pages")) {
01431     return pdf_doc_page_tree();
01432   }
01433   if (!strcmp (name, "names")) {
01434     return pdf_doc_names();
01435   }
01436   if (!strcmp (name, "resources")) {
01437     return pdf_doc_current_page_resources();
01438   }
01439   if (!strcmp (name, "catalog")) {
01440     return pdf_doc_catalog();
01441   }
01442 
01443   for (i=0; i<number_named_references; i++) {
01444     if (!strcmp (named_references[i].name, name)) {
01445       break;
01446     }
01447   }
01448   if (i == number_named_references)
01449     return NULL;
01450   if (named_references[i].object == NULL)
01451     fprintf (stderr, "lookup_object: Referenced object not defined or already closed\n");
01452   return (named_references[i].object);
01453 }
01454 
01455 char *lookup_ref_res_name(char *name)
01456 {
01457   int i;
01458   for (i=0; i<number_named_references; i++) {
01459   if (named_references[i].name != NULL)
01460     if (named_references[i].name != NULL &&
01461        !strcmp (named_references[i].name, name)) {
01462       break;
01463     }
01464   }
01465   if (i == number_named_references)
01466     return NULL;
01467   if (named_references[i].res_name == NULL)
01468     fprintf (stderr, "lookup_object: Referenced object not useable as a form!\n");
01469   return (named_references[i].res_name);
01470 }
01471 
01472 static void release_reference (char *name)
01473 {
01474   int i;
01475   for (i=0; i<number_named_references; i++) {
01476     if (!strcmp (named_references[i].name, name)) {
01477       break;
01478     }
01479   }
01480   if (i == number_named_references) {
01481     fprintf (stderr, "\nrelease_reference: tried to release nonexistent reference\n");
01482     return;
01483   }
01484   if (named_references[i].object != NULL) {
01485     pdf_release_obj (named_references[i].object);
01486     named_references[i].object = NULL;
01487   }
01488   else
01489     fprintf (stderr, "\nrelease_reference: @%s: trying to close an object twice?\n", name);
01490 }
01491 
01492 
01493 void pdf_finish_specials (void)
01494 {
01495   int i;
01496   /* Flush out any pending objects that weren't properly closeed.
01497      Threads never get closed.  Is this a bug? */
01498   for (i=0; i<number_named_references; i++) {
01499     pdf_release_obj (named_references[i].object_ref);
01500     if (named_references[i].object != NULL) {
01501       pdf_release_obj (named_references[i].object);
01502       named_references[i].object = NULL;
01503     }
01504     if (named_references[i].res_name != NULL) {
01505       RELEASE (named_references[i].res_name);
01506       named_references[i].res_name = NULL;
01507     }
01508     RELEASE (named_references[i].name);
01509   }
01510   if (number_named_references > 0)
01511     RELEASE (named_references);
01512 }
01513 
01514 int pdf_parse_special(char *buffer, UNSIGNED_QUAD size, double
01515                      x_user, double y_user)
01516 {
01517   int pdfmark, result=0;
01518   char *start = buffer, *end;
01519   end = buffer + size;
01520 
01521 #ifdef MEM_DEBUG
01522 MEM_START
01523 #endif
01524 
01525   if (is_pdf_special(&start, end)) {
01526     result = 1; /* This means it really was a pdf special.  It doesn't
01527                  mean it succeeded */
01528     /* Must have a pdf special */
01529     pdfmark = parse_pdfmark(&start, end);
01530     switch (pdfmark) {
01531     case ANN:
01532       do_ann(&start, end);
01533       break;
01534     case BANN:
01535       do_bann(&start, end);
01536       break;
01537     case LINK_ANNOT:
01538       dev_link_annot(1);
01539       break;
01540     case NOLINK_ANNOT:
01541       dev_link_annot(0);
01542       break;
01543     case EANN:
01544       do_eann(&start, end);
01545       break;
01546     case OUTLINE:
01547       do_outline(&start, end);
01548       break;
01549     case ARTICLE:
01550       do_article(&start, end);
01551       break;
01552     case BEAD:
01553       do_bead(&start, end);
01554       break;
01555     case DEST:
01556       do_dest(&start, end);
01557       break;
01558     case DOCINFO:
01559       do_docinfo(&start, end);
01560       break;
01561     case DOCVIEW:
01562       do_docview(&start, end);
01563       break;
01564     case OBJ:
01565       do_obj(&start, end);
01566       break;
01567     case CONTENT:
01568       do_content(&start, end, x_user, y_user);
01569       break;
01570     case PUT:
01571       do_put(&start, end);
01572       break;
01573     case CLOSE:
01574       do_close(&start, end);
01575       break;
01576     case BOP:
01577       do_bop(&start, end);
01578       break;
01579     case EOP:
01580       do_eop(&start, end);
01581       break;
01582     case EPDF:
01583       do_image(&start, end, x_user, y_user);
01584       break;
01585     case IMAGE:
01586       do_image(&start, end, x_user, y_user);
01587       break;
01588     case BGCOLOR:
01589       if (!ignore_colors)
01590        do_bgcolor (&start, end);
01591       break;
01592     case SCOLOR:
01593       if (!ignore_colors)
01594        do_scolor (&start, end);
01595       break;
01596     case BCOLOR:
01597       if (!ignore_colors)
01598        do_bcolor (&start, end);
01599       break;
01600     case ECOLOR:
01601       if (!ignore_colors)
01602        do_ecolor ();
01603       break;
01604     case BGRAY:
01605       do_bgray (&start, end);
01606       break;
01607     case EGRAY:
01608       do_egray ();
01609       break;
01610     case BXFORM:
01611       do_bxform (&start, end, x_user, y_user);
01612       break;
01613     case EXFORM:
01614       do_exform ();
01615       break;
01616     case DVIPDFM_PAGE_SIZE:
01617       do_pagesize(&start, end);
01618       break;
01619     case BXOBJ:
01620       do_bxobj (&start, end, x_user, y_user);
01621       break;
01622     case EXOBJ:
01623       do_exobj ();
01624       break;
01625     case UXOBJ:
01626       do_uxobj (&start, end, x_user, y_user);
01627       break;
01628     default:
01629       dump (start, end);
01630       fprintf (stderr, "Invalid pdf special ignored\n");
01631       break;
01632     }
01633     skip_white (&start, end);
01634     if (start < end) {
01635       fprintf (stderr, "\nUnparsed material at end of special ignored...");
01636       dump (start, end);
01637     }
01638   } else {
01639     result = 0;
01640   }
01641 #ifdef MEM_DEBUG
01642 MEM_END
01643 #endif
01644   return result;
01645 }
01646 
01647 /* Compute a transformation matrix
01648    transformations are applied in the following
01649    order: scaling, rotate, displacement. */
01650 void add_xform_matrix (double xoff, double yoff,
01651                      double xscale, double yscale,
01652                      double rotate) 
01653 {
01654   double c, s;
01655   c = ROUND(cos(rotate),1e-5);
01656   s = ROUND(sin(rotate),1e-5);
01657   sprintf (work_buffer, " %g %g %g %g %.2f %.2f cm",
01658           c*xscale, s*xscale, -s*yscale, c*yscale, xoff, yoff);
01659   pdf_doc_add_to_page (work_buffer, strlen(work_buffer));
01660 }
01661 
01662 
01663 pdf_obj *jpeg_start_image(FILE *file)
01664 {
01665   pdf_obj *xobject, *xobj_dict;
01666   struct jpeg *jpeg;
01667   jpeg = jpeg_open (file);
01668   xobject = pdf_new_stream(0);
01669   xobj_dict = pdf_stream_dict (xobject);
01670   pdf_add_dict (xobj_dict, pdf_new_name ("Width"),
01671               pdf_new_number (jpeg -> width));
01672   pdf_add_dict (xobj_dict, pdf_new_name ("Height"),
01673               pdf_new_number (jpeg -> height));
01674   pdf_add_dict (xobj_dict, pdf_new_name ("BitsPerComponent"),
01675               pdf_new_number (jpeg -> bits_per_color));
01676   if (jpeg->colors == 1)
01677     pdf_add_dict (xobj_dict, pdf_new_name ("ColorSpace"),
01678                 pdf_new_name ("DeviceGray"));
01679   if (jpeg->colors > 1)
01680     pdf_add_dict (xobj_dict, pdf_new_name ("ColorSpace"),
01681                 pdf_new_name ("DeviceRGB"));
01682   pdf_add_dict (xobj_dict, pdf_new_name ("Filter"),
01683               pdf_new_name ("DCTDecode"));
01684   {
01685     int length;
01686     rewind (jpeg -> file);
01687     while ((length = fread (work_buffer, sizeof (char),
01688                          WORK_BUFFER_SIZE, jpeg -> file)) > 0) {
01689       pdf_add_stream (xobject, work_buffer, length);
01690     }
01691   }
01692   jpeg_close (jpeg);
01693   return (xobject);
01694 }
01695 
01696 void pdf_scale_image (struct xform_info *p)
01697 {
01698   double xscale = 1.0, yscale = 1.0, nat_width, nat_height;
01699   if (p->user_bbox) {  /* Did user override natural bounding box */
01700     nat_width  = p->u_urx - p->u_llx;
01701     nat_height = p->u_ury - p->u_lly;
01702   } else {           /* If not, use media width and height */
01703     nat_width  = p->c_urx - p->c_llx;
01704     nat_height = p->c_ury - p->c_lly;
01705     p->u_llx = 0.0;    /* Initialize u_llx and u_lly because they
01706     p->u_lly = 0.0;       are used to set the origin within the crop
01707                        area */
01708   }
01709   if (p->clip && p->user_bbox) {  /* Clip to user specified bbox? */
01710     p->c_urx = p->u_urx;
01711     p->c_ury = p->u_ury;
01712     p->c_llx = p->u_llx;
01713     p->c_lly = p->u_lly;
01714   }
01715   if (p->scale != 0) {
01716     xscale = p->scale;
01717     yscale = p->scale;
01718   }
01719   if (p->xscale != 0) {
01720     xscale = p->xscale;
01721   }
01722   if (p->yscale != 0) {
01723     yscale = p->yscale;
01724   }
01725   if (p->width != 0.0 && nat_width != 0.0) {
01726     xscale = p->width/nat_width;
01727     if (p->height == 0.0)
01728       yscale = xscale;
01729   }
01730   if (p->height != 0.0 && nat_height != 0.0) {
01731     yscale = p->height/nat_height;
01732     if (p->width == 0.0)
01733       xscale = yscale;
01734   }
01735   /* We overwrite p->xscale and p->yscale to pass values back to
01736      caller to user */
01737   p->xscale = xscale;
01738   p->yscale = yscale;
01739   return;
01740 }
01741 
01742 static void finish_image (pdf_obj *image_res, struct xform_info *p,
01743                        char *res_name)
01744 {
01745   pdf_obj *image_dict;
01746   image_dict = pdf_stream_dict (image_res);
01747   pdf_add_dict (image_dict, pdf_new_name ("Name"),
01748               pdf_new_name (res_name));
01749   pdf_add_dict (image_dict, pdf_new_name ("Type"),
01750               pdf_new_name ("XObject"));
01751   pdf_add_dict (image_dict, pdf_new_name ("Subtype"),
01752               pdf_new_name ("Image"));
01753   { /* Compute new xscale and yscale based on user specified values
01754        and image dimensions (recall that PDF changes dimensions of all
01755        images to 1x1 :-( */
01756     int width, height;
01757     width = pdf_number_value(pdf_lookup_dict (image_dict, "Width"));
01758     height = pdf_number_value(pdf_lookup_dict (image_dict, "Height"));
01759     /* Following routine sets xscale and yscale to a fraction of
01760        their natural values */
01761     p->c_llx = 0;
01762     p->c_lly = 0;
01763     p->c_urx = width*(72.0/100.0);
01764     p->c_ury = height*(72.0/100.0);
01765     if (p->user_bbox) {
01766       fprintf (stderr,
01767               "\nWarning:  Ignoring user specified bounding box for raster image.\n");
01768       p->user_bbox = 0;
01769     }
01770     pdf_scale_image (p);
01771     /* Since bitmapped images are always 1x1 in PDF, we must rescale
01772        again */
01773     p->xscale *= width*(72.0/100.0);
01774     p->yscale *= height*(72.0/100.0);
01775   }
01776   return;
01777 }
01778 
01779 static void do_bxobj (char **start, char *end, double x_user, double y_user)
01780 {
01781   char *objname = NULL;
01782   static unsigned long num_forms = 0;
01783   pdf_obj *xobject = NULL;
01784   struct xform_info *p = NULL;
01785   int errors = 0;
01786   skip_white(start, end);
01787   /* If there's an object name, check dimensions */
01788   if ((objname = parse_opt_ident(start, end)) != NULL) {
01789     p = new_xform_info ();
01790     skip_white(start, end);
01791     if (!parse_dimension(start, end, p)) {
01792       fprintf (stderr, "\nFailed to find a valid dimension.\n");
01793       errors = 1;
01794     }
01795     if (p->scale != 0.0 || p->xscale != 0.0 || p->yscale != 0.0) {
01796       fprintf (stderr, "\nScale information is meaningless for form xobjects\n");
01797       errors = 1;
01798     }
01799     if (p->width == 0.0 || p->depth+p->height == 0.0) {
01800       fprintf (stderr, "\nSpecial: bxobj: Bounding box has a zero dimension\n");
01801       fprintf (stderr, "width:%g, height:%g, depth:%g\n", p->width,
01802               p->height, p->depth);
01803       errors = 1;
01804     }
01805     /* If there's an object name and valid dimension, add it to the
01806        tables */
01807     if (!errors) {
01808       char *res_name;
01809       sprintf (work_buffer, "Fm%ld", ++num_forms);
01810       res_name = NEW (strlen(work_buffer)+1, char);
01811       strcpy (res_name, work_buffer);
01812       xobject = begin_form_xobj (x_user, y_user,
01813                              x_user, y_user-p->depth,
01814                              x_user+p->width, y_user+p->height,
01815                              res_name);
01816       add_reference (objname, xobject, res_name);
01817       RELEASE (res_name);
01818     /* Next line has Same explanation as for do_ann. */
01819       release_reference (objname);
01820     }
01821     release_xform_info (p);
01822     RELEASE (objname);
01823   }
01824   else {
01825     fprintf (stderr, "\nSpecial: beginxobj:  A form XObject must be named\n");
01826   }
01827   return;
01828 }
01829 
01830 
01831 static void do_exobj (void)
01832 {
01833   end_form_xobj();
01834 }
01835 
01836 static void do_uxobj (char **start, char *end, double x_user, double y_user)
01837 {
01838   char *objname, *res_name = NULL;
01839   pdf_obj *xobj_res = NULL;
01840   skip_white (start, end);
01841   if (((objname = parse_opt_ident(start, end)) != NULL) &&
01842       ((res_name = lookup_ref_res_name (objname)) != NULL) &&
01843       ((xobj_res = lookup_reference (objname)) != NULL)) {
01844     sprintf (work_buffer, " q 1 0 0 1 %.2f %.2f cm /%s Do Q", x_user, y_user, res_name);
01845     pdf_doc_add_to_page (work_buffer, strlen(work_buffer));
01846     pdf_doc_add_to_page_xobjects (res_name, xobj_res);
01847   }
01848   if (objname != NULL && (res_name == NULL || xobj_res == NULL)) {
01849       fprintf (stderr, "\nSpecial: usexobj:  Specified XObject doesn't exist: %s\n", 
01850               objname);
01851   }
01852   if (objname != NULL)
01853     RELEASE (objname);
01854   return;
01855 }
01856