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