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