Back to index

tetex-bin  3.0
pdfdev.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 #include "config.h"
00026 #include <math.h>
00027 #include <string.h>
00028 #include <ctype.h>
00029 #include <stdlib.h>
00030 #include "system.h"
00031 #include "mem.h"
00032 #include "error.h"
00033 #include "mfileio.h"
00034 #include "numbers.h"
00035 #include "dvi.h"
00036 #include "tfm.h"
00037 #include "pdfdev.h"
00038 #include "pdfdoc.h"
00039 #include "pdfobj.h"
00040 #include "type1.h"
00041 #include "ttf.h"
00042 #include "pkfont.h"
00043 #include "pdfspecial.h"
00044 #include "pdfparse.h"
00045 #include "tpic.h"
00046 #include "htex.h"
00047 #include "mpost.h"
00048 #include "psspecial.h"
00049 #include "colorsp.h"
00050 #include "pdflimits.h"
00051 #include "twiddle.h"
00052 #include "encodings.h"
00053 
00054 /* Internal functions */
00055 static void dev_clear_color_stack (void);
00056 static void dev_clear_xform_stack (void);
00057 
00058 double hoffset = 72.0, voffset=72.0;
00059 
00060 static double dvi2pts = 0.0;
00061 
00062  /* Acrobat doesn't seem to like coordinate systems
00063     that involve scalings around 0.01, so we use
00064     a scaline of 1.0.  In other words, device units = pts */ 
00065 /* Following dimensions in virtual device coordinates,
00066 which are points */
00067 
00068 static double page_width=612.0, page_height=792.0;
00069 int page_size_readonly = 0;
00070 
00071 void dev_set_page_size (double width, double height)
00072 {
00073   if (page_size_readonly) {
00074     fprintf (stderr, "\nSorry.  Too late to change page size\n");
00075   } else {
00076     page_width = width;
00077     page_height = height;
00078   }
00079 }
00080 
00081 double dev_page_width(void)
00082 {
00083   page_size_readonly = 1;
00084   return page_width;
00085 }
00086 
00087 double dev_page_height(void)
00088 {
00089   page_size_readonly = 1;
00090   return page_height;
00091 }
00092 
00093 static int debug = 0, verbose = 0;
00094 
00095 void dev_set_verbose (void)
00096 {
00097   verbose += 1;
00098 }
00099 void dev_set_debug (void)
00100 {
00101   debug = 1;
00102 }
00103 
00104 #define GRAPHICS_MODE 1
00105 #define TEXT_MODE 2
00106 #define STRING_MODE 3
00107 
00108 int motion_state = GRAPHICS_MODE; /* Start in graphics mode */
00109 
00110 #define FORMAT_BUF_SIZE 4096
00111 static char format_buffer[FORMAT_BUF_SIZE];
00112 
00113 /* The coordinate system in the pdf file is setup so that 1 unit in the
00114    PDF content stream's coordinate system represents 65,800 DVI units.
00115    This choice was made so that a PDF coordinate represented only
00116    to the hundredths place represents an exact integer number of DVI units.
00117    Doing so allows relative motions in a PDF file to be known
00118    precisely in DVI units, and allows us to keep track of relative motions
00119    using integer arithmetic.  Relative motions in the PDF file are
00120    represented in decimal with no more than two digits after the decimal
00121    point.  In the PDF stream, a PDF user coordinate of 0.01 represents
00122    exactly 658 DVI units.
00123 
00124    The "pdfdev" module is the only module that knows the
00125    relationship between PDF units and true points.  It provides
00126    pdf_dev_scale() to inform other modules of the scale.
00127    Modules that render PDF marking operators (such as those
00128    that render tpic specials or PS specials) need this value.
00129    The overhead of this call is a slight performance hit for
00130    rendering images, but allows dvipdfm to set text blazingly fast
00131   
00132  
00133    Some constants related to this representation follow: */
00134 
00135 #define PDF_U 65800L /* Number of DVI units in a PDF unit */
00136 #define CENTI_PDF_U 658     /* Number of DVI units in a centi PDF unit */
00137 
00138 /* pdf_dev_scale() returns the factor by which a PDF unit
00139  * must be multiplied to produce an Adobe point (big point) */
00140 
00141 double pdf_dev_scale (void)
00142 {
00143   return 65800.0*dvi2pts;
00144 }
00145 
00146 
00147  /* Device coordinates are relative to upper left of page.  One of the
00148     first things appearing in the page stream is a coordinate transformation
00149     matrix that forces this to be true.  This coordinate
00150     transformation is the only place where the paper size is required.
00151     Unfortunately, positive is up, which doesn't agree with TeX's convention.  */
00152 
00153 static spt_t text_xorigin = 0, text_yorigin = 0,
00154   text_offset = 0;
00155 double text_slant = 0.0, text_extend = 1.0;
00156 
00157 unsigned  num_dev_fonts = 0;
00158 unsigned  num_phys_fonts = 0;
00159 int current_font = -1;
00160 
00161 #define PHYSICAL 1
00162 #define VIRTUAL 2
00163 
00164 #define TYPE1 1
00165 #define PK 2
00166 #define TRUETYPE 3
00167 
00168 #define DEFAULT_MAP_FILE "dvipdfm.map"
00169 
00170 static struct dev_font {
00171   char short_name[7];       /* Needs to be big enough to hold name "Fxxx"
00172                         where xxx is number of largest font */
00173   unsigned char used_on_this_page, format;
00174   char *tex_name;
00175   spt_t sptsize;
00176   pdf_obj *font_resource;
00177   char *used_chars;
00178   double extend, slant;
00179   int remap;
00180 } *dev_font = NULL;
00181 
00182 static unsigned max_device_fonts = 0;
00183 
00184 void dev_fonts_need (unsigned n)
00185 {
00186   if (n > max_device_fonts) {
00187     max_device_fonts = MAX(max_device_fonts+MAX_FONTS, n);
00188     dev_font = RENEW (dev_font, max_device_fonts, struct dev_font);
00189   }
00190 }
00191 
00192 struct map_record {
00193    char *tex_name, *font_name, *enc_name;
00194    double slant, extend;
00195    int remap;
00196 }  *font_map = NULL;
00197 
00198 unsigned int num_font_map = 0, max_font_map = 0;
00199 
00200 static void font_maps_need (int n)
00201 {
00202   if (n > max_font_map) {
00203     max_font_map = MAX(max_font_map+MAX_FONTS, n);
00204     font_map = RENEW (font_map, max_font_map, struct map_record);
00205   }
00206   return;
00207 }
00208 
00209 static void init_map_record (struct map_record *r) 
00210 {
00211   r->tex_name = NULL;
00212   r->enc_name = NULL;
00213   r->font_name = NULL;
00214   r->slant = 0.0;
00215   r->extend = 1.0;
00216   r->remap = 0.0;
00217   return;
00218 }
00219 
00220 static void release_map_record (struct map_record *r)
00221 {
00222   if (r && r->tex_name)
00223     RELEASE (r->tex_name);
00224   if (r && r->enc_name)
00225     RELEASE (r->enc_name);
00226   if (r && r->font_name)
00227     RELEASE (r->font_name);
00228 }
00229 
00230 
00231 static void fill_in_defaults (struct map_record *this_map_record)
00232 {
00233   if (this_map_record -> enc_name != NULL && 
00234       (!strcmp (this_map_record->enc_name, "default") ||
00235        !strcmp (this_map_record->enc_name, "none"))) {
00236     RELEASE(this_map_record->enc_name);
00237     this_map_record -> enc_name = NULL;
00238   }
00239   if (this_map_record -> font_name != NULL && 
00240       (!strcmp (this_map_record->font_name, "default") ||
00241        !strcmp (this_map_record->font_name, "none"))) {
00242     RELEASE(this_map_record->font_name);
00243     this_map_record -> font_name = NULL;
00244   }
00245   /* We *must* fill in a font_name either explicitly or by default
00246      (the tex_name) */
00247   if (this_map_record -> font_name == NULL) {
00248     this_map_record -> font_name = NEW (strlen(this_map_record->tex_name)+1, char);
00249     strcpy (this_map_record->font_name, this_map_record->tex_name);
00250   }
00251   return;
00252 }
00253 
00254 void dev_read_mapfile (char *filename)
00255 {
00256   FILE *mapfile;
00257   char *full_map_filename, *start = NULL, *end, *tex_name;
00258   if (verbose > 0)
00259     fprintf (stderr, "<%s", filename);
00260   full_map_filename = kpse_find_file (filename, kpse_fontmap_format,
00261                                   0);
00262   if (full_map_filename == NULL || 
00263       (mapfile = MFOPEN (full_map_filename, FOPEN_R_MODE)) == NULL) {
00264     fprintf (stderr, "Warning:  Couldn't open font map file %s\n", filename);
00265     mapfile = NULL;
00266   }
00267   if (mapfile) {
00268     while ((start = mfgets (work_buffer, WORK_BUFFER_SIZE, mapfile)) !=
00269           NULL) {
00270       end = work_buffer + strlen(work_buffer);
00271       skip_white (&start, end);
00272       if (start >= end)
00273        continue;
00274       if (*start == '%')
00275        continue;
00276       if ((tex_name = parse_ident (&start, end)) == NULL)
00277        continue;
00278       /* Parse record line in map file.  First two fields (after TeX font
00279         name) are position specific.  Arguments start at the first token
00280         beginning with a  '-' */
00281       font_maps_need (num_font_map+1);
00282       init_map_record(font_map+num_font_map);
00283       font_map[num_font_map].tex_name = tex_name;
00284       skip_white (&start, end);
00285       if (*start != '-') {
00286        font_map[num_font_map].enc_name = parse_ident (&start, end); /* May be null */  
00287        skip_white (&start, end);
00288       }
00289       if (*start != '-') {
00290        font_map[num_font_map].font_name = parse_ident (&start, end); /* May be null */
00291        skip_white (&start, end);
00292       }
00293       /* Parse any remaining arguments */ 
00294       while (start+1 < end && *start == '-') {
00295        char *number;
00296        switch (*(start+1)) {
00297        case 's': /* Slant option */
00298          start += 2;
00299          skip_white (&start, end);
00300          if (start < end && 
00301              (number = parse_number(&start, end))) {
00302            font_map[num_font_map].slant = atof (number);
00303            RELEASE (number);
00304          } else {
00305            fprintf (stderr, "\n\nMissing slant value in map file for %s\n\n",
00306                    tex_name);
00307          }
00308          break;
00309        case 'e': /* Extend option */
00310          start += 2;
00311          skip_white (&start, end);
00312          if (start < end && 
00313              (number = parse_number(&start, end))) {
00314            font_map[num_font_map].extend = atof (number);
00315            RELEASE (number);
00316          } else {
00317            fprintf (stderr, "\n\nMissing extend value in map file for %s\n\n",
00318                    tex_name);
00319          }
00320          break;
00321        case 'r': /* Remap option */
00322          start += 2;
00323          skip_white (&start, end);
00324          font_map[num_font_map].remap = 1;
00325          break;
00326        default: 
00327          fprintf (stderr, "\n\nWarning: Unrecognized option in map file %s: -->%s<--\n\n",
00328                  tex_name, start);
00329          start = end;
00330        }
00331        skip_white (&start, end);
00332       }
00333       fill_in_defaults (font_map+num_font_map);
00334       num_font_map += 1;
00335     }
00336     MFCLOSE (mapfile);
00337     if (verbose > 0)
00338       fprintf (stderr, ">");
00339   }
00340   return;
00341 }
00342 
00343 struct map_record *get_map_record (const char *tex_name)
00344 {
00345   struct map_record *result = NULL;
00346   int tried_default = 0;
00347   unsigned int i;
00348   if (!font_map && !tried_default) {
00349     dev_read_mapfile (DEFAULT_MAP_FILE);
00350     tried_default = 1;
00351   }
00352   if (!font_map)
00353     return result;
00354   for (i=0; i<num_font_map; i++) {
00355     if (!strcmp (font_map[i].tex_name, tex_name)) {
00356       result = font_map+i;
00357       break;
00358     }
00359   }
00360   return result;
00361 }
00362 
00363 /*
00364  * reset_text_state() outputs a BT
00365  * and does any necessary coordinate transformations
00366  * to get ready to ship out text
00367  */
00368 
00369 static void reset_text_state(void)
00370 {
00371   int len;
00372   text_xorigin = 0;
00373   text_yorigin = 0;
00374   text_offset = 0;
00375   /* 
00376    * We need to reset the line matrix to handle slanted fonts 
00377    */
00378   sprintf (format_buffer, " BT");
00379   len = strlen (format_buffer);
00380   if (current_font >= 0 && /* If not at top of page */
00381       (dev_font[current_font].slant != 0.0 ||
00382        dev_font[current_font].extend != 1.0)) {
00383     sprintf (format_buffer+len, " %.7g 0 %.3g 1 ",
00384             dev_font[current_font].extend,
00385             dev_font[current_font].slant);
00386     len += strlen (format_buffer+len);
00387     len += centi_u_to_a (format_buffer+len, IDIVRND (text_xorigin, CENTI_PDF_U));
00388     format_buffer[len++] = ' ';
00389     len += centi_u_to_a (format_buffer+len, IDIVRND (text_yorigin, CENTI_PDF_U));
00390     format_buffer[len++] = ' ';
00391     format_buffer[len++] = 'T';
00392     format_buffer[len++] = 'm';
00393   }
00394   pdf_doc_add_to_page (format_buffer, len);
00395 }
00396 
00397 static void text_mode (void)
00398 {
00399   switch (motion_state) {
00400   case STRING_MODE:
00401     pdf_doc_add_to_page (")]TJ", 4);
00402   case TEXT_MODE:
00403     break;
00404   case GRAPHICS_MODE:
00405     reset_text_state();
00406     break;
00407   }
00408   motion_state = TEXT_MODE;
00409   text_offset = 0;
00410   return;
00411 }
00412 
00413 void graphics_mode (void)
00414 {
00415   switch (motion_state) {
00416   case GRAPHICS_MODE:
00417     break;
00418   case STRING_MODE:
00419     pdf_doc_add_to_page (")]TJ", 4);
00420   case TEXT_MODE:
00421     pdf_doc_add_to_page (" ET", 3);
00422     break;
00423   }
00424   motion_state = GRAPHICS_MODE;
00425   return;
00426 }
00427 
00428 static void string_mode (spt_t xpos, spt_t ypos, double slant, double extend)
00429 {
00430   spt_t delx, dely;
00431   int len = 0;
00432   switch (motion_state) {
00433   case STRING_MODE:
00434     break;
00435   case GRAPHICS_MODE:
00436     reset_text_state();
00437     /* Fall through now... */
00438     /* Following may be necessary after a rule (and also after
00439        specials) */
00440   case TEXT_MODE:
00441     delx = xpos - text_xorigin;
00442     {
00443       spt_t rounded_delx, desired_delx;
00444       spt_t rounded_dely, desired_dely;
00445       spt_t dvi_xerror, dvi_yerror;
00446 
00447       /* First round dely (it is needed for delx) */
00448       dely = ypos - text_yorigin;
00449       desired_dely = dely;
00450       rounded_dely = IDIVRND(desired_dely, CENTI_PDF_U) * CENTI_PDF_U;
00451       /* Next round delx, precompensating for line transformation matrix */
00452       desired_delx = (delx-desired_dely*slant)/extend;
00453       rounded_delx = IDIVRND(desired_delx, CENTI_PDF_U) * CENTI_PDF_U;
00454       /* Estimate errors in DVI units */
00455       dvi_yerror = (desired_dely - rounded_dely);
00456       dvi_xerror = (extend*(desired_delx - rounded_delx)+slant*dvi_yerror);
00457       format_buffer[len++] = ' ';
00458       len += centi_u_to_a (format_buffer+len, rounded_delx/CENTI_PDF_U);
00459       format_buffer[len++] = ' ';
00460       len += centi_u_to_a (format_buffer+len, rounded_dely/CENTI_PDF_U);
00461       pdf_doc_add_to_page (format_buffer, len);
00462       len = 0;
00463       pdf_doc_add_to_page (" TD[(", 5);
00464       text_xorigin = xpos-dvi_xerror;
00465       text_yorigin = ypos-dvi_yerror;
00466     }
00467     text_offset = 0;
00468     break;
00469   }
00470   motion_state = STRING_MODE;
00471   return;
00472 }
00473 
00474 /* The purpose of the following routine is to force a Tf only
00475    when it's actually necessary.  This became a problem when the
00476    VF code was added.  The VF spec says to instantiate the
00477    first font contained in the VF file before drawing a virtual
00478    character.  However, that font may not be used for
00479    many characters (e.g. small caps fonts).  For this reason, 
00480    dev_select_font() should not force a "physical" font selection.
00481    This routine prevents a PDF Tf font selection until there's
00482    really a character in that font.  */
00483 
00484 static void dev_set_font (int font_id)
00485 {
00486   int len = 0;
00487   text_mode();
00488   sprintf (format_buffer, "/%s ", dev_font[font_id].short_name);
00489   len = strlen (format_buffer);
00490   len += centi_u_to_a (format_buffer+len, IDIVRND(dev_font[font_id].sptsize, CENTI_PDF_U));
00491   format_buffer[len++] = ' ';
00492   format_buffer[len++] = 'T';
00493   format_buffer[len++] = 'f';
00494   if (dev_font[font_id].slant != text_slant ||
00495       dev_font[font_id].extend != text_extend) {
00496     sprintf (format_buffer+len, " %.7g 0 %.3g 1 ",
00497             dev_font[font_id].extend,
00498             dev_font[font_id].slant);
00499     len += strlen (format_buffer+len);
00500     len += centi_u_to_a (format_buffer+len, IDIVRND(text_xorigin, CENTI_PDF_U));
00501     format_buffer[len++] = ' ';
00502     len += centi_u_to_a (format_buffer+len, IDIVRND(text_yorigin, CENTI_PDF_U));
00503     format_buffer[len++] = ' ';
00504     format_buffer[len++] = 'T';
00505     format_buffer[len++] = 'm';
00506      /* There's no longer any uncertainty about where we are */
00507     text_slant = dev_font[font_id].slant;
00508     text_extend = dev_font[font_id].extend;
00509   }
00510   pdf_doc_add_to_page (format_buffer, len);
00511   /* Add to Font list in Resource dictionary for this page */
00512   if (!dev_font[font_id].used_on_this_page) { 
00513     pdf_doc_add_to_page_fonts (dev_font[font_id].short_name,
00514                             pdf_link_obj(dev_font[font_id].font_resource));
00515     dev_font[font_id].used_on_this_page = 1;
00516   }
00517   current_font = font_id;
00518   return;
00519 }
00520 
00521 void dev_set_string (spt_t xpos, spt_t ypos, unsigned char *s, int
00522                    length, spt_t width, int font_id)
00523 {
00524   int len = 0;
00525   long kern;
00526   
00527   if (font_id != current_font)
00528     dev_set_font(font_id); /* Force a Tf since we are actually trying
00529                             to write a character */
00530   /* Kern is in units of character units, i.e., 1000 = 1 em. */
00531   /* The following formula is of the form a*x/b where a, x, and b are
00532      long integers.  Since in integer arithmetic (a*x) could overflow
00533      and a*(x/b) would not be accurate, we use floating point
00534      arithmetic rather than trying to do this all with integer
00535      arithmetic. */
00536   kern =
00537     (1000.0/dev_font[font_id].extend*(text_xorigin+text_offset-xpos))/dev_font[font_id].sptsize;
00538   
00539   if (labs(ypos-text_yorigin) > CENTI_PDF_U || /* CENTI_PDF_U is smallest resolvable dimension */
00540       abs(kern) > 32000) { /* Some PDF Readers fail on large kerns */
00541     text_mode();
00542     kern = 0;
00543   }
00544   if (motion_state != STRING_MODE)
00545     string_mode(xpos, ypos, dev_font[font_id].slant, dev_font[font_id].extend);
00546   else if (kern != 0) {
00547     text_offset -=
00548       kern*dev_font[font_id].extend*(dev_font[font_id].sptsize/1000.0);
00549     /* Same issues as earlier.  Use floating point for simplicity */
00550     /* This routine needs to be fast, so we don't call sprintf() or
00551        strcpy() */
00552     format_buffer[len++] = ')';
00553     len += inttoa (format_buffer+len, kern);
00554     format_buffer[len++] = '(';
00555     pdf_doc_add_to_page (format_buffer, len);
00556     len = 0;
00557   }
00558   len += pdfobj_escape_str (format_buffer+len, FORMAT_BUF_SIZE-len, s,
00559                          length,
00560                          dev_font[font_id].remap);
00561   pdf_doc_add_to_page (format_buffer, len);
00562 
00563   /* Record characters used for partial font embedding */
00564   /* Fonts without pfbs don't get counted and have used_chars set to
00565      null */
00566   if (dev_font[font_id].used_chars != NULL) {
00567     int i;
00568     if (dev_font[font_id].remap)
00569       for (i=0; i<length; i++){
00570        (dev_font[font_id].used_chars)[twiddle(s[i])] = 1;
00571       }
00572     else 
00573       for (i=0; i<length; i++){
00574        (dev_font[font_id].used_chars)[s[i]] = 1;
00575       }
00576   }
00577   text_offset += width;
00578 }
00579 
00580 void dev_init (double scale, double x_offset, double y_offset)
00581 {
00582   dvi2pts = scale;
00583   hoffset = x_offset;
00584   voffset = y_offset;
00585   if (debug) fprintf (stderr, "dev_init:\n");
00586   graphics_mode();
00587   dev_clear_color_stack();
00588   dev_clear_xform_stack();
00589 }
00590 
00591 void dev_close (void)
00592 {
00593   /* Set page origin now that user has had plenty of time
00594      to set page size */
00595   pdf_doc_set_origin((double) hoffset, (double)
00596                    dev_page_height()-voffset);
00597 }
00598 
00599 void dev_add_comment (char *comment)
00600 {
00601   pdf_doc_creator (comment);
00602 }
00603 
00604 
00605 /*  BOP, EOP, and FONT section.
00606    BOP and EOP manipulate some of the same data structures
00607    as the font stuff */ 
00608 
00609 #define GRAY 1
00610 #define RGB 2
00611 #define CMYK 3
00612 struct color {
00613   int colortype;
00614   double c1, c2, c3, c4;
00615 } colorstack[MAX_COLORS], background = {GRAY, 1.0, 1.0, 1.0, 1.0},
00616     default_color = {GRAY, 0.0, 0.0, 0.0, 0.0};
00617 
00618 #include "colors.h"
00619 
00620 struct color color_by_name (char *s) 
00621 {
00622   int i;
00623   struct color result;
00624   for (i=0; i<sizeof(colors_by_name)/sizeof(colors_by_name[0]); i++) {
00625     if (!strcmp (s, colors_by_name[i].name)) {
00626       break;
00627     }
00628   }
00629   if (i == sizeof(colors_by_name)/sizeof(colors_by_name[0])) {
00630     fprintf (stderr, "Color \"%s\" no known.  Using \"Black\" instead.\n", s);
00631     result = default_color;
00632   } else {
00633     result = colors_by_name[i].color;
00634   }
00635   return result;
00636 }
00637 
00638 static int num_colors = 0;
00639 
00640 static void fill_page (void)
00641 {
00642   if (background.colortype == GRAY && background.c1 == 1.0)
00643     return;
00644   switch (background.colortype) {
00645   case GRAY:
00646     sprintf (format_buffer, " q 0 w %.3f g %.3f G", background.c1, background.c1);
00647     break;
00648   case RGB:
00649     sprintf (format_buffer, " q 0 w %.3f %.3f %.3f rg %.3f %.3f %.3f RG",
00650             background.c1, background.c2, background.c3,
00651             background.c1, background.c2, background.c3);
00652     break;
00653   case CMYK:
00654     sprintf (format_buffer, " q 0 w %.3f %.3f %.3f %.3f k %.3f %.3f %.3f %.3f K ",
00655             background.c1, background.c2, background.c3, background.c4,
00656             background.c1, background.c2, background.c3, background.c4);
00657     break;
00658   }
00659   pdf_doc_this_bop (format_buffer, strlen(format_buffer));
00660   sprintf (format_buffer,
00661           " 0 0 m %.2f 0 l %.2f %.2f l 0 %.2f l b Q ",
00662           dev_page_width(), dev_page_width(), dev_page_height(),
00663           dev_page_height());
00664   pdf_doc_this_bop (format_buffer, strlen(format_buffer));
00665   return;
00666 }
00667 
00668 void dev_bg_rgb_color (double r, double g, double b)
00669 {
00670   background.colortype = RGB;
00671   background.c1 = r;
00672   background.c2 = g;
00673   background.c3 = b;
00674   return;
00675 }
00676 
00677 void dev_bg_cmyk_color (double c, double m, double y, double k)
00678 {
00679   background.colortype = CMYK;
00680   background.c1 = c;
00681   background.c2 = m;
00682   background.c3 = y;
00683   background.c4 = k;
00684   return;
00685 }
00686 
00687 void dev_bg_gray (double value)
00688 {
00689   background.colortype = GRAY;
00690   background.c1 = value;
00691   return;
00692 }
00693 
00694 void dev_bg_named_color (char *s)
00695 {
00696   struct color color = color_by_name (s);
00697   switch (color.colortype) {
00698   case GRAY:
00699     dev_bg_gray (color.c1);
00700     break;
00701   case RGB:
00702     dev_bg_rgb_color (color.c1, color.c2, color.c3);
00703     break;
00704   case CMYK:
00705     dev_bg_cmyk_color (color.c1, color.c2, color.c3, color.c4);
00706     break;
00707   }
00708   return;
00709 }
00710 
00711 static void dev_clear_color_stack (void)
00712 {
00713   num_colors = 0;
00714   return;
00715 }
00716 static void dev_set_color (struct color color)
00717 {
00718   switch (color.colortype) {
00719     int len;
00720   case RGB:
00721     sprintf (format_buffer, " %.2f %.2f %.2f",
00722             color.c1,
00723             color.c2,
00724             color.c3);
00725     len = strlen (format_buffer);
00726     pdf_doc_add_to_page (format_buffer, len);
00727     pdf_doc_add_to_page (" rg", 3);
00728     pdf_doc_add_to_page (format_buffer, len);
00729     pdf_doc_add_to_page (" RG", 3);
00730     break;
00731   case CMYK:
00732     sprintf (format_buffer, " %.2f %.2f %.2f %.2f",
00733             color.c1,
00734             color.c2,
00735             color.c3,
00736             color.c4);
00737     len = strlen (format_buffer);
00738     pdf_doc_add_to_page (format_buffer, len);
00739     pdf_doc_add_to_page (" k", 2);
00740     pdf_doc_add_to_page (format_buffer, len);
00741     pdf_doc_add_to_page (" K ", 3);
00742     break;
00743   case GRAY:
00744     sprintf (format_buffer, " %.2f", color.c1);
00745     len = strlen (format_buffer);
00746     pdf_doc_add_to_page (format_buffer, len);
00747     pdf_doc_add_to_page (" g", 2);
00748     pdf_doc_add_to_page (format_buffer, len);
00749     pdf_doc_add_to_page (" G", 2);
00750     break;
00751   default:
00752     ERROR ("Internal error: Invalid default color item");
00753   }
00754 }
00755 
00756 
00757 void dev_do_color (void) 
00758 {
00759   if (num_colors == 0) {
00760     dev_set_color (default_color);
00761   } else {
00762     dev_set_color (colorstack[num_colors-1]);
00763   }
00764   return;
00765 }
00766 
00767 void dev_set_def_rgb_color (double r, double g, double b)
00768 {
00769   default_color.c1 = r;
00770   default_color.c2 = g;
00771   default_color.c3 = b;
00772   default_color.colortype = RGB;
00773   dev_do_color();
00774   return;
00775 }
00776 
00777 void dev_set_def_gray (double g) 
00778 {
00779   default_color.c1 = g;
00780   default_color.colortype = GRAY;
00781   dev_do_color();
00782   return;
00783 }
00784 
00785 void dev_set_def_named_color (char *s)
00786 {
00787   struct color color = color_by_name (s);
00788   switch (color.colortype) {
00789   case GRAY:
00790     dev_set_def_gray (color.c1);
00791     break;
00792   case RGB:
00793     dev_set_def_rgb_color (color.c1, color.c2, color.c3);
00794     break;
00795   case CMYK:
00796     dev_set_def_cmyk_color (color.c1, color.c2, color.c3, color.c4);
00797     break;
00798   }
00799   return;
00800 }
00801 
00802 void dev_set_def_cmyk_color (double c, double m, double y, double k)
00803 {
00804   default_color.c1 = c;
00805   default_color.c2 = m;
00806   default_color.c3 = y;
00807   default_color.c4 = k;
00808   default_color.colortype = CMYK;
00809   dev_do_color();
00810   return;
00811 }
00812 
00813 void dev_begin_named_color (char *s)
00814 {
00815   struct color color = color_by_name (s);
00816   switch (color.colortype) {
00817   case GRAY:
00818     dev_begin_gray (color.c1);
00819     break;
00820   case RGB:
00821     dev_begin_rgb_color (color.c1, color.c2, color.c3);
00822     break;
00823   case CMYK:
00824     dev_begin_cmyk_color (color.c1, color.c2, color.c3, color.c4);
00825     break;
00826   }
00827   return;
00828 }
00829 
00830 void dev_begin_rgb_color (double r, double g, double b)
00831 {
00832   if (num_colors >= MAX_COLORS) {
00833     fprintf (stderr, "\ndev_set_color:  Exceeded depth of color stack\n");
00834     return;
00835   }
00836   colorstack[num_colors].c1 = r;
00837   colorstack[num_colors].c2 = g;
00838   colorstack[num_colors].c3 = b;
00839   colorstack[num_colors].colortype = RGB;
00840   num_colors+= 1;
00841   dev_do_color();
00842 }
00843 
00844 void dev_begin_cmyk_color (double c, double m, double y, double k)
00845 {
00846   if (num_colors >= MAX_COLORS) {
00847     fprintf (stderr, "\ndev_set_color:  Exceeded depth of color stack\n");
00848     return;
00849   }
00850   colorstack[num_colors].c1 = c;
00851   colorstack[num_colors].c2 = m;
00852   colorstack[num_colors].c3 = y;
00853   colorstack[num_colors].c4 = k;
00854   colorstack[num_colors].colortype = CMYK;
00855   num_colors+= 1;
00856   dev_do_color();
00857 }
00858 
00859 void dev_begin_gray (double value)
00860 {
00861   if (num_colors >= MAX_COLORS) {
00862     fprintf (stderr, "\ndev_begin_gray:  Exceeded depth of color stack\n");
00863     return;
00864   }
00865   colorstack[num_colors].c1 = value;
00866   colorstack[num_colors].colortype = GRAY;
00867   num_colors+= 1;
00868   dev_do_color();
00869 }
00870 
00871 void dev_end_color (void)
00872 {
00873   if (num_colors <= 0) {
00874     fprintf (stderr, "\ndev_set_color:  End color with no corresponding begin color\n");
00875     return;
00876   }
00877   num_colors -= 1;
00878   dev_do_color();
00879 }
00880 
00881 static int num_transforms = 0;
00882 
00883 static void dev_clear_xform_stack (void)
00884 {
00885   num_transforms = 0;
00886   return;
00887 }
00888 
00889 void dev_begin_xform (double xscale, double yscale, double rotate,
00890                     double x_user, double y_user)
00891 {
00892   double c, s;
00893   if (num_transforms >= MAX_TRANSFORMS) {
00894     fprintf (stderr, "\ndev_begin_xform:  Exceeded depth of transformation stack\n");
00895     return;
00896   }
00897   c = ROUND (cos(rotate),1e-5);
00898   s = ROUND (sin(rotate),1e-5);
00899   sprintf (work_buffer, " q %g %g %g %g %.2f %.2f cm",
00900           xscale*c, xscale*s, -yscale*s, yscale*c,
00901           (1.0-xscale*c)*x_user+yscale*s*y_user,
00902           -xscale*s*x_user+(1.0-yscale*c)*y_user);
00903   pdf_doc_add_to_page (work_buffer, strlen(work_buffer));
00904   num_transforms += 1;
00905   return;
00906 }
00907 
00908 void dev_end_xform (void)
00909 {
00910   if (num_transforms <= 0) {
00911     fprintf (stderr, "\ndev_end_xform:  End transform with no corresponding begin\n");
00912     return;
00913   }
00914   pdf_doc_add_to_page (" Q", 2);
00915   num_transforms -= 1;
00916   /* Unfortunately, the following two lines are necessary in case of a font or color
00917      change inside of the save/restore pair.  Anything that was done
00918      there must be redone, so in effect, we make no assumptions about
00919      what fonts. We act like we are starting a new page */
00920   dev_reselect_font();
00921   dev_do_color();
00922   return;
00923 }
00924 
00925 int dev_xform_depth (void)
00926 {
00927   return num_transforms;
00928 }
00929 
00930 void dev_close_all_xforms (int depth)
00931 {
00932   if (num_transforms > depth) {
00933     fprintf (stderr, "\nspecial: Closing pending transformations at end of page/XObject\n");
00934     while (num_transforms > depth) {
00935       num_transforms -= 1;
00936       pdf_doc_add_to_page (" Q", 2);
00937     }
00938     dev_reselect_font();
00939     dev_do_color();
00940   }
00941   return;
00942 }
00943 
00944 
00945 /* The following routine is here for forms.  Since
00946    a form is self-contained, it will need its own Tf command
00947    at the beginningg even if it is continuing to set type
00948    in the current font.  This routine simply forces reinstantiation
00949    of the current font. */
00950 void dev_reselect_font(void)
00951 {
00952   int i;
00953   current_font = -1;
00954   for (i=0; i<num_dev_fonts; i++) {
00955     dev_font[i].used_on_this_page = 0;
00956   }
00957   text_slant = 0.0;
00958   text_extend = 1.0;
00959 }
00960 
00961 static void bop_font_reset(void)
00962 {
00963   dev_reselect_font();
00964 }
00965 
00966 void dev_bop (void)
00967 {
00968 #ifdef MEM_DEBUG
00969 MEM_START
00970 #endif
00971   if (debug) {
00972     fprintf (stderr, "dev_bop:\n");
00973   }
00974   pdf_doc_new_page ();
00975   graphics_mode();
00976   {
00977     text_slant = 0.0;
00978     text_extend = 1.0;
00979   }
00980   bop_font_reset();
00981   /* This shouldn't be necessary because line widths are now
00982      explicitly set for each rule */
00983   /*  pdf_doc_add_to_page ("0 w", 3); */
00984   dev_do_color(); /* Set text color since new page loses color state */
00985 #ifdef MEM_DEBUG
00986 MEM_END
00987 #endif
00988 }
00989 
00990 void dev_eop (void)
00991 {
00992 #ifdef MEM_DEBUG
00993 MEM_START
00994 #endif
00995   if (debug) {
00996     fprintf (stderr, "dev_eop:\n");
00997   }
00998   graphics_mode();
00999   dev_close_all_xforms(0);
01000   fill_page();
01001   pdf_doc_finish_page ();
01002   /* Finish any pending PS specials */
01003   mp_eop_cleanup();
01004 #ifdef MEM_DEBUG
01005 MEM_END
01006 #endif
01007 }
01008 
01009 int dev_locate_font (const char *tex_name, spt_t ptsize)
01010 {
01011   int i;
01012   int this_font;
01013 
01014   if (ptsize == 0) {
01015     ERROR ("locate_dev_font() called with ptsize = 0");
01016   }
01017   /* Make sure we have room for a new one, even though we
01018      may not actually create one */
01019   dev_fonts_need (num_dev_fonts+1);
01020   this_font = num_dev_fonts;
01021   
01022   for (i=0; i<this_font; i++) {
01023     /* PK must match in name and size to resolve to the same device
01024        font */
01025     if (dev_font[i].tex_name &&
01026        strcmp (tex_name, dev_font[i].tex_name) == 0 &&
01027        dev_font[i].sptsize == ptsize &&
01028        dev_font[i].format == PK)
01029       break;
01030     /* Scaleable fonts must match in name; however, this routine
01031        must return a different id if the ptsize is different */
01032     if (dev_font[i].tex_name &&
01033        strcmp (tex_name, dev_font[i].tex_name) == 0 &&
01034        dev_font[i].format != PK)
01035       break;
01036   }
01037   if (i == this_font) { /* There is no physical font we can use */
01038     struct map_record *map_record;
01039     int font_id = -1, font_format = -1, tfm_id = -1, encoding_id = -1;
01040     int remap = 0;
01041     double extend= 1.0, slant = 0.0;
01042     const char *font_name;
01043     char short_name[7];
01044     /* Get appropriate info from map file (yes, PK fonts at two
01045        different point sizes would be looked up twice unecessarily) */
01046     if ((map_record = get_map_record (tex_name))) {
01047       remap = map_record -> remap;
01048       slant = map_record -> slant;
01049       extend = map_record -> extend;
01050       font_name = map_record -> font_name;
01051     } else {
01052       font_name = tex_name;
01053     }
01054     if (verbose>1){
01055       if (map_record) {
01056        fprintf (stderr, "\nfontmap: %s -> %s", tex_name,
01057                map_record->font_name);
01058        if (map_record->enc_name)
01059          fprintf (stderr, "(%s)", map_record->enc_name);
01060        if (map_record->slant)
01061          fprintf (stderr, "[slant=%g]", map_record->slant);
01062        if (map_record->extend != 1.0)
01063          fprintf (stderr, "[extend=%g]", map_record->extend);
01064        if (map_record->remap)
01065          fprintf (stderr, "[remap]");
01066        fprintf (stderr, "\n");
01067       } else {
01068        fprintf (stderr, "\nfontmap: %s (no map)\n", tex_name);
01069       }
01070     }
01071     /* If this font has an encoding specified on the record, get its id */
01072     if (map_record && map_record -> enc_name != NULL) {
01073       encoding_id = get_encoding (map_record -> enc_name);
01074     } else { /* Otherwise set the encoding_id to -1 */
01075       encoding_id = -1;
01076     }
01077     tfm_id = tfm_open (tex_name);
01078     /* We assume, for now that we will find this as a physical font,
01079        as opposed to a vf, so we need a device name to tell the
01080        lower-level routines what we want this to be called.  We'll
01081        blast this name away later if we don't need it. */
01082     short_name[0] = 'F';
01083     inttoa (short_name+1, num_phys_fonts+1);
01084     if ((font_id = type1_font (font_name, tfm_id,
01085                             short_name, encoding_id, remap))>=0) {
01086       font_format = TYPE1;
01087 #ifdef HAVE_TTF_FORMATS
01088     } else if ((font_id = ttf_font (font_name, tfm_id,
01089                                 short_name, encoding_id, remap))>=0) {
01090       font_format = TRUETYPE;
01091 #endif /* HAVE_TTF_FORMATS */
01092     } else if ((font_id = pk_font (font_name, ptsize*dvi2pts,
01093                                tfm_id,
01094                                short_name))>=0) {
01095       font_format = PK;
01096     }
01097     if (font_format >= 0) { /* This is a new physical font and we found a physical
01098                             font we can use */
01099       strcpy (dev_font[this_font].short_name, short_name);
01100       dev_font[this_font].tex_name = NEW (strlen (tex_name)+1, char);
01101       strcpy (dev_font[this_font].tex_name, tex_name);
01102       dev_font[this_font].sptsize = ptsize;
01103       dev_font[this_font].format = font_format;
01104       dev_font[this_font].slant = slant;
01105       dev_font[this_font].extend = extend;
01106       dev_font[this_font].remap = remap;
01107       dev_font[this_font].used_on_this_page = 0;
01108       switch (font_format) {
01109       case TYPE1:
01110        dev_font[this_font].font_resource =
01111          type1_font_resource(font_id);
01112        dev_font[this_font].used_chars = type1_font_used(font_id);
01113        break;
01114 #ifdef HAVE_TTF_FORMATS
01115       case TRUETYPE:
01116        dev_font[this_font].font_resource =
01117          ttf_font_resource(font_id);
01118        dev_font[this_font].used_chars = ttf_font_used(font_id);
01119        break;
01120 #endif /* HAVE_TTF_FORMATS */
01121       case PK:
01122        dev_font[this_font].font_resource = pk_font_resource (font_id);
01123        dev_font[this_font].used_chars = pk_font_used(font_id);
01124        break;
01125       default:
01126        ERROR ("Impossible font format in dev_locate_font()");
01127       }
01128       num_phys_fonts += 1;
01129     } else { /* No appropriate physical font exists */
01130       this_font = -1; /* A flag indicating no physical font */
01131     }
01132   } else { /* A previously existing physical font can be used;
01133              however, this routine must return a distinct ID if the
01134              ptsizes are different.  Copy the information from the
01135              previous record to the new record */
01136     strcpy (dev_font[this_font].short_name, dev_font[i].short_name);
01137     dev_font[this_font].tex_name = NEW (strlen (tex_name)+1, char);
01138     strcpy (dev_font[this_font].tex_name, tex_name);
01139     dev_font[this_font].sptsize = ptsize;
01140     dev_font[this_font].format = dev_font[i].format;
01141     dev_font[this_font].used_chars = dev_font[i].used_chars;
01142     dev_font[this_font].slant = dev_font[i].slant;
01143     dev_font[this_font].extend = dev_font[i].extend;
01144     dev_font[this_font].remap = dev_font[i].remap;
01145     /* The value in useD_on_this_page will be incorrect if the font
01146        has already been used on a page in a different point size.
01147        It's too hard to do right.  The only negative consequence is
01148        that there will be an attempt to add the resource to the page
01149        resource dict.  However, the second attempt will do nothing */
01150     dev_font[this_font].used_on_this_page = 0;
01151     dev_font[this_font].font_resource = pdf_link_obj(dev_font[i].font_resource);
01152     /* These two fonts are treated as having the same physical
01153        "used_chars" */
01154     dev_font[this_font].used_chars = dev_font[i].used_chars;
01155   }
01156   if (this_font >= 0)
01157     num_dev_fonts += 1;
01158   return this_font;
01159 }
01160   
01161 void dev_close_all_fonts(void)
01162 {
01163   int i;
01164   for (i=0; i<num_dev_fonts; i++) {
01165     pdf_release_obj (dev_font[i].font_resource);
01166     RELEASE (dev_font[i].tex_name);
01167   }
01168   if (dev_font)
01169     RELEASE (dev_font);
01170 
01171   /* Release all map entries */
01172   for (i=0; i<num_font_map; i++) {
01173     release_map_record (font_map+i);
01174   }
01175 
01176   if (font_map)
01177     RELEASE (font_map);
01178   /* Close the various font handlers */
01179   type1_close_all();
01180   pk_close_all();
01181 #ifdef HAVE_TTF_FORMATS   
01182   ttf_close_all();
01183 #endif   
01184 
01185   /* Now do encodings. */
01186   encoding_flush_all();
01187 }
01188 
01189 void dev_rule (spt_t xpos, spt_t ypos, spt_t width, spt_t height)
01190 {
01191   int len = 0;
01192   long w, p1, p2, p3, p4;
01193   graphics_mode();
01194    /* Is using a real stroke the right thing to do?  It seems to preserve
01195       the logical meaning of a "rule" as opposed to a filled rectangle.
01196       I am assume the reader can more intelligently render a rule than a filled rectangle */
01197   if (width> height) {  /* Horizontal stroke? */
01198     w = IDIVRND(height, CENTI_PDF_U);
01199     p1 = IDIVRND(xpos, CENTI_PDF_U);
01200     p2 = IDIVRND (2*ypos+height, 2*CENTI_PDF_U);
01201     p3 = IDIVRND(xpos+width,CENTI_PDF_U);
01202     p4 = IDIVRND (2*ypos+height, 2*CENTI_PDF_U);
01203   } else { /* Vertical stroke */
01204     w = IDIVRND(width,CENTI_PDF_U);
01205     p1 = IDIVRND (2*xpos+width, 2*CENTI_PDF_U);
01206     p2 = IDIVRND(ypos, CENTI_PDF_U);
01207     p3 = IDIVRND (2*xpos+width, 2*CENTI_PDF_U);
01208     p4 = IDIVRND(ypos+height,CENTI_PDF_U);
01209   }
01210   /* This needs to be quick */
01211   {
01212     format_buffer[len++] = ' ';
01213     len += centi_u_to_a (format_buffer+len, w);
01214     format_buffer[len++] = ' ';
01215     format_buffer[len++] = 'w';
01216     format_buffer[len++] = ' ';
01217     len += centi_u_to_a (format_buffer+len, p1);
01218     format_buffer[len++] = ' ';
01219     len += centi_u_to_a (format_buffer+len, p2);
01220     format_buffer[len++] = ' ';
01221     format_buffer[len++] = 'm';
01222     format_buffer[len++] = ' ';
01223     len += centi_u_to_a (format_buffer+len, p3);
01224     format_buffer[len++] = ' ';
01225     len += centi_u_to_a (format_buffer+len, p4);
01226     format_buffer[len++] = ' ';
01227     format_buffer[len++] = 'l';
01228     format_buffer[len++] = ' ';
01229     format_buffer[len++] = 'S';
01230   }
01231   pdf_doc_add_to_page (format_buffer, len);
01232 }
01233 
01234 /* The following routines tell the coordinates in true Adobe points
01235    with the coordinate system having its origin at the bottom
01236    left of the page. */
01237 
01238 double dev_phys_x (void)
01239 {
01240   return dvi_dev_xpos()*dvi_tell_mag() + hoffset;
01241 }
01242 
01243 double dev_phys_y (void)
01244 {
01245   return dev_page_height() + dvi_tell_mag()*dvi_dev_ypos() -voffset;
01246 }
01247 
01248 static int src_special (char *buffer, UNSIGNED_QUAD size) {
01249   char *start = buffer;
01250   char *end = buffer + size;
01251   int result = 0;
01252   skip_white (&start, end);
01253   if ((start+3 < end) &&
01254       (!strncmp ("src:", start, 4)))
01255     result = 1;
01256   return result;
01257 }
01258 
01259 void dev_do_special (void *buffer, UNSIGNED_QUAD size, spt_t x_user, 
01260                    spt_t y_user)
01261 {
01262   double dev_xuser, dev_yuser;
01263   dev_xuser = ((double) x_user) / ((double) PDF_U);
01264   dev_yuser = ((double) -y_user) / ((double) PDF_U);
01265   graphics_mode();
01266   if (!pdf_parse_special (buffer, size, dev_xuser, dev_yuser) &&
01267       !tpic_parse_special (buffer, size, dev_xuser, dev_yuser) &&
01268       !htex_parse_special (buffer, size) &&
01269       !color_special (buffer, size) &&
01270       !ps_parse_special (buffer, size, dev_xuser, dev_yuser) &&
01271       !src_special (buffer, size)) {
01272     fprintf (stderr, "\nUnrecognized special ignored");
01273     dump (buffer, ((char *)buffer)+size);
01274   }
01275 }
01276 
01277 static unsigned dvi_stack_depth = 0;
01278 static int dvi_tagged_depth = -1;
01279 static unsigned char link_annot = 1;
01280 
01281 void dev_link_annot (unsigned char flag)
01282 {
01283   link_annot = flag;
01284 }
01285 
01286 void dev_stack_depth (unsigned int depth)
01287 {
01288   /* If decreasing below tagged_depth */
01289   if (link_annot && 
01290       dvi_stack_depth == dvi_tagged_depth &&
01291       depth == dvi_tagged_depth - 1) {
01292   /* See if this appears to be the end of a "logical unit"
01293      that's been broken.  If so, flush the logical unit */
01294     pdf_doc_flush_annot();
01295   }
01296   dvi_stack_depth = depth;
01297   return;
01298 }
01299 
01300 /* The following routines setup and tear down a callback at
01301    a certain stack depth.  This is used to handle broken (linewise)
01302    links */
01303 
01304 void dev_tag_depth (void)
01305 {
01306   dvi_tagged_depth = dvi_stack_depth;
01307   dvi_compute_boxes (1);
01308   return;
01309 }
01310 
01311 void dev_untag_depth (void)
01312 {
01313   dvi_tagged_depth = -1;
01314   dvi_compute_boxes (0);
01315   return;
01316 }
01317 
01318 void dev_expand_box (spt_t width, spt_t height, spt_t depth)
01319 {
01320   double phys_width, phys_height, phys_depth, scale;
01321   if (link_annot && dvi_stack_depth >= dvi_tagged_depth) {
01322     scale = dvi2pts*dvi_tell_mag();
01323     phys_width = scale*width;
01324     phys_height = scale*height;
01325     phys_depth = scale*depth;
01326     pdf_doc_expand_box (dev_phys_x(), dev_phys_y()-phys_depth,
01327                      dev_phys_x()+phys_width,
01328                      dev_phys_y()+phys_height);
01329   }
01330 }
01331 
01332 
01333 
01334 
01335 
01336 
01337 
01338 
01339 
01340 
01341 
01342 
01343