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

Last change on this file since 250 was 250, checked in by Eugene Romanenko, 13 years ago

PDF plugin: poppler library updated to version 0.8.3

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