diff options
Diffstat (limited to 'xpdf/PSOutputDev.cc')
-rw-r--r-- | xpdf/PSOutputDev.cc | 6222 |
1 files changed, 6222 insertions, 0 deletions
diff --git a/xpdf/PSOutputDev.cc b/xpdf/PSOutputDev.cc new file mode 100644 index 0000000..8a1a9e9 --- /dev/null +++ b/xpdf/PSOutputDev.cc @@ -0,0 +1,6222 @@ +//======================================================================== +// +// PSOutputDev.cc +// +// Copyright 1996-2003 Glyph & Cog, LLC +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <stdio.h> +#include <stddef.h> +#include <stdarg.h> +#include <signal.h> +#include <math.h> +#include "GString.h" +#include "GList.h" +#include "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 "FoFiType1C.h" +#include "FoFiTrueType.h" +#include "Catalog.h" +#include "Page.h" +#include "Stream.h" +#include "Annot.h" +#include "XRef.h" +#include "PreScanOutputDev.h" +#if HAVE_SPLASH +# include "Splash.h" +# include "SplashBitmap.h" +# include "SplashOutputDev.h" +#endif +#include "PSOutputDev.h" + +#ifdef MACOS +// needed for setting type/creator of MacOS files +#include "ICSupport.h" +#endif + +// the MSVC math.h doesn't define this +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +//------------------------------------------------------------------------ + +// Resolution at which pages with transparency will be rasterized. +#define splashDPI 300 + +//------------------------------------------------------------------------ +// 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 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 {", + " 3 1 roll 2 array astore", + " /setpagedevice where {", + " pop 3 dict begin", + " /PageSize exch def", + " /ImagingBBox null def", + " /Policies 1 dict dup begin /PageSize 3 def end def", + " { /Duplex true def } if", + " currentdict end setpagedevice", + " } {", + " pop pop", + " } ifelse", + "} def", + "~1sn", + "/pdfOpNames [", + " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", + " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender", + " /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", + "~123sn", + " /pdfLastFill false def", + " /pdfLastStroke false def", + " /pdfTextMat [1 0 0 1 0 0] def", + " /pdfFontSize 0 def", + " /pdfCharSpacing 0 def", + " /pdfTextRender 0 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", + "~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", + "~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", + "} 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", + "/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", + "/cshow where {", + " pop", + " /cshow2 {", + " dup {", + " pop pop", + " 1 string dup 0 3 index put 3 index exec", + " } exch cshow", + " pop pop", + " } def", + "}{", + " /cshow2 {", + " currentfont /FontType get 0 eq {", + " 0 2 2 index length 1 sub {", + " 2 copy get exch 1 add 2 index exch get", + " 2 copy exch 256 mul add", + " 2 string dup 0 6 5 roll put dup 1 5 4 roll put", + " 3 index exec", + " } for", + " } {", + " dup {", + " 1 string dup 0 3 index put 3 index exec", + " } forall", + " } ifelse", + " pop pop", + " } def", + "} ifelse", + "/awcp {", // awidthcharpath + " exch {", + " false charpath", + " 5 index 5 index rmoveto", + " 6 index eq { 7 index 7 index rmoveto } if", + " } exch cshow2", + " 6 {pop} repeat", + "} def", + "/Tj {", + " fCol", // because stringwidth has to draw Type 3 chars + " 1 index stringwidth pdfTextMat idtransform pop", + " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse", + " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", + " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", + " pdfTextMat dtransform", + " 6 5 roll Tj1", + "} def", + "/Tj16 {", + " fCol", // because stringwidth has to draw Type 3 chars + " 2 index stringwidth pdfTextMat idtransform pop", + " sub exch div", + " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", + " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", + " pdfTextMat dtransform", + " 6 5 roll Tj1", + "} def", + "/Tj16V {", + " fCol", // because stringwidth has to draw Type 3 chars + " 2 index stringwidth pdfTextMat idtransform exch pop", + " sub exch div", + " 0 pdfWordSpacing pdfTextMat dtransform 32", + " 4 3 roll pdfCharSpacing add 0 exch", + " pdfTextMat dtransform", + " 6 5 roll Tj1", + "} def", + "/Tj1 {", + " 0 pdfTextRise pdfTextMat dtransform rmoveto", + " currentpoint 8 2 roll", + " pdfTextRender 1 and 0 eq {", + " 6 copy awidthshow", + " } if", + " pdfTextRender 3 and dup 1 eq exch 2 eq or {", + " 7 index 7 index moveto", + " 6 copy", + " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", + " false awcp currentpoint stroke moveto", + " } if", + " pdfTextRender 4 and 0 ne {", + " 8 6 roll moveto", + " false awcp", + " /pdfTextClipPath [ pdfTextClipPath aload pop", + " {/moveto cvx}", + " {/lineto cvx}", + " {/curveto cvx}", + " {/closepath cvx}", + " pathforall ] def", + " currentpoint newpath moveto", + " } {", + " 8 {pop} repeat", + " } ifelse", + " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", + "} def", + "/TJm { pdfFontSize 0.001 mul mul neg 0", + " pdfTextMat dtransform rmoveto } def", + "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch", + " pdfTextMat dtransform rmoveto } def", + "/Tclip { pdfTextClipPath cvx exec clip newpath", + " /pdfTextClipPath [] def } def", + "~1ns", + "% Level 1 image operators", + "~1n", + "/pdfIm1 {", + " /pdfImBuf1 4 index string def", + " { currentfile pdfImBuf1 readhexstring 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", + "~1ns", + "/pdfImM1 {", + " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", + " { currentfile pdfImBuf1 readhexstring pop } imagemask", + "} def", + "/pdfImM1a {", + " { 2 copy get exch 1 add exch } imagemask", + " pop pop", + "} def", + "~23sn", + "% Level 2 image operators", + "/pdfImBuf 100 string def", + "/pdfIm {", + " image", + " { currentfile pdfImBuf readline", + " not { pop exit } if", + " (%-EOD-) eq { exit } if } loop", + "} 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", + " { currentfile pdfImBuf readline", + " not { pop exit } if", + " (%-EOD-) eq { exit } if } loop", + "} def", + "~23sn", + "/pdfImM {", + " fCol imagemask", + " { currentfile pdfImBuf readline", + " not { pop exit } if", + " (%-EOD-) eq { exit } if } loop", + "} def", + "/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", + NULL +}; + +static 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", + NULL +}; + +//------------------------------------------------------------------------ +// Fonts +//------------------------------------------------------------------------ + +struct PSSubstFont { + char *psName; // PostScript name + double mWidth; // width of 'm' character +}; + +static char *psFonts[] = { + "Courier", + "Courier-Bold", + "Courier-Oblique", + "Courier-BoldOblique", + "Helvetica", + "Helvetica-Bold", + "Helvetica-Oblique", + "Helvetica-BoldOblique", + "Symbol", + "Times-Roman", + "Times-Bold", + "Times-Italic", + "Times-BoldItalic", + "ZapfDingbats", + NULL +}; + +static PSSubstFont psSubstFonts[] = { + {"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}, + {"Courier", 0.600}, + {"Courier-Oblique", 0.600}, + {"Courier-Bold", 0.600}, + {"Courier-BoldOblique", 0.600} +}; + +// Info for 8-bit fonts +struct PSFont8Info { + Ref fontID; + Gushort *codeToGID; // code-to-GID mapping for TrueType fonts +}; + +// Encoding info for substitute 16-bit font +struct PSFont16Enc { + Ref fontID; + GString *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, GString *nameA); + ~PSOutCustomColor(); + + double c, m, y, k; + GString *name; + PSOutCustomColor *next; +}; + +PSOutCustomColor::PSOutCustomColor(double cA, double mA, + double yA, double kA, GString *nameA) { + c = cA; + m = mA; + y = yA; + k = kA; + name = nameA; + next = NULL; +} + +PSOutCustomColor::~PSOutCustomColor() { + delete name; +} + +//------------------------------------------------------------------------ + +struct PSOutImgClipRect { + int x0, x1, y0, y1; +}; + +//------------------------------------------------------------------------ +// DeviceNRecoder +//------------------------------------------------------------------------ + +class DeviceNRecoder: public FilterStream { +public: + + DeviceNRecoder(Stream *strA, int widthA, int heightA, + GfxImageColorMap *colorMapA); + virtual ~DeviceNRecoder(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual int getChar() + { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } + virtual int lookChar() + { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } + virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; } + virtual GBool isBinary(GBool last = gTrue) { return gTrue; } + virtual GBool isEncoder() { return gTrue; } + +private: + + GBool fillBuf(); + + int width, height; + GfxImageColorMap *colorMap; + 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 = NULL; + pixelIdx = 0; + bufIdx = gfxColorMaxComps; + bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> + getAlt()->getNComps(); + func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> + getTintTransformFunc(); +} + +DeviceNRecoder::~DeviceNRecoder() { + if (imgStr) { + delete imgStr; + } +} + +void DeviceNRecoder::reset() { + imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), + colorMap->getBits()); + imgStr->reset(); +} + +GBool DeviceNRecoder::fillBuf() { + Guchar pixBuf[gfxColorMaxComps]; + GfxColor color; + double x[gfxColorMaxComps], y[gfxColorMaxComps]; + int i; + + if (pixelIdx >= width * height) { + return gFalse; + } + 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 gTrue; +} + +//------------------------------------------------------------------------ +// PSOutputDev +//------------------------------------------------------------------------ + +extern "C" { +typedef void (*SignalFunc)(int); +} + +static void outputToFile(void *stream, char *data, int len) { + fwrite(data, 1, len, (FILE *)stream); +} + +PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, + GBool manualCtrlA) { + FILE *f; + PSFileType fileTypeA; + + underlayCbk = NULL; + underlayCbkData = NULL; + overlayCbk = NULL; + overlayCbkData = NULL; + + fontIDs = NULL; + fontFileIDs = NULL; + fontFileNames = NULL; + font8Info = NULL; + font16Enc = NULL; + imgIDs = NULL; + formIDs = NULL; + xobjStack = NULL; + embFontList = NULL; + customColors = NULL; + haveTextClip = gFalse; + t3String = NULL; + + // 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(-1, "Couldn't run print command '%s'", fileName); + ok = gFalse; + return; + } +#else + error(-1, "Print commands are not supported ('%s')", fileName); + ok = gFalse; + return; +#endif + } else { + fileTypeA = psFile; + if (!(f = fopen(fileName, "w"))) { + error(-1, "Couldn't open PostScript file '%s'", fileName); + ok = gFalse; + return; + } + } + + init(outputToFile, f, fileTypeA, + xrefA, catalog, firstPage, lastPage, modeA, + imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); +} + +PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, + XRef *xrefA, Catalog *catalog, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, + GBool manualCtrlA) { + underlayCbk = NULL; + underlayCbkData = NULL; + overlayCbk = NULL; + overlayCbkData = NULL; + + fontIDs = NULL; + fontFileIDs = NULL; + fontFileNames = NULL; + font8Info = NULL; + font16Enc = NULL; + imgIDs = NULL; + formIDs = NULL; + xobjStack = NULL; + embFontList = NULL; + customColors = NULL; + haveTextClip = gFalse; + t3String = NULL; + + init(outputFuncA, outputStreamA, psGeneric, + xrefA, catalog, firstPage, lastPage, modeA, + imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); +} + +void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, + PSFileType fileTypeA, XRef *xrefA, Catalog *catalog, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, + GBool manualCtrlA) { + Page *page; + PDFRectangle *box; + + // initialize + ok = gTrue; + outputFunc = outputFuncA; + outputStream = outputStreamA; + fileType = fileTypeA; + xref = xrefA; + level = globalParams->getPSLevel(); + mode = modeA; + paperWidth = globalParams->getPSPaperWidth(); + paperHeight = globalParams->getPSPaperHeight(); + imgLLX = imgLLXA; + imgLLY = imgLLYA; + imgURX = imgURXA; + imgURY = imgURYA; + if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) { + globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY); + } + if (paperWidth < 0 || paperHeight < 0) { + // this check is needed in case the document has zero pages + if (firstPage > 0 && firstPage <= catalog->getNumPages()) { + page = catalog->getPage(firstPage); + paperWidth = (int)ceil(page->getMediaWidth()); + paperHeight = (int)ceil(page->getMediaHeight()); + } else { + paperWidth = 1; + paperHeight = 1; + } + imgLLX = imgLLY = 0; + imgURX = paperWidth; + imgURY = paperHeight; + } + preload = globalParams->getPSPreload(); + manualCtrl = manualCtrlA; + if (mode == psModeForm) { + lastPage = firstPage; + } + processColors = 0; + inType3Char = gFalse; + +#if 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 fontIDs, fontFileIDs, and fontFileNames lists + fontIDSize = 64; + fontIDLen = 0; + fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref)); + fontFileIDSize = 64; + fontFileIDLen = 0; + fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref)); + fontFileNameSize = 64; + fontFileNameLen = 0; + fontFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *)); + nextTrueTypeNum = 0; + font8InfoLen = 0; + font8InfoSize = 0; + font16EncLen = 0; + font16EncSize = 0; + imgIDLen = 0; + imgIDSize = 0; + formIDLen = 0; + formIDSize = 0; + + xobjStack = new GList(); + numSaves = 0; + numTilingPatterns = 0; + nextFunc = 0; + + // initialize embedded font resource comment list + embFontList = new GString(); + + if (!manualCtrl) { + // this check is needed in case the document has zero pages + if (firstPage > 0 && firstPage <= catalog->getNumPages()) { + writeHeader(firstPage, lastPage, + catalog->getPage(firstPage)->getMediaBox(), + catalog->getPage(firstPage)->getCropBox(), + catalog->getPage(firstPage)->getRotate()); + } else { + box = new PDFRectangle(0, 0, 1, 1); + writeHeader(firstPage, lastPage, box, box, 0); + delete box; + } + if (mode != psModeForm) { + writePS("%%BeginProlog\n"); + } + writeXpdfProcset(); + if (mode != psModeForm) { + writePS("%%EndProlog\n"); + writePS("%%BeginSetup\n"); + } + writeDocSetup(catalog, firstPage, lastPage); + if (mode != psModeForm) { + writePS("%%EndSetup\n"); + } + } + + // initialize sequential page number + seqPage = 1; +} + +PSOutputDev::~PSOutputDev() { + PSOutCustomColor *cc; + int i; + + if (ok) { + if (!manualCtrl) { + writePS("%%Trailer\n"); + writeTrailer(); + if (mode != psModeForm) { + writePS("%%EOF\n"); + } + } + if (fileType == psFile) { +#ifdef MACOS + ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle); +#endif + fclose((FILE *)outputStream); + } +#ifdef HAVE_POPEN + else if (fileType == psPipe) { + pclose((FILE *)outputStream); +#ifndef WIN32 + signal(SIGPIPE, (SignalFunc)SIG_DFL); +#endif + } +#endif + } + if (embFontList) { + delete embFontList; + } + if (fontIDs) { + gfree(fontIDs); + } + if (fontFileIDs) { + gfree(fontFileIDs); + } + if (fontFileNames) { + for (i = 0; i < fontFileNameLen; ++i) { + delete fontFileNames[i]; + } + gfree(fontFileNames); + } + if (font8Info) { + for (i = 0; i < font8InfoLen; ++i) { + gfree(font8Info[i].codeToGID); + } + gfree(font8Info); + } + if (font16Enc) { + for (i = 0; i < font16EncLen; ++i) { + delete font16Enc[i].enc; + } + gfree(font16Enc); + } + gfree(imgIDs); + gfree(formIDs); + if (xobjStack) { + delete xobjStack; + } + while (customColors) { + cc = customColors; + customColors = cc->next; + delete cc; + } +} + +void PSOutputDev::writeHeader(int firstPage, int lastPage, + PDFRectangle *mediaBox, PDFRectangle *cropBox, + int pageRotate) { + Object info, obj1; + 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 xpdf/pdftops {0:s}\n", xpdfVersion); + xref->getDocInfo(&info); + if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) { + writePS("%%Creator: "); + writePSTextLine(obj1.getString()); + } + obj1.free(); + if (info.isDict() && info.dictLookup("Title", &obj1)->isString()) { + writePS("%%Title: "); + writePSTextLine(obj1.getString()); + } + obj1.free(); + info.free(); + 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"); + + switch (mode) { + case psModePS: + writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n", + paperWidth, paperHeight); + writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight); + writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1); + writePS("%%EndComments\n"); + writePS("%%BeginDefaults\n"); + writePS("%%PageMedia: plain\n"); + 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)); + if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) || + floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) { + writePSFmt("%%HiResBoundingBox: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n", + x1, y1, x2, y2); + } + 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() { + GBool lev1, lev2, lev3, sep, nonSep; + char **p; + char *q; + + writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion); + writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright); + lev1 = lev2 = lev3 = sep = nonSep = gTrue; + for (p = prolog; *p; ++p) { + if ((*p)[0] == '~') { + lev1 = lev2 = lev3 = sep = nonSep = gFalse; + for (q = *p + 1; *q; ++q) { + switch (*q) { + case '1': lev1 = gTrue; break; + case '2': lev2 = gTrue; break; + case '3': lev3 = gTrue; break; + case 's': sep = gTrue; break; + case 'n': nonSep = gTrue; break; + } + } + } else if ((level == psLevel1 && lev1 && nonSep) || + (level == psLevel1Sep && lev1 && sep) || + (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, + int firstPage, int lastPage) { + Page *page; + Dict *resDict; + Annots *annots; + Object obj1, obj2; + int pg, i; + + if (mode == psModeForm) { + // swap the form and xpdf dicts + writePS("xpdf end begin dup begin\n"); + } else { + writePS("xpdf begin\n"); + } + for (pg = firstPage; pg <= lastPage; ++pg) { + page = catalog->getPage(pg); + if ((resDict = page->getResourceDict())) { + setupResources(resDict); + } + annots = new Annots(xref, catalog, page->getAnnots(&obj1)); + obj1.free(); + for (i = 0; i < annots->getNumAnnots(); ++i) { + if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) { + obj1.streamGetDict()->lookup("Resources", &obj2); + if (obj2.isDict()) { + setupResources(obj2.getDict()); + } + obj2.free(); + } + obj1.free(); + } + delete annots; + } + if (mode != psModeForm) { + if (mode != psModeEPS && !manualCtrl) { + writePSFmt("{0:d} {1:d} {2:s} pdfSetup\n", + paperWidth, paperHeight, + globalParams->getPSDuplex() ? "true" : "false"); + } +#if OPI_SUPPORT + if (globalParams->getPSOPI()) { + writePS("/opiMatrix matrix currentmatrix def\n"); + } +#endif + } +} + +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->getCString()); + 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) { + writePSFmt(" ({0:s})", cc->name->getCString()); + } + writePS("\n"); + writePS("%%CMYKCustomColor:\n"); + for (cc = customColors; cc; cc = cc->next) { + writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t})\n", + cc->c, cc->m, cc->y, cc->k, cc->name); + } + } + } +} + +void PSOutputDev::setupResources(Dict *resDict) { + Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj; + Ref ref0, ref1; + GBool skip; + int i, j; + + setupFonts(resDict); + setupImages(resDict); + setupForms(resDict); + + //----- recursively scan XObjects + resDict->lookup("XObject", &xObjDict); + if (xObjDict.isDict()) { + for (i = 0; i < xObjDict.dictGetLength(); ++i) { + + // avoid infinite recursion on XObjects + skip = gFalse; + if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) { + ref0 = xObjRef.getRef(); + for (j = 0; j < xobjStack->getLength(); ++j) { + ref1 = *(Ref *)xobjStack->get(j); + if (ref1.num == ref0.num && ref1.gen == ref0.gen) { + skip = gTrue; + break; + } + } + if (!skip) { + xobjStack->append(&ref0); + } + } + if (!skip) { + + // process the XObject's resource dictionary + xObjDict.dictGetVal(i, &xObj); + if (xObj.isStream()) { + xObj.streamGetDict()->lookup("Resources", &resObj); + if (resObj.isDict()) { + setupResources(resObj.getDict()); + } + resObj.free(); + } + xObj.free(); + } + + if (xObjRef.isRef() && !skip) { + xobjStack->del(xobjStack->getLength() - 1); + } + xObjRef.free(); + } + } + xObjDict.free(); + + //----- recursively scan Patterns + resDict->lookup("Pattern", &patDict); + if (patDict.isDict()) { + inType3Char = gTrue; + for (i = 0; i < patDict.dictGetLength(); ++i) { + + // avoid infinite recursion on Patterns + skip = gFalse; + if ((patDict.dictGetValNF(i, &patRef)->isRef())) { + ref0 = patRef.getRef(); + for (j = 0; j < xobjStack->getLength(); ++j) { + ref1 = *(Ref *)xobjStack->get(j); + if (ref1.num == ref0.num && ref1.gen == ref0.gen) { + skip = gTrue; + break; + } + } + if (!skip) { + xobjStack->append(&ref0); + } + } + if (!skip) { + + // process the Pattern's resource dictionary + patDict.dictGetVal(i, &pat); + if (pat.isStream()) { + pat.streamGetDict()->lookup("Resources", &resObj); + if (resObj.isDict()) { + setupResources(resObj.getDict()); + } + resObj.free(); + } + pat.free(); + } + + if (patRef.isRef() && !skip) { + xobjStack->del(xobjStack->getLength() - 1); + } + patRef.free(); + } + inType3Char = gFalse; + } + patDict.free(); +} + +void PSOutputDev::setupFonts(Dict *resDict) { + Object obj1, obj2; + Ref r; + GfxFontDict *gfxFontDict; + GfxFont *font; + int i; + + gfxFontDict = NULL; + resDict->lookupNF("Font", &obj1); + if (obj1.isRef()) { + obj1.fetch(xref, &obj2); + if (obj2.isDict()) { + r = obj1.getRef(); + gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict()); + } + obj2.free(); + } else if (obj1.isDict()) { + gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict()); + } + if (gfxFontDict) { + for (i = 0; i < gfxFontDict->getNumFonts(); ++i) { + if ((font = gfxFontDict->getFont(i))) { + setupFont(font, resDict); + } + } + delete gfxFontDict; + } + obj1.free(); +} + +void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { + Ref fontFileID; + GString *name; + PSFontParam *fontParam; + GString *psName; + char buf[16]; + GBool subst; + UnicodeMap *uMap; + char *charName; + double xs, ys; + int code; + double w1, w2; + double *fm; + int i, j; + + // check if font is already set up + for (i = 0; i < fontIDLen; ++i) { + if (fontIDs[i].num == font->getID()->num && + fontIDs[i].gen == font->getID()->gen) { + return; + } + } + + // add entry to fontIDs list + if (fontIDLen >= fontIDSize) { + fontIDSize += 64; + fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref)); + } + fontIDs[fontIDLen++] = *font->getID(); + + xs = ys = 1; + subst = gFalse; + + // check for resident 8-bit font + if (font->getName() && + (fontParam = globalParams->getPSFont(font->getName()))) { + psName = new GString(fontParam->psFontName->getCString()); + + // check for embedded Type 1 font + } else if (globalParams->getPSEmbedType1() && + font->getType() == fontType1 && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); + setupEmbeddedType1Font(&fontFileID, psName); + + // check for embedded Type 1C font + } else if (globalParams->getPSEmbedType1() && + font->getType() == fontType1C && + font->getEmbeddedFontID(&fontFileID)) { + // use the PDF font name because the embedded font name might + // not include the subset prefix + psName = filterPSName(font->getOrigName()); + setupEmbeddedType1CFont(font, &fontFileID, psName); + + // check for embedded OpenType - Type 1C font + } else if (globalParams->getPSEmbedType1() && + font->getType() == fontType1COT && + font->getEmbeddedFontID(&fontFileID)) { + // use the PDF font name because the embedded font name might + // not include the subset prefix + psName = filterPSName(font->getOrigName()); + setupEmbeddedOpenTypeT1CFont(font, &fontFileID, psName); + + // check for external Type 1 font file + } else if (globalParams->getPSEmbedType1() && + font->getType() == fontType1 && + font->getExtFontFile()) { + // this assumes that the PS font name matches the PDF font name + psName = font->getName()->copy(); + setupExternalType1Font(font->getExtFontFile(), psName); + + // check for embedded TrueType font + } else if (globalParams->getPSEmbedTrueType() && + (font->getType() == fontTrueType || + font->getType() == fontTrueTypeOT) && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); + setupEmbeddedTrueTypeFont(font, &fontFileID, psName); + + // check for external TrueType font file + } else if (globalParams->getPSEmbedTrueType() && + font->getType() == fontTrueType && + font->getExtFontFile()) { + psName = filterPSName(font->getName()); + setupExternalTrueTypeFont(font, psName); + + // check for embedded CID PostScript font + } else if (globalParams->getPSEmbedCIDPostScript() && + font->getType() == fontCIDType0C && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); + setupEmbeddedCIDType0Font(font, &fontFileID, psName); + + // check for embedded CID TrueType font + } else if (globalParams->getPSEmbedCIDTrueType() && + (font->getType() == fontCIDType2 || + font->getType() == fontCIDType2OT) && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); + //~ should check to see if font actually uses vertical mode + setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue); + + // check for embedded OpenType - CID CFF font + } else if (globalParams->getPSEmbedCIDPostScript() && + font->getType() == fontCIDType0COT && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); + setupEmbeddedOpenTypeCFFFont(font, &fontFileID, psName); + + // check for Type 3 font + } else if (font->getType() == fontType3) { + psName = GString::format("T3_{0:d}_{1:d}", + font->getID()->num, font->getID()->gen); + setupType3Font(font, psName, parentResDict); + + // do 8-bit font substitution + } else if (!font->isCIDFont()) { + subst = gTrue; + name = font->getName(); + psName = NULL; + if (name) { + for (i = 0; psFonts[i]; ++i) { + if (name->cmp(psFonts[i]) == 0) { + psName = new GString(psFonts[i]); + break; + } + } + } + if (!psName) { + if (font->isFixedWidth()) { + i = 8; + } else if (font->isSerif()) { + i = 4; + } else { + i = 0; + } + if (font->isBold()) { + i += 2; + } + if (font->isItalic()) { + i += 1; + } + psName = new GString(psSubstFonts[i].psName); + 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 = psSubstFonts[i].mWidth; + xs = w1 / w2; + if (xs < 0.1) { + xs = 1; + } + if (font->getType() == fontType3) { + // This is a hack which makes it possible to substitute for some + // Type 3 fonts. The problem is that it's impossible to know what + // the base coordinate system used in the font is without actually + // rendering the font. + ys = xs; + fm = font->getFontMatrix(); + if (fm[0] != 0) { + ys *= fm[3] / fm[0]; + } + } else { + ys = 1; + } + } + + // do 16-bit font substitution + } else if ((fontParam = globalParams-> + getPSFont16(font->getName(), + ((GfxCIDFont *)font)->getCollection(), + font->getWMode()))) { + subst = gTrue; + psName = fontParam->psFontName->copy(); + if (font16EncLen >= font16EncSize) { + font16EncSize += 16; + font16Enc = (PSFont16Enc *)greallocn(font16Enc, + font16EncSize, sizeof(PSFont16Enc)); + } + font16Enc[font16EncLen].fontID = *font->getID(); + font16Enc[font16EncLen].enc = fontParam->encoding->copy(); + if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) { + uMap->decRefCnt(); + ++font16EncLen; + } else { + error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'", + font16Enc[font16EncLen].enc->getCString()); + } + + // give up - can't do anything with this font + } else { + error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)", + font->getName() ? font->getName()->getCString() : "(unnamed)", + ((GfxCIDFont *)font)->getCollection() + ? ((GfxCIDFont *)font)->getCollection()->getCString() + : "(unknown)"); + return; + } + + // 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:.4g} {4:.4g}\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); + // this is a kludge for broken PDF files that encode char 32 + // as .notdef + if (i+j == 32 && charName && !strcmp(charName, ".notdef")) { + charName = "space"; + } + } + 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, GString *psName) { + static char hexChar[17] = "0123456789abcdef"; + Object refObj, strObj, obj1, obj2, obj3; + Dict *dict; + int length1, length2, length3; + int c; + int start[4]; + GBool binMode; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) + return; + } + + // add entry to fontFileIDs list + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + + // get the font stream and info + refObj.initRef(id->num, id->gen); + refObj.fetch(xref, &strObj); + refObj.free(); + if (!strObj.isStream()) { + error(-1, "Embedded font file object is not a stream"); + goto err1; + } + if (!(dict = strObj.streamGetDict())) { + error(-1, "Embedded font stream is missing its dictionary"); + goto err1; + } + dict->lookup("Length1", &obj1); + dict->lookup("Length2", &obj2); + dict->lookup("Length3", &obj3); + if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) { + error(-1, "Missing length fields in embedded font stream dictionary"); + obj1.free(); + obj2.free(); + obj3.free(); + goto err1; + } + length1 = obj1.getInt(); + length2 = obj2.getInt(); + length3 = obj3.getInt(); + obj1.free(); + obj2.free(); + obj3.free(); + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // copy ASCII portion of font + strObj.streamReset(); + for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) { + writePSChar(c); + } + + // figure out if encrypted portion is binary or ASCII + binMode = gFalse; + for (i = 0; i < 4; ++i) { + start[i] = strObj.streamGetChar(); + if (start[i] == EOF) { + error(-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 = gTrue; + } + + // convert binary data to ASCII + if (binMode) { + 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); + } + } + + // write padding and "cleartomark" + for (i = 0; i < 8; ++i) { + writePS("00000000000000000000000000000000" + "00000000000000000000000000000000\n"); + } + writePS("cleartomark\n"); + + // ending comment + writePS("%%EndResource\n"); + + err1: + strObj.streamClose(); + strObj.free(); +} + +//~ This doesn't handle .pfb files or binary eexec data (which only +//~ happens in pfb files?). +void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) { + FILE *fontFile; + int c; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileNameLen; ++i) { + if (!fontFileNames[i]->cmp(fileName)) { + return; + } + } + + // add entry to fontFileNames list + if (fontFileNameLen >= fontFileNameSize) { + fontFileNameSize += 64; + fontFileNames = (GString **)greallocn(fontFileNames, + fontFileNameSize, sizeof(GString *)); + } + fontFileNames[fontFileNameLen++] = fileName->copy(); + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // copy the font file + if (!(fontFile = fopen(fileName->getCString(), "rb"))) { + error(-1, "Couldn't open external font file"); + return; + } + while ((c = fgetc(fontFile)) != EOF) { + writePSChar(c); + } + fclose(fontFile); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id, + GString *psName) { + char *fontBuf; + int fontLen; + FoFiType1C *ffT1C; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) + return; + } + + // add entry to fontFileIDs list + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 1 font + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { + ffT1C->convertToType1(psName->getCString(), NULL, gTrue, + outputFunc, outputStream); + delete ffT1C; + } + gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, + GString *psName) { + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) + return; + } + + // add entry to fontFileIDs list + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 1 font + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { + if (ffTT->isOpenTypeCFF()) { + ffTT->convertToType1(psName->getCString(), NULL, gTrue, + outputFunc, outputStream); + } + delete ffTT; + } + gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, + GString *psName) { + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + Gushort *codeToGID; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) { + psName->appendf("_{0:d}", nextTrueTypeNum++); + break; + } + } + + // add entry to fontFileIDs list + if (i == fontFileIDLen) { + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + } + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 42 font + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { + codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); + ffTT->convertToType42(psName->getCString(), + ((Gfx8BitFont *)font)->getHasEncoding() + ? ((Gfx8BitFont *)font)->getEncoding() + : (char **)NULL, + 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, GString *psName) { + GString *fileName; + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + Gushort *codeToGID; + int i; + + // check if font is already embedded + fileName = font->getExtFontFile(); + for (i = 0; i < fontFileNameLen; ++i) { + if (!fontFileNames[i]->cmp(fileName)) { + psName->appendf("_{0:d}", nextTrueTypeNum++); + break; + } + } + + // add entry to fontFileNames list + if (i == fontFileNameLen) { + if (fontFileNameLen >= fontFileNameSize) { + fontFileNameSize += 64; + fontFileNames = + (GString **)greallocn(fontFileNames, + fontFileNameSize, sizeof(GString *)); + } + fontFileNames[fontFileNameLen++] = fileName->copy(); + } + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 42 font + fontBuf = font->readExtFontFile(&fontLen); + if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { + codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); + ffTT->convertToType42(psName->getCString(), + ((Gfx8BitFont *)font)->getHasEncoding() + ? ((Gfx8BitFont *)font)->getEncoding() + : (char **)NULL, + 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::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, + GString *psName) { + char *fontBuf; + int fontLen; + FoFiType1C *ffT1C; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) + return; + } + + // add entry to fontFileIDs list + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 0 font + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { + if (globalParams->getPSLevel() >= psLevel3) { + // Level 3: use a CID font + ffT1C->convertToCIDType0(psName->getCString(), outputFunc, outputStream); + } else { + // otherwise: use a non-CID composite font + ffT1C->convertToType0(psName->getCString(), outputFunc, outputStream); + } + delete ffT1C; + } + gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, + GString *psName, + GBool needVerticalMetrics) { + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) { + psName->appendf("_{0:d}", nextTrueTypeNum++); + break; + } + } + + // add entry to fontFileIDs list + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 0 font + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { + if (globalParams->getPSLevel() >= psLevel3) { + // Level 3: use a CID font + ffTT->convertToCIDType2(psName->getCString(), + ((GfxCIDFont *)font)->getCIDToGID(), + ((GfxCIDFont *)font)->getCIDToGIDLen(), + needVerticalMetrics, + outputFunc, outputStream); + } else { + // otherwise: use a non-CID composite font + ffTT->convertToType0(psName->getCString(), + ((GfxCIDFont *)font)->getCIDToGID(), + ((GfxCIDFont *)font)->getCIDToGIDLen(), + needVerticalMetrics, + outputFunc, outputStream); + } + delete ffTT; + } + gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, + GString *psName) { + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) + return; + } + + // add entry to fontFileIDs list + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 0 font + 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->getCString(), + outputFunc, outputStream); + } else { + // otherwise: use a non-CID composite font + ffTT->convertToType0(psName->getCString(), outputFunc, outputStream); + } + } + delete ffTT; + } + gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupType3Font(GfxFont *font, GString *psName, + Dict *parentResDict) { + Dict *resDict; + Dict *charProcs; + Object charProc; + Gfx *gfx; + PDFRectangle box; + double *m; + GString *buf; + int i; + + // set up resources used by font + if ((resDict = ((Gfx8BitFont *)font)->getResources())) { + inType3Char = gTrue; + setupResources(resDict); + inType3Char = gFalse; + } else { + resDict = parentResDict; + } + + // beginning comment + writePSFmt("%%BeginResource: font {0:t}\n", psName); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // font dictionary + writePS("8 dict begin\n"); + writePS("/FontType 3 def\n"); + m = font->getFontMatrix(); + writePSFmt("/FontMatrix [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] def\n", + m[0], m[1], m[2], m[3], m[4], m[5]); + m = font->getFontBBox(); + writePSFmt("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] 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(xref, this, resDict, &box, NULL); + inType3Char = gTrue; + for (i = 0; i < charProcs->getLength(); ++i) { + t3Cacheable = gFalse; + t3NeedsRestore = gFalse; + writePS("/"); + writePSName(charProcs->getKey(i)); + writePS(" {\n"); + gfx->display(charProcs->getVal(i, &charProc)); + charProc.free(); + if (t3String) { + if (t3Cacheable) { + buf = GString::format("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} setcachedevice\n", + t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); + } else { + buf = GString::format("{0:.4g} {1:.4g} setcharwidth\n", t3WX, t3WY); + } + (*outputFunc)(outputStream, buf->getCString(), buf->getLength()); + delete buf; + (*outputFunc)(outputStream, t3String->getCString(), + t3String->getLength()); + delete t3String; + t3String = NULL; + } + if (t3NeedsRestore) { + (*outputFunc)(outputStream, "Q\n", 2); + } + writePS("} def\n"); + } + inType3Char = gFalse; + delete gfx; + writePS("end\n"); + } + writePS("currentdict end\n"); + writePSFmt("/{0:t} exch definefont pop\n", psName); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupImages(Dict *resDict) { + Object xObjDict, xObj, xObjRef, subtypeObj; + int i; + + if (!(mode == psModeForm || inType3Char || preload)) { + return; + } + + resDict->lookup("XObject", &xObjDict); + if (xObjDict.isDict()) { + for (i = 0; i < xObjDict.dictGetLength(); ++i) { + xObjDict.dictGetValNF(i, &xObjRef); + xObjDict.dictGetVal(i, &xObj); + if (xObj.isStream()) { + xObj.streamGetDict()->lookup("Subtype", &subtypeObj); + if (subtypeObj.isName("Image")) { + if (xObjRef.isRef()) { + setupImage(xObjRef.getRef(), xObj.getStream()); + } else { + error(-1, "Image in resource dict is not an indirect reference"); + } + } + subtypeObj.free(); + } + xObj.free(); + xObjRef.free(); + } + } + xObjDict.free(); +} + +void PSOutputDev::setupImage(Ref id, Stream *str) { + GBool useRLE, useCompressed, useASCIIHex; + GString *s; + int c; + int size, line, col, i; + + // check if image is already setup + for (i = 0; i < imgIDLen; ++i) { + if (imgIDs[i].num == id.num && imgIDs[i].gen == id.gen) { + return; + } + } + + // add entry to imgIDs list + if (imgIDLen >= imgIDSize) { + if (imgIDSize == 0) { + imgIDSize = 64; + } else { + imgIDSize *= 2; + } + imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); + } + imgIDs[imgIDLen++] = id; + + // filters + //~ this does not correctly handle the DeviceN color space + //~ -- need to use DeviceNRecoder + if (level < psLevel2) { + useRLE = gFalse; + useCompressed = gFalse; + useASCIIHex = gTrue; + } else { + s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); + if (s) { + useRLE = gFalse; + useCompressed = gTrue; + delete s; + } else { + useRLE = gTrue; + useCompressed = gFalse; + } + useASCIIHex = level == psLevel1 || level == psLevel1Sep || + globalParams->getPSASCIIHex(); + } + if (useCompressed) { + str = str->getUndecodedStream(); + } + if (useRLE) { + str = new RunLengthEncoder(str); + } + if (useASCIIHex) { + 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 == (useASCIIHex ? '>' : '~') || c == EOF) { + break; + } + if (c == 'z') { + ++col; + } else { + ++col; + for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { + do { + c = str->getChar(); + } while (c == '\n' || c == '\r'); + if (c == (useASCIIHex ? '>' : '~') || c == EOF) { + break; + } + ++col; + } + } + if (col > 225) { + ++size; + col = 0; + } + } while (c != (useASCIIHex ? '>' : '~') && c != EOF); + // add one entry for the final line of data; add another entry + // because the RunLengthDecode filter may read past the end + ++size; + if (useRLE) { + ++size; + } + writePSFmt("{0:d} array dup /ImData_{1:d}_{2:d} exch def\n", + size, id.num, id.gen); + str->close(); + + // write the data into the array + str->reset(); + line = col = 0; + writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~")); + do { + do { + c = str->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 = str->getChar(); + } while (c == '\n' || c == '\r'); + if (c == (useASCIIHex ? '>' : '~') || c == EOF) { + break; + } + writePSChar(c); + ++col; + } + } + // 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 *)(useASCIIHex ? "> put\n" : "~> put\n")); + ++line; + writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line); + col = 0; + } + } while (c != (useASCIIHex ? '>' : '~') && c != EOF); + writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); + if (useRLE) { + ++line; + writePSFmt("{0:d} <> put\n", line); + } else { + writePS("pop\n"); + } + str->close(); + + delete str; +} + +void PSOutputDev::setupForms(Dict *resDict) { + Object xObjDict, xObj, xObjRef, subtypeObj; + int i; + + if (!preload) { + return; + } + + resDict->lookup("XObject", &xObjDict); + if (xObjDict.isDict()) { + for (i = 0; i < xObjDict.dictGetLength(); ++i) { + xObjDict.dictGetValNF(i, &xObjRef); + xObjDict.dictGetVal(i, &xObj); + if (xObj.isStream()) { + xObj.streamGetDict()->lookup("Subtype", &subtypeObj); + if (subtypeObj.isName("Form")) { + if (xObjRef.isRef()) { + setupForm(xObjRef.getRef(), &xObj); + } else { + error(-1, "Form in resource dict is not an indirect reference"); + } + } + subtypeObj.free(); + } + xObj.free(); + xObjRef.free(); + } + } + xObjDict.free(); +} + +void PSOutputDev::setupForm(Ref id, Object *strObj) { + Dict *dict, *resDict; + Object matrixObj, bboxObj, resObj, obj1; + double m[6], bbox[4]; + PDFRectangle box; + Gfx *gfx; + int i; + + // check if form is already defined + for (i = 0; i < formIDLen; ++i) { + if (formIDs[i].num == id.num && formIDs[i].gen == id.gen) { + 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 + dict->lookup("BBox", &bboxObj); + if (!bboxObj.isArray()) { + bboxObj.free(); + error(-1, "Bad form bounding box"); + return; + } + for (i = 0; i < 4; ++i) { + bboxObj.arrayGet(i, &obj1); + bbox[i] = obj1.getNum(); + obj1.free(); + } + bboxObj.free(); + + // get matrix + dict->lookup("Matrix", &matrixObj); + if (matrixObj.isArray()) { + for (i = 0; i < 6; ++i) { + matrixObj.arrayGet(i, &obj1); + m[i] = obj1.getNum(); + obj1.free(); + } + } else { + m[0] = 1; m[1] = 0; + m[2] = 0; m[3] = 1; + m[4] = 0; m[5] = 0; + } + matrixObj.free(); + + // get resources + dict->lookup("Resources", &resObj); + resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; + + writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen); + writePS("q\n"); + writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] 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(xref, this, resDict, &box, &box); + gfx->display(strObj); + delete gfx; + + writePS("Q\n"); + writePS("} def\n"); + + resObj.free(); +} + +GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI, + int rotateA, GBool useMediaBox, GBool crop, + int sliceX, int sliceY, + int sliceW, int sliceH, + GBool printing, Catalog *catalog, + GBool (*abortCheckCbk)(void *data), + void *abortCheckCbkData) { +#if HAVE_SPLASH + PreScanOutputDev *scan; + GBool rasterize; + SplashOutputDev *splashOut; + SplashColor paperColor; + PDFRectangle box; + GfxState *state; + SplashBitmap *bitmap; + Stream *str0, *str; + Object obj; + Guchar *p; + Guchar col[4]; + double m0, m1, m2, m3, m4, m5; + int c, w, h, x, y, comp, i; + + scan = new PreScanOutputDev(); + page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, + sliceX, sliceY, sliceW, sliceH, + printing, catalog, abortCheckCbk, abortCheckCbkData); + rasterize = scan->usesTransparency(); + delete scan; + if (!rasterize) { + return gTrue; + } + + // rasterize the page + if (level == psLevel1) { + paperColor[0] = 0xff; + splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, + paperColor, gTrue, gFalse); +#if SPLASH_CMYK + } else if (level == psLevel1Sep) { + paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; + splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, + paperColor, gTrue, gFalse); +#endif + } else { + paperColor[0] = paperColor[1] = paperColor[2] = 0xff; + splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, + paperColor, gTrue, gFalse); + } + splashOut->startDoc(xref); + page->displaySlice(splashOut, splashDPI, splashDPI, rotateA, + useMediaBox, crop, + sliceX, sliceY, sliceW, sliceH, + printing, catalog, abortCheckCbk, abortCheckCbkData); + + // start the PS page + page->makeBox(splashDPI, splashDPI, rotateA, useMediaBox, gFalse, + sliceX, sliceY, sliceW, sliceH, &box, &crop); + rotateA += page->getRotate(); + if (rotateA >= 360) { + rotateA -= 360; + } else if (rotateA < 0) { + rotateA += 360; + } + state = new GfxState(splashDPI, splashDPI, &box, rotateA, gFalse); + startPage(page->getNum(), state); + delete state; + switch (rotateA) { + case 0: + default: // this should never happen + m0 = box.x2 - box.x1; + m1 = 0; + m2 = 0; + m3 = box.y2 - box.y1; + m4 = box.x1; + m5 = box.y1; + break; + case 90: + m0 = 0; + m1 = box.y2 - box.y1; + m2 = -(box.x2 - box.x1); + m3 = 0; + m4 = box.x2; + m5 = box.y1; + break; + case 180: + m0 = -(box.x2 - box.x1); + m1 = 0; + m2 = 0; + m3 = -(box.y2 - box.y1); + m4 = box.x2; + m5 = box.y2; + break; + case 270: + m0 = 0; + m1 = -(box.y2 - box.y1); + m2 = box.x2 - box.x1; + m3 = 0; + m4 = box.x1; + m5 = box.y2; + break; + } + + //~ need to add the process colors + + // draw the rasterized image + bitmap = splashOut->getBitmap(); + w = bitmap->getWidth(); + h = bitmap->getHeight(); + writePS("gsave\n"); + writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] 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\n", + w, h, w, -h, h); + p = bitmap->getDataPtr(); + i = 0; + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + writePSFmt("{0:02x}", *p++); + if (++i == 32) { + writePSChar('\n'); + i = 0; + } + } + } + if (i != 0) { + writePSChar('\n'); + } + break; + case psLevel1Sep: + writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", + w, h, w, -h, h); + p = bitmap->getDataPtr(); + i = 0; + col[0] = col[1] = col[2] = col[3] = 0; + for (y = 0; y < h; ++y) { + for (comp = 0; comp < 4; ++comp) { + for (x = 0; x < w; ++x) { + writePSFmt("{0:02x}", p[4*x + comp]); + col[comp] |= p[4*x + comp]; + if (++i == 32) { + writePSChar('\n'); + i = 0; + } + } + } + p += bitmap->getRowSize(); + } + if (i != 0) { + writePSChar('\n'); + } + 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: + writePS("/DeviceRGB 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"); + writePS(" /Decode [0 1 0 1 0 1]\n"); + writePS(" /DataSource currentfile\n"); + if (globalParams->getPSASCIIHex()) { + writePS(" /ASCIIHexDecode filter\n"); + } else { + writePS(" /ASCII85Decode filter\n"); + } + writePS(" /RunLengthDecode filter\n"); + writePS(">>\n"); + writePS("image\n"); + obj.initNull(); + str0 = new MemStream((char *)bitmap->getDataPtr(), 0, w * h * 3, &obj); + str = new RunLengthEncoder(str0); + if (globalParams->getPSASCIIHex()) { + str = new ASCIIHexEncoder(str); + } else { + str = new ASCII85Encoder(str); + } + str->reset(); + while ((c = str->getChar()) != EOF) { + writePSChar(c); + } + str->close(); + delete str; + delete str0; + processColors |= psProcessCMYK; + break; + } + delete splashOut; + writePS("grestore\n"); + + // finish the PS page + endPage(); + + return gFalse; +#else + return gTrue; +#endif +} + +void PSOutputDev::startPage(int pageNum, GfxState *state) { + int x1, y1, x2, y2, width, height; + int imgWidth, imgHeight, imgWidth2, imgHeight2; + GBool landscape; + + + if (mode == psModePS) { + writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); + writePS("%%BeginPageSetup\n"); + } + + // underlays + if (underlayCbk) { + (*underlayCbk)(this, underlayCbkData); + } + if (overlayCbk) { + saveState(NULL); + } + + 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 (rotate0 >= 0) { + rotate = (360 - rotate0) % 360; + landscape = gFalse; + } else { + rotate = (360 - state->getRotate()) % 360; + if (rotate == 0 || rotate == 180) { + if (width > height && width > imgWidth) { + rotate += 90; + landscape = gTrue; + } else { + landscape = gFalse; + } + } else { // rotate == 90 || rotate == 270 + if (height > width && height > imgWidth) { + rotate = 270 - rotate; + landscape = gTrue; + } else { + landscape = gFalse; + } + } + } + writePSFmt("%%PageOrientation: {0:s}\n", + landscape ? "Landscape" : "Portrait"); + writePS("pdfStartPage\n"); + if (rotate == 0) { + imgWidth2 = imgWidth; + imgHeight2 = imgHeight; + } else if (rotate == 90) { + writePS("90 rotate\n"); + ty = -imgWidth; + imgWidth2 = imgHeight; + imgHeight2 = imgWidth; + } else if (rotate == 180) { + writePS("180 rotate\n"); + imgWidth2 = imgWidth; + imgHeight2 = imgHeight; + tx = -imgWidth; + ty = -imgHeight; + } else { // rotate == 270 + writePS("270 rotate\n"); + 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; + } + } else { + xScale = yScale = 1; + } + // 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 ? tx0 : ty0; + ty += rotate == 0 ? ty0 : -tx0; + } else if (globalParams->getPSCenter()) { + 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 ? imgLLX : imgLLY; + ty += rotate == 0 ? imgLLY : -imgLLX; + if (tx != 0 || ty != 0) { + writePSFmt("{0:.4g} {1:.4g} translate\n", tx, ty); + } + if (xScale != 1 || yScale != 1) { + writePSFmt("{0:.4f} {0:.4f} scale\n", xScale); + } + if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { + writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} 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); + } + + writePS("%%EndPageSetup\n"); + ++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:.4g} {1:.4g} translate\n", tx, ty); + } + xScale = yScale = 1; + break; + + case psModeForm: + writePS("/PaintProc {\n"); + writePS("begin xpdf begin\n"); + writePS("pdfStartPage\n"); + tx = ty = 0; + xScale = yScale = 1; + rotate = 0; + break; + } +} + +void PSOutputDev::endPage() { + if (overlayCbk) { + restoreState(NULL); + (*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:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] 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:.4g}{1:w}", + dash[i] < 0 ? 0 : dash[i], + (i == length-1) ? 0 : 1); + } + writePSFmt("] {0:.4g} 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:.4g} M\n", state->getMiterLimit()); +} + +void PSOutputDev::updateLineWidth(GfxState *state) { + writePSFmt("{0:.4g} w\n", state->getLineWidth()); +} + +void PSOutputDev::updateFillColorSpace(GfxState *state) { + switch (level) { + case psLevel1: + case psLevel1Sep: + break; + case psLevel2: + case psLevel3: + if (state->getFillColorSpace()->getMode() != csPattern) { + dumpColorSpaceL2(state->getFillColorSpace(), gTrue, gFalse, gFalse); + writePS(" cs\n"); + } + break; + case psLevel2Sep: + case psLevel3Sep: + break; + } +} + +void PSOutputDev::updateStrokeColorSpace(GfxState *state) { + switch (level) { + case psLevel1: + case psLevel1Sep: + break; + case psLevel2: + case psLevel3: + if (state->getStrokeColorSpace()->getMode() != csPattern) { + dumpColorSpaceL2(state->getStrokeColorSpace(), gTrue, gFalse, gFalse); + writePS(" CS\n"); + } + break; + case psLevel2Sep: + case psLevel3Sep: + break; + } +} + +void PSOutputDev::updateFillColor(GfxState *state) { + GfxColor color; + GfxColor *colorPtr; + GfxGray gray; + GfxCMYK cmyk; + GfxSeparationColorSpace *sepCS; + double c, m, y, k; + int i; + + switch (level) { + case psLevel1: + state->getFillGray(&gray); + writePSFmt("{0:.4g} g\n", colToDbl(gray)); + break; + case psLevel1Sep: + state->getFillCMYK(&cmyk); + c = colToDbl(cmyk.c); + m = colToDbl(cmyk.m); + y = colToDbl(cmyk.y); + k = colToDbl(cmyk.k); + writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); + addProcessColor(c, m, y, k); + break; + case psLevel2: + case psLevel3: + if (state->getFillColorSpace()->getMode() != csPattern) { + 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 psLevel2Sep: + case psLevel3Sep: + if (state->getFillColorSpace()->getMode() == csSeparation) { + 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); + writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); + addProcessColor(c, m, y, k); + } + break; + } + t3Cacheable = gFalse; +} + +void PSOutputDev::updateStrokeColor(GfxState *state) { + GfxColor color; + GfxColor *colorPtr; + GfxGray gray; + GfxCMYK cmyk; + GfxSeparationColorSpace *sepCS; + double c, m, y, k; + int i; + + switch (level) { + case psLevel1: + state->getStrokeGray(&gray); + writePSFmt("{0:.4g} G\n", colToDbl(gray)); + break; + case psLevel1Sep: + state->getStrokeCMYK(&cmyk); + c = colToDbl(cmyk.c); + m = colToDbl(cmyk.m); + y = colToDbl(cmyk.y); + k = colToDbl(cmyk.k); + writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); + addProcessColor(c, m, y, k); + break; + case psLevel2: + case psLevel3: + if (state->getStrokeColorSpace()->getMode() != csPattern) { + 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 psLevel2Sep: + case psLevel3Sep: + if (state->getStrokeColorSpace()->getMode() == csSeparation) { + 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); + writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); + addProcessColor(c, m, y, k); + } + break; + } + t3Cacheable = gFalse; +} + +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; + + 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::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:.4g} Tf\n", + state->getFont()->getID()->num, state->getFont()->getID()->gen, + fabs(state->getFontSize()) < 0.00001 ? 0.00001 + : state->getFontSize()); + } +} + +void PSOutputDev::updateTextMat(GfxState *state) { + double *mat; + + 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:.4g} {1:.4g}] Tm\n", mat[4], mat[5]); + } else { + writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] Tm\n", + mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); + } +} + +void PSOutputDev::updateCharSpace(GfxState *state) { + writePSFmt("{0:.4g} 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 = gFalse; + } +} + +void PSOutputDev::updateRise(GfxState *state) { + writePSFmt("{0:.4g} Ts\n", state->getRise()); +} + +void PSOutputDev::updateWordSpace(GfxState *state) { + writePSFmt("{0:.4g} Tw\n", state->getWordSpace()); +} + +void PSOutputDev::updateHorizScaling(GfxState *state) { + double h; + + h = state->getHorizScaling(); + if (fabs(h) < 0.01) { + h = 0.01; + } + writePSFmt("{0:.4g} Tz\n", h); +} + +void PSOutputDev::updateTextPos(GfxState *state) { + writePSFmt("{0:.4g} {1:.4g} Td\n", state->getLineX(), state->getLineY()); +} + +void PSOutputDev::updateTextShift(GfxState *state, double shift) { + if (state->getFont()->getWMode()) { + writePSFmt("{0:.4g} TJmV\n", shift); + } else { + writePSFmt("{0:.4g} TJm\n", shift); + } +} + +void PSOutputDev::stroke(GfxState *state) { + doPath(state->getPath()); + if (t3String) { + // if we're construct 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"); +} + +void PSOutputDev::tilingPatternFill(GfxState *state, Object *str, + int paintType, Dict *resDict, + double *mat, 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:.4g} {1:.4g} {2:.4g} {3:.4g}] 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(xref, this, resDict, &box, NULL); + writePS("/x {\n"); + if (paintType == 2) { + writePSFmt("{0:.4g} 0 {1:.4g} {2:.4g} {3:.4g} {4:.4g} setcachedevice\n", + xStep, bbox[0], bbox[1], bbox[2], bbox[3]); + } else { + if (x1 - 1 <= x0) { + writePS("1 0 setcharwidth\n"); + } else { + writePSFmt("{0:.4g} 0 setcharwidth\n", xStep); + } + } + inType3Char = gTrue; + ++numTilingPatterns; + gfx->display(str); + --numTilingPatterns; + inType3Char = gFalse; + 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); + writePSFmt("gsave [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] concat\n", + mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); + writePSFmt("{0:d} 1 {1:d} {{ {2:.4g} exch {3:.4g} 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"); +} + +GBool PSOutputDev::functionShadedFill(GfxState *state, + GfxFunctionShading *shading) { + double x0, y0, x1, y1; + double *mat; + int i; + + if (level == psLevel2Sep || level == psLevel3Sep) { + if (shading->getColorSpace()->getMode() != csDeviceCMYK) { + return gFalse; + } + processColors |= psProcessCMYK; + } + + shading->getDomain(&x0, &y0, &x1, &y1); + mat = shading->getMatrix(); + writePSFmt("/mat [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] 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:.4g} {1:.4g} {2:.4g} {3:.4g} 0 funcSH\n", x0, y0, x1, y1); + + return gTrue; +} + +GBool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) { + 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 gFalse; + } + 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 gTrue; + } 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:.4g} def\n", t0); + writePSFmt("/t1 {0:.4g} def\n", t1); + writePSFmt("/dt {0:.4g} def\n", t1 - t0); + writePSFmt("/x0 {0:.4g} def\n", x0); + writePSFmt("/y0 {0:.4g} def\n", y0); + writePSFmt("/dx {0:.4g} def\n", x1 - x0); + writePSFmt("/x1 {0:.4g} def\n", x1); + writePSFmt("/y1 {0:.4g} def\n", y1); + writePSFmt("/dy {0:.4g} def\n", y1 - y0); + writePSFmt("/xMin {0:.4g} def\n", xMin); + writePSFmt("/yMin {0:.4g} def\n", yMin); + writePSFmt("/xMax {0:.4g} def\n", xMax); + writePSFmt("/yMax {0:.4g} 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:.4g} {1:.4g} 0 axialSH\n", tMin, tMax); + + return gTrue; +} + +GBool PSOutputDev::radialShadedFill(GfxState *state, + GfxRadialShading *shading) { + double xMin, yMin, xMax, yMax; + double x0, y0, r0, x1, y1, r1, t0, t1; + double xa, ya, ra; + double sz, xz, yz, sMin, sMax, sa, ta; + double theta, alpha, a1, a2; + GBool enclosed; + int i; + + if (level == psLevel2Sep || level == psLevel3Sep) { + if (shading->getColorSpace()->getMode() != csDeviceCMYK) { + return gFalse; + } + 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. + if (r0 == r1) { + enclosed = x0 == x1 && y0 == y1; + theta = 0; + sz = 0; // make gcc happy + } else { + sz = -r0 / (r1 - r0); + xz = x0 + sz * (x1 - x0); + yz = y0 + sz * (y1 - y0); + enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0; + theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz))); + if (r0 > r1) { + theta = -theta; + } + } + 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 { + sMin = 1; + sMax = 0; + // solve for x(s) + r(s) = xMin + if ((x1 + r1) - (x0 + r0) != 0) { + sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); + if (sa < sMin) { + sMin = sa; + } else if (sa > sMax) { + sMax = sa; + } + } + // solve for x(s) - r(s) = xMax + if ((x1 - r1) - (x0 - r0) != 0) { + sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); + if (sa < sMin) { + sMin = sa; + } else if (sa > sMax) { + sMax = sa; + } + } + // solve for y(s) + r(s) = yMin + if ((y1 + r1) - (y0 + r0) != 0) { + sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); + if (sa < sMin) { + sMin = sa; + } else if (sa > sMax) { + sMax = sa; + } + } + // solve for y(s) - r(s) = yMax + if ((y1 - r1) - (y0 - r0) != 0) { + sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); + if (sa < sMin) { + sMin = sa; + } else if (sa > sMax) { + sMax = sa; + } + } + // check against sz + if (r0 < r1) { + if (sMin < sz) { + sMin = sz; + } + } else if (r0 > r1) { + if (sMax > sz) { + sMax = sz; + } + } + // check the 'extend' flags + if (!shading->getExtend0() && sMin < 0) { + sMin = 0; + } + if (!shading->getExtend1() && sMax > 1) { + sMax = 1; + } + } + + // generate the PS code + writePSFmt("/x0 {0:.4g} def\n", x0); + writePSFmt("/x1 {0:.4g} def\n", x1); + writePSFmt("/dx {0:.4g} def\n", x1 - x0); + writePSFmt("/y0 {0:.4g} def\n", y0); + writePSFmt("/y1 {0:.4g} def\n", y1); + writePSFmt("/dy {0:.4g} def\n", y1 - y0); + writePSFmt("/r0 {0:.4g} def\n", r0); + writePSFmt("/r1 {0:.4g} def\n", r1); + writePSFmt("/dr {0:.4g} def\n", r1 - r0); + writePSFmt("/t0 {0:.4g} def\n", t0); + writePSFmt("/t1 {0:.4g} def\n", t1); + writePSFmt("/dt {0:.4g} 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:.4g} def\n", a1); + writePSFmt("/a2 {0:.4g} 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:.4g} {1:.4g} 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:.4g} radialCol aload pop k\n", ta); + } else { + writePSFmt("{0:.4g} radialCol sc\n", ta); + } + writePSFmt("{0:.4g} {1:.4g} {2:.4g} 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:.4g} radialCol aload pop k\n", ta); + } else { + writePSFmt("{0:.4g} radialCol sc\n", ta); + } + writePSFmt("{0:.4g} {1:.4g} {2:.4g} 0 360 arc h\n", xa, ya, ra); + writePSFmt("{0:.4g} {1:.4g} m {2:.4g} {3:.4g} l {4:.4g} {5:.4g} l {6:.4g} {7:.4g} l h f*\n", + xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin); + } + } + + return gTrue; +} + +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(GfxPath *path) { + GfxSubpath *subpath; + 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) { + 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:.4g} {1:.4g} {2:.4g} {3:.4g} 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:.4g} {1:.4g} {2:.4g} {3:.4g} re\n", + x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, + fabs(x1 - x0), fabs(y2 - y0)); + return; + } + } + } + + for (i = 0; i < n; ++i) { + subpath = path->getSubpath(i); + m = subpath->getNumPoints(); + writePSFmt("{0:.4g} {1:.4g} m\n", subpath->getX(0), subpath->getY(0)); + j = 1; + while (j < m) { + if (subpath->getCurve(j)) { + writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} 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:.4g} {1:.4g} l\n", subpath->getX(j), subpath->getY(j)); + ++j; + } + } + if (subpath->isClosed()) { + writePS("h\n"); + } + } +} + +void PSOutputDev::drawString(GfxState *state, GString *s) { + GfxFont *font; + int wMode; + Gushort *codeToGID; + GString *s2; + double dx, dy, dx2, dy2, originX, originY; + char *p; + UnicodeMap *uMap; + CharCode code; + Unicode u[8]; + char buf[8]; + int len, nChars, uLen, n, m, i, j; + + // 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; + } + wMode = font->getWMode(); + + // check for a subtitute 16-bit font + uMap = NULL; + codeToGID = NULL; + if (font->isCIDFont()) { + for (i = 0; i < font16EncLen; ++i) { + if (font->getID()->num == font16Enc[i].fontID.num && + font->getID()->gen == font16Enc[i].fontID.gen) { + uMap = globalParams->getUnicodeMap(font16Enc[i].enc); + break; + } + } + + // check for a code-to-GID map + } else { + for (i = 0; i < font8InfoLen; ++i) { + if (font->getID()->num == font8Info[i].fontID.num && + font->getID()->gen == font8Info[i].fontID.gen) { + codeToGID = font8Info[i].codeToGID; + break; + } + } + } + + // compute width of chars in string, ignoring char spacing and word + // spacing -- the Tj operator will adjust for the metrics of the + // font that's actually used + dx = dy = 0; + nChars = 0; + p = s->getCString(); + len = s->getLength(); + s2 = new GString(); + while (len > 0) { + n = font->getNextChar(p, len, &code, + u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, + &dx2, &dy2, &originX, &originY); + if (font->isCIDFont()) { + if (uMap) { + 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 + nChars += uLen; + } else { + s2->append((char)((code >> 8) & 0xff)); + s2->append((char)(code & 0xff)); + ++nChars; + } + } else { + if (!codeToGID || codeToGID[code]) { + s2->append((char)code); + } + } + dx += dx2; + dy += dy2; + p += n; + len -= n; + } + dx *= state->getFontSize() * state->getHorizScaling(); + dy *= state->getFontSize(); + if (uMap) { + uMap->decRefCnt(); + } + + if (s2->getLength() > 0) { + writePSString(s2); + if (font->isCIDFont()) { + if (wMode) { + writePSFmt(" {0:d} {1:.4g} Tj16V\n", nChars, dy); + } else { + writePSFmt(" {0:d} {1:.4g} Tj16\n", nChars, dx); + } + } else { + writePSFmt(" {0:.4g} Tj\n", dx); + } + } + delete s2; + + if (state->getRender() & 4) { + haveTextClip = gTrue; + } +} + +void PSOutputDev::endTextObject(GfxState *state) { + if (haveTextClip) { + writePS("Tclip\n"); + haveTextClip = gFalse; + } +} + +void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg) { + int len; + + len = height * ((width + 7) / 8); + switch (level) { + case psLevel1: + case psLevel1Sep: + doImageL1(ref, NULL, invert, inlineImg, str, width, height, len); + break; + case psLevel2: + case psLevel2Sep: + doImageL2(ref, NULL, invert, inlineImg, str, width, height, len, + NULL, NULL, 0, 0, gFalse); + break; + case psLevel3: + case psLevel3Sep: + doImageL3(ref, NULL, invert, inlineImg, str, width, height, len, + NULL, NULL, 0, 0, gFalse); + break; + } +} + +void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + int *maskColors, GBool inlineImg) { + int len; + + len = height * ((width * colorMap->getNumPixelComps() * + colorMap->getBits() + 7) / 8); + switch (level) { + case psLevel1: + doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len); + break; + case psLevel1Sep: + //~ handle indexed, separation, ... color spaces + doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len); + break; + case psLevel2: + case psLevel2Sep: + doImageL2(ref, colorMap, gFalse, inlineImg, str, + width, height, len, maskColors, NULL, 0, 0, gFalse); + break; + case psLevel3: + case psLevel3Sep: + doImageL3(ref, colorMap, gFalse, inlineImg, str, + width, height, len, maskColors, NULL, 0, 0, gFalse); + break; + } + t3Cacheable = gFalse; +} + +void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, + int width, int height, + GfxImageColorMap *colorMap, + Stream *maskStr, + int maskWidth, int maskHeight, + GBool maskInvert) { + int len; + + len = height * ((width * colorMap->getNumPixelComps() * + colorMap->getBits() + 7) / 8); + switch (level) { + case psLevel1: + doImageL1(ref, colorMap, gFalse, gFalse, str, width, height, len); + break; + case psLevel1Sep: + //~ handle indexed, separation, ... color spaces + doImageL1Sep(colorMap, gFalse, gFalse, str, width, height, len); + break; + case psLevel2: + case psLevel2Sep: + doImageL2(ref, colorMap, gFalse, gFalse, str, width, height, len, + NULL, maskStr, maskWidth, maskHeight, maskInvert); + break; + case psLevel3: + case psLevel3Sep: + doImageL3(ref, colorMap, gFalse, gFalse, str, width, height, len, + NULL, maskStr, maskWidth, maskHeight, maskInvert); + break; + } + t3Cacheable = gFalse; +} + +void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, + GBool invert, GBool inlineImg, + Stream *str, int width, int height, int len) { + ImageStream *imgStr; + Guchar pixBuf[gfxColorMaxComps]; + GfxGray gray; + int col, x, y, c, i; + + if ((inType3Char || preload) && !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 { + // set up to use the array already created by setupImages() + writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); + } + } + + // image/imagemask command + if ((inType3Char || preload) && !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\n", + width, height, + width, -height, height); + } else { + writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n", + width, height, invert ? "true" : "false", + width, -height, height); + } + + // image data + if (!((inType3Char || preload) && !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); + writePSFmt("{0:02x}", colToByte(gray)); + if (++i == 32) { + writePSChar('\n'); + i = 0; + } + } + } + if (i != 0) { + writePSChar('\n'); + } + str->close(); + delete imgStr; + + // imagemask + } else { + str->reset(); + i = 0; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; x += 8) { + writePSFmt("{0:02x}", str->getChar() & 0xff); + if (++i == 32) { + writePSChar('\n'); + i = 0; + } + } + } + if (i != 0) { + writePSChar('\n'); + } + str->close(); + } + } +} + +void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap, + GBool invert, GBool inlineImg, + Stream *str, int width, int height, int len) { + ImageStream *imgStr; + Guchar *lineBuf; + Guchar pixBuf[gfxColorMaxComps]; + GfxCMYK cmyk; + int x, y, i, comp; + + // width, height, matrix, bits per component + writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", + width, height, + width, -height, height); + + // allocate a line buffer + lineBuf = (Guchar *)gmalloc(4 * width); + + // 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) { + + // read the line + 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)); + } + + // write one line of each color component + for (comp = 0; comp < 4; ++comp) { + for (x = 0; x < width; ++x) { + writePSFmt("{0:02x}", lineBuf[4*x + comp]); + if (++i == 32) { + writePSChar('\n'); + i = 0; + } + } + } + } + + if (i != 0) { + writePSChar('\n'); + } + + str->close(); + delete imgStr; + gfree(lineBuf); +} + +void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap, + GBool invert, GBool inlineImg, + Stream *str, int width, int height, int len, + int *maskColors, Stream *maskStr, + int maskWidth, int maskHeight, GBool maskInvert) { + Stream *str2; + ImageStream *imgStr; + Guchar *line; + PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; + int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; + GBool emitRect, addRect, extendRect; + GString *s; + int n, numComps; + GBool useRLE, useASCII, useASCIIHex, useCompressed; + GfxSeparationColorSpace *sepCS; + GfxColor color; + GfxCMYK cmyk; + int c; + int col, i, j, x0, x1, y, maskXor; + + // 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 = rectsOutLen = 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 = gFalse; + if (x0 >= width) { + emitRect = gTrue; + } else if (i >= rects0Len) { + addRect = gTrue; + } else if (rects0[i].x0 < x0) { + emitRect = gTrue; + } else if (x0 < rects0[i].x0) { + addRect = gTrue; + } else if (rects0[i].x1 == x1) { + extendRect = gTrue; + } else { + emitRect = addRect = gTrue; + } + 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; + } + 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); + gfree(rectsOut); + gfree(rects0); + gfree(rects1); + delete imgStr; + str->close(); + + // explicit masking + } else if (maskStr) { + 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 = gFalse; + if (x0 >= maskWidth) { + emitRect = gTrue; + } else if (i >= rects0Len) { + addRect = gTrue; + } else if (rects0[i].x0 < x0) { + emitRect = gTrue; + } else if (x0 < rects0[i].x0) { + addRect = gTrue; + } else if (rects0[i].x1 == x1) { + extendRect = gTrue; + } else { + emitRect = addRect = gTrue; + } + 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; + } + 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); + gfree(rectsOut); + gfree(rects0); + gfree(rects1); + delete imgStr; + maskStr->close(); + } + + // color space + if (colorMap) { + dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue, gFalse); + writePS(" setcolorspace\n"); + } + + useASCIIHex = globalParams->getPSASCIIHex(); + + // set up the image data + if (mode == psModeForm || inType3Char || preload) { + if (inlineImg) { + // create an array + str2 = new FixedLengthEncoder(str, len); + 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 RunLengthDecode filter may + // read past the end + writePS("<>]\n"); + writePS("0\n"); + str2->close(); + delete str2; + } else { + // set up to use the array already created by setupImages() + writePSFmt("ImData_{0:d}_{1:d} 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 || preload) { + writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); + } else { + writePS(" /DataSource currentfile\n"); + } + + // filters + s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, + " "); + if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || + inlineImg || !s) { + useRLE = gTrue; + useASCII = !(mode == psModeForm || inType3Char || preload); + useCompressed = gFalse; + } else { + useRLE = gFalse; + useASCII = str->isBinary() && + !(mode == psModeForm || inType3Char || preload); + useCompressed = gTrue; + } + if (useASCII) { + writePSFmt(" /ASCII{0:s}Decode filter\n", + useASCIIHex ? "Hex" : "85"); + } + if (useRLE) { + writePS(" /RunLengthDecode filter\n"); + } + if (useCompressed) { + writePS(s->getCString()); + } + if (s) { + delete s; + } + + if (mode == psModeForm || inType3Char || preload) { + + // end of image dictionary + writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask"); + + // get rid of the array and index + 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 RunLengthEncode and ASCIIHex/85 encode filters + 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"); +#if OPI_SUPPORT + if (opi13Nest) { + if (inlineImg) { + // this can't happen -- OPI dictionaries are in XObjects + error(-1, "Internal: OPI in inline image"); + n = 0; + } else { + // need to read the stream to count characters -- the length + // is data-dependent (because of ASCII and 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) { + 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(); + while ((c = str->getChar()) != EOF) { + writePSChar(c); + } + str->close(); + + // add newline and trailer to the end + writePSChar('\n'); + writePS("%-EOD-\n"); +#if OPI_SUPPORT + if (opi13Nest) { + writePS("%%EndData\n"); + } +#endif + + // delete encoders + if (useRLE || useASCII || inlineImg) { + delete str; + } + } + + if ((maskColors && colorMap && !inlineImg) || maskStr) { + writePS("pdfImClipEnd\n"); + } +} + +//~ this doesn't currently support OPI +void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap, + GBool invert, GBool inlineImg, + Stream *str, int width, int height, int len, + int *maskColors, Stream *maskStr, + int maskWidth, int maskHeight, GBool maskInvert) { + Stream *str2; + GString *s; + int n, numComps; + GBool useRLE, useASCII, useASCIIHex, useCompressed; + GBool maskUseRLE, maskUseASCII, maskUseCompressed; + GfxSeparationColorSpace *sepCS; + GfxColor color; + GfxCMYK cmyk; + int c; + int col, i; + + useASCIIHex = globalParams->getPSASCIIHex(); + useRLE = useASCII = useCompressed = gFalse; // make gcc happy + maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy + + // color space + if (colorMap) { + dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue, gFalse); + writePS(" setcolorspace\n"); + } + + // set up the image data + if (mode == psModeForm || inType3Char || preload) { + if (inlineImg) { + // create an array + str2 = new FixedLengthEncoder(str, len); + 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 RunLengthDecode filter may + // read past the end + writePS("<>]\n"); + writePS("0\n"); + str2->close(); + delete str2; + } else { + // set up to use the array already created by setupImages() + writePSFmt("ImData_{0:d}_{1:d} 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 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 || preload) { + writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); + } else { + writePS(" /DataSource currentfile\n"); + } + + // filters + s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, + " "); + if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || + inlineImg || !s) { + useRLE = gTrue; + useASCII = !(mode == psModeForm || inType3Char || preload); + useCompressed = gFalse; + } else { + useRLE = gFalse; + useASCII = str->isBinary() && + !(mode == psModeForm || inType3Char || preload); + useCompressed = gTrue; + } + if (useASCII) { + writePSFmt(" /ASCII{0:s}Decode filter\n", + useASCIIHex ? "Hex" : "85"); + } + if (useRLE) { + writePS(" /RunLengthDecode filter\n"); + } + if (useCompressed) { + writePS(s->getCString()); + } + 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 + writePS(" /DataSource currentfile\n"); + s = maskStr->getPSFilter(3, " "); + if (!s) { + maskUseRLE = gTrue; + maskUseASCII = gTrue; + maskUseCompressed = gFalse; + } else { + maskUseRLE = gFalse; + maskUseASCII = maskStr->isBinary(); + maskUseCompressed = gTrue; + } + if (maskUseASCII) { + writePSFmt(" /ASCII{0:s}Decode filter\n", + useASCIIHex ? "Hex" : "85"); + } + if (maskUseRLE) { + writePS(" /RunLengthDecode filter\n"); + } + if (maskUseCompressed) { + writePS(s->getCString()); + } + if (s) { + delete s; + } + + writePS(">>\n"); + writePS(">>\n"); + } + + if (mode == psModeForm || inType3Char || preload) { + + // image command + writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask"); + + } else { + + if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && + colorMap->getColorSpace()->getMode() == csSeparation) { + 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"); + } + + } + + // explicit masking + if (maskStr) { + + if (maskUseCompressed) { + maskStr = maskStr->getUndecodedStream(); + } + + // add RunLengthEncode and ASCIIHex/85 encode filters + 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'); + + // delete encoders + if (maskUseRLE || maskUseASCII) { + delete maskStr; + } + } + + // get rid of the array and index + if (mode == psModeForm || inType3Char || preload) { + 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(); + } + + // recode DeviceN data + if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { + str = new DeviceNRecoder(str, width, height, colorMap); + } + + // add RunLengthEncode and ASCIIHex/85 encode filters + 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 (useRLE || useASCII || inlineImg) { + delete str; + } + } +} + +void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace, + GBool genXform, GBool updateColors, + GBool map01) { + GfxCalGrayColorSpace *calGrayCS; + GfxCalRGBColorSpace *calRGBCS; + GfxLabColorSpace *labCS; + GfxIndexedColorSpace *indexedCS; + GfxSeparationColorSpace *separationCS; + GfxDeviceNColorSpace *deviceNCS; + GfxColorSpace *baseCS; + Guchar *lookup, *p; + double x[gfxColorMaxComps], y[gfxColorMaxComps]; + double low[gfxColorMaxComps], range[gfxColorMaxComps]; + GfxColor color; + GfxCMYK cmyk; + Function *func; + 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, gFalse); + break; + + case csIndexed: + indexedCS = (GfxIndexedColorSpace *)colorSpace; + baseCS = indexedCS->getBase(); + writePS("[/Indexed "); + dumpColorSpaceL2(baseCS, gFalse, gFalse, gTrue); + n = indexedCS->getIndexHigh(); + numComps = baseCS->getNComps(); + lookup = indexedCS->getLookup(); + writePSFmt(" {0:d} <\n", n); + if (baseCS->getMode() == csDeviceN) { + func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); + baseCS->getDefaultRanges(low, range, indexedCS->getIndexHigh()); + if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) { + labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt(); + } else { + labCS = NULL; + } + 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()); + writePS(" "); + dumpColorSpaceL2(separationCS->getAlt(), gFalse, gFalse, gFalse); + writePS("\n"); + cvtFunction(separationCS->getFunc()); + writePS("]"); + if (genXform) { + writePS(" {}"); + } + if (updateColors) { + addCustomColor(separationCS); + } + break; + + case csDeviceN: + // DeviceN color spaces are a Level 3 PostScript feature. + deviceNCS = (GfxDeviceNColorSpace *)colorSpace; + dumpColorSpaceL2(deviceNCS->getAlt(), gFalse, updateColors, map01); + if (genXform) { + writePS(" "); + cvtFunction(deviceNCS->getTintTransformFunc()); + } + break; + + case csPattern: + //~ unimplemented + break; + } +} + +#if OPI_SUPPORT +void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) { + Object dict; + + if (globalParams->getPSOPI()) { + opiDict->lookup("2.0", &dict); + if (dict.isDict()) { + opiBegin20(state, dict.getDict()); + dict.free(); + } else { + dict.free(); + opiDict->lookup("1.3", &dict); + if (dict.isDict()) { + opiBegin13(state, dict.getDict()); + } + dict.free(); + } + } +} + +void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { + Object obj1, obj2, obj3, obj4; + double width, height, left, right, top, bottom; + int w, h; + int i; + + writePS("%%BeginOPI: 2.0\n"); + writePS("%%Distilled\n"); + + dict->lookup("F", &obj1); + if (getFileSpec(&obj1, &obj2)) { + writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString()); + obj2.free(); + } + obj1.free(); + + dict->lookup("MainImage", &obj1); + if (obj1.isString()) { + writePSFmt("%%MainImage: {0:t}\n", obj1.getString()); + } + obj1.free(); + + //~ ignoring 'Tags' entry + //~ need to use writePSString() and deal with >255-char lines + + dict->lookup("Size", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 2) { + obj1.arrayGet(0, &obj2); + width = obj2.getNum(); + obj2.free(); + obj1.arrayGet(1, &obj2); + height = obj2.getNum(); + obj2.free(); + writePSFmt("%%ImageDimensions: {0:.4g} {1:.4g}\n", width, height); + } + obj1.free(); + + dict->lookup("CropRect", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 4) { + obj1.arrayGet(0, &obj2); + left = obj2.getNum(); + obj2.free(); + obj1.arrayGet(1, &obj2); + top = obj2.getNum(); + obj2.free(); + obj1.arrayGet(2, &obj2); + right = obj2.getNum(); + obj2.free(); + obj1.arrayGet(3, &obj2); + bottom = obj2.getNum(); + obj2.free(); + writePSFmt("%%ImageCropRect: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n", + left, top, right, bottom); + } + obj1.free(); + + dict->lookup("Overprint", &obj1); + if (obj1.isBool()) { + writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); + } + obj1.free(); + + dict->lookup("Inks", &obj1); + if (obj1.isName()) { + writePSFmt("%%ImageInks: {0:s}\n", obj1.getName()); + } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) { + obj1.arrayGet(0, &obj2); + if (obj2.isName()) { + writePSFmt("%%ImageInks: {0:s} {1:d}", + obj2.getName(), (obj1.arrayGetLength() - 1) / 2); + for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) { + obj1.arrayGet(i, &obj3); + obj1.arrayGet(i+1, &obj4); + if (obj3.isString() && obj4.isNum()) { + writePS(" "); + writePSString(obj3.getString()); + writePSFmt(" {0:.4g}", obj4.getNum()); + } + obj3.free(); + obj4.free(); + } + writePS("\n"); + } + obj2.free(); + } + obj1.free(); + + writePS("gsave\n"); + + writePS("%%BeginIncludedImage\n"); + + dict->lookup("IncludedImageDimensions", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 2) { + obj1.arrayGet(0, &obj2); + w = obj2.getInt(); + obj2.free(); + obj1.arrayGet(1, &obj2); + h = obj2.getInt(); + obj2.free(); + writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h); + } + obj1.free(); + + dict->lookup("IncludedImageQuality", &obj1); + if (obj1.isNum()) { + writePSFmt("%%IncludedImageQuality: {0:.4g}\n", obj1.getNum()); + } + obj1.free(); + + ++opi20Nest; +} + +void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { + Object obj1, obj2; + 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"); + + dict->lookup("F", &obj1); + if (getFileSpec(&obj1, &obj2)) { + writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString()); + obj2.free(); + } + obj1.free(); + + dict->lookup("CropRect", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 4) { + obj1.arrayGet(0, &obj2); + left = obj2.getInt(); + obj2.free(); + obj1.arrayGet(1, &obj2); + top = obj2.getInt(); + obj2.free(); + obj1.arrayGet(2, &obj2); + right = obj2.getInt(); + obj2.free(); + obj1.arrayGet(3, &obj2); + bottom = obj2.getInt(); + obj2.free(); + writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n", + left, top, right, bottom); + } + obj1.free(); + + dict->lookup("Color", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 5) { + obj1.arrayGet(0, &obj2); + c = obj2.getNum(); + obj2.free(); + obj1.arrayGet(1, &obj2); + m = obj2.getNum(); + obj2.free(); + obj1.arrayGet(2, &obj2); + y = obj2.getNum(); + obj2.free(); + obj1.arrayGet(3, &obj2); + k = obj2.getNum(); + obj2.free(); + obj1.arrayGet(4, &obj2); + if (obj2.isString()) { + writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", + c, m, y, k); + writePSString(obj2.getString()); + writePS("\n"); + } + obj2.free(); + } + obj1.free(); + + dict->lookup("ColorType", &obj1); + if (obj1.isName()) { + writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName()); + } + obj1.free(); + + //~ ignores 'Comments' entry + //~ need to handle multiple lines + + dict->lookup("CropFixed", &obj1); + if (obj1.isArray()) { + obj1.arrayGet(0, &obj2); + ulx = obj2.getNum(); + obj2.free(); + obj1.arrayGet(1, &obj2); + uly = obj2.getNum(); + obj2.free(); + obj1.arrayGet(2, &obj2); + lrx = obj2.getNum(); + obj2.free(); + obj1.arrayGet(3, &obj2); + lry = obj2.getNum(); + obj2.free(); + writePSFmt("%ALDImageCropFixed: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n", + ulx, uly, lrx, lry); + } + obj1.free(); + + dict->lookup("GrayMap", &obj1); + 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) { + obj1.arrayGet(i+j, &obj2); + writePSFmt(" {0:d}", obj2.getInt()); + obj2.free(); + } + } + writePS("\n"); + } + obj1.free(); + + dict->lookup("ID", &obj1); + if (obj1.isString()) { + writePSFmt("%ALDImageID: {0:t}\n", obj1.getString()); + } + obj1.free(); + + dict->lookup("ImageType", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 2) { + obj1.arrayGet(0, &obj2); + samples = obj2.getInt(); + obj2.free(); + obj1.arrayGet(1, &obj2); + bits = obj2.getInt(); + obj2.free(); + writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits); + } + obj1.free(); + + dict->lookup("Overprint", &obj1); + if (obj1.isBool()) { + writePSFmt("%ALDImageOverprint: {0:s}\n", + obj1.getBool() ? "true" : "false"); + } + obj1.free(); + + dict->lookup("Position", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 8) { + obj1.arrayGet(0, &obj2); + llx = obj2.getNum(); + obj2.free(); + obj1.arrayGet(1, &obj2); + lly = obj2.getNum(); + obj2.free(); + obj1.arrayGet(2, &obj2); + ulx = obj2.getNum(); + obj2.free(); + obj1.arrayGet(3, &obj2); + uly = obj2.getNum(); + obj2.free(); + obj1.arrayGet(4, &obj2); + urx = obj2.getNum(); + obj2.free(); + obj1.arrayGet(5, &obj2); + ury = obj2.getNum(); + obj2.free(); + obj1.arrayGet(6, &obj2); + lrx = obj2.getNum(); + obj2.free(); + obj1.arrayGet(7, &obj2); + lry = obj2.getNum(); + obj2.free(); + 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:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g}\n", + tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); + obj2.free(); + } + obj1.free(); + + dict->lookup("Resolution", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 2) { + obj1.arrayGet(0, &obj2); + horiz = obj2.getNum(); + obj2.free(); + obj1.arrayGet(1, &obj2); + vert = obj2.getNum(); + obj2.free(); + writePSFmt("%ALDImageResoution: {0:.4g} {1:.4g}\n", horiz, vert); + obj2.free(); + } + obj1.free(); + + dict->lookup("Size", &obj1); + if (obj1.isArray() && obj1.arrayGetLength() == 2) { + obj1.arrayGet(0, &obj2); + width = obj2.getInt(); + obj2.free(); + obj1.arrayGet(1, &obj2); + height = obj2.getInt(); + obj2.free(); + writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height); + } + obj1.free(); + + //~ ignoring 'Tags' entry + //~ need to use writePSString() and deal with >255-char lines + + dict->lookup("Tint", &obj1); + if (obj1.isNum()) { + writePSFmt("%ALDImageTint: {0:.4g}\n", obj1.getNum()); + } + obj1.free(); + + dict->lookup("Transparency", &obj1); + if (obj1.isBool()) { + writePSFmt("%ALDImageTransparency: {0:s}\n", + obj1.getBool() ? "true" : "false"); + } + obj1.free(); + + 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) { + Object dict; + + if (globalParams->getPSOPI()) { + opiDict->lookup("2.0", &dict); + if (dict.isDict()) { + writePS("%%EndIncludedImage\n"); + writePS("%%EndOPI\n"); + writePS("grestore\n"); + --opi20Nest; + dict.free(); + } else { + dict.free(); + opiDict->lookup("1.3", &dict); + if (dict.isDict()) { + writePS("%%EndObject\n"); + writePS("restore\n"); + --opi13Nest; + } + dict.free(); + } + } +} + +GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) { + if (fileSpec->isString()) { + fileSpec->copy(fileName); + return gTrue; + } + if (fileSpec->isDict()) { + fileSpec->dictLookup("DOS", fileName); + if (fileName->isString()) { + return gTrue; + } + fileName->free(); + fileSpec->dictLookup("Mac", fileName); + if (fileName->isString()) { + return gTrue; + } + fileName->free(); + fileSpec->dictLookup("Unix", fileName); + if (fileName->isString()) { + return gTrue; + } + fileName->free(); + fileSpec->dictLookup("F", fileName); + if (fileName->isString()) { + return gTrue; + } + fileName->free(); + } + return gFalse; +} +#endif // OPI_SUPPORT + +void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { + writePSFmt("{0:.4g} {1:.4g} setcharwidth\n", wx, wy); + writePS("q\n"); + t3NeedsRestore = gTrue; +} + +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 GString(); + writePS("q\n"); + t3Cacheable = gTrue; + t3NeedsRestore = gTrue; +} + +void PSOutputDev::drawForm(Ref id) { + writePSFmt("f_{0:d}_{1:d}\n", id.num, id.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(Function *func) { + SampledFunction *func0; + ExponentialFunction *func2; + StitchingFunction *func3; + PostScriptFunction *func4; + int thisFunc, m, n, nSamples, i, j, k; + + switch (func->getType()) { + + case -1: // identity + writePS("{}\n"); + break; + + case 0: // sampled + func0 = (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:.4g}\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:.4g} sub {1:.4g} mul {2:.4g} 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:.4g} mul {1:.4g} add\n", + func0->getDecodeMax(i) - func0->getDecodeMin(i), + func0->getDecodeMin(i)); + writePSFmt("dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} 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); + break; + + case 2: // exponential + func2 = (ExponentialFunction *)func; + n = func2->getOutputSize(); + writePSFmt("{{ dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} 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:.4g} exp {2:.4g} mul {3:.4g} add\n", + i, func2->getE(), func2->getC1()[i] - func2->getC0()[i], + func2->getC0()[i]); + if (func2->getHasRange()) { + writePSFmt("dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} 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); + break; + + case 3: // stitching + func3 = (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:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} 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:.4g} lt {{ {1:.4g} sub {2:.4g} mul {3:.4g} 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:.4g} sub {1:.4g} mul {2:.4g} 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"); + } + writePS("}\n"); + break; + + case 4: // PostScript + func4 = (PostScriptFunction *)func; + writePS(func4->getCodeString()->getCString()); + writePS("\n"); + break; + } +} + +void PSOutputDev::writePSChar(char c) { + if (t3String) { + t3String->append(c); + } else { + (*outputFunc)(outputStream, &c, 1); + } +} + +void PSOutputDev::writePS(char *s) { + if (t3String) { + t3String->append(s); + } else { + (*outputFunc)(outputStream, s, strlen(s)); + } +} + +void PSOutputDev::writePSFmt(const char *fmt, ...) { + va_list args; + GString *buf; + + va_start(args, fmt); + if (t3String) { + t3String->appendfv((char *)fmt, args); + } else { + buf = GString::formatv((char *)fmt, args); + (*outputFunc)(outputStream, buf->getCString(), buf->getLength()); + delete buf; + } + va_end(args); +} + +void PSOutputDev::writePSString(GString *s) { + Guchar *p; + int n, line; + char buf[8]; + + writePSChar('('); + line = 1; + for (p = (Guchar *)s->getCString(), n = s->getLength(); 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(char *s) { + 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 == '%') { + writePSFmt("#{0:02x}", c & 0xff); + } else { + writePSChar(c); + } + } +} + +GString *PSOutputDev::filterPSName(GString *name) { + GString *name2; + char buf[8]; + int i; + char c; + + name2 = new GString(); + + // 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; +} + +// Write a DSC-compliant <textline>. +void PSOutputDev::writePSTextLine(GString *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"); +} |