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