| //======================================================================== |
| // |
| // PSOutputDev.cc |
| // |
| // Copyright 1996-2013 Glyph & Cog, LLC |
| // |
| //======================================================================== |
| |
| //======================================================================== |
| // |
| // Modified under the Poppler project - http://poppler.freedesktop.org |
| // |
| // All changes made under the Poppler project to this file are licensed |
| // under GPL version 2 or later |
| // |
| // Copyright (C) 2005 Martin Kretzschmar <martink@gnome.org> |
| // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com> |
| // Copyright (C) 2006-2009, 2011-2013, 2015-2019 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2006 Jeff Muizelaar <jeff@infidigm.net> |
| // Copyright (C) 2007, 2008 Brad Hards <bradh@kde.org> |
| // Copyright (C) 2008, 2009 Koji Otani <sho@bbr.jp> |
| // Copyright (C) 2008, 2010 Hib Eris <hib@hiberis.nl> |
| // Copyright (C) 2009-2013 Thomas Freitag <Thomas.Freitag@alfa.de> |
| // Copyright (C) 2009 Till Kamppeter <till.kamppeter@gmail.com> |
| // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org> |
| // Copyright (C) 2009, 2011, 2012, 2014-2017, 2019 William Bader <williambader@hotmail.com> |
| // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net> |
| // Copyright (C) 2009-2011, 2013-2015, 2017 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2012, 2014 Fabio D'Urso <fabiodurso@hotmail.it> |
| // Copyright (C) 2012 Lu Wang <coolwanglu@gmail.com> |
| // Copyright (C) 2014 Till Kamppeter <till.kamppeter@gmail.com> |
| // Copyright (C) 2015 Marek Kasik <mkasik@redhat.com> |
| // Copyright (C) 2016 Caolán McNamara <caolanm@redhat.com> |
| // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich |
| // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
| // Copyright (C) 2018 Philipp Knechtges <philipp-dev@knechtges.com> |
| // Copyright (C) 2019 Christian Persch <chpe@src.gnome.org> |
| // Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de> |
| // |
| // To see a description of the changes please see the Changelog file that |
| // came with your tarball or type make ChangeLog if you are building from git |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #include <cstdio> |
| #include <cstddef> |
| #include <cstdarg> |
| #include <csignal> |
| #include <cmath> |
| #include <climits> |
| #include <algorithm> |
| #include <array> |
| #include "goo/GooString.h" |
| #include "poppler-config.h" |
| #include "GlobalParams.h" |
| #include "Object.h" |
| #include "Error.h" |
| #include "Function.h" |
| #include "Gfx.h" |
| #include "GfxState.h" |
| #include "GfxFont.h" |
| #include "UnicodeMap.h" |
| #include <fofi/FoFiType1C.h> |
| #include <fofi/FoFiTrueType.h> |
| #include "Catalog.h" |
| #include "Page.h" |
| #include "Stream.h" |
| #ifdef ENABLE_ZLIB |
| # include "FlateEncoder.h" |
| #endif |
| #ifdef ENABLE_ZLIB_UNCOMPRESS |
| # include "FlateStream.h" |
| #endif |
| #include "Annot.h" |
| #include "XRef.h" |
| #include "PreScanOutputDev.h" |
| #include "FileSpec.h" |
| #include "CharCodeToUnicode.h" |
| #ifdef HAVE_SPLASH |
| # include "splash/Splash.h" |
| # include "splash/SplashBitmap.h" |
| # include "SplashOutputDev.h" |
| #endif |
| #include "PSOutputDev.h" |
| #include "PDFDoc.h" |
| |
| // the MSVC math.h doesn't define this |
| #ifndef M_PI |
| #define M_PI 3.14159265358979323846 |
| #endif |
| |
| //------------------------------------------------------------------------ |
| |
| // Max size of a slice when rasterizing pages, in pixels. |
| #define rasterizationSliceSize 20000000 |
| |
| //------------------------------------------------------------------------ |
| // PostScript prolog and setup |
| //------------------------------------------------------------------------ |
| |
| // The '~' escapes mark prolog code that is emitted only in certain |
| // levels: |
| // |
| // ~[123][sn] |
| // ^ ^----- s=psLevel*Sep, n=psLevel* |
| // +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3* |
| |
| static const char *prolog[] = { |
| "/xpdf 75 dict def xpdf begin", |
| "% PDF special state", |
| "/pdfDictSize 15 def", |
| "~1sn", |
| "/pdfStates 64 array def", |
| " 0 1 63 {", |
| " pdfStates exch pdfDictSize dict", |
| " dup /pdfStateIdx 3 index put", |
| " put", |
| " } for", |
| "~123sn", |
| "/pdfSetup {", |
| " /setpagedevice where {", |
| " pop 2 dict begin", |
| " /Policies 1 dict dup begin /PageSize 6 def end def", |
| " { /Duplex true def } if", |
| " currentdict end setpagedevice", |
| " } {", |
| " pop", |
| " } ifelse", |
| "} def", |
| "/pdfSetupPaper {", |
| " % Change paper size, but only if different from previous paper size otherwise", |
| " % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size", |
| " % so we use the same when checking if the size changes.", |
| " /setpagedevice where {", |
| " pop currentpagedevice", |
| " /PageSize known {", |
| " 2 copy", |
| " currentpagedevice /PageSize get aload pop", |
| " exch 4 1 roll", |
| " sub abs 5 gt", |
| " 3 1 roll", |
| " sub abs 5 gt", |
| " or", |
| " } {", |
| " true", |
| " } ifelse", |
| " {", |
| " 2 array astore", |
| " 2 dict begin", |
| " /PageSize exch def", |
| " /ImagingBBox null def", |
| " currentdict end", |
| " setpagedevice", |
| " } {", |
| " pop pop", |
| " } ifelse", |
| " } {", |
| " pop", |
| " } ifelse", |
| "} def", |
| "~1sn", |
| "/pdfOpNames [", |
| " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", |
| " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender /pdfPatternCS", |
| " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", |
| "] def", |
| "~123sn", |
| "/pdfStartPage {", |
| "~1sn", |
| " pdfStates 0 get begin", |
| "~23sn", |
| " pdfDictSize dict begin", |
| "~23n", |
| " /pdfFillCS [] def", |
| " /pdfFillXform {} def", |
| " /pdfStrokeCS [] def", |
| " /pdfStrokeXform {} def", |
| "~1n", |
| " /pdfFill 0 def", |
| " /pdfStroke 0 def", |
| "~1s", |
| " /pdfFill [0 0 0 1] def", |
| " /pdfStroke [0 0 0 1] def", |
| "~23sn", |
| " /pdfFill [0] def", |
| " /pdfStroke [0] def", |
| " /pdfFillOP false def", |
| " /pdfStrokeOP false def", |
| "~3sn", |
| " /pdfOPM false def", |
| "~123sn", |
| " /pdfLastFill false def", |
| " /pdfLastStroke false def", |
| " /pdfTextMat [1 0 0 1 0 0] def", |
| " /pdfFontSize 0 def", |
| " /pdfCharSpacing 0 def", |
| " /pdfTextRender 0 def", |
| " /pdfPatternCS false def", |
| " /pdfTextRise 0 def", |
| " /pdfWordSpacing 0 def", |
| " /pdfHorizScaling 1 def", |
| " /pdfTextClipPath [] def", |
| "} def", |
| "/pdfEndPage { end } def", |
| "~23s", |
| "% separation convention operators", |
| "/findcmykcustomcolor where {", |
| " pop", |
| "}{", |
| " /findcmykcustomcolor { 5 array astore } def", |
| "} ifelse", |
| "/setcustomcolor where {", |
| " pop", |
| "}{", |
| " /setcustomcolor {", |
| " exch", |
| " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", |
| " 0 4 getinterval cvx", |
| " [ exch /dup load exch { mul exch dup } /forall load", |
| " /pop load dup ] cvx", |
| " ] setcolorspace setcolor", |
| " } def", |
| "} ifelse", |
| "/customcolorimage where {", |
| " pop", |
| "}{", |
| " /customcolorimage {", |
| " gsave", |
| " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", |
| " 0 4 getinterval", |
| " [ exch /dup load exch { mul exch dup } /forall load", |
| " /pop load dup ] cvx", |
| " ] setcolorspace", |
| " 10 dict begin", |
| " /ImageType 1 def", |
| " /DataSource exch def", |
| " /ImageMatrix exch def", |
| " /BitsPerComponent exch def", |
| " /Height exch def", |
| " /Width exch def", |
| " /Decode [1 0] def", |
| " currentdict end", |
| " image", |
| " grestore", |
| " } def", |
| "} ifelse", |
| "~123sn", |
| "% PDF color state", |
| "~1n", |
| "/g { dup /pdfFill exch def setgray", |
| " /pdfLastFill true def /pdfLastStroke false def } def", |
| "/G { dup /pdfStroke exch def setgray", |
| " /pdfLastStroke true def /pdfLastFill false def } def", |
| "/fCol {", |
| " pdfLastFill not {", |
| " pdfFill setgray", |
| " /pdfLastFill true def /pdfLastStroke false def", |
| " } if", |
| "} def", |
| "/sCol {", |
| " pdfLastStroke not {", |
| " pdfStroke setgray", |
| " /pdfLastStroke true def /pdfLastFill false def", |
| " } if", |
| "} def", |
| "~1s", |
| "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", |
| " /pdfLastFill true def /pdfLastStroke false def } def", |
| "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", |
| " /pdfLastStroke true def /pdfLastFill false def } def", |
| "/fCol {", |
| " pdfLastFill not {", |
| " pdfFill aload pop setcmykcolor", |
| " /pdfLastFill true def /pdfLastStroke false def", |
| " } if", |
| "} def", |
| "/sCol {", |
| " pdfLastStroke not {", |
| " pdfStroke aload pop setcmykcolor", |
| " /pdfLastStroke true def /pdfLastFill false def", |
| " } if", |
| "} def", |
| "~3n", |
| "/opm { dup /pdfOPM exch def", |
| " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def", |
| "~23n", |
| "/cs { /pdfFillXform exch def dup /pdfFillCS exch def", |
| " setcolorspace } def", |
| "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def", |
| " setcolorspace } def", |
| "/sc { pdfLastFill not { pdfFillCS setcolorspace } if", |
| " dup /pdfFill exch def aload pop pdfFillXform setcolor", |
| " /pdfLastFill true def /pdfLastStroke false def } def", |
| "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if", |
| " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor", |
| " /pdfLastStroke true def /pdfLastFill false def } def", |
| "/op { /pdfFillOP exch def", |
| " pdfLastFill { pdfFillOP setoverprint } if } def", |
| "/OP { /pdfStrokeOP exch def", |
| " pdfLastStroke { pdfStrokeOP setoverprint } if } def", |
| "/fCol {", |
| " pdfLastFill not {", |
| " pdfFillCS setcolorspace", |
| " pdfFill aload pop pdfFillXform setcolor", |
| " pdfFillOP setoverprint", |
| " /pdfLastFill true def /pdfLastStroke false def", |
| " } if", |
| "} def", |
| "/sCol {", |
| " pdfLastStroke not {", |
| " pdfStrokeCS setcolorspace", |
| " pdfStroke aload pop pdfStrokeXform setcolor", |
| " pdfStrokeOP setoverprint", |
| " /pdfLastStroke true def /pdfLastFill false def", |
| " } if", |
| "} def", |
| "~3s", |
| "/opm { dup /pdfOPM exch def", |
| " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def", |
| "~23s", |
| "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", |
| " /pdfLastFill true def /pdfLastStroke false def } def", |
| "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", |
| " /pdfLastStroke true def /pdfLastFill false def } def", |
| "/ck { 6 copy 6 array astore /pdfFill exch def", |
| " findcmykcustomcolor exch setcustomcolor", |
| " /pdfLastFill true def /pdfLastStroke false def } def", |
| "/CK { 6 copy 6 array astore /pdfStroke exch def", |
| " findcmykcustomcolor exch setcustomcolor", |
| " /pdfLastStroke true def /pdfLastFill false def } def", |
| "/op { /pdfFillOP exch def", |
| " pdfLastFill { pdfFillOP setoverprint } if } def", |
| "/OP { /pdfStrokeOP exch def", |
| " pdfLastStroke { pdfStrokeOP setoverprint } if } def", |
| "/fCol {", |
| " pdfLastFill not {", |
| " pdfFill aload length 4 eq {", |
| " setcmykcolor", |
| " }{", |
| " findcmykcustomcolor exch setcustomcolor", |
| " } ifelse", |
| " pdfFillOP setoverprint", |
| " /pdfLastFill true def /pdfLastStroke false def", |
| " } if", |
| "} def", |
| "/sCol {", |
| " pdfLastStroke not {", |
| " pdfStroke aload length 4 eq {", |
| " setcmykcolor", |
| " }{", |
| " findcmykcustomcolor exch setcustomcolor", |
| " } ifelse", |
| " pdfStrokeOP setoverprint", |
| " /pdfLastStroke true def /pdfLastFill false def", |
| " } if", |
| "} def", |
| "~123sn", |
| "% build a font", |
| "/pdfMakeFont {", |
| " 4 3 roll findfont", |
| " 4 2 roll matrix scale makefont", |
| " dup length dict begin", |
| " { 1 index /FID ne { def } { pop pop } ifelse } forall", |
| " /Encoding exch def", |
| " currentdict", |
| " end", |
| " definefont pop", |
| "} def", |
| "/pdfMakeFont16 {", |
| " exch findfont", |
| " dup length dict begin", |
| " { 1 index /FID ne { def } { pop pop } ifelse } forall", |
| " /WMode exch def", |
| " currentdict", |
| " end", |
| " definefont pop", |
| "} def", |
| "~3sn", |
| "/pdfMakeFont16L3 {", |
| " 1 index /CIDFont resourcestatus {", |
| " pop pop 1 index /CIDFont findresource /CIDFontType known", |
| " } {", |
| " false", |
| " } ifelse", |
| " {", |
| " 0 eq { /Identity-H } { /Identity-V } ifelse", |
| " exch 1 array astore composefont pop", |
| " } {", |
| " pdfMakeFont16", |
| " } ifelse", |
| "} def", |
| "~123sn", |
| "% graphics state operators", |
| "~1sn", |
| "/q {", |
| " gsave", |
| " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for", |
| " pdfStates pdfStateIdx 1 add get begin", |
| " pdfOpNames { exch def } forall", |
| "} def", |
| "/Q { end grestore } def", |
| "~23sn", |
| "/q { gsave pdfDictSize dict begin } def", |
| "/Q {", |
| " end grestore", |
| " /pdfLastFill where {", |
| " pop", |
| " pdfLastFill {", |
| " pdfFillOP setoverprint", |
| " } {", |
| " pdfStrokeOP setoverprint", |
| " } ifelse", |
| " } if", |
| "~3sn", |
| " /pdfOPM where {", |
| " pop", |
| " pdfOPM /setoverprintmode where{pop setoverprintmode}{pop}ifelse ", |
| " } if", |
| "~23sn", |
| "} def", |
| "~123sn", |
| "/cm { concat } def", |
| "/d { setdash } def", |
| "/i { setflat } def", |
| "/j { setlinejoin } def", |
| "/J { setlinecap } def", |
| "/M { setmiterlimit } def", |
| "/w { setlinewidth } def", |
| "% path segment operators", |
| "/m { moveto } def", |
| "/l { lineto } def", |
| "/c { curveto } def", |
| "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto", |
| " neg 0 rlineto closepath } def", |
| "/h { closepath } def", |
| "% path painting operators", |
| "/S { sCol stroke } def", |
| "/Sf { fCol stroke } def", |
| "/f { fCol fill } def", |
| "/f* { fCol eofill } def", |
| "% clipping operators", |
| "/W { clip newpath } def", |
| "/W* { eoclip newpath } def", |
| "/Ws { strokepath clip newpath } def", |
| "% text state operators", |
| "/Tc { /pdfCharSpacing exch def } def", |
| "/Tf { dup /pdfFontSize exch def", |
| " dup pdfHorizScaling mul exch matrix scale", |
| " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put", |
| " exch findfont exch makefont setfont } def", |
| "/Tr { /pdfTextRender exch def } def", |
| "/Tp { /pdfPatternCS exch def } def", |
| "/Ts { /pdfTextRise exch def } def", |
| "/Tw { /pdfWordSpacing exch def } def", |
| "/Tz { /pdfHorizScaling exch def } def", |
| "% text positioning operators", |
| "/Td { pdfTextMat transform moveto } def", |
| "/Tm { /pdfTextMat exch def } def", |
| "% text string operators", |
| "/xyshow where {", |
| " pop", |
| " /xyshow2 {", |
| " dup length array", |
| " 0 2 2 index length 1 sub {", |
| " 2 index 1 index 2 copy get 3 1 roll 1 add get", |
| " pdfTextMat dtransform", |
| " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put", |
| " } for", |
| " exch pop", |
| " xyshow", |
| " } def", |
| "}{", |
| " /xyshow2 {", |
| " currentfont /FontType get 0 eq {", |
| " 0 2 3 index length 1 sub {", |
| " currentpoint 4 index 3 index 2 getinterval show moveto", |
| " 2 copy get 2 index 3 2 roll 1 add get", |
| " pdfTextMat dtransform rmoveto", |
| " } for", |
| " } {", |
| " 0 1 3 index length 1 sub {", |
| " currentpoint 4 index 3 index 1 getinterval show moveto", |
| " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", |
| " pdfTextMat dtransform rmoveto", |
| " } for", |
| " } ifelse", |
| " pop pop", |
| " } def", |
| "} ifelse", |
| "/cshow where {", |
| " pop", |
| " /xycp {", // xycharpath |
| " 0 3 2 roll", |
| " {", |
| " pop pop currentpoint 3 2 roll", |
| " 1 string dup 0 4 3 roll put false charpath moveto", |
| " 2 copy get 2 index 2 index 1 add get", |
| " pdfTextMat dtransform rmoveto", |
| " 2 add", |
| " } exch cshow", |
| " pop pop", |
| " } def", |
| "}{", |
| " /xycp {", // xycharpath |
| " currentfont /FontType get 0 eq {", |
| " 0 2 3 index length 1 sub {", |
| " currentpoint 4 index 3 index 2 getinterval false charpath moveto", |
| " 2 copy get 2 index 3 2 roll 1 add get", |
| " pdfTextMat dtransform rmoveto", |
| " } for", |
| " } {", |
| " 0 1 3 index length 1 sub {", |
| " currentpoint 4 index 3 index 1 getinterval false charpath moveto", |
| " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", |
| " pdfTextMat dtransform rmoveto", |
| " } for", |
| " } ifelse", |
| " pop pop", |
| " } def", |
| "} ifelse", |
| "/Tj {", |
| " fCol", // because stringwidth has to draw Type 3 chars |
| " 0 pdfTextRise pdfTextMat dtransform rmoveto", |
| " currentpoint 4 2 roll", |
| " pdfTextRender 1 and 0 eq {", |
| " 2 copy xyshow2", |
| " } if", |
| " pdfTextRender 3 and dup 1 eq exch 2 eq or {", |
| " 3 index 3 index moveto", |
| " 2 copy", |
| " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", |
| " xycp currentpoint stroke moveto", |
| " } if", |
| " pdfTextRender 4 and 0 ne {", |
| " 4 2 roll moveto xycp", |
| " /pdfTextClipPath [ pdfTextClipPath aload pop", |
| " {/moveto cvx}", |
| " {/lineto cvx}", |
| " {/curveto cvx}", |
| " {/closepath cvx}", |
| " pathforall ] def", |
| " currentpoint newpath moveto", |
| " } {", |
| " pop pop pop pop", |
| " } ifelse", |
| " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", |
| "} def", |
| "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0", |
| " pdfTextMat dtransform rmoveto } def", |
| "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch", |
| " pdfTextMat dtransform rmoveto } def", |
| "/Tclip { pdfTextClipPath cvx exec clip newpath", |
| " /pdfTextClipPath [] def } def", |
| "/Tclip* { pdfTextClipPath cvx exec eoclip newpath", |
| " /pdfTextClipPath [] def } def", |
| "~1ns", |
| "% Level 1 image operators", |
| "/pdfIm1 {", |
| " /pdfImBuf1 4 index string def", |
| " { currentfile pdfImBuf1 readhexstring pop } image", |
| "} def", |
| "/pdfIm1Bin {", |
| " /pdfImBuf1 4 index string def", |
| " { currentfile pdfImBuf1 readstring pop } image", |
| "} def", |
| "~1s", |
| "/pdfIm1Sep {", |
| " /pdfImBuf1 4 index string def", |
| " /pdfImBuf2 4 index string def", |
| " /pdfImBuf3 4 index string def", |
| " /pdfImBuf4 4 index string def", |
| " { currentfile pdfImBuf1 readhexstring pop }", |
| " { currentfile pdfImBuf2 readhexstring pop }", |
| " { currentfile pdfImBuf3 readhexstring pop }", |
| " { currentfile pdfImBuf4 readhexstring pop }", |
| " true 4 colorimage", |
| "} def", |
| "/pdfIm1SepBin {", |
| " /pdfImBuf1 4 index string def", |
| " /pdfImBuf2 4 index string def", |
| " /pdfImBuf3 4 index string def", |
| " /pdfImBuf4 4 index string def", |
| " { currentfile pdfImBuf1 readstring pop }", |
| " { currentfile pdfImBuf2 readstring pop }", |
| " { currentfile pdfImBuf3 readstring pop }", |
| " { currentfile pdfImBuf4 readstring pop }", |
| " true 4 colorimage", |
| "} def", |
| "~1ns", |
| "/pdfImM1 {", |
| " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", |
| " { currentfile pdfImBuf1 readhexstring pop } imagemask", |
| "} def", |
| "/pdfImM1Bin {", |
| " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", |
| " { currentfile pdfImBuf1 readstring pop } imagemask", |
| "} def", |
| "/pdfImStr {", |
| " 2 copy exch length lt {", |
| " 2 copy get exch 1 add exch", |
| " } {", |
| " ()", |
| " } ifelse", |
| "} def", |
| "/pdfImM1a {", |
| " { pdfImStr } imagemask", |
| " pop pop", |
| "} def", |
| "~23sn", |
| "% Level 2/3 image operators", |
| "/pdfImBuf 100 string def", |
| "/pdfImStr {", |
| " 2 copy exch length lt {", |
| " 2 copy get exch 1 add exch", |
| " } {", |
| " ()", |
| " } ifelse", |
| "} def", |
| "/skipEOD {", |
| " { currentfile pdfImBuf readline", |
| " not { pop exit } if", |
| " (%-EOD-) eq { exit } if } loop", |
| "} def", |
| "/pdfIm { image skipEOD } def", |
| "~3sn", |
| "/pdfMask {", |
| " /ReusableStreamDecode filter", |
| " skipEOD", |
| " /maskStream exch def", |
| "} def", |
| "/pdfMaskEnd { maskStream closefile } def", |
| "/pdfMaskInit {", |
| " /maskArray exch def", |
| " /maskIdx 0 def", |
| "} def", |
| "/pdfMaskSrc {", |
| " maskIdx maskArray length lt {", |
| " maskArray maskIdx get", |
| " /maskIdx maskIdx 1 add def", |
| " } {", |
| " ()", |
| " } ifelse", |
| "} def", |
| "~23s", |
| "/pdfImSep {", |
| " findcmykcustomcolor exch", |
| " dup /Width get /pdfImBuf1 exch string def", |
| " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", |
| " /pdfImDecodeLow exch def", |
| " begin Width Height BitsPerComponent ImageMatrix DataSource end", |
| " /pdfImData exch def", |
| " { pdfImData pdfImBuf1 readstring pop", |
| " 0 1 2 index length 1 sub {", |
| " 1 index exch 2 copy get", |
| " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", |
| " 255 exch sub put", |
| " } for }", |
| " 6 5 roll customcolorimage", |
| " skipEOD", |
| "} def", |
| "~23sn", |
| "/pdfImM { fCol imagemask skipEOD } def", |
| "~123sn", |
| "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def", |
| "/pdfImClip {", |
| " gsave", |
| " 0 2 4 index length 1 sub {", |
| " dup 4 index exch 2 copy", |
| " get 5 index div put", |
| " 1 add 3 index exch 2 copy", |
| " get 3 index div put", |
| " } for", |
| " pop pop rectclip", |
| "} def", |
| "/pdfImClipEnd { grestore } def", |
| "~23sn", |
| "% shading operators", |
| "/colordelta {", |
| " false 0 1 3 index length 1 sub {", |
| " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {", |
| " pop true", |
| " } if", |
| " } for", |
| " exch pop exch pop", |
| "} def", |
| "/funcCol { func n array astore } def", |
| "/funcSH {", |
| " dup 0 eq {", |
| " true", |
| " } {", |
| " dup 6 eq {", |
| " false", |
| " } {", |
| " 4 index 4 index funcCol dup", |
| " 6 index 4 index funcCol dup", |
| " 3 1 roll colordelta 3 1 roll", |
| " 5 index 5 index funcCol dup", |
| " 3 1 roll colordelta 3 1 roll", |
| " 6 index 8 index funcCol dup", |
| " 3 1 roll colordelta 3 1 roll", |
| " colordelta or or or", |
| " } ifelse", |
| " } ifelse", |
| " {", |
| " 1 add", |
| " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch", |
| " 6 index 6 index 4 index 4 index 4 index funcSH", |
| " 2 index 6 index 6 index 4 index 4 index funcSH", |
| " 6 index 2 index 4 index 6 index 4 index funcSH", |
| " 5 3 roll 3 2 roll funcSH pop pop", |
| " } {", |
| " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul", |
| "~23n", |
| " funcCol sc", |
| "~23s", |
| " funcCol aload pop k", |
| "~23sn", |
| " dup 4 index exch mat transform m", |
| " 3 index 3 index mat transform l", |
| " 1 index 3 index mat transform l", |
| " mat transform l pop pop h f*", |
| " } ifelse", |
| "} def", |
| "/axialCol {", |
| " dup 0 lt {", |
| " pop t0", |
| " } {", |
| " dup 1 gt {", |
| " pop t1", |
| " } {", |
| " dt mul t0 add", |
| " } ifelse", |
| " } ifelse", |
| " func n array astore", |
| "} def", |
| "/axialSH {", |
| " dup 0 eq {", |
| " true", |
| " } {", |
| " dup 8 eq {", |
| " false", |
| " } {", |
| " 2 index axialCol 2 index axialCol colordelta", |
| " } ifelse", |
| " } ifelse", |
| " {", |
| " 1 add 3 1 roll 2 copy add 0.5 mul", |
| " dup 4 3 roll exch 4 index axialSH", |
| " exch 3 2 roll axialSH", |
| " } {", |
| " pop 2 copy add 0.5 mul", |
| "~23n", |
| " axialCol sc", |
| "~23s", |
| " axialCol aload pop k", |
| "~23sn", |
| " exch dup dx mul x0 add exch dy mul y0 add", |
| " 3 2 roll dup dx mul x0 add exch dy mul y0 add", |
| " dx abs dy abs ge {", |
| " 2 copy yMin sub dy mul dx div add yMin m", |
| " yMax sub dy mul dx div add yMax l", |
| " 2 copy yMax sub dy mul dx div add yMax l", |
| " yMin sub dy mul dx div add yMin l", |
| " h f*", |
| " } {", |
| " exch 2 copy xMin sub dx mul dy div add xMin exch m", |
| " xMax sub dx mul dy div add xMax exch l", |
| " exch 2 copy xMax sub dx mul dy div add xMax exch l", |
| " xMin sub dx mul dy div add xMin exch l", |
| " h f*", |
| " } ifelse", |
| " } ifelse", |
| "} def", |
| "/radialCol {", |
| " dup t0 lt {", |
| " pop t0", |
| " } {", |
| " dup t1 gt {", |
| " pop t1", |
| " } if", |
| " } ifelse", |
| " func n array astore", |
| "} def", |
| "/radialSH {", |
| " dup 0 eq {", |
| " true", |
| " } {", |
| " dup 8 eq {", |
| " false", |
| " } {", |
| " 2 index dt mul t0 add radialCol", |
| " 2 index dt mul t0 add radialCol colordelta", |
| " } ifelse", |
| " } ifelse", |
| " {", |
| " 1 add 3 1 roll 2 copy add 0.5 mul", |
| " dup 4 3 roll exch 4 index radialSH", |
| " exch 3 2 roll radialSH", |
| " } {", |
| " pop 2 copy add 0.5 mul dt mul t0 add", |
| "~23n", |
| " radialCol sc", |
| "~23s", |
| " radialCol aload pop k", |
| "~23sn", |
| " encl {", |
| " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", |
| " 0 360 arc h", |
| " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", |
| " 360 0 arcn h f", |
| " } {", |
| " 2 copy", |
| " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", |
| " a1 a2 arcn", |
| " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", |
| " a2 a1 arcn h", |
| " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", |
| " a1 a2 arc", |
| " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", |
| " a2 a1 arc h f", |
| " } ifelse", |
| " } ifelse", |
| "} def", |
| "~123sn", |
| "end", |
| nullptr |
| }; |
| |
| static const char *cmapProlog[] = { |
| "/CIDInit /ProcSet findresource begin", |
| "10 dict begin", |
| " begincmap", |
| " /CMapType 1 def", |
| " /CMapName /Identity-H def", |
| " /CIDSystemInfo 3 dict dup begin", |
| " /Registry (Adobe) def", |
| " /Ordering (Identity) def", |
| " /Supplement 0 def", |
| " end def", |
| " 1 begincodespacerange", |
| " <0000> <ffff>", |
| " endcodespacerange", |
| " 0 usefont", |
| " 1 begincidrange", |
| " <0000> <ffff> 0", |
| " endcidrange", |
| " endcmap", |
| " currentdict CMapName exch /CMap defineresource pop", |
| "end", |
| "10 dict begin", |
| " begincmap", |
| " /CMapType 1 def", |
| " /CMapName /Identity-V def", |
| " /CIDSystemInfo 3 dict dup begin", |
| " /Registry (Adobe) def", |
| " /Ordering (Identity) def", |
| " /Supplement 0 def", |
| " end def", |
| " /WMode 1 def", |
| " 1 begincodespacerange", |
| " <0000> <ffff>", |
| " endcodespacerange", |
| " 0 usefont", |
| " 1 begincidrange", |
| " <0000> <ffff> 0", |
| " endcidrange", |
| " endcmap", |
| " currentdict CMapName exch /CMap defineresource pop", |
| "end", |
| "end", |
| nullptr |
| }; |
| |
| //------------------------------------------------------------------------ |
| // Fonts |
| //------------------------------------------------------------------------ |
| |
| struct PSSubstFont { |
| const char *psName; // PostScript name |
| double mWidth; // width of 'm' character |
| }; |
| |
| // NB: must be in same order as base14SubstFonts in GfxFont.cc |
| static PSSubstFont psBase14SubstFonts[14] = { |
| {"Courier", 0.600}, |
| {"Courier-Oblique", 0.600}, |
| {"Courier-Bold", 0.600}, |
| {"Courier-BoldOblique", 0.600}, |
| {"Helvetica", 0.833}, |
| {"Helvetica-Oblique", 0.833}, |
| {"Helvetica-Bold", 0.889}, |
| {"Helvetica-BoldOblique", 0.889}, |
| {"Times-Roman", 0.788}, |
| {"Times-Italic", 0.722}, |
| {"Times-Bold", 0.833}, |
| {"Times-BoldItalic", 0.778}, |
| // the last two are never used for substitution |
| {"Symbol", 0}, |
| {"ZapfDingbats", 0} |
| }; |
| |
| // Mapping from Type 1/1C font file to PS font name. |
| struct PST1FontName { |
| Ref fontFileID; |
| GooString *psName; // PostScript font name used for this |
| // embedded font file |
| }; |
| |
| // Info for 8-bit fonts |
| struct PSFont8Info { |
| Ref fontID; |
| int *codeToGID; // code-to-GID mapping for TrueType fonts |
| }; |
| |
| // Encoding info for substitute 16-bit font |
| struct PSFont16Enc { |
| Ref fontID; |
| GooString *enc; |
| }; |
| |
| //------------------------------------------------------------------------ |
| // process colors |
| //------------------------------------------------------------------------ |
| |
| #define psProcessCyan 1 |
| #define psProcessMagenta 2 |
| #define psProcessYellow 4 |
| #define psProcessBlack 8 |
| #define psProcessCMYK 15 |
| |
| //------------------------------------------------------------------------ |
| // PSOutCustomColor |
| //------------------------------------------------------------------------ |
| |
| class PSOutCustomColor { |
| public: |
| |
| PSOutCustomColor(double cA, double mA, |
| double yA, double kA, GooString *nameA); |
| ~PSOutCustomColor(); |
| |
| PSOutCustomColor(const PSOutCustomColor &) = delete; |
| PSOutCustomColor& operator=(const PSOutCustomColor &) = delete; |
| |
| double c, m, y, k; |
| GooString *name; |
| PSOutCustomColor *next; |
| }; |
| |
| PSOutCustomColor::PSOutCustomColor(double cA, double mA, |
| double yA, double kA, GooString *nameA) { |
| c = cA; |
| m = mA; |
| y = yA; |
| k = kA; |
| name = nameA; |
| next = nullptr; |
| } |
| |
| PSOutCustomColor::~PSOutCustomColor() { |
| delete name; |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| struct PSOutImgClipRect { |
| int x0, x1, y0, y1; |
| }; |
| |
| //------------------------------------------------------------------------ |
| |
| struct PSOutPaperSize { |
| PSOutPaperSize(GooString *nameA, int wA, int hA) { name = nameA; w = wA; h = hA; } |
| ~PSOutPaperSize() { delete name; } |
| PSOutPaperSize(const PSOutPaperSize &) = delete; |
| PSOutPaperSize& operator=(const PSOutPaperSize &) = delete; |
| GooString *name; |
| int w, h; |
| }; |
| |
| //------------------------------------------------------------------------ |
| // DeviceNRecoder |
| //------------------------------------------------------------------------ |
| |
| class DeviceNRecoder: public FilterStream { |
| public: |
| |
| DeviceNRecoder(Stream *strA, int widthA, int heightA, |
| GfxImageColorMap *colorMapA); |
| ~DeviceNRecoder() override; |
| StreamKind getKind() const override { return strWeird; } |
| void reset() override; |
| int getChar() override |
| { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } |
| int lookChar() override |
| { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } |
| GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; } |
| bool isBinary(bool last = true) override { return true; } |
| bool isEncoder() override { return true; } |
| |
| private: |
| |
| bool fillBuf(); |
| |
| int width, height; |
| GfxImageColorMap *colorMap; |
| const Function *func; |
| ImageStream *imgStr; |
| int buf[gfxColorMaxComps]; |
| int pixelIdx; |
| int bufIdx; |
| int bufSize; |
| }; |
| |
| DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, |
| GfxImageColorMap *colorMapA): |
| FilterStream(strA) { |
| width = widthA; |
| height = heightA; |
| colorMap = colorMapA; |
| imgStr = nullptr; |
| pixelIdx = 0; |
| bufIdx = gfxColorMaxComps; |
| bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> |
| getAlt()->getNComps(); |
| func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> |
| getTintTransformFunc(); |
| } |
| |
| DeviceNRecoder::~DeviceNRecoder() { |
| if (imgStr) { |
| delete imgStr; |
| } |
| if (str->isEncoder()) { |
| delete str; |
| } |
| } |
| |
| void DeviceNRecoder::reset() { |
| imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), |
| colorMap->getBits()); |
| imgStr->reset(); |
| } |
| |
| bool DeviceNRecoder::fillBuf() { |
| unsigned char pixBuf[gfxColorMaxComps]; |
| GfxColor color; |
| double x[gfxColorMaxComps], y[gfxColorMaxComps]; |
| int i; |
| |
| if (pixelIdx >= width * height) { |
| return false; |
| } |
| imgStr->getPixel(pixBuf); |
| colorMap->getColor(pixBuf, &color); |
| for (i = 0; |
| i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps(); |
| ++i) { |
| x[i] = colToDbl(color.c[i]); |
| } |
| func->transform(x, y); |
| for (i = 0; i < bufSize; ++i) { |
| buf[i] = (int)(y[i] * 255 + 0.5); |
| } |
| bufIdx = 0; |
| ++pixelIdx; |
| return true; |
| } |
| |
| //------------------------------------------------------------------------ |
| // PSOutputDev |
| //------------------------------------------------------------------------ |
| |
| extern "C" { |
| typedef void (*SignalFunc)(int); |
| } |
| |
| static void outputToFile(void *stream, const char *data, int len) { |
| fwrite(data, 1, len, (FILE *)stream); |
| } |
| |
| PSOutputDev::PSOutputDev(const char *fileName, PDFDoc *docA, |
| char *psTitleA, |
| const std::vector<int> &pagesA, PSOutMode modeA, |
| int paperWidthA, int paperHeightA, |
| bool noCropA, bool duplexA, |
| int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, |
| bool forceRasterizeA, |
| bool manualCtrlA, |
| PSOutCustomCodeCbk customCodeCbkA, |
| void *customCodeCbkDataA) { |
| FILE *f; |
| PSFileType fileTypeA; |
| |
| underlayCbk = nullptr; |
| underlayCbkData = nullptr; |
| overlayCbk = nullptr; |
| overlayCbkData = nullptr; |
| customCodeCbk = customCodeCbkA; |
| customCodeCbkData = customCodeCbkDataA; |
| |
| fontIDs = nullptr; |
| t1FontNames = nullptr; |
| font8Info = nullptr; |
| font16Enc = nullptr; |
| imgIDs = nullptr; |
| formIDs = nullptr; |
| paperSizes = nullptr; |
| embFontList = nullptr; |
| customColors = nullptr; |
| haveTextClip = false; |
| t3String = nullptr; |
| forceRasterize = forceRasterizeA; |
| psTitle = nullptr; |
| |
| // open file or pipe |
| if (!strcmp(fileName, "-")) { |
| fileTypeA = psStdout; |
| f = stdout; |
| } else if (fileName[0] == '|') { |
| fileTypeA = psPipe; |
| #ifdef HAVE_POPEN |
| #ifndef _WIN32 |
| signal(SIGPIPE, (SignalFunc)SIG_IGN); |
| #endif |
| if (!(f = popen(fileName + 1, "w"))) { |
| error(errIO, -1, "Couldn't run print command '{0:s}'", fileName); |
| ok = false; |
| return; |
| } |
| #else |
| error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName); |
| ok = false; |
| return; |
| #endif |
| } else { |
| fileTypeA = psFile; |
| if (!(f = openFile(fileName, "w"))) { |
| error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); |
| ok = false; |
| return; |
| } |
| } |
| |
| init(outputToFile, f, fileTypeA, psTitleA, |
| docA, pagesA, modeA, |
| imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, |
| paperWidthA, paperHeightA, noCropA, duplexA); |
| } |
| |
| PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, |
| char *psTitleA, |
| PDFDoc *docA, |
| const std::vector<int> &pagesA, PSOutMode modeA, |
| int paperWidthA, int paperHeightA, |
| bool noCropA, bool duplexA, |
| int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, |
| bool forceRasterizeA, |
| bool manualCtrlA, |
| PSOutCustomCodeCbk customCodeCbkA, |
| void *customCodeCbkDataA) { |
| underlayCbk = nullptr; |
| underlayCbkData = nullptr; |
| overlayCbk = nullptr; |
| overlayCbkData = nullptr; |
| customCodeCbk = customCodeCbkA; |
| customCodeCbkData = customCodeCbkDataA; |
| |
| fontIDs = nullptr; |
| t1FontNames = nullptr; |
| font8Info = nullptr; |
| font16Enc = nullptr; |
| imgIDs = nullptr; |
| formIDs = nullptr; |
| paperSizes = nullptr; |
| embFontList = nullptr; |
| customColors = nullptr; |
| haveTextClip = false; |
| t3String = nullptr; |
| forceRasterize = forceRasterizeA; |
| psTitle = nullptr; |
| |
| init(outputFuncA, outputStreamA, psGeneric, psTitleA, |
| docA, pagesA, modeA, |
| imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, |
| paperWidthA, paperHeightA, noCropA, duplexA); |
| } |
| |
| struct StandardMedia { |
| const char *name; |
| int width; |
| int height; |
| }; |
| |
| static const StandardMedia standardMedia[] = |
| { |
| { "A0", 2384, 3371 }, |
| { "A1", 1685, 2384 }, |
| { "A2", 1190, 1684 }, |
| { "A3", 842, 1190 }, |
| { "A4", 595, 842 }, |
| { "A5", 420, 595 }, |
| { "B4", 729, 1032 }, |
| { "B5", 516, 729 }, |
| { "Letter", 612, 792 }, |
| { "Tabloid", 792, 1224 }, |
| { "Ledger", 1224, 792 }, |
| { "Legal", 612, 1008 }, |
| { "Statement", 396, 612 }, |
| { "Executive", 540, 720 }, |
| { "Folio", 612, 936 }, |
| { "Quarto", 610, 780 }, |
| { "10x14", 720, 1008 }, |
| { nullptr, 0, 0 } |
| }; |
| |
| /* PLRM specifies a tolerance of 5 points when matching page sizes */ |
| static bool pageDimensionEqual(int a, int b) { |
| return (abs (a - b) < 5); |
| } |
| |
| // Shared initialization of PSOutputDev members. |
| // Store the values but do not process them so the function that |
| // created the PSOutputDev can use the various setters to change defaults. |
| |
| void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, |
| PSFileType fileTypeA, char *psTitleA, PDFDoc *docA, |
| const std::vector<int> &pagesA, PSOutMode modeA, |
| int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, |
| bool manualCtrlA, int paperWidthA, int paperHeightA, |
| bool noCropA, bool duplexA) { |
| |
| if (pagesA.empty()) { |
| ok = false; |
| return; |
| } |
| |
| // initialize |
| postInitDone = false; |
| embedType1 = true; |
| embedTrueType = true; |
| embedCIDPostScript = true; |
| embedCIDTrueType = true; |
| fontPassthrough = false; |
| optimizeColorSpace = false; |
| passLevel1CustomColor = false; |
| preloadImagesForms = false; |
| generateOPI = false; |
| useASCIIHex = false; |
| useBinary = false; |
| enableLZW = true; |
| enableFlate = true; |
| rasterMono = false; |
| rasterResolution = 300; |
| uncompressPreloadedImages = false; |
| psCenter = true; |
| rasterAntialias = false; |
| displayText = true; |
| ok = true; |
| outputFunc = outputFuncA; |
| outputStream = outputStreamA; |
| fileType = fileTypeA; |
| psTitle = (psTitleA? strdup(psTitleA): nullptr); |
| doc = docA; |
| level = globalParams->getPSLevel(); |
| pages = pagesA; |
| mode = modeA; |
| paperWidth = paperWidthA; |
| paperHeight = paperHeightA; |
| noCrop = noCropA; |
| duplex = duplexA; |
| imgLLX = imgLLXA; |
| imgLLY = imgLLYA; |
| imgURX = imgURXA; |
| imgURY = imgURYA; |
| manualCtrl = manualCtrlA; |
| |
| xref = nullptr; |
| |
| processColors = 0; |
| inType3Char = false; |
| inUncoloredPattern = false; |
| t3FillColorOnly = false; |
| |
| #ifdef OPI_SUPPORT |
| // initialize OPI nesting levels |
| opi13Nest = 0; |
| opi20Nest = 0; |
| #endif |
| |
| tx0 = ty0 = -1; |
| xScale0 = yScale0 = 0; |
| rotate0 = -1; |
| clipLLX0 = clipLLY0 = 0; |
| clipURX0 = clipURY0 = -1; |
| |
| // initialize sequential page number |
| seqPage = 1; |
| } |
| |
| // Complete the initialization after the function that created the PSOutputDev |
| // has had a chance to modify default values with the various setters. |
| |
| void PSOutputDev::postInit() |
| { |
| Catalog *catalog; |
| PDFRectangle *box; |
| PSOutPaperSize *size; |
| int w, h, i; |
| |
| if (postInitDone || !ok) { |
| return; |
| } |
| |
| postInitDone = true; |
| |
| xref = doc->getXRef(); |
| catalog = doc->getCatalog(); |
| |
| if (paperWidth < 0 || paperHeight < 0) { |
| paperMatch = true; |
| } else { |
| paperMatch = false; |
| } |
| |
| paperSizes = new std::vector<PSOutPaperSize*>(); |
| for (const int pg : pages) { |
| Page *page = catalog->getPage(pg); |
| if (page == nullptr) |
| paperMatch = false; |
| if (!paperMatch) { |
| w = paperWidth; |
| h = paperHeight; |
| if (w < 0 || h < 0) { |
| // Unable to obtain a paper size from the document and no page size |
| // specified. In this case use A4 as the page size to ensure the PS output is |
| // valid. This will only occur if the PDF is very broken. |
| w = 595; |
| h = 842; |
| } |
| } else if (noCrop) { |
| w = (int)ceil(page->getMediaWidth()); |
| h = (int)ceil(page->getMediaHeight()); |
| } else { |
| w = (int)ceil(page->getCropWidth()); |
| h = (int)ceil(page->getCropHeight()); |
| } |
| if (paperMatch) { |
| const int pageRotate = page->getRotate(); |
| if (pageRotate == 90 || pageRotate == 270) |
| std::swap(w, h); |
| } |
| if (w > paperWidth) |
| paperWidth = w; |
| if (h > paperHeight) |
| paperHeight = h; |
| for (i = 0; i < (int)paperSizes->size(); ++i) { |
| size = (*paperSizes)[i]; |
| if (pageDimensionEqual(w, size->w) && pageDimensionEqual(h, size->h)) |
| break; |
| } |
| if (i == (int)paperSizes->size()) { |
| const StandardMedia *media = standardMedia; |
| GooString *name = nullptr; |
| while (media->name) { |
| if (pageDimensionEqual(w, media->width) && pageDimensionEqual(h, media->height)) { |
| name = new GooString(media->name); |
| w = media->width; |
| h = media->height; |
| break; |
| } |
| media++; |
| } |
| if (!name) |
| name = GooString::format("{0:d}x{1:d}mm", int(w*25.4/72), int(h*25.4/72)); |
| paperSizes->push_back(new PSOutPaperSize(name, w, h)); |
| } |
| pagePaperSize.insert(std::pair<int,int>(pg, i)); |
| if (!paperMatch) |
| break; // we only need one entry when all pages are the same size |
| } |
| if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) { |
| imgLLX = imgLLY = 0; |
| imgURX = paperWidth; |
| imgURY = paperHeight; |
| } |
| std::vector<int> pageList; |
| if (mode == psModeForm) { |
| pageList.push_back(pages[0]); |
| } else { |
| pageList = pages; |
| } |
| |
| // initialize fontIDs, fontFileIDs, and fontFileNames lists |
| fontIDSize = 64; |
| fontIDLen = 0; |
| fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref)); |
| for (i = 0; i < 14; ++i) { |
| fontNames.emplace(psBase14SubstFonts[i].psName); |
| } |
| t1FontNameSize = 64; |
| t1FontNameLen = 0; |
| t1FontNames = (PST1FontName *)gmallocn(t1FontNameSize, sizeof(PST1FontName)); |
| font8InfoLen = 0; |
| font8InfoSize = 0; |
| font16EncLen = 0; |
| font16EncSize = 0; |
| imgIDLen = 0; |
| imgIDSize = 0; |
| formIDLen = 0; |
| formIDSize = 0; |
| |
| numSaves = 0; |
| numTilingPatterns = 0; |
| nextFunc = 0; |
| |
| // initialize embedded font resource comment list |
| embFontList = new GooString(); |
| |
| if (!manualCtrl) { |
| Page *page; |
| // this check is needed in case the document has zero pages |
| if ((page = doc->getPage(pageList[0]))) { |
| writeHeader(pageList.size(), |
| page->getMediaBox(), |
| page->getCropBox(), |
| page->getRotate(), |
| psTitle); |
| } else { |
| error(errSyntaxError, -1, "Invalid page {0:d}", pageList[0]); |
| box = new PDFRectangle(0, 0, 1, 1); |
| writeHeader(pageList.size(), box, box, 0, psTitle); |
| delete box; |
| } |
| if (mode != psModeForm) { |
| writePS("%%BeginProlog\n"); |
| } |
| writeXpdfProcset(); |
| if (mode != psModeForm) { |
| writePS("%%EndProlog\n"); |
| writePS("%%BeginSetup\n"); |
| } |
| writeDocSetup(catalog, pageList, duplex); |
| if (mode != psModeForm) { |
| writePS("%%EndSetup\n"); |
| } |
| } |
| } |
| |
| PSOutputDev::~PSOutputDev() { |
| PSOutCustomColor *cc; |
| int i; |
| |
| if (ok) { |
| if (!postInitDone) { |
| postInit(); |
| } |
| if (!manualCtrl) { |
| writePS("%%Trailer\n"); |
| writeTrailer(); |
| if (mode != psModeForm) { |
| writePS("%%EOF\n"); |
| } |
| } |
| if (fileType == psFile) { |
| fclose((FILE *)outputStream); |
| } |
| #ifdef HAVE_POPEN |
| else if (fileType == psPipe) { |
| pclose((FILE *)outputStream); |
| #ifndef _WIN32 |
| signal(SIGPIPE, (SignalFunc)SIG_DFL); |
| #endif |
| } |
| #endif |
| } |
| if (paperSizes) { |
| for (auto entry : *paperSizes) { |
| delete entry; |
| } |
| delete paperSizes; |
| } |
| if (embFontList) { |
| delete embFontList; |
| } |
| if (fontIDs) { |
| gfree(fontIDs); |
| } |
| if (t1FontNames) { |
| for (i = 0; i < t1FontNameLen; ++i) { |
| delete t1FontNames[i].psName; |
| } |
| gfree(t1FontNames); |
| } |
| if (font8Info) { |
| for (i = 0; i < font8InfoLen; ++i) { |
| gfree(font8Info[i].codeToGID); |
| } |
| gfree(font8Info); |
| } |
| if (font16Enc) { |
| for (i = 0; i < font16EncLen; ++i) { |
| if (font16Enc[i].enc) { |
| delete font16Enc[i].enc; |
| } |
| } |
| gfree(font16Enc); |
| } |
| gfree(imgIDs); |
| gfree(formIDs); |
| while (customColors) { |
| cc = customColors; |
| customColors = cc->next; |
| delete cc; |
| } |
| gfree(psTitle); |
| } |
| |
| void PSOutputDev::writeHeader(int nPages, |
| const PDFRectangle *mediaBox, const PDFRectangle *cropBox, |
| int pageRotate, const char *title) { |
| PSOutPaperSize *size; |
| double x1, y1, x2, y2; |
| |
| switch (mode) { |
| case psModePS: |
| writePS("%!PS-Adobe-3.0\n"); |
| break; |
| case psModeEPS: |
| writePS("%!PS-Adobe-3.0 EPSF-3.0\n"); |
| break; |
| case psModeForm: |
| writePS("%!PS-Adobe-3.0 Resource-Form\n"); |
| break; |
| } |
| writePSFmt("%Produced by poppler pdftops version: {0:s} (http://poppler.freedesktop.org)\n", PACKAGE_VERSION); |
| Object info = xref->getDocInfo(); |
| if (info.isDict()) { |
| Object obj1 = info.dictLookup("Creator"); |
| if (obj1.isString()) { |
| writePS("%%Creator: "); |
| writePSTextLine(obj1.getString()); |
| } |
| } |
| if(title) { |
| char *sanitizedTitle = strdup(title); |
| for (size_t i = 0; i < strlen(sanitizedTitle); ++i) { |
| if (sanitizedTitle[i] == '\n' || sanitizedTitle[i] == '\r') { |
| sanitizedTitle[i] = ' '; |
| } |
| } |
| writePSFmt("%%Title: {0:s}\n", sanitizedTitle); |
| free(sanitizedTitle); |
| } |
| writePSFmt("%%LanguageLevel: {0:d}\n", |
| (level == psLevel1 || level == psLevel1Sep) ? 1 : |
| (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); |
| if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { |
| writePS("%%DocumentProcessColors: (atend)\n"); |
| writePS("%%DocumentCustomColors: (atend)\n"); |
| } |
| writePS("%%DocumentSuppliedResources: (atend)\n"); |
| if ((level == psLevel1 || level == psLevel1Sep) && useBinary) { |
| writePS("%%DocumentData: Binary\n"); |
| } |
| |
| switch (mode) { |
| case psModePS: |
| for (std::size_t i = 0; i < paperSizes->size(); ++i) { |
| size = (*paperSizes)[i]; |
| writePSFmt("%%{0:s} {1:t} {2:d} {3:d} 0 () ()\n", |
| i==0 ? "DocumentMedia:" : "+", size->name, size->w, size->h); |
| } |
| writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight); |
| writePSFmt("%%Pages: {0:d}\n", nPages); |
| writePS("%%EndComments\n"); |
| if (!paperMatch) { |
| size = (*paperSizes)[0]; |
| writePS("%%BeginDefaults\n"); |
| writePSFmt("%%PageMedia: {0:t}\n", size->name); |
| writePS("%%EndDefaults\n"); |
| } |
| break; |
| case psModeEPS: |
| epsX1 = cropBox->x1; |
| epsY1 = cropBox->y1; |
| epsX2 = cropBox->x2; |
| epsY2 = cropBox->y2; |
| if (pageRotate == 0 || pageRotate == 180) { |
| x1 = epsX1; |
| y1 = epsY1; |
| x2 = epsX2; |
| y2 = epsY2; |
| } else { // pageRotate == 90 || pageRotate == 270 |
| x1 = 0; |
| y1 = 0; |
| x2 = epsY2 - epsY1; |
| y2 = epsX2 - epsX1; |
| } |
| writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n", |
| (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2)); |
| writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", |
| x1, y1, x2, y2); |
| writePS("%%DocumentSuppliedResources: (atend)\n"); |
| writePS("%%EndComments\n"); |
| break; |
| case psModeForm: |
| writePS("%%EndComments\n"); |
| writePS("32 dict dup begin\n"); |
| writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n", |
| (int)floor(mediaBox->x1), (int)floor(mediaBox->y1), |
| (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2)); |
| writePS("/FormType 1 def\n"); |
| writePS("/Matrix [1 0 0 1 0 0] def\n"); |
| break; |
| } |
| } |
| |
| void PSOutputDev::writeXpdfProcset() { |
| bool lev1, lev2, lev3, sep, nonSep; |
| const char **p; |
| const char *q; |
| |
| writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", "3.00"); |
| writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright); |
| lev1 = lev2 = lev3 = sep = nonSep = true; |
| for (p = prolog; *p; ++p) { |
| if ((*p)[0] == '~') { |
| lev1 = lev2 = lev3 = sep = nonSep = false; |
| for (q = *p + 1; *q; ++q) { |
| switch (*q) { |
| case '1': lev1 = true; break; |
| case '2': lev2 = true; break; |
| case '3': lev3 = true; break; |
| case 's': sep = true; break; |
| case 'n': nonSep = true; break; |
| } |
| } |
| } else if ((level == psLevel1 && lev1 && nonSep) || |
| (level == psLevel1Sep && lev1 && sep) || |
| (level == psLevel1Sep && lev2 && sep && getPassLevel1CustomColor()) || |
| (level == psLevel2 && lev2 && nonSep) || |
| (level == psLevel2Sep && lev2 && sep) || |
| (level == psLevel3 && lev3 && nonSep) || |
| (level == psLevel3Sep && lev3 && sep)) { |
| writePSFmt("{0:s}\n", *p); |
| } |
| } |
| writePS("%%EndResource\n"); |
| |
| if (level >= psLevel3) { |
| for (p = cmapProlog; *p; ++p) { |
| writePSFmt("{0:s}\n", *p); |
| } |
| } |
| } |
| |
| void PSOutputDev::writeDocSetup(Catalog *catalog, |
| const std::vector<int> &pageList, |
| bool duplexA) { |
| Page *page; |
| Dict *resDict; |
| Annots *annots; |
| Object *acroForm; |
| GooString *s; |
| |
| if (mode == psModeForm) { |
| // swap the form and xpdf dicts |
| writePS("xpdf end begin dup begin\n"); |
| } else { |
| writePS("xpdf begin\n"); |
| } |
| for (const int pg : pageList) { |
| page = doc->getPage(pg); |
| if (!page) { |
| error(errSyntaxError, -1, "Failed writing resources for page {0:d}", pg); |
| continue; |
| } |
| if ((resDict = page->getResourceDict())) { |
| setupResources(resDict); |
| } |
| annots = page->getAnnots(); |
| for (int i = 0; i < annots->getNumAnnots(); ++i) { |
| Object obj1 = annots->getAnnot(i)->getAppearanceResDict(); |
| if (obj1.isDict()) { |
| setupResources(obj1.getDict()); |
| } |
| } |
| } |
| if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) { |
| Object obj1 = acroForm->dictLookup("DR"); |
| if (obj1.isDict()) { |
| setupResources(obj1.getDict()); |
| } |
| obj1 = acroForm->dictLookup("Fields"); |
| if (obj1.isArray()) { |
| for (int i = 0; i < obj1.arrayGetLength(); ++i) { |
| Object obj2 = obj1.arrayGet(i); |
| if (obj2.isDict()) { |
| Object obj3 = obj2.dictLookup("DR"); |
| if (obj3.isDict()) { |
| setupResources(obj3.getDict()); |
| } |
| } |
| } |
| } |
| } |
| if (mode != psModeForm) { |
| if (mode != psModeEPS && !manualCtrl) { |
| writePSFmt("{0:s} pdfSetup\n", |
| duplexA ? "true" : "false"); |
| if (!paperMatch) { |
| writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight); |
| } |
| } |
| #ifdef OPI_SUPPORT |
| if (generateOPI) { |
| writePS("/opiMatrix matrix currentmatrix def\n"); |
| } |
| #endif |
| } |
| if (customCodeCbk) { |
| if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, |
| customCodeCbkData))) { |
| writePS(s->c_str()); |
| delete s; |
| } |
| } |
| } |
| |
| void PSOutputDev::writePageTrailer() { |
| if (mode != psModeForm) { |
| writePS("pdfEndPage\n"); |
| } |
| } |
| |
| void PSOutputDev::writeTrailer() { |
| PSOutCustomColor *cc; |
| |
| if (mode == psModeForm) { |
| writePS("/Foo exch /Form defineresource pop\n"); |
| } else { |
| writePS("end\n"); |
| writePS("%%DocumentSuppliedResources:\n"); |
| writePS(embFontList->c_str()); |
| if (level == psLevel1Sep || level == psLevel2Sep || |
| level == psLevel3Sep) { |
| writePS("%%DocumentProcessColors:"); |
| if (processColors & psProcessCyan) { |
| writePS(" Cyan"); |
| } |
| if (processColors & psProcessMagenta) { |
| writePS(" Magenta"); |
| } |
| if (processColors & psProcessYellow) { |
| writePS(" Yellow"); |
| } |
| if (processColors & psProcessBlack) { |
| writePS(" Black"); |
| } |
| writePS("\n"); |
| writePS("%%DocumentCustomColors:"); |
| for (cc = customColors; cc; cc = cc->next) { |
| writePS(" "); |
| writePSString(cc->name->toStr()); |
| } |
| writePS("\n"); |
| writePS("%%CMYKCustomColor:\n"); |
| for (cc = customColors; cc; cc = cc->next) { |
| writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", |
| cc->c, cc->m, cc->y, cc->k); |
| writePSString(cc->name->toStr()); |
| writePS("\n"); |
| } |
| } |
| } |
| } |
| |
| void PSOutputDev::setupResources(Dict *resDict) { |
| bool skip; |
| |
| setupFonts(resDict); |
| setupImages(resDict); |
| setupForms(resDict); |
| |
| //----- recursively scan XObjects |
| Object xObjDict = resDict->lookup("XObject"); |
| if (xObjDict.isDict()) { |
| for (int i = 0; i < xObjDict.dictGetLength(); ++i) { |
| |
| // avoid infinite recursion on XObjects |
| skip = false; |
| const Object &xObjRef = xObjDict.dictGetValNF(i); |
| if (xObjRef.isRef()) { |
| Ref ref0 = xObjRef.getRef(); |
| if (resourceIDs.find(ref0.num) != resourceIDs.end()) { |
| skip = true; |
| } else { |
| resourceIDs.insert(ref0.num); |
| } |
| } |
| if (!skip) { |
| |
| // process the XObject's resource dictionary |
| Object xObj = xObjDict.dictGetVal(i); |
| if (xObj.isStream()) { |
| Object resObj = xObj.streamGetDict()->lookup("Resources"); |
| if (resObj.isDict()) { |
| setupResources(resObj.getDict()); |
| } |
| } |
| } |
| } |
| } |
| |
| //----- recursively scan Patterns |
| Object patDict = resDict->lookup("Pattern"); |
| if (patDict.isDict()) { |
| inType3Char = true; |
| for (int i = 0; i < patDict.dictGetLength(); ++i) { |
| |
| // avoid infinite recursion on Patterns |
| skip = false; |
| const Object &patRef = patDict.dictGetValNF(i); |
| if (patRef.isRef()) { |
| Ref ref0 = patRef.getRef(); |
| if (resourceIDs.find(ref0.num) != resourceIDs.end()) { |
| skip = true; |
| } else { |
| resourceIDs.insert(ref0.num); |
| } |
| } |
| if (!skip) { |
| |
| // process the Pattern's resource dictionary |
| Object pat = patDict.dictGetVal(i); |
| if (pat.isStream()) { |
| Object resObj = pat.streamGetDict()->lookup("Resources"); |
| if (resObj.isDict()) { |
| setupResources(resObj.getDict()); |
| } |
| } |
| } |
| } |
| inType3Char = false; |
| } |
| } |
| |
| void PSOutputDev::setupFonts(Dict *resDict) { |
| Ref r; |
| GfxFontDict *gfxFontDict; |
| GfxFont *font; |
| int i; |
| |
| gfxFontDict = nullptr; |
| const Object &obj1 = resDict->lookupNF("Font"); |
| if (obj1.isRef()) { |
| Object obj2 = obj1.fetch(xref); |
| if (obj2.isDict()) { |
| r = obj1.getRef(); |
| gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict()); |
| } |
| } else if (obj1.isDict()) { |
| gfxFontDict = new GfxFontDict(xref, nullptr, obj1.getDict()); |
| } |
| if (gfxFontDict) { |
| for (i = 0; i < gfxFontDict->getNumFonts(); ++i) { |
| if ((font = gfxFontDict->getFont(i))) { |
| setupFont(font, resDict); |
| } |
| } |
| delete gfxFontDict; |
| } |
| } |
| |
| void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { |
| GfxFontLoc *fontLoc; |
| GooString *psName; |
| char buf[16]; |
| bool subst; |
| UnicodeMap *uMap; |
| const char *charName; |
| double xs, ys; |
| int code; |
| double w1, w2; |
| int i, j; |
| |
| // check if font is already set up |
| for (i = 0; i < fontIDLen; ++i) { |
| if (fontIDs[i] == *font->getID()) { |
| return; |
| } |
| } |
| |
| // add entry to fontIDs list |
| if (fontIDLen >= fontIDSize) { |
| fontIDSize += 64; |
| fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref)); |
| } |
| fontIDs[fontIDLen++] = *font->getID(); |
| |
| psName = nullptr; |
| xs = ys = 1; |
| subst = false; |
| |
| if (font->getType() == fontType3) { |
| psName = GooString::format("T3_{0:d}_{1:d}", |
| font->getID()->num, font->getID()->gen); |
| setupType3Font(font, psName, parentResDict); |
| } else { |
| fontLoc = font->locateFont(xref, this); |
| if (fontLoc != nullptr) { |
| switch (fontLoc->locType) { |
| case gfxFontLocEmbedded: |
| switch (fontLoc->fontType) { |
| case fontType1: |
| // this assumes that the PS font name matches the PDF font name |
| psName = font->getEmbeddedFontName() ? font->getEmbeddedFontName()->copy() : new GooString(); |
| setupEmbeddedType1Font(&fontLoc->embFontID, psName); |
| break; |
| case fontType1C: |
| psName = makePSFontName(font, &fontLoc->embFontID); |
| setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName); |
| break; |
| case fontType1COT: |
| psName = makePSFontName(font, &fontLoc->embFontID); |
| setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName); |
| break; |
| case fontTrueType: |
| case fontTrueTypeOT: |
| psName = makePSFontName(font, font->getID()); |
| setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName); |
| break; |
| case fontCIDType0C: |
| psName = makePSFontName(font, &fontLoc->embFontID); |
| setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName); |
| break; |
| case fontCIDType2: |
| case fontCIDType2OT: |
| psName = makePSFontName(font, font->getID()); |
| //~ should check to see if font actually uses vertical mode |
| setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName, true); |
| break; |
| case fontCIDType0COT: |
| psName = makePSFontName(font, &fontLoc->embFontID); |
| setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName); |
| break; |
| default: |
| break; |
| } |
| break; |
| case gfxFontLocExternal: |
| //~ add cases for external 16-bit fonts |
| switch (fontLoc->fontType) { |
| case fontType1: |
| if (font->getEmbeddedFontName()) { |
| // this assumes that the PS font name matches the PDF font name |
| psName = font->getEmbeddedFontName()->copy(); |
| } else { |
| //~ this won't work -- the PS font name won't match |
| psName = makePSFontName(font, font->getID()); |
| } |
| setupExternalType1Font(fontLoc->path, psName); |
| break; |
| case fontTrueType: |
| case fontTrueTypeOT: |
| psName = makePSFontName(font, font->getID()); |
| setupExternalTrueTypeFont(font, fontLoc->path, psName); |
| break; |
| case fontCIDType2: |
| case fontCIDType2OT: |
| psName = makePSFontName(font, font->getID()); |
| //~ should check to see if font actually uses vertical mode |
| setupExternalCIDTrueTypeFont(font, fontLoc->path, psName, true); |
| break; |
| default: |
| break; |
| } |
| break; |
| case gfxFontLocResident: |
| psName = fontLoc->path->copy(); |
| break; |
| } |
| } |
| |
| if (!psName) { |
| if (font->isCIDFont()) { |
| error(errSyntaxError, -1, |
| "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)", |
| font->getName() ? font->getName()->c_str() |
| : "(unnamed)", |
| ((GfxCIDFont *)font)->getCollection() |
| ? ((GfxCIDFont *)font)->getCollection()->c_str() |
| : "(unknown)"); |
| if (font16EncLen >= font16EncSize) { |
| font16EncSize += 16; |
| font16Enc = (PSFont16Enc *)greallocn(font16Enc, |
| font16EncSize, |
| sizeof(PSFont16Enc)); |
| } |
| font16Enc[font16EncLen].fontID = *font->getID(); |
| font16Enc[font16EncLen].enc = nullptr; |
| ++font16EncLen; |
| } else { |
| error(errSyntaxError, -1, |
| "Couldn't find a font to substitute for '{0:s}'", |
| font->getName() ? font->getName()->c_str() |
| : "(unnamed)"); |
| } |
| delete fontLoc; |
| return; |
| } |
| |
| // scale substituted 8-bit fonts |
| if (fontLoc->locType == gfxFontLocResident && |
| fontLoc->substIdx >= 0) { |
| subst = true; |
| for (code = 0; code < 256; ++code) { |
| if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && |
| charName[0] == 'm' && charName[1] == '\0') { |
| break; |
| } |
| } |
| if (code < 256) { |
| w1 = ((Gfx8BitFont *)font)->getWidth(code); |
| } else { |
| w1 = 0; |
| } |
| w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth; |
| xs = w1 / w2; |
| if (xs < 0.1) { |
| xs = 1; |
| } |
| } |
| |
| // handle encodings for substituted CID fonts |
| if (fontLoc->locType == gfxFontLocResident && |
| fontLoc->fontType >= fontCIDType0) { |
| subst = true; |
| if (font16EncLen >= font16EncSize) { |
| font16EncSize += 16; |
| font16Enc = (PSFont16Enc *)greallocn(font16Enc, |
| font16EncSize, |
| sizeof(PSFont16Enc)); |
| } |
| font16Enc[font16EncLen].fontID = *font->getID(); |
| if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { |
| font16Enc[font16EncLen].enc = fontLoc->encoding->copy(); |
| uMap->decRefCnt(); |
| } else { |
| error(errSyntaxError, -1, |
| "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", |
| fontLoc->encoding); |
| font16Enc[font16EncLen].enc = nullptr; |
| } |
| ++font16EncLen; |
| } |
| |
| delete fontLoc; |
| } |
| |
| // generate PostScript code to set up the font |
| if (font->isCIDFont()) { |
| if (level == psLevel3 || level == psLevel3Sep) { |
| writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", |
| font->getID()->num, font->getID()->gen, psName, |
| font->getWMode()); |
| } else { |
| writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", |
| font->getID()->num, font->getID()->gen, psName, |
| font->getWMode()); |
| } |
| } else { |
| writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n", |
| font->getID()->num, font->getID()->gen, psName, xs, ys); |
| for (i = 0; i < 256; i += 8) { |
| writePS((char *)((i == 0) ? "[ " : " ")); |
| for (j = 0; j < 8; ++j) { |
| if (font->getType() == fontTrueType && |
| !subst && |
| !((Gfx8BitFont *)font)->getHasEncoding()) { |
| sprintf(buf, "c%02x", i+j); |
| charName = buf; |
| } else { |
| charName = ((Gfx8BitFont *)font)->getCharName(i+j); |
| } |
| writePS("/"); |
| writePSName(charName ? charName : (char *)".notdef"); |
| // the empty name is legal in PDF and PostScript, but PostScript |
| // uses a double-slash (//...) for "immediately evaluated names", |
| // so we need to add a space character here |
| if (charName && !charName[0]) { |
| writePS(" "); |
| } |
| } |
| writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n"); |
| } |
| writePS("pdfMakeFont\n"); |
| } |
| |
| delete psName; |
| } |
| |
| void PSOutputDev::setupEmbeddedType1Font(Ref *id, GooString *psName) { |
| static const char hexChar[17] = "0123456789abcdef"; |
| Dict *dict; |
| long length1, length2, length3, i; |
| int c; |
| int start[4]; |
| bool binMode; |
| bool writePadding = true; |
| |
| // check if font is already embedded |
| if (!fontNames.emplace(psName->toStr()).second) { |
| return; |
| } |
| |
| // get the font stream and info |
| Object obj1, obj2, obj3; |
| Object refObj(*id); |
| Object strObj = refObj.fetch(xref); |
| if (!strObj.isStream()) { |
| error(errSyntaxError, -1, "Embedded font file object is not a stream"); |
| goto err1; |
| } |
| if (!(dict = strObj.streamGetDict())) { |
| error(errSyntaxError, -1, |
| "Embedded font stream is missing its dictionary"); |
| goto err1; |
| } |
| obj1 = dict->lookup("Length1"); |
| obj2 = dict->lookup("Length2"); |
| obj3 = dict->lookup("Length3"); |
| if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) { |
| error(errSyntaxError, -1, |
| "Missing length fields in embedded font stream dictionary"); |
| goto err1; |
| } |
| length1 = obj1.getInt(); |
| length2 = obj2.getInt(); |
| length3 = obj3.getInt(); |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| strObj.streamReset(); |
| if (strObj.streamGetChar() == 0x80 && |
| strObj.streamGetChar() == 1) { |
| // PFB format |
| length1 = strObj.streamGetChar() | |
| (strObj.streamGetChar() << 8) | |
| (strObj.streamGetChar() << 16) | |
| (strObj.streamGetChar() << 24); |
| } else { |
| strObj.streamReset(); |
| } |
| // copy ASCII portion of font |
| for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) { |
| writePSChar(c); |
| } |
| |
| // figure out if encrypted portion is binary or ASCII |
| binMode = false; |
| for (i = 0; i < 4; ++i) { |
| start[i] = strObj.streamGetChar(); |
| if (start[i] == EOF) { |
| error(errSyntaxError, -1, |
| "Unexpected end of file in embedded font stream"); |
| goto err1; |
| } |
| if (!((start[i] >= '0' && start[i] <= '9') || |
| (start[i] >= 'A' && start[i] <= 'F') || |
| (start[i] >= 'a' && start[i] <= 'f'))) |
| binMode = true; |
| } |
| |
| if (length2 == 0) |
| { |
| // length2 == 0 is an error |
| // trying to solve it by just piping all |
| // the stream data |
| error(errSyntaxWarning, -1, "Font has length2 as 0, trying to overcome the problem reading the stream until the end"); |
| length2 = INT_MAX; |
| writePadding = false; |
| } |
| |
| |
| // convert binary data to ASCII |
| if (binMode) { |
| if (start[0] == 0x80 && |
| start[1] == 2) { |
| length2 = start[2] | |
| (start[3] << 8) | |
| (strObj.streamGetChar() << 16) | |
| (strObj.streamGetChar() << 24); |
| i = 0; |
| } else { |
| for (i = 0; i < 4; ++i) { |
| writePSChar(hexChar[(start[i] >> 4) & 0x0f]); |
| writePSChar(hexChar[start[i] & 0x0f]); |
| } |
| } |
| #if 0 // this causes trouble for various PostScript printers |
| // if Length2 is incorrect (too small), font data gets chopped, so |
| // we take a few extra characters from the trailer just in case |
| length2 += length3 >= 8 ? 8 : length3; |
| #endif |
| while (i < length2) { |
| if ((c = strObj.streamGetChar()) == EOF) { |
| break; |
| } |
| writePSChar(hexChar[(c >> 4) & 0x0f]); |
| writePSChar(hexChar[c & 0x0f]); |
| if (++i % 32 == 0) { |
| writePSChar('\n'); |
| } |
| } |
| if (i % 32 > 0) { |
| writePSChar('\n'); |
| } |
| |
| // already in ASCII format -- just copy it |
| } else { |
| for (i = 0; i < 4; ++i) { |
| writePSChar(start[i]); |
| } |
| for (i = 4; i < length2; ++i) { |
| if ((c = strObj.streamGetChar()) == EOF) { |
| break; |
| } |
| writePSChar(c); |
| } |
| } |
| |
| if (writePadding) |
| { |
| if (length3 > 0) { |
| // write fixed-content portion |
| c = strObj.streamGetChar(); |
| if (c == 0x80) { |
| c = strObj.streamGetChar(); |
| if (c == 1) { |
| length3 = strObj.streamGetChar() | |
| (strObj.streamGetChar() << 8) | |
| (strObj.streamGetChar() << 16) | |
| (strObj.streamGetChar() << 24); |
| |
| i = 0; |
| while (i < length3) { |
| if ((c = strObj.streamGetChar()) == EOF) { |
| break; |
| } |
| writePSChar(c); |
| ++i; |
| } |
| } |
| } else { |
| if (c != EOF) { |
| writePSChar(c); |
| |
| while ((c = strObj.streamGetChar()) != EOF) { |
| writePSChar(c); |
| } |
| } |
| } |
| } else { |
| // write padding and "cleartomark" |
| for (i = 0; i < 8; ++i) { |
| writePS("00000000000000000000000000000000" |
| "00000000000000000000000000000000\n"); |
| } |
| writePS("cleartomark\n"); |
| } |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| |
| err1: |
| if (strObj.isStream()) |
| strObj.streamClose(); |
| } |
| |
| void PSOutputDev::setupExternalType1Font(GooString *fileName, GooString *psName) { |
| static const char hexChar[17] = "0123456789abcdef"; |
| FILE *fontFile; |
| int c; |
| |
| if (!fontNames.emplace(psName->toStr()).second) { |
| return; |
| } |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // copy the font file |
| if (!(fontFile = openFile(fileName->c_str(), "rb"))) { |
| error(errIO, -1, "Couldn't open external font file"); |
| return; |
| } |
| |
| c = fgetc(fontFile); |
| if (c == 0x80) { |
| // PFB file |
| ungetc(c, fontFile); |
| while (!feof(fontFile)) { |
| fgetc(fontFile); // skip start of segment byte (0x80) |
| int segType = fgetc(fontFile); |
| long segLen = fgetc(fontFile) | |
| (fgetc(fontFile) << 8) | |
| (fgetc(fontFile) << 16) | |
| (fgetc(fontFile) << 24); |
| if (feof(fontFile)) |
| break; |
| |
| if (segType == 1) { |
| // ASCII segment |
| for (long i = 0; i < segLen; i++) { |
| c = fgetc(fontFile); |
| if (c == EOF) |
| break; |
| writePSChar(c); |
| } |
| } else if (segType == 2) { |
| // binary segment |
| for (long i = 0; i < segLen; i++) { |
| c = fgetc(fontFile); |
| if (c == EOF) |
| break; |
| writePSChar(hexChar[(c >> 4) & 0x0f]); |
| writePSChar(hexChar[c & 0x0f]); |
| if (i % 36 == 35) |
| writePSChar('\n'); |
| } |
| } else { |
| // end of file |
| break; |
| } |
| } |
| } else if (c != EOF) { |
| writePSChar(c); |
| while ((c = fgetc(fontFile)) != EOF) |
| writePSChar(c); |
| } |
| fclose(fontFile); |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id, |
| GooString *psName) { |
| char *fontBuf; |
| int fontLen; |
| FoFiType1C *ffT1C; |
| int i; |
| |
| // check if font is already embedded |
| for (i = 0; i < t1FontNameLen; ++i) { |
| if (t1FontNames[i].fontFileID == *id) { |
| psName->clear(); |
| psName->insert(0, t1FontNames[i].psName); |
| return; |
| } |
| } |
| if (t1FontNameLen == t1FontNameSize) { |
| t1FontNameSize *= 2; |
| t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, |
| sizeof(PST1FontName)); |
| } |
| t1FontNames[t1FontNameLen].fontFileID = *id; |
| t1FontNames[t1FontNameLen].psName = psName->copy(); |
| ++t1FontNameLen; |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // convert it to a Type 1 font |
| if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { |
| if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { |
| ffT1C->convertToType1(psName->c_str(), nullptr, true, |
| outputFunc, outputStream); |
| delete ffT1C; |
| } |
| gfree(fontBuf); |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, |
| GooString *psName) { |
| char *fontBuf; |
| int fontLen; |
| FoFiTrueType *ffTT; |
| int i; |
| |
| // check if font is already embedded |
| for (i = 0; i < t1FontNameLen; ++i) { |
| if (t1FontNames[i].fontFileID == *id) { |
| psName->clear(); |
| psName->insert(0, t1FontNames[i].psName); |
| return; |
| } |
| } |
| if (t1FontNameLen == t1FontNameSize) { |
| t1FontNameSize *= 2; |
| t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, |
| sizeof(PST1FontName)); |
| } |
| t1FontNames[t1FontNameLen].fontFileID = *id; |
| t1FontNames[t1FontNameLen].psName = psName->copy(); |
| ++t1FontNameLen; |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // convert it to a Type 1 font |
| if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { |
| if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { |
| if (ffTT->isOpenTypeCFF()) { |
| ffTT->convertToType1(psName->c_str(), nullptr, true, |
| outputFunc, outputStream); |
| } |
| delete ffTT; |
| } |
| gfree(fontBuf); |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, |
| GooString *psName) { |
| char *fontBuf; |
| int fontLen; |
| FoFiTrueType *ffTT; |
| int *codeToGID; |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // convert it to a Type 42 font |
| if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { |
| if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { |
| codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); |
| ffTT->convertToType42(psName->c_str(), |
| ((Gfx8BitFont *)font)->getHasEncoding() |
| ? ((Gfx8BitFont *)font)->getEncoding() |
| : nullptr, |
| codeToGID, outputFunc, outputStream); |
| if (codeToGID) { |
| if (font8InfoLen >= font8InfoSize) { |
| font8InfoSize += 16; |
| font8Info = (PSFont8Info *)greallocn(font8Info, |
| font8InfoSize, |
| sizeof(PSFont8Info)); |
| } |
| font8Info[font8InfoLen].fontID = *font->getID(); |
| font8Info[font8InfoLen].codeToGID = codeToGID; |
| ++font8InfoLen; |
| } |
| delete ffTT; |
| } |
| gfree(fontBuf); |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GooString *fileName, |
| GooString *psName) { |
| FoFiTrueType *ffTT; |
| int *codeToGID; |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // convert it to a Type 42 font |
| if ((ffTT = FoFiTrueType::load(fileName->c_str()))) { |
| codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); |
| ffTT->convertToType42(psName->c_str(), |
| ((Gfx8BitFont *)font)->getHasEncoding() |
| ? ((Gfx8BitFont *)font)->getEncoding() |
| : nullptr, |
| codeToGID, outputFunc, outputStream); |
| if (codeToGID) { |
| if (font8InfoLen >= font8InfoSize) { |
| font8InfoSize += 16; |
| font8Info = (PSFont8Info *)greallocn(font8Info, |
| font8InfoSize, |
| sizeof(PSFont8Info)); |
| } |
| font8Info[font8InfoLen].fontID = *font->getID(); |
| font8Info[font8InfoLen].codeToGID = codeToGID; |
| ++font8InfoLen; |
| } |
| delete ffTT; |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::updateFontMaxValidGlyph(GfxFont *font, int maxValidGlyph) { |
| if (maxValidGlyph >= 0 && font->getName()) { |
| auto& fontMaxValidGlyph = perFontMaxValidGlyph[font->getName()->toStr()]; |
| if (fontMaxValidGlyph < maxValidGlyph) { |
| fontMaxValidGlyph = maxValidGlyph; |
| } |
| } |
| } |
| |
| void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, |
| GooString *fileName, |
| GooString *psName, |
| bool needVerticalMetrics) { |
| FoFiTrueType *ffTT; |
| int *codeToGID; |
| int codeToGIDLen; |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // convert it to a Type 0 font |
| //~ this should use fontNum to load the correct font |
| if ((ffTT = FoFiTrueType::load(fileName->c_str()))) { |
| |
| // check for embedding permission |
| if (ffTT->getEmbeddingRights() >= 1) { |
| codeToGID = nullptr; |
| codeToGIDLen = 0; |
| if (((GfxCIDFont *)font)->getCIDToGID()) { |
| codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen(); |
| if (codeToGIDLen) { |
| codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); |
| memcpy(codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), |
| codeToGIDLen * sizeof(int)); |
| } |
| } else { |
| codeToGID = ((GfxCIDFont *)font)->getCodeToGIDMap(ffTT, &codeToGIDLen); |
| } |
| if (ffTT->isOpenTypeCFF()) { |
| ffTT->convertToCIDType0(psName->c_str(), |
| codeToGID, codeToGIDLen, |
| outputFunc, outputStream); |
| } else if (globalParams->getPSLevel() >= psLevel3) { |
| // Level 3: use a CID font |
| ffTT->convertToCIDType2(psName->c_str(), |
| codeToGID, codeToGIDLen, |
| needVerticalMetrics, |
| outputFunc, outputStream); |
| } else { |
| // otherwise: use a non-CID composite font |
| int maxValidGlyph = -1; |
| ffTT->convertToType0(psName->c_str(), |
| codeToGID, codeToGIDLen, |
| needVerticalMetrics, |
| &maxValidGlyph, |
| outputFunc, outputStream); |
| updateFontMaxValidGlyph(font, maxValidGlyph); |
| } |
| gfree(codeToGID); |
| } else { |
| error(errSyntaxError, -1, |
| "TrueType font '{0:s}' does not allow embedding", |
| font->getName() ? font->getName()->c_str() : "(unnamed)"); |
| |
| } |
| delete ffTT; |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, |
| GooString *psName) { |
| char *fontBuf; |
| int fontLen; |
| FoFiType1C *ffT1C; |
| int i; |
| |
| // check if font is already embedded |
| for (i = 0; i < t1FontNameLen; ++i) { |
| if (t1FontNames[i].fontFileID == *id) { |
| psName->clear(); |
| psName->insert(0, t1FontNames[i].psName); |
| return; |
| } |
| } |
| if (t1FontNameLen == t1FontNameSize) { |
| t1FontNameSize *= 2; |
| t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, |
| sizeof(PST1FontName)); |
| } |
| t1FontNames[t1FontNameLen].fontFileID = *id; |
| t1FontNames[t1FontNameLen].psName = psName->copy(); |
| ++t1FontNameLen; |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // convert it to a Type 0 font |
| if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { |
| if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { |
| if (globalParams->getPSLevel() >= psLevel3) { |
| // Level 3: use a CID font |
| ffT1C->convertToCIDType0(psName->c_str(), nullptr, 0, |
| outputFunc, outputStream); |
| } else { |
| // otherwise: use a non-CID composite font |
| ffT1C->convertToType0(psName->c_str(), nullptr, 0, |
| outputFunc, outputStream); |
| } |
| delete ffT1C; |
| } |
| gfree(fontBuf); |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, |
| GooString *psName, |
| bool needVerticalMetrics) { |
| char *fontBuf; |
| int fontLen; |
| FoFiTrueType *ffTT; |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // convert it to a Type 0 font |
| if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { |
| if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { |
| if (globalParams->getPSLevel() >= psLevel3) { |
| // Level 3: use a CID font |
| ffTT->convertToCIDType2(psName->c_str(), |
| ((GfxCIDFont *)font)->getCIDToGID(), |
| ((GfxCIDFont *)font)->getCIDToGIDLen(), |
| needVerticalMetrics, |
| outputFunc, outputStream); |
| } else { |
| // otherwise: use a non-CID composite font |
| int maxValidGlyph = -1; |
| ffTT->convertToType0(psName->c_str(), |
| ((GfxCIDFont *)font)->getCIDToGID(), |
| ((GfxCIDFont *)font)->getCIDToGIDLen(), |
| needVerticalMetrics, |
| &maxValidGlyph, |
| outputFunc, outputStream); |
| updateFontMaxValidGlyph(font, maxValidGlyph); |
| } |
| delete ffTT; |
| } |
| gfree(fontBuf); |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, |
| GooString *psName) { |
| char *fontBuf; |
| int fontLen; |
| FoFiTrueType *ffTT; |
| int i; |
| |
| // check if font is already embedded |
| for (i = 0; i < t1FontNameLen; ++i) { |
| if (t1FontNames[i].fontFileID == *id) { |
| psName->clear(); |
| psName->insert(0, t1FontNames[i].psName); |
| return; |
| } |
| } |
| if (t1FontNameLen == t1FontNameSize) { |
| t1FontNameSize *= 2; |
| t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, |
| sizeof(PST1FontName)); |
| } |
| t1FontNames[t1FontNameLen].fontFileID = *id; |
| t1FontNames[t1FontNameLen].psName = psName->copy(); |
| ++t1FontNameLen; |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // convert it to a Type 0 font |
| if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { |
| if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { |
| if (ffTT->isOpenTypeCFF()) { |
| if (globalParams->getPSLevel() >= psLevel3) { |
| // Level 3: use a CID font |
| ffTT->convertToCIDType0(psName->c_str(), |
| ((GfxCIDFont *)font)->getCIDToGID(), |
| ((GfxCIDFont *)font)->getCIDToGIDLen(), |
| outputFunc, outputStream); |
| } else { |
| // otherwise: use a non-CID composite font |
| ffTT->convertToType0(psName->c_str(), |
| ((GfxCIDFont *)font)->getCIDToGID(), |
| ((GfxCIDFont *)font)->getCIDToGIDLen(), |
| outputFunc, outputStream); |
| } |
| } |
| delete ffTT; |
| } |
| gfree(fontBuf); |
| } |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| void PSOutputDev::setupType3Font(GfxFont *font, GooString *psName, |
| Dict *parentResDict) { |
| Dict *resDict; |
| Dict *charProcs; |
| Gfx *gfx; |
| PDFRectangle box; |
| const double *m; |
| GooString *buf; |
| int i; |
| |
| // set up resources used by font |
| if ((resDict = ((Gfx8BitFont *)font)->getResources())) { |
| inType3Char = true; |
| setupResources(resDict); |
| inType3Char = false; |
| } else { |
| resDict = parentResDict; |
| } |
| |
| // beginning comment |
| writePSFmt("%%BeginResource: font {0:t}\n", psName); |
| embFontList->append("%%+ font "); |
| embFontList->append(psName->c_str()); |
| embFontList->append("\n"); |
| |
| // font dictionary |
| writePS("8 dict begin\n"); |
| writePS("/FontType 3 def\n"); |
| m = font->getFontMatrix(); |
| writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", |
| m[0], m[1], m[2], m[3], m[4], m[5]); |
| m = font->getFontBBox(); |
| writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", |
| m[0], m[1], m[2], m[3]); |
| writePS("/Encoding 256 array def\n"); |
| writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); |
| writePS("/BuildGlyph {\n"); |
| writePS(" exch /CharProcs get exch\n"); |
| writePS(" 2 copy known not { pop /.notdef } if\n"); |
| writePS(" get exec\n"); |
| writePS("} bind def\n"); |
| writePS("/BuildChar {\n"); |
| writePS(" 1 index /Encoding get exch get\n"); |
| writePS(" 1 index /BuildGlyph get exec\n"); |
| writePS("} bind def\n"); |
| if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) { |
| writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength()); |
| writePS("CharProcs begin\n"); |
| box.x1 = m[0]; |
| box.y1 = m[1]; |
| box.x2 = m[2]; |
| box.y2 = m[3]; |
| gfx = new Gfx(doc, this, resDict, &box, nullptr); |
| inType3Char = true; |
| for (i = 0; i < charProcs->getLength(); ++i) { |
| t3FillColorOnly = false; |
| t3Cacheable = false; |
| t3NeedsRestore = false; |
| writePS("/"); |
| writePSName(charProcs->getKey(i)); |
| writePS(" {\n"); |
| Object charProc = charProcs->getVal(i); |
| gfx->display(&charProc); |
| if (t3String) { |
| if (t3Cacheable) { |
| buf = GooString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n", |
| t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); |
| } else { |
| buf = GooString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY); |
| } |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| (*outputFunc)(outputStream, t3String->c_str(), |
| t3String->getLength()); |
| delete t3String; |
| t3String = nullptr; |
| } |
| if (t3NeedsRestore) { |
| (*outputFunc)(outputStream, "Q\n", 2); |
| } |
| writePS("} def\n"); |
| } |
| inType3Char = false; |
| delete gfx; |
| writePS("end\n"); |
| } |
| writePS("currentdict end\n"); |
| writePSFmt("/{0:t} exch definefont pop\n", psName); |
| |
| // ending comment |
| writePS("%%EndResource\n"); |
| } |
| |
| // Make a unique PS font name, based on the names given in the PDF |
| // font object, and an object ID (font file object for |
| GooString *PSOutputDev::makePSFontName(GfxFont *font, const Ref *id) { |
| GooString *psName; |
| const GooString *s; |
| |
| if ((s = font->getEmbeddedFontName())) { |
| psName = filterPSName(s); |
| if (fontNames.emplace(psName->toStr()).second) { |
| return psName; |
| } |
| delete psName; |
| } |
| if ((s = font->getName())) { |
| psName = filterPSName(s); |
| if (fontNames.emplace(psName->toStr()).second) { |
| return psName; |
| } |
| delete psName; |
| } |
| psName = GooString::format("FF{0:d}_{1:d}", id->num, id->gen); |
| if ((s = font->getEmbeddedFontName())) { |
| s = filterPSName(s); |
| psName->append('_')->append(s); |
| delete s; |
| } else if ((s = font->getName())) { |
| s = filterPSName(s); |
| psName->append('_')->append(s); |
| delete s; |
| } |
| fontNames.emplace(psName->toStr()); |
| return psName; |
| } |
| |
| void PSOutputDev::setupImages(Dict *resDict) { |
| Ref imgID; |
| |
| if (!(mode == psModeForm || inType3Char || preloadImagesForms)) { |
| return; |
| } |
| |
| //----- recursively scan XObjects |
| Object xObjDict = resDict->lookup("XObject"); |
| if (xObjDict.isDict()) { |
| for (int i = 0; i < xObjDict.dictGetLength(); ++i) { |
| const Object &xObjRef = xObjDict.dictGetValNF(i); |
| Object xObj = xObjDict.dictGetVal(i); |
| if (xObj.isStream()) { |
| Object subtypeObj = xObj.streamGetDict()->lookup("Subtype"); |
| if (subtypeObj.isName("Image")) { |
| if (xObjRef.isRef()) { |
| imgID = xObjRef.getRef(); |
| int j; |
| for (j = 0; j < imgIDLen; ++j) { |
| if (imgIDs[j] == imgID) { |
| break; |
| } |
| } |
| if (j == imgIDLen) { |
| if (imgIDLen >= imgIDSize) { |
| if (imgIDSize == 0) { |
| imgIDSize = 64; |
| } else { |
| imgIDSize *= 2; |
| } |
| imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); |
| } |
| imgIDs[imgIDLen++] = imgID; |
| setupImage(imgID, xObj.getStream(), false); |
| if (level >= psLevel3) { |
| Object maskObj = xObj.streamGetDict()->lookup("Mask"); |
| if (maskObj.isStream()) { |
| setupImage(imgID, maskObj.getStream(), true); |
| } |
| } |
| } |
| } else { |
| error(errSyntaxError, -1, |
| "Image in resource dict is not an indirect reference"); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void PSOutputDev::setupImage(Ref id, Stream *str, bool mask) { |
| bool useFlate, useLZW, useRLE, useCompressed, doUseASCIIHex; |
| GooString *s; |
| int c; |
| int size, line, col, i; |
| int outerSize, outer; |
| |
| // filters |
| //~ this does not correctly handle the DeviceN color space |
| //~ -- need to use DeviceNRecoder |
| |
| useFlate = useLZW = useRLE = false; |
| useCompressed = false; |
| doUseASCIIHex = false; |
| |
| if (level < psLevel2) { |
| doUseASCIIHex = true; |
| } else { |
| if (uncompressPreloadedImages) { |
| /* nothing to do */; |
| } else { |
| s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); |
| if (s) { |
| useCompressed = true; |
| delete s; |
| } else { |
| if (level >= psLevel3 && getEnableFlate()) { |
| useFlate = true; |
| } else if (getEnableLZW()) { |
| useLZW = true; |
| } else { |
| useRLE = true; |
| } |
| } |
| } |
| doUseASCIIHex = useASCIIHex; |
| } |
| if (useCompressed) { |
| str = str->getUndecodedStream(); |
| } |
| #ifdef ENABLE_ZLIB |
| if (useFlate) { |
| str = new FlateEncoder(str); |
| } else |
| #endif |
| if (useLZW) { |
| str = new LZWEncoder(str); |
| } else if (useRLE) { |
| str = new RunLengthEncoder(str); |
| } |
| if (doUseASCIIHex) { |
| str = new ASCIIHexEncoder(str); |
| } else { |
| str = new ASCII85Encoder(str); |
| } |
| |
| // compute image data size |
| str->reset(); |
| col = size = 0; |
| do { |
| do { |
| c = str->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| if (c == 'z') { |
| ++col; |
| } else { |
| ++col; |
| for (i = 1; i <= (doUseASCIIHex ? 1 : 4); ++i) { |
| do { |
| c = str->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| ++col; |
| } |
| if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| } |
| if (col > 225) { |
| ++size; |
| col = 0; |
| } |
| } while (c != (doUseASCIIHex ? '>' : '~') && c != EOF); |
| // add one entry for the final line of data; add another entry |
| // because the LZWDecode/RunLengthDecode filter may read past the end |
| ++size; |
| if (useLZW || useRLE) { |
| ++size; |
| } |
| outerSize = size/65535 + 1; |
| |
| writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", |
| outerSize, mask ? "Mask" : "Im", id.num, id.gen); |
| str->close(); |
| |
| // write the data into the array |
| str->reset(); |
| for (outer = 0;outer < outerSize;outer++) { |
| int innerSize = size > 65535 ? 65535 : size; |
| |
| // put the inner array into the outer array |
| writePSFmt("{0:d} array 1 index {1:d} 2 index put\n", |
| innerSize, outer); |
| line = col = 0; |
| writePS((char *)(doUseASCIIHex ? "dup 0 <" : "dup 0 <~")); |
| for (;;) { |
| do { |
| c = str->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| if (c == 'z') { |
| writePSChar(c); |
| ++col; |
| } else { |
| writePSChar(c); |
| ++col; |
| for (i = 1; i <= (doUseASCIIHex ? 1 : 4); ++i) { |
| do { |
| c = str->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| writePSChar(c); |
| ++col; |
| } |
| } |
| if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| // each line is: "dup nnnnn <~...data...~> put<eol>" |
| // so max data length = 255 - 20 = 235 |
| // chunks are 1 or 4 bytes each, so we have to stop at 232 |
| // but make it 225 just to be safe |
| if (col > 225) { |
| writePS((char *)(doUseASCIIHex ? "> put\n" : "~> put\n")); |
| ++line; |
| if (line >= innerSize) break; |
| writePSFmt((char *)(doUseASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line); |
| col = 0; |
| } |
| } |
| if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { |
| writePS((char *)(doUseASCIIHex ? "> put\n" : "~> put\n")); |
| if (useLZW || useRLE) { |
| ++line; |
| writePSFmt("{0:d} <> put\n", line); |
| } else { |
| writePS("pop\n"); |
| } |
| break; |
| } |
| writePS("pop\n"); |
| size -= innerSize; |
| } |
| writePS("pop\n"); |
| str->close(); |
| |
| delete str; |
| } |
| |
| void PSOutputDev::setupForms(Dict *resDict) { |
| if (!preloadImagesForms) { |
| return; |
| } |
| |
| Object xObjDict = resDict->lookup("XObject"); |
| if (xObjDict.isDict()) { |
| for (int i = 0; i < xObjDict.dictGetLength(); ++i) { |
| const Object &xObjRef = xObjDict.dictGetValNF(i); |
| Object xObj = xObjDict.dictGetVal(i); |
| if (xObj.isStream()) { |
| Object subtypeObj = xObj.streamGetDict()->lookup("Subtype"); |
| if (subtypeObj.isName("Form")) { |
| if (xObjRef.isRef()) { |
| setupForm(xObjRef.getRef(), &xObj); |
| } else { |
| error(errSyntaxError, -1, |
| "Form in resource dict is not an indirect reference"); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void PSOutputDev::setupForm(Ref id, Object *strObj) { |
| Dict *dict, *resDict; |
| double m[6], bbox[4]; |
| PDFRectangle box; |
| Gfx *gfx; |
| |
| // check if form is already defined |
| for (int i = 0; i < formIDLen; ++i) { |
| if (formIDs[i] == id) { |
| return; |
| } |
| } |
| |
| // add entry to formIDs list |
| if (formIDLen >= formIDSize) { |
| if (formIDSize == 0) { |
| formIDSize = 64; |
| } else { |
| formIDSize *= 2; |
| } |
| formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref)); |
| } |
| formIDs[formIDLen++] = id; |
| |
| dict = strObj->streamGetDict(); |
| |
| // get bounding box |
| Object bboxObj = dict->lookup("BBox"); |
| if (!bboxObj.isArray()) { |
| error(errSyntaxError, -1, "Bad form bounding box"); |
| return; |
| } |
| for (int i = 0; i < 4; ++i) { |
| Object obj1 = bboxObj.arrayGet(i); |
| bbox[i] = obj1.getNum(); |
| } |
| |
| // get matrix |
| Object matrixObj = dict->lookup("Matrix"); |
| if (matrixObj.isArray()) { |
| for (int i = 0; i < 6; ++i) { |
| Object obj1 = matrixObj.arrayGet(i); |
| m[i] = obj1.getNum(); |
| } |
| } else { |
| m[0] = 1; m[1] = 0; |
| m[2] = 0; m[3] = 1; |
| m[4] = 0; m[5] = 0; |
| } |
| |
| // get resources |
| Object resObj = dict->lookup("Resources"); |
| resDict = resObj.isDict() ? resObj.getDict() : nullptr; |
| |
| writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen); |
| writePS("q\n"); |
| writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n", |
| m[0], m[1], m[2], m[3], m[4], m[5]); |
| |
| box.x1 = bbox[0]; |
| box.y1 = bbox[1]; |
| box.x2 = bbox[2]; |
| box.y2 = bbox[3]; |
| gfx = new Gfx(doc, this, resDict, &box, &box); |
| gfx->display(strObj); |
| delete gfx; |
| |
| writePS("Q\n"); |
| writePS("} def\n"); |
| } |
| |
| bool PSOutputDev::checkPageSlice(Page *page, double /*hDPI*/, double /*vDPI*/, |
| int rotateA, bool useMediaBox, bool crop, |
| int sliceX, int sliceY, |
| int sliceW, int sliceH, |
| bool printing, |
| bool (*abortCheckCbk)(void *data), |
| void *abortCheckCbkData, |
| bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), |
| void *annotDisplayDecideCbkData) { |
| PreScanOutputDev *scan; |
| bool rasterize; |
| #ifdef HAVE_SPLASH |
| bool useFlate, useLZW; |
| SplashOutputDev *splashOut; |
| SplashColor paperColor; |
| PDFRectangle box; |
| GfxState *state; |
| SplashBitmap *bitmap; |
| Stream *str0, *str; |
| unsigned char *p; |
| unsigned char col[4]; |
| double hDPI2, vDPI2; |
| double m0, m1, m2, m3, m4, m5; |
| int nStripes, stripeH, stripeY; |
| int c, w, h, x, y, comp, i; |
| int numComps, initialNumComps; |
| char hexBuf[32*2 + 2]; // 32 values X 2 chars/value + line ending + null |
| unsigned char digit; |
| bool isGray; |
| #endif |
| |
| if (!postInitDone) { |
| postInit(); |
| } |
| if (forceRasterize) { |
| rasterize = true; |
| } else { |
| scan = new PreScanOutputDev(doc); |
| page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, |
| sliceX, sliceY, sliceW, sliceH, |
| printing, abortCheckCbk, abortCheckCbkData, |
| annotDisplayDecideCbk, annotDisplayDecideCbkData); |
| rasterize = scan->usesTransparency() || scan->usesPatternImageMask(); |
| delete scan; |
| } |
| if (!rasterize) { |
| return true; |
| } |
| |
| #ifdef HAVE_SPLASH |
| // get the rasterization parameters |
| useFlate = getEnableFlate() && level >= psLevel3; |
| useLZW = getEnableLZW(); |
| // start the PS page |
| page->makeBox(rasterResolution, rasterResolution, rotateA, useMediaBox, false, |
| sliceX, sliceY, sliceW, sliceH, &box, &crop); |
| rotateA += page->getRotate(); |
| if (rotateA >= 360) { |
| rotateA -= 360; |
| } else if (rotateA < 0) { |
| rotateA += 360; |
| } |
| state = new GfxState(rasterResolution, rasterResolution, &box, rotateA, false); |
| startPage(page->getNum(), state, xref); |
| delete state; |
| |
| // set up the SplashOutputDev |
| if (rasterMono || level == psLevel1) { |
| numComps = 1; |
| paperColor[0] = 0xff; |
| splashOut = new SplashOutputDev(splashModeMono8, 1, false, |
| paperColor, false); |
| } else if (level == psLevel1Sep || level == psLevel2Sep || |
| level == psLevel3Sep || globalParams->getOverprintPreview()) { |
| numComps = 4; |
| paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; |
| splashOut = new SplashOutputDev(splashModeCMYK8, 1, false, |
| paperColor, false); |
| } else { |
| numComps = 3; |
| paperColor[0] = paperColor[1] = paperColor[2] = 0xff; |
| splashOut = new SplashOutputDev(splashModeRGB8, 1, false, |
| paperColor, false); |
| } |
| splashOut->setFontAntialias(rasterAntialias); |
| splashOut->setVectorAntialias(rasterAntialias); |
| splashOut->startDoc(doc); |
| |
| // break the page into stripes |
| hDPI2 = xScale * rasterResolution; |
| vDPI2 = yScale * rasterResolution; |
| if (sliceW < 0 || sliceH < 0) { |
| if (useMediaBox) { |
| box = *page->getMediaBox(); |
| } else { |
| box = *page->getCropBox(); |
| } |
| sliceX = sliceY = 0; |
| sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); |
| sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); |
| } |
| nStripes = (int)ceil((double)(sliceW * sliceH) / |
| (double)rasterizationSliceSize); |
| if (unlikely(nStripes == 0)) { |
| delete splashOut; |
| return false; |
| } |
| stripeH = (sliceH + nStripes - 1) / nStripes; |
| |
| // render the stripes |
| initialNumComps = numComps; |
| for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) { |
| |
| // rasterize a stripe |
| page->makeBox(hDPI2, vDPI2, 0, useMediaBox, false, |
| sliceX, stripeY, sliceW, stripeH, &box, &crop); |
| m0 = box.x2 - box.x1; |
| m1 = 0; |
| m2 = 0; |
| m3 = box.y2 - box.y1; |
| m4 = box.x1; |
| m5 = box.y1; |
| page->displaySlice(splashOut, hDPI2, vDPI2, |
| (360 - page->getRotate()) % 360, useMediaBox, crop, |
| sliceX, stripeY, sliceW, stripeH, |
| printing, abortCheckCbk, abortCheckCbkData, |
| annotDisplayDecideCbk, annotDisplayDecideCbkData); |
| |
| // draw the rasterized image |
| bitmap = splashOut->getBitmap(); |
| numComps = initialNumComps; |
| w = bitmap->getWidth(); |
| h = bitmap->getHeight(); |
| writePS("gsave\n"); |
| writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", |
| m0, m1, m2, m3, m4, m5); |
| switch (level) { |
| case psLevel1: |
| writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}\n", |
| w, h, w, -h, h, |
| useBinary ? "Bin" : ""); |
| p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); |
| i = 0; |
| if (useBinary) { |
| for (y = 0; y < h; ++y) { |
| for (x = 0; x < w; ++x) { |
| hexBuf[i++] = *p++; |
| if (i >= 64) { |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| } else { |
| for (y = 0; y < h; ++y) { |
| for (x = 0; x < w; ++x) { |
| digit = *p / 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| digit = *p++ % 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| if (i >= 64) { |
| hexBuf[i++] = '\n'; |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| } |
| if (i != 0) { |
| if (!useBinary) { |
| hexBuf[i++] = '\n'; |
| } |
| writePSBuf(hexBuf, i); |
| } |
| break; |
| case psLevel1Sep: |
| p = bitmap->getDataPtr(); |
| // Check for an all gray image |
| if (getOptimizeColorSpace()) { |
| isGray = true; |
| for (y = 0; y < h; ++y) { |
| for (x = 0; x < w; ++x) { |
| if (p[4*x] != p[4*x + 1] || p[4*x] != p[4*x + 2]) { |
| isGray = false; |
| y = h; |
| break; |
| } |
| } |
| p += bitmap->getRowSize(); |
| } |
| } else { |
| isGray = false; |
| } |
| writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}{6:s}\n", |
| w, h, w, -h, h, |
| isGray ? "" : "Sep", |
| useBinary ? "Bin" : ""); |
| p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); |
| i = 0; |
| col[0] = col[1] = col[2] = col[3] = 0; |
| if (isGray) { |
| int g; |
| if ((psProcessBlack & processColors) == 0) { |
| // Check if the image uses black |
| for (y = 0; y < h; ++y) { |
| for (x = 0; x < w; ++x) { |
| if (p[4*x] > 0 || p[4*x + 3] > 0) { |
| col[3] = 1; |
| y = h; |
| break; |
| } |
| } |
| p -= bitmap->getRowSize(); |
| } |
| p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); |
| } |
| for (y = 0; y < h; ++y) { |
| if (useBinary) { |
| // Binary gray image |
| for (x = 0; x < w; ++x) { |
| g = p[4*x] + p[4*x + 3]; |
| g = 255 - g; |
| if (g < 0) g = 0; |
| hexBuf[i++] = (unsigned char) g; |
| if (i >= 64) { |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } else { |
| // Hex gray image |
| for (x = 0; x < w; ++x) { |
| g = p[4*x] + p[4*x + 3]; |
| g = 255 - g; |
| if (g < 0) g = 0; |
| digit = g / 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| digit = g % 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| if (i >= 64) { |
| hexBuf[i++] = '\n'; |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| p -= bitmap->getRowSize(); |
| } |
| } else if (((psProcessCyan | psProcessMagenta | psProcessYellow | psProcessBlack) & ~processColors) != 0) { |
| // Color image, need to check color flags for each dot |
| for (y = 0; y < h; ++y) { |
| for (comp = 0; comp < 4; ++comp) { |
| if (useBinary) { |
| // Binary color image |
| for (x = 0; x < w; ++x) { |
| col[comp] |= p[4*x + comp]; |
| hexBuf[i++] = p[4*x + comp]; |
| if (i >= 64) { |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } else { |
| // Gray color image |
| for (x = 0; x < w; ++x) { |
| col[comp] |= p[4*x + comp]; |
| digit = p[4*x + comp] / 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| digit = p[4*x + comp] % 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| if (i >= 64) { |
| hexBuf[i++] = '\n'; |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| } |
| p -= bitmap->getRowSize(); |
| } |
| } else { |
| // Color image, do not need to check color flags |
| for (y = 0; y < h; ++y) { |
| for (comp = 0; comp < 4; ++comp) { |
| if (useBinary) { |
| // Binary color image |
| for (x = 0; x < w; ++x) { |
| hexBuf[i++] = p[4*x + comp]; |
| if (i >= 64) { |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } else { |
| // Hex color image |
| for (x = 0; x < w; ++x) { |
| digit = p[4*x + comp] / 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| digit = p[4*x + comp] % 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| if (i >= 64) { |
| hexBuf[i++] = '\n'; |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| } |
| p -= bitmap->getRowSize(); |
| } |
| } |
| if (i != 0) { |
| if (!useBinary) { |
| hexBuf[i++] = '\n'; |
| } |
| writePSBuf(hexBuf, i); |
| } |
| if (col[0]) { |
| processColors |= psProcessCyan; |
| } |
| if (col[1]) { |
| processColors |= psProcessMagenta; |
| } |
| if (col[2]) { |
| processColors |= psProcessYellow; |
| } |
| if (col[3]) { |
| processColors |= psProcessBlack; |
| } |
| break; |
| case psLevel2: |
| case psLevel2Sep: |
| case psLevel3: |
| case psLevel3Sep: |
| p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); |
| str0 = new MemStream((char *)p, 0, w * h * numComps, Object(objNull)); |
| // Check for a color image that uses only gray |
| if (!getOptimizeColorSpace()) { |
| isGray = false; |
| } else if (numComps == 4) { |
| int compCyan; |
| isGray = true; |
| while ((compCyan = str0->getChar()) != EOF) { |
| if (str0->getChar() != compCyan || |
| str0->getChar() != compCyan) { |
| isGray = false; |
| break; |
| } |
| str0->getChar(); |
| } |
| } else if (numComps == 3) { |
| int compRed; |
| isGray = true; |
| while ((compRed = str0->getChar()) != EOF) { |
| if (str0->getChar() != compRed || |
| str0->getChar() != compRed) { |
| isGray = false; |
| break; |
| } |
| } |
| } else { |
| isGray = false; |
| } |
| str0->reset(); |
| #ifdef ENABLE_ZLIB |
| if (useFlate) { |
| if (isGray && numComps == 4) { |
| str = new FlateEncoder(new CMYKGrayEncoder(str0)); |
| numComps = 1; |
| } else if (isGray && numComps == 3) { |
| str = new FlateEncoder(new RGBGrayEncoder(str0)); |
| numComps = 1; |
| } else { |
| str = new FlateEncoder(str0); |
| } |
| } else |
| #endif |
| if (useLZW) { |
| if (isGray && numComps == 4) { |
| str = new LZWEncoder(new CMYKGrayEncoder(str0)); |
| numComps = 1; |
| } else if (isGray && numComps == 3) { |
| str = new LZWEncoder(new RGBGrayEncoder(str0)); |
| numComps = 1; |
| } else { |
| str = new LZWEncoder(str0); |
| } |
| } else { |
| if (isGray && numComps == 4) { |
| str = new RunLengthEncoder(new CMYKGrayEncoder(str0)); |
| numComps = 1; |
| } else if (isGray && numComps == 3) { |
| str = new RunLengthEncoder(new RGBGrayEncoder(str0)); |
| numComps = 1; |
| } else { |
| str = new RunLengthEncoder(str0); |
| } |
| } |
| if (numComps == 1) { |
| writePS("/DeviceGray setcolorspace\n"); |
| } else if (numComps == 3) { |
| writePS("/DeviceRGB setcolorspace\n"); |
| } else { |
| writePS("/DeviceCMYK setcolorspace\n"); |
| } |
| writePS("<<\n /ImageType 1\n"); |
| writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); |
| writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); |
| writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); |
| writePS(" /BitsPerComponent 8\n"); |
| if (numComps == 1) { |
| writePS(" /Decode [1 0]\n"); |
| } else if (numComps == 3) { |
| writePS(" /Decode [0 1 0 1 0 1]\n"); |
| } else { |
| writePS(" /Decode [0 1 0 1 0 1 0 1]\n"); |
| } |
| writePS(" /DataSource currentfile\n"); |
| if (useBinary) { |
| /* nothing to do */; |
| } else if (useASCIIHex) { |
| writePS(" /ASCIIHexDecode filter\n"); |
| } else { |
| writePS(" /ASCII85Decode filter\n"); |
| } |
| if (useFlate) { |
| writePS(" /FlateDecode filter\n"); |
| } else if (useLZW) { |
| writePS(" /LZWDecode filter\n"); |
| } else { |
| writePS(" /RunLengthDecode filter\n"); |
| } |
| writePS(">>\n"); |
| if (useBinary) { |
| /* nothing to do */; |
| } else if (useASCIIHex) { |
| str = new ASCIIHexEncoder(str); |
| } else { |
| str = new ASCII85Encoder(str); |
| } |
| str->reset(); |
| if (useBinary) { |
| // Count the bytes to write a document comment |
| int len = 0; |
| while (str->getChar() != EOF) { |
| len++; |
| } |
| str->reset(); |
| writePSFmt("%%BeginData: {0:d} Binary Bytes\n", len+6+1); |
| } |
| writePS("image\n"); |
| while ((c = str->getChar()) != EOF) { |
| writePSChar(c); |
| } |
| str->close(); |
| delete str; |
| delete str0; |
| writePSChar('\n'); |
| if (useBinary) { |
| writePS("%%EndData\n"); |
| } |
| processColors |= (numComps == 1) ? psProcessBlack : psProcessCMYK; |
| break; |
| } |
| writePS("grestore\n"); |
| } |
| |
| delete splashOut; |
| |
| // finish the PS page |
| endPage(); |
| |
| return false; |
| |
| #else // HAVE_SPLASH |
| |
| error(errSyntaxWarning, -1, |
| "PDF page uses transparency and PSOutputDev was built without" |
| " the Splash rasterizer - output may not be correct"); |
| return true; |
| #endif // HAVE_SPLASH |
| } |
| |
| void PSOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { |
| Page *page; |
| int x1, y1, x2, y2, width, height, t; |
| int imgWidth, imgHeight, imgWidth2, imgHeight2; |
| bool landscape; |
| GooString *s; |
| PSOutPaperSize *paperSize; |
| |
| if (!postInitDone) { |
| postInit(); |
| } |
| xref = xrefA; |
| if (mode == psModePS) { |
| GooString pageLabel; |
| const bool gotLabel = doc->getCatalog()->indexToLabel(pageNum -1, &pageLabel); |
| if (gotLabel) { |
| // See bug13338 for why we try to avoid parentheses... |
| bool needParens; |
| GooString *filteredString = filterPSLabel(&pageLabel, &needParens); |
| if (needParens) { |
| writePSFmt("%%Page: ({0:t}) {1:d}\n", filteredString, seqPage); |
| } else { |
| writePSFmt("%%Page: {0:t} {1:d}\n", filteredString, seqPage); |
| } |
| delete filteredString; |
| } else { |
| writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); |
| } |
| if (paperMatch) { |
| page = doc->getCatalog()->getPage(pageNum); |
| imgLLX = imgLLY = 0; |
| if (noCrop) { |
| imgURX = (int)ceil(page->getMediaWidth()); |
| imgURY = (int)ceil(page->getMediaHeight()); |
| } else { |
| imgURX = (int)ceil(page->getCropWidth()); |
| imgURY = (int)ceil(page->getCropHeight()); |
| } |
| if (state->getRotate() == 90 || state->getRotate() == 270) { |
| t = imgURX; |
| imgURX = imgURY; |
| imgURY = t; |
| } |
| } |
| } |
| |
| // underlays |
| if (underlayCbk) { |
| (*underlayCbk)(this, underlayCbkData); |
| } |
| if (overlayCbk) { |
| saveState(nullptr); |
| } |
| |
| xScale = yScale = 1; |
| switch (mode) { |
| |
| case psModePS: |
| // rotate, translate, and scale page |
| imgWidth = imgURX - imgLLX; |
| imgHeight = imgURY - imgLLY; |
| x1 = (int)floor(state->getX1()); |
| y1 = (int)floor(state->getY1()); |
| x2 = (int)ceil(state->getX2()); |
| y2 = (int)ceil(state->getY2()); |
| width = x2 - x1; |
| height = y2 - y1; |
| tx = ty = 0; |
| // rotation and portrait/landscape mode |
| if (paperMatch) { |
| rotate = (360 - state->getRotate()) % 360; |
| landscape = false; |
| } else if (rotate0 >= 0) { |
| rotate = (360 - rotate0) % 360; |
| landscape = false; |
| } else { |
| rotate = (360 - state->getRotate()) % 360; |
| if (rotate == 0 || rotate == 180) { |
| if ((width < height && imgWidth > imgHeight && height > imgHeight) || |
| (width > height && imgWidth < imgHeight && width > imgWidth)) { |
| rotate += 90; |
| landscape = true; |
| } else { |
| landscape = false; |
| } |
| } else { // rotate == 90 || rotate == 270 |
| if ((height < width && imgWidth > imgHeight && width > imgHeight) || |
| (height > width && imgWidth < imgHeight && height > imgWidth)) { |
| rotate = 270 - rotate; |
| landscape = true; |
| } else { |
| landscape = false; |
| } |
| } |
| } |
| if (rotate == 0) { |
| imgWidth2 = imgWidth; |
| imgHeight2 = imgHeight; |
| } else if (rotate == 90) { |
| ty = -imgWidth; |
| imgWidth2 = imgHeight; |
| imgHeight2 = imgWidth; |
| } else if (rotate == 180) { |
| imgWidth2 = imgWidth; |
| imgHeight2 = imgHeight; |
| tx = -imgWidth; |
| ty = -imgHeight; |
| } else { // rotate == 270 |
| tx = -imgHeight; |
| imgWidth2 = imgHeight; |
| imgHeight2 = imgWidth; |
| } |
| // shrink or expand |
| if (xScale0 > 0 && yScale0 > 0) { |
| xScale = xScale0; |
| yScale = yScale0; |
| } else if ((globalParams->getPSShrinkLarger() && |
| (width > imgWidth2 || height > imgHeight2)) || |
| (globalParams->getPSExpandSmaller() && |
| (width < imgWidth2 && height < imgHeight2))) { |
| xScale = (double)imgWidth2 / (double)width; |
| yScale = (double)imgHeight2 / (double)height; |
| if (yScale < xScale) { |
| xScale = yScale; |
| } else { |
| yScale = xScale; |
| } |
| } |
| // deal with odd bounding boxes or clipping |
| if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { |
| tx -= xScale * clipLLX0; |
| ty -= yScale * clipLLY0; |
| } else { |
| tx -= xScale * x1; |
| ty -= yScale * y1; |
| } |
| // center |
| if (tx0 >= 0 && ty0 >= 0) { |
| tx += (rotate == 0 || rotate == 180) ? tx0 : ty0; |
| ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0; |
| } else if (psCenter) { |
| if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { |
| tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2; |
| ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2; |
| } else { |
| tx += (imgWidth2 - xScale * width) / 2; |
| ty += (imgHeight2 - yScale * height) / 2; |
| } |
| } |
| tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY; |
| ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; |
| |
| if (paperMatch) { |
| paperSize = (*paperSizes)[pagePaperSize[pageNum]]; |
| writePSFmt("%%PageMedia: {0:t}\n", paperSize->name); |
| } |
| |
| // Create a matrix with the same transform that will be output to PS |
| Matrix m; |
| switch (rotate) { |
| default: |
| case 0: |
| m.init(1, 0, |
| 0, 1, |
| 0, 0); |
| break; |
| case 90: |
| m.init(0, 1, |
| -1, 0, |
| 0, 0); |
| break; |
| case 180: |
| m.init(-1, 0, |
| 0, -1, |
| 0, 0); |
| break; |
| case 270: |
| m.init(0, -1, |
| 1, 0, |
| 0, 0); |
| break; |
| } |
| m.translate(tx, ty); |
| m.scale(xScale, yScale); |
| |
| double bboxX1, bboxY1, bboxX2, bboxY2; |
| m.transform(0, 0, &bboxX1, &bboxY1); |
| m.transform(width, height, &bboxX2, &bboxY2); |
| |
| writePSFmt("%%PageBoundingBox: {0:g} {1:g} {2:g} {3:g}\n", |
| floor(std::min(bboxX1, bboxX2)), |
| floor(std::min(bboxY1, bboxY2)), |
| ceil (std::max(bboxX1, bboxX2)), |
| ceil (std::max(bboxY1, bboxY2))); |
| |
| writePSFmt("%%PageOrientation: {0:s}\n", |
| landscape ? "Landscape" : "Portrait"); |
| writePS("%%BeginPageSetup\n"); |
| if (paperMatch) { |
| writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY); |
| } |
| writePS("pdfStartPage\n"); |
| if (rotate) |
| writePSFmt("{0:d} rotate\n", rotate); |
| if (tx != 0 || ty != 0) { |
| writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); |
| } |
| if (xScale != 1 || yScale != 1) { |
| writePSFmt("{0:.6f} {1:.6f} scale\n", xScale, yScale); |
| } |
| if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", |
| clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); |
| } else { |
| writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1); |
| } |
| |
| ++seqPage; |
| break; |
| |
| case psModeEPS: |
| writePS("pdfStartPage\n"); |
| tx = ty = 0; |
| rotate = (360 - state->getRotate()) % 360; |
| if (rotate == 0) { |
| } else if (rotate == 90) { |
| writePS("90 rotate\n"); |
| tx = -epsX1; |
| ty = -epsY2; |
| } else if (rotate == 180) { |
| writePS("180 rotate\n"); |
| tx = -(epsX1 + epsX2); |
| ty = -(epsY1 + epsY2); |
| } else { // rotate == 270 |
| writePS("270 rotate\n"); |
| tx = -epsX2; |
| ty = -epsY1; |
| } |
| if (tx != 0 || ty != 0) { |
| writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); |
| } |
| break; |
| |
| case psModeForm: |
| writePS("/PaintProc {\n"); |
| writePS("begin xpdf begin\n"); |
| writePS("pdfStartPage\n"); |
| tx = ty = 0; |
| rotate = 0; |
| break; |
| } |
| |
| if (customCodeCbk) { |
| if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, |
| customCodeCbkData))) { |
| writePS(s->c_str()); |
| delete s; |
| } |
| } |
| |
| writePS("%%EndPageSetup\n"); |
| } |
| |
| void PSOutputDev::endPage() { |
| if (overlayCbk) { |
| restoreState(nullptr); |
| (*overlayCbk)(this, overlayCbkData); |
| } |
| |
| |
| if (mode == psModeForm) { |
| writePS("pdfEndPage\n"); |
| writePS("end end\n"); |
| writePS("} def\n"); |
| writePS("end end\n"); |
| } else { |
| if (!manualCtrl) { |
| writePS("showpage\n"); |
| } |
| writePS("%%PageTrailer\n"); |
| writePageTrailer(); |
| } |
| } |
| |
| void PSOutputDev::saveState(GfxState *state) { |
| writePS("q\n"); |
| ++numSaves; |
| } |
| |
| void PSOutputDev::restoreState(GfxState *state) { |
| writePS("Q\n"); |
| --numSaves; |
| } |
| |
| void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, |
| double m21, double m22, double m31, double m32) { |
| writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n", |
| m11, m12, m21, m22, m31, m32); |
| } |
| |
| void PSOutputDev::updateLineDash(GfxState *state) { |
| double *dash; |
| double start; |
| int length, i; |
| |
| state->getLineDash(&dash, &length, &start); |
| writePS("["); |
| for (i = 0; i < length; ++i) { |
| writePSFmt("{0:.6g}{1:w}", |
| dash[i] < 0 ? 0 : dash[i], |
| (i == length-1) ? 0 : 1); |
| } |
| writePSFmt("] {0:.6g} d\n", start); |
| } |
| |
| void PSOutputDev::updateFlatness(GfxState *state) { |
| writePSFmt("{0:d} i\n", state->getFlatness()); |
| } |
| |
| void PSOutputDev::updateLineJoin(GfxState *state) { |
| writePSFmt("{0:d} j\n", state->getLineJoin()); |
| } |
| |
| void PSOutputDev::updateLineCap(GfxState *state) { |
| writePSFmt("{0:d} J\n", state->getLineCap()); |
| } |
| |
| void PSOutputDev::updateMiterLimit(GfxState *state) { |
| writePSFmt("{0:.6g} M\n", state->getMiterLimit()); |
| } |
| |
| void PSOutputDev::updateLineWidth(GfxState *state) { |
| writePSFmt("{0:.6g} w\n", state->getLineWidth()); |
| } |
| |
| void PSOutputDev::updateFillColorSpace(GfxState *state) { |
| if (inUncoloredPattern) { |
| return; |
| } |
| switch (level) { |
| case psLevel1: |
| case psLevel1Sep: |
| break; |
| case psLevel2: |
| case psLevel3: |
| if (state->getFillColorSpace()->getMode() != csPattern) { |
| dumpColorSpaceL2(state->getFillColorSpace(), true, false, false); |
| writePS(" cs\n"); |
| } |
| break; |
| case psLevel2Sep: |
| case psLevel3Sep: |
| break; |
| } |
| } |
| |
| void PSOutputDev::updateStrokeColorSpace(GfxState *state) { |
| if (inUncoloredPattern) { |
| return; |
| } |
| switch (level) { |
| case psLevel1: |
| case psLevel1Sep: |
| break; |
| case psLevel2: |
| case psLevel3: |
| if (state->getStrokeColorSpace()->getMode() != csPattern) { |
| dumpColorSpaceL2(state->getStrokeColorSpace(), true, false, false); |
| writePS(" CS\n"); |
| } |
| break; |
| case psLevel2Sep: |
| case psLevel3Sep: |
| break; |
| } |
| } |
| |
| void PSOutputDev::updateFillColor(GfxState *state) { |
| GfxColor color; |
| GfxGray gray; |
| GfxCMYK cmyk; |
| GfxSeparationColorSpace *sepCS; |
| double c, m, y, k; |
| int i; |
| |
| if (inUncoloredPattern) { |
| return; |
| } |
| switch (level) { |
| case psLevel1: |
| state->getFillGray(&gray); |
| writePSFmt("{0:.4g} g\n", colToDbl(gray)); |
| break; |
| case psLevel2: |
| case psLevel3: |
| if (state->getFillColorSpace()->getMode() != csPattern) { |
| const GfxColor *colorPtr = state->getFillColor(); |
| writePS("["); |
| for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) { |
| if (i > 0) { |
| writePS(" "); |
| } |
| writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); |
| } |
| writePS("] sc\n"); |
| } |
| break; |
| case psLevel1Sep: |
| case psLevel2Sep: |
| case psLevel3Sep: |
| if (state->getFillColorSpace()->getMode() == csSeparation && (level > psLevel1Sep || getPassLevel1CustomColor())) { |
| sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace(); |
| color.c[0] = gfxColorComp1; |
| sepCS->getCMYK(&color, &cmyk); |
| writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n", |
| colToDbl(state->getFillColor()->c[0]), |
| colToDbl(cmyk.c), colToDbl(cmyk.m), |
| colToDbl(cmyk.y), colToDbl(cmyk.k), |
| sepCS->getName()); |
| addCustomColor(sepCS); |
| } else { |
| state->getFillCMYK(&cmyk); |
| c = colToDbl(cmyk.c); |
| m = colToDbl(cmyk.m); |
| y = colToDbl(cmyk.y); |
| k = colToDbl(cmyk.k); |
| if (getOptimizeColorSpace()) { |
| double g; |
| g = 0.299*c + 0.587*m + 0.114*y; |
| if ((fabs(m - c) < 0.01 && fabs(m - y) < 0.01) || |
| (fabs(m - c) < 0.2 && fabs(m - y) < 0.2 && k + g > 1.5)) { |
| c = m = y = 0.0; |
| k += g; |
| if (k > 1.0) k = 1.0; |
| } |
| } |
| writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); |
| addProcessColor(c, m, y, k); |
| } |
| break; |
| } |
| t3Cacheable = false; |
| } |
| |
| void PSOutputDev::updateStrokeColor(GfxState *state) { |
| GfxColor color; |
| GfxGray gray; |
| GfxCMYK cmyk; |
| GfxSeparationColorSpace *sepCS; |
| double c, m, y, k; |
| int i; |
| |
| if (inUncoloredPattern) { |
| return; |
| } |
| switch (level) { |
| case psLevel1: |
| state->getStrokeGray(&gray); |
| writePSFmt("{0:.4g} G\n", colToDbl(gray)); |
| break; |
| case psLevel2: |
| case psLevel3: |
| if (state->getStrokeColorSpace()->getMode() != csPattern) { |
| const GfxColor *colorPtr = state->getStrokeColor(); |
| writePS("["); |
| for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) { |
| if (i > 0) { |
| writePS(" "); |
| } |
| writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); |
| } |
| writePS("] SC\n"); |
| } |
| break; |
| case psLevel1Sep: |
| case psLevel2Sep: |
| case psLevel3Sep: |
| if (state->getStrokeColorSpace()->getMode() == csSeparation && (level > psLevel1Sep || getPassLevel1CustomColor())) { |
| sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace(); |
| color.c[0] = gfxColorComp1; |
| sepCS->getCMYK(&color, &cmyk); |
| writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n", |
| colToDbl(state->getStrokeColor()->c[0]), |
| colToDbl(cmyk.c), colToDbl(cmyk.m), |
| colToDbl(cmyk.y), colToDbl(cmyk.k), |
| sepCS->getName()); |
| addCustomColor(sepCS); |
| } else { |
| state->getStrokeCMYK(&cmyk); |
| c = colToDbl(cmyk.c); |
| m = colToDbl(cmyk.m); |
| y = colToDbl(cmyk.y); |
| k = colToDbl(cmyk.k); |
| if (getOptimizeColorSpace()) { |
| double g; |
| g = 0.299*c + 0.587*m + 0.114*y; |
| if ((fabs(m - c) < 0.01 && fabs(m - y) < 0.01) || |
| (fabs(m - c) < 0.2 && fabs(m - y) < 0.2 && k + g > 1.5)) { |
| c = m = y = 0.0; |
| k += g; |
| if (k > 1.0) k = 1.0; |
| } |
| } |
| writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); |
| addProcessColor(c, m, y, k); |
| } |
| break; |
| } |
| t3Cacheable = false; |
| } |
| |
| void PSOutputDev::addProcessColor(double c, double m, double y, double k) { |
| if (c > 0) { |
| processColors |= psProcessCyan; |
| } |
| if (m > 0) { |
| processColors |= psProcessMagenta; |
| } |
| if (y > 0) { |
| processColors |= psProcessYellow; |
| } |
| if (k > 0) { |
| processColors |= psProcessBlack; |
| } |
| } |
| |
| void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) { |
| PSOutCustomColor *cc; |
| GfxColor color; |
| GfxCMYK cmyk; |
| |
| if (!sepCS->getName()->cmp("Black")) { |
| processColors |= psProcessBlack; |
| return; |
| } |
| if (!sepCS->getName()->cmp("Cyan")) { |
| processColors |= psProcessCyan; |
| return; |
| } |
| if (!sepCS->getName()->cmp("Yellow")) { |
| processColors |= psProcessYellow; |
| return; |
| } |
| if (!sepCS->getName()->cmp("Magenta")) { |
| processColors |= psProcessMagenta; |
| return; |
| } |
| if (!sepCS->getName()->cmp("All")) |
| return; |
| if (!sepCS->getName()->cmp("None")) |
| return; |
| for (cc = customColors; cc; cc = cc->next) { |
| if (!cc->name->cmp(sepCS->getName())) { |
| return; |
| } |
| } |
| color.c[0] = gfxColorComp1; |
| sepCS->getCMYK(&color, &cmyk); |
| cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), |
| colToDbl(cmyk.y), colToDbl(cmyk.k), |
| sepCS->getName()->copy()); |
| cc->next = customColors; |
| customColors = cc; |
| } |
| |
| void PSOutputDev::updateFillOverprint(GfxState *state) { |
| if (level >= psLevel2) { |
| writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false"); |
| } |
| } |
| |
| void PSOutputDev::updateStrokeOverprint(GfxState *state) { |
| if (level >= psLevel2) { |
| writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false"); |
| } |
| } |
| |
| void PSOutputDev::updateOverprintMode(GfxState *state) { |
| if (level >= psLevel3) { |
| writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false"); |
| } |
| } |
| |
| void PSOutputDev::updateTransfer(GfxState *state) { |
| Function **funcs; |
| int i; |
| |
| funcs = state->getTransfer(); |
| if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) { |
| if (level >= psLevel2) { |
| for (i = 0; i < 4; ++i) { |
| cvtFunction(funcs[i]); |
| } |
| writePS("setcolortransfer\n"); |
| } else { |
| cvtFunction(funcs[3]); |
| writePS("settransfer\n"); |
| } |
| } else if (funcs[0]) { |
| cvtFunction(funcs[0]); |
| writePS("settransfer\n"); |
| } else { |
| writePS("{} settransfer\n"); |
| } |
| } |
| |
| void PSOutputDev::updateFont(GfxState *state) { |
| if (state->getFont()) { |
| writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n", |
| state->getFont()->getID()->num, state->getFont()->getID()->gen, |
| fabs(state->getFontSize()) < 0.0001 ? 0.0001 |
| : state->getFontSize()); |
| } |
| } |
| |
| void PSOutputDev::updateTextMat(GfxState *state) { |
| const double *mat = state->getTextMat(); |
| if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) { |
| // avoid a singular (or close-to-singular) matrix |
| writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]); |
| } else { |
| writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n", |
| mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); |
| } |
| } |
| |
| void PSOutputDev::updateCharSpace(GfxState *state) { |
| writePSFmt("{0:.6g} Tc\n", state->getCharSpace()); |
| } |
| |
| void PSOutputDev::updateRender(GfxState *state) { |
| int rm; |
| |
| rm = state->getRender(); |
| writePSFmt("{0:d} Tr\n", rm); |
| rm &= 3; |
| if (rm != 0 && rm != 3) { |
| t3Cacheable = false; |
| } |
| } |
| |
| void PSOutputDev::updateRise(GfxState *state) { |
| writePSFmt("{0:.6g} Ts\n", state->getRise()); |
| } |
| |
| void PSOutputDev::updateWordSpace(GfxState *state) { |
| writePSFmt("{0:.6g} Tw\n", state->getWordSpace()); |
| } |
| |
| void PSOutputDev::updateHorizScaling(GfxState *state) { |
| double h; |
| |
| h = state->getHorizScaling(); |
| if (fabs(h) < 0.01) { |
| h = 0.01; |
| } |
| writePSFmt("{0:.6g} Tz\n", h); |
| } |
| |
| void PSOutputDev::updateTextPos(GfxState *state) { |
| writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY()); |
| } |
| |
| void PSOutputDev::updateTextShift(GfxState *state, double shift) { |
| if (state->getFont()->getWMode()) { |
| writePSFmt("{0:.6g} TJmV\n", shift); |
| } else { |
| writePSFmt("{0:.6g} TJm\n", shift); |
| } |
| } |
| |
| void PSOutputDev::saveTextPos(GfxState *state) { |
| writePS("currentpoint\n"); |
| } |
| |
| void PSOutputDev::restoreTextPos(GfxState *state) { |
| writePS("m\n"); |
| } |
| |
| void PSOutputDev::stroke(GfxState *state) { |
| doPath(state->getPath()); |
| if (inType3Char && t3FillColorOnly) { |
| // if we're constructing a cacheable Type 3 glyph, we need to do |
| // everything in the fill color |
| writePS("Sf\n"); |
| } else { |
| writePS("S\n"); |
| } |
| } |
| |
| void PSOutputDev::fill(GfxState *state) { |
| doPath(state->getPath()); |
| writePS("f\n"); |
| } |
| |
| void PSOutputDev::eoFill(GfxState *state) { |
| doPath(state->getPath()); |
| writePS("f*\n"); |
| } |
| |
| bool PSOutputDev::tilingPatternFillL1(GfxState *state, Catalog *cat, Object *str, |
| const double *pmat, int paintType, int tilingType, Dict *resDict, |
| const double *mat, const double *bbox, |
| int x0, int y0, int x1, int y1, |
| double xStep, double yStep) { |
| PDFRectangle box; |
| Gfx *gfx; |
| |
| // define a Type 3 font |
| writePS("8 dict begin\n"); |
| writePS("/FontType 3 def\n"); |
| writePS("/FontMatrix [1 0 0 1 0 0] def\n"); |
| writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", |
| bbox[0], bbox[1], bbox[2], bbox[3]); |
| writePS("/Encoding 256 array def\n"); |
| writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); |
| writePS(" Encoding 120 /x put\n"); |
| writePS("/BuildGlyph {\n"); |
| writePS(" exch /CharProcs get exch\n"); |
| writePS(" 2 copy known not { pop /.notdef } if\n"); |
| writePS(" get exec\n"); |
| writePS("} bind def\n"); |
| writePS("/BuildChar {\n"); |
| writePS(" 1 index /Encoding get exch get\n"); |
| writePS(" 1 index /BuildGlyph get exec\n"); |
| writePS("} bind def\n"); |
| writePS("/CharProcs 1 dict def\n"); |
| writePS("CharProcs begin\n"); |
| box.x1 = bbox[0]; |
| box.y1 = bbox[1]; |
| box.x2 = bbox[2]; |
| box.y2 = bbox[3]; |
| gfx = new Gfx(doc, this, resDict, &box, nullptr); |
| writePS("/x {\n"); |
| if (paintType == 2) { |
| writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", |
| xStep, bbox[0], bbox[1], bbox[2], bbox[3]); |
| t3FillColorOnly = true; |
| } else |
| { |
| if (x1 - 1 <= x0) { |
| writePS("1 0 setcharwidth\n"); |
| } else { |
| writePSFmt("{0:.6g} 0 setcharwidth\n", xStep); |
| } |
| t3FillColorOnly = false; |
| } |
| inType3Char = true; |
| if (paintType == 2) { |
| inUncoloredPattern = true; |
| // ensure any PS procedures that contain sCol or fCol do not change the color |
| writePS("/pdfLastFill true def\n"); |
| writePS("/pdfLastStroke true def\n"); |
| } |
| ++numTilingPatterns; |
| gfx->display(str); |
| --numTilingPatterns; |
| if (paintType == 2) { |
| inUncoloredPattern = false; |
| // ensure the next PS procedures that uses sCol or fCol will update the color |
| writePS("/pdfLastFill false def\n"); |
| writePS("/pdfLastStroke false def\n"); |
| } |
| inType3Char = false; |
| writePS("} def\n"); |
| delete gfx; |
| writePS("end\n"); |
| writePS("currentdict end\n"); |
| writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns); |
| |
| // draw the tiles |
| writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns); |
| writePS("fCol\n"); |
| writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", |
| mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); |
| writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", |
| y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1); |
| writePS("grestore\n"); |
| |
| return true; |
| } |
| |
| bool PSOutputDev::tilingPatternFillL2(GfxState *state, Catalog *cat, Object *str, |
| const double *pmat, int paintType, int tilingType, Dict *resDict, |
| const double *mat, const double *bbox, |
| int x0, int y0, int x1, int y1, |
| double xStep, double yStep) { |
| PDFRectangle box; |
| Gfx *gfx; |
| |
| if (paintType == 2) { |
| // setpattern with PaintType 2 needs the paint color |
| writePS("currentcolor\n"); |
| } |
| writePS("<<\n /PatternType 1\n"); |
| writePSFmt(" /PaintType {0:d}\n", paintType); |
| writePSFmt(" /TilingType {0:d}\n", tilingType); |
| writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n", bbox[0], bbox[1], bbox[2], bbox[3]); |
| writePSFmt(" /XStep {0:.6g}\n", xStep); |
| writePSFmt(" /YStep {0:.6g}\n", yStep); |
| writePS(" /PaintProc { \n"); |
| box.x1 = bbox[0]; |
| box.y1 = bbox[1]; |
| box.x2 = bbox[2]; |
| box.y2 = bbox[3]; |
| gfx = new Gfx(doc, this, resDict, &box, nullptr); |
| inType3Char = true; |
| if (paintType == 2) { |
| inUncoloredPattern = true; |
| // ensure any PS procedures that contain sCol or fCol do not change the color |
| writePS("/pdfLastFill true def\n"); |
| writePS("/pdfLastStroke true def\n"); |
| } |
| gfx->display(str); |
| if (paintType == 2) { |
| inUncoloredPattern = false; |
| // ensure the next PS procedures that uses sCol or fCol will update the color |
| writePS("/pdfLastFill false def\n"); |
| writePS("/pdfLastStroke false def\n"); |
| } |
| inType3Char = false; |
| delete gfx; |
| writePS(" }\n"); |
| writePS(">>\n"); |
| 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]); |
| writePS("makepattern setpattern\n"); |
| writePS("clippath fill\n"); // Gfx sets up a clip before calling out->tilingPatternFill() |
| |
| return true; |
| } |
| |
| bool PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, Object *str, |
| const double *pmat, int paintType, int tilingType, Dict *resDict, |
| const double *mat, const double *bbox, |
| int x0, int y0, int x1, int y1, |
| double xStep, double yStep) { |
| if (x1 - x0 == 1 && y1 - y0 == 1) { |
| // Don't need to use patterns if only one instance of the pattern is used |
| PDFRectangle box; |
| Gfx *gfx; |
| |
| const double singleStep_x = x0 * xStep; |
| const double singleStep_y = y0 * yStep; |
| const double singleStep_tx = singleStep_x * mat[0] + singleStep_y * mat[2] + mat[4]; |
| const double singleStep_ty = singleStep_x * mat[1] + singleStep_y * mat[3] + mat[5]; |
| box.x1 = bbox[0]; |
| box.y1 = bbox[1]; |
| box.x2 = bbox[2]; |
| box.y2 = bbox[3]; |
| gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA); |
| writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", mat[0], mat[1], mat[2], mat[3], singleStep_tx, singleStep_ty); |
| inType3Char = true; |
| gfx->display(str); |
| inType3Char = false; |
| delete gfx; |
| return true; |
| } |
| |
| if (level == psLevel1 || level == psLevel1Sep) { |
| return tilingPatternFillL1(state, cat, str, pmat, paintType, tilingType, resDict, |
| mat, bbox, x0, y0, x1, y1, xStep, yStep); |
| } else { |
| return tilingPatternFillL2(state, cat, str, pmat, paintType, tilingType, resDict, |
| mat, bbox, x0, y0, x1, y1, xStep, yStep); |
| } |
| } |
| |
| bool PSOutputDev::functionShadedFill(GfxState *state, |
| GfxFunctionShading *shading) { |
| double x0, y0, x1, y1; |
| int i; |
| |
| if (level == psLevel2Sep || level == psLevel3Sep) { |
| if (shading->getColorSpace()->getMode() != csDeviceCMYK) { |
| return false; |
| } |
| processColors |= psProcessCMYK; |
| } |
| |
| shading->getDomain(&x0, &y0, &x1, &y1); |
| const double *mat = shading->getMatrix(); |
| writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", |
| mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); |
| writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); |
| if (shading->getNFuncs() == 1) { |
| writePS("/func "); |
| cvtFunction(shading->getFunc(0)); |
| writePS("def\n"); |
| } else { |
| writePS("/func {\n"); |
| for (i = 0; i < shading->getNFuncs(); ++i) { |
| if (i < shading->getNFuncs() - 1) { |
| writePS("2 copy\n"); |
| } |
| cvtFunction(shading->getFunc(i)); |
| writePS("exec\n"); |
| if (i < shading->getNFuncs() - 1) { |
| writePS("3 1 roll\n"); |
| } |
| } |
| writePS("} def\n"); |
| } |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1); |
| |
| return true; |
| } |
| |
| bool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double /*tMin*/, double /*tMax*/) { |
| double xMin, yMin, xMax, yMax; |
| double x0, y0, x1, y1, dx, dy, mul; |
| double tMin, tMax, t, t0, t1; |
| int i; |
| |
| if (level == psLevel2Sep || level == psLevel3Sep) { |
| if (shading->getColorSpace()->getMode() != csDeviceCMYK) { |
| return false; |
| } |
| processColors |= psProcessCMYK; |
| } |
| |
| // get the clip region bbox |
| state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
| |
| // compute min and max t values, based on the four corners of the |
| // clip region bbox |
| shading->getCoords(&x0, &y0, &x1, &y1); |
| dx = x1 - x0; |
| dy = y1 - y0; |
| if (fabs(dx) < 0.01 && fabs(dy) < 0.01) { |
| return true; |
| } else { |
| mul = 1 / (dx * dx + dy * dy); |
| tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; |
| t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; |
| if (t < tMin) { |
| tMin = t; |
| } else if (t > tMax) { |
| tMax = t; |
| } |
| t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; |
| if (t < tMin) { |
| tMin = t; |
| } else if (t > tMax) { |
| tMax = t; |
| } |
| t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; |
| if (t < tMin) { |
| tMin = t; |
| } else if (t > tMax) { |
| tMax = t; |
| } |
| if (tMin < 0 && !shading->getExtend0()) { |
| tMin = 0; |
| } |
| if (tMax > 1 && !shading->getExtend1()) { |
| tMax = 1; |
| } |
| } |
| |
| // get the function domain |
| t0 = shading->getDomain0(); |
| t1 = shading->getDomain1(); |
| |
| // generate the PS code |
| writePSFmt("/t0 {0:.6g} def\n", t0); |
| writePSFmt("/t1 {0:.6g} def\n", t1); |
| writePSFmt("/dt {0:.6g} def\n", t1 - t0); |
| writePSFmt("/x0 {0:.6g} def\n", x0); |
| writePSFmt("/y0 {0:.6g} def\n", y0); |
| writePSFmt("/dx {0:.6g} def\n", x1 - x0); |
| writePSFmt("/x1 {0:.6g} def\n", x1); |
| writePSFmt("/y1 {0:.6g} def\n", y1); |
| writePSFmt("/dy {0:.6g} def\n", y1 - y0); |
| writePSFmt("/xMin {0:.6g} def\n", xMin); |
| writePSFmt("/yMin {0:.6g} def\n", yMin); |
| writePSFmt("/xMax {0:.6g} def\n", xMax); |
| writePSFmt("/yMax {0:.6g} def\n", yMax); |
| writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); |
| if (shading->getNFuncs() == 1) { |
| writePS("/func "); |
| cvtFunction(shading->getFunc(0)); |
| writePS("def\n"); |
| } else { |
| writePS("/func {\n"); |
| for (i = 0; i < shading->getNFuncs(); ++i) { |
| if (i < shading->getNFuncs() - 1) { |
| writePS("dup\n"); |
| } |
| cvtFunction(shading->getFunc(i)); |
| writePS("exec\n"); |
| if (i < shading->getNFuncs() - 1) { |
| writePS("exch\n"); |
| } |
| } |
| writePS("} def\n"); |
| } |
| writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax); |
| |
| return true; |
| } |
| |
| bool PSOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/) { |
| double xMin, yMin, xMax, yMax; |
| double x0, y0, r0, x1, y1, r1, t0, t1; |
| double xa, ya, ra; |
| double sMin, sMax, h, ta; |
| double sLeft, sRight, sTop, sBottom, sZero, sDiag; |
| bool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; |
| bool haveSMin, haveSMax; |
| double theta, alpha, a1, a2; |
| bool enclosed; |
| int i; |
| |
| if (level == psLevel2Sep || level == psLevel3Sep) { |
| if (shading->getColorSpace()->getMode() != csDeviceCMYK) { |
| return false; |
| } |
| processColors |= psProcessCMYK; |
| } |
| |
| // get the shading info |
| shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); |
| t0 = shading->getDomain0(); |
| t1 = shading->getDomain1(); |
| |
| // Compute the point at which r(s) = 0; check for the enclosed |
| // circles case; and compute the angles for the tangent lines. |
| // Compute the point at which r(s) = 0; check for the enclosed |
| // circles case; and compute the angles for the tangent lines. |
| h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); |
| if (h == 0) { |
| enclosed = true; |
| theta = 0; // make gcc happy |
| } else if (r1 - r0 == 0) { |
| enclosed = false; |
| theta = 0; |
| } else if (fabs(r1 - r0) >= h) { |
| enclosed = true; |
| theta = 0; // make gcc happy |
| } else { |
| enclosed = false; |
| theta = asin((r1 - r0) / h); |
| } |
| if (enclosed) { |
| a1 = 0; |
| a2 = 360; |
| } else { |
| alpha = atan2(y1 - y0, x1 - x0); |
| a1 = (180 / M_PI) * (alpha + theta) + 90; |
| a2 = (180 / M_PI) * (alpha - theta) - 90; |
| while (a2 < a1) { |
| a2 += 360; |
| } |
| } |
| |
| // compute the (possibly extended) s range |
| state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
| if (enclosed) { |
| sMin = 0; |
| sMax = 1; |
| } else { |
| // solve x(sLeft) + r(sLeft) = xMin |
| if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { |
| sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); |
| } else { |
| sLeft = 0; // make gcc happy |
| } |
| // solve x(sRight) - r(sRight) = xMax |
| if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { |
| sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); |
| } else { |
| sRight = 0; // make gcc happy |
| } |
| // solve y(sBottom) + r(sBottom) = yMin |
| if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { |
| sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); |
| } else { |
| sBottom = 0; // make gcc happy |
| } |
| // solve y(sTop) - r(sTop) = yMax |
| if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { |
| sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); |
| } else { |
| sTop = 0; // make gcc happy |
| } |
| // solve r(sZero) = 0 |
| if ((haveSZero = fabs(r1 - r0) > 0.000001)) { |
| sZero = -r0 / (r1 - r0); |
| } else { |
| sZero = 0; // make gcc happy |
| } |
| // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) |
| if (haveSZero) { |
| sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + |
| (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); |
| } else { |
| sDiag = 0; // make gcc happy |
| } |
| // compute sMin |
| if (shading->getExtend0()) { |
| sMin = 0; |
| haveSMin = false; |
| if (x0 < x1 && haveSLeft && sLeft < 0) { |
| sMin = sLeft; |
| haveSMin = true; |
| } else if (x0 > x1 && haveSRight && sRight < 0) { |
| sMin = sRight; |
| haveSMin = true; |
| } |
| if (y0 < y1 && haveSBottom && sBottom < 0) { |
| if (!haveSMin || sBottom > sMin) { |
| sMin = sBottom; |
| haveSMin = true; |
| } |
| } else if (y0 > y1 && haveSTop && sTop < 0) { |
| if (!haveSMin || sTop > sMin) { |
| sMin = sTop; |
| haveSMin = true; |
| } |
| } |
| if (haveSZero && sZero < 0) { |
| if (!haveSMin || sZero > sMin) { |
| sMin = sZero; |
| } |
| } |
| } else { |
| sMin = 0; |
| } |
| // compute sMax |
| if (shading->getExtend1()) { |
| sMax = 1; |
| haveSMax = false; |
| if (x1 < x0 && haveSLeft && sLeft > 1) { |
| sMax = sLeft; |
| haveSMax = true; |
| } else if (x1 > x0 && haveSRight && sRight > 1) { |
| sMax = sRight; |
| haveSMax = true; |
| } |
| if (y1 < y0 && haveSBottom && sBottom > 1) { |
| if (!haveSMax || sBottom < sMax) { |
| sMax = sBottom; |
| haveSMax = true; |
| } |
| } else if (y1 > y0 && haveSTop && sTop > 1) { |
| if (!haveSMax || sTop < sMax) { |
| sMax = sTop; |
| haveSMax = true; |
| } |
| } |
| if (haveSZero && sDiag > 1) { |
| if (!haveSMax || sDiag < sMax) { |
| sMax = sDiag; |
| } |
| } |
| } else { |
| sMax = 1; |
| } |
| } |
| |
| // generate the PS code |
| writePSFmt("/x0 {0:.6g} def\n", x0); |
| writePSFmt("/x1 {0:.6g} def\n", x1); |
| writePSFmt("/dx {0:.6g} def\n", x1 - x0); |
| writePSFmt("/y0 {0:.6g} def\n", y0); |
| writePSFmt("/y1 {0:.6g} def\n", y1); |
| writePSFmt("/dy {0:.6g} def\n", y1 - y0); |
| writePSFmt("/r0 {0:.6g} def\n", r0); |
| writePSFmt("/r1 {0:.6g} def\n", r1); |
| writePSFmt("/dr {0:.6g} def\n", r1 - r0); |
| writePSFmt("/t0 {0:.6g} def\n", t0); |
| writePSFmt("/t1 {0:.6g} def\n", t1); |
| writePSFmt("/dt {0:.6g} def\n", t1 - t0); |
| writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); |
| writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false"); |
| writePSFmt("/a1 {0:.6g} def\n", a1); |
| writePSFmt("/a2 {0:.6g} def\n", a2); |
| if (shading->getNFuncs() == 1) { |
| writePS("/func "); |
| cvtFunction(shading->getFunc(0)); |
| writePS("def\n"); |
| } else { |
| writePS("/func {\n"); |
| for (i = 0; i < shading->getNFuncs(); ++i) { |
| if (i < shading->getNFuncs() - 1) { |
| writePS("dup\n"); |
| } |
| cvtFunction(shading->getFunc(i)); |
| writePS("exec\n"); |
| if (i < shading->getNFuncs() - 1) { |
| writePS("exch\n"); |
| } |
| } |
| writePS("} def\n"); |
| } |
| writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax); |
| |
| // extend the 'enclosed' case |
| if (enclosed) { |
| // extend the smaller circle |
| if ((shading->getExtend0() && r0 <= r1) || |
| (shading->getExtend1() && r1 < r0)) { |
| if (r0 <= r1) { |
| ta = t0; |
| ra = r0; |
| xa = x0; |
| ya = y0; |
| } else { |
| ta = t1; |
| ra = r1; |
| xa = x1; |
| ya = y1; |
| } |
| if (level == psLevel2Sep || level == psLevel3Sep) { |
| writePSFmt("{0:.6g} radialCol aload pop k\n", ta); |
| } else { |
| writePSFmt("{0:.6g} radialCol sc\n", ta); |
| } |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra); |
| } |
| |
| // extend the larger circle |
| if ((shading->getExtend0() && r0 > r1) || |
| (shading->getExtend1() && r1 >= r0)) { |
| if (r0 > r1) { |
| ta = t0; |
| ra = r0; |
| xa = x0; |
| ya = y0; |
| } else { |
| ta = t1; |
| ra = r1; |
| xa = x1; |
| ya = y1; |
| } |
| if (level == psLevel2Sep || level == psLevel3Sep) { |
| writePSFmt("{0:.6g} radialCol aload pop k\n", ta); |
| } else { |
| writePSFmt("{0:.6g} radialCol sc\n", ta); |
| } |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra); |
| writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n", |
| xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool PSOutputDev::patchMeshShadedFill(GfxState *state, |
| GfxPatchMeshShading *shading) { |
| // TODO: support parametrized shading |
| if (level < psLevel3 || shading->isParameterized()) { |
| return false; |
| } |
| |
| writePS("%% Begin patchMeshShadedFill\n"); |
| |
| // ShadingType 7 shadings are pretty much the same for pdf and ps. |
| // As such, we basically just need to invert GfxPatchMeshShading::parse here. |
| |
| writePS("<<\n"); |
| writePS(" /ShadingType 7\n"); |
| writePS(" /ColorSpace "); |
| dumpColorSpaceL2(shading->getColorSpace(), false, false, false); |
| writePS("\n"); |
| writePS(" /DataSource [\n"); |
| |
| const int ncomps = shading->getColorSpace()->getNComps(); |
| |
| for (int i = 0; i < shading->getNPatches(); ++i) { |
| const auto& patch = *shading->getPatch(i); |
| // Print Flag, for us always f = 0 |
| writePS(" 0 \n"); |
| |
| // Print coordinates |
| const std::array<std::pair<int,int>, 16> coordindices = {{ {0,0}, {0,1}, {0,2}, {0,3}, |
| {1,3}, {2,3}, {3,3}, {3,2}, |
| {3,1}, {3,0}, {2,0}, {1,0}, |
| {1,1}, {1,2}, {2,2}, {2,1} }}; |
| for (const auto& index: coordindices) { |
| writePSFmt(" {0:.6g} {1:.6g}\n", patch.x[index.first][index.second], |
| patch.y[index.first][index.second]); |
| } |
| |
| // Print colors |
| const std::array<std::pair<int, int>, 4> colindices = {{ {0,0}, {0,1}, {1,1}, {1,0} }}; |
| for (const auto& index: colindices) { |
| writePS(" "); |
| for (int comp = 0; comp < ncomps; ++comp) { |
| writePSFmt(" {0:.6g}", colToDbl(patch.color[index.first][index.second].c[comp])); |
| } |
| writePS("\n"); |
| } |
| } |
| |
| writePS(" ]\n"); |
| |
| writePS(">> shfill\n"); |
| writePS("%% End patchMeshShadedFill\n"); |
| return true; |
| } |
| |
| void PSOutputDev::clip(GfxState *state) { |
| doPath(state->getPath()); |
| writePS("W\n"); |
| } |
| |
| void PSOutputDev::eoClip(GfxState *state) { |
| doPath(state->getPath()); |
| writePS("W*\n"); |
| } |
| |
| void PSOutputDev::clipToStrokePath(GfxState *state) { |
| doPath(state->getPath()); |
| writePS("Ws\n"); |
| } |
| |
| void PSOutputDev::doPath(const GfxPath *path) { |
| double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4; |
| int n, m, i, j; |
| |
| n = path->getNumSubpaths(); |
| |
| if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) { |
| const GfxSubpath *subpath = path->getSubpath(0); |
| x0 = subpath->getX(0); |
| y0 = subpath->getY(0); |
| x4 = subpath->getX(4); |
| y4 = subpath->getY(4); |
| if (x4 == x0 && y4 == y0) { |
| x1 = subpath->getX(1); |
| y1 = subpath->getY(1); |
| x2 = subpath->getX(2); |
| y2 = subpath->getY(2); |
| x3 = subpath->getX(3); |
| y3 = subpath->getY(3); |
| if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) { |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", |
| x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, |
| fabs(x2 - x0), fabs(y1 - y0)); |
| return; |
| } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) { |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", |
| x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, |
| fabs(x1 - x0), fabs(y2 - y0)); |
| return; |
| } |
| } |
| } |
| |
| for (i = 0; i < n; ++i) { |
| const GfxSubpath *subpath = path->getSubpath(i); |
| m = subpath->getNumPoints(); |
| writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0)); |
| j = 1; |
| while (j < m) { |
| if (subpath->getCurve(j)) { |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n", |
| subpath->getX(j), subpath->getY(j), |
| subpath->getX(j+1), subpath->getY(j+1), |
| subpath->getX(j+2), subpath->getY(j+2)); |
| j += 3; |
| } else { |
| writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j)); |
| ++j; |
| } |
| } |
| if (subpath->isClosed()) { |
| writePS("h\n"); |
| } |
| } |
| } |
| |
| void PSOutputDev::drawString(GfxState *state, const GooString *s) { |
| GfxFont *font; |
| int wMode; |
| int *codeToGID; |
| GooString *s2; |
| double dx, dy, originX, originY; |
| const char *p; |
| UnicodeMap *uMap; |
| CharCode code; |
| const Unicode *u; |
| char buf[8]; |
| double *dxdy; |
| int dxdySize, len, nChars, uLen, n, m, i, j; |
| int maxGlyphInt; |
| CharCode maxGlyph; |
| |
| // for pdftohtml, output PS without text |
| if( displayText == false ) |
| return; |
| |
| // check for invisible text -- this is used by Acrobat Capture |
| if (state->getRender() == 3) { |
| return; |
| } |
| |
| // ignore empty strings |
| if (s->getLength() == 0) { |
| return; |
| } |
| |
| // get the font |
| if (!(font = state->getFont())) { |
| return; |
| } |
| maxGlyphInt = (font->getName() ? perFontMaxValidGlyph[font->getName()->toStr()] : 0); |
| if (maxGlyphInt < 0) maxGlyphInt = 0; |
| maxGlyph = (CharCode) maxGlyphInt; |
| wMode = font->getWMode(); |
| |
| // check for a subtitute 16-bit font |
| uMap = nullptr; |
| codeToGID = nullptr; |
| if (font->isCIDFont()) { |
| for (i = 0; i < font16EncLen; ++i) { |
| if (*font->getID() == font16Enc[i].fontID) { |
| if (!font16Enc[i].enc) { |
| // font substitution failed, so don't output any text |
| return; |
| } |
| uMap = globalParams->getUnicodeMap(font16Enc[i].enc); |
| break; |
| } |
| } |
| |
| // check for a code-to-GID map |
| } else { |
| for (i = 0; i < font8InfoLen; ++i) { |
| if (*font->getID() == font8Info[i].fontID) { |
| codeToGID = font8Info[i].codeToGID; |
| break; |
| } |
| } |
| } |
| |
| // compute the positioning (dx, dy) for each char in the string |
| nChars = 0; |
| p = s->c_str(); |
| len = s->getLength(); |
| s2 = new GooString(); |
| dxdySize = font->isCIDFont() ? 8 : s->getLength(); |
| dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double)); |
| while (len > 0) { |
| n = font->getNextChar(p, len, &code, |
| &u, &uLen, |
| &dx, &dy, &originX, &originY); |
| dx *= state->getFontSize(); |
| dy *= state->getFontSize(); |
| if (wMode) { |
| dy += state->getCharSpace(); |
| if (n == 1 && *p == ' ') { |
| dy += state->getWordSpace(); |
| } |
| } else { |
| dx += state->getCharSpace(); |
| if (n == 1 && *p == ' ') { |
| dx += state->getWordSpace(); |
| } |
| } |
| dx *= state->getHorizScaling(); |
| if (font->isCIDFont()) { |
| if (uMap) { |
| if (nChars + uLen > dxdySize) { |
| do { |
| dxdySize *= 2; |
| } while (nChars + uLen > dxdySize); |
| dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); |
| } |
| for (i = 0; i < uLen; ++i) { |
| m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); |
| for (j = 0; j < m; ++j) { |
| s2->append(buf[j]); |
| } |
| //~ this really needs to get the number of chars in the target |
| //~ encoding - which may be more than the number of Unicode |
| //~ chars |
| dxdy[2 * nChars] = dx; |
| dxdy[2 * nChars + 1] = dy; |
| ++nChars; |
| } |
| } else if (maxGlyph > 0 && code > maxGlyph) { |
| // Ignore this code. |
| // Using it will exceed the number of glyphs in the font and generate |
| // /rangecheck in --xyshow-- |
| if (nChars > 0) { |
| dxdy[2 * (nChars-1) ] += dx; |
| dxdy[2 * (nChars-1) + 1 ] += dy; |
| } |
| } else { |
| if (nChars + 1 > dxdySize) { |
| dxdySize *= 2; |
| dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); |
| } |
| s2->append((char)((code >> 8) & 0xff)); |
| s2->append((char)(code & 0xff)); |
| dxdy[2 * nChars] = dx; |
| dxdy[2 * nChars + 1] = dy; |
| ++nChars; |
| } |
| } else { |
| if (!codeToGID || codeToGID[code] >= 0) { |
| s2->append((char)code); |
| dxdy[2 * nChars] = dx; |
| dxdy[2 * nChars + 1] = dy; |
| ++nChars; |
| } |
| } |
| p += n; |
| len -= n; |
| } |
| if (uMap) { |
| uMap->decRefCnt(); |
| } |
| |
| if (nChars > 0) { |
| writePSString(s2->toStr()); |
| writePS("\n["); |
| for (i = 0; i < 2 * nChars; ++i) { |
| if (i > 0) { |
| writePS("\n"); |
| } |
| writePSFmt("{0:.6g}", dxdy[i]); |
| } |
| writePS("] Tj\n"); |
| } |
| gfree(dxdy); |
| delete s2; |
| |
| if (state->getRender() & 4) { |
| haveTextClip = true; |
| } |
| } |
| |
| void PSOutputDev::beginTextObject(GfxState *state) { |
| } |
| |
| void PSOutputDev::endTextObject(GfxState *state) { |
| if (haveTextClip) { |
| writePS("Tclip\n"); |
| haveTextClip = false; |
| } |
| } |
| |
| void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, |
| int width, int height, bool invert, |
| bool interpolate, bool inlineImg) { |
| int len; |
| |
| len = height * ((width + 7) / 8); |
| switch (level) { |
| case psLevel1: |
| case psLevel1Sep: |
| doImageL1(ref, nullptr, invert, inlineImg, str, width, height, len, |
| nullptr, nullptr, 0, 0, false); |
| break; |
| case psLevel2: |
| case psLevel2Sep: |
| doImageL2(ref, nullptr, invert, inlineImg, str, width, height, len, |
| nullptr, nullptr, 0, 0, false); |
| break; |
| case psLevel3: |
| case psLevel3Sep: |
| doImageL3(ref, nullptr, invert, inlineImg, str, width, height, len, |
| nullptr, nullptr, 0, 0, false); |
| break; |
| } |
| } |
| |
| void PSOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, |
| int width, int height, bool invert, |
| bool inlineImg, double *baseMatrix) { |
| if (level != psLevel1 && level != psLevel1Sep) { |
| maskToClippingPath(str, width, height, invert); |
| } |
| } |
| |
| void PSOutputDev::unsetSoftMaskFromImageMask(GfxState * state, double *baseMatrix) { |
| if (level != psLevel1 && level != psLevel1Sep) { |
| writePS("pdfImClipEnd\n"); |
| } |
| } |
| |
| void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, |
| int width, int height, GfxImageColorMap *colorMap, |
| bool interpolate, const int *maskColors, bool inlineImg) { |
| int len; |
| |
| len = height * ((width * colorMap->getNumPixelComps() * |
| colorMap->getBits() + 7) / 8); |
| switch (level) { |
| case psLevel1: |
| doImageL1(ref, colorMap, false, inlineImg, str, |
| width, height, len, maskColors, nullptr, 0, 0, false); |
| break; |
| case psLevel1Sep: |
| //~ handle indexed, separation, ... color spaces |
| doImageL1Sep(ref, colorMap, false, inlineImg, str, |
| width, height, len, maskColors, nullptr, 0, 0, false); |
| break; |
| case psLevel2: |
| case psLevel2Sep: |
| doImageL2(ref, colorMap, false, inlineImg, str, |
| width, height, len, maskColors, nullptr, 0, 0, false); |
| break; |
| case psLevel3: |
| case psLevel3Sep: |
| doImageL3(ref, colorMap, false, inlineImg, str, |
| width, height, len, maskColors, nullptr, 0, 0, false); |
| break; |
| } |
| t3Cacheable = false; |
| } |
| |
| void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, |
| int width, int height, |
| GfxImageColorMap *colorMap, |
| bool interpolate, |
| Stream *maskStr, |
| int maskWidth, int maskHeight, |
| bool maskInvert, bool maskInterpolate) { |
| int len; |
| |
| len = height * ((width * colorMap->getNumPixelComps() * |
| colorMap->getBits() + 7) / 8); |
| switch (level) { |
| case psLevel1: |
| doImageL1(ref, colorMap, false, false, str, width, height, len, |
| nullptr, maskStr, maskWidth, maskHeight, maskInvert); |
| break; |
| case psLevel1Sep: |
| //~ handle indexed, separation, ... color spaces |
| doImageL1Sep(ref, colorMap, false, false, str, width, height, len, |
| nullptr, maskStr, maskWidth, maskHeight, maskInvert); |
| break; |
| case psLevel2: |
| case psLevel2Sep: |
| doImageL2(ref, colorMap, false, false, str, width, height, len, |
| nullptr, maskStr, maskWidth, maskHeight, maskInvert); |
| break; |
| case psLevel3: |
| case psLevel3Sep: |
| doImageL3(ref, colorMap, false, false, str, width, height, len, |
| nullptr, maskStr, maskWidth, maskHeight, maskInvert); |
| break; |
| } |
| t3Cacheable = false; |
| } |
| |
| void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, |
| bool invert, bool inlineImg, |
| Stream *str, int width, int height, int len, |
| const int *maskColors, Stream *maskStr, |
| int maskWidth, int maskHeight, bool maskInvert) { |
| ImageStream *imgStr; |
| unsigned char pixBuf[gfxColorMaxComps]; |
| GfxGray gray; |
| int col, x, y, c, i; |
| char hexBuf[32*2 + 2]; // 32 values X 2 chars/value + line ending + null |
| unsigned char digit, grayValue; |
| |
| // explicit masking |
| if (maskStr && !(maskColors && colorMap)) { |
| maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert); |
| } |
| |
| if ((inType3Char || preloadImagesForms) && !colorMap) { |
| if (inlineImg) { |
| // create an array |
| str = new FixedLengthEncoder(str, len); |
| str = new ASCIIHexEncoder(str); |
| str->reset(); |
| col = 0; |
| writePS("[<"); |
| do { |
| do { |
| c = str->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == '>' || c == EOF) { |
| break; |
| } |
| writePSChar(c); |
| ++col; |
| // each line is: "<...data...><eol>" |
| // so max data length = 255 - 4 = 251 |
| // but make it 240 just to be safe |
| // chunks are 2 bytes each, so we need to stop on an even col number |
| if (col == 240) { |
| writePS(">\n<"); |
| col = 0; |
| } |
| } while (c != '>' && c != EOF); |
| writePS(">]\n"); |
| writePS("0\n"); |
| str->close(); |
| delete str; |
| } else { |
| // make sure the image is setup, it sometimes is not like on bug #17645 |
| setupImage(ref->getRef(), str, false); |
| // set up to use the array already created by setupImages() |
| writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref->getRefNum(), ref->getRefGen()); |
| } |
| } |
| |
| // image/imagemask command |
| if ((inType3Char || preloadImagesForms) && !colorMap) { |
| writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n", |
| width, height, invert ? "true" : "false", |
| width, -height, height); |
| } else if (colorMap) { |
| writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}\n", |
| width, height, |
| width, -height, height, |
| useBinary ? "Bin" : ""); |
| } else { |
| writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1{6:s}\n", |
| width, height, invert ? "true" : "false", |
| width, -height, height, |
| useBinary ? "Bin" : ""); |
| } |
| |
| // image data |
| if (!((inType3Char || preloadImagesForms) && !colorMap)) { |
| |
| if (colorMap) { |
| |
| // set up to process the data stream |
| imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), |
| colorMap->getBits()); |
| imgStr->reset(); |
| |
| // process the data stream |
| i = 0; |
| for (y = 0; y < height; ++y) { |
| |
| // write the line |
| for (x = 0; x < width; ++x) { |
| imgStr->getPixel(pixBuf); |
| colorMap->getGray(pixBuf, &gray); |
| grayValue = colToByte(gray); |
| if (useBinary) { |
| hexBuf[i++] = grayValue; |
| } else { |
| digit = grayValue / 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| digit = grayValue % 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| } |
| if (i >= 64) { |
| if (!useBinary) { |
| hexBuf[i++] = '\n'; |
| } |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| if (i != 0) { |
| if (!useBinary) { |
| hexBuf[i++] = '\n'; |
| } |
| writePSBuf(hexBuf, i); |
| } |
| str->close(); |
| delete imgStr; |
| |
| // imagemask |
| } else { |
| str->reset(); |
| i = 0; |
| for (y = 0; y < height; ++y) { |
| for (x = 0; x < width; x += 8) { |
| grayValue = str->getChar(); |
| if (useBinary) { |
| hexBuf[i++] = grayValue; |
| } else { |
| digit = grayValue / 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| digit = grayValue % 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a' - 10: '0'); |
| } |
| if (i >= 64) { |
| if (!useBinary) { |
| hexBuf[i++] = '\n'; |
| } |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| if (i != 0) { |
| if (!useBinary) { |
| hexBuf[i++] = '\n'; |
| } |
| writePSBuf(hexBuf, i); |
| } |
| str->close(); |
| } |
| } |
| |
| if (maskStr && !(maskColors && colorMap)) { |
| writePS("pdfImClipEnd\n"); |
| } |
| } |
| |
| void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, |
| bool invert, bool inlineImg, |
| Stream *str, int width, int height, int len, |
| const int *maskColors, Stream *maskStr, |
| int maskWidth, int maskHeight, bool maskInvert) { |
| ImageStream *imgStr; |
| unsigned char *lineBuf; |
| unsigned char pixBuf[gfxColorMaxComps]; |
| GfxCMYK cmyk; |
| int x, y, i, comp; |
| bool checkProcessColor; |
| char hexBuf[32*2 + 2]; // 32 values X 2 chars/value + line ending + null |
| unsigned char digit; |
| bool isGray; |
| |
| // explicit masking |
| if (maskStr && !(maskColors && colorMap)) { |
| maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert); |
| } |
| |
| // allocate a line buffer |
| lineBuf = (unsigned char *)gmallocn(width, 4); |
| |
| // scan for all gray |
| if (getOptimizeColorSpace()) { |
| ImageStream *imgCheckStr; |
| imgCheckStr = new ImageStream(str, width, colorMap->getNumPixelComps(), |
| colorMap->getBits()); |
| imgCheckStr->reset(); |
| isGray = true; |
| for (y = 0; y < height; ++y) { |
| for (x = 0; x < width; ++x) { |
| imgCheckStr->getPixel(pixBuf); |
| colorMap->getCMYK(pixBuf, &cmyk); |
| if (colToByte(cmyk.c) != colToByte(cmyk.m) || colToByte(cmyk.c) != colToByte(cmyk.y)) { |
| isGray = false; |
| y = height; // end outer loop |
| break; |
| } |
| } |
| } |
| imgCheckStr->close(); |
| delete imgCheckStr; |
| } else { |
| isGray = false; |
| } |
| |
| // set up to process the data stream |
| imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), |
| colorMap->getBits()); |
| imgStr->reset(); |
| |
| // width, height, matrix, bits per component |
| writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}{6:s}\n", |
| width, height, |
| width, -height, height, |
| isGray ? "" : "Sep", |
| useBinary ? "Bin" : ""); |
| |
| // process the data stream |
| checkProcessColor = true; |
| i = 0; |
| |
| if (isGray) { |
| int g; |
| for (y = 0; y < height; ++y) { |
| |
| // read the line |
| if (checkProcessColor) { |
| checkProcessColor = ((psProcessBlack & processColors) == 0); |
| } |
| for (x = 0; x < width; ++x) { |
| imgStr->getPixel(pixBuf); |
| colorMap->getCMYK(pixBuf, &cmyk); |
| g = colToByte(cmyk.c) + colToByte(cmyk.k); |
| if (checkProcessColor && g > 0) { |
| processColors |= psProcessBlack; |
| } |
| g = 255 - g; |
| if (g < 0) g = 0; |
| if (useBinary) { |
| hexBuf[i++] = g; |
| } else { |
| digit = g / 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a'-10: '0'); |
| digit = g % 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a'-10: '0'); |
| } |
| if (i >= 64) { |
| if (!useBinary) { |
| hexBuf[i++] = '\n'; |
| } |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| } else { |
| for (y = 0; y < height; ++y) { |
| |
| // read the line |
| if (checkProcessColor) { |
| checkProcessColor = (((psProcessCyan | psProcessMagenta | psProcessYellow | psProcessBlack) & ~processColors) != 0); |
| } |
| if (checkProcessColor) { |
| for (x = 0; x < width; ++x) { |
| imgStr->getPixel(pixBuf); |
| colorMap->getCMYK(pixBuf, &cmyk); |
| lineBuf[4*x+0] = colToByte(cmyk.c); |
| lineBuf[4*x+1] = colToByte(cmyk.m); |
| lineBuf[4*x+2] = colToByte(cmyk.y); |
| lineBuf[4*x+3] = colToByte(cmyk.k); |
| addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), |
| colToDbl(cmyk.y), colToDbl(cmyk.k)); |
| } |
| } else { |
| for (x = 0; x < width; ++x) { |
| imgStr->getPixel(pixBuf); |
| colorMap->getCMYK(pixBuf, &cmyk); |
| lineBuf[4*x+0] = colToByte(cmyk.c); |
| lineBuf[4*x+1] = colToByte(cmyk.m); |
| lineBuf[4*x+2] = colToByte(cmyk.y); |
| lineBuf[4*x+3] = colToByte(cmyk.k); |
| } |
| } |
| |
| // write one line of each color component |
| if (useBinary) { |
| for (comp = 0; comp < 4; ++comp) { |
| for (x = 0; x < width; ++x) { |
| hexBuf[i++] = lineBuf[4*x + comp]; |
| if (i >= 64) { |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| } else { |
| for (comp = 0; comp < 4; ++comp) { |
| for (x = 0; x < width; ++x) { |
| digit = lineBuf[4*x + comp] / 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a'-10: '0'); |
| digit = lineBuf[4*x + comp] % 16; |
| hexBuf[i++] = digit + ((digit >= 10)? 'a'-10: '0'); |
| if (i >= 64) { |
| hexBuf[i++] = '\n'; |
| writePSBuf(hexBuf, i); |
| i = 0; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (i != 0) { |
| if (!useBinary) { |
| hexBuf[i++] = '\n'; |
| } |
| writePSBuf(hexBuf, i); |
| } |
| |
| str->close(); |
| delete imgStr; |
| gfree(lineBuf); |
| |
| if (maskStr && !(maskColors && colorMap)) { |
| writePS("pdfImClipEnd\n"); |
| } |
| } |
| |
| void PSOutputDev::maskToClippingPath(Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert) { |
| ImageStream *imgStr; |
| unsigned char *line; |
| PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; |
| int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; |
| bool emitRect, addRect, extendRect; |
| int i, x0, x1, y, maskXor; |
| |
| imgStr = new ImageStream(maskStr, maskWidth, 1, 1); |
| imgStr->reset(); |
| rects0Len = rects1Len = rectsOutLen = 0; |
| rectsSize = rectsOutSize = 64; |
| rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); |
| rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); |
| rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); |
| maskXor = maskInvert ? 1 : 0; |
| for (y = 0; y < maskHeight; ++y) { |
| if (!(line = imgStr->getLine())) { |
| break; |
| } |
| i = 0; |
| rects1Len = 0; |
| for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; |
| for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; |
| while (x0 < maskWidth || i < rects0Len) { |
| emitRect = addRect = extendRect = false; |
| if (x0 >= maskWidth) { |
| emitRect = true; |
| } else if (i >= rects0Len) { |
| addRect = true; |
| } else if (rects0[i].x0 < x0) { |
| emitRect = true; |
| } else if (x0 < rects0[i].x0) { |
| addRect = true; |
| } else if (rects0[i].x1 == x1) { |
| extendRect = true; |
| } else { |
| emitRect = addRect = true; |
| } |
| if (emitRect) { |
| if (rectsOutLen == rectsOutSize) { |
| rectsOutSize *= 2; |
| rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); |
| } |
| rectsOut[rectsOutLen].x0 = rects0[i].x0; |
| rectsOut[rectsOutLen].x1 = rects0[i].x1; |
| rectsOut[rectsOutLen].y0 = maskHeight - y - 1; |
| rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1; |
| ++rectsOutLen; |
| ++i; |
| } |
| if (addRect || extendRect) { |
| if (rects1Len == rectsSize) { |
| rectsSize *= 2; |
| rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect)); |
| rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect)); |
| } |
| rects1[rects1Len].x0 = x0; |
| rects1[rects1Len].x1 = x1; |
| if (addRect) { |
| rects1[rects1Len].y0 = y; |
| } |
| if (extendRect) { |
| rects1[rects1Len].y0 = rects0[i].y0; |
| ++i; |
| } |
| ++rects1Len; |
| for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; |
| for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; |
| } |
| } |
| rectsTmp = rects0; |
| rects0 = rects1; |
| rects1 = rectsTmp; |
| i = rects0Len; |
| rects0Len = rects1Len; |
| rects1Len = i; |
| } |
| for (i = 0; i < rects0Len; ++i) { |
| if (rectsOutLen == rectsOutSize) { |
| rectsOutSize *= 2; |
| rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); |
| } |
| rectsOut[rectsOutLen].x0 = rects0[i].x0; |
| rectsOut[rectsOutLen].x1 = rects0[i].x1; |
| rectsOut[rectsOutLen].y0 = maskHeight - y - 1; |
| rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1; |
| ++rectsOutLen; |
| } |
| if (rectsOutLen < 65536/4) { |
| writePSFmt("{0:d} array 0\n", rectsOutLen * 4); |
| for (i = 0; i < rectsOutLen; ++i) { |
| writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n", |
| rectsOut[i].x0, rectsOut[i].y0, |
| rectsOut[i].x1 - rectsOut[i].x0, |
| rectsOut[i].y1 - rectsOut[i].y0); |
| } |
| writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth, maskHeight); |
| } else { |
| // would be over the limit of array size. |
| // make each rectangle path and clip. |
| writePS("gsave newpath\n"); |
| for (i = 0; i < rectsOutLen; ++i) { |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", |
| ((double)rectsOut[i].x0)/maskWidth, |
| ((double)rectsOut[i].y0)/maskHeight, |
| ((double)(rectsOut[i].x1 - rectsOut[i].x0))/maskWidth, |
| ((double)(rectsOut[i].y1 - rectsOut[i].y0))/maskHeight); |
| } |
| writePS("clip\n"); |
| } |
| gfree(rectsOut); |
| gfree(rects0); |
| gfree(rects1); |
| delete imgStr; |
| maskStr->close(); |
| } |
| |
| void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap, |
| bool invert, bool inlineImg, |
| Stream *str, int width, int height, int len, |
| const int *maskColors, Stream *maskStr, |
| int maskWidth, int maskHeight, bool maskInvert) { |
| Stream *str2; |
| ImageStream *imgStr; |
| unsigned char *line; |
| PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; |
| int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; |
| bool emitRect, addRect, extendRect; |
| GooString *s; |
| int n, numComps; |
| bool useLZW, useRLE, useASCII, useCompressed; |
| GfxSeparationColorSpace *sepCS; |
| GfxColor color; |
| GfxCMYK cmyk; |
| int c; |
| int col, i, j, x0, x1, y; |
| char dataBuf[4096]; |
| |
| rectsOutLen = 0; |
| |
| // color key masking |
| if (maskColors && colorMap && !inlineImg) { |
| // can't read the stream twice for inline images -- but masking |
| // isn't allowed with inline images anyway |
| numComps = colorMap->getNumPixelComps(); |
| imgStr = new ImageStream(str, width, numComps, colorMap->getBits()); |
| imgStr->reset(); |
| rects0Len = rects1Len = 0; |
| rectsSize = rectsOutSize = 64; |
| rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); |
| rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); |
| rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, |
| sizeof(PSOutImgClipRect)); |
| for (y = 0; y < height; ++y) { |
| if (!(line = imgStr->getLine())) { |
| break; |
| } |
| i = 0; |
| rects1Len = 0; |
| for (x0 = 0; x0 < width; ++x0) { |
| for (j = 0; j < numComps; ++j) { |
| if (line[x0*numComps+j] < maskColors[2*j] || |
| line[x0*numComps+j] > maskColors[2*j+1]) { |
| break; |
| } |
| } |
| if (j < numComps) { |
| break; |
| } |
| } |
| for (x1 = x0; x1 < width; ++x1) { |
| for (j = 0; j < numComps; ++j) { |
| if (line[x1*numComps+j] < maskColors[2*j] || |
| line[x1*numComps+j] > maskColors[2*j+1]) { |
| break; |
| } |
| } |
| if (j == numComps) { |
| break; |
| } |
| } |
| while (x0 < width || i < rects0Len) { |
| emitRect = addRect = extendRect = false; |
| if (x0 >= width) { |
| emitRect = true; |
| } else if (i >= rects0Len) { |
| addRect = true; |
| } else if (rects0[i].x0 < x0) { |
| emitRect = true; |
| } else if (x0 < rects0[i].x0) { |
| addRect = true; |
| } else if (rects0[i].x1 == x1) { |
| extendRect = true; |
| } else { |
| emitRect = addRect = true; |
| } |
| if (emitRect) { |
| if (rectsOutLen == rectsOutSize) { |
| rectsOutSize *= 2; |
| rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, |
| sizeof(PSOutImgClipRect)); |
| } |
| rectsOut[rectsOutLen].x0 = rects0[i].x0; |
| rectsOut[rectsOutLen].x1 = rects0[i].x1; |
| rectsOut[rectsOutLen].y0 = height - y - 1; |
| rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1; |
| ++rectsOutLen; |
| ++i; |
| } |
| if (addRect || extendRect) { |
| if (rects1Len == rectsSize) { |
| rectsSize *= 2; |
| rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, |
| sizeof(PSOutImgClipRect)); |
| rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, |
| sizeof(PSOutImgClipRect)); |
| } |
| rects1[rects1Len].x0 = x0; |
| rects1[rects1Len].x1 = x1; |
| if (addRect) { |
| rects1[rects1Len].y0 = y; |
| } |
| if (extendRect) { |
| rects1[rects1Len].y0 = rects0[i].y0; |
| ++i; |
| } |
| ++rects1Len; |
| for (x0 = x1; x0 < width; ++x0) { |
| for (j = 0; j < numComps; ++j) { |
| if (line[x0*numComps+j] < maskColors[2*j] || |
| line[x0*numComps+j] > maskColors[2*j+1]) { |
| break; |
| } |
| } |
| if (j < numComps) { |
| break; |
| } |
| } |
| for (x1 = x0; x1 < width; ++x1) { |
| for (j = 0; j < numComps; ++j) { |
| if (line[x1*numComps+j] < maskColors[2*j] || |
| line[x1*numComps+j] > maskColors[2*j+1]) { |
| break; |
| } |
| } |
| if (j == numComps) { |
| break; |
| } |
| } |
| } |
| } |
| rectsTmp = rects0; |
| rects0 = rects1; |
| rects1 = rectsTmp; |
| i = rects0Len; |
| rects0Len = rects1Len; |
| rects1Len = i; |
| } |
| for (i = 0; i < rects0Len; ++i) { |
| if (rectsOutLen == rectsOutSize) { |
| rectsOutSize *= 2; |
| rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, |
| sizeof(PSOutImgClipRect)); |
| } |
| rectsOut[rectsOutLen].x0 = rects0[i].x0; |
| rectsOut[rectsOutLen].x1 = rects0[i].x1; |
| rectsOut[rectsOutLen].y0 = height - y - 1; |
| rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1; |
| ++rectsOutLen; |
| } |
| if (rectsOutLen < 65536/4) { |
| writePSFmt("{0:d} array 0\n", rectsOutLen * 4); |
| for (i = 0; i < rectsOutLen; ++i) { |
| writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n", |
| rectsOut[i].x0, rectsOut[i].y0, |
| rectsOut[i].x1 - rectsOut[i].x0, |
| rectsOut[i].y1 - rectsOut[i].y0); |
| } |
| writePSFmt("pop {0:d} {1:d} pdfImClip\n", width, height); |
| } else { |
| // would be over the limit of array size. |
| // make each rectangle path and clip. |
| writePS("gsave newpath\n"); |
| for (i = 0; i < rectsOutLen; ++i) { |
| writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", |
| ((double)rectsOut[i].x0)/width, |
| ((double)rectsOut[i].y0)/height, |
| ((double)(rectsOut[i].x1 - rectsOut[i].x0))/width, |
| ((double)(rectsOut[i].y1 - rectsOut[i].y0))/height); |
| } |
| writePS("clip\n"); |
| } |
| gfree(rectsOut); |
| gfree(rects0); |
| gfree(rects1); |
| delete imgStr; |
| str->close(); |
| |
| // explicit masking |
| } else if (maskStr) { |
| maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert); |
| } |
| |
| // color space |
| if (colorMap) { |
| // Do not update the process color list for custom colors |
| bool isCustomColor = |
| (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) && |
| colorMap->getColorSpace()->getMode() == csDeviceN; |
| dumpColorSpaceL2(colorMap->getColorSpace(), false, !isCustomColor, false); |
| writePS(" setcolorspace\n"); |
| } |
| |
| // set up the image data |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| if (inlineImg) { |
| // create an array |
| str2 = new FixedLengthEncoder(str, len); |
| if (getEnableLZW()) { |
| str2 = new LZWEncoder(str2); |
| } else { |
| str2 = new RunLengthEncoder(str2); |
| } |
| if (useASCIIHex) { |
| str2 = new ASCIIHexEncoder(str2); |
| } else { |
| str2 = new ASCII85Encoder(str2); |
| } |
| str2->reset(); |
| col = 0; |
| writePS((char *)(useASCIIHex ? "[<" : "[<~")); |
| do { |
| do { |
| c = str2->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == (useASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| if (c == 'z') { |
| writePSChar(c); |
| ++col; |
| } else { |
| writePSChar(c); |
| ++col; |
| for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { |
| do { |
| c = str2->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == (useASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| writePSChar(c); |
| ++col; |
| } |
| } |
| // each line is: "<~...data...~><eol>" |
| // so max data length = 255 - 6 = 249 |
| // chunks are 1 or 5 bytes each, so we have to stop at 245 |
| // but make it 240 just to be safe |
| if (col > 240) { |
| writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); |
| col = 0; |
| } |
| } while (c != (useASCIIHex ? '>' : '~') && c != EOF); |
| writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); |
| // add an extra entry because the LZWDecode/RunLengthDecode filter may |
| // read past the end |
| writePS("<>]\n"); |
| writePS("0\n"); |
| str2->close(); |
| delete str2; |
| } else { |
| // make sure the image is setup, it sometimes is not like on bug #17645 |
| setupImage(ref->getRef(), str, false); |
| // set up to use the array already created by setupImages() |
| writePSFmt("ImData_{0:d}_{1:d} 0 0\n",ref->getRefNum(), ref->getRefGen()); |
| } |
| } |
| |
| // image dictionary |
| writePS("<<\n /ImageType 1\n"); |
| |
| // width, height, matrix, bits per component |
| writePSFmt(" /Width {0:d}\n", width); |
| writePSFmt(" /Height {0:d}\n", height); |
| writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", |
| width, -height, height); |
| if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { |
| writePS(" /BitsPerComponent 8\n"); |
| } else { |
| writePSFmt(" /BitsPerComponent {0:d}\n", |
| colorMap ? colorMap->getBits() : 1); |
| } |
| |
| // decode |
| if (colorMap) { |
| writePS(" /Decode ["); |
| if ((level == psLevel2Sep || level == psLevel3Sep) && |
| colorMap->getColorSpace()->getMode() == csSeparation) { |
| // this matches up with the code in the pdfImSep operator |
| n = (1 << colorMap->getBits()) - 1; |
| writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, |
| colorMap->getDecodeHigh(0) * n); |
| } else if (colorMap->getColorSpace()->getMode() == csDeviceN) { |
| numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> |
| getAlt()->getNComps(); |
| for (i = 0; i < numComps; ++i) { |
| if (i > 0) { |
| writePS(" "); |
| } |
| writePS("0 1"); |
| } |
| } else { |
| numComps = colorMap->getNumPixelComps(); |
| for (i = 0; i < numComps; ++i) { |
| if (i > 0) { |
| writePS(" "); |
| } |
| writePSFmt("{0:.4g} {1:.4g}", |
| colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i)); |
| } |
| } |
| writePS("]\n"); |
| } else { |
| writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); |
| } |
| |
| // data source |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| if (inlineImg) { |
| writePS(" /DataSource { pdfImStr }\n"); |
| } else { |
| writePS(" /DataSource { dup 65535 ge { pop 1 add 0 } if 2 index 2" |
| " index get 1 index get exch 1 add exch }\n"); |
| } |
| } else { |
| writePS(" /DataSource currentfile\n"); |
| } |
| |
| // filters |
| if ((mode == psModeForm || inType3Char || preloadImagesForms) && |
| uncompressPreloadedImages) { |
| s = nullptr; |
| useLZW = useRLE = false; |
| useCompressed = false; |
| useASCII = false; |
| } else { |
| s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, |
| " "); |
| if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || |
| inlineImg || !s) { |
| if (getEnableLZW()) { |
| useLZW = true; |
| useRLE = false; |
| } else { |
| useRLE = true; |
| useLZW = false; |
| } |
| useASCII = !(mode == psModeForm || inType3Char || preloadImagesForms); |
| useCompressed = false; |
| } else { |
| useLZW = useRLE = false; |
| useASCII = str->isBinary() && |
| !(mode == psModeForm || inType3Char || preloadImagesForms); |
| useCompressed = true; |
| } |
| } |
| if (useASCII) { |
| writePSFmt(" /ASCII{0:s}Decode filter\n", |
| useASCIIHex ? "Hex" : "85"); |
| } |
| if (useLZW) { |
| writePS(" /LZWDecode filter\n"); |
| } else if (useRLE) { |
| writePS(" /RunLengthDecode filter\n"); |
| } |
| if (useCompressed) { |
| writePS(s->c_str()); |
| } |
| if (s) { |
| delete s; |
| } |
| |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| |
| // end of image dictionary |
| writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask"); |
| |
| // get rid of the array and index |
| if (!inlineImg) writePS("pop "); |
| writePS("pop pop\n"); |
| |
| } else { |
| |
| // cut off inline image streams at appropriate length |
| if (inlineImg) { |
| str = new FixedLengthEncoder(str, len); |
| } else if (useCompressed) { |
| str = str->getUndecodedStream(); |
| } |
| |
| // recode DeviceN data |
| if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { |
| str = new DeviceNRecoder(str, width, height, colorMap); |
| } |
| |
| // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters |
| if (useLZW) { |
| str = new LZWEncoder(str); |
| } else if (useRLE) { |
| str = new RunLengthEncoder(str); |
| } |
| if (useASCII) { |
| if (useASCIIHex) { |
| str = new ASCIIHexEncoder(str); |
| } else { |
| str = new ASCII85Encoder(str); |
| } |
| } |
| |
| // end of image dictionary |
| writePS(">>\n"); |
| #ifdef OPI_SUPPORT |
| if (opi13Nest) { |
| if (inlineImg) { |
| // this can't happen -- OPI dictionaries are in XObjects |
| error(errSyntaxError, -1, "OPI in inline image"); |
| n = 0; |
| } else { |
| // need to read the stream to count characters -- the length |
| // is data-dependent (because of ASCII and LZW/RLE filters) |
| str->reset(); |
| n = 0; |
| while ((c = str->getChar()) != EOF) { |
| ++n; |
| } |
| str->close(); |
| } |
| // +6/7 for "pdfIm\n" / "pdfImM\n" |
| // +8 for newline + trailer |
| n += colorMap ? 14 : 15; |
| writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n); |
| } |
| #endif |
| if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && |
| colorMap->getColorSpace()->getMode() == csSeparation && colorMap->getBits() == 8) { |
| color.c[0] = gfxColorComp1; |
| sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); |
| sepCS->getCMYK(&color, &cmyk); |
| writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", |
| colToDbl(cmyk.c), colToDbl(cmyk.m), |
| colToDbl(cmyk.y), colToDbl(cmyk.k), |
| sepCS->getName()); |
| } else { |
| writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); |
| } |
| |
| // copy the stream data |
| str->reset(); |
| i = 0; |
| while ((c = str->getChar()) != EOF) { |
| dataBuf[i++] = c; |
| if (i >= (int)sizeof(dataBuf)) { |
| writePSBuf(dataBuf, i); |
| i = 0; |
| } |
| } |
| if (i > 0) { |
| writePSBuf(dataBuf, i); |
| } |
| str->close(); |
| |
| // add newline and trailer to the end |
| writePSChar('\n'); |
| writePS("%-EOD-\n"); |
| #ifdef OPI_SUPPORT |
| if (opi13Nest) { |
| writePS("%%EndData\n"); |
| } |
| #endif |
| |
| // delete encoders |
| if (useLZW || useRLE || useASCII || inlineImg) { |
| delete str; |
| } |
| } |
| |
| if ((maskColors && colorMap && !inlineImg) || maskStr) { |
| if (rectsOutLen < 65536/4) { |
| writePS("pdfImClipEnd\n"); |
| } else { |
| writePS("grestore\n"); |
| } |
| } |
| } |
| |
| //~ this doesn't currently support OPI |
| void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap, |
| bool invert, bool inlineImg, |
| Stream *str, int width, int height, int len, |
| const int *maskColors, Stream *maskStr, |
| int maskWidth, int maskHeight, bool maskInvert) { |
| Stream *str2; |
| GooString *s; |
| int n, numComps; |
| bool useFlate, useLZW, useRLE, useASCII, useCompressed; |
| bool maskUseFlate, maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed; |
| GooString *maskFilters; |
| GfxSeparationColorSpace *sepCS; |
| GfxColor color; |
| GfxCMYK cmyk; |
| int c; |
| int col, i; |
| |
| useFlate = useLZW = useRLE = useASCII = useCompressed = false; |
| maskUseFlate = maskUseLZW = maskUseRLE = maskUseASCII = maskUseCompressed = false; |
| maskFilters = nullptr; // make gcc happy |
| |
| // explicit masking |
| if (maskStr) { |
| |
| // mask data source |
| if ((mode == psModeForm || inType3Char || preloadImagesForms) && |
| uncompressPreloadedImages) { |
| s = nullptr; |
| } else { |
| s = maskStr->getPSFilter(3, " "); |
| if (!s) { |
| if (getEnableFlate()) { |
| maskUseFlate = true; |
| } else if (getEnableLZW()) { |
| maskUseLZW = true; |
| } else { |
| maskUseRLE = true; |
| } |
| maskUseASCII = !(mode == psModeForm || inType3Char || preloadImagesForms); |
| } else { |
| maskUseASCII = maskStr->isBinary() && |
| !(mode == psModeForm || inType3Char || preloadImagesForms); |
| maskUseCompressed = true; |
| } |
| } |
| maskFilters = new GooString(); |
| if (maskUseASCII) { |
| maskFilters->appendf(" /ASCII{0:s}Decode filter\n", |
| useASCIIHex ? "Hex" : "85"); |
| } |
| if (maskUseFlate) { |
| maskFilters->append(" /FlateDecode filter\n"); |
| } else if (maskUseLZW) { |
| maskFilters->append(" /LZWDecode filter\n"); |
| } else if (maskUseRLE) { |
| maskFilters->append(" /RunLengthDecode filter\n"); |
| } |
| if (maskUseCompressed) { |
| maskFilters->append(s); |
| } |
| if (s) { |
| delete s; |
| } |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", |
| ref->getRefNum(), ref->getRefGen()); |
| } else { |
| writePS("currentfile\n"); |
| writePS(maskFilters->c_str()); |
| writePS("pdfMask\n"); |
| |
| // add FlateEncode/LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters |
| if (maskUseCompressed) { |
| maskStr = maskStr->getUndecodedStream(); |
| } |
| #ifdef ENABLE_ZLIB |
| if (maskUseFlate) { |
| maskStr = new FlateEncoder(maskStr); |
| } else |
| #endif |
| if (maskUseLZW) { |
| maskStr = new LZWEncoder(maskStr); |
| } else if (maskUseRLE) { |
| maskStr = new RunLengthEncoder(maskStr); |
| } |
| if (maskUseASCII) { |
| if (useASCIIHex) { |
| maskStr = new ASCIIHexEncoder(maskStr); |
| } else { |
| maskStr = new ASCII85Encoder(maskStr); |
| } |
| } |
| |
| // copy the stream data |
| maskStr->reset(); |
| while ((c = maskStr->getChar()) != EOF) { |
| writePSChar(c); |
| } |
| maskStr->close(); |
| writePSChar('\n'); |
| writePS("%-EOD-\n"); |
| |
| // delete encoders |
| if (maskUseFlate || maskUseLZW || maskUseRLE || maskUseASCII) { |
| delete maskStr; |
| } |
| } |
| } |
| |
| // color space |
| if (colorMap) { |
| // Do not update the process color list for custom colors |
| bool isCustomColor = |
| (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) && |
| colorMap->getColorSpace()->getMode() == csDeviceN; |
| dumpColorSpaceL2(colorMap->getColorSpace(), false, !isCustomColor, false); |
| writePS(" setcolorspace\n"); |
| } |
| |
| // set up the image data |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| if (inlineImg) { |
| // create an array |
| str2 = new FixedLengthEncoder(str, len); |
| #ifdef ENABLE_ZLIB |
| if (getEnableFlate()) { |
| str2 = new FlateEncoder(str2); |
| } else |
| #endif |
| if (getEnableLZW()) { |
| str2 = new LZWEncoder(str2); |
| } else { |
| str2 = new RunLengthEncoder(str2); |
| } |
| if (useASCIIHex) { |
| str2 = new ASCIIHexEncoder(str2); |
| } else { |
| str2 = new ASCII85Encoder(str2); |
| } |
| str2->reset(); |
| col = 0; |
| writePS((char *)(useASCIIHex ? "[<" : "[<~")); |
| do { |
| do { |
| c = str2->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == (useASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| if (c == 'z') { |
| writePSChar(c); |
| ++col; |
| } else { |
| writePSChar(c); |
| ++col; |
| for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { |
| do { |
| c = str2->getChar(); |
| } while (c == '\n' || c == '\r'); |
| if (c == (useASCIIHex ? '>' : '~') || c == EOF) { |
| break; |
| } |
| writePSChar(c); |
| ++col; |
| } |
| } |
| // each line is: "<~...data...~><eol>" |
| // so max data length = 255 - 6 = 249 |
| // chunks are 1 or 5 bytes each, so we have to stop at 245 |
| // but make it 240 just to be safe |
| if (col > 240) { |
| writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); |
| col = 0; |
| } |
| } while (c != (useASCIIHex ? '>' : '~') && c != EOF); |
| writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); |
| // add an extra entry because the FlateEncode/LZWDecode/RunLengthDecode filter may |
| // read past the end |
| writePS("<>]\n"); |
| writePS("0\n"); |
| str2->close(); |
| delete str2; |
| } else { |
| // make sure the image is setup, it sometimes is not like on bug #17645 |
| setupImage(ref->getRef(), str, false); |
| // set up to use the array already created by setupImages() |
| writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref->getRefNum(), ref->getRefGen()); |
| } |
| } |
| |
| // explicit masking |
| if (maskStr) { |
| writePS("<<\n /ImageType 3\n"); |
| writePS(" /InterleaveType 3\n"); |
| writePS(" /DataDict\n"); |
| } |
| |
| // image (data) dictionary |
| writePSFmt("<<\n /ImageType {0:d}\n", (maskColors && colorMap) ? 4 : 1); |
| |
| // color key masking |
| if (maskColors && colorMap) { |
| writePS(" /MaskColor [\n"); |
| numComps = colorMap->getNumPixelComps(); |
| for (i = 0; i < 2 * numComps; i += 2) { |
| writePSFmt(" {0:d} {1:d}\n", maskColors[i], maskColors[i+1]); |
| } |
| writePS(" ]\n"); |
| } |
| |
| // width, height, matrix, bits per component |
| writePSFmt(" /Width {0:d}\n", width); |
| writePSFmt(" /Height {0:d}\n", height); |
| writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", |
| width, -height, height); |
| if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { |
| writePS(" /BitsPerComponent 8\n"); |
| } else { |
| writePSFmt(" /BitsPerComponent {0:d}\n", |
| colorMap ? colorMap->getBits() : 1); |
| } |
| |
| // decode |
| if (colorMap) { |
| writePS(" /Decode ["); |
| if ((level == psLevel2Sep || level == psLevel3Sep) && |
| colorMap->getColorSpace()->getMode() == csSeparation) { |
| // this matches up with the code in the pdfImSep operator |
| n = (1 << colorMap->getBits()) - 1; |
| writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, |
| colorMap->getDecodeHigh(0) * n); |
| } else { |
| numComps = colorMap->getNumPixelComps(); |
| for (i = 0; i < numComps; ++i) { |
| if (i > 0) { |
| writePS(" "); |
| } |
| writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), |
| colorMap->getDecodeHigh(i)); |
| } |
| } |
| writePS("]\n"); |
| } else { |
| writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); |
| } |
| |
| // data source |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| if (inlineImg) { |
| writePS(" /DataSource { pdfImStr }\n"); |
| } else { |
| writePS(" /DataSource { dup 65535 ge { pop 1 add 0 } if 2 index 2" |
| " index get 1 index get exch 1 add exch }\n"); |
| } |
| } else { |
| writePS(" /DataSource currentfile\n"); |
| } |
| |
| // filters |
| |
| useFlate = useLZW = useRLE = false; |
| useCompressed = false; |
| useASCII = false; |
| |
| if ((mode == psModeForm || inType3Char || preloadImagesForms) && |
| uncompressPreloadedImages) { |
| s = nullptr; |
| } else { |
| s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, |
| " "); |
| if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || |
| inlineImg || !s) { |
| if (getEnableFlate()) { |
| useFlate = true; |
| } else if (getEnableLZW()) { |
| useLZW = true; |
| } else { |
| useRLE = true; |
| } |
| useASCII = !(mode == psModeForm || inType3Char || preloadImagesForms); |
| } else { |
| useASCII = str->isBinary() && |
| !(mode == psModeForm || inType3Char || preloadImagesForms); |
| useCompressed = true; |
| } |
| } |
| if (useASCII) { |
| writePSFmt(" /ASCII{0:s}Decode filter\n", |
| useASCIIHex ? "Hex" : "85"); |
| } |
| if (useFlate) { |
| writePS(" /FlateDecode filter\n"); |
| } else if (useLZW) { |
| writePS(" /LZWDecode filter\n"); |
| } else if (useRLE) { |
| writePS(" /RunLengthDecode filter\n"); |
| } |
| if (useCompressed) { |
| writePS(s->c_str()); |
| } |
| if (s) { |
| delete s; |
| } |
| |
| // end of image (data) dictionary |
| writePS(">>\n"); |
| |
| // explicit masking |
| if (maskStr) { |
| writePS(" /MaskDict\n"); |
| writePS("<<\n"); |
| writePS(" /ImageType 1\n"); |
| writePSFmt(" /Width {0:d}\n", maskWidth); |
| writePSFmt(" /Height {0:d}\n", maskHeight); |
| writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", |
| maskWidth, -maskHeight, maskHeight); |
| writePS(" /BitsPerComponent 1\n"); |
| writePSFmt(" /Decode [{0:d} {1:d}]\n", |
| maskInvert ? 1 : 0, maskInvert ? 0 : 1); |
| |
| // mask data source |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| writePS(" /DataSource {pdfMaskSrc}\n"); |
| writePS(maskFilters->c_str()); |
| } else { |
| writePS(" /DataSource maskStream\n"); |
| } |
| delete maskFilters; |
| |
| writePS(">>\n"); |
| writePS(">>\n"); |
| } |
| |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| |
| // image command |
| writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask"); |
| |
| } else { |
| |
| if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && |
| colorMap->getColorSpace()->getMode() == csSeparation && colorMap->getBits() == 8) { |
| color.c[0] = gfxColorComp1; |
| sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); |
| sepCS->getCMYK(&color, &cmyk); |
| writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", |
| colToDbl(cmyk.c), colToDbl(cmyk.m), |
| colToDbl(cmyk.y), colToDbl(cmyk.k), |
| sepCS->getName()); |
| } else { |
| writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); |
| } |
| |
| } |
| |
| // get rid of the array and index |
| if (mode == psModeForm || inType3Char || preloadImagesForms) { |
| if (!inlineImg) writePS("pop "); |
| writePS("pop pop\n"); |
| |
| // image data |
| } else { |
| |
| // cut off inline image streams at appropriate length |
| if (inlineImg) { |
| str = new FixedLengthEncoder(str, len); |
| } else if (useCompressed) { |
| str = str->getUndecodedStream(); |
| } |
| |
| // add FlateEncode/LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters |
| #ifdef ENABLE_ZLIB |
| if (useFlate) { |
| str = new FlateEncoder(str); |
| } else |
| #endif |
| if (useLZW) { |
| str = new LZWEncoder(str); |
| } else if (useRLE) { |
| str = new RunLengthEncoder(str); |
| } |
| if (useASCII) { |
| if (useASCIIHex) { |
| str = new ASCIIHexEncoder(str); |
| } else { |
| str = new ASCII85Encoder(str); |
| } |
| } |
| |
| // copy the stream data |
| str->reset(); |
| while ((c = str->getChar()) != EOF) { |
| writePSChar(c); |
| } |
| str->close(); |
| |
| // add newline and trailer to the end |
| writePSChar('\n'); |
| writePS("%-EOD-\n"); |
| |
| // delete encoders |
| if (useFlate || useLZW || useRLE || useASCII || inlineImg) { |
| delete str; |
| } |
| } |
| |
| // close the mask stream |
| if (maskStr) { |
| if (!(mode == psModeForm || inType3Char || preloadImagesForms)) { |
| writePS("pdfMaskEnd\n"); |
| } |
| } |
| } |
| |
| void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace, |
| bool genXform, bool updateColors, |
| bool map01) { |
| GfxCalGrayColorSpace *calGrayCS; |
| GfxCalRGBColorSpace *calRGBCS; |
| GfxLabColorSpace *labCS; |
| GfxIndexedColorSpace *indexedCS; |
| GfxSeparationColorSpace *separationCS; |
| GfxDeviceNColorSpace *deviceNCS; |
| GfxColorSpace *baseCS; |
| unsigned char *lookup, *p; |
| double x[gfxColorMaxComps], y[gfxColorMaxComps]; |
| double low[gfxColorMaxComps], range[gfxColorMaxComps]; |
| GfxColor color; |
| GfxCMYK cmyk; |
| int n, numComps, numAltComps; |
| int byte; |
| int i, j, k; |
| |
| switch (colorSpace->getMode()) { |
| |
| case csDeviceGray: |
| writePS("/DeviceGray"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| if (updateColors) { |
| processColors |= psProcessBlack; |
| } |
| break; |
| |
| case csCalGray: |
| calGrayCS = (GfxCalGrayColorSpace *)colorSpace; |
| writePS("[/CIEBasedA <<\n"); |
| writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", calGrayCS->getGamma()); |
| writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n", |
| calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), |
| calGrayCS->getWhiteZ()); |
| writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", |
| calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), |
| calGrayCS->getWhiteZ()); |
| writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", |
| calGrayCS->getBlackX(), calGrayCS->getBlackY(), |
| calGrayCS->getBlackZ()); |
| writePS(">>]"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| if (updateColors) { |
| processColors |= psProcessBlack; |
| } |
| break; |
| |
| case csDeviceRGB: |
| writePS("/DeviceRGB"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| if (updateColors) { |
| processColors |= psProcessCMYK; |
| } |
| break; |
| |
| case csCalRGB: |
| calRGBCS = (GfxCalRGBColorSpace *)colorSpace; |
| writePS("[/CIEBasedABC <<\n"); |
| writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n", |
| calRGBCS->getGammaR(), calRGBCS->getGammaG(), |
| calRGBCS->getGammaB()); |
| writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n", |
| calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1], |
| calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3], |
| calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5], |
| calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7], |
| calRGBCS->getMatrix()[8]); |
| writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", |
| calRGBCS->getWhiteX(), calRGBCS->getWhiteY(), |
| calRGBCS->getWhiteZ()); |
| writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", |
| calRGBCS->getBlackX(), calRGBCS->getBlackY(), |
| calRGBCS->getBlackZ()); |
| writePS(">>]"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| if (updateColors) { |
| processColors |= psProcessCMYK; |
| } |
| break; |
| |
| case csDeviceCMYK: |
| writePS("/DeviceCMYK"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| if (updateColors) { |
| processColors |= psProcessCMYK; |
| } |
| break; |
| |
| case csLab: |
| labCS = (GfxLabColorSpace *)colorSpace; |
| writePS("[/CIEBasedABC <<\n"); |
| if (map01) { |
| writePS(" /RangeABC [0 1 0 1 0 1]\n"); |
| writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n", |
| (labCS->getAMax() - labCS->getAMin()) / 500.0, |
| labCS->getAMin() / 500.0, |
| (labCS->getBMax() - labCS->getBMin()) / 200.0, |
| labCS->getBMin() / 200.0); |
| } else { |
| writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n", |
| labCS->getAMin(), labCS->getAMax(), |
| labCS->getBMin(), labCS->getBMax()); |
| writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n"); |
| } |
| writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n"); |
| writePS(" /DecodeLMN\n"); |
| writePS(" [{dup 6 29 div ge {dup dup mul mul}\n"); |
| writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", |
| labCS->getWhiteX()); |
| writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); |
| writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", |
| labCS->getWhiteY()); |
| writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); |
| writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n", |
| labCS->getWhiteZ()); |
| writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", |
| labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ()); |
| writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", |
| labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ()); |
| writePS(">>]"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| if (updateColors) { |
| processColors |= psProcessCMYK; |
| } |
| break; |
| |
| case csICCBased: |
| // there is no transform function to the alternate color space, so |
| // we can use it directly |
| dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt(), |
| genXform, updateColors, false); |
| break; |
| |
| case csIndexed: |
| indexedCS = (GfxIndexedColorSpace *)colorSpace; |
| baseCS = indexedCS->getBase(); |
| writePS("[/Indexed "); |
| dumpColorSpaceL2(baseCS, false, false, true); |
| n = indexedCS->getIndexHigh(); |
| numComps = baseCS->getNComps(); |
| lookup = indexedCS->getLookup(); |
| writePSFmt(" {0:d} <\n", n); |
| if (baseCS->getMode() == csDeviceN && level != psLevel3 && level != psLevel3Sep) { |
| const Function *func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); |
| baseCS->getDefaultRanges(low, range, indexedCS->getIndexHigh()); |
| if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) { |
| labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt(); |
| } else { |
| labCS = nullptr; |
| } |
| numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps(); |
| p = lookup; |
| for (i = 0; i <= n; i += 8) { |
| writePS(" "); |
| for (j = i; j < i+8 && j <= n; ++j) { |
| for (k = 0; k < numComps; ++k) { |
| x[k] = low[k] + (*p++ / 255.0) * range[k]; |
| } |
| func->transform(x, y); |
| if (labCS) { |
| y[0] /= 100.0; |
| y[1] = (y[1] - labCS->getAMin()) / |
| (labCS->getAMax() - labCS->getAMin()); |
| y[2] = (y[2] - labCS->getBMin()) / |
| (labCS->getBMax() - labCS->getBMin()); |
| } |
| for (k = 0; k < numAltComps; ++k) { |
| byte = (int)(y[k] * 255 + 0.5); |
| if (byte < 0) { |
| byte = 0; |
| } else if (byte > 255) { |
| byte = 255; |
| } |
| writePSFmt("{0:02x}", byte); |
| } |
| if (updateColors) { |
| color.c[0] = dblToCol(j); |
| indexedCS->getCMYK(&color, &cmyk); |
| addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), |
| colToDbl(cmyk.y), colToDbl(cmyk.k)); |
| } |
| } |
| writePS("\n"); |
| } |
| } else { |
| for (i = 0; i <= n; i += 8) { |
| writePS(" "); |
| for (j = i; j < i+8 && j <= n; ++j) { |
| for (k = 0; k < numComps; ++k) { |
| writePSFmt("{0:02x}", lookup[j * numComps + k]); |
| } |
| if (updateColors) { |
| color.c[0] = dblToCol(j); |
| indexedCS->getCMYK(&color, &cmyk); |
| addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), |
| colToDbl(cmyk.y), colToDbl(cmyk.k)); |
| } |
| } |
| writePS("\n"); |
| } |
| } |
| writePS(">]"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| break; |
| |
| case csSeparation: |
| separationCS = (GfxSeparationColorSpace *)colorSpace; |
| writePS("[/Separation "); |
| writePSString(separationCS->getName()->toStr()); |
| writePS(" "); |
| dumpColorSpaceL2(separationCS->getAlt(), false, false, false); |
| writePS("\n"); |
| cvtFunction(separationCS->getFunc()); |
| writePS("]"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| if (updateColors) { |
| addCustomColor(separationCS); |
| } |
| break; |
| |
| case csDeviceN: |
| deviceNCS = (GfxDeviceNColorSpace *)colorSpace; |
| if (level == psLevel3 || level == psLevel3Sep) { |
| writePS("[/DeviceN\n"); |
| writePS(" [ "); |
| for (i = 0; i < deviceNCS->getNComps(); i++) { |
| writePSString(deviceNCS->getColorantName(i)); |
| writePS(" "); |
| } |
| writePS("]\n"); |
| dumpColorSpaceL2(deviceNCS->getAlt(), false, updateColors, false); |
| writePS("\n"); |
| cvtFunction(deviceNCS->getTintTransformFunc(), map01 && deviceNCS->getAlt()->getMode() == csLab); |
| writePS("]\n"); |
| if (genXform) { |
| writePS(" {}"); |
| } |
| } else { |
| // DeviceN color spaces are a Level 3 PostScript feature. |
| dumpColorSpaceL2(deviceNCS->getAlt(), false, updateColors, map01); |
| if (genXform) { |
| writePS(" "); |
| cvtFunction(deviceNCS->getTintTransformFunc()); |
| } |
| } |
| break; |
| |
| case csPattern: |
| //~ unimplemented |
| break; |
| } |
| } |
| |
| #ifdef OPI_SUPPORT |
| void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) { |
| if (generateOPI) { |
| Object dict = opiDict->lookup("2.0"); |
| if (dict.isDict()) { |
| opiBegin20(state, dict.getDict()); |
| } else { |
| dict = opiDict->lookup("1.3"); |
| if (dict.isDict()) { |
| opiBegin13(state, dict.getDict()); |
| } |
| } |
| } |
| } |
| |
| void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { |
| double width, height, left, right, top, bottom; |
| int w, h; |
| |
| writePS("%%BeginOPI: 2.0\n"); |
| writePS("%%Distilled\n"); |
| |
| Object obj1 = dict->lookup("F"); |
| Object obj2 = getFileSpecName(&obj1); |
| if (obj2.isString()) { |
| writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString()); |
| } |
| |
| obj1 = dict->lookup("MainImage"); |
| if (obj1.isString()) { |
| writePSFmt("%%MainImage: {0:t}\n", obj1.getString()); |
| } |
| |
| //~ ignoring 'Tags' entry |
| //~ need to use writePSString() and deal with >255-char lines |
| |
| obj1 = dict->lookup("Size"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 2) { |
| obj2 = obj1.arrayGet(0); |
| width = obj2.getNum(); |
| obj2 = obj1.arrayGet(1); |
| height = obj2.getNum(); |
| writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width, height); |
| } |
| |
| obj1 = dict->lookup("CropRect"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 4) { |
| obj2 = obj1.arrayGet(0); |
| left = obj2.getNum(); |
| obj2 = obj1.arrayGet(1); |
| top = obj2.getNum(); |
| obj2 = obj1.arrayGet(2); |
| right = obj2.getNum(); |
| obj2 = obj1.arrayGet(3); |
| bottom = obj2.getNum(); |
| writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", |
| left, top, right, bottom); |
| } |
| |
| obj1 = dict->lookup("Overprint"); |
| if (obj1.isBool()) { |
| writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); |
| } |
| |
| obj1 = dict->lookup("Inks"); |
| if (obj1.isName()) { |
| writePSFmt("%%ImageInks: {0:s}\n", obj1.getName()); |
| } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) { |
| obj2 = obj1.arrayGet(0); |
| if (obj2.isName()) { |
| writePSFmt("%%ImageInks: {0:s} {1:d}", |
| obj2.getName(), (obj1.arrayGetLength() - 1) / 2); |
| for (int i = 1; i+1 < obj1.arrayGetLength(); i += 2) { |
| Object obj3 = obj1.arrayGet(i); |
| Object obj4 = obj1.arrayGet(i+1); |
| if (obj3.isString() && obj4.isNum()) { |
| writePS(" "); |
| writePSString(obj3.getString()->toStr()); |
| writePSFmt(" {0:.6g}", obj4.getNum()); |
| } |
| } |
| writePS("\n"); |
| } |
| } |
| |
| writePS("gsave\n"); |
| |
| writePS("%%BeginIncludedImage\n"); |
| |
| obj1 = dict->lookup("IncludedImageDimensions"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 2) { |
| obj2 = obj1.arrayGet(0); |
| w = obj2.getInt(); |
| obj2 = obj1.arrayGet(1); |
| h = obj2.getInt(); |
| writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h); |
| } |
| |
| obj1 = dict->lookup("IncludedImageQuality"); |
| if (obj1.isNum()) { |
| writePSFmt("%%IncludedImageQuality: {0:.6g}\n", obj1.getNum()); |
| } |
| |
| ++opi20Nest; |
| } |
| |
| void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { |
| int left, right, top, bottom, samples, bits, width, height; |
| double c, m, y, k; |
| double llx, lly, ulx, uly, urx, ury, lrx, lry; |
| double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry; |
| double horiz, vert; |
| int i, j; |
| |
| writePS("save\n"); |
| writePS("/opiMatrix2 matrix currentmatrix def\n"); |
| writePS("opiMatrix setmatrix\n"); |
| |
| Object obj1 = dict->lookup("F"); |
| Object obj2 = getFileSpecName(&obj1); |
| if (obj2.isString()) { |
| writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString()); |
| } |
| |
| obj1 = dict->lookup("CropRect"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 4) { |
| obj2 = obj1.arrayGet(0); |
| left = obj2.getInt(); |
| obj2 = obj1.arrayGet(1); |
| top = obj2.getInt(); |
| obj2 = obj1.arrayGet(2); |
| right = obj2.getInt(); |
| obj2 = obj1.arrayGet(3); |
| bottom = obj2.getInt(); |
| writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n", |
| left, top, right, bottom); |
| } |
| |
| obj1 = dict->lookup("Color"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 5) { |
| obj2 = obj1.arrayGet(0); |
| c = obj2.getNum(); |
| obj2 = obj1.arrayGet(1); |
| m = obj2.getNum(); |
| obj2 = obj1.arrayGet(2); |
| y = obj2.getNum(); |
| obj2 = obj1.arrayGet(3); |
| k = obj2.getNum(); |
| obj2 = obj1.arrayGet(4); |
| if (obj2.isString()) { |
| writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", |
| c, m, y, k); |
| writePSString(obj2.getString()->toStr()); |
| writePS("\n"); |
| } |
| } |
| |
| obj1 = dict->lookup("ColorType"); |
| if (obj1.isName()) { |
| writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName()); |
| } |
| |
| //~ ignores 'Comments' entry |
| //~ need to handle multiple lines |
| |
| obj1 = dict->lookup("CropFixed"); |
| if (obj1.isArray()) { |
| obj2 = obj1.arrayGet(0); |
| ulx = obj2.getNum(); |
| obj2 = obj1.arrayGet(1); |
| uly = obj2.getNum(); |
| obj2 = obj1.arrayGet(2); |
| lrx = obj2.getNum(); |
| obj2 = obj1.arrayGet(3); |
| lry = obj2.getNum(); |
| writePSFmt("%ALDImageCropFixed: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", |
| ulx, uly, lrx, lry); |
| } |
| |
| obj1 = dict->lookup("GrayMap"); |
| if (obj1.isArray()) { |
| writePS("%ALDImageGrayMap:"); |
| for (i = 0; i < obj1.arrayGetLength(); i += 16) { |
| if (i > 0) { |
| writePS("\n%%+"); |
| } |
| for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) { |
| obj2 = obj1.arrayGet(i+j); |
| writePSFmt(" {0:d}", obj2.getInt()); |
| } |
| } |
| writePS("\n"); |
| } |
| |
| obj1 = dict->lookup("ID"); |
| if (obj1.isString()) { |
| writePSFmt("%ALDImageID: {0:t}\n", obj1.getString()); |
| } |
| |
| obj1 = dict->lookup("ImageType"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 2) { |
| obj2 = obj1.arrayGet(0); |
| samples = obj2.getInt(); |
| obj2 = obj1.arrayGet(1); |
| bits = obj2.getInt(); |
| writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits); |
| } |
| |
| dict->lookup("Overprint"); |
| if (obj1.isBool()) { |
| writePSFmt("%ALDImageOverprint: {0:s}\n", |
| obj1.getBool() ? "true" : "false"); |
| } |
| |
| obj1 = dict->lookup("Position"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 8) { |
| obj2 = obj1.arrayGet(0); |
| llx = obj2.getNum(); |
| obj2 = obj1.arrayGet(1); |
| lly = obj2.getNum(); |
| obj2 = obj1.arrayGet(2); |
| ulx = obj2.getNum(); |
| obj2 = obj1.arrayGet(3); |
| uly = obj2.getNum(); |
| obj2 = obj1.arrayGet(4); |
| urx = obj2.getNum(); |
| obj2 = obj1.arrayGet(5); |
| ury = obj2.getNum(); |
| obj2 = obj1.arrayGet(6); |
| lrx = obj2.getNum(); |
| obj2 = obj1.arrayGet(7); |
| lry = obj2.getNum(); |
| opiTransform(state, llx, lly, &tllx, &tlly); |
| opiTransform(state, ulx, uly, &tulx, &tuly); |
| opiTransform(state, urx, ury, &turx, &tury); |
| opiTransform(state, lrx, lry, &tlrx, &tlry); |
| writePSFmt("%ALDImagePosition: {0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} {6:.6g} {7:.6g}\n", |
| tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); |
| } |
| |
| obj1 = dict->lookup("Resolution"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 2) { |
| obj2 = obj1.arrayGet(0); |
| horiz = obj2.getNum(); |
| obj2 = obj1.arrayGet(1); |
| vert = obj2.getNum(); |
| writePSFmt("%ALDImageResoution: {0:.6g} {1:.6g}\n", horiz, vert); |
| } |
| |
| obj1 = dict->lookup("Size"); |
| if (obj1.isArray() && obj1.arrayGetLength() == 2) { |
| obj2 = obj1.arrayGet(0); |
| width = obj2.getInt(); |
| obj2 = obj1.arrayGet(1); |
| height = obj2.getInt(); |
| writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height); |
| } |
| |
| //~ ignoring 'Tags' entry |
| //~ need to use writePSString() and deal with >255-char lines |
| |
| obj1 = dict->lookup("Tint"); |
| if (obj1.isNum()) { |
| writePSFmt("%ALDImageTint: {0:.6g}\n", obj1.getNum()); |
| } |
| |
| obj1 = dict->lookup("Transparency"); |
| if (obj1.isBool()) { |
| writePSFmt("%ALDImageTransparency: {0:s}\n", |
| obj1.getBool() ? "true" : "false"); |
| } |
| |
| writePS("%%BeginObject: image\n"); |
| writePS("opiMatrix2 setmatrix\n"); |
| ++opi13Nest; |
| } |
| |
| // Convert PDF user space coordinates to PostScript default user space |
| // coordinates. This has to account for both the PDF CTM and the |
| // PSOutputDev page-fitting transform. |
| void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, |
| double *x1, double *y1) { |
| double t; |
| |
| state->transform(x0, y0, x1, y1); |
| *x1 += tx; |
| *y1 += ty; |
| if (rotate == 90) { |
| t = *x1; |
| *x1 = -*y1; |
| *y1 = t; |
| } else if (rotate == 180) { |
| *x1 = -*x1; |
| *y1 = -*y1; |
| } else if (rotate == 270) { |
| t = *x1; |
| *x1 = *y1; |
| *y1 = -t; |
| } |
| *x1 *= xScale; |
| *y1 *= yScale; |
| } |
| |
| void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) { |
| if (generateOPI) { |
| Object dict = opiDict->lookup("2.0"); |
| if (dict.isDict()) { |
| writePS("%%EndIncludedImage\n"); |
| writePS("%%EndOPI\n"); |
| writePS("grestore\n"); |
| --opi20Nest; |
| } else { |
| dict = opiDict->lookup("1.3"); |
| if (dict.isDict()) { |
| writePS("%%EndObject\n"); |
| writePS("restore\n"); |
| --opi13Nest; |
| } |
| } |
| } |
| } |
| #endif // OPI_SUPPORT |
| |
| void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { |
| writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx, wy); |
| writePS("q\n"); |
| t3NeedsRestore = true; |
| } |
| |
| void PSOutputDev::type3D1(GfxState *state, double wx, double wy, |
| double llx, double lly, double urx, double ury) { |
| t3WX = wx; |
| t3WY = wy; |
| t3LLX = llx; |
| t3LLY = lly; |
| t3URX = urx; |
| t3URY = ury; |
| t3String = new GooString(); |
| writePS("q\n"); |
| t3FillColorOnly = true; |
| t3Cacheable = true; |
| t3NeedsRestore = true; |
| } |
| |
| void PSOutputDev::drawForm(Ref ref) { |
| writePSFmt("f_{0:d}_{1:d}\n", ref.num, ref.gen); |
| } |
| |
| void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) { |
| Stream *str; |
| int c; |
| |
| if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) { |
| str = level1Stream; |
| } else { |
| str = psStream; |
| } |
| str->reset(); |
| while ((c = str->getChar()) != EOF) { |
| writePSChar(c); |
| } |
| str->close(); |
| } |
| |
| //~ can nextFunc be reset to 0 -- maybe at the start of each page? |
| //~ or maybe at the start of each color space / pattern? |
| void PSOutputDev::cvtFunction(const Function *func, bool invertPSFunction) { |
| const SampledFunction *func0; |
| const ExponentialFunction *func2; |
| const StitchingFunction *func3; |
| const PostScriptFunction *func4; |
| int thisFunc, m, n, nSamples, i, j, k; |
| |
| switch (func->getType()) { |
| |
| case -1: // identity |
| writePS("{}\n"); |
| break; |
| |
| case 0: // sampled |
| func0 = (const SampledFunction *)func; |
| thisFunc = nextFunc++; |
| m = func0->getInputSize(); |
| n = func0->getOutputSize(); |
| nSamples = n; |
| for (i = 0; i < m; ++i) { |
| nSamples *= func0->getSampleSize(i); |
| } |
| writePSFmt("/xpdfSamples{0:d} [\n", thisFunc); |
| for (i = 0; i < nSamples; ++i) { |
| writePSFmt("{0:.6g}\n", func0->getSamples()[i]); |
| } |
| writePS("] def\n"); |
| writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2); |
| // [e01] [efrac] x0 x1 ... xm-1 |
| for (i = m-1; i >= 0; --i) { |
| // [e01] [efrac] x0 x1 ... xi |
| writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n", |
| func0->getDomainMin(i), |
| (func0->getEncodeMax(i) - func0->getEncodeMin(i)) / |
| (func0->getDomainMax(i) - func0->getDomainMin(i)), |
| func0->getEncodeMin(i)); |
| // [e01] [efrac] x0 x1 ... xi-1 xi' |
| writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n", |
| func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1); |
| // [e01] [efrac] x0 x1 ... xi-1 xi' |
| writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n"); |
| // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi') |
| writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i); |
| // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') |
| writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1); |
| // [e01] [efrac] x0 x1 ... xi-1 floor(xi') |
| writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i); |
| // [e01] [efrac] x0 x1 ... xi-1 |
| } |
| // [e01] [efrac] |
| for (i = 0; i < n; ++i) { |
| // [e01] [efrac] y(0) ... y(i-1) |
| for (j = 0; j < (1<<m); ++j) { |
| // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1) |
| writePSFmt("xpdfSamples{0:d}\n", thisFunc); |
| k = m - 1; |
| writePSFmt("{0:d} index {1:d} get\n", i+j+2, 2 * k + ((j >> k) & 1)); |
| for (k = m - 2; k >= 0; --k) { |
| writePSFmt("{0:d} mul {1:d} index {2:d} get add\n", |
| func0->getSampleSize(k), |
| i + j + 3, |
| 2 * k + ((j >> k) & 1)); |
| } |
| if (n > 1) { |
| writePSFmt("{0:d} mul {1:d} add ", n, i); |
| } |
| writePS("get\n"); |
| } |
| // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1) |
| for (j = 0; j < m; ++j) { |
| // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1) |
| for (k = 0; k < (1 << (m - j)); k += 2) { |
| // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values> |
| writePSFmt("{0:d} index {1:d} get dup\n", |
| i + k/2 + (1 << (m-j)) - k, j); |
| writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n"); |
| writePSFmt("{0:d} 1 roll\n", k/2 + (1 << (m-j)) - k - 1); |
| } |
| // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1) |
| } |
| // [e01] [efrac] y(0) ... y(i-1) s |
| writePSFmt("{0:.6g} mul {1:.6g} add\n", |
| func0->getDecodeMax(i) - func0->getDecodeMin(i), |
| func0->getDecodeMin(i)); |
| writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", |
| func0->getRangeMin(i), func0->getRangeMin(i), |
| func0->getRangeMax(i), func0->getRangeMax(i)); |
| // [e01] [efrac] y(0) ... y(i-1) y(i) |
| } |
| // [e01] [efrac] y(0) ... y(n-1) |
| writePSFmt("{0:d} {1:d} roll pop pop \n", n+2, n); |
| if (invertPSFunction) { |
| for (i = 0; i < n; ++i) { |
| writePSFmt("{0:d} -1 roll ", n); |
| writePSFmt("{0:.6g} sub {1:.6g} div ", func0->getRangeMin(i), func0->getRangeMax(i) - func0->getRangeMin(i)); |
| } |
| } |
| writePS("}\n"); |
| break; |
| |
| case 2: // exponential |
| func2 = (const ExponentialFunction *)func; |
| n = func2->getOutputSize(); |
| writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", |
| func2->getDomainMin(0), func2->getDomainMin(0), |
| func2->getDomainMax(0), func2->getDomainMax(0)); |
| // x |
| for (i = 0; i < n; ++i) { |
| // x y(0) .. y(i-1) |
| writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n", |
| i, func2->getE(), func2->getC1()[i] - func2->getC0()[i], |
| func2->getC0()[i]); |
| if (func2->getHasRange()) { |
| writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", |
| func2->getRangeMin(i), func2->getRangeMin(i), |
| func2->getRangeMax(i), func2->getRangeMax(i)); |
| } |
| } |
| // x y(0) .. y(n-1) |
| writePSFmt("{0:d} {1:d} roll pop \n", n+1, n); |
| if (invertPSFunction && func2->getHasRange()) { |
| for (i = 0; i < n; ++i) { |
| writePSFmt("{0:d} -1 roll ", n); |
| writePSFmt("{0:.6g} sub {1:.6g} div ", func2->getRangeMin(i), func2->getRangeMax(i) - func2->getRangeMin(i)); |
| } |
| } |
| writePS("}\n"); |
| break; |
| |
| case 3: // stitching |
| func3 = (const StitchingFunction *)func; |
| thisFunc = nextFunc++; |
| for (i = 0; i < func3->getNumFuncs(); ++i) { |
| cvtFunction(func3->getFunc(i)); |
| writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i); |
| } |
| writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", |
| func3->getDomainMin(0), func3->getDomainMin(0), |
| func3->getDomainMax(0), func3->getDomainMax(0)); |
| for (i = 0; i < func3->getNumFuncs() - 1; ++i) { |
| writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n", |
| func3->getBounds()[i+1], |
| func3->getBounds()[i], |
| func3->getScale()[i], |
| func3->getEncode()[2*i], |
| thisFunc, i); |
| } |
| writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n", |
| func3->getBounds()[i], |
| func3->getScale()[i], |
| func3->getEncode()[2*i], |
| thisFunc, i); |
| for (i = 0; i < func3->getNumFuncs() - 1; ++i) { |
| writePS("} ifelse\n"); |
| } |
| if (invertPSFunction && func3->getHasRange()) { |
| n = func3->getOutputSize(); |
| for (i = 0; i < n; ++i) { |
| writePSFmt("{0:d} -1 roll ", n); |
| writePSFmt("{0:.6g} sub {1:.6g} div ", func3->getRangeMin(i), func3->getRangeMax(i) - func3->getRangeMin(i)); |
| } |
| } |
| writePS("}\n"); |
| break; |
| |
| case 4: // PostScript |
| func4 = (const PostScriptFunction *)func; |
| if (invertPSFunction) { |
| GooString *codeString = new GooString(func4->getCodeString()); |
| for (i = codeString->getLength() -1; i > 0; i--) { |
| if (codeString->getChar(i) == '}') { |
| codeString->del(i); |
| break; |
| } |
| } |
| writePS(codeString->c_str()); |
| writePS("\n"); |
| delete codeString; |
| n = func4->getOutputSize(); |
| for (i = 0; i < n; ++i) { |
| writePSFmt("{0:d} -1 roll ", n); |
| writePSFmt("{0:.6g} sub {1:.6g} div ", func4->getRangeMin(i), func4->getRangeMax(i) - func4->getRangeMin(i)); |
| } |
| writePS("}\n"); |
| } else { |
| writePS(func4->getCodeString()->c_str()); |
| writePS("\n"); |
| } |
| break; |
| } |
| } |
| |
| void PSOutputDev::writePSChar(char c) { |
| if (t3String) { |
| t3String->append(c); |
| } else { |
| (*outputFunc)(outputStream, &c, 1); |
| } |
| } |
| |
| void PSOutputDev::writePS(const char *s) { |
| if (t3String) { |
| t3String->append(s); |
| } else { |
| (*outputFunc)(outputStream, s, strlen(s)); |
| } |
| } |
| |
| void PSOutputDev::writePSBuf(const char *s, int len) { |
| if (t3String) { |
| for (int i = 0; i < len; i++) { |
| t3String->append(s[i]); |
| } |
| } else { |
| (*outputFunc)(outputStream, s, len); |
| } |
| } |
| |
| void PSOutputDev::writePSFmt(const char *fmt, ...) { |
| va_list args; |
| GooString *buf; |
| |
| va_start(args, fmt); |
| if (t3String) { |
| t3String->appendfv((char *)fmt, args); |
| } else { |
| buf = GooString::formatv((char *)fmt, args); |
| (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
| delete buf; |
| } |
| va_end(args); |
| } |
| |
| void PSOutputDev::writePSString(const std::string &s) { |
| unsigned char *p; |
| int n, line; |
| char buf[8]; |
| |
| writePSChar('('); |
| line = 1; |
| for (p = (unsigned char *)s.c_str(), n = s.size(); n; ++p, --n) { |
| if (line >= 64) { |
| writePSChar('\\'); |
| writePSChar('\n'); |
| line = 0; |
| } |
| if (*p == '(' || *p == ')' || *p == '\\') { |
| writePSChar('\\'); |
| writePSChar((char)*p); |
| line += 2; |
| } else if (*p < 0x20 || *p >= 0x80) { |
| sprintf(buf, "\\%03o", *p); |
| writePS(buf); |
| line += 4; |
| } else { |
| writePSChar((char)*p); |
| ++line; |
| } |
| } |
| writePSChar(')'); |
| } |
| |
| void PSOutputDev::writePSName(const char *s) { |
| const char *p; |
| char c; |
| |
| p = s; |
| while ((c = *p++)) { |
| if (c <= (char)0x20 || c >= (char)0x7f || |
| c == '(' || c == ')' || c == '<' || c == '>' || |
| c == '[' || c == ']' || c == '{' || c == '}' || |
| c == '/' || c == '%' || c == '\\') { |
| writePSFmt("#{0:02x}", c & 0xff); |
| } else { |
| writePSChar(c); |
| } |
| } |
| } |
| |
| GooString *PSOutputDev::filterPSName(const GooString *name) { |
| GooString *name2; |
| char buf[8]; |
| int i; |
| char c; |
| |
| name2 = new GooString(); |
| |
| // ghostscript chokes on names that begin with out-of-limits |
| // numbers, e.g., 1e4foo is handled correctly (as a name), but |
| // 1e999foo generates a limitcheck error |
| c = name->getChar(0); |
| if (c >= '0' && c <= '9') { |
| name2->append('f'); |
| } |
| |
| for (i = 0; i < name->getLength(); ++i) { |
| c = name->getChar(i); |
| if (c <= (char)0x20 || c >= (char)0x7f || |
| c == '(' || c == ')' || c == '<' || c == '>' || |
| c == '[' || c == ']' || c == '{' || c == '}' || |
| c == '/' || c == '%') { |
| sprintf(buf, "#%02x", c & 0xff); |
| name2->append(buf); |
| } else { |
| name2->append(c); |
| } |
| } |
| return name2; |
| } |
| |
| // Convert GooString to GooString, with appropriate escaping |
| // of things that can't appear in a label |
| // This is heavily based on the writePSTextLine() method |
| GooString* PSOutputDev::filterPSLabel(GooString *label, bool *needParens) { |
| int i, step; |
| bool isNumeric; |
| |
| // - DSC comments must be printable ASCII; control chars and |
| // backslashes have to be escaped (we do cheap UCS2-to-ASCII |
| // conversion by simply ignoring the high byte) |
| // - parentheses are escaped. this isn't strictly necessary for matched |
| // parentheses, but shouldn't be a problem |
| // - lines are limited to 255 chars (we limit to 200 here to allow |
| // for the keyword, which was emitted by the caller) |
| |
| GooString *label2 = new GooString(); |
| int labelLength = label->getLength(); |
| |
| if (labelLength == 0) { |
| isNumeric = false; |
| } else { |
| // this gets changed later if we find a non-numeric character |
| isNumeric = true; |
| } |
| |
| if ( (labelLength >= 2) && |
| ( (label->getChar(0) & 0xff) == 0xfe) && |
| ( (label->getChar(1) & 0xff) == 0xff) ) { |
| // UCS2 mode |
| i = 3; |
| step = 2; |
| if ( (label->getChar(labelLength-1) == 0) ) { |
| // prune the trailing null (0x000 for UCS2) |
| labelLength -= 2; |
| } |
| } else { |
| i = 0; |
| step = 1; |
| } |
| for (int j = 0; i < labelLength && j < 200; i += step) { |
| char c = label->getChar(i); |
| if ( (c < '0') || (c > '9') ) { |
| isNumeric = false; |
| } |
| if (c == '\\') { |
| label2->append("\\\\"); |
| j += 2; |
| } else if (c == ')') { |
| label2->append("\\)"); |
| } else if (c == '(') { |
| label2->append("\\("); |
| } else if (c < 0x20 || c > 0x7e) { |
| GooString *aux = GooString::format("\\{0:03o}", c); |
| label2->append(aux); |
| j += 4; |
| delete aux; |
| } else { |
| label2->append(c); |
| ++j; |
| } |
| } |
| if (needParens) { |
| *needParens = !(isNumeric); |
| } |
| return label2; |
| } |
| |
| // Write a DSC-compliant <textline>. |
| void PSOutputDev::writePSTextLine(const GooString *s) { |
| int i, j, step; |
| int c; |
| |
| // - DSC comments must be printable ASCII; control chars and |
| // backslashes have to be escaped (we do cheap Unicode-to-ASCII |
| // conversion by simply ignoring the high byte) |
| // - lines are limited to 255 chars (we limit to 200 here to allow |
| // for the keyword, which was emitted by the caller) |
| // - lines that start with a left paren are treated as <text> |
| // instead of <textline>, so we escape a leading paren |
| if (s->getLength() >= 2 && |
| (s->getChar(0) & 0xff) == 0xfe && |
| (s->getChar(1) & 0xff) == 0xff) { |
| i = 3; |
| step = 2; |
| } else { |
| i = 0; |
| step = 1; |
| } |
| for (j = 0; i < s->getLength() && j < 200; i += step) { |
| c = s->getChar(i) & 0xff; |
| if (c == '\\') { |
| writePS("\\\\"); |
| j += 2; |
| } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) { |
| writePSFmt("\\{0:03o}", c); |
| j += 4; |
| } else { |
| writePSChar(c); |
| ++j; |
| } |
| } |
| writePS("\n"); |
| } |