]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/PSOutputDev.cc
Synched with Xpdf 0.92
[evince.git] / pdf / xpdf / PSOutputDev.cc
1 //========================================================================
2 //
3 // PSOutputDev.cc
4 //
5 // Copyright 1996 Derek B. Noonburg
6 //
7 //========================================================================
8
9 #ifdef __GNUC__
10 #pragma implementation
11 #endif
12
13 #include <stdio.h>
14 #include <stddef.h>
15 #include <stdarg.h>
16 #include <signal.h>
17 #include <math.h>
18 #include "GString.h"
19 #include "config.h"
20 #include "Object.h"
21 #include "Error.h"
22 #include "GfxState.h"
23 #include "GfxFont.h"
24 #include "FontFile.h"
25 #include "Catalog.h"
26 #include "Page.h"
27 #include "Stream.h"
28 #include "FormWidget.h"
29 #include "PSOutputDev.h"
30
31 #if JAPANESE_SUPPORT
32 #include "Japan12ToRKSJ.h"
33 #endif
34
35 #ifdef MACOS
36 // needed for setting type/creator of MacOS files
37 #include "ICSupport.h"
38 #endif
39
40 //------------------------------------------------------------------------
41 // Parameters
42 //------------------------------------------------------------------------
43
44 // Generate Level 1 PostScript?
45 GBool psOutLevel1 = gFalse;
46
47 // Generate Level 1 separable PostScript?
48 GBool psOutLevel1Sep = gFalse;
49
50 // Generate Encapsulated PostScript?
51 GBool psOutEPS = gFalse;
52
53 #if OPI_SUPPORT
54 // Generate OPI comments?
55 GBool psOutOPI = gFalse;
56 #endif
57
58 int paperWidth = defPaperWidth;
59 int paperHeight = defPaperHeight;
60
61 //------------------------------------------------------------------------
62 // PostScript prolog and setup
63 //------------------------------------------------------------------------
64
65 static char *prolog[] = {
66   "/xpdf 75 dict def xpdf begin",
67   "% PDF special state",
68   "/pdfDictSize 14 def",
69   "/pdfSetup {",
70   "  2 array astore",
71   "  /setpagedevice where {",
72   "    pop 3 dict dup begin",
73   "      exch /PageSize exch def",
74   "      /ImagingBBox null def",
75   "      /Policies 1 dict dup begin /PageSize 3 def end def",
76   "    end setpagedevice",
77   "  } {",
78   "    pop",
79   "  } ifelse",
80   "} def",
81   "/pdfStartPage {",
82   "  pdfDictSize dict begin",
83   "  /pdfFill [0] def",
84   "  /pdfStroke [0] def",
85   "  /pdfLastFill false def",
86   "  /pdfLastStroke false def",
87   "  /pdfTextMat [1 0 0 1 0 0] def",
88   "  /pdfFontSize 0 def",
89   "  /pdfCharSpacing 0 def",
90   "  /pdfTextRender 0 def",
91   "  /pdfTextRise 0 def",
92   "  /pdfWordSpacing 0 def",
93   "  /pdfHorizScaling 1 def",
94   "} def",
95   "/pdfEndPage { end } def",
96   "/sCol {",
97   "  pdfLastStroke not {",
98   "    pdfStroke aload length",
99   "    dup 1 eq { pop setgray }",
100   "      { 3 eq { setrgbcolor } { setcmykcolor } ifelse } ifelse",
101   "    /pdfLastStroke true def /pdfLastFill false def",
102   "  } if",
103   "} def",
104   "/fCol {",
105   "  pdfLastFill not {",
106   "    pdfFill aload length",
107   "    dup 1 eq { pop setgray }",
108   "      { 3 eq { setrgbcolor } { setcmykcolor } ifelse } ifelse",
109   "    /pdfLastFill true def /pdfLastStroke false def",
110   "  } if",
111   "} def",
112   "% build a font",
113   "/pdfMakeFont {",
114   "  4 3 roll findfont",
115   "  4 2 roll matrix scale makefont",
116   "  dup length dict begin",
117   "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
118   "    /Encoding exch def",
119   "    currentdict",
120   "  end",
121   "  definefont pop",
122   "} def",
123   "/pdfMakeFont16 { findfont definefont pop } def",
124   "% graphics state operators",
125   "/q { gsave pdfDictSize dict begin } def",
126   "/Q { end grestore } def",
127   "/cm { concat } def",
128   "/d { setdash } def",
129   "/i { setflat } def",
130   "/j { setlinejoin } def",
131   "/J { setlinecap } def",
132   "/M { setmiterlimit } def",
133   "/w { setlinewidth } def",
134   "% color operators",
135   "/g { dup 1 array astore /pdfFill exch def setgray",
136   "     /pdfLastFill true def /pdfLastStroke false def } def",
137   "/G { dup 1 array astore /pdfStroke exch def setgray",
138   "     /pdfLastStroke true def /pdfLastFill false def } def",
139   "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor",
140   "      /pdfLastFill true def /pdfLastStroke false def } def",
141   "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor",
142   "      /pdfLastStroke true def /pdfLastFill false def } def",
143   "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
144   "     /pdfLastFill true def /pdfLastStroke false def } def",
145   "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
146   "     /pdfLastStroke true def /pdfLastFill false def } def",
147   "% path segment operators",
148   "/m { moveto } def",
149   "/l { lineto } def",
150   "/c { curveto } def",
151   "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
152   "      neg 0 rlineto closepath } def",
153   "/h { closepath } def",
154   "% path painting operators",
155   "/S { sCol stroke } def",
156   "/f { fCol fill } def",
157   "/f* { fCol eofill } def",
158   "% clipping operators",
159   "/W { clip newpath } def",
160   "/W* { eoclip newpath } def",
161   "% text state operators",
162   "/Tc { /pdfCharSpacing exch def } def",
163   "/Tf { dup /pdfFontSize exch def",
164   "      dup pdfHorizScaling mul exch matrix scale",
165   "      pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
166   "      exch findfont exch makefont setfont } def",
167   "/Tr { /pdfTextRender exch def } def",
168   "/Ts { /pdfTextRise exch def } def",
169   "/Tw { /pdfWordSpacing exch def } def",
170   "/Tz { /pdfHorizScaling exch def } def",
171   "% text positioning operators",
172   "/Td { pdfTextMat transform moveto } def",
173   "/Tm { /pdfTextMat exch def } def",
174   "% text string operators",
175   "/Tj { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
176   "      0 pdfTextRise pdfTextMat dtransform rmoveto",
177   "      pdfFontSize mul pdfHorizScaling mul",
178   "      1 index stringwidth pdfTextMat idtransform pop",
179   "      sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
180   "      pdfWordSpacing 0 pdfTextMat dtransform 32",
181   "      4 3 roll pdfCharSpacing add 0 pdfTextMat dtransform",
182   "      6 5 roll awidthshow",
183   "      0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
184   "/TJm { pdfFontSize 0.001 mul mul neg 0",
185   "       pdfTextMat dtransform rmoveto } def",
186   "% Level 1 image operators",
187   "/pdfIm1 {",
188   "  /pdfImBuf1 4 index string def",
189   "  { currentfile pdfImBuf1 readhexstring pop } image",
190   "} def",
191   "/pdfIm1Sep {",
192   "  /pdfImBuf1 4 index string def",
193   "  /pdfImBuf2 4 index string def",
194   "  /pdfImBuf3 4 index string def",
195   "  /pdfImBuf4 4 index string def",
196   "  { currentfile pdfImBuf1 readhexstring pop }",
197   "  { currentfile pdfImBuf2 readhexstring pop }",
198   "  { currentfile pdfImBuf3 readhexstring pop }",
199   "  { currentfile pdfImBuf4 readhexstring pop }",
200   "  true 4 colorimage",
201   "} def",
202   "/pdfImM1 {",
203   "  /pdfImBuf1 4 index 7 add 8 idiv string def",
204   "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
205   "} def",
206   "% Level 2 image operators",
207   "/pdfImBuf 100 string def",
208   "/pdfIm {",
209   "  image",
210   "  { currentfile pdfImBuf readline",
211   "    not { pop exit } if",
212   "    (%-EOD-) eq { exit } if } loop",
213   "} def",
214   "/pdfImM {",
215   "  fCol imagemask",
216   "  { currentfile pdfImBuf readline",
217   "    not { pop exit } if",
218   "    (%-EOD-) eq { exit } if } loop",
219   "} def",
220   "end",
221   NULL
222 };
223
224 //------------------------------------------------------------------------
225 // Fonts
226 //------------------------------------------------------------------------
227
228 struct PSFont {
229   char *name;                   // PDF name
230   char *psName;                 // PostScript name
231 };
232
233 struct PSSubstFont {
234   char *psName;                 // PostScript name
235   double mWidth;                // width of 'm' character
236 };
237
238 static PSFont psFonts[] = {
239   {"Courier",               "Courier"},
240   {"Courier-Bold",          "Courier-Bold"},
241   {"Courier-Oblique",       "Courier-Bold"},
242   {"Courier-BoldOblique",   "Courier-BoldOblique"},
243   {"Helvetica",             "Helvetica"},
244   {"Helvetica-Bold",        "Helvetica-Bold"},
245   {"Helvetica-Oblique",     "Helvetica-Oblique"},
246   {"Helvetica-BoldOblique", "Helvetica-BoldOblique"},
247   {"Symbol",                "Symbol"},
248   {"Times-Roman",           "Times-Roman"},
249   {"Times-Bold",            "Times-Bold"},
250   {"Times-Italic",          "Times-Italic"},
251   {"Times-BoldItalic",      "Times-BoldItalic"},
252   {"ZapfDingbats",          "ZapfDingbats"},
253   {NULL}
254 };
255
256 static PSSubstFont psSubstFonts[] = {
257   {"Helvetica",             0.833},
258   {"Helvetica-Oblique",     0.833},
259   {"Helvetica-Bold",        0.889},
260   {"Helvetica-BoldOblique", 0.889},
261   {"Times-Roman",           0.788},
262   {"Times-Italic",          0.722},
263   {"Times-Bold",            0.833},
264   {"Times-BoldItalic",      0.778},
265   {"Courier",               0.600},
266   {"Courier-Oblique",       0.600},
267   {"Courier-Bold",          0.600},
268   {"Courier-BoldOblique",   0.600}
269 };
270
271 //------------------------------------------------------------------------
272 // PSOutputDev
273 //------------------------------------------------------------------------
274
275 PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog,
276                          int firstPage, int lastPage,
277                          GBool embedType11, GBool doForm1) {
278   Page *page;
279   Dict *resDict;
280   FormWidgets *formWidgets;
281   char **p;
282   int pg;
283   Object obj1, obj2;
284   int i;
285
286   // initialize
287   embedType1 = embedType11;
288   doForm = doForm1;
289   fontIDs = NULL;
290   fontFileIDs = NULL;
291   fontFileNames = NULL;
292   embFontList = NULL;
293   f = NULL;
294   if (doForm)
295     lastPage = firstPage;
296
297   // open file or pipe
298   ok = gTrue;
299   if (!strcmp(fileName, "-")) {
300     fileType = psStdout;
301     f = stdout;
302   } else if (fileName[0] == '|') {
303     fileType = psPipe;
304 #ifdef HAVE_POPEN
305 #ifndef WIN32
306     signal(SIGPIPE, (void (*)(int))SIG_IGN);
307 #endif
308     if (!(f = popen(fileName + 1, "w"))) {
309       error(-1, "Couldn't run print command '%s'", fileName);
310       ok = gFalse;
311       return;
312     }
313 #else
314     error(-1, "Print commands are not supported ('%s')", fileName);
315     ok = gFalse;
316     return;
317 #endif
318   } else {
319     fileType = psFile;
320     if (!(f = fopen(fileName, "w"))) {
321       error(-1, "Couldn't open PostScript file '%s'", fileName);
322       ok = gFalse;
323       return;
324     }
325   }
326
327   // initialize fontIDs, fontFileIDs, and fontFileNames lists
328   fontIDSize = 64;
329   fontIDLen = 0;
330   fontIDs = (Ref *)gmalloc(fontIDSize * sizeof(Ref));
331   fontFileIDSize = 64;
332   fontFileIDLen = 0;
333   fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref));
334   fontFileNameSize = 64;
335   fontFileNameLen = 0;
336   fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *));
337
338   // initialize embedded font resource comment list
339   embFontList = new GString();
340
341   // write header
342   if (doForm) {
343     writePS("%%!PS-Adobe-3.0 Resource-Form\n");
344     writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
345     writePS("%%%%LanguageLevel: %d\n",
346             (psOutLevel1 || psOutLevel1Sep) ? 1 : 2);
347     if (psOutLevel1Sep) {
348       writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
349     }
350     writePS("%%%%EndComments\n");
351     page = catalog->getPage(firstPage);
352     writePS("32 dict dup begin\n");
353     writePS("/BBox [%d %d %d %d] def\n",
354             (int)page->getX1(), (int)page->getY1(),
355             (int)page->getX2(), (int)page->getY2());
356     writePS("/FormType 1 def\n");
357     writePS("/Matrix [1 0 0 1 0 0] def\n");
358   } else if (psOutEPS) {
359     writePS("%%!PS-Adobe-3.0 EPSF-3.0\n");
360     writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
361     writePS("%%%%LanguageLevel: %d\n",
362             (psOutLevel1 || psOutLevel1Sep) ? 1 : 2);
363     if (psOutLevel1Sep) {
364       writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
365     }
366     page = catalog->getPage(firstPage);
367     writePS("%%%%BoundingBox: %d %d %d %d\n",
368             (int)floor(page->getX1()), (int)floor(page->getY1()),
369             (int)ceil(page->getX2()), (int)ceil(page->getY2()));
370     if (floor(page->getX1()) != ceil(page->getX1()) ||
371         floor(page->getY1()) != ceil(page->getY1()) ||
372         floor(page->getX2()) != ceil(page->getX2()) ||
373         floor(page->getY2()) != ceil(page->getY2())) {
374       writePS("%%%%HiResBoundingBox: %g %g %g %g\n",
375               page->getX1(), page->getY1(),
376               page->getX2(), page->getY2());
377     }
378     writePS("%%%%DocumentSuppliedResources: (atend)\n");
379     writePS("%%%%EndComments\n");
380   } else {
381     writePS("%%!PS-Adobe-3.0\n");
382     writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
383     writePS("%%%%LanguageLevel: %d\n",
384             (psOutLevel1 || psOutLevel1Sep) ? 1 : 2);
385     if (psOutLevel1Sep) {
386       writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
387     }
388     writePS("%%%%DocumentMedia: plain %d %d 0 () ()\n",
389             paperWidth, paperHeight);
390     writePS("%%%%Pages: %d\n", lastPage - firstPage + 1);
391     writePS("%%%%EndComments\n");
392     writePS("%%%%BeginDefaults\n");
393     writePS("%%%%PageMedia: plain\n");
394     writePS("%%%%EndDefaults\n");
395   }
396
397   // write prolog
398   if (!doForm) {
399     writePS("%%%%BeginProlog\n");
400   }
401   writePS("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
402   for (p = prolog; *p; ++p) {
403     writePS("%s\n", *p);
404   }
405   writePS("%%%%EndResource\n");
406   if (!doForm) {
407     writePS("%%%%EndProlog\n");
408   }
409
410   // set up fonts and images
411   type3Warning = gFalse;
412   if (doForm) {
413     // swap the form and xpdf dicts
414     writePS("xpdf end begin dup begin\n");
415   } else {
416     writePS("%%%%BeginSetup\n");
417     writePS("xpdf begin\n");
418   }
419   for (pg = firstPage; pg <= lastPage; ++pg) {
420     page = catalog->getPage(pg);
421     if ((resDict = page->getResourceDict())) {
422       setupResources(resDict);
423     }
424     formWidgets = new FormWidgets(page->getAnnots(&obj1));
425     obj1.free();
426     for (i = 0; i < formWidgets->getNumWidgets(); ++i) {
427       if (formWidgets->getWidget(i)->getAppearance(&obj1)->isStream()) {
428         obj1.streamGetDict()->lookup("Resources", &obj2);
429         if (obj2.isDict()) {
430           setupResources(obj2.getDict());
431         }
432         obj2.free();
433       }
434       obj1.free();
435     }
436     delete formWidgets;
437   }
438   if (!doForm) {
439 #if OPI_SUPPORT
440     if (psOutOPI) {
441       writePS("/opiMatrix matrix currentmatrix def\n");
442     }
443 #endif
444     if (!psOutEPS) {
445       writePS("%d %d pdfSetup\n", paperWidth, paperHeight);
446     }
447     writePS("%%%%EndSetup\n");
448   }
449
450   // initialize sequential page number
451   seqPage = 1;
452
453 #if OPI_SUPPORT
454   // initialize OPI nesting levels
455   opi13Nest = 0;
456   opi20Nest = 0;
457 #endif
458 }
459
460 PSOutputDev::~PSOutputDev() {
461   int i;
462
463   if (f) {
464     if (doForm) {
465       writePS("/Foo exch /Form defineresource pop\n");
466     } else if (psOutEPS) {
467       writePS("%%%%Trailer\n");
468       writePS("end\n");
469       writePS("%%%%DocumentSuppliedResources:\n");
470       writePS("%s", embFontList->getCString());
471       writePS("%%%%EOF\n");
472     } else {
473       writePS("%%%%Trailer\n");
474       writePS("end\n");
475       writePS("%%%%EOF\n");
476     }
477     if (fileType == psFile) {
478 #ifdef MACOS
479       ICS_MapRefNumAndAssign((short)f->handle);
480 #endif
481       fclose(f);
482     }
483 #ifdef HAVE_POPEN
484     else if (fileType == psPipe) {
485       pclose(f);
486 #ifndef WIN32
487       signal(SIGPIPE, (void (*)(int))SIG_DFL);
488 #endif
489     }
490 #endif
491   }
492   if (embFontList) {
493     delete embFontList;
494   }
495   if (fontIDs) {
496     gfree(fontIDs);
497   }
498   if (fontFileIDs) {
499     gfree(fontFileIDs);
500   }
501   if (fontFileNames) {
502     for (i = 0; i < fontFileNameLen; ++i) {
503       delete fontFileNames[i];
504     }
505     gfree(fontFileNames);
506   }
507 }
508
509 void PSOutputDev::setupResources(Dict *resDict) {
510   Object xObjDict, xObj, resObj;
511   int i;
512
513   setupFonts(resDict);
514   setupImages(resDict);
515
516   resDict->lookup("XObject", &xObjDict);
517   if (xObjDict.isDict()) {
518     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
519       xObjDict.dictGetVal(i, &xObj);
520       if (xObj.isStream()) {
521         xObj.streamGetDict()->lookup("Resources", &resObj);
522         if (resObj.isDict()) {
523           setupResources(resObj.getDict());
524         }
525         resObj.free();
526       }
527       xObj.free();
528     }
529   }
530   xObjDict.free();
531 }
532
533 void PSOutputDev::setupFonts(Dict *resDict) {
534   Object fontDict;
535   GfxFontDict *gfxFontDict;
536   GfxFont *font;
537   int i;
538
539   resDict->lookup("Font", &fontDict);
540   if (fontDict.isDict()) {
541     gfxFontDict = new GfxFontDict(fontDict.getDict());
542     for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
543       font = gfxFontDict->getFont(i);
544       setupFont(font);
545     }
546     delete gfxFontDict;
547   }
548   fontDict.free();
549 }
550
551 void PSOutputDev::setupFont(GfxFont *font) {
552   Ref fontFileID;
553   GString *name;
554   char *psName;
555   char *charName;
556   double xs, ys;
557   GBool do16Bit;
558   int code;
559   double w1, w2;
560   double *fm;
561   int i, j;
562
563   // check if font is already set up
564   for (i = 0; i < fontIDLen; ++i) {
565     if (fontIDs[i].num == font->getID().num &&
566         fontIDs[i].gen == font->getID().gen)
567       return;
568   }
569
570   // add entry to fontIDs list
571   if (fontIDLen >= fontIDSize) {
572     fontIDSize += 64;
573     fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref));
574   }
575   fontIDs[fontIDLen++] = font->getID();
576
577   xs = ys = 1;
578   do16Bit = gFalse;
579
580   // check for embedded Type 1 font
581   if (embedType1 && font->getType() == fontType1 &&
582       font->getEmbeddedFontID(&fontFileID)) {
583     psName = font->getEmbeddedFontName();
584     setupEmbeddedType1Font(&fontFileID, psName);
585
586   // check for external Type 1 font file
587   } else if (embedType1 && font->getType() == fontType1 &&
588              font->getExtFontFile()) {
589     // this assumes that the PS font name matches the PDF font name
590     psName = font->getName()->getCString();
591     setupEmbeddedType1Font(font->getExtFontFile(), psName);
592
593   // check for embedded Type 1C font
594   } else if (embedType1 && font->getType() == fontType1C &&
595              font->getEmbeddedFontID(&fontFileID)) {
596     psName = font->getEmbeddedFontName();
597     setupEmbeddedType1CFont(font, &fontFileID, psName);
598
599   } else if (font->is16Bit() && font->getCharSet16() == font16AdobeJapan12) {
600     psName = "Ryumin-Light-RKSJ";
601     do16Bit = gTrue;
602
603   // do font substitution
604   } else {
605     if (!type3Warning && font->getType() == fontType3) {
606       error(-1, "This document uses Type 3 fonts - some text may not be correctly printed");
607       type3Warning = gTrue;
608     }
609     name = font->getName();
610     psName = NULL;
611     if (name) {
612       for (i = 0; psFonts[i].name; ++i) {
613         if (name->cmp(psFonts[i].name) == 0) {
614           psName = psFonts[i].psName;
615           break;
616         }
617       }
618     }
619     if (!psName) {
620       if (font->isFixedWidth())
621         i = 8;
622       else if (font->isSerif())
623         i = 4;
624       else
625         i = 0;
626       if (font->isBold())
627         i += 2;
628       if (font->isItalic())
629         i += 1;
630       psName = psSubstFonts[i].psName;
631       if ((code = font->getCharCode("m")) >= 0) {
632         w1 = font->getWidth(code);
633       } else {
634         w1 = 0;
635       }
636       w2 = psSubstFonts[i].mWidth;
637       xs = w1 / w2;
638       if (xs < 0.1) {
639         xs = 1;
640       }
641       if (font->getType() == fontType3) {
642         // This is a hack which makes it possible to substitute for some
643         // Type 3 fonts.  The problem is that it's impossible to know what
644         // the base coordinate system used in the font is without actually
645         // rendering the font.
646         ys = xs;
647         fm = font->getFontMatrix();
648         if (fm[0] != 0) {
649           ys *= fm[3] / fm[0];
650         }
651       } else {
652         ys = 1;
653       }
654     }
655   }
656
657   // generate PostScript code to set up the font
658   if (do16Bit) {
659     writePS("/F%d_%d /%s pdfMakeFont16\n",
660             font->getID().num, font->getID().gen, psName);
661   } else {
662     writePS("/F%d_%d /%s %g %g\n",
663             font->getID().num, font->getID().gen, psName, xs, ys);
664     for (i = 0; i < 256; i += 8) {
665       writePS((i == 0) ? "[ " : "  ");
666       for (j = 0; j < 8; ++j) {
667         charName = font->getCharName(i+j);
668         writePS("/%s", charName ? charName : ".notdef");
669       }
670       writePS((i == 256-8) ? "]\n" : "\n");
671     }
672     writePS("pdfMakeFont\n");
673   }
674 }
675
676 void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
677   static char hexChar[17] = "0123456789abcdef";
678   Object refObj, strObj, obj1, obj2;
679   Dict *dict;
680   int length1, length2;
681   int c;
682   int start[4];
683   GBool binMode;
684   int i;
685
686   // check if font is already embedded
687   for (i = 0; i < fontFileIDLen; ++i) {
688     if (fontFileIDs[i].num == id->num &&
689         fontFileIDs[i].gen == id->gen)
690       return;
691   }
692
693   // add entry to fontFileIDs list
694   if (fontFileIDLen >= fontFileIDSize) {
695     fontFileIDSize += 64;
696     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
697   }
698   fontFileIDs[fontFileIDLen++] = *id;
699
700   // get the font stream and info
701   refObj.initRef(id->num, id->gen);
702   refObj.fetch(&strObj);
703   refObj.free();
704   if (!strObj.isStream()) {
705     error(-1, "Embedded font file object is not a stream");
706     goto err1;
707   }
708   if (!(dict = strObj.streamGetDict())) {
709     error(-1, "Embedded font stream is missing its dictionary");
710     goto err1;
711   }
712   dict->lookup("Length1", &obj1);
713   dict->lookup("Length2", &obj2);
714   if (!obj1.isInt() || !obj2.isInt()) {
715     error(-1, "Missing length fields in embedded font stream dictionary");
716     obj1.free();
717     obj2.free();
718     goto err1;
719   }
720   length1 = obj1.getInt();
721   length2 = obj2.getInt();
722   obj1.free();
723   obj2.free();
724
725   // beginning comment
726   if (psOutEPS) {
727     writePS("%%%%BeginResource: font %s\n", psName);
728     embFontList->append("%%+ font ");
729     embFontList->append(psName);
730     embFontList->append("\n");
731   }
732
733   // copy ASCII portion of font
734   strObj.streamReset();
735   for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i)
736     fputc(c, f);
737
738   // figure out if encrypted portion is binary or ASCII
739   binMode = gFalse;
740   for (i = 0; i < 4; ++i) {
741     start[i] = strObj.streamGetChar();
742     if (start[i] == EOF) {
743       error(-1, "Unexpected end of file in embedded font stream");
744       goto err1;
745     }
746     if (!((start[i] >= '0' && start[i] <= '9') ||
747           (start[i] >= 'A' && start[i] <= 'F') ||
748           (start[i] >= 'a' && start[i] <= 'f')))
749       binMode = gTrue;
750   }
751
752   // convert binary data to ASCII
753   if (binMode) {
754     for (i = 0; i < 4; ++i) {
755       fputc(hexChar[(start[i] >> 4) & 0x0f], f);
756       fputc(hexChar[start[i] & 0x0f], f);
757     }
758     while (i < length2) {
759       if ((c = strObj.streamGetChar()) == EOF)
760         break;
761       fputc(hexChar[(c >> 4) & 0x0f], f);
762       fputc(hexChar[c & 0x0f], f);
763       if (++i % 32 == 0)
764         fputc('\n', f);
765     }
766     if (i % 32 > 0)
767       fputc('\n', f);
768
769   // already in ASCII format -- just copy it
770   } else {
771     for (i = 0; i < 4; ++i)
772       fputc(start[i], f);
773     for (i = 4; i < length2; ++i) {
774       if ((c = strObj.streamGetChar()) == EOF)
775         break;
776       fputc(c, f);
777     }
778   }
779
780   // write padding and "cleartomark"
781   for (i = 0; i < 8; ++i)
782     writePS("00000000000000000000000000000000"
783             "00000000000000000000000000000000\n");
784   writePS("cleartomark\n");
785
786   // ending comment
787   if (psOutEPS) {
788     writePS("%%%%EndResource\n");
789   }
790
791  err1:
792   strObj.streamClose();
793   strObj.free();
794 }
795
796 //~ This doesn't handle .pfb files or binary eexec data (which only
797 //~ happens in pfb files?).
798 void PSOutputDev::setupEmbeddedType1Font(GString *fileName, char *psName) {
799   FILE *fontFile;
800   int c;
801   int i;
802
803   // check if font is already embedded
804   for (i = 0; i < fontFileNameLen; ++i) {
805     if (!fontFileNames[i]->cmp(fileName)) {
806       return;
807     }
808   }
809
810   // add entry to fontFileNames list
811   if (fontFileNameLen >= fontFileNameSize) {
812     fontFileNameSize += 64;
813     fontFileNames = (GString **)grealloc(fontFileNames,
814                                          fontFileNameSize * sizeof(GString *));
815   }
816   fontFileNames[fontFileNameLen++] = fileName->copy();
817
818   // beginning comment
819   if (psOutEPS) {
820     writePS("%%%%BeginResource: font %s\n", psName);
821     embFontList->append("%%+ font ");
822     embFontList->append(psName);
823     embFontList->append("\n");
824   }
825
826   // copy the font file
827   if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
828     error(-1, "Couldn't open external font file");
829     return;
830   }
831   while ((c = fgetc(fontFile)) != EOF)
832     fputc(c, f);
833   fclose(fontFile);
834
835   // ending comment
836   if (psOutEPS) {
837     writePS("%%%%EndResource\n");
838   }
839 }
840
841 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
842                                           char *psName) {
843   char *fontBuf;
844   int fontLen;
845   Type1CFontConverter *cvt;
846   int i;
847
848   // check if font is already embedded
849   for (i = 0; i < fontFileIDLen; ++i) {
850     if (fontFileIDs[i].num == id->num &&
851         fontFileIDs[i].gen == id->gen)
852       return;
853   }
854
855   // add entry to fontFileIDs list
856   if (fontFileIDLen >= fontFileIDSize) {
857     fontFileIDSize += 64;
858     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
859   }
860   fontFileIDs[fontFileIDLen++] = *id;
861
862   // beginning comment
863   if (psOutEPS) {
864     writePS("%%%%BeginResource: font %s\n", psName);
865     embFontList->append("%%+ font ");
866     embFontList->append(psName);
867     embFontList->append("\n");
868   }
869
870   // convert it to a Type 1 font
871   fontBuf = font->readEmbFontFile(&fontLen);
872   cvt = new Type1CFontConverter(fontBuf, fontLen, f);
873   cvt->convert();
874   delete cvt;
875   gfree(fontBuf);
876
877   // ending comment
878   if (psOutEPS) {
879     writePS("%%%%EndResource\n");
880   }
881 }
882
883 void PSOutputDev::setupImages(Dict *resDict) {
884   Object xObjDict, xObj, xObjRef, subtypeObj;
885   int i;
886
887   if (!doForm) {
888     return;
889   }
890
891   resDict->lookup("XObject", &xObjDict);
892   if (xObjDict.isDict()) {
893     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
894       xObjDict.dictGetValNF(i, &xObjRef);
895       xObjDict.dictGetVal(i, &xObj);
896       if (xObj.isStream()) {
897         xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
898         if (subtypeObj.isName("Image")) {
899           if (xObjRef.isRef()) {
900             setupImage(xObjRef.getRef(), xObj.getStream());
901           } else {
902             error(-1, "Image in resource dict is not an indirect reference");
903           }
904         }
905         subtypeObj.free();
906       }
907       xObj.free();
908       xObjRef.free();
909     }
910   }
911   xObjDict.free();
912 }
913
914 void PSOutputDev::setupImage(Ref id, Stream *str) {
915   int c;
916   int size, line, col, i;
917
918   // construct an encoder stream
919   str = new ASCII85Encoder(str);
920
921   // compute image data size
922   str->reset();
923   col = size = 0;
924   do {
925     do {
926       c = str->getChar();
927     } while (c == '\n' || c == '\r');
928     if (c == '~' || c == EOF) {
929       break;
930     }
931     if (c == 'z') {
932       ++col;
933     } else {
934       ++col;
935       for (i = 1; i <= 4; ++i) {
936         do {
937           c = str->getChar();
938         } while (c == '\n' || c == '\r');
939         if (c == '~' || c == EOF) {
940           break;
941         }
942         ++col;
943       }
944     }
945     if (col > 225) {
946       ++size;
947       col = 0;
948     }
949   } while (c != '~' && c != EOF);
950   ++size;
951   writePS("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
952
953   // write the data into the array
954   str->reset();
955   line = col = 0;
956   writePS("dup 0 <~");
957   do {
958     do {
959       c = str->getChar();
960     } while (c == '\n' || c == '\r');
961     if (c == '~' || c == EOF) {
962       break;
963     }
964     if (c == 'z') {
965       fputc(c, f);
966       ++col;
967     } else {
968       fputc(c, f);
969       ++col;
970       for (i = 1; i <= 4; ++i) {
971         do {
972           c = str->getChar();
973         } while (c == '\n' || c == '\r');
974         if (c == '~' || c == EOF) {
975           break;
976         }
977         fputc(c, f);
978         ++col;
979       }
980     }
981     // each line is: "dup nnnnn <~...data...~> put<eol>"
982     // so max data length = 255 - 20 = 235
983     // chunks are 1 or 4 bytes each, so we have to stop at 232
984     // but make it 225 just to be safe
985     if (col > 225) {
986       writePS("~> put\n");
987       ++line;
988       writePS("dup %d <~", line);
989       col = 0;
990     }
991   } while (c != '~' && c != EOF);
992   writePS("~> put\n");
993   writePS("pop\n");
994
995   delete str;
996 }
997
998 void PSOutputDev::startPage(int pageNum, GfxState *state) {
999   int x1, y1, x2, y2, width, height, t;
1000
1001   if (doForm) {
1002
1003     writePS("/PaintProc {\n");
1004     writePS("begin xpdf begin\n");
1005     writePS("pdfStartPage\n");
1006     tx = ty = 0;
1007     xScale = yScale = 1;
1008     landscape = gFalse;
1009
1010   } else if (psOutEPS) {
1011
1012     writePS("pdfStartPage\n");
1013     tx = ty = 0;
1014     xScale = yScale = 1;
1015     landscape = gFalse;
1016
1017   } else {
1018
1019     writePS("%%%%Page: %d %d\n", pageNum, seqPage);
1020     writePS("%%%%BeginPageSetup\n");
1021
1022     // rotate, translate, and scale page
1023     x1 = (int)(state->getX1() + 0.5);
1024     y1 = (int)(state->getY1() + 0.5);
1025     x2 = (int)(state->getX2() + 0.5);
1026     y2 = (int)(state->getY2() + 0.5);
1027     width = x2 - x1;
1028     height = y2 - y1;
1029     if (width > height && width > paperWidth) {
1030       landscape = gTrue;
1031       writePS("%%%%PageOrientation: Landscape\n");
1032       writePS("pdfStartPage\n");
1033       writePS("90 rotate\n");
1034       tx = -x1;
1035       ty = -(y1 + paperWidth);
1036       t = width;
1037       width = height;
1038       height = t;
1039     } else {
1040       landscape = gFalse;
1041       writePS("%%%%PageOrientation: Portrait\n");
1042       writePS("pdfStartPage\n");
1043       tx = -x1;
1044       ty = -y1;
1045     }
1046     if (width < paperWidth) {
1047       tx += (paperWidth - width) / 2;
1048     }
1049     if (height < paperHeight) {
1050       ty += (paperHeight - height) / 2;
1051     }
1052     if (tx != 0 || ty != 0) {
1053       writePS("%g %g translate\n", tx, ty);
1054     }
1055     if (width > paperWidth || height > paperHeight) {
1056       xScale = (double)paperWidth / (double)width;
1057       yScale = (double)paperHeight / (double)height;
1058       if (yScale < xScale) {
1059         xScale = yScale;
1060       }
1061       writePS("%0.4f %0.4f scale\n", xScale, xScale);
1062     } else {
1063       xScale = yScale = 1;
1064     }
1065
1066     writePS("%%%%EndPageSetup\n");
1067     ++seqPage;
1068   }
1069 }
1070
1071 void PSOutputDev::endPage() {
1072   if (doForm) {
1073     writePS("pdfEndPage\n");
1074     writePS("end end\n");
1075     writePS("} def\n");
1076     writePS("end end\n");
1077   } else {
1078     writePS("showpage\n");
1079     writePS("%%%%PageTrailer\n");
1080     writePS("pdfEndPage\n");
1081   }
1082 }
1083
1084 void PSOutputDev::saveState(GfxState *state) {
1085   writePS("q\n");
1086 }
1087
1088 void PSOutputDev::restoreState(GfxState *state) {
1089   writePS("Q\n");
1090 }
1091
1092 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
1093                             double m21, double m22, double m31, double m32) {
1094   writePS("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
1095 }
1096
1097 void PSOutputDev::updateLineDash(GfxState *state) {
1098   double *dash;
1099   double start;
1100   int length, i;
1101
1102   state->getLineDash(&dash, &length, &start);
1103   writePS("[");
1104   for (i = 0; i < length; ++i)
1105     writePS("%g%s", dash[i], (i == length-1) ? "" : " ");
1106   writePS("] %g d\n", start);
1107 }
1108
1109 void PSOutputDev::updateFlatness(GfxState *state) {
1110   writePS("%d i\n", state->getFlatness());
1111 }
1112
1113 void PSOutputDev::updateLineJoin(GfxState *state) {
1114   writePS("%d j\n", state->getLineJoin());
1115 }
1116
1117 void PSOutputDev::updateLineCap(GfxState *state) {
1118   writePS("%d J\n", state->getLineCap());
1119 }
1120
1121 void PSOutputDev::updateMiterLimit(GfxState *state) {
1122   writePS("%g M\n", state->getMiterLimit());
1123 }
1124
1125 void PSOutputDev::updateLineWidth(GfxState *state) {
1126   writePS("%g w\n", state->getLineWidth());
1127 }
1128
1129 void PSOutputDev::updateFillColor(GfxState *state) {
1130   GfxRGB rgb;
1131   GfxCMYK cmyk;
1132
1133   if (psOutLevel1Sep) {
1134     state->getFillCMYK(&cmyk);
1135     writePS("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1136   } else {
1137     state->getFillRGB(&rgb);
1138     if (rgb.r == rgb.g && rgb.g == rgb.b) {
1139       writePS("%g g\n", rgb.r);
1140     } else {
1141       writePS("%g %g %g rg\n", rgb.r, rgb.g, rgb.b);
1142     }
1143   }
1144 }
1145
1146 void PSOutputDev::updateStrokeColor(GfxState *state) {
1147   GfxRGB rgb;
1148   GfxCMYK cmyk;
1149
1150   if (psOutLevel1Sep) {
1151     state->getStrokeCMYK(&cmyk);
1152     writePS("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1153   } else {
1154     state->getStrokeRGB(&rgb);
1155     if (rgb.r == rgb.g && rgb.g == rgb.b) {
1156       writePS("%g G\n", rgb.r);
1157     } else {
1158       writePS("%g %g %g RG\n", rgb.r, rgb.g, rgb.b);
1159     }
1160   }
1161 }
1162
1163 void PSOutputDev::updateFont(GfxState *state) {
1164   if (state->getFont()) {
1165     writePS("/F%d_%d %g Tf\n",
1166             state->getFont()->getID().num, state->getFont()->getID().gen,
1167             state->getFontSize());
1168   }
1169 }
1170
1171 void PSOutputDev::updateTextMat(GfxState *state) {
1172   double *mat;
1173
1174   mat = state->getTextMat();
1175   writePS("[%g %g %g %g %g %g] Tm\n",
1176           mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
1177 }
1178
1179 void PSOutputDev::updateCharSpace(GfxState *state) {
1180   writePS("%g Tc\n", state->getCharSpace());
1181 }
1182
1183 void PSOutputDev::updateRender(GfxState *state) {
1184   writePS("%d Tr\n", state->getRender());
1185 }
1186
1187 void PSOutputDev::updateRise(GfxState *state) {
1188   writePS("%g Ts\n", state->getRise());
1189 }
1190
1191 void PSOutputDev::updateWordSpace(GfxState *state) {
1192   writePS("%g Tw\n", state->getWordSpace());
1193 }
1194
1195 void PSOutputDev::updateHorizScaling(GfxState *state) {
1196   writePS("%g Tz\n", state->getHorizScaling());
1197 }
1198
1199 void PSOutputDev::updateTextPos(GfxState *state) {
1200   writePS("%g %g Td\n", state->getLineX(), state->getLineY());
1201 }
1202
1203 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
1204   writePS("%g TJm\n", shift);
1205 }
1206
1207 void PSOutputDev::stroke(GfxState *state) {
1208   doPath(state->getPath());
1209   writePS("S\n");
1210 }
1211
1212 void PSOutputDev::fill(GfxState *state) {
1213   doPath(state->getPath());
1214   writePS("f\n");
1215 }
1216
1217 void PSOutputDev::eoFill(GfxState *state) {
1218   doPath(state->getPath());
1219   writePS("f*\n");
1220 }
1221
1222 void PSOutputDev::clip(GfxState *state) {
1223   doPath(state->getPath());
1224   writePS("W\n");
1225 }
1226
1227 void PSOutputDev::eoClip(GfxState *state) {
1228   doPath(state->getPath());
1229   writePS("W*\n");
1230 }
1231
1232 void PSOutputDev::doPath(GfxPath *path) {
1233   GfxSubpath *subpath;
1234   double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
1235   int n, m, i, j;
1236
1237   n = path->getNumSubpaths();
1238
1239   if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
1240     subpath = path->getSubpath(0);
1241     x0 = subpath->getX(0);
1242     y0 = subpath->getY(0);
1243     x4 = subpath->getX(4);
1244     y4 = subpath->getY(4);
1245     if (x4 == x0 && y4 == y0) {
1246       x1 = subpath->getX(1);
1247       y1 = subpath->getY(1);
1248       x2 = subpath->getX(2);
1249       y2 = subpath->getY(2);
1250       x3 = subpath->getX(3);
1251       y3 = subpath->getY(3);
1252       if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
1253         writePS("%g %g %g %g re\n",
1254                 x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
1255                 fabs(x2 - x0), fabs(y1 - y0));
1256         return;
1257       } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
1258         writePS("%g %g %g %g re\n",
1259                 x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
1260                 fabs(x1 - x0), fabs(y2 - y0));
1261         return;
1262       }
1263     }
1264   }
1265
1266   for (i = 0; i < n; ++i) {
1267     subpath = path->getSubpath(i);
1268     m = subpath->getNumPoints();
1269     writePS("%g %g m\n", subpath->getX(0), subpath->getY(0));
1270     j = 1;
1271     while (j < m) {
1272       if (subpath->getCurve(j)) {
1273         writePS("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
1274                 subpath->getX(j+1), subpath->getY(j+1),
1275                 subpath->getX(j+2), subpath->getY(j+2));
1276         j += 3;
1277       } else {
1278         writePS("%g %g l\n", subpath->getX(j), subpath->getY(j));
1279         ++j;
1280       }
1281     }
1282     if (subpath->isClosed()) {
1283       writePS("h\n");
1284     }
1285   }
1286 }
1287
1288 void PSOutputDev::drawString(GfxState *state, GString *s) {
1289   // check for invisible text -- this is used by Acrobat Capture
1290   if ((state->getRender() & 3) == 3)
1291     return;
1292
1293   writePSString(s);
1294   writePS(" %g Tj\n", state->getFont()->getWidth(s));
1295 }
1296
1297 void PSOutputDev::drawString16(GfxState *state, GString *s) {
1298   int c1, c2;
1299   double w;
1300   int i;
1301
1302   // check for invisible text -- this is used by Acrobat Capture
1303   if ((state->getRender() & 3) == 3)
1304     return;
1305
1306   switch (state->getFont()->getCharSet16()) {
1307
1308   case font16AdobeJapan12:
1309 #if JAPANESE_SUPPORT
1310     writePS("<");
1311     w = 0;
1312     for (i = 0; i < s->getLength(); i += 2) {
1313       c1 = ((s->getChar(i) & 0xff) << 8) + (s->getChar(i+1) & 0xff);
1314       if (c1 <= 8285) {
1315         c2 = japan12ToRKSJ[c1];
1316       } else {
1317         c2 = 0x20;
1318       }
1319       if (c2 <= 0xff) {
1320         writePS("%02x", c2);
1321       } else {
1322         writePS("%02x%02x", c2 >> 8, c2 & 0xff);
1323       }
1324       w += state->getFont()->getWidth16(c1);
1325     }
1326     writePS("> %g Tj\n", w);
1327 #endif
1328     break;
1329
1330   case font16AdobeGB12:
1331     break;
1332
1333   case font16AdobeCNS13:
1334     break;
1335   }
1336 }
1337
1338 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1339                                 int width, int height, GBool invert,
1340                                 GBool inlineImg) {
1341   int len;
1342
1343   len = height * ((width + 7) / 8);
1344   if (psOutLevel1 || psOutLevel1Sep) {
1345     doImageL1(NULL, invert, inlineImg, str, width, height, len);
1346   } else {
1347     doImageL2(ref, NULL, invert, inlineImg, str, width, height, len);
1348   }
1349 }
1350
1351 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1352                             int width, int height, GfxImageColorMap *colorMap,
1353                             GBool inlineImg) {
1354   int len;
1355
1356   len = height * ((width * colorMap->getNumPixelComps() *
1357                    colorMap->getBits() + 7) / 8);
1358   if (psOutLevel1) {
1359     doImageL1(colorMap, gFalse, inlineImg, str, width, height, len);
1360   } else if (psOutLevel1Sep) {
1361     //~ handle indexed, separation, ... color spaces
1362     doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
1363   } else {
1364     doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len);
1365   }
1366 }
1367
1368 void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
1369                             GBool invert, GBool inlineImg,
1370                             Stream *str, int width, int height, int len) {
1371   ImageStream *imgStr;
1372   Guchar pixBuf[gfxColorMaxComps];
1373   double gray;
1374   int x, y, i;
1375
1376   // width, height, matrix, bits per component
1377   if (colorMap) {
1378     writePS("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
1379             width, height,
1380             width, -height, height);
1381   } else {
1382     writePS("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
1383             width, height, invert ? "true" : "false",
1384             width, -height, height);
1385   }
1386
1387   // image
1388   if (colorMap) {
1389
1390     // set up to process the data stream
1391     imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
1392                              colorMap->getBits());
1393     imgStr->reset();
1394
1395     // process the data stream
1396     i = 0;
1397     for (y = 0; y < height; ++y) {
1398
1399       // write the line
1400       for (x = 0; x < width; ++x) {
1401         imgStr->getPixel(pixBuf);
1402         colorMap->getGray(pixBuf, &gray);
1403         fprintf(f, "%02x", (int)(gray * 255 + 0.5));
1404         if (++i == 32) {
1405           fputc('\n', f);
1406           i = 0;
1407         }
1408       }
1409     }
1410     if (i != 0)
1411       fputc('\n', f);
1412     delete imgStr;
1413
1414   // imagemask
1415   } else {
1416     str->reset();
1417     i = 0;
1418     for (y = 0; y < height; ++y) {
1419       for (x = 0; x < width; x += 8) {
1420         fprintf(f, "%02x", str->getChar() & 0xff);
1421         if (++i == 32) {
1422           fputc('\n', f);
1423           i = 0;
1424         }
1425       }
1426     }
1427     if (i != 0)
1428       fputc('\n', f);
1429   }
1430 }
1431
1432 void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
1433                                GBool invert, GBool inlineImg,
1434                                Stream *str, int width, int height, int len) {
1435   ImageStream *imgStr;
1436   Guchar *lineBuf;
1437   Guchar pixBuf[gfxColorMaxComps];
1438   GfxCMYK cmyk;
1439   int x, y, i, comp;
1440
1441   // width, height, matrix, bits per component
1442   writePS("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n",
1443             width, height,
1444             width, -height, height);
1445
1446   // allocate a line buffer
1447   lineBuf = (Guchar *)gmalloc(4 * width);
1448
1449   // set up to process the data stream
1450   imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
1451                            colorMap->getBits());
1452   imgStr->reset();
1453
1454   // process the data stream
1455   i = 0;
1456   for (y = 0; y < height; ++y) {
1457
1458     // read the line
1459     for (x = 0; x < width; ++x) {
1460       imgStr->getPixel(pixBuf);
1461       colorMap->getCMYK(pixBuf, &cmyk);
1462       lineBuf[4*x+0] = (int)(255 * cmyk.c + 0.5);
1463       lineBuf[4*x+1] = (int)(255 * cmyk.m + 0.5);
1464       lineBuf[4*x+2] = (int)(255 * cmyk.y + 0.5);
1465       lineBuf[4*x+3] = (int)(255 * cmyk.k + 0.5);
1466     }
1467
1468     // write one line of each color component
1469     for (comp = 0; comp < 4; ++comp) {
1470       for (x = 0; x < width; ++x) {
1471         fprintf(f, "%02x", lineBuf[4*x + comp]);
1472         if (++i == 32) {
1473           fputc('\n', f);
1474           i = 0;
1475         }
1476       }
1477     }
1478   }
1479
1480   if (i != 0) {
1481     fputc('\n', f);
1482   }
1483
1484   delete imgStr;
1485   gfree(lineBuf);
1486 }
1487
1488 void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
1489                             GBool invert, GBool inlineImg,
1490                             Stream *str, int width, int height, int len) {
1491   GString *s;
1492   int n, numComps;
1493   GBool useRLE, useA85;
1494   int c;
1495   int i;
1496
1497   // color space
1498   if (colorMap) {
1499     dumpColorSpaceL2(colorMap->getColorSpace());
1500     writePS(" setcolorspace\n");
1501   }
1502
1503   // set up to use the array created by setupImages()
1504   if (doForm && !inlineImg) {
1505     writePS("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
1506   }
1507
1508   // image dictionary
1509   writePS("<<\n  /ImageType 1\n");
1510
1511   // width, height, matrix, bits per component
1512   writePS("  /Width %d\n", width);
1513   writePS("  /Height %d\n", height);
1514   writePS("  /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
1515   writePS("  /BitsPerComponent %d\n",
1516           colorMap ? colorMap->getBits() : 1);
1517
1518   // decode 
1519   if (colorMap) {
1520     writePS("  /Decode [");
1521     if (colorMap->getColorSpace()->getMode() == csSeparation) {
1522       //~ this is a kludge -- see comment in dumpColorSpaceL2
1523       n = (1 << colorMap->getBits()) - 1;
1524       writePS("%g %g", colorMap->getDecodeLow(0) * n,
1525               colorMap->getDecodeHigh(0) * n);
1526     } else {
1527       numComps = colorMap->getNumPixelComps();
1528       for (i = 0; i < numComps; ++i) {
1529         if (i > 0) {
1530           writePS(" ");
1531         }
1532         writePS("%g %g", colorMap->getDecodeLow(i),
1533                 colorMap->getDecodeHigh(i));
1534       }
1535     }
1536     writePS("]\n");
1537   } else {
1538     writePS("  /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
1539   }
1540
1541   if (doForm) {
1542
1543     if (inlineImg) {
1544
1545       // data source
1546       writePS("  /DataSource <~\n");
1547
1548       // write image data stream, using ASCII85 encode filter
1549       str = new FixedLengthEncoder(str, len);
1550       str = new ASCII85Encoder(str);
1551       str->reset();
1552       while ((c = str->getChar()) != EOF) {
1553         fputc(c, f);
1554       }
1555       fputc('\n', f);
1556       delete str;
1557
1558     } else {
1559       writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
1560     }
1561
1562     // end of image dictionary
1563     writePS(">>\n%s\n", colorMap ? "image" : "imagemask");
1564
1565     // get rid of the array and index
1566     if (!inlineImg) {
1567       writePS("pop pop\n");
1568     }
1569
1570   } else {
1571
1572     // data source
1573     writePS("  /DataSource currentfile\n");
1574     s = str->getPSFilter("    ");
1575     if (inlineImg || !s) {
1576       useRLE = gTrue;
1577       useA85 = gTrue;
1578     } else {
1579       useRLE = gFalse;
1580       useA85 = str->isBinary();
1581     }
1582     if (useA85)
1583       writePS("    /ASCII85Decode filter\n");
1584     if (useRLE)
1585       writePS("    /RunLengthDecode filter\n");
1586     else
1587       writePS("%s", s->getCString());
1588     if (s)
1589       delete s;
1590
1591     // cut off inline image streams at appropriate length
1592     if (inlineImg)
1593       str = new FixedLengthEncoder(str, len);
1594     else if (!useRLE)
1595       str = str->getBaseStream();
1596
1597     // add RunLengthEncode and ASCII85 encode filters
1598     if (useRLE)
1599       str = new RunLengthEncoder(str);
1600     if (useA85)
1601       str = new ASCII85Encoder(str);
1602
1603     // end of image dictionary
1604     writePS(">>\n");
1605 #if OPI_SUPPORT
1606     if (opi13Nest) {
1607       if (inlineImg) {
1608         // this can't happen -- OPI dictionaries are in XObjects
1609         error(-1, "Internal: OPI in inline image");
1610         n = 0;
1611       } else {
1612         // need to read the stream to count characters -- the length
1613         // is data-dependent (because of A85 and RLE filters)
1614         str->reset();
1615         n = 0;
1616         while ((c = str->getChar()) != EOF) {
1617           ++n;
1618         }
1619       }
1620       // +6/7 for "pdfIm\n" / "pdfImM\n"
1621       // +8 for newline + trailer
1622       n += colorMap ? 14 : 15;
1623       writePS("%%%%BeginData: %d Hex Bytes\n", n);
1624     }
1625 #endif
1626     writePS("%s\n", colorMap ? "pdfIm" : "pdfImM");
1627
1628     // copy the stream data
1629     str->reset();
1630     while ((c = str->getChar()) != EOF)
1631       fputc(c, f);
1632
1633     // add newline and trailer to the end
1634     fputc('\n', f);
1635     fputs("%-EOD-\n", f);
1636 #if OPI_SUPPORT
1637     if (opi13Nest) {
1638       writePS("%%%%EndData\n");
1639     }
1640 #endif
1641
1642     // delete encoders
1643     if (useRLE || useA85)
1644       delete str;
1645   }
1646 }
1647
1648 void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
1649   GfxCalGrayColorSpace *calGrayCS;
1650   GfxCalRGBColorSpace *calRGBCS;
1651   GfxLabColorSpace *labCS;
1652   GfxIndexedColorSpace *indexedCS;
1653   GfxSeparationColorSpace *separationCS;
1654   Guchar *lookup;
1655   double x[1], y[gfxColorMaxComps];
1656   int n, numComps;
1657   int i, j, k;
1658
1659   switch (colorSpace->getMode()) {
1660
1661   case csDeviceGray:
1662     writePS("/DeviceGray");
1663     break;
1664
1665   case csCalGray:
1666     calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
1667     writePS("[/CIEBasedA <<\n");
1668     writePS(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
1669     writePS(" /MatrixA [%g %g %g]\n",
1670             calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
1671             calGrayCS->getWhiteZ());
1672     writePS(" /WhitePoint [%g %g %g]\n",
1673             calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
1674             calGrayCS->getWhiteZ());
1675     writePS(" /BlackPoint [%g %g %g]\n",
1676             calGrayCS->getBlackX(), calGrayCS->getBlackY(),
1677             calGrayCS->getBlackZ());
1678     writePS(">>]");
1679     break;
1680
1681   case csDeviceRGB:
1682     writePS("/DeviceRGB");
1683     break;
1684
1685   case csCalRGB:
1686     calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
1687     writePS("[/CIEBasedABC <<\n");
1688     writePS(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
1689             calRGBCS->getGammaR(), calRGBCS->getGammaG(),
1690             calRGBCS->getGammaB());
1691     writePS(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n",
1692             calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
1693             calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
1694             calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
1695             calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
1696             calRGBCS->getMatrix()[8]);
1697     writePS(" /WhitePoint [%g %g %g]\n",
1698             calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
1699             calRGBCS->getWhiteZ());
1700     writePS(" /BlackPoint [%g %g %g]\n",
1701             calRGBCS->getBlackX(), calRGBCS->getBlackY(),
1702             calRGBCS->getBlackZ());
1703     writePS(">>]");
1704     break;
1705
1706   case csDeviceCMYK:
1707     writePS("/DeviceCMYK");
1708     break;
1709
1710   case csLab:
1711     labCS = (GfxLabColorSpace *)colorSpace;
1712     writePS("[/CIEBasedABC <<\n");
1713     writePS(" /RangeABC [0 100 %g %g %g %g]\n",
1714             labCS->getAMin(), labCS->getAMax(),
1715             labCS->getBMin(), labCS->getBMax());
1716     writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
1717     writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
1718     writePS(" /DecodeLMN\n");
1719     writePS("   [{dup 6 29 div ge {dup dup mul mul}\n");
1720     writePS("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
1721             labCS->getWhiteX());
1722     writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
1723     writePS("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
1724             labCS->getWhiteY());
1725     writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
1726     writePS("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
1727             labCS->getWhiteZ());
1728     writePS(" /WhitePoint [%g %g %g]\n",
1729             labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
1730     writePS(" /BlackPoint [%g %g %g]\n",
1731             labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
1732     writePS(">>]");
1733     break;
1734
1735   case csICCBased:
1736     dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt());
1737     break;
1738
1739   case csIndexed:
1740     indexedCS = (GfxIndexedColorSpace *)colorSpace;
1741     writePS("[/Indexed ");
1742     dumpColorSpaceL2(indexedCS->getBase());
1743     n = indexedCS->getIndexHigh();
1744     numComps = indexedCS->getBase()->getNComps();
1745     lookup = indexedCS->getLookup();
1746     writePS(" %d <\n", n);
1747     for (i = 0; i <= n; i += 8) {
1748       writePS("  ");
1749       for (j = i; j < i+8 && j <= n; ++j) {
1750         for (k = 0; k < numComps; ++k) {
1751           writePS("%02x", lookup[j * numComps + k]);
1752         }
1753       }
1754       writePS("\n");
1755     }
1756     writePS(">]");
1757     break;
1758
1759   case csSeparation:
1760     //~ this is a kludge -- the correct thing would to ouput a
1761     //~ separation color space, with the specified alternate color
1762     //~ space and tint transform
1763     separationCS = (GfxSeparationColorSpace *)colorSpace;
1764     writePS(" [/Indexed ");
1765     dumpColorSpaceL2(separationCS->getAlt());
1766     writePS(" 255 <\n");
1767     numComps = separationCS->getAlt()->getNComps();
1768     for (i = 0; i <= 255; i += 8) {
1769       writePS("  ");
1770       for (j = i; j < i+8 && j <= 255; ++j) {
1771         x[0] = (double)j / 255.0;
1772         separationCS->getFunc()->transform(x, y);
1773         for (k = 0; k < numComps; ++k) {
1774           writePS("%02x", (int)(255 * y[k] + 0.5));
1775         }
1776       }
1777       writePS("\n");
1778     }
1779     writePS(">]");
1780     break;
1781
1782   case csDeviceN:
1783     // DeviceN color spaces are a Level 3 PostScript feature.
1784     dumpColorSpaceL2(((GfxDeviceNColorSpace *)colorSpace)->getAlt());
1785     break;
1786
1787   case csPattern:
1788     //~ unimplemented
1789     break;
1790
1791   }
1792 }
1793
1794 #if OPI_SUPPORT
1795 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
1796   Object dict;
1797
1798   if (psOutOPI) {
1799     opiDict->lookup("2.0", &dict);
1800     if (dict.isDict()) {
1801       opiBegin20(state, dict.getDict());
1802       dict.free();
1803     } else {
1804       dict.free();
1805       opiDict->lookup("1.3", &dict);
1806       if (dict.isDict()) {
1807         opiBegin13(state, dict.getDict());
1808       }
1809       dict.free();
1810     }
1811   }
1812 }
1813
1814 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
1815   Object obj1, obj2, obj3, obj4;
1816   double width, height, left, right, top, bottom;
1817   int w, h;
1818   int i;
1819
1820   writePS("%%%%BeginOPI: 2.0\n");
1821   writePS("%%%%Distilled\n");
1822
1823   dict->lookup("F", &obj1);
1824   if (getFileSpec(&obj1, &obj2)) {
1825     writePS("%%%%ImageFileName: %s\n",
1826             obj2.getString()->getCString());
1827     obj2.free();
1828   }
1829   obj1.free();
1830
1831   dict->lookup("MainImage", &obj1);
1832   if (obj1.isString()) {
1833     writePS("%%%%MainImage: %s\n", obj1.getString()->getCString());
1834   }
1835   obj1.free();
1836
1837   //~ ignoring 'Tags' entry
1838   //~ need to use writePSString() and deal with >255-char lines
1839
1840   dict->lookup("Size", &obj1);
1841   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
1842     obj1.arrayGet(0, &obj2);
1843     width = obj2.getNum();
1844     obj2.free();
1845     obj1.arrayGet(1, &obj2);
1846     height = obj2.getNum();
1847     obj2.free();
1848     writePS("%%%%ImageDimensions: %g %g\n", width, height);
1849   }
1850   obj1.free();
1851
1852   dict->lookup("CropRect", &obj1);
1853   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
1854     obj1.arrayGet(0, &obj2);
1855     left = obj2.getNum();
1856     obj2.free();
1857     obj1.arrayGet(1, &obj2);
1858     top = obj2.getNum();
1859     obj2.free();
1860     obj1.arrayGet(2, &obj2);
1861     right = obj2.getNum();
1862     obj2.free();
1863     obj1.arrayGet(3, &obj2);
1864     bottom = obj2.getNum();
1865     obj2.free();
1866     writePS("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
1867   }
1868   obj1.free();
1869
1870   dict->lookup("Overprint", &obj1);
1871   if (obj1.isBool()) {
1872     writePS("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
1873   }
1874   obj1.free();
1875
1876   dict->lookup("Inks", &obj1);
1877   if (obj1.isName()) {
1878     writePS("%%%%ImageInks: %s\n", obj1.getName());
1879   } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
1880     obj1.arrayGet(0, &obj2);
1881     if (obj2.isName()) {
1882       writePS("%%%%ImageInks: %s %d",
1883               obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
1884       for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
1885         obj1.arrayGet(i, &obj3);
1886         obj1.arrayGet(i+1, &obj4);
1887         if (obj3.isString() && obj4.isNum()) {
1888           writePS(" ");
1889           writePSString(obj3.getString());
1890           writePS(" %g", obj4.getNum());
1891         }
1892         obj3.free();
1893         obj4.free();
1894       }
1895       writePS("\n");
1896     }
1897     obj2.free();
1898   }
1899   obj1.free();
1900
1901   writePS("gsave\n");
1902
1903   writePS("%%%%BeginIncludedImage\n");
1904
1905   dict->lookup("IncludedImageDimensions", &obj1);
1906   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
1907     obj1.arrayGet(0, &obj2);
1908     w = obj2.getInt();
1909     obj2.free();
1910     obj1.arrayGet(1, &obj2);
1911     h = obj2.getInt();
1912     obj2.free();
1913     writePS("%%%%IncludedImageDimensions: %d %d\n", w, h);
1914   }
1915   obj1.free();
1916
1917   dict->lookup("IncludedImageQuality", &obj1);
1918   if (obj1.isNum()) {
1919     writePS("%%%%IncludedImageQuality: %g\n", obj1.getNum());
1920   }
1921   obj1.free();
1922
1923   ++opi20Nest;
1924 }
1925
1926 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
1927   Object obj1, obj2;
1928   int left, right, top, bottom, samples, bits, width, height;
1929   double c, m, y, k;
1930   double llx, lly, ulx, uly, urx, ury, lrx, lry;
1931   double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
1932   double horiz, vert;
1933   int i, j;
1934
1935   writePS("save\n");
1936   writePS("/opiMatrix2 matrix currentmatrix def\n");
1937   writePS("opiMatrix setmatrix\n");
1938
1939   dict->lookup("F", &obj1);
1940   if (getFileSpec(&obj1, &obj2)) {
1941     writePS("%%ALDImageFileName: %s\n",
1942             obj2.getString()->getCString());
1943     obj2.free();
1944   }
1945   obj1.free();
1946
1947   dict->lookup("CropRect", &obj1);
1948   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
1949     obj1.arrayGet(0, &obj2);
1950     left = obj2.getInt();
1951     obj2.free();
1952     obj1.arrayGet(1, &obj2);
1953     top = obj2.getInt();
1954     obj2.free();
1955     obj1.arrayGet(2, &obj2);
1956     right = obj2.getInt();
1957     obj2.free();
1958     obj1.arrayGet(3, &obj2);
1959     bottom = obj2.getInt();
1960     obj2.free();
1961     writePS("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
1962   }
1963   obj1.free();
1964
1965   dict->lookup("Color", &obj1);
1966   if (obj1.isArray() && obj1.arrayGetLength() == 5) {
1967     obj1.arrayGet(0, &obj2);
1968     c = obj2.getNum();
1969     obj2.free();
1970     obj1.arrayGet(1, &obj2);
1971     m = obj2.getNum();
1972     obj2.free();
1973     obj1.arrayGet(2, &obj2);
1974     y = obj2.getNum();
1975     obj2.free();
1976     obj1.arrayGet(3, &obj2);
1977     k = obj2.getNum();
1978     obj2.free();
1979     obj1.arrayGet(4, &obj2);
1980     if (obj2.isString()) {
1981       writePS("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
1982       writePSString(obj2.getString());
1983       writePS("\n");
1984     }
1985     obj2.free();
1986   }
1987   obj1.free();
1988
1989   dict->lookup("ColorType", &obj1);
1990   if (obj1.isName()) {
1991     writePS("%%ALDImageColorType: %s\n", obj1.getName());
1992   }
1993   obj1.free();
1994
1995   //~ ignores 'Comments' entry
1996   //~ need to handle multiple lines
1997
1998   dict->lookup("CropFixed", &obj1);
1999   if (obj1.isArray()) {
2000     obj1.arrayGet(0, &obj2);
2001     ulx = obj2.getNum();
2002     obj2.free();
2003     obj1.arrayGet(1, &obj2);
2004     uly = obj2.getNum();
2005     obj2.free();
2006     obj1.arrayGet(2, &obj2);
2007     lrx = obj2.getNum();
2008     obj2.free();
2009     obj1.arrayGet(3, &obj2);
2010     lry = obj2.getNum();
2011     obj2.free();
2012     writePS("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
2013   }
2014   obj1.free();
2015
2016   dict->lookup("GrayMap", &obj1);
2017   if (obj1.isArray()) {
2018     writePS("%%ALDImageGrayMap:");
2019     for (i = 0; i < obj1.arrayGetLength(); i += 16) {
2020       if (i > 0) {
2021         writePS("\n%%%%+");
2022       }
2023       for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
2024         obj1.arrayGet(i+j, &obj2);
2025         writePS(" %d", obj2.getInt());
2026         obj2.free();
2027       }
2028     }
2029     writePS("\n");
2030   }
2031   obj1.free();
2032
2033   dict->lookup("ID", &obj1);
2034   if (obj1.isString()) {
2035     writePS("%%ALDImageID: %s\n", obj1.getString()->getCString());
2036   }
2037   obj1.free();
2038
2039   dict->lookup("ImageType", &obj1);
2040   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
2041     obj1.arrayGet(0, &obj2);
2042     samples = obj2.getInt();
2043     obj2.free();
2044     obj1.arrayGet(1, &obj2);
2045     bits = obj2.getInt();
2046     obj2.free();
2047     writePS("%%ALDImageType: %d %d\n", samples, bits);
2048   }
2049   obj1.free();
2050
2051   dict->lookup("Overprint", &obj1);
2052   if (obj1.isBool()) {
2053     writePS("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
2054   }
2055   obj1.free();
2056
2057   dict->lookup("Position", &obj1);
2058   if (obj1.isArray() && obj1.arrayGetLength() == 8) {
2059     obj1.arrayGet(0, &obj2);
2060     llx = obj2.getNum();
2061     obj2.free();
2062     obj1.arrayGet(1, &obj2);
2063     lly = obj2.getNum();
2064     obj2.free();
2065     obj1.arrayGet(2, &obj2);
2066     ulx = obj2.getNum();
2067     obj2.free();
2068     obj1.arrayGet(3, &obj2);
2069     uly = obj2.getNum();
2070     obj2.free();
2071     obj1.arrayGet(4, &obj2);
2072     urx = obj2.getNum();
2073     obj2.free();
2074     obj1.arrayGet(5, &obj2);
2075     ury = obj2.getNum();
2076     obj2.free();
2077     obj1.arrayGet(6, &obj2);
2078     lrx = obj2.getNum();
2079     obj2.free();
2080     obj1.arrayGet(7, &obj2);
2081     lry = obj2.getNum();
2082     obj2.free();
2083     opiTransform(state, llx, lly, &tllx, &tlly);
2084     opiTransform(state, ulx, uly, &tulx, &tuly);
2085     opiTransform(state, urx, ury, &turx, &tury);
2086     opiTransform(state, lrx, lry, &tlrx, &tlry);
2087     writePS("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
2088             tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
2089     obj2.free();
2090   }
2091   obj1.free();
2092
2093   dict->lookup("Resolution", &obj1);
2094   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
2095     obj1.arrayGet(0, &obj2);
2096     horiz = obj2.getNum();
2097     obj2.free();
2098     obj1.arrayGet(1, &obj2);
2099     vert = obj2.getNum();
2100     obj2.free();
2101     writePS("%%ALDImageResoution: %g %g\n", horiz, vert);
2102     obj2.free();
2103   }
2104   obj1.free();
2105
2106   dict->lookup("Size", &obj1);
2107   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
2108     obj1.arrayGet(0, &obj2);
2109     width = obj2.getInt();
2110     obj2.free();
2111     obj1.arrayGet(1, &obj2);
2112     height = obj2.getInt();
2113     obj2.free();
2114     writePS("%%ALDImageDimensions: %d %d\n", width, height);
2115   }
2116   obj1.free();
2117
2118   //~ ignoring 'Tags' entry
2119   //~ need to use writePSString() and deal with >255-char lines
2120
2121   dict->lookup("Tint", &obj1);
2122   if (obj1.isNum()) {
2123     writePS("%%ALDImageTint: %g\n", obj1.getNum());
2124   }
2125   obj1.free();
2126
2127   dict->lookup("Transparency", &obj1);
2128   if (obj1.isBool()) {
2129     writePS("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
2130   }
2131   obj1.free();
2132
2133   writePS("%%%%BeginObject: image\n");
2134   writePS("opiMatrix2 setmatrix\n");
2135   ++opi13Nest;
2136 }
2137
2138 // Convert PDF user space coordinates to PostScript default user space
2139 // coordinates.  This has to account for both the PDF CTM and the
2140 // PSOutputDev page-fitting transform.
2141 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
2142                                double *x1, double *y1) {
2143   double t;
2144
2145   state->transform(x0, y0, x1, y1);
2146   *x1 += tx;
2147   *y1 += ty;
2148   if (landscape) {
2149     t = *x1;
2150     *x1 = -*y1;
2151     *y1 = t;
2152   }
2153   *x1 *= xScale;
2154   *y1 *= yScale;
2155 }
2156
2157 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
2158   Object dict;
2159
2160   if (psOutOPI) {
2161     opiDict->lookup("2.0", &dict);
2162     if (dict.isDict()) {
2163       writePS("%%%%EndIncludedImage\n");
2164       writePS("%%%%EndOPI\n");
2165       writePS("grestore\n");
2166       --opi20Nest;
2167       dict.free();
2168     } else {
2169       dict.free();
2170       opiDict->lookup("1.3", &dict);
2171       if (dict.isDict()) {
2172         writePS("%%%%EndObject\n");
2173         writePS("restore\n");
2174         --opi13Nest;
2175       }
2176       dict.free();
2177     }
2178   }
2179 }
2180
2181 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
2182   if (fileSpec->isString()) {
2183     fileSpec->copy(fileName);
2184     return gTrue;
2185   }
2186   if (fileSpec->isDict()) {
2187     fileSpec->dictLookup("DOS", fileName);
2188     if (fileName->isString()) {
2189       return gTrue;
2190     }
2191     fileName->free();
2192     fileSpec->dictLookup("Mac", fileName);
2193     if (fileName->isString()) {
2194       return gTrue;
2195     }
2196     fileName->free();
2197     fileSpec->dictLookup("Unix", fileName);
2198     if (fileName->isString()) {
2199       return gTrue;
2200     }
2201     fileName->free();
2202     fileSpec->dictLookup("F", fileName);
2203     if (fileName->isString()) {
2204       return gTrue;
2205     }
2206     fileName->free();
2207   }
2208   return gFalse;
2209 }
2210 #endif // OPI_SUPPORT
2211
2212 void PSOutputDev::writePS(const char *fmt, ...) {
2213   va_list args;
2214
2215   va_start(args, fmt);
2216   vfprintf(f, fmt, args);
2217   va_end(args);
2218 }
2219
2220 void PSOutputDev::writePSString(GString *s) {
2221   Guchar *p;
2222   int n;
2223
2224   fputc('(', f);
2225   for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
2226     if (*p == '(' || *p == ')' || *p == '\\')
2227       fprintf(f, "\\%c", *p);
2228     else if (*p < 0x20 || *p >= 0x80)
2229       fprintf(f, "\\%03o", *p);
2230     else
2231       fputc(*p, f);
2232   }
2233   fputc(')', f);
2234 }