]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/PSOutputDev.cc
34d7fdc96bf406addfbca1f48710743ab3c57e30
[evince.git] / pdf / xpdf / PSOutputDev.cc
1 //========================================================================
2 //
3 // PSOutputDev.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stdio.h>
16 #include <stddef.h>
17 #include <stdarg.h>
18 #include <signal.h>
19 #include <math.h>
20 #include "GString.h"
21 #include "GList.h"
22 #include "config.h"
23 #include "GlobalParams.h"
24 #include "Object.h"
25 #include "Error.h"
26 #include "Function.h"
27 #include "Gfx.h"
28 #include "GfxState.h"
29 #include "GfxFont.h"
30 #include "UnicodeMap.h"
31 #include "FontFile.h"
32 #include "Catalog.h"
33 #include "Page.h"
34 #include "Stream.h"
35 #include "Annot.h"
36 #include "PSOutputDev.h"
37
38 #ifdef MACOS
39 // needed for setting type/creator of MacOS files
40 #include "ICSupport.h"
41 #endif
42
43 //------------------------------------------------------------------------
44 // PostScript prolog and setup
45 //------------------------------------------------------------------------
46
47 static char *prolog[] = {
48   "/xpdf 75 dict def xpdf begin",
49   "% PDF special state",
50   "/pdfDictSize 15 def",
51   "~1",
52   "/pdfStates 64 array def",
53   "  0 1 63 {",
54   "    pdfStates exch pdfDictSize dict",
55   "    dup /pdfStateIdx 3 index put",
56   "    put",
57   "  } for",
58   "~a",
59   "/pdfSetup {",
60   "  3 1 roll 2 array astore",
61   "  /setpagedevice where {",
62   "    pop 3 dict begin",
63   "      /PageSize exch def",
64   "      /ImagingBBox null def",
65   "      /Policies 1 dict dup begin /PageSize 3 def end def",
66   "      { /Duplex true def } if",
67   "    currentdict end setpagedevice",
68   "  } {",
69   "    pop pop",
70   "  } ifelse",
71   "} def",
72   "~1",
73   "/pdfOpNames [",
74   "  /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
75   "  /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
76   "  /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
77   "] def",
78   "~a",
79   "/pdfStartPage {",
80   "~1",
81   "  pdfStates 0 get begin",
82   "~2",
83   "  pdfDictSize dict begin",
84   "~a",
85   "  /pdfFill [0] def",
86   "  /pdfStroke [0] def",
87   "  /pdfLastFill false def",
88   "  /pdfLastStroke false def",
89   "  /pdfTextMat [1 0 0 1 0 0] def",
90   "  /pdfFontSize 0 def",
91   "  /pdfCharSpacing 0 def",
92   "  /pdfTextRender 0 def",
93   "  /pdfTextRise 0 def",
94   "  /pdfWordSpacing 0 def",
95   "  /pdfHorizScaling 1 def",
96   "  /pdfTextClipPath [] def",
97   "} def",
98   "/pdfEndPage { end } def",
99   "% separation convention operators",
100   "/findcmykcustomcolor where {",
101   "  pop",
102   "}{",
103   "  /findcmykcustomcolor { 5 array astore } def",
104   "} ifelse",
105   "/setcustomcolor where {",
106   "  pop",
107   "}{",
108   "  /setcustomcolor {",
109   "    exch",
110   "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
111   "      0 4 getinterval cvx",
112   "      [ exch /dup load exch { mul exch dup } /forall load",
113   "        /pop load dup ] cvx",
114   "    ] setcolorspace setcolor",
115   "  } def",
116   "} ifelse",
117   "/customcolorimage where {",
118   "  pop",
119   "}{",
120   "  /customcolorimage {",
121   "    gsave",
122   "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
123   "      0 4 getinterval",
124   "      [ exch /dup load exch { mul exch dup } /forall load",
125   "        /pop load dup ] cvx",
126   "    ] setcolorspace",
127   "    10 dict begin",
128   "      /ImageType 1 def",
129   "      /DataSource exch def",
130   "      /ImageMatrix exch def",
131   "      /BitsPerComponent exch def",
132   "      /Height exch def",
133   "      /Width exch def",
134   "      /Decode [1 0] def",
135   "    currentdict end",
136   "    image",
137   "    grestore",
138   "  } def",
139   "} ifelse",
140   "% PDF color state",
141   "/sCol {",
142   "  pdfLastStroke not {",
143   "    pdfStroke aload length",
144   "    dup 1 eq {",
145   "      pop setgray",
146   "    }{",
147   "      dup 3 eq {",
148   "        pop setrgbcolor",
149   "      }{",
150   "        4 eq {",
151   "          setcmykcolor",
152   "        }{",
153   "          findcmykcustomcolor exch setcustomcolor",
154   "        } ifelse",
155   "      } ifelse",
156   "    } ifelse",
157   "    /pdfLastStroke true def /pdfLastFill false def",
158   "  } if",
159   "} def",
160   "/fCol {",
161   "  pdfLastFill not {",
162   "    pdfFill aload length",
163   "    dup 1 eq {",
164   "      pop setgray",
165   "    }{",
166   "      dup 3 eq {",
167   "        pop setrgbcolor",
168   "      }{",
169   "        4 eq {",
170   "          setcmykcolor",
171   "        }{",
172   "          findcmykcustomcolor exch setcustomcolor",
173   "        } ifelse",
174   "      } ifelse",
175   "    } ifelse",
176   "    /pdfLastFill true def /pdfLastStroke false def",
177   "  } if",
178   "} def",
179   "% build a font",
180   "/pdfMakeFont {",
181   "  4 3 roll findfont",
182   "  4 2 roll matrix scale makefont",
183   "  dup length dict begin",
184   "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
185   "    /Encoding exch def",
186   "    currentdict",
187   "  end",
188   "  definefont pop",
189   "} def",
190   "/pdfMakeFont16 {",
191   "  exch findfont",
192   "  dup length dict begin",
193   "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
194   "    /WMode exch def",
195   "    currentdict",
196   "  end",
197   "  definefont pop",
198   "} def",
199   "/pdfMakeFont16L3 {",
200   "  1 index /CIDFont resourcestatus {",
201   "    pop pop 1 index /CIDFont findresource /CIDFontType known",
202   "  } {",
203   "    false",
204   "  } ifelse",
205   "  {",
206   "    0 eq { /Identity-H } { /Identity-V } ifelse",
207   "    exch 1 array astore composefont pop",
208   "  } {",
209   "    pdfMakeFont16",
210   "  } ifelse",
211   "} def",
212   "% graphics state operators",
213   "~1",
214   "/q {",
215   "  gsave",
216   "  pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
217   "  pdfStates pdfStateIdx 1 add get begin",
218   "  pdfOpNames { exch def } forall",
219   "} def",
220   "~2",
221   "/q { gsave pdfDictSize dict begin } def",
222   "~a",
223   "/Q { end grestore } def",
224   "/cm { concat } def",
225   "/d { setdash } def",
226   "/i { setflat } def",
227   "/j { setlinejoin } def",
228   "/J { setlinecap } def",
229   "/M { setmiterlimit } def",
230   "/w { setlinewidth } def",
231   "% color operators",
232   "/g { dup 1 array astore /pdfFill exch def setgray",
233   "     /pdfLastFill true def /pdfLastStroke false def } def",
234   "/G { dup 1 array astore /pdfStroke exch def setgray",
235   "     /pdfLastStroke true def /pdfLastFill false def } def",
236   "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor",
237   "      /pdfLastFill true def /pdfLastStroke false def } def",
238   "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor",
239   "      /pdfLastStroke true def /pdfLastFill false def } def",
240   "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
241   "     /pdfLastFill true def /pdfLastStroke false def } def",
242   "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
243   "     /pdfLastStroke true def /pdfLastFill false def } def",
244   "/ck { 6 copy 6 array astore /pdfFill exch def",
245   "      findcmykcustomcolor exch setcustomcolor",
246   "      /pdfLastFill true def /pdfLastStroke false def } def",
247   "/CK { 6 copy 6 array astore /pdfStroke exch def",
248   "      findcmykcustomcolor exch setcustomcolor",
249   "      /pdfLastStroke true def /pdfLastFill false def } def",
250   "% path segment operators",
251   "/m { moveto } def",
252   "/l { lineto } def",
253   "/c { curveto } def",
254   "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
255   "      neg 0 rlineto closepath } def",
256   "/h { closepath } def",
257   "% path painting operators",
258   "/S { sCol stroke } def",
259   "/Sf { fCol stroke } def",
260   "/f { fCol fill } def",
261   "/f* { fCol eofill } def",
262   "% clipping operators",
263   "/W { clip newpath } def",
264   "/W* { eoclip newpath } def",
265   "% text state operators",
266   "/Tc { /pdfCharSpacing exch def } def",
267   "/Tf { dup /pdfFontSize exch def",
268   "      dup pdfHorizScaling mul exch matrix scale",
269   "      pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
270   "      exch findfont exch makefont setfont } def",
271   "/Tr { /pdfTextRender exch def } def",
272   "/Ts { /pdfTextRise exch def } def",
273   "/Tw { /pdfWordSpacing exch def } def",
274   "/Tz { /pdfHorizScaling exch def } def",
275   "% text positioning operators",
276   "/Td { pdfTextMat transform moveto } def",
277   "/Tm { /pdfTextMat exch def } def",
278   "% text string operators",
279   "/cshow where {",
280   "  pop",
281   "  /cshow2 {",
282   "    dup {",
283   "      pop pop",
284   "      1 string dup 0 3 index put 3 index exec",
285   "    } exch cshow",
286   "    pop pop",
287   "  } def",
288   "}{",
289   "  /cshow2 {",
290   "    currentfont /FontType get 0 eq {",
291   "      0 2 2 index length 1 sub {",
292   "        2 copy get exch 1 add 2 index exch get",
293   "        2 copy exch 256 mul add",
294   "        2 string dup 0 6 5 roll put dup 1 5 4 roll put",
295   "        3 index exec",
296   "      } for",
297   "    } {",
298   "      dup {",
299   "        1 string dup 0 3 index put 3 index exec",
300   "      } forall",
301   "    } ifelse",
302   "    pop pop",
303   "  } def",
304   "} ifelse",
305   "/awcp {", // awidthcharpath
306   "  exch {",
307   "    false charpath",
308   "    5 index 5 index rmoveto",
309   "    6 index eq { 7 index 7 index rmoveto } if",
310   "  } exch cshow2",
311   "  6 {pop} repeat",
312   "} def",
313   "/Tj {",
314   "  fCol",  // because stringwidth has to draw Type 3 chars
315   "  1 index stringwidth pdfTextMat idtransform pop",
316   "  sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
317   "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
318   "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
319   "  pdfTextMat dtransform",
320   "  6 5 roll Tj1",
321   "} def",
322   "/Tj16 {",
323   "  fCol",  // because stringwidth has to draw Type 3 chars
324   "  2 index stringwidth pdfTextMat idtransform pop",
325   "  sub exch div",
326   "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
327   "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
328   "  pdfTextMat dtransform",
329   "  6 5 roll Tj1",
330   "} def",
331   "/Tj16V {",
332   "  fCol",  // because stringwidth has to draw Type 3 chars
333   "  2 index stringwidth pdfTextMat idtransform exch pop",
334   "  sub exch div",
335   "  0 pdfWordSpacing pdfTextMat dtransform 32",
336   "  4 3 roll pdfCharSpacing add 0 exch",
337   "  pdfTextMat dtransform",
338   "  6 5 roll Tj1",
339   "} def",
340   "/Tj1 {",
341   "  0 pdfTextRise pdfTextMat dtransform rmoveto",
342   "  currentpoint 8 2 roll",
343   "  pdfTextRender 1 and 0 eq {",
344   "    6 copy awidthshow",
345   "  } if",
346   "  pdfTextRender 3 and dup 1 eq exch 2 eq or {",
347   "    7 index 7 index moveto",
348   "    6 copy",
349   "    currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
350   "    false awcp currentpoint stroke moveto",
351   "  } if",
352   "  pdfTextRender 4 and 0 ne {",
353   "    8 6 roll moveto",
354   "    false awcp",
355   "    /pdfTextClipPath [ pdfTextClipPath aload pop",
356   "      {/moveto cvx}",
357   "      {/lineto cvx}",
358   "      {/curveto cvx}",
359   "      {/closepath cvx}",
360   "    pathforall ] def",
361   "    currentpoint newpath moveto",
362   "  } {",
363   "    8 {pop} repeat",
364   "  } ifelse",
365   "  0 pdfTextRise neg pdfTextMat dtransform rmoveto",
366   "} def",
367   "/TJm { pdfFontSize 0.001 mul mul neg 0",
368   "       pdfTextMat dtransform rmoveto } def",
369   "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
370   "        pdfTextMat dtransform rmoveto } def",
371   "/Tclip { pdfTextClipPath cvx exec clip",
372   "         /pdfTextClipPath [] def } def",
373   "% Level 1 image operators",
374   "/pdfIm1 {",
375   "  /pdfImBuf1 4 index string def",
376   "  { currentfile pdfImBuf1 readhexstring pop } image",
377   "} def",
378   "/pdfIm1Sep {",
379   "  /pdfImBuf1 4 index string def",
380   "  /pdfImBuf2 4 index string def",
381   "  /pdfImBuf3 4 index string def",
382   "  /pdfImBuf4 4 index string def",
383   "  { currentfile pdfImBuf1 readhexstring pop }",
384   "  { currentfile pdfImBuf2 readhexstring pop }",
385   "  { currentfile pdfImBuf3 readhexstring pop }",
386   "  { currentfile pdfImBuf4 readhexstring pop }",
387   "  true 4 colorimage",
388   "} def",
389   "/pdfImM1 {",
390   "  /pdfImBuf1 4 index 7 add 8 idiv string def",
391   "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
392   "} def",
393   "% Level 2 image operators",
394   "/pdfImBuf 100 string def",
395   "/pdfIm {",
396   "  image",
397   "  { currentfile pdfImBuf readline",
398   "    not { pop exit } if",
399   "    (%-EOD-) eq { exit } if } loop",
400   "} def",
401   "/pdfImSep {",
402   "  findcmykcustomcolor exch",
403   "  dup /Width get /pdfImBuf1 exch string def",
404   "  dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
405   "  /pdfImDecodeLow exch def",
406   "  begin Width Height BitsPerComponent ImageMatrix DataSource end",
407   "  /pdfImData exch def",
408   "  { pdfImData pdfImBuf1 readstring pop",
409   "    0 1 2 index length 1 sub {",
410   "      1 index exch 2 copy get",
411   "      pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
412   "      255 exch sub put",
413   "    } for }",
414   "  6 5 roll customcolorimage",
415   "  { currentfile pdfImBuf readline",
416   "    not { pop exit } if",
417   "    (%-EOD-) eq { exit } if } loop",
418   "} def",
419   "/pdfImM {",
420   "  fCol imagemask",
421   "  { currentfile pdfImBuf readline",
422   "    not { pop exit } if",
423   "    (%-EOD-) eq { exit } if } loop",
424   "} def",
425   "end",
426   NULL
427 };
428
429 static char *cmapProlog[] = {
430   "/CIDInit /ProcSet findresource begin",
431   "10 dict begin",
432   "  begincmap",
433   "  /CMapType 1 def",
434   "  /CMapName /Identity-H def",
435   "  /CIDSystemInfo 3 dict dup begin",
436   "    /Registry (Adobe) def",
437   "    /Ordering (Identity) def",
438   "    /Supplement 0 def",
439   "  end def",
440   "  1 begincodespacerange",
441   "    <0000> <ffff>",
442   "  endcodespacerange",
443   "  0 usefont",
444   "  1 begincidrange",
445   "    <0000> <ffff> 0",
446   "  endcidrange",
447   "  endcmap",
448   "  currentdict CMapName exch /CMap defineresource pop",
449   "end",
450   "10 dict begin",
451   "  begincmap",
452   "  /CMapType 1 def",
453   "  /CMapName /Identity-V def",
454   "  /CIDSystemInfo 3 dict dup begin",
455   "    /Registry (Adobe) def",
456   "    /Ordering (Identity) def",
457   "    /Supplement 0 def",
458   "  end def",
459   "  /WMode 1 def",
460   "  1 begincodespacerange",
461   "    <0000> <ffff>",
462   "  endcodespacerange",
463   "  0 usefont",
464   "  1 begincidrange",
465   "    <0000> <ffff> 0",
466   "  endcidrange",
467   "  endcmap",
468   "  currentdict CMapName exch /CMap defineresource pop",
469   "end",
470   "end",
471   NULL
472 };
473
474 //------------------------------------------------------------------------
475 // Fonts
476 //------------------------------------------------------------------------
477
478 struct PSSubstFont {
479   char *psName;                 // PostScript name
480   double mWidth;                // width of 'm' character
481 };
482
483 static char *psFonts[] = {
484   "Courier",
485   "Courier-Bold",
486   "Courier-Oblique",
487   "Courier-BoldOblique",
488   "Helvetica",
489   "Helvetica-Bold",
490   "Helvetica-Oblique",
491   "Helvetica-BoldOblique",
492   "Symbol",
493   "Times-Roman",
494   "Times-Bold",
495   "Times-Italic",
496   "Times-BoldItalic",
497   "ZapfDingbats",
498   NULL
499 };
500
501 static PSSubstFont psSubstFonts[] = {
502   {"Helvetica",             0.833},
503   {"Helvetica-Oblique",     0.833},
504   {"Helvetica-Bold",        0.889},
505   {"Helvetica-BoldOblique", 0.889},
506   {"Times-Roman",           0.788},
507   {"Times-Italic",          0.722},
508   {"Times-Bold",            0.833},
509   {"Times-BoldItalic",      0.778},
510   {"Courier",               0.600},
511   {"Courier-Oblique",       0.600},
512   {"Courier-Bold",          0.600},
513   {"Courier-BoldOblique",   0.600}
514 };
515
516 // Encoding info for substitute 16-bit font
517 struct PSFont16Enc {
518   Ref fontID;
519   GString *enc;
520 };
521
522 //------------------------------------------------------------------------
523 // process colors
524 //------------------------------------------------------------------------
525
526 #define psProcessCyan     1
527 #define psProcessMagenta  2
528 #define psProcessYellow   4
529 #define psProcessBlack    8
530 #define psProcessCMYK    15
531
532 //------------------------------------------------------------------------
533 // PSOutCustomColor
534 //------------------------------------------------------------------------
535
536 class PSOutCustomColor {
537 public:
538
539   PSOutCustomColor(double cA, double mA,
540                    double yA, double kA, GString *nameA);
541   ~PSOutCustomColor();
542
543   double c, m, y, k;
544   GString *name;
545   PSOutCustomColor *next;
546 };
547
548 PSOutCustomColor::PSOutCustomColor(double cA, double mA,
549                                    double yA, double kA, GString *nameA) {
550   c = cA;
551   m = mA;
552   y = yA;
553   k = kA;
554   name = nameA;
555   next = NULL;
556 }
557
558 PSOutCustomColor::~PSOutCustomColor() {
559   delete name;
560 }
561
562 //------------------------------------------------------------------------
563 // PSOutputDev
564 //------------------------------------------------------------------------
565
566 extern "C" {
567 typedef void (*SignalFunc)(int);
568 }
569
570 static void outputToFile(void *stream, char *data, int len) {
571   fwrite(data, 1, len, (FILE *)stream);
572 }
573
574 PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
575                          int firstPage, int lastPage, PSOutMode modeA,
576                          int paperWidthA, int paperHeightA,
577                          GBool manualCtrlA) {
578   FILE *f;
579   PSFileType fileTypeA;
580
581   underlayCbk = NULL;
582   underlayCbkData = NULL;
583   overlayCbk = NULL;
584   overlayCbkData = NULL;
585
586   fontIDs = NULL;
587   fontFileIDs = NULL;
588   fontFileNames = NULL;
589   font16Enc = NULL;
590   xobjStack = NULL;
591   embFontList = NULL;
592   customColors = NULL;
593   haveTextClip = gFalse;
594   t3String = NULL;
595
596   // open file or pipe
597   if (!strcmp(fileName, "-")) {
598     fileTypeA = psStdout;
599     f = stdout;
600   } else if (fileName[0] == '|') {
601     fileTypeA = psPipe;
602 #ifdef HAVE_POPEN
603 #ifndef WIN32
604     signal(SIGPIPE, (SignalFunc)SIG_IGN);
605 #endif
606     if (!(f = popen(fileName + 1, "w"))) {
607       error(-1, "Couldn't run print command '%s'", fileName);
608       ok = gFalse;
609       return;
610     }
611 #else
612     error(-1, "Print commands are not supported ('%s')", fileName);
613     ok = gFalse;
614     return;
615 #endif
616   } else {
617     fileTypeA = psFile;
618     if (!(f = fopen(fileName, "w"))) {
619       error(-1, "Couldn't open PostScript file '%s'", fileName);
620       ok = gFalse;
621       return;
622     }
623   }
624
625   init(outputToFile, f, fileTypeA,
626        xrefA, catalog, firstPage, lastPage, modeA,
627        paperWidthA, paperHeightA, manualCtrlA);
628 }
629
630 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
631                          XRef *xrefA, Catalog *catalog,
632                          int firstPage, int lastPage, PSOutMode modeA,
633                          int paperWidthA, int paperHeightA,
634                          GBool manualCtrlA) {
635   fontIDs = NULL;
636   fontFileIDs = NULL;
637   fontFileNames = NULL;
638   font16Enc = NULL;
639   xobjStack = NULL;
640   embFontList = NULL;
641   customColors = NULL;
642   haveTextClip = gFalse;
643   t3String = NULL;
644
645   init(outputFuncA, outputStreamA, psGeneric,
646        xrefA, catalog, firstPage, lastPage, modeA,
647        paperWidthA, paperHeightA, manualCtrlA);
648 }
649
650 void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
651                        PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
652                        int firstPage, int lastPage, PSOutMode modeA,
653                        int paperWidthA, int paperHeightA,
654                        GBool manualCtrlA) {
655   Page *page;
656
657   // initialize
658   ok = gTrue;
659   outputFunc = outputFuncA;
660   outputStream = outputStreamA;
661   fileType = fileTypeA;
662   xref = xrefA;
663   level = globalParams->getPSLevel();
664   mode = modeA;
665   paperWidth = paperWidthA;
666   paperHeight = paperHeightA;
667   if (paperWidth == 0) {
668     paperWidth = globalParams->getPSPaperWidth();
669   }
670   if (paperHeight == 0) {
671     paperHeight = globalParams->getPSPaperHeight();
672   }
673   if (paperWidth < 0 || paperHeight < 0) {
674     page = catalog->getPage(firstPage);
675     paperWidth = (int)(page->getWidth() + 0.5);
676     paperHeight = (int)(page->getHeight() + 0.5);
677   }
678   manualCtrl = manualCtrlA;
679   if (mode == psModeForm) {
680     lastPage = firstPage;
681   }
682   processColors = 0;
683   inType3Char = gFalse;
684
685 #if OPI_SUPPORT
686   // initialize OPI nesting levels
687   opi13Nest = 0;
688   opi20Nest = 0;
689 #endif
690
691   // initialize fontIDs, fontFileIDs, and fontFileNames lists
692   fontIDSize = 64;
693   fontIDLen = 0;
694   fontIDs = (Ref *)gmalloc(fontIDSize * sizeof(Ref));
695   fontFileIDSize = 64;
696   fontFileIDLen = 0;
697   fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref));
698   fontFileNameSize = 64;
699   fontFileNameLen = 0;
700   fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *));
701   nextTrueTypeNum = 0;
702   font16EncLen = 0;
703   font16EncSize = 0;
704
705   xobjStack = new GList();
706   numSaves = 0;
707
708   // initialize embedded font resource comment list
709   embFontList = new GString();
710
711   if (!manualCtrl) {
712     writeHeader(firstPage, lastPage, catalog->getPage(firstPage)->getBox());
713     if (mode != psModeForm) {
714       writePS("%%BeginProlog\n");
715     }
716     writeXpdfProcset();
717     if (mode != psModeForm) {
718       writePS("%%EndProlog\n");
719       writePS("%%BeginSetup\n");
720     }
721     writeDocSetup(catalog, firstPage, lastPage);
722     if (mode != psModeForm) {
723       writePS("%%EndSetup\n");
724     }
725   }
726
727   // initialize sequential page number
728   seqPage = 1;
729 }
730
731 PSOutputDev::~PSOutputDev() {
732   PSOutCustomColor *cc;
733   int i;
734
735   if (ok) {
736     if (!manualCtrl) {
737       writePS("%%Trailer\n");
738       writeTrailer();
739       if (mode != psModeForm) {
740         writePS("%%EOF\n");
741       }
742     }
743     if (fileType == psFile) {
744 #ifdef MACOS
745       ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
746 #endif
747       fclose((FILE *)outputStream);
748     }
749 #ifdef HAVE_POPEN
750     else if (fileType == psPipe) {
751       pclose((FILE *)outputStream);
752 #ifndef WIN32
753       signal(SIGPIPE, (SignalFunc)SIG_DFL);
754 #endif
755     }
756 #endif
757   }
758   if (embFontList) {
759     delete embFontList;
760   }
761   if (fontIDs) {
762     gfree(fontIDs);
763   }
764   if (fontFileIDs) {
765     gfree(fontFileIDs);
766   }
767   if (fontFileNames) {
768     for (i = 0; i < fontFileNameLen; ++i) {
769       delete fontFileNames[i];
770     }
771     gfree(fontFileNames);
772   }
773   if (font16Enc) {
774     for (i = 0; i < font16EncLen; ++i) {
775       delete font16Enc[i].enc;
776     }
777     gfree(font16Enc);
778   }
779   if (xobjStack) {
780     delete xobjStack;
781   }
782   while (customColors) {
783     cc = customColors;
784     customColors = cc->next;
785     delete cc;
786   }
787 }
788
789 void PSOutputDev::writeHeader(int firstPage, int lastPage, PDFRectangle *box) {
790   switch (mode) {
791   case psModePS:
792     writePS("%!PS-Adobe-3.0\n");
793     writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
794     writePSFmt("%%%%LanguageLevel: %d\n",
795                (level == psLevel1 || level == psLevel1Sep) ? 1 :
796                (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
797     if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
798       writePS("%%DocumentProcessColors: (atend)\n");
799       writePS("%%DocumentCustomColors: (atend)\n");
800     }
801     writePS("%%DocumentSuppliedResources: (atend)\n");
802     writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
803                paperWidth, paperHeight);
804     writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight);
805     writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
806     writePS("%%EndComments\n");
807     writePS("%%BeginDefaults\n");
808     writePS("%%PageMedia: plain\n");
809     writePS("%%EndDefaults\n");
810     break;
811   case psModeEPS:
812     writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
813     writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
814     writePSFmt("%%%%LanguageLevel: %d\n",
815                (level == psLevel1 || level == psLevel1Sep) ? 1 :
816                (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
817     if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
818       writePS("%%DocumentProcessColors: (atend)\n");
819       writePS("%%DocumentCustomColors: (atend)\n");
820     }
821     writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
822                (int)floor(box->x1), (int)floor(box->y1),
823                (int)ceil(box->x2), (int)ceil(box->y2));
824     if (floor(box->x1) != ceil(box->x1) ||
825         floor(box->y1) != ceil(box->y1) ||
826         floor(box->x2) != ceil(box->x2) ||
827         floor(box->y2) != ceil(box->y2)) {
828       writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n",
829                  box->x1, box->y1, box->x2, box->y2);
830     }
831     writePS("%%DocumentSuppliedResources: (atend)\n");
832     writePS("%%EndComments\n");
833     break;
834   case psModeForm:
835     writePS("%!PS-Adobe-3.0 Resource-Form\n");
836     writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
837     writePSFmt("%%%%LanguageLevel: %d\n",
838                (level == psLevel1 || level == psLevel1Sep) ? 1 :
839                (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
840     if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
841       writePS("%%DocumentProcessColors: (atend)\n");
842       writePS("%%DocumentCustomColors: (atend)\n");
843     }
844     writePS("%%DocumentSuppliedResources: (atend)\n");
845     writePS("%%EndComments\n");
846     writePS("32 dict dup begin\n");
847     writePSFmt("/BBox [%d %d %d %d] def\n",
848                (int)box->x1, (int)box->y1, (int)box->x2, (int)box->y2);
849     writePS("/FormType 1 def\n");
850     writePS("/Matrix [1 0 0 1 0 0] def\n");
851     break;
852   }
853 }
854
855 void PSOutputDev::writeXpdfProcset() {
856   char prologLevel;
857   char **p;
858
859   writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
860   prologLevel = 'a';
861   for (p = prolog; *p; ++p) {
862     if ((*p)[0] == '~' && (*p)[1] == '1') {
863       prologLevel = '1';
864     } else if ((*p)[0] == '~' && (*p)[1] == '2') {
865       prologLevel = '2';
866     } else if ((*p)[0] == '~' && (*p)[1] == 'a') {
867       prologLevel = 'a';
868     } else if (prologLevel == 'a' ||
869                (prologLevel == '1' && level < psLevel2) ||
870                (prologLevel == '2' && level >= psLevel2)) {
871       writePSFmt("%s\n", *p);
872     }
873   }
874   writePS("%%EndResource\n");
875
876   if (level >= psLevel3) {
877     for (p = cmapProlog; *p; ++p) {
878       writePSFmt("%s\n", *p);
879     }
880   }
881 }
882
883 void PSOutputDev::writeDocSetup(Catalog *catalog,
884                                 int firstPage, int lastPage) {
885   Page *page;
886   Dict *resDict;
887   Annots *annots;
888   Object obj1, obj2;
889   int pg, i;
890
891   if (mode == psModeForm) {
892     // swap the form and xpdf dicts
893     writePS("xpdf end begin dup begin\n");
894   } else {
895     writePS("xpdf begin\n");
896   }
897   for (pg = firstPage; pg <= lastPage; ++pg) {
898     page = catalog->getPage(pg);
899     if ((resDict = page->getResourceDict())) {
900       setupResources(resDict);
901     }
902     annots = new Annots(xref, page->getAnnots(&obj1));
903     obj1.free();
904     for (i = 0; i < annots->getNumAnnots(); ++i) {
905       if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
906         obj1.streamGetDict()->lookup("Resources", &obj2);
907         if (obj2.isDict()) {
908           setupResources(obj2.getDict());
909         }
910         obj2.free();
911       }
912       obj1.free();
913     }
914     delete annots;
915   }
916   if (mode != psModeForm) {
917     if (mode != psModeEPS && !manualCtrl) {
918       writePSFmt("%d %d %s pdfSetup\n",
919                  paperWidth, paperHeight,
920                  globalParams->getPSDuplex() ? "true" : "false");
921     }
922 #if OPI_SUPPORT
923     if (globalParams->getPSOPI()) {
924       writePS("/opiMatrix matrix currentmatrix def\n");
925     }
926 #endif
927   }
928 }
929
930 void PSOutputDev::writePageTrailer() {
931   if (mode != psModeForm) {
932     writePS("pdfEndPage\n");
933   }
934 }
935
936 void PSOutputDev::writeTrailer() {
937   PSOutCustomColor *cc;
938
939   if (mode == psModeForm) {
940     writePS("/Foo exch /Form defineresource pop\n");
941   } else {
942     writePS("end\n");
943     writePS("%%DocumentSuppliedResources:\n");
944     writePS(embFontList->getCString());
945     if (level == psLevel1Sep || level == psLevel2Sep ||
946         level == psLevel3Sep) {
947       writePS("%%DocumentProcessColors:");
948       if (processColors & psProcessCyan) {
949         writePS(" Cyan");
950          }
951       if (processColors & psProcessMagenta) {
952         writePS(" Magenta");
953       }
954       if (processColors & psProcessYellow) {
955         writePS(" Yellow");
956       }
957       if (processColors & psProcessBlack) {
958         writePS(" Black");
959       }
960       writePS("\n");
961       writePS("%%DocumentCustomColors:");
962       for (cc = customColors; cc; cc = cc->next) {
963         writePSFmt(" (%s)", cc->name->getCString());
964       }
965       writePS("\n");
966       writePS("%%CMYKCustomColor:\n");
967       for (cc = customColors; cc; cc = cc->next) {
968         writePSFmt("%%%%+ %g %g %g %g (%s)\n",
969                    cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
970       }
971     }
972   }
973 }
974
975 void PSOutputDev::setupResources(Dict *resDict) {
976   Object xObjDict, xObjRef, xObj, resObj;
977   Ref ref0, ref1;
978   GBool skip;
979   int i, j;
980
981   setupFonts(resDict);
982   setupImages(resDict);
983
984   resDict->lookup("XObject", &xObjDict);
985   if (xObjDict.isDict()) {
986     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
987
988       // avoid infinite recursion on XObjects
989       skip = gFalse;
990       if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
991         ref0 = xObjRef.getRef();
992         for (j = 0; j < xobjStack->getLength(); ++j) {
993           ref1 = *(Ref *)xobjStack->get(j);
994           if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
995             skip = gTrue;
996             break;
997           }
998         }
999         if (!skip) {
1000           xobjStack->append(&ref0);
1001         }
1002       }
1003       if (!skip) {
1004
1005         // process the XObject's resource dictionary
1006         xObjDict.dictGetVal(i, &xObj);
1007         if (xObj.isStream()) {
1008           xObj.streamGetDict()->lookup("Resources", &resObj);
1009           if (resObj.isDict()) {
1010             setupResources(resObj.getDict());
1011           }
1012           resObj.free();
1013         }
1014         xObj.free();
1015       }
1016
1017       if (xObjRef.isRef() && !skip) {
1018         xobjStack->del(xobjStack->getLength() - 1);
1019       }
1020       xObjRef.free();
1021     }
1022   }
1023   xObjDict.free();
1024 }
1025
1026 void PSOutputDev::setupFonts(Dict *resDict) {
1027   Object obj1, obj2;
1028   Ref r;
1029   GfxFontDict *gfxFontDict;
1030   GfxFont *font;
1031   int i;
1032
1033   gfxFontDict = NULL;
1034   resDict->lookupNF("Font", &obj1);
1035   if (obj1.isRef()) {
1036     obj1.fetch(xref, &obj2);
1037     if (obj2.isDict()) {
1038       r = obj1.getRef();
1039       gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
1040     }
1041     obj2.free();
1042   } else if (obj1.isDict()) {
1043     gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
1044   }
1045   if (gfxFontDict) {
1046     for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
1047       font = gfxFontDict->getFont(i);
1048       setupFont(font, resDict);
1049     }
1050     delete gfxFontDict;
1051   }
1052   obj1.free();
1053 }
1054
1055 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
1056   Ref fontFileID;
1057   GString *name;
1058   PSFontParam *fontParam;
1059   GString *psName;
1060   char type3Name[64], buf[16];
1061   GBool subst;
1062   UnicodeMap *uMap;
1063   char *charName;
1064   double xs, ys;
1065   int code;
1066   double w1, w2;
1067   double *fm;
1068   int i, j;
1069
1070   // check if font is already set up
1071   for (i = 0; i < fontIDLen; ++i) {
1072     if (fontIDs[i].num == font->getID()->num &&
1073         fontIDs[i].gen == font->getID()->gen) {
1074       return;
1075     }
1076   }
1077
1078   // add entry to fontIDs list
1079   if (fontIDLen >= fontIDSize) {
1080     fontIDSize += 64;
1081     fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref));
1082   }
1083   fontIDs[fontIDLen++] = *font->getID();
1084
1085   xs = ys = 1;
1086   subst = gFalse;
1087
1088   // check for resident 8-bit font
1089   if (font->getName() &&
1090       (fontParam = globalParams->getPSFont(font->getName()))) {
1091     psName = new GString(fontParam->psFontName->getCString());
1092
1093   // check for embedded Type 1 font
1094   } else if (globalParams->getPSEmbedType1() &&
1095              font->getType() == fontType1 &&
1096              font->getEmbeddedFontID(&fontFileID)) {
1097     psName = filterPSName(font->getEmbeddedFontName());
1098     setupEmbeddedType1Font(&fontFileID, psName);
1099
1100   // check for embedded Type 1C font
1101   } else if (globalParams->getPSEmbedType1() &&
1102              font->getType() == fontType1C &&
1103              font->getEmbeddedFontID(&fontFileID)) {
1104     psName = filterPSName(font->getEmbeddedFontName());
1105     setupEmbeddedType1CFont(font, &fontFileID, psName);
1106
1107   // check for external Type 1 font file
1108   } else if (globalParams->getPSEmbedType1() &&
1109              font->getType() == fontType1 &&
1110              font->getExtFontFile()) {
1111     // this assumes that the PS font name matches the PDF font name
1112     psName = font->getName()->copy();
1113     setupExternalType1Font(font->getExtFontFile(), psName);
1114
1115   // check for embedded TrueType font
1116   } else if (globalParams->getPSEmbedTrueType() &&
1117              font->getType() == fontTrueType &&
1118              font->getEmbeddedFontID(&fontFileID)) {
1119     psName = filterPSName(font->getEmbeddedFontName());
1120     setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
1121
1122   // check for external TrueType font file
1123   } else if (globalParams->getPSEmbedTrueType() &&
1124              font->getType() == fontTrueType &&
1125              font->getExtFontFile()) {
1126     psName = filterPSName(font->getName());
1127     setupExternalTrueTypeFont(font, psName);
1128
1129   // check for embedded CID PostScript font
1130   } else if (globalParams->getPSEmbedCIDPostScript() &&
1131              font->getType() == fontCIDType0C &&
1132              font->getEmbeddedFontID(&fontFileID)) {
1133     psName = filterPSName(font->getEmbeddedFontName());
1134     setupEmbeddedCIDType0Font(font, &fontFileID, psName);
1135
1136   // check for embedded CID TrueType font
1137   } else if (globalParams->getPSEmbedCIDTrueType() &&
1138              font->getType() == fontCIDType2 &&
1139              font->getEmbeddedFontID(&fontFileID)) {
1140     psName = filterPSName(font->getEmbeddedFontName());
1141     setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName);
1142
1143   } else if (font->getType() == fontType3) {
1144     sprintf(type3Name, "T3_%d_%d",
1145             font->getID()->num, font->getID()->gen);
1146     psName = new GString(type3Name);
1147     setupType3Font(font, psName, parentResDict);
1148
1149   // do 8-bit font substitution
1150   } else if (!font->isCIDFont()) {
1151     subst = gTrue;
1152     name = font->getName();
1153     psName = NULL;
1154     if (name) {
1155       for (i = 0; psFonts[i]; ++i) {
1156         if (name->cmp(psFonts[i]) == 0) {
1157           psName = new GString(psFonts[i]);
1158           break;
1159         }
1160       }
1161     }
1162     if (!psName) {
1163       if (font->isFixedWidth()) {
1164         i = 8;
1165       } else if (font->isSerif()) {
1166         i = 4;
1167       } else {
1168         i = 0;
1169       }
1170       if (font->isBold()) {
1171         i += 2;
1172       }
1173       if (font->isItalic()) {
1174         i += 1;
1175       }
1176       psName = new GString(psSubstFonts[i].psName);
1177       for (code = 0; code < 256; ++code) {
1178         if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
1179             charName[0] == 'm' && charName[1] == '\0') {
1180           break;
1181         }
1182       }
1183       if (code < 256) {
1184         w1 = ((Gfx8BitFont *)font)->getWidth(code);
1185       } else {
1186         w1 = 0;
1187       }
1188       w2 = psSubstFonts[i].mWidth;
1189       xs = w1 / w2;
1190       if (xs < 0.1) {
1191         xs = 1;
1192       }
1193       if (font->getType() == fontType3) {
1194         // This is a hack which makes it possible to substitute for some
1195         // Type 3 fonts.  The problem is that it's impossible to know what
1196         // the base coordinate system used in the font is without actually
1197         // rendering the font.
1198         ys = xs;
1199         fm = font->getFontMatrix();
1200         if (fm[0] != 0) {
1201           ys *= fm[3] / fm[0];
1202         }
1203       } else {
1204         ys = 1;
1205       }
1206     }
1207
1208   // do 16-bit font substitution
1209   } else if ((fontParam = globalParams->
1210                 getPSFont16(font->getName(),
1211                             ((GfxCIDFont *)font)->getCollection(),
1212                             font->getWMode()))) {
1213     subst = gTrue;
1214     psName = fontParam->psFontName->copy();
1215     if (font16EncLen >= font16EncSize) {
1216       font16EncSize += 16;
1217       font16Enc = (PSFont16Enc *)grealloc(font16Enc,
1218                                           font16EncSize * sizeof(PSFont16Enc));
1219     }
1220     font16Enc[font16EncLen].fontID = *font->getID();
1221     font16Enc[font16EncLen].enc = fontParam->encoding->copy();
1222     if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
1223       uMap->decRefCnt();
1224       ++font16EncLen;
1225     } else {
1226       error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
1227             font16Enc[font16EncLen].enc->getCString());
1228     }
1229
1230   // give up - can't do anything with this font
1231   } else {
1232     error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
1233           font->getName() ? font->getName()->getCString() : "(unnamed)",
1234           ((GfxCIDFont *)font)->getCollection()
1235             ? ((GfxCIDFont *)font)->getCollection()->getCString()
1236             : "(unknown)");
1237     return;
1238   }
1239
1240   // generate PostScript code to set up the font
1241   if (font->isCIDFont()) {
1242     if (level == psLevel3 || level == psLevel3Sep) {
1243       writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
1244                  font->getID()->num, font->getID()->gen, psName->getCString(),
1245                  font->getWMode());
1246     } else {
1247       writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
1248                  font->getID()->num, font->getID()->gen, psName->getCString(),
1249                  font->getWMode());
1250     }
1251   } else {
1252     writePSFmt("/F%d_%d /%s %g %g\n",
1253                font->getID()->num, font->getID()->gen, psName->getCString(),
1254                xs, ys);
1255     for (i = 0; i < 256; i += 8) {
1256       writePSFmt((i == 0) ? "[ " : "  ");
1257       for (j = 0; j < 8; ++j) {
1258         if (font->getType() == fontTrueType &&
1259             !subst &&
1260             !((Gfx8BitFont *)font)->getHasEncoding()) {
1261           sprintf(buf, "c%02x", i+j);
1262           charName = buf;
1263         } else {
1264           charName = ((Gfx8BitFont *)font)->getCharName(i+j);
1265           // this is a kludge for broken PDF files that encode char 32
1266           // as .notdef
1267           if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
1268             charName = "space";
1269           }
1270         }
1271         writePS("/");
1272         writePSName(charName ? charName : (char *)".notdef");
1273       }
1274       writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
1275     }
1276     writePS("pdfMakeFont\n");
1277   }
1278
1279   delete psName;
1280 }
1281
1282 void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
1283   static char hexChar[17] = "0123456789abcdef";
1284   Object refObj, strObj, obj1, obj2, obj3;
1285   Dict *dict;
1286   int length1, length2, length3;
1287   int c;
1288   int start[4];
1289   GBool binMode;
1290   int i;
1291
1292   // check if font is already embedded
1293   for (i = 0; i < fontFileIDLen; ++i) {
1294     if (fontFileIDs[i].num == id->num &&
1295         fontFileIDs[i].gen == id->gen)
1296       return;
1297   }
1298
1299   // add entry to fontFileIDs list
1300   if (fontFileIDLen >= fontFileIDSize) {
1301     fontFileIDSize += 64;
1302     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1303   }
1304   fontFileIDs[fontFileIDLen++] = *id;
1305
1306   // get the font stream and info
1307   refObj.initRef(id->num, id->gen);
1308   refObj.fetch(xref, &strObj);
1309   refObj.free();
1310   if (!strObj.isStream()) {
1311     error(-1, "Embedded font file object is not a stream");
1312     goto err1;
1313   }
1314   if (!(dict = strObj.streamGetDict())) {
1315     error(-1, "Embedded font stream is missing its dictionary");
1316     goto err1;
1317   }
1318   dict->lookup("Length1", &obj1);
1319   dict->lookup("Length2", &obj2);
1320   dict->lookup("Length3", &obj3);
1321   if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
1322     error(-1, "Missing length fields in embedded font stream dictionary");
1323     obj1.free();
1324     obj2.free();
1325     obj3.free();
1326     goto err1;
1327   }
1328   length1 = obj1.getInt();
1329   length2 = obj2.getInt();
1330   length3 = obj3.getInt();
1331   obj1.free();
1332   obj2.free();
1333   obj3.free();
1334
1335   // beginning comment
1336   writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1337   embFontList->append("%%+ font ");
1338   embFontList->append(psName->getCString());
1339   embFontList->append("\n");
1340
1341   // copy ASCII portion of font
1342   strObj.streamReset();
1343   for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
1344     writePSChar(c);
1345   }
1346
1347   // figure out if encrypted portion is binary or ASCII
1348   binMode = gFalse;
1349   for (i = 0; i < 4; ++i) {
1350     start[i] = strObj.streamGetChar();
1351     if (start[i] == EOF) {
1352       error(-1, "Unexpected end of file in embedded font stream");
1353       goto err1;
1354     }
1355     if (!((start[i] >= '0' && start[i] <= '9') ||
1356           (start[i] >= 'A' && start[i] <= 'F') ||
1357           (start[i] >= 'a' && start[i] <= 'f')))
1358       binMode = gTrue;
1359   }
1360
1361   // convert binary data to ASCII
1362   if (binMode) {
1363     for (i = 0; i < 4; ++i) {
1364       writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
1365       writePSChar(hexChar[start[i] & 0x0f]);
1366     }
1367     // if Length2 is incorrect (too small), font data gets chopped, so
1368     // we take a few extra characters from the trailer just in case
1369     length2 += length3 >= 8 ? 8 : length3;
1370     while (i < length2) {
1371       if ((c = strObj.streamGetChar()) == EOF) {
1372         break;
1373       }
1374       writePSChar(hexChar[(c >> 4) & 0x0f]);
1375       writePSChar(hexChar[c & 0x0f]);
1376       if (++i % 32 == 0) {
1377         writePSChar('\n');
1378       }
1379     }
1380     if (i % 32 > 0) {
1381       writePSChar('\n');
1382     }
1383
1384   // already in ASCII format -- just copy it
1385   } else {
1386     for (i = 0; i < 4; ++i) {
1387       writePSChar(start[i]);
1388     }
1389     for (i = 4; i < length2; ++i) {
1390       if ((c = strObj.streamGetChar()) == EOF) {
1391         break;
1392       }
1393       writePSChar(c);
1394     }
1395   }
1396
1397   // write padding and "cleartomark"
1398   for (i = 0; i < 8; ++i) {
1399     writePS("00000000000000000000000000000000"
1400             "00000000000000000000000000000000\n");
1401   }
1402   writePS("cleartomark\n");
1403
1404   // ending comment
1405   writePS("%%EndResource\n");
1406
1407  err1:
1408   strObj.streamClose();
1409   strObj.free();
1410 }
1411
1412 //~ This doesn't handle .pfb files or binary eexec data (which only
1413 //~ happens in pfb files?).
1414 void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
1415   FILE *fontFile;
1416   int c;
1417   int i;
1418
1419   // check if font is already embedded
1420   for (i = 0; i < fontFileNameLen; ++i) {
1421     if (!fontFileNames[i]->cmp(fileName)) {
1422       return;
1423     }
1424   }
1425
1426   // add entry to fontFileNames list
1427   if (fontFileNameLen >= fontFileNameSize) {
1428     fontFileNameSize += 64;
1429     fontFileNames = (GString **)grealloc(fontFileNames,
1430                                          fontFileNameSize * sizeof(GString *));
1431   }
1432   fontFileNames[fontFileNameLen++] = fileName->copy();
1433
1434   // beginning comment
1435   writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1436   embFontList->append("%%+ font ");
1437   embFontList->append(psName->getCString());
1438   embFontList->append("\n");
1439
1440   // copy the font file
1441   if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
1442     error(-1, "Couldn't open external font file");
1443     return;
1444   }
1445   while ((c = fgetc(fontFile)) != EOF) {
1446     writePSChar(c);
1447   }
1448   fclose(fontFile);
1449
1450   // ending comment
1451   writePS("%%EndResource\n");
1452 }
1453
1454 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
1455                                           GString *psName) {
1456   char *fontBuf;
1457   int fontLen;
1458   Type1CFontFile *t1cFile;
1459   int i;
1460
1461   // check if font is already embedded
1462   for (i = 0; i < fontFileIDLen; ++i) {
1463     if (fontFileIDs[i].num == id->num &&
1464         fontFileIDs[i].gen == id->gen)
1465       return;
1466   }
1467
1468   // add entry to fontFileIDs list
1469   if (fontFileIDLen >= fontFileIDSize) {
1470     fontFileIDSize += 64;
1471     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1472   }
1473   fontFileIDs[fontFileIDLen++] = *id;
1474
1475   // beginning comment
1476   writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1477   embFontList->append("%%+ font ");
1478   embFontList->append(psName->getCString());
1479   embFontList->append("\n");
1480
1481   // convert it to a Type 1 font
1482   fontBuf = font->readEmbFontFile(xref, &fontLen);
1483   t1cFile = new Type1CFontFile(fontBuf, fontLen);
1484   if (t1cFile->isOk()) {
1485     t1cFile->convertToType1(outputFunc, outputStream);
1486   }
1487   delete t1cFile;
1488   gfree(fontBuf);
1489
1490   // ending comment
1491   writePS("%%EndResource\n");
1492 }
1493
1494 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
1495                                             GString *psName) {
1496   char unique[32];
1497   char *fontBuf;
1498   int fontLen;
1499   TrueTypeFontFile *ttFile;
1500   Gushort *codeToGID;
1501   int i;
1502
1503   // check if font is already embedded
1504   for (i = 0; i < fontFileIDLen; ++i) {
1505     if (fontFileIDs[i].num == id->num &&
1506         fontFileIDs[i].gen == id->gen) {
1507       sprintf(unique, "_%d", nextTrueTypeNum++);
1508       psName->append(unique);
1509       break;
1510     }
1511   }
1512
1513   // add entry to fontFileIDs list
1514   if (i == fontFileIDLen) {
1515     if (fontFileIDLen >= fontFileIDSize) {
1516       fontFileIDSize += 64;
1517       fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1518     }
1519     fontFileIDs[fontFileIDLen++] = *id;
1520   }
1521
1522   // beginning comment
1523   writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1524   embFontList->append("%%+ font ");
1525   embFontList->append(psName->getCString());
1526   embFontList->append("\n");
1527
1528   // convert it to a Type 42 font
1529   fontBuf = font->readEmbFontFile(xref, &fontLen);
1530   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1531   codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ttFile);
1532   ttFile->convertToType42(psName->getCString(),
1533                           ((Gfx8BitFont *)font)->getEncoding(),
1534                           ((Gfx8BitFont *)font)->getHasEncoding(),
1535                           codeToGID,
1536                           outputFunc, outputStream);
1537   gfree(codeToGID);
1538   delete ttFile;
1539   gfree(fontBuf);
1540
1541   // ending comment
1542   writePS("%%EndResource\n");
1543 }
1544
1545 void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) {
1546   char unique[32];
1547   GString *fileName;
1548   char *fontBuf;
1549   int fontLen;
1550   TrueTypeFontFile *ttFile;
1551   Gushort *codeToGID;
1552   int i;
1553
1554   // check if font is already embedded
1555   fileName = font->getExtFontFile();
1556   for (i = 0; i < fontFileNameLen; ++i) {
1557     if (!fontFileNames[i]->cmp(fileName)) {
1558       sprintf(unique, "_%d", nextTrueTypeNum++);
1559       psName->append(unique);
1560       break;
1561     }
1562   }
1563
1564   // add entry to fontFileNames list
1565   if (i == fontFileNameLen) {
1566     if (fontFileNameLen >= fontFileNameSize) {
1567       fontFileNameSize += 64;
1568       fontFileNames =
1569         (GString **)grealloc(fontFileNames,
1570                              fontFileNameSize * sizeof(GString *));
1571     }
1572   }
1573   fontFileNames[fontFileNameLen++] = fileName->copy();
1574
1575   // beginning comment
1576   writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1577   embFontList->append("%%+ font ");
1578   embFontList->append(psName->getCString());
1579   embFontList->append("\n");
1580
1581   // convert it to a Type 42 font
1582   fontBuf = font->readExtFontFile(&fontLen);
1583   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1584   codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ttFile);
1585   ttFile->convertToType42(psName->getCString(),
1586                           ((Gfx8BitFont *)font)->getEncoding(),
1587                           ((Gfx8BitFont *)font)->getHasEncoding(),
1588                           codeToGID,
1589                           outputFunc, outputStream);
1590   delete ttFile;
1591   gfree(fontBuf);
1592
1593   // ending comment
1594   writePS("%%EndResource\n");
1595 }
1596
1597 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
1598                                             GString *psName) {
1599   char *fontBuf;
1600   int fontLen;
1601   Type1CFontFile *t1cFile;
1602   int i;
1603
1604   // check if font is already embedded
1605   for (i = 0; i < fontFileIDLen; ++i) {
1606     if (fontFileIDs[i].num == id->num &&
1607         fontFileIDs[i].gen == id->gen)
1608       return;
1609   }
1610
1611   // add entry to fontFileIDs list
1612   if (fontFileIDLen >= fontFileIDSize) {
1613     fontFileIDSize += 64;
1614     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1615   }
1616   fontFileIDs[fontFileIDLen++] = *id;
1617
1618   // beginning comment
1619   writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1620   embFontList->append("%%+ font ");
1621   embFontList->append(psName->getCString());
1622   embFontList->append("\n");
1623
1624   // convert it to a Type 0 font
1625   fontBuf = font->readEmbFontFile(xref, &fontLen);
1626   t1cFile = new Type1CFontFile(fontBuf, fontLen);
1627   if (t1cFile->isOk()) {
1628     if (globalParams->getPSLevel() >= psLevel3) {
1629       // Level 3: use a CID font
1630       t1cFile->convertToCIDType0(psName->getCString(),
1631                                  outputFunc, outputStream);
1632     } else {
1633       // otherwise: use a non-CID composite font
1634       t1cFile->convertToType0(psName->getCString(), outputFunc, outputStream);
1635     }
1636   }
1637   delete t1cFile;
1638   gfree(fontBuf);
1639
1640   // ending comment
1641   writePS("%%EndResource\n");
1642 }
1643
1644 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
1645                                                GString *psName) {
1646   char *fontBuf;
1647   int fontLen;
1648   TrueTypeFontFile *ttFile;
1649   int i;
1650
1651   // check if font is already embedded
1652   for (i = 0; i < fontFileIDLen; ++i) {
1653     if (fontFileIDs[i].num == id->num &&
1654         fontFileIDs[i].gen == id->gen)
1655       return;
1656   }
1657
1658   // add entry to fontFileIDs list
1659   if (fontFileIDLen >= fontFileIDSize) {
1660     fontFileIDSize += 64;
1661     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1662   }
1663   fontFileIDs[fontFileIDLen++] = *id;
1664
1665   // beginning comment
1666   writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1667   embFontList->append("%%+ font ");
1668   embFontList->append(psName->getCString());
1669   embFontList->append("\n");
1670
1671   // convert it to a Type 0 font
1672   fontBuf = font->readEmbFontFile(xref, &fontLen);
1673   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1674   if (globalParams->getPSLevel() >= psLevel3) {
1675     ttFile->convertToCIDType2(psName->getCString(),
1676                               ((GfxCIDFont *)font)->getCIDToGID(),
1677                               ((GfxCIDFont *)font)->getCIDToGIDLen(),
1678                               outputFunc, outputStream);
1679   } else {
1680     // otherwise: use a non-CID composite font
1681     ttFile->convertToType0(psName->getCString(),
1682                            ((GfxCIDFont *)font)->getCIDToGID(),
1683                            ((GfxCIDFont *)font)->getCIDToGIDLen(),
1684                            outputFunc, outputStream);
1685   }
1686   delete ttFile;
1687   gfree(fontBuf);
1688
1689   // ending comment
1690   writePS("%%EndResource\n");
1691 }
1692
1693 void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
1694                                  Dict *parentResDict) {
1695   Dict *resDict;
1696   Dict *charProcs;
1697   Object charProc;
1698   Gfx *gfx;
1699   PDFRectangle box;
1700   double *m;
1701   char buf[256];
1702   int i;
1703
1704   // set up resources used by font
1705   if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
1706     inType3Char = gTrue;
1707     setupResources(resDict);
1708     inType3Char = gFalse;
1709   } else {
1710     resDict = parentResDict;
1711   }
1712
1713   // beginning comment
1714   writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1715   embFontList->append("%%+ font ");
1716   embFontList->append(psName->getCString());
1717   embFontList->append("\n");
1718
1719   // font dictionary
1720   writePS("7 dict begin\n");
1721   writePS("/FontType 3 def\n");
1722   m = font->getFontMatrix();
1723   writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n",
1724              m[0], m[1], m[2], m[3], m[4], m[5]);
1725   m = font->getFontBBox();
1726   writePSFmt("/FontBBox [%g %g %g %g] def\n",
1727              m[0], m[1], m[2], m[3]);
1728   writePS("/Encoding 256 array def\n");
1729   writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
1730   writePS("/BuildGlyph {\n");
1731   writePS("  exch /CharProcs get exch\n");
1732   writePS("  2 copy known not { pop /.notdef } if\n");
1733   writePS("  get exec\n");
1734   writePS("} bind def\n");
1735   writePS("/BuildChar {\n");
1736   writePS("  1 index /Encoding get exch get\n");
1737   writePS("  1 index /BuildGlyph get exec\n");
1738   writePS("} bind def\n");
1739   if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
1740     writePSFmt("/CharProcs %d dict def\n", charProcs->getLength());
1741     writePS("CharProcs begin\n");
1742     box.x1 = m[0];
1743     box.y1 = m[1];
1744     box.x2 = m[2];
1745     box.y2 = m[3];
1746     gfx = new Gfx(xref, this, resDict, &box, gFalse, NULL);
1747     inType3Char = gTrue;
1748     t3Cacheable = gFalse;
1749     for (i = 0; i < charProcs->getLength(); ++i) {
1750       writePS("/");
1751       writePSName(charProcs->getKey(i));
1752       writePS(" {\n");
1753       gfx->display(charProcs->getVal(i, &charProc));
1754       charProc.free();
1755       if (t3String) {
1756         if (t3Cacheable) {
1757           sprintf(buf, "%g %g %g %g %g %g setcachedevice\n",
1758                   t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
1759         } else {
1760           sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY);
1761         }
1762         (*outputFunc)(outputStream, buf, strlen(buf));
1763         (*outputFunc)(outputStream, t3String->getCString(),
1764                       t3String->getLength());
1765         delete t3String;
1766         t3String = NULL;
1767       }
1768       (*outputFunc)(outputStream, "Q\n", 2);
1769       writePS("} def\n");
1770     }
1771     inType3Char = gFalse;
1772     delete gfx;
1773     writePS("end\n");
1774   }
1775   writePS("currentdict end\n");
1776   writePSFmt("/%s exch definefont pop\n", psName->getCString());
1777
1778   // ending comment
1779   writePS("%%EndResource\n");
1780 }
1781
1782 void PSOutputDev::setupImages(Dict *resDict) {
1783   Object xObjDict, xObj, xObjRef, subtypeObj;
1784   int i;
1785
1786   if (!(mode == psModeForm || inType3Char)) {
1787     return;
1788   }
1789
1790   resDict->lookup("XObject", &xObjDict);
1791   if (xObjDict.isDict()) {
1792     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
1793       xObjDict.dictGetValNF(i, &xObjRef);
1794       xObjDict.dictGetVal(i, &xObj);
1795       if (xObj.isStream()) {
1796         xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
1797         if (subtypeObj.isName("Image")) {
1798           if (xObjRef.isRef()) {
1799             setupImage(xObjRef.getRef(), xObj.getStream());
1800           } else {
1801             error(-1, "Image in resource dict is not an indirect reference");
1802           }
1803         }
1804         subtypeObj.free();
1805       }
1806       xObj.free();
1807       xObjRef.free();
1808     }
1809   }
1810   xObjDict.free();
1811 }
1812
1813 void PSOutputDev::setupImage(Ref id, Stream *str) {
1814   int c;
1815   int size, line, col, i;
1816
1817   // construct an encoder stream
1818   if (globalParams->getPSASCIIHex()) {
1819     str = new ASCIIHexEncoder(str);
1820   } else {
1821     str = new ASCII85Encoder(str);
1822   }
1823
1824   // compute image data size
1825   str->reset();
1826   col = size = 0;
1827   do {
1828     do {
1829       c = str->getChar();
1830     } while (c == '\n' || c == '\r');
1831     if (c == '~' || c == EOF) {
1832       break;
1833     }
1834     if (c == 'z') {
1835       ++col;
1836     } else {
1837       ++col;
1838       for (i = 1; i <= 4; ++i) {
1839         do {
1840           c = str->getChar();
1841         } while (c == '\n' || c == '\r');
1842         if (c == '~' || c == EOF) {
1843           break;
1844         }
1845         ++col;
1846       }
1847     }
1848     if (col > 225) {
1849       ++size;
1850       col = 0;
1851     }
1852   } while (c != '~' && c != EOF);
1853   ++size;
1854   writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
1855   str->close();
1856
1857   // write the data into the array
1858   str->reset();
1859   line = col = 0;
1860   writePS("dup 0 <~");
1861   do {
1862     do {
1863       c = str->getChar();
1864     } while (c == '\n' || c == '\r');
1865     if (c == '~' || c == EOF) {
1866       break;
1867     }
1868     if (c == 'z') {
1869       writePSChar(c);
1870       ++col;
1871     } else {
1872       writePSChar(c);
1873       ++col;
1874       for (i = 1; i <= 4; ++i) {
1875         do {
1876           c = str->getChar();
1877         } while (c == '\n' || c == '\r');
1878         if (c == '~' || c == EOF) {
1879           break;
1880         }
1881         writePSChar(c);
1882         ++col;
1883       }
1884     }
1885     // each line is: "dup nnnnn <~...data...~> put<eol>"
1886     // so max data length = 255 - 20 = 235
1887     // chunks are 1 or 4 bytes each, so we have to stop at 232
1888     // but make it 225 just to be safe
1889     if (col > 225) {
1890       writePS("~> put\n");
1891       ++line;
1892       writePSFmt("dup %d <~", line);
1893       col = 0;
1894     }
1895   } while (c != '~' && c != EOF);
1896   writePS("~> put\n");
1897   writePS("pop\n");
1898   str->close();
1899
1900   delete str;
1901 }
1902
1903 void PSOutputDev::startPage(int pageNum, GfxState *state) {
1904   int x1, y1, x2, y2, width, height, paperWidth2, paperHeight2;
1905
1906
1907   switch (mode) {
1908
1909   case psModePS:
1910     writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage);
1911     writePS("%%BeginPageSetup\n");
1912
1913     // rotate, translate, and scale page
1914     x1 = (int)(state->getX1() + 0.5);
1915     y1 = (int)(state->getY1() + 0.5);
1916     x2 = (int)(state->getX2() + 0.5);
1917     y2 = (int)(state->getY2() + 0.5);
1918     width = x2 - x1;
1919     height = y2 - y1;
1920     if (width > height && width > paperWidth) {
1921       landscape = gTrue;
1922       writePSFmt("%%%%PageOrientation: %s\n",
1923                  state->getCTM()[0] ? "Landscape" : "Portrait");
1924       writePS("pdfStartPage\n");
1925       writePS("90 rotate\n");
1926       tx = -x1;
1927       ty = -(y1 + paperWidth);
1928       paperWidth2 = paperHeight;
1929       paperHeight2 = paperWidth;
1930     } else {
1931       landscape = gFalse;
1932       writePSFmt("%%%%PageOrientation: %s\n",
1933                  state->getCTM()[0] ? "Portrait" : "Landscape");
1934       writePS("pdfStartPage\n");
1935       tx = -x1;
1936       ty = -y1;
1937       paperWidth2 = paperWidth;
1938       paperHeight2 = paperHeight;
1939     }
1940     if (width < paperWidth2) {
1941       tx += (paperWidth2 - width) / 2;
1942     }
1943     if (height < paperHeight2) {
1944       ty += (paperHeight2 - height) / 2;
1945     }
1946     if (tx != 0 || ty != 0) {
1947       writePSFmt("%g %g translate\n", tx, ty);
1948     }
1949     if (width > paperWidth2 || height > paperHeight2) {
1950       xScale = (double)paperWidth2 / (double)width;
1951       yScale = (double)paperHeight2 / (double)height;
1952       if (yScale < xScale) {
1953         xScale = yScale;
1954       } else {
1955         yScale = xScale;
1956       }
1957       writePSFmt("%0.4f %0.4f scale\n", xScale, xScale);
1958     } else {
1959       xScale = yScale = 1;
1960     }
1961
1962     writePS("%%EndPageSetup\n");
1963     ++seqPage;
1964     break;
1965
1966   case psModeEPS:
1967     writePS("pdfStartPage\n");
1968     tx = ty = 0;
1969     xScale = yScale = 1;
1970     landscape = gFalse;
1971     break;
1972
1973   case psModeForm:
1974     writePS("/PaintProc {\n");
1975     writePS("begin xpdf begin\n");
1976     writePS("pdfStartPage\n");
1977     tx = ty = 0;
1978     xScale = yScale = 1;
1979     landscape = gFalse;
1980     break;
1981   }
1982
1983   if (underlayCbk) {
1984     (*underlayCbk)(this, underlayCbkData);
1985   }
1986 }
1987
1988 void PSOutputDev::endPage() {
1989   if (overlayCbk) {
1990     (*overlayCbk)(this, overlayCbkData);
1991   }
1992
1993
1994   if (mode == psModeForm) {
1995     writePS("pdfEndPage\n");
1996     writePS("end end\n");
1997     writePS("} def\n");
1998     writePS("end end\n");
1999   } else {
2000     if (!manualCtrl) {
2001       writePS("showpage\n");
2002       writePS("%%PageTrailer\n");
2003       writePageTrailer();
2004     }
2005   }
2006 }
2007
2008 void PSOutputDev::saveState(GfxState *state) {
2009   writePS("q\n");
2010   ++numSaves;
2011 }
2012
2013 void PSOutputDev::restoreState(GfxState *state) {
2014   writePS("Q\n");
2015   --numSaves;
2016 }
2017
2018 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
2019                             double m21, double m22, double m31, double m32) {
2020   writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
2021 }
2022
2023 void PSOutputDev::updateLineDash(GfxState *state) {
2024   double *dash;
2025   double start;
2026   int length, i;
2027
2028   state->getLineDash(&dash, &length, &start);
2029   writePS("[");
2030   for (i = 0; i < length; ++i)
2031     writePSFmt("%g%s", dash[i], (i == length-1) ? "" : " ");
2032   writePSFmt("] %g d\n", start);
2033 }
2034
2035 void PSOutputDev::updateFlatness(GfxState *state) {
2036   writePSFmt("%d i\n", state->getFlatness());
2037 }
2038
2039 void PSOutputDev::updateLineJoin(GfxState *state) {
2040   writePSFmt("%d j\n", state->getLineJoin());
2041 }
2042
2043 void PSOutputDev::updateLineCap(GfxState *state) {
2044   writePSFmt("%d J\n", state->getLineCap());
2045 }
2046
2047 void PSOutputDev::updateMiterLimit(GfxState *state) {
2048   writePSFmt("%g M\n", state->getMiterLimit());
2049 }
2050
2051 void PSOutputDev::updateLineWidth(GfxState *state) {
2052   writePSFmt("%g w\n", state->getLineWidth());
2053 }
2054
2055 void PSOutputDev::updateFillColor(GfxState *state) {
2056   GfxColor color;
2057   double gray;
2058   GfxRGB rgb;
2059   GfxCMYK cmyk;
2060   GfxSeparationColorSpace *sepCS;
2061
2062   switch (level) {
2063   case psLevel1:
2064     state->getFillGray(&gray);
2065     writePSFmt("%g g\n", gray);
2066     break;
2067   case psLevel1Sep:
2068     state->getFillCMYK(&cmyk);
2069     writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2070     addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2071     break;
2072   case psLevel2:
2073   case psLevel3:
2074     if (state->getFillColorSpace()->getMode() == csDeviceCMYK) {
2075       state->getFillCMYK(&cmyk);
2076       writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2077     } else {
2078       state->getFillRGB(&rgb);
2079       if (rgb.r == rgb.g && rgb.g == rgb.b) {
2080         writePSFmt("%g g\n", rgb.r);
2081       } else {
2082         writePSFmt("%g %g %g rg\n", rgb.r, rgb.g, rgb.b);
2083       }
2084     }
2085     break;
2086   case psLevel2Sep:
2087   case psLevel3Sep:
2088     if (state->getFillColorSpace()->getMode() == csSeparation) {
2089       sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
2090       color.c[0] = 1;
2091       sepCS->getCMYK(&color, &cmyk);
2092       writePSFmt("%g %g %g %g %g (%s) ck\n",
2093                  state->getFillColor()->c[0],
2094                  cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2095                  sepCS->getName()->getCString());
2096       addCustomColor(sepCS);
2097     } else {
2098       state->getFillCMYK(&cmyk);
2099       writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2100       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2101     }
2102     break;
2103   }
2104   t3Cacheable = gFalse;
2105 }
2106
2107 void PSOutputDev::updateStrokeColor(GfxState *state) {
2108   GfxColor color;
2109   double gray;
2110   GfxRGB rgb;
2111   GfxCMYK cmyk;
2112   GfxSeparationColorSpace *sepCS;
2113
2114   switch (level) {
2115   case psLevel1:
2116     state->getStrokeGray(&gray);
2117     writePSFmt("%g G\n", gray);
2118     break;
2119   case psLevel1Sep:
2120     state->getStrokeCMYK(&cmyk);
2121     writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2122     addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2123     break;
2124   case psLevel2:
2125   case psLevel3:
2126     if (state->getStrokeColorSpace()->getMode() == csDeviceCMYK) {
2127       state->getStrokeCMYK(&cmyk);
2128       writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2129     } else {
2130       state->getStrokeRGB(&rgb);
2131       if (rgb.r == rgb.g && rgb.g == rgb.b) {
2132         writePSFmt("%g G\n", rgb.r);
2133       } else {
2134         writePSFmt("%g %g %g RG\n", rgb.r, rgb.g, rgb.b);
2135       }
2136     }
2137     break;
2138   case psLevel2Sep:
2139   case psLevel3Sep:
2140     if (state->getStrokeColorSpace()->getMode() == csSeparation) {
2141       sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
2142       color.c[0] = 1;
2143       sepCS->getCMYK(&color, &cmyk);
2144       writePSFmt("%g %g %g %g %g (%s) CK\n",
2145                  state->getStrokeColor()->c[0],
2146                  cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2147                  sepCS->getName()->getCString());
2148       addCustomColor(sepCS);
2149     } else {
2150       state->getStrokeCMYK(&cmyk);
2151       writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2152       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2153     }
2154     break;
2155   }
2156   t3Cacheable = gFalse;
2157 }
2158
2159 void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
2160   if (c > 0) {
2161     processColors |= psProcessCyan;
2162   }
2163   if (m > 0) {
2164     processColors |= psProcessMagenta;
2165   }
2166   if (y > 0) {
2167     processColors |= psProcessYellow;
2168   }
2169   if (k > 0) {
2170     processColors |= psProcessBlack;
2171   }
2172 }
2173
2174 void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
2175   PSOutCustomColor *cc;
2176   GfxColor color;
2177   GfxCMYK cmyk;
2178
2179   for (cc = customColors; cc; cc = cc->next) {
2180     if (!cc->name->cmp(sepCS->getName())) {
2181       return;
2182     }
2183   }
2184   color.c[0] = 1;
2185   sepCS->getCMYK(&color, &cmyk);
2186   cc = new PSOutCustomColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2187                             sepCS->getName()->copy());
2188   cc->next = customColors;
2189   customColors = cc;
2190 }
2191
2192 void PSOutputDev::updateFont(GfxState *state) {
2193   if (state->getFont()) {
2194     writePSFmt("/F%d_%d %g Tf\n",
2195                state->getFont()->getID()->num, state->getFont()->getID()->gen,
2196                state->getFontSize());
2197   }
2198 }
2199
2200 void PSOutputDev::updateTextMat(GfxState *state) {
2201   double *mat;
2202
2203   mat = state->getTextMat();
2204   writePSFmt("[%g %g %g %g %g %g] Tm\n",
2205              mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
2206 }
2207
2208 void PSOutputDev::updateCharSpace(GfxState *state) {
2209   writePSFmt("%g Tc\n", state->getCharSpace());
2210 }
2211
2212 void PSOutputDev::updateRender(GfxState *state) {
2213   int rm;
2214
2215   rm = state->getRender();
2216   writePSFmt("%d Tr\n", rm);
2217   rm &= 3;
2218   if (rm != 0 && rm != 3) {
2219     t3Cacheable = gFalse;
2220   }
2221 }
2222
2223 void PSOutputDev::updateRise(GfxState *state) {
2224   writePSFmt("%g Ts\n", state->getRise());
2225 }
2226
2227 void PSOutputDev::updateWordSpace(GfxState *state) {
2228   writePSFmt("%g Tw\n", state->getWordSpace());
2229 }
2230
2231 void PSOutputDev::updateHorizScaling(GfxState *state) {
2232   writePSFmt("%g Tz\n", state->getHorizScaling());
2233 }
2234
2235 void PSOutputDev::updateTextPos(GfxState *state) {
2236   writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
2237 }
2238
2239 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
2240   if (state->getFont()->getWMode()) {
2241     writePSFmt("%g TJmV\n", shift);
2242   } else {
2243     writePSFmt("%g TJm\n", shift);
2244   }
2245 }
2246
2247 void PSOutputDev::stroke(GfxState *state) {
2248   doPath(state->getPath());
2249   if (t3String) {
2250     // if we're construct a cacheable Type 3 glyph, we need to do
2251     // everything in the fill color
2252     writePS("Sf\n");
2253   } else {
2254     writePS("S\n");
2255   }
2256 }
2257
2258 void PSOutputDev::fill(GfxState *state) {
2259   doPath(state->getPath());
2260   writePS("f\n");
2261 }
2262
2263 void PSOutputDev::eoFill(GfxState *state) {
2264   doPath(state->getPath());
2265   writePS("f*\n");
2266 }
2267
2268 void PSOutputDev::clip(GfxState *state) {
2269   doPath(state->getPath());
2270   writePS("W\n");
2271 }
2272
2273 void PSOutputDev::eoClip(GfxState *state) {
2274   doPath(state->getPath());
2275   writePS("W*\n");
2276 }
2277
2278 void PSOutputDev::doPath(GfxPath *path) {
2279   GfxSubpath *subpath;
2280   double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
2281   int n, m, i, j;
2282
2283   n = path->getNumSubpaths();
2284
2285   if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
2286     subpath = path->getSubpath(0);
2287     x0 = subpath->getX(0);
2288     y0 = subpath->getY(0);
2289     x4 = subpath->getX(4);
2290     y4 = subpath->getY(4);
2291     if (x4 == x0 && y4 == y0) {
2292       x1 = subpath->getX(1);
2293       y1 = subpath->getY(1);
2294       x2 = subpath->getX(2);
2295       y2 = subpath->getY(2);
2296       x3 = subpath->getX(3);
2297       y3 = subpath->getY(3);
2298       if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
2299         writePSFmt("%g %g %g %g re\n",
2300                    x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
2301                    fabs(x2 - x0), fabs(y1 - y0));
2302         return;
2303       } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
2304         writePSFmt("%g %g %g %g re\n",
2305                    x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
2306                    fabs(x1 - x0), fabs(y2 - y0));
2307         return;
2308       }
2309     }
2310   }
2311
2312   for (i = 0; i < n; ++i) {
2313     subpath = path->getSubpath(i);
2314     m = subpath->getNumPoints();
2315     writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0));
2316     j = 1;
2317     while (j < m) {
2318       if (subpath->getCurve(j)) {
2319         writePSFmt("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
2320                    subpath->getX(j+1), subpath->getY(j+1),
2321                    subpath->getX(j+2), subpath->getY(j+2));
2322         j += 3;
2323       } else {
2324         writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j));
2325         ++j;
2326       }
2327     }
2328     if (subpath->isClosed()) {
2329       writePS("h\n");
2330     }
2331   }
2332 }
2333
2334 void PSOutputDev::drawString(GfxState *state, GString *s) {
2335   GfxFont *font;
2336   int wMode;
2337   GString *s2;
2338   double dx, dy, dx2, dy2, originX, originY;
2339   char *p;
2340   UnicodeMap *uMap;
2341   CharCode code;
2342   Unicode u[8];
2343   char buf[8];
2344   int len, nChars, uLen, n, m, i, j;
2345
2346   // check for invisible text -- this is used by Acrobat Capture
2347   if (state->getRender() == 3) {
2348     return;
2349   }
2350
2351   // ignore empty strings
2352   if (s->getLength() == 0) {
2353     return;
2354   }
2355
2356   // get the font
2357   if (!(font = state->getFont())) {
2358     return;
2359   }
2360   wMode = font->getWMode();
2361
2362   // check for a subtitute 16-bit font
2363   uMap = NULL;
2364   if (font->isCIDFont()) {
2365     for (i = 0; i < font16EncLen; ++i) {
2366       if (font->getID()->num == font16Enc[i].fontID.num &&
2367           font->getID()->gen == font16Enc[i].fontID.gen) {
2368         uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
2369         break;
2370       }
2371     }
2372   }
2373
2374   // compute width of chars in string, ignoring char spacing and word
2375   // spacing -- the Tj operator will adjust for the metrics of the
2376   // font that's actually used
2377   dx = dy = 0;
2378   nChars = 0;
2379   p = s->getCString();
2380   len = s->getLength();
2381   if (font->isCIDFont()) {
2382     s2 = new GString();
2383   } else {
2384     s2 = s;
2385   }
2386   while (len > 0) {
2387     n = font->getNextChar(p, len, &code,
2388                           u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2389                           &dx2, &dy2, &originX, &originY);
2390     if (font->isCIDFont()) {
2391       if (uMap) {
2392         for (i = 0; i < uLen; ++i) {
2393           m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
2394           for (j = 0; j < m; ++j) {
2395             s2->append(buf[j]);
2396           }
2397         }
2398         //~ this really needs to get the number of chars in the target
2399         //~ encoding - which may be more than the number of Unicode
2400         //~ chars
2401         nChars += uLen;
2402       } else {
2403         s2->append((char)((code >> 8) & 0xff));
2404         s2->append((char)(code & 0xff));
2405         ++nChars;
2406       }
2407     }
2408     dx += dx2;
2409     dy += dy2;
2410     p += n;
2411     len -= n;
2412   }
2413   dx *= state->getFontSize() * state->getHorizScaling();
2414   dy *= state->getFontSize();
2415   if (uMap) {
2416     uMap->decRefCnt();
2417   }
2418
2419   if (s2->getLength() > 0) {
2420     writePSString(s2);
2421     if (font->isCIDFont()) {
2422       if (wMode) {
2423         writePSFmt(" %d %g Tj16V\n", nChars, dy);
2424       } else {
2425         writePSFmt(" %d %g Tj16\n", nChars, dx);
2426       }
2427     } else {
2428       writePSFmt(" %g Tj\n", dx);
2429     }
2430   }
2431   if (font->isCIDFont()) {
2432     delete s2;
2433   }
2434
2435   if (state->getRender() & 4) {
2436     haveTextClip = gTrue;
2437   }
2438 }
2439
2440 void PSOutputDev::endTextObject(GfxState *state) {
2441   if (haveTextClip) {
2442     writePS("Tclip\n");
2443     haveTextClip = gFalse;
2444   }
2445 }
2446
2447 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2448                                 int width, int height, GBool invert,
2449                                 GBool inlineImg) {
2450   int len;
2451
2452   len = height * ((width + 7) / 8);
2453   if (level == psLevel1 || level == psLevel1Sep) {
2454     doImageL1(NULL, invert, inlineImg, str, width, height, len);
2455   } else {
2456     doImageL2(ref, NULL, invert, inlineImg, str, width, height, len);
2457   }
2458 }
2459
2460 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2461                             int width, int height, GfxImageColorMap *colorMap,
2462                             int *maskColors, GBool inlineImg) {
2463   int len;
2464
2465   len = height * ((width * colorMap->getNumPixelComps() *
2466                    colorMap->getBits() + 7) / 8);
2467   switch (level) {
2468   case psLevel1:
2469     doImageL1(colorMap, gFalse, inlineImg, str, width, height, len);
2470     break;
2471   case psLevel1Sep:
2472     //~ handle indexed, separation, ... color spaces
2473     doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
2474     break;
2475   case psLevel2:
2476   case psLevel2Sep:
2477   case psLevel3:
2478   case psLevel3Sep:
2479     doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len);
2480     break;
2481   }
2482   t3Cacheable = gFalse;
2483 }
2484
2485 void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
2486                             GBool invert, GBool inlineImg,
2487                             Stream *str, int width, int height, int len) {
2488   ImageStream *imgStr;
2489   Guchar pixBuf[gfxColorMaxComps];
2490   double gray;
2491   int x, y, i;
2492
2493   // width, height, matrix, bits per component
2494   if (colorMap) {
2495     writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
2496                width, height,
2497                width, -height, height);
2498   } else {
2499     writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
2500                width, height, invert ? "true" : "false",
2501                width, -height, height);
2502   }
2503
2504   // image
2505   if (colorMap) {
2506
2507     // set up to process the data stream
2508     imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
2509                              colorMap->getBits());
2510     imgStr->reset();
2511
2512     // process the data stream
2513     i = 0;
2514     for (y = 0; y < height; ++y) {
2515
2516       // write the line
2517       for (x = 0; x < width; ++x) {
2518         imgStr->getPixel(pixBuf);
2519         colorMap->getGray(pixBuf, &gray);
2520         writePSFmt("%02x", (int)(gray * 255 + 0.5));
2521         if (++i == 32) {
2522           writePSChar('\n');
2523           i = 0;
2524         }
2525       }
2526     }
2527     if (i != 0) {
2528       writePSChar('\n');
2529     }
2530     delete imgStr;
2531
2532   // imagemask
2533   } else {
2534     str->reset();
2535     i = 0;
2536     for (y = 0; y < height; ++y) {
2537       for (x = 0; x < width; x += 8) {
2538         writePSFmt("%02x", str->getChar() & 0xff);
2539         if (++i == 32) {
2540           writePSChar('\n');
2541           i = 0;
2542         }
2543       }
2544     }
2545     if (i != 0) {
2546       writePSChar('\n');
2547     }
2548     str->close();
2549   }
2550 }
2551
2552 void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
2553                                GBool invert, GBool inlineImg,
2554                                Stream *str, int width, int height, int len) {
2555   ImageStream *imgStr;
2556   Guchar *lineBuf;
2557   Guchar pixBuf[gfxColorMaxComps];
2558   GfxCMYK cmyk;
2559   int x, y, i, comp;
2560
2561   // width, height, matrix, bits per component
2562   writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n",
2563              width, height,
2564              width, -height, height);
2565
2566   // allocate a line buffer
2567   lineBuf = (Guchar *)gmalloc(4 * width);
2568
2569   // set up to process the data stream
2570   imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
2571                            colorMap->getBits());
2572   imgStr->reset();
2573
2574   // process the data stream
2575   i = 0;
2576   for (y = 0; y < height; ++y) {
2577
2578     // read the line
2579     for (x = 0; x < width; ++x) {
2580       imgStr->getPixel(pixBuf);
2581       colorMap->getCMYK(pixBuf, &cmyk);
2582       lineBuf[4*x+0] = (int)(255 * cmyk.c + 0.5);
2583       lineBuf[4*x+1] = (int)(255 * cmyk.m + 0.5);
2584       lineBuf[4*x+2] = (int)(255 * cmyk.y + 0.5);
2585       lineBuf[4*x+3] = (int)(255 * cmyk.k + 0.5);
2586       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2587     }
2588
2589     // write one line of each color component
2590     for (comp = 0; comp < 4; ++comp) {
2591       for (x = 0; x < width; ++x) {
2592         writePSFmt("%02x", lineBuf[4*x + comp]);
2593         if (++i == 32) {
2594           writePSChar('\n');
2595           i = 0;
2596         }
2597       }
2598     }
2599   }
2600
2601   if (i != 0) {
2602     writePSChar('\n');
2603   }
2604
2605   delete imgStr;
2606   gfree(lineBuf);
2607 }
2608
2609 void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
2610                             GBool invert, GBool inlineImg,
2611                             Stream *str, int width, int height, int len) {
2612   GString *s;
2613   int n, numComps;
2614   GBool useRLE, useASCII, useCompressed;
2615   GfxSeparationColorSpace *sepCS;
2616   GfxColor color;
2617   GfxCMYK cmyk;
2618   int c;
2619   int line, col, i;
2620
2621   // color space
2622   if (colorMap) {
2623     dumpColorSpaceL2(colorMap->getColorSpace());
2624     writePS(" setcolorspace\n");
2625   }
2626
2627   // set up the image data
2628   if (mode == psModeForm || inType3Char) {
2629     if (inlineImg) {
2630       // create an array
2631       str = new FixedLengthEncoder(str, len);
2632       if (globalParams->getPSASCIIHex()) {
2633         str = new ASCIIHexEncoder(str);
2634       } else {
2635         str = new ASCII85Encoder(str);
2636       }
2637       str->reset();
2638       line = col = 0;
2639       writePS("[<~");
2640       do {
2641         do {
2642           c = str->getChar();
2643         } while (c == '\n' || c == '\r');
2644         if (c == '~' || c == EOF) {
2645           break;
2646         }
2647         if (c == 'z') {
2648           writePSChar(c);
2649           ++col;
2650         } else {
2651           writePSChar(c);
2652           ++col;
2653           for (i = 1; i <= 4; ++i) {
2654             do {
2655               c = str->getChar();
2656             } while (c == '\n' || c == '\r');
2657             if (c == '~' || c == EOF) {
2658               break;
2659             }
2660             writePSChar(c);
2661             ++col;
2662           }
2663         }
2664         // each line is: "dup nnnnn <~...data...~> put<eol>"
2665         // so max data length = 255 - 20 = 235
2666         // chunks are 1 or 4 bytes each, so we have to stop at 232
2667         // but make it 225 just to be safe
2668         if (col > 225) {
2669           writePS("~>\n");
2670           ++line;
2671           writePSFmt("<~", line);
2672           col = 0;
2673         }
2674       } while (c != '~' && c != EOF);
2675       writePS("~>]\n");
2676       writePS("0\n");
2677       str->close();
2678       delete str;
2679     } else {
2680       // set up to use the array already created by setupImages()
2681       writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
2682     }
2683   }
2684
2685   // image dictionary
2686   writePS("<<\n  /ImageType 1\n");
2687
2688   // width, height, matrix, bits per component
2689   writePSFmt("  /Width %d\n", width);
2690   writePSFmt("  /Height %d\n", height);
2691   writePSFmt("  /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
2692   writePSFmt("  /BitsPerComponent %d\n",
2693              colorMap ? colorMap->getBits() : 1);
2694
2695   // decode 
2696   if (colorMap) {
2697     writePS("  /Decode [");
2698     if (colorMap->getColorSpace()->getMode() == csSeparation) {
2699       //~ this is a kludge -- see comment in dumpColorSpaceL2
2700       n = (1 << colorMap->getBits()) - 1;
2701       writePSFmt("%g %g", colorMap->getDecodeLow(0) * n,
2702                  colorMap->getDecodeHigh(0) * n);
2703     } else {
2704       numComps = colorMap->getNumPixelComps();
2705       for (i = 0; i < numComps; ++i) {
2706         if (i > 0) {
2707           writePS(" ");
2708         }
2709         writePSFmt("%g %g", colorMap->getDecodeLow(i),
2710                    colorMap->getDecodeHigh(i));
2711       }
2712     }
2713     writePS("]\n");
2714   } else {
2715     writePSFmt("  /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
2716   }
2717
2718   if (mode == psModeForm || inType3Char) {
2719
2720     // data source
2721     writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
2722
2723     // end of image dictionary
2724     writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask");
2725
2726     // get rid of the array and index
2727     writePS("pop pop\n");
2728
2729   } else {
2730
2731     // data source
2732     writePS("  /DataSource currentfile\n");
2733     s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
2734                          "    ");
2735     if (inlineImg || !s) {
2736       useRLE = gTrue;
2737       useASCII = gTrue;
2738       useCompressed = gFalse;
2739     } else {
2740       useRLE = gFalse;
2741       useASCII = str->isBinary();
2742       useCompressed = gTrue;
2743     }
2744     if (useASCII) {
2745       writePSFmt("    /ASCII%sDecode filter\n",
2746                  globalParams->getPSASCIIHex() ? "Hex" : "85");
2747     }
2748     if (useRLE) {
2749       writePS("    /RunLengthDecode filter\n");
2750     }
2751     if (useCompressed) {
2752       writePS(s->getCString());
2753     }
2754     if (s) {
2755       delete s;
2756     }
2757
2758     // cut off inline image streams at appropriate length
2759     if (inlineImg) {
2760       str = new FixedLengthEncoder(str, len);
2761     } else if (useCompressed) {
2762       str = str->getBaseStream();
2763     }
2764
2765     // add RunLengthEncode and ASCIIHex/85 encode filters
2766     if (useRLE) {
2767       str = new RunLengthEncoder(str);
2768     }
2769     if (useASCII) {
2770       if (globalParams->getPSASCIIHex()) {
2771         str = new ASCIIHexEncoder(str);
2772       } else {
2773         str = new ASCII85Encoder(str);
2774       }
2775     }
2776
2777     // end of image dictionary
2778     writePS(">>\n");
2779 #if OPI_SUPPORT
2780     if (opi13Nest) {
2781       if (inlineImg) {
2782         // this can't happen -- OPI dictionaries are in XObjects
2783         error(-1, "Internal: OPI in inline image");
2784         n = 0;
2785       } else {
2786         // need to read the stream to count characters -- the length
2787         // is data-dependent (because of ASCII and RLE filters)
2788         str->reset();
2789         n = 0;
2790         while ((c = str->getChar()) != EOF) {
2791           ++n;
2792         }
2793         str->close();
2794       }
2795       // +6/7 for "pdfIm\n" / "pdfImM\n"
2796       // +8 for newline + trailer
2797       n += colorMap ? 14 : 15;
2798       writePSFmt("%%%%BeginData: %d Hex Bytes\n", n);
2799     }
2800 #endif
2801     if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
2802         colorMap->getColorSpace()->getMode() == csSeparation) {
2803       color.c[0] = 1;
2804       sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
2805       sepCS->getCMYK(&color, &cmyk);
2806       writePSFmt("%g %g %g %g (%s) pdfImSep\n",
2807                  cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2808                  sepCS->getName()->getCString());
2809     } else {
2810       writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM");
2811     }
2812
2813     // copy the stream data
2814     str->reset();
2815     while ((c = str->getChar()) != EOF) {
2816       writePSChar(c);
2817     }
2818     str->close();
2819
2820     // add newline and trailer to the end
2821     writePSChar('\n');
2822     writePS("%-EOD-\n");
2823 #if OPI_SUPPORT
2824     if (opi13Nest) {
2825       writePS("%%EndData\n");
2826     }
2827 #endif
2828
2829     // delete encoders
2830     if (useRLE || useASCII || inlineImg) {
2831       delete str;
2832     }
2833   }
2834 }
2835
2836 void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
2837   GfxCalGrayColorSpace *calGrayCS;
2838   GfxCalRGBColorSpace *calRGBCS;
2839   GfxLabColorSpace *labCS;
2840   GfxIndexedColorSpace *indexedCS;
2841   GfxSeparationColorSpace *separationCS;
2842   GfxColorSpace *baseCS;
2843   Guchar *lookup, *p;
2844   double x[gfxColorMaxComps], y[gfxColorMaxComps];
2845   GfxColor color;
2846   GfxCMYK cmyk;
2847   Function *func;
2848   int n, numComps, numAltComps;
2849   int byte;
2850   int i, j, k;
2851
2852   switch (colorSpace->getMode()) {
2853
2854   case csDeviceGray:
2855     writePS("/DeviceGray");
2856     processColors |= psProcessBlack;
2857     break;
2858
2859   case csCalGray:
2860     calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
2861     writePS("[/CIEBasedA <<\n");
2862     writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
2863     writePSFmt(" /MatrixA [%g %g %g]\n",
2864                calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
2865                calGrayCS->getWhiteZ());
2866     writePSFmt(" /WhitePoint [%g %g %g]\n",
2867                calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
2868                calGrayCS->getWhiteZ());
2869     writePSFmt(" /BlackPoint [%g %g %g]\n",
2870                calGrayCS->getBlackX(), calGrayCS->getBlackY(),
2871                calGrayCS->getBlackZ());
2872     writePS(">>]");
2873     processColors |= psProcessBlack;
2874     break;
2875
2876   case csDeviceRGB:
2877     writePS("/DeviceRGB");
2878     processColors |= psProcessCMYK;
2879     break;
2880
2881   case csCalRGB:
2882     calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
2883     writePS("[/CIEBasedABC <<\n");
2884     writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
2885                calRGBCS->getGammaR(), calRGBCS->getGammaG(),
2886                calRGBCS->getGammaB());
2887     writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n",
2888                calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
2889                calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
2890                calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
2891                calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
2892                calRGBCS->getMatrix()[8]);
2893     writePSFmt(" /WhitePoint [%g %g %g]\n",
2894                calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
2895                calRGBCS->getWhiteZ());
2896     writePSFmt(" /BlackPoint [%g %g %g]\n",
2897                calRGBCS->getBlackX(), calRGBCS->getBlackY(),
2898                calRGBCS->getBlackZ());
2899     writePS(">>]");
2900     processColors |= psProcessCMYK;
2901     break;
2902
2903   case csDeviceCMYK:
2904     writePS("/DeviceCMYK");
2905     processColors |= psProcessCMYK;
2906     break;
2907
2908   case csLab:
2909     labCS = (GfxLabColorSpace *)colorSpace;
2910     writePS("[/CIEBasedABC <<\n");
2911     writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n",
2912                labCS->getAMin(), labCS->getAMax(),
2913                labCS->getBMin(), labCS->getBMax());
2914     writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
2915     writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
2916     writePS(" /DecodeLMN\n");
2917     writePS("   [{dup 6 29 div ge {dup dup mul mul}\n");
2918     writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
2919                labCS->getWhiteX());
2920     writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
2921     writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
2922                labCS->getWhiteY());
2923     writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
2924     writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
2925                labCS->getWhiteZ());
2926     writePSFmt(" /WhitePoint [%g %g %g]\n",
2927                labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
2928     writePSFmt(" /BlackPoint [%g %g %g]\n",
2929                labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
2930     writePS(">>]");
2931     processColors |= psProcessCMYK;
2932     break;
2933
2934   case csICCBased:
2935     dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt());
2936     break;
2937
2938   case csIndexed:
2939     indexedCS = (GfxIndexedColorSpace *)colorSpace;
2940     baseCS = indexedCS->getBase();
2941     writePS("[/Indexed ");
2942     dumpColorSpaceL2(baseCS);
2943     n = indexedCS->getIndexHigh();
2944     numComps = baseCS->getNComps();
2945     lookup = indexedCS->getLookup();
2946     writePSFmt(" %d <\n", n);
2947     if (baseCS->getMode() == csDeviceN) {
2948       func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
2949       numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
2950       p = lookup;
2951       for (i = 0; i <= n; i += 8) {
2952         writePS("  ");
2953         for (j = i; j < i+8 && j <= n; ++j) {
2954           for (k = 0; k < numComps; ++k) {
2955             x[k] = *p++ / 255.0;
2956           }
2957           func->transform(x, y);
2958           for (k = 0; k < numAltComps; ++k) {
2959             byte = (int)(y[k] * 255 + 0.5);
2960             if (byte < 0) {
2961               byte = 0;
2962             } else if (byte > 255) {
2963               byte = 255;
2964             }
2965             writePSFmt("%02x", byte);
2966           }
2967           color.c[0] = j;
2968           indexedCS->getCMYK(&color, &cmyk);
2969           addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2970         }
2971         writePS("\n");
2972       }
2973     } else {
2974       for (i = 0; i <= n; i += 8) {
2975         writePS("  ");
2976         for (j = i; j < i+8 && j <= n; ++j) {
2977           for (k = 0; k < numComps; ++k) {
2978             writePSFmt("%02x", lookup[j * numComps + k]);
2979           }
2980           color.c[0] = j;
2981           indexedCS->getCMYK(&color, &cmyk);
2982           addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2983         }
2984         writePS("\n");
2985       }
2986     }
2987     writePS(">]");
2988     break;
2989
2990   case csSeparation:
2991     //~ this is a kludge -- the correct thing would to ouput a
2992     //~ separation color space, with the specified alternate color
2993     //~ space and tint transform
2994     separationCS = (GfxSeparationColorSpace *)colorSpace;
2995     writePS("[/Indexed ");
2996     dumpColorSpaceL2(separationCS->getAlt());
2997     writePS(" 255 <\n");
2998     numComps = separationCS->getAlt()->getNComps();
2999     for (i = 0; i <= 255; i += 8) {
3000       writePS("  ");
3001       for (j = i; j < i+8 && j <= 255; ++j) {
3002         x[0] = (double)j / 255.0;
3003         separationCS->getFunc()->transform(x, y);
3004         for (k = 0; k < numComps; ++k) {
3005           writePSFmt("%02x", (int)(255 * y[k] + 0.5));
3006         }
3007       }
3008       writePS("\n");
3009     }
3010     writePS(">]");
3011 #if 0 //~ this shouldn't be here since the PS file doesn't actually refer
3012       //~ to this colorant (it's converted to CMYK instead)
3013     addCustomColor(separationCS);
3014 #endif
3015     break;
3016
3017   case csDeviceN:
3018     // DeviceN color spaces are a Level 3 PostScript feature.
3019     dumpColorSpaceL2(((GfxDeviceNColorSpace *)colorSpace)->getAlt());
3020     break;
3021
3022   case csPattern:
3023     //~ unimplemented
3024     break;
3025
3026   }
3027 }
3028
3029 #if OPI_SUPPORT
3030 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
3031   Object dict;
3032
3033   if (globalParams->getPSOPI()) {
3034     opiDict->lookup("2.0", &dict);
3035     if (dict.isDict()) {
3036       opiBegin20(state, dict.getDict());
3037       dict.free();
3038     } else {
3039       dict.free();
3040       opiDict->lookup("1.3", &dict);
3041       if (dict.isDict()) {
3042         opiBegin13(state, dict.getDict());
3043       }
3044       dict.free();
3045     }
3046   }
3047 }
3048
3049 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
3050   Object obj1, obj2, obj3, obj4;
3051   double width, height, left, right, top, bottom;
3052   int w, h;
3053   int i;
3054
3055   writePS("%%BeginOPI: 2.0\n");
3056   writePS("%%Distilled\n");
3057
3058   dict->lookup("F", &obj1);
3059   if (getFileSpec(&obj1, &obj2)) {
3060     writePSFmt("%%%%ImageFileName: %s\n",
3061                obj2.getString()->getCString());
3062     obj2.free();
3063   }
3064   obj1.free();
3065
3066   dict->lookup("MainImage", &obj1);
3067   if (obj1.isString()) {
3068     writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString());
3069   }
3070   obj1.free();
3071
3072   //~ ignoring 'Tags' entry
3073   //~ need to use writePSString() and deal with >255-char lines
3074
3075   dict->lookup("Size", &obj1);
3076   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3077     obj1.arrayGet(0, &obj2);
3078     width = obj2.getNum();
3079     obj2.free();
3080     obj1.arrayGet(1, &obj2);
3081     height = obj2.getNum();
3082     obj2.free();
3083     writePSFmt("%%%%ImageDimensions: %g %g\n", width, height);
3084   }
3085   obj1.free();
3086
3087   dict->lookup("CropRect", &obj1);
3088   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
3089     obj1.arrayGet(0, &obj2);
3090     left = obj2.getNum();
3091     obj2.free();
3092     obj1.arrayGet(1, &obj2);
3093     top = obj2.getNum();
3094     obj2.free();
3095     obj1.arrayGet(2, &obj2);
3096     right = obj2.getNum();
3097     obj2.free();
3098     obj1.arrayGet(3, &obj2);
3099     bottom = obj2.getNum();
3100     obj2.free();
3101     writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
3102   }
3103   obj1.free();
3104
3105   dict->lookup("Overprint", &obj1);
3106   if (obj1.isBool()) {
3107     writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
3108   }
3109   obj1.free();
3110
3111   dict->lookup("Inks", &obj1);
3112   if (obj1.isName()) {
3113     writePSFmt("%%%%ImageInks: %s\n", obj1.getName());
3114   } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
3115     obj1.arrayGet(0, &obj2);
3116     if (obj2.isName()) {
3117       writePSFmt("%%%%ImageInks: %s %d",
3118                  obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
3119       for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
3120         obj1.arrayGet(i, &obj3);
3121         obj1.arrayGet(i+1, &obj4);
3122         if (obj3.isString() && obj4.isNum()) {
3123           writePS(" ");
3124           writePSString(obj3.getString());
3125           writePSFmt(" %g", obj4.getNum());
3126         }
3127         obj3.free();
3128         obj4.free();
3129       }
3130       writePS("\n");
3131     }
3132     obj2.free();
3133   }
3134   obj1.free();
3135
3136   writePS("gsave\n");
3137
3138   writePS("%%BeginIncludedImage\n");
3139
3140   dict->lookup("IncludedImageDimensions", &obj1);
3141   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3142     obj1.arrayGet(0, &obj2);
3143     w = obj2.getInt();
3144     obj2.free();
3145     obj1.arrayGet(1, &obj2);
3146     h = obj2.getInt();
3147     obj2.free();
3148     writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h);
3149   }
3150   obj1.free();
3151
3152   dict->lookup("IncludedImageQuality", &obj1);
3153   if (obj1.isNum()) {
3154     writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum());
3155   }
3156   obj1.free();
3157
3158   ++opi20Nest;
3159 }
3160
3161 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
3162   Object obj1, obj2;
3163   int left, right, top, bottom, samples, bits, width, height;
3164   double c, m, y, k;
3165   double llx, lly, ulx, uly, urx, ury, lrx, lry;
3166   double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
3167   double horiz, vert;
3168   int i, j;
3169
3170   writePS("save\n");
3171   writePS("/opiMatrix2 matrix currentmatrix def\n");
3172   writePS("opiMatrix setmatrix\n");
3173
3174   dict->lookup("F", &obj1);
3175   if (getFileSpec(&obj1, &obj2)) {
3176     writePSFmt("%%ALDImageFileName: %s\n",
3177                obj2.getString()->getCString());
3178     obj2.free();
3179   }
3180   obj1.free();
3181
3182   dict->lookup("CropRect", &obj1);
3183   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
3184     obj1.arrayGet(0, &obj2);
3185     left = obj2.getInt();
3186     obj2.free();
3187     obj1.arrayGet(1, &obj2);
3188     top = obj2.getInt();
3189     obj2.free();
3190     obj1.arrayGet(2, &obj2);
3191     right = obj2.getInt();
3192     obj2.free();
3193     obj1.arrayGet(3, &obj2);
3194     bottom = obj2.getInt();
3195     obj2.free();
3196     writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
3197   }
3198   obj1.free();
3199
3200   dict->lookup("Color", &obj1);
3201   if (obj1.isArray() && obj1.arrayGetLength() == 5) {
3202     obj1.arrayGet(0, &obj2);
3203     c = obj2.getNum();
3204     obj2.free();
3205     obj1.arrayGet(1, &obj2);
3206     m = obj2.getNum();
3207     obj2.free();
3208     obj1.arrayGet(2, &obj2);
3209     y = obj2.getNum();
3210     obj2.free();
3211     obj1.arrayGet(3, &obj2);
3212     k = obj2.getNum();
3213     obj2.free();
3214     obj1.arrayGet(4, &obj2);
3215     if (obj2.isString()) {
3216       writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
3217       writePSString(obj2.getString());
3218       writePS("\n");
3219     }
3220     obj2.free();
3221   }
3222   obj1.free();
3223
3224   dict->lookup("ColorType", &obj1);
3225   if (obj1.isName()) {
3226     writePSFmt("%%ALDImageColorType: %s\n", obj1.getName());
3227   }
3228   obj1.free();
3229
3230   //~ ignores 'Comments' entry
3231   //~ need to handle multiple lines
3232
3233   dict->lookup("CropFixed", &obj1);
3234   if (obj1.isArray()) {
3235     obj1.arrayGet(0, &obj2);
3236     ulx = obj2.getNum();
3237     obj2.free();
3238     obj1.arrayGet(1, &obj2);
3239     uly = obj2.getNum();
3240     obj2.free();
3241     obj1.arrayGet(2, &obj2);
3242     lrx = obj2.getNum();
3243     obj2.free();
3244     obj1.arrayGet(3, &obj2);
3245     lry = obj2.getNum();
3246     obj2.free();
3247     writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
3248   }
3249   obj1.free();
3250
3251   dict->lookup("GrayMap", &obj1);
3252   if (obj1.isArray()) {
3253     writePS("%ALDImageGrayMap:");
3254     for (i = 0; i < obj1.arrayGetLength(); i += 16) {
3255       if (i > 0) {
3256         writePS("\n%%+");
3257       }
3258       for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
3259         obj1.arrayGet(i+j, &obj2);
3260         writePSFmt(" %d", obj2.getInt());
3261         obj2.free();
3262       }
3263     }
3264     writePS("\n");
3265   }
3266   obj1.free();
3267
3268   dict->lookup("ID", &obj1);
3269   if (obj1.isString()) {
3270     writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString());
3271   }
3272   obj1.free();
3273
3274   dict->lookup("ImageType", &obj1);
3275   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3276     obj1.arrayGet(0, &obj2);
3277     samples = obj2.getInt();
3278     obj2.free();
3279     obj1.arrayGet(1, &obj2);
3280     bits = obj2.getInt();
3281     obj2.free();
3282     writePSFmt("%%ALDImageType: %d %d\n", samples, bits);
3283   }
3284   obj1.free();
3285
3286   dict->lookup("Overprint", &obj1);
3287   if (obj1.isBool()) {
3288     writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
3289   }
3290   obj1.free();
3291
3292   dict->lookup("Position", &obj1);
3293   if (obj1.isArray() && obj1.arrayGetLength() == 8) {
3294     obj1.arrayGet(0, &obj2);
3295     llx = obj2.getNum();
3296     obj2.free();
3297     obj1.arrayGet(1, &obj2);
3298     lly = obj2.getNum();
3299     obj2.free();
3300     obj1.arrayGet(2, &obj2);
3301     ulx = obj2.getNum();
3302     obj2.free();
3303     obj1.arrayGet(3, &obj2);
3304     uly = obj2.getNum();
3305     obj2.free();
3306     obj1.arrayGet(4, &obj2);
3307     urx = obj2.getNum();
3308     obj2.free();
3309     obj1.arrayGet(5, &obj2);
3310     ury = obj2.getNum();
3311     obj2.free();
3312     obj1.arrayGet(6, &obj2);
3313     lrx = obj2.getNum();
3314     obj2.free();
3315     obj1.arrayGet(7, &obj2);
3316     lry = obj2.getNum();
3317     obj2.free();
3318     opiTransform(state, llx, lly, &tllx, &tlly);
3319     opiTransform(state, ulx, uly, &tulx, &tuly);
3320     opiTransform(state, urx, ury, &turx, &tury);
3321     opiTransform(state, lrx, lry, &tlrx, &tlry);
3322     writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
3323                tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
3324     obj2.free();
3325   }
3326   obj1.free();
3327
3328   dict->lookup("Resolution", &obj1);
3329   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3330     obj1.arrayGet(0, &obj2);
3331     horiz = obj2.getNum();
3332     obj2.free();
3333     obj1.arrayGet(1, &obj2);
3334     vert = obj2.getNum();
3335     obj2.free();
3336     writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert);
3337     obj2.free();
3338   }
3339   obj1.free();
3340
3341   dict->lookup("Size", &obj1);
3342   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3343     obj1.arrayGet(0, &obj2);
3344     width = obj2.getInt();
3345     obj2.free();
3346     obj1.arrayGet(1, &obj2);
3347     height = obj2.getInt();
3348     obj2.free();
3349     writePSFmt("%%ALDImageDimensions: %d %d\n", width, height);
3350   }
3351   obj1.free();
3352
3353   //~ ignoring 'Tags' entry
3354   //~ need to use writePSString() and deal with >255-char lines
3355
3356   dict->lookup("Tint", &obj1);
3357   if (obj1.isNum()) {
3358     writePSFmt("%%ALDImageTint: %g\n", obj1.getNum());
3359   }
3360   obj1.free();
3361
3362   dict->lookup("Transparency", &obj1);
3363   if (obj1.isBool()) {
3364     writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
3365   }
3366   obj1.free();
3367
3368   writePS("%%BeginObject: image\n");
3369   writePS("opiMatrix2 setmatrix\n");
3370   ++opi13Nest;
3371 }
3372
3373 // Convert PDF user space coordinates to PostScript default user space
3374 // coordinates.  This has to account for both the PDF CTM and the
3375 // PSOutputDev page-fitting transform.
3376 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
3377                                double *x1, double *y1) {
3378   double t;
3379
3380   state->transform(x0, y0, x1, y1);
3381   *x1 += tx;
3382   *y1 += ty;
3383   if (landscape) {
3384     t = *x1;
3385     *x1 = -*y1;
3386     *y1 = t;
3387   }
3388   *x1 *= xScale;
3389   *y1 *= yScale;
3390 }
3391
3392 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
3393   Object dict;
3394
3395   if (globalParams->getPSOPI()) {
3396     opiDict->lookup("2.0", &dict);
3397     if (dict.isDict()) {
3398       writePS("%%EndIncludedImage\n");
3399       writePS("%%EndOPI\n");
3400       writePS("grestore\n");
3401       --opi20Nest;
3402       dict.free();
3403     } else {
3404       dict.free();
3405       opiDict->lookup("1.3", &dict);
3406       if (dict.isDict()) {
3407         writePS("%%EndObject\n");
3408         writePS("restore\n");
3409         --opi13Nest;
3410       }
3411       dict.free();
3412     }
3413   }
3414 }
3415
3416 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
3417   if (fileSpec->isString()) {
3418     fileSpec->copy(fileName);
3419     return gTrue;
3420   }
3421   if (fileSpec->isDict()) {
3422     fileSpec->dictLookup("DOS", fileName);
3423     if (fileName->isString()) {
3424       return gTrue;
3425     }
3426     fileName->free();
3427     fileSpec->dictLookup("Mac", fileName);
3428     if (fileName->isString()) {
3429       return gTrue;
3430     }
3431     fileName->free();
3432     fileSpec->dictLookup("Unix", fileName);
3433     if (fileName->isString()) {
3434       return gTrue;
3435     }
3436     fileName->free();
3437     fileSpec->dictLookup("F", fileName);
3438     if (fileName->isString()) {
3439       return gTrue;
3440     }
3441     fileName->free();
3442   }
3443   return gFalse;
3444 }
3445 #endif // OPI_SUPPORT
3446
3447 void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
3448   writePSFmt("%g %g setcharwidth\n", wx, wy);
3449   writePS("q\n");
3450 }
3451
3452 void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
3453                           double llx, double lly, double urx, double ury) {
3454   t3WX = wx;
3455   t3WY = wy;
3456   t3LLX = llx;
3457   t3LLY = lly;
3458   t3URX = urx;
3459   t3URY = ury;
3460   t3String = new GString();
3461   writePS("q\n");
3462   t3Cacheable = gTrue;
3463 }
3464
3465 void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
3466   Stream *str;
3467   int c;
3468
3469   if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
3470     str = level1Stream;
3471   } else {
3472     str = psStream;
3473   }
3474   str->reset();
3475   while ((c = str->getChar()) != EOF) {
3476     writePSChar(c);
3477   }
3478   str->close();
3479 }
3480
3481 void PSOutputDev::writePSChar(char c) {
3482   if (t3String) {
3483     t3String->append(c);
3484   } else {
3485     (*outputFunc)(outputStream, &c, 1);
3486   }
3487 }
3488
3489 void PSOutputDev::writePS(char *s) {
3490   if (t3String) {
3491     t3String->append(s);
3492   } else {
3493     (*outputFunc)(outputStream, s, strlen(s));
3494   }
3495 }
3496
3497 void PSOutputDev::writePSFmt(const char *fmt, ...) {
3498   va_list args;
3499   char buf[512];
3500
3501   va_start(args, fmt);
3502   vsprintf(buf, fmt, args);
3503   va_end(args);
3504   if (t3String) {
3505     t3String->append(buf);
3506   } else {
3507     (*outputFunc)(outputStream, buf, strlen(buf));
3508   }
3509 }
3510
3511 void PSOutputDev::writePSString(GString *s) {
3512   Guchar *p;
3513   int n;
3514   char buf[8];
3515
3516   writePSChar('(');
3517   for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
3518     if (*p == '(' || *p == ')' || *p == '\\') {
3519       writePSChar('\\');
3520       writePSChar((char)*p);
3521     } else if (*p < 0x20 || *p >= 0x80) {
3522       sprintf(buf, "\\%03o", *p);
3523       if (t3String) {
3524         t3String->append(buf);
3525       } else {
3526         (*outputFunc)(outputStream, buf, strlen(buf));
3527       }
3528     } else {
3529       writePSChar((char)*p);
3530     }
3531   }
3532   writePSChar(')');
3533 }
3534
3535 void PSOutputDev::writePSName(char *s) {
3536   char *p;
3537   char c;
3538
3539   p = s;
3540   while ((c = *p++)) {
3541     if (c <= (char)0x20 || c >= (char)0x7f ||
3542         c == '(' || c == ')' || c == '<' || c == '>' ||
3543         c == '[' || c == ']' || c == '{' || c == '}' ||
3544         c == '/' || c == '%') {
3545       writePSFmt("#%02x", c & 0xff);
3546     } else {
3547       writePSChar(c);
3548     }
3549   }
3550 }
3551
3552 GString *PSOutputDev::filterPSName(GString *name) {
3553   GString *name2;
3554   char buf[8];
3555   int i;
3556   char c;
3557
3558   name2 = new GString();
3559
3560   // ghostscript chokes on names that begin with out-of-limits
3561   // numbers, e.g., 1e4foo is handled correctly (as a name), but
3562   // 1e999foo generates a limitcheck error
3563   c = name->getChar(0);
3564   if (c >= '0' && c <= '9') {
3565     name2->append('f');
3566   }
3567
3568   for (i = 0; i < name->getLength(); ++i) {
3569     c = name->getChar(i);
3570     if (c <= (char)0x20 || c >= (char)0x7f ||
3571         c == '(' || c == ')' || c == '<' || c == '>' ||
3572         c == '[' || c == ']' || c == '{' || c == '}' ||
3573         c == '/' || c == '%') {
3574       sprintf(buf, "#%02x", c & 0xff);
3575       name2->append(buf);
3576     } else {
3577       name2->append(c);
3578     }
3579   }
3580   return name2;
3581 }