1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
9 //========================================================================
11 // Modified under the Poppler project - http://poppler.freedesktop.org
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
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, 2011-2013, 2015 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, 2010 Hib Eris <hib@hiberis.nl>
23 // Copyright (C) 2009-2013 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, 2011, 2012, 2014, 2015 William Bader <williambader@hotmail.com>
27 // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
28 // Copyright (C) 2009-2011, 2013, 2014 Adrian Johnson <ajohnson@redneon.com>
29 // Copyright (C) 2012, 2014 Fabio D'Urso <fabiodurso@hotmail.it>
30 // Copyright (C) 2012 Lu Wang <coolwanglu@gmail.com>
31 // Copyright (C) 2014 Till Kamppeter <till.kamppeter@gmail.com>
32 // Copyright (C) 2015 Marek Kasik <mkasik@redhat.com>
34 // To see a description of the changes please see the Changelog file that
35 // came with your tarball or type make ChangeLog if you are building from git
37 //========================================================================
41 #ifdef USE_GCC_PRAGMAS
42 #pragma implementation
51 #include "goo/GooString.h"
52 #include "goo/GooList.h"
53 #include "goo/GooHash.h"
54 #include "poppler-config.h"
55 #include "GlobalParams.h"
62 #include "UnicodeMap.h"
63 #include <fofi/FoFiType1C.h>
64 #include <fofi/FoFiTrueType.h>
70 #include "PreScanOutputDev.h"
72 #include "CharCodeToUnicode.h"
74 # include "splash/Splash.h"
75 # include "splash/SplashBitmap.h"
76 # include "SplashOutputDev.h"
78 #include "PSOutputDev.h"
82 // needed for setting type/creator of MacOS files
83 #include "ICSupport.h"
86 // the MSVC math.h doesn't define this
88 #define M_PI 3.14159265358979323846
91 //------------------------------------------------------------------------
93 // Max size of a slice when rasterizing pages, in pixels.
94 #define rasterizationSliceSize 20000000
96 //------------------------------------------------------------------------
97 // PostScript prolog and setup
98 //------------------------------------------------------------------------
100 // The '~' escapes mark prolog code that is emitted only in certain
104 // ^ ^----- s=psLevel*Sep, n=psLevel*
105 // +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3*
107 static const char *prolog
[] = {
108 "/xpdf 75 dict def xpdf begin",
109 "% PDF special state",
110 "/pdfDictSize 15 def",
112 "/pdfStates 64 array def",
114 " pdfStates exch pdfDictSize dict",
115 " dup /pdfStateIdx 3 index put",
120 " /setpagedevice where {",
122 " /Policies 1 dict dup begin /PageSize 6 def end def",
123 " { /Duplex true def } if",
124 " currentdict end setpagedevice",
130 " % Change paper size, but only if different from previous paper size otherwise",
131 " % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size",
132 " % so we use the same when checking if the size changes.",
133 " /setpagedevice where {",
134 " pop currentpagedevice",
135 " /PageSize known {",
137 " currentpagedevice /PageSize get aload pop",
149 " /PageSize exch def",
150 " /ImagingBBox null def",
162 " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
163 " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender /pdfPatternCS",
164 " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
169 " pdfStates 0 get begin",
171 " pdfDictSize dict begin",
173 " /pdfFillCS [] def",
174 " /pdfFillXform {} def",
175 " /pdfStrokeCS [] def",
176 " /pdfStrokeXform {} def",
181 " /pdfFill [0 0 0 1] def",
182 " /pdfStroke [0 0 0 1] def",
185 " /pdfStroke [0] def",
186 " /pdfFillOP false def",
187 " /pdfStrokeOP false def",
189 " /pdfOPM false def",
191 " /pdfLastFill false def",
192 " /pdfLastStroke false def",
193 " /pdfTextMat [1 0 0 1 0 0] def",
194 " /pdfFontSize 0 def",
195 " /pdfCharSpacing 0 def",
196 " /pdfTextRender 0 def",
197 " /pdfPatternCS false def",
198 " /pdfTextRise 0 def",
199 " /pdfWordSpacing 0 def",
200 " /pdfHorizScaling 1 def",
201 " /pdfTextClipPath [] def",
203 "/pdfEndPage { end } def",
205 "% separation convention operators",
206 "/findcmykcustomcolor where {",
209 " /findcmykcustomcolor { 5 array astore } def",
211 "/setcustomcolor where {",
214 " /setcustomcolor {",
216 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
217 " 0 4 getinterval cvx",
218 " [ exch /dup load exch { mul exch dup } /forall load",
219 " /pop load dup ] cvx",
220 " ] setcolorspace setcolor",
223 "/customcolorimage where {",
226 " /customcolorimage {",
228 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
230 " [ exch /dup load exch { mul exch dup } /forall load",
231 " /pop load dup ] cvx",
235 " /DataSource exch def",
236 " /ImageMatrix exch def",
237 " /BitsPerComponent exch def",
240 " /Decode [1 0] def",
249 "/g { dup /pdfFill exch def setgray",
250 " /pdfLastFill true def /pdfLastStroke false def } def",
251 "/G { dup /pdfStroke exch def setgray",
252 " /pdfLastStroke true def /pdfLastFill false def } def",
254 " pdfLastFill not {",
256 " /pdfLastFill true def /pdfLastStroke false def",
260 " pdfLastStroke not {",
261 " pdfStroke setgray",
262 " /pdfLastStroke true def /pdfLastFill false def",
266 "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
267 " /pdfLastFill true def /pdfLastStroke false def } def",
268 "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
269 " /pdfLastStroke true def /pdfLastFill false def } def",
271 " pdfLastFill not {",
272 " pdfFill aload pop setcmykcolor",
273 " /pdfLastFill true def /pdfLastStroke false def",
277 " pdfLastStroke not {",
278 " pdfStroke aload pop setcmykcolor",
279 " /pdfLastStroke true def /pdfLastFill false def",
283 "/opm { dup /pdfOPM exch def",
284 " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def",
286 "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
287 " setcolorspace } def",
288 "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
289 " setcolorspace } def",
290 "/sc { pdfLastFill not { pdfFillCS setcolorspace } if",
291 " dup /pdfFill exch def aload pop pdfFillXform setcolor",
292 " /pdfLastFill true def /pdfLastStroke false def } def",
293 "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if",
294 " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
295 " /pdfLastStroke true def /pdfLastFill false def } def",
296 "/op { /pdfFillOP exch def",
297 " pdfLastFill { pdfFillOP setoverprint } if } def",
298 "/OP { /pdfStrokeOP exch def",
299 " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
301 " pdfLastFill not {",
302 " pdfFillCS setcolorspace",
303 " pdfFill aload pop pdfFillXform setcolor",
304 " pdfFillOP setoverprint",
305 " /pdfLastFill true def /pdfLastStroke false def",
309 " pdfLastStroke not {",
310 " pdfStrokeCS setcolorspace",
311 " pdfStroke aload pop pdfStrokeXform setcolor",
312 " pdfStrokeOP setoverprint",
313 " /pdfLastStroke true def /pdfLastFill false def",
317 "/opm { dup /pdfOPM exch def",
318 " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def",
320 "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
321 " /pdfLastFill true def /pdfLastStroke false def } def",
322 "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
323 " /pdfLastStroke true def /pdfLastFill false def } def",
324 "/ck { 6 copy 6 array astore /pdfFill exch def",
325 " findcmykcustomcolor exch setcustomcolor",
326 " /pdfLastFill true def /pdfLastStroke false def } def",
327 "/CK { 6 copy 6 array astore /pdfStroke exch def",
328 " findcmykcustomcolor exch setcustomcolor",
329 " /pdfLastStroke true def /pdfLastFill false def } def",
330 "/op { /pdfFillOP exch def",
331 " pdfLastFill { pdfFillOP setoverprint } if } def",
332 "/OP { /pdfStrokeOP exch def",
333 " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
335 " pdfLastFill not {",
336 " pdfFill aload length 4 eq {",
339 " findcmykcustomcolor exch setcustomcolor",
341 " pdfFillOP setoverprint",
342 " /pdfLastFill true def /pdfLastStroke false def",
346 " pdfLastStroke not {",
347 " pdfStroke aload length 4 eq {",
350 " findcmykcustomcolor exch setcustomcolor",
352 " pdfStrokeOP setoverprint",
353 " /pdfLastStroke true def /pdfLastFill false def",
359 " 4 3 roll findfont",
360 " 4 2 roll matrix scale makefont",
361 " dup length dict begin",
362 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
363 " /Encoding exch def",
370 " dup length dict begin",
371 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
378 "/pdfMakeFont16L3 {",
379 " 1 index /CIDFont resourcestatus {",
380 " pop pop 1 index /CIDFont findresource /CIDFontType known",
385 " 0 eq { /Identity-H } { /Identity-V } ifelse",
386 " exch 1 array astore composefont pop",
392 "% graphics state operators",
396 " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
397 " pdfStates pdfStateIdx 1 add get begin",
398 " pdfOpNames { exch def } forall",
400 "/Q { end grestore } def",
402 "/q { gsave pdfDictSize dict begin } def",
405 " /pdfLastFill where {",
408 " pdfFillOP setoverprint",
410 " pdfStrokeOP setoverprint",
416 " pdfOPM /setoverprintmode where{pop setoverprintmode}{pop}ifelse ",
421 "/cm { concat } def",
422 "/d { setdash } def",
423 "/i { setflat } def",
424 "/j { setlinejoin } def",
425 "/J { setlinecap } def",
426 "/M { setmiterlimit } def",
427 "/w { setlinewidth } def",
428 "% path segment operators",
431 "/c { curveto } def",
432 "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
433 " neg 0 rlineto closepath } def",
434 "/h { closepath } def",
435 "% path painting operators",
436 "/S { sCol stroke } def",
437 "/Sf { fCol stroke } def",
438 "/f { fCol fill } def",
439 "/f* { fCol eofill } def",
440 "% clipping operators",
441 "/W { clip newpath } def",
442 "/W* { eoclip newpath } def",
443 "/Ws { strokepath clip newpath } def",
444 "% text state operators",
445 "/Tc { /pdfCharSpacing exch def } def",
446 "/Tf { dup /pdfFontSize exch def",
447 " dup pdfHorizScaling mul exch matrix scale",
448 " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
449 " exch findfont exch makefont setfont } def",
450 "/Tr { /pdfTextRender exch def } def",
451 "/Tp { /pdfPatternCS exch def } def",
452 "/Ts { /pdfTextRise exch def } def",
453 "/Tw { /pdfWordSpacing exch def } def",
454 "/Tz { /pdfHorizScaling exch def } def",
455 "% text positioning operators",
456 "/Td { pdfTextMat transform moveto } def",
457 "/Tm { /pdfTextMat exch def } def",
458 "% text string operators",
463 " 0 2 2 index length 1 sub {",
464 " 2 index 1 index 2 copy get 3 1 roll 1 add get",
465 " pdfTextMat dtransform",
466 " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put",
473 " currentfont /FontType get 0 eq {",
474 " 0 2 3 index length 1 sub {",
475 " currentpoint 4 index 3 index 2 getinterval show moveto",
476 " 2 copy get 2 index 3 2 roll 1 add get",
477 " pdfTextMat dtransform rmoveto",
480 " 0 1 3 index length 1 sub {",
481 " currentpoint 4 index 3 index 1 getinterval show moveto",
482 " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
483 " pdfTextMat dtransform rmoveto",
491 " /xycp {", // xycharpath
494 " pop pop currentpoint 3 2 roll",
495 " 1 string dup 0 4 3 roll put false charpath moveto",
496 " 2 copy get 2 index 2 index 1 add get",
497 " pdfTextMat dtransform rmoveto",
503 " /xycp {", // xycharpath
504 " currentfont /FontType get 0 eq {",
505 " 0 2 3 index length 1 sub {",
506 " currentpoint 4 index 3 index 2 getinterval false charpath moveto",
507 " 2 copy get 2 index 3 2 roll 1 add get",
508 " pdfTextMat dtransform rmoveto",
511 " 0 1 3 index length 1 sub {",
512 " currentpoint 4 index 3 index 1 getinterval false charpath moveto",
513 " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
514 " pdfTextMat dtransform rmoveto",
521 " fCol", // because stringwidth has to draw Type 3 chars
522 " 0 pdfTextRise pdfTextMat dtransform rmoveto",
523 " currentpoint 4 2 roll",
524 " pdfTextRender 1 and 0 eq {",
527 " pdfTextRender 3 and dup 1 eq exch 2 eq or {",
528 " 3 index 3 index moveto",
530 " currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
531 " xycp currentpoint stroke moveto",
533 " pdfTextRender 4 and 0 ne {",
534 " 4 2 roll moveto xycp",
535 " /pdfTextClipPath [ pdfTextClipPath aload pop",
541 " currentpoint newpath moveto",
545 " 0 pdfTextRise neg pdfTextMat dtransform rmoveto",
547 "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0",
548 " pdfTextMat dtransform rmoveto } def",
549 "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch",
550 " pdfTextMat dtransform rmoveto } def",
551 "/Tclip { pdfTextClipPath cvx exec clip newpath",
552 " /pdfTextClipPath [] def } def",
553 "/Tclip* { pdfTextClipPath cvx exec eoclip newpath",
554 " /pdfTextClipPath [] def } def",
556 "% Level 1 image operators",
558 " /pdfImBuf1 4 index string def",
559 " { currentfile pdfImBuf1 readhexstring pop } image",
562 " /pdfImBuf1 4 index string def",
563 " { currentfile pdfImBuf1 readstring pop } image",
567 " /pdfImBuf1 4 index string def",
568 " /pdfImBuf2 4 index string def",
569 " /pdfImBuf3 4 index string def",
570 " /pdfImBuf4 4 index string def",
571 " { currentfile pdfImBuf1 readhexstring pop }",
572 " { currentfile pdfImBuf2 readhexstring pop }",
573 " { currentfile pdfImBuf3 readhexstring pop }",
574 " { currentfile pdfImBuf4 readhexstring pop }",
575 " true 4 colorimage",
578 " /pdfImBuf1 4 index string def",
579 " /pdfImBuf2 4 index string def",
580 " /pdfImBuf3 4 index string def",
581 " /pdfImBuf4 4 index string def",
582 " { currentfile pdfImBuf1 readstring pop }",
583 " { currentfile pdfImBuf2 readstring pop }",
584 " { currentfile pdfImBuf3 readstring pop }",
585 " { currentfile pdfImBuf4 readstring pop }",
586 " true 4 colorimage",
590 " fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
591 " { currentfile pdfImBuf1 readhexstring pop } imagemask",
594 " fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
595 " { currentfile pdfImBuf1 readstring pop } imagemask",
598 " 2 copy exch length lt {",
599 " 2 copy get exch 1 add exch",
605 " { pdfImStr } imagemask",
609 "% Level 2/3 image operators",
610 "/pdfImBuf 100 string def",
612 " 2 copy exch length lt {",
613 " 2 copy get exch 1 add exch",
619 " { currentfile pdfImBuf readline",
620 " not { pop exit } if",
621 " (%-EOD-) eq { exit } if } loop",
623 "/pdfIm { image skipEOD } def",
626 " /ReusableStreamDecode filter",
628 " /maskStream exch def",
630 "/pdfMaskEnd { maskStream closefile } def",
632 " /maskArray exch def",
636 " maskIdx maskArray length lt {",
637 " maskArray maskIdx get",
638 " /maskIdx maskIdx 1 add def",
645 " findcmykcustomcolor exch",
646 " dup /Width get /pdfImBuf1 exch string def",
647 " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
648 " /pdfImDecodeLow exch def",
649 " begin Width Height BitsPerComponent ImageMatrix DataSource end",
650 " /pdfImData exch def",
651 " { pdfImData pdfImBuf1 readstring pop",
652 " 0 1 2 index length 1 sub {",
653 " 1 index exch 2 copy get",
654 " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
657 " 6 5 roll customcolorimage",
661 "/pdfImM { fCol imagemask skipEOD } def",
663 "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def",
666 " 0 2 4 index length 1 sub {",
667 " dup 4 index exch 2 copy",
668 " get 5 index div put",
669 " 1 add 3 index exch 2 copy",
670 " get 3 index div put",
674 "/pdfImClipEnd { grestore } def",
676 "% shading operators",
678 " false 0 1 3 index length 1 sub {",
679 " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
683 " exch pop exch pop",
685 "/funcCol { func n array astore } def",
693 " 4 index 4 index funcCol dup",
694 " 6 index 4 index funcCol dup",
695 " 3 1 roll colordelta 3 1 roll",
696 " 5 index 5 index funcCol dup",
697 " 3 1 roll colordelta 3 1 roll",
698 " 6 index 8 index funcCol dup",
699 " 3 1 roll colordelta 3 1 roll",
700 " colordelta or or or",
705 " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
706 " 6 index 6 index 4 index 4 index 4 index funcSH",
707 " 2 index 6 index 6 index 4 index 4 index funcSH",
708 " 6 index 2 index 4 index 6 index 4 index funcSH",
709 " 5 3 roll 3 2 roll funcSH pop pop",
711 " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul",
715 " funcCol aload pop k",
717 " dup 4 index exch mat transform m",
718 " 3 index 3 index mat transform l",
719 " 1 index 3 index mat transform l",
720 " mat transform l pop pop h f*",
733 " func n array astore",
742 " 2 index axialCol 2 index axialCol colordelta",
746 " 1 add 3 1 roll 2 copy add 0.5 mul",
747 " dup 4 3 roll exch 4 index axialSH",
748 " exch 3 2 roll axialSH",
750 " pop 2 copy add 0.5 mul",
754 " axialCol aload pop k",
756 " exch dup dx mul x0 add exch dy mul y0 add",
757 " 3 2 roll dup dx mul x0 add exch dy mul y0 add",
758 " dx abs dy abs ge {",
759 " 2 copy yMin sub dy mul dx div add yMin m",
760 " yMax sub dy mul dx div add yMax l",
761 " 2 copy yMax sub dy mul dx div add yMax l",
762 " yMin sub dy mul dx div add yMin l",
765 " exch 2 copy xMin sub dx mul dy div add xMin exch m",
766 " xMax sub dx mul dy div add xMax exch l",
767 " exch 2 copy xMax sub dx mul dy div add xMax exch l",
768 " xMin sub dx mul dy div add xMin exch l",
781 " func n array astore",
790 " 2 index dt mul t0 add radialCol",
791 " 2 index dt mul t0 add radialCol colordelta",
795 " 1 add 3 1 roll 2 copy add 0.5 mul",
796 " dup 4 3 roll exch 4 index radialSH",
797 " exch 3 2 roll radialSH",
799 " pop 2 copy add 0.5 mul dt mul t0 add",
803 " radialCol aload pop k",
806 " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
808 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
812 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
814 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
816 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
818 " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
828 static const char *cmapProlog
[] = {
829 "/CIDInit /ProcSet findresource begin",
833 " /CMapName /Identity-H def",
834 " /CIDSystemInfo 3 dict dup begin",
835 " /Registry (Adobe) def",
836 " /Ordering (Identity) def",
837 " /Supplement 0 def",
839 " 1 begincodespacerange",
841 " endcodespacerange",
847 " currentdict CMapName exch /CMap defineresource pop",
852 " /CMapName /Identity-V def",
853 " /CIDSystemInfo 3 dict dup begin",
854 " /Registry (Adobe) def",
855 " /Ordering (Identity) def",
856 " /Supplement 0 def",
859 " 1 begincodespacerange",
861 " endcodespacerange",
867 " currentdict CMapName exch /CMap defineresource pop",
873 //------------------------------------------------------------------------
875 //------------------------------------------------------------------------
878 const char *psName
; // PostScript name
879 double mWidth
; // width of 'm' character
882 // NB: must be in same order as base14SubstFonts in GfxFont.cc
883 static PSSubstFont psBase14SubstFonts
[14] = {
885 {"Courier-Oblique", 0.600},
886 {"Courier-Bold", 0.600},
887 {"Courier-BoldOblique", 0.600},
888 {"Helvetica", 0.833},
889 {"Helvetica-Oblique", 0.833},
890 {"Helvetica-Bold", 0.889},
891 {"Helvetica-BoldOblique", 0.889},
892 {"Times-Roman", 0.788},
893 {"Times-Italic", 0.722},
894 {"Times-Bold", 0.833},
895 {"Times-BoldItalic", 0.778},
896 // the last two are never used for substitution
901 // Mapping from Type 1/1C font file to PS font name.
902 struct PST1FontName
{
904 GooString
*psName
; // PostScript font name used for this
905 // embedded font file
908 // Info for 8-bit fonts
911 int *codeToGID
; // code-to-GID mapping for TrueType fonts
914 // Encoding info for substitute 16-bit font
920 //------------------------------------------------------------------------
922 //------------------------------------------------------------------------
924 #define psProcessCyan 1
925 #define psProcessMagenta 2
926 #define psProcessYellow 4
927 #define psProcessBlack 8
928 #define psProcessCMYK 15
930 //------------------------------------------------------------------------
932 //------------------------------------------------------------------------
934 class PSOutCustomColor
{
937 PSOutCustomColor(double cA
, double mA
,
938 double yA
, double kA
, GooString
*nameA
);
943 PSOutCustomColor
*next
;
946 PSOutCustomColor::PSOutCustomColor(double cA
, double mA
,
947 double yA
, double kA
, GooString
*nameA
) {
956 PSOutCustomColor::~PSOutCustomColor() {
960 //------------------------------------------------------------------------
962 struct PSOutImgClipRect
{
966 //------------------------------------------------------------------------
968 struct PSOutPaperSize
{
969 PSOutPaperSize(GooString
*nameA
, int wA
, int hA
) { name
= nameA
; w
= wA
; h
= hA
; }
970 ~PSOutPaperSize() { delete name
; }
975 //------------------------------------------------------------------------
977 //------------------------------------------------------------------------
979 class DeviceNRecoder
: public FilterStream
{
982 DeviceNRecoder(Stream
*strA
, int widthA
, int heightA
,
983 GfxImageColorMap
*colorMapA
);
984 virtual ~DeviceNRecoder();
985 virtual StreamKind
getKind() { return strWeird
; }
986 virtual void reset();
987 virtual int getChar()
988 { return (bufIdx
>= bufSize
&& !fillBuf()) ? EOF
: buf
[bufIdx
++]; }
989 virtual int lookChar()
990 { return (bufIdx
>= bufSize
&& !fillBuf()) ? EOF
: buf
[bufIdx
]; }
991 virtual GooString
*getPSFilter(int psLevel
, const char *indent
) { return NULL
; }
992 virtual GBool
isBinary(GBool last
= gTrue
) { return gTrue
; }
993 virtual GBool
isEncoder() { return gTrue
; }
1000 GfxImageColorMap
*colorMap
;
1002 ImageStream
*imgStr
;
1003 int buf
[gfxColorMaxComps
];
1009 DeviceNRecoder::DeviceNRecoder(Stream
*strA
, int widthA
, int heightA
,
1010 GfxImageColorMap
*colorMapA
):
1011 FilterStream(strA
) {
1014 colorMap
= colorMapA
;
1017 bufIdx
= gfxColorMaxComps
;
1018 bufSize
= ((GfxDeviceNColorSpace
*)colorMap
->getColorSpace())->
1019 getAlt()->getNComps();
1020 func
= ((GfxDeviceNColorSpace
*)colorMap
->getColorSpace())->
1021 getTintTransformFunc();
1024 DeviceNRecoder::~DeviceNRecoder() {
1028 if (str
->isEncoder()) {
1033 void DeviceNRecoder::reset() {
1034 imgStr
= new ImageStream(str
, width
, colorMap
->getNumPixelComps(),
1035 colorMap
->getBits());
1039 GBool
DeviceNRecoder::fillBuf() {
1040 Guchar pixBuf
[gfxColorMaxComps
];
1042 double x
[gfxColorMaxComps
], y
[gfxColorMaxComps
];
1045 if (pixelIdx
>= width
* height
) {
1048 imgStr
->getPixel(pixBuf
);
1049 colorMap
->getColor(pixBuf
, &color
);
1051 i
< ((GfxDeviceNColorSpace
*)colorMap
->getColorSpace())->getNComps();
1053 x
[i
] = colToDbl(color
.c
[i
]);
1055 func
->transform(x
, y
);
1056 for (i
= 0; i
< bufSize
; ++i
) {
1057 buf
[i
] = (int)(y
[i
] * 255 + 0.5);
1064 //------------------------------------------------------------------------
1066 //------------------------------------------------------------------------
1069 typedef void (*SignalFunc
)(int);
1072 static void outputToFile(void *stream
, const char *data
, int len
) {
1073 fwrite(data
, 1, len
, (FILE *)stream
);
1076 PSOutputDev::PSOutputDev(const char *fileName
, PDFDoc
*doc
,
1078 const std::vector
<int> &pages
, PSOutMode modeA
,
1079 int paperWidthA
, int paperHeightA
,
1080 GBool noCropA
, GBool duplexA
,
1081 int imgLLXA
, int imgLLYA
, int imgURXA
, int imgURYA
,
1082 GBool forceRasterizeA
,
1084 PSOutCustomCodeCbk customCodeCbkA
,
1085 void *customCodeCbkDataA
) {
1087 PSFileType fileTypeA
;
1090 underlayCbkData
= NULL
;
1092 overlayCbkData
= NULL
;
1093 customCodeCbk
= customCodeCbkA
;
1094 customCodeCbkData
= customCodeCbkDataA
;
1097 fontNames
= new GooHash(gTrue
);
1105 customColors
= NULL
;
1106 haveTextClip
= gFalse
;
1108 forceRasterize
= forceRasterizeA
;
1111 // open file or pipe
1112 if (!strcmp(fileName
, "-")) {
1113 fileTypeA
= psStdout
;
1115 } else if (fileName
[0] == '|') {
1119 signal(SIGPIPE
, (SignalFunc
)SIG_IGN
);
1121 if (!(f
= popen(fileName
+ 1, "w"))) {
1122 error(errIO
, -1, "Couldn't run print command '{0:s}'", fileName
);
1127 error(errIO
, -1, "Print commands are not supported ('{0:s}')", fileName
);
1133 if (!(f
= fopen(fileName
, "w"))) {
1134 error(errIO
, -1, "Couldn't open PostScript file '{0:s}'", fileName
);
1140 init(outputToFile
, f
, fileTypeA
, psTitleA
,
1142 imgLLXA
, imgLLYA
, imgURXA
, imgURYA
, manualCtrlA
,
1143 paperWidthA
, paperHeightA
, noCropA
, duplexA
);
1146 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA
, void *outputStreamA
,
1149 const std::vector
<int> &pages
, PSOutMode modeA
,
1150 int paperWidthA
, int paperHeightA
,
1151 GBool noCropA
, GBool duplexA
,
1152 int imgLLXA
, int imgLLYA
, int imgURXA
, int imgURYA
,
1153 GBool forceRasterizeA
,
1155 PSOutCustomCodeCbk customCodeCbkA
,
1156 void *customCodeCbkDataA
) {
1158 underlayCbkData
= NULL
;
1160 overlayCbkData
= NULL
;
1161 customCodeCbk
= customCodeCbkA
;
1162 customCodeCbkData
= customCodeCbkDataA
;
1165 fontNames
= new GooHash(gTrue
);
1173 customColors
= NULL
;
1174 haveTextClip
= gFalse
;
1176 forceRasterize
= forceRasterizeA
;
1179 init(outputFuncA
, outputStreamA
, psGeneric
, psTitleA
,
1181 imgLLXA
, imgLLYA
, imgURXA
, imgURYA
, manualCtrlA
,
1182 paperWidthA
, paperHeightA
, noCropA
, duplexA
);
1185 struct StandardMedia
{
1191 static const StandardMedia standardMedia
[] =
1193 { "A0", 2384, 3371 },
1194 { "A1", 1685, 2384 },
1195 { "A2", 1190, 1684 },
1196 { "A3", 842, 1190 },
1199 { "B4", 729, 1032 },
1201 { "Letter", 612, 792 },
1202 { "Tabloid", 792, 1224 },
1203 { "Ledger", 1224, 792 },
1204 { "Legal", 612, 1008 },
1205 { "Statement", 396, 612 },
1206 { "Executive", 540, 720 },
1207 { "Folio", 612, 936 },
1208 { "Quarto", 610, 780 },
1209 { "10x14", 720, 1008 },
1213 /* PLRM specifies a tolerance of 5 points when matching page sizes */
1214 static bool pageDimensionEqual(int a
, int b
) {
1215 return (abs (a
- b
) < 5);
1218 // Shared initialization of PSOutputDev members.
1219 // Store the values but do not process them so the function that
1220 // created the PSOutputDev can use the various setters to change defaults.
1222 void PSOutputDev::init(PSOutputFunc outputFuncA
, void *outputStreamA
,
1223 PSFileType fileTypeA
, char *psTitleA
, PDFDoc
*docA
,
1224 const std::vector
<int> &pagesA
, PSOutMode modeA
,
1225 int imgLLXA
, int imgLLYA
, int imgURXA
, int imgURYA
,
1226 GBool manualCtrlA
, int paperWidthA
, int paperHeightA
,
1227 GBool noCropA
, GBool duplexA
) {
1229 if (pagesA
.empty()) {
1235 postInitDone
= gFalse
;
1237 embedTrueType
= gTrue
;
1238 embedCIDPostScript
= gTrue
;
1239 embedCIDTrueType
= gTrue
;
1240 fontPassthrough
= gFalse
;
1241 optimizeColorSpace
= gFalse
;
1242 preloadImagesForms
= gFalse
;
1243 generateOPI
= gFalse
;
1244 useASCIIHex
= gFalse
;
1246 rasterMono
= gFalse
;
1247 rasterResolution
= 300;
1248 uncompressPreloadedImages
= gFalse
;
1249 rasterAntialias
= gFalse
;
1250 displayText
= gTrue
;
1252 outputFunc
= outputFuncA
;
1253 outputStream
= outputStreamA
;
1254 fileType
= fileTypeA
;
1255 psTitle
= (psTitleA
? strdup(psTitleA
): NULL
);
1257 level
= globalParams
->getPSLevel();
1260 paperWidth
= paperWidthA
;
1261 paperHeight
= paperHeightA
;
1268 manualCtrl
= manualCtrlA
;
1273 inType3Char
= gFalse
;
1274 inUncoloredPattern
= gFalse
;
1275 t3FillColorOnly
= gFalse
;
1278 // initialize OPI nesting levels
1284 xScale0
= yScale0
= 0;
1286 clipLLX0
= clipLLY0
= 0;
1287 clipURX0
= clipURY0
= -1;
1289 // initialize sequential page number
1293 // Complete the initialization after the function that created the PSOutputDev
1294 // has had a chance to modify default values with the various setters.
1296 void PSOutputDev::postInit()
1300 PSOutPaperSize
*size
;
1304 if (postInitDone
|| !ok
) {
1308 postInitDone
= gTrue
;
1310 xref
= doc
->getXRef();
1311 catalog
= doc
->getCatalog();
1313 if (paperWidth
< 0 || paperHeight
< 0) {
1316 paperMatch
= gFalse
;
1319 paperSizes
= new GooList();
1320 for (size_t pgi
= 0; pgi
< pages
.size(); ++pgi
) {
1321 const int pg
= pages
[pgi
];
1322 page
= catalog
->getPage(pg
);
1324 paperMatch
= gFalse
;
1328 if (w
< 0 || h
< 0) {
1329 // Unable to obtain a paper size from the document and no page size
1330 // specified. In this case use A4 as the page size to ensure the PS output is
1331 // valid. This will only occur if the PDF is very broken.
1335 } else if (noCrop
) {
1336 w
= (int)ceil(page
->getMediaWidth());
1337 h
= (int)ceil(page
->getMediaHeight());
1339 w
= (int)ceil(page
->getCropWidth());
1340 h
= (int)ceil(page
->getCropHeight());
1343 int rotate
= page
->getRotate();
1344 if (rotate
== 90 || rotate
== 270)
1349 if (h
> paperHeight
)
1351 for (i
= 0; i
< paperSizes
->getLength(); ++i
) {
1352 size
= (PSOutPaperSize
*)paperSizes
->get(i
);
1353 if (pageDimensionEqual(w
, size
->w
) && pageDimensionEqual(h
, size
->h
))
1356 if (i
== paperSizes
->getLength()) {
1357 const StandardMedia
*media
= standardMedia
;
1358 GooString
*name
= NULL
;
1359 while (media
->name
) {
1360 if (pageDimensionEqual(w
, media
->width
) && pageDimensionEqual(h
, media
->height
)) {
1361 name
= new GooString(media
->name
);
1369 name
= GooString::format("{0:d}x{1:d}mm", int(w
*25.4/72), int(h
*25.4/72));
1370 paperSizes
->append(new PSOutPaperSize(name
, w
, h
));
1372 pagePaperSize
.insert(std::pair
<int,int>(pg
, i
));
1374 break; // we only need one entry when all pages are the same size
1376 if (imgLLX
== 0 && imgURX
== 0 && imgLLY
== 0 && imgURY
== 0) {
1377 imgLLX
= imgLLY
= 0;
1378 imgURX
= paperWidth
;
1379 imgURY
= paperHeight
;
1381 std::vector
<int> pageList
;
1382 if (mode
== psModeForm
) {
1383 pageList
.push_back(pages
[0]);
1388 // initialize fontIDs, fontFileIDs, and fontFileNames lists
1391 fontIDs
= (Ref
*)gmallocn(fontIDSize
, sizeof(Ref
));
1392 for (i
= 0; i
< 14; ++i
) {
1393 fontNames
->add(new GooString(psBase14SubstFonts
[i
].psName
), 1);
1395 names
= globalParams
->getPSResidentFonts();
1396 for (i
= 0; i
< names
->getLength(); ++i
) {
1397 fontNames
->add((GooString
*)names
->get(i
), 1);
1400 t1FontNameSize
= 64;
1402 t1FontNames
= (PST1FontName
*)gmallocn(t1FontNameSize
, sizeof(PST1FontName
));
1413 numTilingPatterns
= 0;
1416 // initialize embedded font resource comment list
1417 embFontList
= new GooString();
1421 // this check is needed in case the document has zero pages
1422 if ((page
= doc
->getPage(pageList
[0]))) {
1423 writeHeader(pageList
,
1424 page
->getMediaBox(),
1429 error(errSyntaxError
, -1, "Invalid page {0:d}", pageList
[0]);
1430 box
= new PDFRectangle(0, 0, 1, 1);
1431 writeHeader(pageList
, box
, box
, 0, psTitle
);
1434 if (mode
!= psModeForm
) {
1435 writePS("%%BeginProlog\n");
1438 if (mode
!= psModeForm
) {
1439 writePS("%%EndProlog\n");
1440 writePS("%%BeginSetup\n");
1442 writeDocSetup(doc
, catalog
, pageList
, duplex
);
1443 if (mode
!= psModeForm
) {
1444 writePS("%%EndSetup\n");
1449 PSOutputDev::~PSOutputDev() {
1450 PSOutCustomColor
*cc
;
1454 if (!postInitDone
) {
1458 writePS("%%Trailer\n");
1460 if (mode
!= psModeForm
) {
1464 if (fileType
== psFile
) {
1466 ICS_MapRefNumAndAssign((short)((FILE *)outputStream
)->handle
);
1468 fclose((FILE *)outputStream
);
1471 else if (fileType
== psPipe
) {
1472 pclose((FILE *)outputStream
);
1474 signal(SIGPIPE
, (SignalFunc
)SIG_DFL
);
1480 deleteGooList(paperSizes
, PSOutPaperSize
);
1490 for (i
= 0; i
< t1FontNameLen
; ++i
) {
1491 delete t1FontNames
[i
].psName
;
1496 for (i
= 0; i
< font8InfoLen
; ++i
) {
1497 gfree(font8Info
[i
].codeToGID
);
1502 for (i
= 0; i
< font16EncLen
; ++i
) {
1503 if (font16Enc
[i
].enc
) {
1504 delete font16Enc
[i
].enc
;
1511 while (customColors
) {
1513 customColors
= cc
->next
;
1519 void PSOutputDev::writeHeader(const std::vector
<int> &pages
,
1520 PDFRectangle
*mediaBox
, PDFRectangle
*cropBox
,
1521 int pageRotate
, char *psTitle
) {
1523 PSOutPaperSize
*size
;
1524 double x1
, y1
, x2
, y2
;
1529 writePS("%!PS-Adobe-3.0\n");
1532 writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
1535 writePS("%!PS-Adobe-3.0 Resource-Form\n");
1538 writePSFmt("%Produced by poppler pdftops version: {0:s} (http://poppler.freedesktop.org)\n", PACKAGE_VERSION
);
1539 xref
->getDocInfo(&info
);
1540 if (info
.isDict() && info
.dictLookup("Creator", &obj1
)->isString()) {
1541 writePS("%%Creator: ");
1542 writePSTextLine(obj1
.getString());
1547 char *sanitizedTitle
= strdup(psTitle
);
1548 for (Guint i
= 0; i
< strlen(sanitizedTitle
); ++i
) {
1549 if (sanitizedTitle
[i
] == '\n' || sanitizedTitle
[i
] == '\r') {
1550 sanitizedTitle
[i
] = ' ';
1553 writePSFmt("%%Title: {0:s}\n", sanitizedTitle
);
1554 free(sanitizedTitle
);
1556 writePSFmt("%%LanguageLevel: {0:d}\n",
1557 (level
== psLevel1
|| level
== psLevel1Sep
) ? 1 :
1558 (level
== psLevel2
|| level
== psLevel2Sep
) ? 2 : 3);
1559 if (level
== psLevel1Sep
|| level
== psLevel2Sep
|| level
== psLevel3Sep
) {
1560 writePS("%%DocumentProcessColors: (atend)\n");
1561 writePS("%%DocumentCustomColors: (atend)\n");
1563 writePS("%%DocumentSuppliedResources: (atend)\n");
1564 if ((level
== psLevel1
|| level
== psLevel1Sep
) && useBinary
) {
1565 writePS("%%DocumentData: Binary\n");
1570 for (i
= 0; i
< paperSizes
->getLength(); ++i
) {
1571 size
= (PSOutPaperSize
*)paperSizes
->get(i
);
1572 writePSFmt("%%{0:s} {1:t} {2:d} {3:d} 0 () ()\n",
1573 i
==0 ? "DocumentMedia:" : "+", size
->name
, size
->w
, size
->h
);
1575 writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth
, paperHeight
);
1576 writePSFmt("%%Pages: {0:d}\n", static_cast<int>(pages
.size()));
1577 writePS("%%EndComments\n");
1579 size
= (PSOutPaperSize
*)paperSizes
->get(0);
1580 writePS("%%BeginDefaults\n");
1581 writePSFmt("%%PageMedia: {0:t}\n", size
->name
);
1582 writePS("%%EndDefaults\n");
1586 epsX1
= cropBox
->x1
;
1587 epsY1
= cropBox
->y1
;
1588 epsX2
= cropBox
->x2
;
1589 epsY2
= cropBox
->y2
;
1590 if (pageRotate
== 0 || pageRotate
== 180) {
1595 } else { // pageRotate == 90 || pageRotate == 270
1601 writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n",
1602 (int)floor(x1
), (int)floor(y1
), (int)ceil(x2
), (int)ceil(y2
));
1603 writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
1605 writePS("%%DocumentSuppliedResources: (atend)\n");
1606 writePS("%%EndComments\n");
1609 writePS("%%EndComments\n");
1610 writePS("32 dict dup begin\n");
1611 writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n",
1612 (int)floor(mediaBox
->x1
), (int)floor(mediaBox
->y1
),
1613 (int)ceil(mediaBox
->x2
), (int)ceil(mediaBox
->y2
));
1614 writePS("/FormType 1 def\n");
1615 writePS("/Matrix [1 0 0 1 0 0] def\n");
1620 void PSOutputDev::writeXpdfProcset() {
1621 GBool lev1
, lev2
, lev3
, sep
, nonSep
;
1625 writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", "3.00");
1626 writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright
);
1627 lev1
= lev2
= lev3
= sep
= nonSep
= gTrue
;
1628 for (p
= prolog
; *p
; ++p
) {
1629 if ((*p
)[0] == '~') {
1630 lev1
= lev2
= lev3
= sep
= nonSep
= gFalse
;
1631 for (q
= *p
+ 1; *q
; ++q
) {
1633 case '1': lev1
= gTrue
; break;
1634 case '2': lev2
= gTrue
; break;
1635 case '3': lev3
= gTrue
; break;
1636 case 's': sep
= gTrue
; break;
1637 case 'n': nonSep
= gTrue
; break;
1640 } else if ((level
== psLevel1
&& lev1
&& nonSep
) ||
1641 (level
== psLevel1Sep
&& lev1
&& sep
) ||
1642 (level
== psLevel2
&& lev2
&& nonSep
) ||
1643 (level
== psLevel2Sep
&& lev2
&& sep
) ||
1644 (level
== psLevel3
&& lev3
&& nonSep
) ||
1645 (level
== psLevel3Sep
&& lev3
&& sep
)) {
1646 writePSFmt("{0:s}\n", *p
);
1649 writePS("%%EndResource\n");
1651 if (level
>= psLevel3
) {
1652 for (p
= cmapProlog
; *p
; ++p
) {
1653 writePSFmt("{0:s}\n", *p
);
1658 void PSOutputDev::writeDocSetup(PDFDoc
*doc
, Catalog
*catalog
,
1659 const std::vector
<int> &pages
,
1665 Object obj1
, obj2
, obj3
;
1669 if (mode
== psModeForm
) {
1670 // swap the form and xpdf dicts
1671 writePS("xpdf end begin dup begin\n");
1673 writePS("xpdf begin\n");
1675 for (size_t pgi
= 0; pgi
< pages
.size(); ++pgi
) {
1676 const int pg
= pages
[pgi
];
1677 page
= doc
->getPage(pg
);
1679 error(errSyntaxError
, -1, "Failed writing resources for page {0:d}", pg
);
1682 if ((resDict
= page
->getResourceDict())) {
1683 setupResources(resDict
);
1685 annots
= page
->getAnnots();
1686 for (i
= 0; i
< annots
->getNumAnnots(); ++i
) {
1687 if (annots
->getAnnot(i
)->getAppearanceResDict(&obj1
)->isDict()) {
1688 setupResources(obj1
.getDict());
1693 if ((acroForm
= catalog
->getAcroForm()) && acroForm
->isDict()) {
1694 if (acroForm
->dictLookup("DR", &obj1
)->isDict()) {
1695 setupResources(obj1
.getDict());
1698 if (acroForm
->dictLookup("Fields", &obj1
)->isArray()) {
1699 for (i
= 0; i
< obj1
.arrayGetLength(); ++i
) {
1700 if (obj1
.arrayGet(i
, &obj2
)->isDict()) {
1701 if (obj2
.dictLookup("DR", &obj3
)->isDict()) {
1702 setupResources(obj3
.getDict());
1711 if (mode
!= psModeForm
) {
1712 if (mode
!= psModeEPS
&& !manualCtrl
) {
1713 writePSFmt("{0:s} pdfSetup\n",
1714 duplexA
? "true" : "false");
1716 writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth
, paperHeight
);
1721 writePS("/opiMatrix matrix currentmatrix def\n");
1725 if (customCodeCbk
) {
1726 if ((s
= (*customCodeCbk
)(this, psOutCustomDocSetup
, 0,
1727 customCodeCbkData
))) {
1728 writePS(s
->getCString());
1734 void PSOutputDev::writePageTrailer() {
1735 if (mode
!= psModeForm
) {
1736 writePS("pdfEndPage\n");
1740 void PSOutputDev::writeTrailer() {
1741 PSOutCustomColor
*cc
;
1743 if (mode
== psModeForm
) {
1744 writePS("/Foo exch /Form defineresource pop\n");
1747 writePS("%%DocumentSuppliedResources:\n");
1748 writePS(embFontList
->getCString());
1749 if (level
== psLevel1Sep
|| level
== psLevel2Sep
||
1750 level
== psLevel3Sep
) {
1751 writePS("%%DocumentProcessColors:");
1752 if (processColors
& psProcessCyan
) {
1755 if (processColors
& psProcessMagenta
) {
1756 writePS(" Magenta");
1758 if (processColors
& psProcessYellow
) {
1761 if (processColors
& psProcessBlack
) {
1765 writePS("%%DocumentCustomColors:");
1766 for (cc
= customColors
; cc
; cc
= cc
->next
) {
1768 writePSString(cc
->name
);
1771 writePS("%%CMYKCustomColor:\n");
1772 for (cc
= customColors
; cc
; cc
= cc
->next
) {
1773 writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ",
1774 cc
->c
, cc
->m
, cc
->y
, cc
->k
);
1775 writePSString(cc
->name
);
1782 void PSOutputDev::setupResources(Dict
*resDict
) {
1783 Object xObjDict
, xObjRef
, xObj
, patDict
, patRef
, pat
, resObj
;
1788 setupFonts(resDict
);
1789 setupImages(resDict
);
1790 setupForms(resDict
);
1792 //----- recursively scan XObjects
1793 resDict
->lookup("XObject", &xObjDict
);
1794 if (xObjDict
.isDict()) {
1795 for (i
= 0; i
< xObjDict
.dictGetLength(); ++i
) {
1797 // avoid infinite recursion on XObjects
1799 if ((xObjDict
.dictGetValNF(i
, &xObjRef
)->isRef())) {
1800 ref0
= xObjRef
.getRef();
1801 if (resourceIDs
.find(ref0
.num
) != resourceIDs
.end()) {
1804 resourceIDs
.insert(ref0
.num
);
1809 // process the XObject's resource dictionary
1810 xObjDict
.dictGetVal(i
, &xObj
);
1811 if (xObj
.isStream()) {
1812 xObj
.streamGetDict()->lookup("Resources", &resObj
);
1813 if (resObj
.isDict()) {
1814 setupResources(resObj
.getDict());
1826 //----- recursively scan Patterns
1827 resDict
->lookup("Pattern", &patDict
);
1828 if (patDict
.isDict()) {
1829 inType3Char
= gTrue
;
1830 for (i
= 0; i
< patDict
.dictGetLength(); ++i
) {
1832 // avoid infinite recursion on Patterns
1834 if ((patDict
.dictGetValNF(i
, &patRef
)->isRef())) {
1835 ref0
= patRef
.getRef();
1836 if (resourceIDs
.find(ref0
.num
) != resourceIDs
.end()) {
1839 resourceIDs
.insert(ref0
.num
);
1844 // process the Pattern's resource dictionary
1845 patDict
.dictGetVal(i
, &pat
);
1846 if (pat
.isStream()) {
1847 pat
.streamGetDict()->lookup("Resources", &resObj
);
1848 if (resObj
.isDict()) {
1849 setupResources(resObj
.getDict());
1858 inType3Char
= gFalse
;
1863 void PSOutputDev::setupFonts(Dict
*resDict
) {
1866 GfxFontDict
*gfxFontDict
;
1871 resDict
->lookupNF("Font", &obj1
);
1873 obj1
.fetch(xref
, &obj2
);
1874 if (obj2
.isDict()) {
1876 gfxFontDict
= new GfxFontDict(xref
, &r
, obj2
.getDict());
1879 } else if (obj1
.isDict()) {
1880 gfxFontDict
= new GfxFontDict(xref
, NULL
, obj1
.getDict());
1883 for (i
= 0; i
< gfxFontDict
->getNumFonts(); ++i
) {
1884 if ((font
= gfxFontDict
->getFont(i
))) {
1885 setupFont(font
, resDict
);
1893 void PSOutputDev::setupFont(GfxFont
*font
, Dict
*parentResDict
) {
1894 GfxFontLoc
*fontLoc
;
1899 const char *charName
;
1905 // check if font is already set up
1906 for (i
= 0; i
< fontIDLen
; ++i
) {
1907 if (fontIDs
[i
].num
== font
->getID()->num
&&
1908 fontIDs
[i
].gen
== font
->getID()->gen
) {
1913 // add entry to fontIDs list
1914 if (fontIDLen
>= fontIDSize
) {
1916 fontIDs
= (Ref
*)greallocn(fontIDs
, fontIDSize
, sizeof(Ref
));
1918 fontIDs
[fontIDLen
++] = *font
->getID();
1924 if (font
->getType() == fontType3
) {
1925 psName
= GooString::format("T3_{0:d}_{1:d}",
1926 font
->getID()->num
, font
->getID()->gen
);
1927 setupType3Font(font
, psName
, parentResDict
);
1929 fontLoc
= font
->locateFont(xref
, this);
1930 if (fontLoc
!= NULL
) {
1931 switch (fontLoc
->locType
) {
1932 case gfxFontLocEmbedded
:
1933 switch (fontLoc
->fontType
) {
1935 // this assumes that the PS font name matches the PDF font name
1936 psName
= font
->getEmbeddedFontName() ? font
->getEmbeddedFontName()->copy() : new GooString();
1937 setupEmbeddedType1Font(&fontLoc
->embFontID
, psName
);
1940 psName
= makePSFontName(font
, &fontLoc
->embFontID
);
1941 setupEmbeddedType1CFont(font
, &fontLoc
->embFontID
, psName
);
1944 psName
= makePSFontName(font
, &fontLoc
->embFontID
);
1945 setupEmbeddedOpenTypeT1CFont(font
, &fontLoc
->embFontID
, psName
);
1948 case fontTrueTypeOT
:
1949 psName
= makePSFontName(font
, font
->getID());
1950 setupEmbeddedTrueTypeFont(font
, &fontLoc
->embFontID
, psName
);
1953 psName
= makePSFontName(font
, &fontLoc
->embFontID
);
1954 setupEmbeddedCIDType0Font(font
, &fontLoc
->embFontID
, psName
);
1957 case fontCIDType2OT
:
1958 psName
= makePSFontName(font
, font
->getID());
1959 //~ should check to see if font actually uses vertical mode
1960 setupEmbeddedCIDTrueTypeFont(font
, &fontLoc
->embFontID
, psName
, gTrue
);
1962 case fontCIDType0COT
:
1963 psName
= makePSFontName(font
, &fontLoc
->embFontID
);
1964 setupEmbeddedOpenTypeCFFFont(font
, &fontLoc
->embFontID
, psName
);
1970 case gfxFontLocExternal
:
1971 //~ add cases for external 16-bit fonts
1972 switch (fontLoc
->fontType
) {
1974 if (font
->getEmbeddedFontName()) {
1975 // this assumes that the PS font name matches the PDF font name
1976 psName
= font
->getEmbeddedFontName()->copy();
1978 //~ this won't work -- the PS font name won't match
1979 psName
= makePSFontName(font
, font
->getID());
1981 setupExternalType1Font(fontLoc
->path
, psName
);
1984 case fontTrueTypeOT
:
1985 psName
= makePSFontName(font
, font
->getID());
1986 setupExternalTrueTypeFont(font
, fontLoc
->path
, psName
);
1989 case fontCIDType2OT
:
1990 psName
= makePSFontName(font
, font
->getID());
1991 //~ should check to see if font actually uses vertical mode
1992 setupExternalCIDTrueTypeFont(font
, fontLoc
->path
, psName
, gTrue
);
1998 case gfxFontLocResident
:
1999 psName
= fontLoc
->path
->copy();
2005 if (font
->isCIDFont()) {
2006 error(errSyntaxError
, -1,
2007 "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)",
2008 font
->getName() ? font
->getName()->getCString()
2010 ((GfxCIDFont
*)font
)->getCollection()
2011 ? ((GfxCIDFont
*)font
)->getCollection()->getCString()
2013 if (font16EncLen
>= font16EncSize
) {
2014 font16EncSize
+= 16;
2015 font16Enc
= (PSFont16Enc
*)greallocn(font16Enc
,
2017 sizeof(PSFont16Enc
));
2019 font16Enc
[font16EncLen
].fontID
= *font
->getID();
2020 font16Enc
[font16EncLen
].enc
= NULL
;
2023 error(errSyntaxError
, -1,
2024 "Couldn't find a font to substitute for '{0:s}'",
2025 font
->getName() ? font
->getName()->getCString()
2032 // scale substituted 8-bit fonts
2033 if (fontLoc
->locType
== gfxFontLocResident
&&
2034 fontLoc
->substIdx
>= 0) {
2036 for (code
= 0; code
< 256; ++code
) {
2037 if ((charName
= ((Gfx8BitFont
*)font
)->getCharName(code
)) &&
2038 charName
[0] == 'm' && charName
[1] == '\0') {
2043 w1
= ((Gfx8BitFont
*)font
)->getWidth(code
);
2047 w2
= psBase14SubstFonts
[fontLoc
->substIdx
].mWidth
;
2054 // handle encodings for substituted CID fonts
2055 if (fontLoc
->locType
== gfxFontLocResident
&&
2056 fontLoc
->fontType
>= fontCIDType0
) {
2058 if (font16EncLen
>= font16EncSize
) {
2059 font16EncSize
+= 16;
2060 font16Enc
= (PSFont16Enc
*)greallocn(font16Enc
,
2062 sizeof(PSFont16Enc
));
2064 font16Enc
[font16EncLen
].fontID
= *font
->getID();
2065 if ((uMap
= globalParams
->getUnicodeMap(fontLoc
->encoding
))) {
2066 font16Enc
[font16EncLen
].enc
= fontLoc
->encoding
->copy();
2069 error(errSyntaxError
, -1,
2070 "Couldn't find Unicode map for 16-bit font encoding '{0:t}'",
2072 font16Enc
[font16EncLen
].enc
= NULL
;
2080 // generate PostScript code to set up the font
2081 if (font
->isCIDFont()) {
2082 if (level
== psLevel3
|| level
== psLevel3Sep
) {
2083 writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n",
2084 font
->getID()->num
, font
->getID()->gen
, psName
,
2087 writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",
2088 font
->getID()->num
, font
->getID()->gen
, psName
,
2092 writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n",
2093 font
->getID()->num
, font
->getID()->gen
, psName
, xs
, ys
);
2094 for (i
= 0; i
< 256; i
+= 8) {
2095 writePS((char *)((i
== 0) ? "[ " : " "));
2096 for (j
= 0; j
< 8; ++j
) {
2097 if (font
->getType() == fontTrueType
&&
2099 !((Gfx8BitFont
*)font
)->getHasEncoding()) {
2100 sprintf(buf
, "c%02x", i
+j
);
2103 charName
= ((Gfx8BitFont
*)font
)->getCharName(i
+j
);
2106 writePSName(charName
? charName
: (char *)".notdef");
2107 // the empty name is legal in PDF and PostScript, but PostScript
2108 // uses a double-slash (//...) for "immediately evaluated names",
2109 // so we need to add a space character here
2110 if (charName
&& !charName
[0]) {
2114 writePS((i
== 256-8) ? (char *)"]\n" : (char *)"\n");
2116 writePS("pdfMakeFont\n");
2122 void PSOutputDev::setupEmbeddedType1Font(Ref
*id
, GooString
*psName
) {
2123 static const char hexChar
[17] = "0123456789abcdef";
2124 Object refObj
, strObj
, obj1
, obj2
, obj3
;
2126 long length1
, length2
, length3
;
2130 GBool writePadding
= gTrue
;
2133 // check if font is already embedded
2134 if (fontNames
->lookupInt(psName
)) {
2137 fontNames
->add(psName
->copy(), 1);
2139 // get the font stream and info
2140 refObj
.initRef(id
->num
, id
->gen
);
2141 refObj
.fetch(xref
, &strObj
);
2143 if (!strObj
.isStream()) {
2144 error(errSyntaxError
, -1, "Embedded font file object is not a stream");
2147 if (!(dict
= strObj
.streamGetDict())) {
2148 error(errSyntaxError
, -1,
2149 "Embedded font stream is missing its dictionary");
2152 dict
->lookup("Length1", &obj1
);
2153 dict
->lookup("Length2", &obj2
);
2154 dict
->lookup("Length3", &obj3
);
2155 if (!obj1
.isInt() || !obj2
.isInt() || !obj3
.isInt()) {
2156 error(errSyntaxError
, -1,
2157 "Missing length fields in embedded font stream dictionary");
2163 length1
= obj1
.getInt();
2164 length2
= obj2
.getInt();
2165 length3
= obj3
.getInt();
2170 // beginning comment
2171 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2172 embFontList
->append("%%+ font ");
2173 embFontList
->append(psName
->getCString());
2174 embFontList
->append("\n");
2176 strObj
.streamReset();
2177 if (strObj
.streamGetChar() == 0x80 &&
2178 strObj
.streamGetChar() == 1) {
2180 length1
= strObj
.streamGetChar() |
2181 (strObj
.streamGetChar() << 8) |
2182 (strObj
.streamGetChar() << 16) |
2183 (strObj
.streamGetChar() << 24);
2185 strObj
.streamReset();
2187 // copy ASCII portion of font
2188 for (i
= 0; i
< length1
&& (c
= strObj
.streamGetChar()) != EOF
; ++i
) {
2192 // figure out if encrypted portion is binary or ASCII
2194 for (i
= 0; i
< 4; ++i
) {
2195 start
[i
] = strObj
.streamGetChar();
2196 if (start
[i
] == EOF
) {
2197 error(errSyntaxError
, -1,
2198 "Unexpected end of file in embedded font stream");
2201 if (!((start
[i
] >= '0' && start
[i
] <= '9') ||
2202 (start
[i
] >= 'A' && start
[i
] <= 'F') ||
2203 (start
[i
] >= 'a' && start
[i
] <= 'f')))
2209 // length2 == 0 is an error
2210 // trying to solve it by just piping all
2212 error(errSyntaxWarning
, -1, "Font has length2 as 0, trying to overcome the problem reading the stream until the end");
2214 writePadding
= gFalse
;
2218 // convert binary data to ASCII
2220 if (start
[0] == 0x80 &&
2222 length2
= start
[2] |
2224 (strObj
.streamGetChar() << 16) |
2225 (strObj
.streamGetChar() << 24);
2228 for (i
= 0; i
< 4; ++i
) {
2229 writePSChar(hexChar
[(start
[i
] >> 4) & 0x0f]);
2230 writePSChar(hexChar
[start
[i
] & 0x0f]);
2233 #if 0 // this causes trouble for various PostScript printers
2234 // if Length2 is incorrect (too small), font data gets chopped, so
2235 // we take a few extra characters from the trailer just in case
2236 length2
+= length3
>= 8 ? 8 : length3
;
2238 while (i
< length2
) {
2239 if ((c
= strObj
.streamGetChar()) == EOF
) {
2242 writePSChar(hexChar
[(c
>> 4) & 0x0f]);
2243 writePSChar(hexChar
[c
& 0x0f]);
2244 if (++i
% 32 == 0) {
2252 // already in ASCII format -- just copy it
2254 for (i
= 0; i
< 4; ++i
) {
2255 writePSChar(start
[i
]);
2257 for (i
= 4; i
< length2
; ++i
) {
2258 if ((c
= strObj
.streamGetChar()) == EOF
) {
2268 // write fixed-content portion
2269 c
= strObj
.streamGetChar();
2271 c
= strObj
.streamGetChar();
2273 length3
= strObj
.streamGetChar() |
2274 (strObj
.streamGetChar() << 8) |
2275 (strObj
.streamGetChar() << 16) |
2276 (strObj
.streamGetChar() << 24);
2279 while (i
< length3
) {
2280 if ((c
= strObj
.streamGetChar()) == EOF
) {
2291 while ((c
= strObj
.streamGetChar()) != EOF
) {
2297 // write padding and "cleartomark"
2298 for (i
= 0; i
< 8; ++i
) {
2299 writePS("00000000000000000000000000000000"
2300 "00000000000000000000000000000000\n");
2302 writePS("cleartomark\n");
2307 writePS("%%EndResource\n");
2310 if (strObj
.isStream())
2311 strObj
.streamClose();
2315 void PSOutputDev::setupExternalType1Font(GooString
*fileName
, GooString
*psName
) {
2316 static const char hexChar
[17] = "0123456789abcdef";
2320 if (fontNames
->lookupInt(psName
)) {
2323 fontNames
->add(psName
->copy(), 1);
2325 // beginning comment
2326 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2327 embFontList
->append("%%+ font ");
2328 embFontList
->append(psName
->getCString());
2329 embFontList
->append("\n");
2331 // copy the font file
2332 if (!(fontFile
= fopen(fileName
->getCString(), "rb"))) {
2333 error(errIO
, -1, "Couldn't open external font file");
2337 c
= fgetc(fontFile
);
2340 ungetc(c
, fontFile
);
2341 while (!feof(fontFile
)) {
2342 fgetc(fontFile
); // skip start of segment byte (0x80)
2343 int segType
= fgetc(fontFile
);
2344 long segLen
= fgetc(fontFile
) |
2345 (fgetc(fontFile
) << 8) |
2346 (fgetc(fontFile
) << 16) |
2347 (fgetc(fontFile
) << 24);
2353 for (long i
= 0; i
< segLen
; i
++) {
2354 c
= fgetc(fontFile
);
2359 } else if (segType
== 2) {
2361 for (long i
= 0; i
< segLen
; i
++) {
2362 c
= fgetc(fontFile
);
2365 writePSChar(hexChar
[(c
>> 4) & 0x0f]);
2366 writePSChar(hexChar
[c
& 0x0f]);
2375 } else if (c
!= EOF
) {
2377 while ((c
= fgetc(fontFile
)) != EOF
)
2383 writePS("%%EndResource\n");
2386 void PSOutputDev::setupEmbeddedType1CFont(GfxFont
*font
, Ref
*id
,
2387 GooString
*psName
) {
2393 // check if font is already embedded
2394 for (i
= 0; i
< t1FontNameLen
; ++i
) {
2395 if (t1FontNames
[i
].fontFileID
.num
== id
->num
&&
2396 t1FontNames
[i
].fontFileID
.gen
== id
->gen
) {
2398 psName
->insert(0, t1FontNames
[i
].psName
);
2402 if (t1FontNameLen
== t1FontNameSize
) {
2403 t1FontNameSize
*= 2;
2404 t1FontNames
= (PST1FontName
*)greallocn(t1FontNames
, t1FontNameSize
,
2405 sizeof(PST1FontName
));
2407 t1FontNames
[t1FontNameLen
].fontFileID
= *id
;
2408 t1FontNames
[t1FontNameLen
].psName
= psName
->copy();
2411 // beginning comment
2412 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2413 embFontList
->append("%%+ font ");
2414 embFontList
->append(psName
->getCString());
2415 embFontList
->append("\n");
2417 // convert it to a Type 1 font
2418 if ((fontBuf
= font
->readEmbFontFile(xref
, &fontLen
))) {
2419 if ((ffT1C
= FoFiType1C::make(fontBuf
, fontLen
))) {
2420 ffT1C
->convertToType1(psName
->getCString(), NULL
, gTrue
,
2421 outputFunc
, outputStream
);
2428 writePS("%%EndResource\n");
2431 void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont
*font
, Ref
*id
,
2432 GooString
*psName
) {
2438 // check if font is already embedded
2439 for (i
= 0; i
< t1FontNameLen
; ++i
) {
2440 if (t1FontNames
[i
].fontFileID
.num
== id
->num
&&
2441 t1FontNames
[i
].fontFileID
.gen
== id
->gen
) {
2443 psName
->insert(0, t1FontNames
[i
].psName
);
2447 if (t1FontNameLen
== t1FontNameSize
) {
2448 t1FontNameSize
*= 2;
2449 t1FontNames
= (PST1FontName
*)greallocn(t1FontNames
, t1FontNameSize
,
2450 sizeof(PST1FontName
));
2452 t1FontNames
[t1FontNameLen
].fontFileID
= *id
;
2453 t1FontNames
[t1FontNameLen
].psName
= psName
->copy();
2456 // beginning comment
2457 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2458 embFontList
->append("%%+ font ");
2459 embFontList
->append(psName
->getCString());
2460 embFontList
->append("\n");
2462 // convert it to a Type 1 font
2463 if ((fontBuf
= font
->readEmbFontFile(xref
, &fontLen
))) {
2464 if ((ffTT
= FoFiTrueType::make(fontBuf
, fontLen
))) {
2465 if (ffTT
->isOpenTypeCFF()) {
2466 ffTT
->convertToType1(psName
->getCString(), NULL
, gTrue
,
2467 outputFunc
, outputStream
);
2475 writePS("%%EndResource\n");
2478 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont
*font
, Ref
*id
,
2479 GooString
*psName
) {
2485 // beginning comment
2486 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2487 embFontList
->append("%%+ font ");
2488 embFontList
->append(psName
->getCString());
2489 embFontList
->append("\n");
2491 // convert it to a Type 42 font
2492 if ((fontBuf
= font
->readEmbFontFile(xref
, &fontLen
))) {
2493 if ((ffTT
= FoFiTrueType::make(fontBuf
, fontLen
))) {
2494 codeToGID
= ((Gfx8BitFont
*)font
)->getCodeToGIDMap(ffTT
);
2495 ffTT
->convertToType42(psName
->getCString(),
2496 ((Gfx8BitFont
*)font
)->getHasEncoding()
2497 ? ((Gfx8BitFont
*)font
)->getEncoding()
2499 codeToGID
, outputFunc
, outputStream
);
2501 if (font8InfoLen
>= font8InfoSize
) {
2502 font8InfoSize
+= 16;
2503 font8Info
= (PSFont8Info
*)greallocn(font8Info
,
2505 sizeof(PSFont8Info
));
2507 font8Info
[font8InfoLen
].fontID
= *font
->getID();
2508 font8Info
[font8InfoLen
].codeToGID
= codeToGID
;
2517 writePS("%%EndResource\n");
2520 void PSOutputDev::setupExternalTrueTypeFont(GfxFont
*font
, GooString
*fileName
,
2521 GooString
*psName
) {
2525 // beginning comment
2526 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2527 embFontList
->append("%%+ font ");
2528 embFontList
->append(psName
->getCString());
2529 embFontList
->append("\n");
2531 // convert it to a Type 42 font
2532 if ((ffTT
= FoFiTrueType::load(fileName
->getCString()))) {
2533 codeToGID
= ((Gfx8BitFont
*)font
)->getCodeToGIDMap(ffTT
);
2534 ffTT
->convertToType42(psName
->getCString(),
2535 ((Gfx8BitFont
*)font
)->getHasEncoding()
2536 ? ((Gfx8BitFont
*)font
)->getEncoding()
2538 codeToGID
, outputFunc
, outputStream
);
2540 if (font8InfoLen
>= font8InfoSize
) {
2541 font8InfoSize
+= 16;
2542 font8Info
= (PSFont8Info
*)greallocn(font8Info
,
2544 sizeof(PSFont8Info
));
2546 font8Info
[font8InfoLen
].fontID
= *font
->getID();
2547 font8Info
[font8InfoLen
].codeToGID
= codeToGID
;
2554 writePS("%%EndResource\n");
2557 void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont
*font
,
2558 GooString
*fileName
,
2560 GBool needVerticalMetrics
) {
2565 // beginning comment
2566 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2567 embFontList
->append("%%+ font ");
2568 embFontList
->append(psName
->getCString());
2569 embFontList
->append("\n");
2571 // convert it to a Type 0 font
2572 //~ this should use fontNum to load the correct font
2573 if ((ffTT
= FoFiTrueType::load(fileName
->getCString()))) {
2575 // check for embedding permission
2576 if (ffTT
->getEmbeddingRights() >= 1) {
2579 if (((GfxCIDFont
*)font
)->getCIDToGID()) {
2580 codeToGIDLen
= ((GfxCIDFont
*)font
)->getCIDToGIDLen();
2582 codeToGID
= (int *)gmallocn(codeToGIDLen
, sizeof(int));
2583 memcpy(codeToGID
, ((GfxCIDFont
*)font
)->getCIDToGID(),
2584 codeToGIDLen
* sizeof(int));
2587 codeToGID
= ((GfxCIDFont
*)font
)->getCodeToGIDMap(ffTT
, &codeToGIDLen
);
2589 if (ffTT
->isOpenTypeCFF()) {
2590 ffTT
->convertToCIDType0(psName
->getCString(),
2591 codeToGID
, codeToGIDLen
,
2592 outputFunc
, outputStream
);
2593 } else if (globalParams
->getPSLevel() >= psLevel3
) {
2594 // Level 3: use a CID font
2595 ffTT
->convertToCIDType2(psName
->getCString(),
2596 codeToGID
, codeToGIDLen
,
2597 needVerticalMetrics
,
2598 outputFunc
, outputStream
);
2600 // otherwise: use a non-CID composite font
2601 ffTT
->convertToType0(psName
->getCString(),
2602 codeToGID
, codeToGIDLen
,
2603 needVerticalMetrics
,
2604 outputFunc
, outputStream
);
2608 error(errSyntaxError
, -1,
2609 "TrueType font '{0:s}' does not allow embedding",
2610 font
->getName() ? font
->getName()->getCString() : "(unnamed)");
2617 writePS("%%EndResource\n");
2620 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont
*font
, Ref
*id
,
2621 GooString
*psName
) {
2627 // check if font is already embedded
2628 for (i
= 0; i
< t1FontNameLen
; ++i
) {
2629 if (t1FontNames
[i
].fontFileID
.num
== id
->num
&&
2630 t1FontNames
[i
].fontFileID
.gen
== id
->gen
) {
2632 psName
->insert(0, t1FontNames
[i
].psName
);
2636 if (t1FontNameLen
== t1FontNameSize
) {
2637 t1FontNameSize
*= 2;
2638 t1FontNames
= (PST1FontName
*)greallocn(t1FontNames
, t1FontNameSize
,
2639 sizeof(PST1FontName
));
2641 t1FontNames
[t1FontNameLen
].fontFileID
= *id
;
2642 t1FontNames
[t1FontNameLen
].psName
= psName
->copy();
2645 // beginning comment
2646 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2647 embFontList
->append("%%+ font ");
2648 embFontList
->append(psName
->getCString());
2649 embFontList
->append("\n");
2651 // convert it to a Type 0 font
2652 if ((fontBuf
= font
->readEmbFontFile(xref
, &fontLen
))) {
2653 if ((ffT1C
= FoFiType1C::make(fontBuf
, fontLen
))) {
2654 if (globalParams
->getPSLevel() >= psLevel3
) {
2655 // Level 3: use a CID font
2656 ffT1C
->convertToCIDType0(psName
->getCString(), NULL
, 0,
2657 outputFunc
, outputStream
);
2659 // otherwise: use a non-CID composite font
2660 ffT1C
->convertToType0(psName
->getCString(), NULL
, 0,
2661 outputFunc
, outputStream
);
2669 writePS("%%EndResource\n");
2672 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont
*font
, Ref
*id
,
2674 GBool needVerticalMetrics
) {
2679 // beginning comment
2680 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2681 embFontList
->append("%%+ font ");
2682 embFontList
->append(psName
->getCString());
2683 embFontList
->append("\n");
2685 // convert it to a Type 0 font
2686 if ((fontBuf
= font
->readEmbFontFile(xref
, &fontLen
))) {
2687 if ((ffTT
= FoFiTrueType::make(fontBuf
, fontLen
))) {
2688 if (globalParams
->getPSLevel() >= psLevel3
) {
2689 // Level 3: use a CID font
2690 ffTT
->convertToCIDType2(psName
->getCString(),
2691 ((GfxCIDFont
*)font
)->getCIDToGID(),
2692 ((GfxCIDFont
*)font
)->getCIDToGIDLen(),
2693 needVerticalMetrics
,
2694 outputFunc
, outputStream
);
2696 // otherwise: use a non-CID composite font
2697 ffTT
->convertToType0(psName
->getCString(),
2698 ((GfxCIDFont
*)font
)->getCIDToGID(),
2699 ((GfxCIDFont
*)font
)->getCIDToGIDLen(),
2700 needVerticalMetrics
,
2701 outputFunc
, outputStream
);
2709 writePS("%%EndResource\n");
2712 void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont
*font
, Ref
*id
,
2713 GooString
*psName
) {
2719 // check if font is already embedded
2720 for (i
= 0; i
< t1FontNameLen
; ++i
) {
2721 if (t1FontNames
[i
].fontFileID
.num
== id
->num
&&
2722 t1FontNames
[i
].fontFileID
.gen
== id
->gen
) {
2724 psName
->insert(0, t1FontNames
[i
].psName
);
2728 if (t1FontNameLen
== t1FontNameSize
) {
2729 t1FontNameSize
*= 2;
2730 t1FontNames
= (PST1FontName
*)greallocn(t1FontNames
, t1FontNameSize
,
2731 sizeof(PST1FontName
));
2733 t1FontNames
[t1FontNameLen
].fontFileID
= *id
;
2734 t1FontNames
[t1FontNameLen
].psName
= psName
->copy();
2737 // beginning comment
2738 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2739 embFontList
->append("%%+ font ");
2740 embFontList
->append(psName
->getCString());
2741 embFontList
->append("\n");
2743 // convert it to a Type 0 font
2744 if ((fontBuf
= font
->readEmbFontFile(xref
, &fontLen
))) {
2745 if ((ffTT
= FoFiTrueType::make(fontBuf
, fontLen
))) {
2746 if (ffTT
->isOpenTypeCFF()) {
2747 if (globalParams
->getPSLevel() >= psLevel3
) {
2748 // Level 3: use a CID font
2749 ffTT
->convertToCIDType0(psName
->getCString(),
2750 ((GfxCIDFont
*)font
)->getCIDToGID(),
2751 ((GfxCIDFont
*)font
)->getCIDToGIDLen(),
2752 outputFunc
, outputStream
);
2754 // otherwise: use a non-CID composite font
2755 ffTT
->convertToType0(psName
->getCString(),
2756 ((GfxCIDFont
*)font
)->getCIDToGID(),
2757 ((GfxCIDFont
*)font
)->getCIDToGIDLen(),
2758 outputFunc
, outputStream
);
2767 writePS("%%EndResource\n");
2770 void PSOutputDev::setupType3Font(GfxFont
*font
, GooString
*psName
,
2771 Dict
*parentResDict
) {
2781 // set up resources used by font
2782 if ((resDict
= ((Gfx8BitFont
*)font
)->getResources())) {
2783 inType3Char
= gTrue
;
2784 setupResources(resDict
);
2785 inType3Char
= gFalse
;
2787 resDict
= parentResDict
;
2790 // beginning comment
2791 writePSFmt("%%BeginResource: font {0:t}\n", psName
);
2792 embFontList
->append("%%+ font ");
2793 embFontList
->append(psName
->getCString());
2794 embFontList
->append("\n");
2797 writePS("8 dict begin\n");
2798 writePS("/FontType 3 def\n");
2799 m
= font
->getFontMatrix();
2800 writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n",
2801 m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
2802 m
= font
->getFontBBox();
2803 writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n",
2804 m
[0], m
[1], m
[2], m
[3]);
2805 writePS("/Encoding 256 array def\n");
2806 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
2807 writePS("/BuildGlyph {\n");
2808 writePS(" exch /CharProcs get exch\n");
2809 writePS(" 2 copy known not { pop /.notdef } if\n");
2810 writePS(" get exec\n");
2811 writePS("} bind def\n");
2812 writePS("/BuildChar {\n");
2813 writePS(" 1 index /Encoding get exch get\n");
2814 writePS(" 1 index /BuildGlyph get exec\n");
2815 writePS("} bind def\n");
2816 if ((charProcs
= ((Gfx8BitFont
*)font
)->getCharProcs())) {
2817 writePSFmt("/CharProcs {0:d} dict def\n", charProcs
->getLength());
2818 writePS("CharProcs begin\n");
2823 gfx
= new Gfx(doc
, this, resDict
, &box
, NULL
);
2824 inType3Char
= gTrue
;
2825 for (i
= 0; i
< charProcs
->getLength(); ++i
) {
2826 t3FillColorOnly
= gFalse
;
2827 t3Cacheable
= gFalse
;
2828 t3NeedsRestore
= gFalse
;
2830 writePSName(charProcs
->getKey(i
));
2832 gfx
->display(charProcs
->getVal(i
, &charProc
));
2836 buf
= GooString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n",
2837 t3WX
, t3WY
, t3LLX
, t3LLY
, t3URX
, t3URY
);
2839 buf
= GooString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX
, t3WY
);
2841 (*outputFunc
)(outputStream
, buf
->getCString(), buf
->getLength());
2843 (*outputFunc
)(outputStream
, t3String
->getCString(),
2844 t3String
->getLength());
2848 if (t3NeedsRestore
) {
2849 (*outputFunc
)(outputStream
, "Q\n", 2);
2853 inType3Char
= gFalse
;
2857 writePS("currentdict end\n");
2858 writePSFmt("/{0:t} exch definefont pop\n", psName
);
2861 writePS("%%EndResource\n");
2864 // Make a unique PS font name, based on the names given in the PDF
2865 // font object, and an object ID (font file object for
2866 GooString
*PSOutputDev::makePSFontName(GfxFont
*font
, Ref
*id
) {
2867 GooString
*psName
, *s
;
2869 if ((s
= font
->getEmbeddedFontName())) {
2870 psName
= filterPSName(s
);
2871 if (!fontNames
->lookupInt(psName
)) {
2872 fontNames
->add(psName
->copy(), 1);
2877 if ((s
= font
->getName())) {
2878 psName
= filterPSName(s
);
2879 if (!fontNames
->lookupInt(psName
)) {
2880 fontNames
->add(psName
->copy(), 1);
2885 psName
= GooString::format("FF{0:d}_{1:d}", id
->num
, id
->gen
);
2886 if ((s
= font
->getEmbeddedFontName())) {
2887 s
= filterPSName(s
);
2888 psName
->append('_')->append(s
);
2890 } else if ((s
= font
->getName())) {
2891 s
= filterPSName(s
);
2892 psName
->append('_')->append(s
);
2895 fontNames
->add(psName
->copy(), 1);
2899 void PSOutputDev::setupImages(Dict
*resDict
) {
2900 Object xObjDict
, xObj
, xObjRef
, subtypeObj
, maskObj
, maskRef
;
2904 if (!(mode
== psModeForm
|| inType3Char
|| preloadImagesForms
)) {
2908 //----- recursively scan XObjects
2909 resDict
->lookup("XObject", &xObjDict
);
2910 if (xObjDict
.isDict()) {
2911 for (i
= 0; i
< xObjDict
.dictGetLength(); ++i
) {
2912 xObjDict
.dictGetValNF(i
, &xObjRef
);
2913 xObjDict
.dictGetVal(i
, &xObj
);
2914 if (xObj
.isStream()) {
2915 xObj
.streamGetDict()->lookup("Subtype", &subtypeObj
);
2916 if (subtypeObj
.isName("Image")) {
2917 if (xObjRef
.isRef()) {
2918 imgID
= xObjRef
.getRef();
2919 for (j
= 0; j
< imgIDLen
; ++j
) {
2920 if (imgIDs
[j
].num
== imgID
.num
&& imgIDs
[j
].gen
== imgID
.gen
) {
2924 if (j
== imgIDLen
) {
2925 if (imgIDLen
>= imgIDSize
) {
2926 if (imgIDSize
== 0) {
2931 imgIDs
= (Ref
*)greallocn(imgIDs
, imgIDSize
, sizeof(Ref
));
2933 imgIDs
[imgIDLen
++] = imgID
;
2934 setupImage(imgID
, xObj
.getStream(), gFalse
);
2935 if (level
>= psLevel3
&&
2936 xObj
.streamGetDict()->lookup("Mask", &maskObj
)->isStream()) {
2937 setupImage(imgID
, maskObj
.getStream(), gTrue
);
2942 error(errSyntaxError
, -1,
2943 "Image in resource dict is not an indirect reference");
2955 void PSOutputDev::setupImage(Ref id
, Stream
*str
, GBool mask
) {
2956 GBool useRLE
, useCompressed
, doUseASCIIHex
;
2959 int size
, line
, col
, i
;
2960 int outerSize
, outer
;
2963 //~ this does not correctly handle the DeviceN color space
2964 //~ -- need to use DeviceNRecoder
2965 if (level
< psLevel2
) {
2967 useCompressed
= gFalse
;
2968 doUseASCIIHex
= gTrue
;
2970 if (uncompressPreloadedImages
) {
2972 useCompressed
= gFalse
;
2974 s
= str
->getPSFilter(level
< psLevel3
? 2 : 3, "");
2977 useCompressed
= gTrue
;
2981 useCompressed
= gFalse
;
2984 doUseASCIIHex
= useASCIIHex
;
2986 if (useCompressed
) {
2987 str
= str
->getUndecodedStream();
2990 str
= new RunLengthEncoder(str
);
2992 if (doUseASCIIHex
) {
2993 str
= new ASCIIHexEncoder(str
);
2995 str
= new ASCII85Encoder(str
);
2998 // compute image data size
3004 } while (c
== '\n' || c
== '\r');
3005 if (c
== (doUseASCIIHex
? '>' : '~') || c
== EOF
) {
3012 for (i
= 1; i
<= (doUseASCIIHex
? 1 : 4); ++i
) {
3015 } while (c
== '\n' || c
== '\r');
3016 if (c
== (doUseASCIIHex
? '>' : '~') || c
== EOF
) {
3021 if (c
== (doUseASCIIHex
? '>' : '~') || c
== EOF
) {
3029 } while (c
!= (doUseASCIIHex
? '>' : '~') && c
!= EOF
);
3030 // add one entry for the final line of data; add another entry
3031 // because the RunLengthDecode filter may read past the end
3036 outerSize
= size
/65535 + 1;
3038 writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n",
3039 outerSize
, mask
? "Mask" : "Im", id
.num
, id
.gen
);
3042 // write the data into the array
3044 for (outer
= 0;outer
< outerSize
;outer
++) {
3045 int innerSize
= size
> 65535 ? 65535 : size
;
3047 // put the inner array into the outer array
3048 writePSFmt("{0:d} array 1 index {1:d} 2 index put\n",
3051 writePS((char *)(doUseASCIIHex
? "dup 0 <" : "dup 0 <~"));
3055 } while (c
== '\n' || c
== '\r');
3056 if (c
== (doUseASCIIHex
? '>' : '~') || c
== EOF
) {
3065 for (i
= 1; i
<= (doUseASCIIHex
? 1 : 4); ++i
) {
3068 } while (c
== '\n' || c
== '\r');
3069 if (c
== (doUseASCIIHex
? '>' : '~') || c
== EOF
) {
3076 if (c
== (doUseASCIIHex
? '>' : '~') || c
== EOF
) {
3079 // each line is: "dup nnnnn <~...data...~> put<eol>"
3080 // so max data length = 255 - 20 = 235
3081 // chunks are 1 or 4 bytes each, so we have to stop at 232
3082 // but make it 225 just to be safe
3084 writePS((char *)(doUseASCIIHex
? "> put\n" : "~> put\n"));
3086 if (line
>= innerSize
) break;
3087 writePSFmt((char *)(doUseASCIIHex
? "dup {0:d} <" : "dup {0:d} <~"), line
);
3091 if (c
== (doUseASCIIHex
? '>' : '~') || c
== EOF
) {
3092 writePS((char *)(doUseASCIIHex
? "> put\n" : "~> put\n"));
3095 writePSFmt("{0:d} <> put\n", line
);
3110 void PSOutputDev::setupForms(Dict
*resDict
) {
3111 Object xObjDict
, xObj
, xObjRef
, subtypeObj
;
3114 if (!preloadImagesForms
) {
3118 resDict
->lookup("XObject", &xObjDict
);
3119 if (xObjDict
.isDict()) {
3120 for (i
= 0; i
< xObjDict
.dictGetLength(); ++i
) {
3121 xObjDict
.dictGetValNF(i
, &xObjRef
);
3122 xObjDict
.dictGetVal(i
, &xObj
);
3123 if (xObj
.isStream()) {
3124 xObj
.streamGetDict()->lookup("Subtype", &subtypeObj
);
3125 if (subtypeObj
.isName("Form")) {
3126 if (xObjRef
.isRef()) {
3127 setupForm(xObjRef
.getRef(), &xObj
);
3129 error(errSyntaxError
, -1,
3130 "Form in resource dict is not an indirect reference");
3142 void PSOutputDev::setupForm(Ref id
, Object
*strObj
) {
3143 Dict
*dict
, *resDict
;
3144 Object matrixObj
, bboxObj
, resObj
, obj1
;
3145 double m
[6], bbox
[4];
3150 // check if form is already defined
3151 for (i
= 0; i
< formIDLen
; ++i
) {
3152 if (formIDs
[i
].num
== id
.num
&& formIDs
[i
].gen
== id
.gen
) {
3157 // add entry to formIDs list
3158 if (formIDLen
>= formIDSize
) {
3159 if (formIDSize
== 0) {
3164 formIDs
= (Ref
*)greallocn(formIDs
, formIDSize
, sizeof(Ref
));
3166 formIDs
[formIDLen
++] = id
;
3168 dict
= strObj
->streamGetDict();
3171 dict
->lookup("BBox", &bboxObj
);
3172 if (!bboxObj
.isArray()) {
3174 error(errSyntaxError
, -1, "Bad form bounding box");
3177 for (i
= 0; i
< 4; ++i
) {
3178 bboxObj
.arrayGet(i
, &obj1
);
3179 bbox
[i
] = obj1
.getNum();
3185 dict
->lookup("Matrix", &matrixObj
);
3186 if (matrixObj
.isArray()) {
3187 for (i
= 0; i
< 6; ++i
) {
3188 matrixObj
.arrayGet(i
, &obj1
);
3189 m
[i
] = obj1
.getNum();
3200 dict
->lookup("Resources", &resObj
);
3201 resDict
= resObj
.isDict() ? resObj
.getDict() : (Dict
*)NULL
;
3203 writePSFmt("/f_{0:d}_{1:d} {{\n", id
.num
, id
.gen
);
3205 writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n",
3206 m
[0], m
[1], m
[2], m
[3], m
[4], m
[5]);
3212 gfx
= new Gfx(doc
, this, resDict
, &box
, &box
);
3213 gfx
->display(strObj
);
3222 GBool
PSOutputDev::checkPageSlice(Page
*page
, double /*hDPI*/, double /*vDPI*/,
3223 int rotateA
, GBool useMediaBox
, GBool crop
,
3224 int sliceX
, int sliceY
,
3225 int sliceW
, int sliceH
,
3227 GBool (*abortCheckCbk
)(void *data
),
3228 void *abortCheckCbkData
,
3229 GBool (*annotDisplayDecideCbk
)(Annot
*annot
, void *user_data
),
3230 void *annotDisplayDecideCbkData
) {
3231 PreScanOutputDev
*scan
;
3234 SplashOutputDev
*splashOut
;
3235 SplashColor paperColor
;
3238 SplashBitmap
*bitmap
;
3243 double hDPI2
, vDPI2
;
3244 double m0
, m1
, m2
, m3
, m4
, m5
;
3245 int nStripes
, stripeH
, stripeY
;
3246 int c
, w
, h
, x
, y
, comp
, i
;
3247 int numComps
, initialNumComps
;
3249 char hexBuf
[32*2 + 2]; // 32 values X 2 chars/value + line ending + null
3253 if (!postInitDone
) {
3256 if (forceRasterize
) {
3259 scan
= new PreScanOutputDev(doc
);
3260 page
->displaySlice(scan
, 72, 72, rotateA
, useMediaBox
, crop
,
3261 sliceX
, sliceY
, sliceW
, sliceH
,
3262 printing
, abortCheckCbk
, abortCheckCbkData
,
3263 annotDisplayDecideCbk
, annotDisplayDecideCbkData
);
3264 rasterize
= scan
->usesTransparency() || scan
->usesPatternImageMask();
3272 // start the PS page
3273 page
->makeBox(rasterResolution
, rasterResolution
, rotateA
, useMediaBox
, gFalse
,
3274 sliceX
, sliceY
, sliceW
, sliceH
, &box
, &crop
);
3275 rotateA
+= page
->getRotate();
3276 if (rotateA
>= 360) {
3278 } else if (rotateA
< 0) {
3281 state
= new GfxState(rasterResolution
, rasterResolution
, &box
, rotateA
, gFalse
);
3282 startPage(page
->getNum(), state
, xref
);
3285 // set up the SplashOutputDev
3286 if (rasterMono
|| level
== psLevel1
) {
3288 paperColor
[0] = 0xff;
3289 splashOut
= new SplashOutputDev(splashModeMono8
, 1, gFalse
,
3290 paperColor
, gFalse
);
3292 } else if (level
== psLevel1Sep
|| level
== psLevel2Sep
||
3293 level
== psLevel3Sep
|| globalParams
->getOverprintPreview()) {
3295 paperColor
[0] = paperColor
[1] = paperColor
[2] = paperColor
[3] = 0;
3296 splashOut
= new SplashOutputDev(splashModeCMYK8
, 1, gFalse
,
3297 paperColor
, gFalse
);
3301 paperColor
[0] = paperColor
[1] = paperColor
[2] = 0xff;
3302 splashOut
= new SplashOutputDev(splashModeRGB8
, 1, gFalse
,
3303 paperColor
, gFalse
);
3305 splashOut
->setFontAntialias(rasterAntialias
);
3306 splashOut
->setVectorAntialias(rasterAntialias
);
3307 splashOut
->startDoc(doc
);
3309 // break the page into stripes
3310 hDPI2
= xScale
* rasterResolution
;
3311 vDPI2
= yScale
* rasterResolution
;
3312 if (sliceW
< 0 || sliceH
< 0) {
3314 box
= *page
->getMediaBox();
3316 box
= *page
->getCropBox();
3318 sliceX
= sliceY
= 0;
3319 sliceW
= (int)((box
.x2
- box
.x1
) * hDPI2
/ 72.0);
3320 sliceH
= (int)((box
.y2
- box
.y1
) * vDPI2
/ 72.0);
3322 nStripes
= (int)ceil((double)(sliceW
* sliceH
) /
3323 (double)rasterizationSliceSize
);
3324 stripeH
= (sliceH
+ nStripes
- 1) / nStripes
;
3326 // render the stripes
3327 initialNumComps
= numComps
;
3328 for (stripeY
= sliceY
; stripeY
< sliceH
; stripeY
+= stripeH
) {
3330 // rasterize a stripe
3331 page
->makeBox(hDPI2
, vDPI2
, 0, useMediaBox
, gFalse
,
3332 sliceX
, stripeY
, sliceW
, stripeH
, &box
, &crop
);
3333 m0
= box
.x2
- box
.x1
;
3336 m3
= box
.y2
- box
.y1
;
3339 page
->displaySlice(splashOut
, hDPI2
, vDPI2
,
3340 (360 - page
->getRotate()) % 360, useMediaBox
, crop
,
3341 sliceX
, stripeY
, sliceW
, stripeH
,
3342 printing
, abortCheckCbk
, abortCheckCbkData
,
3343 annotDisplayDecideCbk
, annotDisplayDecideCbkData
);
3345 // draw the rasterized image
3346 bitmap
= splashOut
->getBitmap();
3347 numComps
= initialNumComps
;
3348 w
= bitmap
->getWidth();
3349 h
= bitmap
->getHeight();
3351 writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n",
3352 m0
, m1
, m2
, m3
, m4
, m5
);
3355 writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}\n",
3357 useBinary
? "Bin" : "");
3358 p
= bitmap
->getDataPtr() + (h
- 1) * bitmap
->getRowSize();
3361 for (y
= 0; y
< h
; ++y
) {
3362 for (x
= 0; x
< w
; ++x
) {
3365 writePSBuf(hexBuf
, i
);
3371 for (y
= 0; y
< h
; ++y
) {
3372 for (x
= 0; x
< w
; ++x
) {
3374 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
3376 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
3379 writePSBuf(hexBuf
, i
);
3389 writePSBuf(hexBuf
, i
);
3393 p
= bitmap
->getDataPtr();
3394 // Check for an all gray image
3395 if (getOptimizeColorSpace()) {
3397 for (y
= 0; y
< h
; ++y
) {
3398 for (x
= 0; x
< w
; ++x
) {
3399 if (p
[4*x
] != p
[4*x
+ 1] || p
[4*x
] != p
[4*x
+ 2]) {
3405 p
+= bitmap
->getRowSize();
3410 writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}{6:s}\n",
3412 isGray
? "" : "Sep",
3413 useBinary
? "Bin" : "");
3414 p
= bitmap
->getDataPtr() + (h
- 1) * bitmap
->getRowSize();
3416 col
[0] = col
[1] = col
[2] = col
[3] = 0;
3419 if ((psProcessBlack
& processColors
) == 0) {
3420 // Check if the image uses black
3421 for (y
= 0; y
< h
; ++y
) {
3422 for (x
= 0; x
< w
; ++x
) {
3423 if (p
[4*x
] > 0 || p
[4*x
+ 3] > 0) {
3429 p
-= bitmap
->getRowSize();
3431 p
= bitmap
->getDataPtr() + (h
- 1) * bitmap
->getRowSize();
3433 for (y
= 0; y
< h
; ++y
) {
3435 // Binary gray image
3436 for (x
= 0; x
< w
; ++x
) {
3437 g
= p
[4*x
] + p
[4*x
+ 3];
3440 hexBuf
[i
++] = (Guchar
) g
;
3442 writePSBuf(hexBuf
, i
);
3448 for (x
= 0; x
< w
; ++x
) {
3449 g
= p
[4*x
] + p
[4*x
+ 3];
3453 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
3455 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
3458 writePSBuf(hexBuf
, i
);
3463 p
-= bitmap
->getRowSize();
3465 } else if (((psProcessCyan
| psProcessMagenta
| psProcessYellow
| psProcessBlack
) & ~processColors
) != 0) {
3466 // Color image, need to check color flags for each dot
3467 for (y
= 0; y
< h
; ++y
) {
3468 for (comp
= 0; comp
< 4; ++comp
) {
3470 // Binary color image
3471 for (x
= 0; x
< w
; ++x
) {
3472 col
[comp
] |= p
[4*x
+ comp
];
3473 hexBuf
[i
++] = p
[4*x
+ comp
];
3475 writePSBuf(hexBuf
, i
);
3481 for (x
= 0; x
< w
; ++x
) {
3482 col
[comp
] |= p
[4*x
+ comp
];
3483 digit
= p
[4*x
+ comp
] / 16;
3484 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
3485 digit
= p
[4*x
+ comp
] % 16;
3486 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
3489 writePSBuf(hexBuf
, i
);
3495 p
-= bitmap
->getRowSize();
3498 // Color image, do not need to check color flags
3499 for (y
= 0; y
< h
; ++y
) {
3500 for (comp
= 0; comp
< 4; ++comp
) {
3502 // Binary color image
3503 for (x
= 0; x
< w
; ++x
) {
3504 hexBuf
[i
++] = p
[4*x
+ comp
];
3506 writePSBuf(hexBuf
, i
);
3512 for (x
= 0; x
< w
; ++x
) {
3513 digit
= p
[4*x
+ comp
] / 16;
3514 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
3515 digit
= p
[4*x
+ comp
] % 16;
3516 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
3519 writePSBuf(hexBuf
, i
);
3525 p
-= bitmap
->getRowSize();
3532 writePSBuf(hexBuf
, i
);
3535 processColors
|= psProcessCyan
;
3538 processColors
|= psProcessMagenta
;
3541 processColors
|= psProcessYellow
;
3544 processColors
|= psProcessBlack
;
3552 p
= bitmap
->getDataPtr() + (h
- 1) * bitmap
->getRowSize();
3553 str0
= new MemStream((char *)p
, 0, w
* h
* numComps
, &obj
);
3554 // Check for a color image that uses only gray
3555 if (!getOptimizeColorSpace()) {
3557 } else if (numComps
== 4) {
3560 while ((compCyan
= str0
->getChar()) != EOF
) {
3561 if (str0
->getChar() != compCyan
||
3562 str0
->getChar() != compCyan
) {
3568 } else if (numComps
== 3) {
3571 while ((compRed
= str0
->getChar()) != EOF
) {
3572 if (str0
->getChar() != compRed
||
3573 str0
->getChar() != compRed
) {
3582 if (isGray
&& numComps
== 4) {
3583 str
= new RunLengthEncoder(new CMYKGrayEncoder(str0
));
3585 } else if (isGray
&& numComps
== 3) {
3586 str
= new RunLengthEncoder(new RGBGrayEncoder(str0
));
3589 str
= new RunLengthEncoder(str0
);
3591 if (numComps
== 1) {
3592 writePS("/DeviceGray setcolorspace\n");
3593 } else if (numComps
== 3) {
3594 writePS("/DeviceRGB setcolorspace\n");
3596 writePS("/DeviceCMYK setcolorspace\n");
3598 writePS("<<\n /ImageType 1\n");
3599 writePSFmt(" /Width {0:d}\n", bitmap
->getWidth());
3600 writePSFmt(" /Height {0:d}\n", bitmap
->getHeight());
3601 writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w
, -h
, h
);
3602 writePS(" /BitsPerComponent 8\n");
3603 if (numComps
== 1) {
3604 writePS(" /Decode [1 0]\n");
3605 } else if (numComps
== 3) {
3606 writePS(" /Decode [0 1 0 1 0 1]\n");
3608 writePS(" /Decode [0 1 0 1 0 1 0 1]\n");
3610 writePS(" /DataSource currentfile\n");
3612 /* nothing to do */;
3613 } else if (useASCIIHex
) {
3614 writePS(" /ASCIIHexDecode filter\n");
3616 writePS(" /ASCII85Decode filter\n");
3618 writePS(" /RunLengthDecode filter\n");
3621 /* nothing to do */;
3622 } else if (useASCIIHex
) {
3623 str
= new ASCIIHexEncoder(str
);
3625 str
= new ASCII85Encoder(str
);
3629 // Count the bytes to write a document comment
3631 while (str
->getChar() != EOF
) {
3635 writePSFmt("%%BeginData: {0:d} Binary Bytes\n", len
+6+1);
3638 while ((c
= str
->getChar()) != EOF
) {
3646 writePS("%%EndData\n");
3648 processColors
|= (numComps
== 1) ? psProcessBlack
: psProcessCMYK
;
3651 writePS("grestore\n");
3656 // finish the PS page
3661 #else // HAVE_SPLASH
3663 error(errSyntaxWarning
, -1,
3664 "PDF page uses transparency and PSOutputDev was built without"
3665 " the Splash rasterizer - output may not be correct");
3667 #endif // HAVE_SPLASH
3670 void PSOutputDev::startPage(int pageNum
, GfxState
*state
, XRef
*xrefA
) {
3672 int x1
, y1
, x2
, y2
, width
, height
, t
;
3673 int imgWidth
, imgHeight
, imgWidth2
, imgHeight2
;
3676 PSOutPaperSize
*paperSize
;
3678 if (!postInitDone
) {
3682 if (mode
== psModePS
) {
3683 GooString pageLabel
;
3684 const GBool gotLabel
= doc
->getCatalog()->indexToLabel(pageNum
-1, &pageLabel
);
3686 // See bug13338 for why we try to avoid parentheses...
3688 GooString
*filteredString
= filterPSLabel(&pageLabel
, &needParens
);
3690 writePSFmt("%%Page: ({0:t}) {1:d}\n", filteredString
, seqPage
);
3692 writePSFmt("%%Page: {0:t} {1:d}\n", filteredString
, seqPage
);
3694 delete filteredString
;
3696 writePSFmt("%%Page: {0:d} {1:d}\n", pageNum
, seqPage
);
3699 page
= doc
->getCatalog()->getPage(pageNum
);
3700 imgLLX
= imgLLY
= 0;
3702 imgURX
= (int)ceil(page
->getMediaWidth());
3703 imgURY
= (int)ceil(page
->getMediaHeight());
3705 imgURX
= (int)ceil(page
->getCropWidth());
3706 imgURY
= (int)ceil(page
->getCropHeight());
3708 if (state
->getRotate() == 90 || state
->getRotate() == 270) {
3718 (*underlayCbk
)(this, underlayCbkData
);
3724 xScale
= yScale
= 1;
3728 // rotate, translate, and scale page
3729 imgWidth
= imgURX
- imgLLX
;
3730 imgHeight
= imgURY
- imgLLY
;
3731 x1
= (int)floor(state
->getX1());
3732 y1
= (int)floor(state
->getY1());
3733 x2
= (int)ceil(state
->getX2());
3734 y2
= (int)ceil(state
->getY2());
3738 // rotation and portrait/landscape mode
3740 rotate
= (360 - state
->getRotate()) % 360;
3742 } else if (rotate0
>= 0) {
3743 rotate
= (360 - rotate0
) % 360;
3746 rotate
= (360 - state
->getRotate()) % 360;
3747 if (rotate
== 0 || rotate
== 180) {
3748 if ((width
< height
&& imgWidth
> imgHeight
&& height
> imgHeight
) ||
3749 (width
> height
&& imgWidth
< imgHeight
&& width
> imgWidth
)) {
3755 } else { // rotate == 90 || rotate == 270
3756 if ((height
< width
&& imgWidth
> imgHeight
&& width
> imgHeight
) ||
3757 (height
> width
&& imgWidth
< imgHeight
&& height
> imgWidth
)) {
3758 rotate
= 270 - rotate
;
3766 paperSize
= (PSOutPaperSize
*)paperSizes
->get(pagePaperSize
[pageNum
]);
3767 writePSFmt("%%PageMedia: {0:t}\n", paperSize
->name
);
3769 if (rotate
== 0 || rotate
== 180) {
3770 writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", width
, height
);
3772 writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", height
, width
);
3774 writePSFmt("%%PageOrientation: {0:s}\n",
3775 landscape
? "Landscape" : "Portrait");
3776 writePS("%%BeginPageSetup\n");
3778 writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX
, imgURY
);
3780 writePS("pdfStartPage\n");
3782 imgWidth2
= imgWidth
;
3783 imgHeight2
= imgHeight
;
3784 } else if (rotate
== 90) {
3785 writePS("90 rotate\n");
3787 imgWidth2
= imgHeight
;
3788 imgHeight2
= imgWidth
;
3789 } else if (rotate
== 180) {
3790 writePS("180 rotate\n");
3791 imgWidth2
= imgWidth
;
3792 imgHeight2
= imgHeight
;
3795 } else { // rotate == 270
3796 writePS("270 rotate\n");
3798 imgWidth2
= imgHeight
;
3799 imgHeight2
= imgWidth
;
3802 if (xScale0
> 0 && yScale0
> 0) {
3805 } else if ((globalParams
->getPSShrinkLarger() &&
3806 (width
> imgWidth2
|| height
> imgHeight2
)) ||
3807 (globalParams
->getPSExpandSmaller() &&
3808 (width
< imgWidth2
&& height
< imgHeight2
))) {
3809 xScale
= (double)imgWidth2
/ (double)width
;
3810 yScale
= (double)imgHeight2
/ (double)height
;
3811 if (yScale
< xScale
) {
3817 // deal with odd bounding boxes or clipping
3818 if (clipLLX0
< clipURX0
&& clipLLY0
< clipURY0
) {
3819 tx
-= xScale
* clipLLX0
;
3820 ty
-= yScale
* clipLLY0
;
3826 if (tx0
>= 0 && ty0
>= 0) {
3827 tx
+= (rotate
== 0 || rotate
== 180) ? tx0
: ty0
;
3828 ty
+= (rotate
== 0 || rotate
== 180) ? ty0
: -tx0
;
3829 } else if (globalParams
->getPSCenter()) {
3830 if (clipLLX0
< clipURX0
&& clipLLY0
< clipURY0
) {
3831 tx
+= (imgWidth2
- xScale
* (clipURX0
- clipLLX0
)) / 2;
3832 ty
+= (imgHeight2
- yScale
* (clipURY0
- clipLLY0
)) / 2;
3834 tx
+= (imgWidth2
- xScale
* width
) / 2;
3835 ty
+= (imgHeight2
- yScale
* height
) / 2;
3838 tx
+= (rotate
== 0 || rotate
== 180) ? imgLLX
: imgLLY
;
3839 ty
+= (rotate
== 0 || rotate
== 180) ? imgLLY
: -imgLLX
;
3840 if (tx
!= 0 || ty
!= 0) {
3841 writePSFmt("{0:.6g} {1:.6g} translate\n", tx
, ty
);
3843 if (xScale
!= 1 || yScale
!= 1) {
3844 writePSFmt("{0:.6f} {1:.6f} scale\n", xScale
, yScale
);
3846 if (clipLLX0
< clipURX0
&& clipLLY0
< clipURY0
) {
3847 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n",
3848 clipLLX0
, clipLLY0
, clipURX0
- clipLLX0
, clipURY0
- clipLLY0
);
3850 writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1
, y1
, x2
- x1
, y2
- y1
);
3857 writePS("pdfStartPage\n");
3859 rotate
= (360 - state
->getRotate()) % 360;
3861 } else if (rotate
== 90) {
3862 writePS("90 rotate\n");
3865 } else if (rotate
== 180) {
3866 writePS("180 rotate\n");
3867 tx
= -(epsX1
+ epsX2
);
3868 ty
= -(epsY1
+ epsY2
);
3869 } else { // rotate == 270
3870 writePS("270 rotate\n");
3874 if (tx
!= 0 || ty
!= 0) {
3875 writePSFmt("{0:.6g} {1:.6g} translate\n", tx
, ty
);
3880 writePS("/PaintProc {\n");
3881 writePS("begin xpdf begin\n");
3882 writePS("pdfStartPage\n");
3888 if (customCodeCbk
) {
3889 if ((s
= (*customCodeCbk
)(this, psOutCustomPageSetup
, pageNum
,
3890 customCodeCbkData
))) {
3891 writePS(s
->getCString());
3896 writePS("%%EndPageSetup\n");
3899 void PSOutputDev::endPage() {
3902 (*overlayCbk
)(this, overlayCbkData
);
3906 if (mode
== psModeForm
) {
3907 writePS("pdfEndPage\n");
3908 writePS("end end\n");
3910 writePS("end end\n");
3913 writePS("showpage\n");
3915 writePS("%%PageTrailer\n");
3920 void PSOutputDev::saveState(GfxState
*state
) {
3925 void PSOutputDev::restoreState(GfxState
*state
) {
3930 void PSOutputDev::updateCTM(GfxState
*state
, double m11
, double m12
,
3931 double m21
, double m22
, double m31
, double m32
) {
3932 writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n",
3933 m11
, m12
, m21
, m22
, m31
, m32
);
3936 void PSOutputDev::updateLineDash(GfxState
*state
) {
3941 state
->getLineDash(&dash
, &length
, &start
);
3943 for (i
= 0; i
< length
; ++i
) {
3944 writePSFmt("{0:.6g}{1:w}",
3945 dash
[i
] < 0 ? 0 : dash
[i
],
3946 (i
== length
-1) ? 0 : 1);
3948 writePSFmt("] {0:.6g} d\n", start
);
3951 void PSOutputDev::updateFlatness(GfxState
*state
) {
3952 writePSFmt("{0:d} i\n", state
->getFlatness());
3955 void PSOutputDev::updateLineJoin(GfxState
*state
) {
3956 writePSFmt("{0:d} j\n", state
->getLineJoin());
3959 void PSOutputDev::updateLineCap(GfxState
*state
) {
3960 writePSFmt("{0:d} J\n", state
->getLineCap());
3963 void PSOutputDev::updateMiterLimit(GfxState
*state
) {
3964 writePSFmt("{0:.6g} M\n", state
->getMiterLimit());
3967 void PSOutputDev::updateLineWidth(GfxState
*state
) {
3968 writePSFmt("{0:.6g} w\n", state
->getLineWidth());
3971 void PSOutputDev::updateFillColorSpace(GfxState
*state
) {
3972 if (inUncoloredPattern
) {
3981 if (state
->getFillColorSpace()->getMode() != csPattern
) {
3982 dumpColorSpaceL2(state
->getFillColorSpace(), gTrue
, gFalse
, gFalse
);
3992 void PSOutputDev::updateStrokeColorSpace(GfxState
*state
) {
3993 if (inUncoloredPattern
) {
4002 if (state
->getStrokeColorSpace()->getMode() != csPattern
) {
4003 dumpColorSpaceL2(state
->getStrokeColorSpace(), gTrue
, gFalse
, gFalse
);
4013 void PSOutputDev::updateFillColor(GfxState
*state
) {
4018 GfxSeparationColorSpace
*sepCS
;
4022 if (inUncoloredPattern
) {
4027 state
->getFillGray(&gray
);
4028 writePSFmt("{0:.4g} g\n", colToDbl(gray
));
4031 state
->getFillCMYK(&cmyk
);
4032 c
= colToDbl(cmyk
.c
);
4033 m
= colToDbl(cmyk
.m
);
4034 y
= colToDbl(cmyk
.y
);
4035 k
= colToDbl(cmyk
.k
);
4036 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c
, m
, y
, k
);
4037 addProcessColor(c
, m
, y
, k
);
4041 if (state
->getFillColorSpace()->getMode() != csPattern
) {
4042 colorPtr
= state
->getFillColor();
4044 for (i
= 0; i
< state
->getFillColorSpace()->getNComps(); ++i
) {
4048 writePSFmt("{0:.4g}", colToDbl(colorPtr
->c
[i
]));
4055 if (state
->getFillColorSpace()->getMode() == csSeparation
) {
4056 sepCS
= (GfxSeparationColorSpace
*)state
->getFillColorSpace();
4057 color
.c
[0] = gfxColorComp1
;
4058 sepCS
->getCMYK(&color
, &cmyk
);
4059 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n",
4060 colToDbl(state
->getFillColor()->c
[0]),
4061 colToDbl(cmyk
.c
), colToDbl(cmyk
.m
),
4062 colToDbl(cmyk
.y
), colToDbl(cmyk
.k
),
4064 addCustomColor(sepCS
);
4066 state
->getFillCMYK(&cmyk
);
4067 c
= colToDbl(cmyk
.c
);
4068 m
= colToDbl(cmyk
.m
);
4069 y
= colToDbl(cmyk
.y
);
4070 k
= colToDbl(cmyk
.k
);
4071 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c
, m
, y
, k
);
4072 addProcessColor(c
, m
, y
, k
);
4076 t3Cacheable
= gFalse
;
4079 void PSOutputDev::updateStrokeColor(GfxState
*state
) {
4084 GfxSeparationColorSpace
*sepCS
;
4088 if (inUncoloredPattern
) {
4093 state
->getStrokeGray(&gray
);
4094 writePSFmt("{0:.4g} G\n", colToDbl(gray
));
4097 state
->getStrokeCMYK(&cmyk
);
4098 c
= colToDbl(cmyk
.c
);
4099 m
= colToDbl(cmyk
.m
);
4100 y
= colToDbl(cmyk
.y
);
4101 k
= colToDbl(cmyk
.k
);
4102 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c
, m
, y
, k
);
4103 addProcessColor(c
, m
, y
, k
);
4107 if (state
->getStrokeColorSpace()->getMode() != csPattern
) {
4108 colorPtr
= state
->getStrokeColor();
4110 for (i
= 0; i
< state
->getStrokeColorSpace()->getNComps(); ++i
) {
4114 writePSFmt("{0:.4g}", colToDbl(colorPtr
->c
[i
]));
4121 if (state
->getStrokeColorSpace()->getMode() == csSeparation
) {
4122 sepCS
= (GfxSeparationColorSpace
*)state
->getStrokeColorSpace();
4123 color
.c
[0] = gfxColorComp1
;
4124 sepCS
->getCMYK(&color
, &cmyk
);
4125 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n",
4126 colToDbl(state
->getStrokeColor()->c
[0]),
4127 colToDbl(cmyk
.c
), colToDbl(cmyk
.m
),
4128 colToDbl(cmyk
.y
), colToDbl(cmyk
.k
),
4130 addCustomColor(sepCS
);
4132 state
->getStrokeCMYK(&cmyk
);
4133 c
= colToDbl(cmyk
.c
);
4134 m
= colToDbl(cmyk
.m
);
4135 y
= colToDbl(cmyk
.y
);
4136 k
= colToDbl(cmyk
.k
);
4137 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c
, m
, y
, k
);
4138 addProcessColor(c
, m
, y
, k
);
4142 t3Cacheable
= gFalse
;
4145 void PSOutputDev::addProcessColor(double c
, double m
, double y
, double k
) {
4147 processColors
|= psProcessCyan
;
4150 processColors
|= psProcessMagenta
;
4153 processColors
|= psProcessYellow
;
4156 processColors
|= psProcessBlack
;
4160 void PSOutputDev::addCustomColor(GfxSeparationColorSpace
*sepCS
) {
4161 PSOutCustomColor
*cc
;
4165 if (!sepCS
->getName()->cmp("Black")) {
4166 processColors
|= psProcessBlack
;
4169 if (!sepCS
->getName()->cmp("Cyan")) {
4170 processColors
|= psProcessCyan
;
4173 if (!sepCS
->getName()->cmp("Yellow")) {
4174 processColors
|= psProcessYellow
;
4177 if (!sepCS
->getName()->cmp("Magenta")) {
4178 processColors
|= psProcessMagenta
;
4181 if (!sepCS
->getName()->cmp("All"))
4183 if (!sepCS
->getName()->cmp("None"))
4185 for (cc
= customColors
; cc
; cc
= cc
->next
) {
4186 if (!cc
->name
->cmp(sepCS
->getName())) {
4190 color
.c
[0] = gfxColorComp1
;
4191 sepCS
->getCMYK(&color
, &cmyk
);
4192 cc
= new PSOutCustomColor(colToDbl(cmyk
.c
), colToDbl(cmyk
.m
),
4193 colToDbl(cmyk
.y
), colToDbl(cmyk
.k
),
4194 sepCS
->getName()->copy());
4195 cc
->next
= customColors
;
4199 void PSOutputDev::updateFillOverprint(GfxState
*state
) {
4200 if (level
>= psLevel2
) {
4201 writePSFmt("{0:s} op\n", state
->getFillOverprint() ? "true" : "false");
4205 void PSOutputDev::updateStrokeOverprint(GfxState
*state
) {
4206 if (level
>= psLevel2
) {
4207 writePSFmt("{0:s} OP\n", state
->getStrokeOverprint() ? "true" : "false");
4211 void PSOutputDev::updateOverprintMode(GfxState
*state
) {
4212 if (level
>= psLevel3
) {
4213 writePSFmt("{0:s} opm\n", state
->getOverprintMode() ? "true" : "false");
4217 void PSOutputDev::updateTransfer(GfxState
*state
) {
4221 funcs
= state
->getTransfer();
4222 if (funcs
[0] && funcs
[1] && funcs
[2] && funcs
[3]) {
4223 if (level
>= psLevel2
) {
4224 for (i
= 0; i
< 4; ++i
) {
4225 cvtFunction(funcs
[i
]);
4227 writePS("setcolortransfer\n");
4229 cvtFunction(funcs
[3]);
4230 writePS("settransfer\n");
4232 } else if (funcs
[0]) {
4233 cvtFunction(funcs
[0]);
4234 writePS("settransfer\n");
4236 writePS("{} settransfer\n");
4240 void PSOutputDev::updateFont(GfxState
*state
) {
4241 if (state
->getFont()) {
4242 writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n",
4243 state
->getFont()->getID()->num
, state
->getFont()->getID()->gen
,
4244 fabs(state
->getFontSize()) < 0.0001 ? 0.0001
4245 : state
->getFontSize());
4249 void PSOutputDev::updateTextMat(GfxState
*state
) {
4252 mat
= state
->getTextMat();
4253 if (fabs(mat
[0] * mat
[3] - mat
[1] * mat
[2]) < 0.00001) {
4254 // avoid a singular (or close-to-singular) matrix
4255 writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat
[4], mat
[5]);
4257 writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n",
4258 mat
[0], mat
[1], mat
[2], mat
[3], mat
[4], mat
[5]);
4262 void PSOutputDev::updateCharSpace(GfxState
*state
) {
4263 writePSFmt("{0:.6g} Tc\n", state
->getCharSpace());
4266 void PSOutputDev::updateRender(GfxState
*state
) {
4269 rm
= state
->getRender();
4270 writePSFmt("{0:d} Tr\n", rm
);
4272 if (rm
!= 0 && rm
!= 3) {
4273 t3Cacheable
= gFalse
;
4277 void PSOutputDev::updateRise(GfxState
*state
) {
4278 writePSFmt("{0:.6g} Ts\n", state
->getRise());
4281 void PSOutputDev::updateWordSpace(GfxState
*state
) {
4282 writePSFmt("{0:.6g} Tw\n", state
->getWordSpace());
4285 void PSOutputDev::updateHorizScaling(GfxState
*state
) {
4288 h
= state
->getHorizScaling();
4289 if (fabs(h
) < 0.01) {
4292 writePSFmt("{0:.6g} Tz\n", h
);
4295 void PSOutputDev::updateTextPos(GfxState
*state
) {
4296 writePSFmt("{0:.6g} {1:.6g} Td\n", state
->getLineX(), state
->getLineY());
4299 void PSOutputDev::updateTextShift(GfxState
*state
, double shift
) {
4300 if (state
->getFont()->getWMode()) {
4301 writePSFmt("{0:.6g} TJmV\n", shift
);
4303 writePSFmt("{0:.6g} TJm\n", shift
);
4307 void PSOutputDev::saveTextPos(GfxState
*state
) {
4308 writePS("currentpoint\n");
4311 void PSOutputDev::restoreTextPos(GfxState
*state
) {
4315 void PSOutputDev::stroke(GfxState
*state
) {
4316 doPath(state
->getPath());
4317 if (inType3Char
&& t3FillColorOnly
) {
4318 // if we're construct a cacheable Type 3 glyph, we need to do
4319 // everything in the fill color
4326 void PSOutputDev::fill(GfxState
*state
) {
4327 doPath(state
->getPath());
4331 void PSOutputDev::eoFill(GfxState
*state
) {
4332 doPath(state
->getPath());
4336 GBool
PSOutputDev::tilingPatternFillL1(GfxState
*state
, Catalog
*cat
, Object
*str
,
4337 double *pmat
, int paintType
, int tilingType
, Dict
*resDict
,
4338 double *mat
, double *bbox
,
4339 int x0
, int y0
, int x1
, int y1
,
4340 double xStep
, double yStep
) {
4344 // define a Type 3 font
4345 writePS("8 dict begin\n");
4346 writePS("/FontType 3 def\n");
4347 writePS("/FontMatrix [1 0 0 1 0 0] def\n");
4348 writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n",
4349 bbox
[0], bbox
[1], bbox
[2], bbox
[3]);
4350 writePS("/Encoding 256 array def\n");
4351 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
4352 writePS(" Encoding 120 /x put\n");
4353 writePS("/BuildGlyph {\n");
4354 writePS(" exch /CharProcs get exch\n");
4355 writePS(" 2 copy known not { pop /.notdef } if\n");
4356 writePS(" get exec\n");
4357 writePS("} bind def\n");
4358 writePS("/BuildChar {\n");
4359 writePS(" 1 index /Encoding get exch get\n");
4360 writePS(" 1 index /BuildGlyph get exec\n");
4361 writePS("} bind def\n");
4362 writePS("/CharProcs 1 dict def\n");
4363 writePS("CharProcs begin\n");
4368 gfx
= new Gfx(doc
, this, resDict
, &box
, NULL
);
4370 if (paintType
== 2) {
4371 writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n",
4372 xStep
, bbox
[0], bbox
[1], bbox
[2], bbox
[3]);
4373 t3FillColorOnly
= gTrue
;
4377 writePS("1 0 setcharwidth\n");
4379 writePSFmt("{0:.6g} 0 setcharwidth\n", xStep
);
4381 t3FillColorOnly
= gFalse
;
4383 inType3Char
= gTrue
;
4384 if (paintType
== 2) {
4385 inUncoloredPattern
= gTrue
;
4386 // ensure any PS procedures that contain sCol or fCol do not change the color
4387 writePS("/pdfLastFill true def\n");
4388 writePS("/pdfLastStroke true def\n");
4390 ++numTilingPatterns
;
4392 --numTilingPatterns
;
4393 if (paintType
== 2) {
4394 inUncoloredPattern
= gFalse
;
4395 // ensure the next PS procedures that uses sCol or fCol will update the color
4396 writePS("/pdfLastFill false def\n");
4397 writePS("/pdfLastStroke false def\n");
4399 inType3Char
= gFalse
;
4403 writePS("currentdict end\n");
4404 writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns
);
4407 writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns
);
4409 writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n",
4410 mat
[0], mat
[1], mat
[2], mat
[3], mat
[4], mat
[5]);
4411 writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n",
4412 y0
, y1
- 1, x0
* xStep
, yStep
, x0
, x1
- 1);
4413 writePS("grestore\n");
4418 GBool
PSOutputDev::tilingPatternFillL2(GfxState
*state
, Catalog
*cat
, Object
*str
,
4419 double *pmat
, int paintType
, int tilingType
, Dict
*resDict
,
4420 double *mat
, double *bbox
,
4421 int x0
, int y0
, int x1
, int y1
,
4422 double xStep
, double yStep
) {
4426 if (paintType
== 2) {
4427 // setpattern with PaintType 2 needs the paint color
4428 writePS("currentcolor\n");
4430 writePS("<<\n /PatternType 1\n");
4431 writePSFmt(" /PaintType {0:d}\n", paintType
);
4432 writePSFmt(" /TilingType {0:d}\n", tilingType
);
4433 writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n", bbox
[0], bbox
[1], bbox
[2], bbox
[3]);
4434 writePSFmt(" /XStep {0:.6g}\n", xStep
);
4435 writePSFmt(" /YStep {0:.6g}\n", yStep
);
4436 writePS(" /PaintProc { \n");
4441 gfx
= new Gfx(doc
, this, resDict
, &box
, NULL
);
4442 inType3Char
= gTrue
;
4443 if (paintType
== 2) {
4444 inUncoloredPattern
= gTrue
;
4445 // ensure any PS procedures that contain sCol or fCol do not change the color
4446 writePS("/pdfLastFill true def\n");
4447 writePS("/pdfLastStroke true def\n");
4450 if (paintType
== 2) {
4451 inUncoloredPattern
= gFalse
;
4452 // ensure the next PS procedures that uses sCol or fCol will update the color
4453 writePS("/pdfLastFill false def\n");
4454 writePS("/pdfLastStroke false def\n");
4456 inType3Char
= gFalse
;
4460 writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}]\n", mat
[0], mat
[1], mat
[2], mat
[3], mat
[4], mat
[5]);
4461 writePS("makepattern setpattern\n");
4462 writePS("clippath fill\n"); // Gfx sets up a clip before calling out->tilingPatternFill()
4467 GBool
PSOutputDev::tilingPatternFill(GfxState
*state
, Gfx
*gfxA
, Catalog
*cat
, Object
*str
,
4468 double *pmat
, int paintType
, int tilingType
, Dict
*resDict
,
4469 double *mat
, double *bbox
,
4470 int x0
, int y0
, int x1
, int y1
,
4471 double xStep
, double yStep
) {
4472 if (x1
- x0
== 1 && y1
- y0
== 1) {
4473 // Don't need to use patterns if only one instance of the pattern is used
4476 double x
, y
, tx
, ty
;
4480 tx
= x
* mat
[0] + y
* mat
[2] + mat
[4];
4481 ty
= x
* mat
[1] + y
* mat
[3] + mat
[5];
4486 gfx
= new Gfx(doc
, this, resDict
, &box
, NULL
, NULL
, NULL
, gfxA
->getXRef());
4487 writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", mat
[0], mat
[1], mat
[2], mat
[3], tx
, ty
);
4488 inType3Char
= gTrue
;
4490 inType3Char
= gFalse
;
4495 if (level
== psLevel1
|| level
== psLevel1Sep
) {
4496 return tilingPatternFillL1(state
, cat
, str
, pmat
, paintType
, tilingType
, resDict
,
4497 mat
, bbox
, x0
, y0
, x1
, y1
, xStep
, yStep
);
4499 return tilingPatternFillL2(state
, cat
, str
, pmat
, paintType
, tilingType
, resDict
,
4500 mat
, bbox
, x0
, y0
, x1
, y1
, xStep
, yStep
);
4504 GBool
PSOutputDev::functionShadedFill(GfxState
*state
,
4505 GfxFunctionShading
*shading
) {
4506 double x0
, y0
, x1
, y1
;
4510 if (level
== psLevel2Sep
|| level
== psLevel3Sep
) {
4511 if (shading
->getColorSpace()->getMode() != csDeviceCMYK
) {
4514 processColors
|= psProcessCMYK
;
4517 shading
->getDomain(&x0
, &y0
, &x1
, &y1
);
4518 mat
= shading
->getMatrix();
4519 writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n",
4520 mat
[0], mat
[1], mat
[2], mat
[3], mat
[4], mat
[5]);
4521 writePSFmt("/n {0:d} def\n", shading
->getColorSpace()->getNComps());
4522 if (shading
->getNFuncs() == 1) {
4524 cvtFunction(shading
->getFunc(0));
4527 writePS("/func {\n");
4528 for (i
= 0; i
< shading
->getNFuncs(); ++i
) {
4529 if (i
< shading
->getNFuncs() - 1) {
4530 writePS("2 copy\n");
4532 cvtFunction(shading
->getFunc(i
));
4534 if (i
< shading
->getNFuncs() - 1) {
4535 writePS("3 1 roll\n");
4540 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0
, y0
, x1
, y1
);
4545 GBool
PSOutputDev::axialShadedFill(GfxState
*state
, GfxAxialShading
*shading
, double /*tMin*/, double /*tMax*/) {
4546 double xMin
, yMin
, xMax
, yMax
;
4547 double x0
, y0
, x1
, y1
, dx
, dy
, mul
;
4548 double tMin
, tMax
, t
, t0
, t1
;
4551 if (level
== psLevel2Sep
|| level
== psLevel3Sep
) {
4552 if (shading
->getColorSpace()->getMode() != csDeviceCMYK
) {
4555 processColors
|= psProcessCMYK
;
4558 // get the clip region bbox
4559 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
4561 // compute min and max t values, based on the four corners of the
4563 shading
->getCoords(&x0
, &y0
, &x1
, &y1
);
4566 if (fabs(dx
) < 0.01 && fabs(dy
) < 0.01) {
4569 mul
= 1 / (dx
* dx
+ dy
* dy
);
4570 tMin
= tMax
= ((xMin
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
4571 t
= ((xMin
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
4574 } else if (t
> tMax
) {
4577 t
= ((xMax
- x0
) * dx
+ (yMin
- y0
) * dy
) * mul
;
4580 } else if (t
> tMax
) {
4583 t
= ((xMax
- x0
) * dx
+ (yMax
- y0
) * dy
) * mul
;
4586 } else if (t
> tMax
) {
4589 if (tMin
< 0 && !shading
->getExtend0()) {
4592 if (tMax
> 1 && !shading
->getExtend1()) {
4597 // get the function domain
4598 t0
= shading
->getDomain0();
4599 t1
= shading
->getDomain1();
4601 // generate the PS code
4602 writePSFmt("/t0 {0:.6g} def\n", t0
);
4603 writePSFmt("/t1 {0:.6g} def\n", t1
);
4604 writePSFmt("/dt {0:.6g} def\n", t1
- t0
);
4605 writePSFmt("/x0 {0:.6g} def\n", x0
);
4606 writePSFmt("/y0 {0:.6g} def\n", y0
);
4607 writePSFmt("/dx {0:.6g} def\n", x1
- x0
);
4608 writePSFmt("/x1 {0:.6g} def\n", x1
);
4609 writePSFmt("/y1 {0:.6g} def\n", y1
);
4610 writePSFmt("/dy {0:.6g} def\n", y1
- y0
);
4611 writePSFmt("/xMin {0:.6g} def\n", xMin
);
4612 writePSFmt("/yMin {0:.6g} def\n", yMin
);
4613 writePSFmt("/xMax {0:.6g} def\n", xMax
);
4614 writePSFmt("/yMax {0:.6g} def\n", yMax
);
4615 writePSFmt("/n {0:d} def\n", shading
->getColorSpace()->getNComps());
4616 if (shading
->getNFuncs() == 1) {
4618 cvtFunction(shading
->getFunc(0));
4621 writePS("/func {\n");
4622 for (i
= 0; i
< shading
->getNFuncs(); ++i
) {
4623 if (i
< shading
->getNFuncs() - 1) {
4626 cvtFunction(shading
->getFunc(i
));
4628 if (i
< shading
->getNFuncs() - 1) {
4634 writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin
, tMax
);
4639 GBool
PSOutputDev::radialShadedFill(GfxState
*state
, GfxRadialShading
*shading
, double /*sMin*/, double /*sMax*/) {
4640 double xMin
, yMin
, xMax
, yMax
;
4641 double x0
, y0
, r0
, x1
, y1
, r1
, t0
, t1
;
4643 double sMin
, sMax
, h
, ta
;
4644 double sLeft
, sRight
, sTop
, sBottom
, sZero
, sDiag
;
4645 GBool haveSLeft
, haveSRight
, haveSTop
, haveSBottom
, haveSZero
;
4646 GBool haveSMin
, haveSMax
;
4647 double theta
, alpha
, a1
, a2
;
4651 if (level
== psLevel2Sep
|| level
== psLevel3Sep
) {
4652 if (shading
->getColorSpace()->getMode() != csDeviceCMYK
) {
4655 processColors
|= psProcessCMYK
;
4658 // get the shading info
4659 shading
->getCoords(&x0
, &y0
, &r0
, &x1
, &y1
, &r1
);
4660 t0
= shading
->getDomain0();
4661 t1
= shading
->getDomain1();
4663 // Compute the point at which r(s) = 0; check for the enclosed
4664 // circles case; and compute the angles for the tangent lines.
4665 // Compute the point at which r(s) = 0; check for the enclosed
4666 // circles case; and compute the angles for the tangent lines.
4667 h
= sqrt((x1
- x0
) * (x1
- x0
) + (y1
- y0
) * (y1
- y0
));
4670 theta
= 0; // make gcc happy
4671 } else if (r1
- r0
== 0) {
4674 } else if (fabs(r1
- r0
) >= h
) {
4676 theta
= 0; // make gcc happy
4679 theta
= asin((r1
- r0
) / h
);
4685 alpha
= atan2(y1
- y0
, x1
- x0
);
4686 a1
= (180 / M_PI
) * (alpha
+ theta
) + 90;
4687 a2
= (180 / M_PI
) * (alpha
- theta
) - 90;
4693 // compute the (possibly extended) s range
4694 state
->getUserClipBBox(&xMin
, &yMin
, &xMax
, &yMax
);
4699 // solve x(sLeft) + r(sLeft) = xMin
4700 if ((haveSLeft
= fabs((x1
+ r1
) - (x0
+ r0
)) > 0.000001)) {
4701 sLeft
= (xMin
- (x0
+ r0
)) / ((x1
+ r1
) - (x0
+ r0
));
4703 sLeft
= 0; // make gcc happy
4705 // solve x(sRight) - r(sRight) = xMax
4706 if ((haveSRight
= fabs((x1
- r1
) - (x0
- r0
)) > 0.000001)) {
4707 sRight
= (xMax
- (x0
- r0
)) / ((x1
- r1
) - (x0
- r0
));
4709 sRight
= 0; // make gcc happy
4711 // solve y(sBottom) + r(sBottom) = yMin
4712 if ((haveSBottom
= fabs((y1
+ r1
) - (y0
+ r0
)) > 0.000001)) {
4713 sBottom
= (yMin
- (y0
+ r0
)) / ((y1
+ r1
) - (y0
+ r0
));
4715 sBottom
= 0; // make gcc happy
4717 // solve y(sTop) - r(sTop) = yMax
4718 if ((haveSTop
= fabs((y1
- r1
) - (y0
- r0
)) > 0.000001)) {
4719 sTop
= (yMax
- (y0
- r0
)) / ((y1
- r1
) - (y0
- r0
));
4721 sTop
= 0; // make gcc happy
4723 // solve r(sZero) = 0
4724 if ((haveSZero
= fabs(r1
- r0
) > 0.000001)) {
4725 sZero
= -r0
/ (r1
- r0
);
4727 sZero
= 0; // make gcc happy
4729 // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2)
4731 sDiag
= (sqrt((xMax
- xMin
) * (xMax
- xMin
) +
4732 (yMax
- yMin
) * (yMax
- yMin
)) - r0
) / (r1
- r0
);
4734 sDiag
= 0; // make gcc happy
4737 if (shading
->getExtend0()) {
4740 if (x0
< x1
&& haveSLeft
&& sLeft
< 0) {
4743 } else if (x0
> x1
&& haveSRight
&& sRight
< 0) {
4747 if (y0
< y1
&& haveSBottom
&& sBottom
< 0) {
4748 if (!haveSMin
|| sBottom
> sMin
) {
4752 } else if (y0
> y1
&& haveSTop
&& sTop
< 0) {
4753 if (!haveSMin
|| sTop
> sMin
) {
4758 if (haveSZero
&& sZero
< 0) {
4759 if (!haveSMin
|| sZero
> sMin
) {
4767 if (shading
->getExtend1()) {
4770 if (x1
< x0
&& haveSLeft
&& sLeft
> 1) {
4773 } else if (x1
> x0
&& haveSRight
&& sRight
> 1) {
4777 if (y1
< y0
&& haveSBottom
&& sBottom
> 1) {
4778 if (!haveSMax
|| sBottom
< sMax
) {
4782 } else if (y1
> y0
&& haveSTop
&& sTop
> 1) {
4783 if (!haveSMax
|| sTop
< sMax
) {
4788 if (haveSZero
&& sDiag
> 1) {
4789 if (!haveSMax
|| sDiag
< sMax
) {
4798 // generate the PS code
4799 writePSFmt("/x0 {0:.6g} def\n", x0
);
4800 writePSFmt("/x1 {0:.6g} def\n", x1
);
4801 writePSFmt("/dx {0:.6g} def\n", x1
- x0
);
4802 writePSFmt("/y0 {0:.6g} def\n", y0
);
4803 writePSFmt("/y1 {0:.6g} def\n", y1
);
4804 writePSFmt("/dy {0:.6g} def\n", y1
- y0
);
4805 writePSFmt("/r0 {0:.6g} def\n", r0
);
4806 writePSFmt("/r1 {0:.6g} def\n", r1
);
4807 writePSFmt("/dr {0:.6g} def\n", r1
- r0
);
4808 writePSFmt("/t0 {0:.6g} def\n", t0
);
4809 writePSFmt("/t1 {0:.6g} def\n", t1
);
4810 writePSFmt("/dt {0:.6g} def\n", t1
- t0
);
4811 writePSFmt("/n {0:d} def\n", shading
->getColorSpace()->getNComps());
4812 writePSFmt("/encl {0:s} def\n", enclosed
? "true" : "false");
4813 writePSFmt("/a1 {0:.6g} def\n", a1
);
4814 writePSFmt("/a2 {0:.6g} def\n", a2
);
4815 if (shading
->getNFuncs() == 1) {
4817 cvtFunction(shading
->getFunc(0));
4820 writePS("/func {\n");
4821 for (i
= 0; i
< shading
->getNFuncs(); ++i
) {
4822 if (i
< shading
->getNFuncs() - 1) {
4825 cvtFunction(shading
->getFunc(i
));
4827 if (i
< shading
->getNFuncs() - 1) {
4833 writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin
, sMax
);
4835 // extend the 'enclosed' case
4837 // extend the smaller circle
4838 if ((shading
->getExtend0() && r0
<= r1
) ||
4839 (shading
->getExtend1() && r1
< r0
)) {
4851 if (level
== psLevel2Sep
|| level
== psLevel3Sep
) {
4852 writePSFmt("{0:.6g} radialCol aload pop k\n", ta
);
4854 writePSFmt("{0:.6g} radialCol sc\n", ta
);
4856 writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa
, ya
, ra
);
4859 // extend the larger circle
4860 if ((shading
->getExtend0() && r0
> r1
) ||
4861 (shading
->getExtend1() && r1
>= r0
)) {
4873 if (level
== psLevel2Sep
|| level
== psLevel3Sep
) {
4874 writePSFmt("{0:.6g} radialCol aload pop k\n", ta
);
4876 writePSFmt("{0:.6g} radialCol sc\n", ta
);
4878 writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa
, ya
, ra
);
4879 writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n",
4880 xMin
, yMin
, xMin
, yMax
, xMax
, yMax
, xMax
, yMin
);
4887 void PSOutputDev::clip(GfxState
*state
) {
4888 doPath(state
->getPath());
4892 void PSOutputDev::eoClip(GfxState
*state
) {
4893 doPath(state
->getPath());
4897 void PSOutputDev::clipToStrokePath(GfxState
*state
) {
4898 doPath(state
->getPath());
4902 void PSOutputDev::doPath(GfxPath
*path
) {
4903 GfxSubpath
*subpath
;
4904 double x0
, y0
, x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
4907 n
= path
->getNumSubpaths();
4909 if (n
== 1 && path
->getSubpath(0)->getNumPoints() == 5) {
4910 subpath
= path
->getSubpath(0);
4911 x0
= subpath
->getX(0);
4912 y0
= subpath
->getY(0);
4913 x4
= subpath
->getX(4);
4914 y4
= subpath
->getY(4);
4915 if (x4
== x0
&& y4
== y0
) {
4916 x1
= subpath
->getX(1);
4917 y1
= subpath
->getY(1);
4918 x2
= subpath
->getX(2);
4919 y2
= subpath
->getY(2);
4920 x3
= subpath
->getX(3);
4921 y3
= subpath
->getY(3);
4922 if (x0
== x1
&& x2
== x3
&& y0
== y3
&& y1
== y2
) {
4923 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
4924 x0
< x2
? x0
: x2
, y0
< y1
? y0
: y1
,
4925 fabs(x2
- x0
), fabs(y1
- y0
));
4927 } else if (x0
== x3
&& x1
== x2
&& y0
== y1
&& y2
== y3
) {
4928 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
4929 x0
< x1
? x0
: x1
, y0
< y2
? y0
: y2
,
4930 fabs(x1
- x0
), fabs(y2
- y0
));
4936 for (i
= 0; i
< n
; ++i
) {
4937 subpath
= path
->getSubpath(i
);
4938 m
= subpath
->getNumPoints();
4939 writePSFmt("{0:.6g} {1:.6g} m\n", subpath
->getX(0), subpath
->getY(0));
4942 if (subpath
->getCurve(j
)) {
4943 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n",
4944 subpath
->getX(j
), subpath
->getY(j
),
4945 subpath
->getX(j
+1), subpath
->getY(j
+1),
4946 subpath
->getX(j
+2), subpath
->getY(j
+2));
4949 writePSFmt("{0:.6g} {1:.6g} l\n", subpath
->getX(j
), subpath
->getY(j
));
4953 if (subpath
->isClosed()) {
4959 void PSOutputDev::drawString(GfxState
*state
, GooString
*s
) {
4964 double dx
, dy
, originX
, originY
;
4971 int dxdySize
, len
, nChars
, uLen
, n
, m
, i
, j
;
4973 // for pdftohtml, output PS without text
4974 if( displayText
== gFalse
)
4977 // check for invisible text -- this is used by Acrobat Capture
4978 if (state
->getRender() == 3) {
4982 // ignore empty strings
4983 if (s
->getLength() == 0) {
4988 if (!(font
= state
->getFont())) {
4991 wMode
= font
->getWMode();
4993 // check for a subtitute 16-bit font
4996 if (font
->isCIDFont()) {
4997 for (i
= 0; i
< font16EncLen
; ++i
) {
4998 if (font
->getID()->num
== font16Enc
[i
].fontID
.num
&&
4999 font
->getID()->gen
== font16Enc
[i
].fontID
.gen
) {
5000 if (!font16Enc
[i
].enc
) {
5001 // font substitution failed, so don't output any text
5004 uMap
= globalParams
->getUnicodeMap(font16Enc
[i
].enc
);
5009 // check for a code-to-GID map
5011 for (i
= 0; i
< font8InfoLen
; ++i
) {
5012 if (font
->getID()->num
== font8Info
[i
].fontID
.num
&&
5013 font
->getID()->gen
== font8Info
[i
].fontID
.gen
) {
5014 codeToGID
= font8Info
[i
].codeToGID
;
5020 // compute the positioning (dx, dy) for each char in the string
5022 p
= s
->getCString();
5023 len
= s
->getLength();
5024 s2
= new GooString();
5025 dxdySize
= font
->isCIDFont() ? 8 : s
->getLength();
5026 dxdy
= (double *)gmallocn(2 * dxdySize
, sizeof(double));
5028 n
= font
->getNextChar(p
, len
, &code
,
5030 &dx
, &dy
, &originX
, &originY
);
5031 dx
*= state
->getFontSize();
5032 dy
*= state
->getFontSize();
5034 dy
+= state
->getCharSpace();
5035 if (n
== 1 && *p
== ' ') {
5036 dy
+= state
->getWordSpace();
5039 dx
+= state
->getCharSpace();
5040 if (n
== 1 && *p
== ' ') {
5041 dx
+= state
->getWordSpace();
5044 dx
*= state
->getHorizScaling();
5045 if (font
->isCIDFont()) {
5047 if (nChars
+ uLen
> dxdySize
) {
5050 } while (nChars
+ uLen
> dxdySize
);
5051 dxdy
= (double *)greallocn(dxdy
, 2 * dxdySize
, sizeof(double));
5053 for (i
= 0; i
< uLen
; ++i
) {
5054 m
= uMap
->mapUnicode(u
[i
], buf
, (int)sizeof(buf
));
5055 for (j
= 0; j
< m
; ++j
) {
5058 //~ this really needs to get the number of chars in the target
5059 //~ encoding - which may be more than the number of Unicode
5061 dxdy
[2 * nChars
] = dx
;
5062 dxdy
[2 * nChars
+ 1] = dy
;
5066 if (nChars
+ 1 > dxdySize
) {
5068 dxdy
= (double *)greallocn(dxdy
, 2 * dxdySize
, sizeof(double));
5070 s2
->append((char)((code
>> 8) & 0xff));
5071 s2
->append((char)(code
& 0xff));
5072 dxdy
[2 * nChars
] = dx
;
5073 dxdy
[2 * nChars
+ 1] = dy
;
5077 if (!codeToGID
|| codeToGID
[code
] >= 0) {
5078 s2
->append((char)code
);
5079 dxdy
[2 * nChars
] = dx
;
5080 dxdy
[2 * nChars
+ 1] = dy
;
5094 for (i
= 0; i
< 2 * nChars
; ++i
) {
5098 writePSFmt("{0:.6g}", dxdy
[i
]);
5105 if (state
->getRender() & 4) {
5106 haveTextClip
= gTrue
;
5110 void PSOutputDev::beginTextObject(GfxState
*state
) {
5113 void PSOutputDev::endTextObject(GfxState
*state
) {
5116 haveTextClip
= gFalse
;
5120 void PSOutputDev::drawImageMask(GfxState
*state
, Object
*ref
, Stream
*str
,
5121 int width
, int height
, GBool invert
,
5122 GBool interpolate
, GBool inlineImg
) {
5125 len
= height
* ((width
+ 7) / 8);
5129 doImageL1(ref
, NULL
, invert
, inlineImg
, str
, width
, height
, len
,
5130 NULL
, NULL
, 0, 0, gFalse
);
5134 doImageL2(ref
, NULL
, invert
, inlineImg
, str
, width
, height
, len
,
5135 NULL
, NULL
, 0, 0, gFalse
);
5139 doImageL3(ref
, NULL
, invert
, inlineImg
, str
, width
, height
, len
,
5140 NULL
, NULL
, 0, 0, gFalse
);
5145 void PSOutputDev::setSoftMaskFromImageMask(GfxState
*state
, Object
*ref
, Stream
*str
,
5146 int width
, int height
, GBool invert
,
5147 GBool inlineImg
, double *baseMatrix
) {
5148 if (level
!= psLevel1
&& level
!= psLevel1Sep
) {
5149 maskToClippingPath(str
, width
, height
, invert
);
5153 void PSOutputDev::unsetSoftMaskFromImageMask(GfxState
* state
, double *baseMatrix
) {
5154 if (level
!= psLevel1
&& level
!= psLevel1Sep
) {
5155 writePS("pdfImClipEnd\n");
5159 void PSOutputDev::drawImage(GfxState
*state
, Object
*ref
, Stream
*str
,
5160 int width
, int height
, GfxImageColorMap
*colorMap
,
5161 GBool interpolate
, int *maskColors
, GBool inlineImg
) {
5164 len
= height
* ((width
* colorMap
->getNumPixelComps() *
5165 colorMap
->getBits() + 7) / 8);
5168 doImageL1(ref
, colorMap
, gFalse
, inlineImg
, str
,
5169 width
, height
, len
, maskColors
, NULL
, 0, 0, gFalse
);
5172 //~ handle indexed, separation, ... color spaces
5173 doImageL1Sep(ref
, colorMap
, gFalse
, inlineImg
, str
,
5174 width
, height
, len
, maskColors
, NULL
, 0, 0, gFalse
);
5178 doImageL2(ref
, colorMap
, gFalse
, inlineImg
, str
,
5179 width
, height
, len
, maskColors
, NULL
, 0, 0, gFalse
);
5183 doImageL3(ref
, colorMap
, gFalse
, inlineImg
, str
,
5184 width
, height
, len
, maskColors
, NULL
, 0, 0, gFalse
);
5187 t3Cacheable
= gFalse
;
5190 void PSOutputDev::drawMaskedImage(GfxState
*state
, Object
*ref
, Stream
*str
,
5191 int width
, int height
,
5192 GfxImageColorMap
*colorMap
,
5195 int maskWidth
, int maskHeight
,
5196 GBool maskInvert
, GBool maskInterpolate
) {
5199 len
= height
* ((width
* colorMap
->getNumPixelComps() *
5200 colorMap
->getBits() + 7) / 8);
5203 doImageL1(ref
, colorMap
, gFalse
, gFalse
, str
, width
, height
, len
,
5204 NULL
, maskStr
, maskWidth
, maskHeight
, maskInvert
);
5207 //~ handle indexed, separation, ... color spaces
5208 doImageL1Sep(ref
, colorMap
, gFalse
, gFalse
, str
, width
, height
, len
,
5209 NULL
, maskStr
, maskWidth
, maskHeight
, maskInvert
);
5213 doImageL2(ref
, colorMap
, gFalse
, gFalse
, str
, width
, height
, len
,
5214 NULL
, maskStr
, maskWidth
, maskHeight
, maskInvert
);
5218 doImageL3(ref
, colorMap
, gFalse
, gFalse
, str
, width
, height
, len
,
5219 NULL
, maskStr
, maskWidth
, maskHeight
, maskInvert
);
5222 t3Cacheable
= gFalse
;
5225 void PSOutputDev::doImageL1(Object
*ref
, GfxImageColorMap
*colorMap
,
5226 GBool invert
, GBool inlineImg
,
5227 Stream
*str
, int width
, int height
, int len
,
5228 int *maskColors
, Stream
*maskStr
,
5229 int maskWidth
, int maskHeight
, GBool maskInvert
) {
5230 ImageStream
*imgStr
;
5231 Guchar pixBuf
[gfxColorMaxComps
];
5233 int col
, x
, y
, c
, i
;
5234 char hexBuf
[32*2 + 2]; // 32 values X 2 chars/value + line ending + null
5235 Guchar digit
, grayValue
;
5238 if (maskStr
&& !(maskColors
&& colorMap
)) {
5239 maskToClippingPath(maskStr
, maskWidth
, maskHeight
, maskInvert
);
5242 if ((inType3Char
|| preloadImagesForms
) && !colorMap
) {
5245 str
= new FixedLengthEncoder(str
, len
);
5246 str
= new ASCIIHexEncoder(str
);
5253 } while (c
== '\n' || c
== '\r');
5254 if (c
== '>' || c
== EOF
) {
5259 // each line is: "<...data...><eol>"
5260 // so max data length = 255 - 4 = 251
5261 // but make it 240 just to be safe
5262 // chunks are 2 bytes each, so we need to stop on an even col number
5267 } while (c
!= '>' && c
!= EOF
);
5273 // make sure the image is setup, it sometimes is not like on bug #17645
5274 setupImage(ref
->getRef(), str
, gFalse
);
5275 // set up to use the array already created by setupImages()
5276 writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref
->getRefNum(), ref
->getRefGen());
5280 // image/imagemask command
5281 if ((inType3Char
|| preloadImagesForms
) && !colorMap
) {
5282 writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n",
5283 width
, height
, invert
? "true" : "false",
5284 width
, -height
, height
);
5285 } else if (colorMap
) {
5286 writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}\n",
5288 width
, -height
, height
,
5289 useBinary
? "Bin" : "");
5291 writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1{6:s}\n",
5292 width
, height
, invert
? "true" : "false",
5293 width
, -height
, height
,
5294 useBinary
? "Bin" : "");
5298 if (!((inType3Char
|| preloadImagesForms
) && !colorMap
)) {
5302 // set up to process the data stream
5303 imgStr
= new ImageStream(str
, width
, colorMap
->getNumPixelComps(),
5304 colorMap
->getBits());
5307 // process the data stream
5309 for (y
= 0; y
< height
; ++y
) {
5312 for (x
= 0; x
< width
; ++x
) {
5313 imgStr
->getPixel(pixBuf
);
5314 colorMap
->getGray(pixBuf
, &gray
);
5315 grayValue
= colToByte(gray
);
5317 hexBuf
[i
++] = grayValue
;
5319 digit
= grayValue
/ 16;
5320 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
5321 digit
= grayValue
% 16;
5322 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
5328 writePSBuf(hexBuf
, i
);
5337 writePSBuf(hexBuf
, i
);
5346 for (y
= 0; y
< height
; ++y
) {
5347 for (x
= 0; x
< width
; x
+= 8) {
5348 grayValue
= str
->getChar();
5350 hexBuf
[i
++] = grayValue
;
5352 digit
= grayValue
/ 16;
5353 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
5354 digit
= grayValue
% 16;
5355 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a' - 10: '0');
5361 writePSBuf(hexBuf
, i
);
5370 writePSBuf(hexBuf
, i
);
5376 if (maskStr
&& !(maskColors
&& colorMap
)) {
5377 writePS("pdfImClipEnd\n");
5381 void PSOutputDev::doImageL1Sep(Object
*ref
, GfxImageColorMap
*colorMap
,
5382 GBool invert
, GBool inlineImg
,
5383 Stream
*str
, int width
, int height
, int len
,
5384 int *maskColors
, Stream
*maskStr
,
5385 int maskWidth
, int maskHeight
, GBool maskInvert
) {
5386 ImageStream
*imgStr
;
5388 Guchar pixBuf
[gfxColorMaxComps
];
5391 GBool checkProcessColor
;
5392 char hexBuf
[32*2 + 2]; // 32 values X 2 chars/value + line ending + null
5396 if (maskStr
&& !(maskColors
&& colorMap
)) {
5397 maskToClippingPath(maskStr
, maskWidth
, maskHeight
, maskInvert
);
5400 // width, height, matrix, bits per component
5401 writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep{5:s}\n",
5403 width
, -height
, height
,
5404 useBinary
? "Bin" : "");
5406 // allocate a line buffer
5407 lineBuf
= (Guchar
*)gmallocn(width
, 4);
5409 // set up to process the data stream
5410 imgStr
= new ImageStream(str
, width
, colorMap
->getNumPixelComps(),
5411 colorMap
->getBits());
5414 // process the data stream
5415 checkProcessColor
= gTrue
;
5417 for (y
= 0; y
< height
; ++y
) {
5420 if (checkProcessColor
) {
5421 checkProcessColor
= (((psProcessCyan
| psProcessMagenta
| psProcessYellow
| psProcessBlack
) & ~processColors
) != 0);
5423 if (checkProcessColor
) {
5424 for (x
= 0; x
< width
; ++x
) {
5425 imgStr
->getPixel(pixBuf
);
5426 colorMap
->getCMYK(pixBuf
, &cmyk
);
5427 lineBuf
[4*x
+0] = colToByte(cmyk
.c
);
5428 lineBuf
[4*x
+1] = colToByte(cmyk
.m
);
5429 lineBuf
[4*x
+2] = colToByte(cmyk
.y
);
5430 lineBuf
[4*x
+3] = colToByte(cmyk
.k
);
5431 addProcessColor(colToDbl(cmyk
.c
), colToDbl(cmyk
.m
),
5432 colToDbl(cmyk
.y
), colToDbl(cmyk
.k
));
5435 for (x
= 0; x
< width
; ++x
) {
5436 imgStr
->getPixel(pixBuf
);
5437 colorMap
->getCMYK(pixBuf
, &cmyk
);
5438 lineBuf
[4*x
+0] = colToByte(cmyk
.c
);
5439 lineBuf
[4*x
+1] = colToByte(cmyk
.m
);
5440 lineBuf
[4*x
+2] = colToByte(cmyk
.y
);
5441 lineBuf
[4*x
+3] = colToByte(cmyk
.k
);
5445 // write one line of each color component
5447 for (comp
= 0; comp
< 4; ++comp
) {
5448 for (x
= 0; x
< width
; ++x
) {
5449 hexBuf
[i
++] = lineBuf
[4*x
+ comp
];
5451 writePSBuf(hexBuf
, i
);
5457 for (comp
= 0; comp
< 4; ++comp
) {
5458 for (x
= 0; x
< width
; ++x
) {
5459 digit
= lineBuf
[4*x
+ comp
] / 16;
5460 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a'-10: '0');
5461 digit
= lineBuf
[4*x
+ comp
] % 16;
5462 hexBuf
[i
++] = digit
+ ((digit
>= 10)? 'a'-10: '0');
5465 writePSBuf(hexBuf
, i
);
5477 writePSBuf(hexBuf
, i
);
5484 if (maskStr
&& !(maskColors
&& colorMap
)) {
5485 writePS("pdfImClipEnd\n");
5489 void PSOutputDev::maskToClippingPath(Stream
*maskStr
, int maskWidth
, int maskHeight
, GBool maskInvert
) {
5490 ImageStream
*imgStr
;
5492 PSOutImgClipRect
*rects0
, *rects1
, *rectsTmp
, *rectsOut
;
5493 int rects0Len
, rects1Len
, rectsSize
, rectsOutLen
, rectsOutSize
;
5494 GBool emitRect
, addRect
, extendRect
;
5495 int i
, x0
, x1
, y
, maskXor
;
5497 imgStr
= new ImageStream(maskStr
, maskWidth
, 1, 1);
5499 rects0Len
= rects1Len
= rectsOutLen
= 0;
5500 rectsSize
= rectsOutSize
= 64;
5501 rects0
= (PSOutImgClipRect
*)gmallocn(rectsSize
, sizeof(PSOutImgClipRect
));
5502 rects1
= (PSOutImgClipRect
*)gmallocn(rectsSize
, sizeof(PSOutImgClipRect
));
5503 rectsOut
= (PSOutImgClipRect
*)gmallocn(rectsOutSize
, sizeof(PSOutImgClipRect
));
5504 maskXor
= maskInvert
? 1 : 0;
5505 for (y
= 0; y
< maskHeight
; ++y
) {
5506 if (!(line
= imgStr
->getLine())) {
5511 for (x0
= 0; x0
< maskWidth
&& (line
[x0
] ^ maskXor
); ++x0
) ;
5512 for (x1
= x0
; x1
< maskWidth
&& !(line
[x1
] ^ maskXor
); ++x1
) ;
5513 while (x0
< maskWidth
|| i
< rects0Len
) {
5514 emitRect
= addRect
= extendRect
= gFalse
;
5515 if (x0
>= maskWidth
) {
5517 } else if (i
>= rects0Len
) {
5519 } else if (rects0
[i
].x0
< x0
) {
5521 } else if (x0
< rects0
[i
].x0
) {
5523 } else if (rects0
[i
].x1
== x1
) {
5526 emitRect
= addRect
= gTrue
;
5529 if (rectsOutLen
== rectsOutSize
) {
5531 rectsOut
= (PSOutImgClipRect
*)greallocn(rectsOut
, rectsOutSize
, sizeof(PSOutImgClipRect
));
5533 rectsOut
[rectsOutLen
].x0
= rects0
[i
].x0
;
5534 rectsOut
[rectsOutLen
].x1
= rects0
[i
].x1
;
5535 rectsOut
[rectsOutLen
].y0
= maskHeight
- y
- 1;
5536 rectsOut
[rectsOutLen
].y1
= maskHeight
- rects0
[i
].y0
- 1;
5540 if (addRect
|| extendRect
) {
5541 if (rects1Len
== rectsSize
) {
5543 rects0
= (PSOutImgClipRect
*)greallocn(rects0
, rectsSize
, sizeof(PSOutImgClipRect
));
5544 rects1
= (PSOutImgClipRect
*)greallocn(rects1
, rectsSize
, sizeof(PSOutImgClipRect
));
5546 rects1
[rects1Len
].x0
= x0
;
5547 rects1
[rects1Len
].x1
= x1
;
5549 rects1
[rects1Len
].y0
= y
;
5552 rects1
[rects1Len
].y0
= rects0
[i
].y0
;
5556 for (x0
= x1
; x0
< maskWidth
&& (line
[x0
] ^ maskXor
); ++x0
) ;
5557 for (x1
= x0
; x1
< maskWidth
&& !(line
[x1
] ^ maskXor
); ++x1
) ;
5564 rects0Len
= rects1Len
;
5567 for (i
= 0; i
< rects0Len
; ++i
) {
5568 if (rectsOutLen
== rectsOutSize
) {
5570 rectsOut
= (PSOutImgClipRect
*)greallocn(rectsOut
, rectsOutSize
, sizeof(PSOutImgClipRect
));
5572 rectsOut
[rectsOutLen
].x0
= rects0
[i
].x0
;
5573 rectsOut
[rectsOutLen
].x1
= rects0
[i
].x1
;
5574 rectsOut
[rectsOutLen
].y0
= maskHeight
- y
- 1;
5575 rectsOut
[rectsOutLen
].y1
= maskHeight
- rects0
[i
].y0
- 1;
5578 if (rectsOutLen
< 65536/4) {
5579 writePSFmt("{0:d} array 0\n", rectsOutLen
* 4);
5580 for (i
= 0; i
< rectsOutLen
; ++i
) {
5581 writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
5582 rectsOut
[i
].x0
, rectsOut
[i
].y0
,
5583 rectsOut
[i
].x1
- rectsOut
[i
].x0
,
5584 rectsOut
[i
].y1
- rectsOut
[i
].y0
);
5586 writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth
, maskHeight
);
5588 // would be over the limit of array size.
5589 // make each rectangle path and clip.
5590 writePS("gsave newpath\n");
5591 for (i
= 0; i
< rectsOutLen
; ++i
) {
5592 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
5593 ((double)rectsOut
[i
].x0
)/maskWidth
,
5594 ((double)rectsOut
[i
].y0
)/maskHeight
,
5595 ((double)(rectsOut
[i
].x1
- rectsOut
[i
].x0
))/maskWidth
,
5596 ((double)(rectsOut
[i
].y1
- rectsOut
[i
].y0
))/maskHeight
);
5607 void PSOutputDev::doImageL2(Object
*ref
, GfxImageColorMap
*colorMap
,
5608 GBool invert
, GBool inlineImg
,
5609 Stream
*str
, int width
, int height
, int len
,
5610 int *maskColors
, Stream
*maskStr
,
5611 int maskWidth
, int maskHeight
, GBool maskInvert
) {
5613 ImageStream
*imgStr
;
5615 PSOutImgClipRect
*rects0
, *rects1
, *rectsTmp
, *rectsOut
;
5616 int rects0Len
, rects1Len
, rectsSize
, rectsOutLen
, rectsOutSize
;
5617 GBool emitRect
, addRect
, extendRect
;
5620 GBool useRLE
, useASCII
, useCompressed
;
5621 GfxSeparationColorSpace
*sepCS
;
5625 int col
, i
, j
, x0
, x1
, y
;
5630 // color key masking
5631 if (maskColors
&& colorMap
&& !inlineImg
) {
5632 // can't read the stream twice for inline images -- but masking
5633 // isn't allowed with inline images anyway
5634 numComps
= colorMap
->getNumPixelComps();
5635 imgStr
= new ImageStream(str
, width
, numComps
, colorMap
->getBits());
5637 rects0Len
= rects1Len
= 0;
5638 rectsSize
= rectsOutSize
= 64;
5639 rects0
= (PSOutImgClipRect
*)gmallocn(rectsSize
, sizeof(PSOutImgClipRect
));
5640 rects1
= (PSOutImgClipRect
*)gmallocn(rectsSize
, sizeof(PSOutImgClipRect
));
5641 rectsOut
= (PSOutImgClipRect
*)gmallocn(rectsOutSize
,
5642 sizeof(PSOutImgClipRect
));
5643 for (y
= 0; y
< height
; ++y
) {
5644 if (!(line
= imgStr
->getLine())) {
5649 for (x0
= 0; x0
< width
; ++x0
) {
5650 for (j
= 0; j
< numComps
; ++j
) {
5651 if (line
[x0
*numComps
+j
] < maskColors
[2*j
] ||
5652 line
[x0
*numComps
+j
] > maskColors
[2*j
+1]) {
5660 for (x1
= x0
; x1
< width
; ++x1
) {
5661 for (j
= 0; j
< numComps
; ++j
) {
5662 if (line
[x1
*numComps
+j
] < maskColors
[2*j
] ||
5663 line
[x1
*numComps
+j
] > maskColors
[2*j
+1]) {
5667 if (j
== numComps
) {
5671 while (x0
< width
|| i
< rects0Len
) {
5672 emitRect
= addRect
= extendRect
= gFalse
;
5675 } else if (i
>= rects0Len
) {
5677 } else if (rects0
[i
].x0
< x0
) {
5679 } else if (x0
< rects0
[i
].x0
) {
5681 } else if (rects0
[i
].x1
== x1
) {
5684 emitRect
= addRect
= gTrue
;
5687 if (rectsOutLen
== rectsOutSize
) {
5689 rectsOut
= (PSOutImgClipRect
*)greallocn(rectsOut
, rectsOutSize
,
5690 sizeof(PSOutImgClipRect
));
5692 rectsOut
[rectsOutLen
].x0
= rects0
[i
].x0
;
5693 rectsOut
[rectsOutLen
].x1
= rects0
[i
].x1
;
5694 rectsOut
[rectsOutLen
].y0
= height
- y
- 1;
5695 rectsOut
[rectsOutLen
].y1
= height
- rects0
[i
].y0
- 1;
5699 if (addRect
|| extendRect
) {
5700 if (rects1Len
== rectsSize
) {
5702 rects0
= (PSOutImgClipRect
*)greallocn(rects0
, rectsSize
,
5703 sizeof(PSOutImgClipRect
));
5704 rects1
= (PSOutImgClipRect
*)greallocn(rects1
, rectsSize
,
5705 sizeof(PSOutImgClipRect
));
5707 rects1
[rects1Len
].x0
= x0
;
5708 rects1
[rects1Len
].x1
= x1
;
5710 rects1
[rects1Len
].y0
= y
;
5713 rects1
[rects1Len
].y0
= rects0
[i
].y0
;
5717 for (x0
= x1
; x0
< width
; ++x0
) {
5718 for (j
= 0; j
< numComps
; ++j
) {
5719 if (line
[x0
*numComps
+j
] < maskColors
[2*j
] ||
5720 line
[x0
*numComps
+j
] > maskColors
[2*j
+1]) {
5728 for (x1
= x0
; x1
< width
; ++x1
) {
5729 for (j
= 0; j
< numComps
; ++j
) {
5730 if (line
[x1
*numComps
+j
] < maskColors
[2*j
] ||
5731 line
[x1
*numComps
+j
] > maskColors
[2*j
+1]) {
5735 if (j
== numComps
) {
5745 rects0Len
= rects1Len
;
5748 for (i
= 0; i
< rects0Len
; ++i
) {
5749 if (rectsOutLen
== rectsOutSize
) {
5751 rectsOut
= (PSOutImgClipRect
*)greallocn(rectsOut
, rectsOutSize
,
5752 sizeof(PSOutImgClipRect
));
5754 rectsOut
[rectsOutLen
].x0
= rects0
[i
].x0
;
5755 rectsOut
[rectsOutLen
].x1
= rects0
[i
].x1
;
5756 rectsOut
[rectsOutLen
].y0
= height
- y
- 1;
5757 rectsOut
[rectsOutLen
].y1
= height
- rects0
[i
].y0
- 1;
5760 if (rectsOutLen
< 65536/4) {
5761 writePSFmt("{0:d} array 0\n", rectsOutLen
* 4);
5762 for (i
= 0; i
< rectsOutLen
; ++i
) {
5763 writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
5764 rectsOut
[i
].x0
, rectsOut
[i
].y0
,
5765 rectsOut
[i
].x1
- rectsOut
[i
].x0
,
5766 rectsOut
[i
].y1
- rectsOut
[i
].y0
);
5768 writePSFmt("pop {0:d} {1:d} pdfImClip\n", width
, height
);
5770 // would be over the limit of array size.
5771 // make each rectangle path and clip.
5772 writePS("gsave newpath\n");
5773 for (i
= 0; i
< rectsOutLen
; ++i
) {
5774 writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
5775 ((double)rectsOut
[i
].x0
)/width
,
5776 ((double)rectsOut
[i
].y0
)/height
,
5777 ((double)(rectsOut
[i
].x1
- rectsOut
[i
].x0
))/width
,
5778 ((double)(rectsOut
[i
].y1
- rectsOut
[i
].y0
))/height
);
5789 } else if (maskStr
) {
5790 maskToClippingPath(maskStr
, maskWidth
, maskHeight
, maskInvert
);
5795 dumpColorSpaceL2(colorMap
->getColorSpace(), gFalse
, gTrue
, gFalse
);
5796 writePS(" setcolorspace\n");
5799 // set up the image data
5800 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
5803 str2
= new FixedLengthEncoder(str
, len
);
5804 str2
= new RunLengthEncoder(str2
);
5806 str2
= new ASCIIHexEncoder(str2
);
5808 str2
= new ASCII85Encoder(str2
);
5812 writePS((char *)(useASCIIHex
? "[<" : "[<~"));
5815 c
= str2
->getChar();
5816 } while (c
== '\n' || c
== '\r');
5817 if (c
== (useASCIIHex
? '>' : '~') || c
== EOF
) {
5826 for (i
= 1; i
<= (useASCIIHex
? 1 : 4); ++i
) {
5828 c
= str2
->getChar();
5829 } while (c
== '\n' || c
== '\r');
5830 if (c
== (useASCIIHex
? '>' : '~') || c
== EOF
) {
5837 // each line is: "<~...data...~><eol>"
5838 // so max data length = 255 - 6 = 249
5839 // chunks are 1 or 5 bytes each, so we have to stop at 245
5840 // but make it 240 just to be safe
5842 writePS((char *)(useASCIIHex
? ">\n<" : "~>\n<~"));
5845 } while (c
!= (useASCIIHex
? '>' : '~') && c
!= EOF
);
5846 writePS((char *)(useASCIIHex
? ">\n" : "~>\n"));
5847 // add an extra entry because the RunLengthDecode filter may
5848 // read past the end
5854 // make sure the image is setup, it sometimes is not like on bug #17645
5855 setupImage(ref
->getRef(), str
, gFalse
);
5856 // set up to use the array already created by setupImages()
5857 writePSFmt("ImData_{0:d}_{1:d} 0 0\n",ref
->getRefNum(), ref
->getRefGen());
5862 writePS("<<\n /ImageType 1\n");
5864 // width, height, matrix, bits per component
5865 writePSFmt(" /Width {0:d}\n", width
);
5866 writePSFmt(" /Height {0:d}\n", height
);
5867 writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
5868 width
, -height
, height
);
5869 if (colorMap
&& colorMap
->getColorSpace()->getMode() == csDeviceN
) {
5870 writePS(" /BitsPerComponent 8\n");
5872 writePSFmt(" /BitsPerComponent {0:d}\n",
5873 colorMap
? colorMap
->getBits() : 1);
5878 writePS(" /Decode [");
5879 if ((level
== psLevel2Sep
|| level
== psLevel3Sep
) &&
5880 colorMap
->getColorSpace()->getMode() == csSeparation
) {
5881 // this matches up with the code in the pdfImSep operator
5882 n
= (1 << colorMap
->getBits()) - 1;
5883 writePSFmt("{0:.4g} {1:.4g}", colorMap
->getDecodeLow(0) * n
,
5884 colorMap
->getDecodeHigh(0) * n
);
5885 } else if (colorMap
->getColorSpace()->getMode() == csDeviceN
) {
5886 numComps
= ((GfxDeviceNColorSpace
*)colorMap
->getColorSpace())->
5887 getAlt()->getNComps();
5888 for (i
= 0; i
< numComps
; ++i
) {
5895 numComps
= colorMap
->getNumPixelComps();
5896 for (i
= 0; i
< numComps
; ++i
) {
5900 writePSFmt("{0:.4g} {1:.4g}",
5901 colorMap
->getDecodeLow(i
), colorMap
->getDecodeHigh(i
));
5906 writePSFmt(" /Decode [{0:d} {1:d}]\n", invert
? 1 : 0, invert
? 0 : 1);
5910 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
5912 writePS(" /DataSource { pdfImStr }\n");
5914 writePS(" /DataSource { dup 65535 ge { pop 1 add 0 } if 2 index 2"
5915 " index get 1 index get exch 1 add exch }\n");
5918 writePS(" /DataSource currentfile\n");
5922 if ((mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) &&
5923 uncompressPreloadedImages
) {
5926 useCompressed
= gFalse
;
5929 s
= str
->getPSFilter(level
< psLevel2
? 1 : level
< psLevel3
? 2 : 3,
5931 if ((colorMap
&& colorMap
->getColorSpace()->getMode() == csDeviceN
) ||
5934 useASCII
= !(mode
== psModeForm
|| inType3Char
|| preloadImagesForms
);
5935 useCompressed
= gFalse
;
5938 useASCII
= str
->isBinary() &&
5939 !(mode
== psModeForm
|| inType3Char
|| preloadImagesForms
);
5940 useCompressed
= gTrue
;
5944 writePSFmt(" /ASCII{0:s}Decode filter\n",
5945 useASCIIHex
? "Hex" : "85");
5948 writePS(" /RunLengthDecode filter\n");
5950 if (useCompressed
) {
5951 writePS(s
->getCString());
5957 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
5959 // end of image dictionary
5960 writePSFmt(">>\n{0:s}\n", colorMap
? "image" : "imagemask");
5962 // get rid of the array and index
5963 if (!inlineImg
) writePS("pop ");
5964 writePS("pop pop\n");
5968 // cut off inline image streams at appropriate length
5970 str
= new FixedLengthEncoder(str
, len
);
5971 } else if (useCompressed
) {
5972 str
= str
->getUndecodedStream();
5975 // recode DeviceN data
5976 if (colorMap
&& colorMap
->getColorSpace()->getMode() == csDeviceN
) {
5977 str
= new DeviceNRecoder(str
, width
, height
, colorMap
);
5980 // add RunLengthEncode and ASCIIHex/85 encode filters
5982 str
= new RunLengthEncoder(str
);
5986 str
= new ASCIIHexEncoder(str
);
5988 str
= new ASCII85Encoder(str
);
5992 // end of image dictionary
5997 // this can't happen -- OPI dictionaries are in XObjects
5998 error(errSyntaxError
, -1, "OPI in inline image");
6001 // need to read the stream to count characters -- the length
6002 // is data-dependent (because of ASCII and RLE filters)
6005 while ((c
= str
->getChar()) != EOF
) {
6010 // +6/7 for "pdfIm\n" / "pdfImM\n"
6011 // +8 for newline + trailer
6012 n
+= colorMap
? 14 : 15;
6013 writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n
);
6016 if ((level
== psLevel2Sep
|| level
== psLevel3Sep
) && colorMap
&&
6017 colorMap
->getColorSpace()->getMode() == csSeparation
&& colorMap
->getBits() == 8) {
6018 color
.c
[0] = gfxColorComp1
;
6019 sepCS
= (GfxSeparationColorSpace
*)colorMap
->getColorSpace();
6020 sepCS
->getCMYK(&color
, &cmyk
);
6021 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
6022 colToDbl(cmyk
.c
), colToDbl(cmyk
.m
),
6023 colToDbl(cmyk
.y
), colToDbl(cmyk
.k
),
6026 writePSFmt("{0:s}\n", colorMap
? "pdfIm" : "pdfImM");
6029 // copy the stream data
6032 while ((c
= str
->getChar()) != EOF
) {
6034 if (i
>= (int)sizeof(dataBuf
)) {
6035 writePSBuf(dataBuf
, i
);
6040 writePSBuf(dataBuf
, i
);
6044 // add newline and trailer to the end
6046 writePS("%-EOD-\n");
6049 writePS("%%EndData\n");
6054 if (useRLE
|| useASCII
|| inlineImg
) {
6059 if ((maskColors
&& colorMap
&& !inlineImg
) || maskStr
) {
6060 if (rectsOutLen
< 65536/4) {
6061 writePS("pdfImClipEnd\n");
6063 writePS("grestore\n");
6068 //~ this doesn't currently support OPI
6069 void PSOutputDev::doImageL3(Object
*ref
, GfxImageColorMap
*colorMap
,
6070 GBool invert
, GBool inlineImg
,
6071 Stream
*str
, int width
, int height
, int len
,
6072 int *maskColors
, Stream
*maskStr
,
6073 int maskWidth
, int maskHeight
, GBool maskInvert
) {
6077 GBool useRLE
, useASCII
, useCompressed
;
6078 GBool maskUseRLE
, maskUseASCII
, maskUseCompressed
;
6079 GooString
*maskFilters
;
6080 GfxSeparationColorSpace
*sepCS
;
6086 useRLE
= useASCII
= useCompressed
= gFalse
; // make gcc happy
6087 maskUseRLE
= maskUseASCII
= maskUseCompressed
= gFalse
; // make gcc happy
6088 maskFilters
= NULL
; // make gcc happy
6094 if ((mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) &&
6095 uncompressPreloadedImages
) {
6097 maskUseRLE
= gFalse
;
6098 maskUseCompressed
= gFalse
;
6099 maskUseASCII
= gFalse
;
6101 s
= maskStr
->getPSFilter(3, " ");
6104 maskUseASCII
= !(mode
== psModeForm
|| inType3Char
|| preloadImagesForms
);
6105 maskUseCompressed
= gFalse
;
6107 maskUseRLE
= gFalse
;
6108 maskUseASCII
= maskStr
->isBinary() &&
6109 !(mode
== psModeForm
|| inType3Char
|| preloadImagesForms
);
6110 maskUseCompressed
= gTrue
;
6113 maskFilters
= new GooString();
6115 maskFilters
->appendf(" /ASCII{0:s}Decode filter\n",
6116 useASCIIHex
? "Hex" : "85");
6119 maskFilters
->append(" /RunLengthDecode filter\n");
6121 if (maskUseCompressed
) {
6122 maskFilters
->append(s
);
6127 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
6128 writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n",
6129 ref
->getRefNum(), ref
->getRefGen());
6131 writePS("currentfile\n");
6132 writePS(maskFilters
->getCString());
6133 writePS("pdfMask\n");
6135 // add RunLengthEncode and ASCIIHex/85 encode filters
6136 if (maskUseCompressed
) {
6137 maskStr
= maskStr
->getUndecodedStream();
6140 maskStr
= new RunLengthEncoder(maskStr
);
6144 maskStr
= new ASCIIHexEncoder(maskStr
);
6146 maskStr
= new ASCII85Encoder(maskStr
);
6150 // copy the stream data
6152 while ((c
= maskStr
->getChar()) != EOF
) {
6157 writePS("%-EOD-\n");
6160 if (maskUseRLE
|| maskUseASCII
) {
6168 dumpColorSpaceL2(colorMap
->getColorSpace(), gFalse
, gTrue
, gFalse
);
6169 writePS(" setcolorspace\n");
6172 // set up the image data
6173 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
6176 str2
= new FixedLengthEncoder(str
, len
);
6177 str2
= new RunLengthEncoder(str2
);
6179 str2
= new ASCIIHexEncoder(str2
);
6181 str2
= new ASCII85Encoder(str2
);
6185 writePS((char *)(useASCIIHex
? "[<" : "[<~"));
6188 c
= str2
->getChar();
6189 } while (c
== '\n' || c
== '\r');
6190 if (c
== (useASCIIHex
? '>' : '~') || c
== EOF
) {
6199 for (i
= 1; i
<= (useASCIIHex
? 1 : 4); ++i
) {
6201 c
= str2
->getChar();
6202 } while (c
== '\n' || c
== '\r');
6203 if (c
== (useASCIIHex
? '>' : '~') || c
== EOF
) {
6210 // each line is: "<~...data...~><eol>"
6211 // so max data length = 255 - 6 = 249
6212 // chunks are 1 or 5 bytes each, so we have to stop at 245
6213 // but make it 240 just to be safe
6215 writePS((char *)(useASCIIHex
? ">\n<" : "~>\n<~"));
6218 } while (c
!= (useASCIIHex
? '>' : '~') && c
!= EOF
);
6219 writePS((char *)(useASCIIHex
? ">\n" : "~>\n"));
6220 // add an extra entry because the RunLengthDecode filter may
6221 // read past the end
6227 // make sure the image is setup, it sometimes is not like on bug #17645
6228 setupImage(ref
->getRef(), str
, gFalse
);
6229 // set up to use the array already created by setupImages()
6230 writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref
->getRefNum(), ref
->getRefGen());
6236 writePS("<<\n /ImageType 3\n");
6237 writePS(" /InterleaveType 3\n");
6238 writePS(" /DataDict\n");
6241 // image (data) dictionary
6242 writePSFmt("<<\n /ImageType {0:d}\n", (maskColors
&& colorMap
) ? 4 : 1);
6244 // color key masking
6245 if (maskColors
&& colorMap
) {
6246 writePS(" /MaskColor [\n");
6247 numComps
= colorMap
->getNumPixelComps();
6248 for (i
= 0; i
< 2 * numComps
; i
+= 2) {
6249 writePSFmt(" {0:d} {1:d}\n", maskColors
[i
], maskColors
[i
+1]);
6254 // width, height, matrix, bits per component
6255 writePSFmt(" /Width {0:d}\n", width
);
6256 writePSFmt(" /Height {0:d}\n", height
);
6257 writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
6258 width
, -height
, height
);
6259 if (colorMap
&& colorMap
->getColorSpace()->getMode() == csDeviceN
) {
6260 writePS(" /BitsPerComponent 8\n");
6262 writePSFmt(" /BitsPerComponent {0:d}\n",
6263 colorMap
? colorMap
->getBits() : 1);
6268 writePS(" /Decode [");
6269 if ((level
== psLevel2Sep
|| level
== psLevel3Sep
) &&
6270 colorMap
->getColorSpace()->getMode() == csSeparation
) {
6271 // this matches up with the code in the pdfImSep operator
6272 n
= (1 << colorMap
->getBits()) - 1;
6273 writePSFmt("{0:.4g} {1:.4g}", colorMap
->getDecodeLow(0) * n
,
6274 colorMap
->getDecodeHigh(0) * n
);
6276 numComps
= colorMap
->getNumPixelComps();
6277 for (i
= 0; i
< numComps
; ++i
) {
6281 writePSFmt("{0:.4g} {1:.4g}", colorMap
->getDecodeLow(i
),
6282 colorMap
->getDecodeHigh(i
));
6287 writePSFmt(" /Decode [{0:d} {1:d}]\n", invert
? 1 : 0, invert
? 0 : 1);
6291 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
6293 writePS(" /DataSource { pdfImStr }\n");
6295 writePS(" /DataSource { dup 65535 ge { pop 1 add 0 } if 2 index 2"
6296 " index get 1 index get exch 1 add exch }\n");
6299 writePS(" /DataSource currentfile\n");
6303 if ((mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) &&
6304 uncompressPreloadedImages
) {
6307 useCompressed
= gFalse
;
6310 s
= str
->getPSFilter(level
< psLevel2
? 1 : level
< psLevel3
? 2 : 3,
6312 if ((colorMap
&& colorMap
->getColorSpace()->getMode() == csDeviceN
) ||
6315 useASCII
= !(mode
== psModeForm
|| inType3Char
|| preloadImagesForms
);
6316 useCompressed
= gFalse
;
6319 useASCII
= str
->isBinary() &&
6320 !(mode
== psModeForm
|| inType3Char
|| preloadImagesForms
);
6321 useCompressed
= gTrue
;
6325 writePSFmt(" /ASCII{0:s}Decode filter\n",
6326 useASCIIHex
? "Hex" : "85");
6329 writePS(" /RunLengthDecode filter\n");
6331 if (useCompressed
) {
6332 writePS(s
->getCString());
6338 // end of image (data) dictionary
6343 writePS(" /MaskDict\n");
6345 writePS(" /ImageType 1\n");
6346 writePSFmt(" /Width {0:d}\n", maskWidth
);
6347 writePSFmt(" /Height {0:d}\n", maskHeight
);
6348 writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
6349 maskWidth
, -maskHeight
, maskHeight
);
6350 writePS(" /BitsPerComponent 1\n");
6351 writePSFmt(" /Decode [{0:d} {1:d}]\n",
6352 maskInvert
? 1 : 0, maskInvert
? 0 : 1);
6355 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
6356 writePS(" /DataSource {pdfMaskSrc}\n");
6357 writePS(maskFilters
->getCString());
6359 writePS(" /DataSource maskStream\n");
6367 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
6370 writePSFmt("{0:s}\n", colorMap
? "image" : "imagemask");
6374 if ((level
== psLevel2Sep
|| level
== psLevel3Sep
) && colorMap
&&
6375 colorMap
->getColorSpace()->getMode() == csSeparation
&& colorMap
->getBits() == 8) {
6376 color
.c
[0] = gfxColorComp1
;
6377 sepCS
= (GfxSeparationColorSpace
*)colorMap
->getColorSpace();
6378 sepCS
->getCMYK(&color
, &cmyk
);
6379 writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
6380 colToDbl(cmyk
.c
), colToDbl(cmyk
.m
),
6381 colToDbl(cmyk
.y
), colToDbl(cmyk
.k
),
6384 writePSFmt("{0:s}\n", colorMap
? "pdfIm" : "pdfImM");
6389 // get rid of the array and index
6390 if (mode
== psModeForm
|| inType3Char
|| preloadImagesForms
) {
6391 if (!inlineImg
) writePS("pop ");
6392 writePS("pop pop\n");
6397 // cut off inline image streams at appropriate length
6399 str
= new FixedLengthEncoder(str
, len
);
6400 } else if (useCompressed
) {
6401 str
= str
->getUndecodedStream();
6404 // add RunLengthEncode and ASCIIHex/85 encode filters
6406 str
= new RunLengthEncoder(str
);
6410 str
= new ASCIIHexEncoder(str
);
6412 str
= new ASCII85Encoder(str
);
6416 // copy the stream data
6418 while ((c
= str
->getChar()) != EOF
) {
6423 // add newline and trailer to the end
6425 writePS("%-EOD-\n");
6428 if (useRLE
|| useASCII
|| inlineImg
) {
6433 // close the mask stream
6435 if (!(mode
== psModeForm
|| inType3Char
|| preloadImagesForms
)) {
6436 writePS("pdfMaskEnd\n");
6441 void PSOutputDev::dumpColorSpaceL2(GfxColorSpace
*colorSpace
,
6442 GBool genXform
, GBool updateColors
,
6444 GfxCalGrayColorSpace
*calGrayCS
;
6445 GfxCalRGBColorSpace
*calRGBCS
;
6446 GfxLabColorSpace
*labCS
;
6447 GfxIndexedColorSpace
*indexedCS
;
6448 GfxSeparationColorSpace
*separationCS
;
6449 GfxDeviceNColorSpace
*deviceNCS
;
6450 GfxColorSpace
*baseCS
;
6452 double x
[gfxColorMaxComps
], y
[gfxColorMaxComps
];
6453 double low
[gfxColorMaxComps
], range
[gfxColorMaxComps
];
6457 int n
, numComps
, numAltComps
;
6461 switch (colorSpace
->getMode()) {
6464 writePS("/DeviceGray");
6469 processColors
|= psProcessBlack
;
6474 calGrayCS
= (GfxCalGrayColorSpace
*)colorSpace
;
6475 writePS("[/CIEBasedA <<\n");
6476 writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", calGrayCS
->getGamma());
6477 writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n",
6478 calGrayCS
->getWhiteX(), calGrayCS
->getWhiteY(),
6479 calGrayCS
->getWhiteZ());
6480 writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
6481 calGrayCS
->getWhiteX(), calGrayCS
->getWhiteY(),
6482 calGrayCS
->getWhiteZ());
6483 writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
6484 calGrayCS
->getBlackX(), calGrayCS
->getBlackY(),
6485 calGrayCS
->getBlackZ());
6491 processColors
|= psProcessBlack
;
6496 writePS("/DeviceRGB");
6501 processColors
|= psProcessCMYK
;
6506 calRGBCS
= (GfxCalRGBColorSpace
*)colorSpace
;
6507 writePS("[/CIEBasedABC <<\n");
6508 writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n",
6509 calRGBCS
->getGammaR(), calRGBCS
->getGammaG(),
6510 calRGBCS
->getGammaB());
6511 writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n",
6512 calRGBCS
->getMatrix()[0], calRGBCS
->getMatrix()[1],
6513 calRGBCS
->getMatrix()[2], calRGBCS
->getMatrix()[3],
6514 calRGBCS
->getMatrix()[4], calRGBCS
->getMatrix()[5],
6515 calRGBCS
->getMatrix()[6], calRGBCS
->getMatrix()[7],
6516 calRGBCS
->getMatrix()[8]);
6517 writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
6518 calRGBCS
->getWhiteX(), calRGBCS
->getWhiteY(),
6519 calRGBCS
->getWhiteZ());
6520 writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
6521 calRGBCS
->getBlackX(), calRGBCS
->getBlackY(),
6522 calRGBCS
->getBlackZ());
6528 processColors
|= psProcessCMYK
;
6533 writePS("/DeviceCMYK");
6538 processColors
|= psProcessCMYK
;
6543 labCS
= (GfxLabColorSpace
*)colorSpace
;
6544 writePS("[/CIEBasedABC <<\n");
6546 writePS(" /RangeABC [0 1 0 1 0 1]\n");
6547 writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n",
6548 (labCS
->getAMax() - labCS
->getAMin()) / 500.0,
6549 labCS
->getAMin() / 500.0,
6550 (labCS
->getBMax() - labCS
->getBMin()) / 200.0,
6551 labCS
->getBMin() / 200.0);
6553 writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n",
6554 labCS
->getAMin(), labCS
->getAMax(),
6555 labCS
->getBMin(), labCS
->getBMax());
6556 writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
6558 writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
6559 writePS(" /DecodeLMN\n");
6560 writePS(" [{dup 6 29 div ge {dup dup mul mul}\n");
6561 writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
6562 labCS
->getWhiteX());
6563 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
6564 writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
6565 labCS
->getWhiteY());
6566 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
6567 writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n",
6568 labCS
->getWhiteZ());
6569 writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
6570 labCS
->getWhiteX(), labCS
->getWhiteY(), labCS
->getWhiteZ());
6571 writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
6572 labCS
->getBlackX(), labCS
->getBlackY(), labCS
->getBlackZ());
6578 processColors
|= psProcessCMYK
;
6583 // there is no transform function to the alternate color space, so
6584 // we can use it directly
6585 dumpColorSpaceL2(((GfxICCBasedColorSpace
*)colorSpace
)->getAlt(),
6586 genXform
, updateColors
, gFalse
);
6590 indexedCS
= (GfxIndexedColorSpace
*)colorSpace
;
6591 baseCS
= indexedCS
->getBase();
6592 writePS("[/Indexed ");
6593 dumpColorSpaceL2(baseCS
, gFalse
, gFalse
, gTrue
);
6594 n
= indexedCS
->getIndexHigh();
6595 numComps
= baseCS
->getNComps();
6596 lookup
= indexedCS
->getLookup();
6597 writePSFmt(" {0:d} <\n", n
);
6598 if (baseCS
->getMode() == csDeviceN
&& level
!= psLevel3
&& level
!= psLevel3Sep
) {
6599 func
= ((GfxDeviceNColorSpace
*)baseCS
)->getTintTransformFunc();
6600 baseCS
->getDefaultRanges(low
, range
, indexedCS
->getIndexHigh());
6601 if (((GfxDeviceNColorSpace
*)baseCS
)->getAlt()->getMode() == csLab
) {
6602 labCS
= (GfxLabColorSpace
*)((GfxDeviceNColorSpace
*)baseCS
)->getAlt();
6606 numAltComps
= ((GfxDeviceNColorSpace
*)baseCS
)->getAlt()->getNComps();
6608 for (i
= 0; i
<= n
; i
+= 8) {
6610 for (j
= i
; j
< i
+8 && j
<= n
; ++j
) {
6611 for (k
= 0; k
< numComps
; ++k
) {
6612 x
[k
] = low
[k
] + (*p
++ / 255.0) * range
[k
];
6614 func
->transform(x
, y
);
6617 y
[1] = (y
[1] - labCS
->getAMin()) /
6618 (labCS
->getAMax() - labCS
->getAMin());
6619 y
[2] = (y
[2] - labCS
->getBMin()) /
6620 (labCS
->getBMax() - labCS
->getBMin());
6622 for (k
= 0; k
< numAltComps
; ++k
) {
6623 byte
= (int)(y
[k
] * 255 + 0.5);
6626 } else if (byte
> 255) {
6629 writePSFmt("{0:02x}", byte
);
6632 color
.c
[0] = dblToCol(j
);
6633 indexedCS
->getCMYK(&color
, &cmyk
);
6634 addProcessColor(colToDbl(cmyk
.c
), colToDbl(cmyk
.m
),
6635 colToDbl(cmyk
.y
), colToDbl(cmyk
.k
));
6641 for (i
= 0; i
<= n
; i
+= 8) {
6643 for (j
= i
; j
< i
+8 && j
<= n
; ++j
) {
6644 for (k
= 0; k
< numComps
; ++k
) {
6645 writePSFmt("{0:02x}", lookup
[j
* numComps
+ k
]);
6648 color
.c
[0] = dblToCol(j
);
6649 indexedCS
->getCMYK(&color
, &cmyk
);
6650 addProcessColor(colToDbl(cmyk
.c
), colToDbl(cmyk
.m
),
6651 colToDbl(cmyk
.y
), colToDbl(cmyk
.k
));
6664 separationCS
= (GfxSeparationColorSpace
*)colorSpace
;
6665 writePS("[/Separation ");
6666 writePSString(separationCS
->getName());
6668 dumpColorSpaceL2(separationCS
->getAlt(), gFalse
, gFalse
, gFalse
);
6670 cvtFunction(separationCS
->getFunc());
6676 addCustomColor(separationCS
);
6681 deviceNCS
= (GfxDeviceNColorSpace
*)colorSpace
;
6682 if (level
== psLevel3
|| level
== psLevel3Sep
) {
6683 writePS("[/DeviceN\n");
6685 for (i
= 0; i
< deviceNCS
->getNComps(); i
++) {
6686 writePSString(deviceNCS
->getColorantName(i
));
6690 dumpColorSpaceL2(deviceNCS
->getAlt(), gFalse
, updateColors
, gFalse
);
6692 cvtFunction(deviceNCS
->getTintTransformFunc(), map01
&& deviceNCS
->getAlt()->getMode() == csLab
);
6698 // DeviceN color spaces are a Level 3 PostScript feature.
6699 dumpColorSpaceL2(deviceNCS
->getAlt(), gFalse
, updateColors
, map01
);
6702 cvtFunction(deviceNCS
->getTintTransformFunc());
6714 void PSOutputDev::opiBegin(GfxState
*state
, Dict
*opiDict
) {
6718 opiDict
->lookup("2.0", &dict
);
6719 if (dict
.isDict()) {
6720 opiBegin20(state
, dict
.getDict());
6724 opiDict
->lookup("1.3", &dict
);
6725 if (dict
.isDict()) {
6726 opiBegin13(state
, dict
.getDict());
6733 void PSOutputDev::opiBegin20(GfxState
*state
, Dict
*dict
) {
6734 Object obj1
, obj2
, obj3
, obj4
;
6735 double width
, height
, left
, right
, top
, bottom
;
6739 writePS("%%BeginOPI: 2.0\n");
6740 writePS("%%Distilled\n");
6742 dict
->lookup("F", &obj1
);
6743 if (getFileSpecName(&obj1
, &obj2
)) {
6744 writePSFmt("%%ImageFileName: {0:t}\n", obj2
.getString());
6749 dict
->lookup("MainImage", &obj1
);
6750 if (obj1
.isString()) {
6751 writePSFmt("%%MainImage: {0:t}\n", obj1
.getString());
6755 //~ ignoring 'Tags' entry
6756 //~ need to use writePSString() and deal with >255-char lines
6758 dict
->lookup("Size", &obj1
);
6759 if (obj1
.isArray() && obj1
.arrayGetLength() == 2) {
6760 obj1
.arrayGet(0, &obj2
);
6761 width
= obj2
.getNum();
6763 obj1
.arrayGet(1, &obj2
);
6764 height
= obj2
.getNum();
6766 writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width
, height
);
6770 dict
->lookup("CropRect", &obj1
);
6771 if (obj1
.isArray() && obj1
.arrayGetLength() == 4) {
6772 obj1
.arrayGet(0, &obj2
);
6773 left
= obj2
.getNum();
6775 obj1
.arrayGet(1, &obj2
);
6776 top
= obj2
.getNum();
6778 obj1
.arrayGet(2, &obj2
);
6779 right
= obj2
.getNum();
6781 obj1
.arrayGet(3, &obj2
);
6782 bottom
= obj2
.getNum();
6784 writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
6785 left
, top
, right
, bottom
);
6789 dict
->lookup("Overprint", &obj1
);
6790 if (obj1
.isBool()) {
6791 writePSFmt("%%ImageOverprint: {0:s}\n", obj1
.getBool() ? "true" : "false");
6795 dict
->lookup("Inks", &obj1
);
6796 if (obj1
.isName()) {
6797 writePSFmt("%%ImageInks: {0:s}\n", obj1
.getName());
6798 } else if (obj1
.isArray() && obj1
.arrayGetLength() >= 1) {
6799 obj1
.arrayGet(0, &obj2
);
6800 if (obj2
.isName()) {
6801 writePSFmt("%%ImageInks: {0:s} {1:d}",
6802 obj2
.getName(), (obj1
.arrayGetLength() - 1) / 2);
6803 for (i
= 1; i
+1 < obj1
.arrayGetLength(); i
+= 2) {
6804 obj1
.arrayGet(i
, &obj3
);
6805 obj1
.arrayGet(i
+1, &obj4
);
6806 if (obj3
.isString() && obj4
.isNum()) {
6808 writePSString(obj3
.getString());
6809 writePSFmt(" {0:.6g}", obj4
.getNum());
6822 writePS("%%BeginIncludedImage\n");
6824 dict
->lookup("IncludedImageDimensions", &obj1
);
6825 if (obj1
.isArray() && obj1
.arrayGetLength() == 2) {
6826 obj1
.arrayGet(0, &obj2
);
6829 obj1
.arrayGet(1, &obj2
);
6832 writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w
, h
);
6836 dict
->lookup("IncludedImageQuality", &obj1
);
6838 writePSFmt("%%IncludedImageQuality: {0:.6g}\n", obj1
.getNum());
6845 void PSOutputDev::opiBegin13(GfxState
*state
, Dict
*dict
) {
6847 int left
, right
, top
, bottom
, samples
, bits
, width
, height
;
6849 double llx
, lly
, ulx
, uly
, urx
, ury
, lrx
, lry
;
6850 double tllx
, tlly
, tulx
, tuly
, turx
, tury
, tlrx
, tlry
;
6855 writePS("/opiMatrix2 matrix currentmatrix def\n");
6856 writePS("opiMatrix setmatrix\n");
6858 dict
->lookup("F", &obj1
);
6859 if (getFileSpecName(&obj1
, &obj2
)) {
6860 writePSFmt("%ALDImageFileName: {0:t}\n", obj2
.getString());
6865 dict
->lookup("CropRect", &obj1
);
6866 if (obj1
.isArray() && obj1
.arrayGetLength() == 4) {
6867 obj1
.arrayGet(0, &obj2
);
6868 left
= obj2
.getInt();
6870 obj1
.arrayGet(1, &obj2
);
6871 top
= obj2
.getInt();
6873 obj1
.arrayGet(2, &obj2
);
6874 right
= obj2
.getInt();
6876 obj1
.arrayGet(3, &obj2
);
6877 bottom
= obj2
.getInt();
6879 writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n",
6880 left
, top
, right
, bottom
);
6884 dict
->lookup("Color", &obj1
);
6885 if (obj1
.isArray() && obj1
.arrayGetLength() == 5) {
6886 obj1
.arrayGet(0, &obj2
);
6889 obj1
.arrayGet(1, &obj2
);
6892 obj1
.arrayGet(2, &obj2
);
6895 obj1
.arrayGet(3, &obj2
);
6898 obj1
.arrayGet(4, &obj2
);
6899 if (obj2
.isString()) {
6900 writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ",
6902 writePSString(obj2
.getString());
6909 dict
->lookup("ColorType", &obj1
);
6910 if (obj1
.isName()) {
6911 writePSFmt("%ALDImageColorType: {0:s}\n", obj1
.getName());
6915 //~ ignores 'Comments' entry
6916 //~ need to handle multiple lines
6918 dict
->lookup("CropFixed", &obj1
);
6919 if (obj1
.isArray()) {
6920 obj1
.arrayGet(0, &obj2
);
6921 ulx
= obj2
.getNum();
6923 obj1
.arrayGet(1, &obj2
);
6924 uly
= obj2
.getNum();
6926 obj1
.arrayGet(2, &obj2
);
6927 lrx
= obj2
.getNum();
6929 obj1
.arrayGet(3, &obj2
);
6930 lry
= obj2
.getNum();
6932 writePSFmt("%ALDImageCropFixed: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
6933 ulx
, uly
, lrx
, lry
);
6937 dict
->lookup("GrayMap", &obj1
);
6938 if (obj1
.isArray()) {
6939 writePS("%ALDImageGrayMap:");
6940 for (i
= 0; i
< obj1
.arrayGetLength(); i
+= 16) {
6944 for (j
= 0; j
< 16 && i
+j
< obj1
.arrayGetLength(); ++j
) {
6945 obj1
.arrayGet(i
+j
, &obj2
);
6946 writePSFmt(" {0:d}", obj2
.getInt());
6954 dict
->lookup("ID", &obj1
);
6955 if (obj1
.isString()) {
6956 writePSFmt("%ALDImageID: {0:t}\n", obj1
.getString());
6960 dict
->lookup("ImageType", &obj1
);
6961 if (obj1
.isArray() && obj1
.arrayGetLength() == 2) {
6962 obj1
.arrayGet(0, &obj2
);
6963 samples
= obj2
.getInt();
6965 obj1
.arrayGet(1, &obj2
);
6966 bits
= obj2
.getInt();
6968 writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples
, bits
);
6972 dict
->lookup("Overprint", &obj1
);
6973 if (obj1
.isBool()) {
6974 writePSFmt("%ALDImageOverprint: {0:s}\n",
6975 obj1
.getBool() ? "true" : "false");
6979 dict
->lookup("Position", &obj1
);
6980 if (obj1
.isArray() && obj1
.arrayGetLength() == 8) {
6981 obj1
.arrayGet(0, &obj2
);
6982 llx
= obj2
.getNum();
6984 obj1
.arrayGet(1, &obj2
);
6985 lly
= obj2
.getNum();
6987 obj1
.arrayGet(2, &obj2
);
6988 ulx
= obj2
.getNum();
6990 obj1
.arrayGet(3, &obj2
);
6991 uly
= obj2
.getNum();
6993 obj1
.arrayGet(4, &obj2
);
6994 urx
= obj2
.getNum();
6996 obj1
.arrayGet(5, &obj2
);
6997 ury
= obj2
.getNum();
6999 obj1
.arrayGet(6, &obj2
);
7000 lrx
= obj2
.getNum();
7002 obj1
.arrayGet(7, &obj2
);
7003 lry
= obj2
.getNum();
7005 opiTransform(state
, llx
, lly
, &tllx
, &tlly
);
7006 opiTransform(state
, ulx
, uly
, &tulx
, &tuly
);
7007 opiTransform(state
, urx
, ury
, &turx
, &tury
);
7008 opiTransform(state
, lrx
, lry
, &tlrx
, &tlry
);
7009 writePSFmt("%ALDImagePosition: {0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} {6:.6g} {7:.6g}\n",
7010 tllx
, tlly
, tulx
, tuly
, turx
, tury
, tlrx
, tlry
);
7015 dict
->lookup("Resolution", &obj1
);
7016 if (obj1
.isArray() && obj1
.arrayGetLength() == 2) {
7017 obj1
.arrayGet(0, &obj2
);
7018 horiz
= obj2
.getNum();
7020 obj1
.arrayGet(1, &obj2
);
7021 vert
= obj2
.getNum();
7023 writePSFmt("%ALDImageResoution: {0:.6g} {1:.6g}\n", horiz
, vert
);
7028 dict
->lookup("Size", &obj1
);
7029 if (obj1
.isArray() && obj1
.arrayGetLength() == 2) {
7030 obj1
.arrayGet(0, &obj2
);
7031 width
= obj2
.getInt();
7033 obj1
.arrayGet(1, &obj2
);
7034 height
= obj2
.getInt();
7036 writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width
, height
);
7040 //~ ignoring 'Tags' entry
7041 //~ need to use writePSString() and deal with >255-char lines
7043 dict
->lookup("Tint", &obj1
);
7045 writePSFmt("%ALDImageTint: {0:.6g}\n", obj1
.getNum());
7049 dict
->lookup("Transparency", &obj1
);
7050 if (obj1
.isBool()) {
7051 writePSFmt("%ALDImageTransparency: {0:s}\n",
7052 obj1
.getBool() ? "true" : "false");
7056 writePS("%%BeginObject: image\n");
7057 writePS("opiMatrix2 setmatrix\n");
7061 // Convert PDF user space coordinates to PostScript default user space
7062 // coordinates. This has to account for both the PDF CTM and the
7063 // PSOutputDev page-fitting transform.
7064 void PSOutputDev::opiTransform(GfxState
*state
, double x0
, double y0
,
7065 double *x1
, double *y1
) {
7068 state
->transform(x0
, y0
, x1
, y1
);
7075 } else if (rotate
== 180) {
7078 } else if (rotate
== 270) {
7087 void PSOutputDev::opiEnd(GfxState
*state
, Dict
*opiDict
) {
7091 opiDict
->lookup("2.0", &dict
);
7092 if (dict
.isDict()) {
7093 writePS("%%EndIncludedImage\n");
7094 writePS("%%EndOPI\n");
7095 writePS("grestore\n");
7100 opiDict
->lookup("1.3", &dict
);
7101 if (dict
.isDict()) {
7102 writePS("%%EndObject\n");
7103 writePS("restore\n");
7110 #endif // OPI_SUPPORT
7112 void PSOutputDev::type3D0(GfxState
*state
, double wx
, double wy
) {
7113 writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx
, wy
);
7115 t3NeedsRestore
= gTrue
;
7118 void PSOutputDev::type3D1(GfxState
*state
, double wx
, double wy
,
7119 double llx
, double lly
, double urx
, double ury
) {
7126 t3String
= new GooString();
7128 t3FillColorOnly
= gTrue
;
7129 t3Cacheable
= gTrue
;
7130 t3NeedsRestore
= gTrue
;
7133 void PSOutputDev::drawForm(Ref id
) {
7134 writePSFmt("f_{0:d}_{1:d}\n", id
.num
, id
.gen
);
7137 void PSOutputDev::psXObject(Stream
*psStream
, Stream
*level1Stream
) {
7141 if ((level
== psLevel1
|| level
== psLevel1Sep
) && level1Stream
) {
7147 while ((c
= str
->getChar()) != EOF
) {
7153 //~ can nextFunc be reset to 0 -- maybe at the start of each page?
7154 //~ or maybe at the start of each color space / pattern?
7155 void PSOutputDev::cvtFunction(Function
*func
, GBool invertPSFunction
) {
7156 SampledFunction
*func0
;
7157 ExponentialFunction
*func2
;
7158 StitchingFunction
*func3
;
7159 PostScriptFunction
*func4
;
7160 int thisFunc
, m
, n
, nSamples
, i
, j
, k
;
7162 switch (func
->getType()) {
7164 case -1: // identity
7169 func0
= (SampledFunction
*)func
;
7170 thisFunc
= nextFunc
++;
7171 m
= func0
->getInputSize();
7172 n
= func0
->getOutputSize();
7174 for (i
= 0; i
< m
; ++i
) {
7175 nSamples
*= func0
->getSampleSize(i
);
7177 writePSFmt("/xpdfSamples{0:d} [\n", thisFunc
);
7178 for (i
= 0; i
< nSamples
; ++i
) {
7179 writePSFmt("{0:.6g}\n", func0
->getSamples()[i
]);
7182 writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m
, m
, m
+2);
7183 // [e01] [efrac] x0 x1 ... xm-1
7184 for (i
= m
-1; i
>= 0; --i
) {
7185 // [e01] [efrac] x0 x1 ... xi
7186 writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n",
7187 func0
->getDomainMin(i
),
7188 (func0
->getEncodeMax(i
) - func0
->getEncodeMin(i
)) /
7189 (func0
->getDomainMax(i
) - func0
->getDomainMin(i
)),
7190 func0
->getEncodeMin(i
));
7191 // [e01] [efrac] x0 x1 ... xi-1 xi'
7192 writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n",
7193 func0
->getSampleSize(i
) - 1, func0
->getSampleSize(i
) - 1);
7194 // [e01] [efrac] x0 x1 ... xi-1 xi'
7195 writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
7196 // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
7197 writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i
+3, i
);
7198 // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
7199 writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i
+3, 2*i
+1);
7200 // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
7201 writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i
+2, 2*i
);
7202 // [e01] [efrac] x0 x1 ... xi-1
7205 for (i
= 0; i
< n
; ++i
) {
7206 // [e01] [efrac] y(0) ... y(i-1)
7207 for (j
= 0; j
< (1<<m
); ++j
) {
7208 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
7209 writePSFmt("xpdfSamples{0:d}\n", thisFunc
);
7211 writePSFmt("{0:d} index {1:d} get\n", i
+j
+2, 2 * k
+ ((j
>> k
) & 1));
7212 for (k
= m
- 2; k
>= 0; --k
) {
7213 writePSFmt("{0:d} mul {1:d} index {2:d} get add\n",
7214 func0
->getSampleSize(k
),
7216 2 * k
+ ((j
>> k
) & 1));
7219 writePSFmt("{0:d} mul {1:d} add ", n
, i
);
7223 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
7224 for (j
= 0; j
< m
; ++j
) {
7225 // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
7226 for (k
= 0; k
< (1 << (m
- j
)); k
+= 2) {
7227 // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
7228 writePSFmt("{0:d} index {1:d} get dup\n",
7229 i
+ k
/2 + (1 << (m
-j
)) - k
, j
);
7230 writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
7231 writePSFmt("{0:d} 1 roll\n", k
/2 + (1 << (m
-j
)) - k
- 1);
7233 // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
7235 // [e01] [efrac] y(0) ... y(i-1) s
7236 writePSFmt("{0:.6g} mul {1:.6g} add\n",
7237 func0
->getDecodeMax(i
) - func0
->getDecodeMin(i
),
7238 func0
->getDecodeMin(i
));
7239 writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
7240 func0
->getRangeMin(i
), func0
->getRangeMin(i
),
7241 func0
->getRangeMax(i
), func0
->getRangeMax(i
));
7242 // [e01] [efrac] y(0) ... y(i-1) y(i)
7244 // [e01] [efrac] y(0) ... y(n-1)
7245 writePSFmt("{0:d} {1:d} roll pop pop \n", n
+2, n
);
7246 if (invertPSFunction
) {
7247 for (i
= 0; i
< n
; ++i
) {
7248 writePSFmt("{0:d} -1 roll ", n
);
7249 writePSFmt("{0:.6g} sub {1:.6g} div ", func0
->getRangeMin(i
), func0
->getRangeMax(i
) - func0
->getRangeMin(i
));
7255 case 2: // exponential
7256 func2
= (ExponentialFunction
*)func
;
7257 n
= func2
->getOutputSize();
7258 writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
7259 func2
->getDomainMin(0), func2
->getDomainMin(0),
7260 func2
->getDomainMax(0), func2
->getDomainMax(0));
7262 for (i
= 0; i
< n
; ++i
) {
7264 writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n",
7265 i
, func2
->getE(), func2
->getC1()[i
] - func2
->getC0()[i
],
7267 if (func2
->getHasRange()) {
7268 writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
7269 func2
->getRangeMin(i
), func2
->getRangeMin(i
),
7270 func2
->getRangeMax(i
), func2
->getRangeMax(i
));
7274 writePSFmt("{0:d} {1:d} roll pop \n", n
+1, n
);
7275 if (invertPSFunction
&& func2
->getHasRange()) {
7276 for (i
= 0; i
< n
; ++i
) {
7277 writePSFmt("{0:d} -1 roll ", n
);
7278 writePSFmt("{0:.6g} sub {1:.6g} div ", func2
->getRangeMin(i
), func2
->getRangeMax(i
) - func2
->getRangeMin(i
));
7284 case 3: // stitching
7285 func3
= (StitchingFunction
*)func
;
7286 thisFunc
= nextFunc
++;
7287 for (i
= 0; i
< func3
->getNumFuncs(); ++i
) {
7288 cvtFunction(func3
->getFunc(i
));
7289 writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc
, i
);
7291 writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
7292 func3
->getDomainMin(0), func3
->getDomainMin(0),
7293 func3
->getDomainMax(0), func3
->getDomainMax(0));
7294 for (i
= 0; i
< func3
->getNumFuncs() - 1; ++i
) {
7295 writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n",
7296 func3
->getBounds()[i
+1],
7297 func3
->getBounds()[i
],
7298 func3
->getScale()[i
],
7299 func3
->getEncode()[2*i
],
7302 writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n",
7303 func3
->getBounds()[i
],
7304 func3
->getScale()[i
],
7305 func3
->getEncode()[2*i
],
7307 for (i
= 0; i
< func3
->getNumFuncs() - 1; ++i
) {
7308 writePS("} ifelse\n");
7310 if (invertPSFunction
&& func3
->getHasRange()) {
7311 n
= func3
->getOutputSize();
7312 for (i
= 0; i
< n
; ++i
) {
7313 writePSFmt("{0:d} -1 roll ", n
);
7314 writePSFmt("{0:.6g} sub {1:.6g} div ", func3
->getRangeMin(i
), func3
->getRangeMax(i
) - func3
->getRangeMin(i
));
7320 case 4: // PostScript
7321 func4
= (PostScriptFunction
*)func
;
7322 if (invertPSFunction
) {
7323 GooString
*codeString
= new GooString(func4
->getCodeString());
7324 for (i
= codeString
->getLength() -1; i
> 0; i
--) {
7325 if (codeString
->getChar(i
) == '}') {
7330 writePS(codeString
->getCString());
7333 n
= func4
->getOutputSize();
7334 for (i
= 0; i
< n
; ++i
) {
7335 writePSFmt("{0:d} -1 roll ", n
);
7336 writePSFmt("{0:.6g} sub {1:.6g} div ", func4
->getRangeMin(i
), func4
->getRangeMax(i
) - func4
->getRangeMin(i
));
7340 writePS(func4
->getCodeString()->getCString());
7347 void PSOutputDev::writePSChar(char c
) {
7349 t3String
->append(c
);
7351 (*outputFunc
)(outputStream
, &c
, 1);
7355 void PSOutputDev::writePS(const char *s
) {
7357 t3String
->append(s
);
7359 (*outputFunc
)(outputStream
, s
, strlen(s
));
7363 void PSOutputDev::writePSBuf(const char *s
, int len
) {
7365 for (int i
= 0; i
< len
; i
++) {
7366 t3String
->append(s
[i
]);
7369 (*outputFunc
)(outputStream
, s
, len
);
7373 void PSOutputDev::writePSFmt(const char *fmt
, ...) {
7377 va_start(args
, fmt
);
7379 t3String
->appendfv((char *)fmt
, args
);
7381 buf
= GooString::formatv((char *)fmt
, args
);
7382 (*outputFunc
)(outputStream
, buf
->getCString(), buf
->getLength());
7388 void PSOutputDev::writePSString(GooString
*s
) {
7395 for (p
= (Guchar
*)s
->getCString(), n
= s
->getLength(); n
; ++p
, --n
) {
7401 if (*p
== '(' || *p
== ')' || *p
== '\\') {
7403 writePSChar((char)*p
);
7405 } else if (*p
< 0x20 || *p
>= 0x80) {
7406 sprintf(buf
, "\\%03o", *p
);
7410 writePSChar((char)*p
);
7417 void PSOutputDev::writePSName(const char *s
) {
7422 while ((c
= *p
++)) {
7423 if (c
<= (char)0x20 || c
>= (char)0x7f ||
7424 c
== '(' || c
== ')' || c
== '<' || c
== '>' ||
7425 c
== '[' || c
== ']' || c
== '{' || c
== '}' ||
7426 c
== '/' || c
== '%' || c
== '\\') {
7427 writePSFmt("#{0:02x}", c
& 0xff);
7434 GooString
*PSOutputDev::filterPSName(GooString
*name
) {
7440 name2
= new GooString();
7442 // ghostscript chokes on names that begin with out-of-limits
7443 // numbers, e.g., 1e4foo is handled correctly (as a name), but
7444 // 1e999foo generates a limitcheck error
7445 c
= name
->getChar(0);
7446 if (c
>= '0' && c
<= '9') {
7450 for (i
= 0; i
< name
->getLength(); ++i
) {
7451 c
= name
->getChar(i
);
7452 if (c
<= (char)0x20 || c
>= (char)0x7f ||
7453 c
== '(' || c
== ')' || c
== '<' || c
== '>' ||
7454 c
== '[' || c
== ']' || c
== '{' || c
== '}' ||
7455 c
== '/' || c
== '%') {
7456 sprintf(buf
, "#%02x", c
& 0xff);
7465 // Convert GooString to GooString, with appropriate escaping
7466 // of things that can't appear in a label
7467 // This is heavily based on the writePSTextLine() method
7468 GooString
* PSOutputDev::filterPSLabel(GooString
*label
, GBool
*needParens
) {
7472 // - DSC comments must be printable ASCII; control chars and
7473 // backslashes have to be escaped (we do cheap UCS2-to-ASCII
7474 // conversion by simply ignoring the high byte)
7475 // - parentheses are escaped. this isn't strictly necessary for matched
7476 // parentheses, but shouldn't be a problem
7477 // - lines are limited to 255 chars (we limit to 200 here to allow
7478 // for the keyword, which was emitted by the caller)
7480 GooString
*label2
= new GooString();
7481 int labelLength
= label
->getLength();
7483 if (labelLength
== 0) {
7486 // this gets changed later if we find a non-numeric character
7490 if ( (labelLength
>= 2) &&
7491 ( (label
->getChar(0) & 0xff) == 0xfe) &&
7492 ( (label
->getChar(1) & 0xff) == 0xff) ) {
7496 if ( (label
->getChar(labelLength
-1) == 0) ) {
7497 // prune the trailing null (0x000 for UCS2)
7504 for (int j
= 0; i
< labelLength
&& j
< 200; i
+= step
) {
7505 char c
= label
->getChar(i
);
7506 if ( (c
< '0') || (c
> '9') ) {
7510 label2
->append("\\\\");
7512 } else if (c
== ')') {
7513 label2
->append("\\)");
7514 } else if (c
== '(') {
7515 label2
->append("\\(");
7516 } else if (c
< 0x20 || c
> 0x7e) {
7517 label2
->append(GooString::format("\\{0:03o}", c
));
7525 *needParens
= !(isNumeric
);
7530 // Write a DSC-compliant <textline>.
7531 void PSOutputDev::writePSTextLine(GooString
*s
) {
7535 // - DSC comments must be printable ASCII; control chars and
7536 // backslashes have to be escaped (we do cheap Unicode-to-ASCII
7537 // conversion by simply ignoring the high byte)
7538 // - lines are limited to 255 chars (we limit to 200 here to allow
7539 // for the keyword, which was emitted by the caller)
7540 // - lines that start with a left paren are treated as <text>
7541 // instead of <textline>, so we escape a leading paren
7542 if (s
->getLength() >= 2 &&
7543 (s
->getChar(0) & 0xff) == 0xfe &&
7544 (s
->getChar(1) & 0xff) == 0xff) {
7551 for (j
= 0; i
< s
->getLength() && j
< 200; i
+= step
) {
7552 c
= s
->getChar(i
) & 0xff;
7556 } else if (c
< 0x20 || c
> 0x7e || (j
== 0 && c
== '(')) {
7557 writePSFmt("\\{0:03o}", c
);