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

Last change on this file since 461 was 461, checked in by Silvan Scherrer, 11 years ago

poppler update to 0.14.2

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