aboutsummaryrefslogtreecommitdiff
path: root/xpdf/PSOutputDev.cc
diff options
context:
space:
mode:
Diffstat (limited to 'xpdf/PSOutputDev.cc')
-rw-r--r--xpdf/PSOutputDev.cc8448
1 files changed, 8448 insertions, 0 deletions
diff --git a/xpdf/PSOutputDev.cc b/xpdf/PSOutputDev.cc
new file mode 100644
index 0000000..b9fff4f
--- /dev/null
+++ b/xpdf/PSOutputDev.cc
@@ -0,0 +1,8448 @@
+//========================================================================
+//
+// PSOutputDev.cc
+//
+// Copyright 1996-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <math.h>
+#include "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> <ffff>",
+ " endcodespacerange",
+ " 0 usefont",
+ " 1 begincidrange",
+ " <0000> <ffff> 0",
+ " endcidrange",
+ " endcmap",
+ " currentdict CMapName exch /CMap defineresource pop",
+ "end",
+ "10 dict begin",
+ " begincmap",
+ " /CMapType 1 def",
+ " /CMapName /Identity-V def",
+ " /CIDSystemInfo 3 dict dup begin",
+ " /Registry (Adobe) def",
+ " /Ordering (Identity) def",
+ " /Supplement 0 def",
+ " end def",
+ " /WMode 1 def",
+ " 1 begincodespacerange",
+ " <0000> <ffff>",
+ " endcodespacerange",
+ " 0 usefont",
+ " 1 begincidrange",
+ " <0000> <ffff> 0",
+ " endcidrange",
+ " endcmap",
+ " currentdict CMapName exch /CMap defineresource pop",
+ "end",
+ "end",
+ NULL
+};
+
+//------------------------------------------------------------------------
+// Fonts
+//------------------------------------------------------------------------
+
+struct PSSubstFont {
+ 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 <in> is ASCIIHex-encoded, decode it, delete <in>, and return the
+// binary version. Else return <in> 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<eol>"
+ // so max data length = 255 - 20 = 235
+ // chunks are 1 or 4 bytes each, so we have to stop at 232
+ // but make it 225 just to be safe
+ if (col > 225) {
+ writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
+ ++line;
+ writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line);
+ col = 0;
+ }
+ } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
+ writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
+ if (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...><eol>"
+ // so max data length = 255 - 4 = 251
+ // but make it 240 just to be safe
+ // chunks are 2 bytes each, so we need to stop on an even col number
+ if (col == 240) {
+ writePS(">\n<");
+ col = 0;
+ }
+ } while (c != '>' && c != EOF);
+ writePS(">]\n");
+ writePS("0\n");
+ str->close();
+ delete str;
+ } else {
+ // set up to use the array already created by setupImages()
+ writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
+ }
+ }
+
+ // image/imagemask command
+ if ((inType3Char || preload) && !colorMap) {
+ writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n",
+ width, height, invert ? "true" : "false",
+ width, -height, height);
+ } else if (colorMap) {
+ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
+ width, height,
+ width, -height, height);
+ } else {
+ writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n",
+ width, height, invert ? "true" : "false",
+ width, -height, height);
+ }
+
+ // image data
+ if (!((inType3Char || preload) && !colorMap)) {
+
+ if (colorMap) {
+
+ // set up to process the data stream
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+
+ // process the data stream
+ i = 0;
+ for (y = 0; y < height; ++y) {
+
+ // write the line
+ for (x = 0; x < width; ++x) {
+ imgStr->getPixel(pixBuf);
+ colorMap->getGray(pixBuf, &gray, 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...~><eol>"
+ // so max data length = 255 - 6 = 249
+ // chunks are 1 or 5 bytes each, so we have to stop at 245
+ // but make it 240 just to be safe
+ if (col > 240) {
+ writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
+ col = 0;
+ }
+ } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
+ writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
+ writePS("<>]\n");
+ writePS("0\n");
+ str2->close();
+ delete str2;
+ } else {
+ // 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...~><eol>"
+ // so max data length = 255 - 6 = 249
+ // chunks are 1 or 5 bytes each, so we have to stop at 245
+ // but make it 240 just to be safe
+ if (col > 240) {
+ writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
+ col = 0;
+ }
+ } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
+ writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
+ writePS("<>]\n");
+ writePS("0\n");
+ str2->close();
+ delete str2;
+ } else {
+ // 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<<m); ++j) {
+ // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
+ writePSFmt("xpdfSamples{0:d}\n", thisFunc);
+ k = m - 1;
+ writePSFmt("{0:d} index {1:d} get\n", i+j+2, 2 * k + ((j >> k) & 1));
+ for (k = m - 2; k >= 0; --k) {
+ writePSFmt("{0:d} mul {1:d} index {2:d} get add\n",
+ func0->getSampleSize(k),
+ i + j + 3,
+ 2 * k + ((j >> k) & 1));
+ }
+ if (n > 1) {
+ writePSFmt("{0:d} mul {1:d} add ", n, i);
+ }
+ writePS("get\n");
+ }
+ // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
+ for (j = 0; j < m; ++j) {
+ // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
+ for (k = 0; k < (1 << (m - j)); k += 2) {
+ // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
+ writePSFmt("{0:d} index {1:d} get dup\n",
+ i + k/2 + (1 << (m-j)) - k, j);
+ writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
+ writePSFmt("{0:d} 1 roll\n", k/2 + (1 << (m-j)) - k - 1);
+ }
+ // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
+ }
+ // [e01] [efrac] y(0) ... y(i-1) s
+ writePSFmt("{0:.6g} mul {1:.6g} add\n",
+ func0->getDecodeMax(i) - func0->getDecodeMin(i),
+ func0->getDecodeMin(i));
+ writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
+ func0->getRangeMin(i), func0->getRangeMin(i),
+ func0->getRangeMax(i), func0->getRangeMax(i));
+ // [e01] [efrac] y(0) ... y(i-1) y(i)
+ }
+ // [e01] [efrac] y(0) ... y(n-1)
+ writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n);
+ 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 <textline>.
+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 <text>
+ // instead of <textline>, 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;
+}