1 //========================================================================
2 //
3 // PSOutputDev.cc
4 //
5 // Copyright 1996-2013 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 "gmempp.h"
21 #include "GString.h"
22 #include "GList.h"
23 #include "GHash.h"
24 #include "config.h"
25 #include "GlobalParams.h"
26 #include "Object.h"
27 #include "Error.h"
28 #include "Function.h"
29 #include "Gfx.h"
30 #include "GfxState.h"
31 #include "GfxFont.h"
32 #include "UnicodeMap.h"
33 #include "FoFiType1C.h"
34 #include "FoFiTrueType.h"
35 #include "Catalog.h"
36 #include "Page.h"
37 #include "Stream.h"
38 #include "Annot.h"
39 #include "PDFDoc.h"
40 #include "XRef.h"
41 #include "PreScanOutputDev.h"
42 #include "CharCodeToUnicode.h"
43 #include "AcroForm.h"
44 #include "TextString.h"
45 #if HAVE_SPLASH
46 #  include "Splash.h"
47 #  include "SplashBitmap.h"
48 #  include "SplashOutputDev.h"
49 #endif
50 #include "PSOutputDev.h"
51 
52 // the MSVC math.h doesn't define this
53 #ifndef M_PI
54 #define M_PI 3.14159265358979323846
55 #endif
56 
57 //------------------------------------------------------------------------
58 // PostScript prolog and setup
59 //------------------------------------------------------------------------
60 
61 // The '~' escapes mark prolog code that is emitted only in certain
62 // levels:
63 //
64 //   ~[123][ngs]
65 //      ^   ^----- n=psLevel_, g=psLevel_Gray, s=psLevel_Sep
66 //      +----- 1=psLevel1__, 2=psLevel2__, 3=psLevel3__
67 
68 static const char *prolog[] = {
69   "/xpdf 75 dict def xpdf begin",
70   "% PDF special state",
71   "/pdfDictSize 15 def",
72   "~1ns",
73   "/pdfStates 64 array def",
74   "  0 1 63 {",
75   "    pdfStates exch pdfDictSize dict",
76   "    dup /pdfStateIdx 3 index put",
77   "    put",
78   "  } for",
79   "~123ngs",
80   "/bdef { bind def } bind def",
81   "/pdfSetup {",
82   "  /pdfDuplex exch def",
83   "  /setpagedevice where {",
84   "    pop 2 dict begin",
85   "      /Policies 1 dict dup begin /PageSize 6 def end def",
86   "      pdfDuplex { /Duplex true def } if",
87   "    currentdict end setpagedevice",
88   "  } if",
89   "  /pdfPageW 0 def",
90   "  /pdfPageH 0 def",
91   "} def",
92   "/pdfSetupPaper {",
93   "  2 copy pdfPageH ne exch pdfPageW ne or {",
94   "    /pdfPageH exch def",
95   "    /pdfPageW exch def",
96   "    /setpagedevice where {",
97   "      pop 3 dict begin",
98   "        /PageSize [pdfPageW pdfPageH] def",
99   "        pdfDuplex { /Duplex true def } if",
100   "        /ImagingBBox null def",
101   "      currentdict end setpagedevice",
102   "    } if",
103   "  } {",
104   "    pop pop",
105   "  } ifelse",
106   "} def",
107   "~1ns",
108   "/pdfOpNames [",
109   "  /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
110   "  /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
111   "  /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
112   "] def",
113   "~123ngs",
114   "/pdfStartPage {",
115   "~1ns",
116   "  pdfStates 0 get begin",
117   "~23ngs",
118   "  pdfDictSize dict begin",
119   "~23n",
120   "  /pdfFillCS [] def",
121   "  /pdfFillXform {} def",
122   "  /pdfStrokeCS [] def",
123   "  /pdfStrokeXform {} def",
124   "~1n",
125   "  /pdfFill 0 def",
126   "  /pdfStroke 0 def",
127   "~1s",
128   "  /pdfFill [0 0 0 1] def",
129   "  /pdfStroke [0 0 0 1] def",
130   "~23g",
131   "  /pdfFill 0 def",
132   "  /pdfStroke 0 def",
133   "~23ns",
134   "  /pdfFill [0] def",
135   "  /pdfStroke [0] def",
136   "  /pdfFillOP false def",
137   "  /pdfStrokeOP false def",
138   "~123ngs",
139   "  /pdfLastFill false def",
140   "  /pdfLastStroke false def",
141   "  /pdfTextMat [1 0 0 1 0 0] def",
142   "  /pdfFontSize 0 def",
143   "  /pdfCharSpacing 0 def",
144   "  /pdfTextRender 0 def",
145   "  /pdfTextRise 0 def",
146   "  /pdfWordSpacing 0 def",
147   "  /pdfHorizScaling 1 def",
148   "  /pdfTextClipPath [] def",
149   "} def",
150   "/pdfEndPage { end } def",
151   "~23s",
152   "% separation convention operators",
153   "/findcmykcustomcolor where {",
154   "  pop",
155   "}{",
156   "  /findcmykcustomcolor { 5 array astore } def",
157   "} ifelse",
158   "/setcustomcolor where {",
159   "  pop",
160   "}{",
161   "  /setcustomcolor {",
162   "    exch",
163   "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
164   "      0 4 getinterval cvx",
165   "      [ exch /dup load exch { mul exch dup } /forall load",
166   "        /pop load dup ] cvx",
167   "    ] setcolorspace setcolor",
168   "  } def",
169   "} ifelse",
170   "/customcolorimage where {",
171   "  pop",
172   "}{",
173   "  /customcolorimage {",
174   "    gsave",
175   "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
176   "      0 4 getinterval",
177   "      [ exch /dup load exch { mul exch dup } /forall load",
178   "        /pop load dup ] cvx",
179   "    ] setcolorspace",
180   "    10 dict begin",
181   "      /ImageType 1 def",
182   "      /DataSource exch def",
183   "      /ImageMatrix exch def",
184   "      /BitsPerComponent exch def",
185   "      /Height exch def",
186   "      /Width exch def",
187   "      /Decode [1 0] def",
188   "    currentdict end",
189   "    image",
190   "    grestore",
191   "  } def",
192   "} ifelse",
193   "~123ngs",
194   "% PDF color state",
195   "~1n",
196   "/g { dup /pdfFill exch def setgray",
197   "     /pdfLastFill true def /pdfLastStroke false def } def",
198   "/G { dup /pdfStroke exch def setgray",
199   "     /pdfLastStroke true def /pdfLastFill false def } def",
200   "/fCol {",
201   "  pdfLastFill not {",
202   "    pdfFill setgray",
203   "    /pdfLastFill true def /pdfLastStroke false def",
204   "  } if",
205   "} def",
206   "/sCol {",
207   "  pdfLastStroke not {",
208   "    pdfStroke setgray",
209   "    /pdfLastStroke true def /pdfLastFill false def",
210   "  } if",
211   "} def",
212   "~1s",
213   "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
214   "     /pdfLastFill true def /pdfLastStroke false def } def",
215   "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
216   "     /pdfLastStroke true def /pdfLastFill false def } def",
217   "/fCol {",
218   "  pdfLastFill not {",
219   "    pdfFill aload pop setcmykcolor",
220   "    /pdfLastFill true def /pdfLastStroke false def",
221   "  } if",
222   "} def",
223   "/sCol {",
224   "  pdfLastStroke not {",
225   "    pdfStroke aload pop setcmykcolor",
226   "    /pdfLastStroke true def /pdfLastFill false def",
227   "  } if",
228   "} def",
229   "~23n",
230   "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
231   "      setcolorspace } def",
232   "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
233   "      setcolorspace } def",
234   "/sc { pdfLastFill not {",
235   "        pdfFillCS setcolorspace pdfFillOP setoverprint",
236   "      } if",
237   "      dup /pdfFill exch def aload pop pdfFillXform setcolor",
238   "      /pdfLastFill true def /pdfLastStroke false def } def",
239   "/SC { pdfLastStroke not {",
240   "        pdfStrokeCS setcolorspace pdfStrokeOP setoverprint",
241   "      } if",
242   "      dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
243   "      /pdfLastStroke true def /pdfLastFill false def } def",
244   "/op { /pdfFillOP exch def",
245   "      pdfLastFill { pdfFillOP setoverprint } if } def",
246   "/OP { /pdfStrokeOP exch def",
247   "      pdfLastStroke { pdfStrokeOP setoverprint } if } def",
248   "/fCol {",
249   "  pdfLastFill not {",
250   "    pdfFillCS setcolorspace",
251   "    pdfFill aload pop pdfFillXform setcolor",
252   "    pdfFillOP setoverprint",
253   "    /pdfLastFill true def /pdfLastStroke false def",
254   "  } if",
255   "} def",
256   "/sCol {",
257   "  pdfLastStroke not {",
258   "    pdfStrokeCS setcolorspace",
259   "    pdfStroke aload pop pdfStrokeXform setcolor",
260   "    pdfStrokeOP setoverprint",
261   "    /pdfLastStroke true def /pdfLastFill false def",
262   "  } if",
263   "} def",
264   "~23g",
265   "/g { dup /pdfFill exch def setgray",
266   "     /pdfLastFill true def /pdfLastStroke false def } def",
267   "/G { dup /pdfStroke exch def setgray",
268   "     /pdfLastStroke true def /pdfLastFill false def } def",
269   "/fCol {",
270   "  pdfLastFill not {",
271   "    pdfFill setgray",
272   "    /pdfLastFill true def /pdfLastStroke false def",
273   "  } if",
274   "} def",
275   "/sCol {",
276   "  pdfLastStroke not {",
277   "    pdfStroke setgray",
278   "    /pdfLastStroke true def /pdfLastFill false def",
279   "  } if",
280   "} def",
281   "~23s",
282   "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
283   "     pdfFillOP setoverprint",
284   "     /pdfLastFill true def /pdfLastStroke false def } def",
285   "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
286   "     pdfStrokeOP setoverprint",
287   "     /pdfLastStroke true def /pdfLastFill false def } def",
288   "/ck { 6 copy 6 array astore /pdfFill exch def",
289   "      findcmykcustomcolor exch setcustomcolor",
290   "      pdfFillOP setoverprint",
291   "      /pdfLastFill true def /pdfLastStroke false def } def",
292   "/CK { 6 copy 6 array astore /pdfStroke exch def",
293   "      findcmykcustomcolor exch setcustomcolor",
294   "      pdfStrokeOP setoverprint",
295   "      /pdfLastStroke true def /pdfLastFill false def } def",
296   "/op { /pdfFillOP exch def",
297   "      pdfLastFill { pdfFillOP setoverprint } if } def",
298   "/OP { /pdfStrokeOP exch def",
299   "      pdfLastStroke { pdfStrokeOP setoverprint } if } def",
300   "/fCol {",
301   "  pdfLastFill not {",
302   "    pdfFill aload length 4 eq {",
303   "      setcmykcolor",
304   "    }{",
305   "      findcmykcustomcolor exch setcustomcolor",
306   "    } ifelse",
307   "    pdfFillOP setoverprint",
308   "    /pdfLastFill true def /pdfLastStroke false def",
309   "  } if",
310   "} def",
311   "/sCol {",
312   "  pdfLastStroke not {",
313   "    pdfStroke aload length 4 eq {",
314   "      setcmykcolor",
315   "    }{",
316   "      findcmykcustomcolor exch setcustomcolor",
317   "    } ifelse",
318   "    pdfStrokeOP setoverprint",
319   "    /pdfLastStroke true def /pdfLastFill false def",
320   "  } if",
321   "} def",
322   "~3ns",
323   "/opm {",
324   "  /setoverprintmode where { pop setoverprintmode } { pop } ifelse",
325   "} def",
326   "~123ngs",
327   "% build a font",
328   "/pdfMakeFont {",
329   "  4 3 roll findfont",
330   "  4 2 roll matrix scale makefont",
331   "  dup length dict begin",
332   "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
333   "    /Encoding exch def",
334   "    currentdict",
335   "  end",
336   "  definefont pop",
337   "} def",
338   "/pdfMakeFont16 {",
339   "  exch findfont",
340   "  dup length dict begin",
341   "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
342   "    /WMode exch def",
343   "    currentdict",
344   "  end",
345   "  definefont pop",
346   "} def",
347   "~3ngs",
348   "/pdfMakeFont16L3 {",
349   "  1 index /CIDFont resourcestatus {",
350   "    pop pop 1 index /CIDFont findresource /CIDFontType known",
351   "  } {",
352   "    false",
353   "  } ifelse",
354   "  {",
355   "    0 eq { /Identity-H } { /Identity-V } ifelse",
356   "    exch 1 array astore composefont pop",
357   "  } {",
358   "    pdfMakeFont16",
359   "  } ifelse",
360   "} def",
361   "~123ngs",
362   "% graphics state operators",
363   "~1ns",
364   "/q {",
365   "  gsave",
366   "  pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
367   "  pdfStates pdfStateIdx 1 add get begin",
368   "  pdfOpNames { exch def } forall",
369   "} def",
370   "/Q { end grestore } def",
371   "~23ngs",
372   "/q { gsave pdfDictSize dict begin } def",
373   "/Q {",
374   "  end grestore",
375   "} def",
376   "~123ngs",
377   "/cm { concat } def",
378   "/d { setdash } def",
379   "/i { setflat } def",
380   "/j { setlinejoin } def",
381   "/J { setlinecap } def",
382   "/M { setmiterlimit } def",
383   "/w { setlinewidth } def",
384   "% path segment operators",
385   "/m { moveto } def",
386   "/l { lineto } def",
387   "/c { curveto } def",
388   "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
389   "      neg 0 rlineto closepath } def",
390   "/h { closepath } def",
391   "% path painting operators",
392   "/S { sCol stroke } def",
393   "/Sf { fCol stroke } def",
394   "/f { fCol fill } def",
395   "/f* { fCol eofill } def",
396   "% clipping operators",
397   "/W { clip newpath } bdef",
398   "/W* { eoclip newpath } bdef",
399   "/Ws { strokepath clip newpath } bdef",
400   "% text state operators",
401   "/Tc { /pdfCharSpacing exch def } def",
402   "/Tf { dup /pdfFontSize exch def",
403   "      dup pdfHorizScaling mul exch matrix scale",
404   "      pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
405   "      exch findfont exch makefont setfont } def",
406   "/Tr { /pdfTextRender exch def } def",
407   "/Ts { /pdfTextRise exch def } def",
408   "/Tw { /pdfWordSpacing exch def } def",
409   "/Tz { /pdfHorizScaling exch def } def",
410   "% text positioning operators",
411   "/Td { pdfTextMat transform moveto } def",
412   "/Tm { /pdfTextMat exch def } def",
413   "% text string operators",
414   "/xyshow where {",
415   "  pop",
416   "  /xyshow2 {",
417   "    dup length array",
418   "    0 2 2 index length 1 sub {",
419   "      2 index 1 index 2 copy get 3 1 roll 1 add get",
420   "      pdfTextMat dtransform",
421   "      4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put",
422   "    } for",
423   "    exch pop",
424   "    xyshow",
425   "  } def",
426   "}{",
427   "  /xyshow2 {",
428   "    currentfont /FontType get 0 eq {",
429   "      0 2 3 index length 1 sub {",
430   "        currentpoint 4 index 3 index 2 getinterval show moveto",
431   "        2 copy get 2 index 3 2 roll 1 add get",
432   "        pdfTextMat dtransform rmoveto",
433   "      } for",
434   "    } {",
435   "      0 1 3 index length 1 sub {",
436   "        currentpoint 4 index 3 index 1 getinterval show moveto",
437   "        2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
438   "        pdfTextMat dtransform rmoveto",
439   "      } for",
440   "    } ifelse",
441   "    pop pop",
442   "  } def",
443   "} ifelse",
444   "/cshow where {",
445   "  pop",
446   "  /xycp {", // xycharpath
447   "    0 3 2 roll",
448   "    {",
449   "      pop pop currentpoint 3 2 roll",
450   "      1 string dup 0 4 3 roll put false charpath moveto",
451   "      2 copy get 2 index 2 index 1 add get",
452   "      pdfTextMat dtransform rmoveto",
453   "      2 add",
454   "    } exch cshow",
455   "    pop pop",
456   "  } def",
457   "}{",
458   "  /xycp {", // xycharpath
459   "    currentfont /FontType get 0 eq {",
460   "      0 2 3 index length 1 sub {",
461   "        currentpoint 4 index 3 index 2 getinterval false charpath moveto",
462   "        2 copy get 2 index 3 2 roll 1 add get",
463   "        pdfTextMat dtransform rmoveto",
464   "      } for",
465   "    } {",
466   "      0 1 3 index length 1 sub {",
467   "        currentpoint 4 index 3 index 1 getinterval false charpath moveto",
468   "        2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
469   "        pdfTextMat dtransform rmoveto",
470   "      } for",
471   "    } ifelse",
472   "    pop pop",
473   "  } def",
474   "} ifelse",
475   "/Tj {",
476   "  fCol",  // because stringwidth has to draw Type 3 chars
477   "  0 pdfTextRise pdfTextMat dtransform rmoveto",
478   "  currentpoint 4 2 roll",
479   "  pdfTextRender 1 and 0 eq {",
480   "    2 copy xyshow2",
481   "  } if",
482   "  pdfTextRender 3 and dup 1 eq exch 2 eq or {",
483   "    3 index 3 index moveto",
484   "    2 copy",
485   "    currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
486   "    xycp currentpoint stroke moveto",
487   "  } if",
488   "  pdfTextRender 4 and 0 ne {",
489   "    4 2 roll moveto xycp",
490   "    /pdfTextClipPath [ pdfTextClipPath aload pop",
491   "      {/moveto cvx}",
492   "      {/lineto cvx}",
493   "      {/curveto cvx}",
494   "      {/closepath cvx}",
495   "    pathforall ] def",
496   "    currentpoint newpath moveto",
497   "  } {",
498   "    pop pop pop pop",
499   "  } ifelse",
500   "  0 pdfTextRise neg pdfTextMat dtransform rmoveto",
501   "} def",
502   "/Tj3 {",
503   "  pdfTextRender 3 and 3 ne {"
504   "    fCol",  // because stringwidth has to draw Type 3 chars
505   "    0 pdfTextRise pdfTextMat dtransform rmoveto",
506   "    xyshow2",
507   "    0 pdfTextRise neg pdfTextMat dtransform rmoveto",
508   "  } {",
509   "    pop pop",
510   "  } ifelse",
511   "} def",
512   "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0",
513   "       pdfTextMat dtransform rmoveto } def",
514   "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch",
515   "        pdfTextMat dtransform rmoveto } def",
516   "/Tclip { pdfTextClipPath cvx exec clip newpath",
517   "         /pdfTextClipPath [] def } def",
518   "~1ns",
519   "% Level 1 image operators",
520   "~1n",
521   "/pdfIm1 {",
522   "  /pdfImBuf1 4 index string def",
523   "  { currentfile pdfImBuf1 readhexstring pop } image",
524   "} def",
525   "~1s",
526   "/pdfIm1Sep {",
527   "  /pdfImBuf1 4 index string def",
528   "  /pdfImBuf2 4 index string def",
529   "  /pdfImBuf3 4 index string def",
530   "  /pdfImBuf4 4 index string def",
531   "  { currentfile pdfImBuf1 readhexstring pop }",
532   "  { currentfile pdfImBuf2 readhexstring pop }",
533   "  { currentfile pdfImBuf3 readhexstring pop }",
534   "  { currentfile pdfImBuf4 readhexstring pop }",
535   "  true 4 colorimage",
536   "} def",
537   "~1ns",
538   "/pdfImM1 {",
539   "  fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
540   "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
541   "} def",
542   "/pdfImStr {",
543   "  2 copy exch length lt {",
544   "    2 copy get exch 1 add exch",
545   "  } {",
546   "    ()",
547   "  } ifelse",
548   "} def",
549   "/pdfImM1a {",
550   "  { pdfImStr } imagemask",
551   "  pop pop",
552   "} def",
553   "~23ngs",
554   "% Level 2/3 image operators",
555   "/pdfImBuf 100 string def",
556   "/pdfImStr {",
557   "  2 copy exch length lt {",
558   "    2 copy get exch 1 add exch",
559   "  } {",
560   "    ()",
561   "  } ifelse",
562   "} def",
563   "/skipEOD {",
564   "  { currentfile pdfImBuf readline",
565   "    not { pop exit } if",
566   "    (%-EOD-) eq { exit } if } loop",
567   "} def",
568   "/pdfIm { image skipEOD } def",
569   "~3ngs",
570   "/pdfMask {",
571   "  /ReusableStreamDecode filter",
572   "  skipEOD",
573   "  /maskStream exch def",
574   "} def",
575   "/pdfMaskEnd { maskStream closefile } def",
576   "/pdfMaskInit {",
577   "  /maskArray exch def",
578   "  /maskIdx 0 def",
579   "} def",
580   "/pdfMaskSrc {",
581   "  maskIdx maskArray length lt {",
582   "    maskArray maskIdx get",
583   "    /maskIdx maskIdx 1 add def",
584   "  } {",
585   "    ()",
586   "  } ifelse",
587   "} def",
588   "~23s",
589   "/pdfImSep {",
590   "  findcmykcustomcolor exch",
591   "  dup /Width get /pdfImBuf1 exch string def",
592   "  dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
593   "  /pdfImDecodeLow exch def",
594   "  begin Width Height BitsPerComponent ImageMatrix DataSource end",
595   "  /pdfImData exch def",
596   "  { pdfImData pdfImBuf1 readstring pop",
597   "    0 1 2 index length 1 sub {",
598   "      1 index exch 2 copy get",
599   "      pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
600   "      255 exch sub put",
601   "    } for }",
602   "  6 5 roll customcolorimage",
603   "  skipEOD",
604   "} def",
605   "~23ngs",
606   "/pdfImM { fCol imagemask skipEOD } def",
607   "/pr {",
608   "  4 2 roll exch 5 index div exch 4 index div moveto",
609   "  exch 3 index div dup 0 rlineto",
610   "  exch 2 index div 0 exch rlineto",
611   "  neg 0 rlineto",
612   "  closepath",
613   "} def",
614   "/pdfImClip { gsave clip } def",
615   "/pdfImClipEnd { grestore } def",
616   "~23ns",
617   "% shading operators",
618   "/colordelta {",
619   "  false 0 1 3 index length 1 sub {",
620   "    dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
621   "      pop true",
622   "    } if",
623   "  } for",
624   "  exch pop exch pop",
625   "} def",
626   "/funcCol { func n array astore } def",
627   "/funcSH {",
628   "  dup 0 eq {",
629   "    true",
630   "  } {",
631   "    dup 6 eq {",
632   "      false",
633   "    } {",
634   "      4 index 4 index funcCol dup",
635   "      6 index 4 index funcCol dup",
636   "      3 1 roll colordelta 3 1 roll",
637   "      5 index 5 index funcCol dup",
638   "      3 1 roll colordelta 3 1 roll",
639   "      6 index 8 index funcCol dup",
640   "      3 1 roll colordelta 3 1 roll",
641   "      colordelta or or or",
642   "    } ifelse",
643   "  } ifelse",
644   "  {",
645   "    1 add",
646   "    4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
647   "    6 index 6 index 4 index 4 index 4 index funcSH",
648   "    2 index 6 index 6 index 4 index 4 index funcSH",
649   "    6 index 2 index 4 index 6 index 4 index funcSH",
650   "    5 3 roll 3 2 roll funcSH pop pop",
651   "  } {",
652   "    pop 3 index 2 index add 0.5 mul 3 index  2 index add 0.5 mul",
653   "~23n",
654   "    funcCol sc",
655   "~23s",
656   "    funcCol aload pop k",
657   "~23ns",
658   "    dup 4 index exch mat transform m",
659   "    3 index 3 index mat transform l",
660   "    1 index 3 index mat transform l",
661   "    mat transform l pop pop h f*",
662   "  } ifelse",
663   "} def",
664   "/axialCol {",
665   "  dup 0 lt {",
666   "    pop t0",
667   "  } {",
668   "    dup 1 gt {",
669   "      pop t1",
670   "    } {",
671   "      dt mul t0 add",
672   "    } ifelse",
673   "  } ifelse",
674   "  func n array astore",
675   "} def",
676   "/axialSH {",
677   "  dup 2 lt {",
678   "    true",
679   "  } {",
680   "    dup 8 eq {",
681   "      false",
682   "    } {",
683   "      2 index axialCol 2 index axialCol colordelta",
684   "    } ifelse",
685   "  } ifelse",
686   "  {",
687   "    1 add 3 1 roll 2 copy add 0.5 mul",
688   "    dup 4 3 roll exch 4 index axialSH",
689   "    exch 3 2 roll axialSH",
690   "  } {",
691   "    pop 2 copy add 0.5 mul",
692   "~23n",
693   "    axialCol sc",
694   "~23s",
695   "    axialCol aload pop k",
696   "~23ns",
697   "    exch dup dx mul x0 add exch dy mul y0 add",
698   "    3 2 roll dup dx mul x0 add exch dy mul y0 add",
699   "    dx abs dy abs ge {",
700   "      2 copy yMin sub dy mul dx div add yMin m",
701   "      yMax sub dy mul dx div add yMax l",
702   "      2 copy yMax sub dy mul dx div add yMax l",
703   "      yMin sub dy mul dx div add yMin l",
704   "      h f*",
705   "    } {",
706   "      exch 2 copy xMin sub dx mul dy div add xMin exch m",
707   "      xMax sub dx mul dy div add xMax exch l",
708   "      exch 2 copy xMax sub dx mul dy div add xMax exch l",
709   "      xMin sub dx mul dy div add xMin exch l",
710   "      h f*",
711   "    } ifelse",
712   "  } ifelse",
713   "} def",
714   "/radialCol {",
715   "  dup t0 lt {",
716   "    pop t0",
717   "  } {",
718   "    dup t1 gt {",
719   "      pop t1",
720   "    } if",
721   "  } ifelse",
722   "  func n array astore",
723   "} def",
724   "/radialSH {",
725   "  dup 0 eq {",
726   "    true",
727   "  } {",
728   "    dup 8 eq {",
729   "      false",
730   "    } {",
731   "      2 index dt mul t0 add radialCol",
732   "      2 index dt mul t0 add radialCol colordelta",
733   "    } ifelse",
734   "  } ifelse",
735   "  {",
736   "    1 add 3 1 roll 2 copy add 0.5 mul",
737   "    dup 4 3 roll exch 4 index radialSH",
738   "    exch 3 2 roll radialSH",
739   "  } {",
740   "    pop 2 copy add 0.5 mul dt mul t0 add",
741   "~23n",
742   "    radialCol sc",
743   "~23s",
744   "    radialCol aload pop k",
745   "~23ns",
746   "    encl {",
747   "      exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
748   "      0 360 arc h",
749   "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
750   "      360 0 arcn h f",
751   "    } {",
752   "      2 copy",
753   "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
754   "      a1 a2 arcn",
755   "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
756   "      a2 a1 arcn h",
757   "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
758   "      a1 a2 arc",
759   "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
760   "      a2 a1 arc h f",
761   "    } ifelse",
762   "  } ifelse",
763   "} def",
764   "~123ngs",
765   "end",
766   NULL
767 };
768 
769 static const char *minLineWidthProlog[] = {
770   "/pdfDist { dup dtransform dup mul exch dup mul add 0.5 mul sqrt } def",
771   "/pdfIDist { dup idtransform dup mul exch dup mul add 0.5 mul sqrt } def",
772   "/pdfMinLineDist pdfMinLineWidth pdfDist def",
773   "/setlinewidth {",
774   "  dup pdfDist pdfMinLineDist lt {",
775   "    pop pdfMinLineDist pdfIDist",
776   "  } if",
777   "  setlinewidth",
778   "} bind def",
779   NULL
780 };
781 
782 static const char *cmapProlog[] = {
783   "/CIDInit /ProcSet findresource begin",
784   "10 dict begin",
785   "  begincmap",
786   "  /CMapType 1 def",
787   "  /CMapName /Identity-H def",
788   "  /CIDSystemInfo 3 dict dup begin",
789   "    /Registry (Adobe) def",
790   "    /Ordering (Identity) def",
791   "    /Supplement 0 def",
792   "  end def",
793   "  1 begincodespacerange",
794   "    <0000> <ffff>",
795   "  endcodespacerange",
796   "  0 usefont",
797   "  1 begincidrange",
798   "    <0000> <ffff> 0",
799   "  endcidrange",
800   "  endcmap",
801   "  currentdict CMapName exch /CMap defineresource pop",
802   "end",
803   "10 dict begin",
804   "  begincmap",
805   "  /CMapType 1 def",
806   "  /CMapName /Identity-V def",
807   "  /CIDSystemInfo 3 dict dup begin",
808   "    /Registry (Adobe) def",
809   "    /Ordering (Identity) def",
810   "    /Supplement 0 def",
811   "  end def",
812   "  /WMode 1 def",
813   "  1 begincodespacerange",
814   "    <0000> <ffff>",
815   "  endcodespacerange",
816   "  0 usefont",
817   "  1 begincidrange",
818   "    <0000> <ffff> 0",
819   "  endcidrange",
820   "  endcmap",
821   "  currentdict CMapName exch /CMap defineresource pop",
822   "end",
823   "end",
824   NULL
825 };
826 
827 //------------------------------------------------------------------------
828 // Fonts
829 //------------------------------------------------------------------------
830 
831 struct PSSubstFont {
832   const char *psName;		// PostScript name
833   double mWidth;		// width of 'm' character
834 };
835 
836 // NB: must be in same order as base14SubstFonts in GfxFont.cc
837 static PSSubstFont psBase14SubstFonts[14] = {
838   {"Courier",               0.600},
839   {"Courier-Oblique",       0.600},
840   {"Courier-Bold",          0.600},
841   {"Courier-BoldOblique",   0.600},
842   {"Helvetica",             0.833},
843   {"Helvetica-Oblique",     0.833},
844   {"Helvetica-Bold",        0.889},
845   {"Helvetica-BoldOblique", 0.889},
846   {"Times-Roman",           0.788},
847   {"Times-Italic",          0.722},
848   {"Times-Bold",            0.833},
849   {"Times-BoldItalic",      0.778},
850   // the last two are never used for substitution
851   {"Symbol",                0},
852   {"ZapfDingbats",          0}
853 };
854 
855 class PSFontInfo {
856 public:
857 
PSFontInfo(Ref fontIDA)858   PSFontInfo(Ref fontIDA)
859     { fontID = fontIDA; ff = NULL; }
860 
861   Ref fontID;
862   PSFontFileInfo *ff;		// pointer to font file info; NULL indicates
863 				//   font mapping failed
864 };
865 
866 enum PSFontFileLocation {
867   psFontFileResident,
868   psFontFileEmbedded,
869   psFontFileExternal
870 };
871 
872 class PSFontFileInfo {
873 public:
874 
875   PSFontFileInfo(GString *psNameA, GfxFontType typeA,
876 		 PSFontFileLocation locA);
877   ~PSFontFileInfo();
878 
879   GString *psName;		// name under which font is defined
880   GfxFontType type;		// font type
881   PSFontFileLocation loc;	// font location
882   Ref embFontID;		// object ID for the embedded font file
883 				//   (for all embedded fonts)
884   GString *extFileName;		// external font file path
885 				//   (for all external fonts)
886   GString *encoding;		// encoding name (for resident CID fonts)
887   int *codeToGID;		// mapping from code/CID to GID
888 				//   (for TrueType, OpenType-TrueType, and
889 				//   CID OpenType-CFF fonts)
890   int codeToGIDLen;		// length of codeToGID array
891 };
892 
PSFontFileInfo(GString * psNameA,GfxFontType typeA,PSFontFileLocation locA)893 PSFontFileInfo::PSFontFileInfo(GString *psNameA, GfxFontType typeA,
894 			       PSFontFileLocation locA) {
895   psName = psNameA;
896   type = typeA;
897   loc = locA;
898   embFontID.num = embFontID.gen = -1;
899   extFileName = NULL;
900   encoding = NULL;
901   codeToGID = NULL;
902   codeToGIDLen = 0;
903 }
904 
~PSFontFileInfo()905 PSFontFileInfo::~PSFontFileInfo() {
906   delete psName;
907   if (extFileName) {
908     delete extFileName;
909   }
910   if (encoding) {
911     delete encoding;
912   }
913   if (codeToGID) {
914     gfree(codeToGID);
915   }
916 }
917 
918 //------------------------------------------------------------------------
919 // process colors
920 //------------------------------------------------------------------------
921 
922 #define psProcessCyan     1
923 #define psProcessMagenta  2
924 #define psProcessYellow   4
925 #define psProcessBlack    8
926 #define psProcessCMYK    15
927 
928 //------------------------------------------------------------------------
929 // PSOutCustomColor
930 //------------------------------------------------------------------------
931 
932 class PSOutCustomColor {
933 public:
934 
935   PSOutCustomColor(double cA, double mA,
936 		   double yA, double kA, GString *nameA);
937   ~PSOutCustomColor();
938 
939   double c, m, y, k;
940   GString *name;
941   PSOutCustomColor *next;
942 };
943 
PSOutCustomColor(double cA,double mA,double yA,double kA,GString * nameA)944 PSOutCustomColor::PSOutCustomColor(double cA, double mA,
945 				   double yA, double kA, GString *nameA) {
946   c = cA;
947   m = mA;
948   y = yA;
949   k = kA;
950   name = nameA;
951   next = NULL;
952 }
953 
~PSOutCustomColor()954 PSOutCustomColor::~PSOutCustomColor() {
955   delete name;
956 }
957 
958 //------------------------------------------------------------------------
959 
960 struct PSOutImgClipRect {
961   int x0, x1, y0, y1;
962 };
963 
964 //------------------------------------------------------------------------
965 
966 struct PSOutPaperSize {
PSOutPaperSizePSOutPaperSize967   PSOutPaperSize(int wA, int hA) { w = wA; h = hA; }
968   int w, h;
969 };
970 
971 //------------------------------------------------------------------------
972 // DeviceNRecoder
973 //------------------------------------------------------------------------
974 
975 class DeviceNRecoder: public FilterStream {
976 public:
977 
978   DeviceNRecoder(Stream *strA, int widthA, int heightA,
979 		 GfxImageColorMap *colorMapA);
980   virtual ~DeviceNRecoder();
981   virtual Stream *copy();
getKind()982   virtual StreamKind getKind() { return strWeird; }
983   virtual void reset();
984   virtual void close();
getChar()985   virtual int getChar()
986     { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
lookChar()987   virtual int lookChar()
988     { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
getPSFilter(int psLevel,const char * indent,GBool okToReadStream)989   virtual GString *getPSFilter(int psLevel, const char *indent,
990 			       GBool okToReadStream)
991     { return NULL; }
isBinary(GBool last=gTrue)992   virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
isEncoder()993   virtual GBool isEncoder() { return gTrue; }
994 
995 private:
996 
997   GBool fillBuf();
998 
999   int width, height;
1000   GfxImageColorMap *colorMap;
1001   Function *func;
1002   ImageStream *imgStr;
1003   int buf[gfxColorMaxComps];
1004   int pixelIdx;
1005   int bufIdx;
1006   int bufSize;
1007 };
1008 
DeviceNRecoder(Stream * strA,int widthA,int heightA,GfxImageColorMap * colorMapA)1009 DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA,
1010 			       GfxImageColorMap *colorMapA):
1011     FilterStream(strA) {
1012   width = widthA;
1013   height = heightA;
1014   colorMap = colorMapA;
1015   imgStr = NULL;
1016   pixelIdx = 0;
1017   bufIdx = gfxColorMaxComps;
1018   bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
1019               getAlt()->getNComps();
1020   func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
1021            getTintTransformFunc();
1022 }
1023 
~DeviceNRecoder()1024 DeviceNRecoder::~DeviceNRecoder() {
1025   if (str->isEncoder()) {
1026     delete str;
1027   }
1028 }
1029 
copy()1030 Stream *DeviceNRecoder::copy() {
1031   error(errInternal, -1, "Called copy() on DeviceNRecoder");
1032   return NULL;
1033 }
1034 
reset()1035 void DeviceNRecoder::reset() {
1036   imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
1037 			   colorMap->getBits());
1038   imgStr->reset();
1039 }
1040 
close()1041 void DeviceNRecoder::close() {
1042   delete imgStr;
1043   imgStr = NULL;
1044   str->close();
1045 }
1046 
fillBuf()1047 GBool DeviceNRecoder::fillBuf() {
1048   Guchar pixBuf[gfxColorMaxComps];
1049   GfxColor color;
1050   double x[gfxColorMaxComps], y[gfxColorMaxComps];
1051   int i;
1052 
1053   if (pixelIdx >= width * height) {
1054     return gFalse;
1055   }
1056   imgStr->getPixel(pixBuf);
1057   colorMap->getColor(pixBuf, &color);
1058   for (i = 0;
1059        i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps();
1060        ++i) {
1061     x[i] = colToDbl(color.c[i]);
1062   }
1063   func->transform(x, y);
1064   for (i = 0; i < bufSize; ++i) {
1065     buf[i] = (int)(y[i] * 255 + 0.5);
1066   }
1067   bufIdx = 0;
1068   ++pixelIdx;
1069   return gTrue;
1070 }
1071 
1072 //------------------------------------------------------------------------
1073 // GrayRecoder
1074 //------------------------------------------------------------------------
1075 
1076 class GrayRecoder: public FilterStream {
1077 public:
1078 
1079   GrayRecoder(Stream *strA, int widthA, int heightA,
1080 	      GfxImageColorMap *colorMapA);
1081   virtual ~GrayRecoder();
1082   virtual Stream *copy();
getKind()1083   virtual StreamKind getKind() { return strWeird; }
1084   virtual void reset();
1085   virtual void close();
getChar()1086   virtual int getChar()
1087     { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx++]; }
lookChar()1088   virtual int lookChar()
1089     { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx]; }
getPSFilter(int psLevel,const char * indent,GBool okToReadStream)1090   virtual GString *getPSFilter(int psLevel, const char *indent,
1091 			       GBool okToReadStream)
1092     { return NULL; }
isBinary(GBool last=gTrue)1093   virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
isEncoder()1094   virtual GBool isEncoder() { return gTrue; }
1095 
1096 private:
1097 
1098   GBool fillBuf();
1099 
1100   int width, height;
1101   GfxImageColorMap *colorMap;
1102   ImageStream *imgStr;
1103   Guchar *buf;
1104   int bufIdx;
1105 };
1106 
GrayRecoder(Stream * strA,int widthA,int heightA,GfxImageColorMap * colorMapA)1107 GrayRecoder::GrayRecoder(Stream *strA, int widthA, int heightA,
1108 			 GfxImageColorMap *colorMapA):
1109     FilterStream(strA) {
1110   width = widthA;
1111   height = heightA;
1112   colorMap = colorMapA;
1113   imgStr = NULL;
1114   buf = (Guchar *)gmalloc(width);
1115   bufIdx = width;
1116 }
1117 
~GrayRecoder()1118 GrayRecoder::~GrayRecoder() {
1119   gfree(buf);
1120   if (str->isEncoder()) {
1121     delete str;
1122   }
1123 }
1124 
copy()1125 Stream *GrayRecoder::copy() {
1126   error(errInternal, -1, "Called copy() on GrayRecoder");
1127   return NULL;
1128 }
1129 
reset()1130 void GrayRecoder::reset() {
1131   imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
1132 			   colorMap->getBits());
1133   imgStr->reset();
1134 }
1135 
close()1136 void GrayRecoder::close() {
1137   delete imgStr;
1138   imgStr = NULL;
1139   str->close();
1140 }
1141 
fillBuf()1142 GBool GrayRecoder::fillBuf() {
1143   Guchar *line;
1144 
1145   if (!(line = imgStr->getLine())) {
1146     bufIdx = width;
1147     return gFalse;
1148   }
1149   //~ this should probably use the rendering intent from the image
1150   //~   dict, or from the content stream
1151   colorMap->getGrayByteLine(line, buf, width,
1152 			    gfxRenderingIntentRelativeColorimetric);
1153   bufIdx = 0;
1154   return gTrue;
1155 }
1156 
1157 //------------------------------------------------------------------------
1158 // ColorKeyToMaskEncoder
1159 //------------------------------------------------------------------------
1160 
1161 class ColorKeyToMaskEncoder: public FilterStream {
1162 public:
1163 
1164   ColorKeyToMaskEncoder(Stream *strA, int widthA, int heightA,
1165 			GfxImageColorMap *colorMapA, int *maskColorsA);
1166   virtual ~ColorKeyToMaskEncoder();
1167   virtual Stream *copy();
getKind()1168   virtual StreamKind getKind() { return strWeird; }
1169   virtual void reset();
1170   virtual void close();
getChar()1171   virtual int getChar()
1172     { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
lookChar()1173   virtual int lookChar()
1174     { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
getPSFilter(int psLevel,const char * indent,GBool okToReadStream)1175   virtual GString *getPSFilter(int psLevel, const char *indent,
1176 			       GBool okToReadStream)
1177     { return NULL; }
isBinary(GBool last=gTrue)1178   virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
isEncoder()1179   virtual GBool isEncoder() { return gTrue; }
1180 
1181 private:
1182 
1183   GBool fillBuf();
1184 
1185   int width, height;
1186   GfxImageColorMap *colorMap;
1187   int numComps;
1188   int *maskColors;
1189   ImageStream *imgStr;
1190   Guchar *buf;
1191   int bufIdx;
1192   int bufSize;
1193 };
1194 
ColorKeyToMaskEncoder(Stream * strA,int widthA,int heightA,GfxImageColorMap * colorMapA,int * maskColorsA)1195 ColorKeyToMaskEncoder::ColorKeyToMaskEncoder(Stream *strA,
1196 					     int widthA, int heightA,
1197 					     GfxImageColorMap *colorMapA,
1198 					     int *maskColorsA):
1199   FilterStream(strA)
1200 {
1201   width = widthA;
1202   height = heightA;
1203   colorMap = colorMapA;
1204   numComps = colorMap->getNumPixelComps();
1205   maskColors = maskColorsA;
1206   imgStr = NULL;
1207   bufSize = (width + 7) / 8;
1208   buf = (Guchar *)gmalloc(bufSize);
1209   bufIdx = width;
1210 }
1211 
~ColorKeyToMaskEncoder()1212 ColorKeyToMaskEncoder::~ColorKeyToMaskEncoder() {
1213   gfree(buf);
1214   if (str->isEncoder()) {
1215     delete str;
1216   }
1217 }
1218 
copy()1219 Stream *ColorKeyToMaskEncoder::copy() {
1220   error(errInternal, -1, "Called copy() on ColorKeyToMaskEncoder");
1221   return NULL;
1222 }
1223 
reset()1224 void ColorKeyToMaskEncoder::reset() {
1225   imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
1226 			   colorMap->getBits());
1227   imgStr->reset();
1228 }
1229 
close()1230 void ColorKeyToMaskEncoder::close() {
1231   delete imgStr;
1232   imgStr = NULL;
1233   str->close();
1234 }
1235 
fillBuf()1236 GBool ColorKeyToMaskEncoder::fillBuf() {
1237   Guchar *line, *linePtr, *bufPtr;
1238   Guchar byte;
1239   int x, xx, i;
1240 
1241   if (!(line = imgStr->getLine())) {
1242     bufIdx = width;
1243     return gFalse;
1244   }
1245   linePtr = line;
1246   bufPtr = buf;
1247   for (x = 0; x < width; x += 8) {
1248     byte = 0;
1249     for (xx = 0; xx < 8; ++xx) {
1250       byte = (Guchar)(byte << 1);
1251       if (x + xx < width) {
1252 	for (i = 0; i < numComps; ++i) {
1253 	  if (linePtr[i] < maskColors[2 * i] ||
1254 	      linePtr[i] > maskColors[2 * i + 1]) {
1255 	    break;
1256 	  }
1257 	}
1258 	if (i >= numComps) {
1259 	  byte |= 1;
1260 	}
1261 	linePtr += numComps;
1262       } else {
1263 	byte |= 1;
1264       }
1265     }
1266     *bufPtr++ = byte;
1267   }
1268   bufIdx = 0;
1269   return gTrue;
1270 }
1271 
1272 //------------------------------------------------------------------------
1273 // PSOutputDev
1274 //------------------------------------------------------------------------
1275 
1276 extern "C" {
1277 typedef void (*SignalFunc)(int);
1278 }
1279 
outputToFile(void * stream,const char * data,int len)1280 static void outputToFile(void *stream, const char *data, int len) {
1281   fwrite(data, 1, len, (FILE *)stream);
1282 }
1283 
PSOutputDev(char * fileName,PDFDoc * docA,int firstPageA,int lastPageA,PSOutMode modeA,int imgLLXA,int imgLLYA,int imgURXA,int imgURYA,GBool manualCtrlA,PSOutCustomCodeCbk customCodeCbkA,void * customCodeCbkDataA,GBool honorUserUnitA,GBool fileNameIsUTF8)1284 PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA,
1285 			 int firstPageA, int lastPageA, PSOutMode modeA,
1286 			 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
1287 			 GBool manualCtrlA,
1288 			 PSOutCustomCodeCbk customCodeCbkA,
1289 			 void *customCodeCbkDataA,
1290 			 GBool honorUserUnitA,
1291 			 GBool fileNameIsUTF8) {
1292   FILE *f;
1293   PSFileType fileTypeA;
1294 
1295   underlayCbk = NULL;
1296   underlayCbkData = NULL;
1297   overlayCbk = NULL;
1298   overlayCbkData = NULL;
1299   customCodeCbk = customCodeCbkA;
1300   customCodeCbkData = customCodeCbkDataA;
1301 
1302   rasterizePage = NULL;
1303   fontInfo = new GList();
1304   fontFileInfo = new GHash();
1305   imgIDs = NULL;
1306   formIDs = NULL;
1307   visitedResources = NULL;
1308   saveStack = NULL;
1309   paperSizes = NULL;
1310   embFontList = NULL;
1311   customColors = NULL;
1312   haveTextClip = gFalse;
1313   t3String = NULL;
1314 
1315   // open file or pipe
1316   if (!strcmp(fileName, "-")) {
1317     fileTypeA = psStdout;
1318     f = stdout;
1319   } else if (fileName[0] == '|') {
1320     fileTypeA = psPipe;
1321 #ifdef HAVE_POPEN
1322 #ifndef _WIN32
1323     signal(SIGPIPE, (SignalFunc)SIG_IGN);
1324 #endif
1325     if (!(f = popen(fileName + 1, "w"))) {
1326       error(errIO, -1, "Couldn't run print command '{0:s}'", fileName);
1327       ok = gFalse;
1328       return;
1329     }
1330 #else
1331     error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName);
1332     ok = gFalse;
1333     return;
1334 #endif
1335   } else {
1336     fileTypeA = psFile;
1337     if (fileNameIsUTF8) {
1338       f = openFile(fileName, "w");
1339     } else {
1340       f = fopen(fileName, "w");
1341     }
1342     if (!f) {
1343       error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName);
1344       ok = gFalse;
1345       return;
1346     }
1347   }
1348 
1349   init(outputToFile, f, fileTypeA,
1350        docA, firstPageA, lastPageA, modeA,
1351        imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA);
1352 }
1353 
PSOutputDev(PSOutputFunc outputFuncA,void * outputStreamA,PDFDoc * docA,int firstPageA,int lastPageA,PSOutMode modeA,int imgLLXA,int imgLLYA,int imgURXA,int imgURYA,GBool manualCtrlA,PSOutCustomCodeCbk customCodeCbkA,void * customCodeCbkDataA,GBool honorUserUnitA)1354 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
1355 			 PDFDoc *docA,
1356 			 int firstPageA, int lastPageA, PSOutMode modeA,
1357 			 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
1358 			 GBool manualCtrlA,
1359 			 PSOutCustomCodeCbk customCodeCbkA,
1360 			 void *customCodeCbkDataA,
1361 			 GBool honorUserUnitA) {
1362   underlayCbk = NULL;
1363   underlayCbkData = NULL;
1364   overlayCbk = NULL;
1365   overlayCbkData = NULL;
1366   customCodeCbk = customCodeCbkA;
1367   customCodeCbkData = customCodeCbkDataA;
1368 
1369   rasterizePage = NULL;
1370   fontInfo = new GList();
1371   fontFileInfo = new GHash();
1372   imgIDs = NULL;
1373   formIDs = NULL;
1374   visitedResources = NULL;
1375   saveStack = NULL;
1376   paperSizes = NULL;
1377   embFontList = NULL;
1378   customColors = NULL;
1379   haveTextClip = gFalse;
1380   t3String = NULL;
1381 
1382   init(outputFuncA, outputStreamA, psGeneric,
1383        docA, firstPageA, lastPageA, modeA,
1384        imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA);
1385 }
1386 
init(PSOutputFunc outputFuncA,void * outputStreamA,PSFileType fileTypeA,PDFDoc * docA,int firstPageA,int lastPageA,PSOutMode modeA,int imgLLXA,int imgLLYA,int imgURXA,int imgURYA,GBool manualCtrlA,GBool honorUserUnitA)1387 void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
1388 		       PSFileType fileTypeA, PDFDoc *docA,
1389 		       int firstPageA, int lastPageA, PSOutMode modeA,
1390 		       int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
1391 		       GBool manualCtrlA, GBool honorUserUnitA) {
1392   Catalog *catalog;
1393   Page *page;
1394   PDFRectangle *box;
1395   PSOutPaperSize *size;
1396   PSFontFileInfo *ff;
1397   GList *names;
1398   double userUnit;
1399   int pg, w, h, i;
1400 
1401   // initialize
1402   ok = gTrue;
1403   outputFunc = outputFuncA;
1404   outputStream = outputStreamA;
1405   fileType = fileTypeA;
1406   doc = docA;
1407   xref = doc->getXRef();
1408   catalog = doc->getCatalog();
1409   if ((firstPage = firstPageA) < 1) {
1410     firstPage = 1;
1411   }
1412   if ((lastPage = lastPageA) > doc->getNumPages()) {
1413     lastPage = doc->getNumPages();
1414   }
1415   level = globalParams->getPSLevel();
1416   mode = modeA;
1417   honorUserUnit = honorUserUnitA;
1418   paperWidth = globalParams->getPSPaperWidth();
1419   paperHeight = globalParams->getPSPaperHeight();
1420   imgLLX = imgLLXA;
1421   imgLLY = imgLLYA;
1422   imgURX = imgURXA;
1423   imgURY = imgURYA;
1424   if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) {
1425     globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY);
1426   }
1427   if (paperWidth < 0 || paperHeight < 0) {
1428     paperMatch = gTrue;
1429     paperSizes = new GList();
1430     paperWidth = paperHeight = 1; // in case the document has zero pages
1431     for (pg = firstPage; pg <= lastPage; ++pg) {
1432       page = catalog->getPage(pg);
1433       if (honorUserUnit) {
1434 	userUnit = page->getUserUnit();
1435       } else {
1436 	userUnit = 1;
1437       }
1438       if (globalParams->getPSUseCropBoxAsPage()) {
1439 	w = (int)ceil(page->getCropWidth() * userUnit);
1440 	h = (int)ceil(page->getCropHeight() * userUnit);
1441       } else {
1442 	w = (int)ceil(page->getMediaWidth() * userUnit);
1443 	h = (int)ceil(page->getMediaHeight() * userUnit);
1444       }
1445       for (i = 0; i < paperSizes->getLength(); ++i) {
1446 	size = (PSOutPaperSize *)paperSizes->get(i);
1447 	if (size->w == w && size->h == h) {
1448 	  break;
1449 	}
1450       }
1451       if (i == paperSizes->getLength()) {
1452 	paperSizes->append(new PSOutPaperSize(w, h));
1453       }
1454       if (w > paperWidth) {
1455 	paperWidth = w;
1456       }
1457       if (h > paperHeight) {
1458 	paperHeight = h;
1459       }
1460     }
1461     // NB: img{LLX,LLY,URX,URY} will be set by startPage()
1462   } else {
1463     paperMatch = gFalse;
1464   }
1465   preload = globalParams->getPSPreload();
1466   manualCtrl = manualCtrlA;
1467   if (mode == psModeForm) {
1468     lastPage = firstPage;
1469   }
1470   processColors = 0;
1471   inType3Char = gFalse;
1472 
1473 #if OPI_SUPPORT
1474   // initialize OPI nesting levels
1475   opi13Nest = 0;
1476   opi20Nest = 0;
1477 #endif
1478 
1479   tx0 = ty0 = -1;
1480   xScale0 = yScale0 = 0;
1481   rotate0 = -1;
1482   clipLLX0 = clipLLY0 = 0;
1483   clipURX0 = clipURY0 = -1;
1484 
1485   // initialize font lists, etc.
1486   for (i = 0; i < 14; ++i) {
1487     ff = new PSFontFileInfo(new GString(psBase14SubstFonts[i].psName),
1488 			    fontType1, psFontFileResident);
1489     fontFileInfo->add(ff->psName, ff);
1490   }
1491   names = globalParams->getPSResidentFonts();
1492   for (i = 0; i < names->getLength(); ++i) {
1493     if (!fontFileInfo->lookup((GString *)names->get(i))) {
1494       ff = new PSFontFileInfo((GString *)names->get(i), fontType1,
1495 			      psFontFileResident);
1496       fontFileInfo->add(ff->psName, ff);
1497     }
1498   }
1499   delete names;
1500   imgIDLen = 0;
1501   imgIDSize = 0;
1502   formIDLen = 0;
1503   formIDSize = 0;
1504 
1505   noStateChanges = gFalse;
1506   saveStack = new GList();
1507   numTilingPatterns = 0;
1508   nextFunc = 0;
1509 
1510   // initialize embedded font resource comment list
1511   embFontList = new GString();
1512 
1513   if (!manualCtrl) {
1514     // this check is needed in case the document has zero pages
1515     if (firstPage <= catalog->getNumPages()) {
1516       writeHeader(catalog->getPage(firstPage)->getMediaBox(),
1517 		  catalog->getPage(firstPage)->getCropBox(),
1518 		  catalog->getPage(firstPage)->getRotate());
1519     } else {
1520       box = new PDFRectangle(0, 0, 1, 1);
1521       writeHeader(box, box, 0);
1522       delete box;
1523     }
1524     if (mode != psModeForm) {
1525       writePS("%%BeginProlog\n");
1526     }
1527     writeXpdfProcset();
1528     if (mode != psModeForm) {
1529       writePS("%%EndProlog\n");
1530       writePS("%%BeginSetup\n");
1531     }
1532     writeDocSetup(catalog);
1533     if (mode != psModeForm) {
1534       writePS("%%EndSetup\n");
1535     }
1536   }
1537 
1538   // initialize sequential page number
1539   seqPage = 1;
1540 }
1541 
~PSOutputDev()1542 PSOutputDev::~PSOutputDev() {
1543   PSOutCustomColor *cc;
1544 
1545   if (ok) {
1546     if (!manualCtrl) {
1547       writePS("%%Trailer\n");
1548       writeTrailer();
1549       if (mode != psModeForm) {
1550 	writePS("%%EOF\n");
1551       }
1552     }
1553     if (fileType == psFile) {
1554       fclose((FILE *)outputStream);
1555     }
1556 #ifdef HAVE_POPEN
1557     else if (fileType == psPipe) {
1558       pclose((FILE *)outputStream);
1559 #ifndef _WIN32
1560       signal(SIGPIPE, (SignalFunc)SIG_DFL);
1561 #endif
1562     }
1563 #endif
1564   }
1565   gfree(rasterizePage);
1566   if (paperSizes) {
1567     deleteGList(paperSizes, PSOutPaperSize);
1568   }
1569   if (embFontList) {
1570     delete embFontList;
1571   }
1572   deleteGList(fontInfo, PSFontInfo);
1573   deleteGHash(fontFileInfo, PSFontFileInfo);
1574   gfree(imgIDs);
1575   gfree(formIDs);
1576   if (saveStack) {
1577     delete saveStack;
1578   }
1579   while (customColors) {
1580     cc = customColors;
1581     customColors = cc->next;
1582     delete cc;
1583   }
1584 }
1585 
checkIO()1586 GBool PSOutputDev::checkIO() {
1587   if (fileType == psFile || fileType == psPipe || fileType == psStdout) {
1588     if (ferror((FILE *)outputStream)) {
1589       error(errIO, -1, "Error writing to PostScript file");
1590       return gFalse;
1591     }
1592   }
1593   return gTrue;
1594 }
1595 
writeHeader(PDFRectangle * mediaBox,PDFRectangle * cropBox,int pageRotate)1596 void PSOutputDev::writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox,
1597 			      int pageRotate) {
1598   Object info, obj1;
1599   PSOutPaperSize *size;
1600   double x1, y1, x2, y2;
1601   int i;
1602 
1603   switch (mode) {
1604   case psModePS:
1605     writePS("%!PS-Adobe-3.0\n");
1606     break;
1607   case psModeEPS:
1608     writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
1609     break;
1610   case psModeForm:
1611     writePS("%!PS-Adobe-3.0 Resource-Form\n");
1612     break;
1613   }
1614 
1615   writePSFmt("%XpdfVersion: {0:s}\n", xpdfVersion);
1616   xref->getDocInfo(&info);
1617   if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) {
1618     writePS("%%Creator: ");
1619     writePSTextLine(obj1.getString());
1620   }
1621   obj1.free();
1622   if (info.isDict() && info.dictLookup("Title", &obj1)->isString()) {
1623     writePS("%%Title: ");
1624     writePSTextLine(obj1.getString());
1625   }
1626   obj1.free();
1627   info.free();
1628   writePSFmt("%%LanguageLevel: {0:d}\n",
1629 	     level >= psLevel3 ? 3 : level >= psLevel2 ? 2 : 1);
1630   if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1631     writePS("%%DocumentProcessColors: (atend)\n");
1632     writePS("%%DocumentCustomColors: (atend)\n");
1633   }
1634   writePS("%%DocumentSuppliedResources: (atend)\n");
1635 
1636   switch (mode) {
1637   case psModePS:
1638     if (paperMatch) {
1639       for (i = 0; i < paperSizes->getLength(); ++i) {
1640 	size = (PSOutPaperSize *)paperSizes->get(i);
1641 	writePSFmt("%%{0:s} {1:d}x{2:d} {1:d} {2:d} 0 () ()\n",
1642 		   i==0 ? "DocumentMedia:" : "+", size->w, size->h);
1643       }
1644     } else {
1645       writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n",
1646 		 paperWidth, paperHeight);
1647     }
1648     writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight);
1649     writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1);
1650     writePS("%%EndComments\n");
1651     if (!paperMatch) {
1652       writePS("%%BeginDefaults\n");
1653       writePS("%%PageMedia: plain\n");
1654       writePS("%%EndDefaults\n");
1655     }
1656     break;
1657   case psModeEPS:
1658     epsX1 = cropBox->x1;
1659     epsY1 = cropBox->y1;
1660     epsX2 = cropBox->x2;
1661     epsY2 = cropBox->y2;
1662     if (pageRotate == 0 || pageRotate == 180) {
1663       x1 = epsX1;
1664       y1 = epsY1;
1665       x2 = epsX2;
1666       y2 = epsY2;
1667     } else { // pageRotate == 90 || pageRotate == 270
1668       x1 = 0;
1669       y1 = 0;
1670       x2 = epsY2 - epsY1;
1671       y2 = epsX2 - epsX1;
1672     }
1673     writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n",
1674 	       (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
1675     if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) ||
1676 	floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) {
1677       writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
1678 		 x1, y1, x2, y2);
1679     }
1680     writePS("%%EndComments\n");
1681     break;
1682   case psModeForm:
1683     writePS("%%EndComments\n");
1684     writePS("32 dict dup begin\n");
1685     writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n",
1686 	       (int)floor(mediaBox->x1), (int)floor(mediaBox->y1),
1687 	       (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2));
1688     writePS("/FormType 1 def\n");
1689     writePS("/Matrix [1 0 0 1 0 0] def\n");
1690     break;
1691   }
1692 }
1693 
writeXpdfProcset()1694 void PSOutputDev::writeXpdfProcset() {
1695   GBool lev1, lev2, lev3, nonSep, gray, sep;
1696   const char **p;
1697   const char *q;
1698   double w;
1699 
1700   writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion);
1701   writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
1702   lev1 = lev2 = lev3 = nonSep = gray = sep = gTrue;
1703   for (p = prolog; *p; ++p) {
1704     if ((*p)[0] == '~') {
1705       lev1 = lev2 = lev3 = nonSep = gray = sep = gFalse;
1706       for (q = *p + 1; *q; ++q) {
1707 	switch (*q) {
1708 	case '1': lev1 = gTrue; break;
1709 	case '2': lev2 = gTrue; break;
1710 	case '3': lev3 = gTrue; break;
1711 	case 'g': gray = gTrue; break;
1712 	case 'n': nonSep = gTrue; break;
1713 	case 's': sep = gTrue; break;
1714 	}
1715       }
1716     } else if ((level == psLevel1 && lev1 && nonSep) ||
1717 	       (level == psLevel1Sep && lev1 && sep) ||
1718 	       (level == psLevel2 && lev2 && nonSep) ||
1719 	       (level == psLevel2Gray && lev2 && gray) ||
1720 	       (level == psLevel2Sep && lev2 && sep) ||
1721 	       (level == psLevel3 && lev3 && nonSep) ||
1722 	       (level == psLevel3Gray && lev3 && gray) ||
1723 	       (level == psLevel3Sep && lev3 && sep)) {
1724       writePSFmt("{0:s}\n", *p);
1725     }
1726   }
1727   if ((w = globalParams->getPSMinLineWidth()) > 0) {
1728     writePSFmt("/pdfMinLineWidth {0:.4g} def\n", w);
1729     for (p = minLineWidthProlog; *p; ++p) {
1730       writePSFmt("{0:s}\n", *p);
1731     }
1732   }
1733   writePS("%%EndResource\n");
1734 
1735   if (level >= psLevel3) {
1736     for (p = cmapProlog; *p; ++p) {
1737       writePSFmt("{0:s}\n", *p);
1738     }
1739   }
1740 }
1741 
writeDocSetup(Catalog * catalog)1742 void PSOutputDev::writeDocSetup(Catalog *catalog) {
1743   Page *page;
1744   Dict *resDict;
1745   Annots *annots;
1746   AcroForm *form;
1747   Object obj1, obj2, obj3;
1748   GString *s;
1749   GBool needDefaultFont;
1750   int pg, i, j;
1751 
1752   // check to see which pages will be rasterized
1753   if (firstPage <= lastPage) {
1754     rasterizePage = (char *)gmalloc(lastPage - firstPage + 1);
1755     for (pg = firstPage; pg <= lastPage; ++pg) {
1756       rasterizePage[pg - firstPage] = (char)checkIfPageNeedsToBeRasterized(pg);
1757     }
1758   } else {
1759     rasterizePage = NULL;
1760   }
1761 
1762   visitedResources = (char *)gmalloc(xref->getNumObjects());
1763   memset(visitedResources, 0, xref->getNumObjects());
1764 
1765   if (mode == psModeForm) {
1766     // swap the form and xpdf dicts
1767     writePS("xpdf end begin dup begin\n");
1768   } else {
1769     writePS("xpdf begin\n");
1770   }
1771   needDefaultFont = gFalse;
1772   for (pg = firstPage; pg <= lastPage; ++pg) {
1773     if (rasterizePage[pg - firstPage]) {
1774       continue;
1775     }
1776     page = catalog->getPage(pg);
1777     if ((resDict = page->getResourceDict())) {
1778       setupResources(resDict);
1779     }
1780     annots = new Annots(doc, page->getAnnots(&obj1));
1781     obj1.free();
1782     if (annots->getNumAnnots()) {
1783       needDefaultFont = gTrue;
1784     }
1785     for (i = 0; i < annots->getNumAnnots(); ++i) {
1786       if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
1787 	obj1.streamGetDict()->lookup("Resources", &obj2);
1788 	if (obj2.isDict()) {
1789 	  setupResources(obj2.getDict());
1790 	}
1791 	obj2.free();
1792       }
1793       obj1.free();
1794     }
1795     delete annots;
1796   }
1797   if ((form = catalog->getForm())) {
1798     if (form->getNumFields() > 0) {
1799       needDefaultFont = gTrue;
1800     }
1801     for (i = 0; i < form->getNumFields(); ++i) {
1802       form->getField(i)->getResources(&obj1);
1803       if (obj1.isArray()) {
1804 	for (j = 0; j < obj1.arrayGetLength(); ++j) {
1805 	  obj1.arrayGet(j, &obj2);
1806 	  if (obj2.isDict()) {
1807 	    setupResources(obj2.getDict());
1808 	  }
1809 	  obj2.free();
1810 	}
1811       } else if (obj1.isDict()) {
1812 	setupResources(obj1.getDict());
1813       }
1814       obj1.free();
1815     }
1816   }
1817   if (needDefaultFont) {
1818     setupDefaultFont();
1819   }
1820   if (mode != psModeForm) {
1821     if (mode != psModeEPS && !manualCtrl) {
1822       writePSFmt("{0:s} pdfSetup\n",
1823 		 globalParams->getPSDuplex() ? "true" : "false");
1824       if (!paperMatch) {
1825 	writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight);
1826       }
1827     }
1828 #if OPI_SUPPORT
1829     if (globalParams->getPSOPI()) {
1830       writePS("/opiMatrix matrix currentmatrix def\n");
1831     }
1832 #endif
1833   }
1834   if (customCodeCbk) {
1835     if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0,
1836 			      customCodeCbkData))) {
1837       writePS(s->getCString());
1838       delete s;
1839     }
1840   }
1841   if (mode != psModeForm) {
1842     writePS("end\n");
1843   }
1844 
1845   gfree(visitedResources);
1846   visitedResources = NULL;
1847 }
1848 
writePageTrailer()1849 void PSOutputDev::writePageTrailer() {
1850   if (mode != psModeForm) {
1851     writePS("pdfEndPage\n");
1852   }
1853 }
1854 
writeTrailer()1855 void PSOutputDev::writeTrailer() {
1856   PSOutCustomColor *cc;
1857 
1858   if (mode == psModeForm) {
1859     writePS("/Foo exch /Form defineresource pop\n");
1860   } else {
1861     writePS("%%DocumentSuppliedResources:\n");
1862     writePS(embFontList->getCString());
1863     if (level == psLevel1Sep || level == psLevel2Sep ||
1864 	level == psLevel3Sep) {
1865       writePS("%%DocumentProcessColors:");
1866       if (processColors & psProcessCyan) {
1867 	writePS(" Cyan");
1868       }
1869       if (processColors & psProcessMagenta) {
1870 	writePS(" Magenta");
1871       }
1872       if (processColors & psProcessYellow) {
1873 	writePS(" Yellow");
1874       }
1875       if (processColors & psProcessBlack) {
1876 	writePS(" Black");
1877       }
1878       writePS("\n");
1879       writePS("%%DocumentCustomColors:");
1880       for (cc = customColors; cc; cc = cc->next) {
1881 	writePS(" ");
1882 	writePSString(cc->name);
1883       }
1884       writePS("\n");
1885       writePS("%%CMYKCustomColor:\n");
1886       for (cc = customColors; cc; cc = cc->next) {
1887 	writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ",
1888 		   cc->c, cc->m, cc->y, cc->k);
1889 	writePSString(cc->name);
1890 	writePS("\n");
1891       }
1892     }
1893   }
1894 }
1895 
checkIfPageNeedsToBeRasterized(int pg)1896 GBool PSOutputDev::checkIfPageNeedsToBeRasterized(int pg) {
1897   PreScanOutputDev *scan;
1898   GBool rasterize;
1899 
1900   if (globalParams->getPSAlwaysRasterize()) {
1901     rasterize = gTrue;
1902   } else {
1903     scan = new PreScanOutputDev();
1904     //~ this could depend on the printing flag, e.g., if an annotation
1905     //~   uses transparency --> need to pass the printing flag into
1906     //~   constructor, init, writeDocSetup
1907     doc->getCatalog()->getPage(pg)->display(scan, 72, 72, 0,
1908 					    gTrue, gTrue, gTrue);
1909     rasterize = scan->usesTransparency() || scan->usesPatternImageMask();
1910     delete scan;
1911     if (rasterize && globalParams->getPSNeverRasterize()) {
1912       error(errSyntaxWarning, -1,
1913 	    "PDF page uses transparency and the psNeverRasterize option is "
1914 	    "set - output may not be correct");
1915       rasterize = gFalse;
1916     }
1917   }
1918   return rasterize;
1919 }
1920 
setupResources(Dict * resDict)1921 void PSOutputDev::setupResources(Dict *resDict) {
1922   Object xObjDict, xObjRef, xObj, patDict, patRef, pat;
1923   Object gsDict, gsRef, gs, smask, smaskGroup, resObj;
1924   Ref ref0;
1925   GBool skip;
1926   int i;
1927 
1928   setupFonts(resDict);
1929   setupImages(resDict);
1930 
1931   //----- recursively scan XObjects
1932   resDict->lookup("XObject", &xObjDict);
1933   if (xObjDict.isDict()) {
1934     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
1935 
1936       // check for an already-visited XObject
1937       skip = gFalse;
1938       if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
1939 	ref0 = xObjRef.getRef();
1940 	if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) {
1941 	  skip = gTrue;
1942 	} else {
1943 	  skip = (GBool)visitedResources[ref0.num];
1944 	  visitedResources[ref0.num] = 1;
1945 	}
1946       }
1947       if (!skip) {
1948 
1949 	// process the XObject's resource dictionary
1950 	xObjDict.dictGetVal(i, &xObj);
1951 	if (xObj.isStream()) {
1952 	  xObj.streamGetDict()->lookup("Resources", &resObj);
1953 	  if (resObj.isDict()) {
1954 	    setupResources(resObj.getDict());
1955 	  }
1956 	  resObj.free();
1957 	}
1958 	xObj.free();
1959       }
1960 
1961       xObjRef.free();
1962     }
1963   }
1964   xObjDict.free();
1965 
1966   //----- recursively scan Patterns
1967   resDict->lookup("Pattern", &patDict);
1968   if (patDict.isDict()) {
1969     inType3Char = gTrue;
1970     for (i = 0; i < patDict.dictGetLength(); ++i) {
1971 
1972       // check for an already-visited Pattern
1973       skip = gFalse;
1974       if ((patDict.dictGetValNF(i, &patRef)->isRef())) {
1975 	ref0 = patRef.getRef();
1976 	if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) {
1977 	  skip = gTrue;
1978 	} else {
1979 	  skip = (GBool)visitedResources[ref0.num];
1980 	  visitedResources[ref0.num] = 1;
1981 	}
1982       }
1983       if (!skip) {
1984 
1985 	// process the Pattern's resource dictionary
1986 	patDict.dictGetVal(i, &pat);
1987 	if (pat.isStream()) {
1988 	  pat.streamGetDict()->lookup("Resources", &resObj);
1989 	  if (resObj.isDict()) {
1990 	    setupResources(resObj.getDict());
1991 	  }
1992 	  resObj.free();
1993 	}
1994 	pat.free();
1995       }
1996 
1997       patRef.free();
1998     }
1999     inType3Char = gFalse;
2000   }
2001   patDict.free();
2002 
2003 
2004   //----- recursively scan SMask transparency groups in ExtGState dicts
2005   resDict->lookup("ExtGState", &gsDict);
2006   if (gsDict.isDict()) {
2007     for (i = 0; i < gsDict.dictGetLength(); ++i) {
2008 
2009       // check for an already-visited ExtGState
2010       skip = gFalse;
2011       if ((gsDict.dictGetValNF(i, &gsRef)->isRef())) {
2012 	ref0 = gsRef.getRef();
2013 	if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) {
2014 	  skip = gTrue;
2015 	} else {
2016 	  skip = (GBool)visitedResources[ref0.num];
2017 	  visitedResources[ref0.num] = 1;
2018 	}
2019       }
2020       if (!skip) {
2021 
2022 	// process the ExtGState's SMask's transparency group's resource dict
2023 	if (gsDict.dictGetVal(i, &gs)->isDict()) {
2024 	  if (gs.dictLookup("SMask", &smask)->isDict()) {
2025 	    if (smask.dictLookup("G", &smaskGroup)->isStream()) {
2026 	      smaskGroup.streamGetDict()->lookup("Resources", &resObj);
2027 	      if (resObj.isDict()) {
2028 		setupResources(resObj.getDict());
2029 	      }
2030 	      resObj.free();
2031 	    }
2032 	    smaskGroup.free();
2033 	  }
2034 	  smask.free();
2035 	}
2036 	gs.free();
2037       }
2038 
2039       gsRef.free();
2040     }
2041   }
2042   gsDict.free();
2043 
2044   setupForms(resDict);
2045 }
2046 
setupFonts(Dict * resDict)2047 void PSOutputDev::setupFonts(Dict *resDict) {
2048   Object obj1, obj2;
2049   Ref r;
2050   GfxFontDict *gfxFontDict;
2051   GfxFont *font;
2052   int i;
2053 
2054   gfxFontDict = NULL;
2055   resDict->lookupNF("Font", &obj1);
2056   if (obj1.isRef()) {
2057     obj1.fetch(xref, &obj2);
2058     if (obj2.isDict()) {
2059       r = obj1.getRef();
2060       gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
2061     }
2062     obj2.free();
2063   } else if (obj1.isDict()) {
2064     gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
2065   }
2066   if (gfxFontDict) {
2067     for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
2068       if ((font = gfxFontDict->getFont(i))) {
2069 	setupFont(font, resDict);
2070       }
2071     }
2072     delete gfxFontDict;
2073   }
2074   obj1.free();
2075 }
2076 
setupFont(GfxFont * font,Dict * parentResDict)2077 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
2078   PSFontInfo *fi;
2079   GfxFontLoc *fontLoc;
2080   GBool subst;
2081   char buf[16];
2082   UnicodeMap *uMap;
2083   char *charName;
2084   double xs, ys;
2085   int code;
2086   double w1, w2;
2087   int i, j;
2088 
2089   // check if font is already set up
2090   for (i = 0; i < fontInfo->getLength(); ++i) {
2091     fi = (PSFontInfo *)fontInfo->get(i);
2092     if (fi->fontID.num == font->getID()->num &&
2093 	fi->fontID.gen == font->getID()->gen) {
2094       return;
2095     }
2096   }
2097 
2098   // add fontInfo entry
2099   fi = new PSFontInfo(*font->getID());
2100   fontInfo->append(fi);
2101 
2102   xs = ys = 1;
2103   subst = gFalse;
2104 
2105   if (font->getType() == fontType3) {
2106     fi->ff = setupType3Font(font, parentResDict);
2107   } else {
2108     if ((fontLoc = font->locateFont(xref, gTrue))) {
2109       switch (fontLoc->locType) {
2110       case gfxFontLocEmbedded:
2111 	switch (fontLoc->fontType) {
2112 	case fontType1:
2113 	  fi->ff = setupEmbeddedType1Font(font, &fontLoc->embFontID);
2114 	  break;
2115 	case fontType1C:
2116 	  fi->ff = setupEmbeddedType1CFont(font, &fontLoc->embFontID);
2117 	  break;
2118 	case fontType1COT:
2119 	  fi->ff = setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID);
2120 	  break;
2121 	case fontTrueType:
2122 	case fontTrueTypeOT:
2123 	  fi->ff = setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID);
2124 	  break;
2125 	case fontCIDType0C:
2126 	  fi->ff = setupEmbeddedCIDType0Font(font, &fontLoc->embFontID);
2127 	  break;
2128 	case fontCIDType2:
2129 	case fontCIDType2OT:
2130 	  //~ should check to see if font actually uses vertical mode
2131 	  fi->ff = setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID,
2132 						gTrue);
2133 	  break;
2134 	case fontCIDType0COT:
2135 	  fi->ff = setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID);
2136 	  break;
2137 	default:
2138 	  break;
2139 	}
2140 	break;
2141       case gfxFontLocExternal:
2142 	//~ add cases for other external 16-bit fonts
2143 	switch (fontLoc->fontType) {
2144 	case fontType1:
2145 	  fi->ff = setupExternalType1Font(font, fontLoc->path);
2146 	  break;
2147 	case fontTrueType:
2148 	case fontTrueTypeOT:
2149 	  fi->ff = setupExternalTrueTypeFont(font, fontLoc->path,
2150 					     fontLoc->fontNum);
2151 	  break;
2152 	case fontCIDType2:
2153 	case fontCIDType2OT:
2154 	  //~ should check to see if font actually uses vertical mode
2155 	  fi->ff = setupExternalCIDTrueTypeFont(font, fontLoc->path,
2156 						fontLoc->fontNum, gTrue);
2157 	  break;
2158 	case fontCIDType0COT:
2159 	  fi->ff = setupExternalOpenTypeCFFFont(font, fontLoc->path);
2160 	  break;
2161 	default:
2162 	  break;
2163 	}
2164 	break;
2165       case gfxFontLocResident:
2166 	if (!(fi->ff = (PSFontFileInfo *)fontFileInfo->lookup(fontLoc->path))) {
2167 	  // handle psFontPassthrough
2168 	  fi->ff = new PSFontFileInfo(fontLoc->path->copy(), fontLoc->fontType,
2169 				      psFontFileResident);
2170 	  fontFileInfo->add(fi->ff->psName, fi->ff);
2171 	}
2172 	break;
2173       }
2174     }
2175 
2176     if (!fi->ff) {
2177       if (font->isCIDFont()) {
2178 	error(errSyntaxError, -1,
2179 	      "Couldn't find a font for '{0:s}' ('{1:s}' character collection)",
2180 	      font->getName() ? font->getName()->getCString()
2181 	                      : "(unnamed)",
2182 	      ((GfxCIDFont *)font)->getCollection()
2183 	          ? ((GfxCIDFont *)font)->getCollection()->getCString()
2184 	          : "(unknown)");
2185       } else {
2186 	error(errSyntaxError, -1,
2187 	      "Couldn't find a font for '{0:s}'",
2188 	      font->getName() ? font->getName()->getCString()
2189 	                      : "(unnamed)");
2190       }
2191       delete fontLoc;
2192       return;
2193     }
2194 
2195     // scale substituted 8-bit fonts
2196     if (fontLoc->locType == gfxFontLocResident &&
2197 	fontLoc->substIdx >= 0) {
2198       subst = gTrue;
2199       for (code = 0; code < 256; ++code) {
2200 	if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
2201 	    charName[0] == 'm' && charName[1] == '\0') {
2202 	  break;
2203 	}
2204       }
2205       if (code < 256) {
2206 	w1 = ((Gfx8BitFont *)font)->getWidth((Guchar)code);
2207       } else {
2208 	w1 = 0;
2209       }
2210       w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth;
2211       xs = w1 / w2;
2212       if (xs < 0.1) {
2213 	xs = 1;
2214       }
2215     }
2216 
2217     // handle encodings for substituted CID fonts
2218     if (fontLoc->locType == gfxFontLocResident &&
2219 	fontLoc->fontType >= fontCIDType0) {
2220       subst = gTrue;
2221       if (!fi->ff->encoding) {
2222 	if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) {
2223 	  fi->ff->encoding = fontLoc->encoding->copy();
2224 	  uMap->decRefCnt();
2225 	} else {
2226 	  error(errSyntaxError, -1,
2227 		"Couldn't find Unicode map for 16-bit font encoding '{0:t}'",
2228 		fontLoc->encoding);
2229 	}
2230       }
2231     }
2232 
2233     delete fontLoc;
2234   }
2235 
2236   // generate PostScript code to set up the font
2237   if (font->isCIDFont()) {
2238     if (level >= psLevel3) {
2239       writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n",
2240 		 font->getID()->num, font->getID()->gen, fi->ff->psName,
2241 		 font->getWMode());
2242     } else {
2243       writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",
2244 		 font->getID()->num, font->getID()->gen, fi->ff->psName,
2245 		 font->getWMode());
2246     }
2247   } else {
2248     writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n",
2249 	       font->getID()->num, font->getID()->gen, fi->ff->psName, xs, ys);
2250     for (i = 0; i < 256; i += 8) {
2251       writePS((char *)((i == 0) ? "[ " : "  "));
2252       for (j = 0; j < 8; ++j) {
2253 	if (font->getType() == fontTrueType &&
2254 	    !subst &&
2255 	    !((Gfx8BitFont *)font)->getHasEncoding()) {
2256 	  snprintf(buf, sizeof(buf), "c%02x", i+j);
2257 	  charName = buf;
2258 	} else {
2259 	  charName = ((Gfx8BitFont *)font)->getCharName(i+j);
2260 	}
2261 	writePS("/");
2262 	writePSName(charName ? charName : (char *)".notdef");
2263 	// the empty name is legal in PDF and PostScript, but PostScript
2264 	// uses a double-slash (//...) for "immediately evaluated names",
2265 	// so we need to add a space character here
2266 	if (charName && !charName[0]) {
2267 	  writePS(" ");
2268 	}
2269       }
2270       writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
2271     }
2272     writePS("pdfMakeFont\n");
2273   }
2274 }
2275 
setupEmbeddedType1Font(GfxFont * font,Ref * id)2276 PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) {
2277   GString *psName, *origFont, *cleanFont;
2278   PSFontFileInfo *ff;
2279   Object refObj, strObj, obj1, obj2;
2280   Dict *dict;
2281   char buf[4096];
2282   GBool rename;
2283   int length1, length2, n;
2284 
2285   // check if font is already embedded
2286   if (!font->getEmbeddedFontName()) {
2287     rename = gTrue;
2288   } else if ((ff = (PSFontFileInfo *)
2289 	             fontFileInfo->lookup(font->getEmbeddedFontName()))) {
2290     if (ff->loc == psFontFileEmbedded &&
2291 	ff->embFontID.num == id->num &&
2292 	ff->embFontID.gen == id->gen) {
2293       return ff;
2294     }
2295     rename = gTrue;
2296   } else {
2297     rename = gFalse;
2298   }
2299 
2300   // generate name
2301   // (this assumes that the PS font name matches the PDF font name)
2302   if (rename) {
2303     psName = makePSFontName(font, id);
2304   } else {
2305     psName = font->getEmbeddedFontName()->copy();
2306   }
2307 
2308   // get the font stream and info
2309   refObj.initRef(id->num, id->gen);
2310   refObj.fetch(xref, &strObj);
2311   refObj.free();
2312   if (!strObj.isStream()) {
2313     error(errSyntaxError, -1, "Embedded font file object is not a stream");
2314     goto err1;
2315   }
2316   if (!(dict = strObj.streamGetDict())) {
2317     error(errSyntaxError, -1,
2318 	  "Embedded font stream is missing its dictionary");
2319     goto err1;
2320   }
2321   dict->lookup("Length1", &obj1);
2322   dict->lookup("Length2", &obj2);
2323   if (!obj1.isInt() || !obj2.isInt()) {
2324     error(errSyntaxError, -1,
2325 	  "Missing length fields in embedded font stream dictionary");
2326     obj1.free();
2327     obj2.free();
2328     goto err1;
2329   }
2330   length1 = obj1.getInt();
2331   length2 = obj2.getInt();
2332   obj1.free();
2333   obj2.free();
2334 
2335   // read the font file
2336   origFont = new GString();
2337   strObj.streamReset();
2338   while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) {
2339     origFont->append(buf, n);
2340   }
2341   strObj.streamClose();
2342   strObj.free();
2343 
2344   // beginning comment
2345   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2346   embFontList->append("%%+ font ");
2347   embFontList->append(psName->getCString());
2348   embFontList->append("\n");
2349 
2350   // clean up the font file
2351   cleanFont = fixType1Font(origFont, length1, length2);
2352   if (rename) {
2353     renameType1Font(cleanFont, psName);
2354   }
2355   writePSBlock(cleanFont->getCString(), cleanFont->getLength());
2356   delete cleanFont;
2357   delete origFont;
2358 
2359   // ending comment
2360   writePS("%%EndResource\n");
2361 
2362   ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
2363   ff->embFontID = *id;
2364   fontFileInfo->add(ff->psName, ff);
2365   return ff;
2366 
2367  err1:
2368   strObj.free();
2369   delete psName;
2370   return NULL;
2371 }
2372 
setupExternalType1Font(GfxFont * font,GString * fileName)2373 PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font,
2374 						    GString *fileName) {
2375   static char hexChar[17] = "0123456789abcdef";
2376   GString *psName;
2377   PSFontFileInfo *ff;
2378   FILE *fontFile;
2379   int buf[6];
2380   int c, n, i;
2381 
2382   if (font->getName()) {
2383     // check if font is already embedded
2384     if ((ff = (PSFontFileInfo *)fontFileInfo->lookup(font->getName()))) {
2385       return ff;
2386     }
2387     // this assumes that the PS font name matches the PDF font name
2388     psName = font->getName()->copy();
2389   } else {
2390     // generate name
2391     //~ this won't work -- the PS font name won't match
2392     psName = makePSFontName(font, font->getID());
2393   }
2394 
2395   // beginning comment
2396   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2397   embFontList->append("%%+ font ");
2398   embFontList->append(psName->getCString());
2399   embFontList->append("\n");
2400 
2401   // open the font file
2402   if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
2403     error(errIO, -1, "Couldn't open external font file");
2404     delete psName;
2405     return NULL;
2406   }
2407 
2408   // check for PFB format
2409   buf[0] = fgetc(fontFile);
2410   buf[1] = fgetc(fontFile);
2411   if (buf[0] == 0x80 && buf[1] == 0x01) {
2412     while (1) {
2413       for (i = 2; i < 6; ++i) {
2414 	buf[i] = fgetc(fontFile);
2415       }
2416       if (buf[2] == EOF || buf[3] == EOF || buf[4] == EOF || buf[5] == EOF) {
2417 	break;
2418       }
2419       n = buf[2] + (buf[3] << 8) + (buf[4] << 16) + (buf[5] << 24);
2420       if (buf[1] == 0x01) {
2421 	for (i = 0; i < n; ++i) {
2422 	  if ((c = fgetc(fontFile)) == EOF) {
2423 	    break;
2424 	  }
2425 	  writePSChar((char)c);
2426 	}
2427       } else {
2428 	for (i = 0; i < n; ++i) {
2429 	  if ((c = fgetc(fontFile)) == EOF) {
2430 	    break;
2431 	  }
2432 	  writePSChar(hexChar[(c >> 4) & 0x0f]);
2433 	  writePSChar(hexChar[c & 0x0f]);
2434 	  if (i % 32 == 31) {
2435 	    writePSChar('\n');
2436 	  }
2437 	}
2438       }
2439       buf[0] = fgetc(fontFile);
2440       buf[1] = fgetc(fontFile);
2441       if (buf[0] == EOF || buf[1] == EOF ||
2442 	  (buf[0] == 0x80 && buf[1] == 0x03)) {
2443 	break;
2444       } else if (!(buf[0] == 0x80 &&
2445 		   (buf[1] == 0x01 || buf[1] == 0x02))) {
2446 	error(errSyntaxError, -1,
2447 	      "Invalid PFB header in external font file");
2448 	break;
2449       }
2450     }
2451     writePSChar('\n');
2452 
2453   // plain text (PFA) format
2454   } else {
2455     writePSChar((char)buf[0]);
2456     writePSChar((char)buf[1]);
2457     while ((c = fgetc(fontFile)) != EOF) {
2458       writePSChar((char)c);
2459     }
2460   }
2461 
2462   fclose(fontFile);
2463 
2464   // ending comment
2465   writePS("%%EndResource\n");
2466 
2467   ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
2468   ff->extFileName = fileName->copy();
2469   fontFileInfo->add(ff->psName, ff);
2470   return ff;
2471 }
2472 
setupEmbeddedType1CFont(GfxFont * font,Ref * id)2473 PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) {
2474   GString *psName;
2475   PSFontFileInfo *ff;
2476   char *fontBuf;
2477   int fontLen;
2478   FoFiType1C *ffT1C;
2479   GHashIter *iter;
2480 
2481   // check if font is already embedded
2482   fontFileInfo->startIter(&iter);
2483   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
2484     if (ff->loc == psFontFileEmbedded &&
2485 	ff->embFontID.num == id->num &&
2486 	ff->embFontID.gen == id->gen) {
2487       fontFileInfo->killIter(&iter);
2488       return ff;
2489     }
2490   }
2491 
2492   // generate name
2493   psName = makePSFontName(font, id);
2494 
2495   // beginning comment
2496   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2497   embFontList->append("%%+ font ");
2498   embFontList->append(psName->getCString());
2499   embFontList->append("\n");
2500 
2501   // convert it to a Type 1 font
2502   if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2503     if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2504       ffT1C->convertToType1(psName->getCString(), NULL, gTrue,
2505 			    outputFunc, outputStream);
2506       delete ffT1C;
2507     }
2508     gfree(fontBuf);
2509   }
2510 
2511   // ending comment
2512   writePS("%%EndResource\n");
2513 
2514   ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
2515   ff->embFontID = *id;
2516   fontFileInfo->add(ff->psName, ff);
2517   return ff;
2518 }
2519 
setupEmbeddedOpenTypeT1CFont(GfxFont * font,Ref * id)2520 PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font,
2521 							  Ref *id) {
2522   GString *psName;
2523   PSFontFileInfo *ff;
2524   char *fontBuf;
2525   int fontLen;
2526   FoFiTrueType *ffTT;
2527   GHashIter *iter;
2528 
2529   // check if font is already embedded
2530   fontFileInfo->startIter(&iter);
2531   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
2532     if (ff->loc == psFontFileEmbedded &&
2533 	ff->embFontID.num == id->num &&
2534 	ff->embFontID.gen == id->gen) {
2535       fontFileInfo->killIter(&iter);
2536       return ff;
2537     }
2538   }
2539 
2540   // generate name
2541   psName = makePSFontName(font, id);
2542 
2543   // beginning comment
2544   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2545   embFontList->append("%%+ font ");
2546   embFontList->append(psName->getCString());
2547   embFontList->append("\n");
2548 
2549   // convert it to a Type 1 font
2550   if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2551     if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) {
2552       if (ffTT->isOpenTypeCFF()) {
2553 	ffTT->convertToType1(psName->getCString(), NULL, gTrue,
2554 			     outputFunc, outputStream);
2555       }
2556       delete ffTT;
2557     }
2558     gfree(fontBuf);
2559   }
2560 
2561   // ending comment
2562   writePS("%%EndResource\n");
2563 
2564   ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
2565   ff->embFontID = *id;
2566   fontFileInfo->add(ff->psName, ff);
2567   return ff;
2568 }
2569 
setupEmbeddedTrueTypeFont(GfxFont * font,Ref * id)2570 PSFontFileInfo *PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id) {
2571   GString *psName;
2572   PSFontFileInfo *ff;
2573   char *fontBuf;
2574   int fontLen;
2575   FoFiTrueType *ffTT;
2576   int *codeToGID;
2577   GHashIter *iter;
2578 
2579   // get the code-to-GID mapping
2580   if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2581     return NULL;
2582   }
2583   if (!(ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
2584     gfree(fontBuf);
2585     return NULL;
2586   }
2587   codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2588 
2589   // check if font is already embedded
2590   fontFileInfo->startIter(&iter);
2591   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
2592     if (ff->loc == psFontFileEmbedded &&
2593 	ff->type == font->getType() &&
2594 	ff->embFontID.num == id->num &&
2595 	ff->embFontID.gen == id->gen &&
2596 	ff->codeToGIDLen == 256 &&
2597 	!memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
2598       fontFileInfo->killIter(&iter);
2599       gfree(codeToGID);
2600       delete ffTT;
2601       gfree(fontBuf);
2602       return ff;
2603     }
2604   }
2605 
2606   // generate name
2607   psName = makePSFontName(font, id);
2608 
2609   // beginning comment
2610   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2611   embFontList->append("%%+ font ");
2612   embFontList->append(psName->getCString());
2613   embFontList->append("\n");
2614 
2615   // convert it to a Type 42 font
2616   ffTT->convertToType42(psName->getCString(),
2617 			((Gfx8BitFont *)font)->getHasEncoding()
2618 			  ? ((Gfx8BitFont *)font)->getEncoding()
2619 			  : (char **)NULL,
2620 			codeToGID, outputFunc, outputStream);
2621   delete ffTT;
2622   gfree(fontBuf);
2623 
2624   // ending comment
2625   writePS("%%EndResource\n");
2626 
2627   ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
2628   ff->embFontID = *id;
2629   ff->codeToGID = codeToGID;
2630   ff->codeToGIDLen = 256;
2631   fontFileInfo->add(ff->psName, ff);
2632   return ff;
2633 }
2634 
setupExternalTrueTypeFont(GfxFont * font,GString * fileName,int fontNum)2635 PSFontFileInfo *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font,
2636 						       GString *fileName,
2637 						       int fontNum) {
2638   GString *psName;
2639   PSFontFileInfo *ff;
2640   FoFiTrueType *ffTT;
2641   int *codeToGID;
2642   GHashIter *iter;
2643 
2644   // get the code-to-GID mapping
2645   if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
2646     return NULL;
2647   }
2648   codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2649 
2650   // check if font is already embedded
2651   fontFileInfo->startIter(&iter);
2652   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
2653     if (ff->loc == psFontFileExternal &&
2654 	ff->type == font->getType() &&
2655 	!ff->extFileName->cmp(fileName) &&
2656 	ff->codeToGIDLen == 256 &&
2657 	!memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
2658       fontFileInfo->killIter(&iter);
2659       gfree(codeToGID);
2660       delete ffTT;
2661       return ff;
2662     }
2663   }
2664 
2665   // generate name
2666   psName = makePSFontName(font, font->getID());
2667 
2668   // beginning comment
2669   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2670   embFontList->append("%%+ font ");
2671   embFontList->append(psName->getCString());
2672   embFontList->append("\n");
2673 
2674   // convert it to a Type 42 font
2675   ffTT->convertToType42(psName->getCString(),
2676 			((Gfx8BitFont *)font)->getHasEncoding()
2677 			  ? ((Gfx8BitFont *)font)->getEncoding()
2678 			  : (char **)NULL,
2679 			codeToGID, outputFunc, outputStream);
2680   delete ffTT;
2681 
2682   // ending comment
2683   writePS("%%EndResource\n");
2684 
2685   ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
2686   ff->extFileName = fileName->copy();
2687   ff->codeToGID = codeToGID;
2688   ff->codeToGIDLen = 256;
2689   fontFileInfo->add(ff->psName, ff);
2690   return ff;
2691 }
2692 
setupEmbeddedCIDType0Font(GfxFont * font,Ref * id)2693 PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) {
2694   GString *psName;
2695   PSFontFileInfo *ff;
2696   char *fontBuf;
2697   int fontLen;
2698   FoFiType1C *ffT1C;
2699   GHashIter *iter;
2700 
2701   // check if font is already embedded
2702   fontFileInfo->startIter(&iter);
2703   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
2704     if (ff->loc == psFontFileEmbedded &&
2705 	ff->embFontID.num == id->num &&
2706 	ff->embFontID.gen == id->gen) {
2707       fontFileInfo->killIter(&iter);
2708       return ff;
2709     }
2710   }
2711 
2712   // generate name
2713   psName = makePSFontName(font, id);
2714 
2715   // beginning comment
2716   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2717   embFontList->append("%%+ font ");
2718   embFontList->append(psName->getCString());
2719   embFontList->append("\n");
2720 
2721   // convert it to a Type 0 font
2722   if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2723     if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2724       if (globalParams->getPSLevel() >= psLevel3) {
2725 	// Level 3: use a CID font
2726 	ffT1C->convertToCIDType0(psName->getCString(),
2727 				 ((GfxCIDFont *)font)->getCIDToGID(),
2728 				 ((GfxCIDFont *)font)->getCIDToGIDLen(),
2729 				 outputFunc, outputStream);
2730       } else {
2731 	// otherwise: use a non-CID composite font
2732 	ffT1C->convertToType0(psName->getCString(),
2733 			      ((GfxCIDFont *)font)->getCIDToGID(),
2734 			      ((GfxCIDFont *)font)->getCIDToGIDLen(),
2735 			      outputFunc, outputStream);
2736       }
2737       delete ffT1C;
2738     }
2739     gfree(fontBuf);
2740   }
2741 
2742   // ending comment
2743   writePS("%%EndResource\n");
2744 
2745   ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
2746   ff->embFontID = *id;
2747   fontFileInfo->add(ff->psName, ff);
2748   return ff;
2749 }
2750 
setupEmbeddedCIDTrueTypeFont(GfxFont * font,Ref * id,GBool needVerticalMetrics)2751 PSFontFileInfo *PSOutputDev::setupEmbeddedCIDTrueTypeFont(
2752 				 GfxFont *font, Ref *id,
2753 				 GBool needVerticalMetrics) {
2754   GString *psName;
2755   PSFontFileInfo *ff;
2756   char *fontBuf;
2757   int fontLen;
2758   FoFiTrueType *ffTT;
2759   int *codeToGID;
2760   int codeToGIDLen;
2761   GHashIter *iter;
2762 
2763   // get the code-to-GID mapping
2764   codeToGID = ((GfxCIDFont *)font)->getCIDToGID();
2765   codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen();
2766 
2767   // check if font is already embedded
2768   fontFileInfo->startIter(&iter);
2769   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
2770     if (ff->loc == psFontFileEmbedded &&
2771 	ff->type == font->getType() &&
2772 	ff->embFontID.num == id->num &&
2773 	ff->embFontID.gen == id->gen &&
2774 	ff->codeToGIDLen == codeToGIDLen &&
2775 	((!ff->codeToGID && !codeToGID) ||
2776 	 (ff->codeToGID && codeToGID &&
2777 	  !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))))) {
2778       fontFileInfo->killIter(&iter);
2779       return ff;
2780     }
2781   }
2782 
2783   // generate name
2784   psName = makePSFontName(font, id);
2785 
2786   // beginning comment
2787   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2788   embFontList->append("%%+ font ");
2789   embFontList->append(psName->getCString());
2790   embFontList->append("\n");
2791 
2792   // convert it to a Type 0 font
2793   if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2794     if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
2795       if (globalParams->getPSLevel() >= psLevel3) {
2796 	// Level 3: use a CID font
2797 	ffTT->convertToCIDType2(psName->getCString(),
2798 				codeToGID, codeToGIDLen,
2799 				needVerticalMetrics,
2800 				outputFunc, outputStream);
2801       } else {
2802 	// otherwise: use a non-CID composite font
2803 	ffTT->convertToType0(psName->getCString(),
2804 			     codeToGID, codeToGIDLen,
2805 			     needVerticalMetrics,
2806 			     outputFunc, outputStream);
2807       }
2808       delete ffTT;
2809     }
2810     gfree(fontBuf);
2811   }
2812 
2813   // ending comment
2814   writePS("%%EndResource\n");
2815 
2816   ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
2817   ff->embFontID = *id;
2818   if (codeToGIDLen) {
2819     ff->codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
2820     memcpy(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int));
2821     ff->codeToGIDLen = codeToGIDLen;
2822   }
2823   fontFileInfo->add(ff->psName, ff);
2824   return ff;
2825 }
2826 
setupExternalCIDTrueTypeFont(GfxFont * font,GString * fileName,int fontNum,GBool needVerticalMetrics)2827 PSFontFileInfo *PSOutputDev::setupExternalCIDTrueTypeFont(
2828 				 GfxFont *font,
2829 				 GString *fileName,
2830 				 int fontNum,
2831 				 GBool needVerticalMetrics) {
2832   GString *psName;
2833   PSFontFileInfo *ff;
2834   FoFiTrueType *ffTT;
2835   int *codeToGID;
2836   int codeToGIDLen;
2837   CharCodeToUnicode *ctu;
2838   Unicode uBuf[8];
2839   int cmap, cmapPlatform, cmapEncoding, code;
2840   GHashIter *iter;
2841 
2842   // create a code-to-GID mapping, via Unicode
2843   if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
2844     return NULL;
2845   }
2846   if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) {
2847     error(errSyntaxError, -1,
2848 	  "Couldn't find a mapping to Unicode for font '{0:s}'",
2849 	  font->getName() ? font->getName()->getCString() : "(unnamed)");
2850     delete ffTT;
2851     return NULL;
2852   }
2853   // look for a Unicode cmap
2854   for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
2855     cmapPlatform = ffTT->getCmapPlatform(cmap);
2856     cmapEncoding = ffTT->getCmapEncoding(cmap);
2857     if ((cmapPlatform == 3 && cmapEncoding == 1) ||
2858 	(cmapPlatform == 0 && cmapEncoding <= 4)) {
2859       break;
2860     }
2861   }
2862   if (cmap >= ffTT->getNumCmaps()) {
2863     error(errSyntaxError, -1,
2864 	  "Couldn't find a Unicode cmap in font '{0:s}'",
2865 	  font->getName() ? font->getName()->getCString() : "(unnamed)");
2866     ctu->decRefCnt();
2867     delete ffTT;
2868     return NULL;
2869   }
2870   // map CID -> Unicode -> GID
2871   if (ctu->isIdentity()) {
2872     codeToGIDLen = 65536;
2873   } else {
2874     codeToGIDLen = ctu->getLength();
2875   }
2876   codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
2877   for (code = 0; code < codeToGIDLen; ++code) {
2878     if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
2879       codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
2880     } else {
2881       codeToGID[code] = 0;
2882     }
2883   }
2884   ctu->decRefCnt();
2885 
2886   // check if font is already embedded
2887   fontFileInfo->startIter(&iter);
2888   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
2889     if (ff->loc == psFontFileExternal &&
2890 	ff->type == font->getType() &&
2891 	!ff->extFileName->cmp(fileName) &&
2892 	ff->codeToGIDLen == codeToGIDLen &&
2893 	ff->codeToGID &&
2894 	!memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) {
2895       fontFileInfo->killIter(&iter);
2896       gfree(codeToGID);
2897       delete ffTT;
2898       return ff;
2899     }
2900   }
2901 
2902   // check for embedding permission
2903   if (ffTT->getEmbeddingRights() < 1) {
2904     error(errSyntaxError, -1,
2905 	  "TrueType font '{0:s}' does not allow embedding",
2906 	  font->getName() ? font->getName()->getCString() : "(unnamed)");
2907     gfree(codeToGID);
2908     delete ffTT;
2909     return NULL;
2910   }
2911 
2912   // generate name
2913   psName = makePSFontName(font, font->getID());
2914 
2915   // beginning comment
2916   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2917   embFontList->append("%%+ font ");
2918   embFontList->append(psName->getCString());
2919   embFontList->append("\n");
2920 
2921   // convert it to a Type 0 font
2922   //~ this should use fontNum to load the correct font
2923   if (globalParams->getPSLevel() >= psLevel3) {
2924     // Level 3: use a CID font
2925     ffTT->convertToCIDType2(psName->getCString(),
2926 			    codeToGID, codeToGIDLen,
2927 			    needVerticalMetrics,
2928 			    outputFunc, outputStream);
2929   } else {
2930     // otherwise: use a non-CID composite font
2931     ffTT->convertToType0(psName->getCString(),
2932 			 codeToGID, codeToGIDLen,
2933 			 needVerticalMetrics,
2934 			 outputFunc, outputStream);
2935   }
2936   delete ffTT;
2937 
2938   // ending comment
2939   writePS("%%EndResource\n");
2940 
2941   ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
2942   ff->extFileName = fileName->copy();
2943   ff->codeToGID = codeToGID;
2944   ff->codeToGIDLen = codeToGIDLen;
2945   fontFileInfo->add(ff->psName, ff);
2946   return ff;
2947 }
2948 
setupEmbeddedOpenTypeCFFFont(GfxFont * font,Ref * id)2949 PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font,
2950 							  Ref *id) {
2951   GString *psName;
2952   PSFontFileInfo *ff;
2953   char *fontBuf;
2954   int fontLen;
2955   FoFiTrueType *ffTT;
2956   GHashIter *iter;
2957   int n;
2958 
2959   // check if font is already embedded
2960   fontFileInfo->startIter(&iter);
2961   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
2962     if (ff->loc == psFontFileEmbedded &&
2963 	ff->embFontID.num == id->num &&
2964 	ff->embFontID.gen == id->gen) {
2965       fontFileInfo->killIter(&iter);
2966       return ff;
2967     }
2968   }
2969 
2970   // generate name
2971   psName = makePSFontName(font, id);
2972 
2973   // beginning comment
2974   writePSFmt("%%BeginResource: font {0:t}\n", psName);
2975   embFontList->append("%%+ font ");
2976   embFontList->append(psName->getCString());
2977   embFontList->append("\n");
2978 
2979   // convert it to a Type 0 font
2980   if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
2981     if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) {
2982       if (ffTT->isOpenTypeCFF()) {
2983 	if (globalParams->getPSLevel() >= psLevel3) {
2984 	  // Level 3: use a CID font
2985 	  ffTT->convertToCIDType0(psName->getCString(),
2986 				  ((GfxCIDFont *)font)->getCIDToGID(),
2987 				  ((GfxCIDFont *)font)->getCIDToGIDLen(),
2988 				  outputFunc, outputStream);
2989 	} else {
2990 	  // otherwise: use a non-CID composite font
2991 	  ffTT->convertToType0(psName->getCString(),
2992 			       ((GfxCIDFont *)font)->getCIDToGID(),
2993 			       ((GfxCIDFont *)font)->getCIDToGIDLen(),
2994 			       outputFunc, outputStream);
2995 	}
2996       }
2997       delete ffTT;
2998     }
2999     gfree(fontBuf);
3000   }
3001 
3002   // ending comment
3003   writePS("%%EndResource\n");
3004 
3005   ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
3006   ff->embFontID = *id;
3007   if ((n = ((GfxCIDFont *)font)->getCIDToGIDLen())) {
3008     ff->codeToGID = (int *)gmallocn(n, sizeof(int));
3009     memcpy(ff->codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(int));
3010     ff->codeToGIDLen = n;
3011   }
3012   fontFileInfo->add(ff->psName, ff);
3013   return ff;
3014 }
3015 
3016 // This assumes an OpenType CFF font that has a Unicode cmap (in the
3017 // OpenType section), and a CFF blob that uses an identity CID-to-GID
3018 // mapping.
setupExternalOpenTypeCFFFont(GfxFont * font,GString * fileName)3019 PSFontFileInfo *PSOutputDev::setupExternalOpenTypeCFFFont(GfxFont *font,
3020 							  GString *fileName) {
3021   GString *psName;
3022   PSFontFileInfo *ff;
3023   FoFiTrueType *ffTT;
3024   GHashIter *iter;
3025   CharCodeToUnicode *ctu;
3026   Unicode uBuf[8];
3027   int *codeToGID;
3028   int codeToGIDLen;
3029   int cmap, cmapPlatform, cmapEncoding, code;
3030 
3031   // create a code-to-GID mapping, via Unicode
3032   if (!(ffTT = FoFiTrueType::load(fileName->getCString(), 0, gTrue))) {
3033     return NULL;
3034   }
3035   if (!ffTT->isOpenTypeCFF()) {
3036     delete ffTT;
3037     return NULL;
3038   }
3039   if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) {
3040     error(errSyntaxError, -1,
3041 	  "Couldn't find a mapping to Unicode for font '{0:s}'",
3042 	  font->getName() ? font->getName()->getCString() : "(unnamed)");
3043     delete ffTT;
3044     return NULL;
3045   }
3046   // look for a Unicode cmap
3047   for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
3048     cmapPlatform = ffTT->getCmapPlatform(cmap);
3049     cmapEncoding = ffTT->getCmapEncoding(cmap);
3050     if ((cmapPlatform == 3 && cmapEncoding == 1) ||
3051 	(cmapPlatform == 0 && cmapEncoding <= 4)) {
3052       break;
3053     }
3054   }
3055   if (cmap >= ffTT->getNumCmaps()) {
3056     error(errSyntaxError, -1,
3057 	  "Couldn't find a Unicode cmap in font '{0:s}'",
3058 	  font->getName() ? font->getName()->getCString() : "(unnamed)");
3059     ctu->decRefCnt();
3060     delete ffTT;
3061     return NULL;
3062   }
3063   // map CID -> Unicode -> GID
3064   if (ctu->isIdentity()) {
3065     codeToGIDLen = 65536;
3066   } else {
3067     codeToGIDLen = ctu->getLength();
3068   }
3069   codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
3070   for (code = 0; code < codeToGIDLen; ++code) {
3071     if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
3072       codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
3073     } else {
3074       codeToGID[code] = 0;
3075     }
3076   }
3077   ctu->decRefCnt();
3078 
3079   // check if font is already embedded
3080   fontFileInfo->startIter(&iter);
3081   while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
3082     if (ff->loc == psFontFileExternal &&
3083 	ff->type == font->getType() &&
3084 	!ff->extFileName->cmp(fileName) &&
3085 	ff->codeToGIDLen == codeToGIDLen &&
3086 	ff->codeToGID &&
3087 	!memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) {
3088       fontFileInfo->killIter(&iter);
3089       gfree(codeToGID);
3090       delete ffTT;
3091       return ff;
3092     }
3093   }
3094 
3095   // generate name
3096   psName = makePSFontName(font, font->getID());
3097 
3098   // beginning comment
3099   writePSFmt("%%BeginResource: font {0:t}\n", psName);
3100   embFontList->append("%%+ font ");
3101   embFontList->append(psName->getCString());
3102   embFontList->append("\n");
3103 
3104   // convert it to a Type 0 font
3105   if (globalParams->getPSLevel() >= psLevel3) {
3106     // Level 3: use a CID font
3107     ffTT->convertToCIDType0(psName->getCString(),
3108 			    codeToGID, codeToGIDLen,
3109 			    outputFunc, outputStream);
3110   } else {
3111     // otherwise: use a non-CID composite font
3112     ffTT->convertToType0(psName->getCString(),
3113 			 codeToGID, codeToGIDLen,
3114 			 outputFunc, outputStream);
3115   }
3116   delete ffTT;
3117 
3118   // ending comment
3119   writePS("%%EndResource\n");
3120 
3121   ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
3122   ff->extFileName = fileName->copy();
3123   ff->codeToGID = codeToGID;
3124   ff->codeToGIDLen = codeToGIDLen;
3125   fontFileInfo->add(ff->psName, ff);
3126   return ff;
3127 }
3128 
setupType3Font(GfxFont * font,Dict * parentResDict)3129 PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font,
3130 					    Dict *parentResDict) {
3131   PSFontFileInfo *ff;
3132   GString *psName;
3133   Dict *resDict;
3134   Dict *charProcs;
3135   Object charProc;
3136   Gfx *gfx;
3137   PDFRectangle box;
3138   double *m;
3139   GString *buf;
3140   int i;
3141 
3142   // generate name
3143   psName = GString::format("T3_{0:d}_{1:d}",
3144 			   font->getID()->num, font->getID()->gen);
3145 
3146   // set up resources used by font
3147   if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
3148     inType3Char = gTrue;
3149     setupResources(resDict);
3150     inType3Char = gFalse;
3151   } else {
3152     resDict = parentResDict;
3153   }
3154 
3155   // beginning comment
3156   writePSFmt("%%BeginResource: font {0:t}\n", psName);
3157   embFontList->append("%%+ font ");
3158   embFontList->append(psName->getCString());
3159   embFontList->append("\n");
3160 
3161   // font dictionary
3162   writePS("8 dict begin\n");
3163   writePS("/FontType 3 def\n");
3164   m = font->getFontMatrix();
3165   writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n",
3166 	     m[0], m[1], m[2], m[3], m[4], m[5]);
3167   m = font->getFontBBox();
3168   writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n",
3169 	     m[0], m[1], m[2], m[3]);
3170   writePS("/Encoding 256 array def\n");
3171   writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
3172   writePS("/BuildGlyph {\n");
3173   writePS("  exch /CharProcs get exch\n");
3174   writePS("  2 copy known not { pop /.notdef } if\n");
3175   writePS("  get exec\n");
3176   writePS("} bind def\n");
3177   writePS("/BuildChar {\n");
3178   writePS("  1 index /Encoding get exch get\n");
3179   writePS("  1 index /BuildGlyph get exec\n");
3180   writePS("} bind def\n");
3181   if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
3182     writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength());
3183     writePS("CharProcs begin\n");
3184     box.x1 = m[0];
3185     box.y1 = m[1];
3186     box.x2 = m[2];
3187     box.y2 = m[3];
3188     gfx = new Gfx(doc, this, resDict, &box, NULL);
3189     inType3Char = gTrue;
3190     for (i = 0; i < charProcs->getLength(); ++i) {
3191       t3FillColorOnly = gFalse;
3192       t3Cacheable = gFalse;
3193       t3NeedsRestore = gFalse;
3194       writePS("/");
3195       writePSName(charProcs->getKey(i));
3196       writePS(" {\n");
3197       gfx->display(charProcs->getValNF(i, &charProc));
3198       charProc.free();
3199       if (t3String) {
3200 	if (t3Cacheable) {
3201 	  buf = GString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n",
3202 				t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
3203 	} else {
3204 	  buf = GString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY);
3205 	}
3206 	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
3207 	delete buf;
3208 	(*outputFunc)(outputStream, t3String->getCString(),
3209 		      t3String->getLength());
3210 	delete t3String;
3211 	t3String = NULL;
3212       }
3213       if (t3NeedsRestore) {
3214 	(*outputFunc)(outputStream, "Q\n", 2);
3215       }
3216       writePS("} def\n");
3217     }
3218     inType3Char = gFalse;
3219     delete gfx;
3220     writePS("end\n");
3221   }
3222   writePS("currentdict end\n");
3223   writePSFmt("/{0:t} exch definefont pop\n", psName);
3224 
3225   // ending comment
3226   writePS("%%EndResource\n");
3227 
3228   ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
3229   fontFileInfo->add(ff->psName, ff);
3230   return ff;
3231 }
3232 
3233 // Make a unique PS font name, based on the names given in the PDF
3234 // font object, and an object ID (font file object for
makePSFontName(GfxFont * font,Ref * id)3235 GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) {
3236   GString *psName, *s;
3237 
3238   if ((s = font->getEmbeddedFontName())) {
3239     psName = filterPSName(s);
3240     if (!fontFileInfo->lookup(psName)) {
3241       return psName;
3242     }
3243     delete psName;
3244   }
3245   if ((s = font->getName())) {
3246     psName = filterPSName(s);
3247     if (!fontFileInfo->lookup(psName)) {
3248       return psName;
3249     }
3250     delete psName;
3251   }
3252   psName = GString::format("FF{0:d}_{1:d}", id->num, id->gen);
3253   if ((s = font->getEmbeddedFontName())) {
3254     s = filterPSName(s);
3255     psName->append('_')->append(s);
3256     delete s;
3257   } else if ((s = font->getName())) {
3258     s = filterPSName(s);
3259     psName->append('_')->append(s);
3260     delete s;
3261   }
3262   return psName;
3263 }
3264 
fixType1Font(GString * font,int length1,int length2)3265 GString *PSOutputDev::fixType1Font(GString *font, int length1, int length2) {
3266   Guchar *fontData;
3267   GString *out, *binSection;
3268   GBool pfb;
3269   int fontSize, i;
3270 
3271   fontData = (Guchar *)font->getCString();
3272   fontSize = font->getLength();
3273 
3274   // check for PFB
3275   pfb = fontSize >= 6 && fontData[0] == 0x80 && fontData[1] == 0x01;
3276   out = new GString();
3277   binSection = new GString();
3278   if (pfb) {
3279     if (!splitType1PFB(fontData, fontSize, out, binSection)) {
3280       delete out;
3281       delete binSection;
3282       return copyType1PFB(fontData, fontSize);
3283     }
3284   } else {
3285     if (!splitType1PFA(fontData, fontSize, length1, length2,
3286 		       out, binSection)) {
3287       delete out;
3288       delete binSection;
3289       return copyType1PFA(fontData, fontSize);
3290     }
3291   }
3292 
3293   out->append('\n');
3294 
3295   binSection = asciiHexDecodeType1EexecSection(binSection);
3296 
3297   if (!fixType1EexecSection(binSection, out)) {
3298     delete out;
3299     delete binSection;
3300     return pfb ? copyType1PFB(fontData, fontSize)
3301                : copyType1PFA(fontData, fontSize);
3302   }
3303   delete binSection;
3304 
3305   for (i = 0; i < 8; ++i) {
3306     out->append("0000000000000000000000000000000000000000000000000000000000000000\n");
3307   }
3308   out->append("cleartomark\n");
3309 
3310   return out;
3311 }
3312 
3313 // Split a Type 1 font in PFA format into a text section and a binary
3314 // section.
splitType1PFA(Guchar * font,int fontSize,int length1,int length2,GString * textSection,GString * binSection)3315 GBool PSOutputDev::splitType1PFA(Guchar *font, int fontSize,
3316 				 int length1, int length2,
3317 				 GString *textSection, GString *binSection) {
3318   int textLength, binStart, binLength, lastSpace, i;
3319 
3320   //--- extract the text section
3321 
3322   // Length1 is correct, and the text section ends with whitespace
3323   if (length1 <= fontSize &&
3324       length1 >= 18 &&
3325       !memcmp(font + length1 - 18, "currentfile eexec", 17)) {
3326     textLength = length1 - 1;
3327 
3328   // Length1 is correct, but the trailing whitespace is missing
3329   } else if (length1 <= fontSize &&
3330 	     length1 >= 17 &&
3331 	     !memcmp(font + length1 - 17, "currentfile eexec", 17)) {
3332     textLength = length1;
3333 
3334   // Length1 is incorrect
3335   } else {
3336     for (textLength = 17; textLength <= fontSize; ++textLength) {
3337       if (!memcmp(font + textLength - 17, "currentfile eexec", 17)) {
3338 	break;
3339       }
3340     }
3341     if (textLength > fontSize) {
3342       return gFalse;
3343     }
3344   }
3345 
3346   textSection->append((char *)font, textLength);
3347 
3348   //--- skip whitespace between the text section and the binary section
3349 
3350   for (i = 0, binStart = textLength;
3351        i < 8 && binStart < fontSize;
3352        ++i, ++binStart) {
3353     if (font[binStart] != ' ' && font[binStart] != '\t' &&
3354 	font[binStart] != '\n' && font[binStart] != '\r') {
3355       break;
3356     }
3357   }
3358   if (i == 8) {
3359     return gFalse;
3360   }
3361 
3362   //--- extract binary section
3363 
3364   // if we see "0000", assume Length2 is correct
3365   // (if Length2 is too long, it will be corrected by fixType1EexecSection)
3366   if (length2 > 0 && length2 < INT_MAX - 4 &&
3367       binStart <= fontSize - length2 - 4 &&
3368       !memcmp(font + binStart + length2, "0000", 4)) {
3369     binLength = length2;
3370 
3371   } else {
3372 
3373     // look for "0000" near the end of the font (note that there can
3374     // be intervening "\n", "\r\n", etc.), then search backward
3375     if (fontSize - binStart < 512) {
3376       return gFalse;
3377     }
3378     if (!memcmp(font + fontSize - 256, "0000", 4) ||
3379 	!memcmp(font + fontSize - 255, "0000", 4) ||
3380 	!memcmp(font + fontSize - 254, "0000", 4) ||
3381 	!memcmp(font + fontSize - 253, "0000", 4) ||
3382 	!memcmp(font + fontSize - 252, "0000", 4) ||
3383 	!memcmp(font + fontSize - 251, "0000", 4)) {
3384       i = fontSize - 252;
3385       lastSpace = -1;
3386       while (i >= binStart) {
3387 	if (font[i] == ' ' || font[i] == '\t' ||
3388 	    font[i] == '\n' || font[i] == '\r') {
3389 	  lastSpace = i;
3390 	  --i;
3391 	} else if (font[i] == '0') {
3392 	  --i;
3393 	} else {
3394 	  break;
3395 	}
3396       }
3397       if (lastSpace < 0) {
3398 	return gFalse;
3399       }
3400       // check for the case where the newline/space is missing between
3401       // the binary section and the first set of 64 '0' chars
3402       if (lastSpace - binStart > 64 &&
3403 	  !memcmp(font + lastSpace - 64,
3404 		  "0000000000000000000000000000000000000000000000000000000000000000",
3405 		  64)) {
3406 	binLength = lastSpace - 64 - binStart;
3407       } else {
3408 	binLength = lastSpace - binStart;
3409       }
3410 
3411     // couldn't find zeros after binary section -- assume they're
3412     // missing and the binary section extends to the end of the file
3413     } else {
3414       binLength = fontSize - binStart;
3415     }
3416   }
3417 
3418   binSection->append((char *)(font + binStart), binLength);
3419 
3420   return gTrue;
3421 }
3422 
3423 // Split a Type 1 font in PFB format into a text section and a binary
3424 // section.
splitType1PFB(Guchar * font,int fontSize,GString * textSection,GString * binSection)3425 GBool PSOutputDev::splitType1PFB(Guchar *font, int fontSize,
3426 				 GString *textSection, GString *binSection) {
3427   Guchar *p;
3428   int state, remain, len, n;
3429 
3430   // states:
3431   // 0: text section
3432   // 1: binary section
3433   // 2: trailer section
3434   // 3: eof
3435 
3436   state = 0;
3437   p = font;
3438   remain = fontSize;
3439   while (remain >= 2) {
3440     if (p[0] != 0x80) {
3441       return gFalse;
3442     }
3443     switch (state) {
3444     case 0:
3445       if (p[1] == 0x02) {
3446 	state = 1;
3447       } else if (p[1] != 0x01) {
3448 	return gFalse;
3449       }
3450       break;
3451     case 1:
3452       if (p[1] == 0x01) {
3453 	state = 2;
3454       } else if (p[1] != 0x02) {
3455 	return gFalse;
3456       }
3457       break;
3458     case 2:
3459       if (p[1] == 0x03) {
3460 	state = 3;
3461       } else if (p[1] != 0x01) {
3462 	return gFalse;
3463       }
3464       break;
3465     default: // shouldn't happen
3466       return gFalse;
3467     }
3468     if (state == 3) {
3469       break;
3470     }
3471 
3472     if (remain < 6) {
3473       break;
3474     }
3475     len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24);
3476     if (len < 0 || len > remain - 6) {
3477       return gFalse;
3478     }
3479 
3480     switch (state) {
3481     case 0:
3482       textSection->append((char *)(p + 6), len);
3483       break;
3484     case 1:
3485       binSection->append((char *)(p + 6), len);
3486       break;
3487     case 2:
3488       // we don't use the trailer
3489       break;
3490     default: // shouldn't happen
3491       return gFalse;
3492     }
3493 
3494     p += len + 6;
3495     remain -= len + 6;
3496   }
3497 
3498   if (state != 3) {
3499     return gFalse;
3500   }
3501 
3502   n = textSection->getLength();
3503   if (n >= 18 && !memcmp(textSection->getCString() + n - 18,
3504 			 "currentfile eexec", 17)) {
3505     // remove the trailing whitespace
3506     textSection->del(n - 1, 1);
3507   } else if (n >= 17 && !memcmp(textSection->getCString() + n - 17,
3508 				"currentfile eexec", 17)) {
3509     // missing whitespace at end -- leave as-is
3510   } else {
3511     return gFalse;
3512   }
3513 
3514   return gTrue;
3515 }
3516 
3517 // If <in> is ASCIIHex-encoded, decode it, delete <in>, and return the
3518 // binary version.  Else return <in> unchanged.
asciiHexDecodeType1EexecSection(GString * in)3519 GString *PSOutputDev::asciiHexDecodeType1EexecSection(GString *in) {
3520   GString *out;
3521   char c;
3522   Guchar byte;
3523   int state, i;
3524 
3525   out = new GString();
3526   state = 0;
3527   byte = 0;
3528   for (i = 0; i < in->getLength(); ++i) {
3529     c = in->getChar(i);
3530     if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
3531       continue;
3532     }
3533     if (c >= '0' && c <= '9') {
3534       byte = (Guchar)(byte + (c - '0'));
3535     } else if (c >= 'A' && c <= 'F') {
3536       byte = (Guchar)(byte + (c - 'A' + 10));
3537     } else if (c >= 'a' && c <= 'f') {
3538       byte = (Guchar)(byte + (c - 'a' + 10));
3539     } else {
3540       delete out;
3541       return in;
3542     }
3543     if (state == 0) {
3544       byte = (Guchar)(byte << 4);
3545       state = 1;
3546     } else {
3547       out->append((char)byte);
3548       state = 0;
3549       byte = 0;
3550     }
3551   }
3552   delete in;
3553   return out;
3554 }
3555 
fixType1EexecSection(GString * binSection,GString * out)3556 GBool PSOutputDev::fixType1EexecSection(GString *binSection, GString *out) {
3557   static char hexChars[17] = "0123456789abcdef";
3558   Guchar buf[16], buf2[16];
3559   Guchar byte;
3560   int r, i, j;
3561 
3562   // eexec-decode the binary section, keeping the last 16 bytes
3563   r = 55665;
3564   for (i = 0; i < binSection->getLength(); ++i) {
3565     byte = (Guchar)binSection->getChar(i);
3566     buf[i & 15] = byte ^ (Guchar)(r >> 8);
3567     r = ((r + byte) * 52845 + 22719) & 0xffff;
3568   }
3569   for (j = 0; j < 16; ++j) {
3570     buf2[j] = buf[(i + j) & 15];
3571   }
3572 
3573   // look for 'closefile'
3574   for (i = 0; i <= 16 - 9; ++i) {
3575     if (!memcmp(buf2 + i, "closefile", 9)) {
3576       break;
3577     }
3578   }
3579   if (i > 16 - 9) {
3580     return gFalse;
3581   }
3582   // three cases:
3583   // - short: missing space after "closefile" (i == 16 - 9)
3584   // - correct: exactly one space after "closefile" (i == 16 - 10)
3585   // - long: extra chars after "closefile" (i < 16 - 10)
3586   if (i == 16 - 9) {
3587     binSection->append((char)((Guchar)'\n' ^ (Guchar)(r >> 8)));
3588   } else if (i < 16 - 10) {
3589     binSection->del(binSection->getLength() - (16 - 10 - i), 16 - 10 - i);
3590   }
3591 
3592   // ASCIIHex encode
3593   for (i = 0; i < binSection->getLength(); i += 32) {
3594     for (j = 0; j < 32 && i+j < binSection->getLength(); ++j) {
3595       byte = (Guchar)binSection->getChar(i+j);
3596       out->append(hexChars[(byte >> 4) & 0x0f]);
3597       out->append(hexChars[byte & 0x0f]);
3598     }
3599     out->append('\n');
3600   }
3601 
3602   return gTrue;
3603 }
3604 
3605 // The Type 1 cleanup code failed -- assume it's a valid PFA-format
3606 // font and copy it to the output.
copyType1PFA(Guchar * font,int fontSize)3607 GString *PSOutputDev::copyType1PFA(Guchar *font, int fontSize) {
3608   GString *out;
3609 
3610   error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 font");
3611 
3612   out = new GString((char *)font, fontSize);
3613   // append a newline to avoid problems where the original font
3614   // doesn't end with one
3615   out->append('\n');
3616   return out;
3617 }
3618 
3619 // The Type 1 cleanup code failed -- assume it's a valid PFB-format
3620 // font, decode the PFB blocks, and copy them to the output.
copyType1PFB(Guchar * font,int fontSize)3621 GString *PSOutputDev::copyType1PFB(Guchar *font, int fontSize) {
3622   static char hexChars[17] = "0123456789abcdef";
3623   GString *out;
3624   Guchar *p;
3625   int remain, len, i, j;
3626 
3627   error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 (PFB) font");
3628 
3629   out = new GString();
3630   p = font;
3631   remain = fontSize;
3632   while (remain >= 6 &&
3633 	 p[0] == 0x80 &&
3634 	 (p[1] == 0x01 || p[1] == 0x02)) {
3635     len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24);
3636     if (len < 0 || len > remain - 6) {
3637       break;
3638     }
3639     if (p[1] == 0x01) {
3640       out->append((char *)(p + 6), len);
3641     } else {
3642       for (i = 0; i < len; i += 32) {
3643 	for (j = 0; j < 32 && i+j < len; ++j) {
3644 	  out->append(hexChars[(p[6+i+j] >> 4) & 0x0f]);
3645 	  out->append(hexChars[p[6+i+j] & 0x0f]);
3646 	}
3647 	out->append('\n');
3648       }
3649     }
3650     p += len + 6;
3651     remain -= len + 6;
3652   }
3653   // append a newline to avoid problems where the original font
3654   // doesn't end with one
3655   out->append('\n');
3656   return out;
3657 }
3658 
renameType1Font(GString * font,GString * name)3659 void PSOutputDev::renameType1Font(GString *font, GString *name) {
3660   char *p1, *p2;
3661   int i;
3662 
3663   if (!(p1 = strstr(font->getCString(), "\n/FontName")) &&
3664       !(p1 = strstr(font->getCString(), "\r/FontName"))) {
3665     return;
3666   }
3667   p1 += 10;
3668   while (*p1 == ' ' || *p1 == '\t' || *p1 == '\n' || *p1 == '\r') {
3669     ++p1;
3670   }
3671   if (*p1 != '/') {
3672     return;
3673   }
3674   ++p1;
3675   p2 = p1;
3676   while (*p2 && *p2 != ' ' && *p2 != '\t' && *p2 != '\n' && *p2 != '\r') {
3677     ++p2;
3678   }
3679   if (!*p2) {
3680     return;
3681   }
3682   i = (int)(p1 - font->getCString());
3683   font->del(i, (int)(p2 - p1));
3684   font->insert(i, name);
3685 }
3686 
setupDefaultFont()3687 void PSOutputDev::setupDefaultFont() {
3688   writePS("/xpdf_default_font /Helvetica 1 1 ISOLatin1Encoding pdfMakeFont\n");
3689 }
3690 
setupImages(Dict * resDict)3691 void PSOutputDev::setupImages(Dict *resDict) {
3692   Object xObjDict, xObj, xObjRef, subtypeObj, maskObj, maskRef;
3693   Ref imgID;
3694   int i, j;
3695 
3696   if (!(mode == psModeForm || inType3Char || preload)) {
3697     return;
3698   }
3699 
3700   resDict->lookup("XObject", &xObjDict);
3701   if (xObjDict.isDict()) {
3702     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
3703       xObjDict.dictGetValNF(i, &xObjRef);
3704       xObjDict.dictGetVal(i, &xObj);
3705       if (xObj.isStream()) {
3706 	xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
3707 	if (subtypeObj.isName("Image")) {
3708 	  if (xObjRef.isRef()) {
3709 	    imgID = xObjRef.getRef();
3710 	    for (j = 0; j < imgIDLen; ++j) {
3711 	      if (imgIDs[j].num == imgID.num && imgIDs[j].gen == imgID.gen) {
3712 		break;
3713 	      }
3714 	    }
3715 	    if (j == imgIDLen) {
3716 	      if (imgIDLen >= imgIDSize) {
3717 		if (imgIDSize == 0) {
3718 		  imgIDSize = 64;
3719 		} else {
3720 		  imgIDSize *= 2;
3721 		}
3722 		imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref));
3723 	      }
3724 	      imgIDs[imgIDLen++] = imgID;
3725 	      setupImage(imgID, xObj.getStream(), gFalse, NULL);
3726 	      if (level >= psLevel3) {
3727 		xObj.streamGetDict()->lookup("Mask", &maskObj);
3728 		if (maskObj.isStream()) {
3729 		  setupImage(imgID, maskObj.getStream(), gTrue, NULL);
3730 		} else if (level == psLevel3Gray && maskObj.isArray()) {
3731 		  setupImage(imgID, xObj.getStream(), gFalse,
3732 			     maskObj.getArray());
3733 		}
3734 	        maskObj.free();
3735 	      }
3736 	    }
3737 	  } else {
3738 	    error(errSyntaxError, -1,
3739 		  "Image in resource dict is not an indirect reference");
3740 	  }
3741 	}
3742 	subtypeObj.free();
3743       }
3744       xObj.free();
3745       xObjRef.free();
3746     }
3747   }
3748   xObjDict.free();
3749 }
3750 
setupImage(Ref id,Stream * str,GBool mask,Array * colorKeyMask)3751 void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask,
3752 			     Array *colorKeyMask) {
3753   StreamColorSpaceMode csMode;
3754   GfxColorSpace *colorSpace;
3755   GfxImageColorMap *colorMap;
3756   int maskColors[2*gfxColorMaxComps];
3757   Object obj1;
3758   GBool imageMask, useLZW, useRLE, useCompressed, useASCIIHex;
3759   GString *s;
3760   int c, width, height, bits, size, line, col, i;
3761 
3762   // check for mask
3763   str->getDict()->lookup("ImageMask", &obj1);
3764   if (obj1.isBool()) {
3765     imageMask = obj1.getBool();
3766   } else {
3767     imageMask = gFalse;
3768   }
3769   obj1.free();
3770 
3771   // get image size
3772   str->getDict()->lookup("Width", &obj1);
3773   if (!obj1.isInt() || obj1.getInt() <= 0) {
3774     error(errSyntaxError, -1, "Invalid Width in image");
3775     obj1.free();
3776     return;
3777   }
3778   width = obj1.getInt();
3779   obj1.free();
3780   str->getDict()->lookup("Height", &obj1);
3781   if (!obj1.isInt() || obj1.getInt() <= 0) {
3782     error(errSyntaxError, -1, "Invalid Height in image");
3783     obj1.free();
3784     return;
3785   }
3786   height = obj1.getInt();
3787   obj1.free();
3788 
3789   // build the color map
3790   if (mask || imageMask) {
3791     colorMap = NULL;
3792   } else {
3793     bits = 0;
3794     csMode = streamCSNone;
3795     str->getImageParams(&bits, &csMode);
3796     if (bits == 0) {
3797       str->getDict()->lookup("BitsPerComponent", &obj1);
3798       if (!obj1.isInt()) {
3799 	error(errSyntaxError, -1, "Invalid BitsPerComponent in image");
3800 	obj1.free();
3801 	return;
3802       }
3803       bits = obj1.getInt();
3804       obj1.free();
3805     }
3806     str->getDict()->lookup("ColorSpace", &obj1);
3807     if (!obj1.isNull()) {
3808       colorSpace = GfxColorSpace::parse(&obj1
3809 					);
3810     } else if (csMode == streamCSDeviceGray) {
3811       colorSpace = GfxColorSpace::create(csDeviceGray);
3812     } else if (csMode == streamCSDeviceRGB) {
3813       colorSpace = GfxColorSpace::create(csDeviceRGB);
3814     } else if (csMode == streamCSDeviceCMYK) {
3815       colorSpace = GfxColorSpace::create(csDeviceCMYK);
3816     } else {
3817       colorSpace = NULL;
3818     }
3819     obj1.free();
3820     if (!colorSpace) {
3821       error(errSyntaxError, -1, "Invalid ColorSpace in image");
3822       return;
3823     }
3824     str->getDict()->lookup("Decode", &obj1);
3825     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
3826     obj1.free();
3827   }
3828 
3829   // filters
3830   if (level < psLevel2) {
3831     useLZW = useRLE = gFalse;
3832     useCompressed = gFalse;
3833     useASCIIHex = gTrue;
3834   } else {
3835     if (colorKeyMask) {
3836       if (globalParams->getPSUncompressPreloadedImages()) {
3837 	useLZW = useRLE = gFalse;
3838       } else if (globalParams->getPSLZW()) {
3839 	useLZW = gTrue;
3840 	useRLE = gFalse;
3841       } else {
3842 	useRLE = gTrue;
3843 	useLZW = gFalse;
3844       }
3845       useCompressed = gFalse;
3846     } else if (colorMap &&
3847 	       (colorMap->getColorSpace()->getMode() == csDeviceN ||
3848 		level == psLevel2Gray || level == psLevel3Gray)) {
3849       if (globalParams->getPSLZW()) {
3850 	useLZW = gTrue;
3851 	useRLE = gFalse;
3852       } else {
3853 	useRLE = gTrue;
3854 	useLZW = gFalse;
3855       }
3856       useCompressed = gFalse;
3857     } else if (globalParams->getPSUncompressPreloadedImages()) {
3858       useLZW = useRLE = gFalse;
3859       useCompressed = gFalse;
3860     } else {
3861       s = str->getPSFilter(level < psLevel3 ? 2 : 3, "", gTrue);
3862       if (s) {
3863 	useLZW = useRLE = gFalse;
3864 	useCompressed = gTrue;
3865 	delete s;
3866       } else {
3867 	if (globalParams->getPSLZW()) {
3868 	  useLZW = gTrue;
3869 	  useRLE = gFalse;
3870 	} else {
3871 	  useRLE = gTrue;
3872 	  useLZW = gFalse;
3873 	}
3874 	useCompressed = gFalse;
3875       }
3876     }
3877     useASCIIHex = globalParams->getPSASCIIHex();
3878   }
3879   if (useCompressed) {
3880     str = str->getUndecodedStream();
3881   }
3882   if (colorKeyMask) {
3883     memset(maskColors, 0, sizeof(maskColors));
3884     for (i = 0; i < colorKeyMask->getLength() && i < 2*gfxColorMaxComps; ++i) {
3885       colorKeyMask->get(i, &obj1);
3886       if (obj1.isInt()) {
3887 	maskColors[i] = obj1.getInt();
3888       }
3889       obj1.free();
3890     }
3891     str = new ColorKeyToMaskEncoder(str, width, height, colorMap, maskColors);
3892   } else if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) {
3893     str = new GrayRecoder(str, width, height, colorMap);
3894   } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
3895     str = new DeviceNRecoder(str, width, height, colorMap);
3896   }
3897   if (useLZW) {
3898     str = new LZWEncoder(str);
3899   } else if (useRLE) {
3900     str = new RunLengthEncoder(str);
3901   }
3902   if (useASCIIHex) {
3903     str = new ASCIIHexEncoder(str);
3904   } else {
3905     str = new ASCII85Encoder(str);
3906   }
3907 
3908   // compute image data size
3909   str->reset();
3910   col = size = 0;
3911   do {
3912     do {
3913       c = str->getChar();
3914     } while (c == '\n' || c == '\r');
3915     if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3916       break;
3917     }
3918     if (c == 'z') {
3919       ++col;
3920     } else {
3921       ++col;
3922       for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
3923 	do {
3924 	  c = str->getChar();
3925 	} while (c == '\n' || c == '\r');
3926 	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3927 	  break;
3928 	}
3929 	++col;
3930       }
3931     }
3932     if (col > 225) {
3933       ++size;
3934       col = 0;
3935     }
3936   } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
3937   // add one entry for the final line of data; add another entry
3938   // because the LZWDecode/RunLengthDecode filter may read past the end
3939   ++size;
3940   if (useLZW || useRLE) {
3941     ++size;
3942   }
3943   writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n",
3944 	     size, (mask || colorKeyMask) ? "Mask" : "Im", id.num, id.gen);
3945   str->close();
3946 
3947   // write the data into the array
3948   str->reset();
3949   line = col = 0;
3950   writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~"));
3951   do {
3952     do {
3953       c = str->getChar();
3954     } while (c == '\n' || c == '\r');
3955     if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3956       break;
3957     }
3958     if (c == 'z') {
3959       writePSChar((char)c);
3960       ++col;
3961     } else {
3962       writePSChar((char)c);
3963       ++col;
3964       for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
3965 	do {
3966 	  c = str->getChar();
3967 	} while (c == '\n' || c == '\r');
3968 	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3969 	  break;
3970 	}
3971 	writePSChar((char)c);
3972 	++col;
3973       }
3974     }
3975     // each line is: "dup nnnnn <~...data...~> put<eol>"
3976     // so max data length = 255 - 20 = 235
3977     // chunks are 1 or 4 bytes each, so we have to stop at 232
3978     // but make it 225 just to be safe
3979     if (col > 225) {
3980       writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
3981       ++line;
3982       writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line);
3983       col = 0;
3984     }
3985   } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
3986   writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
3987   if (useLZW || useRLE) {
3988     ++line;
3989     writePSFmt("{0:d} <> put\n", line);
3990   } else {
3991     writePS("pop\n");
3992   }
3993   str->close();
3994 
3995   delete str;
3996 
3997   if (colorMap) {
3998     delete colorMap;
3999   }
4000 }
4001 
setupForms(Dict * resDict)4002 void PSOutputDev::setupForms(Dict *resDict) {
4003   Object xObjDict, xObj, xObjRef, subtypeObj;
4004   int i;
4005 
4006   if (!preload) {
4007     return;
4008   }
4009 
4010   resDict->lookup("XObject", &xObjDict);
4011   if (xObjDict.isDict()) {
4012     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
4013       xObjDict.dictGetValNF(i, &xObjRef);
4014       xObjDict.dictGetVal(i, &xObj);
4015       if (xObj.isStream()) {
4016 	xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
4017 	if (subtypeObj.isName("Form")) {
4018 	  if (xObjRef.isRef()) {
4019 	    setupForm(&xObjRef, &xObj);
4020 	  } else {
4021 	    error(errSyntaxError, -1,
4022 		  "Form in resource dict is not an indirect reference");
4023 	  }
4024 	}
4025 	subtypeObj.free();
4026       }
4027       xObj.free();
4028       xObjRef.free();
4029     }
4030   }
4031   xObjDict.free();
4032 }
4033 
setupForm(Object * strRef,Object * strObj)4034 void PSOutputDev::setupForm(Object *strRef, Object *strObj) {
4035   Dict *dict, *resDict;
4036   Object matrixObj, bboxObj, resObj, obj1;
4037   double m[6], bbox[4];
4038   PDFRectangle box;
4039   Gfx *gfx;
4040   int i;
4041 
4042   // check if form is already defined
4043   for (i = 0; i < formIDLen; ++i) {
4044     if (formIDs[i].num == strRef->getRefNum() &&
4045 	formIDs[i].gen == strRef->getRefGen()) {
4046       return;
4047     }
4048   }
4049 
4050   // add entry to formIDs list
4051   if (formIDLen >= formIDSize) {
4052     if (formIDSize == 0) {
4053       formIDSize = 64;
4054     } else {
4055       formIDSize *= 2;
4056     }
4057     formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
4058   }
4059   formIDs[formIDLen++] = strRef->getRef();
4060 
4061   dict = strObj->streamGetDict();
4062 
4063   // get bounding box
4064   dict->lookup("BBox", &bboxObj);
4065   if (!bboxObj.isArray()) {
4066     bboxObj.free();
4067     error(errSyntaxError, -1, "Bad form bounding box");
4068     return;
4069   }
4070   for (i = 0; i < 4; ++i) {
4071     bboxObj.arrayGet(i, &obj1);
4072     bbox[i] = obj1.getNum();
4073     obj1.free();
4074   }
4075   bboxObj.free();
4076 
4077   // get matrix
4078   dict->lookup("Matrix", &matrixObj);
4079   if (matrixObj.isArray()) {
4080     for (i = 0; i < 6; ++i) {
4081       matrixObj.arrayGet(i, &obj1);
4082       m[i] = obj1.getNum();
4083       obj1.free();
4084     }
4085   } else {
4086     m[0] = 1; m[1] = 0;
4087     m[2] = 0; m[3] = 1;
4088     m[4] = 0; m[5] = 0;
4089   }
4090   matrixObj.free();
4091 
4092   // get resources
4093   dict->lookup("Resources", &resObj);
4094   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
4095 
4096   writePSFmt("/f_{0:d}_{1:d} {{\n", strRef->getRefNum(), strRef->getRefGen());
4097   writePS("q\n");
4098   writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
4099 	     m[0], m[1], m[2], m[3], m[4], m[5]);
4100 
4101   box.x1 = bbox[0];
4102   box.y1 = bbox[1];
4103   box.x2 = bbox[2];
4104   box.y2 = bbox[3];
4105   gfx = new Gfx(doc, this, resDict, &box, &box);
4106   gfx->display(strRef);
4107   delete gfx;
4108 
4109   writePS("Q\n");
4110   writePS("} def\n");
4111 
4112   resObj.free();
4113 }
4114 
4115 
checkPageSlice(Page * page,double hDPI,double vDPI,int rotateA,GBool useMediaBox,GBool crop,int sliceX,int sliceY,int sliceW,int sliceH,GBool printing,GBool (* abortCheckCbk)(void * data),void * abortCheckCbkData)4116 GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
4117 				  int rotateA, GBool useMediaBox, GBool crop,
4118 				  int sliceX, int sliceY,
4119 				  int sliceW, int sliceH,
4120 				  GBool printing,
4121 				  GBool (*abortCheckCbk)(void *data),
4122 				  void *abortCheckCbkData) {
4123   int pg;
4124 #if HAVE_SPLASH
4125   GBool mono;
4126   GBool useLZW;
4127   double dpi;
4128   SplashOutputDev *splashOut;
4129   SplashColor paperColor;
4130   PDFRectangle box;
4131   GfxState *state;
4132   SplashBitmap *bitmap;
4133   Stream *str0, *str;
4134   Object obj;
4135   Guchar *p;
4136   Guchar col[4];
4137   char buf[4096];
4138   double userUnit, hDPI2, vDPI2;
4139   double m0, m1, m2, m3, m4, m5;
4140   int nStripes, stripeH, stripeY;
4141   int w, h, x, y, comp, i, n;
4142 #endif
4143 
4144   pg = page->getNum();
4145   if (!(pg >= firstPage && pg <= lastPage &&
4146 	rasterizePage[pg - firstPage])) {
4147     return gTrue;
4148   }
4149 
4150 #if HAVE_SPLASH
4151   // get the rasterization parameters
4152   dpi = globalParams->getPSRasterResolution();
4153   mono = globalParams->getPSRasterMono() ||
4154          level == psLevel1 ||
4155          level == psLevel2Gray ||
4156          level == psLevel3Gray;
4157   useLZW = globalParams->getPSLZW();
4158 
4159   // get the UserUnit
4160   if (honorUserUnit) {
4161     userUnit = page->getUserUnit();
4162   } else {
4163     userUnit = 1;
4164   }
4165 
4166   // start the PS page
4167   page->makeBox(userUnit * dpi, userUnit * dpi, rotateA, useMediaBox, gFalse,
4168 		sliceX, sliceY, sliceW, sliceH, &box, &crop);
4169   rotateA += page->getRotate();
4170   if (rotateA >= 360) {
4171     rotateA -= 360;
4172   } else if (rotateA < 0) {
4173     rotateA += 360;
4174   }
4175   state = new GfxState(dpi, dpi, &box, rotateA, gFalse);
4176   startPage(page->getNum(), state);
4177   delete state;
4178 
4179   // set up the SplashOutputDev
4180   if (mono) {
4181     paperColor[0] = 0xff;
4182     splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse,
4183 				    paperColor, gFalse,
4184 				    globalParams->getAntialiasPrinting());
4185 #if SPLASH_CMYK
4186   } else if (level == psLevel1Sep) {
4187     paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0;
4188     splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse,
4189 				    paperColor, gFalse,
4190 				    globalParams->getAntialiasPrinting());
4191 #endif
4192   } else {
4193     paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
4194     splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse,
4195 				    paperColor, gFalse,
4196 				    globalParams->getAntialiasPrinting());
4197   }
4198   splashOut->startDoc(xref);
4199 
4200   // break the page into stripes
4201   // NB: startPage() has already multiplied xScale and yScale by UserUnit
4202   hDPI2 = xScale * dpi;
4203   vDPI2 = yScale * dpi;
4204   if (sliceW < 0 || sliceH < 0) {
4205     if (useMediaBox) {
4206       box = *page->getMediaBox();
4207     } else {
4208       box = *page->getCropBox();
4209     }
4210     sliceX = sliceY = 0;
4211     sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0);
4212     if (sliceW == 0) {
4213       sliceW = 1;
4214     }
4215     sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0);
4216     if (sliceH == 0) {
4217       sliceH = 1;
4218     }
4219   }
4220   nStripes = (int)ceil(((double)sliceW * (double)sliceH) /
4221 		       (double)globalParams->getPSRasterSliceSize());
4222   stripeH = (sliceH + nStripes - 1) / nStripes;
4223 
4224   // render the stripes
4225   for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) {
4226 
4227     // rasterize a stripe
4228     page->makeBox(hDPI2, vDPI2, 0, useMediaBox, gFalse,
4229 		  sliceX, stripeY, sliceW, stripeH, &box, &crop);
4230     m0 = box.x2 - box.x1;
4231     m1 = 0;
4232     m2 = 0;
4233     m3 = box.y2 - box.y1;
4234     m4 = box.x1;
4235     m5 = box.y1;
4236     page->displaySlice(splashOut, hDPI2, vDPI2,
4237 		       (360 - page->getRotate()) % 360, useMediaBox, crop,
4238 		       sliceX, stripeY, sliceW, stripeH,
4239 		       printing, abortCheckCbk, abortCheckCbkData);
4240 
4241     // draw the rasterized image
4242     bitmap = splashOut->getBitmap();
4243     w = bitmap->getWidth();
4244     h = bitmap->getHeight();
4245     writePS("gsave\n");
4246     writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n",
4247 	       m0, m1, m2, m3, m4, m5);
4248     switch (level) {
4249     case psLevel1:
4250       writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
4251 		 w, h, w, -h, h);
4252       p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
4253       i = 0;
4254       for (y = 0; y < h; ++y) {
4255 	for (x = 0; x < w; ++x) {
4256 	  writePSFmt("{0:02x}", *p++);
4257 	  if (++i == 32) {
4258 	    writePSChar('\n');
4259 	    i = 0;
4260 	  }
4261 	}
4262       }
4263       if (i != 0) {
4264 	writePSChar('\n');
4265       }
4266       break;
4267     case psLevel1Sep:
4268       writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n",
4269 		 w, h, w, -h, h);
4270       p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
4271       i = 0;
4272       col[0] = col[1] = col[2] = col[3] = 0;
4273       for (y = 0; y < h; ++y) {
4274 	for (comp = 0; comp < 4; ++comp) {
4275 	  for (x = 0; x < w; ++x) {
4276 	    writePSFmt("{0:02x}", p[4*x + comp]);
4277 	    col[comp] |= p[4*x + comp];
4278 	    if (++i == 32) {
4279 	      writePSChar('\n');
4280 	      i = 0;
4281 	    }
4282 	  }
4283 	}
4284 	p -= bitmap->getRowSize();
4285       }
4286       if (i != 0) {
4287 	writePSChar('\n');
4288       }
4289       if (col[0]) {
4290 	processColors |= psProcessCyan;
4291       }
4292       if (col[1]) {
4293 	processColors |= psProcessMagenta;
4294       }
4295       if (col[2]) {
4296 	processColors |= psProcessYellow;
4297       }
4298       if (col[3]) {
4299 	processColors |= psProcessBlack;
4300       }
4301       break;
4302     case psLevel2:
4303     case psLevel2Gray:
4304     case psLevel2Sep:
4305     case psLevel3:
4306     case psLevel3Gray:
4307     case psLevel3Sep:
4308       if (mono) {
4309 	writePS("/DeviceGray setcolorspace\n");
4310       } else {
4311 	writePS("/DeviceRGB setcolorspace\n");
4312       }
4313       writePS("<<\n  /ImageType 1\n");
4314       writePSFmt("  /Width {0:d}\n", bitmap->getWidth());
4315       writePSFmt("  /Height {0:d}\n", bitmap->getHeight());
4316       writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h);
4317       writePS("  /BitsPerComponent 8\n");
4318       if (mono) {
4319 	writePS("  /Decode [0 1]\n");
4320       } else {
4321 	writePS("  /Decode [0 1 0 1 0 1]\n");
4322       }
4323       writePS("  /DataSource currentfile\n");
4324       if (globalParams->getPSASCIIHex()) {
4325 	writePS("    /ASCIIHexDecode filter\n");
4326       } else {
4327 	writePS("    /ASCII85Decode filter\n");
4328       }
4329       if (useLZW) {
4330 	writePS("    /LZWDecode filter\n");
4331       } else {
4332 	writePS("    /RunLengthDecode filter\n");
4333       }
4334       writePS(">>\n");
4335       writePS("image\n");
4336       obj.initNull();
4337       p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
4338       str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj);
4339       if (useLZW) {
4340 	str = new LZWEncoder(str0);
4341       } else {
4342 	str = new RunLengthEncoder(str0);
4343       }
4344       if (globalParams->getPSASCIIHex()) {
4345 	str = new ASCIIHexEncoder(str);
4346       } else {
4347 	str = new ASCII85Encoder(str);
4348       }
4349       str->reset();
4350       while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
4351 	writePSBlock(buf, n);
4352       }
4353       str->close();
4354       delete str;
4355       delete str0;
4356       writePSChar('\n');
4357       processColors |= mono ? psProcessBlack : psProcessCMYK;
4358       break;
4359     }
4360     writePS("grestore\n");
4361   }
4362 
4363   delete splashOut;
4364 
4365   // finish the PS page
4366   endPage();
4367 
4368   return gFalse;
4369 
4370 #else // HAVE_SPLASH
4371 
4372   error(errSyntaxWarning, -1,
4373 	"PDF page uses transparency and PSOutputDev was built without"
4374 	" the Splash rasterizer - output may not be correct");
4375   return gTrue;
4376 #endif // HAVE_SPLASH
4377 }
4378 
startPage(int pageNum,GfxState * state)4379 void PSOutputDev::startPage(int pageNum, GfxState *state) {
4380   Page *page;
4381   double userUnit;
4382   int x1, y1, x2, y2, width, height, t;
4383   int imgWidth, imgHeight, imgWidth2, imgHeight2;
4384   GBool landscape;
4385   GString *s;
4386 
4387   page = doc->getCatalog()->getPage(pageNum);
4388   if (honorUserUnit) {
4389     userUnit = page->getUserUnit();
4390   } else {
4391     userUnit = 1;
4392   }
4393 
4394   if (mode == psModePS) {
4395     writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage);
4396     if (paperMatch) {
4397       imgLLX = imgLLY = 0;
4398       if (globalParams->getPSUseCropBoxAsPage()) {
4399 	imgURX = (int)ceil(page->getCropWidth() * userUnit);
4400 	imgURY = (int)ceil(page->getCropHeight() * userUnit);
4401       } else {
4402 	imgURX = (int)ceil(page->getMediaWidth() * userUnit);
4403 	imgURY = (int)ceil(page->getMediaHeight() * userUnit);
4404       }
4405       if (state->getRotate() == 90 || state->getRotate() == 270) {
4406 	t = imgURX;
4407 	imgURX = imgURY;
4408 	imgURY = t;
4409       }
4410       writePSFmt("%%PageMedia: {0:d}x{1:d}\n", imgURX, imgURY);
4411       writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", imgURX, imgURY);
4412     }
4413     writePS("%%BeginPageSetup\n");
4414   }
4415   if (mode != psModeForm) {
4416     writePS("xpdf begin\n");
4417   }
4418 
4419   // set up paper size for paper=match mode
4420   // NB: this must be done *before* the saveState() for overlays.
4421   if (mode == psModePS && paperMatch) {
4422     writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY);
4423   }
4424 
4425   // underlays
4426   if (underlayCbk) {
4427     (*underlayCbk)(this, underlayCbkData);
4428   }
4429   if (overlayCbk) {
4430     saveState(NULL);
4431   }
4432 
4433   switch (mode) {
4434 
4435   case psModePS:
4436     // rotate, translate, and scale page
4437     imgWidth = imgURX - imgLLX;
4438     imgHeight = imgURY - imgLLY;
4439     x1 = (int)floor(state->getX1());
4440     y1 = (int)floor(state->getY1());
4441     x2 = (int)ceil(state->getX2());
4442     y2 = (int)ceil(state->getY2());
4443     width = x2 - x1;
4444     height = y2 - y1;
4445     tx = ty = 0;
4446     // rotation and portrait/landscape mode
4447     if (paperMatch) {
4448       rotate = (360 - state->getRotate()) % 360;
4449       landscape = gFalse;
4450     } else if (rotate0 >= 0) {
4451       rotate = (360 - rotate0) % 360;
4452       landscape = gFalse;
4453     } else {
4454       rotate = (360 - state->getRotate()) % 360;
4455       double scaledWidth = width * userUnit;
4456       double scaledHeight = height * userUnit;
4457       if (xScale0 > 0 && yScale0 > 0) {
4458 	scaledWidth *= xScale0;
4459 	scaledHeight *= yScale0;
4460       }
4461       if (rotate == 0 || rotate == 180) {
4462 	if ((scaledWidth < scaledHeight && imgWidth > imgHeight &&
4463 	     scaledHeight > imgHeight) ||
4464 	    (scaledWidth > scaledHeight && imgWidth < imgHeight &&
4465 	     scaledWidth > imgWidth)) {
4466 	  rotate += 90;
4467 	  landscape = gTrue;
4468 	} else {
4469 	  landscape = gFalse;
4470 	}
4471       } else { // rotate == 90 || rotate == 270
4472 	if ((scaledHeight < scaledWidth && imgWidth > imgHeight &&
4473 	     scaledWidth > imgHeight) ||
4474 	    (scaledHeight > scaledWidth && imgWidth < imgHeight &&
4475 	     scaledHeight > imgWidth)) {
4476 	  rotate = 270 - rotate;
4477 	  landscape = gTrue;
4478 	} else {
4479 	  landscape = gFalse;
4480 	}
4481       }
4482     }
4483     writePSFmt("%%PageOrientation: {0:s}\n",
4484 	       landscape ? "Landscape" : "Portrait");
4485     writePS("pdfStartPage\n");
4486     if (rotate == 0) {
4487       imgWidth2 = imgWidth;
4488       imgHeight2 = imgHeight;
4489     } else if (rotate == 90) {
4490       writePS("90 rotate\n");
4491       ty = -imgWidth;
4492       imgWidth2 = imgHeight;
4493       imgHeight2 = imgWidth;
4494     } else if (rotate == 180) {
4495       writePS("180 rotate\n");
4496       imgWidth2 = imgWidth;
4497       imgHeight2 = imgHeight;
4498       tx = -imgWidth;
4499       ty = -imgHeight;
4500     } else { // rotate == 270
4501       writePS("270 rotate\n");
4502       tx = -imgHeight;
4503       imgWidth2 = imgHeight;
4504       imgHeight2 = imgWidth;
4505     }
4506     // shrink or expand
4507     if (xScale0 > 0 && yScale0 > 0) {
4508       xScale = xScale0 * userUnit;
4509       yScale = yScale0 * userUnit;
4510     } else if ((globalParams->getPSShrinkLarger() &&
4511 		(width * userUnit > imgWidth2 ||
4512 		 height * userUnit > imgHeight2)) ||
4513 	       (globalParams->getPSExpandSmaller() &&
4514 		(width * userUnit < imgWidth2 &&
4515 		 height * userUnit < imgHeight2))) {
4516       xScale = (double)imgWidth2 / (double)width;
4517       yScale = (double)imgHeight2 / (double)height;
4518       if (yScale < xScale) {
4519 	xScale = yScale;
4520       } else {
4521 	yScale = xScale;
4522       }
4523     } else {
4524       xScale = yScale = userUnit;
4525     }
4526     // deal with odd bounding boxes or clipping
4527     if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
4528       tx -= xScale * clipLLX0;
4529       ty -= yScale * clipLLY0;
4530     } else {
4531       tx -= xScale * x1;
4532       ty -= yScale * y1;
4533     }
4534     // center
4535     if (tx0 >= 0 && ty0 >= 0) {
4536       tx += (rotate == 0 || rotate == 180) ? tx0 : ty0;
4537       ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0;
4538     } else if (globalParams->getPSCenter()) {
4539       if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
4540 	tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
4541 	ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
4542       } else {
4543 	tx += (imgWidth2 - xScale * width) / 2;
4544 	ty += (imgHeight2 - yScale * height) / 2;
4545       }
4546     }
4547     tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY;
4548     ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX;
4549     if (tx != 0 || ty != 0) {
4550       writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty);
4551     }
4552     if (xScale != 1 || yScale != 1) {
4553       writePSFmt("{0:.4f} {1:.4f} scale\n", xScale, yScale);
4554     }
4555     if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
4556       writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n",
4557 		 clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
4558     } else {
4559       writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1);
4560     }
4561 
4562     ++seqPage;
4563     break;
4564 
4565   case psModeEPS:
4566     writePS("pdfStartPage\n");
4567     tx = ty = 0;
4568     rotate = (360 - state->getRotate()) % 360;
4569     if (rotate == 0) {
4570     } else if (rotate == 90) {
4571       writePS("90 rotate\n");
4572       tx = -epsX1;
4573       ty = -epsY2;
4574     } else if (rotate == 180) {
4575       writePS("180 rotate\n");
4576       tx = -(epsX1 + epsX2);
4577       ty = -(epsY1 + epsY2);
4578     } else { // rotate == 270
4579       writePS("270 rotate\n");
4580       tx = -epsX2;
4581       ty = -epsY1;
4582     }
4583     if (tx != 0 || ty != 0) {
4584       writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty);
4585     }
4586     xScale = yScale = 1;
4587     break;
4588 
4589   case psModeForm:
4590     writePS("/PaintProc {\n");
4591     writePS("begin xpdf begin\n");
4592     writePS("pdfStartPage\n");
4593     tx = ty = 0;
4594     xScale = yScale = 1;
4595     rotate = 0;
4596     break;
4597   }
4598 
4599   if (level == psLevel2Gray || level == psLevel3Gray) {
4600     writePS("/DeviceGray setcolorspace\n");
4601   }
4602 
4603   if (customCodeCbk) {
4604     if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum,
4605 			      customCodeCbkData))) {
4606       writePS(s->getCString());
4607       delete s;
4608     }
4609   }
4610 
4611   if (mode == psModePS) {
4612     writePS("%%EndPageSetup\n");
4613   }
4614 
4615   noStateChanges = gFalse;
4616 }
4617 
endPage()4618 void PSOutputDev::endPage() {
4619   if (overlayCbk) {
4620     restoreState(NULL);
4621     (*overlayCbk)(this, overlayCbkData);
4622   }
4623 
4624   if (mode == psModeForm) {
4625     writePS("pdfEndPage\n");
4626     writePS("end end\n");
4627     writePS("} def\n");
4628     writePS("end end\n");
4629   } else {
4630     if (!manualCtrl) {
4631       writePS("showpage\n");
4632     }
4633     writePS("%%PageTrailer\n");
4634     writePageTrailer();
4635     writePS("end\n");
4636   }
4637 }
4638 
saveState(GfxState * state)4639 void PSOutputDev::saveState(GfxState *state) {
4640   // The noStateChanges and saveStack fields are used to implement an
4641   // optimization to reduce gsave/grestore nesting.  The idea is to
4642   // look for sequences like this:
4643   //   q  q AAA Q BBB Q     (where AAA and BBB are sequences of operations)
4644   // and transform them to:
4645   //   q AAA Q q BBB Q
4646   if (noStateChanges) {
4647     // any non-NULL pointer will work here
4648     saveStack->append(this);
4649   } else {
4650     saveStack->append((PSOutputDev *)NULL);
4651     writePS("q\n");
4652     noStateChanges = gTrue;
4653   }
4654 }
4655 
restoreState(GfxState * state)4656 void PSOutputDev::restoreState(GfxState *state) {
4657   if (saveStack->getLength()) {
4658     writePS("Q\n");
4659     if (saveStack->del(saveStack->getLength() - 1)) {
4660       writePS("q\n");
4661       noStateChanges = gTrue;
4662     } else {
4663       noStateChanges = gFalse;
4664     }
4665   }
4666 }
4667 
updateCTM(GfxState * state,double m11,double m12,double m21,double m22,double m31,double m32)4668 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
4669 			    double m21, double m22, double m31, double m32) {
4670   if (m11 == 1 && m12 == 0 && m21 == 0 && m22 == 1 && m31 == 0 && m32 == 0) {
4671     return;
4672   }
4673   if (fabs(m11 * m22 - m12 * m21) < 1e-10) {
4674     // avoid a singular (or close-to-singular) matrix
4675     writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] cm\n", m31, m32);
4676   } else {
4677     writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
4678 	       m11, m12, m21, m22, m31, m32);
4679   }
4680   noStateChanges = gFalse;
4681 }
4682 
updateLineDash(GfxState * state)4683 void PSOutputDev::updateLineDash(GfxState *state) {
4684   double *dash;
4685   double start;
4686   int length, i;
4687 
4688   state->getLineDash(&dash, &length, &start);
4689   writePS("[");
4690   for (i = 0; i < length; ++i) {
4691     writePSFmt("{0:.6g}{1:w}",
4692 	       dash[i] < 0 ? 0 : dash[i],
4693 	       (i == length-1) ? 0 : 1);
4694   }
4695   writePSFmt("] {0:.6g} d\n", start);
4696   noStateChanges = gFalse;
4697 }
4698 
updateFlatness(GfxState * state)4699 void PSOutputDev::updateFlatness(GfxState *state) {
4700   writePSFmt("{0:.4g} i\n", state->getFlatness());
4701   noStateChanges = gFalse;
4702 }
4703 
updateLineJoin(GfxState * state)4704 void PSOutputDev::updateLineJoin(GfxState *state) {
4705   writePSFmt("{0:d} j\n", state->getLineJoin());
4706   noStateChanges = gFalse;
4707 }
4708 
updateLineCap(GfxState * state)4709 void PSOutputDev::updateLineCap(GfxState *state) {
4710   writePSFmt("{0:d} J\n", state->getLineCap());
4711   noStateChanges = gFalse;
4712 }
4713 
updateMiterLimit(GfxState * state)4714 void PSOutputDev::updateMiterLimit(GfxState *state) {
4715   writePSFmt("{0:.4g} M\n", state->getMiterLimit());
4716   noStateChanges = gFalse;
4717 }
4718 
updateLineWidth(GfxState * state)4719 void PSOutputDev::updateLineWidth(GfxState *state) {
4720   writePSFmt("{0:.6g} w\n", state->getLineWidth());
4721   noStateChanges = gFalse;
4722 }
4723 
updateFillColorSpace(GfxState * state)4724 void PSOutputDev::updateFillColorSpace(GfxState *state) {
4725   switch (level) {
4726   case psLevel1:
4727   case psLevel1Sep:
4728     break;
4729   case psLevel2:
4730   case psLevel3:
4731     if (state->getFillColorSpace()->getMode() != csPattern) {
4732       dumpColorSpaceL2(state, state->getFillColorSpace(),
4733 		       gTrue, gFalse, gFalse);
4734       writePS(" cs\n");
4735       noStateChanges = gFalse;
4736     }
4737     break;
4738   case psLevel2Gray:
4739   case psLevel3Gray:
4740   case psLevel2Sep:
4741   case psLevel3Sep:
4742     break;
4743   }
4744 }
4745 
updateStrokeColorSpace(GfxState * state)4746 void PSOutputDev::updateStrokeColorSpace(GfxState *state) {
4747   switch (level) {
4748   case psLevel1:
4749   case psLevel1Sep:
4750     break;
4751   case psLevel2:
4752   case psLevel3:
4753     if (state->getStrokeColorSpace()->getMode() != csPattern) {
4754       dumpColorSpaceL2(state, state->getStrokeColorSpace(),
4755 		       gTrue, gFalse, gFalse);
4756       writePS(" CS\n");
4757       noStateChanges = gFalse;
4758     }
4759     break;
4760   case psLevel2Gray:
4761   case psLevel3Gray:
4762   case psLevel2Sep:
4763   case psLevel3Sep:
4764     break;
4765   }
4766 }
4767 
updateFillColor(GfxState * state)4768 void PSOutputDev::updateFillColor(GfxState *state) {
4769   GfxColor color;
4770   GfxColor *colorPtr;
4771   GfxGray gray;
4772   GfxCMYK cmyk;
4773   GfxSeparationColorSpace *sepCS;
4774   double c, m, y, k;
4775   int i;
4776 
4777   switch (level) {
4778   case psLevel1:
4779   case psLevel2Gray:
4780   case psLevel3Gray:
4781     state->getFillGray(&gray);
4782     writePSFmt("{0:.4g} g\n", colToDbl(gray));
4783     break;
4784   case psLevel1Sep:
4785     state->getFillCMYK(&cmyk);
4786     c = colToDbl(cmyk.c);
4787     m = colToDbl(cmyk.m);
4788     y = colToDbl(cmyk.y);
4789     k = colToDbl(cmyk.k);
4790     writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
4791     addProcessColor(c, m, y, k);
4792     break;
4793   case psLevel2:
4794   case psLevel3:
4795     if (state->getFillColorSpace()->getMode() != csPattern) {
4796       colorPtr = state->getFillColor();
4797       writePS("[");
4798       for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
4799 	if (i > 0) {
4800 	  writePS(" ");
4801 	}
4802 	writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
4803       }
4804       writePS("] sc\n");
4805     }
4806     break;
4807   case psLevel2Sep:
4808   case psLevel3Sep:
4809     if (state->getFillColorSpace()->getMode() == csSeparation) {
4810       sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
4811       color.c[0] = gfxColorComp1;
4812       sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
4813       writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n",
4814 		 colToDbl(state->getFillColor()->c[0]),
4815 		 colToDbl(cmyk.c), colToDbl(cmyk.m),
4816 		 colToDbl(cmyk.y), colToDbl(cmyk.k),
4817 		 sepCS->getName());
4818       addCustomColor(state, sepCS);
4819     } else {
4820       state->getFillCMYK(&cmyk);
4821       c = colToDbl(cmyk.c);
4822       m = colToDbl(cmyk.m);
4823       y = colToDbl(cmyk.y);
4824       k = colToDbl(cmyk.k);
4825       writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
4826       addProcessColor(c, m, y, k);
4827     }
4828     break;
4829   }
4830   t3Cacheable = gFalse;
4831   noStateChanges = gFalse;
4832 }
4833 
updateStrokeColor(GfxState * state)4834 void PSOutputDev::updateStrokeColor(GfxState *state) {
4835   GfxColor color;
4836   GfxColor *colorPtr;
4837   GfxGray gray;
4838   GfxCMYK cmyk;
4839   GfxSeparationColorSpace *sepCS;
4840   double c, m, y, k;
4841   int i;
4842 
4843   switch (level) {
4844   case psLevel1:
4845   case psLevel2Gray:
4846   case psLevel3Gray:
4847     state->getStrokeGray(&gray);
4848     writePSFmt("{0:.4g} G\n", colToDbl(gray));
4849     break;
4850   case psLevel1Sep:
4851     state->getStrokeCMYK(&cmyk);
4852     c = colToDbl(cmyk.c);
4853     m = colToDbl(cmyk.m);
4854     y = colToDbl(cmyk.y);
4855     k = colToDbl(cmyk.k);
4856     writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
4857     addProcessColor(c, m, y, k);
4858     break;
4859   case psLevel2:
4860   case psLevel3:
4861     if (state->getStrokeColorSpace()->getMode() != csPattern) {
4862       colorPtr = state->getStrokeColor();
4863       writePS("[");
4864       for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
4865 	if (i > 0) {
4866 	  writePS(" ");
4867 	}
4868 	writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
4869       }
4870       writePS("] SC\n");
4871     }
4872     break;
4873   case psLevel2Sep:
4874   case psLevel3Sep:
4875     if (state->getStrokeColorSpace()->getMode() == csSeparation) {
4876       sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
4877       color.c[0] = gfxColorComp1;
4878       sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
4879       writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n",
4880 		 colToDbl(state->getStrokeColor()->c[0]),
4881 		 colToDbl(cmyk.c), colToDbl(cmyk.m),
4882 		 colToDbl(cmyk.y), colToDbl(cmyk.k),
4883 		 sepCS->getName());
4884       addCustomColor(state, sepCS);
4885     } else {
4886       state->getStrokeCMYK(&cmyk);
4887       c = colToDbl(cmyk.c);
4888       m = colToDbl(cmyk.m);
4889       y = colToDbl(cmyk.y);
4890       k = colToDbl(cmyk.k);
4891       writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
4892       addProcessColor(c, m, y, k);
4893     }
4894     break;
4895   }
4896   t3Cacheable = gFalse;
4897   noStateChanges = gFalse;
4898 }
4899 
addProcessColor(double c,double m,double y,double k)4900 void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
4901   if (c > 0) {
4902     processColors |= psProcessCyan;
4903   }
4904   if (m > 0) {
4905     processColors |= psProcessMagenta;
4906   }
4907   if (y > 0) {
4908     processColors |= psProcessYellow;
4909   }
4910   if (k > 0) {
4911     processColors |= psProcessBlack;
4912   }
4913 }
4914 
addCustomColor(GfxState * state,GfxSeparationColorSpace * sepCS)4915 void PSOutputDev::addCustomColor(GfxState *state,
4916 				 GfxSeparationColorSpace *sepCS) {
4917   PSOutCustomColor *cc;
4918   GfxColor color;
4919   GfxCMYK cmyk;
4920 
4921   for (cc = customColors; cc; cc = cc->next) {
4922     if (!cc->name->cmp(sepCS->getName())) {
4923       return;
4924     }
4925   }
4926   color.c[0] = gfxColorComp1;
4927   sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
4928   cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
4929 			    colToDbl(cmyk.y), colToDbl(cmyk.k),
4930 			    sepCS->getName()->copy());
4931   cc->next = customColors;
4932   customColors = cc;
4933 }
4934 
addCustomColors(GfxState * state,GfxDeviceNColorSpace * devnCS)4935 void PSOutputDev::addCustomColors(GfxState *state,
4936 				  GfxDeviceNColorSpace *devnCS) {
4937   PSOutCustomColor *cc;
4938   GfxColor color;
4939   GfxCMYK cmyk;
4940   int i;
4941 
4942   for (i = 0; i < devnCS->getNComps(); ++i) {
4943     color.c[i] = 0;
4944   }
4945   for (i = 0; i < devnCS->getNComps(); ++i) {
4946     for (cc = customColors; cc; cc = cc->next) {
4947       if (!cc->name->cmp(devnCS->getColorantName(i))) {
4948 	break;
4949       }
4950     }
4951     if (cc) {
4952       continue;
4953     }
4954     color.c[i] = gfxColorComp1;
4955     devnCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
4956     color.c[i] = 0;
4957     cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
4958 			      colToDbl(cmyk.y), colToDbl(cmyk.k),
4959 			      devnCS->getColorantName(i)->copy());
4960     cc->next = customColors;
4961     customColors = cc;
4962   }
4963 }
4964 
updateFillOverprint(GfxState * state)4965 void PSOutputDev::updateFillOverprint(GfxState *state) {
4966   if (level == psLevel2 || level == psLevel2Sep ||
4967       level == psLevel3 || level == psLevel3Sep) {
4968     writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false");
4969     noStateChanges = gFalse;
4970   }
4971 }
4972 
updateStrokeOverprint(GfxState * state)4973 void PSOutputDev::updateStrokeOverprint(GfxState *state) {
4974   if (level == psLevel2 || level == psLevel2Sep ||
4975       level == psLevel3 || level == psLevel3Sep) {
4976     writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false");
4977     noStateChanges = gFalse;
4978   }
4979 }
4980 
updateOverprintMode(GfxState * state)4981 void PSOutputDev::updateOverprintMode(GfxState *state) {
4982   if (level == psLevel3 || level == psLevel3Sep) {
4983     writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false");
4984     noStateChanges = gFalse;
4985   }
4986 }
4987 
updateTransfer(GfxState * state)4988 void PSOutputDev::updateTransfer(GfxState *state) {
4989   Function **funcs;
4990   int i;
4991 
4992   funcs = state->getTransfer();
4993   if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) {
4994     if (level == psLevel2 || level == psLevel2Sep ||
4995 	level == psLevel3 || level == psLevel3Sep) {
4996       for (i = 0; i < 4; ++i) {
4997 	cvtFunction(funcs[i]);
4998       }
4999       writePS("setcolortransfer\n");
5000     } else {
5001       cvtFunction(funcs[3]);
5002       writePS("settransfer\n");
5003     }
5004   } else if (funcs[0]) {
5005     cvtFunction(funcs[0]);
5006     writePS("settransfer\n");
5007   } else {
5008     writePS("{} settransfer\n");
5009   }
5010   noStateChanges = gFalse;
5011 }
5012 
updateFont(GfxState * state)5013 void PSOutputDev::updateFont(GfxState *state) {
5014   if (state->getFont()) {
5015     if (state->getFont()->getTag() &&
5016 	!state->getFont()->getTag()->cmp("xpdf_default_font")) {
5017       writePSFmt("/xpdf_default_font {0:.6g} Tf\n",
5018 		 fabs(state->getFontSize()) < 0.0001 ? 0.0001
5019 		                                     : state->getFontSize());
5020     } else {
5021       writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n",
5022 		 state->getFont()->getID()->num, state->getFont()->getID()->gen,
5023 		 fabs(state->getFontSize()) < 0.0001 ? 0.0001
5024 		                                     : state->getFontSize());
5025     }
5026     noStateChanges = gFalse;
5027   }
5028 }
5029 
updateTextMat(GfxState * state)5030 void PSOutputDev::updateTextMat(GfxState *state) {
5031   double *mat;
5032 
5033   mat = state->getTextMat();
5034   if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 1e-10) {
5035     // avoid a singular (or close-to-singular) matrix
5036     writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]);
5037   } else {
5038     writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n",
5039 	       mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
5040   }
5041   noStateChanges = gFalse;
5042 }
5043 
updateCharSpace(GfxState * state)5044 void PSOutputDev::updateCharSpace(GfxState *state) {
5045   writePSFmt("{0:.6g} Tc\n", state->getCharSpace());
5046   noStateChanges = gFalse;
5047 }
5048 
updateRender(GfxState * state)5049 void PSOutputDev::updateRender(GfxState *state) {
5050   int rm;
5051 
5052   rm = state->getRender();
5053   writePSFmt("{0:d} Tr\n", rm);
5054   rm &= 3;
5055   if (rm != 0 && rm != 3) {
5056     t3Cacheable = gFalse;
5057   }
5058   noStateChanges = gFalse;
5059 }
5060 
updateRise(GfxState * state)5061 void PSOutputDev::updateRise(GfxState *state) {
5062   writePSFmt("{0:.6g} Ts\n", state->getRise());
5063   noStateChanges = gFalse;
5064 }
5065 
updateWordSpace(GfxState * state)5066 void PSOutputDev::updateWordSpace(GfxState *state) {
5067   writePSFmt("{0:.6g} Tw\n", state->getWordSpace());
5068   noStateChanges = gFalse;
5069 }
5070 
updateHorizScaling(GfxState * state)5071 void PSOutputDev::updateHorizScaling(GfxState *state) {
5072   double h;
5073 
5074   h = state->getHorizScaling();
5075   if (fabs(h) < 0.01) {
5076     h = 0.01;
5077   }
5078   writePSFmt("{0:.6g} Tz\n", h);
5079   noStateChanges = gFalse;
5080 }
5081 
updateTextPos(GfxState * state)5082 void PSOutputDev::updateTextPos(GfxState *state) {
5083   writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY());
5084   noStateChanges = gFalse;
5085 }
5086 
updateTextShift(GfxState * state,double shift)5087 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
5088   if (state->getFont()->getWMode()) {
5089     writePSFmt("{0:.6g} TJmV\n", shift);
5090   } else {
5091     writePSFmt("{0:.6g} TJm\n", shift);
5092   }
5093   noStateChanges = gFalse;
5094 }
5095 
saveTextPos(GfxState * state)5096 void PSOutputDev::saveTextPos(GfxState *state) {
5097   writePS("currentpoint\n");
5098   noStateChanges = gFalse;
5099 }
5100 
restoreTextPos(GfxState * state)5101 void PSOutputDev::restoreTextPos(GfxState *state) {
5102   writePS("m\n");
5103   noStateChanges = gFalse;
5104 }
5105 
stroke(GfxState * state)5106 void PSOutputDev::stroke(GfxState *state) {
5107   doPath(state->getPath());
5108   if (inType3Char && t3FillColorOnly) {
5109     // if we're constructing a cacheable Type 3 glyph, we need to do
5110     // everything in the fill color
5111     writePS("Sf\n");
5112   } else {
5113     writePS("S\n");
5114   }
5115   noStateChanges = gFalse;
5116 }
5117 
fill(GfxState * state)5118 void PSOutputDev::fill(GfxState *state) {
5119   doPath(state->getPath());
5120   writePS("f\n");
5121   noStateChanges = gFalse;
5122 }
5123 
eoFill(GfxState * state)5124 void PSOutputDev::eoFill(GfxState *state) {
5125   doPath(state->getPath());
5126   writePS("f*\n");
5127   noStateChanges = gFalse;
5128 }
5129 
tilingPatternFill(GfxState * state,Gfx * gfx,Object * strRef,int paintType,int tilingType,Dict * resDict,double * mat,double * bbox,int x0,int y0,int x1,int y1,double xStep,double yStep)5130 void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
5131 				    int paintType, int tilingType,
5132 				    Dict *resDict,
5133 				    double *mat, double *bbox,
5134 				    int x0, int y0, int x1, int y1,
5135 				    double xStep, double yStep) {
5136   if (level <= psLevel1Sep) {
5137     tilingPatternFillL1(state, gfx, strRef, paintType, tilingType,
5138 			resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
5139   } else {
5140     tilingPatternFillL2(state, gfx, strRef, paintType, tilingType,
5141 			resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
5142   }
5143 }
5144 
tilingPatternFillL1(GfxState * state,Gfx * gfx,Object * strRef,int paintType,int tilingType,Dict * resDict,double * mat,double * bbox,int x0,int y0,int x1,int y1,double xStep,double yStep)5145 void PSOutputDev::tilingPatternFillL1(GfxState *state, Gfx *gfx,
5146 				      Object *strRef,
5147 				      int paintType, int tilingType,
5148 				      Dict *resDict,
5149 				      double *mat, double *bbox,
5150 				      int x0, int y0, int x1, int y1,
5151 				      double xStep, double yStep) {
5152   PDFRectangle box;
5153   Gfx *gfx2;
5154 
5155   // define a Type 3 font
5156   writePS("8 dict begin\n");
5157   writePS("/FontType 3 def\n");
5158   writePS("/FontMatrix [1 0 0 1 0 0] def\n");
5159   writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n",
5160 	     bbox[0], bbox[1], bbox[2], bbox[3]);
5161   writePS("/Encoding 256 array def\n");
5162   writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
5163   writePS("  Encoding 120 /x put\n");
5164   writePS("/BuildGlyph {\n");
5165   writePS("  exch /CharProcs get exch\n");
5166   writePS("  2 copy known not { pop /.notdef } if\n");
5167   writePS("  get exec\n");
5168   writePS("} bind def\n");
5169   writePS("/BuildChar {\n");
5170   writePS("  1 index /Encoding get exch get\n");
5171   writePS("  1 index /BuildGlyph get exec\n");
5172   writePS("} bind def\n");
5173   writePS("/CharProcs 1 dict def\n");
5174   writePS("CharProcs begin\n");
5175   box.x1 = bbox[0];
5176   box.y1 = bbox[1];
5177   box.x2 = bbox[2];
5178   box.y2 = bbox[3];
5179   gfx2 = new Gfx(doc, this, resDict, &box, NULL);
5180   gfx2->takeContentStreamStack(gfx);
5181   writePS("/x {\n");
5182   if (paintType == 2) {
5183     writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n",
5184 	       xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
5185     t3FillColorOnly = gTrue;
5186   } else {
5187     if (x1 - 1 <= x0) {
5188       writePS("1 0 setcharwidth\n");
5189     } else {
5190       writePSFmt("{0:.6g} 0 setcharwidth\n", xStep);
5191     }
5192     t3FillColorOnly = gFalse;
5193   }
5194   inType3Char = gTrue;
5195   ++numTilingPatterns;
5196   gfx2->display(strRef);
5197   --numTilingPatterns;
5198   inType3Char = gFalse;
5199   writePS("} def\n");
5200   delete gfx2;
5201   writePS("end\n");
5202   writePS("currentdict end\n");
5203   writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns);
5204 
5205   // draw the tiles
5206   writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns);
5207   writePS("fCol\n");
5208   writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n",
5209 	     mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
5210   writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n",
5211 	     y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1);
5212   writePS("grestore\n");
5213   noStateChanges = gFalse;
5214 }
5215 
tilingPatternFillL2(GfxState * state,Gfx * gfx,Object * strRef,int paintType,int tilingType,Dict * resDict,double * mat,double * bbox,int x0,int y0,int x1,int y1,double xStep,double yStep)5216 void PSOutputDev::tilingPatternFillL2(GfxState *state, Gfx *gfx,
5217 				      Object *strRef,
5218 				      int paintType, int tilingType,
5219 				      Dict *resDict,
5220 				      double *mat, double *bbox,
5221 				      int x0, int y0, int x1, int y1,
5222 				      double xStep, double yStep) {
5223   PDFRectangle box;
5224   Gfx *gfx2;
5225 
5226   // switch to pattern space
5227   writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n",
5228 	     mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
5229 
5230   // define a pattern
5231   writePSFmt("/xpdfTile{0:d}\n", numTilingPatterns);
5232   writePS("<<\n");
5233   writePS("  /PatternType 1\n");
5234   writePSFmt("  /PaintType {0:d}\n", paintType);
5235   writePSFmt("  /TilingType {0:d}\n", tilingType);
5236   writePSFmt("  /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n",
5237 	     bbox[0], bbox[1], bbox[2], bbox[3]);
5238   writePSFmt("  /XStep {0:.6g}\n", xStep);
5239   writePSFmt("  /YStep {0:.6g}\n", yStep);
5240   writePS("  /PaintProc {\n");
5241   writePS("    pop\n");
5242   box.x1 = bbox[0];
5243   box.y1 = bbox[1];
5244   box.x2 = bbox[2];
5245   box.y2 = bbox[3];
5246   gfx2 = new Gfx(doc, this, resDict, &box, NULL);
5247   gfx2->takeContentStreamStack(gfx);
5248   t3FillColorOnly = paintType == 2;
5249   inType3Char = gTrue;
5250   ++numTilingPatterns;
5251   gfx2->display(strRef);
5252   --numTilingPatterns;
5253   inType3Char = gFalse;
5254   delete gfx2;
5255   writePS("  }\n");
5256   writePS(">> matrix makepattern def\n");
5257 
5258   // set the pattern
5259   if (paintType == 2) {
5260     writePS("fCol\n");
5261     writePS("currentcolor ");
5262   }
5263   writePSFmt("xpdfTile{0:d} setpattern\n", numTilingPatterns);
5264 
5265   // fill with the pattern
5266   writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} rectfill\n",
5267 	     x0 * xStep + bbox[0],
5268 	     y0 * yStep + bbox[1],
5269 	     (x1 - x0) * xStep + bbox[2],
5270 	     (y1 - y0) * yStep + bbox[3]);
5271 
5272   writePS("grestore\n");
5273   noStateChanges = gFalse;
5274 }
5275 
shadedFill(GfxState * state,GfxShading * shading)5276 GBool PSOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
5277   if (level != psLevel2 &&
5278       level != psLevel2Sep &&
5279       level != psLevel3 &&
5280       level != psLevel3Sep) {
5281     return gFalse;
5282   }
5283 
5284   switch (shading->getType()) {
5285   case 1:
5286     functionShadedFill(state, (GfxFunctionShading *)shading);
5287     return gTrue;
5288   case 2:
5289     axialShadedFill(state, (GfxAxialShading *)shading);
5290     return gTrue;
5291   case 3:
5292     radialShadedFill(state, (GfxRadialShading *)shading);
5293     return gTrue;
5294   default:
5295     return gFalse;
5296   }
5297 }
5298 
functionShadedFill(GfxState * state,GfxFunctionShading * shading)5299 GBool PSOutputDev::functionShadedFill(GfxState *state,
5300 				      GfxFunctionShading *shading) {
5301   double x0, y0, x1, y1;
5302   double *mat;
5303   int i;
5304 
5305   if (level == psLevel2Sep || level == psLevel3Sep) {
5306     if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
5307       return gFalse;
5308     }
5309     processColors |= psProcessCMYK;
5310   }
5311 
5312   shading->getDomain(&x0, &y0, &x1, &y1);
5313   mat = shading->getMatrix();
5314   writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n",
5315 	     mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
5316   writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
5317   if (shading->getNFuncs() == 1) {
5318     writePS("/func ");
5319     cvtFunction(shading->getFunc(0));
5320     writePS("def\n");
5321   } else {
5322     writePS("/func {\n");
5323     for (i = 0; i < shading->getNFuncs(); ++i) {
5324       if (i < shading->getNFuncs() - 1) {
5325 	writePS("2 copy\n");
5326       }
5327       cvtFunction(shading->getFunc(i));
5328       writePS("exec\n");
5329       if (i < shading->getNFuncs() - 1) {
5330 	writePS("3 1 roll\n");
5331       }
5332     }
5333     writePS("} def\n");
5334   }
5335   writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1);
5336 
5337   noStateChanges = gFalse;
5338   return gTrue;
5339 }
5340 
axialShadedFill(GfxState * state,GfxAxialShading * shading)5341 GBool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) {
5342   double xMin, yMin, xMax, yMax;
5343   double x0, y0, x1, y1, dx, dy, mul;
5344   double tMin, tMax, t, t0, t1;
5345   int i;
5346 
5347   if (level == psLevel2Sep || level == psLevel3Sep) {
5348     if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
5349       return gFalse;
5350     }
5351     processColors |= psProcessCMYK;
5352   }
5353 
5354   // get the clip region bbox
5355   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
5356 
5357   // compute min and max t values, based on the four corners of the
5358   // clip region bbox
5359   shading->getCoords(&x0, &y0, &x1, &y1);
5360   dx = x1 - x0;
5361   dy = y1 - y0;
5362   if (fabs(dx) < 0.01 && fabs(dy) < 0.01) {
5363     return gTrue;
5364   } else {
5365     mul = 1 / (dx * dx + dy * dy);
5366     tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
5367     t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
5368     if (t < tMin) {
5369       tMin = t;
5370     } else if (t > tMax) {
5371       tMax = t;
5372     }
5373     t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
5374     if (t < tMin) {
5375       tMin = t;
5376     } else if (t > tMax) {
5377       tMax = t;
5378     }
5379     t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
5380     if (t < tMin) {
5381       tMin = t;
5382     } else if (t > tMax) {
5383       tMax = t;
5384     }
5385     if (tMin < 0 && !shading->getExtend0()) {
5386       tMin = 0;
5387     }
5388     if (tMax > 1 && !shading->getExtend1()) {
5389       tMax = 1;
5390     }
5391   }
5392 
5393   // get the function domain
5394   t0 = shading->getDomain0();
5395   t1 = shading->getDomain1();
5396 
5397   // generate the PS code
5398   writePSFmt("/t0 {0:.6g} def\n", t0);
5399   writePSFmt("/t1 {0:.6g} def\n", t1);
5400   writePSFmt("/dt {0:.6g} def\n", t1 - t0);
5401   writePSFmt("/x0 {0:.6g} def\n", x0);
5402   writePSFmt("/y0 {0:.6g} def\n", y0);
5403   writePSFmt("/dx {0:.6g} def\n", x1 - x0);
5404   writePSFmt("/x1 {0:.6g} def\n", x1);
5405   writePSFmt("/y1 {0:.6g} def\n", y1);
5406   writePSFmt("/dy {0:.6g} def\n", y1 - y0);
5407   writePSFmt("/xMin {0:.6g} def\n", xMin);
5408   writePSFmt("/yMin {0:.6g} def\n", yMin);
5409   writePSFmt("/xMax {0:.6g} def\n", xMax);
5410   writePSFmt("/yMax {0:.6g} def\n", yMax);
5411   writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
5412   if (shading->getNFuncs() == 1) {
5413     writePS("/func ");
5414     cvtFunction(shading->getFunc(0));
5415     writePS("def\n");
5416   } else {
5417     writePS("/func {\n");
5418     for (i = 0; i < shading->getNFuncs(); ++i) {
5419       if (i < shading->getNFuncs() - 1) {
5420 	writePS("dup\n");
5421       }
5422       cvtFunction(shading->getFunc(i));
5423       writePS("exec\n");
5424       if (i < shading->getNFuncs() - 1) {
5425 	writePS("exch\n");
5426       }
5427     }
5428     writePS("} def\n");
5429   }
5430   writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax);
5431 
5432   noStateChanges = gFalse;
5433   return gTrue;
5434 }
5435 
radialShadedFill(GfxState * state,GfxRadialShading * shading)5436 GBool PSOutputDev::radialShadedFill(GfxState *state,
5437 				    GfxRadialShading *shading) {
5438   double xMin, yMin, xMax, yMax;
5439   double x0, y0, r0, x1, y1, r1, t0, t1;
5440   double xa, ya, ra;
5441   double sMin, sMax, h, ta;
5442   double sLeft, sRight, sTop, sBottom, sZero, sDiag;
5443   GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
5444   GBool haveSMin, haveSMax;
5445   double theta, alpha, a1, a2;
5446   GBool enclosed;
5447   int i;
5448 
5449   if (level == psLevel2Sep || level == psLevel3Sep) {
5450     if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
5451       return gFalse;
5452     }
5453     processColors |= psProcessCMYK;
5454   }
5455 
5456   // get the shading info
5457   shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
5458   t0 = shading->getDomain0();
5459   t1 = shading->getDomain1();
5460 
5461   // Compute the point at which r(s) = 0; check for the enclosed
5462   // circles case; and compute the angles for the tangent lines.
5463   h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
5464   if (h == 0) {
5465     enclosed = gTrue;
5466     theta = 0; // make gcc happy
5467   } else if (r1 - r0 == 0) {
5468     enclosed = gFalse;
5469     theta = 0;
5470   } else if (fabs(r1 - r0) >= h) {
5471     enclosed = gTrue;
5472     theta = 0; // make gcc happy
5473   } else {
5474     enclosed = gFalse;
5475     theta = asin((r1 - r0) / h);
5476   }
5477   if (enclosed) {
5478     a1 = 0;
5479     a2 = 360;
5480   } else {
5481     alpha = atan2(y1 - y0, x1 - x0);
5482     a1 = (180 / M_PI) * (alpha + theta) + 90;
5483     a2 = (180 / M_PI) * (alpha - theta) - 90;
5484     while (a2 < a1) {
5485       a2 += 360;
5486     }
5487   }
5488 
5489   // compute the (possibly extended) s range
5490   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
5491   if (enclosed) {
5492     sMin = 0;
5493     sMax = 1;
5494   } else {
5495     // solve x(sLeft) + r(sLeft) = xMin
5496     if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) {
5497       sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
5498     } else {
5499       sLeft = 0; // make gcc happy
5500     }
5501     // solve x(sRight) - r(sRight) = xMax
5502     if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) {
5503       sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
5504     } else {
5505       sRight = 0; // make gcc happy
5506     }
5507     // solve y(sBottom) + r(sBottom) = yMin
5508     if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) {
5509       sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
5510     } else {
5511       sBottom = 0; // make gcc happy
5512     }
5513     // solve y(sTop) - r(sTop) = yMax
5514     if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) {
5515       sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
5516     } else {
5517       sTop = 0; // make gcc happy
5518     }
5519     // solve r(sZero) = 0
5520     if ((haveSZero = fabs(r1 - r0) > 0.000001)) {
5521       sZero = -r0 / (r1 - r0);
5522     } else {
5523       sZero = 0; // make gcc happy
5524     }
5525     // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2)
5526     if (haveSZero) {
5527       sDiag = (sqrt((xMax - xMin) * (xMax - xMin) +
5528 		    (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
5529     } else {
5530       sDiag = 0; // make gcc happy
5531     }
5532     // compute sMin
5533     if (shading->getExtend0()) {
5534       sMin = 0;
5535       haveSMin = gFalse;
5536       if (x0 < x1 && haveSLeft && sLeft < 0) {
5537 	sMin = sLeft;
5538 	haveSMin = gTrue;
5539       } else if (x0 > x1 && haveSRight && sRight < 0) {
5540 	sMin = sRight;
5541 	haveSMin = gTrue;
5542       }
5543       if (y0 < y1 && haveSBottom && sBottom < 0) {
5544 	if (!haveSMin || sBottom > sMin) {
5545 	  sMin = sBottom;
5546 	  haveSMin = gTrue;
5547 	}
5548       } else if (y0 > y1 && haveSTop && sTop < 0) {
5549 	if (!haveSMin || sTop > sMin) {
5550 	  sMin = sTop;
5551 	  haveSMin = gTrue;
5552 	}
5553       }
5554       if (haveSZero && sZero < 0) {
5555 	if (!haveSMin || sZero > sMin) {
5556 	  sMin = sZero;
5557 	}
5558       }
5559     } else {
5560       sMin = 0;
5561     }
5562     // compute sMax
5563     if (shading->getExtend1()) {
5564       sMax = 1;
5565       haveSMax = gFalse;
5566       if (x1 < x0 && haveSLeft && sLeft > 1) {
5567 	sMax = sLeft;
5568 	haveSMax = gTrue;
5569       } else if (x1 > x0 && haveSRight && sRight > 1) {
5570 	sMax = sRight;
5571 	haveSMax = gTrue;
5572       }
5573       if (y1 < y0 && haveSBottom && sBottom > 1) {
5574 	if (!haveSMax || sBottom < sMax) {
5575 	  sMax = sBottom;
5576 	  haveSMax = gTrue;
5577 	}
5578       } else if (y1 > y0 && haveSTop && sTop > 1) {
5579 	if (!haveSMax || sTop < sMax) {
5580 	  sMax = sTop;
5581 	  haveSMax = gTrue;
5582 	}
5583       }
5584       if (haveSZero && sDiag > 1) {
5585 	if (!haveSMax || sDiag < sMax) {
5586 	  sMax = sDiag;
5587 	}
5588       }
5589     } else {
5590       sMax = 1;
5591     }
5592   }
5593 
5594   // generate the PS code
5595   writePSFmt("/x0 {0:.6g} def\n", x0);
5596   writePSFmt("/x1 {0:.6g} def\n", x1);
5597   writePSFmt("/dx {0:.6g} def\n", x1 - x0);
5598   writePSFmt("/y0 {0:.6g} def\n", y0);
5599   writePSFmt("/y1 {0:.6g} def\n", y1);
5600   writePSFmt("/dy {0:.6g} def\n", y1 - y0);
5601   writePSFmt("/r0 {0:.6g} def\n", r0);
5602   writePSFmt("/r1 {0:.6g} def\n", r1);
5603   writePSFmt("/dr {0:.6g} def\n", r1 - r0);
5604   writePSFmt("/t0 {0:.6g} def\n", t0);
5605   writePSFmt("/t1 {0:.6g} def\n", t1);
5606   writePSFmt("/dt {0:.6g} def\n", t1 - t0);
5607   writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
5608   writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false");
5609   writePSFmt("/a1 {0:.6g} def\n", a1);
5610   writePSFmt("/a2 {0:.6g} def\n", a2);
5611   if (shading->getNFuncs() == 1) {
5612     writePS("/func ");
5613     cvtFunction(shading->getFunc(0));
5614     writePS("def\n");
5615   } else {
5616     writePS("/func {\n");
5617     for (i = 0; i < shading->getNFuncs(); ++i) {
5618       if (i < shading->getNFuncs() - 1) {
5619 	writePS("dup\n");
5620       }
5621       cvtFunction(shading->getFunc(i));
5622       writePS("exec\n");
5623       if (i < shading->getNFuncs() - 1) {
5624 	writePS("exch\n");
5625       }
5626     }
5627     writePS("} def\n");
5628   }
5629   writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax);
5630 
5631   // extend the 'enclosed' case
5632   if (enclosed) {
5633     // extend the smaller circle
5634     if ((shading->getExtend0() && r0 <= r1) ||
5635 	(shading->getExtend1() && r1 < r0)) {
5636       if (r0 <= r1) {
5637 	ta = t0;
5638 	ra = r0;
5639 	xa = x0;
5640 	ya = y0;
5641       } else {
5642 	ta = t1;
5643 	ra = r1;
5644 	xa = x1;
5645 	ya = y1;
5646       }
5647       if (level == psLevel2Sep || level == psLevel3Sep) {
5648 	writePSFmt("{0:.6g} radialCol aload pop k\n", ta);
5649       } else {
5650 	writePSFmt("{0:.6g} radialCol sc\n", ta);
5651       }
5652       writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra);
5653     }
5654 
5655     // extend the larger circle
5656     if ((shading->getExtend0() && r0 > r1) ||
5657 	(shading->getExtend1() && r1 >= r0)) {
5658       if (r0 > r1) {
5659 	ta = t0;
5660 	ra = r0;
5661 	xa = x0;
5662 	ya = y0;
5663       } else {
5664 	ta = t1;
5665 	ra = r1;
5666 	xa = x1;
5667 	ya = y1;
5668       }
5669       if (level == psLevel2Sep || level == psLevel3Sep) {
5670 	writePSFmt("{0:.6g} radialCol aload pop k\n", ta);
5671       } else {
5672 	writePSFmt("{0:.6g} radialCol sc\n", ta);
5673       }
5674       writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra);
5675       writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n",
5676 		 xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin);
5677     }
5678   }
5679 
5680   noStateChanges = gFalse;
5681   return gTrue;
5682 }
5683 
clip(GfxState * state)5684 void PSOutputDev::clip(GfxState *state) {
5685   doPath(state->getPath());
5686   writePS("W\n");
5687   noStateChanges = gFalse;
5688 }
5689 
eoClip(GfxState * state)5690 void PSOutputDev::eoClip(GfxState *state) {
5691   doPath(state->getPath());
5692   writePS("W*\n");
5693   noStateChanges = gFalse;
5694 }
5695 
clipToStrokePath(GfxState * state)5696 void PSOutputDev::clipToStrokePath(GfxState *state) {
5697   doPath(state->getPath());
5698   writePS("Ws\n");
5699   noStateChanges = gFalse;
5700 }
5701 
doPath(GfxPath * path)5702 void PSOutputDev::doPath(GfxPath *path) {
5703   GfxSubpath *subpath;
5704   double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
5705   int n, m, i, j;
5706 
5707   n = path->getNumSubpaths();
5708 
5709   if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
5710     subpath = path->getSubpath(0);
5711     x0 = subpath->getX(0);
5712     y0 = subpath->getY(0);
5713     x4 = subpath->getX(4);
5714     y4 = subpath->getY(4);
5715     if (x4 == x0 && y4 == y0) {
5716       x1 = subpath->getX(1);
5717       y1 = subpath->getY(1);
5718       x2 = subpath->getX(2);
5719       y2 = subpath->getY(2);
5720       x3 = subpath->getX(3);
5721       y3 = subpath->getY(3);
5722       if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
5723 	writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
5724 		   x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
5725 		   fabs(x2 - x0), fabs(y1 - y0));
5726 	return;
5727       } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
5728 	writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
5729 		   x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
5730 		   fabs(x1 - x0), fabs(y2 - y0));
5731 	return;
5732       }
5733     }
5734   }
5735 
5736   for (i = 0; i < n; ++i) {
5737     subpath = path->getSubpath(i);
5738     m = subpath->getNumPoints();
5739     writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0));
5740     j = 1;
5741     while (j < m) {
5742       if (subpath->getCurve(j)) {
5743 	writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n",
5744 		   subpath->getX(j), subpath->getY(j),
5745 		   subpath->getX(j+1), subpath->getY(j+1),
5746 		   subpath->getX(j+2), subpath->getY(j+2));
5747 	j += 3;
5748       } else {
5749 	writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j));
5750 	++j;
5751       }
5752     }
5753     if (subpath->isClosed()) {
5754       writePS("h\n");
5755     }
5756   }
5757 }
5758 
drawString(GfxState * state,GString * s)5759 void PSOutputDev::drawString(GfxState *state, GString *s) {
5760   GfxFont *font;
5761   int wMode;
5762   int *codeToGID;
5763   GString *s2;
5764   double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0;
5765   char *p;
5766   PSFontInfo *fi;
5767   UnicodeMap *uMap;
5768   CharCode code;
5769   Unicode u[8];
5770   char buf[8];
5771   double *dxdy;
5772   int dxdySize, len, nChars, uLen, n, m, i, j;
5773 
5774   // check for invisible text -- this is used by Acrobat Capture
5775   if (state->getRender() == 3) {
5776     return;
5777   }
5778 
5779   // ignore empty strings
5780   if (s->getLength() == 0) {
5781     return;
5782   }
5783 
5784   // get the font
5785   if (!(font = state->getFont())) {
5786     return;
5787   }
5788   wMode = font->getWMode();
5789 
5790   fi = NULL;
5791   for (i = 0; i < fontInfo->getLength(); ++i) {
5792     fi = (PSFontInfo *)fontInfo->get(i);
5793     if (fi->fontID.num == font->getID()->num &&
5794 	fi->fontID.gen == font->getID()->gen) {
5795       break;
5796     }
5797     fi = NULL;
5798   }
5799 
5800   // check for a subtitute 16-bit font
5801   uMap = NULL;
5802   codeToGID = NULL;
5803   if (font->isCIDFont()) {
5804     if (!(fi && fi->ff)) {
5805       // font substitution failed, so don't output any text
5806       return;
5807     }
5808     if (fi->ff->encoding) {
5809       uMap = globalParams->getUnicodeMap(fi->ff->encoding);
5810     }
5811 
5812   // check for an 8-bit code-to-GID map
5813   } else {
5814     if (fi && fi->ff) {
5815       codeToGID = fi->ff->codeToGID;
5816     }
5817   }
5818 
5819   // compute the positioning (dx, dy) for each char in the string
5820   nChars = 0;
5821   p = s->getCString();
5822   len = s->getLength();
5823   s2 = new GString();
5824   dxdySize = font->isCIDFont() ? 8 : s->getLength();
5825   dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double));
5826   originX0 = originY0 = 0; // make gcc happy
5827   while (len > 0) {
5828     n = font->getNextChar(p, len, &code,
5829 			  u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
5830 			  &dx, &dy, &originX, &originY);
5831     //~ this doesn't handle the case where the origin offset changes
5832     //~   within a string of characters -- which could be fixed by
5833     //~   modifying dx,dy as needed for each character
5834     if (p == s->getCString()) {
5835       originX0 = originX;
5836       originY0 = originY;
5837     }
5838     dx *= state->getFontSize();
5839     dy *= state->getFontSize();
5840     if (wMode) {
5841       dy += state->getCharSpace();
5842       if (n == 1 && *p == ' ') {
5843 	dy += state->getWordSpace();
5844       }
5845     } else {
5846       dx += state->getCharSpace();
5847       if (n == 1 && *p == ' ') {
5848 	dx += state->getWordSpace();
5849       }
5850     }
5851     dx *= state->getHorizScaling();
5852     if (font->isCIDFont()) {
5853       if (uMap) {
5854 	if (nChars + uLen > dxdySize) {
5855 	  do {
5856 	    dxdySize *= 2;
5857 	  } while (nChars + uLen > dxdySize);
5858 	  dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double));
5859 	}
5860 	for (i = 0; i < uLen; ++i) {
5861 	  m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
5862 	  for (j = 0; j < m; ++j) {
5863 	    s2->append(buf[j]);
5864 	  }
5865 	  //~ this really needs to get the number of chars in the target
5866 	  //~ encoding - which may be more than the number of Unicode
5867 	  //~ chars
5868 	  dxdy[2 * nChars] = dx;
5869 	  dxdy[2 * nChars + 1] = dy;
5870 	  ++nChars;
5871 	}
5872       } else {
5873 	if (nChars + 1 > dxdySize) {
5874 	  dxdySize *= 2;
5875 	  dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double));
5876 	}
5877 	s2->append((char)((code >> 8) & 0xff));
5878 	s2->append((char)(code & 0xff));
5879 	dxdy[2 * nChars] = dx;
5880 	dxdy[2 * nChars + 1] = dy;
5881 	++nChars;
5882       }
5883     } else {
5884       if (!codeToGID || codeToGID[code] >= 0) {
5885 	s2->append((char)code);
5886 	dxdy[2 * nChars] = dx;
5887 	dxdy[2 * nChars + 1] = dy;
5888 	++nChars;
5889       }
5890     }
5891     p += n;
5892     len -= n;
5893   }
5894   if (uMap) {
5895     uMap->decRefCnt();
5896   }
5897   originX0 *= state->getFontSize();
5898   originY0 *= state->getFontSize();
5899   state->textTransformDelta(originX0, originY0, &tOriginX0, &tOriginY0);
5900 
5901   if (nChars > 0) {
5902     if (wMode) {
5903       writePSFmt("{0:.6g} {1:.6g} rmoveto\n", -tOriginX0, -tOriginY0);
5904     }
5905     writePSString(s2);
5906     writePS("\n[");
5907     for (i = 0; i < 2 * nChars; ++i) {
5908       if (i > 0) {
5909 	writePS("\n");
5910       }
5911       writePSFmt("{0:.6g}", dxdy[i]);
5912     }
5913     if (font->getType() == fontType3) {
5914       writePS("] Tj3\n");
5915     } else {
5916       writePS("] Tj\n");
5917     }
5918     if (wMode) {
5919       writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0);
5920     }
5921   }
5922   gfree(dxdy);
5923   delete s2;
5924 
5925   if ((state->getRender() & 4) && font->getType() != fontType3) {
5926     haveTextClip = gTrue;
5927   }
5928 
5929   noStateChanges = gFalse;
5930 }
5931 
endTextObject(GfxState * state)5932 void PSOutputDev::endTextObject(GfxState *state) {
5933   if (haveTextClip) {
5934     writePS("Tclip\n");
5935     haveTextClip = gFalse;
5936     noStateChanges = gFalse;
5937   }
5938 }
5939 
drawImageMask(GfxState * state,Object * ref,Stream * str,int width,int height,GBool invert,GBool inlineImg,GBool interpolate)5940 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
5941 				int width, int height, GBool invert,
5942 				GBool inlineImg, GBool interpolate) {
5943   int len;
5944 
5945   len = height * ((width + 7) / 8);
5946   switch (level) {
5947   case psLevel1:
5948   case psLevel1Sep:
5949     doImageL1(ref, state, NULL, invert, inlineImg, str, width, height, len);
5950     break;
5951   case psLevel2:
5952   case psLevel2Gray:
5953   case psLevel2Sep:
5954     doImageL2(ref, state, NULL, invert, inlineImg, str, width, height, len,
5955 	      NULL, NULL, 0, 0, gFalse);
5956     break;
5957   case psLevel3:
5958   case psLevel3Gray:
5959   case psLevel3Sep:
5960     doImageL3(ref, state, NULL, invert, inlineImg, str, width, height, len,
5961 	      NULL, NULL, 0, 0, gFalse);
5962     break;
5963   }
5964   noStateChanges = gFalse;
5965 }
5966 
drawImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,int * maskColors,GBool inlineImg,GBool interpolate)5967 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
5968 			    int width, int height, GfxImageColorMap *colorMap,
5969 			    int *maskColors, GBool inlineImg,
5970 			    GBool interpolate) {
5971   int len;
5972 
5973   len = height * ((width * colorMap->getNumPixelComps() *
5974 		   colorMap->getBits() + 7) / 8);
5975   switch (level) {
5976   case psLevel1:
5977     doImageL1(ref, state, colorMap, gFalse, inlineImg, str,
5978 	      width, height, len);
5979     break;
5980   case psLevel1Sep:
5981     //~ handle indexed, separation, ... color spaces
5982     doImageL1Sep(state, colorMap, gFalse, inlineImg, str, width, height, len);
5983     break;
5984   case psLevel2:
5985   case psLevel2Gray:
5986   case psLevel2Sep:
5987     doImageL2(ref, state, colorMap, gFalse, inlineImg, str,
5988 	      width, height, len, maskColors, NULL, 0, 0, gFalse);
5989     break;
5990   case psLevel3:
5991   case psLevel3Gray:
5992   case psLevel3Sep:
5993     doImageL3(ref, state, colorMap, gFalse, inlineImg, str,
5994 	      width, height, len, maskColors, NULL, 0, 0, gFalse);
5995     break;
5996   }
5997   t3Cacheable = gFalse;
5998   noStateChanges = gFalse;
5999 }
6000 
drawMaskedImage(GfxState * state,Object * ref,Stream * str,int width,int height,GfxImageColorMap * colorMap,Object * maskRef,Stream * maskStr,int maskWidth,int maskHeight,GBool maskInvert,GBool interpolate)6001 void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
6002 				  int width, int height,
6003 				  GfxImageColorMap *colorMap,
6004 				  Object *maskRef, Stream *maskStr,
6005 				  int maskWidth, int maskHeight,
6006 				  GBool maskInvert, GBool interpolate) {
6007   int len;
6008 
6009   len = height * ((width * colorMap->getNumPixelComps() *
6010 		   colorMap->getBits() + 7) / 8);
6011   switch (level) {
6012   case psLevel1:
6013     doImageL1(ref, state, colorMap, gFalse, gFalse, str, width, height, len);
6014     break;
6015   case psLevel1Sep:
6016     //~ handle indexed, separation, ... color spaces
6017     doImageL1Sep(state, colorMap, gFalse, gFalse, str, width, height, len);
6018     break;
6019   case psLevel2:
6020   case psLevel2Gray:
6021   case psLevel2Sep:
6022     doImageL2(ref, state, colorMap, gFalse, gFalse, str, width, height, len,
6023 	      NULL, maskStr, maskWidth, maskHeight, maskInvert);
6024     break;
6025   case psLevel3:
6026   case psLevel3Gray:
6027   case psLevel3Sep:
6028     doImageL3(ref, state, colorMap, gFalse, gFalse, str, width, height, len,
6029 	      NULL, maskStr, maskWidth, maskHeight, maskInvert);
6030     break;
6031   }
6032   t3Cacheable = gFalse;
6033   noStateChanges = gFalse;
6034 }
6035 
doImageL1(Object * ref,GfxState * state,GfxImageColorMap * colorMap,GBool invert,GBool inlineImg,Stream * str,int width,int height,int len)6036 void PSOutputDev::doImageL1(Object *ref, GfxState *state,
6037 			    GfxImageColorMap *colorMap,
6038 			    GBool invert, GBool inlineImg,
6039 			    Stream *str, int width, int height, int len) {
6040   ImageStream *imgStr;
6041   Guchar pixBuf[gfxColorMaxComps];
6042   GfxGray gray;
6043   int col, x, y, c, i;
6044 
6045   if ((inType3Char || preload) && !colorMap) {
6046     if (inlineImg) {
6047       // create an array
6048       str = new FixedLengthEncoder(str, len);
6049       str = new ASCIIHexEncoder(str);
6050       str->reset();
6051       col = 0;
6052       writePS("[<");
6053       do {
6054 	do {
6055 	  c = str->getChar();
6056 	} while (c == '\n' || c == '\r');
6057 	if (c == '>' || c == EOF) {
6058 	  break;
6059 	}
6060 	writePSChar((char)c);
6061 	++col;
6062 	// each line is: "<...data...><eol>"
6063 	// so max data length = 255 - 4 = 251
6064 	// but make it 240 just to be safe
6065 	// chunks are 2 bytes each, so we need to stop on an even col number
6066 	if (col == 240) {
6067 	  writePS(">\n<");
6068 	  col = 0;
6069 	}
6070       } while (c != '>' && c != EOF);
6071       writePS(">]\n");
6072       writePS("0\n");
6073       str->close();
6074       delete str;
6075     } else {
6076       // set up to use the array already created by setupImages()
6077       writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
6078     }
6079   }
6080 
6081   // image/imagemask command
6082   if ((inType3Char || preload) && !colorMap) {
6083     writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n",
6084 	       width, height, invert ? "true" : "false",
6085 	       width, -height, height);
6086   } else if (colorMap) {
6087     writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
6088 	       width, height,
6089 	       width, -height, height);
6090   } else {
6091     writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n",
6092 	       width, height, invert ? "true" : "false",
6093 	       width, -height, height);
6094   }
6095 
6096   // image data
6097   if (!((inType3Char || preload) && !colorMap)) {
6098 
6099     if (colorMap) {
6100 
6101       // set up to process the data stream
6102       imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
6103 			       colorMap->getBits());
6104       imgStr->reset();
6105 
6106       // process the data stream
6107       i = 0;
6108       for (y = 0; y < height; ++y) {
6109 
6110 	// write the line
6111 	for (x = 0; x < width; ++x) {
6112 	  imgStr->getPixel(pixBuf);
6113 	  colorMap->getGray(pixBuf, &gray, state->getRenderingIntent());
6114 	  writePSFmt("{0:02x}", colToByte(gray));
6115 	  if (++i == 32) {
6116 	    writePSChar('\n');
6117 	    i = 0;
6118 	  }
6119 	}
6120       }
6121       if (i != 0) {
6122 	writePSChar('\n');
6123       }
6124       str->close();
6125       delete imgStr;
6126 
6127     // imagemask
6128     } else {
6129       str->reset();
6130       i = 0;
6131       for (y = 0; y < height; ++y) {
6132 	for (x = 0; x < width; x += 8) {
6133 	  writePSFmt("{0:02x}", str->getChar() & 0xff);
6134 	  if (++i == 32) {
6135 	    writePSChar('\n');
6136 	    i = 0;
6137 	  }
6138 	}
6139       }
6140       if (i != 0) {
6141 	writePSChar('\n');
6142       }
6143       str->close();
6144     }
6145   }
6146 }
6147 
doImageL1Sep(GfxState * state,GfxImageColorMap * colorMap,GBool invert,GBool inlineImg,Stream * str,int width,int height,int len)6148 void PSOutputDev::doImageL1Sep(GfxState *state, GfxImageColorMap *colorMap,
6149 			       GBool invert, GBool inlineImg,
6150 			       Stream *str, int width, int height, int len) {
6151   ImageStream *imgStr;
6152   Guchar *lineBuf;
6153   Guchar pixBuf[gfxColorMaxComps];
6154   GfxCMYK cmyk;
6155   int x, y, i, comp;
6156 
6157   // width, height, matrix, bits per component
6158   writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n",
6159 	     width, height,
6160 	     width, -height, height);
6161 
6162   // allocate a line buffer
6163   lineBuf = (Guchar *)gmallocn(width, 4);
6164 
6165   // set up to process the data stream
6166   imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
6167 			   colorMap->getBits());
6168   imgStr->reset();
6169 
6170   // process the data stream
6171   i = 0;
6172   for (y = 0; y < height; ++y) {
6173 
6174     // read the line
6175     for (x = 0; x < width; ++x) {
6176       imgStr->getPixel(pixBuf);
6177       colorMap->getCMYK(pixBuf, &cmyk, state->getRenderingIntent());
6178       lineBuf[4*x+0] = colToByte(cmyk.c);
6179       lineBuf[4*x+1] = colToByte(cmyk.m);
6180       lineBuf[4*x+2] = colToByte(cmyk.y);
6181       lineBuf[4*x+3] = colToByte(cmyk.k);
6182       addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
6183 		      colToDbl(cmyk.y), colToDbl(cmyk.k));
6184     }
6185 
6186     // write one line of each color component
6187     for (comp = 0; comp < 4; ++comp) {
6188       for (x = 0; x < width; ++x) {
6189 	writePSFmt("{0:02x}", lineBuf[4*x + comp]);
6190 	if (++i == 32) {
6191 	  writePSChar('\n');
6192 	  i = 0;
6193 	}
6194       }
6195     }
6196   }
6197 
6198   if (i != 0) {
6199     writePSChar('\n');
6200   }
6201 
6202   str->close();
6203   delete imgStr;
6204   gfree(lineBuf);
6205 }
6206 
doImageL2(Object * ref,GfxState * state,GfxImageColorMap * colorMap,GBool invert,GBool inlineImg,Stream * str,int width,int height,int len,int * maskColors,Stream * maskStr,int maskWidth,int maskHeight,GBool maskInvert)6207 void PSOutputDev::doImageL2(Object *ref, GfxState *state,
6208 			    GfxImageColorMap *colorMap,
6209 			    GBool invert, GBool inlineImg,
6210 			    Stream *str, int width, int height, int len,
6211 			    int *maskColors, Stream *maskStr,
6212 			    int maskWidth, int maskHeight, GBool maskInvert) {
6213   Stream *str2;
6214   GString *s;
6215   int n, numComps;
6216   GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
6217   GfxSeparationColorSpace *sepCS;
6218   GfxColor color;
6219   GfxCMYK cmyk;
6220   char buf[4096];
6221   int c, col, i;
6222 
6223   // color key masking
6224   if (maskColors && colorMap && !inlineImg) {
6225     // can't read the stream twice for inline images -- but masking
6226     // isn't allowed with inline images anyway
6227     convertColorKeyMaskToClipRects(colorMap, str, width, height, maskColors);
6228 
6229   // explicit masking
6230   } else if (maskStr) {
6231     convertExplicitMaskToClipRects(maskStr, maskWidth, maskHeight, maskInvert);
6232   }
6233 
6234   // color space
6235   if (colorMap && !(level == psLevel2Gray || level == psLevel3Gray)) {
6236     dumpColorSpaceL2(state, colorMap->getColorSpace(), gFalse, gTrue, gFalse);
6237     writePS(" setcolorspace\n");
6238   }
6239 
6240   useASCIIHex = globalParams->getPSASCIIHex();
6241 
6242   // set up the image data
6243   if (mode == psModeForm || inType3Char || preload) {
6244     if (inlineImg) {
6245       // create an array
6246       str2 = new FixedLengthEncoder(str, len);
6247       if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) {
6248 	str2 = new GrayRecoder(str2, width, height, colorMap);
6249       }
6250       if (globalParams->getPSLZW()) {
6251 	str2 = new LZWEncoder(str2);
6252       } else {
6253 	str2 = new RunLengthEncoder(str2);
6254       }
6255       if (useASCIIHex) {
6256 	str2 = new ASCIIHexEncoder(str2);
6257       } else {
6258 	str2 = new ASCII85Encoder(str2);
6259       }
6260       str2->reset();
6261       col = 0;
6262       writePS((char *)(useASCIIHex ? "[<" : "[<~"));
6263       do {
6264 	do {
6265 	  c = str2->getChar();
6266 	} while (c == '\n' || c == '\r');
6267 	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
6268 	  break;
6269 	}
6270 	if (c == 'z') {
6271 	  writePSChar((char)c);
6272 	  ++col;
6273 	} else {
6274 	  writePSChar((char)c);
6275 	  ++col;
6276 	  for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
6277 	    do {
6278 	      c = str2->getChar();
6279 	    } while (c == '\n' || c == '\r');
6280 	    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
6281 	      break;
6282 	    }
6283 	    writePSChar((char)c);
6284 	    ++col;
6285 	  }
6286 	}
6287 	// each line is: "<~...data...~><eol>"
6288 	// so max data length = 255 - 6 = 249
6289 	// chunks are 1 or 5 bytes each, so we have to stop at 245
6290 	// but make it 240 just to be safe
6291 	if (col > 240) {
6292 	  writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
6293 	  col = 0;
6294 	}
6295       } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
6296       writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
6297       // add an extra entry because the LZWDecode/RunLengthDecode
6298       // filter may read past the end
6299       writePS("<>]\n");
6300       writePS("0\n");
6301       str2->close();
6302       delete str2;
6303     } else {
6304       // set up to use the array already created by setupImages()
6305       writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
6306     }
6307   }
6308 
6309   // image dictionary
6310   writePS("<<\n  /ImageType 1\n");
6311 
6312   // width, height, matrix, bits per component
6313   writePSFmt("  /Width {0:d}\n", width);
6314   writePSFmt("  /Height {0:d}\n", height);
6315   writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
6316 	     width, -height, height);
6317   if (colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN ||
6318 		   level == psLevel2Gray || level == psLevel3Gray)) {
6319     writePS("  /BitsPerComponent 8\n");
6320   } else {
6321     writePSFmt("  /BitsPerComponent {0:d}\n",
6322 	       colorMap ? colorMap->getBits() : 1);
6323   }
6324 
6325   // decode
6326   if (colorMap) {
6327     writePS("  /Decode [");
6328     if ((level == psLevel2Sep || level == psLevel3Sep) &&
6329 	colorMap->getColorSpace()->getMode() == csSeparation) {
6330       // this matches up with the code in the pdfImSep operator
6331       n = (1 << colorMap->getBits()) - 1;
6332       writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n,
6333 		 colorMap->getDecodeHigh(0) * n);
6334     } else if (level == psLevel2Gray || level == psLevel3Gray) {
6335       writePS("0 1");
6336     } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
6337       numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
6338 	           getAlt()->getNComps();
6339       for (i = 0; i < numComps; ++i) {
6340 	if (i > 0) {
6341 	  writePS(" ");
6342 	}
6343 	writePS("0 1");
6344       }
6345     } else {
6346       numComps = colorMap->getNumPixelComps();
6347       for (i = 0; i < numComps; ++i) {
6348 	if (i > 0) {
6349 	  writePS(" ");
6350 	}
6351 	writePSFmt("{0:.4g} {1:.4g}",
6352 		   colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i));
6353       }
6354     }
6355     writePS("]\n");
6356   } else {
6357     writePSFmt("  /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
6358   }
6359 
6360   // data source
6361   if (mode == psModeForm || inType3Char || preload) {
6362     writePS("  /DataSource { pdfImStr }\n");
6363   } else {
6364     writePS("  /DataSource currentfile\n");
6365   }
6366 
6367   // filters
6368   if ((mode == psModeForm || inType3Char || preload) &&
6369       globalParams->getPSUncompressPreloadedImages()) {
6370     s = NULL;
6371     useLZW = useRLE = gFalse;
6372     useCompressed = gFalse;
6373     useASCII = gFalse;
6374   } else {
6375     s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
6376 			 "    ", !inlineImg);
6377     if ((colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN ||
6378 		      level == psLevel2Gray || level == psLevel3Gray)) ||
6379 	inlineImg || !s) {
6380       if (globalParams->getPSLZW()) {
6381 	useLZW = gTrue;
6382 	useRLE = gFalse;
6383       } else {
6384 	useRLE = gTrue;
6385 	useLZW = gFalse;
6386       }
6387       useASCII = !(mode == psModeForm || inType3Char || preload);
6388       useCompressed = gFalse;
6389     } else {
6390       useLZW = useRLE = gFalse;
6391       useASCII = str->isBinary() &&
6392 	         !(mode == psModeForm || inType3Char || preload);
6393       useCompressed = gTrue;
6394     }
6395   }
6396   if (useASCII) {
6397     writePSFmt("    /ASCII{0:s}Decode filter\n",
6398 	       useASCIIHex ? "Hex" : "85");
6399   }
6400   if (useLZW) {
6401     writePS("    /LZWDecode filter\n");
6402   } else if (useRLE) {
6403     writePS("    /RunLengthDecode filter\n");
6404   }
6405   if (useCompressed) {
6406     writePS(s->getCString());
6407   }
6408   if (s) {
6409     delete s;
6410   }
6411 
6412   if (mode == psModeForm || inType3Char || preload) {
6413 
6414     // end of image dictionary
6415     writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask");
6416 
6417     // get rid of the array and index
6418     writePS("pop pop\n");
6419 
6420   } else {
6421 
6422     // cut off inline image streams at appropriate length
6423     if (inlineImg) {
6424       str = new FixedLengthEncoder(str, len);
6425     } else if (useCompressed) {
6426       str = str->getUndecodedStream();
6427     }
6428 
6429     // recode to grayscale
6430     if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) {
6431       str = new GrayRecoder(str, width, height, colorMap);
6432 
6433     // recode DeviceN data
6434     } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
6435       str = new DeviceNRecoder(str, width, height, colorMap);
6436     }
6437 
6438     // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
6439     if (useLZW) {
6440       str = new LZWEncoder(str);
6441     } else if (useRLE) {
6442       str = new RunLengthEncoder(str);
6443     }
6444     if (useASCII) {
6445       if (useASCIIHex) {
6446 	str = new ASCIIHexEncoder(str);
6447       } else {
6448 	str = new ASCII85Encoder(str);
6449       }
6450     }
6451 
6452     // end of image dictionary
6453     writePS(">>\n");
6454 #if OPI_SUPPORT
6455     if (opi13Nest) {
6456       if (inlineImg) {
6457 	// this can't happen -- OPI dictionaries are in XObjects
6458 	error(errSyntaxError, -1, "OPI in inline image");
6459 	n = 0;
6460       } else {
6461 	// need to read the stream to count characters -- the length
6462 	// is data-dependent (because of ASCII and LZW/RunLength
6463 	// filters)
6464 	str->reset();
6465 	n = 0;
6466 	do {
6467 	  i = str->discardChars(4096);
6468 	  n += i;
6469 	} while (i == 4096);
6470 	str->close();
6471       }
6472       // +6/7 for "pdfIm\n" / "pdfImM\n"
6473       // +8 for newline + trailer
6474       n += colorMap ? 14 : 15;
6475       writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n);
6476     }
6477 #endif
6478     if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
6479 	colorMap->getColorSpace()->getMode() == csSeparation) {
6480       color.c[0] = gfxColorComp1;
6481       sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
6482       sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
6483       writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
6484 		 colToDbl(cmyk.c), colToDbl(cmyk.m),
6485 		 colToDbl(cmyk.y), colToDbl(cmyk.k),
6486 		 sepCS->getName());
6487     } else {
6488       writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
6489     }
6490 
6491     // copy the stream data
6492     str->reset();
6493     while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
6494       writePSBlock(buf, n);
6495     }
6496     str->close();
6497 
6498     // add newline and trailer to the end
6499     writePSChar('\n');
6500     writePS("%-EOD-\n");
6501 #if OPI_SUPPORT
6502     if (opi13Nest) {
6503       writePS("%%EndData\n");
6504     }
6505 #endif
6506 
6507     // delete encoders
6508     if (useLZW || useRLE || useASCII || inlineImg) {
6509       delete str;
6510     }
6511   }
6512 
6513   if ((maskColors && colorMap && !inlineImg) || maskStr) {
6514     writePS("pdfImClipEnd\n");
6515   }
6516 }
6517 
6518 // Convert color key masking to a clipping region consisting of a
6519 // sequence of clip rectangles.
convertColorKeyMaskToClipRects(GfxImageColorMap * colorMap,Stream * str,int width,int height,int * maskColors)6520 void PSOutputDev::convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap,
6521 						 Stream *str,
6522 						 int width, int height,
6523 						 int *maskColors) {
6524   ImageStream *imgStr;
6525   Guchar *line;
6526   PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;
6527   int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;
6528   GBool emitRect, addRect, extendRect;
6529   int numComps, i, j, x0, x1, y;
6530 
6531   numComps = colorMap->getNumPixelComps();
6532   imgStr = new ImageStream(str, width, numComps, colorMap->getBits());
6533   imgStr->reset();
6534   rects0Len = rects1Len = rectsOutLen = 0;
6535   rectsSize = rectsOutSize = 64;
6536   rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
6537   rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
6538   rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize,
6539 					  sizeof(PSOutImgClipRect));
6540   for (y = 0; y < height; ++y) {
6541     if (!(line = imgStr->getLine())) {
6542       break;
6543     }
6544     i = 0;
6545     rects1Len = 0;
6546     for (x0 = 0; x0 < width; ++x0) {
6547       for (j = 0; j < numComps; ++j) {
6548 	if (line[x0*numComps+j] < maskColors[2*j] ||
6549 	    line[x0*numComps+j] > maskColors[2*j+1]) {
6550 	  break;
6551 	}
6552       }
6553       if (j < numComps) {
6554 	break;
6555       }
6556     }
6557     for (x1 = x0; x1 < width; ++x1) {
6558       for (j = 0; j < numComps; ++j) {
6559 	if (line[x1*numComps+j] < maskColors[2*j] ||
6560 	    line[x1*numComps+j] > maskColors[2*j+1]) {
6561 	  break;
6562 	}
6563       }
6564       if (j == numComps) {
6565 	break;
6566       }
6567     }
6568     while (x0 < width || i < rects0Len) {
6569       emitRect = addRect = extendRect = gFalse;
6570       if (x0 >= width) {
6571 	emitRect = gTrue;
6572       } else if (i >= rects0Len) {
6573 	addRect = gTrue;
6574       } else if (rects0[i].x0 < x0) {
6575 	emitRect = gTrue;
6576       } else if (x0 < rects0[i].x0) {
6577 	addRect = gTrue;
6578       } else if (rects0[i].x1 == x1) {
6579 	extendRect = gTrue;
6580       } else {
6581 	emitRect = addRect = gTrue;
6582       }
6583       if (emitRect) {
6584 	if (rectsOutLen == rectsOutSize) {
6585 	  rectsOutSize *= 2;
6586 	  rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
6587 						   sizeof(PSOutImgClipRect));
6588 	}
6589 	rectsOut[rectsOutLen].x0 = rects0[i].x0;
6590 	rectsOut[rectsOutLen].x1 = rects0[i].x1;
6591 	rectsOut[rectsOutLen].y0 = height - y - 1;
6592 	rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
6593 	++rectsOutLen;
6594 	++i;
6595       }
6596       if (addRect || extendRect) {
6597 	if (rects1Len == rectsSize) {
6598 	  rectsSize *= 2;
6599 	  rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize,
6600 						 sizeof(PSOutImgClipRect));
6601 	  rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize,
6602 						 sizeof(PSOutImgClipRect));
6603 	}
6604 	rects1[rects1Len].x0 = x0;
6605 	rects1[rects1Len].x1 = x1;
6606 	if (addRect) {
6607 	  rects1[rects1Len].y0 = y;
6608 	}
6609 	if (extendRect) {
6610 	  rects1[rects1Len].y0 = rects0[i].y0;
6611 	  ++i;
6612 	}
6613 	++rects1Len;
6614 	for (x0 = x1; x0 < width; ++x0) {
6615 	  for (j = 0; j < numComps; ++j) {
6616 	    if (line[x0*numComps+j] < maskColors[2*j] ||
6617 		line[x0*numComps+j] > maskColors[2*j+1]) {
6618 	      break;
6619 	    }
6620 	  }
6621 	  if (j < numComps) {
6622 	    break;
6623 	  }
6624 	}
6625 	for (x1 = x0; x1 < width; ++x1) {
6626 	  for (j = 0; j < numComps; ++j) {
6627 	    if (line[x1*numComps+j] < maskColors[2*j] ||
6628 		line[x1*numComps+j] > maskColors[2*j+1]) {
6629 	      break;
6630 	    }
6631 	  }
6632 	  if (j == numComps) {
6633 	    break;
6634 	  }
6635 	}
6636       }
6637     }
6638     rectsTmp = rects0;
6639     rects0 = rects1;
6640     rects1 = rectsTmp;
6641     i = rects0Len;
6642     rects0Len = rects1Len;
6643     rects1Len = i;
6644   }
6645   for (i = 0; i < rects0Len; ++i) {
6646     if (rectsOutLen == rectsOutSize) {
6647       rectsOutSize *= 2;
6648       rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
6649 					       sizeof(PSOutImgClipRect));
6650     }
6651     rectsOut[rectsOutLen].x0 = rects0[i].x0;
6652     rectsOut[rectsOutLen].x1 = rects0[i].x1;
6653     rectsOut[rectsOutLen].y0 = height - y - 1;
6654     rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
6655     ++rectsOutLen;
6656   }
6657   writePSFmt("{0:d} {1:d}\n", width, height);
6658   for (i = 0; i < rectsOutLen; ++i) {
6659     writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n",
6660 	       rectsOut[i].x0, rectsOut[i].y0,
6661 	       rectsOut[i].x1 - rectsOut[i].x0,
6662 	       rectsOut[i].y1 - rectsOut[i].y0);
6663   }
6664   writePS("pop pop pdfImClip\n");
6665   gfree(rectsOut);
6666   gfree(rects0);
6667   gfree(rects1);
6668   delete imgStr;
6669   str->close();
6670 }
6671 
6672 // Convert an explicit mask image to a clipping region consisting of a
6673 // sequence of clip rectangles.
convertExplicitMaskToClipRects(Stream * maskStr,int maskWidth,int maskHeight,GBool maskInvert)6674 void PSOutputDev::convertExplicitMaskToClipRects(Stream *maskStr,
6675 						 int maskWidth, int maskHeight,
6676 						 GBool maskInvert) {
6677   ImageStream *imgStr;
6678   Guchar *line;
6679   PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;
6680   int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;
6681   GBool emitRect, addRect, extendRect;
6682   int i, x0, x1, y, maskXor;
6683 
6684   imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
6685   imgStr->reset();
6686   rects0Len = rects1Len = rectsOutLen = 0;
6687   rectsSize = rectsOutSize = 64;
6688   rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
6689   rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
6690   rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize,
6691 					  sizeof(PSOutImgClipRect));
6692   maskXor = maskInvert ? 1 : 0;
6693   for (y = 0; y < maskHeight; ++y) {
6694     if (!(line = imgStr->getLine())) {
6695       break;
6696     }
6697     i = 0;
6698     rects1Len = 0;
6699     for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ;
6700     for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ;
6701     while (x0 < maskWidth || i < rects0Len) {
6702       emitRect = addRect = extendRect = gFalse;
6703       if (x0 >= maskWidth) {
6704 	emitRect = gTrue;
6705       } else if (i >= rects0Len) {
6706 	addRect = gTrue;
6707       } else if (rects0[i].x0 < x0) {
6708 	emitRect = gTrue;
6709       } else if (x0 < rects0[i].x0) {
6710 	addRect = gTrue;
6711       } else if (rects0[i].x1 == x1) {
6712 	extendRect = gTrue;
6713       } else {
6714 	emitRect = addRect = gTrue;
6715       }
6716       if (emitRect) {
6717 	if (rectsOutLen == rectsOutSize) {
6718 	  rectsOutSize *= 2;
6719 	  rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
6720 						   sizeof(PSOutImgClipRect));
6721 	}
6722 	rectsOut[rectsOutLen].x0 = rects0[i].x0;
6723 	rectsOut[rectsOutLen].x1 = rects0[i].x1;
6724 	rectsOut[rectsOutLen].y0 = maskHeight - y - 1;
6725 	rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
6726 	++rectsOutLen;
6727 	++i;
6728       }
6729       if (addRect || extendRect) {
6730 	if (rects1Len == rectsSize) {
6731 	  rectsSize *= 2;
6732 	  rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize,
6733 						 sizeof(PSOutImgClipRect));
6734 	  rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize,
6735 						 sizeof(PSOutImgClipRect));
6736 	}
6737 	rects1[rects1Len].x0 = x0;
6738 	rects1[rects1Len].x1 = x1;
6739 	if (addRect) {
6740 	  rects1[rects1Len].y0 = y;
6741 	}
6742 	if (extendRect) {
6743 	  rects1[rects1Len].y0 = rects0[i].y0;
6744 	  ++i;
6745 	}
6746 	++rects1Len;
6747 	for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ;
6748 	for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ;
6749       }
6750     }
6751     rectsTmp = rects0;
6752     rects0 = rects1;
6753     rects1 = rectsTmp;
6754     i = rects0Len;
6755     rects0Len = rects1Len;
6756     rects1Len = i;
6757   }
6758   for (i = 0; i < rects0Len; ++i) {
6759     if (rectsOutLen == rectsOutSize) {
6760       rectsOutSize *= 2;
6761       rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
6762 					       sizeof(PSOutImgClipRect));
6763     }
6764     rectsOut[rectsOutLen].x0 = rects0[i].x0;
6765     rectsOut[rectsOutLen].x1 = rects0[i].x1;
6766     rectsOut[rectsOutLen].y0 = maskHeight - y - 1;
6767     rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
6768     ++rectsOutLen;
6769   }
6770   writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight);
6771   for (i = 0; i < rectsOutLen; ++i) {
6772     writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n",
6773 	       rectsOut[i].x0, rectsOut[i].y0,
6774 	       rectsOut[i].x1 - rectsOut[i].x0,
6775 	       rectsOut[i].y1 - rectsOut[i].y0);
6776   }
6777   writePS("pop pop pdfImClip\n");
6778   gfree(rectsOut);
6779   gfree(rects0);
6780   gfree(rects1);
6781   delete imgStr;
6782   maskStr->close();
6783 }
6784 
6785 //~ this doesn't currently support OPI
doImageL3(Object * ref,GfxState * state,GfxImageColorMap * colorMap,GBool invert,GBool inlineImg,Stream * str,int width,int height,int len,int * maskColors,Stream * maskStr,int maskWidth,int maskHeight,GBool maskInvert)6786 void PSOutputDev::doImageL3(Object *ref, GfxState *state,
6787 			    GfxImageColorMap *colorMap,
6788 			    GBool invert, GBool inlineImg,
6789 			    Stream *str, int width, int height, int len,
6790 			    int *maskColors, Stream *maskStr,
6791 			    int maskWidth, int maskHeight, GBool maskInvert) {
6792   Stream *str2;
6793   GString *s;
6794   int n, numComps;
6795   GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
6796   GBool maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed;
6797   GString *maskFilters;
6798   GfxSeparationColorSpace *sepCS;
6799   GfxColor color;
6800   GfxCMYK cmyk;
6801   char buf[4096];
6802   int c;
6803   int col, i;
6804 
6805   useASCIIHex = globalParams->getPSASCIIHex();
6806   useLZW = useRLE = useASCII = useCompressed = gFalse; // make gcc happy
6807   maskUseLZW = maskUseRLE = maskUseASCII = gFalse; // make gcc happy
6808   maskUseCompressed = gFalse; // make gcc happy
6809   maskFilters = NULL; // make gcc happy
6810 
6811   // explicit masking
6812   // -- this also converts color key masking in grayscale mode
6813   if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) {
6814 
6815     // mask data source
6816     if (maskColors && colorMap && level == psLevel3Gray) {
6817       s = NULL;
6818       if (mode == psModeForm || inType3Char || preload) {
6819 	if (globalParams->getPSUncompressPreloadedImages()) {
6820 	  maskUseLZW = maskUseRLE = gFalse;
6821 	} else if (globalParams->getPSLZW()) {
6822 	  maskUseLZW = gTrue;
6823 	  maskUseRLE = gFalse;
6824 	} else {
6825 	  maskUseRLE = gTrue;
6826 	  maskUseLZW = gFalse;
6827 	}
6828 	maskUseASCII = gFalse;
6829 	maskUseCompressed = gFalse;
6830       } else {
6831 	if (globalParams->getPSLZW()) {
6832 	  maskUseLZW = gTrue;
6833 	  maskUseRLE = gFalse;
6834 	} else {
6835 	  maskUseRLE = gTrue;
6836 	  maskUseLZW = gFalse;
6837 	}
6838 	maskUseASCII = gTrue;
6839       }
6840       maskUseCompressed = gFalse;
6841       maskWidth = width;
6842       maskHeight = height;
6843       maskInvert = gFalse;
6844     } else if ((mode == psModeForm || inType3Char || preload) &&
6845 	       globalParams->getPSUncompressPreloadedImages()) {
6846       s = NULL;
6847       maskUseLZW = maskUseRLE = gFalse;
6848       maskUseCompressed = gFalse;
6849       maskUseASCII = gFalse;
6850     } else {
6851       s = maskStr->getPSFilter(3, "  ", !inlineImg);
6852       if (!s) {
6853 	if (globalParams->getPSLZW()) {
6854 	  maskUseLZW = gTrue;
6855 	  maskUseRLE = gFalse;
6856 	} else {
6857 	  maskUseRLE = gTrue;
6858 	  maskUseLZW = gFalse;
6859 	}
6860 	maskUseASCII = !(mode == psModeForm || inType3Char || preload);
6861 	maskUseCompressed = gFalse;
6862       } else {
6863 	maskUseLZW = maskUseRLE = gFalse;
6864 	maskUseASCII = maskStr->isBinary() &&
6865 	               !(mode == psModeForm || inType3Char || preload);
6866 	maskUseCompressed = gTrue;
6867       }
6868     }
6869     maskFilters = new GString();
6870     if (maskUseASCII) {
6871       maskFilters->appendf("    /ASCII{0:s}Decode filter\n",
6872 			   useASCIIHex ? "Hex" : "85");
6873     }
6874     if (maskUseLZW) {
6875       maskFilters->append("    /LZWDecode filter\n");
6876     } else if (maskUseRLE) {
6877       maskFilters->append("    /RunLengthDecode filter\n");
6878     }
6879     if (maskUseCompressed) {
6880       maskFilters->append(s);
6881     }
6882     if (s) {
6883       delete s;
6884     }
6885     if (mode == psModeForm || inType3Char || preload) {
6886       writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n",
6887 		 ref->getRefNum(), ref->getRefGen());
6888     } else {
6889       writePS("currentfile\n");
6890       writePS(maskFilters->getCString());
6891       writePS("pdfMask\n");
6892 
6893       // add the ColorKeyToMask filter
6894       if (maskColors && colorMap && level == psLevel3Gray) {
6895 	maskStr = new ColorKeyToMaskEncoder(str, width, height, colorMap,
6896 					    maskColors);
6897       }
6898 
6899       // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
6900       if (maskUseCompressed) {
6901 	maskStr = maskStr->getUndecodedStream();
6902       }
6903       if (maskUseLZW) {
6904 	maskStr = new LZWEncoder(maskStr);
6905       } else if (maskUseRLE) {
6906 	maskStr = new RunLengthEncoder(maskStr);
6907       }
6908       if (maskUseASCII) {
6909 	if (useASCIIHex) {
6910 	  maskStr = new ASCIIHexEncoder(maskStr);
6911 	} else {
6912 	  maskStr = new ASCII85Encoder(maskStr);
6913 	}
6914       }
6915 
6916       // copy the stream data
6917       maskStr->reset();
6918       while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) {
6919 	writePSBlock(buf, n);
6920       }
6921       maskStr->close();
6922       writePSChar('\n');
6923       writePS("%-EOD-\n");
6924 
6925       // delete encoders
6926       if (maskUseLZW || maskUseRLE || maskUseASCII) {
6927 	delete maskStr;
6928       }
6929     }
6930   }
6931 
6932   // color space
6933   if (colorMap && level != psLevel3Gray) {
6934     dumpColorSpaceL2(state, colorMap->getColorSpace(), gFalse, gTrue, gFalse);
6935     writePS(" setcolorspace\n");
6936   }
6937 
6938   // set up the image data
6939   if (mode == psModeForm || inType3Char || preload) {
6940     if (inlineImg) {
6941       // create an array
6942       str2 = new FixedLengthEncoder(str, len);
6943       if (colorMap && level == psLevel3Gray) {
6944 	str2 = new GrayRecoder(str2, width, height, colorMap);
6945       }
6946       if (globalParams->getPSLZW()) {
6947 	str2 = new LZWEncoder(str2);
6948       } else {
6949 	str2 = new RunLengthEncoder(str2);
6950       }
6951       if (useASCIIHex) {
6952 	str2 = new ASCIIHexEncoder(str2);
6953       } else {
6954 	str2 = new ASCII85Encoder(str2);
6955       }
6956       str2->reset();
6957       col = 0;
6958       writePS((char *)(useASCIIHex ? "[<" : "[<~"));
6959       do {
6960 	do {
6961 	  c = str2->getChar();
6962 	} while (c == '\n' || c == '\r');
6963 	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
6964 	  break;
6965 	}
6966 	if (c == 'z') {
6967 	  writePSChar((char)c);
6968 	  ++col;
6969 	} else {
6970 	  writePSChar((char)c);
6971 	  ++col;
6972 	  for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
6973 	    do {
6974 	      c = str2->getChar();
6975 	    } while (c == '\n' || c == '\r');
6976 	    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
6977 	      break;
6978 	    }
6979 	    writePSChar((char)c);
6980 	    ++col;
6981 	  }
6982 	}
6983 	// each line is: "<~...data...~><eol>"
6984 	// so max data length = 255 - 6 = 249
6985 	// chunks are 1 or 5 bytes each, so we have to stop at 245
6986 	// but make it 240 just to be safe
6987 	if (col > 240) {
6988 	  writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
6989 	  col = 0;
6990 	}
6991       } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
6992       writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
6993       // add an extra entry because the LZWDecode/RunLengthDecode
6994       // filter may read past the end
6995       writePS("<>]\n");
6996       writePS("0\n");
6997       str2->close();
6998       delete str2;
6999     } else {
7000       // set up to use the array already created by setupImages()
7001       writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
7002     }
7003   }
7004 
7005   // explicit masking
7006   if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) {
7007     writePS("<<\n  /ImageType 3\n");
7008     writePS("  /InterleaveType 3\n");
7009     writePS("  /DataDict\n");
7010   }
7011 
7012   // image (data) dictionary
7013   writePSFmt("<<\n  /ImageType {0:d}\n",
7014 	     (maskColors && colorMap && level != psLevel3Gray) ? 4 : 1);
7015 
7016   // color key masking
7017   if (maskColors && colorMap && level != psLevel3Gray) {
7018     writePS("  /MaskColor [\n");
7019     numComps = colorMap->getNumPixelComps();
7020     for (i = 0; i < 2 * numComps; i += 2) {
7021       writePSFmt("    {0:d} {1:d}\n", maskColors[i], maskColors[i+1]);
7022     }
7023     writePS("  ]\n");
7024   }
7025 
7026   // width, height, matrix, bits per component
7027   writePSFmt("  /Width {0:d}\n", width);
7028   writePSFmt("  /Height {0:d}\n", height);
7029   writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
7030 	     width, -height, height);
7031   if (colorMap && level == psLevel3Gray) {
7032     writePS("  /BitsPerComponent 8\n");
7033   } else {
7034     writePSFmt("  /BitsPerComponent {0:d}\n",
7035 	       colorMap ? colorMap->getBits() : 1);
7036   }
7037 
7038   // decode
7039   if (colorMap) {
7040     writePS("  /Decode [");
7041     if (level == psLevel3Sep &&
7042 	colorMap->getColorSpace()->getMode() == csSeparation) {
7043       // this matches up with the code in the pdfImSep operator
7044       n = (1 << colorMap->getBits()) - 1;
7045       writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n,
7046 		 colorMap->getDecodeHigh(0) * n);
7047     } else if (level == psLevel3Gray) {
7048       writePS("0 1");
7049     } else {
7050       numComps = colorMap->getNumPixelComps();
7051       for (i = 0; i < numComps; ++i) {
7052 	if (i > 0) {
7053 	  writePS(" ");
7054 	}
7055 	writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i),
7056 		   colorMap->getDecodeHigh(i));
7057       }
7058     }
7059     writePS("]\n");
7060   } else {
7061     writePSFmt("  /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
7062   }
7063 
7064   // data source
7065   if (mode == psModeForm || inType3Char || preload) {
7066     writePS("  /DataSource { pdfImStr }\n");
7067   } else {
7068     writePS("  /DataSource currentfile\n");
7069   }
7070 
7071   // filters
7072   if ((mode == psModeForm || inType3Char || preload) &&
7073       globalParams->getPSUncompressPreloadedImages()) {
7074     s = NULL;
7075     useLZW = useRLE = gFalse;
7076     useCompressed = gFalse;
7077     useASCII = gFalse;
7078   } else {
7079     s = str->getPSFilter(3, "    ", !inlineImg);
7080     if ((colorMap && level == psLevel3Gray) || inlineImg || !s) {
7081       if (globalParams->getPSLZW()) {
7082 	useLZW = gTrue;
7083 	useRLE = gFalse;
7084       } else {
7085 	useRLE = gTrue;
7086 	useLZW = gFalse;
7087       }
7088       useASCII = !(mode == psModeForm || inType3Char || preload);
7089       useCompressed = gFalse;
7090     } else {
7091       useLZW = useRLE = gFalse;
7092       useASCII = str->isBinary() &&
7093                  !(mode == psModeForm || inType3Char || preload);
7094       useCompressed = gTrue;
7095     }
7096   }
7097   if (useASCII) {
7098     writePSFmt("    /ASCII{0:s}Decode filter\n",
7099 	       useASCIIHex ? "Hex" : "85");
7100   }
7101   if (useLZW) {
7102     writePS("    /LZWDecode filter\n");
7103   } else if (useRLE) {
7104     writePS("    /RunLengthDecode filter\n");
7105   }
7106   if (useCompressed) {
7107     writePS(s->getCString());
7108   }
7109   if (s) {
7110     delete s;
7111   }
7112 
7113   // end of image (data) dictionary
7114   writePS(">>\n");
7115 
7116   // explicit masking
7117   if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) {
7118     writePS("  /MaskDict\n");
7119     writePS("<<\n");
7120     writePS("  /ImageType 1\n");
7121     writePSFmt("  /Width {0:d}\n", maskWidth);
7122     writePSFmt("  /Height {0:d}\n", maskHeight);
7123     writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
7124 	       maskWidth, -maskHeight, maskHeight);
7125     writePS("  /BitsPerComponent 1\n");
7126     writePSFmt("  /Decode [{0:d} {1:d}]\n",
7127 	       maskInvert ? 1 : 0, maskInvert ? 0 : 1);
7128 
7129     // mask data source
7130     if (mode == psModeForm || inType3Char || preload) {
7131       writePS("  /DataSource {pdfMaskSrc}\n");
7132       writePS(maskFilters->getCString());
7133     } else {
7134       writePS("  /DataSource maskStream\n");
7135     }
7136     delete maskFilters;
7137 
7138     writePS(">>\n");
7139     writePS(">>\n");
7140   }
7141 
7142   if (mode == psModeForm || inType3Char || preload) {
7143 
7144     // image command
7145     writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask");
7146 
7147   } else {
7148 
7149     if (level == psLevel3Sep && colorMap &&
7150 	colorMap->getColorSpace()->getMode() == csSeparation) {
7151       color.c[0] = gfxColorComp1;
7152       sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
7153       sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
7154       writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
7155 		 colToDbl(cmyk.c), colToDbl(cmyk.m),
7156 		 colToDbl(cmyk.y), colToDbl(cmyk.k),
7157 		 sepCS->getName());
7158     } else {
7159       writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
7160     }
7161 
7162   }
7163 
7164   // get rid of the array and index
7165   if (mode == psModeForm || inType3Char || preload) {
7166     writePS("pop pop\n");
7167 
7168   // image data
7169   } else {
7170 
7171     // cut off inline image streams at appropriate length
7172     if (inlineImg) {
7173       str = new FixedLengthEncoder(str, len);
7174     } else if (useCompressed) {
7175       str = str->getUndecodedStream();
7176     }
7177 
7178     // recode to grayscale
7179     if (colorMap && level == psLevel3Gray) {
7180       str = new GrayRecoder(str, width, height, colorMap);
7181     }
7182 
7183     // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
7184     if (useLZW) {
7185       str = new LZWEncoder(str);
7186     } else if (useRLE) {
7187       str = new RunLengthEncoder(str);
7188     }
7189     if (useASCII) {
7190       if (useASCIIHex) {
7191 	str = new ASCIIHexEncoder(str);
7192       } else {
7193 	str = new ASCII85Encoder(str);
7194       }
7195     }
7196 
7197     // copy the stream data
7198     str->reset();
7199     while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
7200       writePSBlock(buf, n);
7201     }
7202     str->close();
7203 
7204     // add newline and trailer to the end
7205     writePSChar('\n');
7206     writePS("%-EOD-\n");
7207 
7208     // delete encoders
7209     if (useLZW || useRLE || useASCII || inlineImg) {
7210       delete str;
7211     }
7212   }
7213 
7214   // close the mask stream
7215   if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) {
7216     if (!(mode == psModeForm || inType3Char || preload)) {
7217       writePS("pdfMaskEnd\n");
7218     }
7219   }
7220 }
7221 
dumpColorSpaceL2(GfxState * state,GfxColorSpace * colorSpace,GBool genXform,GBool updateColors,GBool map01)7222 void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace,
7223 				   GBool genXform, GBool updateColors,
7224 				   GBool map01) {
7225   switch (colorSpace->getMode()) {
7226   case csDeviceGray:
7227     dumpDeviceGrayColorSpace((GfxDeviceGrayColorSpace *)colorSpace,
7228 			     genXform, updateColors, map01);
7229     break;
7230   case csCalGray:
7231     dumpCalGrayColorSpace((GfxCalGrayColorSpace *)colorSpace,
7232 			  genXform, updateColors, map01);
7233     break;
7234   case csDeviceRGB:
7235     dumpDeviceRGBColorSpace((GfxDeviceRGBColorSpace *)colorSpace,
7236 			    genXform, updateColors, map01);
7237     break;
7238   case csCalRGB:
7239     dumpCalRGBColorSpace((GfxCalRGBColorSpace *)colorSpace,
7240 			 genXform, updateColors, map01);
7241     break;
7242   case csDeviceCMYK:
7243     dumpDeviceCMYKColorSpace((GfxDeviceCMYKColorSpace *)colorSpace,
7244 			     genXform, updateColors, map01);
7245     break;
7246   case csLab:
7247     dumpLabColorSpace((GfxLabColorSpace *)colorSpace,
7248 		      genXform, updateColors, map01);
7249     break;
7250   case csICCBased:
7251     dumpICCBasedColorSpace(state, (GfxICCBasedColorSpace *)colorSpace,
7252 			   genXform, updateColors, map01);
7253     break;
7254   case csIndexed:
7255     dumpIndexedColorSpace(state, (GfxIndexedColorSpace *)colorSpace,
7256 			  genXform, updateColors, map01);
7257     break;
7258   case csSeparation:
7259     dumpSeparationColorSpace(state, (GfxSeparationColorSpace *)colorSpace,
7260 			     genXform, updateColors, map01);
7261     break;
7262   case csDeviceN:
7263     if (level >= psLevel3) {
7264       dumpDeviceNColorSpaceL3(state, (GfxDeviceNColorSpace *)colorSpace,
7265 			      genXform, updateColors, map01);
7266     } else {
7267       dumpDeviceNColorSpaceL2(state, (GfxDeviceNColorSpace *)colorSpace,
7268 			      genXform, updateColors, map01);
7269     }
7270     break;
7271   case csPattern:
7272     //~ unimplemented
7273     break;
7274   }
7275 }
7276 
dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7277 void PSOutputDev::dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs,
7278 					   GBool genXform, GBool updateColors,
7279 					   GBool map01) {
7280   writePS("/DeviceGray");
7281   if (genXform) {
7282     writePS(" {}");
7283   }
7284   if (updateColors) {
7285     processColors |= psProcessBlack;
7286   }
7287 }
7288 
dumpCalGrayColorSpace(GfxCalGrayColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7289 void PSOutputDev::dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs,
7290 					GBool genXform, GBool updateColors,
7291 					GBool map01) {
7292   writePS("[/CIEBasedA <<\n");
7293   writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", cs->getGamma());
7294   writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n",
7295 	     cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ());
7296   writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
7297 	     cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ());
7298   writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
7299 	     cs->getBlackX(), cs->getBlackY(), cs->getBlackZ());
7300   writePS(">>]");
7301   if (genXform) {
7302     writePS(" {}");
7303   }
7304   if (updateColors) {
7305     processColors |= psProcessBlack;
7306   }
7307 }
7308 
dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7309 void PSOutputDev::dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs,
7310 					  GBool genXform, GBool updateColors,
7311 					  GBool map01) {
7312   writePS("/DeviceRGB");
7313   if (genXform) {
7314     writePS(" {}");
7315   }
7316   if (updateColors) {
7317     processColors |= psProcessCMYK;
7318   }
7319 }
7320 
dumpCalRGBColorSpace(GfxCalRGBColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7321 void PSOutputDev::dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs,
7322 				       GBool genXform, GBool updateColors,
7323 				       GBool map01) {
7324   writePS("[/CIEBasedABC <<\n");
7325   writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n",
7326 	     cs->getGammaR(), cs->getGammaG(), cs->getGammaB());
7327   writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n",
7328 	     cs->getMatrix()[0], cs->getMatrix()[1], cs->getMatrix()[2],
7329 	     cs->getMatrix()[3], cs->getMatrix()[4], cs->getMatrix()[5],
7330 	     cs->getMatrix()[6], cs->getMatrix()[7], cs->getMatrix()[8]);
7331   writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
7332 	     cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ());
7333   writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
7334 	     cs->getBlackX(), cs->getBlackY(), cs->getBlackZ());
7335   writePS(">>]");
7336   if (genXform) {
7337     writePS(" {}");
7338   }
7339   if (updateColors) {
7340     processColors |= psProcessCMYK;
7341   }
7342 }
7343 
dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7344 void PSOutputDev::dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs,
7345 					   GBool genXform, GBool updateColors,
7346 					   GBool map01) {
7347   writePS("/DeviceCMYK");
7348   if (genXform) {
7349     writePS(" {}");
7350   }
7351   if (updateColors) {
7352     processColors |= psProcessCMYK;
7353   }
7354 }
7355 
dumpLabColorSpace(GfxLabColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7356 void PSOutputDev::dumpLabColorSpace(GfxLabColorSpace *cs,
7357 				    GBool genXform, GBool updateColors,
7358 				    GBool map01) {
7359   writePS("[/CIEBasedABC <<\n");
7360   if (map01) {
7361     writePS(" /RangeABC [0 1 0 1 0 1]\n");
7362     writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n",
7363 	       (cs->getAMax() - cs->getAMin()) / 500.0,
7364 	       cs->getAMin() / 500.0,
7365 	       (cs->getBMax() - cs->getBMin()) / 200.0,
7366 	       cs->getBMin() / 200.0);
7367   } else {
7368     writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n",
7369 	       cs->getAMin(), cs->getAMax(),
7370 	       cs->getBMin(), cs->getBMax());
7371     writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
7372   }
7373   writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
7374   writePS(" /DecodeLMN\n");
7375   writePS("   [{dup 6 29 div ge {dup dup mul mul}\n");
7376   writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
7377 	     cs->getWhiteX());
7378   writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
7379   writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
7380 	     cs->getWhiteY());
7381   writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
7382   writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n",
7383 	     cs->getWhiteZ());
7384   writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
7385 	     cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ());
7386   writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
7387 	     cs->getBlackX(), cs->getBlackY(), cs->getBlackZ());
7388   writePS(">>]");
7389   if (genXform) {
7390     writePS(" {}");
7391   }
7392   if (updateColors) {
7393     processColors |= psProcessCMYK;
7394   }
7395 }
7396 
dumpICCBasedColorSpace(GfxState * state,GfxICCBasedColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7397 void PSOutputDev::dumpICCBasedColorSpace(GfxState *state,
7398 					 GfxICCBasedColorSpace *cs,
7399 					 GBool genXform, GBool updateColors,
7400 					 GBool map01) {
7401   // there is no transform function to the alternate color space, so
7402   // we can use it directly
7403   dumpColorSpaceL2(state, cs->getAlt(), genXform, updateColors, gFalse);
7404 }
7405 
7406 
dumpIndexedColorSpace(GfxState * state,GfxIndexedColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7407 void PSOutputDev::dumpIndexedColorSpace(GfxState *state,
7408 					GfxIndexedColorSpace *cs,
7409 					GBool genXform, GBool updateColors,
7410 					GBool map01) {
7411   GfxColorSpace *baseCS;
7412   GfxLabColorSpace *labCS;
7413   Guchar *lookup, *p;
7414   double x[gfxColorMaxComps], y[gfxColorMaxComps];
7415   double low[gfxColorMaxComps], range[gfxColorMaxComps];
7416   GfxColor color;
7417   GfxCMYK cmyk;
7418   Function *func;
7419   int n, numComps, numAltComps;
7420   int byte;
7421   int i, j, k;
7422 
7423   baseCS = cs->getBase();
7424   writePS("[/Indexed ");
7425   dumpColorSpaceL2(state, baseCS, gFalse, updateColors, gTrue);
7426   n = cs->getIndexHigh();
7427   numComps = baseCS->getNComps();
7428   lookup = cs->getLookup();
7429   writePSFmt(" {0:d} <\n", n);
7430   if (baseCS->getMode() == csDeviceN && level < psLevel3) {
7431     func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
7432     baseCS->getDefaultRanges(low, range, cs->getIndexHigh());
7433     if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) {
7434       labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt();
7435     } else {
7436       labCS = NULL;
7437     }
7438     numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
7439     p = lookup;
7440     for (i = 0; i <= n; i += 8) {
7441       writePS("  ");
7442       for (j = i; j < i+8 && j <= n; ++j) {
7443 	for (k = 0; k < numComps; ++k) {
7444 	  x[k] = low[k] + (*p++ / 255.0) * range[k];
7445 	}
7446 	func->transform(x, y);
7447 	if (labCS) {
7448 	  y[0] /= 100.0;
7449 	  y[1] = (y[1] - labCS->getAMin()) /
7450 	         (labCS->getAMax() - labCS->getAMin());
7451 	  y[2] = (y[2] - labCS->getBMin()) /
7452 	         (labCS->getBMax() - labCS->getBMin());
7453 	}
7454 	for (k = 0; k < numAltComps; ++k) {
7455 	  byte = (int)(y[k] * 255 + 0.5);
7456 	  if (byte < 0) {
7457 	    byte = 0;
7458 	  } else if (byte > 255) {
7459 	    byte = 255;
7460 	  }
7461 	  writePSFmt("{0:02x}", byte);
7462 	}
7463 	if (updateColors) {
7464 	  color.c[0] = dblToCol(j);
7465 	  cs->getCMYK(&color, &cmyk, state->getRenderingIntent());
7466 	  addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
7467 			  colToDbl(cmyk.y), colToDbl(cmyk.k));
7468 	}
7469       }
7470       writePS("\n");
7471     }
7472   } else {
7473     for (i = 0; i <= n; i += 8) {
7474       writePS("  ");
7475       for (j = i; j < i+8 && j <= n; ++j) {
7476 	for (k = 0; k < numComps; ++k) {
7477 	  writePSFmt("{0:02x}", lookup[j * numComps + k]);
7478 	}
7479 	if (updateColors) {
7480 	  color.c[0] = dblToCol(j);
7481 	  cs->getCMYK(&color, &cmyk, state->getRenderingIntent());
7482 	  addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
7483 			  colToDbl(cmyk.y), colToDbl(cmyk.k));
7484 	}
7485       }
7486       writePS("\n");
7487     }
7488   }
7489   writePS(">]");
7490   if (genXform) {
7491     writePS(" {}");
7492   }
7493 }
7494 
dumpSeparationColorSpace(GfxState * state,GfxSeparationColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7495 void PSOutputDev::dumpSeparationColorSpace(GfxState *state,
7496 					   GfxSeparationColorSpace *cs,
7497 					   GBool genXform, GBool updateColors,
7498 					   GBool map01) {
7499   writePS("[/Separation ");
7500   writePSString(cs->getName());
7501   writePS(" ");
7502   dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse);
7503   writePS("\n");
7504   cvtFunction(cs->getFunc());
7505   writePS("]");
7506   if (genXform) {
7507     writePS(" {}");
7508   }
7509   if (updateColors) {
7510     addCustomColor(state, cs);
7511   }
7512 }
7513 
dumpDeviceNColorSpaceL2(GfxState * state,GfxDeviceNColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7514 void PSOutputDev::dumpDeviceNColorSpaceL2(GfxState *state,
7515 					  GfxDeviceNColorSpace *cs,
7516 					  GBool genXform, GBool updateColors,
7517 					  GBool map01) {
7518   dumpColorSpaceL2(state, cs->getAlt(), gFalse, updateColors, map01);
7519   if (genXform) {
7520     writePS(" ");
7521     cvtFunction(cs->getTintTransformFunc());
7522   }
7523 }
7524 
dumpDeviceNColorSpaceL3(GfxState * state,GfxDeviceNColorSpace * cs,GBool genXform,GBool updateColors,GBool map01)7525 void PSOutputDev::dumpDeviceNColorSpaceL3(GfxState *state,
7526 					  GfxDeviceNColorSpace *cs,
7527 					  GBool genXform, GBool updateColors,
7528 					  GBool map01) {
7529   GString *tint;
7530   int i;
7531 
7532   writePS("[/DeviceN [\n");
7533   for (i = 0; i < cs->getNComps(); ++i) {
7534     writePSString(cs->getColorantName(i));
7535     writePS("\n");
7536   }
7537   writePS("]\n");
7538   if ((tint = createDeviceNTintFunc(cs))) {
7539     writePS("/DeviceCMYK\n");
7540     writePS(tint->getCString());
7541     delete tint;
7542   } else {
7543     dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse);
7544     writePS("\n");
7545     cvtFunction(cs->getTintTransformFunc());
7546   }
7547   writePS("]");
7548   if (genXform) {
7549     writePS(" {}");
7550   }
7551   if (updateColors) {
7552     addCustomColors(state, cs);
7553   }
7554 }
7555 
7556 // If the DeviceN color space has a Colorants dictionary, and all of
7557 // the colorants are one of: "None", "Cyan", "Magenta", "Yellow",
7558 // "Black", or have an entry in the Colorants dict that maps to
7559 // DeviceCMYK, then build a new tint function; else use the existing
7560 // tint function.
createDeviceNTintFunc(GfxDeviceNColorSpace * cs)7561 GString *PSOutputDev::createDeviceNTintFunc(GfxDeviceNColorSpace *cs) {
7562   Object *attrs;
7563   Object colorants, sepCSObj, funcObj, obj1;
7564   GString *name;
7565   Function *func;
7566   double sepIn;
7567   double cmyk[gfxColorMaxComps][4];
7568   GString *tint;
7569   GBool first;
7570   int i, j;
7571 
7572   attrs = cs->getAttrs();
7573   if (!attrs->isDict()) {
7574     return NULL;
7575   }
7576   if (!attrs->dictLookup("Colorants", &colorants)->isDict()) {
7577     colorants.free();
7578     return NULL;
7579   }
7580   for (i = 0; i < cs->getNComps(); ++i) {
7581     name = cs->getColorantName(i);
7582     if (!name->cmp("None")) {
7583       cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0;
7584     } else if (!name->cmp("Cyan")) {
7585       cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0;
7586       cmyk[i][0] = 1;
7587     } else if (!name->cmp("Magenta")) {
7588       cmyk[i][0] = cmyk[i][2] = cmyk[i][3] = 0;
7589       cmyk[i][1] = 1;
7590     } else if (!name->cmp("Yellow")) {
7591       cmyk[i][0] = cmyk[i][1] = cmyk[i][3] = 0;
7592       cmyk[i][2] = 1;
7593     } else if (!name->cmp("Black")) {
7594       cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = 0;
7595       cmyk[i][3] = 1;
7596     } else {
7597       colorants.dictLookup(name->getCString(), &sepCSObj);
7598       if (!sepCSObj.isArray() || sepCSObj.arrayGetLength() != 4) {
7599 	sepCSObj.free();
7600 	colorants.free();
7601 	return NULL;
7602       }
7603       if (!sepCSObj.arrayGet(0, &obj1)->isName("Separation")) {
7604 	obj1.free();
7605 	sepCSObj.free();
7606 	colorants.free();
7607 	return NULL;
7608       }
7609       obj1.free();
7610       if (!sepCSObj.arrayGet(2, &obj1)->isName("DeviceCMYK")) {
7611 	obj1.free();
7612 	sepCSObj.free();
7613 	colorants.free();
7614 	return NULL;
7615       }
7616       obj1.free();
7617       sepCSObj.arrayGet(3, &funcObj);
7618       if (!(func = Function::parse(&funcObj, 1, 4))) {
7619 	funcObj.free();
7620 	sepCSObj.free();
7621 	colorants.free();
7622 	return NULL;
7623       }
7624       funcObj.free();
7625       sepIn = 1;
7626       func->transform(&sepIn, cmyk[i]);
7627       delete func;
7628       sepCSObj.free();
7629     }
7630   }
7631   colorants.free();
7632 
7633   tint = new GString();
7634   tint->append("{\n");
7635   for (j = 0; j < 4; ++j) {  // C, M, Y, K
7636     first = gTrue;
7637     for (i = 0; i < cs->getNComps(); ++i) {
7638       if (cmyk[i][j] != 0) {
7639 	tint->appendf("{0:d} index {1:.4f} mul{2:s}\n",
7640 		      j + cs->getNComps() - 1 - i, cmyk[i][j],
7641 		      first ? "" : " add");
7642 	first = gFalse;
7643       }
7644     }
7645     if (first) {
7646       tint->append("0\n");
7647     }
7648   }
7649   tint->appendf("{0:d} 4 roll\n", cs->getNComps() + 4);
7650   for (i = 0; i < cs->getNComps(); ++i) {
7651     tint->append("pop\n");
7652   }
7653   tint->append("}\n");
7654 
7655   return tint;
7656 }
7657 
7658 #if OPI_SUPPORT
opiBegin(GfxState * state,Dict * opiDict)7659 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
7660   Object dict;
7661 
7662   if (globalParams->getPSOPI()) {
7663     opiDict->lookup("2.0", &dict);
7664     if (dict.isDict()) {
7665       opiBegin20(state, dict.getDict());
7666       dict.free();
7667     } else {
7668       dict.free();
7669       opiDict->lookup("1.3", &dict);
7670       if (dict.isDict()) {
7671 	opiBegin13(state, dict.getDict());
7672       }
7673       dict.free();
7674     }
7675   }
7676 }
7677 
opiBegin20(GfxState * state,Dict * dict)7678 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
7679   Object obj1, obj2, obj3, obj4;
7680   double width, height, left, right, top, bottom;
7681   int w, h;
7682   int i;
7683 
7684   writePS("%%BeginOPI: 2.0\n");
7685   writePS("%%Distilled\n");
7686 
7687   dict->lookup("F", &obj1);
7688   if (getFileSpec(&obj1, &obj2)) {
7689     writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString());
7690     obj2.free();
7691   }
7692   obj1.free();
7693 
7694   dict->lookup("MainImage", &obj1);
7695   if (obj1.isString()) {
7696     writePSFmt("%%MainImage: {0:t}\n", obj1.getString());
7697   }
7698   obj1.free();
7699 
7700   //~ ignoring 'Tags' entry
7701   //~ need to use writePSString() and deal with >255-char lines
7702 
7703   dict->lookup("Size", &obj1);
7704   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
7705     obj1.arrayGet(0, &obj2);
7706     width = obj2.getNum();
7707     obj2.free();
7708     obj1.arrayGet(1, &obj2);
7709     height = obj2.getNum();
7710     obj2.free();
7711     writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width, height);
7712   }
7713   obj1.free();
7714 
7715   dict->lookup("CropRect", &obj1);
7716   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
7717     obj1.arrayGet(0, &obj2);
7718     left = obj2.getNum();
7719     obj2.free();
7720     obj1.arrayGet(1, &obj2);
7721     top = obj2.getNum();
7722     obj2.free();
7723     obj1.arrayGet(2, &obj2);
7724     right = obj2.getNum();
7725     obj2.free();
7726     obj1.arrayGet(3, &obj2);
7727     bottom = obj2.getNum();
7728     obj2.free();
7729     writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
7730 	       left, top, right, bottom);
7731   }
7732   obj1.free();
7733 
7734   dict->lookup("Overprint", &obj1);
7735   if (obj1.isBool()) {
7736     writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false");
7737   }
7738   obj1.free();
7739 
7740   dict->lookup("Inks", &obj1);
7741   if (obj1.isName()) {
7742     writePSFmt("%%ImageInks: {0:s}\n", obj1.getName());
7743   } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
7744     obj1.arrayGet(0, &obj2);
7745     if (obj2.isName()) {
7746       writePSFmt("%%ImageInks: {0:s} {1:d}",
7747 		 obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
7748       for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
7749 	obj1.arrayGet(i, &obj3);
7750 	obj1.arrayGet(i+1, &obj4);
7751 	if (obj3.isString() && obj4.isNum()) {
7752 	  writePS(" ");
7753 	  writePSString(obj3.getString());
7754 	  writePSFmt(" {0:.4g}", obj4.getNum());
7755 	}
7756 	obj3.free();
7757 	obj4.free();
7758       }
7759       writePS("\n");
7760     }
7761     obj2.free();
7762   }
7763   obj1.free();
7764 
7765   writePS("gsave\n");
7766 
7767   writePS("%%BeginIncludedImage\n");
7768 
7769   dict->lookup("IncludedImageDimensions", &obj1);
7770   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
7771     obj1.arrayGet(0, &obj2);
7772     w = obj2.getInt();
7773     obj2.free();
7774     obj1.arrayGet(1, &obj2);
7775     h = obj2.getInt();
7776     obj2.free();
7777     writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h);
7778   }
7779   obj1.free();
7780 
7781   dict->lookup("IncludedImageQuality", &obj1);
7782   if (obj1.isNum()) {
7783     writePSFmt("%%IncludedImageQuality: {0:.4g}\n", obj1.getNum());
7784   }
7785   obj1.free();
7786 
7787   ++opi20Nest;
7788 }
7789 
opiBegin13(GfxState * state,Dict * dict)7790 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
7791   Object obj1, obj2;
7792   int left, right, top, bottom, samples, bits, width, height;
7793   double c, m, y, k;
7794   double llx, lly, ulx, uly, urx, ury, lrx, lry;
7795   double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
7796   double horiz, vert;
7797   int i, j;
7798 
7799   writePS("save\n");
7800   writePS("/opiMatrix2 matrix currentmatrix def\n");
7801   writePS("opiMatrix setmatrix\n");
7802 
7803   dict->lookup("F", &obj1);
7804   if (getFileSpec(&obj1, &obj2)) {
7805     writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString());
7806     obj2.free();
7807   }
7808   obj1.free();
7809 
7810   dict->lookup("CropRect", &obj1);
7811   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
7812     obj1.arrayGet(0, &obj2);
7813     left = obj2.getInt();
7814     obj2.free();
7815     obj1.arrayGet(1, &obj2);
7816     top = obj2.getInt();
7817     obj2.free();
7818     obj1.arrayGet(2, &obj2);
7819     right = obj2.getInt();
7820     obj2.free();
7821     obj1.arrayGet(3, &obj2);
7822     bottom = obj2.getInt();
7823     obj2.free();
7824     writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n",
7825 	       left, top, right, bottom);
7826   }
7827   obj1.free();
7828 
7829   dict->lookup("Color", &obj1);
7830   if (obj1.isArray() && obj1.arrayGetLength() == 5) {
7831     obj1.arrayGet(0, &obj2);
7832     c = obj2.getNum();
7833     obj2.free();
7834     obj1.arrayGet(1, &obj2);
7835     m = obj2.getNum();
7836     obj2.free();
7837     obj1.arrayGet(2, &obj2);
7838     y = obj2.getNum();
7839     obj2.free();
7840     obj1.arrayGet(3, &obj2);
7841     k = obj2.getNum();
7842     obj2.free();
7843     obj1.arrayGet(4, &obj2);
7844     if (obj2.isString()) {
7845       writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ",
7846 		 c, m, y, k);
7847       writePSString(obj2.getString());
7848       writePS("\n");
7849     }
7850     obj2.free();
7851   }
7852   obj1.free();
7853 
7854   dict->lookup("ColorType", &obj1);
7855   if (obj1.isName()) {
7856     writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName());
7857   }
7858   obj1.free();
7859 
7860   //~ ignores 'Comments' entry
7861   //~ need to handle multiple lines
7862 
7863   dict->lookup("CropFixed", &obj1);
7864   if (obj1.isArray()) {
7865     obj1.arrayGet(0, &obj2);
7866     ulx = obj2.getNum();
7867     obj2.free();
7868     obj1.arrayGet(1, &obj2);
7869     uly = obj2.getNum();
7870     obj2.free();
7871     obj1.arrayGet(2, &obj2);
7872     lrx = obj2.getNum();
7873     obj2.free();
7874     obj1.arrayGet(3, &obj2);
7875     lry = obj2.getNum();
7876     obj2.free();
7877     writePSFmt("%ALDImageCropFixed: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n",
7878 	       ulx, uly, lrx, lry);
7879   }
7880   obj1.free();
7881 
7882   dict->lookup("GrayMap", &obj1);
7883   if (obj1.isArray()) {
7884     writePS("%ALDImageGrayMap:");
7885     for (i = 0; i < obj1.arrayGetLength(); i += 16) {
7886       if (i > 0) {
7887 	writePS("\n%%+");
7888       }
7889       for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
7890 	obj1.arrayGet(i+j, &obj2);
7891 	writePSFmt(" {0:d}", obj2.getInt());
7892 	obj2.free();
7893       }
7894     }
7895     writePS("\n");
7896   }
7897   obj1.free();
7898 
7899   dict->lookup("ID", &obj1);
7900   if (obj1.isString()) {
7901     writePSFmt("%ALDImageID: {0:t}\n", obj1.getString());
7902   }
7903   obj1.free();
7904 
7905   dict->lookup("ImageType", &obj1);
7906   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
7907     obj1.arrayGet(0, &obj2);
7908     samples = obj2.getInt();
7909     obj2.free();
7910     obj1.arrayGet(1, &obj2);
7911     bits = obj2.getInt();
7912     obj2.free();
7913     writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits);
7914   }
7915   obj1.free();
7916 
7917   dict->lookup("Overprint", &obj1);
7918   if (obj1.isBool()) {
7919     writePSFmt("%ALDImageOverprint: {0:s}\n",
7920 	       obj1.getBool() ? "true" : "false");
7921   }
7922   obj1.free();
7923 
7924   dict->lookup("Position", &obj1);
7925   if (obj1.isArray() && obj1.arrayGetLength() == 8) {
7926     obj1.arrayGet(0, &obj2);
7927     llx = obj2.getNum();
7928     obj2.free();
7929     obj1.arrayGet(1, &obj2);
7930     lly = obj2.getNum();
7931     obj2.free();
7932     obj1.arrayGet(2, &obj2);
7933     ulx = obj2.getNum();
7934     obj2.free();
7935     obj1.arrayGet(3, &obj2);
7936     uly = obj2.getNum();
7937     obj2.free();
7938     obj1.arrayGet(4, &obj2);
7939     urx = obj2.getNum();
7940     obj2.free();
7941     obj1.arrayGet(5, &obj2);
7942     ury = obj2.getNum();
7943     obj2.free();
7944     obj1.arrayGet(6, &obj2);
7945     lrx = obj2.getNum();
7946     obj2.free();
7947     obj1.arrayGet(7, &obj2);
7948     lry = obj2.getNum();
7949     obj2.free();
7950     opiTransform(state, llx, lly, &tllx, &tlly);
7951     opiTransform(state, ulx, uly, &tulx, &tuly);
7952     opiTransform(state, urx, ury, &turx, &tury);
7953     opiTransform(state, lrx, lry, &tlrx, &tlry);
7954     writePSFmt("%ALDImagePosition: {0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g}\n",
7955 	       tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
7956     obj2.free();
7957   }
7958   obj1.free();
7959 
7960   dict->lookup("Resolution", &obj1);
7961   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
7962     obj1.arrayGet(0, &obj2);
7963     horiz = obj2.getNum();
7964     obj2.free();
7965     obj1.arrayGet(1, &obj2);
7966     vert = obj2.getNum();
7967     obj2.free();
7968     writePSFmt("%ALDImageResoution: {0:.4g} {1:.4g}\n", horiz, vert);
7969     obj2.free();
7970   }
7971   obj1.free();
7972 
7973   dict->lookup("Size", &obj1);
7974   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
7975     obj1.arrayGet(0, &obj2);
7976     width = obj2.getInt();
7977     obj2.free();
7978     obj1.arrayGet(1, &obj2);
7979     height = obj2.getInt();
7980     obj2.free();
7981     writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height);
7982   }
7983   obj1.free();
7984 
7985   //~ ignoring 'Tags' entry
7986   //~ need to use writePSString() and deal with >255-char lines
7987 
7988   dict->lookup("Tint", &obj1);
7989   if (obj1.isNum()) {
7990     writePSFmt("%ALDImageTint: {0:.4g}\n", obj1.getNum());
7991   }
7992   obj1.free();
7993 
7994   dict->lookup("Transparency", &obj1);
7995   if (obj1.isBool()) {
7996     writePSFmt("%ALDImageTransparency: {0:s}\n",
7997 	       obj1.getBool() ? "true" : "false");
7998   }
7999   obj1.free();
8000 
8001   writePS("%%BeginObject: image\n");
8002   writePS("opiMatrix2 setmatrix\n");
8003   ++opi13Nest;
8004 }
8005 
8006 // Convert PDF user space coordinates to PostScript default user space
8007 // coordinates.  This has to account for both the PDF CTM and the
8008 // PSOutputDev page-fitting transform.
opiTransform(GfxState * state,double x0,double y0,double * x1,double * y1)8009 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
8010 			       double *x1, double *y1) {
8011   double t;
8012 
8013   state->transform(x0, y0, x1, y1);
8014   *x1 += tx;
8015   *y1 += ty;
8016   if (rotate == 90) {
8017     t = *x1;
8018     *x1 = -*y1;
8019     *y1 = t;
8020   } else if (rotate == 180) {
8021     *x1 = -*x1;
8022     *y1 = -*y1;
8023   } else if (rotate == 270) {
8024     t = *x1;
8025     *x1 = *y1;
8026     *y1 = -t;
8027   }
8028   *x1 *= xScale;
8029   *y1 *= yScale;
8030 }
8031 
opiEnd(GfxState * state,Dict * opiDict)8032 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
8033   Object dict;
8034 
8035   if (globalParams->getPSOPI()) {
8036     opiDict->lookup("2.0", &dict);
8037     if (dict.isDict()) {
8038       writePS("%%EndIncludedImage\n");
8039       writePS("%%EndOPI\n");
8040       writePS("grestore\n");
8041       --opi20Nest;
8042       dict.free();
8043     } else {
8044       dict.free();
8045       opiDict->lookup("1.3", &dict);
8046       if (dict.isDict()) {
8047 	writePS("%%EndObject\n");
8048 	writePS("restore\n");
8049 	--opi13Nest;
8050       }
8051       dict.free();
8052     }
8053   }
8054 }
8055 
getFileSpec(Object * fileSpec,Object * fileName)8056 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
8057   if (fileSpec->isString()) {
8058     fileSpec->copy(fileName);
8059     return gTrue;
8060   }
8061   if (fileSpec->isDict()) {
8062     fileSpec->dictLookup("DOS", fileName);
8063     if (fileName->isString()) {
8064       return gTrue;
8065     }
8066     fileName->free();
8067     fileSpec->dictLookup("Mac", fileName);
8068     if (fileName->isString()) {
8069       return gTrue;
8070     }
8071     fileName->free();
8072     fileSpec->dictLookup("Unix", fileName);
8073     if (fileName->isString()) {
8074       return gTrue;
8075     }
8076     fileName->free();
8077     fileSpec->dictLookup("F", fileName);
8078     if (fileName->isString()) {
8079       return gTrue;
8080     }
8081     fileName->free();
8082   }
8083   return gFalse;
8084 }
8085 #endif // OPI_SUPPORT
8086 
type3D0(GfxState * state,double wx,double wy)8087 void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
8088   writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx, wy);
8089   writePS("q\n");
8090   t3NeedsRestore = gTrue;
8091   noStateChanges = gFalse;
8092 }
8093 
type3D1(GfxState * state,double wx,double wy,double llx,double lly,double urx,double ury)8094 void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
8095 			  double llx, double lly, double urx, double ury) {
8096   if (t3String) {
8097     error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc");
8098     return;
8099   }
8100   t3WX = wx;
8101   t3WY = wy;
8102   t3LLX = llx;
8103   t3LLY = lly;
8104   t3URX = urx;
8105   t3URY = ury;
8106   t3String = new GString();
8107   writePS("q\n");
8108   t3FillColorOnly = gTrue;
8109   t3Cacheable = gTrue;
8110   t3NeedsRestore = gTrue;
8111   noStateChanges = gFalse;
8112 }
8113 
drawForm(Ref id)8114 void PSOutputDev::drawForm(Ref id) {
8115   writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen);
8116   noStateChanges = gFalse;
8117 }
8118 
psXObject(Stream * psStream,Stream * level1Stream)8119 void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
8120   Stream *str;
8121   char buf[4096];
8122   int n;
8123 
8124   if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
8125     str = level1Stream;
8126   } else {
8127     str = psStream;
8128   }
8129   str->reset();
8130   while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
8131     writePSBlock(buf, n);
8132   }
8133   str->close();
8134   noStateChanges = gFalse;
8135 }
8136 
8137 //~ can nextFunc be reset to 0 -- maybe at the start of each page?
8138 //~   or maybe at the start of each color space / pattern?
cvtFunction(Function * func)8139 void PSOutputDev::cvtFunction(Function *func) {
8140   SampledFunction *func0;
8141   ExponentialFunction *func2;
8142   StitchingFunction *func3;
8143   PostScriptFunction *func4;
8144   int thisFunc, m, n, nSamples, i, j, k;
8145 
8146   switch (func->getType()) {
8147 
8148   case -1:			// identity
8149     writePS("{}\n");
8150     break;
8151 
8152   case 0:			// sampled
8153     func0 = (SampledFunction *)func;
8154     thisFunc = nextFunc++;
8155     m = func0->getInputSize();
8156     n = func0->getOutputSize();
8157     nSamples = n;
8158     for (i = 0; i < m; ++i) {
8159       nSamples *= func0->getSampleSize(i);
8160     }
8161     writePSFmt("/xpdfSamples{0:d} [\n", thisFunc);
8162     for (i = 0; i < nSamples; ++i) {
8163       writePSFmt("{0:.6g}\n", func0->getSamples()[i]);
8164     }
8165     writePS("] def\n");
8166     writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2);
8167     // [e01] [efrac] x0 x1 ... xm-1
8168     for (i = m-1; i >= 0; --i) {
8169       // [e01] [efrac] x0 x1 ... xi
8170       writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n",
8171 	      func0->getDomainMin(i),
8172 	      (func0->getEncodeMax(i) - func0->getEncodeMin(i)) /
8173 	        (func0->getDomainMax(i) - func0->getDomainMin(i)),
8174 	      func0->getEncodeMin(i));
8175       // [e01] [efrac] x0 x1 ... xi-1 xi'
8176       writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n",
8177 		 func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1);
8178       // [e01] [efrac] x0 x1 ... xi-1 xi'
8179       writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
8180       // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
8181       writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i);
8182       // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
8183       writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1);
8184       // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
8185       writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i);
8186       // [e01] [efrac] x0 x1 ... xi-1
8187     }
8188     // [e01] [efrac]
8189     for (i = 0; i < n; ++i) {
8190       // [e01] [efrac] y(0) ... y(i-1)
8191       for (j = 0; j < (1<<m); ++j) {
8192 	// [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
8193 	writePSFmt("xpdfSamples{0:d}\n", thisFunc);
8194 	k = m - 1;
8195 	writePSFmt("{0:d} index {1:d} get\n", i+j+2, 2 * k + ((j >> k) & 1));
8196 	for (k = m - 2; k >= 0; --k) {
8197 	  writePSFmt("{0:d} mul {1:d} index {2:d} get add\n",
8198 		     func0->getSampleSize(k),
8199 		     i + j + 3,
8200 		     2 * k + ((j >> k) & 1));
8201 	}
8202 	if (n > 1) {
8203 	  writePSFmt("{0:d} mul {1:d} add ", n, i);
8204 	}
8205 	writePS("get\n");
8206       }
8207       // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
8208       for (j = 0; j < m; ++j) {
8209 	// [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
8210 	for (k = 0; k < (1 << (m - j)); k += 2) {
8211 	  // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
8212 	  writePSFmt("{0:d} index {1:d} get dup\n",
8213 		     i + k/2 + (1 << (m-j)) - k, j);
8214 	  writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
8215 	  writePSFmt("{0:d} 1 roll\n", k/2 + (1 << (m-j)) - k - 1);
8216 	}
8217 	// [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
8218       }
8219       // [e01] [efrac] y(0) ... y(i-1) s
8220       writePSFmt("{0:.6g} mul {1:.6g} add\n",
8221 		 func0->getDecodeMax(i) - func0->getDecodeMin(i),
8222 		 func0->getDecodeMin(i));
8223       writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
8224 		 func0->getRangeMin(i), func0->getRangeMin(i),
8225 		 func0->getRangeMax(i), func0->getRangeMax(i));
8226       // [e01] [efrac] y(0) ... y(i-1) y(i)
8227     }
8228     // [e01] [efrac] y(0) ... y(n-1)
8229     writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n);
8230     break;
8231 
8232   case 2:			// exponential
8233     func2 = (ExponentialFunction *)func;
8234     n = func2->getOutputSize();
8235     writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
8236 	       func2->getDomainMin(0), func2->getDomainMin(0),
8237 	       func2->getDomainMax(0), func2->getDomainMax(0));
8238     // x
8239     for (i = 0; i < n; ++i) {
8240       // x y(0) .. y(i-1)
8241       writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n",
8242 		 i, func2->getE(), func2->getC1()[i] - func2->getC0()[i],
8243 		 func2->getC0()[i]);
8244       if (func2->getHasRange()) {
8245 	writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
8246 		   func2->getRangeMin(i), func2->getRangeMin(i),
8247 		   func2->getRangeMax(i), func2->getRangeMax(i));
8248       }
8249     }
8250     // x y(0) .. y(n-1)
8251     writePSFmt("{0:d} {1:d} roll pop }}\n", n+1, n);
8252     break;
8253 
8254   case 3:			// stitching
8255     func3 = (StitchingFunction *)func;
8256     thisFunc = nextFunc++;
8257     for (i = 0; i < func3->getNumFuncs(); ++i) {
8258       cvtFunction(func3->getFunc(i));
8259       writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i);
8260     }
8261     writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
8262 	       func3->getDomainMin(0), func3->getDomainMin(0),
8263 	       func3->getDomainMax(0), func3->getDomainMax(0));
8264     for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
8265       writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n",
8266 		 func3->getBounds()[i+1],
8267 		 func3->getBounds()[i],
8268 		 func3->getScale()[i],
8269 		 func3->getEncode()[2*i],
8270 		 thisFunc, i);
8271     }
8272     writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n",
8273 	       func3->getBounds()[i],
8274 	       func3->getScale()[i],
8275 	       func3->getEncode()[2*i],
8276 	       thisFunc, i);
8277     for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
8278       writePS("} ifelse\n");
8279     }
8280     writePS("}\n");
8281     break;
8282 
8283   case 4:			// PostScript
8284     func4 = (PostScriptFunction *)func;
8285     writePS(func4->getCodeString()->getCString());
8286     writePS("\n");
8287     break;
8288   }
8289 }
8290 
writePSChar(char c)8291 void PSOutputDev::writePSChar(char c) {
8292   if (t3String) {
8293     t3String->append(c);
8294   } else {
8295     (*outputFunc)(outputStream, &c, 1);
8296   }
8297 }
8298 
writePSBlock(char * s,int len)8299 void PSOutputDev::writePSBlock(char *s, int len) {
8300   if (t3String) {
8301     t3String->append(s, len);
8302   } else {
8303     (*outputFunc)(outputStream, s, len);
8304   }
8305 }
8306 
writePS(const char * s)8307 void PSOutputDev::writePS(const char *s) {
8308   if (t3String) {
8309     t3String->append(s);
8310   } else {
8311     (*outputFunc)(outputStream, s, (int)strlen(s));
8312   }
8313 }
8314 
writePSFmt(const char * fmt,...)8315 void PSOutputDev::writePSFmt(const char *fmt, ...) {
8316   va_list args;
8317   GString *buf;
8318 
8319   va_start(args, fmt);
8320   if (t3String) {
8321     t3String->appendfv((char *)fmt, args);
8322   } else {
8323     buf = GString::formatv((char *)fmt, args);
8324     (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
8325     delete buf;
8326   }
8327   va_end(args);
8328 }
8329 
writePSString(GString * s)8330 void PSOutputDev::writePSString(GString *s) {
8331   Guchar *p;
8332   int n, line;
8333   char buf[8];
8334 
8335   writePSChar('(');
8336   line = 1;
8337   for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
8338     if (line >= 64) {
8339       writePSChar('\\');
8340       writePSChar('\n');
8341       line = 0;
8342     }
8343     if (*p == '(' || *p == ')' || *p == '\\') {
8344       writePSChar('\\');
8345       writePSChar((char)*p);
8346       line += 2;
8347     } else if (*p < 0x20 || *p >= 0x80) {
8348       snprintf(buf, sizeof(buf), "\\%03o", *p);
8349       writePS(buf);
8350       line += 4;
8351     } else {
8352       writePSChar((char)*p);
8353       ++line;
8354     }
8355   }
8356   writePSChar(')');
8357 }
8358 
writePSName(const char * s)8359 void PSOutputDev::writePSName(const char *s) {
8360   const char *p;
8361   char c;
8362 
8363   p = s;
8364   while ((c = *p++)) {
8365     if (c <= (char)0x20 || c >= (char)0x7f ||
8366 	c == '(' || c == ')' || c == '<' || c == '>' ||
8367 	c == '[' || c == ']' || c == '{' || c == '}' ||
8368 	c == '/' || c == '%') {
8369       writePSFmt("#{0:02x}", c & 0xff);
8370     } else {
8371       writePSChar(c);
8372     }
8373   }
8374 }
8375 
filterPSName(GString * name)8376 GString *PSOutputDev::filterPSName(GString *name) {
8377   GString *name2;
8378   char buf[8];
8379   int i;
8380   char c;
8381 
8382   name2 = new GString();
8383 
8384   // ghostscript chokes on names that begin with out-of-limits
8385   // numbers, e.g., 1e4foo is handled correctly (as a name), but
8386   // 1e999foo generates a limitcheck error
8387   c = name->getChar(0);
8388   if (c >= '0' && c <= '9') {
8389     name2->append('f');
8390   }
8391 
8392   for (i = 0; i < name->getLength(); ++i) {
8393     c = name->getChar(i);
8394     if (c <= (char)0x20 || c >= (char)0x7f ||
8395 	c == '(' || c == ')' || c == '<' || c == '>' ||
8396 	c == '[' || c == ']' || c == '{' || c == '}' ||
8397 	c == '/' || c == '%') {
8398       snprintf(buf, sizeof(buf), "#%02x", c & 0xff);
8399       name2->append(buf);
8400     } else {
8401       name2->append(c);
8402     }
8403   }
8404   return name2;
8405 }
8406 
8407 // Write a DSC-compliant <textline>.
writePSTextLine(GString * s)8408 void PSOutputDev::writePSTextLine(GString *s) {
8409   TextString *ts;
8410   Unicode *u;
8411   int i, j;
8412   int c;
8413 
8414   // - DSC comments must be printable ASCII; control chars and
8415   //   backslashes have to be escaped (we do cheap Unicode-to-ASCII
8416   //   conversion by simply ignoring the high byte)
8417   // - lines are limited to 255 chars (we limit to 200 here to allow
8418   //   for the keyword, which was emitted by the caller)
8419   // - lines that start with a left paren are treated as <text>
8420   //   instead of <textline>, so we escape a leading paren
8421   ts = new TextString(s);
8422   u = ts->getUnicode();
8423   for (i = 0, j = 0; i < ts->getLength() && j < 200; ++i) {
8424     c = u[i] & 0xff;
8425     if (c == '\\') {
8426       writePS("\\\\");
8427       j += 2;
8428     } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) {
8429       writePSFmt("\\{0:03o}", c);
8430       j += 4;
8431     } else {
8432       writePSChar((char)c);
8433       ++j;
8434     }
8435   }
8436   writePS("\n");
8437   delete ts;
8438 }
8439