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

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

PDF plugin: Poppler library updated to version 0.10.0

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