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