//======================================================================== // // PSOutputDev.cc // // Copyright 1996-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #include "gmempp.h" #include "GString.h" #include "GList.h" #include "GHash.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 "PDFDoc.h" #include "XRef.h" #include "PreScanOutputDev.h" #include "CharCodeToUnicode.h" #include "AcroForm.h" #include "TextString.h" #if HAVE_SPLASH # include "Splash.h" # include "SplashBitmap.h" # include "SplashOutputDev.h" #endif #include "PSOutputDev.h" // the MSVC math.h doesn't define this #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // PostScript prolog and setup //------------------------------------------------------------------------ // The '~' escapes mark prolog code that is emitted only in certain // levels: // // ~[123][ngs] // ^ ^----- n=psLevel_, g=psLevel_Gray, s=psLevel_Sep // +----- 1=psLevel1__, 2=psLevel2__, 3=psLevel3__ static const char *prolog[] = { "/xpdf 75 dict def xpdf begin", "% PDF special state", "/pdfDictSize 15 def", "~1ns", "/pdfStates 64 array def", " 0 1 63 {", " pdfStates exch pdfDictSize dict", " dup /pdfStateIdx 3 index put", " put", " } for", "~123ngs", "/bdef { bind def } bind def", "/pdfSetup {", " /pdfDuplex exch def", " /setpagedevice where {", " pop 2 dict begin", " /Policies 1 dict dup begin /PageSize 6 def end def", " pdfDuplex { /Duplex true def } if", " currentdict end setpagedevice", " } if", " /pdfPageW 0 def", " /pdfPageH 0 def", "} def", "/pdfSetupPaper {", " 2 copy pdfPageH ne exch pdfPageW ne or {", " /pdfPageH exch def", " /pdfPageW exch def", " /setpagedevice where {", " pop 3 dict begin", " /PageSize [pdfPageW pdfPageH] def", " pdfDuplex { /Duplex true def } if", " /ImagingBBox null def", " currentdict end setpagedevice", " } if", " } {", " pop pop", " } ifelse", "} def", "~1ns", "/pdfOpNames [", " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender", " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", "] def", "~123ngs", "/pdfStartPage {", "~1ns", " pdfStates 0 get begin", "~23ngs", " 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", "~23g", " /pdfFill 0 def", " /pdfStroke 0 def", "~23ns", " /pdfFill [0] def", " /pdfStroke [0] def", " /pdfFillOP false def", " /pdfStrokeOP false def", "~123ngs", " /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", "~123ngs", "% 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 pdfFillOP setoverprint", " } if", " dup /pdfFill exch def aload pop pdfFillXform setcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/SC { pdfLastStroke not {", " pdfStrokeCS setcolorspace pdfStrokeOP setoverprint", " } 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", "~23g", "/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", "~23s", "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def } def", "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def } def", "/ck { 6 copy 6 array astore /pdfFill exch def", " findcmykcustomcolor exch setcustomcolor", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def } def", "/CK { 6 copy 6 array astore /pdfStroke exch def", " findcmykcustomcolor exch setcustomcolor", " pdfStrokeOP setoverprint", " /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", "~3ns", "/opm {", " /setoverprintmode where { pop setoverprintmode } { pop } ifelse", "} def", "~123ngs", "% 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", "~3ngs", "/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", "~123ngs", "% graphics state operators", "~1ns", "/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", "~23ngs", "/q { gsave pdfDictSize dict begin } def", "/Q {", " end grestore", "} def", "~123ngs", "/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 } bdef", "/W* { eoclip newpath } bdef", "/Ws { strokepath clip newpath } bdef", "% 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", "/xyshow where {", " pop", " /xyshow2 {", " dup length array", " 0 2 2 index length 1 sub {", " 2 index 1 index 2 copy get 3 1 roll 1 add get", " pdfTextMat dtransform", " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put", " } for", " exch pop", " xyshow", " } def", "}{", " /xyshow2 {", " currentfont /FontType get 0 eq {", " 0 2 3 index length 1 sub {", " currentpoint 4 index 3 index 2 getinterval show moveto", " 2 copy get 2 index 3 2 roll 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } {", " 0 1 3 index length 1 sub {", " currentpoint 4 index 3 index 1 getinterval show moveto", " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } ifelse", " pop pop", " } def", "} ifelse", "/cshow where {", " pop", " /xycp {", // xycharpath " 0 3 2 roll", " {", " pop pop currentpoint 3 2 roll", " 1 string dup 0 4 3 roll put false charpath moveto", " 2 copy get 2 index 2 index 1 add get", " pdfTextMat dtransform rmoveto", " 2 add", " } exch cshow", " pop pop", " } def", "}{", " /xycp {", // xycharpath " currentfont /FontType get 0 eq {", " 0 2 3 index length 1 sub {", " currentpoint 4 index 3 index 2 getinterval false charpath moveto", " 2 copy get 2 index 3 2 roll 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } {", " 0 1 3 index length 1 sub {", " currentpoint 4 index 3 index 1 getinterval false charpath moveto", " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } ifelse", " pop pop", " } def", "} ifelse", "/Tj {", " fCol", // because stringwidth has to draw Type 3 chars " 0 pdfTextRise pdfTextMat dtransform rmoveto", " currentpoint 4 2 roll", " pdfTextRender 1 and 0 eq {", " 2 copy xyshow2", " } if", " pdfTextRender 3 and dup 1 eq exch 2 eq or {", " 3 index 3 index moveto", " 2 copy", " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", " xycp currentpoint stroke moveto", " } if", " pdfTextRender 4 and 0 ne {", " 4 2 roll moveto xycp", " /pdfTextClipPath [ pdfTextClipPath aload pop", " {/moveto cvx}", " {/lineto cvx}", " {/curveto cvx}", " {/closepath cvx}", " pathforall ] def", " currentpoint newpath moveto", " } {", " pop pop pop pop", " } ifelse", " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", "} def", "/Tj3 {", " pdfTextRender 3 and 3 ne {" " fCol", // because stringwidth has to draw Type 3 chars " 0 pdfTextRise pdfTextMat dtransform rmoveto", " xyshow2", " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", " } {", " pop pop", " } ifelse", "} def", "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0", " pdfTextMat dtransform rmoveto } def", "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch", " pdfTextMat dtransform rmoveto } def", "/Tclip { pdfTextClipPath cvx exec clip newpath", " /pdfTextClipPath [] def } def", "~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", "/pdfImStr {", " 2 copy exch length lt {", " 2 copy get exch 1 add exch", " } {", " ()", " } ifelse", "} def", "/pdfImM1a {", " { pdfImStr } imagemask", " pop pop", "} def", "~23ngs", "% Level 2/3 image operators", "/pdfImBuf 100 string def", "/pdfImStr {", " 2 copy exch length lt {", " 2 copy get exch 1 add exch", " } {", " ()", " } ifelse", "} def", "/skipEOD {", " { currentfile pdfImBuf readline", " not { pop exit } if", " (%-EOD-) eq { exit } if } loop", "} def", "/pdfIm { image skipEOD } def", "~3ngs", "/pdfMask {", " /ReusableStreamDecode filter", " skipEOD", " /maskStream exch def", "} def", "/pdfMaskEnd { maskStream closefile } def", "/pdfMaskInit {", " /maskArray exch def", " /maskIdx 0 def", "} def", "/pdfMaskSrc {", " maskIdx maskArray length lt {", " maskArray maskIdx get", " /maskIdx maskIdx 1 add def", " } {", " ()", " } ifelse", "} def", "~23s", "/pdfImSep {", " findcmykcustomcolor exch", " dup /Width get /pdfImBuf1 exch string def", " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", " /pdfImDecodeLow exch def", " begin Width Height BitsPerComponent ImageMatrix DataSource end", " /pdfImData exch def", " { pdfImData pdfImBuf1 readstring pop", " 0 1 2 index length 1 sub {", " 1 index exch 2 copy get", " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", " 255 exch sub put", " } for }", " 6 5 roll customcolorimage", " skipEOD", "} def", "~23ngs", "/pdfImM { fCol imagemask skipEOD } def", "/pr {", " 4 2 roll exch 5 index div exch 4 index div moveto", " exch 3 index div dup 0 rlineto", " exch 2 index div 0 exch rlineto", " neg 0 rlineto", " closepath", "} def", "/pdfImClip { gsave clip } def", "/pdfImClipEnd { grestore } def", "~23ns", "% 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", "~23ns", " 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 2 lt {", " 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", "~23ns", " 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", "~23ns", " 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", "~123ngs", "end", NULL }; static const char *minLineWidthProlog[] = { "/pdfDist { dup dtransform dup mul exch dup mul add 0.5 mul sqrt } def", "/pdfIDist { dup idtransform dup mul exch dup mul add 0.5 mul sqrt } def", "/pdfMinLineDist pdfMinLineWidth pdfDist def", "/setlinewidth {", " dup pdfDist pdfMinLineDist lt {", " pop pdfMinLineDist pdfIDist", " } if", " setlinewidth", "} bind def", NULL }; static const char *cmapProlog[] = { "/CIDInit /ProcSet findresource begin", "10 dict begin", " begincmap", " /CMapType 1 def", " /CMapName /Identity-H def", " /CIDSystemInfo 3 dict dup begin", " /Registry (Adobe) def", " /Ordering (Identity) def", " /Supplement 0 def", " end def", " 1 begincodespacerange", " <0000> ", " endcodespacerange", " 0 usefont", " 1 begincidrange", " <0000> 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> ", " endcodespacerange", " 0 usefont", " 1 begincidrange", " <0000> 0", " endcidrange", " endcmap", " currentdict CMapName exch /CMap defineresource pop", "end", "end", NULL }; //------------------------------------------------------------------------ // Fonts //------------------------------------------------------------------------ struct PSSubstFont { const char *psName; // PostScript name double mWidth; // width of 'm' character }; // NB: must be in same order as base14SubstFonts in GfxFont.cc static PSSubstFont psBase14SubstFonts[14] = { {"Courier", 0.600}, {"Courier-Oblique", 0.600}, {"Courier-Bold", 0.600}, {"Courier-BoldOblique", 0.600}, {"Helvetica", 0.833}, {"Helvetica-Oblique", 0.833}, {"Helvetica-Bold", 0.889}, {"Helvetica-BoldOblique", 0.889}, {"Times-Roman", 0.788}, {"Times-Italic", 0.722}, {"Times-Bold", 0.833}, {"Times-BoldItalic", 0.778}, // the last two are never used for substitution {"Symbol", 0}, {"ZapfDingbats", 0} }; class PSFontInfo { public: PSFontInfo(Ref fontIDA) { fontID = fontIDA; ff = NULL; } Ref fontID; PSFontFileInfo *ff; // pointer to font file info; NULL indicates // font mapping failed }; enum PSFontFileLocation { psFontFileResident, psFontFileEmbedded, psFontFileExternal }; class PSFontFileInfo { public: PSFontFileInfo(GString *psNameA, GfxFontType typeA, PSFontFileLocation locA); ~PSFontFileInfo(); GString *psName; // name under which font is defined GfxFontType type; // font type PSFontFileLocation loc; // font location Ref embFontID; // object ID for the embedded font file // (for all embedded fonts) GString *extFileName; // external font file path // (for all external fonts) GString *encoding; // encoding name (for resident CID fonts) int *codeToGID; // mapping from code/CID to GID // (for TrueType, OpenType-TrueType, and // CID OpenType-CFF fonts) int codeToGIDLen; // length of codeToGID array }; PSFontFileInfo::PSFontFileInfo(GString *psNameA, GfxFontType typeA, PSFontFileLocation locA) { psName = psNameA; type = typeA; loc = locA; embFontID.num = embFontID.gen = -1; extFileName = NULL; encoding = NULL; codeToGID = NULL; codeToGIDLen = 0; } PSFontFileInfo::~PSFontFileInfo() { delete psName; if (extFileName) { delete extFileName; } if (encoding) { delete encoding; } if (codeToGID) { gfree(codeToGID); } } //------------------------------------------------------------------------ // 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; }; //------------------------------------------------------------------------ struct PSOutPaperSize { PSOutPaperSize(int wA, int hA) { w = wA; h = hA; } int w, h; }; //------------------------------------------------------------------------ // DeviceNRecoder //------------------------------------------------------------------------ class DeviceNRecoder: public FilterStream { public: DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA); virtual ~DeviceNRecoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); 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, const char *indent, GBool okToReadStream) { 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 (str->isEncoder()) { delete str; } } Stream *DeviceNRecoder::copy() { error(errInternal, -1, "Called copy() on DeviceNRecoder"); return NULL; } void DeviceNRecoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } void DeviceNRecoder::close() { delete imgStr; imgStr = NULL; str->close(); } 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; } //------------------------------------------------------------------------ // GrayRecoder //------------------------------------------------------------------------ class GrayRecoder: public FilterStream { public: GrayRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA); virtual ~GrayRecoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); virtual int getChar() { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx++]; } virtual int lookChar() { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx]; } virtual GString *getPSFilter(int psLevel, const char *indent, GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: GBool fillBuf(); int width, height; GfxImageColorMap *colorMap; ImageStream *imgStr; Guchar *buf; int bufIdx; }; GrayRecoder::GrayRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA): FilterStream(strA) { width = widthA; height = heightA; colorMap = colorMapA; imgStr = NULL; buf = (Guchar *)gmalloc(width); bufIdx = width; } GrayRecoder::~GrayRecoder() { gfree(buf); if (str->isEncoder()) { delete str; } } Stream *GrayRecoder::copy() { error(errInternal, -1, "Called copy() on GrayRecoder"); return NULL; } void GrayRecoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } void GrayRecoder::close() { delete imgStr; imgStr = NULL; str->close(); } GBool GrayRecoder::fillBuf() { Guchar *line; if (!(line = imgStr->getLine())) { bufIdx = width; return gFalse; } //~ this should probably use the rendering intent from the image //~ dict, or from the content stream colorMap->getGrayByteLine(line, buf, width, gfxRenderingIntentRelativeColorimetric); bufIdx = 0; return gTrue; } //------------------------------------------------------------------------ // ColorKeyToMaskEncoder //------------------------------------------------------------------------ class ColorKeyToMaskEncoder: public FilterStream { public: ColorKeyToMaskEncoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA, int *maskColorsA); virtual ~ColorKeyToMaskEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); 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, const char *indent, GBool okToReadStream) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: GBool fillBuf(); int width, height; GfxImageColorMap *colorMap; int numComps; int *maskColors; ImageStream *imgStr; Guchar *buf; int bufIdx; int bufSize; }; ColorKeyToMaskEncoder::ColorKeyToMaskEncoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA, int *maskColorsA): FilterStream(strA) { width = widthA; height = heightA; colorMap = colorMapA; numComps = colorMap->getNumPixelComps(); maskColors = maskColorsA; imgStr = NULL; bufSize = (width + 7) / 8; buf = (Guchar *)gmalloc(bufSize); bufIdx = width; } ColorKeyToMaskEncoder::~ColorKeyToMaskEncoder() { gfree(buf); if (str->isEncoder()) { delete str; } } Stream *ColorKeyToMaskEncoder::copy() { error(errInternal, -1, "Called copy() on ColorKeyToMaskEncoder"); return NULL; } void ColorKeyToMaskEncoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } void ColorKeyToMaskEncoder::close() { delete imgStr; imgStr = NULL; str->close(); } GBool ColorKeyToMaskEncoder::fillBuf() { Guchar *line, *linePtr, *bufPtr; Guchar byte; int x, xx, i; if (!(line = imgStr->getLine())) { bufIdx = width; return gFalse; } linePtr = line; bufPtr = buf; for (x = 0; x < width; x += 8) { byte = 0; for (xx = 0; xx < 8; ++xx) { byte = (Guchar)(byte << 1); if (x + xx < width) { for (i = 0; i < numComps; ++i) { if (linePtr[i] < maskColors[2 * i] || linePtr[i] > maskColors[2 * i + 1]) { break; } } if (i >= numComps) { byte |= 1; } linePtr += numComps; } else { byte |= 1; } } *bufPtr++ = byte; } bufIdx = 0; return gTrue; } //------------------------------------------------------------------------ // PSOutputDev //------------------------------------------------------------------------ extern "C" { typedef void (*SignalFunc)(int); } static void outputToFile(void *stream, const char *data, int len) { fwrite(data, 1, len, (FILE *)stream); } PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, GBool honorUserUnitA, GBool fileNameIsUTF8) { FILE *f; PSFileType fileTypeA; underlayCbk = NULL; underlayCbkData = NULL; overlayCbk = NULL; overlayCbkData = NULL; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; rasterizePage = NULL; fontInfo = new GList(); fontFileInfo = new GHash(); imgIDs = NULL; formIDs = NULL; visitedResources = NULL; saveStack = NULL; paperSizes = 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(errIO, -1, "Couldn't run print command '{0:s}'", fileName); ok = gFalse; return; } #else error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName); ok = gFalse; return; #endif } else { fileTypeA = psFile; if (fileNameIsUTF8) { f = openFile(fileName, "w"); } else { f = fopen(fileName, "w"); } if (!f) { error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); ok = gFalse; return; } } init(outputToFile, f, fileTypeA, docA, firstPageA, lastPageA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA); } PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, GBool honorUserUnitA) { underlayCbk = NULL; underlayCbkData = NULL; overlayCbk = NULL; overlayCbkData = NULL; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; rasterizePage = NULL; fontInfo = new GList(); fontFileInfo = new GHash(); imgIDs = NULL; formIDs = NULL; visitedResources = NULL; saveStack = NULL; paperSizes = NULL; embFontList = NULL; customColors = NULL; haveTextClip = gFalse; t3String = NULL; init(outputFuncA, outputStreamA, psGeneric, docA, firstPageA, lastPageA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA); } void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, PSFileType fileTypeA, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, GBool honorUserUnitA) { Catalog *catalog; Page *page; PDFRectangle *box; PSOutPaperSize *size; PSFontFileInfo *ff; GList *names; double userUnit; int pg, w, h, i; // initialize ok = gTrue; outputFunc = outputFuncA; outputStream = outputStreamA; fileType = fileTypeA; doc = docA; xref = doc->getXRef(); catalog = doc->getCatalog(); if ((firstPage = firstPageA) < 1) { firstPage = 1; } if ((lastPage = lastPageA) > doc->getNumPages()) { lastPage = doc->getNumPages(); } level = globalParams->getPSLevel(); mode = modeA; honorUserUnit = honorUserUnitA; 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) { paperMatch = gTrue; paperSizes = new GList(); paperWidth = paperHeight = 1; // in case the document has zero pages for (pg = firstPage; pg <= lastPage; ++pg) { page = catalog->getPage(pg); if (honorUserUnit) { userUnit = page->getUserUnit(); } else { userUnit = 1; } if (globalParams->getPSUseCropBoxAsPage()) { w = (int)ceil(page->getCropWidth() * userUnit); h = (int)ceil(page->getCropHeight() * userUnit); } else { w = (int)ceil(page->getMediaWidth() * userUnit); h = (int)ceil(page->getMediaHeight() * userUnit); } for (i = 0; i < paperSizes->getLength(); ++i) { size = (PSOutPaperSize *)paperSizes->get(i); if (size->w == w && size->h == h) { break; } } if (i == paperSizes->getLength()) { paperSizes->append(new PSOutPaperSize(w, h)); } if (w > paperWidth) { paperWidth = w; } if (h > paperHeight) { paperHeight = h; } } // NB: img{LLX,LLY,URX,URY} will be set by startPage() } else { paperMatch = gFalse; } 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; expandSmallPages = globalParams->getPSExpandSmaller(); // initialize font lists, etc. for (i = 0; i < 14; ++i) { ff = new PSFontFileInfo(new GString(psBase14SubstFonts[i].psName), fontType1, psFontFileResident); fontFileInfo->add(ff->psName, ff); } names = globalParams->getPSResidentFonts(); for (i = 0; i < names->getLength(); ++i) { if (!fontFileInfo->lookup((GString *)names->get(i))) { ff = new PSFontFileInfo((GString *)names->get(i), fontType1, psFontFileResident); fontFileInfo->add(ff->psName, ff); } } delete names; imgIDLen = 0; imgIDSize = 0; formIDLen = 0; formIDSize = 0; noStateChanges = gFalse; saveStack = new GList(); 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 <= catalog->getNumPages()) { writeHeader(catalog->getPage(firstPage)->getMediaBox(), catalog->getPage(firstPage)->getCropBox(), catalog->getPage(firstPage)->getRotate()); } else { box = new PDFRectangle(0, 0, 1, 1); writeHeader(box, box, 0); delete box; } if (mode != psModeForm) { writePS("%%BeginProlog\n"); } writeXpdfProcset(); if (mode != psModeForm) { writePS("%%EndProlog\n"); writePS("%%BeginSetup\n"); } writeDocSetup(catalog); if (mode != psModeForm) { writePS("%%EndSetup\n"); } } // initialize sequential page number seqPage = 1; } PSOutputDev::~PSOutputDev() { PSOutCustomColor *cc; if (ok) { if (!manualCtrl) { writePS("%%Trailer\n"); writeTrailer(); if (mode != psModeForm) { writePS("%%EOF\n"); } } if (fileType == psFile) { fclose((FILE *)outputStream); } #ifdef HAVE_POPEN else if (fileType == psPipe) { pclose((FILE *)outputStream); #ifndef _WIN32 signal(SIGPIPE, (SignalFunc)SIG_DFL); #endif } #endif } gfree(rasterizePage); if (paperSizes) { deleteGList(paperSizes, PSOutPaperSize); } if (embFontList) { delete embFontList; } deleteGList(fontInfo, PSFontInfo); deleteGHash(fontFileInfo, PSFontFileInfo); gfree(imgIDs); gfree(formIDs); if (saveStack) { delete saveStack; } while (customColors) { cc = customColors; customColors = cc->next; delete cc; } if (t3String) { // this will only happen if the 'd1' operator is used outside of a // Type 3 CharProc delete t3String; } } GBool PSOutputDev::checkIO() { if (fileType == psFile || fileType == psPipe || fileType == psStdout) { if (ferror((FILE *)outputStream)) { error(errIO, -1, "Error writing to PostScript file"); return gFalse; } } return gTrue; } void PSOutputDev::writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox, int pageRotate) { Object info, obj1; PSOutPaperSize *size; double x1, y1, x2, y2; int i; 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("%XpdfVersion: {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 >= psLevel3 ? 3 : level >= psLevel2 ? 2 : 1); if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { writePS("%%DocumentProcessColors: (atend)\n"); writePS("%%DocumentCustomColors: (atend)\n"); } writePS("%%DocumentSuppliedResources: (atend)\n"); switch (mode) { case psModePS: if (paperMatch) { for (i = 0; i < paperSizes->getLength(); ++i) { size = (PSOutPaperSize *)paperSizes->get(i); writePSFmt("%%{0:s} {1:d}x{2:d} {1:d} {2:d} 0 () ()\n", i==0 ? "DocumentMedia:" : "+", size->w, size->h); } } else { 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"); if (!paperMatch) { 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:.6g} {1:.6g} {2:.6g} {3:.6g}\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, nonSep, gray, sep; const char **p; const char *q; double w; writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion); writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright); lev1 = lev2 = lev3 = nonSep = gray = sep = gTrue; for (p = prolog; *p; ++p) { if ((*p)[0] == '~') { lev1 = lev2 = lev3 = nonSep = gray = sep = 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 'g': gray = gTrue; break; case 'n': nonSep = gTrue; break; case 's': sep = gTrue; break; } } } else if ((level == psLevel1 && lev1 && nonSep) || (level == psLevel1Sep && lev1 && sep) || (level == psLevel2 && lev2 && nonSep) || (level == psLevel2Gray && lev2 && gray) || (level == psLevel2Sep && lev2 && sep) || (level == psLevel3 && lev3 && nonSep) || (level == psLevel3Gray && lev3 && gray) || (level == psLevel3Sep && lev3 && sep)) { writePSFmt("{0:s}\n", *p); } } if ((w = globalParams->getPSMinLineWidth()) > 0) { writePSFmt("/pdfMinLineWidth {0:.4g} def\n", w); for (p = minLineWidthProlog; *p; ++p) { 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) { Page *page; Dict *resDict; Annots *annots; AcroForm *form; Object obj1, obj2, obj3; GString *s; GBool needDefaultFont; int pg, i, j; // check to see which pages will be rasterized if (firstPage <= lastPage) { rasterizePage = (char *)gmalloc(lastPage - firstPage + 1); for (pg = firstPage; pg <= lastPage; ++pg) { rasterizePage[pg - firstPage] = (char)checkIfPageNeedsToBeRasterized(pg); } } else { rasterizePage = NULL; } visitedResources = (char *)gmalloc(xref->getNumObjects()); memset(visitedResources, 0, xref->getNumObjects()); if (mode == psModeForm) { // swap the form and xpdf dicts writePS("xpdf end begin dup begin\n"); } else { writePS("xpdf begin\n"); } needDefaultFont = gFalse; for (pg = firstPage; pg <= lastPage; ++pg) { if (rasterizePage[pg - firstPage]) { continue; } page = catalog->getPage(pg); if ((resDict = page->getResourceDict())) { setupResources(resDict); } annots = new Annots(doc, page->getAnnots(&obj1)); obj1.free(); if (annots->getNumAnnots()) { needDefaultFont = gTrue; } 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 ((form = catalog->getForm())) { if (form->getNumFields() > 0) { needDefaultFont = gTrue; } for (i = 0; i < form->getNumFields(); ++i) { form->getField(i)->getResources(&obj1); if (obj1.isArray()) { for (j = 0; j < obj1.arrayGetLength(); ++j) { obj1.arrayGet(j, &obj2); if (obj2.isDict()) { setupResources(obj2.getDict()); } obj2.free(); } } else if (obj1.isDict()) { setupResources(obj1.getDict()); } obj1.free(); } } if (needDefaultFont) { setupDefaultFont(); } if (mode != psModeForm) { if (mode != psModeEPS && !manualCtrl) { writePSFmt("{0:s} pdfSetup\n", globalParams->getPSDuplex() ? "true" : "false"); if (!paperMatch) { writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight); } } #if OPI_SUPPORT if (globalParams->getPSOPI()) { writePS("/opiMatrix matrix currentmatrix def\n"); } #endif } if (customCodeCbk) { if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, customCodeCbkData))) { writePS(s->getCString()); delete s; } } if (mode != psModeForm) { writePS("end\n"); } gfree(visitedResources); visitedResources = NULL; } 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("%%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) { writePS(" "); writePSString(cc->name); } writePS("\n"); writePS("%%CMYKCustomColor:\n"); for (cc = customColors; cc; cc = cc->next) { writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", cc->c, cc->m, cc->y, cc->k); writePSString(cc->name); writePS("\n"); } } } } GBool PSOutputDev::checkIfPageNeedsToBeRasterized(int pg) { PreScanOutputDev *scan; GBool rasterize; if (globalParams->getPSAlwaysRasterize()) { rasterize = gTrue; } else { scan = new PreScanOutputDev(); //~ this could depend on the printing flag, e.g., if an annotation //~ uses transparency --> need to pass the printing flag into //~ constructor, init, writeDocSetup doc->getCatalog()->getPage(pg)->display(scan, 72, 72, 0, gTrue, gTrue, gTrue); rasterize = scan->usesTransparency() || scan->usesPatternImageMask(); delete scan; if (rasterize && globalParams->getPSNeverRasterize()) { error(errSyntaxWarning, -1, "PDF page uses transparency and the psNeverRasterize option is " "set - output may not be correct"); rasterize = gFalse; } } return rasterize; } void PSOutputDev::setupResources(Dict *resDict) { Object xObjDict, xObjRef, xObj, patDict, patRef, pat; Object gsDict, gsRef, gs, smask, smaskGroup, resObj; Ref ref0; GBool skip; int i; setupFonts(resDict); setupImages(resDict); //----- recursively scan XObjects resDict->lookup("XObject", &xObjDict); if (xObjDict.isDict()) { for (i = 0; i < xObjDict.dictGetLength(); ++i) { // check for an already-visited XObject skip = gFalse; if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) { ref0 = xObjRef.getRef(); if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { skip = gTrue; } else { skip = (GBool)visitedResources[ref0.num]; visitedResources[ref0.num] = 1; } } 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(); } xObjRef.free(); } } xObjDict.free(); //----- recursively scan Patterns resDict->lookup("Pattern", &patDict); if (patDict.isDict()) { inType3Char = gTrue; for (i = 0; i < patDict.dictGetLength(); ++i) { // check for an already-visited Pattern skip = gFalse; if ((patDict.dictGetValNF(i, &patRef)->isRef())) { ref0 = patRef.getRef(); if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { skip = gTrue; } else { skip = (GBool)visitedResources[ref0.num]; visitedResources[ref0.num] = 1; } } 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(); } patRef.free(); } inType3Char = gFalse; } patDict.free(); //----- recursively scan SMask transparency groups in ExtGState dicts resDict->lookup("ExtGState", &gsDict); if (gsDict.isDict()) { for (i = 0; i < gsDict.dictGetLength(); ++i) { // check for an already-visited ExtGState skip = gFalse; if ((gsDict.dictGetValNF(i, &gsRef)->isRef())) { ref0 = gsRef.getRef(); if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { skip = gTrue; } else { skip = (GBool)visitedResources[ref0.num]; visitedResources[ref0.num] = 1; } } if (!skip) { // process the ExtGState's SMask's transparency group's resource dict if (gsDict.dictGetVal(i, &gs)->isDict()) { if (gs.dictLookup("SMask", &smask)->isDict()) { if (smask.dictLookup("G", &smaskGroup)->isStream()) { smaskGroup.streamGetDict()->lookup("Resources", &resObj); if (resObj.isDict()) { setupResources(resObj.getDict()); } resObj.free(); } smaskGroup.free(); } smask.free(); } gs.free(); } gsRef.free(); } } gsDict.free(); setupForms(resDict); } 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) { PSFontInfo *fi; GfxFontLoc *fontLoc; GBool subst; char buf[16]; UnicodeMap *uMap; char *charName; double xs, ys; int code; double w1, w2; int i, j; // check if font is already set up for (i = 0; i < fontInfo->getLength(); ++i) { fi = (PSFontInfo *)fontInfo->get(i); if (fi->fontID.num == font->getID()->num && fi->fontID.gen == font->getID()->gen) { return; } } // add fontInfo entry fi = new PSFontInfo(*font->getID()); fontInfo->append(fi); xs = ys = 1; subst = gFalse; if (font->getType() == fontType3) { fi->ff = setupType3Font(font, parentResDict); } else { if ((fontLoc = font->locateFont(xref, gTrue))) { switch (fontLoc->locType) { case gfxFontLocEmbedded: switch (fontLoc->fontType) { case fontType1: fi->ff = setupEmbeddedType1Font(font, &fontLoc->embFontID); break; case fontType1C: fi->ff = setupEmbeddedType1CFont(font, &fontLoc->embFontID); break; case fontType1COT: fi->ff = setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID); break; case fontTrueType: case fontTrueTypeOT: fi->ff = setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID); break; case fontCIDType0C: fi->ff = setupEmbeddedCIDType0Font(font, &fontLoc->embFontID); break; case fontCIDType2: case fontCIDType2OT: //~ should check to see if font actually uses vertical mode fi->ff = setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, gTrue); break; case fontCIDType0COT: fi->ff = setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID); break; default: break; } break; case gfxFontLocExternal: //~ add cases for other external 16-bit fonts switch (fontLoc->fontType) { case fontType1: fi->ff = setupExternalType1Font(font, fontLoc->path); break; case fontTrueType: case fontTrueTypeOT: fi->ff = setupExternalTrueTypeFont(font, fontLoc->path, fontLoc->fontNum); break; case fontCIDType2: case fontCIDType2OT: //~ should check to see if font actually uses vertical mode fi->ff = setupExternalCIDTrueTypeFont(font, fontLoc->path, fontLoc->fontNum, gTrue); break; case fontCIDType0COT: fi->ff = setupExternalOpenTypeCFFFont(font, fontLoc->path); break; default: break; } break; case gfxFontLocResident: if (!(fi->ff = (PSFontFileInfo *)fontFileInfo->lookup(fontLoc->path))) { // handle psFontPassthrough fi->ff = new PSFontFileInfo(fontLoc->path->copy(), fontLoc->fontType, psFontFileResident); fontFileInfo->add(fi->ff->psName, fi->ff); } break; } } if (!fi->ff) { if (font->isCIDFont()) { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}' ('{1:s}' character collection)", font->getName() ? font->getName()->getCString() : "(unnamed)", ((GfxCIDFont *)font)->getCollection() ? ((GfxCIDFont *)font)->getCollection()->getCString() : "(unknown)"); } else { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); } delete fontLoc; return; } // scale substituted 8-bit fonts if (fontLoc->locType == gfxFontLocResident && fontLoc->substIdx >= 0) { subst = gTrue; 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((Guchar)code); } else { w1 = 0; } w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth; xs = w1 / w2; if (xs < 0.1) { xs = 1; } } // handle encodings for substituted CID fonts if (fontLoc->locType == gfxFontLocResident && fontLoc->fontType >= fontCIDType0) { subst = gTrue; if (!fi->ff->encoding) { if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { fi->ff->encoding = fontLoc->encoding->copy(); uMap->decRefCnt(); } else { error(errSyntaxError, -1, "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", fontLoc->encoding); } } } delete fontLoc; } // generate PostScript code to set up the font if (font->isCIDFont()) { if (level >= psLevel3) { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", font->getID()->num, font->getID()->gen, fi->ff->psName, font->getWMode()); } else { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", font->getID()->num, font->getID()->gen, fi->ff->psName, font->getWMode()); } } else { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n", font->getID()->num, font->getID()->gen, fi->ff->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()) { snprintf(buf, sizeof(buf), "c%02x", i+j); charName = buf; } else { charName = ((Gfx8BitFont *)font)->getCharName(i+j); } writePS("/"); writePSName(charName ? charName : (char *)".notdef"); // the empty name is legal in PDF and PostScript, but PostScript // uses a double-slash (//...) for "immediately evaluated names", // so we need to add a space character here if (charName && !charName[0]) { writePS(" "); } } writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n"); } writePS("pdfMakeFont\n"); } } PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) { GString *psName, *origFont, *cleanFont; PSFontFileInfo *ff; Object refObj, strObj, obj1, obj2; Dict *dict; char buf[4096]; GBool rename; int length1, length2, n; // check if font is already embedded if (!font->getEmbeddedFontName()) { rename = gTrue; } else if ((ff = (PSFontFileInfo *) fontFileInfo->lookup(font->getEmbeddedFontName()))) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { return ff; } rename = gTrue; } else { rename = gFalse; } // generate name // (this assumes that the PS font name matches the PDF font name) if (rename) { psName = makePSFontName(font, id); } else { psName = font->getEmbeddedFontName()->copy(); } // get the font stream and info refObj.initRef(id->num, id->gen); refObj.fetch(xref, &strObj); refObj.free(); if (!strObj.isStream()) { error(errSyntaxError, -1, "Embedded font file object is not a stream"); goto err1; } if (!(dict = strObj.streamGetDict())) { error(errSyntaxError, -1, "Embedded font stream is missing its dictionary"); goto err1; } dict->lookup("Length1", &obj1); dict->lookup("Length2", &obj2); if (!obj1.isInt() || !obj2.isInt()) { error(errSyntaxError, -1, "Missing length fields in embedded font stream dictionary"); obj1.free(); obj2.free(); goto err1; } length1 = obj1.getInt(); length2 = obj2.getInt(); obj1.free(); obj2.free(); // read the font file origFont = new GString(); strObj.streamReset(); while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) { origFont->append(buf, n); } strObj.streamClose(); strObj.free(); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // clean up the font file cleanFont = fixType1Font(origFont, length1, length2); if (rename) { renameType1Font(cleanFont, psName); } writePSBlock(cleanFont->getCString(), cleanFont->getLength()); delete cleanFont; delete origFont; // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; err1: strObj.free(); delete psName; return NULL; } PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font, GString *fileName) { static char hexChar[17] = "0123456789abcdef"; GString *psName; PSFontFileInfo *ff; FILE *fontFile; int buf[6]; int c, n, i; if (font->getName()) { // check if font is already embedded if ((ff = (PSFontFileInfo *)fontFileInfo->lookup(font->getName()))) { return ff; } // this assumes that the PS font name matches the PDF font name psName = font->getName()->copy(); } else { // generate name //~ this won't work -- the PS font name won't match psName = makePSFontName(font, font->getID()); } // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // open the font file if (!(fontFile = fopen(fileName->getCString(), "rb"))) { error(errIO, -1, "Couldn't open external font file"); delete psName; return NULL; } // check for PFB format buf[0] = fgetc(fontFile); buf[1] = fgetc(fontFile); if (buf[0] == 0x80 && buf[1] == 0x01) { while (1) { for (i = 2; i < 6; ++i) { buf[i] = fgetc(fontFile); } if (buf[2] == EOF || buf[3] == EOF || buf[4] == EOF || buf[5] == EOF) { break; } n = buf[2] + (buf[3] << 8) + (buf[4] << 16) + (buf[5] << 24); if (buf[1] == 0x01) { for (i = 0; i < n; ++i) { if ((c = fgetc(fontFile)) == EOF) { break; } writePSChar((char)c); } } else { for (i = 0; i < n; ++i) { if ((c = fgetc(fontFile)) == EOF) { break; } writePSChar(hexChar[(c >> 4) & 0x0f]); writePSChar(hexChar[c & 0x0f]); if (i % 32 == 31) { writePSChar('\n'); } } } buf[0] = fgetc(fontFile); buf[1] = fgetc(fontFile); if (buf[0] == EOF || buf[1] == EOF || (buf[0] == 0x80 && buf[1] == 0x03)) { break; } else if (!(buf[0] == 0x80 && (buf[1] == 0x01 || buf[1] == 0x02))) { error(errSyntaxError, -1, "Invalid PFB header in external font file"); break; } } writePSChar('\n'); // plain text (PFA) format } else { writePSChar((char)buf[0]); writePSChar((char)buf[1]); while ((c = fgetc(fontFile)) != EOF) { writePSChar((char)c); } } fclose(fontFile); // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); ff->extFileName = fileName->copy(); fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiType1C *ffT1C; GHashIter *iter; // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, 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 if ((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"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; GHashIter *iter; // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, 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 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) { if (ffTT->isOpenTypeCFF()) { ffTT->convertToType1(psName->getCString(), NULL, gTrue, outputFunc, outputStream); } delete ffTT; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; int *codeToGID; GHashIter *iter; // get the code-to-GID mapping if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) { return NULL; } if (!(ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) { gfree(fontBuf); return NULL; } codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->type == font->getType() && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen && ff->codeToGIDLen == 256 && !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) { fontFileInfo->killIter(&iter); gfree(codeToGID); delete ffTT; gfree(fontBuf); return ff; } } // generate name psName = makePSFontName(font, 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 ffTT->convertToType42(psName->getCString(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : (char **)NULL, codeToGID, outputFunc, outputStream); delete ffTT; gfree(fontBuf); // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; ff->codeToGID = codeToGID; ff->codeToGIDLen = 256; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName, int fontNum) { GString *psName; PSFontFileInfo *ff; FoFiTrueType *ffTT; int *codeToGID; GHashIter *iter; // get the code-to-GID mapping if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) { return NULL; } codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileExternal && ff->type == font->getType() && !ff->extFileName->cmp(fileName) && ff->codeToGIDLen == 256 && !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) { fontFileInfo->killIter(&iter); gfree(codeToGID); delete ffTT; return ff; } } // generate name psName = makePSFontName(font, font->getID()); // 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 ffTT->convertToType42(psName->getCString(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : (char **)NULL, codeToGID, outputFunc, outputStream); delete ffTT; // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); ff->extFileName = fileName->copy(); ff->codeToGID = codeToGID; ff->codeToGIDLen = 256; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiType1C *ffT1C; GHashIter *iter; // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, 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 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffT1C->convertToCIDType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffT1C->convertToType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } delete ffT1C; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedCIDTrueTypeFont( GfxFont *font, Ref *id, GBool needVerticalMetrics) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; int *codeToGID; int codeToGIDLen; GHashIter *iter; // get the code-to-GID mapping codeToGID = ((GfxCIDFont *)font)->getCIDToGID(); codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen(); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->type == font->getType() && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen && ff->codeToGIDLen == codeToGIDLen && ((!ff->codeToGID && !codeToGID) || (ff->codeToGID && codeToGID && !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))))) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, 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 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) { if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType2(psName->getCString(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); } delete ffTT; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; if (codeToGIDLen) { ff->codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); memcpy(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int)); ff->codeToGIDLen = codeToGIDLen; } fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupExternalCIDTrueTypeFont( GfxFont *font, GString *fileName, int fontNum, GBool needVerticalMetrics) { GString *psName; PSFontFileInfo *ff; FoFiTrueType *ffTT; int *codeToGID; int codeToGIDLen; CharCodeToUnicode *ctu; Unicode uBuf[8]; int cmap, cmapPlatform, cmapEncoding, code; GHashIter *iter; // create a code-to-GID mapping, via Unicode if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) { return NULL; } if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) { error(errSyntaxError, -1, "Couldn't find a mapping to Unicode for font '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); delete ffTT; return NULL; } // look for a Unicode cmap for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) { cmapPlatform = ffTT->getCmapPlatform(cmap); cmapEncoding = ffTT->getCmapEncoding(cmap); if ((cmapPlatform == 3 && cmapEncoding == 1) || (cmapPlatform == 0 && cmapEncoding <= 4)) { break; } } if (cmap >= ffTT->getNumCmaps()) { error(errSyntaxError, -1, "Couldn't find a Unicode cmap in font '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); ctu->decRefCnt(); delete ffTT; return NULL; } // map CID -> Unicode -> GID if (ctu->isIdentity()) { codeToGIDLen = 65536; } else { codeToGIDLen = ctu->getLength(); } codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); for (code = 0; code < codeToGIDLen; ++code) { if (ctu->mapToUnicode(code, uBuf, 8) > 0) { codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]); } else { codeToGID[code] = 0; } } ctu->decRefCnt(); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileExternal && ff->type == font->getType() && !ff->extFileName->cmp(fileName) && ff->codeToGIDLen == codeToGIDLen && ff->codeToGID && !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) { fontFileInfo->killIter(&iter); gfree(codeToGID); delete ffTT; return ff; } } // check for embedding permission if (ffTT->getEmbeddingRights() < 1) { error(errSyntaxError, -1, "TrueType font '{0:s}' does not allow embedding", font->getName() ? font->getName()->getCString() : "(unnamed)"); gfree(codeToGID); delete ffTT; return NULL; } // generate name psName = makePSFontName(font, font->getID()); // 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 //~ this should use fontNum to load the correct font if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType2(psName->getCString(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); } delete ffTT; // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); ff->extFileName = fileName->copy(); ff->codeToGID = codeToGID; ff->codeToGIDLen = codeToGIDLen; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; GHashIter *iter; int n; // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, 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 if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) { if (ffTT->isOpenTypeCFF()) { if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } } delete ffTT; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; if ((n = ((GfxCIDFont *)font)->getCIDToGIDLen())) { ff->codeToGID = (int *)gmallocn(n, sizeof(int)); memcpy(ff->codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(int)); ff->codeToGIDLen = n; } fontFileInfo->add(ff->psName, ff); return ff; } // This assumes an OpenType CFF font that has a Unicode cmap (in the // OpenType section), and a CFF blob that uses an identity CID-to-GID // mapping. PSFontFileInfo *PSOutputDev::setupExternalOpenTypeCFFFont(GfxFont *font, GString *fileName) { GString *psName; PSFontFileInfo *ff; FoFiTrueType *ffTT; GHashIter *iter; CharCodeToUnicode *ctu; Unicode uBuf[8]; int *codeToGID; int codeToGIDLen; int cmap, cmapPlatform, cmapEncoding, code; // create a code-to-GID mapping, via Unicode if (!(ffTT = FoFiTrueType::load(fileName->getCString(), 0, gTrue))) { return NULL; } if (!ffTT->isOpenTypeCFF()) { delete ffTT; return NULL; } if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) { error(errSyntaxError, -1, "Couldn't find a mapping to Unicode for font '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); delete ffTT; return NULL; } // look for a Unicode cmap for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) { cmapPlatform = ffTT->getCmapPlatform(cmap); cmapEncoding = ffTT->getCmapEncoding(cmap); if ((cmapPlatform == 3 && cmapEncoding == 1) || (cmapPlatform == 0 && cmapEncoding <= 4)) { break; } } if (cmap >= ffTT->getNumCmaps()) { error(errSyntaxError, -1, "Couldn't find a Unicode cmap in font '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); ctu->decRefCnt(); delete ffTT; return NULL; } // map CID -> Unicode -> GID if (ctu->isIdentity()) { codeToGIDLen = 65536; } else { codeToGIDLen = ctu->getLength(); } codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); for (code = 0; code < codeToGIDLen; ++code) { if (ctu->mapToUnicode(code, uBuf, 8) > 0) { codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]); } else { codeToGID[code] = 0; } } ctu->decRefCnt(); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileExternal && ff->type == font->getType() && !ff->extFileName->cmp(fileName) && ff->codeToGIDLen == codeToGIDLen && ff->codeToGID && !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) { fontFileInfo->killIter(&iter); gfree(codeToGID); delete ffTT; return ff; } } // generate name psName = makePSFontName(font, font->getID()); // 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 if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType0(psName->getCString(), codeToGID, codeToGIDLen, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), codeToGID, codeToGIDLen, outputFunc, outputStream); } delete ffTT; // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); ff->extFileName = fileName->copy(); ff->codeToGID = codeToGID; ff->codeToGIDLen = codeToGIDLen; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font, Dict *parentResDict) { PSFontFileInfo *ff; GString *psName; Dict *resDict; Dict *charProcs; Object charProc; Gfx *gfx; PDFRectangle box; double *m; GString *buf; int i; // generate name psName = GString::format("T3_{0:d}_{1:d}", font->getID()->num, font->getID()->gen); // 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:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", m[0], m[1], m[2], m[3], m[4], m[5]); m = font->getFontBBox(); writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", m[0], m[1], m[2], m[3]); writePS("/Encoding 256 array def\n"); writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); writePS("/BuildGlyph {\n"); writePS(" exch /CharProcs get exch\n"); writePS(" 2 copy known not { pop /.notdef } if\n"); writePS(" get exec\n"); writePS("} bind def\n"); writePS("/BuildChar {\n"); writePS(" 1 index /Encoding get exch get\n"); writePS(" 1 index /BuildGlyph get exec\n"); writePS("} bind def\n"); if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) { writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength()); writePS("CharProcs begin\n"); box.x1 = m[0]; box.y1 = m[1]; box.x2 = m[2]; box.y2 = m[3]; gfx = new Gfx(doc, this, resDict, &box, NULL); inType3Char = gTrue; for (i = 0; i < charProcs->getLength(); ++i) { t3FillColorOnly = gFalse; t3Cacheable = gFalse; t3NeedsRestore = gFalse; writePS("/"); writePSName(charProcs->getKey(i)); writePS(" {\n"); gfx->display(charProcs->getValNF(i, &charProc)); charProc.free(); if (t3String) { if (t3Cacheable) { buf = GString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n", t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); } else { buf = GString::format("{0:.6g} {1:.6g} 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"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); fontFileInfo->add(ff->psName, ff); return ff; } // Make a unique PS font name, based on the names given in the PDF // font object, and an object ID (font file object for GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) { GString *psName, *s; if ((s = font->getEmbeddedFontName())) { psName = filterPSName(s); if (!fontFileInfo->lookup(psName)) { return psName; } delete psName; } if ((s = font->getName())) { psName = filterPSName(s); if (!fontFileInfo->lookup(psName)) { return psName; } delete psName; } psName = GString::format("FF{0:d}_{1:d}", id->num, id->gen); if ((s = font->getEmbeddedFontName())) { s = filterPSName(s); psName->append('_')->append(s); delete s; } else if ((s = font->getName())) { s = filterPSName(s); psName->append('_')->append(s); delete s; } return psName; } GString *PSOutputDev::fixType1Font(GString *font, int length1, int length2) { Guchar *fontData; GString *out, *binSection; GBool pfb; int fontSize, i; fontData = (Guchar *)font->getCString(); fontSize = font->getLength(); // check for PFB pfb = fontSize >= 6 && fontData[0] == 0x80 && fontData[1] == 0x01; out = new GString(); binSection = new GString(); if (pfb) { if (!splitType1PFB(fontData, fontSize, out, binSection)) { delete out; delete binSection; return copyType1PFB(fontData, fontSize); } } else { if (!splitType1PFA(fontData, fontSize, length1, length2, out, binSection)) { delete out; delete binSection; return copyType1PFA(fontData, fontSize); } } out->append('\n'); binSection = asciiHexDecodeType1EexecSection(binSection); if (!fixType1EexecSection(binSection, out)) { delete out; delete binSection; return pfb ? copyType1PFB(fontData, fontSize) : copyType1PFA(fontData, fontSize); } delete binSection; for (i = 0; i < 8; ++i) { out->append("0000000000000000000000000000000000000000000000000000000000000000\n"); } out->append("cleartomark\n"); return out; } // Split a Type 1 font in PFA format into a text section and a binary // section. GBool PSOutputDev::splitType1PFA(Guchar *font, int fontSize, int length1, int length2, GString *textSection, GString *binSection) { int textLength, binStart, binLength, lastSpace, i; //--- extract the text section // Length1 is correct, and the text section ends with whitespace if (length1 <= fontSize && length1 >= 18 && !memcmp(font + length1 - 18, "currentfile eexec", 17)) { textLength = length1 - 1; // Length1 is correct, but the trailing whitespace is missing } else if (length1 <= fontSize && length1 >= 17 && !memcmp(font + length1 - 17, "currentfile eexec", 17)) { textLength = length1; // Length1 is incorrect } else { for (textLength = 17; textLength <= fontSize; ++textLength) { if (!memcmp(font + textLength - 17, "currentfile eexec", 17)) { break; } } if (textLength > fontSize) { return gFalse; } } textSection->append((char *)font, textLength); //--- skip whitespace between the text section and the binary section for (i = 0, binStart = textLength; i < 8 && binStart < fontSize; ++i, ++binStart) { if (font[binStart] != ' ' && font[binStart] != '\t' && font[binStart] != '\n' && font[binStart] != '\r') { break; } } if (i == 8) { return gFalse; } //--- extract binary section // if we see "0000", assume Length2 is correct // (if Length2 is too long, it will be corrected by fixType1EexecSection) if (length2 > 0 && length2 < INT_MAX - 4 && binStart <= fontSize - length2 - 4 && !memcmp(font + binStart + length2, "0000", 4)) { binLength = length2; } else { // look for "0000" near the end of the font (note that there can // be intervening "\n", "\r\n", etc.), then search backward if (fontSize - binStart < 512) { return gFalse; } if (!memcmp(font + fontSize - 256, "0000", 4) || !memcmp(font + fontSize - 255, "0000", 4) || !memcmp(font + fontSize - 254, "0000", 4) || !memcmp(font + fontSize - 253, "0000", 4) || !memcmp(font + fontSize - 252, "0000", 4) || !memcmp(font + fontSize - 251, "0000", 4)) { i = fontSize - 252; lastSpace = -1; while (i >= binStart) { if (font[i] == ' ' || font[i] == '\t' || font[i] == '\n' || font[i] == '\r') { lastSpace = i; --i; } else if (font[i] == '0') { --i; } else { break; } } if (lastSpace < 0) { return gFalse; } // check for the case where the newline/space is missing between // the binary section and the first set of 64 '0' chars if (lastSpace - binStart > 64 && !memcmp(font + lastSpace - 64, "0000000000000000000000000000000000000000000000000000000000000000", 64)) { binLength = lastSpace - 64 - binStart; } else { binLength = lastSpace - binStart; } // couldn't find zeros after binary section -- assume they're // missing and the binary section extends to the end of the file } else { binLength = fontSize - binStart; } } binSection->append((char *)(font + binStart), binLength); return gTrue; } // Split a Type 1 font in PFB format into a text section and a binary // section. GBool PSOutputDev::splitType1PFB(Guchar *font, int fontSize, GString *textSection, GString *binSection) { Guchar *p; int state, remain, len, n; // states: // 0: text section // 1: binary section // 2: trailer section // 3: eof state = 0; p = font; remain = fontSize; while (remain >= 2) { if (p[0] != 0x80) { return gFalse; } switch (state) { case 0: if (p[1] == 0x02) { state = 1; } else if (p[1] != 0x01) { return gFalse; } break; case 1: if (p[1] == 0x01) { state = 2; } else if (p[1] != 0x02) { return gFalse; } break; case 2: if (p[1] == 0x03) { state = 3; } else if (p[1] != 0x01) { return gFalse; } break; default: // shouldn't happen return gFalse; } if (state == 3) { break; } if (remain < 6) { break; } len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24); if (len < 0 || len > remain - 6) { return gFalse; } switch (state) { case 0: textSection->append((char *)(p + 6), len); break; case 1: binSection->append((char *)(p + 6), len); break; case 2: // we don't use the trailer break; default: // shouldn't happen return gFalse; } p += len + 6; remain -= len + 6; } if (state != 3) { return gFalse; } n = textSection->getLength(); if (n >= 18 && !memcmp(textSection->getCString() + n - 18, "currentfile eexec", 17)) { // remove the trailing whitespace textSection->del(n - 1, 1); } else if (n >= 17 && !memcmp(textSection->getCString() + n - 17, "currentfile eexec", 17)) { // missing whitespace at end -- leave as-is } else { return gFalse; } return gTrue; } // If is ASCIIHex-encoded, decode it, delete , and return the // binary version. Else return unchanged. GString *PSOutputDev::asciiHexDecodeType1EexecSection(GString *in) { GString *out; char c; Guchar byte; int state, i; out = new GString(); state = 0; byte = 0; for (i = 0; i < in->getLength(); ++i) { c = in->getChar(i); if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { continue; } if (c >= '0' && c <= '9') { byte = (Guchar)(byte + (c - '0')); } else if (c >= 'A' && c <= 'F') { byte = (Guchar)(byte + (c - 'A' + 10)); } else if (c >= 'a' && c <= 'f') { byte = (Guchar)(byte + (c - 'a' + 10)); } else { delete out; return in; } if (state == 0) { byte = (Guchar)(byte << 4); state = 1; } else { out->append((char)byte); state = 0; byte = 0; } } delete in; return out; } GBool PSOutputDev::fixType1EexecSection(GString *binSection, GString *out) { static char hexChars[17] = "0123456789abcdef"; Guchar buf[16], buf2[16]; Guchar byte; int r, i, j; // eexec-decode the binary section, keeping the last 16 bytes r = 55665; for (i = 0; i < binSection->getLength(); ++i) { byte = (Guchar)binSection->getChar(i); buf[i & 15] = byte ^ (Guchar)(r >> 8); r = ((r + byte) * 52845 + 22719) & 0xffff; } for (j = 0; j < 16; ++j) { buf2[j] = buf[(i + j) & 15]; } // look for 'closefile' for (i = 0; i <= 16 - 9; ++i) { if (!memcmp(buf2 + i, "closefile", 9)) { break; } } if (i > 16 - 9) { return gFalse; } // three cases: // - short: missing space after "closefile" (i == 16 - 9) // - correct: exactly one space after "closefile" (i == 16 - 10) // - long: extra chars after "closefile" (i < 16 - 10) if (i == 16 - 9) { binSection->append((char)((Guchar)'\n' ^ (Guchar)(r >> 8))); } else if (i < 16 - 10) { binSection->del(binSection->getLength() - (16 - 10 - i), 16 - 10 - i); } // ASCIIHex encode for (i = 0; i < binSection->getLength(); i += 32) { for (j = 0; j < 32 && i+j < binSection->getLength(); ++j) { byte = (Guchar)binSection->getChar(i+j); out->append(hexChars[(byte >> 4) & 0x0f]); out->append(hexChars[byte & 0x0f]); } out->append('\n'); } return gTrue; } // The Type 1 cleanup code failed -- assume it's a valid PFA-format // font and copy it to the output. GString *PSOutputDev::copyType1PFA(Guchar *font, int fontSize) { GString *out; error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 font"); out = new GString((char *)font, fontSize); // append a newline to avoid problems where the original font // doesn't end with one out->append('\n'); return out; } // The Type 1 cleanup code failed -- assume it's a valid PFB-format // font, decode the PFB blocks, and copy them to the output. GString *PSOutputDev::copyType1PFB(Guchar *font, int fontSize) { static char hexChars[17] = "0123456789abcdef"; GString *out; Guchar *p; int remain, len, i, j; error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 (PFB) font"); out = new GString(); p = font; remain = fontSize; while (remain >= 6 && p[0] == 0x80 && (p[1] == 0x01 || p[1] == 0x02)) { len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24); if (len < 0 || len > remain - 6) { break; } if (p[1] == 0x01) { out->append((char *)(p + 6), len); } else { for (i = 0; i < len; i += 32) { for (j = 0; j < 32 && i+j < len; ++j) { out->append(hexChars[(p[6+i+j] >> 4) & 0x0f]); out->append(hexChars[p[6+i+j] & 0x0f]); } out->append('\n'); } } p += len + 6; remain -= len + 6; } // append a newline to avoid problems where the original font // doesn't end with one out->append('\n'); return out; } void PSOutputDev::renameType1Font(GString *font, GString *name) { char *p1, *p2; int i; if (!(p1 = strstr(font->getCString(), "\n/FontName")) && !(p1 = strstr(font->getCString(), "\r/FontName"))) { return; } p1 += 10; while (*p1 == ' ' || *p1 == '\t' || *p1 == '\n' || *p1 == '\r') { ++p1; } if (*p1 != '/') { return; } ++p1; p2 = p1; while (*p2 && *p2 != ' ' && *p2 != '\t' && *p2 != '\n' && *p2 != '\r') { ++p2; } if (!*p2) { return; } i = (int)(p1 - font->getCString()); font->del(i, (int)(p2 - p1)); font->insert(i, name); } void PSOutputDev::setupDefaultFont() { writePS("/xpdf_default_font /Helvetica 1 1 ISOLatin1Encoding pdfMakeFont\n"); } void PSOutputDev::setupImages(Dict *resDict) { Object xObjDict, xObj, xObjRef, subtypeObj, maskObj, maskRef; Ref imgID; int i, j; 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()) { imgID = xObjRef.getRef(); for (j = 0; j < imgIDLen; ++j) { if (imgIDs[j].num == imgID.num && imgIDs[j].gen == imgID.gen) { break; } } if (j == imgIDLen) { if (imgIDLen >= imgIDSize) { if (imgIDSize == 0) { imgIDSize = 64; } else { imgIDSize *= 2; } imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); } imgIDs[imgIDLen++] = imgID; setupImage(imgID, xObj.getStream(), gFalse, NULL); if (level >= psLevel3) { xObj.streamGetDict()->lookup("Mask", &maskObj); if (maskObj.isStream()) { setupImage(imgID, maskObj.getStream(), gTrue, NULL); } else if (level == psLevel3Gray && maskObj.isArray()) { setupImage(imgID, xObj.getStream(), gFalse, maskObj.getArray()); } maskObj.free(); } } } else { error(errSyntaxError, -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 mask, Array *colorKeyMask) { StreamColorSpaceMode csMode; GfxColorSpace *colorSpace; GfxImageColorMap *colorMap; int maskColors[2*gfxColorMaxComps]; Object obj1; GBool imageMask, useLZW, useRLE, useCompressed, useASCIIHex; GString *s; int c, width, height, bits, size, line, col, i; // check for mask str->getDict()->lookup("ImageMask", &obj1); if (obj1.isBool()) { imageMask = obj1.getBool(); } else { imageMask = gFalse; } obj1.free(); // get image size str->getDict()->lookup("Width", &obj1); if (!obj1.isInt() || obj1.getInt() <= 0) { error(errSyntaxError, -1, "Invalid Width in image"); obj1.free(); return; } width = obj1.getInt(); obj1.free(); str->getDict()->lookup("Height", &obj1); if (!obj1.isInt() || obj1.getInt() <= 0) { error(errSyntaxError, -1, "Invalid Height in image"); obj1.free(); return; } height = obj1.getInt(); obj1.free(); // build the color map if (mask || imageMask) { colorMap = NULL; } else { bits = 0; csMode = streamCSNone; str->getImageParams(&bits, &csMode); if (bits == 0) { str->getDict()->lookup("BitsPerComponent", &obj1); if (!obj1.isInt()) { error(errSyntaxError, -1, "Invalid BitsPerComponent in image"); obj1.free(); return; } bits = obj1.getInt(); obj1.free(); } str->getDict()->lookup("ColorSpace", &obj1); if (!obj1.isNull()) { colorSpace = GfxColorSpace::parse(&obj1 ); } else if (csMode == streamCSDeviceGray) { colorSpace = GfxColorSpace::create(csDeviceGray); } else if (csMode == streamCSDeviceRGB) { colorSpace = GfxColorSpace::create(csDeviceRGB); } else if (csMode == streamCSDeviceCMYK) { colorSpace = GfxColorSpace::create(csDeviceCMYK); } else { colorSpace = NULL; } obj1.free(); if (!colorSpace) { error(errSyntaxError, -1, "Invalid ColorSpace in image"); return; } str->getDict()->lookup("Decode", &obj1); colorMap = new GfxImageColorMap(bits, &obj1, colorSpace); obj1.free(); } // filters str->disableDecompressionBombChecking(); if (level < psLevel2) { useLZW = useRLE = gFalse; useCompressed = gFalse; useASCIIHex = gTrue; } else { if (colorKeyMask) { if (globalParams->getPSUncompressPreloadedImages()) { useLZW = useRLE = gFalse; } else if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useCompressed = gFalse; } else if (colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN || level == psLevel2Gray || level == psLevel3Gray)) { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useCompressed = gFalse; } else if (globalParams->getPSUncompressPreloadedImages()) { useLZW = useRLE = gFalse; useCompressed = gFalse; } else { s = str->getPSFilter(level < psLevel3 ? 2 : 3, "", gTrue); if (s) { useLZW = useRLE = gFalse; useCompressed = gTrue; delete s; } else { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useCompressed = gFalse; } } useASCIIHex = globalParams->getPSASCIIHex(); } if (useCompressed) { str = str->getUndecodedStream(); } if (colorKeyMask) { memset(maskColors, 0, sizeof(maskColors)); for (i = 0; i < colorKeyMask->getLength() && i < 2*gfxColorMaxComps; ++i) { colorKeyMask->get(i, &obj1); if (obj1.isInt()) { maskColors[i] = obj1.getInt(); } obj1.free(); } str = new ColorKeyToMaskEncoder(str, width, height, colorMap, maskColors); } else if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { str = new GrayRecoder(str, width, height, colorMap); } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { str = new DeviceNRecoder(str, width, height, colorMap); } if (useLZW) { str = new LZWEncoder(str); } else 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 LZWDecode/RunLengthDecode filter may read past the end ++size; if (useLZW || useRLE) { ++size; } writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", size, (mask || colorKeyMask) ? "Mask" : "Im", 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((char)c); ++col; } else { writePSChar((char)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((char)c); ++col; } } // each line is: "dup nnnnn <~...data...~> put" // 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 (useLZW || useRLE) { ++line; writePSFmt("{0:d} <> put\n", line); } else { writePS("pop\n"); } str->close(); delete str; if (colorMap) { delete colorMap; } } 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, &xObj); } else { error(errSyntaxError, -1, "Form in resource dict is not an indirect reference"); } } subtypeObj.free(); } xObj.free(); xObjRef.free(); } } xObjDict.free(); } void PSOutputDev::setupForm(Object *strRef, 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 == strRef->getRefNum() && formIDs[i].gen == strRef->getRefGen()) { 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++] = strRef->getRef(); dict = strObj->streamGetDict(); // get bounding box dict->lookup("BBox", &bboxObj); if (!bboxObj.isArray()) { bboxObj.free(); error(errSyntaxError, -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", strRef->getRefNum(), strRef->getRefGen()); writePS("q\n"); writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", m[0], m[1], m[2], m[3], m[4], m[5]); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx = new Gfx(doc, this, resDict, &box, &box); gfx->display(strRef); 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, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { int pg; #if HAVE_SPLASH GBool mono; GBool useLZW; double dpi; SplashOutputDev *splashOut; SplashColor paperColor; PDFRectangle box; GfxState *state; SplashBitmap *bitmap; Stream *str0, *str; Object obj; Guchar *p; Guchar col[4]; char buf[4096]; double userUnit, hDPI2, vDPI2; double m0, m1, m2, m3, m4, m5; int nStripes, stripeH, stripeY; int w, h, x, y, comp, i, n; #endif pg = page->getNum(); if (!(pg >= firstPage && pg <= lastPage && rasterizePage[pg - firstPage])) { return gTrue; } #if HAVE_SPLASH // get the rasterization parameters dpi = globalParams->getPSRasterResolution(); mono = globalParams->getPSRasterMono() || level == psLevel1 || level == psLevel2Gray || level == psLevel3Gray; useLZW = globalParams->getPSLZW(); // get the UserUnit if (honorUserUnit) { userUnit = page->getUserUnit(); } else { userUnit = 1; } // start the PS page page->makeBox(userUnit * dpi, userUnit * dpi, 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(dpi, dpi, &box, rotateA, gFalse); startPage(page->getNum(), state); delete state; // set up the SplashOutputDev if (mono) { paperColor[0] = 0xff; splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); #if SPLASH_CMYK } else if (level == psLevel1Sep) { paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); #endif } else { paperColor[0] = paperColor[1] = paperColor[2] = 0xff; splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); } splashOut->startDoc(xref); // break the page into stripes // NB: startPage() has already multiplied xScale and yScale by UserUnit hDPI2 = xScale * dpi; vDPI2 = yScale * dpi; if (sliceW < 0 || sliceH < 0) { if (useMediaBox) { box = *page->getMediaBox(); } else { box = *page->getCropBox(); } sliceX = sliceY = 0; sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); if (sliceW == 0) { sliceW = 1; } sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); if (sliceH == 0) { sliceH = 1; } } nStripes = (int)ceil(((double)sliceW * (double)sliceH) / (double)globalParams->getPSRasterSliceSize()); stripeH = (sliceH + nStripes - 1) / nStripes; // render the stripes for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) { // rasterize a stripe page->makeBox(hDPI2, vDPI2, 0, useMediaBox, gFalse, sliceX, stripeY, sliceW, stripeH, &box, &crop); m0 = box.x2 - box.x1; m1 = 0; m2 = 0; m3 = box.y2 - box.y1; m4 = box.x1; m5 = box.y1; page->displaySlice(splashOut, hDPI2, vDPI2, (360 - page->getRotate()) % 360, useMediaBox, crop, sliceX, stripeY, sliceW, stripeH, printing, abortCheckCbk, abortCheckCbkData); // draw the rasterized image bitmap = splashOut->getBitmap(); w = bitmap->getWidth(); h = bitmap->getHeight(); writePS("gsave\n"); writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", m0, m1, m2, m3, m4, m5); switch (level) { case psLevel1: writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", w, h, w, -h, h); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); 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: #if SPLASH_CMYK 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() + (h - 1) * bitmap->getRowSize(); 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; // if !SPLASH_CMYK: fall through #endif case psLevel2: case psLevel2Gray: case psLevel2Sep: case psLevel3: case psLevel3Gray: case psLevel3Sep: if (mono) { writePS("/DeviceGray setcolorspace\n"); } else { 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"); if (mono) { writePS(" /Decode [0 1]\n"); } else { 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"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else { writePS(" /RunLengthDecode filter\n"); } writePS(">>\n"); writePS("image\n"); obj.initNull(); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj); if (useLZW) { str = new LZWEncoder(str0); } else { str = new RunLengthEncoder(str0); } if (globalParams->getPSASCIIHex()) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); delete str; delete str0; writePSChar('\n'); processColors |= mono ? psProcessBlack : psProcessCMYK; break; } writePS("grestore\n"); } delete splashOut; // finish the PS page endPage(); return gFalse; #else // HAVE_SPLASH error(errSyntaxWarning, -1, "PDF page uses transparency and PSOutputDev was built without" " the Splash rasterizer - output may not be correct"); return gTrue; #endif // HAVE_SPLASH } void PSOutputDev::startPage(int pageNum, GfxState *state) { Page *page; double userUnit; int x1, y1, x2, y2, width, height, t; int imgWidth, imgHeight, imgWidth2, imgHeight2; GBool landscape; GString *s; page = doc->getCatalog()->getPage(pageNum); if (honorUserUnit) { userUnit = page->getUserUnit(); } else { userUnit = 1; } if (mode == psModePS) { writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); if (paperMatch) { imgLLX = imgLLY = 0; if (globalParams->getPSUseCropBoxAsPage()) { imgURX = (int)ceil(page->getCropWidth() * userUnit); imgURY = (int)ceil(page->getCropHeight() * userUnit); } else { imgURX = (int)ceil(page->getMediaWidth() * userUnit); imgURY = (int)ceil(page->getMediaHeight() * userUnit); } if (state->getRotate() == 90 || state->getRotate() == 270) { t = imgURX; imgURX = imgURY; imgURY = t; } writePSFmt("%%PageMedia: {0:d}x{1:d}\n", imgURX, imgURY); writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", imgURX, imgURY); } writePS("%%BeginPageSetup\n"); } if (mode != psModeForm) { writePS("xpdf begin\n"); } // set up paper size for paper=match mode // NB: this must be done *before* the saveState() for overlays. if (mode == psModePS && paperMatch) { writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY); } // 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 (paperMatch) { rotate = (360 - state->getRotate()) % 360; landscape = gFalse; } else if (rotate0 >= 0) { rotate = (360 - rotate0) % 360; landscape = gFalse; } else { rotate = (360 - state->getRotate()) % 360; double scaledWidth = width * userUnit; double scaledHeight = height * userUnit; if (xScale0 > 0 && yScale0 > 0) { scaledWidth *= xScale0; scaledHeight *= yScale0; } if (rotate == 0 || rotate == 180) { if ((scaledWidth < scaledHeight && imgWidth > imgHeight && scaledHeight > imgHeight) || (scaledWidth > scaledHeight && imgWidth < imgHeight && scaledWidth > imgWidth)) { rotate += 90; landscape = gTrue; } else { landscape = gFalse; } } else { // rotate == 90 || rotate == 270 if ((scaledHeight < scaledWidth && imgWidth > imgHeight && scaledWidth > imgHeight) || (scaledHeight > scaledWidth && imgWidth < imgHeight && scaledHeight > 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 * userUnit; yScale = yScale0 * userUnit; } else if ((globalParams->getPSShrinkLarger() && (width * userUnit > imgWidth2 || height * userUnit > imgHeight2)) || (expandSmallPages && (width * userUnit < imgWidth2 && height * userUnit < imgHeight2))) { xScale = (double)imgWidth2 / (double)width; yScale = (double)imgHeight2 / (double)height; if (yScale < xScale) { xScale = yScale; } else { yScale = xScale; } } else { xScale = yScale = userUnit; } // deal with odd bounding boxes or clipping if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { tx -= xScale * clipLLX0; ty -= yScale * clipLLY0; } else { tx -= xScale * x1; ty -= yScale * y1; } // center if (tx0 >= 0 && ty0 >= 0) { tx += (rotate == 0 || rotate == 180) ? tx0 : ty0; ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0; } else if (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 || rotate == 180) ? imgLLX : imgLLY; ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; if (tx != 0 || ty != 0) { writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); } if (xScale != 1 || yScale != 1) { writePSFmt("{0:.4f} {1:.4f} scale\n", xScale, yScale); } if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); } else { writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1); } ++seqPage; break; case psModeEPS: writePS("pdfStartPage\n"); tx = ty = 0; rotate = (360 - state->getRotate()) % 360; if (rotate == 0) { } else if (rotate == 90) { writePS("90 rotate\n"); tx = -epsX1; ty = -epsY2; } else if (rotate == 180) { writePS("180 rotate\n"); tx = -(epsX1 + epsX2); ty = -(epsY1 + epsY2); } else { // rotate == 270 writePS("270 rotate\n"); tx = -epsX2; ty = -epsY1; } if (tx != 0 || ty != 0) { writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); } 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; } if (level == psLevel2Gray || level == psLevel3Gray) { writePS("/DeviceGray setcolorspace\n"); } if (customCodeCbk) { if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, customCodeCbkData))) { writePS(s->getCString()); delete s; } } if (mode == psModePS) { writePS("%%EndPageSetup\n"); } noStateChanges = gFalse; } 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(); writePS("end\n"); } } void PSOutputDev::saveState(GfxState *state) { // The noStateChanges and saveStack fields are used to implement an // optimization to reduce gsave/grestore nesting. The idea is to // look for sequences like this: // q q AAA Q BBB Q (where AAA and BBB are sequences of operations) // and transform them to: // q AAA Q q BBB Q if (noStateChanges) { // any non-NULL pointer will work here saveStack->append(this); } else { saveStack->append((PSOutputDev *)NULL); writePS("q\n"); noStateChanges = gTrue; } } void PSOutputDev::restoreState(GfxState *state) { if (saveStack->getLength()) { writePS("Q\n"); if (saveStack->del(saveStack->getLength() - 1)) { writePS("q\n"); noStateChanges = gTrue; } else { noStateChanges = gFalse; } } } void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { if (m11 == 1 && m12 == 0 && m21 == 0 && m22 == 1 && m31 == 0 && m32 == 0) { return; } if (fabs(m11 * m22 - m12 * m21) < 1e-10) { // avoid a singular (or close-to-singular) matrix writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] cm\n", m31, m32); } else { writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", m11, m12, m21, m22, m31, m32); } noStateChanges = gFalse; } void PSOutputDev::updateLineDash(GfxState *state) { double *dash; double start; int length, i; state->getLineDash(&dash, &length, &start); writePS("["); for (i = 0; i < length; ++i) { writePSFmt("{0:.6g}{1:w}", dash[i] < 0 ? 0 : dash[i], (i == length-1) ? 0 : 1); } writePSFmt("] {0:.6g} d\n", start); noStateChanges = gFalse; } void PSOutputDev::updateFlatness(GfxState *state) { writePSFmt("{0:.4g} i\n", state->getFlatness()); noStateChanges = gFalse; } void PSOutputDev::updateLineJoin(GfxState *state) { writePSFmt("{0:d} j\n", state->getLineJoin()); noStateChanges = gFalse; } void PSOutputDev::updateLineCap(GfxState *state) { writePSFmt("{0:d} J\n", state->getLineCap()); noStateChanges = gFalse; } void PSOutputDev::updateMiterLimit(GfxState *state) { writePSFmt("{0:.4g} M\n", state->getMiterLimit()); noStateChanges = gFalse; } void PSOutputDev::updateLineWidth(GfxState *state) { writePSFmt("{0:.6g} w\n", state->getLineWidth()); noStateChanges = gFalse; } void PSOutputDev::updateFillColorSpace(GfxState *state) { switch (level) { case psLevel1: case psLevel1Sep: break; case psLevel2: case psLevel3: if (state->getFillColorSpace()->getMode() != csPattern) { dumpColorSpaceL2(state, state->getFillColorSpace(), gTrue, gFalse, gFalse); writePS(" cs\n"); noStateChanges = gFalse; } break; case psLevel2Gray: case psLevel3Gray: 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, state->getStrokeColorSpace(), gTrue, gFalse, gFalse); writePS(" CS\n"); noStateChanges = gFalse; } break; case psLevel2Gray: case psLevel3Gray: 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: case psLevel2Gray: case psLevel3Gray: 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, state->getRenderingIntent()); 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(state, 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; noStateChanges = 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: case psLevel2Gray: case psLevel3Gray: 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, state->getRenderingIntent()); 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(state, 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; noStateChanges = 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(GfxState *state, 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, state->getRenderingIntent()); 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::addCustomColors(GfxState *state, GfxDeviceNColorSpace *devnCS) { PSOutCustomColor *cc; GfxColor color; GfxCMYK cmyk; int i; for (i = 0; i < devnCS->getNComps(); ++i) { color.c[i] = 0; } for (i = 0; i < devnCS->getNComps(); ++i) { for (cc = customColors; cc; cc = cc->next) { if (!cc->name->cmp(devnCS->getColorantName(i))) { break; } } if (cc) { continue; } color.c[i] = gfxColorComp1; devnCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); color.c[i] = 0; cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), devnCS->getColorantName(i)->copy()); cc->next = customColors; customColors = cc; } } void PSOutputDev::updateFillOverprint(GfxState *state) { if (level == psLevel2 || level == psLevel2Sep || level == psLevel3 || level == psLevel3Sep) { writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false"); noStateChanges = gFalse; } } void PSOutputDev::updateStrokeOverprint(GfxState *state) { if (level == psLevel2 || level == psLevel2Sep || level == psLevel3 || level == psLevel3Sep) { writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false"); noStateChanges = gFalse; } } void PSOutputDev::updateOverprintMode(GfxState *state) { if (level == psLevel3 || level == psLevel3Sep) { writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false"); noStateChanges = gFalse; } } void PSOutputDev::updateTransfer(GfxState *state) { Function **funcs; int i; funcs = state->getTransfer(); if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) { if (level == psLevel2 || level == psLevel2Sep || level == psLevel3 || level == psLevel3Sep) { 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"); } noStateChanges = gFalse; } void PSOutputDev::updateFont(GfxState *state) { if (state->getFont()) { if (state->getFont()->getTag() && !state->getFont()->getTag()->cmp("xpdf_default_font")) { writePSFmt("/xpdf_default_font {0:.6g} Tf\n", fabs(state->getFontSize()) < 0.0001 ? 0.0001 : state->getFontSize()); } else { writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n", state->getFont()->getID()->num, state->getFont()->getID()->gen, fabs(state->getFontSize()) < 0.0001 ? 0.0001 : state->getFontSize()); } noStateChanges = gFalse; } } void PSOutputDev::updateTextMat(GfxState *state) { double *mat; mat = state->getTextMat(); if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 1e-10) { // avoid a singular (or close-to-singular) matrix writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]); } else { writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); } noStateChanges = gFalse; } void PSOutputDev::updateCharSpace(GfxState *state) { writePSFmt("{0:.6g} Tc\n", state->getCharSpace()); noStateChanges = gFalse; } 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; } noStateChanges = gFalse; } void PSOutputDev::updateRise(GfxState *state) { writePSFmt("{0:.6g} Ts\n", state->getRise()); noStateChanges = gFalse; } void PSOutputDev::updateWordSpace(GfxState *state) { writePSFmt("{0:.6g} Tw\n", state->getWordSpace()); noStateChanges = gFalse; } void PSOutputDev::updateHorizScaling(GfxState *state) { double h; h = state->getHorizScaling(); if (fabs(h) < 0.01) { h = 0.01; } writePSFmt("{0:.6g} Tz\n", h); noStateChanges = gFalse; } void PSOutputDev::updateTextPos(GfxState *state) { writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY()); noStateChanges = gFalse; } void PSOutputDev::updateTextShift(GfxState *state, double shift) { if (state->getFont()->getWMode()) { writePSFmt("{0:.6g} TJmV\n", shift); } else { writePSFmt("{0:.6g} TJm\n", shift); } noStateChanges = gFalse; } void PSOutputDev::saveTextPos(GfxState *state) { writePS("currentpoint\n"); noStateChanges = gFalse; } void PSOutputDev::restoreTextPos(GfxState *state) { writePS("m\n"); noStateChanges = gFalse; } void PSOutputDev::stroke(GfxState *state) { doPath(state->getPath()); if (inType3Char && t3FillColorOnly) { // if we're constructing a cacheable Type 3 glyph, we need to do // everything in the fill color writePS("Sf\n"); } else { writePS("S\n"); } noStateChanges = gFalse; } void PSOutputDev::fill(GfxState *state) { doPath(state->getPath()); writePS("f\n"); noStateChanges = gFalse; } void PSOutputDev::eoFill(GfxState *state) { doPath(state->getPath()); writePS("f*\n"); noStateChanges = gFalse; } void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { if (level <= psLevel1Sep) { tilingPatternFillL1(state, gfx, strRef, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep); } else { tilingPatternFillL2(state, gfx, strRef, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep); } } void PSOutputDev::tilingPatternFillL1(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx2; // define a Type 3 font writePS("8 dict begin\n"); writePS("/FontType 3 def\n"); writePS("/FontMatrix [1 0 0 1 0 0] def\n"); writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); writePS("/Encoding 256 array def\n"); writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); writePS(" Encoding 120 /x put\n"); writePS("/BuildGlyph {\n"); writePS(" exch /CharProcs get exch\n"); writePS(" 2 copy known not { pop /.notdef } if\n"); writePS(" get exec\n"); writePS("} bind def\n"); writePS("/BuildChar {\n"); writePS(" 1 index /Encoding get exch get\n"); writePS(" 1 index /BuildGlyph get exec\n"); writePS("} bind def\n"); writePS("/CharProcs 1 dict def\n"); writePS("CharProcs begin\n"); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx2 = new Gfx(doc, this, resDict, &box, NULL); gfx2->takeContentStreamStack(gfx); writePS("/x {\n"); if (paintType == 2) { writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", xStep, bbox[0], bbox[1], bbox[2], bbox[3]); t3FillColorOnly = gTrue; } else { if (x1 - 1 <= x0) { writePS("1 0 setcharwidth\n"); } else { writePSFmt("{0:.6g} 0 setcharwidth\n", xStep); } t3FillColorOnly = gFalse; } inType3Char = gTrue; ++numTilingPatterns; gfx2->display(strRef); --numTilingPatterns; inType3Char = gFalse; writePS("} def\n"); delete gfx2; writePS("end\n"); writePS("currentdict end\n"); writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns); // draw the tiles writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns); writePS("fCol\n"); writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1); writePS("grestore\n"); noStateChanges = gFalse; } void PSOutputDev::tilingPatternFillL2(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx2; // switch to pattern space writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); // define a pattern writePSFmt("/xpdfTile{0:d}\n", numTilingPatterns); writePS("<<\n"); writePS(" /PatternType 1\n"); writePSFmt(" /PaintType {0:d}\n", paintType); writePSFmt(" /TilingType {0:d}\n", tilingType); writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n", bbox[0], bbox[1], bbox[2], bbox[3]); writePSFmt(" /XStep {0:.6g}\n", xStep); writePSFmt(" /YStep {0:.6g}\n", yStep); writePS(" /PaintProc {\n"); writePS(" pop\n"); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx2 = new Gfx(doc, this, resDict, &box, NULL); gfx2->takeContentStreamStack(gfx); t3FillColorOnly = paintType == 2; inType3Char = gTrue; ++numTilingPatterns; gfx2->display(strRef); --numTilingPatterns; inType3Char = gFalse; delete gfx2; writePS(" }\n"); writePS(">> matrix makepattern def\n"); // set the pattern if (paintType == 2) { writePS("fCol\n"); writePS("currentcolor "); } writePSFmt("xpdfTile{0:d} setpattern\n", numTilingPatterns); // fill with the pattern writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} rectfill\n", x0 * xStep + bbox[0], y0 * yStep + bbox[1], (x1 - x0) * xStep + bbox[2], (y1 - y0) * yStep + bbox[3]); writePS("grestore\n"); noStateChanges = gFalse; } GBool PSOutputDev::shadedFill(GfxState *state, GfxShading *shading) { if (level != psLevel2 && level != psLevel2Sep && level != psLevel3 && level != psLevel3Sep) { return gFalse; } switch (shading->getType()) { case 1: functionShadedFill(state, (GfxFunctionShading *)shading); return gTrue; case 2: axialShadedFill(state, (GfxAxialShading *)shading); return gTrue; case 3: radialShadedFill(state, (GfxRadialShading *)shading); return gTrue; default: return gFalse; } } 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:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("2 copy\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("3 1 roll\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1); noStateChanges = gFalse; 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:.6g} def\n", t0); writePSFmt("/t1 {0:.6g} def\n", t1); writePSFmt("/dt {0:.6g} def\n", t1 - t0); writePSFmt("/x0 {0:.6g} def\n", x0); writePSFmt("/y0 {0:.6g} def\n", y0); writePSFmt("/dx {0:.6g} def\n", x1 - x0); writePSFmt("/x1 {0:.6g} def\n", x1); writePSFmt("/y1 {0:.6g} def\n", y1); writePSFmt("/dy {0:.6g} def\n", y1 - y0); writePSFmt("/xMin {0:.6g} def\n", xMin); writePSFmt("/yMin {0:.6g} def\n", yMin); writePSFmt("/xMax {0:.6g} def\n", xMax); writePSFmt("/yMax {0:.6g} def\n", yMax); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("dup\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("exch\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax); noStateChanges = gFalse; 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 sMin, sMax, h, ta; double sLeft, sRight, sTop, sBottom, sZero, sDiag; GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; GBool haveSMin, haveSMax; 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. h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); if (h == 0) { enclosed = gTrue; theta = 0; // make gcc happy } else if (r1 - r0 == 0) { enclosed = gFalse; theta = 0; } else if (fabs(r1 - r0) >= h) { enclosed = gTrue; theta = 0; // make gcc happy } else { enclosed = gFalse; theta = asin((r1 - r0) / h); } if (enclosed) { a1 = 0; a2 = 360; } else { alpha = atan2(y1 - y0, x1 - x0); a1 = (180 / M_PI) * (alpha + theta) + 90; a2 = (180 / M_PI) * (alpha - theta) - 90; while (a2 < a1) { a2 += 360; } } // compute the (possibly extended) s range state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); if (enclosed) { sMin = 0; sMax = 1; } else { // solve x(sLeft) + r(sLeft) = xMin if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); } else { sLeft = 0; // make gcc happy } // solve x(sRight) - r(sRight) = xMax if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); } else { sRight = 0; // make gcc happy } // solve y(sBottom) + r(sBottom) = yMin if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); } else { sBottom = 0; // make gcc happy } // solve y(sTop) - r(sTop) = yMax if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); } else { sTop = 0; // make gcc happy } // solve r(sZero) = 0 if ((haveSZero = fabs(r1 - r0) > 0.000001)) { sZero = -r0 / (r1 - r0); } else { sZero = 0; // make gcc happy } // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) if (haveSZero) { sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); } else { sDiag = 0; // make gcc happy } // compute sMin if (shading->getExtend0()) { sMin = 0; haveSMin = gFalse; if (x0 < x1 && haveSLeft && sLeft < 0) { sMin = sLeft; haveSMin = gTrue; } else if (x0 > x1 && haveSRight && sRight < 0) { sMin = sRight; haveSMin = gTrue; } if (y0 < y1 && haveSBottom && sBottom < 0) { if (!haveSMin || sBottom > sMin) { sMin = sBottom; haveSMin = gTrue; } } else if (y0 > y1 && haveSTop && sTop < 0) { if (!haveSMin || sTop > sMin) { sMin = sTop; haveSMin = gTrue; } } if (haveSZero && sZero < 0) { if (!haveSMin || sZero > sMin) { sMin = sZero; } } } else { sMin = 0; } // compute sMax if (shading->getExtend1()) { sMax = 1; haveSMax = gFalse; if (x1 < x0 && haveSLeft && sLeft > 1) { sMax = sLeft; haveSMax = gTrue; } else if (x1 > x0 && haveSRight && sRight > 1) { sMax = sRight; haveSMax = gTrue; } if (y1 < y0 && haveSBottom && sBottom > 1) { if (!haveSMax || sBottom < sMax) { sMax = sBottom; haveSMax = gTrue; } } else if (y1 > y0 && haveSTop && sTop > 1) { if (!haveSMax || sTop < sMax) { sMax = sTop; haveSMax = gTrue; } } if (haveSZero && sDiag > 1) { if (!haveSMax || sDiag < sMax) { sMax = sDiag; } } } else { sMax = 1; } } // generate the PS code writePSFmt("/x0 {0:.6g} def\n", x0); writePSFmt("/x1 {0:.6g} def\n", x1); writePSFmt("/dx {0:.6g} def\n", x1 - x0); writePSFmt("/y0 {0:.6g} def\n", y0); writePSFmt("/y1 {0:.6g} def\n", y1); writePSFmt("/dy {0:.6g} def\n", y1 - y0); writePSFmt("/r0 {0:.6g} def\n", r0); writePSFmt("/r1 {0:.6g} def\n", r1); writePSFmt("/dr {0:.6g} def\n", r1 - r0); writePSFmt("/t0 {0:.6g} def\n", t0); writePSFmt("/t1 {0:.6g} def\n", t1); writePSFmt("/dt {0:.6g} def\n", t1 - t0); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false"); writePSFmt("/a1 {0:.6g} def\n", a1); writePSFmt("/a2 {0:.6g} def\n", a2); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("dup\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("exch\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax); // extend the 'enclosed' case if (enclosed) { // extend the smaller circle if ((shading->getExtend0() && r0 <= r1) || (shading->getExtend1() && r1 < r0)) { if (r0 <= r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } if (level == psLevel2Sep || level == psLevel3Sep) { writePSFmt("{0:.6g} radialCol aload pop k\n", ta); } else { writePSFmt("{0:.6g} radialCol sc\n", ta); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra); } // extend the larger circle if ((shading->getExtend0() && r0 > r1) || (shading->getExtend1() && r1 >= r0)) { if (r0 > r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } if (level == psLevel2Sep || level == psLevel3Sep) { writePSFmt("{0:.6g} radialCol aload pop k\n", ta); } else { writePSFmt("{0:.6g} radialCol sc\n", ta); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra); writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n", xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin); } } noStateChanges = gFalse; return gTrue; } void PSOutputDev::clip(GfxState *state) { doPath(state->getPath()); writePS("W\n"); noStateChanges = gFalse; } void PSOutputDev::eoClip(GfxState *state) { doPath(state->getPath()); writePS("W*\n"); noStateChanges = gFalse; } void PSOutputDev::clipToStrokePath(GfxState *state) { doPath(state->getPath()); writePS("Ws\n"); noStateChanges = gFalse; } 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:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, fabs(x2 - x0), fabs(y1 - y0)); return; } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, fabs(x1 - x0), fabs(y2 - y0)); return; } } } for (i = 0; i < n; ++i) { subpath = path->getSubpath(i); m = subpath->getNumPoints(); writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0)); j = 1; while (j < m) { if (subpath->getCurve(j)) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n", subpath->getX(j), subpath->getY(j), subpath->getX(j+1), subpath->getY(j+1), subpath->getX(j+2), subpath->getY(j+2)); j += 3; } else { writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j)); ++j; } } if (subpath->isClosed()) { writePS("h\n"); } } } void PSOutputDev::drawString(GfxState *state, GString *s) { GfxFont *font; int wMode; int *codeToGID; GString *s2; double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0; char *p; PSFontInfo *fi; UnicodeMap *uMap; CharCode code; Unicode u[8]; char buf[8]; double *dxdy; int dxdySize, 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(); fi = NULL; for (i = 0; i < fontInfo->getLength(); ++i) { fi = (PSFontInfo *)fontInfo->get(i); if (fi->fontID.num == font->getID()->num && fi->fontID.gen == font->getID()->gen) { break; } fi = NULL; } // check for a subtitute 16-bit font uMap = NULL; codeToGID = NULL; if (font->isCIDFont()) { if (!(fi && fi->ff)) { // font substitution failed, so don't output any text return; } if (fi->ff->encoding) { uMap = globalParams->getUnicodeMap(fi->ff->encoding); } // check for an 8-bit code-to-GID map } else { if (fi && fi->ff) { codeToGID = fi->ff->codeToGID; } } // compute the positioning (dx, dy) for each char in the string nChars = 0; p = s->getCString(); len = s->getLength(); s2 = new GString(); dxdySize = font->isCIDFont() ? 8 : s->getLength(); dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double)); originX0 = originY0 = 0; // make gcc happy while (len > 0) { n = font->getNextChar(p, len, &code, u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, &dx, &dy, &originX, &originY); //~ this doesn't handle the case where the origin offset changes //~ within a string of characters -- which could be fixed by //~ modifying dx,dy as needed for each character if (p == s->getCString()) { originX0 = originX; originY0 = originY; } dx *= state->getFontSize(); dy *= state->getFontSize(); if (wMode) { dy += state->getCharSpace(); if (n == 1 && *p == ' ') { dy += state->getWordSpace(); } } else { dx += state->getCharSpace(); if (n == 1 && *p == ' ') { dx += state->getWordSpace(); } } dx *= state->getHorizScaling(); if (font->isCIDFont()) { if (uMap) { if (nChars + uLen > dxdySize) { do { dxdySize *= 2; } while (nChars + uLen > dxdySize); dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); } for (i = 0; i < uLen; ++i) { m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); for (j = 0; j < m; ++j) { s2->append(buf[j]); } //~ this really needs to get the number of chars in the target //~ encoding - which may be more than the number of Unicode //~ chars dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } else { if (nChars + 1 > dxdySize) { dxdySize *= 2; dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); } s2->append((char)((code >> 8) & 0xff)); s2->append((char)(code & 0xff)); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } else { if (!codeToGID || codeToGID[code] >= 0) { s2->append((char)code); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } p += n; len -= n; } if (uMap) { uMap->decRefCnt(); } originX0 *= state->getFontSize(); originY0 *= state->getFontSize(); state->textTransformDelta(originX0, originY0, &tOriginX0, &tOriginY0); if (nChars > 0) { if (wMode) { writePSFmt("{0:.6g} {1:.6g} rmoveto\n", -tOriginX0, -tOriginY0); } writePSString(s2); writePS("\n["); for (i = 0; i < 2 * nChars; ++i) { if (i > 0) { writePS("\n"); } writePSFmt("{0:.6g}", dxdy[i]); } if (font->getType() == fontType3) { writePS("] Tj3\n"); } else { writePS("] Tj\n"); } if (wMode) { writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0); } } gfree(dxdy); delete s2; if ((state->getRender() & 4) && font->getType() != fontType3) { haveTextClip = gTrue; } noStateChanges = gFalse; } void PSOutputDev::endTextObject(GfxState *state) { if (haveTextClip) { writePS("Tclip\n"); haveTextClip = gFalse; noStateChanges = gFalse; } } void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { int len; len = height * ((width + 7) / 8); switch (level) { case psLevel1: case psLevel1Sep: doImageL1(ref, state, NULL, invert, inlineImg, str, width, height, len); break; case psLevel2: case psLevel2Gray: case psLevel2Sep: doImageL2(ref, state, NULL, invert, inlineImg, str, width, height, len, NULL, NULL, 0, 0, gFalse); break; case psLevel3: case psLevel3Gray: case psLevel3Sep: doImageL3(ref, state, NULL, invert, inlineImg, str, width, height, len, NULL, NULL, 0, 0, gFalse); break; } noStateChanges = gFalse; } void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg, GBool interpolate) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); switch (level) { case psLevel1: doImageL1(ref, state, colorMap, gFalse, inlineImg, str, width, height, len); break; case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(state, colorMap, gFalse, inlineImg, str, width, height, len); break; case psLevel2: case psLevel2Gray: case psLevel2Sep: doImageL2(ref, state, colorMap, gFalse, inlineImg, str, width, height, len, maskColors, NULL, 0, 0, gFalse); break; case psLevel3: case psLevel3Gray: case psLevel3Sep: doImageL3(ref, state, colorMap, gFalse, inlineImg, str, width, height, len, maskColors, NULL, 0, 0, gFalse); break; } t3Cacheable = gFalse; noStateChanges = gFalse; } void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Object *maskRef, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); switch (level) { case psLevel1: doImageL1(ref, state, colorMap, gFalse, gFalse, str, width, height, len); break; case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(state, colorMap, gFalse, gFalse, str, width, height, len); break; case psLevel2: case psLevel2Gray: case psLevel2Sep: doImageL2(ref, state, colorMap, gFalse, gFalse, str, width, height, len, NULL, maskStr, maskWidth, maskHeight, maskInvert); break; case psLevel3: case psLevel3Gray: case psLevel3Sep: doImageL3(ref, state, colorMap, gFalse, gFalse, str, width, height, len, NULL, maskStr, maskWidth, maskHeight, maskInvert); break; } t3Cacheable = gFalse; noStateChanges = gFalse; } void PSOutputDev::doImageL1(Object *ref, GfxState *state, 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((char)c); ++col; // each line is: "<...data...>" // 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, state->getRenderingIntent()); 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(GfxState *state, 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 *)gmallocn(width, 4); // 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, state->getRenderingIntent()); 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, GfxState *state, 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 useLZW, useRLE, useASCII, useASCIIHex, useCompressed; GfxSeparationColorSpace *sepCS; GfxColor color; GfxCMYK cmyk; char buf[4096]; int c, col, i; // 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 convertColorKeyMaskToClipRects(colorMap, str, width, height, maskColors); // explicit masking } else if (maskStr) { convertExplicitMaskToClipRects(maskStr, maskWidth, maskHeight, maskInvert); } // color space if (colorMap && !(level == psLevel2Gray || level == psLevel3Gray)) { dumpColorSpaceL2(state, 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); if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { str2 = new GrayRecoder(str2, width, height, colorMap); } if (globalParams->getPSLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); } if (useASCIIHex) { str2 = new ASCIIHexEncoder(str2); } else { str2 = new ASCII85Encoder(str2); } str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar((char)c); ++col; } else { writePSChar((char)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((char)c); ++col; } } // each line is: "<~...data...~>" // so max data length = 255 - 6 = 249 // chunks are 1 or 5 bytes each, so we have to stop at 245 // but make it 240 just to be safe if (col > 240) { writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); // add an extra entry because the LZWDecode/RunLengthDecode // filter may read past the end writePS("<>]\n"); writePS("0\n"); str2->close(); delete str2; } else { // 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 || level == psLevel2Gray || level == psLevel3Gray)) { 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 (level == psLevel2Gray || level == psLevel3Gray) { writePS("0 1"); } 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 { pdfImStr }\n"); } else { writePS(" /DataSource currentfile\n"); } // filters if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; useLZW = useRLE = gFalse; useCompressed = gFalse; useASCII = gFalse; } else { s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " ", !inlineImg); if ((colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN || level == psLevel2Gray || level == psLevel3Gray)) || inlineImg || !s) { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useASCII = !(mode == psModeForm || inType3Char || preload); useCompressed = gFalse; } else { useLZW = useRLE = gFalse; useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preload); useCompressed = gTrue; } } if (useASCII) { writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } if (useCompressed) { writePS(s->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 to grayscale if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { str = new GrayRecoder(str, width, height, colorMap); // recode DeviceN data } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { str = new DeviceNRecoder(str, width, height, colorMap); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCII) { if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } } // end of image dictionary writePS(">>\n"); #if OPI_SUPPORT if (opi13Nest) { if (inlineImg) { // this can't happen -- OPI dictionaries are in XObjects error(errSyntaxError, -1, "OPI in inline image"); n = 0; } else { // need to read the stream to count characters -- the length // is data-dependent (because of ASCII and LZW/RunLength // filters) str->reset(); n = 0; do { i = str->discardChars(4096); n += i; } while (i == 4096); 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, state->getRenderingIntent()); 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 ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } 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 (useLZW || useRLE || useASCII || inlineImg) { delete str; } } if ((maskColors && colorMap && !inlineImg) || maskStr) { writePS("pdfImClipEnd\n"); } } // Convert color key masking to a clipping region consisting of a // sequence of clip rectangles. void PSOutputDev::convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap, Stream *str, int width, int height, int *maskColors) { ImageStream *imgStr; Guchar *line; PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; GBool emitRect, addRect, extendRect; int numComps, i, j, x0, x1, y; 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} {1:d}\n", width, height); 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); } writePS("pop pop pdfImClip\n"); gfree(rectsOut); gfree(rects0); gfree(rects1); delete imgStr; str->close(); } // Convert an explicit mask image to a clipping region consisting of a // sequence of clip rectangles. void PSOutputDev::convertExplicitMaskToClipRects(Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert) { ImageStream *imgStr; Guchar *line; PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; GBool emitRect, addRect, extendRect; int i, x0, x1, y, maskXor; imgStr = new ImageStream(maskStr, maskWidth, 1, 1); imgStr->reset(); rects0Len = rects1Len = rectsOutLen = 0; rectsSize = rectsOutSize = 64; rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); maskXor = maskInvert ? 1 : 0; for (y = 0; y < maskHeight; ++y) { if (!(line = imgStr->getLine())) { break; } i = 0; rects1Len = 0; for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; while (x0 < maskWidth || i < rects0Len) { emitRect = addRect = extendRect = 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} {1:d}\n", maskWidth, maskHeight); 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); } writePS("pop pop pdfImClip\n"); gfree(rectsOut); gfree(rects0); gfree(rects1); delete imgStr; maskStr->close(); } //~ this doesn't currently support OPI void PSOutputDev::doImageL3(Object *ref, GfxState *state, 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 useLZW, useRLE, useASCII, useASCIIHex, useCompressed; GBool maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed; GString *maskFilters; GfxSeparationColorSpace *sepCS; GfxColor color; GfxCMYK cmyk; char buf[4096]; int c; int col, i; useASCIIHex = globalParams->getPSASCIIHex(); useLZW = useRLE = useASCII = useCompressed = gFalse; // make gcc happy maskUseLZW = maskUseRLE = maskUseASCII = gFalse; // make gcc happy maskUseCompressed = gFalse; // make gcc happy maskFilters = NULL; // make gcc happy // explicit masking // -- this also converts color key masking in grayscale mode if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { // mask data source if (maskColors && colorMap && level == psLevel3Gray) { s = NULL; if (mode == psModeForm || inType3Char || preload) { if (globalParams->getPSUncompressPreloadedImages()) { maskUseLZW = maskUseRLE = gFalse; } else if (globalParams->getPSLZW()) { maskUseLZW = gTrue; maskUseRLE = gFalse; } else { maskUseRLE = gTrue; maskUseLZW = gFalse; } maskUseASCII = gFalse; maskUseCompressed = gFalse; } else { if (globalParams->getPSLZW()) { maskUseLZW = gTrue; maskUseRLE = gFalse; } else { maskUseRLE = gTrue; maskUseLZW = gFalse; } maskUseASCII = gTrue; } maskUseCompressed = gFalse; maskWidth = width; maskHeight = height; maskInvert = gFalse; } else if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; maskUseLZW = maskUseRLE = gFalse; maskUseCompressed = gFalse; maskUseASCII = gFalse; } else { s = maskStr->getPSFilter(3, " ", !inlineImg); if (!s) { if (globalParams->getPSLZW()) { maskUseLZW = gTrue; maskUseRLE = gFalse; } else { maskUseRLE = gTrue; maskUseLZW = gFalse; } maskUseASCII = !(mode == psModeForm || inType3Char || preload); maskUseCompressed = gFalse; } else { maskUseLZW = maskUseRLE = gFalse; maskUseASCII = maskStr->isBinary() && !(mode == psModeForm || inType3Char || preload); maskUseCompressed = gTrue; } } maskFilters = new GString(); if (maskUseASCII) { maskFilters->appendf(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (maskUseLZW) { maskFilters->append(" /LZWDecode filter\n"); } else if (maskUseRLE) { maskFilters->append(" /RunLengthDecode filter\n"); } if (maskUseCompressed) { maskFilters->append(s); } if (s) { delete s; } if (mode == psModeForm || inType3Char || preload) { writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", ref->getRefNum(), ref->getRefGen()); } else { writePS("currentfile\n"); writePS(maskFilters->getCString()); writePS("pdfMask\n"); // add the ColorKeyToMask filter if (maskColors && colorMap && level == psLevel3Gray) { maskStr = new ColorKeyToMaskEncoder(str, width, height, colorMap, maskColors); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (maskUseCompressed) { maskStr = maskStr->getUndecodedStream(); } if (maskUseLZW) { maskStr = new LZWEncoder(maskStr); } else if (maskUseRLE) { maskStr = new RunLengthEncoder(maskStr); } if (maskUseASCII) { if (useASCIIHex) { maskStr = new ASCIIHexEncoder(maskStr); } else { maskStr = new ASCII85Encoder(maskStr); } } // copy the stream data maskStr->reset(); while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } maskStr->close(); writePSChar('\n'); writePS("%-EOD-\n"); // delete encoders if (maskUseLZW || maskUseRLE || maskUseASCII) { delete maskStr; } } } // color space if (colorMap && level != psLevel3Gray) { dumpColorSpaceL2(state, 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); if (colorMap && level == psLevel3Gray) { str2 = new GrayRecoder(str2, width, height, colorMap); } if (globalParams->getPSLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); } if (useASCIIHex) { str2 = new ASCIIHexEncoder(str2); } else { str2 = new ASCII85Encoder(str2); } str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar((char)c); ++col; } else { writePSChar((char)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((char)c); ++col; } } // each line is: "<~...data...~>" // so max data length = 255 - 6 = 249 // chunks are 1 or 5 bytes each, so we have to stop at 245 // but make it 240 just to be safe if (col > 240) { writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); // add an extra entry because the LZWDecode/RunLengthDecode // filter may read past the end writePS("<>]\n"); writePS("0\n"); str2->close(); delete str2; } else { // 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 || (maskColors && colorMap && level == psLevel3Gray)) { writePS("<<\n /ImageType 3\n"); writePS(" /InterleaveType 3\n"); writePS(" /DataDict\n"); } // image (data) dictionary writePSFmt("<<\n /ImageType {0:d}\n", (maskColors && colorMap && level != psLevel3Gray) ? 4 : 1); // color key masking if (maskColors && colorMap && level != psLevel3Gray) { 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 && level == psLevel3Gray) { writePS(" /BitsPerComponent 8\n"); } else { writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1); } // decode if (colorMap) { writePS(" /Decode ["); if (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 (level == psLevel3Gray) { 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 { pdfImStr }\n"); } else { writePS(" /DataSource currentfile\n"); } // filters if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; useLZW = useRLE = gFalse; useCompressed = gFalse; useASCII = gFalse; } else { s = str->getPSFilter(3, " ", !inlineImg); if ((colorMap && level == psLevel3Gray) || inlineImg || !s) { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useASCII = !(mode == psModeForm || inType3Char || preload); useCompressed = gFalse; } else { useLZW = useRLE = gFalse; useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preload); useCompressed = gTrue; } } if (useASCII) { writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } if (useCompressed) { writePS(s->getCString()); } if (s) { delete s; } // end of image (data) dictionary writePS(">>\n"); // explicit masking if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { writePS(" /MaskDict\n"); writePS("<<\n"); writePS(" /ImageType 1\n"); writePSFmt(" /Width {0:d}\n", maskWidth); writePSFmt(" /Height {0:d}\n", maskHeight); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", maskWidth, -maskHeight, maskHeight); writePS(" /BitsPerComponent 1\n"); writePSFmt(" /Decode [{0:d} {1:d}]\n", maskInvert ? 1 : 0, maskInvert ? 0 : 1); // mask data source if (mode == psModeForm || inType3Char || preload) { writePS(" /DataSource {pdfMaskSrc}\n"); writePS(maskFilters->getCString()); } else { writePS(" /DataSource maskStream\n"); } delete maskFilters; writePS(">>\n"); writePS(">>\n"); } if (mode == psModeForm || inType3Char || preload) { // image command writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask"); } else { if (level == psLevel3Sep && colorMap && colorMap->getColorSpace()->getMode() == csSeparation) { color.c[0] = gfxColorComp1; sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); } else { writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); } } // get rid of the array and index if (mode == psModeForm || inType3Char || 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 to grayscale if (colorMap && level == psLevel3Gray) { str = new GrayRecoder(str, width, height, colorMap); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCII) { if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } } // copy the stream data str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); // add newline and trailer to the end writePSChar('\n'); writePS("%-EOD-\n"); // delete encoders if (useLZW || useRLE || useASCII || inlineImg) { delete str; } } // close the mask stream if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { if (!(mode == psModeForm || inType3Char || preload)) { writePS("pdfMaskEnd\n"); } } } void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, GBool genXform, GBool updateColors, GBool map01) { switch (colorSpace->getMode()) { case csDeviceGray: dumpDeviceGrayColorSpace((GfxDeviceGrayColorSpace *)colorSpace, genXform, updateColors, map01); break; case csCalGray: dumpCalGrayColorSpace((GfxCalGrayColorSpace *)colorSpace, genXform, updateColors, map01); break; case csDeviceRGB: dumpDeviceRGBColorSpace((GfxDeviceRGBColorSpace *)colorSpace, genXform, updateColors, map01); break; case csCalRGB: dumpCalRGBColorSpace((GfxCalRGBColorSpace *)colorSpace, genXform, updateColors, map01); break; case csDeviceCMYK: dumpDeviceCMYKColorSpace((GfxDeviceCMYKColorSpace *)colorSpace, genXform, updateColors, map01); break; case csLab: dumpLabColorSpace((GfxLabColorSpace *)colorSpace, genXform, updateColors, map01); break; case csICCBased: dumpICCBasedColorSpace(state, (GfxICCBasedColorSpace *)colorSpace, genXform, updateColors, map01); break; case csIndexed: dumpIndexedColorSpace(state, (GfxIndexedColorSpace *)colorSpace, genXform, updateColors, map01); break; case csSeparation: dumpSeparationColorSpace(state, (GfxSeparationColorSpace *)colorSpace, genXform, updateColors, map01); break; case csDeviceN: if (level >= psLevel3) { dumpDeviceNColorSpaceL3(state, (GfxDeviceNColorSpace *)colorSpace, genXform, updateColors, map01); } else { dumpDeviceNColorSpaceL2(state, (GfxDeviceNColorSpace *)colorSpace, genXform, updateColors, map01); } break; case csPattern: //~ unimplemented break; } } void PSOutputDev::dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("/DeviceGray"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessBlack; } } void PSOutputDev::dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("[/CIEBasedA <<\n"); writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", cs->getGamma()); writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessBlack; } } void PSOutputDev::dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("/DeviceRGB"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } } void PSOutputDev::dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("[/CIEBasedABC <<\n"); writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n", cs->getGammaR(), cs->getGammaG(), cs->getGammaB()); writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n", cs->getMatrix()[0], cs->getMatrix()[1], cs->getMatrix()[2], cs->getMatrix()[3], cs->getMatrix()[4], cs->getMatrix()[5], cs->getMatrix()[6], cs->getMatrix()[7], cs->getMatrix()[8]); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } } void PSOutputDev::dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("/DeviceCMYK"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } } void PSOutputDev::dumpLabColorSpace(GfxLabColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { 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", (cs->getAMax() - cs->getAMin()) / 500.0, cs->getAMin() / 500.0, (cs->getBMax() - cs->getBMin()) / 200.0, cs->getBMin() / 200.0); } else { writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n", cs->getAMin(), cs->getAMax(), cs->getBMin(), cs->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", cs->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", cs->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", cs->getWhiteZ()); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } } void PSOutputDev::dumpICCBasedColorSpace(GfxState *state, GfxICCBasedColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { // there is no transform function to the alternate color space, so // we can use it directly dumpColorSpaceL2(state, cs->getAlt(), genXform, updateColors, gFalse); } void PSOutputDev::dumpIndexedColorSpace(GfxState *state, GfxIndexedColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { GfxColorSpace *baseCS; GfxLabColorSpace *labCS; 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; baseCS = cs->getBase(); writePS("[/Indexed "); dumpColorSpaceL2(state, baseCS, gFalse, updateColors, gTrue); n = cs->getIndexHigh(); numComps = baseCS->getNComps(); lookup = cs->getLookup(); writePSFmt(" {0:d} <\n", n); if (baseCS->getMode() == csDeviceN && level < psLevel3) { func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); baseCS->getDefaultRanges(low, range, cs->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); cs->getCMYK(&color, &cmyk, state->getRenderingIntent()); 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); cs->getCMYK(&color, &cmyk, state->getRenderingIntent()); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } } writePS("\n"); } } writePS(">]"); if (genXform) { writePS(" {}"); } } void PSOutputDev::dumpSeparationColorSpace(GfxState *state, GfxSeparationColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("[/Separation "); writePSString(cs->getName()); writePS(" "); dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse); writePS("\n"); cvtFunction(cs->getFunc()); writePS("]"); if (genXform) { writePS(" {}"); } if (updateColors) { addCustomColor(state, cs); } } void PSOutputDev::dumpDeviceNColorSpaceL2(GfxState *state, GfxDeviceNColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { dumpColorSpaceL2(state, cs->getAlt(), gFalse, updateColors, map01); if (genXform) { writePS(" "); cvtFunction(cs->getTintTransformFunc()); } } void PSOutputDev::dumpDeviceNColorSpaceL3(GfxState *state, GfxDeviceNColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { GString *tint; int i; writePS("[/DeviceN [\n"); for (i = 0; i < cs->getNComps(); ++i) { writePSString(cs->getColorantName(i)); writePS("\n"); } writePS("]\n"); if ((tint = createDeviceNTintFunc(cs))) { writePS("/DeviceCMYK\n"); writePS(tint->getCString()); delete tint; } else { dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse); writePS("\n"); cvtFunction(cs->getTintTransformFunc()); } writePS("]"); if (genXform) { writePS(" {}"); } if (updateColors) { addCustomColors(state, cs); } } // If the DeviceN color space has a Colorants dictionary, and all of // the colorants are one of: "None", "Cyan", "Magenta", "Yellow", // "Black", or have an entry in the Colorants dict that maps to // DeviceCMYK, then build a new tint function; else use the existing // tint function. GString *PSOutputDev::createDeviceNTintFunc(GfxDeviceNColorSpace *cs) { Object *attrs; Object colorants, sepCSObj, funcObj, obj1; GString *name; Function *func; double sepIn; double cmyk[gfxColorMaxComps][4]; GString *tint; GBool first; int i, j; attrs = cs->getAttrs(); if (!attrs->isDict()) { return NULL; } if (!attrs->dictLookup("Colorants", &colorants)->isDict()) { colorants.free(); return NULL; } for (i = 0; i < cs->getNComps(); ++i) { name = cs->getColorantName(i); if (!name->cmp("None")) { cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0; } else if (!name->cmp("Cyan")) { cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0; cmyk[i][0] = 1; } else if (!name->cmp("Magenta")) { cmyk[i][0] = cmyk[i][2] = cmyk[i][3] = 0; cmyk[i][1] = 1; } else if (!name->cmp("Yellow")) { cmyk[i][0] = cmyk[i][1] = cmyk[i][3] = 0; cmyk[i][2] = 1; } else if (!name->cmp("Black")) { cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = 0; cmyk[i][3] = 1; } else { colorants.dictLookup(name->getCString(), &sepCSObj); if (!sepCSObj.isArray() || sepCSObj.arrayGetLength() != 4) { sepCSObj.free(); colorants.free(); return NULL; } if (!sepCSObj.arrayGet(0, &obj1)->isName("Separation")) { obj1.free(); sepCSObj.free(); colorants.free(); return NULL; } obj1.free(); if (!sepCSObj.arrayGet(2, &obj1)->isName("DeviceCMYK")) { obj1.free(); sepCSObj.free(); colorants.free(); return NULL; } obj1.free(); sepCSObj.arrayGet(3, &funcObj); if (!(func = Function::parse(&funcObj, 1, 4))) { funcObj.free(); sepCSObj.free(); colorants.free(); return NULL; } funcObj.free(); sepIn = 1; func->transform(&sepIn, cmyk[i]); delete func; sepCSObj.free(); } } colorants.free(); tint = new GString(); tint->append("{\n"); for (j = 0; j < 4; ++j) { // C, M, Y, K first = gTrue; for (i = 0; i < cs->getNComps(); ++i) { if (cmyk[i][j] != 0) { tint->appendf("{0:d} index {1:.4f} mul{2:s}\n", j + cs->getNComps() - 1 - i, cmyk[i][j], first ? "" : " add"); first = gFalse; } } if (first) { tint->append("0\n"); } } tint->appendf("{0:d} 4 roll\n", cs->getNComps() + 4); for (i = 0; i < cs->getNComps(); ++i) { tint->append("pop\n"); } tint->append("}\n"); return tint; } #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:.6g} {1:.6g}\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:.6g} {1:.6g} {2:.6g} {3:.6g}\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:.6g} {1:.6g} setcharwidth\n", wx, wy); writePS("q\n"); t3NeedsRestore = gTrue; noStateChanges = gFalse; } void PSOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { if (t3String) { error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc"); return; } t3WX = wx; t3WY = wy; t3LLX = llx; t3LLY = lly; t3URX = urx; t3URY = ury; t3String = new GString(); writePS("q\n"); t3FillColorOnly = gTrue; t3Cacheable = gTrue; t3NeedsRestore = gTrue; noStateChanges = gFalse; } void PSOutputDev::drawForm(Ref id) { writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen); noStateChanges = gFalse; } void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) { Stream *str; char buf[4096]; int n; if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) { str = level1Stream; } else { str = psStream; } str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); noStateChanges = gFalse; } //~ 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:.6g}\n", func0->getSamples()[i]); } writePS("] def\n"); writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2); // [e01] [efrac] x0 x1 ... xm-1 for (i = m-1; i >= 0; --i) { // [e01] [efrac] x0 x1 ... xi writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n", func0->getDomainMin(i), (func0->getEncodeMax(i) - func0->getEncodeMin(i)) / (func0->getDomainMax(i) - func0->getDomainMin(i)), func0->getEncodeMin(i)); // [e01] [efrac] x0 x1 ... xi-1 xi' writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n", func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1); // [e01] [efrac] x0 x1 ... xi-1 xi' writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n"); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i); // [e01] [efrac] x0 x1 ... xi-1 } // [e01] [efrac] for (i = 0; i < n; ++i) { // [e01] [efrac] y(0) ... y(i-1) for (j = 0; j < (1<> 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) <2^(m-j)-k s values> writePSFmt("{0:d} index {1:d} get dup\n", i + k/2 + (1 << (m-j)) - k, j); writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n"); writePSFmt("{0:d} 1 roll\n", k/2 + (1 << (m-j)) - k - 1); } // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1) } // [e01] [efrac] y(0) ... y(i-1) s writePSFmt("{0:.6g} mul {1:.6g} add\n", func0->getDecodeMax(i) - func0->getDecodeMin(i), func0->getDecodeMin(i)); writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func0->getRangeMin(i), func0->getRangeMin(i), func0->getRangeMax(i), func0->getRangeMax(i)); // [e01] [efrac] y(0) ... y(i-1) y(i) } // [e01] [efrac] y(0) ... y(n-1) writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n); break; case 2: // exponential func2 = (ExponentialFunction *)func; n = func2->getOutputSize(); writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getDomainMin(0), func2->getDomainMin(0), func2->getDomainMax(0), func2->getDomainMax(0)); // x for (i = 0; i < n; ++i) { // x y(0) .. y(i-1) writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n", i, func2->getE(), func2->getC1()[i] - func2->getC0()[i], func2->getC0()[i]); if (func2->getHasRange()) { writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getRangeMin(i), func2->getRangeMin(i), func2->getRangeMax(i), func2->getRangeMax(i)); } } // x y(0) .. y(n-1) writePSFmt("{0:d} {1:d} roll pop }}\n", n+1, n); 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:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func3->getDomainMin(0), func3->getDomainMin(0), func3->getDomainMax(0), func3->getDomainMax(0)); for (i = 0; i < func3->getNumFuncs() - 1; ++i) { writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n", func3->getBounds()[i+1], func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2*i], thisFunc, i); } writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n", func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2*i], thisFunc, i); for (i = 0; i < func3->getNumFuncs() - 1; ++i) { writePS("} ifelse\n"); } 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::writePSBlock(char *s, int len) { if (t3String) { t3String->append(s, len); } else { (*outputFunc)(outputStream, s, len); } } void PSOutputDev::writePS(const char *s) { if (t3String) { t3String->append(s); } else { (*outputFunc)(outputStream, s, (int)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) { snprintf(buf, sizeof(buf), "\\%03o", *p); writePS(buf); line += 4; } else { writePSChar((char)*p); ++line; } } writePSChar(')'); } void PSOutputDev::writePSName(const char *s) { const char *p; char c; p = s; while ((c = *p++)) { if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%') { 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 == '%') { snprintf(buf, sizeof(buf), "#%02x", c & 0xff); name2->append(buf); } else { name2->append(c); } } return name2; } // Write a DSC-compliant . void PSOutputDev::writePSTextLine(GString *s) { TextString *ts; Unicode *u; int i, j; 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 // instead of , so we escape a leading paren ts = new TextString(s); u = ts->getUnicode(); for (i = 0, j = 0; i < ts->getLength() && j < 200; ++i) { c = u[i] & 0xff; if (c == '\\') { writePS("\\\\"); j += 2; } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) { writePSFmt("\\{0:03o}", c); j += 4; } else { writePSChar((char)c); ++j; } } writePS("\n"); delete ts; }