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

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

First import

File size: 131.7 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.