source: trunk/poppler/mypoppler/poppler/PSOutputDev.cc @ 91

Last change on this file since 91 was 91, checked in by Eugene Romanenko, 15 years ago

bitmap printing, preliminary postscript printing, export to PS for djvu plugin, other improvements

File size: 134.5 KB
Line 
1//========================================================================
2//
3// PSOutputDev.cc
4//
5// Copyright 1996-2003 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <config.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 "goo/GooString.h"
21#include "goo/GooList.h"
22#include "poppler-config.h"
23#include "GlobalParams.h"
24#include "Object.h"
25#include "Error.h"
26#include "Function.h"
27#include "Gfx.h"
28#include "GfxState.h"
29#include "GfxFont.h"
30#include "UnicodeMap.h"
31#include <fofi/FoFiType1C.h>
32#include <fofi/FoFiTrueType.h>
33#include "Catalog.h"
34#include "Page.h"
35#include "Stream.h"
36#include "Annot.h"
37#include "PSOutputDev.h"
38#include "UGooString.h"
39
40#ifdef MACOS
41// needed for setting type/creator of MacOS files
42#include "ICSupport.h"
43#endif
44
45//------------------------------------------------------------------------
46// PostScript prolog and setup
47//------------------------------------------------------------------------
48
49// The '~' escapes mark prolog code that is emitted only in certain
50// levels:
51//
52//   ~[123][sn]
53//      ^   ^----- s=psLevel*Sep, n=psLevel*
54//      +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3*
55
56static char *prolog[] = {
57  "/xpdf 75 dict def xpdf begin",
58  "% PDF special state",
59  "/pdfDictSize 15 def",
60  "~1sn",
61  "/pdfStates 64 array def",
62  "  0 1 63 {",
63  "    pdfStates exch pdfDictSize dict",
64  "    dup /pdfStateIdx 3 index put",
65  "    put",
66  "  } for",
67  "~123sn",
68  "/pdfSetup {",
69  "  3 1 roll 2 array astore",
70  "  /setpagedevice where {",
71  "    pop 3 dict begin",
72  "      /PageSize exch def",
73  "      /ImagingBBox null def",
74  "      /Policies 1 dict dup begin /PageSize 3 def end def",
75  "      { /Duplex true def } if",
76  "    currentdict end setpagedevice",
77  "  } {",
78  "    pop pop",
79  "  } ifelse",
80  "} def",
81  "~1sn",
82  "/pdfOpNames [",
83  "  /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
84  "  /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
85  "  /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
86  "] def",
87  "~123sn",
88  "/pdfStartPage {",
89  "~1sn",
90  "  pdfStates 0 get begin",
91  "~23sn",
92  "  pdfDictSize dict begin",
93  "~23n",
94  "  /pdfFillCS [] def",
95  "  /pdfFillXform {} def",
96  "  /pdfStrokeCS [] def",
97  "  /pdfStrokeXform {} def",
98  "~1n",
99  "  /pdfFill 0 def",
100  "  /pdfStroke 0 def",
101  "~1s",
102  "  /pdfFill [0 0 0 1] def",
103  "  /pdfStroke [0 0 0 1] def",
104  "~23sn",
105  "  /pdfFill [0] def",
106  "  /pdfStroke [0] def",
107  "  /pdfFillOP false def",
108  "  /pdfStrokeOP false def",
109  "~123sn",
110  "  /pdfLastFill false def",
111  "  /pdfLastStroke false def",
112  "  /pdfTextMat [1 0 0 1 0 0] def",
113  "  /pdfFontSize 0 def",
114  "  /pdfCharSpacing 0 def",
115  "  /pdfTextRender 0 def",
116  "  /pdfTextRise 0 def",
117  "  /pdfWordSpacing 0 def",
118  "  /pdfHorizScaling 1 def",
119  "  /pdfTextClipPath [] def",
120  "} def",
121  "/pdfEndPage { end } def",
122  "~23s",
123  "% separation convention operators",
124  "/findcmykcustomcolor where {",
125  "  pop",
126  "}{",
127  "  /findcmykcustomcolor { 5 array astore } def",
128  "} ifelse",
129  "/setcustomcolor where {",
130  "  pop",
131  "}{",
132  "  /setcustomcolor {",
133  "    exch",
134  "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
135  "      0 4 getinterval cvx",
136  "      [ exch /dup load exch { mul exch dup } /forall load",
137  "        /pop load dup ] cvx",
138  "    ] setcolorspace setcolor",
139  "  } def",
140  "} ifelse",
141  "/customcolorimage where {",
142  "  pop",
143  "}{",
144  "  /customcolorimage {",
145  "    gsave",
146  "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
147  "      0 4 getinterval",
148  "      [ exch /dup load exch { mul exch dup } /forall load",
149  "        /pop load dup ] cvx",
150  "    ] setcolorspace",
151  "    10 dict begin",
152  "      /ImageType 1 def",
153  "      /DataSource exch def",
154  "      /ImageMatrix exch def",
155  "      /BitsPerComponent exch def",
156  "      /Height exch def",
157  "      /Width exch def",
158  "      /Decode [1 0] def",
159  "    currentdict end",
160  "    image",
161  "    grestore",
162  "  } def",
163  "} ifelse",
164  "~123sn",
165  "% PDF color state",
166  "~1n",
167  "/g { dup /pdfFill exch def setgray",
168  "     /pdfLastFill true def /pdfLastStroke false def } def",
169  "/G { dup /pdfStroke exch def setgray",
170  "     /pdfLastStroke true def /pdfLastFill false def } def",
171  "/fCol {",
172  "  pdfLastFill not {",
173  "    pdfFill setgray",
174  "    /pdfLastFill true def /pdfLastStroke false def",
175  "  } if",
176  "} def",
177  "/sCol {",
178  "  pdfLastStroke not {",
179  "    pdfStroke setgray",
180  "    /pdfLastStroke true def /pdfLastFill false def",
181  "  } if",
182  "} def",
183  "~1s",
184  "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
185  "     /pdfLastFill true def /pdfLastStroke false def } def",
186  "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
187  "     /pdfLastStroke true def /pdfLastFill false def } def",
188  "/fCol {",
189  "  pdfLastFill not {",
190  "    pdfFill aload pop setcmykcolor",
191  "    /pdfLastFill true def /pdfLastStroke false def",
192  "  } if",
193  "} def",
194  "/sCol {",
195  "  pdfLastStroke not {",
196  "    pdfStroke aload pop setcmykcolor",
197  "    /pdfLastStroke true def /pdfLastFill false def",
198  "  } if",
199  "} def",
200  "~23n",
201  "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
202  "      setcolorspace } def",
203  "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
204  "      setcolorspace } def",
205  "/sc { pdfLastFill not { pdfFillCS setcolorspace } if",
206  "      dup /pdfFill exch def aload pop pdfFillXform setcolor",
207  "     /pdfLastFill true def /pdfLastStroke false def } def",
208  "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if",
209  "      dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
210  "     /pdfLastStroke true def /pdfLastFill false def } def",
211  "/op { /pdfFillOP exch def",
212  "      pdfLastFill { pdfFillOP setoverprint } if } def",
213  "/OP { /pdfStrokeOP exch def",
214  "      pdfLastStroke { pdfStrokeOP setoverprint } if } def",
215  "/fCol {",
216  "  pdfLastFill not {",
217  "    pdfFillCS setcolorspace",
218  "    pdfFill aload pop pdfFillXform setcolor",
219  "    pdfFillOP setoverprint",
220  "    /pdfLastFill true def /pdfLastStroke false def",
221  "  } if",
222  "} def",
223  "/sCol {",
224  "  pdfLastStroke not {",
225  "    pdfStrokeCS setcolorspace",
226  "    pdfStroke aload pop pdfStrokeXform setcolor",
227  "    pdfStrokeOP setoverprint",
228  "    /pdfLastStroke true def /pdfLastFill false def",
229  "  } if",
230  "} def",
231  "~23s",
232  "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
233  "     /pdfLastFill true def /pdfLastStroke false def } def",
234  "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
235  "     /pdfLastStroke true def /pdfLastFill false def } def",
236  "/ck { 6 copy 6 array astore /pdfFill exch def",
237  "      findcmykcustomcolor exch setcustomcolor",
238  "      /pdfLastFill true def /pdfLastStroke false def } def",
239  "/CK { 6 copy 6 array astore /pdfStroke exch def",
240  "      findcmykcustomcolor exch setcustomcolor",
241  "      /pdfLastStroke true def /pdfLastFill false def } def",
242  "/op { /pdfFillOP exch def",
243  "      pdfLastFill { pdfFillOP setoverprint } if } def",
244  "/OP { /pdfStrokeOP exch def",
245  "      pdfLastStroke { pdfStrokeOP setoverprint } if } def",
246  "/fCol {",
247  "  pdfLastFill not {",
248  "    pdfFill aload length 4 eq {",
249  "          setcmykcolor",
250  "        }{",
251  "          findcmykcustomcolor exch setcustomcolor",
252  "        } ifelse",
253  "    pdfFillOP setoverprint",
254  "    /pdfLastFill true def /pdfLastStroke false def",
255  "  } if",
256  "} def",
257  "/sCol {",
258  "  pdfLastStroke not {",
259  "    pdfStroke aload length 4 eq {",
260  "      setcmykcolor",
261  "    }{",
262  "      findcmykcustomcolor exch setcustomcolor",
263  "    } ifelse",
264  "    pdfStrokeOP setoverprint",
265  "    /pdfLastStroke true def /pdfLastFill false def",
266  "  } if",
267  "} def",
268  "~123sn",
269  "% build a font",
270  "/pdfMakeFont {",
271  "  4 3 roll findfont",
272  "  4 2 roll matrix scale makefont",
273  "  dup length dict begin",
274  "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
275  "    /Encoding exch def",
276  "    currentdict",
277  "  end",
278  "  definefont pop",
279  "} def",
280  "/pdfMakeFont16 {",
281  "  exch findfont",
282  "  dup length dict begin",
283  "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
284  "    /WMode exch def",
285  "    currentdict",
286  "  end",
287  "  definefont pop",
288  "} def",
289  "~3sn",
290  "/pdfMakeFont16L3 {",
291  "  1 index /CIDFont resourcestatus {",
292  "    pop pop 1 index /CIDFont findresource /CIDFontType known",
293  "  } {",
294  "    false",
295  "  } ifelse",
296  "  {",
297  "    0 eq { /Identity-H } { /Identity-V } ifelse",
298  "    exch 1 array astore composefont pop",
299  "  } {",
300  "    pdfMakeFont16",
301  "  } ifelse",
302  "} def",
303  "~123sn",
304  "% graphics state operators",
305  "~1sn",
306  "/q {",
307  "  gsave",
308  "  pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
309  "  pdfStates pdfStateIdx 1 add get begin",
310  "  pdfOpNames { exch def } forall",
311  "} def",
312  "/Q { end grestore } def",
313  "~23sn",
314  "/q { gsave pdfDictSize dict begin } def",
315  "/Q {",
316  "  end grestore",
317  "  /pdfLastFill where {",
318  "    pop",
319  "    pdfLastFill {",
320  "      pdfFillOP setoverprint",
321  "    } {",
322  "      pdfStrokeOP setoverprint",
323  "    } ifelse",
324  "  } if",
325  "} def",
326  "~123sn",
327  "/cm { concat } def",
328  "/d { setdash } def",
329  "/i { setflat } def",
330  "/j { setlinejoin } def",
331  "/J { setlinecap } def",
332  "/M { setmiterlimit } def",
333  "/w { setlinewidth } def",
334  "% path segment operators",
335  "/m { moveto } def",
336  "/l { lineto } def",
337  "/c { curveto } def",
338  "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
339  "      neg 0 rlineto closepath } def",
340  "/h { closepath } def",
341  "% path painting operators",
342  "/S { sCol stroke } def",
343  "/Sf { fCol stroke } def",
344  "/f { fCol fill } def",
345  "/f* { fCol eofill } def",
346  "% clipping operators",
347  "/W { clip newpath } def",
348  "/W* { eoclip newpath } def",
349  "% text state operators",
350  "/Tc { /pdfCharSpacing exch def } def",
351  "/Tf { dup /pdfFontSize exch def",
352  "      dup pdfHorizScaling mul exch matrix scale",
353  "      pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
354  "      exch findfont exch makefont setfont } def",
355  "/Tr { /pdfTextRender exch def } def",
356  "/Ts { /pdfTextRise exch def } def",
357  "/Tw { /pdfWordSpacing exch def } def",
358  "/Tz { /pdfHorizScaling exch def } def",
359  "% text positioning operators",
360  "/Td { pdfTextMat transform moveto } def",
361  "/Tm { /pdfTextMat exch def } def",
362  "% text string operators",
363  "/cshow where {",
364  "  pop",
365  "  /cshow2 {",
366  "    dup {",
367  "      pop pop",
368  "      1 string dup 0 3 index put 3 index exec",
369  "    } exch cshow",
370  "    pop pop",
371  "  } def",
372  "}{",
373  "  /cshow2 {",
374  "    currentfont /FontType get 0 eq {",
375  "      0 2 2 index length 1 sub {",
376  "        2 copy get exch 1 add 2 index exch get",
377  "        2 copy exch 256 mul add",
378  "        2 string dup 0 6 5 roll put dup 1 5 4 roll put",
379  "        3 index exec",
380  "      } for",
381  "    } {",
382  "      dup {",
383  "        1 string dup 0 3 index put 3 index exec",
384  "      } forall",
385  "    } ifelse",
386  "    pop pop",
387  "  } def",
388  "} ifelse",
389  "/awcp {", // awidthcharpath
390  "  exch {",
391  "    false charpath",
392  "    5 index 5 index rmoveto",
393  "    6 index eq { 7 index 7 index rmoveto } if",
394  "  } exch cshow2",
395  "  6 {pop} repeat",
396  "} def",
397  "/Tj {",
398  "  fCol",  // because stringwidth has to draw Type 3 chars
399  "  1 index stringwidth pdfTextMat idtransform pop",
400  "  sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
401  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
402  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
403  "  pdfTextMat dtransform",
404  "  6 5 roll Tj1",
405  "} def",
406  "/Tj16 {",
407  "  fCol",  // because stringwidth has to draw Type 3 chars
408  "  2 index stringwidth pdfTextMat idtransform pop",
409  "  sub exch div",
410  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
411  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
412  "  pdfTextMat dtransform",
413  "  6 5 roll Tj1",
414  "} def",
415  "/Tj16V {",
416  "  fCol",  // because stringwidth has to draw Type 3 chars
417  "  2 index stringwidth pdfTextMat idtransform exch pop",
418  "  sub exch div",
419  "  0 pdfWordSpacing pdfTextMat dtransform 32",
420  "  4 3 roll pdfCharSpacing add 0 exch",
421  "  pdfTextMat dtransform",
422  "  6 5 roll Tj1",
423  "} def",
424  "/Tj1 {",
425  "  0 pdfTextRise pdfTextMat dtransform rmoveto",
426  "  currentpoint 8 2 roll",
427  "  pdfTextRender 1 and 0 eq {",
428  "    6 copy awidthshow",
429  "  } if",
430  "  pdfTextRender 3 and dup 1 eq exch 2 eq or {",
431  "    7 index 7 index moveto",
432  "    6 copy",
433  "    currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
434  "    false awcp currentpoint stroke moveto",
435  "  } if",
436  "  pdfTextRender 4 and 0 ne {",
437  "    8 6 roll moveto",
438  "    false awcp",
439  "    /pdfTextClipPath [ pdfTextClipPath aload pop",
440  "      {/moveto cvx}",
441  "      {/lineto cvx}",
442  "      {/curveto cvx}",
443  "      {/closepath cvx}",
444  "    pathforall ] def",
445  "    currentpoint newpath moveto",
446  "  } {",
447  "    8 {pop} repeat",
448  "  } ifelse",
449  "  0 pdfTextRise neg pdfTextMat dtransform rmoveto",
450  "} def",
451  "/TJm { pdfFontSize 0.001 mul mul neg 0",
452  "       pdfTextMat dtransform rmoveto } def",
453  "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
454  "        pdfTextMat dtransform rmoveto } def",
455  "/Tclip { pdfTextClipPath cvx exec clip newpath",
456  "         /pdfTextClipPath [] def } def",
457  "~1ns",
458  "% Level 1 image operators",
459  "~1n",
460  "/pdfIm1 {",
461  "  /pdfImBuf1 4 index string def",
462  "  { currentfile pdfImBuf1 readhexstring pop } image",
463  "} def",
464  "~1s",
465  "/pdfIm1Sep {",
466  "  /pdfImBuf1 4 index string def",
467  "  /pdfImBuf2 4 index string def",
468  "  /pdfImBuf3 4 index string def",
469  "  /pdfImBuf4 4 index string def",
470  "  { currentfile pdfImBuf1 readhexstring pop }",
471  "  { currentfile pdfImBuf2 readhexstring pop }",
472  "  { currentfile pdfImBuf3 readhexstring pop }",
473  "  { currentfile pdfImBuf4 readhexstring pop }",
474  "  true 4 colorimage",
475  "} def",
476  "~1ns",
477  "/pdfImM1 {",
478  "  fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
479  "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
480  "} def",
481  "/pdfImM1a {",
482  "  { 2 copy get exch 1 add exch } imagemask",
483  "  pop pop",
484  "} def",
485  "~23sn",
486  "% Level 2 image operators",
487  "/pdfImBuf 100 string def",
488  "/pdfIm {",
489  "  image",
490  "  { currentfile pdfImBuf readline",
491  "    not { pop exit } if",
492  "    (%-EOD-) eq { exit } if } loop",
493  "} def",
494  "~23s",
495  "/pdfImSep {",
496  "  findcmykcustomcolor exch",
497  "  dup /Width get /pdfImBuf1 exch string def",
498  "  dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
499  "  /pdfImDecodeLow exch def",
500  "  begin Width Height BitsPerComponent ImageMatrix DataSource end",
501  "  /pdfImData exch def",
502  "  { pdfImData pdfImBuf1 readstring pop",
503  "    0 1 2 index length 1 sub {",
504  "      1 index exch 2 copy get",
505  "      pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
506  "      255 exch sub put",
507  "    } for }",
508  "  6 5 roll customcolorimage",
509  "  { currentfile pdfImBuf readline",
510  "    not { pop exit } if",
511  "    (%-EOD-) eq { exit } if } loop",
512  "} def",
513  "~23sn",
514  "/pdfImM {",
515  "  fCol imagemask",
516  "  { currentfile pdfImBuf readline",
517  "    not { pop exit } if",
518  "    (%-EOD-) eq { exit } if } loop",
519  "} def",
520  "/pdfImClip {",
521  "  gsave",
522  "  0 2 4 index length 1 sub {",
523  "    dup 4 index exch 2 copy",
524  "    get 5 index div put",
525  "    1 add 3 index exch 2 copy",
526  "    get 3 index div put",
527  "  } for",
528  "  pop pop rectclip",
529  "} def",
530  "/pdfImClipEnd { grestore } def",
531  "~23n",
532  "% shading operators",
533  "/colordelta {",
534  "  false 0 1 3 index length 1 sub {",
535  "    dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
536  "      pop true",
537  "    } if",
538  "  } for",
539  "  exch pop exch pop",
540  "} def",
541  "/funcCol { func n array astore } def",
542  "/funcSH {",
543  "  dup 0 eq {",
544  "    true",
545  "  } {",
546  "    dup 6 eq {",
547  "      false",
548  "    } {",
549  "      4 index 4 index funcCol dup",
550  "      6 index 4 index funcCol dup",
551  "      3 1 roll colordelta 3 1 roll",
552  "      5 index 5 index funcCol dup",
553  "      3 1 roll colordelta 3 1 roll",
554  "      6 index 8 index funcCol dup",
555  "      3 1 roll colordelta 3 1 roll",
556  "      colordelta or or or",
557  "    } ifelse",
558  "  } ifelse",
559  "  {",
560  "    1 add",
561  "    4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
562  "    6 index 6 index 4 index 4 index 4 index funcSH",
563  "    2 index 6 index 6 index 4 index 4 index funcSH",
564  "    6 index 2 index 4 index 6 index 4 index funcSH",
565  "    5 3 roll 3 2 roll funcSH pop pop",
566  "  } {",
567  "    pop 3 index 2 index add 0.5 mul 3 index  2 index add 0.5 mul",
568  "    funcCol sc",
569  "    dup 4 index exch mat transform m",
570  "    3 index 3 index mat transform l",
571  "    1 index 3 index mat transform l",
572  "    mat transform l pop pop h f*",
573  "  } ifelse",
574  "} def",
575  "/axialCol {",
576  "  dup 0 lt {",
577  "    pop t0",
578  "  } {",
579  "    dup 1 gt {",
580  "      pop t1",
581  "    } {",
582  "      dt mul t0 add",
583  "    } ifelse",
584  "  } ifelse",
585  "  func n array astore",
586  "} def",
587  "/axialSH {",
588  "  dup 0 eq {",
589  "    true",
590  "  } {",
591  "    dup 8 eq {",
592  "      false",
593  "    } {",
594  "      2 index axialCol 2 index axialCol colordelta",
595  "    } ifelse",
596  "  } ifelse",
597  "  {",
598  "    1 add 3 1 roll 2 copy add 0.5 mul",
599  "    dup 4 3 roll exch 4 index axialSH",
600  "    exch 3 2 roll axialSH",
601  "  } {",
602  "    pop 2 copy add 0.5 mul axialCol sc",
603  "    exch dup dx mul x0 add exch dy mul y0 add",
604  "    3 2 roll dup dx mul x0 add exch dy mul y0 add",
605  "    dx abs dy abs ge {",
606  "      2 copy yMin sub dy mul dx div add yMin m",
607  "      yMax sub dy mul dx div add yMax l",
608  "      2 copy yMax sub dy mul dx div add yMax l",
609  "      yMin sub dy mul dx div add yMin l",
610  "      h f*",
611  "    } {",
612  "      exch 2 copy xMin sub dx mul dy div add xMin exch m",
613  "      xMax sub dx mul dy div add xMax exch l",
614  "      exch 2 copy xMax sub dx mul dy div add xMax exch l",
615  "      xMin sub dx mul dy div add xMin exch l",
616  "      h f*",
617  "    } ifelse",
618  "  } ifelse",
619  "} def",
620  "/radialCol {",
621  "  dup t0 lt {",
622  "    pop t0",
623  "  } {",
624  "    dup t1 gt {",
625  "      pop t1",
626  "    } if",
627  "  } ifelse",
628  "  func n array astore",
629  "} def",
630  "/radialSH {",
631  "  dup 0 eq {",
632  "    true",
633  "  } {",
634  "    dup 8 eq {",
635  "      false",
636  "    } {",
637  "      2 index dt mul t0 add radialCol",
638  "      2 index dt mul t0 add radialCol colordelta",
639  "    } ifelse",
640  "  } ifelse",
641  "  {",
642  "    1 add 3 1 roll 2 copy add 0.5 mul",
643  "    dup 4 3 roll exch 4 index radialSH",
644  "    exch 3 2 roll radialSH",
645  "  } {",
646  "    pop 2 copy add 0.5 mul dt mul t0 add axialCol sc",
647  "    exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
648  "    0 360 arc h",
649  "    dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
650  "    0 360 arc h f*",
651  "  } ifelse",
652  "} def",
653  "~123sn",
654  "end",
655  NULL
656};
657
658static char *cmapProlog[] = {
659  "/CIDInit /ProcSet findresource begin",
660  "10 dict begin",
661  "  begincmap",
662  "  /CMapType 1 def",
663  "  /CMapName /Identity-H def",
664  "  /CIDSystemInfo 3 dict dup begin",
665  "    /Registry (Adobe) def",
666  "    /Ordering (Identity) def",
667  "    /Supplement 0 def",
668  "  end def",
669  "  1 begincodespacerange",
670  "    <0000> <ffff>",
671  "  endcodespacerange",
672  "  0 usefont",
673  "  1 begincidrange",
674  "    <0000> <ffff> 0",
675  "  endcidrange",
676  "  endcmap",
677  "  currentdict CMapName exch /CMap defineresource pop",
678  "end",
679  "10 dict begin",
680  "  begincmap",
681  "  /CMapType 1 def",
682  "  /CMapName /Identity-V def",
683  "  /CIDSystemInfo 3 dict dup begin",
684  "    /Registry (Adobe) def",
685  "    /Ordering (Identity) def",
686  "    /Supplement 0 def",
687  "  end def",
688  "  /WMode 1 def",
689  "  1 begincodespacerange",
690  "    <0000> <ffff>",
691  "  endcodespacerange",
692  "  0 usefont",
693  "  1 begincidrange",
694  "    <0000> <ffff> 0",
695  "  endcidrange",
696  "  endcmap",
697  "  currentdict CMapName exch /CMap defineresource pop",
698  "end",
699  "end",
700  NULL
701};
702
703//------------------------------------------------------------------------
704// Fonts
705//------------------------------------------------------------------------
706
707struct PSSubstFont {
708  char *psName;         // PostScript name
709  double mWidth;        // width of 'm' character
710};
711
712static char *psFonts[] = {
713  "Courier",
714  "Courier-Bold",
715  "Courier-Oblique",
716  "Courier-BoldOblique",
717  "Helvetica",
718  "Helvetica-Bold",
719  "Helvetica-Oblique",
720  "Helvetica-BoldOblique",
721  "Symbol",
722  "Times-Roman",
723  "Times-Bold",
724  "Times-Italic",
725  "Times-BoldItalic",
726  "ZapfDingbats",
727  NULL
728};
729
730static PSSubstFont psSubstFonts[] = {
731  {"Helvetica",             0.833},
732  {"Helvetica-Oblique",     0.833},
733  {"Helvetica-Bold",        0.889},
734  {"Helvetica-BoldOblique", 0.889},
735  {"Times-Roman",           0.788},
736  {"Times-Italic",          0.722},
737  {"Times-Bold",            0.833},
738  {"Times-BoldItalic",      0.778},
739  {"Courier",               0.600},
740  {"Courier-Oblique",       0.600},
741  {"Courier-Bold",          0.600},
742  {"Courier-BoldOblique",   0.600}
743};
744
745// Encoding info for substitute 16-bit font
746struct PSFont16Enc {
747  Ref fontID;
748  GooString *enc;
749};
750
751//------------------------------------------------------------------------
752// process colors
753//------------------------------------------------------------------------
754
755#define psProcessCyan     1
756#define psProcessMagenta  2
757#define psProcessYellow   4
758#define psProcessBlack    8
759#define psProcessCMYK    15
760
761//------------------------------------------------------------------------
762// PSOutCustomColor
763//------------------------------------------------------------------------
764
765class PSOutCustomColor {
766public:
767
768  PSOutCustomColor(double cA, double mA,
769           double yA, double kA, GooString *nameA);
770  ~PSOutCustomColor();
771
772  double c, m, y, k;
773  GooString *name;
774  PSOutCustomColor *next;
775};
776
777PSOutCustomColor::PSOutCustomColor(double cA, double mA,
778                   double yA, double kA, GooString *nameA) {
779  c = cA;
780  m = mA;
781  y = yA;
782  k = kA;
783  name = nameA;
784  next = NULL;
785}
786
787PSOutCustomColor::~PSOutCustomColor() {
788  delete name;
789}
790
791//------------------------------------------------------------------------
792// DeviceNRecoder
793//------------------------------------------------------------------------
794
795class DeviceNRecoder: public FilterStream {
796public:
797
798  DeviceNRecoder(Stream *strA, int widthA, int heightA,
799         GfxImageColorMap *colorMapA);
800  virtual ~DeviceNRecoder();
801  virtual StreamKind getKind() { return strWeird; }
802  virtual void reset();
803  virtual int getChar()
804    { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
805  virtual int lookChar()
806    { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
807  virtual GooString *getPSFilter(int psLevel, char *indent) { return NULL; }
808  virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
809  virtual GBool isEncoder() { return gTrue; }
810
811private:
812
813  GBool fillBuf();
814
815  int width, height;
816  GfxImageColorMap *colorMap;
817  Function *func;
818  ImageStream *imgStr;
819  int buf[gfxColorMaxComps];
820  int pixelIdx;
821  int bufIdx;
822  int bufSize;
823};
824
825DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA,
826                   GfxImageColorMap *colorMapA):
827    FilterStream(strA) {
828  width = widthA;
829  height = heightA;
830  colorMap = colorMapA;
831  imgStr = NULL;
832  pixelIdx = 0;
833  bufIdx = gfxColorMaxComps;
834  bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
835              getAlt()->getNComps();
836  func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
837           getTintTransformFunc();
838}
839
840DeviceNRecoder::~DeviceNRecoder() {
841  if (imgStr) {
842    delete imgStr;
843  }
844}
845
846void DeviceNRecoder::reset() {
847  imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
848               colorMap->getBits());
849  imgStr->reset();
850}
851
852GBool DeviceNRecoder::fillBuf() {
853  Guchar pixBuf[gfxColorMaxComps];
854  GfxColor color;
855  double x[gfxColorMaxComps], y[gfxColorMaxComps];
856  int i;
857
858  if (pixelIdx >= width * height) {
859    return gFalse;
860  }
861  imgStr->getPixel(pixBuf);
862  colorMap->getColor(pixBuf, &color);
863  for (i = 0;
864       i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps();
865       ++i) {
866    x[i] = colToDbl(color.c[i]);
867  }
868  func->transform(x, y);
869  for (i = 0; i < bufSize; ++i) {
870    buf[i] = (int)(y[i] * 255 + 0.5);
871  }
872  bufIdx = 0;
873  ++pixelIdx;
874  return gTrue;
875}
876
877//------------------------------------------------------------------------
878// PSOutputDev
879//------------------------------------------------------------------------
880
881extern "C" {
882typedef void (*SignalFunc)(int);
883}
884
885static void outputToFile(void *stream, char *data, int len) {
886  fwrite(data, 1, len, (FILE *)stream);
887}
888
889PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
890             int firstPage, int lastPage, PSOutMode modeA,
891                         int paperWidthA, int paperHeightA, GBool duplexA,
892             int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
893             GBool manualCtrlA) {
894  FILE *f;
895  PSFileType fileTypeA;
896
897  underlayCbk = NULL;
898  underlayCbkData = NULL;
899  overlayCbk = NULL;
900  overlayCbkData = NULL;
901
902  fontIDs = NULL;
903  fontFileIDs = NULL;
904  fontFileNames = NULL;
905  font16Enc = NULL;
906  xobjStack = NULL;
907  embFontList = NULL;
908  customColors = NULL;
909  haveTextClip = gFalse;
910  t3String = NULL;
911
912  // open file or pipe
913  if (!strcmp(fileName, "-")) {
914    fileTypeA = psStdout;
915    f = stdout;
916  } else if (fileName[0] == '|') {
917    fileTypeA = psPipe;
918#ifdef HAVE_POPEN
919#ifndef WIN32
920    signal(SIGPIPE, (SignalFunc)SIG_IGN);
921#endif
922    if (!(f = popen(fileName + 1, "w"))) {
923      error(-1, "Couldn't run print command '%s'", fileName);
924      ok = gFalse;
925      return;
926    }
927#else
928    error(-1, "Print commands are not supported ('%s')", fileName);
929    ok = gFalse;
930    return;
931#endif
932  } else {
933    fileTypeA = psFile;
934    if (!(f = fopen(fileName, "w"))) {
935      error(-1, "Couldn't open PostScript file '%s'", fileName);
936      ok = gFalse;
937      return;
938    }
939  }
940
941  init(outputToFile, f, fileTypeA,
942       xrefA, catalog, firstPage, lastPage, modeA,
943       imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA,
944       paperWidthA, paperHeightA, duplexA);
945}
946
947PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
948             XRef *xrefA, Catalog *catalog,
949             int firstPage, int lastPage, PSOutMode modeA,
950                         int paperWidthA, int paperHeightA, GBool duplexA,
951             int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
952             GBool manualCtrlA) {
953  underlayCbk = NULL;
954  underlayCbkData = NULL;
955  overlayCbk = NULL;
956  overlayCbkData = NULL;
957
958  fontIDs = NULL;
959  fontFileIDs = NULL;
960  fontFileNames = NULL;
961  font16Enc = NULL;
962  xobjStack = NULL;
963  embFontList = NULL;
964  customColors = NULL;
965  haveTextClip = gFalse;
966  t3String = NULL;
967
968  init(outputFuncA, outputStreamA, psGeneric,
969       xrefA, catalog, firstPage, lastPage, modeA,
970       imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA,
971       paperWidthA, paperHeightA, duplexA);
972}
973
974void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
975               PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
976               int firstPage, int lastPage, PSOutMode modeA,
977               int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
978               GBool manualCtrlA, int paperWidthA, int paperHeightA,
979                       GBool duplexA) {
980  Page *page;
981  PDFRectangle *box;
982
983  // initialize
984  ok = gTrue;
985  outputFunc = outputFuncA;
986  outputStream = outputStreamA;
987  fileType = fileTypeA;
988  xref = xrefA;
989  level = globalParams->getPSLevel();
990  mode = modeA;
991  paperWidth = paperWidthA;
992  paperHeight = paperHeightA;
993  imgLLX = imgLLXA;
994  imgLLY = imgLLYA;
995  imgURX = imgURXA;
996  imgURY = imgURYA;
997  if (paperWidth < 0 || paperHeight < 0) {
998    // this check is needed in case the document has zero pages
999    if (firstPage > 0 && firstPage <= catalog->getNumPages()) {
1000      page = catalog->getPage(firstPage);
1001      paperWidth = (int)ceil(page->getMediaWidth());
1002      paperHeight = (int)ceil(page->getMediaHeight());
1003    } else {
1004      paperWidth = 1;
1005      paperHeight = 1;
1006    }
1007  }
1008  if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) {
1009    globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY);
1010    if (imgURX <= 0 || imgURY <= 0) {
1011      imgLLX = imgLLY = 0;
1012      imgURX = paperWidth;
1013      imgURY = paperHeight;
1014    }
1015  }
1016  manualCtrl = manualCtrlA;
1017  if (mode == psModeForm) {
1018    lastPage = firstPage;
1019  }
1020  processColors = 0;
1021  inType3Char = gFalse;
1022
1023#if OPI_SUPPORT
1024  // initialize OPI nesting levels
1025  opi13Nest = 0;
1026  opi20Nest = 0;
1027#endif
1028
1029  tx0 = ty0 = 0;
1030  xScale0 = yScale0 = 0;
1031  rotate0 = -1;
1032  clipLLX0 = clipLLY0 = 0;
1033  clipURX0 = clipURY0 = -1;
1034
1035  // initialize fontIDs, fontFileIDs, and fontFileNames lists
1036  fontIDSize = 64;
1037  fontIDLen = 0;
1038  fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
1039  fontFileIDSize = 64;
1040  fontFileIDLen = 0;
1041  fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref));
1042  fontFileNameSize = 64;
1043  fontFileNameLen = 0;
1044  fontFileNames = (GooString **)gmallocn(fontFileNameSize, sizeof(GooString *));
1045  psFileNames = (GooString **)gmallocn(fontFileNameSize, sizeof(GooString *));
1046  nextTrueTypeNum = 0;
1047  font16EncLen = 0;
1048  font16EncSize = 0;
1049
1050  xobjStack = new GooList();
1051  numSaves = 0;
1052  numTilingPatterns = 0;
1053  nextFunc = 0;
1054
1055  // initialize embedded font resource comment list
1056  embFontList = new GooString();
1057
1058  if (!manualCtrl) {
1059    // this check is needed in case the document has zero pages
1060    if (firstPage > 0 && firstPage <= catalog->getNumPages()) {
1061      writeHeader(firstPage, lastPage,
1062          catalog->getPage(firstPage)->getMediaBox(),
1063          catalog->getPage(firstPage)->getCropBox(),
1064          catalog->getPage(firstPage)->getRotate());
1065    } else {
1066      box = new PDFRectangle(0, 0, 1, 1);
1067      writeHeader(firstPage, lastPage, box, box, 0);
1068      delete box;
1069    }
1070    if (mode != psModeForm) {
1071      writePS("%%BeginProlog\n");
1072    }
1073    writeXpdfProcset();
1074    if (mode != psModeForm) {
1075      writePS("%%EndProlog\n");
1076      writePS("%%BeginSetup\n");
1077    }
1078    writeDocSetup(catalog, firstPage, lastPage, duplexA);
1079    if (mode != psModeForm) {
1080      writePS("%%EndSetup\n");
1081    }
1082  }
1083
1084  // initialize sequential page number
1085  seqPage = 1;
1086}
1087
1088PSOutputDev::~PSOutputDev() {
1089  PSOutCustomColor *cc;
1090  int i;
1091
1092  if (ok) {
1093    if (!manualCtrl) {
1094      writePS("%%Trailer\n");
1095      writeTrailer();
1096      if (mode != psModeForm) {
1097    writePS("%%EOF\n");
1098      }
1099    }
1100    if (fileType == psFile) {
1101#ifdef MACOS
1102      ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
1103#endif
1104      fclose((FILE *)outputStream);
1105    }
1106#ifdef HAVE_POPEN
1107    else if (fileType == psPipe) {
1108      pclose((FILE *)outputStream);
1109#ifndef WIN32
1110      signal(SIGPIPE, (SignalFunc)SIG_DFL);
1111#endif
1112    }
1113#endif
1114  }
1115  if (embFontList) {
1116    delete embFontList;
1117  }
1118  if (fontIDs) {
1119    gfree(fontIDs);
1120  }
1121  if (fontFileIDs) {
1122    gfree(fontFileIDs);
1123  }
1124  if (fontFileNames) {
1125    for (i = 0; i < fontFileNameLen; ++i) {
1126      delete fontFileNames[i];
1127    }
1128    gfree(fontFileNames);
1129  }
1130  if (psFileNames) {
1131    for (i = 0; i < fontFileNameLen; ++i) {
1132      if (psFileNames[i])
1133        delete psFileNames[i];
1134    }
1135    gfree(psFileNames);
1136  }
1137  if (font16Enc) {
1138    for (i = 0; i < font16EncLen; ++i) {
1139      delete font16Enc[i].enc;
1140    }
1141    gfree(font16Enc);
1142  }
1143  if (xobjStack) {
1144    delete xobjStack;
1145  }
1146  while (customColors) {
1147    cc = customColors;
1148    customColors = cc->next;
1149    delete cc;
1150  }
1151}
1152
1153void PSOutputDev::writeHeader(int firstPage, int lastPage,
1154                  PDFRectangle *mediaBox, PDFRectangle *cropBox,
1155                  int pageRotate) {
1156  double x1, y1, x2, y2;
1157
1158  switch (mode) {
1159  case psModePS:
1160    writePS("%!PS-Adobe-3.0\n");
1161    writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
1162    writePSFmt("%%%%LanguageLevel: %d\n",
1163           (level == psLevel1 || level == psLevel1Sep) ? 1 :
1164           (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
1165    if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1166      writePS("%%DocumentProcessColors: (atend)\n");
1167      writePS("%%DocumentCustomColors: (atend)\n");
1168    }
1169    writePS("%%DocumentSuppliedResources: (atend)\n");
1170    writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
1171           paperWidth, paperHeight);
1172    writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight);
1173    writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
1174    writePS("%%EndComments\n");
1175    writePS("%%BeginDefaults\n");
1176    writePS("%%PageMedia: plain\n");
1177    writePS("%%EndDefaults\n");
1178    break;
1179  case psModeEPS:
1180    writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
1181    writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
1182    writePSFmt("%%%%LanguageLevel: %d\n",
1183           (level == psLevel1 || level == psLevel1Sep) ? 1 :
1184           (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
1185    if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1186      writePS("%%DocumentProcessColors: (atend)\n");
1187      writePS("%%DocumentCustomColors: (atend)\n");
1188    }
1189    epsX1 = cropBox->x1;
1190    epsY1 = cropBox->y1;
1191    epsX2 = cropBox->x2;
1192    epsY2 = cropBox->y2;
1193    if (pageRotate == 0 || pageRotate == 180) {
1194      x1 = epsX1;
1195      y1 = epsY1;
1196      x2 = epsX2;
1197      y2 = epsY2;
1198    } else { // pageRotate == 90 || pageRotate == 270
1199      x1 = 0;
1200      y1 = 0;
1201      x2 = epsY2 - epsY1;
1202      y2 = epsX2 - epsX1;
1203    }
1204    writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
1205           (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
1206    if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) ||
1207    floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) {
1208      writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n", x1, y1, x2, y2);
1209    }
1210    writePS("%%DocumentSuppliedResources: (atend)\n");
1211    writePS("%%EndComments\n");
1212    break;
1213  case psModeForm:
1214    writePS("%!PS-Adobe-3.0 Resource-Form\n");
1215    writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
1216    writePSFmt("%%%%LanguageLevel: %d\n",
1217           (level == psLevel1 || level == psLevel1Sep) ? 1 :
1218           (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
1219    if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
1220      writePS("%%DocumentProcessColors: (atend)\n");
1221      writePS("%%DocumentCustomColors: (atend)\n");
1222    }
1223    writePS("%%DocumentSuppliedResources: (atend)\n");
1224    writePS("%%EndComments\n");
1225    writePS("32 dict dup begin\n");
1226    writePSFmt("/BBox [%d %d %d %d] def\n",
1227           (int)floor(mediaBox->x1), (int)floor(mediaBox->y1),
1228           (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2));
1229    writePS("/FormType 1 def\n");
1230    writePS("/Matrix [1 0 0 1 0 0] def\n");
1231    break;
1232  }
1233}
1234
1235void PSOutputDev::writeXpdfProcset() {
1236  GBool lev1, lev2, lev3, sep, nonSep;
1237  char **p;
1238  char *q;
1239
1240  writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
1241  lev1 = lev2 = lev3 = sep = nonSep = gTrue;
1242  for (p = prolog; *p; ++p) {
1243    if ((*p)[0] == '~') {
1244      lev1 = lev2 = lev3 = sep = nonSep = gFalse;
1245      for (q = *p + 1; *q; ++q) {
1246    switch (*q) {
1247    case '1': lev1 = gTrue; break;
1248    case '2': lev2 = gTrue; break;
1249    case '3': lev3 = gTrue; break;
1250    case 's': sep = gTrue; break;
1251    case 'n': nonSep = gTrue; break;
1252    }
1253      }
1254    } else if ((level == psLevel1 && lev1 && nonSep) ||
1255           (level == psLevel1Sep && lev1 && sep) ||
1256           (level == psLevel2 && lev2 && nonSep) ||
1257           (level == psLevel2Sep && lev2 && sep) ||
1258           (level == psLevel3 && lev3 && nonSep) ||
1259           (level == psLevel3Sep && lev3 && sep)) {
1260      writePSFmt("%s\n", *p);
1261    }
1262  }
1263  writePS("%%EndResource\n");
1264
1265  if (level >= psLevel3) {
1266    for (p = cmapProlog; *p; ++p) {
1267      writePSFmt("%s\n", *p);
1268    }
1269  }
1270}
1271
1272void PSOutputDev::writeDocSetup(Catalog *catalog,
1273                int firstPage, int lastPage,
1274                                GBool duplexA) {
1275  Page *page;
1276  Dict *resDict;
1277  Annots *annots;
1278  Object obj1, obj2;
1279  int pg, i;
1280
1281  if (mode == psModeForm) {
1282    // swap the form and xpdf dicts
1283    writePS("xpdf end begin dup begin\n");
1284  } else {
1285    writePS("xpdf begin\n");
1286  }
1287  for (pg = firstPage; pg <= lastPage; ++pg) {
1288    page = catalog->getPage(pg);
1289    if ((resDict = page->getResourceDict())) {
1290      setupResources(resDict);
1291    }
1292    annots = new Annots(xref, catalog, page->getAnnots(&obj1));
1293    obj1.free();
1294    for (i = 0; i < annots->getNumAnnots(); ++i) {
1295      if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
1296    obj1.streamGetDict()->lookup("Resources", &obj2);
1297    if (obj2.isDict()) {
1298      setupResources(obj2.getDict());
1299    }
1300    obj2.free();
1301      }
1302      obj1.free();
1303    }
1304    delete annots;
1305  }
1306  if (mode != psModeForm) {
1307    if (mode != psModeEPS && !manualCtrl) {
1308      writePSFmt("%d %d %s pdfSetup\n",
1309         paperWidth, paperHeight, duplexA ? "true" : "false");
1310    }
1311#if OPI_SUPPORT
1312    if (globalParams->getPSOPI()) {
1313      writePS("/opiMatrix matrix currentmatrix def\n");
1314    }
1315#endif
1316  }
1317}
1318
1319void PSOutputDev::writePageTrailer() {
1320  if (mode != psModeForm) {
1321    writePS("pdfEndPage\n");
1322  }
1323}
1324
1325void PSOutputDev::writeTrailer() {
1326  PSOutCustomColor *cc;
1327
1328  if (mode == psModeForm) {
1329    writePS("/Foo exch /Form defineresource pop\n");
1330  } else {
1331    writePS("end\n");
1332    writePS("%%DocumentSuppliedResources:\n");
1333    writePS(embFontList->getCString());
1334    if (level == psLevel1Sep || level == psLevel2Sep ||
1335    level == psLevel3Sep) {
1336      writePS("%%DocumentProcessColors:");
1337      if (processColors & psProcessCyan) {
1338    writePS(" Cyan");
1339     }
1340      if (processColors & psProcessMagenta) {
1341    writePS(" Magenta");
1342      }
1343      if (processColors & psProcessYellow) {
1344    writePS(" Yellow");
1345      }
1346      if (processColors & psProcessBlack) {
1347    writePS(" Black");
1348      }
1349      writePS("\n");
1350      writePS("%%DocumentCustomColors:");
1351      for (cc = customColors; cc; cc = cc->next) {
1352    writePSFmt(" (%s)", cc->name->getCString());
1353      }
1354      writePS("\n");
1355      writePS("%%CMYKCustomColor:\n");
1356      for (cc = customColors; cc; cc = cc->next) {
1357    writePSFmt("%%%%+ %g %g %g %g (%s)\n",
1358           cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
1359      }
1360    }
1361  }
1362}
1363
1364void PSOutputDev::setupResources(Dict *resDict) {
1365  Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;
1366  Ref ref0, ref1;
1367  GBool skip;
1368  int i, j;
1369
1370  setupFonts(resDict);
1371  setupImages(resDict);
1372
1373  //----- recursively scan XObjects
1374  resDict->lookup("XObject", &xObjDict);
1375  if (xObjDict.isDict()) {
1376    for (i = 0; i < xObjDict.dictGetLength(); ++i) {
1377
1378      // avoid infinite recursion on XObjects
1379      skip = gFalse;
1380      if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
1381    ref0 = xObjRef.getRef();
1382    for (j = 0; j < xobjStack->getLength(); ++j) {
1383      ref1 = *(Ref *)xobjStack->get(j);
1384      if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
1385        skip = gTrue;
1386        break;
1387      }
1388    }
1389    if (!skip) {
1390      xobjStack->append(&ref0);
1391    }
1392      }
1393      if (!skip) {
1394
1395    // process the XObject's resource dictionary
1396    xObjDict.dictGetVal(i, &xObj);
1397    if (xObj.isStream()) {
1398      xObj.streamGetDict()->lookup("Resources", &resObj);
1399      if (resObj.isDict()) {
1400        setupResources(resObj.getDict());
1401      }
1402      resObj.free();
1403    }
1404    xObj.free();
1405      }
1406
1407      if (xObjRef.isRef() && !skip) {
1408    xobjStack->del(xobjStack->getLength() - 1);
1409      }
1410      xObjRef.free();
1411    }
1412  }
1413  xObjDict.free();
1414
1415  //----- recursively scan Patterns
1416  resDict->lookup("Pattern", &patDict);
1417  if (patDict.isDict()) {
1418    inType3Char = gTrue;
1419    for (i = 0; i < patDict.dictGetLength(); ++i) {
1420
1421      // avoid infinite recursion on Patterns
1422      skip = gFalse;
1423      if ((patDict.dictGetValNF(i, &patRef)->isRef())) {
1424    ref0 = patRef.getRef();
1425    for (j = 0; j < xobjStack->getLength(); ++j) {
1426      ref1 = *(Ref *)xobjStack->get(j);
1427      if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
1428        skip = gTrue;
1429        break;
1430      }
1431    }
1432    if (!skip) {
1433      xobjStack->append(&ref0);
1434    }
1435      }
1436      if (!skip) {
1437
1438    // process the Pattern's resource dictionary
1439    patDict.dictGetVal(i, &pat);
1440    if (pat.isStream()) {
1441      pat.streamGetDict()->lookup("Resources", &resObj);
1442      if (resObj.isDict()) {
1443        setupResources(resObj.getDict());
1444      }
1445      resObj.free();
1446    }
1447    pat.free();
1448      }
1449
1450      if (patRef.isRef() && !skip) {
1451    xobjStack->del(xobjStack->getLength() - 1);
1452      }
1453      patRef.free();
1454    }
1455    inType3Char = gFalse;
1456  }
1457  patDict.free();
1458}
1459
1460void PSOutputDev::setupFonts(Dict *resDict) {
1461  Object obj1, obj2;
1462  Ref r;
1463  GfxFontDict *gfxFontDict;
1464  GfxFont *font;
1465  int i;
1466
1467  gfxFontDict = NULL;
1468  resDict->lookupNF("Font", &obj1);
1469  if (obj1.isRef()) {
1470    obj1.fetch(xref, &obj2);
1471    if (obj2.isDict()) {
1472      r = obj1.getRef();
1473      gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
1474    }
1475    obj2.free();
1476  } else if (obj1.isDict()) {
1477    gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
1478  }
1479  if (gfxFontDict) {
1480    for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
1481      if ((font = gfxFontDict->getFont(i))) {
1482    setupFont(font, resDict);
1483      }
1484    }
1485    delete gfxFontDict;
1486  }
1487  obj1.free();
1488}
1489
1490void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
1491  Ref fontFileID;
1492  GooString *name;
1493  PSFontParam *fontParam;
1494  GooString *psName;
1495  char type3Name[64], buf[16];
1496  GBool subst;
1497  UnicodeMap *uMap;
1498  char *charName;
1499  double xs, ys;
1500  int code;
1501  double w1, w2;
1502  double *fm;
1503  int i, j;
1504  DisplayFontParam *dfp;
1505
1506  // check if font is already set up
1507  for (i = 0; i < fontIDLen; ++i) {
1508    if (fontIDs[i].num == font->getID()->num &&
1509    fontIDs[i].gen == font->getID()->gen) {
1510      return;
1511    }
1512  }
1513
1514  // add entry to fontIDs list
1515  if (fontIDLen >= fontIDSize) {
1516    fontIDSize += 64;
1517    fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));
1518  }
1519  fontIDs[fontIDLen++] = *font->getID();
1520
1521  xs = ys = 1;
1522  subst = gFalse;
1523
1524  // check for resident 8-bit font
1525  if (font->getName() &&
1526      (fontParam = globalParams->getPSFont(font->getName()))) {
1527    psName = new GooString(fontParam->psFontName->getCString());
1528
1529  // check for embedded Type 1 font
1530  } else if (globalParams->getPSEmbedType1() &&
1531         font->getType() == fontType1 &&
1532         font->getEmbeddedFontID(&fontFileID)) {
1533    psName = filterPSName(font->getEmbeddedFontName());
1534    setupEmbeddedType1Font(&fontFileID, psName);
1535
1536  // check for embedded Type 1C font
1537  } else if (globalParams->getPSEmbedType1() &&
1538         font->getType() == fontType1C &&
1539         font->getEmbeddedFontID(&fontFileID)) {
1540    psName = filterPSName(font->getEmbeddedFontName());
1541    setupEmbeddedType1CFont(font, &fontFileID, psName);
1542
1543  // check for external Type 1 font file
1544  } else if (globalParams->getPSEmbedType1() &&
1545         font->getType() == fontType1 &&
1546         font->getExtFontFile()) {
1547    // this assumes that the PS font name matches the PDF font name
1548    psName = font->getName()->copy();
1549    setupExternalType1Font(font->getExtFontFile(), psName);
1550
1551  // check for embedded TrueType font
1552  } else if (globalParams->getPSEmbedTrueType() &&
1553         font->getType() == fontTrueType &&
1554         font->getEmbeddedFontID(&fontFileID)) {
1555    psName = filterPSName(font->getEmbeddedFontName());
1556    setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
1557
1558  // check for external TrueType font file
1559  } else if (globalParams->getPSEmbedTrueType() &&
1560         font->getType() == fontTrueType &&
1561         font->getExtFontFile()) {
1562    psName = setupExternalTrueTypeFont(font);
1563
1564  // check for embedded CID PostScript font
1565  } else if (globalParams->getPSEmbedCIDPostScript() &&
1566         font->getType() == fontCIDType0C &&
1567         font->getEmbeddedFontID(&fontFileID)) {
1568    psName = filterPSName(font->getEmbeddedFontName());
1569    setupEmbeddedCIDType0Font(font, &fontFileID, psName);
1570
1571  // check for embedded CID TrueType font
1572  } else if (globalParams->getPSEmbedCIDTrueType() &&
1573         font->getType() == fontCIDType2 &&
1574         font->getEmbeddedFontID(&fontFileID)) {
1575    psName = filterPSName(font->getEmbeddedFontName());
1576    setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue);
1577
1578  } else if (font->getType() == fontType3) {
1579    sprintf(type3Name, "T3_%d_%d",
1580        font->getID()->num, font->getID()->gen);
1581    psName = new GooString(type3Name);
1582    setupType3Font(font, psName, parentResDict);
1583
1584  // check for external CID TrueType font file
1585  } else if (globalParams->getPSEmbedCIDTrueType() &&
1586         font->getType() == fontCIDType2 &&
1587         font->getExtFontFile()) {
1588    psName = setupExternalCIDTrueTypeFont(font, font->getExtFontFile());
1589
1590  // do 8-bit font substitution
1591  } else if (!font->isCIDFont()) {
1592    subst = gTrue;
1593    name = font->getName();
1594    psName = NULL;
1595    if (name) {
1596      for (i = 0; psFonts[i]; ++i) {
1597    if (name->cmp(psFonts[i]) == 0) {
1598      psName = new GooString(psFonts[i]);
1599      break;
1600    }
1601      }
1602    }
1603    if (!psName) {
1604      if (font->isFixedWidth()) {
1605    i = 8;
1606      } else if (font->isSerif()) {
1607    i = 4;
1608      } else {
1609    i = 0;
1610      }
1611      if (font->isBold()) {
1612    i += 2;
1613      }
1614      if (font->isItalic()) {
1615    i += 1;
1616      }
1617      psName = new GooString(psSubstFonts[i].psName);
1618      for (code = 0; code < 256; ++code) {
1619    if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
1620        charName[0] == 'm' && charName[1] == '\0') {
1621      break;
1622    }
1623      }
1624      if (code < 256) {
1625    w1 = ((Gfx8BitFont *)font)->getWidth(code);
1626      } else {
1627    w1 = 0;
1628      }
1629      w2 = psSubstFonts[i].mWidth;
1630      xs = w1 / w2;
1631      if (xs < 0.1) {
1632    xs = 1;
1633      }
1634      if (font->getType() == fontType3) {
1635    // This is a hack which makes it possible to substitute for some
1636    // Type 3 fonts.  The problem is that it's impossible to know what
1637    // the base coordinate system used in the font is without actually
1638    // rendering the font.
1639    ys = xs;
1640    fm = font->getFontMatrix();
1641    if (fm[0] != 0) {
1642      ys *= fm[3] / fm[0];
1643    }
1644      } else {
1645    ys = 1;
1646      }
1647    }
1648
1649  // do 16-bit font substitution
1650  } else if ((fontParam = globalParams->
1651            getPSFont16(font->getName(),
1652                ((GfxCIDFont *)font)->getCollection(),
1653                font->getWMode()))) {
1654    subst = gTrue;
1655    psName = fontParam->psFontName->copy();
1656    if (font16EncLen >= font16EncSize) {
1657      font16EncSize += 16;
1658      font16Enc = (PSFont16Enc *)greallocn(font16Enc,
1659                       font16EncSize, sizeof(PSFont16Enc));
1660    }
1661    font16Enc[font16EncLen].fontID = *font->getID();
1662    font16Enc[font16EncLen].enc = fontParam->encoding->copy();
1663    if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
1664      uMap->decRefCnt();
1665      ++font16EncLen;
1666    } else {
1667      error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
1668        font16Enc[font16EncLen].enc->getCString());
1669    }
1670
1671  // try the display font for embedding
1672  } else if (globalParams->getPSEmbedCIDTrueType() &&
1673         ((GfxCIDFont *)font)->getCollection() &&
1674         (dfp = globalParams->
1675          getDisplayFont(font)) &&
1676         dfp->kind == displayFontTT) {
1677    psName = setupExternalCIDTrueTypeFont(font, dfp->tt.fileName, dfp->tt.faceIndex);
1678
1679  // give up - can't do anything with this font
1680  } else {
1681    error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
1682      font->getName() ? font->getName()->getCString() : "(unnamed)",
1683      ((GfxCIDFont *)font)->getCollection()
1684        ? ((GfxCIDFont *)font)->getCollection()->getCString()
1685        : "(unknown)");
1686    return;
1687  }
1688
1689  // generate PostScript code to set up the font
1690  if (font->isCIDFont()) {
1691    if (level == psLevel3 || level == psLevel3Sep) {
1692      writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
1693         font->getID()->num, font->getID()->gen, psName->getCString(),
1694         font->getWMode());
1695    } else {
1696      writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
1697         font->getID()->num, font->getID()->gen, psName->getCString(),
1698         font->getWMode());
1699    }
1700  } else {
1701    writePSFmt("/F%d_%d /%s %g %g\n",
1702           font->getID()->num, font->getID()->gen, psName->getCString(),
1703           xs, ys);
1704    for (i = 0; i < 256; i += 8) {
1705      writePSFmt((i == 0) ? "[ " : "  ");
1706      for (j = 0; j < 8; ++j) {
1707    if (font->getType() == fontTrueType &&
1708        !subst &&
1709        !((Gfx8BitFont *)font)->getHasEncoding()) {
1710      sprintf(buf, "c%02x", i+j);
1711      charName = buf;
1712    } else {
1713      charName = ((Gfx8BitFont *)font)->getCharName(i+j);
1714      // this is a kludge for broken PDF files that encode char 32
1715      // as .notdef
1716      if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
1717        charName = "space";
1718      }
1719    }
1720    writePS("/");
1721    writePSName(charName ? charName : (char *)".notdef");
1722    // the empty name is legal in PDF and PostScript, but PostScript
1723    // uses a double-slash (//...) for "immediately evaluated names",
1724    // so we need to add a space character here
1725    if (charName && !charName[0]) {
1726      writePS(" ");
1727    }
1728      }
1729      writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
1730    }
1731    writePS("pdfMakeFont\n");
1732  }
1733
1734  delete psName;
1735}
1736
1737void PSOutputDev::setupEmbeddedType1Font(Ref *id, GooString *psName) {
1738  static char hexChar[17] = "0123456789abcdef";
1739  Object refObj, strObj, obj1, obj2, obj3;
1740  Dict *dict;
1741  int length1, length2, length3;
1742  int c;
1743  int start[4];
1744  GBool binMode;
1745  int i;
1746
1747  // check if font is already embedded
1748  for (i = 0; i < fontFileIDLen; ++i) {
1749    if (fontFileIDs[i].num == id->num &&
1750    fontFileIDs[i].gen == id->gen)
1751      return;
1752  }
1753
1754  // add entry to fontFileIDs list
1755  if (fontFileIDLen >= fontFileIDSize) {
1756    fontFileIDSize += 64;
1757    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
1758  }
1759  fontFileIDs[fontFileIDLen++] = *id;
1760
1761  // get the font stream and info
1762  refObj.initRef(id->num, id->gen);
1763  refObj.fetch(xref, &strObj);
1764  refObj.free();
1765  if (!strObj.isStream()) {
1766    error(-1, "Embedded font file object is not a stream");
1767    goto err1;
1768  }
1769  if (!(dict = strObj.streamGetDict())) {
1770    error(-1, "Embedded font stream is missing its dictionary");
1771    goto err1;
1772  }
1773  dict->lookup("Length1", &obj1);
1774  dict->lookup("Length2", &obj2);
1775  dict->lookup("Length3", &obj3);
1776  if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
1777    error(-1, "Missing length fields in embedded font stream dictionary");
1778    obj1.free();
1779    obj2.free();
1780    obj3.free();
1781    goto err1;
1782  }
1783  length1 = obj1.getInt();
1784  length2 = obj2.getInt();
1785  length3 = obj3.getInt();
1786  obj1.free();
1787  obj2.free();
1788  obj3.free();
1789
1790  // beginning comment
1791  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1792  embFontList->append("%%+ font ");
1793  embFontList->append(psName->getCString());
1794  embFontList->append("\n");
1795
1796  // copy ASCII portion of font
1797  strObj.streamReset();
1798  for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
1799    writePSChar(c);
1800  }
1801
1802  // figure out if encrypted portion is binary or ASCII
1803  binMode = gFalse;
1804  for (i = 0; i < 4; ++i) {
1805    start[i] = strObj.streamGetChar();
1806    if (start[i] == EOF) {
1807      error(-1, "Unexpected end of file in embedded font stream");
1808      goto err1;
1809    }
1810    if (!((start[i] >= '0' && start[i] <= '9') ||
1811      (start[i] >= 'A' && start[i] <= 'F') ||
1812      (start[i] >= 'a' && start[i] <= 'f')))
1813      binMode = gTrue;
1814  }
1815
1816  // convert binary data to ASCII
1817  if (binMode) {
1818    for (i = 0; i < 4; ++i) {
1819      writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
1820      writePSChar(hexChar[start[i] & 0x0f]);
1821    }
1822#if 0 // this causes trouble for various PostScript printers
1823    // if Length2 is incorrect (too small), font data gets chopped, so
1824    // we take a few extra characters from the trailer just in case
1825    length2 += length3 >= 8 ? 8 : length3;
1826#endif
1827    while (i < length2) {
1828      if ((c = strObj.streamGetChar()) == EOF) {
1829    break;
1830      }
1831      writePSChar(hexChar[(c >> 4) & 0x0f]);
1832      writePSChar(hexChar[c & 0x0f]);
1833      if (++i % 32 == 0) {
1834    writePSChar('\n');
1835      }
1836    }
1837    if (i % 32 > 0) {
1838      writePSChar('\n');
1839    }
1840
1841  // already in ASCII format -- just copy it
1842  } else {
1843    for (i = 0; i < 4; ++i) {
1844      writePSChar(start[i]);
1845    }
1846    for (i = 4; i < length2; ++i) {
1847      if ((c = strObj.streamGetChar()) == EOF) {
1848    break;
1849      }
1850      writePSChar(c);
1851    }
1852  }
1853
1854  // write padding and "cleartomark"
1855  for (i = 0; i < 8; ++i) {
1856    writePS("00000000000000000000000000000000"
1857        "00000000000000000000000000000000\n");
1858  }
1859  writePS("cleartomark\n");
1860
1861  // ending comment
1862  writePS("%%EndResource\n");
1863
1864 err1:
1865  strObj.streamClose();
1866  strObj.free();
1867}
1868
1869//~ This doesn't handle .pfb files or binary eexec data (which only
1870//~ happens in pfb files?).
1871void PSOutputDev::setupExternalType1Font(GooString *fileName, GooString *psName) {
1872  FILE *fontFile;
1873  int c;
1874  int i;
1875
1876  // check if font is already embedded
1877  for (i = 0; i < fontFileNameLen; ++i) {
1878    if (!fontFileNames[i]->cmp(fileName)) {
1879      return;
1880    }
1881  }
1882
1883  // add entry to fontFileNames list
1884  if (fontFileNameLen >= fontFileNameSize) {
1885    fontFileNameSize += 64;
1886    fontFileNames = (GooString **)greallocn(fontFileNames,
1887                      fontFileNameSize, sizeof(GooString *));
1888    psFileNames = (GooString **)greallocn(psFileNames,
1889                       fontFileNameSize, sizeof(GooString *));
1890  }
1891  fontFileNames[fontFileNameLen] = fileName->copy();
1892  psFileNames[fontFileNameLen] = psName->copy();
1893  fontFileNameLen++;
1894
1895  // beginning comment
1896  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1897  embFontList->append("%%+ font ");
1898  embFontList->append(psName->getCString());
1899  embFontList->append("\n");
1900
1901  // copy the font file
1902  if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
1903    error(-1, "Couldn't open external font file");
1904    return;
1905  }
1906  while ((c = fgetc(fontFile)) != EOF) {
1907    writePSChar(c);
1908  }
1909  fclose(fontFile);
1910
1911  // ending comment
1912  writePS("%%EndResource\n");
1913}
1914
1915void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
1916                      GooString *psName) {
1917  char *fontBuf;
1918  int fontLen;
1919  FoFiType1C *ffT1C;
1920  int i;
1921
1922  // check if font is already embedded
1923  for (i = 0; i < fontFileIDLen; ++i) {
1924    if (fontFileIDs[i].num == id->num &&
1925    fontFileIDs[i].gen == id->gen)
1926      return;
1927  }
1928
1929  // add entry to fontFileIDs list
1930  if (fontFileIDLen >= fontFileIDSize) {
1931    fontFileIDSize += 64;
1932    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
1933  }
1934  fontFileIDs[fontFileIDLen++] = *id;
1935
1936  // beginning comment
1937  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1938  embFontList->append("%%+ font ");
1939  embFontList->append(psName->getCString());
1940  embFontList->append("\n");
1941
1942  // convert it to a Type 1 font
1943  fontBuf = font->readEmbFontFile(xref, &fontLen);
1944  if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
1945    ffT1C->convertToType1(NULL, gTrue, outputFunc, outputStream);
1946    delete ffT1C;
1947  }
1948  gfree(fontBuf);
1949
1950  // ending comment
1951  writePS("%%EndResource\n");
1952}
1953
1954void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
1955                        GooString *psName) {
1956  char unique[32];
1957  char *fontBuf;
1958  int fontLen;
1959  FoFiTrueType *ffTT;
1960  Gushort *codeToGID;
1961  int i;
1962
1963  // check if font is already embedded
1964  for (i = 0; i < fontFileIDLen; ++i) {
1965    if (fontFileIDs[i].num == id->num &&
1966    fontFileIDs[i].gen == id->gen) {
1967      sprintf(unique, "_%d", nextTrueTypeNum++);
1968      psName->append(unique);
1969      break;
1970    }
1971  }
1972
1973  // add entry to fontFileIDs list
1974  if (i == fontFileIDLen) {
1975    if (fontFileIDLen >= fontFileIDSize) {
1976      fontFileIDSize += 64;
1977      fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
1978    }
1979    fontFileIDs[fontFileIDLen++] = *id;
1980  }
1981
1982  // beginning comment
1983  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1984  embFontList->append("%%+ font ");
1985  embFontList->append(psName->getCString());
1986  embFontList->append("\n");
1987
1988  // convert it to a Type 42 font
1989  fontBuf = font->readEmbFontFile(xref, &fontLen);
1990  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
1991    codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
1992    ffTT->convertToType42(psName->getCString(),
1993              ((Gfx8BitFont *)font)->getHasEncoding()
1994                ? ((Gfx8BitFont *)font)->getEncoding()
1995                : (char **)NULL,
1996              codeToGID, outputFunc, outputStream);
1997    gfree(codeToGID);
1998    delete ffTT;
1999  }
2000  gfree(fontBuf);
2001
2002  // ending comment
2003  writePS("%%EndResource\n");
2004}
2005
2006GooString *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font) {
2007  GooString *fileName;
2008  char *fontBuf;
2009  int fontLen;
2010  FoFiTrueType *ffTT;
2011  Gushort *codeToGID;
2012  GooString *psName;
2013  int i;
2014
2015  // check if font is already embedded
2016  fileName = font->getExtFontFile();
2017  for (i = 0; i < fontFileNameLen; ++i) {
2018    if (!fontFileNames[i]->cmp(fileName)) {
2019      return psFileNames[i]->copy();
2020    }
2021  }
2022
2023  psName = filterPSName(font->getName());
2024  // add entry to fontFileNames list
2025  if (i == fontFileNameLen) {
2026    if (fontFileNameLen >= fontFileNameSize) {
2027      fontFileNameSize += 64;
2028      fontFileNames =
2029    (GooString **)greallocn(fontFileNames,
2030                  fontFileNameSize, sizeof(GooString *));
2031      psFileNames =
2032    (GooString **)greallocn(psFileNames,
2033                 fontFileNameSize, sizeof(GooString *));
2034    }
2035  }
2036  fontFileNames[fontFileNameLen] = fileName->copy();
2037  psFileNames[fontFileNameLen] = psName->copy();
2038  fontFileNameLen++;
2039
2040  // beginning comment
2041  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2042  embFontList->append("%%+ font ");
2043  embFontList->append(psName->getCString());
2044  embFontList->append("\n");
2045
2046  // convert it to a Type 42 font
2047  fontBuf = font->readExtFontFile(&fontLen);
2048  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2049    codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
2050    ffTT->convertToType42(psName->getCString(),
2051              ((Gfx8BitFont *)font)->getHasEncoding()
2052                ? ((Gfx8BitFont *)font)->getEncoding()
2053                : (char **)NULL,
2054              codeToGID, outputFunc, outputStream);
2055    delete ffTT;
2056  }
2057  gfree(fontBuf);
2058
2059  // ending comment
2060  writePS("%%EndResource\n");
2061  return psName;
2062}
2063
2064GooString *PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, GooString *fileName, int faceIndex) {
2065//   char *fontBuf;
2066//   int fontLen;
2067  FoFiTrueType *ffTT;
2068  Gushort *codeToGID;
2069  GooString *psName;
2070  int i;
2071  GooString *myFileName;
2072
2073  myFileName = fileName->copy();
2074  if (faceIndex > 0) {
2075    char tmp[32];
2076    sprintf(tmp, ",%d", faceIndex);
2077    myFileName->append(tmp);
2078  }
2079  // check if font is already embedded
2080  for (i = 0; i < fontFileNameLen; ++i) {
2081    if (!fontFileNames[i]->cmp(myFileName)) {
2082      delete myFileName;
2083      return psFileNames[i]->copy();
2084    }
2085  }
2086
2087  psName = filterPSName(font->getName());
2088  // add entry to fontFileNames list
2089  if (i == fontFileNameLen) {
2090    if (fontFileNameLen >= fontFileNameSize) {
2091      fontFileNameSize += 64;
2092      fontFileNames =
2093    (GooString **)grealloc(fontFileNames,
2094                 fontFileNameSize * sizeof(GooString *));
2095      psFileNames =
2096    (GooString **)grealloc(psFileNames,
2097                 fontFileNameSize * sizeof(GooString *));
2098    }
2099  }
2100  fontFileNames[fontFileNameLen] = myFileName;
2101  psFileNames[fontFileNameLen] = psName->copy();
2102  fontFileNameLen++;
2103
2104  // beginning comment
2105  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2106  embFontList->append("%%+ font ");
2107  embFontList->append(psName->getCString());
2108  embFontList->append("\n");
2109
2110  // convert it to a CID type2 font
2111  if ((ffTT = FoFiTrueType::load(fileName->getCString(), faceIndex))) {
2112      int n = ((GfxCIDFont *)font)->getCIDToGIDLen();
2113      if (n) {
2114    codeToGID = (Gushort *)gmalloc(n * sizeof(Gushort));
2115    memcpy(codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(Gushort));
2116      } else {
2117    codeToGID = ((GfxCIDFont *)font)->getCodeToGIDMap(ffTT, &n);
2118      }
2119      if (globalParams->getPSLevel() >= psLevel3) {
2120    // Level 3: use a CID font
2121    ffTT->convertToCIDType2(psName->getCString(),
2122                codeToGID, n, gTrue,
2123                outputFunc, outputStream);
2124      } else {
2125    // otherwise: use a non-CID composite font
2126    ffTT->convertToType0(psName->getCString(),
2127                 codeToGID, n, gTrue,
2128                 outputFunc, outputStream);
2129      }
2130      gfree(codeToGID);
2131      delete ffTT;
2132  }
2133
2134  // ending comment
2135  writePS("%%EndResource\n");
2136  return psName;
2137}
2138
2139void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
2140                        GooString *psName) {
2141  char *fontBuf;
2142  int fontLen;
2143  FoFiType1C *ffT1C;
2144  int i;
2145
2146  // check if font is already embedded
2147  for (i = 0; i < fontFileIDLen; ++i) {
2148    if (fontFileIDs[i].num == id->num &&
2149    fontFileIDs[i].gen == id->gen)
2150      return;
2151  }
2152
2153  // add entry to fontFileIDs list
2154  if (fontFileIDLen >= fontFileIDSize) {
2155    fontFileIDSize += 64;
2156    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2157  }
2158  fontFileIDs[fontFileIDLen++] = *id;
2159
2160  // beginning comment
2161  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2162  embFontList->append("%%+ font ");
2163  embFontList->append(psName->getCString());
2164  embFontList->append("\n");
2165
2166  // convert it to a Type 0 font
2167  fontBuf = font->readEmbFontFile(xref, &fontLen);
2168  if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
2169    if (globalParams->getPSLevel() >= psLevel3) {
2170      // Level 3: use a CID font
2171      ffT1C->convertToCIDType0(psName->getCString(), outputFunc, outputStream);
2172    } else {
2173      // otherwise: use a non-CID composite font
2174      ffT1C->convertToType0(psName->getCString(), outputFunc, outputStream);
2175    }
2176    delete ffT1C;
2177  }
2178  gfree(fontBuf);
2179
2180  // ending comment
2181  writePS("%%EndResource\n");
2182}
2183
2184void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
2185                           GooString *psName,
2186                           GBool needVerticalMetrics) {
2187  char unique[32];
2188  char *fontBuf;
2189  int fontLen;
2190  FoFiTrueType *ffTT;
2191  int i;
2192
2193  // check if font is already embedded
2194  for (i = 0; i < fontFileIDLen; ++i) {
2195    if (fontFileIDs[i].num == id->num &&
2196    fontFileIDs[i].gen == id->gen) {
2197      sprintf(unique, "_%d", nextTrueTypeNum++);
2198      psName->append(unique);
2199      break;
2200    }
2201  }
2202
2203  // add entry to fontFileIDs list
2204  if (fontFileIDLen >= fontFileIDSize) {
2205    fontFileIDSize += 64;
2206    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
2207  }
2208  fontFileIDs[fontFileIDLen++] = *id;
2209
2210  // beginning comment
2211  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2212  embFontList->append("%%+ font ");
2213  embFontList->append(psName->getCString());
2214  embFontList->append("\n");
2215
2216  // convert it to a Type 0 font
2217  fontBuf = font->readEmbFontFile(xref, &fontLen);
2218  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
2219    if (globalParams->getPSLevel() >= psLevel3) {
2220      // Level 3: use a CID font
2221      ffTT->convertToCIDType2(psName->getCString(),
2222                  ((GfxCIDFont *)font)->getCIDToGID(),
2223                  ((GfxCIDFont *)font)->getCIDToGIDLen(),
2224                  needVerticalMetrics,
2225                  outputFunc, outputStream);
2226    } else {
2227      // otherwise: use a non-CID composite font
2228      ffTT->convertToType0(psName->getCString(),
2229               ((GfxCIDFont *)font)->getCIDToGID(),
2230               ((GfxCIDFont *)font)->getCIDToGIDLen(),
2231               needVerticalMetrics,
2232               outputFunc, outputStream);
2233    }
2234    delete ffTT;
2235  }
2236  gfree(fontBuf);
2237
2238  // ending comment
2239  writePS("%%EndResource\n");
2240}
2241
2242void PSOutputDev::setupType3Font(GfxFont *font, GooString *psName,
2243                 Dict *parentResDict) {
2244  Dict *resDict;
2245  Dict *charProcs;
2246  Object charProc;
2247  Gfx *gfx;
2248  PDFRectangle box;
2249  double *m;
2250  char buf[256];
2251  int i;
2252
2253  // set up resources used by font
2254  if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2255    inType3Char = gTrue;
2256    setupResources(resDict);
2257    inType3Char = gFalse;
2258  } else {
2259    resDict = parentResDict;
2260  }
2261
2262  // beginning comment
2263  writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
2264  embFontList->append("%%+ font ");
2265  embFontList->append(psName->getCString());
2266  embFontList->append("\n");
2267
2268  // font dictionary
2269  writePS("8 dict begin\n");
2270  writePS("/FontType 3 def\n");
2271  m = font->getFontMatrix();
2272  writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n",
2273         m[0], m[1], m[2], m[3], m[4], m[5]);
2274  m = font->getFontBBox();
2275  writePSFmt("/FontBBox [%g %g %g %g] def\n",
2276         m[0], m[1], m[2], m[3]);
2277  writePS("/Encoding 256 array def\n");
2278  writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
2279  writePS("/BuildGlyph {\n");
2280  writePS("  exch /CharProcs get exch\n");
2281  writePS("  2 copy known not { pop /.notdef } if\n");
2282  writePS("  get exec\n");
2283  writePS("} bind def\n");
2284  writePS("/BuildChar {\n");
2285  writePS("  1 index /Encoding get exch get\n");
2286  writePS("  1 index /BuildGlyph get exec\n");
2287  writePS("} bind def\n");
2288  if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
2289    writePSFmt("/CharProcs %d dict def\n", charProcs->getLength());
2290    writePS("CharProcs begin\n");
2291    box.x1 = m[0];
2292    box.y1 = m[1];
2293    box.x2 = m[2];
2294    box.y2 = m[3];
2295    gfx = new Gfx(xref, this, resDict, &box, NULL);
2296    inType3Char = gTrue;
2297    t3Cacheable = gFalse;
2298    for (i = 0; i < charProcs->getLength(); ++i) {
2299      writePS("/");
2300      char *aux = charProcs->getKey(i)->getCString();
2301      writePSName(aux);
2302      delete[] aux;
2303      writePS(" {\n");
2304      gfx->display(charProcs->getVal(i, &charProc));
2305      charProc.free();
2306      if (t3String) {
2307    if (t3Cacheable) {
2308      sprintf(buf, "%g %g %g %g %g %g setcachedevice\n",
2309          t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
2310    } else {
2311      sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY);
2312    }
2313    (*outputFunc)(outputStream, buf, strlen(buf));
2314    (*outputFunc)(outputStream, t3String->getCString(),
2315              t3String->getLength());
2316    delete t3String;
2317    t3String = NULL;
2318      }
2319      (*outputFunc)(outputStream, "Q\n", 2);
2320      writePS("} def\n");
2321    }
2322    inType3Char = gFalse;
2323    delete gfx;
2324    writePS("end\n");
2325  }
2326  writePS("currentdict end\n");
2327  writePSFmt("/%s exch definefont pop\n", psName->getCString());
2328
2329  // ending comment
2330  writePS("%%EndResource\n");
2331}
2332
2333void PSOutputDev::setupImages(Dict *resDict) {
2334  Object xObjDict, xObj, xObjRef, subtypeObj;
2335  int i;
2336
2337  if (!(mode == psModeForm || inType3Char)) {
2338    return;
2339  }
2340
2341  //----- recursively scan XObjects
2342  resDict->lookup("XObject", &xObjDict);
2343  if (xObjDict.isDict()) {
2344    for (i = 0; i < xObjDict.dictGetLength(); ++i) {
2345      xObjDict.dictGetValNF(i, &xObjRef);
2346      xObjDict.dictGetVal(i, &xObj);
2347      if (xObj.isStream()) {
2348    xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
2349    if (subtypeObj.isName("Image")) {
2350      if (xObjRef.isRef()) {
2351        setupImage(xObjRef.getRef(), xObj.getStream());
2352      } else {
2353        error(-1, "Image in resource dict is not an indirect reference");
2354      }
2355    }
2356    subtypeObj.free();
2357      }
2358      xObj.free();
2359      xObjRef.free();
2360    }
2361  }
2362  xObjDict.free();
2363}
2364
2365void PSOutputDev::setupImage(Ref id, Stream *str) {
2366  GBool useASCIIHex;
2367  int c;
2368  int size, line, col, i;
2369
2370  // construct an encoder stream
2371  useASCIIHex = level == psLevel1 || level == psLevel1Sep ||
2372                globalParams->getPSASCIIHex();
2373  if (useASCIIHex) {
2374    str = new ASCIIHexEncoder(str);
2375  } else {
2376    str = new ASCII85Encoder(str);
2377  }
2378
2379  // compute image data size
2380  str->reset();
2381  col = size = 0;
2382  do {
2383    do {
2384      c = str->getChar();
2385    } while (c == '\n' || c == '\r');
2386    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2387      break;
2388    }
2389    if (c == 'z') {
2390      ++col;
2391    } else {
2392      ++col;
2393      for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
2394    do {
2395      c = str->getChar();
2396    } while (c == '\n' || c == '\r');
2397    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2398      break;
2399    }
2400    ++col;
2401      }
2402    }
2403    if (col > 225) {
2404      ++size;
2405      col = 0;
2406    }
2407  } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
2408  ++size;
2409  writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
2410  str->close();
2411
2412  // write the data into the array
2413  str->reset();
2414  line = col = 0;
2415  writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~"));
2416  do {
2417    do {
2418      c = str->getChar();
2419    } while (c == '\n' || c == '\r');
2420    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2421      break;
2422    }
2423    if (c == 'z') {
2424      writePSChar(c);
2425      ++col;
2426    } else {
2427      writePSChar(c);
2428      ++col;
2429      for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
2430    do {
2431      c = str->getChar();
2432    } while (c == '\n' || c == '\r');
2433    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
2434      break;
2435    }
2436    writePSChar(c);
2437    ++col;
2438      }
2439    }
2440    // each line is: "dup nnnnn <~...data...~> put<eol>"
2441    // so max data length = 255 - 20 = 235
2442    // chunks are 1 or 4 bytes each, so we have to stop at 232
2443    // but make it 225 just to be safe
2444    if (col > 225) {
2445      writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
2446      ++line;
2447      writePSFmt((char *)(useASCIIHex ? "dup %d <" : "dup %d <~"), line);
2448      col = 0;
2449    }
2450  } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
2451  writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
2452  writePS("pop\n");
2453  str->close();
2454
2455  delete str;
2456}
2457
2458void PSOutputDev::startPage(int pageNum, GfxState *state) {
2459  int x1, y1, x2, y2, width, height;
2460  int imgWidth, imgHeight, imgWidth2, imgHeight2;
2461  GBool landscape;
2462
2463
2464  if (mode == psModePS) {
2465    writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage);
2466    writePS("%%BeginPageSetup\n");
2467  }
2468
2469  // underlays
2470  if (underlayCbk) {
2471    (*underlayCbk)(this, underlayCbkData);
2472  }
2473  if (overlayCbk) {
2474    saveState(NULL);
2475  }
2476
2477  switch (mode) {
2478
2479  case psModePS:
2480    // rotate, translate, and scale page
2481    imgWidth = imgURX - imgLLX;
2482    imgHeight = imgURY - imgLLY;
2483    x1 = (int)floor(state->getX1());
2484    y1 = (int)floor(state->getY1());
2485    x2 = (int)ceil(state->getX2());
2486    y2 = (int)ceil(state->getY2());
2487    width = x2 - x1;
2488    height = y2 - y1;
2489    tx = ty = 0;
2490    // rotation and portrait/landscape mode
2491    if (rotate0 >= 0) {
2492      rotate = (360 - rotate0) % 360;
2493      landscape = gFalse;
2494    } else {
2495      rotate = (360 - state->getRotate()) % 360;
2496      if (rotate == 0 || rotate == 180) {
2497    if (width > height && width > imgWidth) {
2498      rotate += 90;
2499      landscape = gTrue;
2500    } else {
2501      landscape = gFalse;
2502    }
2503      } else { // rotate == 90 || rotate == 270
2504    if (height > width && height > imgWidth) {
2505      rotate = 270 - rotate;
2506      landscape = gTrue;
2507    } else {
2508      landscape = gFalse;
2509    }
2510      }
2511    }
2512      writePSFmt("%%%%PageOrientation: %s\n",
2513           landscape ? "Landscape" : "Portrait");
2514      writePS("pdfStartPage\n");
2515    if (rotate == 0) {
2516      imgWidth2 = imgWidth;
2517      imgHeight2 = imgHeight;
2518    } else if (rotate == 90) {
2519      writePS("90 rotate\n");
2520      ty = -imgWidth;
2521      imgWidth2 = imgHeight;
2522      imgHeight2 = imgWidth;
2523    } else if (rotate == 180) {
2524      writePS("180 rotate\n");
2525      imgWidth2 = imgWidth;
2526      imgHeight2 = imgHeight;
2527      tx = -imgWidth;
2528      ty = -imgHeight;
2529    } else { // rotate == 270
2530      writePS("270 rotate\n");
2531      tx = -imgHeight;
2532      imgWidth2 = imgHeight;
2533      imgHeight2 = imgWidth;
2534    }
2535    // shrink or expand
2536    if (xScale0 > 0 && yScale0 > 0) {
2537      xScale = xScale0;
2538      yScale = yScale0;
2539    } else if ((globalParams->getPSShrinkLarger() &&
2540     (width > imgWidth2 || height > imgHeight2)) ||
2541    (globalParams->getPSExpandSmaller() &&
2542     (width < imgWidth2 && height < imgHeight2))) {
2543      xScale = (double)imgWidth2 / (double)width;
2544      yScale = (double)imgHeight2 / (double)height;
2545      if (yScale < xScale) {
2546    xScale = yScale;
2547      } else {
2548    yScale = xScale;
2549      }
2550    } else {
2551      xScale = yScale = 1;
2552    }
2553    // deal with odd bounding boxes or clipping
2554    if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2555      tx -= xScale * clipLLX0;
2556      ty -= yScale * clipLLY0;
2557    } else {
2558    tx -= xScale * x1;
2559    ty -= yScale * y1;
2560    }
2561    // center
2562    if (globalParams->getPSCenter()) {
2563      if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2564    tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
2565    ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
2566      } else {
2567      tx += (imgWidth2 - xScale * width) / 2;
2568      ty += (imgHeight2 - yScale * height) / 2;
2569    }
2570    }
2571    tx += rotate == 0 ? imgLLX + tx0 : imgLLY + ty0;
2572    ty += rotate == 0 ? imgLLY + ty0 : -(imgLLX + tx0);
2573    if (tx != 0 || ty != 0) {
2574      writePSFmt("%g %g translate\n", tx, ty);
2575    }
2576    if (xScale != 1 || yScale != 1) {
2577      writePSFmt("%0.4f %0.4f scale\n", xScale, xScale);
2578    }
2579    if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
2580      writePSFmt("%g %g %g %g re W\n",
2581         clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
2582    } else {
2583      writePSFmt("%d %d %d %d re W\n", x1, y1, x2 - x1, y2 - y1);
2584    }
2585
2586    writePS("%%EndPageSetup\n");
2587    ++seqPage;
2588    break;
2589
2590  case psModeEPS:
2591    writePS("pdfStartPage\n");
2592    tx = ty = 0;
2593    rotate = (360 - state->getRotate()) % 360;
2594    if (rotate == 0) {
2595    } else if (rotate == 90) {
2596      writePS("90 rotate\n");
2597      tx = -epsX1;
2598      ty = -epsY2;
2599    } else if (rotate == 180) {
2600      writePS("180 rotate\n");
2601      tx = -(epsX1 + epsX2);
2602      ty = -(epsY1 + epsY2);
2603    } else { // rotate == 270
2604      writePS("270 rotate\n");
2605      tx = -epsX2;
2606      ty = -epsY1;
2607    }
2608    if (tx != 0 || ty != 0) {
2609      writePSFmt("%g %g translate\n", tx, ty);
2610    }
2611    xScale = yScale = 1;
2612    break;
2613
2614  case psModeForm:
2615    writePS("/PaintProc {\n");
2616    writePS("begin xpdf begin\n");
2617    writePS("pdfStartPage\n");
2618    tx = ty = 0;
2619    xScale = yScale = 1;
2620    rotate = 0;
2621    break;
2622  }
2623}
2624
2625void PSOutputDev::endPage() {
2626  if (overlayCbk) {
2627    restoreState(NULL);
2628    (*overlayCbk)(this, overlayCbkData);
2629  }
2630
2631
2632  if (mode == psModeForm) {
2633    writePS("pdfEndPage\n");
2634    writePS("end end\n");
2635    writePS("} def\n");
2636    writePS("end end\n");
2637  } else {
2638    if (!manualCtrl) {
2639      writePS("showpage\n");
2640    }
2641      writePS("%%PageTrailer\n");
2642      writePageTrailer();
2643    }
2644}
2645
2646void PSOutputDev::saveState(GfxState *state) {
2647  writePS("q\n");
2648  ++numSaves;
2649}
2650
2651void PSOutputDev::restoreState(GfxState *state) {
2652  writePS("Q\n");
2653  --numSaves;
2654}
2655
2656void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
2657                double m21, double m22, double m31, double m32) {
2658  writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
2659}
2660
2661void PSOutputDev::updateLineDash(GfxState *state) {
2662  double *dash;
2663  double start;
2664  int length, i;
2665
2666  state->getLineDash(&dash, &length, &start);
2667  writePS("[");
2668  for (i = 0; i < length; ++i) {
2669    writePSFmt("%g%s",
2670           dash[i] == 0 ? 1 : dash[i],
2671           (i == length-1) ? "" : " ");
2672  }
2673  writePSFmt("] %g d\n", start);
2674}
2675
2676void PSOutputDev::updateFlatness(GfxState *state) {
2677  writePSFmt("%d i\n", state->getFlatness());
2678}
2679
2680void PSOutputDev::updateLineJoin(GfxState *state) {
2681  writePSFmt("%d j\n", state->getLineJoin());
2682}
2683
2684void PSOutputDev::updateLineCap(GfxState *state) {
2685  writePSFmt("%d J\n", state->getLineCap());
2686}
2687
2688void PSOutputDev::updateMiterLimit(GfxState *state) {
2689  writePSFmt("%g M\n", state->getMiterLimit());
2690}
2691
2692void PSOutputDev::updateLineWidth(GfxState *state) {
2693  writePSFmt("%g w\n", state->getLineWidth());
2694}
2695
2696void PSOutputDev::updateFillColorSpace(GfxState *state) {
2697  switch (level) {
2698  case psLevel1:
2699  case psLevel1Sep:
2700    break;
2701  case psLevel2:
2702  case psLevel3:
2703    if (state->getFillColorSpace()->getMode() != csPattern) {
2704      dumpColorSpaceL2(state->getFillColorSpace(), gTrue, gFalse);
2705      writePS(" cs\n");
2706    }
2707    break;
2708  case psLevel2Sep:
2709  case psLevel3Sep:
2710    break;
2711  }
2712}
2713
2714void PSOutputDev::updateStrokeColorSpace(GfxState *state) {
2715  switch (level) {
2716  case psLevel1:
2717  case psLevel1Sep:
2718    break;
2719  case psLevel2:
2720  case psLevel3:
2721    if (state->getStrokeColorSpace()->getMode() != csPattern) {
2722      dumpColorSpaceL2(state->getStrokeColorSpace(), gTrue, gFalse);
2723      writePS(" CS\n");
2724    }
2725    break;
2726  case psLevel2Sep:
2727  case psLevel3Sep:
2728    break;
2729  }
2730}
2731
2732void PSOutputDev::updateFillColor(GfxState *state) {
2733  GfxColor color;
2734  GfxColor *colorPtr;
2735  GfxGray gray;
2736  GfxCMYK cmyk;
2737  GfxSeparationColorSpace *sepCS;
2738  double c, m, y, k;
2739  int i;
2740
2741  switch (level) {
2742  case psLevel1:
2743    state->getFillGray(&gray);
2744    writePSFmt("%g g\n", colToDbl(gray));
2745    break;
2746  case psLevel1Sep:
2747    state->getFillCMYK(&cmyk);
2748    c = colToDbl(cmyk.c);
2749    m = colToDbl(cmyk.m);
2750    y = colToDbl(cmyk.y);
2751    k = colToDbl(cmyk.k);
2752    writePSFmt("%g %g %g %g k\n", c, m, y, k);
2753    addProcessColor(c, m, y, k);
2754    break;
2755  case psLevel2:
2756  case psLevel3:
2757    if (state->getFillColorSpace()->getMode() != csPattern) {
2758      colorPtr = state->getFillColor();
2759      writePS("[");
2760      for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
2761    if (i > 0) {
2762      writePS(" ");
2763      }
2764    writePSFmt("%g", colToDbl(colorPtr->c[i]));
2765      }
2766      writePS("] sc\n");
2767    }
2768    break;
2769  case psLevel2Sep:
2770  case psLevel3Sep:
2771    if (state->getFillColorSpace()->getMode() == csSeparation) {
2772      sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
2773      color.c[0] = gfxColorComp1;
2774      sepCS->getCMYK(&color, &cmyk);
2775      writePSFmt("%g %g %g %g %g (%s) ck\n",
2776         colToDbl(state->getFillColor()->c[0]),
2777         colToDbl(cmyk.c), colToDbl(cmyk.m),
2778         colToDbl(cmyk.y), colToDbl(cmyk.k),
2779         sepCS->getName()->getCString());
2780      addCustomColor(sepCS);
2781    } else {
2782      state->getFillCMYK(&cmyk);
2783      c = colToDbl(cmyk.c);
2784      m = colToDbl(cmyk.m);
2785      y = colToDbl(cmyk.y);
2786      k = colToDbl(cmyk.k);
2787      writePSFmt("%g %g %g %g k\n", c, m, y, k);
2788      addProcessColor(c, m, y, k);
2789    }
2790    break;
2791  }
2792  t3Cacheable = gFalse;
2793}
2794
2795void PSOutputDev::updateStrokeColor(GfxState *state) {
2796  GfxColor color;
2797  GfxColor *colorPtr;
2798  GfxGray gray;
2799  GfxCMYK cmyk;
2800  GfxSeparationColorSpace *sepCS;
2801  double c, m, y, k;
2802  int i;
2803
2804  switch (level) {
2805  case psLevel1:
2806    state->getStrokeGray(&gray);
2807    writePSFmt("%g G\n", colToDbl(gray));
2808    break;
2809  case psLevel1Sep:
2810    state->getStrokeCMYK(&cmyk);
2811    c = colToDbl(cmyk.c);
2812    m = colToDbl(cmyk.m);
2813    y = colToDbl(cmyk.y);
2814    k = colToDbl(cmyk.k);
2815    writePSFmt("%g %g %g %g K\n", c, m, y, k);
2816    addProcessColor(c, m, y, k);
2817    break;
2818  case psLevel2:
2819  case psLevel3:
2820    if (state->getStrokeColorSpace()->getMode() != csPattern) {
2821      colorPtr = state->getStrokeColor();
2822      writePS("[");
2823      for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
2824    if (i > 0) {
2825      writePS(" ");
2826      }
2827    writePSFmt("%g", colToDbl(colorPtr->c[i]));
2828      }
2829      writePS("] SC\n");
2830    }
2831    break;
2832  case psLevel2Sep:
2833  case psLevel3Sep:
2834    if (state->getStrokeColorSpace()->getMode() == csSeparation) {
2835      sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
2836      color.c[0] = gfxColorComp1;
2837      sepCS->getCMYK(&color, &cmyk);
2838      writePSFmt("%g %g %g %g %g (%s) CK\n",
2839         colToDbl(state->getStrokeColor()->c[0]),
2840         colToDbl(cmyk.c), colToDbl(cmyk.m),
2841         colToDbl(cmyk.y), colToDbl(cmyk.k),
2842         sepCS->getName()->getCString());
2843      addCustomColor(sepCS);
2844    } else {
2845      state->getStrokeCMYK(&cmyk);
2846      c = colToDbl(cmyk.c);
2847      m = colToDbl(cmyk.m);
2848      y = colToDbl(cmyk.y);
2849      k = colToDbl(cmyk.k);
2850      writePSFmt("%g %g %g %g K\n", c, m, y, k);
2851      addProcessColor(c, m, y, k);
2852    }
2853    break;
2854  }
2855  t3Cacheable = gFalse;
2856}
2857
2858void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
2859  if (c > 0) {
2860    processColors |= psProcessCyan;
2861  }
2862  if (m > 0) {
2863    processColors |= psProcessMagenta;
2864  }
2865  if (y > 0) {
2866    processColors |= psProcessYellow;
2867  }
2868  if (k > 0) {
2869    processColors |= psProcessBlack;
2870  }
2871}
2872
2873void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
2874  PSOutCustomColor *cc;
2875  GfxColor color;
2876  GfxCMYK cmyk;
2877
2878  for (cc = customColors; cc; cc = cc->next) {
2879    if (!cc->name->cmp(sepCS->getName())) {
2880      return;
2881    }
2882  }
2883  color.c[0] = gfxColorComp1;
2884  sepCS->getCMYK(&color, &cmyk);
2885  cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
2886                colToDbl(cmyk.y), colToDbl(cmyk.k),
2887                sepCS->getName()->copy());
2888  cc->next = customColors;
2889  customColors = cc;
2890}
2891
2892void PSOutputDev::updateFillOverprint(GfxState *state) {
2893  if (level >= psLevel2) {
2894    writePSFmt("%s op\n", state->getFillOverprint() ? "true" : "false");
2895  }
2896}
2897
2898void PSOutputDev::updateStrokeOverprint(GfxState *state) {
2899  if (level >= psLevel2) {
2900    writePSFmt("%s OP\n", state->getStrokeOverprint() ? "true" : "false");
2901  }
2902}
2903
2904void PSOutputDev::updateFont(GfxState *state) {
2905  if (state->getFont()) {
2906    writePSFmt("/F%d_%d %g Tf\n",
2907           state->getFont()->getID()->num, state->getFont()->getID()->gen,
2908           fabs(state->getFontSize()) < 0.00001 ? 0.00001
2909                                                : state->getFontSize());
2910  }
2911}
2912
2913void PSOutputDev::updateTextMat(GfxState *state) {
2914  double *mat;
2915
2916  mat = state->getTextMat();
2917  if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) {
2918    // avoid a singular (or close-to-singular) matrix
2919    writePSFmt("[0.00001 0 0 0.00001 %g %g] Tm\n", mat[4], mat[5]);
2920  } else {
2921  writePSFmt("[%g %g %g %g %g %g] Tm\n",
2922         mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
2923  }
2924}
2925
2926void PSOutputDev::updateCharSpace(GfxState *state) {
2927  writePSFmt("%g Tc\n", state->getCharSpace());
2928}
2929
2930void PSOutputDev::updateRender(GfxState *state) {
2931  int rm;
2932
2933  rm = state->getRender();
2934  writePSFmt("%d Tr\n", rm);
2935  rm &= 3;
2936  if (rm != 0 && rm != 3) {
2937    t3Cacheable = gFalse;
2938  }
2939}
2940
2941void PSOutputDev::updateRise(GfxState *state) {
2942  writePSFmt("%g Ts\n", state->getRise());
2943}
2944
2945void PSOutputDev::updateWordSpace(GfxState *state) {
2946  writePSFmt("%g Tw\n", state->getWordSpace());
2947}
2948
2949void PSOutputDev::updateHorizScaling(GfxState *state) {
2950  double h;
2951
2952  h = state->getHorizScaling();
2953  if (fabs(h) < 0.01) {
2954    h = 0.01;
2955  }
2956  writePSFmt("%g Tz\n", h);
2957}
2958
2959void PSOutputDev::updateTextPos(GfxState *state) {
2960  writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
2961}
2962
2963void PSOutputDev::updateTextShift(GfxState *state, double shift) {
2964  if (state->getFont()->getWMode()) {
2965    writePSFmt("%g TJmV\n", shift);
2966  } else {
2967    writePSFmt("%g TJm\n", shift);
2968  }
2969}
2970
2971void PSOutputDev::stroke(GfxState *state) {
2972  doPath(state->getPath());
2973  if (t3String) {
2974    // if we're construct a cacheable Type 3 glyph, we need to do
2975    // everything in the fill color
2976    writePS("Sf\n");
2977  } else {
2978    writePS("S\n");
2979  }
2980}
2981
2982void PSOutputDev::fill(GfxState *state) {
2983  doPath(state->getPath());
2984  writePS("f\n");
2985}
2986
2987void PSOutputDev::eoFill(GfxState *state) {
2988  doPath(state->getPath());
2989  writePS("f*\n");
2990}
2991
2992void PSOutputDev::tilingPatternFill(GfxState *state, Object *str,
2993                    int paintType, Dict *resDict,
2994                    double *mat, double *bbox,
2995                    int x0, int y0, int x1, int y1,
2996                    double xStep, double yStep) {
2997  PDFRectangle box;
2998  Gfx *gfx;
2999
3000  // define a Type 3 font
3001  writePS("8 dict begin\n");
3002  writePS("/FontType 3 def\n");
3003  writePS("/FontMatrix [1 0 0 1 0 0] def\n");
3004  writePSFmt("/FontBBox [%g %g %g %g] def\n",
3005         bbox[0], bbox[1], bbox[2], bbox[3]);
3006  writePS("/Encoding 256 array def\n");
3007  writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
3008  writePS("  Encoding 120 /x put\n");
3009  writePS("/BuildGlyph {\n");
3010  writePS("  exch /CharProcs get exch\n");
3011  writePS("  2 copy known not { pop /.notdef } if\n");
3012  writePS("  get exec\n");
3013  writePS("} bind def\n");
3014  writePS("/BuildChar {\n");
3015  writePS("  1 index /Encoding get exch get\n");
3016  writePS("  1 index /BuildGlyph get exec\n");
3017  writePS("} bind def\n");
3018  writePS("/CharProcs 1 dict def\n");
3019  writePS("CharProcs begin\n");
3020  box.x1 = bbox[0];
3021  box.y1 = bbox[1];
3022  box.x2 = bbox[2];
3023  box.y2 = bbox[3];
3024  gfx = new Gfx(xref, this, resDict, &box, NULL);
3025  writePS("/x {\n");
3026  if (paintType == 2) {
3027    writePSFmt("%g 0 %g %g %g %g setcachedevice\n",
3028           xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
3029  } else {
3030    writePSFmt("%g 0 setcharwidth\n", xStep);
3031  }
3032  inType3Char = gTrue;
3033  ++numTilingPatterns;
3034  gfx->display(str);
3035  --numTilingPatterns;
3036  inType3Char = gFalse;
3037  writePS("} def\n");
3038  delete gfx;
3039  writePS("end\n");
3040  writePS("currentdict end\n");
3041  writePSFmt("/xpdfTile%d exch definefont pop\n", numTilingPatterns);
3042
3043  // draw the tiles
3044  writePSFmt("/xpdfTile%d findfont setfont\n", numTilingPatterns);
3045  writePSFmt("gsave [%g %g %g %g %g %g] concat\n",
3046         mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
3047  writePSFmt("%d 1 %d { %g exch %g mul m %d 1 %d { pop (x) show } for } for\n",
3048         y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1);
3049  writePS("grestore\n");
3050}
3051
3052void PSOutputDev::functionShadedFill(GfxState *state,
3053                     GfxFunctionShading *shading) {
3054  double x0, y0, x1, y1;
3055  double *mat;
3056  int i;
3057
3058  shading->getDomain(&x0, &y0, &x1, &y1);
3059  mat = shading->getMatrix();
3060  writePSFmt("/mat [%g %g %g %g %g %g] def\n",
3061         mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
3062  writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3063  if (shading->getNFuncs() == 1) {
3064    writePS("/func ");
3065    cvtFunction(shading->getFunc(0));
3066    writePS("def\n");
3067  } else {
3068    writePS("/func {\n");
3069    for (i = 0; i < shading->getNFuncs(); ++i) {
3070      if (i < shading->getNFuncs() - 1) {
3071    writePS("2 copy\n");
3072      }
3073      cvtFunction(shading->getFunc(i));
3074      writePS("exec\n");
3075      if (i < shading->getNFuncs() - 1) {
3076    writePS("3 1 roll\n");
3077      }
3078    }
3079    writePS("} def\n");
3080  }
3081  writePSFmt("%g %g %g %g 0 funcSH\n", x0, y0, x1, y1);
3082}
3083
3084void PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) {
3085  double xMin, yMin, xMax, yMax;
3086  double x0, y0, x1, y1, dx, dy, mul;
3087  double tMin, tMax, t, t0, t1;
3088  int i;
3089
3090  // get the clip region bbox
3091  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3092
3093  // compute min and max t values, based on the four corners of the
3094  // clip region bbox
3095  shading->getCoords(&x0, &y0, &x1, &y1);
3096  dx = x1 - x0;
3097  dy = y1 - y0;
3098  mul = 1 / (dx * dx + dy * dy);
3099  tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
3100  t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
3101  if (t < tMin) {
3102    tMin = t;
3103  } else if (t > tMax) {
3104    tMax = t;
3105  }
3106  t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
3107  if (t < tMin) {
3108    tMin = t;
3109  } else if (t > tMax) {
3110    tMax = t;
3111  }
3112  t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
3113  if (t < tMin) {
3114    tMin = t;
3115  } else if (t > tMax) {
3116    tMax = t;
3117  }
3118  if (tMin < 0 && !shading->getExtend0()) {
3119    tMin = 0;
3120  }
3121  if (tMax > 1 && !shading->getExtend1()) {
3122    tMax = 1;
3123  }
3124
3125  // get the function domain
3126  t0 = shading->getDomain0();
3127  t1 = shading->getDomain1();
3128
3129  // generate the PS code
3130  writePSFmt("/t0 %g def\n", t0);
3131  writePSFmt("/t1 %g def\n", t1);
3132  writePSFmt("/dt %g def\n", t1 - t0);
3133  writePSFmt("/x0 %g def\n", x0);
3134  writePSFmt("/y0 %g def\n", y0);
3135  writePSFmt("/dx %g def\n", x1 - x0);
3136  writePSFmt("/x1 %g def\n", x1);
3137  writePSFmt("/y1 %g def\n", y1);
3138  writePSFmt("/dy %g def\n", y1 - y0);
3139  writePSFmt("/xMin %g def\n", xMin);
3140  writePSFmt("/yMin %g def\n", yMin);
3141  writePSFmt("/xMax %g def\n", xMax);
3142  writePSFmt("/yMax %g def\n", yMax);
3143  writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3144  if (shading->getNFuncs() == 1) {
3145    writePS("/func ");
3146    cvtFunction(shading->getFunc(0));
3147    writePS("def\n");
3148  } else {
3149    writePS("/func {\n");
3150    for (i = 0; i < shading->getNFuncs(); ++i) {
3151      if (i < shading->getNFuncs() - 1) {
3152    writePS("dup\n");
3153      }
3154      cvtFunction(shading->getFunc(i));
3155      writePS("exec\n");
3156      if (i < shading->getNFuncs() - 1) {
3157    writePS("exch\n");
3158      }
3159    }
3160    writePS("} def\n");
3161  }
3162  writePSFmt("%g %g 0 axialSH\n", tMin, tMax);
3163}
3164
3165void PSOutputDev::radialShadedFill(GfxState *state,
3166                   GfxRadialShading *shading) {
3167  double x0, y0, r0, x1, y1, r1, t0, t1, sMin, sMax;
3168  double xMin, yMin, xMax, yMax;
3169  double d0, d1;
3170  int i;
3171
3172  // get the shading info
3173  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
3174  t0 = shading->getDomain0();
3175  t1 = shading->getDomain1();
3176
3177  // compute the (possibly extended) s range
3178  sMin = 0;
3179  sMax = 1;
3180  if (shading->getExtend0()) {
3181    if (r0 < r1) {
3182      // extend the smaller end
3183      sMin = -r0 / (r1 - r0);
3184    } else {
3185      // extend the larger end
3186      state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3187      d0 = (x0 - xMin) * (x0 - xMin);
3188      d1 = (x0 - xMax) * (x0 - xMax);
3189      sMin = d0 > d1 ? d0 : d1;
3190      d0 = (y0 - yMin) * (y0 - yMin);
3191      d1 = (y0 - yMax) * (y0 - yMax);
3192      sMin += d0 > d1 ? d0 : d1;
3193      sMin = (sqrt(sMin) - r0) / (r1 - r0);
3194      if (sMin > 0) {
3195    sMin = 0;
3196      } else if (sMin < -20) {
3197    // sanity check
3198    sMin = -20;
3199      }
3200    }
3201  }
3202  if (shading->getExtend1()) {
3203    if (r1 < r0) {
3204      // extend the smaller end
3205      sMax = -r0 / (r1 - r0);
3206    } else if (r1 > r0) {
3207      // extend the larger end
3208      state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3209      d0 = (x1 - xMin) * (x1 - xMin);
3210      d1 = (x1 - xMax) * (x1 - xMax);
3211      sMax = d0 > d1 ? d0 : d1;
3212      d0 = (y1 - yMin) * (y1 - yMin);
3213      d1 = (y1 - yMax) * (y1 - yMax);
3214      sMax += d0 > d1 ? d0 : d1;
3215      sMax = (sqrt(sMax) - r0) / (r1 - r0);
3216      if (sMax < 1) {
3217    sMax = 1;
3218      } else if (sMax > 20) {
3219    // sanity check
3220    sMax = 20;
3221      }
3222    }
3223  }
3224
3225  // generate the PS code
3226  writePSFmt("/x0 %g def\n", x0);
3227  writePSFmt("/x1 %g def\n", x1);
3228  writePSFmt("/dx %g def\n", x1 - x0);
3229  writePSFmt("/y0 %g def\n", y0);
3230  writePSFmt("/y1 %g def\n", y1);
3231  writePSFmt("/dy %g def\n", y1 - y0);
3232  writePSFmt("/r0 %g def\n", r0);
3233  writePSFmt("/r1 %g def\n", r1);
3234  writePSFmt("/dr %g def\n", r1 - r0);
3235  writePSFmt("/t0 %g def\n", t0);
3236  writePSFmt("/t1 %g def\n", t1);
3237  writePSFmt("/dt %g def\n", t1 - t0);
3238  writePSFmt("/n %d def\n", shading->getColorSpace()->getNComps());
3239  if (shading->getNFuncs() == 1) {
3240    writePS("/func ");
3241    cvtFunction(shading->getFunc(0));
3242    writePS("def\n");
3243  } else {
3244    writePS("/func {\n");
3245    for (i = 0; i < shading->getNFuncs(); ++i) {
3246      if (i < shading->getNFuncs() - 1) {
3247    writePS("dup\n");
3248      }
3249      cvtFunction(shading->getFunc(i));
3250      writePS("exec\n");
3251      if (i < shading->getNFuncs() - 1) {
3252    writePS("exch\n");
3253      }
3254    }
3255    writePS("} def\n");
3256  }
3257  writePSFmt("%g %g 0 radialSH\n", sMin, sMax);
3258}
3259
3260void PSOutputDev::clip(GfxState *state) {
3261  doPath(state->getPath());
3262  writePS("W\n");
3263}
3264
3265void PSOutputDev::eoClip(GfxState *state) {
3266  doPath(state->getPath());
3267  writePS("W*\n");
3268}
3269
3270void PSOutputDev::doPath(GfxPath *path) {
3271  GfxSubpath *subpath;
3272  double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
3273  int n, m, i, j;
3274
3275  n = path->getNumSubpaths();
3276
3277  if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
3278    subpath = path->getSubpath(0);
3279    x0 = subpath->getX(0);
3280    y0 = subpath->getY(0);
3281    x4 = subpath->getX(4);
3282    y4 = subpath->getY(4);
3283    if (x4 == x0 && y4 == y0) {
3284      x1 = subpath->getX(1);
3285      y1 = subpath->getY(1);
3286      x2 = subpath->getX(2);
3287      y2 = subpath->getY(2);
3288      x3 = subpath->getX(3);
3289      y3 = subpath->getY(3);
3290      if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
3291    writePSFmt("%g %g %g %g re\n",
3292           x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
3293           fabs(x2 - x0), fabs(y1 - y0));
3294    return;
3295      } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
3296    writePSFmt("%g %g %g %g re\n",
3297           x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
3298           fabs(x1 - x0), fabs(y2 - y0));
3299    return;
3300      }
3301    }
3302  }
3303
3304  for (i = 0; i < n; ++i) {
3305    subpath = path->getSubpath(i);
3306    m = subpath->getNumPoints();
3307    writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0));
3308    j = 1;
3309    while (j < m) {
3310      if (subpath->getCurve(j)) {
3311    writePSFmt("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
3312           subpath->getX(j+1), subpath->getY(j+1),
3313           subpath->getX(j+2), subpath->getY(j+2));
3314    j += 3;
3315      } else {
3316    writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j));
3317    ++j;
3318      }
3319    }
3320    if (subpath->isClosed()) {
3321      writePS("h\n");
3322    }
3323  }
3324}
3325
3326void PSOutputDev::drawString(GfxState *state, GooString *s) {
3327  GfxFont *font;
3328  int wMode;
3329  GooString *s2;
3330  double dx, dy, dx2, dy2, originX, originY;
3331  char *p;
3332  UnicodeMap *uMap;
3333  CharCode code;
3334  Unicode u[8];
3335  char buf[8];
3336  int len, nChars, uLen, n, m, i, j;
3337
3338  // check for invisible text -- this is used by Acrobat Capture
3339  if (state->getRender() == 3) {
3340    return;
3341  }
3342
3343  // ignore empty strings
3344  if (s->getLength() == 0) {
3345    return;
3346  }
3347
3348  // get the font
3349  if (!(font = state->getFont())) {
3350    return;
3351  }
3352  wMode = font->getWMode();
3353
3354  // check for a subtitute 16-bit font
3355  uMap = NULL;
3356  if (font->isCIDFont()) {
3357    for (i = 0; i < font16EncLen; ++i) {
3358      if (font->getID()->num == font16Enc[i].fontID.num &&
3359      font->getID()->gen == font16Enc[i].fontID.gen) {
3360    uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
3361    break;
3362      }
3363    }
3364  }
3365
3366  // compute width of chars in string, ignoring char spacing and word
3367  // spacing -- the Tj operator will adjust for the metrics of the
3368  // font that's actually used
3369  dx = dy = 0;
3370  nChars = 0;
3371  p = s->getCString();
3372  len = s->getLength();
3373  if (font->isCIDFont()) {
3374    s2 = new GooString();
3375  } else {
3376    s2 = s;
3377  }
3378  while (len > 0) {
3379    n = font->getNextChar(p, len, &code,
3380              u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
3381              &dx2, &dy2, &originX, &originY);
3382    if (font->isCIDFont()) {
3383      if (uMap) {
3384    for (i = 0; i < uLen; ++i) {
3385      m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
3386      for (j = 0; j < m; ++j) {
3387        s2->append(buf[j]);
3388      }
3389    }
3390    //~ this really needs to get the number of chars in the target
3391    //~ encoding - which may be more than the number of Unicode
3392    //~ chars
3393    nChars += uLen;
3394      } else {
3395    s2->append((char)((code >> 8) & 0xff));
3396    s2->append((char)(code & 0xff));
3397    ++nChars;
3398      }
3399    }
3400    dx += dx2;
3401    dy += dy2;
3402    p += n;
3403    len -= n;
3404  }
3405  dx *= state->getFontSize() * state->getHorizScaling();
3406  dy *= state->getFontSize();
3407  if (uMap) {
3408    uMap->decRefCnt();
3409  }
3410
3411  if (s2->getLength() > 0) {
3412    writePSString(s2);
3413    if (font->isCIDFont()) {
3414      if (wMode) {
3415    writePSFmt(" %d %g Tj16V\n", nChars, dy);
3416      } else {
3417    writePSFmt(" %d %g Tj16\n", nChars, dx);
3418      }
3419    } else {
3420      writePSFmt(" %g Tj\n", dx);
3421    }
3422  }
3423  if (font->isCIDFont()) {
3424    delete s2;
3425  }
3426
3427  if (state->getRender() & 4) {
3428    haveTextClip = gTrue;
3429  }
3430}
3431
3432void PSOutputDev::endTextObject(GfxState *state) {
3433  if (haveTextClip) {
3434    writePS("Tclip\n");
3435    haveTextClip = gFalse;
3436  }
3437}
3438
3439void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
3440                int width, int height, GBool invert,
3441                GBool inlineImg) {
3442  int len;
3443
3444  len = height * ((width + 7) / 8);
3445  if (level == psLevel1 || level == psLevel1Sep) {
3446    doImageL1(ref, NULL, invert, inlineImg, str, width, height, len);
3447  } else {
3448    doImageL2(ref, NULL, invert, inlineImg, str, width, height, len,
3449          NULL, NULL, 0, 0, gFalse);
3450  }
3451}
3452
3453void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3454                int width, int height, GfxImageColorMap *colorMap,
3455                int *maskColors, GBool inlineImg) {
3456  int len;
3457
3458  len = height * ((width * colorMap->getNumPixelComps() *
3459           colorMap->getBits() + 7) / 8);
3460  switch (level) {
3461  case psLevel1:
3462    doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len);
3463    break;
3464  case psLevel1Sep:
3465    //~ handle indexed, separation, ... color spaces
3466    doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
3467    break;
3468  case psLevel2:
3469  case psLevel2Sep:
3470  case psLevel3:
3471  case psLevel3Sep:
3472    doImageL2(ref, colorMap, gFalse, inlineImg, str,
3473          width, height, len, maskColors, NULL, 0, 0, gFalse);
3474    break;
3475  }
3476  t3Cacheable = gFalse;
3477}
3478
3479void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
3480                  int width, int height,
3481                  GfxImageColorMap *colorMap,
3482                  Stream *maskStr,
3483                  int maskWidth, int maskHeight,
3484                  GBool maskInvert) {
3485  int len;
3486
3487  len = height * ((width * colorMap->getNumPixelComps() *
3488           colorMap->getBits() + 7) / 8);
3489  switch (level) {
3490  case psLevel1:
3491    doImageL1(ref, colorMap, gFalse, gFalse, str, width, height, len);
3492    break;
3493  case psLevel1Sep:
3494    //~ handle indexed, separation, ... color spaces
3495    doImageL1Sep(colorMap, gFalse, gFalse, str, width, height, len);
3496    break;
3497  case psLevel2:
3498  case psLevel2Sep:
3499  case psLevel3:
3500  case psLevel3Sep:
3501    doImageL2(ref, colorMap, gFalse, gFalse, str, width, height, len,
3502          NULL, maskStr, maskWidth, maskHeight, maskInvert);
3503    break;
3504  }
3505  t3Cacheable = gFalse;
3506}
3507
3508void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap,
3509                GBool invert, GBool inlineImg,
3510                Stream *str, int width, int height, int len) {
3511  ImageStream *imgStr;
3512  Guchar pixBuf[gfxColorMaxComps];
3513  GfxGray gray;
3514  int col, x, y, c, i;
3515
3516  if (inType3Char && !colorMap) {
3517    if (inlineImg) {
3518      // create an array
3519      str = new FixedLengthEncoder(str, len);
3520      str = new ASCIIHexEncoder(str);
3521      str->reset();
3522      col = 0;
3523      writePS("[<");
3524      do {
3525    do {
3526      c = str->getChar();
3527    } while (c == '\n' || c == '\r');
3528    if (c == '>' || c == EOF) {
3529      break;
3530    }
3531    writePSChar(c);
3532    ++col;
3533    // each line is: "<...data...><eol>"
3534    // so max data length = 255 - 4 = 251
3535    // but make it 240 just to be safe
3536    // chunks are 2 bytes each, so we need to stop on an even col number
3537    if (col == 240) {
3538      writePS(">\n<");
3539      col = 0;
3540    }
3541      } while (c != '>' && c != EOF);
3542      writePS(">]\n");
3543      writePS("0\n");
3544      str->close();
3545      delete str;
3546    } else {
3547      // set up to use the array already created by setupImages()
3548      writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
3549    }
3550  }
3551
3552  // image/imagemask command
3553  if (inType3Char && !colorMap) {
3554    writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1a\n",
3555           width, height, invert ? "true" : "false",
3556           width, -height, height);
3557  } else if (colorMap) {
3558    writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
3559           width, height,
3560           width, -height, height);
3561  } else {
3562    writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
3563           width, height, invert ? "true" : "false",
3564           width, -height, height);
3565  }
3566
3567  // image data
3568  if (!(inType3Char && !colorMap)) {
3569
3570    if (colorMap) {
3571
3572      // set up to process the data stream
3573      imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
3574                   colorMap->getBits());
3575      imgStr->reset();
3576
3577      // process the data stream
3578      i = 0;
3579      for (y = 0; y < height; ++y) {
3580
3581    // write the line
3582    for (x = 0; x < width; ++x) {
3583      imgStr->getPixel(pixBuf);
3584      colorMap->getGray(pixBuf, &gray);
3585      writePSFmt("%02x", colToByte(gray));
3586      if (++i == 32) {
3587        writePSChar('\n');
3588        i = 0;
3589      }
3590    }
3591      }
3592      if (i != 0) {
3593    writePSChar('\n');
3594      }
3595      delete imgStr;
3596
3597    // imagemask
3598    } else {
3599      str->reset();
3600      i = 0;
3601      for (y = 0; y < height; ++y) {
3602    for (x = 0; x < width; x += 8) {
3603      writePSFmt("%02x", str->getChar() & 0xff);
3604      if (++i == 32) {
3605        writePSChar('\n');
3606        i = 0;
3607      }
3608    }
3609      }
3610      if (i != 0) {
3611    writePSChar('\n');
3612      }
3613      str->close();
3614    }
3615  }
3616}
3617
3618void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
3619                   GBool invert, GBool inlineImg,
3620                   Stream *str, int width, int height, int len) {
3621  ImageStream *imgStr;
3622  Guchar *lineBuf;
3623  Guchar pixBuf[gfxColorMaxComps];
3624  GfxCMYK cmyk;
3625  int x, y, i, comp;
3626
3627  // width, height, matrix, bits per component
3628  writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n",
3629         width, height,
3630         width, -height, height);
3631
3632  // allocate a line buffer
3633  lineBuf = (Guchar *)gmalloc(4 * width);
3634
3635  // set up to process the data stream
3636  imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
3637               colorMap->getBits());
3638  imgStr->reset();
3639
3640  // process the data stream
3641  i = 0;
3642  for (y = 0; y < height; ++y) {
3643
3644    // read the line
3645    for (x = 0; x < width; ++x) {
3646      imgStr->getPixel(pixBuf);
3647      colorMap->getCMYK(pixBuf, &cmyk);
3648      lineBuf[4*x+0] = colToByte(cmyk.c);
3649      lineBuf[4*x+1] = colToByte(cmyk.m);
3650      lineBuf[4*x+2] = colToByte(cmyk.y);
3651      lineBuf[4*x+3] = colToByte(cmyk.k);
3652      addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
3653              colToDbl(cmyk.y), colToDbl(cmyk.k));
3654    }
3655
3656    // write one line of each color component
3657    for (comp = 0; comp < 4; ++comp) {
3658      for (x = 0; x < width; ++x) {
3659    writePSFmt("%02x", lineBuf[4*x + comp]);
3660    if (++i == 32) {
3661      writePSChar('\n');
3662      i = 0;
3663    }
3664      }
3665    }
3666  }
3667
3668  if (i != 0) {
3669    writePSChar('\n');
3670  }
3671
3672  delete imgStr;
3673  gfree(lineBuf);
3674}
3675
3676void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
3677                GBool invert, GBool inlineImg,
3678                Stream *str, int width, int height, int len,
3679                int *maskColors, Stream *maskStr,
3680                int maskWidth, int maskHeight, GBool maskInvert) {
3681  ImageStream *imgStr;
3682  Guchar *line, *pix;
3683  GooString *s;
3684  int n, numComps;
3685  GBool useRLE, useASCII, useASCIIHex, useCompressed;
3686  GfxSeparationColorSpace *sepCS;
3687  GfxColor color;
3688  GfxCMYK cmyk;
3689  int c;
3690  int col, i, x, x0, y, y0, maskXor;
3691
3692  // color key masking
3693  if (maskColors && colorMap && !inlineImg) {
3694    // can't read the stream twice for inline images -- but masking
3695    // isn't allowed with inline images anyway
3696    writePS("[\n");
3697    numComps = colorMap->getNumPixelComps();
3698    imgStr = new ImageStream(str, width, numComps, colorMap->getBits());
3699    imgStr->reset();
3700    for (y = 0, y0 = 0; y < height; ++y) {
3701      if (!(line = imgStr->getLine())) {
3702    break;
3703      }
3704      for (x = 0, x0 = 0, pix = line; x < width; ++x, pix += numComps) {
3705    for (i = 0; i < numComps; ++i) {
3706      if (pix[i] < maskColors[2*i] ||
3707          pix[i] > maskColors[2*i+1]) {
3708        break;
3709      }
3710    }
3711    if (i == numComps) {
3712      if (y0 < y) {
3713        writePSFmt("0 %d %d %d\n", height - y, width, y - y0);
3714      }
3715      if (x0 < x) {
3716        writePSFmt("%d %d %d 1\n", x0, height - y - 1, x - x0);
3717      }
3718      x0 = x + 1;
3719      y0 = y + 1;
3720    }
3721      }
3722      if (x0 > 0 && x0 < width) {
3723    writePSFmt("%d %d %d 1\n", x0, height - y - 1, width - x0);
3724      }
3725    }
3726    if (y0 < height) {
3727      writePSFmt("0 0 %d %d\n", width, height - y0);
3728    }
3729    delete imgStr;
3730    str->close();
3731    writePSFmt("] %d %d pdfImClip\n", width, height);
3732
3733  // explicit masking
3734  } else if (maskStr) {
3735    writePS("[\n");
3736    imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
3737    imgStr->reset();
3738    maskXor = maskInvert ? 1 : 0;
3739    for (y = 0, y0 = 0; y < maskHeight; ++y) {
3740      if (!(line = imgStr->getLine())) {
3741    break;
3742      }
3743      for (x = 0, x0 = 0, pix = line; x < maskWidth; ++x, ++pix) {
3744    if (*pix ^ maskXor) {
3745      if (y0 < y) {
3746        writePSFmt("0 %d %d %d\n", maskHeight - y, maskWidth, y - y0);
3747      }
3748      if (x0 < x) {
3749        writePSFmt("%d %d %d 1\n", x0, maskHeight - y - 1, x - x0);
3750      }
3751      x0 = x + 1;
3752      y0 = y + 1;
3753    }
3754      }
3755      if (x0 > 0 && x0 < maskWidth) {
3756    writePSFmt("%d %d %d 1\n", x0, maskHeight - y - 1, maskWidth - x0);
3757      }
3758    }
3759    if (y0 < maskHeight) {
3760      writePSFmt("0 0 %d %d\n", maskWidth, maskHeight - y0);
3761    }
3762    delete imgStr;
3763    maskStr->close();
3764    writePSFmt("] %d %d pdfImClip\n", maskWidth, maskHeight);
3765  }
3766
3767  // color space
3768  if (colorMap) {
3769    dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue);
3770    writePS(" setcolorspace\n");
3771  }
3772
3773  useASCIIHex = globalParams->getPSASCIIHex();
3774
3775  // set up the image data
3776  if (mode == psModeForm || inType3Char) {
3777    if (inlineImg) {
3778      // create an array
3779      str = new FixedLengthEncoder(str, len);
3780      if (useASCIIHex) {
3781    str = new ASCIIHexEncoder(str);
3782      } else {
3783    str = new ASCII85Encoder(str);
3784      }
3785      str->reset();
3786      col = 0;
3787      writePS((char *)(useASCIIHex ? "[<" : "[<~"));
3788      do {
3789    do {
3790      c = str->getChar();
3791    } while (c == '\n' || c == '\r');
3792    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3793      break;
3794    }
3795    if (c == 'z') {
3796      writePSChar(c);
3797      ++col;
3798    } else {
3799      writePSChar(c);
3800      ++col;
3801      for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
3802        do {
3803          c = str->getChar();
3804        } while (c == '\n' || c == '\r');
3805        if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
3806          break;
3807        }
3808        writePSChar(c);
3809        ++col;
3810      }
3811    }
3812    // each line is: "<~...data...~><eol>"
3813    // so max data length = 255 - 6 = 249
3814    // chunks are 1 or 5 bytes each, so we have to stop at 245
3815    // but make it 240 just to be safe
3816    if (col > 240) {
3817      writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
3818      col = 0;
3819    }
3820      } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
3821      writePS((char *)(useASCIIHex ? ">]\n" : "~>]\n"));
3822      writePS("0\n");
3823      str->close();
3824      delete str;
3825    } else {
3826      // set up to use the array already created by setupImages()
3827      writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
3828    }
3829  }
3830
3831  // image dictionary
3832  writePS("<<\n  /ImageType 1\n");
3833
3834  // width, height, matrix, bits per component
3835  writePSFmt("  /Width %d\n", width);
3836  writePSFmt("  /Height %d\n", height);
3837  writePSFmt("  /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
3838  if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
3839    writePSFmt("  /BitsPerComponent 8\n");
3840  } else {
3841    writePSFmt("  /BitsPerComponent %d\n",
3842           colorMap ? colorMap->getBits() : 1);
3843  }
3844
3845  // decode
3846  if (colorMap) {
3847    writePS("  /Decode [");
3848    if ((level == psLevel2Sep || level == psLevel3Sep) &&
3849    colorMap->getColorSpace()->getMode() == csSeparation) {
3850      // this matches up with the code in the pdfImSep operator
3851      n = (1 << colorMap->getBits()) - 1;
3852      writePSFmt("%g %g", colorMap->getDecodeLow(0) * n,
3853         colorMap->getDecodeHigh(0) * n);
3854    } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
3855      numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
3856               getAlt()->getNComps();
3857      for (i = 0; i < numComps; ++i) {
3858    if (i > 0) {
3859      writePS(" ");
3860    }
3861    writePS("0 1");
3862      }
3863    } else {
3864      numComps = colorMap->getNumPixelComps();
3865      for (i = 0; i < numComps; ++i) {
3866    if (i > 0) {
3867      writePS(" ");
3868    }
3869    writePSFmt("%g %g", colorMap->getDecodeLow(i),
3870           colorMap->getDecodeHigh(i));
3871      }
3872    }
3873    writePS("]\n");
3874  } else {
3875    writePSFmt("  /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
3876  }
3877
3878  if (mode == psModeForm || inType3Char) {
3879
3880    // data source
3881    writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
3882
3883    // end of image dictionary
3884    writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask");
3885
3886    // get rid of the array and index
3887    writePS("pop pop\n");
3888
3889  } else {
3890
3891    // data source
3892    writePS("  /DataSource currentfile\n");
3893    s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
3894             "    ");
3895    if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
3896    inlineImg || !s) {
3897      useRLE = gTrue;
3898      useASCII = gTrue;
3899      useCompressed = gFalse;
3900    } else {
3901      useRLE = gFalse;
3902      useASCII = str->isBinary();
3903      useCompressed = gTrue;
3904    }
3905    if (useASCII) {
3906      writePSFmt("    /ASCII%sDecode filter\n",
3907         useASCIIHex ? "Hex" : "85");
3908    }
3909    if (useRLE) {
3910      writePS("    /RunLengthDecode filter\n");
3911    }
3912    if (useCompressed) {
3913      writePS(s->getCString());
3914    }
3915    if (s) {
3916      delete s;
3917    }
3918
3919    // cut off inline image streams at appropriate length
3920    if (inlineImg) {
3921      str = new FixedLengthEncoder(str, len);
3922    } else if (useCompressed) {
3923      str = str->getBaseStream();
3924    }
3925
3926    // recode DeviceN data
3927    if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
3928      str = new DeviceNRecoder(str, width, height, colorMap);
3929    }
3930
3931    // add RunLengthEncode and ASCIIHex/85 encode filters
3932    if (useRLE) {
3933      str = new RunLengthEncoder(str);
3934    }
3935    if (useASCII) {
3936      if (useASCIIHex) {
3937    str = new ASCIIHexEncoder(str);
3938      } else {
3939    str = new ASCII85Encoder(str);
3940      }
3941    }
3942
3943    // end of image dictionary
3944    writePS(">>\n");
3945#if OPI_SUPPORT
3946    if (opi13Nest) {
3947      if (inlineImg) {
3948    // this can't happen -- OPI dictionaries are in XObjects
3949    error(-1, "Internal: OPI in inline image");
3950    n = 0;
3951      } else {
3952    // need to read the stream to count characters -- the length
3953    // is data-dependent (because of ASCII and RLE filters)
3954    str->reset();
3955    n = 0;
3956    while ((c = str->getChar()) != EOF) {
3957      ++n;
3958    }
3959    str->close();
3960      }
3961      // +6/7 for "pdfIm\n" / "pdfImM\n"
3962      // +8 for newline + trailer
3963      n += colorMap ? 14 : 15;
3964      writePSFmt("%%%%BeginData: %d Hex Bytes\n", n);
3965    }
3966#endif
3967    if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
3968    colorMap->getColorSpace()->getMode() == csSeparation) {
3969      color.c[0] = gfxColorComp1;
3970      sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
3971      sepCS->getCMYK(&color, &cmyk);
3972      writePSFmt("%g %g %g %g (%s) pdfImSep\n",
3973         colToDbl(cmyk.c), colToDbl(cmyk.m),
3974         colToDbl(cmyk.y), colToDbl(cmyk.k),
3975         sepCS->getName()->getCString());
3976    } else {
3977      writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM");
3978    }
3979
3980    // copy the stream data
3981    str->reset();
3982    while ((c = str->getChar()) != EOF) {
3983      writePSChar(c);
3984    }
3985    str->close();
3986
3987    // add newline and trailer to the end
3988    writePSChar('\n');
3989    writePS("%-EOD-\n");
3990#if OPI_SUPPORT
3991    if (opi13Nest) {
3992      writePS("%%EndData\n");
3993    }
3994#endif
3995
3996    // delete encoders
3997    if (useRLE || useASCII || inlineImg) {
3998      delete str;
3999    }
4000  }
4001
4002  if ((maskColors && colorMap && !inlineImg) || maskStr) {
4003    writePS("pdfImClipEnd\n");
4004  }
4005}
4006
4007void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace,
4008                   GBool genXform, GBool updateColors) {
4009  GfxCalGrayColorSpace *calGrayCS;
4010  GfxCalRGBColorSpace *calRGBCS;
4011  GfxLabColorSpace *labCS;
4012  GfxIndexedColorSpace *indexedCS;
4013  GfxSeparationColorSpace *separationCS;
4014  GfxDeviceNColorSpace *deviceNCS;
4015  GfxColorSpace *baseCS;
4016  Guchar *lookup, *p;
4017  double x[gfxColorMaxComps], y[gfxColorMaxComps];
4018  GfxColor color;
4019  GfxCMYK cmyk;
4020  Function *func;
4021  int n, numComps, numAltComps;
4022  int byte;
4023  int i, j, k;
4024
4025  switch (colorSpace->getMode()) {
4026
4027  case csDeviceGray:
4028    writePS("/DeviceGray");
4029    if (genXform) {
4030      writePS(" {}");
4031    }
4032    if (updateColors) {
4033    processColors |= psProcessBlack;
4034    }
4035    break;
4036
4037  case csCalGray:
4038    calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
4039    writePS("[/CIEBasedA <<\n");
4040    writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
4041    writePSFmt(" /MatrixA [%g %g %g]\n",
4042           calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
4043           calGrayCS->getWhiteZ());
4044    writePSFmt(" /WhitePoint [%g %g %g]\n",
4045           calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
4046           calGrayCS->getWhiteZ());
4047    writePSFmt(" /BlackPoint [%g %g %g]\n",
4048           calGrayCS->getBlackX(), calGrayCS->getBlackY(),
4049           calGrayCS->getBlackZ());
4050    writePS(">>]");
4051    if (genXform) {
4052      writePS(" {}");
4053    }
4054    if (updateColors) {
4055    processColors |= psProcessBlack;
4056    }
4057    break;
4058
4059  case csDeviceRGB:
4060    writePS("/DeviceRGB");
4061    if (genXform) {
4062      writePS(" {}");
4063    }
4064    if (updateColors) {
4065    processColors |= psProcessCMYK;
4066    }
4067    break;
4068
4069  case csCalRGB:
4070    calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
4071    writePS("[/CIEBasedABC <<\n");
4072    writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
4073           calRGBCS->getGammaR(), calRGBCS->getGammaG(),
4074           calRGBCS->getGammaB());
4075    writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n",
4076           calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
4077           calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
4078           calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
4079           calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
4080           calRGBCS->getMatrix()[8]);
4081    writePSFmt(" /WhitePoint [%g %g %g]\n",
4082           calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
4083           calRGBCS->getWhiteZ());
4084    writePSFmt(" /BlackPoint [%g %g %g]\n",
4085           calRGBCS->getBlackX(), calRGBCS->getBlackY(),
4086           calRGBCS->getBlackZ());
4087    writePS(">>]");
4088    if (genXform) {
4089      writePS(" {}");
4090    }
4091    if (updateColors) {
4092    processColors |= psProcessCMYK;
4093    }
4094    break;
4095
4096  case csDeviceCMYK:
4097    writePS("/DeviceCMYK");
4098    if (genXform) {
4099      writePS(" {}");
4100    }
4101    if (updateColors) {
4102    processColors |= psProcessCMYK;
4103    }
4104    break;
4105
4106  case csLab:
4107    labCS = (GfxLabColorSpace *)colorSpace;
4108    writePS("[/CIEBasedABC <<\n");
4109    writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n",
4110           labCS->getAMin(), labCS->getAMax(),
4111           labCS->getBMin(), labCS->getBMax());
4112    writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
4113    writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
4114    writePS(" /DecodeLMN\n");
4115    writePS("   [{dup 6 29 div ge {dup dup mul mul}\n");
4116    writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
4117           labCS->getWhiteX());
4118    writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
4119    writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
4120           labCS->getWhiteY());
4121    writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
4122    writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
4123           labCS->getWhiteZ());
4124    writePSFmt(" /WhitePoint [%g %g %g]\n",
4125           labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
4126    writePSFmt(" /BlackPoint [%g %g %g]\n",
4127           labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
4128    writePS(">>]");
4129    if (genXform) {
4130      writePS(" {}");
4131    }
4132    if (updateColors) {
4133    processColors |= psProcessCMYK;
4134    }
4135    break;
4136
4137  case csICCBased:
4138    // there is no transform function to the alternate color space, so
4139    // we can use it directly
4140    dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt(),
4141             genXform, updateColors);
4142    break;
4143
4144  case csIndexed:
4145    indexedCS = (GfxIndexedColorSpace *)colorSpace;
4146    baseCS = indexedCS->getBase();
4147    writePS("[/Indexed ");
4148    dumpColorSpaceL2(baseCS, gFalse, gFalse);
4149    n = indexedCS->getIndexHigh();
4150    numComps = baseCS->getNComps();
4151    lookup = indexedCS->getLookup();
4152    writePSFmt(" %d <\n", n);
4153    if (baseCS->getMode() == csDeviceN) {
4154      func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
4155      numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
4156      p = lookup;
4157      for (i = 0; i <= n; i += 8) {
4158    writePS("  ");
4159    for (j = i; j < i+8 && j <= n; ++j) {
4160      for (k = 0; k < numComps; ++k) {
4161        x[k] = *p++ / 255.0;
4162      }
4163      func->transform(x, y);
4164      for (k = 0; k < numAltComps; ++k) {
4165        byte = (int)(y[k] * 255 + 0.5);
4166        if (byte < 0) {
4167          byte = 0;
4168        } else if (byte > 255) {
4169          byte = 255;
4170        }
4171        writePSFmt("%02x", byte);
4172      }
4173      if (updateColors) {
4174        color.c[0] = dblToCol(j);
4175      indexedCS->getCMYK(&color, &cmyk);
4176        addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
4177                colToDbl(cmyk.y), colToDbl(cmyk.k));
4178      }
4179    }
4180    writePS("\n");
4181      }
4182    } else {
4183      for (i = 0; i <= n; i += 8) {
4184    writePS("  ");
4185    for (j = i; j < i+8 && j <= n; ++j) {
4186      for (k = 0; k < numComps; ++k) {
4187        writePSFmt("%02x", lookup[j * numComps + k]);
4188      }
4189      if (updateColors) {
4190        color.c[0] = dblToCol(j);
4191      indexedCS->getCMYK(&color, &cmyk);
4192        addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
4193                colToDbl(cmyk.y), colToDbl(cmyk.k));
4194      }
4195    }
4196    writePS("\n");
4197      }
4198    }
4199    writePS(">]");
4200    if (genXform) {
4201      writePS(" {}");
4202    }
4203    break;
4204
4205  case csSeparation:
4206    separationCS = (GfxSeparationColorSpace *)colorSpace;
4207    writePS("[/Separation /");
4208    writePSName(separationCS->getName()->getCString());
4209      writePS("  ");
4210    dumpColorSpaceL2(separationCS->getAlt(), gFalse, gFalse);
4211      writePS("\n");
4212    cvtFunction(separationCS->getFunc());
4213    writePS("]");
4214    if (genXform) {
4215      writePS(" {}");
4216    }
4217    if (updateColors) {
4218    addCustomColor(separationCS);
4219    }
4220    break;
4221
4222  case csDeviceN:
4223    // DeviceN color spaces are a Level 3 PostScript feature.
4224    deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
4225    dumpColorSpaceL2(deviceNCS->getAlt(), gFalse, updateColors);
4226    if (genXform) {
4227      writePS(" ");
4228      cvtFunction(deviceNCS->getTintTransformFunc());
4229    }
4230    break;
4231
4232  case csPattern:
4233    //~ unimplemented
4234    break;
4235  }
4236}
4237
4238#if OPI_SUPPORT
4239void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
4240  Object dict;
4241
4242  if (globalParams->getPSOPI()) {
4243    opiDict->lookup("2.0", &dict);
4244    if (dict.isDict()) {
4245      opiBegin20(state, dict.getDict());
4246      dict.free();
4247    } else {
4248      dict.free();
4249      opiDict->lookup("1.3", &dict);
4250      if (dict.isDict()) {
4251    opiBegin13(state, dict.getDict());
4252      }
4253      dict.free();
4254    }
4255  }
4256}
4257
4258void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
4259  Object obj1, obj2, obj3, obj4;
4260  double width, height, left, right, top, bottom;
4261  int w, h;
4262  int i;
4263
4264  writePS("%%BeginOPI: 2.0\n");
4265  writePS("%%Distilled\n");
4266
4267  dict->lookup("F", &obj1);
4268  if (getFileSpec(&obj1, &obj2)) {
4269    writePSFmt("%%%%ImageFileName: %s\n",
4270           obj2.getString()->getCString());
4271    obj2.free();
4272  }
4273  obj1.free();
4274
4275  dict->lookup("MainImage", &obj1);
4276  if (obj1.isString()) {
4277    writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString());
4278  }
4279  obj1.free();
4280
4281  //~ ignoring 'Tags' entry
4282  //~ need to use writePSString() and deal with >255-char lines
4283
4284  dict->lookup("Size", &obj1);
4285  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4286    obj1.arrayGet(0, &obj2);
4287    width = obj2.getNum();
4288    obj2.free();
4289    obj1.arrayGet(1, &obj2);
4290    height = obj2.getNum();
4291    obj2.free();
4292    writePSFmt("%%%%ImageDimensions: %g %g\n", width, height);
4293  }
4294  obj1.free();
4295
4296  dict->lookup("CropRect", &obj1);
4297  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
4298    obj1.arrayGet(0, &obj2);
4299    left = obj2.getNum();
4300    obj2.free();
4301    obj1.arrayGet(1, &obj2);
4302    top = obj2.getNum();
4303    obj2.free();
4304    obj1.arrayGet(2, &obj2);
4305    right = obj2.getNum();
4306    obj2.free();
4307    obj1.arrayGet(3, &obj2);
4308    bottom = obj2.getNum();
4309    obj2.free();
4310    writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
4311  }
4312  obj1.free();
4313
4314  dict->lookup("Overprint", &obj1);
4315  if (obj1.isBool()) {
4316    writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
4317  }
4318  obj1.free();
4319
4320  dict->lookup("Inks", &obj1);
4321  if (obj1.isName()) {
4322    writePSFmt("%%%%ImageInks: %s\n", obj1.getName());
4323  } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
4324    obj1.arrayGet(0, &obj2);
4325    if (obj2.isName()) {
4326      writePSFmt("%%%%ImageInks: %s %d",
4327         obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
4328      for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
4329    obj1.arrayGet(i, &obj3);
4330    obj1.arrayGet(i+1, &obj4);
4331    if (obj3.isString() && obj4.isNum()) {
4332      writePS(" ");
4333      writePSString(obj3.getString());
4334      writePSFmt(" %g", obj4.getNum());
4335    }
4336    obj3.free();
4337    obj4.free();
4338      }
4339      writePS("\n");
4340    }
4341    obj2.free();
4342  }
4343  obj1.free();
4344
4345  writePS("gsave\n");
4346
4347  writePS("%%BeginIncludedImage\n");
4348
4349  dict->lookup("IncludedImageDimensions", &obj1);
4350  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4351    obj1.arrayGet(0, &obj2);
4352    w = obj2.getInt();
4353    obj2.free();
4354    obj1.arrayGet(1, &obj2);
4355    h = obj2.getInt();
4356    obj2.free();
4357    writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h);
4358  }
4359  obj1.free();
4360
4361  dict->lookup("IncludedImageQuality", &obj1);
4362  if (obj1.isNum()) {
4363    writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum());
4364  }
4365  obj1.free();
4366
4367  ++opi20Nest;
4368}
4369
4370void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
4371  Object obj1, obj2;
4372  int left, right, top, bottom, samples, bits, width, height;
4373  double c, m, y, k;
4374  double llx, lly, ulx, uly, urx, ury, lrx, lry;
4375  double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
4376  double horiz, vert;
4377  int i, j;
4378
4379  writePS("save\n");
4380  writePS("/opiMatrix2 matrix currentmatrix def\n");
4381  writePS("opiMatrix setmatrix\n");
4382
4383  dict->lookup("F", &obj1);
4384  if (getFileSpec(&obj1, &obj2)) {
4385    writePSFmt("%%ALDImageFileName: %s\n",
4386           obj2.getString()->getCString());
4387    obj2.free();
4388  }
4389  obj1.free();
4390
4391  dict->lookup("CropRect", &obj1);
4392  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
4393    obj1.arrayGet(0, &obj2);
4394    left = obj2.getInt();
4395    obj2.free();
4396    obj1.arrayGet(1, &obj2);
4397    top = obj2.getInt();
4398    obj2.free();
4399    obj1.arrayGet(2, &obj2);
4400    right = obj2.getInt();
4401    obj2.free();
4402    obj1.arrayGet(3, &obj2);
4403    bottom = obj2.getInt();
4404    obj2.free();
4405    writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
4406  }
4407  obj1.free();
4408
4409  dict->lookup("Color", &obj1);
4410  if (obj1.isArray() && obj1.arrayGetLength() == 5) {
4411    obj1.arrayGet(0, &obj2);
4412    c = obj2.getNum();
4413    obj2.free();
4414    obj1.arrayGet(1, &obj2);
4415    m = obj2.getNum();
4416    obj2.free();
4417    obj1.arrayGet(2, &obj2);
4418    y = obj2.getNum();
4419    obj2.free();
4420    obj1.arrayGet(3, &obj2);
4421    k = obj2.getNum();
4422    obj2.free();
4423    obj1.arrayGet(4, &obj2);
4424    if (obj2.isString()) {
4425      writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
4426      writePSString(obj2.getString());
4427      writePS("\n");
4428    }
4429    obj2.free();
4430  }
4431  obj1.free();
4432
4433  dict->lookup("ColorType", &obj1);
4434  if (obj1.isName()) {
4435    writePSFmt("%%ALDImageColorType: %s\n", obj1.getName());
4436  }
4437  obj1.free();
4438
4439  //~ ignores 'Comments' entry
4440  //~ need to handle multiple lines
4441
4442  dict->lookup("CropFixed", &obj1);
4443  if (obj1.isArray()) {
4444    obj1.arrayGet(0, &obj2);
4445    ulx = obj2.getNum();
4446    obj2.free();
4447    obj1.arrayGet(1, &obj2);
4448    uly = obj2.getNum();
4449    obj2.free();
4450    obj1.arrayGet(2, &obj2);
4451    lrx = obj2.getNum();
4452    obj2.free();
4453    obj1.arrayGet(3, &obj2);
4454    lry = obj2.getNum();
4455    obj2.free();
4456    writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
4457  }
4458  obj1.free();
4459
4460  dict->lookup("GrayMap", &obj1);
4461  if (obj1.isArray()) {
4462    writePS("%ALDImageGrayMap:");
4463    for (i = 0; i < obj1.arrayGetLength(); i += 16) {
4464      if (i > 0) {
4465    writePS("\n%%+");
4466      }
4467      for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
4468    obj1.arrayGet(i+j, &obj2);
4469    writePSFmt(" %d", obj2.getInt());
4470    obj2.free();
4471      }
4472    }
4473    writePS("\n");
4474  }
4475  obj1.free();
4476
4477  dict->lookup("ID", &obj1);
4478  if (obj1.isString()) {
4479    writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString());
4480  }
4481  obj1.free();
4482
4483  dict->lookup("ImageType", &obj1);
4484  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4485    obj1.arrayGet(0, &obj2);
4486    samples = obj2.getInt();
4487    obj2.free();
4488    obj1.arrayGet(1, &obj2);
4489    bits = obj2.getInt();
4490    obj2.free();
4491    writePSFmt("%%ALDImageType: %d %d\n", samples, bits);
4492  }
4493  obj1.free();
4494
4495  dict->lookup("Overprint", &obj1);
4496  if (obj1.isBool()) {
4497    writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
4498  }
4499  obj1.free();
4500
4501  dict->lookup("Position", &obj1);
4502  if (obj1.isArray() && obj1.arrayGetLength() == 8) {
4503    obj1.arrayGet(0, &obj2);
4504    llx = obj2.getNum();
4505    obj2.free();
4506    obj1.arrayGet(1, &obj2);
4507    lly = obj2.getNum();
4508    obj2.free();
4509    obj1.arrayGet(2, &obj2);
4510    ulx = obj2.getNum();
4511    obj2.free();
4512    obj1.arrayGet(3, &obj2);
4513    uly = obj2.getNum();
4514    obj2.free();
4515    obj1.arrayGet(4, &obj2);
4516    urx = obj2.getNum();
4517    obj2.free();
4518    obj1.arrayGet(5, &obj2);
4519    ury = obj2.getNum();
4520    obj2.free();
4521    obj1.arrayGet(6, &obj2);
4522    lrx = obj2.getNum();
4523    obj2.free();
4524    obj1.arrayGet(7, &obj2);
4525    lry = obj2.getNum();
4526    obj2.free();
4527    opiTransform(state, llx, lly, &tllx, &tlly);
4528    opiTransform(state, ulx, uly, &tulx, &tuly);
4529    opiTransform(state, urx, ury, &turx, &tury);
4530    opiTransform(state, lrx, lry, &tlrx, &tlry);
4531    writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
4532           tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
4533    obj2.free();
4534  }
4535  obj1.free();
4536
4537  dict->lookup("Resolution", &obj1);
4538  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4539    obj1.arrayGet(0, &obj2);
4540    horiz = obj2.getNum();
4541    obj2.free();
4542    obj1.arrayGet(1, &obj2);
4543    vert = obj2.getNum();
4544    obj2.free();
4545    writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert);
4546    obj2.free();
4547  }
4548  obj1.free();
4549
4550  dict->lookup("Size", &obj1);
4551  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
4552    obj1.arrayGet(0, &obj2);
4553    width = obj2.getInt();
4554    obj2.free();
4555    obj1.arrayGet(1, &obj2);
4556    height = obj2.getInt();
4557    obj2.free();
4558    writePSFmt("%%ALDImageDimensions: %d %d\n", width, height);
4559  }
4560  obj1.free();
4561
4562  //~ ignoring 'Tags' entry
4563  //~ need to use writePSString() and deal with >255-char lines
4564
4565  dict->lookup("Tint", &obj1);
4566  if (obj1.isNum()) {
4567    writePSFmt("%%ALDImageTint: %g\n", obj1.getNum());
4568  }
4569  obj1.free();
4570
4571  dict->lookup("Transparency", &obj1);
4572  if (obj1.isBool()) {
4573    writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
4574  }
4575  obj1.free();
4576
4577  writePS("%%BeginObject: image\n");
4578  writePS("opiMatrix2 setmatrix\n");
4579  ++opi13Nest;
4580}
4581
4582// Convert PDF user space coordinates to PostScript default user space
4583// coordinates.  This has to account for both the PDF CTM and the
4584// PSOutputDev page-fitting transform.
4585void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
4586                   double *x1, double *y1) {
4587  double t;
4588
4589  state->transform(x0, y0, x1, y1);
4590  *x1 += tx;
4591  *y1 += ty;
4592  if (rotate == 90) {
4593    t = *x1;
4594    *x1 = -*y1;
4595    *y1 = t;
4596  } else if (rotate == 180) {
4597    *x1 = -*x1;
4598    *y1 = -*y1;
4599  } else if (rotate == 270) {
4600    t = *x1;
4601    *x1 = *y1;
4602    *y1 = -t;
4603  }
4604  *x1 *= xScale;
4605  *y1 *= yScale;
4606}
4607
4608void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
4609  Object dict;
4610
4611  if (globalParams->getPSOPI()) {
4612    opiDict->lookup("2.0", &dict);
4613    if (dict.isDict()) {
4614      writePS("%%EndIncludedImage\n");
4615      writePS("%%EndOPI\n");
4616      writePS("grestore\n");
4617      --opi20Nest;
4618      dict.free();
4619    } else {
4620      dict.free();
4621      opiDict->lookup("1.3", &dict);
4622      if (dict.isDict()) {
4623    writePS("%%EndObject\n");
4624    writePS("restore\n");
4625    --opi13Nest;
4626      }
4627      dict.free();
4628    }
4629  }
4630}
4631
4632GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
4633  if (fileSpec->isString()) {
4634    fileSpec->copy(fileName);
4635    return gTrue;
4636  }
4637  if (fileSpec->isDict()) {
4638    fileSpec->dictLookup("DOS", fileName);
4639    if (fileName->isString()) {
4640      return gTrue;
4641    }
4642    fileName->free();
4643    fileSpec->dictLookup("Mac", fileName);
4644    if (fileName->isString()) {
4645      return gTrue;
4646    }
4647    fileName->free();
4648    fileSpec->dictLookup("Unix", fileName);
4649    if (fileName->isString()) {
4650      return gTrue;
4651    }
4652    fileName->free();
4653    fileSpec->dictLookup("F", fileName);
4654    if (fileName->isString()) {
4655      return gTrue;
4656    }
4657    fileName->free();
4658  }
4659  return gFalse;
4660}
4661#endif // OPI_SUPPORT
4662
4663void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
4664  writePSFmt("%g %g setcharwidth\n", wx, wy);
4665  writePS("q\n");
4666}
4667
4668void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
4669              double llx, double lly, double urx, double ury) {
4670  t3WX = wx;
4671  t3WY = wy;
4672  t3LLX = llx;
4673  t3LLY = lly;
4674  t3URX = urx;
4675  t3URY = ury;
4676  t3String = new GooString();
4677  writePS("q\n");
4678  t3Cacheable = gTrue;
4679}
4680
4681void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
4682  Stream *str;
4683  int c;
4684
4685  if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
4686    str = level1Stream;
4687  } else {
4688    str = psStream;
4689  }
4690  str->reset();
4691  while ((c = str->getChar()) != EOF) {
4692    writePSChar(c);
4693  }
4694  str->close();
4695}
4696
4697//~ can nextFunc be reset to 0 -- maybe at the start of each page?
4698//~   or maybe at the start of each color space / pattern?
4699void PSOutputDev::cvtFunction(Function *func) {
4700  SampledFunction *func0;
4701  ExponentialFunction *func2;
4702  StitchingFunction *func3;
4703  PostScriptFunction *func4;
4704  int thisFunc, m, n, nSamples, i, j, k;
4705
4706  switch (func->getType()) {
4707
4708  case -1:          // identity
4709    writePS("{}\n");
4710    break;
4711
4712  case 0:           // sampled
4713    func0 = (SampledFunction *)func;
4714    thisFunc = nextFunc++;
4715    m = func0->getInputSize();
4716    n = func0->getOutputSize();
4717    nSamples = n;
4718    for (i = 0; i < m; ++i) {
4719      nSamples *= func0->getSampleSize(i);
4720    }
4721    writePSFmt("/xpdfSamples%d [\n", thisFunc);
4722    for (i = 0; i < nSamples; ++i) {
4723      writePSFmt("%g\n", func0->getSamples()[i]);
4724    }
4725    writePS("] def\n");
4726    writePSFmt("{ %d array %d array %d 2 roll\n", 2*m, m, m+2);
4727    // [e01] [efrac] x0 x1 ... xm-1
4728    for (i = m-1; i >= 0; --i) {
4729      // [e01] [efrac] x0 x1 ... xi
4730      writePSFmt("%g sub %g mul %g add\n",
4731          func0->getDomainMin(i),
4732          (func0->getEncodeMax(i) - func0->getEncodeMin(i)) /
4733            (func0->getDomainMax(i) - func0->getDomainMin(i)),
4734          func0->getEncodeMin(i));
4735      // [e01] [efrac] x0 x1 ... xi-1 xi'
4736      writePSFmt("dup 0 lt { pop 0 } { dup %d gt { pop %d } if } ifelse\n",
4737         func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1);
4738      // [e01] [efrac] x0 x1 ... xi-1 xi'
4739      writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
4740      // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
4741      writePSFmt("%d index %d 3 2 roll put\n", i+3, i);
4742      // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
4743      writePSFmt("%d index %d 3 2 roll put\n", i+3, 2*i+1);
4744      // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
4745      writePSFmt("%d index %d 3 2 roll put\n", i+2, 2*i);
4746      // [e01] [efrac] x0 x1 ... xi-1
4747    }
4748    // [e01] [efrac]
4749    for (i = 0; i < n; ++i) {
4750      // [e01] [efrac] y(0) ... y(i-1)
4751      for (j = 0; j < (1<<m); ++j) {
4752    // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
4753    writePSFmt("xpdfSamples%d\n", thisFunc);
4754    k = m - 1;
4755    writePSFmt("%d index %d get\n", i+j+2, 2 * k + ((j >> k) & 1));
4756    for (k = m - 2; k >= 0; --k) {
4757      writePSFmt("%d mul %d index %d get add\n",
4758             func0->getSampleSize(k),
4759             i + j + 3,
4760             2 * k + ((j >> k) & 1));
4761    }
4762    if (n > 1) {
4763      writePSFmt("%d mul %d add ", n, i);
4764    }
4765    writePS("get\n");
4766      }
4767      // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
4768      for (j = 0; j < m; ++j) {
4769    // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
4770    for (k = 0; k < (1 << (m - j)); k += 2) {
4771      // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
4772      writePSFmt("%d index %d get dup\n", i + k/2 + (1 << (m-j)) - k, j);
4773      writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
4774      writePSFmt("%d 1 roll\n", k/2 + (1 << m-j) - k - 1);
4775    }
4776    // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
4777      }
4778      // [e01] [efrac] y(0) ... y(i-1) s
4779      writePSFmt("%g mul %g add\n",
4780         func0->getDecodeMax(i) - func0->getDecodeMin(i),
4781         func0->getDecodeMin(i));
4782      writePSFmt("dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4783         func0->getRangeMin(i), func0->getRangeMin(i),
4784         func0->getRangeMax(i), func0->getRangeMax(i));
4785      // [e01] [efrac] y(0) ... y(i-1) y(i)
4786    }
4787    // [e01] [efrac] y(0) ... y(n-1)
4788    writePSFmt("%d %d roll pop pop }\n", n+2, n);
4789    break;
4790
4791  case 2:           // exponential
4792    func2 = (ExponentialFunction *)func;
4793    n = func2->getOutputSize();
4794    writePSFmt("{ dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4795           func2->getDomainMin(0), func2->getDomainMin(0),
4796           func2->getDomainMax(0), func2->getDomainMax(0));
4797    // x
4798    for (i = 0; i < n; ++i) {
4799      // x y(0) .. y(i-1)
4800      writePSFmt("%d index %g exp %g mul %g add\n",
4801         i, func2->getE(), func2->getC1()[i] - func2->getC0()[i],
4802         func2->getC0()[i]);
4803      if (func2->getHasRange()) {
4804    writePSFmt("dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4805           func2->getRangeMin(i), func2->getRangeMin(i),
4806           func2->getRangeMax(i), func2->getRangeMax(i));
4807      }
4808    }
4809    // x y(0) .. y(n-1)
4810    writePSFmt("%d %d roll pop }\n", n+1, n);
4811    break;
4812
4813  case 3:           // stitching
4814    func3 = (StitchingFunction *)func;
4815    thisFunc = nextFunc++;
4816    for (i = 0; i < func3->getNumFuncs(); ++i) {
4817      cvtFunction(func3->getFunc(i));
4818      writePSFmt("/xpdfFunc%d_%d exch def\n", thisFunc, i);
4819    }
4820    writePSFmt("{ dup %g lt { pop %g } { dup %g gt { pop %g } if } ifelse\n",
4821           func3->getDomainMin(0), func3->getDomainMin(0),
4822           func3->getDomainMax(0), func3->getDomainMax(0));
4823    for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
4824      writePSFmt("dup %g lt { %g sub %g mul %g add xpdfFunc%d_%d } {\n",
4825         func3->getBounds()[i+1],
4826         func3->getBounds()[i],
4827         (func3->getEncode()[2*i+1] - func3->getEncode()[2*i]) /
4828           (func3->getBounds()[i+1] - func3->getBounds()[i]),
4829         func3->getEncode()[2*i],
4830         thisFunc, i);
4831    }
4832    writePSFmt("%g sub %g mul %g add xpdfFunc%d_%d\n",
4833           func3->getBounds()[i],
4834           (func3->getEncode()[2*i+1] - func3->getEncode()[2*i]) /
4835             (func3->getBounds()[i+1] - func3->getBounds()[i]),
4836           func3->getEncode()[2*i],
4837           thisFunc, i);
4838    for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
4839      writePS("} ifelse\n");
4840    }
4841    writePS("}\n");
4842    break;
4843
4844  case 4:           // PostScript
4845    func4 = (PostScriptFunction *)func;
4846    writePS(func4->getCodeString()->getCString());
4847    writePS("\n");
4848    break;
4849  }
4850}
4851
4852void PSOutputDev::writePSChar(char c) {
4853  if (t3String) {
4854    t3String->append(c);
4855  } else {
4856    (*outputFunc)(outputStream, &c, 1);
4857  }
4858}
4859
4860void PSOutputDev::writePS(char *s) {
4861  if (t3String) {
4862    t3String->append(s);
4863  } else {
4864    (*outputFunc)(outputStream, s, strlen(s));
4865  }
4866}
4867
4868void PSOutputDev::writePSFmt(const char *fmt, ...) {
4869  va_list args;
4870  char buf[512];
4871
4872  va_start(args, fmt);
4873  vsprintf(buf, fmt, args);
4874  va_end(args);
4875  if (t3String) {
4876    t3String->append(buf);
4877  } else {
4878    (*outputFunc)(outputStream, buf, strlen(buf));
4879  }
4880}
4881
4882void PSOutputDev::writePSString(GooString *s) {
4883  Guchar *p;
4884  int n, line;
4885  char buf[8];
4886
4887  writePSChar('(');
4888  line = 1;
4889  for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
4890    if (line >= 64) {
4891      writePSChar('\\');
4892      writePSChar('\n');
4893      line = 0;
4894    }
4895    if (*p == '(' || *p == ')' || *p == '\\') {
4896      writePSChar('\\');
4897      writePSChar((char)*p);
4898      line += 2;
4899    } else if (*p < 0x20 || *p >= 0x80) {
4900      sprintf(buf, "\\%03o", *p);
4901      writePS(buf);
4902      line += 4;
4903    } else {
4904      writePSChar((char)*p);
4905      ++line;
4906    }
4907  }
4908  writePSChar(')');
4909}
4910
4911void PSOutputDev::writePSName(char *s) {
4912  char *p;
4913  char c;
4914
4915  p = s;
4916  while ((c = *p++)) {
4917    if (c <= (char)0x20 || c >= (char)0x7f ||
4918    c == '(' || c == ')' || c == '<' || c == '>' ||
4919    c == '[' || c == ']' || c == '{' || c == '}' ||
4920    c == '/' || c == '%') {
4921      writePSFmt("#%02x", c & 0xff);
4922    } else {
4923      writePSChar(c);
4924    }
4925  }
4926}
4927
4928GooString *PSOutputDev::filterPSName(GooString *name) {
4929  GooString *name2;
4930  char buf[8];
4931  int i;
4932  char c;
4933
4934  name2 = new GooString();
4935
4936  // ghostscript chokes on names that begin with out-of-limits
4937  // numbers, e.g., 1e4foo is handled correctly (as a name), but
4938  // 1e999foo generates a limitcheck error
4939  c = name->getChar(0);
4940  if (c >= '0' && c <= '9') {
4941    name2->append('f');
4942  }
4943
4944  for (i = 0; i < name->getLength(); ++i) {
4945    c = name->getChar(i);
4946    if (c <= (char)0x20 || c >= (char)0x7f ||
4947    c == '(' || c == ')' || c == '<' || c == '>' ||
4948    c == '[' || c == ']' || c == '{' || c == '}' ||
4949    c == '/' || c == '%') {
4950      sprintf(buf, "#%02x", c & 0xff);
4951      name2->append(buf);
4952    } else {
4953      name2->append(c);
4954    }
4955  }
4956  return name2;
4957}
Note: See TracBrowser for help on using the repository browser.