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