X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;ds=inline;f=pdf%2Fxpdf%2FPSOutputDev.cc;h=28811a8320593679d91553dc4aa227c648d94eee;hb=bebd9ceae1ec88ddee03bda8c7572c9cb06f6b77;hp=53cf39daababcc245433b4b548720e7a287f3051;hpb=7aac8dc8533347e21311b15186e0af82f1b22fd6;p=evince.git diff --git a/pdf/xpdf/PSOutputDev.cc b/pdf/xpdf/PSOutputDev.cc index 53cf39da..28811a83 100644 --- a/pdf/xpdf/PSOutputDev.cc +++ b/pdf/xpdf/PSOutputDev.cc @@ -2,11 +2,13 @@ // // PSOutputDev.cc // -// Copyright 1996 Derek B. Noonburg +// Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== -#ifdef __GNUC__ +#include + +#ifdef USE_GCC_PRAGMAS #pragma implementation #endif @@ -16,48 +18,29 @@ #include #include #include "GString.h" -#include "config.h" +#include "GList.h" +#include "xpdfconfig.h" +#include "GlobalParams.h" #include "Object.h" #include "Error.h" +#include "Function.h" +#include "Gfx.h" #include "GfxState.h" #include "GfxFont.h" -#include "FontFile.h" +#include "UnicodeMap.h" +#include "FoFiType1C.h" +#include "FoFiTrueType.h" #include "Catalog.h" #include "Page.h" #include "Stream.h" -#include "FormWidget.h" +#include "Annot.h" #include "PSOutputDev.h" -#if JAPANESE_SUPPORT -#include "Japan12ToRKSJ.h" -#endif - #ifdef MACOS // needed for setting type/creator of MacOS files #include "ICSupport.h" #endif -//------------------------------------------------------------------------ -// Parameters -//------------------------------------------------------------------------ - -// Generate Level 1 PostScript? -GBool psOutLevel1 = gFalse; - -// Generate Level 1 separable PostScript? -GBool psOutLevel1Sep = gFalse; - -// Generate Encapsulated PostScript? -GBool psOutEPS = gFalse; - -#if OPI_SUPPORT -// Generate OPI comments? -GBool psOutOPI = gFalse; -#endif - -int paperWidth = defPaperWidth; -int paperHeight = defPaperHeight; - //------------------------------------------------------------------------ // PostScript prolog and setup //------------------------------------------------------------------------ @@ -65,21 +48,41 @@ int paperHeight = defPaperHeight; static char *prolog[] = { "/xpdf 75 dict def xpdf begin", "% PDF special state", - "/pdfDictSize 14 def", + "/pdfDictSize 15 def", + "~1", + "/pdfStates 64 array def", + " 0 1 63 {", + " pdfStates exch pdfDictSize dict", + " dup /pdfStateIdx 3 index put", + " put", + " } for", + "~a", "/pdfSetup {", - " 2 array astore", + " 3 1 roll 2 array astore", " /setpagedevice where {", - " pop 3 dict dup begin", - " exch /PageSize exch def", + " pop 3 dict begin", + " /PageSize exch def", " /ImagingBBox null def", " /Policies 1 dict dup begin /PageSize 3 def end def", - " end setpagedevice", + " { /Duplex true def } if", + " currentdict end setpagedevice", " } {", - " pop", + " pop pop", " } ifelse", "} def", + "~1", + "/pdfOpNames [", + " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", + " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender", + " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", + "] def", + "~a", "/pdfStartPage {", + "~1", + " pdfStates 0 get begin", + "~2", " pdfDictSize dict begin", + "~a", " /pdfFill [0] def", " /pdfStroke [0] def", " /pdfLastFill false def", @@ -91,21 +94,86 @@ static char *prolog[] = { " /pdfTextRise 0 def", " /pdfWordSpacing 0 def", " /pdfHorizScaling 1 def", + " /pdfTextClipPath [] def", "} def", "/pdfEndPage { end } def", + "% 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", + "% PDF color state", "/sCol {", " pdfLastStroke not {", " pdfStroke aload length", - " dup 1 eq { pop setgray }", - " { 3 eq { setrgbcolor } { setcmykcolor } ifelse } ifelse", + " dup 1 eq {", + " pop setgray", + " }{", + " dup 3 eq {", + " pop setrgbcolor", + " }{", + " 4 eq {", + " setcmykcolor", + " }{", + " findcmykcustomcolor exch setcustomcolor", + " } ifelse", + " } ifelse", + " } ifelse", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "/fCol {", " pdfLastFill not {", " pdfFill aload length", - " dup 1 eq { pop setgray }", - " { 3 eq { setrgbcolor } { setcmykcolor } ifelse } ifelse", + " dup 1 eq {", + " pop setgray", + " }{", + " dup 3 eq {", + " pop setrgbcolor", + " }{", + " 4 eq {", + " setcmykcolor", + " }{", + " findcmykcustomcolor exch setcustomcolor", + " } ifelse", + " } ifelse", + " } ifelse", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", @@ -120,9 +188,39 @@ static char *prolog[] = { " end", " definefont pop", "} def", - "/pdfMakeFont16 { findfont 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", + "/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", "% graphics state operators", + "~1", + "/q {", + " gsave", + " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for", + " pdfStates pdfStateIdx 1 add get begin", + " pdfOpNames { exch def } forall", + "} def", + "~2", "/q { gsave pdfDictSize dict begin } def", + "~a", "/Q { end grestore } def", "/cm { concat } def", "/d { setdash } def", @@ -144,6 +242,12 @@ static char *prolog[] = { " /pdfLastFill true def /pdfLastStroke false def } def", "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def } def", + "/ck { 6 copy 6 array astore /pdfFill exch def", + " findcmykcustomcolor exch setcustomcolor", + " /pdfLastFill true def /pdfLastStroke false def } def", + "/CK { 6 copy 6 array astore /pdfStroke exch def", + " findcmykcustomcolor exch setcustomcolor", + " /pdfLastStroke true def /pdfLastFill false def } def", "% path segment operators", "/m { moveto } def", "/l { lineto } def", @@ -153,6 +257,7 @@ static char *prolog[] = { "/h { closepath } def", "% path painting operators", "/S { sCol stroke } def", + "/Sf { fCol stroke } def", "/f { fCol fill } def", "/f* { fCol eofill } def", "% clipping operators", @@ -172,17 +277,101 @@ static char *prolog[] = { "/Td { pdfTextMat transform moveto } def", "/Tm { /pdfTextMat exch def } def", "% text string operators", - "/Tj { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse", - " 0 pdfTextRise pdfTextMat dtransform rmoveto", - " pdfFontSize mul pdfHorizScaling mul", - " 1 index stringwidth pdfTextMat idtransform pop", - " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse", - " pdfWordSpacing 0 pdfTextMat dtransform 32", - " 4 3 roll pdfCharSpacing add 0 pdfTextMat dtransform", - " 6 5 roll awidthshow", - " 0 pdfTextRise neg pdfTextMat dtransform rmoveto } def", + "/cshow where {", + " pop", + " /cshow2 {", + " dup {", + " pop pop", + " 1 string dup 0 3 index put 3 index exec", + " } exch cshow", + " pop pop", + " } def", + "}{", + " /cshow2 {", + " currentfont /FontType get 0 eq {", + " 0 2 2 index length 1 sub {", + " 2 copy get exch 1 add 2 index exch get", + " 2 copy exch 256 mul add", + " 2 string dup 0 6 5 roll put dup 1 5 4 roll put", + " 3 index exec", + " } for", + " } {", + " dup {", + " 1 string dup 0 3 index put 3 index exec", + " } forall", + " } ifelse", + " pop pop", + " } def", + "} ifelse", + "/awcp {", // awidthcharpath + " exch {", + " false charpath", + " 5 index 5 index rmoveto", + " 6 index eq { 7 index 7 index rmoveto } if", + " } exch cshow2", + " 6 {pop} repeat", + "} def", + "/Tj {", + " fCol", // because stringwidth has to draw Type 3 chars + " 1 index stringwidth pdfTextMat idtransform pop", + " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse", + " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", + " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", + " pdfTextMat dtransform", + " 6 5 roll Tj1", + "} def", + "/Tj16 {", + " fCol", // because stringwidth has to draw Type 3 chars + " 2 index stringwidth pdfTextMat idtransform pop", + " sub exch div", + " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32", + " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0", + " pdfTextMat dtransform", + " 6 5 roll Tj1", + "} def", + "/Tj16V {", + " fCol", // because stringwidth has to draw Type 3 chars + " 2 index stringwidth pdfTextMat idtransform exch pop", + " sub exch div", + " 0 pdfWordSpacing pdfTextMat dtransform 32", + " 4 3 roll pdfCharSpacing add 0 exch", + " pdfTextMat dtransform", + " 6 5 roll Tj1", + "} def", + "/Tj1 {", + " 0 pdfTextRise pdfTextMat dtransform rmoveto", + " currentpoint 8 2 roll", + " pdfTextRender 1 and 0 eq {", + " 6 copy awidthshow", + " } if", + " pdfTextRender 3 and dup 1 eq exch 2 eq or {", + " 7 index 7 index moveto", + " 6 copy", + " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", + " false awcp currentpoint stroke moveto", + " } if", + " pdfTextRender 4 and 0 ne {", + " 8 6 roll moveto", + " false awcp", + " /pdfTextClipPath [ pdfTextClipPath aload pop", + " {/moveto cvx}", + " {/lineto cvx}", + " {/curveto cvx}", + " {/closepath cvx}", + " pathforall ] def", + " currentpoint newpath moveto", + " } {", + " 8 {pop} repeat", + " } ifelse", + " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", + "} def", "/TJm { pdfFontSize 0.001 mul mul neg 0", " pdfTextMat dtransform rmoveto } def", + "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch", + " pdfTextMat dtransform rmoveto } def", + "/Tclip { pdfTextClipPath cvx exec clip newpath", + " /pdfTextClipPath [] def } def", + "~1", "% Level 1 image operators", "/pdfIm1 {", " /pdfImBuf1 4 index string def", @@ -203,6 +392,11 @@ static char *prolog[] = { " /pdfImBuf1 4 index 7 add 8 idiv string def", " { currentfile pdfImBuf1 readhexstring pop } imagemask", "} def", + "/pdfImM1a {", + " { 2 copy get exch 1 add exch } imagemask", + " pop pop", + "} def", + "~2", "% Level 2 image operators", "/pdfImBuf 100 string def", "/pdfIm {", @@ -211,12 +405,76 @@ static char *prolog[] = { " not { pop exit } if", " (%-EOD-) eq { exit } if } loop", "} def", + "/pdfImSep {", + " findcmykcustomcolor exch", + " dup /Width get /pdfImBuf1 exch string def", + " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", + " /pdfImDecodeLow exch def", + " begin Width Height BitsPerComponent ImageMatrix DataSource end", + " /pdfImData exch def", + " { pdfImData pdfImBuf1 readstring pop", + " 0 1 2 index length 1 sub {", + " 1 index exch 2 copy get", + " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", + " 255 exch sub put", + " } for }", + " 6 5 roll customcolorimage", + " { currentfile pdfImBuf readline", + " not { pop exit } if", + " (%-EOD-) eq { exit } if } loop", + "} def", "/pdfImM {", " fCol imagemask", " { currentfile pdfImBuf readline", " not { pop exit } if", " (%-EOD-) eq { exit } if } loop", "} def", + "~a", + "end", + NULL +}; + +static char *cmapProlog[] = { + "/CIDInit /ProcSet findresource begin", + "10 dict begin", + " begincmap", + " /CMapType 1 def", + " /CMapName /Identity-H def", + " /CIDSystemInfo 3 dict dup begin", + " /Registry (Adobe) def", + " /Ordering (Identity) def", + " /Supplement 0 def", + " end def", + " 1 begincodespacerange", + " <0000> ", + " endcodespacerange", + " 0 usefont", + " 1 begincidrange", + " <0000> 0", + " endcidrange", + " endcmap", + " currentdict CMapName exch /CMap defineresource pop", + "end", + "10 dict begin", + " begincmap", + " /CMapType 1 def", + " /CMapName /Identity-V def", + " /CIDSystemInfo 3 dict dup begin", + " /Registry (Adobe) def", + " /Ordering (Identity) def", + " /Supplement 0 def", + " end def", + " /WMode 1 def", + " 1 begincodespacerange", + " <0000> ", + " endcodespacerange", + " 0 usefont", + " 1 begincidrange", + " <0000> 0", + " endcidrange", + " endcmap", + " currentdict CMapName exch /CMap defineresource pop", + "end", "end", NULL }; @@ -225,32 +483,27 @@ static char *prolog[] = { // Fonts //------------------------------------------------------------------------ -struct PSFont { - char *name; // PDF name - char *psName; // PostScript name -}; - struct PSSubstFont { char *psName; // PostScript name double mWidth; // width of 'm' character }; -static PSFont psFonts[] = { - {"Courier", "Courier"}, - {"Courier-Bold", "Courier-Bold"}, - {"Courier-Oblique", "Courier-Bold"}, - {"Courier-BoldOblique", "Courier-BoldOblique"}, - {"Helvetica", "Helvetica"}, - {"Helvetica-Bold", "Helvetica-Bold"}, - {"Helvetica-Oblique", "Helvetica-Oblique"}, - {"Helvetica-BoldOblique", "Helvetica-BoldOblique"}, - {"Symbol", "Symbol"}, - {"Times-Roman", "Times-Roman"}, - {"Times-Bold", "Times-Bold"}, - {"Times-Italic", "Times-Italic"}, - {"Times-BoldItalic", "Times-BoldItalic"}, - {"ZapfDingbats", "ZapfDingbats"}, - {NULL} +static char *psFonts[] = { + "Courier", + "Courier-Bold", + "Courier-Oblique", + "Courier-BoldOblique", + "Helvetica", + "Helvetica-Bold", + "Helvetica-Oblique", + "Helvetica-BoldOblique", + "Symbol", + "Times-Roman", + "Times-Bold", + "Times-Italic", + "Times-BoldItalic", + "ZapfDingbats", + NULL }; static PSSubstFont psSubstFonts[] = { @@ -268,42 +521,176 @@ static PSSubstFont psSubstFonts[] = { {"Courier-BoldOblique", 0.600} }; +// Encoding info for substitute 16-bit font +struct PSFont16Enc { + Ref fontID; + GString *enc; +}; + //------------------------------------------------------------------------ -// PSOutputDev +// process colors //------------------------------------------------------------------------ -PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog, - int firstPage, int lastPage, - GBool embedType11, GBool doForm1) { - Page *page; - Dict *resDict; - FormWidgets *formWidgets; - char **p; - int pg; - Object obj1, obj2; +#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; +} + +//------------------------------------------------------------------------ +// DeviceNRecoder +//------------------------------------------------------------------------ + +class DeviceNRecoder: public FilterStream { +public: + + DeviceNRecoder(Stream *strA, int widthA, int heightA, + GfxImageColorMap *colorMapA); + virtual ~DeviceNRecoder(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual int getChar() + { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } + virtual int lookChar() + { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } + virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; } + virtual GBool isBinary(GBool last = gTrue) { return gTrue; } + virtual GBool isEncoder() { return gTrue; } + +private: + + GBool fillBuf(); + + int width, height; + GfxImageColorMap *colorMap; + Function *func; + ImageStream *imgStr; + int buf[gfxColorMaxComps]; + int pixelIdx; + int bufIdx; + int bufSize; +}; + +DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, + GfxImageColorMap *colorMapA): + FilterStream(strA) { + width = widthA; + height = heightA; + colorMap = colorMapA; + imgStr = NULL; + pixelIdx = 0; + bufIdx = gfxColorMaxComps; + bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> + getAlt()->getNComps(); + func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> + getTintTransformFunc(); +} + +DeviceNRecoder::~DeviceNRecoder() { + if (imgStr) { + delete imgStr; + } +} + +void DeviceNRecoder::reset() { + imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), + colorMap->getBits()); + imgStr->reset(); +} + +GBool DeviceNRecoder::fillBuf() { + Guchar pixBuf[gfxColorMaxComps]; + GfxColor color; + double y[gfxColorMaxComps]; int i; - // initialize - embedType1 = embedType11; - doForm = doForm1; + if (pixelIdx >= width * height) { + return gFalse; + } + imgStr->getPixel(pixBuf); + colorMap->getColor(pixBuf, &color); + func->transform(color.c, y); + for (i = 0; i < bufSize; ++i) { + buf[i] = (int)(y[i] * 255 + 0.5); + } + bufIdx = 0; + ++pixelIdx; + return gTrue; +} + +//------------------------------------------------------------------------ +// PSOutputDev +//------------------------------------------------------------------------ + +extern "C" { +typedef void (*SignalFunc)(int); +} + +static void outputToFile(void *stream, char *data, int len) { + fwrite(data, 1, len, (FILE *)stream); +} + +PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, + GBool manualCtrlA) { + FILE *f; + PSFileType fileTypeA; + + underlayCbk = NULL; + underlayCbkData = NULL; + overlayCbk = NULL; + overlayCbkData = NULL; + fontIDs = NULL; fontFileIDs = NULL; fontFileNames = NULL; + font16Enc = NULL; + xobjStack = NULL; embFontList = NULL; - f = NULL; - if (doForm) - lastPage = firstPage; + customColors = NULL; + haveTextClip = gFalse; + t3String = NULL; // open file or pipe - ok = gTrue; if (!strcmp(fileName, "-")) { - fileType = psStdout; + fileTypeA = psStdout; f = stdout; } else if (fileName[0] == '|') { - fileType = psPipe; + fileTypeA = psPipe; #ifdef HAVE_POPEN #ifndef WIN32 - signal(SIGPIPE, (void (*)(int))SIG_IGN); + signal(SIGPIPE, (SignalFunc)SIG_IGN); #endif if (!(f = popen(fileName + 1, "w"))) { error(-1, "Couldn't run print command '%s'", fileName); @@ -316,7 +703,7 @@ PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog, return; #endif } else { - fileType = psFile; + fileTypeA = psFile; if (!(f = fopen(fileName, "w"))) { error(-1, "Couldn't open PostScript file '%s'", fileName); ok = gFalse; @@ -324,6 +711,94 @@ PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog, } } + init(outputToFile, f, fileTypeA, + xrefA, catalog, firstPage, lastPage, modeA, + imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); +} + +PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, + XRef *xrefA, Catalog *catalog, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, + GBool manualCtrlA) { + underlayCbk = NULL; + underlayCbkData = NULL; + overlayCbk = NULL; + overlayCbkData = NULL; + + fontIDs = NULL; + fontFileIDs = NULL; + fontFileNames = NULL; + font16Enc = NULL; + xobjStack = NULL; + embFontList = NULL; + customColors = NULL; + haveTextClip = gFalse; + t3String = NULL; + + init(outputFuncA, outputStreamA, psGeneric, + xrefA, catalog, firstPage, lastPage, modeA, + imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA); +} + +void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, + PSFileType fileTypeA, XRef *xrefA, Catalog *catalog, + int firstPage, int lastPage, PSOutMode modeA, + int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, + GBool manualCtrlA) { + Page *page; + PDFRectangle *box; + + // initialize + ok = gTrue; + outputFunc = outputFuncA; + outputStream = outputStreamA; + fileType = fileTypeA; + xref = xrefA; + level = globalParams->getPSLevel(); + mode = modeA; + paperWidth = globalParams->getPSPaperWidth(); + paperHeight = globalParams->getPSPaperHeight(); + imgLLX = imgLLXA; + imgLLY = imgLLYA; + imgURX = imgURXA; + imgURY = imgURYA; + if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) { + globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY); + } + if (paperWidth < 0 || paperHeight < 0) { + // this check is needed in case the document has zero pages + if (firstPage > 0 && firstPage <= catalog->getNumPages()) { + page = catalog->getPage(firstPage); + paperWidth = (int)(page->getWidth() + 0.5); + paperHeight = (int)(page->getHeight() + 0.5); + } else { + paperWidth = 1; + paperHeight = 1; + } + imgLLX = imgLLY = 0; + imgURX = paperWidth; + imgURY = paperHeight; + } + 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 = 0; + xScale0 = yScale0 = 1; + rotate0 = 0; + clipLLX0 = clipLLY0 = 0; + clipURX0 = clipURY0 = -1; + // initialize fontIDs, fontFileIDs, and fontFileNames lists fontIDSize = 64; fontIDLen = 0; @@ -334,157 +809,68 @@ PSOutputDev::PSOutputDev(char *fileName, Catalog *catalog, fontFileNameSize = 64; fontFileNameLen = 0; fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *)); + nextTrueTypeNum = 0; + font16EncLen = 0; + font16EncSize = 0; + + xobjStack = new GList(); + numSaves = 0; // initialize embedded font resource comment list embFontList = new GString(); - // write header - if (doForm) { - writePS("%%!PS-Adobe-3.0 Resource-Form\n"); - writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); - writePS("%%%%LanguageLevel: %d\n", - (psOutLevel1 || psOutLevel1Sep) ? 1 : 2); - if (psOutLevel1Sep) { - writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n"); - } - writePS("%%%%EndComments\n"); - page = catalog->getPage(firstPage); - writePS("32 dict dup begin\n"); - writePS("/BBox [%d %d %d %d] def\n", - (int)page->getX1(), (int)page->getY1(), - (int)page->getX2(), (int)page->getY2()); - writePS("/FormType 1 def\n"); - writePS("/Matrix [1 0 0 1 0 0] def\n"); - } else if (psOutEPS) { - writePS("%%!PS-Adobe-3.0 EPSF-3.0\n"); - writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); - writePS("%%%%LanguageLevel: %d\n", - (psOutLevel1 || psOutLevel1Sep) ? 1 : 2); - if (psOutLevel1Sep) { - writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n"); - } - page = catalog->getPage(firstPage); - writePS("%%%%BoundingBox: %d %d %d %d\n", - (int)floor(page->getX1()), (int)floor(page->getY1()), - (int)ceil(page->getX2()), (int)ceil(page->getY2())); - if (floor(page->getX1()) != ceil(page->getX1()) || - floor(page->getY1()) != ceil(page->getY1()) || - floor(page->getX2()) != ceil(page->getX2()) || - floor(page->getY2()) != ceil(page->getY2())) { - writePS("%%%%HiResBoundingBox: %g %g %g %g\n", - page->getX1(), page->getY1(), - page->getX2(), page->getY2()); - } - writePS("%%%%DocumentSuppliedResources: (atend)\n"); - writePS("%%%%EndComments\n"); - } else { - writePS("%%!PS-Adobe-3.0\n"); - writePS("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); - writePS("%%%%LanguageLevel: %d\n", - (psOutLevel1 || psOutLevel1Sep) ? 1 : 2); - if (psOutLevel1Sep) { - writePS("%%%%DocumentProcessColors: Cyan Magenta Yellow Black\n"); - } - writePS("%%%%DocumentMedia: plain %d %d 0 () ()\n", - paperWidth, paperHeight); - writePS("%%%%Pages: %d\n", lastPage - firstPage + 1); - writePS("%%%%EndComments\n"); - writePS("%%%%BeginDefaults\n"); - writePS("%%%%PageMedia: plain\n"); - writePS("%%%%EndDefaults\n"); - } - - // write prolog - if (!doForm) { - writePS("%%%%BeginProlog\n"); - } - writePS("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion); - for (p = prolog; *p; ++p) { - writePS("%s\n", *p); - } - writePS("%%%%EndResource\n"); - if (!doForm) { - writePS("%%%%EndProlog\n"); - } - - // set up fonts and images - type3Warning = gFalse; - if (doForm) { - // swap the form and xpdf dicts - writePS("xpdf end begin dup begin\n"); - } else { - writePS("%%%%BeginSetup\n"); - writePS("xpdf begin\n"); - } - for (pg = firstPage; pg <= lastPage; ++pg) { - page = catalog->getPage(pg); - if ((resDict = page->getResourceDict())) { - setupResources(resDict); + if (!manualCtrl) { + // this check is needed in case the document has zero pages + if (firstPage > 0 && firstPage <= catalog->getNumPages()) { + writeHeader(firstPage, lastPage, + catalog->getPage(firstPage)->getBox(), + catalog->getPage(firstPage)->getCropBox()); + } else { + box = new PDFRectangle(0, 0, 1, 1); + writeHeader(firstPage, lastPage, box, box); + delete box; } - formWidgets = new FormWidgets(page->getAnnots(&obj1)); - obj1.free(); - for (i = 0; i < formWidgets->getNumWidgets(); ++i) { - if (formWidgets->getWidget(i)->getAppearance(&obj1)->isStream()) { - obj1.streamGetDict()->lookup("Resources", &obj2); - if (obj2.isDict()) { - setupResources(obj2.getDict()); - } - obj2.free(); - } - obj1.free(); + if (mode != psModeForm) { + writePS("%%BeginProlog\n"); } - delete formWidgets; - } - if (!doForm) { -#if OPI_SUPPORT - if (psOutOPI) { - writePS("/opiMatrix matrix currentmatrix def\n"); + writeXpdfProcset(); + if (mode != psModeForm) { + writePS("%%EndProlog\n"); + writePS("%%BeginSetup\n"); } -#endif - if (!psOutEPS) { - writePS("%d %d pdfSetup\n", paperWidth, paperHeight); + writeDocSetup(catalog, firstPage, lastPage); + if (mode != psModeForm) { + writePS("%%EndSetup\n"); } - writePS("%%%%EndSetup\n"); } // initialize sequential page number seqPage = 1; - -#if OPI_SUPPORT - // initialize OPI nesting levels - opi13Nest = 0; - opi20Nest = 0; -#endif } PSOutputDev::~PSOutputDev() { + PSOutCustomColor *cc; int i; - if (f) { - if (doForm) { - writePS("/Foo exch /Form defineresource pop\n"); - } else if (psOutEPS) { - writePS("%%%%Trailer\n"); - writePS("end\n"); - writePS("%%%%DocumentSuppliedResources:\n"); - writePS("%s", embFontList->getCString()); - writePS("%%%%EOF\n"); - } else { - writePS("%%%%Trailer\n"); - writePS("end\n"); - writePS("%%%%EOF\n"); + if (ok) { + if (!manualCtrl) { + writePS("%%Trailer\n"); + writeTrailer(); + if (mode != psModeForm) { + writePS("%%EOF\n"); + } } if (fileType == psFile) { #ifdef MACOS - ICS_MapRefNumAndAssign((short)f->handle); + ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle); #endif - fclose(f); + fclose((FILE *)outputStream); } #ifdef HAVE_POPEN else if (fileType == psPipe) { - pclose(f); + pclose((FILE *)outputStream); #ifndef WIN32 - signal(SIGPIPE, (void (*)(int))SIG_DFL); + signal(SIGPIPE, (SignalFunc)SIG_DFL); #endif } #endif @@ -504,11 +890,215 @@ PSOutputDev::~PSOutputDev() { } gfree(fontFileNames); } + if (font16Enc) { + for (i = 0; i < font16EncLen; ++i) { + delete font16Enc[i].enc; + } + gfree(font16Enc); + } + if (xobjStack) { + delete xobjStack; + } + while (customColors) { + cc = customColors; + customColors = cc->next; + delete cc; + } +} + +void PSOutputDev::writeHeader(int firstPage, int lastPage, + PDFRectangle *mediaBox, PDFRectangle *cropBox) { + switch (mode) { + case psModePS: + writePS("%!PS-Adobe-3.0\n"); + writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); + writePSFmt("%%%%LanguageLevel: %d\n", + (level == psLevel1 || level == psLevel1Sep) ? 1 : + (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); + if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { + writePS("%%DocumentProcessColors: (atend)\n"); + writePS("%%DocumentCustomColors: (atend)\n"); + } + writePS("%%DocumentSuppliedResources: (atend)\n"); + writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n", + paperWidth, paperHeight); + writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight); + writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1); + writePS("%%EndComments\n"); + writePS("%%BeginDefaults\n"); + writePS("%%PageMedia: plain\n"); + writePS("%%EndDefaults\n"); + break; + case psModeEPS: + writePS("%!PS-Adobe-3.0 EPSF-3.0\n"); + writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); + writePSFmt("%%%%LanguageLevel: %d\n", + (level == psLevel1 || level == psLevel1Sep) ? 1 : + (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); + if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { + writePS("%%DocumentProcessColors: (atend)\n"); + writePS("%%DocumentCustomColors: (atend)\n"); + } + writePSFmt("%%%%BoundingBox: %d %d %d %d\n", + (int)floor(cropBox->x1), (int)floor(cropBox->y1), + (int)ceil(cropBox->x2), (int)ceil(cropBox->y2)); + if (floor(cropBox->x1) != ceil(cropBox->x1) || + floor(cropBox->y1) != ceil(cropBox->y1) || + floor(cropBox->x2) != ceil(cropBox->x2) || + floor(cropBox->y2) != ceil(cropBox->y2)) { + writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n", + cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2); + } + writePS("%%DocumentSuppliedResources: (atend)\n"); + writePS("%%EndComments\n"); + break; + case psModeForm: + writePS("%!PS-Adobe-3.0 Resource-Form\n"); + writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion); + writePSFmt("%%%%LanguageLevel: %d\n", + (level == psLevel1 || level == psLevel1Sep) ? 1 : + (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); + if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { + writePS("%%DocumentProcessColors: (atend)\n"); + writePS("%%DocumentCustomColors: (atend)\n"); + } + writePS("%%DocumentSuppliedResources: (atend)\n"); + writePS("%%EndComments\n"); + writePS("32 dict dup begin\n"); + writePSFmt("/BBox [%d %d %d %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() { + char prologLevel; + char **p; + + writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion); + prologLevel = 'a'; + for (p = prolog; *p; ++p) { + if ((*p)[0] == '~' && (*p)[1] == '1') { + prologLevel = '1'; + } else if ((*p)[0] == '~' && (*p)[1] == '2') { + prologLevel = '2'; + } else if ((*p)[0] == '~' && (*p)[1] == 'a') { + prologLevel = 'a'; + } else if (prologLevel == 'a' || + (prologLevel == '1' && level < psLevel2) || + (prologLevel == '2' && level >= psLevel2)) { + writePSFmt("%s\n", *p); + } + } + writePS("%%EndResource\n"); + + if (level >= psLevel3) { + for (p = cmapProlog; *p; ++p) { + writePSFmt("%s\n", *p); + } + } +} + +void PSOutputDev::writeDocSetup(Catalog *catalog, + int firstPage, int lastPage) { + Page *page; + Dict *resDict; + Annots *annots; + Object obj1, obj2; + int pg, i; + + if (mode == psModeForm) { + // swap the form and xpdf dicts + writePS("xpdf end begin dup begin\n"); + } else { + writePS("xpdf begin\n"); + } + for (pg = firstPage; pg <= lastPage; ++pg) { + page = catalog->getPage(pg); + if ((resDict = page->getResourceDict())) { + setupResources(resDict); + } + annots = new Annots(xref, page->getAnnots(&obj1)); + obj1.free(); + for (i = 0; i < annots->getNumAnnots(); ++i) { + if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) { + obj1.streamGetDict()->lookup("Resources", &obj2); + if (obj2.isDict()) { + setupResources(obj2.getDict()); + } + obj2.free(); + } + obj1.free(); + } + delete annots; + } + if (mode != psModeForm) { + if (mode != psModeEPS && !manualCtrl) { + writePSFmt("%d %d %s pdfSetup\n", + paperWidth, paperHeight, + globalParams->getPSDuplex() ? "true" : "false"); + } +#if OPI_SUPPORT + if (globalParams->getPSOPI()) { + writePS("/opiMatrix matrix currentmatrix def\n"); + } +#endif + } +} + +void PSOutputDev::writePageTrailer() { + if (mode != psModeForm) { + writePS("pdfEndPage\n"); + } +} + +void PSOutputDev::writeTrailer() { + PSOutCustomColor *cc; + + if (mode == psModeForm) { + writePS("/Foo exch /Form defineresource pop\n"); + } else { + writePS("end\n"); + writePS("%%DocumentSuppliedResources:\n"); + writePS(embFontList->getCString()); + if (level == psLevel1Sep || level == psLevel2Sep || + level == psLevel3Sep) { + writePS("%%DocumentProcessColors:"); + if (processColors & psProcessCyan) { + writePS(" Cyan"); + } + if (processColors & psProcessMagenta) { + writePS(" Magenta"); + } + if (processColors & psProcessYellow) { + writePS(" Yellow"); + } + if (processColors & psProcessBlack) { + writePS(" Black"); + } + writePS("\n"); + writePS("%%DocumentCustomColors:"); + for (cc = customColors; cc; cc = cc->next) { + writePSFmt(" (%s)", cc->name->getCString()); + } + writePS("\n"); + writePS("%%CMYKCustomColor:\n"); + for (cc = customColors; cc; cc = cc->next) { + writePSFmt("%%%%+ %g %g %g %g (%s)\n", + cc->c, cc->m, cc->y, cc->k, cc->name->getCString()); + } + } + } } void PSOutputDev::setupResources(Dict *resDict) { - Object xObjDict, xObj, resObj; - int i; + Object xObjDict, xObjRef, xObj, resObj; + Ref ref0, ref1; + GBool skip; + int i, j; setupFonts(resDict); setupImages(resDict); @@ -516,45 +1106,85 @@ void PSOutputDev::setupResources(Dict *resDict) { resDict->lookup("XObject", &xObjDict); if (xObjDict.isDict()) { for (i = 0; i < xObjDict.dictGetLength(); ++i) { - xObjDict.dictGetVal(i, &xObj); - if (xObj.isStream()) { - xObj.streamGetDict()->lookup("Resources", &resObj); - if (resObj.isDict()) { - setupResources(resObj.getDict()); + + // avoid infinite recursion on XObjects + skip = gFalse; + if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) { + ref0 = xObjRef.getRef(); + for (j = 0; j < xobjStack->getLength(); ++j) { + ref1 = *(Ref *)xobjStack->get(j); + if (ref1.num == ref0.num && ref1.gen == ref0.gen) { + skip = gTrue; + break; + } + } + if (!skip) { + xobjStack->append(&ref0); } - resObj.free(); } - xObj.free(); + if (!skip) { + + // process the XObject's resource dictionary + xObjDict.dictGetVal(i, &xObj); + if (xObj.isStream()) { + xObj.streamGetDict()->lookup("Resources", &resObj); + if (resObj.isDict()) { + setupResources(resObj.getDict()); + } + resObj.free(); + } + xObj.free(); + } + + if (xObjRef.isRef() && !skip) { + xobjStack->del(xobjStack->getLength() - 1); + } + xObjRef.free(); } } xObjDict.free(); } void PSOutputDev::setupFonts(Dict *resDict) { - Object fontDict; + Object obj1, obj2; + Ref r; GfxFontDict *gfxFontDict; GfxFont *font; int i; - resDict->lookup("Font", &fontDict); - if (fontDict.isDict()) { - gfxFontDict = new GfxFontDict(fontDict.getDict()); + 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) { - font = gfxFontDict->getFont(i); - setupFont(font); + if ((font = gfxFontDict->getFont(i))) { + setupFont(font, resDict); + } } delete gfxFontDict; } - fontDict.free(); + obj1.free(); } -void PSOutputDev::setupFont(GfxFont *font) { +void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { Ref fontFileID; GString *name; - char *psName; + PSFontParam *fontParam; + GString *psName; + char type3Name[64], buf[16]; + GBool subst; + UnicodeMap *uMap; char *charName; double xs, ys; - GBool do16Bit; int code; double w1, w2; double *fm; @@ -562,9 +1192,10 @@ void PSOutputDev::setupFont(GfxFont *font) { // check if font is already set up for (i = 0; i < fontIDLen; ++i) { - if (fontIDs[i].num == font->getID().num && - fontIDs[i].gen == font->getID().gen) + if (fontIDs[i].num == font->getID()->num && + fontIDs[i].gen == font->getID()->gen) { return; + } } // add entry to fontIDs list @@ -572,64 +1203,108 @@ void PSOutputDev::setupFont(GfxFont *font) { fontIDSize += 64; fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref)); } - fontIDs[fontIDLen++] = font->getID(); + fontIDs[fontIDLen++] = *font->getID(); xs = ys = 1; - do16Bit = gFalse; + subst = gFalse; + + // check for resident 8-bit font + if (font->getName() && + (fontParam = globalParams->getPSFont(font->getName()))) { + psName = new GString(fontParam->psFontName->getCString()); // check for embedded Type 1 font - if (embedType1 && font->getType() == fontType1 && - font->getEmbeddedFontID(&fontFileID)) { - psName = font->getEmbeddedFontName(); + } else if (globalParams->getPSEmbedType1() && + font->getType() == fontType1 && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); setupEmbeddedType1Font(&fontFileID, psName); + // check for embedded Type 1C font + } else if (globalParams->getPSEmbedType1() && + font->getType() == fontType1C && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); + setupEmbeddedType1CFont(font, &fontFileID, psName); + // check for external Type 1 font file - } else if (embedType1 && font->getType() == fontType1 && + } else if (globalParams->getPSEmbedType1() && + font->getType() == fontType1 && font->getExtFontFile()) { // this assumes that the PS font name matches the PDF font name - psName = font->getName()->getCString(); - setupEmbeddedType1Font(font->getExtFontFile(), psName); + psName = font->getName()->copy(); + setupExternalType1Font(font->getExtFontFile(), psName); - // check for embedded Type 1C font - } else if (embedType1 && font->getType() == fontType1C && + // check for embedded TrueType font + } else if (globalParams->getPSEmbedTrueType() && + font->getType() == fontTrueType && font->getEmbeddedFontID(&fontFileID)) { - psName = font->getEmbeddedFontName(); - setupEmbeddedType1CFont(font, &fontFileID, psName); + psName = filterPSName(font->getEmbeddedFontName()); + setupEmbeddedTrueTypeFont(font, &fontFileID, psName); - } else if (font->is16Bit() && font->getCharSet16() == font16AdobeJapan12) { - psName = "Ryumin-Light-RKSJ"; - do16Bit = gTrue; + // check for external TrueType font file + } else if (globalParams->getPSEmbedTrueType() && + font->getType() == fontTrueType && + font->getExtFontFile()) { + psName = filterPSName(font->getName()); + setupExternalTrueTypeFont(font, psName); - // do font substitution - } else { - if (!type3Warning && font->getType() == fontType3) { - error(-1, "This document uses Type 3 fonts - some text may not be correctly printed"); - type3Warning = gTrue; - } + // check for embedded CID PostScript font + } else if (globalParams->getPSEmbedCIDPostScript() && + font->getType() == fontCIDType0C && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); + setupEmbeddedCIDType0Font(font, &fontFileID, psName); + + // check for embedded CID TrueType font + } else if (globalParams->getPSEmbedCIDTrueType() && + font->getType() == fontCIDType2 && + font->getEmbeddedFontID(&fontFileID)) { + psName = filterPSName(font->getEmbeddedFontName()); + setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName); + + } else if (font->getType() == fontType3) { + sprintf(type3Name, "T3_%d_%d", + font->getID()->num, font->getID()->gen); + psName = new GString(type3Name); + setupType3Font(font, psName, parentResDict); + + // do 8-bit font substitution + } else if (!font->isCIDFont()) { + subst = gTrue; name = font->getName(); psName = NULL; if (name) { - for (i = 0; psFonts[i].name; ++i) { - if (name->cmp(psFonts[i].name) == 0) { - psName = psFonts[i].psName; + for (i = 0; psFonts[i]; ++i) { + if (name->cmp(psFonts[i]) == 0) { + psName = new GString(psFonts[i]); break; } } } if (!psName) { - if (font->isFixedWidth()) + if (font->isFixedWidth()) { i = 8; - else if (font->isSerif()) + } else if (font->isSerif()) { i = 4; - else + } else { i = 0; - if (font->isBold()) + } + if (font->isBold()) { i += 2; - if (font->isItalic()) + } + if (font->isItalic()) { i += 1; - psName = psSubstFonts[i].psName; - if ((code = font->getCharCode("m")) >= 0) { - w1 = font->getWidth(code); + } + psName = new GString(psSubstFonts[i].psName); + for (code = 0; code < 256; ++code) { + if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && + charName[0] == 'm' && charName[1] == '\0') { + break; + } + } + if (code < 256) { + w1 = ((Gfx8BitFont *)font)->getWidth(code); } else { w1 = 0; } @@ -652,32 +1327,86 @@ void PSOutputDev::setupFont(GfxFont *font) { ys = 1; } } + + // do 16-bit font substitution + } else if ((fontParam = globalParams-> + getPSFont16(font->getName(), + ((GfxCIDFont *)font)->getCollection(), + font->getWMode()))) { + subst = gTrue; + psName = fontParam->psFontName->copy(); + if (font16EncLen >= font16EncSize) { + font16EncSize += 16; + font16Enc = (PSFont16Enc *)grealloc(font16Enc, + font16EncSize * sizeof(PSFont16Enc)); + } + font16Enc[font16EncLen].fontID = *font->getID(); + font16Enc[font16EncLen].enc = fontParam->encoding->copy(); + if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) { + uMap->decRefCnt(); + ++font16EncLen; + } else { + error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'", + font16Enc[font16EncLen].enc->getCString()); + } + + // give up - can't do anything with this font + } else { + error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)", + font->getName() ? font->getName()->getCString() : "(unnamed)", + ((GfxCIDFont *)font)->getCollection() + ? ((GfxCIDFont *)font)->getCollection()->getCString() + : "(unknown)"); + return; } // generate PostScript code to set up the font - if (do16Bit) { - writePS("/F%d_%d /%s pdfMakeFont16\n", - font->getID().num, font->getID().gen, psName); + if (font->isCIDFont()) { + if (level == psLevel3 || level == psLevel3Sep) { + writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n", + font->getID()->num, font->getID()->gen, psName->getCString(), + font->getWMode()); + } else { + writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n", + font->getID()->num, font->getID()->gen, psName->getCString(), + font->getWMode()); + } } else { - writePS("/F%d_%d /%s %g %g\n", - font->getID().num, font->getID().gen, psName, xs, ys); + writePSFmt("/F%d_%d /%s %g %g\n", + font->getID()->num, font->getID()->gen, psName->getCString(), + xs, ys); for (i = 0; i < 256; i += 8) { - writePS((i == 0) ? "[ " : " "); + writePSFmt((i == 0) ? "[ " : " "); for (j = 0; j < 8; ++j) { - charName = font->getCharName(i+j); - writePS("/%s", charName ? charName : ".notdef"); + if (font->getType() == fontTrueType && + !subst && + !((Gfx8BitFont *)font)->getHasEncoding()) { + sprintf(buf, "c%02x", i+j); + charName = buf; + } else { + charName = ((Gfx8BitFont *)font)->getCharName(i+j); + // this is a kludge for broken PDF files that encode char 32 + // as .notdef + if (i+j == 32 && charName && !strcmp(charName, ".notdef")) { + charName = "space"; + } + } + writePS("/"); + writePSName(charName ? charName : (char *)".notdef"); } - writePS((i == 256-8) ? "]\n" : "\n"); + writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n"); } writePS("pdfMakeFont\n"); } + + delete psName; } -void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) { +void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) { static char hexChar[17] = "0123456789abcdef"; - Object refObj, strObj, obj1, obj2; + Object refObj, strObj, obj1, obj2, obj3; Dict *dict; - int length1, length2; + int length1, length2, length3; int c; int start[4]; GBool binMode; @@ -699,7 +1428,7 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) { // get the font stream and info refObj.initRef(id->num, id->gen); - refObj.fetch(&strObj); + refObj.fetch(xref, &strObj); refObj.free(); if (!strObj.isStream()) { error(-1, "Embedded font file object is not a stream"); @@ -711,29 +1440,32 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) { } dict->lookup("Length1", &obj1); dict->lookup("Length2", &obj2); - if (!obj1.isInt() || !obj2.isInt()) { + dict->lookup("Length3", &obj3); + if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) { error(-1, "Missing length fields in embedded font stream dictionary"); obj1.free(); obj2.free(); + obj3.free(); goto err1; } length1 = obj1.getInt(); length2 = obj2.getInt(); + length3 = obj3.getInt(); obj1.free(); obj2.free(); + obj3.free(); // beginning comment - if (psOutEPS) { - writePS("%%%%BeginResource: font %s\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName); - embFontList->append("\n"); - } + writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); // copy ASCII portion of font strObj.streamReset(); - for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) - fputc(c, f); + for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) { + writePSChar(c); + } // figure out if encrypted portion is binary or ASCII binMode = gFalse; @@ -752,41 +1484,48 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) { // convert binary data to ASCII if (binMode) { for (i = 0; i < 4; ++i) { - fputc(hexChar[(start[i] >> 4) & 0x0f], f); - fputc(hexChar[start[i] & 0x0f], f); + writePSChar(hexChar[(start[i] >> 4) & 0x0f]); + writePSChar(hexChar[start[i] & 0x0f]); } + // if Length2 is incorrect (too small), font data gets chopped, so + // we take a few extra characters from the trailer just in case + length2 += length3 >= 8 ? 8 : length3; while (i < length2) { - if ((c = strObj.streamGetChar()) == EOF) + if ((c = strObj.streamGetChar()) == EOF) { break; - fputc(hexChar[(c >> 4) & 0x0f], f); - fputc(hexChar[c & 0x0f], f); - if (++i % 32 == 0) - fputc('\n', f); + } + writePSChar(hexChar[(c >> 4) & 0x0f]); + writePSChar(hexChar[c & 0x0f]); + if (++i % 32 == 0) { + writePSChar('\n'); + } + } + if (i % 32 > 0) { + writePSChar('\n'); } - if (i % 32 > 0) - fputc('\n', f); // already in ASCII format -- just copy it } else { - for (i = 0; i < 4; ++i) - fputc(start[i], f); + for (i = 0; i < 4; ++i) { + writePSChar(start[i]); + } for (i = 4; i < length2; ++i) { - if ((c = strObj.streamGetChar()) == EOF) + if ((c = strObj.streamGetChar()) == EOF) { break; - fputc(c, f); + } + writePSChar(c); } } // write padding and "cleartomark" - for (i = 0; i < 8; ++i) + for (i = 0; i < 8; ++i) { writePS("00000000000000000000000000000000" "00000000000000000000000000000000\n"); + } writePS("cleartomark\n"); // ending comment - if (psOutEPS) { - writePS("%%%%EndResource\n"); - } + writePS("%%EndResource\n"); err1: strObj.streamClose(); @@ -795,7 +1534,7 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) { //~ This doesn't handle .pfb files or binary eexec data (which only //~ happens in pfb files?). -void PSOutputDev::setupEmbeddedType1Font(GString *fileName, char *psName) { +void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) { FILE *fontFile; int c; int i; @@ -816,33 +1555,30 @@ void PSOutputDev::setupEmbeddedType1Font(GString *fileName, char *psName) { fontFileNames[fontFileNameLen++] = fileName->copy(); // beginning comment - if (psOutEPS) { - writePS("%%%%BeginResource: font %s\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName); - embFontList->append("\n"); - } + writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); // copy the font file if (!(fontFile = fopen(fileName->getCString(), "rb"))) { error(-1, "Couldn't open external font file"); return; } - while ((c = fgetc(fontFile)) != EOF) - fputc(c, f); + while ((c = fgetc(fontFile)) != EOF) { + writePSChar(c); + } fclose(fontFile); // ending comment - if (psOutEPS) { - writePS("%%%%EndResource\n"); - } + writePS("%%EndResource\n"); } void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id, - char *psName) { + GString *psName) { char *fontBuf; int fontLen; - Type1CFontConverter *cvt; + FoFiType1C *ffT1C; int i; // check if font is already embedded @@ -860,31 +1596,318 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id, fontFileIDs[fontFileIDLen++] = *id; // beginning comment - if (psOutEPS) { - writePS("%%%%BeginResource: font %s\n", psName); - embFontList->append("%%+ font "); - embFontList->append(psName); - embFontList->append("\n"); - } + writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); // convert it to a Type 1 font - fontBuf = font->readEmbFontFile(&fontLen); - cvt = new Type1CFontConverter(fontBuf, fontLen, f); - cvt->convert(); - delete cvt; + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { + ffT1C->convertToType1(NULL, gTrue, outputFunc, outputStream); + delete ffT1C; + } gfree(fontBuf); // ending comment - if (psOutEPS) { - writePS("%%%%EndResource\n"); + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, + GString *psName) { + char unique[32]; + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + Gushort *codeToGID; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) { + sprintf(unique, "_%d", nextTrueTypeNum++); + psName->append(unique); + break; + } + } + + // add entry to fontFileIDs list + if (i == fontFileIDLen) { + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; } + + // beginning comment + writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 42 font + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { + codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); + ffTT->convertToType42(psName->getCString(), + ((Gfx8BitFont *)font)->getHasEncoding() + ? ((Gfx8BitFont *)font)->getEncoding() + : (char **)NULL, + codeToGID, outputFunc, outputStream); + gfree(codeToGID); + delete ffTT; + } + gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) { + char unique[32]; + GString *fileName; + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + Gushort *codeToGID; + int i; + + // check if font is already embedded + fileName = font->getExtFontFile(); + for (i = 0; i < fontFileNameLen; ++i) { + if (!fontFileNames[i]->cmp(fileName)) { + sprintf(unique, "_%d", nextTrueTypeNum++); + psName->append(unique); + break; + } + } + + // add entry to fontFileNames list + if (i == fontFileNameLen) { + if (fontFileNameLen >= fontFileNameSize) { + fontFileNameSize += 64; + fontFileNames = + (GString **)grealloc(fontFileNames, + fontFileNameSize * sizeof(GString *)); + } + } + fontFileNames[fontFileNameLen++] = fileName->copy(); + + // beginning comment + writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 42 font + fontBuf = font->readExtFontFile(&fontLen); + if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { + codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); + ffTT->convertToType42(psName->getCString(), + ((Gfx8BitFont *)font)->getHasEncoding() + ? ((Gfx8BitFont *)font)->getEncoding() + : (char **)NULL, + codeToGID, outputFunc, outputStream); + delete ffTT; + } + gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, + GString *psName) { + char *fontBuf; + int fontLen; + FoFiType1C *ffT1C; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) + return; + } + + // add entry to fontFileIDs list + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + + // beginning comment + writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 0 font + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { + if (globalParams->getPSLevel() >= psLevel3) { + // Level 3: use a CID font + ffT1C->convertToCIDType0(psName->getCString(), outputFunc, outputStream); + } else { + // otherwise: use a non-CID composite font + ffT1C->convertToType0(psName->getCString(), outputFunc, outputStream); + } + delete ffT1C; + } + gfree(fontBuf); + + // ending comment + writePS("%%EndResource\n"); +} + +void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, + GString *psName) { + char *fontBuf; + int fontLen; + FoFiTrueType *ffTT; + int i; + + // check if font is already embedded + for (i = 0; i < fontFileIDLen; ++i) { + if (fontFileIDs[i].num == id->num && + fontFileIDs[i].gen == id->gen) + return; + } + + // add entry to fontFileIDs list + if (fontFileIDLen >= fontFileIDSize) { + fontFileIDSize += 64; + fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref)); + } + fontFileIDs[fontFileIDLen++] = *id; + + // beginning comment + writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); + embFontList->append("%%+ font "); + embFontList->append(psName->getCString()); + embFontList->append("\n"); + + // convert it to a Type 0 font + fontBuf = font->readEmbFontFile(xref, &fontLen); + if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) { + if (globalParams->getPSLevel() >= psLevel3) { + // Level 3: use a CID font + ffTT->convertToCIDType2(psName->getCString(), + ((GfxCIDFont *)font)->getCIDToGID(), + ((GfxCIDFont *)font)->getCIDToGIDLen(), + 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"); +} + +void PSOutputDev::setupType3Font(GfxFont *font, GString *psName, + Dict *parentResDict) { + Dict *resDict; + Dict *charProcs; + Object charProc; + Gfx *gfx; + PDFRectangle box; + double *m; + char buf[256]; + int i; + + // set up resources used by font + if ((resDict = ((Gfx8BitFont *)font)->getResources())) { + inType3Char = gTrue; + setupResources(resDict); + inType3Char = gFalse; + } else { + resDict = parentResDict; + } + + // beginning comment + writePSFmt("%%%%BeginResource: font %s\n", psName->getCString()); + 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 [%g %g %g %g %g %g] def\n", + m[0], m[1], m[2], m[3], m[4], m[5]); + m = font->getFontBBox(); + writePSFmt("/FontBBox [%g %g %g %g] 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 %d dict def\n", charProcs->getLength()); + writePS("CharProcs begin\n"); + box.x1 = m[0]; + box.y1 = m[1]; + box.x2 = m[2]; + box.y2 = m[3]; + gfx = new Gfx(xref, this, resDict, &box, gFalse, NULL); + inType3Char = gTrue; + t3Cacheable = gFalse; + for (i = 0; i < charProcs->getLength(); ++i) { + writePS("/"); + writePSName(charProcs->getKey(i)); + writePS(" {\n"); + gfx->display(charProcs->getVal(i, &charProc)); + charProc.free(); + if (t3String) { + if (t3Cacheable) { + sprintf(buf, "%g %g %g %g %g %g setcachedevice\n", + t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); + } else { + sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY); + } + (*outputFunc)(outputStream, buf, strlen(buf)); + (*outputFunc)(outputStream, t3String->getCString(), + t3String->getLength()); + delete t3String; + t3String = NULL; + } + (*outputFunc)(outputStream, "Q\n", 2); + writePS("} def\n"); + } + inType3Char = gFalse; + delete gfx; + writePS("end\n"); + } + writePS("currentdict end\n"); + writePSFmt("/%s exch definefont pop\n", psName->getCString()); + + // ending comment + writePS("%%EndResource\n"); } void PSOutputDev::setupImages(Dict *resDict) { Object xObjDict, xObj, xObjRef, subtypeObj; int i; - if (!doForm) { + if (!(mode == psModeForm || inType3Char)) { return; } @@ -912,11 +1935,18 @@ void PSOutputDev::setupImages(Dict *resDict) { } void PSOutputDev::setupImage(Ref id, Stream *str) { + GBool useASCIIHex; int c; int size, line, col, i; // construct an encoder stream - str = new ASCII85Encoder(str); + useASCIIHex = level == psLevel1 || level == psLevel1Sep || + globalParams->getPSASCIIHex(); + if (useASCIIHex) { + str = new ASCIIHexEncoder(str); + } else { + str = new ASCII85Encoder(str); + } // compute image data size str->reset(); @@ -925,18 +1955,18 @@ void PSOutputDev::setupImage(Ref id, Stream *str) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); - if (c == '~' || c == EOF) { + if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { ++col; } else { ++col; - for (i = 1; i <= 4; ++i) { + for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); - if (c == '~' || c == EOF) { + if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } ++col; @@ -946,35 +1976,36 @@ void PSOutputDev::setupImage(Ref id, Stream *str) { ++size; col = 0; } - } while (c != '~' && c != EOF); + } while (c != (useASCIIHex ? '>' : '~') && c != EOF); ++size; - writePS("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen); + writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen); + str->close(); // write the data into the array str->reset(); line = col = 0; - writePS("dup 0 <~"); + writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~")); do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); - if (c == '~' || c == EOF) { + if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { - fputc(c, f); + writePSChar(c); ++col; } else { - fputc(c, f); + writePSChar(c); ++col; - for (i = 1; i <= 4; ++i) { + for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); - if (c == '~' || c == EOF) { + if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } - fputc(c, f); + writePSChar(c); ++col; } } @@ -983,115 +2014,155 @@ void PSOutputDev::setupImage(Ref id, Stream *str) { // 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("~> put\n"); + writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); ++line; - writePS("dup %d <~", line); + writePSFmt((char *)(useASCIIHex ? "dup %d <" : "dup %d <~"), line); col = 0; } - } while (c != '~' && c != EOF); - writePS("~> put\n"); + } while (c != (useASCIIHex ? '>' : '~') && c != EOF); + writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); writePS("pop\n"); + str->close(); delete str; -} - -void PSOutputDev::startPage(int pageNum, GfxState *state) { - int x1, y1, x2, y2, width, height, t; - - if (doForm) { - - writePS("/PaintProc {\n"); - writePS("begin xpdf begin\n"); - writePS("pdfStartPage\n"); - tx = ty = 0; - xScale = yScale = 1; - landscape = gFalse; +} - } else if (psOutEPS) { +void PSOutputDev::startPage(int pageNum, GfxState *state) { + int x1, y1, x2, y2, width, height; + int imgWidth, imgHeight, imgWidth2, imgHeight2; - writePS("pdfStartPage\n"); - tx = ty = 0; - xScale = yScale = 1; - landscape = gFalse; - } else { + switch (mode) { - writePS("%%%%Page: %d %d\n", pageNum, seqPage); - writePS("%%%%BeginPageSetup\n"); + case psModePS: + writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage); + writePS("%%BeginPageSetup\n"); // rotate, translate, and scale page + imgWidth = imgURX - imgLLX; + imgHeight = imgURY - imgLLY; x1 = (int)(state->getX1() + 0.5); y1 = (int)(state->getY1() + 0.5); x2 = (int)(state->getX2() + 0.5); y2 = (int)(state->getY2() + 0.5); width = x2 - x1; height = y2 - y1; - if (width > height && width > paperWidth) { - landscape = gTrue; - writePS("%%%%PageOrientation: Landscape\n"); + tx = ty = 0; + // portrait or landscape + if (width > height && width > imgWidth) { + rotate = 90; + writePSFmt("%%%%PageOrientation: %s\n", + state->getCTM()[0] ? "Landscape" : "Portrait"); writePS("pdfStartPage\n"); writePS("90 rotate\n"); - tx = -x1; - ty = -(y1 + paperWidth); - t = width; - width = height; - height = t; + ty = -imgWidth; + imgWidth2 = imgHeight; + imgHeight2 = imgWidth; } else { - landscape = gFalse; - writePS("%%%%PageOrientation: Portrait\n"); + rotate = 0; + writePSFmt("%%%%PageOrientation: %s\n", + state->getCTM()[0] ? "Portrait" : "Landscape"); writePS("pdfStartPage\n"); - tx = -x1; - ty = -y1; - } - if (width < paperWidth) { - tx += (paperWidth - width) / 2; - } - if (height < paperHeight) { - ty += (paperHeight - height) / 2; - } - if (tx != 0 || ty != 0) { - writePS("%g %g translate\n", tx, ty); - } - if (width > paperWidth || height > paperHeight) { - xScale = (double)paperWidth / (double)width; - yScale = (double)paperHeight / (double)height; + imgWidth2 = imgWidth; + imgHeight2 = imgHeight; + } + // shrink or expand + if ((globalParams->getPSShrinkLarger() && + (width > imgWidth2 || height > imgHeight2)) || + (globalParams->getPSExpandSmaller() && + (width < imgWidth2 && height < imgHeight2))) { + xScale = (double)imgWidth2 / (double)width; + yScale = (double)imgHeight2 / (double)height; if (yScale < xScale) { xScale = yScale; + } else { + yScale = xScale; } - writePS("%0.4f %0.4f scale\n", xScale, xScale); } else { xScale = yScale = 1; } + // deal with odd bounding boxes + tx -= xScale * x1; + ty -= yScale * y1; + // center + if (globalParams->getPSCenter()) { + tx += (imgWidth2 - xScale * width) / 2; + ty += (imgHeight2 - yScale * height) / 2; + } + tx += imgLLX + tx0; + ty += imgLLY + ty0; + xScale *= xScale0; + yScale *= yScale0; + if (tx != 0 || ty != 0) { + writePSFmt("%g %g translate\n", tx, ty); + } + if (xScale != 1 || yScale != 1) { + writePSFmt("%0.4f %0.4f scale\n", xScale, xScale); + } + if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { + writePSFmt("%g %g %g %g re W\n", + clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); + } - writePS("%%%%EndPageSetup\n"); + writePS("%%EndPageSetup\n"); ++seqPage; + break; + + case psModeEPS: + writePS("pdfStartPage\n"); + tx = ty = 0; + xScale = yScale = 1; + rotate = 0; + break; + + case psModeForm: + writePS("/PaintProc {\n"); + writePS("begin xpdf begin\n"); + writePS("pdfStartPage\n"); + tx = ty = 0; + xScale = yScale = 1; + rotate = 0; + break; + } + + if (underlayCbk) { + (*underlayCbk)(this, underlayCbkData); } } void PSOutputDev::endPage() { - if (doForm) { + if (overlayCbk) { + (*overlayCbk)(this, overlayCbkData); + } + + + if (mode == psModeForm) { writePS("pdfEndPage\n"); writePS("end end\n"); writePS("} def\n"); writePS("end end\n"); } else { - writePS("showpage\n"); - writePS("%%%%PageTrailer\n"); - writePS("pdfEndPage\n"); + if (!manualCtrl) { + writePS("showpage\n"); + writePS("%%PageTrailer\n"); + writePageTrailer(); + } } } void PSOutputDev::saveState(GfxState *state) { writePS("q\n"); + ++numSaves; } void PSOutputDev::restoreState(GfxState *state) { writePS("Q\n"); + --numSaves; } void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { - writePS("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32); + writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32); } void PSOutputDev::updateLineDash(GfxState *state) { @@ -1102,69 +2173,172 @@ void PSOutputDev::updateLineDash(GfxState *state) { state->getLineDash(&dash, &length, &start); writePS("["); for (i = 0; i < length; ++i) - writePS("%g%s", dash[i], (i == length-1) ? "" : " "); - writePS("] %g d\n", start); + writePSFmt("%g%s", dash[i], (i == length-1) ? "" : " "); + writePSFmt("] %g d\n", start); } void PSOutputDev::updateFlatness(GfxState *state) { - writePS("%d i\n", state->getFlatness()); + writePSFmt("%d i\n", state->getFlatness()); } void PSOutputDev::updateLineJoin(GfxState *state) { - writePS("%d j\n", state->getLineJoin()); + writePSFmt("%d j\n", state->getLineJoin()); } void PSOutputDev::updateLineCap(GfxState *state) { - writePS("%d J\n", state->getLineCap()); + writePSFmt("%d J\n", state->getLineCap()); } void PSOutputDev::updateMiterLimit(GfxState *state) { - writePS("%g M\n", state->getMiterLimit()); + writePSFmt("%g M\n", state->getMiterLimit()); } void PSOutputDev::updateLineWidth(GfxState *state) { - writePS("%g w\n", state->getLineWidth()); + writePSFmt("%g w\n", state->getLineWidth()); } void PSOutputDev::updateFillColor(GfxState *state) { + GfxColor color; + double gray; GfxRGB rgb; GfxCMYK cmyk; + GfxSeparationColorSpace *sepCS; - if (psOutLevel1Sep) { + switch (level) { + case psLevel1: + state->getFillGray(&gray); + writePSFmt("%g g\n", gray); + break; + case psLevel1Sep: state->getFillCMYK(&cmyk); - writePS("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); - } else { - state->getFillRGB(&rgb); - if (rgb.r == rgb.g && rgb.g == rgb.b) { - writePS("%g g\n", rgb.r); + writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); + addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); + break; + case psLevel2: + case psLevel3: + if (state->getFillColorSpace()->getMode() == csDeviceCMYK) { + state->getFillCMYK(&cmyk); + writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); + } else { + state->getFillRGB(&rgb); + if (rgb.r == rgb.g && rgb.g == rgb.b) { + writePSFmt("%g g\n", rgb.r); + } else { + writePSFmt("%g %g %g rg\n", rgb.r, rgb.g, rgb.b); + } + } + break; + case psLevel2Sep: + case psLevel3Sep: + if (state->getFillColorSpace()->getMode() == csSeparation) { + sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace(); + color.c[0] = 1; + sepCS->getCMYK(&color, &cmyk); + writePSFmt("%g %g %g %g %g (%s) ck\n", + state->getFillColor()->c[0], + cmyk.c, cmyk.m, cmyk.y, cmyk.k, + sepCS->getName()->getCString()); + addCustomColor(sepCS); } else { - writePS("%g %g %g rg\n", rgb.r, rgb.g, rgb.b); + state->getFillCMYK(&cmyk); + writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); + addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); } + break; } + t3Cacheable = gFalse; } void PSOutputDev::updateStrokeColor(GfxState *state) { + GfxColor color; + double gray; GfxRGB rgb; GfxCMYK cmyk; + GfxSeparationColorSpace *sepCS; - if (psOutLevel1Sep) { + switch (level) { + case psLevel1: + state->getStrokeGray(&gray); + writePSFmt("%g G\n", gray); + break; + case psLevel1Sep: state->getStrokeCMYK(&cmyk); - writePS("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); - } else { - state->getStrokeRGB(&rgb); - if (rgb.r == rgb.g && rgb.g == rgb.b) { - writePS("%g G\n", rgb.r); + writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); + addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); + break; + case psLevel2: + case psLevel3: + if (state->getStrokeColorSpace()->getMode() == csDeviceCMYK) { + state->getStrokeCMYK(&cmyk); + writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); + } else { + state->getStrokeRGB(&rgb); + if (rgb.r == rgb.g && rgb.g == rgb.b) { + writePSFmt("%g G\n", rgb.r); + } else { + writePSFmt("%g %g %g RG\n", rgb.r, rgb.g, rgb.b); + } + } + break; + case psLevel2Sep: + case psLevel3Sep: + if (state->getStrokeColorSpace()->getMode() == csSeparation) { + sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace(); + color.c[0] = 1; + sepCS->getCMYK(&color, &cmyk); + writePSFmt("%g %g %g %g %g (%s) CK\n", + state->getStrokeColor()->c[0], + cmyk.c, cmyk.m, cmyk.y, cmyk.k, + sepCS->getName()->getCString()); + addCustomColor(sepCS); } else { - writePS("%g %g %g RG\n", rgb.r, rgb.g, rgb.b); + state->getStrokeCMYK(&cmyk); + writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k); + addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); + } + break; + } + t3Cacheable = gFalse; +} + +void PSOutputDev::addProcessColor(double c, double m, double y, double k) { + if (c > 0) { + processColors |= psProcessCyan; + } + if (m > 0) { + processColors |= psProcessMagenta; + } + if (y > 0) { + processColors |= psProcessYellow; + } + if (k > 0) { + processColors |= psProcessBlack; + } +} + +void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) { + PSOutCustomColor *cc; + GfxColor color; + GfxCMYK cmyk; + + for (cc = customColors; cc; cc = cc->next) { + if (!cc->name->cmp(sepCS->getName())) { + return; } } + color.c[0] = 1; + sepCS->getCMYK(&color, &cmyk); + cc = new PSOutCustomColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k, + sepCS->getName()->copy()); + cc->next = customColors; + customColors = cc; } void PSOutputDev::updateFont(GfxState *state) { if (state->getFont()) { - writePS("/F%d_%d %g Tf\n", - state->getFont()->getID().num, state->getFont()->getID().gen, - state->getFontSize()); + writePSFmt("/F%d_%d %g Tf\n", + state->getFont()->getID()->num, state->getFont()->getID()->gen, + state->getFontSize()); } } @@ -1172,41 +2346,63 @@ void PSOutputDev::updateTextMat(GfxState *state) { double *mat; mat = state->getTextMat(); - writePS("[%g %g %g %g %g %g] Tm\n", - mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); + writePSFmt("[%g %g %g %g %g %g] Tm\n", + mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); } void PSOutputDev::updateCharSpace(GfxState *state) { - writePS("%g Tc\n", state->getCharSpace()); + writePSFmt("%g Tc\n", state->getCharSpace()); } void PSOutputDev::updateRender(GfxState *state) { - writePS("%d Tr\n", state->getRender()); + int rm; + + rm = state->getRender(); + writePSFmt("%d Tr\n", rm); + rm &= 3; + if (rm != 0 && rm != 3) { + t3Cacheable = gFalse; + } } void PSOutputDev::updateRise(GfxState *state) { - writePS("%g Ts\n", state->getRise()); + writePSFmt("%g Ts\n", state->getRise()); } void PSOutputDev::updateWordSpace(GfxState *state) { - writePS("%g Tw\n", state->getWordSpace()); + writePSFmt("%g Tw\n", state->getWordSpace()); } void PSOutputDev::updateHorizScaling(GfxState *state) { - writePS("%g Tz\n", state->getHorizScaling()); + double h; + + if ((h = state->getHorizScaling()) < 0.01) { + h = 0.01; + } + writePSFmt("%g Tz\n", h); } void PSOutputDev::updateTextPos(GfxState *state) { - writePS("%g %g Td\n", state->getLineX(), state->getLineY()); + writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY()); } void PSOutputDev::updateTextShift(GfxState *state, double shift) { - writePS("%g TJm\n", shift); + if (state->getFont()->getWMode()) { + writePSFmt("%g TJmV\n", shift); + } else { + writePSFmt("%g TJm\n", shift); + } } void PSOutputDev::stroke(GfxState *state) { doPath(state->getPath()); - writePS("S\n"); + if (t3String) { + // if we're construct a cacheable Type 3 glyph, we need to do + // everything in the fill color + writePS("Sf\n"); + } else { + writePS("S\n"); + } } void PSOutputDev::fill(GfxState *state) { @@ -1250,14 +2446,14 @@ void PSOutputDev::doPath(GfxPath *path) { x3 = subpath->getX(3); y3 = subpath->getY(3); if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) { - writePS("%g %g %g %g re\n", - x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, - fabs(x2 - x0), fabs(y1 - y0)); + writePSFmt("%g %g %g %g 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) { - writePS("%g %g %g %g re\n", - x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, - fabs(x1 - x0), fabs(y2 - y0)); + writePSFmt("%g %g %g %g re\n", + x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, + fabs(x1 - x0), fabs(y2 - y0)); return; } } @@ -1266,16 +2462,16 @@ void PSOutputDev::doPath(GfxPath *path) { for (i = 0; i < n; ++i) { subpath = path->getSubpath(i); m = subpath->getNumPoints(); - writePS("%g %g m\n", subpath->getX(0), subpath->getY(0)); + writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0)); j = 1; while (j < m) { if (subpath->getCurve(j)) { - writePS("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j), - subpath->getX(j+1), subpath->getY(j+1), - subpath->getX(j+2), subpath->getY(j+2)); + writePSFmt("%g %g %g %g %g %g 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 { - writePS("%g %g l\n", subpath->getX(j), subpath->getY(j)); + writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j)); ++j; } } @@ -1286,52 +2482,115 @@ void PSOutputDev::doPath(GfxPath *path) { } void PSOutputDev::drawString(GfxState *state, GString *s) { + GfxFont *font; + int wMode; + GString *s2; + double dx, dy, dx2, dy2, originX, originY; + char *p; + UnicodeMap *uMap; + CharCode code; + Unicode u[8]; + char buf[8]; + int len, nChars, uLen, n, m, i, j; + // check for invisible text -- this is used by Acrobat Capture - if ((state->getRender() & 3) == 3) + if (state->getRender() == 3) { return; + } - writePSString(s); - writePS(" %g Tj\n", state->getFont()->getWidth(s)); -} - -void PSOutputDev::drawString16(GfxState *state, GString *s) { - int c1, c2; - double w; - int i; + // ignore empty strings + if (s->getLength() == 0) { + return; + } - // check for invisible text -- this is used by Acrobat Capture - if ((state->getRender() & 3) == 3) + // get the font + if (!(font = state->getFont())) { return; + } + wMode = font->getWMode(); - switch (state->getFont()->getCharSet16()) { + // check for a subtitute 16-bit font + uMap = NULL; + if (font->isCIDFont()) { + for (i = 0; i < font16EncLen; ++i) { + if (font->getID()->num == font16Enc[i].fontID.num && + font->getID()->gen == font16Enc[i].fontID.gen) { + uMap = globalParams->getUnicodeMap(font16Enc[i].enc); + break; + } + } + } - case font16AdobeJapan12: -#if JAPANESE_SUPPORT - writePS("<"); - w = 0; - for (i = 0; i < s->getLength(); i += 2) { - c1 = ((s->getChar(i) & 0xff) << 8) + (s->getChar(i+1) & 0xff); - if (c1 <= 8285) { - c2 = japan12ToRKSJ[c1]; + // compute width of chars in string, ignoring char spacing and word + // spacing -- the Tj operator will adjust for the metrics of the + // font that's actually used + dx = dy = 0; + nChars = 0; + p = s->getCString(); + len = s->getLength(); + if (font->isCIDFont()) { + s2 = new GString(); + } else { + s2 = s; + } + while (len > 0) { + n = font->getNextChar(p, len, &code, + u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, + &dx2, &dy2, &originX, &originY); + if (font->isCIDFont()) { + if (uMap) { + for (i = 0; i < uLen; ++i) { + m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); + for (j = 0; j < m; ++j) { + s2->append(buf[j]); + } + } + //~ this really needs to get the number of chars in the target + //~ encoding - which may be more than the number of Unicode + //~ chars + nChars += uLen; } else { - c2 = 0x20; + s2->append((char)((code >> 8) & 0xff)); + s2->append((char)(code & 0xff)); + ++nChars; } - if (c2 <= 0xff) { - writePS("%02x", c2); + } + dx += dx2; + dy += dy2; + p += n; + len -= n; + } + dx *= state->getFontSize() * state->getHorizScaling(); + dy *= state->getFontSize(); + if (uMap) { + uMap->decRefCnt(); + } + + if (s2->getLength() > 0) { + writePSString(s2); + if (font->isCIDFont()) { + if (wMode) { + writePSFmt(" %d %g Tj16V\n", nChars, dy); } else { - writePS("%02x%02x", c2 >> 8, c2 & 0xff); + writePSFmt(" %d %g Tj16\n", nChars, dx); } - w += state->getFont()->getWidth16(c1); + } else { + writePSFmt(" %g Tj\n", dx); } - writePS("> %g Tj\n", w); -#endif - break; + } + if (font->isCIDFont()) { + delete s2; + } - case font16AdobeGB12: - break; + if (state->getRender() & 4) { + haveTextClip = gTrue; + } +} - case font16AdobeCNS13: - break; +void PSOutputDev::endTextObject(GfxState *state) { + if (haveTextClip) { + writePS("Tclip\n"); + haveTextClip = gFalse; } } @@ -1341,8 +2600,8 @@ void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int len; len = height * ((width + 7) / 8); - if (psOutLevel1 || psOutLevel1Sep) { - doImageL1(NULL, invert, inlineImg, str, width, height, len); + if (level == psLevel1 || level == psLevel1Sep) { + doImageL1(ref, NULL, invert, inlineImg, str, width, height, len); } else { doImageL2(ref, NULL, invert, inlineImg, str, width, height, len); } @@ -1350,82 +2609,136 @@ void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, - GBool inlineImg) { + int *maskColors, GBool inlineImg) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); - if (psOutLevel1) { - doImageL1(colorMap, gFalse, inlineImg, str, width, height, len); - } else if (psOutLevel1Sep) { + switch (level) { + case psLevel1: + doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len); + break; + case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len); - } else { + break; + case psLevel2: + case psLevel2Sep: + case psLevel3: + case psLevel3Sep: doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len); + break; } + t3Cacheable = gFalse; } -void PSOutputDev::doImageL1(GfxImageColorMap *colorMap, +void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len) { ImageStream *imgStr; Guchar pixBuf[gfxColorMaxComps]; double gray; - int x, y, i; + int col, x, y, c, i; - // width, height, matrix, bits per component - if (colorMap) { - writePS("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n", - width, height, - width, -height, height); + if (inType3Char && !colorMap) { + if (inlineImg) { + // create an array + str = new FixedLengthEncoder(str, len); + str = new ASCIIHexEncoder(str); + str->reset(); + col = 0; + writePS("[<"); + do { + do { + c = str->getChar(); + } while (c == '\n' || c == '\r'); + if (c == '>' || c == EOF) { + break; + } + writePSChar(c); + ++col; + // each line is: "<...data...>" + // 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_%d_%d 0\n", ref->getRefNum(), ref->getRefGen()); + } + } + + // image/imagemask command + if (inType3Char && !colorMap) { + writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1a\n", + width, height, invert ? "true" : "false", + width, -height, height); + } else if (colorMap) { + writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n", + width, height, + width, -height, height); } else { - writePS("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n", - width, height, invert ? "true" : "false", - width, -height, height); + writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n", + width, height, invert ? "true" : "false", + width, -height, height); } - // image - if (colorMap) { + // image data + if (!(inType3Char && !colorMap)) { - // set up to process the data stream - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), - colorMap->getBits()); - imgStr->reset(); + if (colorMap) { - // process the data stream - i = 0; - for (y = 0; y < height; ++y) { + // set up to process the data stream + imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), + colorMap->getBits()); + imgStr->reset(); - // write the line - for (x = 0; x < width; ++x) { - imgStr->getPixel(pixBuf); - colorMap->getGray(pixBuf, &gray); - fprintf(f, "%02x", (int)(gray * 255 + 0.5)); - if (++i == 32) { - fputc('\n', f); - i = 0; + // process the data stream + i = 0; + for (y = 0; y < height; ++y) { + + // write the line + for (x = 0; x < width; ++x) { + imgStr->getPixel(pixBuf); + colorMap->getGray(pixBuf, &gray); + writePSFmt("%02x", (int)(gray * 255 + 0.5)); + if (++i == 32) { + writePSChar('\n'); + i = 0; + } } } - } - if (i != 0) - fputc('\n', f); - delete imgStr; + if (i != 0) { + writePSChar('\n'); + } + delete imgStr; - // imagemask - } else { - str->reset(); - i = 0; - for (y = 0; y < height; ++y) { - for (x = 0; x < width; x += 8) { - fprintf(f, "%02x", str->getChar() & 0xff); - if (++i == 32) { - fputc('\n', f); - i = 0; + // imagemask + } else { + str->reset(); + i = 0; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; x += 8) { + writePSFmt("%02x", str->getChar() & 0xff); + if (++i == 32) { + writePSChar('\n'); + i = 0; + } } } + if (i != 0) { + writePSChar('\n'); + } + str->close(); } - if (i != 0) - fputc('\n', f); } } @@ -1439,9 +2752,9 @@ void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap, int x, y, i, comp; // width, height, matrix, bits per component - writePS("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n", - width, height, - width, -height, height); + writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n", + width, height, + width, -height, height); // allocate a line buffer lineBuf = (Guchar *)gmalloc(4 * width); @@ -1463,14 +2776,15 @@ void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap, lineBuf[4*x+1] = (int)(255 * cmyk.m + 0.5); lineBuf[4*x+2] = (int)(255 * cmyk.y + 0.5); lineBuf[4*x+3] = (int)(255 * cmyk.k + 0.5); + addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); } // write one line of each color component for (comp = 0; comp < 4; ++comp) { for (x = 0; x < width; ++x) { - fprintf(f, "%02x", lineBuf[4*x + comp]); + writePSFmt("%02x", lineBuf[4*x + comp]); if (++i == 32) { - fputc('\n', f); + writePSChar('\n'); i = 0; } } @@ -1478,7 +2792,7 @@ void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap, } if (i != 0) { - fputc('\n', f); + writePSChar('\n'); } delete imgStr; @@ -1490,9 +2804,12 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap, Stream *str, int width, int height, int len) { GString *s; int n, numComps; - GBool useRLE, useA85; + GBool useRLE, useASCII, useASCIIHex, useCompressed; + GfxSeparationColorSpace *sepCS; + GfxColor color; + GfxCMYK cmyk; int c; - int i; + int col, i; // color space if (colorMap) { @@ -1500,20 +2817,77 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap, writePS(" setcolorspace\n"); } - // set up to use the array created by setupImages() - if (doForm && !inlineImg) { - writePS("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen()); + useASCIIHex = globalParams->getPSASCIIHex(); + + // set up the image data + if (mode == psModeForm || inType3Char) { + if (inlineImg) { + // create an array + str = new FixedLengthEncoder(str, len); + if (useASCIIHex) { + str = new ASCIIHexEncoder(str); + } else { + str = new ASCII85Encoder(str); + } + str->reset(); + col = 0; + writePS((char *)(useASCIIHex ? "[<" : "[<~")); + do { + do { + c = str->getChar(); + } while (c == '\n' || c == '\r'); + if (c == (useASCIIHex ? '>' : '~') || c == EOF) { + break; + } + if (c == 'z') { + writePSChar(c); + ++col; + } else { + writePSChar(c); + ++col; + for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { + do { + c = str->getChar(); + } while (c == '\n' || c == '\r'); + if (c == (useASCIIHex ? '>' : '~') || c == EOF) { + break; + } + writePSChar(c); + ++col; + } + } + // each line is: "<~...data...~>" + // so max data length = 255 - 6 = 249 + // chunks are 1 or 5 bytes each, so we have to stop at 245 + // but make it 240 just to be safe + if (col > 240) { + writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); + col = 0; + } + } while (c != (useASCIIHex ? '>' : '~') && c != EOF); + writePS((char *)(useASCIIHex ? ">]\n" : "~>]\n")); + writePS("0\n"); + str->close(); + delete str; + } else { + // set up to use the array already created by setupImages() + writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen()); + } } // image dictionary writePS("<<\n /ImageType 1\n"); // width, height, matrix, bits per component - writePS(" /Width %d\n", width); - writePS(" /Height %d\n", height); - writePS(" /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height); - writePS(" /BitsPerComponent %d\n", - colorMap ? colorMap->getBits() : 1); + writePSFmt(" /Width %d\n", width); + writePSFmt(" /Height %d\n", height); + writePSFmt(" /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height); + if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { + writePSFmt(" /BitsPerComponent 8\n"); + } else { + writePSFmt(" /BitsPerComponent %d\n", + colorMap ? colorMap->getBits() : 1); + } // decode if (colorMap) { @@ -1521,84 +2895,97 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap, if (colorMap->getColorSpace()->getMode() == csSeparation) { //~ this is a kludge -- see comment in dumpColorSpaceL2 n = (1 << colorMap->getBits()) - 1; - writePS("%g %g", colorMap->getDecodeLow(0) * n, - colorMap->getDecodeHigh(0) * n); + writePSFmt("%g %g", colorMap->getDecodeLow(0) * n, + colorMap->getDecodeHigh(0) * n); + } else if (colorMap->getColorSpace()->getMode() == csDeviceN) { + numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> + getAlt()->getNComps(); + for (i = 0; i < numComps; ++i) { + if (i > 0) { + writePS(" "); + } + writePSFmt("0 1", colorMap->getDecodeLow(i), + colorMap->getDecodeHigh(i)); + } } else { numComps = colorMap->getNumPixelComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } - writePS("%g %g", colorMap->getDecodeLow(i), - colorMap->getDecodeHigh(i)); + writePSFmt("%g %g", colorMap->getDecodeLow(i), + colorMap->getDecodeHigh(i)); } } writePS("]\n"); } else { - writePS(" /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1); + writePSFmt(" /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1); } - if (doForm) { - - if (inlineImg) { - - // data source - writePS(" /DataSource <~\n"); - - // write image data stream, using ASCII85 encode filter - str = new FixedLengthEncoder(str, len); - str = new ASCII85Encoder(str); - str->reset(); - while ((c = str->getChar()) != EOF) { - fputc(c, f); - } - fputc('\n', f); - delete str; + if (mode == psModeForm || inType3Char) { - } else { - writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); - } + // data source + writePS(" /DataSource { 2 copy get exch 1 add exch }\n"); // end of image dictionary - writePS(">>\n%s\n", colorMap ? "image" : "imagemask"); + writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask"); // get rid of the array and index - if (!inlineImg) { - writePS("pop pop\n"); - } + writePS("pop pop\n"); } else { // data source writePS(" /DataSource currentfile\n"); - s = str->getPSFilter(" "); - if (inlineImg || !s) { + s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, + " "); + if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || + inlineImg || !s) { useRLE = gTrue; - useA85 = gTrue; + useASCII = gTrue; + useCompressed = gFalse; } else { useRLE = gFalse; - useA85 = str->isBinary(); + useASCII = str->isBinary(); + useCompressed = gTrue; } - if (useA85) - writePS(" /ASCII85Decode filter\n"); - if (useRLE) + if (useASCII) { + writePSFmt(" /ASCII%sDecode filter\n", + useASCIIHex ? "Hex" : "85"); + } + if (useRLE) { writePS(" /RunLengthDecode filter\n"); - else - writePS("%s", s->getCString()); - if (s) + } + if (useCompressed) { + writePS(s->getCString()); + } + if (s) { delete s; + } // cut off inline image streams at appropriate length - if (inlineImg) + if (inlineImg) { str = new FixedLengthEncoder(str, len); - else if (!useRLE) + } else if (useCompressed) { str = str->getBaseStream(); + } - // add RunLengthEncode and ASCII85 encode filters - if (useRLE) + // recode DeviceN data + if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { + str = new DeviceNRecoder(str, width, height, colorMap); + } + + // add RunLengthEncode and ASCIIHex/85 encode filters + if (useRLE) { str = new RunLengthEncoder(str); - if (useA85) - str = new ASCII85Encoder(str); + } + if (useASCII) { + if (useASCIIHex) { + str = new ASCIIHexEncoder(str); + } else { + str = new ASCII85Encoder(str); + } + } // end of image dictionary writePS(">>\n"); @@ -1610,38 +2997,52 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap, n = 0; } else { // need to read the stream to count characters -- the length - // is data-dependent (because of A85 and RLE filters) + // is data-dependent (because of ASCII and RLE filters) str->reset(); n = 0; while ((c = str->getChar()) != EOF) { ++n; } + str->close(); } // +6/7 for "pdfIm\n" / "pdfImM\n" // +8 for newline + trailer n += colorMap ? 14 : 15; - writePS("%%%%BeginData: %d Hex Bytes\n", n); + writePSFmt("%%%%BeginData: %d Hex Bytes\n", n); } #endif - writePS("%s\n", colorMap ? "pdfIm" : "pdfImM"); + if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && + colorMap->getColorSpace()->getMode() == csSeparation) { + color.c[0] = 1; + sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); + sepCS->getCMYK(&color, &cmyk); + writePSFmt("%g %g %g %g (%s) pdfImSep\n", + cmyk.c, cmyk.m, cmyk.y, cmyk.k, + sepCS->getName()->getCString()); + } else { + writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM"); + } // copy the stream data str->reset(); - while ((c = str->getChar()) != EOF) - fputc(c, f); + while ((c = str->getChar()) != EOF) { + writePSChar(c); + } + str->close(); // add newline and trailer to the end - fputc('\n', f); - fputs("%-EOD-\n", f); + writePSChar('\n'); + writePS("%-EOD-\n"); #if OPI_SUPPORT if (opi13Nest) { - writePS("%%%%EndData\n"); + writePS("%%EndData\n"); } #endif // delete encoders - if (useRLE || useA85) + if (useRLE || useASCII || inlineImg) { delete str; + } } } @@ -1651,107 +3052,152 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) { GfxLabColorSpace *labCS; GfxIndexedColorSpace *indexedCS; GfxSeparationColorSpace *separationCS; - Guchar *lookup; - double x[1], y[gfxColorMaxComps]; - int n, numComps; + GfxColorSpace *baseCS; + Guchar *lookup, *p; + double x[gfxColorMaxComps], y[gfxColorMaxComps]; + GfxColor color; + GfxCMYK cmyk; + Function *func; + int n, numComps, numAltComps; + int byte; int i, j, k; switch (colorSpace->getMode()) { case csDeviceGray: writePS("/DeviceGray"); + processColors |= psProcessBlack; break; case csCalGray: calGrayCS = (GfxCalGrayColorSpace *)colorSpace; writePS("[/CIEBasedA <<\n"); - writePS(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma()); - writePS(" /MatrixA [%g %g %g]\n", - calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), - calGrayCS->getWhiteZ()); - writePS(" /WhitePoint [%g %g %g]\n", - calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), - calGrayCS->getWhiteZ()); - writePS(" /BlackPoint [%g %g %g]\n", - calGrayCS->getBlackX(), calGrayCS->getBlackY(), - calGrayCS->getBlackZ()); + writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma()); + writePSFmt(" /MatrixA [%g %g %g]\n", + calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), + calGrayCS->getWhiteZ()); + writePSFmt(" /WhitePoint [%g %g %g]\n", + calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), + calGrayCS->getWhiteZ()); + writePSFmt(" /BlackPoint [%g %g %g]\n", + calGrayCS->getBlackX(), calGrayCS->getBlackY(), + calGrayCS->getBlackZ()); writePS(">>]"); + processColors |= psProcessBlack; break; case csDeviceRGB: writePS("/DeviceRGB"); + processColors |= psProcessCMYK; break; case csCalRGB: calRGBCS = (GfxCalRGBColorSpace *)colorSpace; writePS("[/CIEBasedABC <<\n"); - writePS(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n", - calRGBCS->getGammaR(), calRGBCS->getGammaG(), - calRGBCS->getGammaB()); - writePS(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n", - calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1], - calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3], - calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5], - calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7], - calRGBCS->getMatrix()[8]); - writePS(" /WhitePoint [%g %g %g]\n", - calRGBCS->getWhiteX(), calRGBCS->getWhiteY(), - calRGBCS->getWhiteZ()); - writePS(" /BlackPoint [%g %g %g]\n", - calRGBCS->getBlackX(), calRGBCS->getBlackY(), - calRGBCS->getBlackZ()); + writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n", + calRGBCS->getGammaR(), calRGBCS->getGammaG(), + calRGBCS->getGammaB()); + writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n", + calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1], + calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3], + calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5], + calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7], + calRGBCS->getMatrix()[8]); + writePSFmt(" /WhitePoint [%g %g %g]\n", + calRGBCS->getWhiteX(), calRGBCS->getWhiteY(), + calRGBCS->getWhiteZ()); + writePSFmt(" /BlackPoint [%g %g %g]\n", + calRGBCS->getBlackX(), calRGBCS->getBlackY(), + calRGBCS->getBlackZ()); writePS(">>]"); + processColors |= psProcessCMYK; break; case csDeviceCMYK: writePS("/DeviceCMYK"); + processColors |= psProcessCMYK; break; case csLab: labCS = (GfxLabColorSpace *)colorSpace; writePS("[/CIEBasedABC <<\n"); - writePS(" /RangeABC [0 100 %g %g %g %g]\n", - labCS->getAMin(), labCS->getAMax(), - labCS->getBMin(), labCS->getBMax()); + writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n", + labCS->getAMin(), labCS->getAMax(), + labCS->getBMin(), labCS->getBMax()); writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n"); writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n"); writePS(" /DecodeLMN\n"); writePS(" [{dup 6 29 div ge {dup dup mul mul}\n"); - writePS(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n", - labCS->getWhiteX()); + writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n", + labCS->getWhiteX()); writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); - writePS(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n", - labCS->getWhiteY()); + writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n", + labCS->getWhiteY()); writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); - writePS(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n", - labCS->getWhiteZ()); - writePS(" /WhitePoint [%g %g %g]\n", - labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ()); - writePS(" /BlackPoint [%g %g %g]\n", - labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ()); + writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n", + labCS->getWhiteZ()); + writePSFmt(" /WhitePoint [%g %g %g]\n", + labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ()); + writePSFmt(" /BlackPoint [%g %g %g]\n", + labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ()); writePS(">>]"); + processColors |= psProcessCMYK; break; case csICCBased: + // there is no transform function to the alternate color space, so + // we can use it directly dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt()); break; case csIndexed: indexedCS = (GfxIndexedColorSpace *)colorSpace; + baseCS = indexedCS->getBase(); writePS("[/Indexed "); - dumpColorSpaceL2(indexedCS->getBase()); + dumpColorSpaceL2(baseCS); n = indexedCS->getIndexHigh(); - numComps = indexedCS->getBase()->getNComps(); + numComps = baseCS->getNComps(); lookup = indexedCS->getLookup(); - writePS(" %d <\n", n); - for (i = 0; i <= n; i += 8) { - writePS(" "); - for (j = i; j < i+8 && j <= n; ++j) { - for (k = 0; k < numComps; ++k) { - writePS("%02x", lookup[j * numComps + k]); + writePSFmt(" %d <\n", n); + if (baseCS->getMode() == csDeviceN) { + func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); + 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] = *p++ / 255.0; + } + func->transform(x, y); + 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("%02x", byte); + } + color.c[0] = j; + indexedCS->getCMYK(&color, &cmyk); + addProcessColor(cmyk.c, cmyk.m, cmyk.y, 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("%02x", lookup[j * numComps + k]); + } + color.c[0] = j; + indexedCS->getCMYK(&color, &cmyk); + addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k); + } + writePS("\n"); } - writePS("\n"); } writePS(">]"); break; @@ -1761,7 +3207,7 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) { //~ separation color space, with the specified alternate color //~ space and tint transform separationCS = (GfxSeparationColorSpace *)colorSpace; - writePS(" [/Indexed "); + writePS("[/Indexed "); dumpColorSpaceL2(separationCS->getAlt()); writePS(" 255 <\n"); numComps = separationCS->getAlt()->getNComps(); @@ -1771,12 +3217,16 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) { x[0] = (double)j / 255.0; separationCS->getFunc()->transform(x, y); for (k = 0; k < numComps; ++k) { - writePS("%02x", (int)(255 * y[k] + 0.5)); + writePSFmt("%02x", (int)(255 * y[k] + 0.5)); } } writePS("\n"); } writePS(">]"); +#if 0 //~ this shouldn't be here since the PS file doesn't actually refer + //~ to this colorant (it's converted to CMYK instead) + addCustomColor(separationCS); +#endif break; case csDeviceN: @@ -1795,7 +3245,7 @@ void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) { void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) { Object dict; - if (psOutOPI) { + if (globalParams->getPSOPI()) { opiDict->lookup("2.0", &dict); if (dict.isDict()) { opiBegin20(state, dict.getDict()); @@ -1817,20 +3267,20 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { int w, h; int i; - writePS("%%%%BeginOPI: 2.0\n"); - writePS("%%%%Distilled\n"); + writePS("%%BeginOPI: 2.0\n"); + writePS("%%Distilled\n"); dict->lookup("F", &obj1); if (getFileSpec(&obj1, &obj2)) { - writePS("%%%%ImageFileName: %s\n", - obj2.getString()->getCString()); + writePSFmt("%%%%ImageFileName: %s\n", + obj2.getString()->getCString()); obj2.free(); } obj1.free(); dict->lookup("MainImage", &obj1); if (obj1.isString()) { - writePS("%%%%MainImage: %s\n", obj1.getString()->getCString()); + writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString()); } obj1.free(); @@ -1845,7 +3295,7 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { obj1.arrayGet(1, &obj2); height = obj2.getNum(); obj2.free(); - writePS("%%%%ImageDimensions: %g %g\n", width, height); + writePSFmt("%%%%ImageDimensions: %g %g\n", width, height); } obj1.free(); @@ -1863,31 +3313,31 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { obj1.arrayGet(3, &obj2); bottom = obj2.getNum(); obj2.free(); - writePS("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom); + writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom); } obj1.free(); dict->lookup("Overprint", &obj1); if (obj1.isBool()) { - writePS("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false"); + writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false"); } obj1.free(); dict->lookup("Inks", &obj1); if (obj1.isName()) { - writePS("%%%%ImageInks: %s\n", obj1.getName()); + writePSFmt("%%%%ImageInks: %s\n", obj1.getName()); } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) { obj1.arrayGet(0, &obj2); if (obj2.isName()) { - writePS("%%%%ImageInks: %s %d", - obj2.getName(), (obj1.arrayGetLength() - 1) / 2); + writePSFmt("%%%%ImageInks: %s %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()); - writePS(" %g", obj4.getNum()); + writePSFmt(" %g", obj4.getNum()); } obj3.free(); obj4.free(); @@ -1900,7 +3350,7 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { writePS("gsave\n"); - writePS("%%%%BeginIncludedImage\n"); + writePS("%%BeginIncludedImage\n"); dict->lookup("IncludedImageDimensions", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { @@ -1910,13 +3360,13 @@ void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { obj1.arrayGet(1, &obj2); h = obj2.getInt(); obj2.free(); - writePS("%%%%IncludedImageDimensions: %d %d\n", w, h); + writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h); } obj1.free(); dict->lookup("IncludedImageQuality", &obj1); if (obj1.isNum()) { - writePS("%%%%IncludedImageQuality: %g\n", obj1.getNum()); + writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum()); } obj1.free(); @@ -1938,8 +3388,8 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { dict->lookup("F", &obj1); if (getFileSpec(&obj1, &obj2)) { - writePS("%%ALDImageFileName: %s\n", - obj2.getString()->getCString()); + writePSFmt("%%ALDImageFileName: %s\n", + obj2.getString()->getCString()); obj2.free(); } obj1.free(); @@ -1958,7 +3408,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { obj1.arrayGet(3, &obj2); bottom = obj2.getInt(); obj2.free(); - writePS("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom); + writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom); } obj1.free(); @@ -1978,7 +3428,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { obj2.free(); obj1.arrayGet(4, &obj2); if (obj2.isString()) { - writePS("%%ALDImageColor: %g %g %g %g ", c, m, y, k); + writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k); writePSString(obj2.getString()); writePS("\n"); } @@ -1988,7 +3438,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { dict->lookup("ColorType", &obj1); if (obj1.isName()) { - writePS("%%ALDImageColorType: %s\n", obj1.getName()); + writePSFmt("%%ALDImageColorType: %s\n", obj1.getName()); } obj1.free(); @@ -2009,20 +3459,20 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { obj1.arrayGet(3, &obj2); lry = obj2.getNum(); obj2.free(); - writePS("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry); + writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry); } obj1.free(); dict->lookup("GrayMap", &obj1); if (obj1.isArray()) { - writePS("%%ALDImageGrayMap:"); + writePS("%ALDImageGrayMap:"); for (i = 0; i < obj1.arrayGetLength(); i += 16) { if (i > 0) { - writePS("\n%%%%+"); + writePS("\n%%+"); } for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) { obj1.arrayGet(i+j, &obj2); - writePS(" %d", obj2.getInt()); + writePSFmt(" %d", obj2.getInt()); obj2.free(); } } @@ -2032,7 +3482,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { dict->lookup("ID", &obj1); if (obj1.isString()) { - writePS("%%ALDImageID: %s\n", obj1.getString()->getCString()); + writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString()); } obj1.free(); @@ -2044,13 +3494,13 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { obj1.arrayGet(1, &obj2); bits = obj2.getInt(); obj2.free(); - writePS("%%ALDImageType: %d %d\n", samples, bits); + writePSFmt("%%ALDImageType: %d %d\n", samples, bits); } obj1.free(); dict->lookup("Overprint", &obj1); if (obj1.isBool()) { - writePS("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false"); + writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false"); } obj1.free(); @@ -2084,8 +3534,8 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { opiTransform(state, ulx, uly, &tulx, &tuly); opiTransform(state, urx, ury, &turx, &tury); opiTransform(state, lrx, lry, &tlrx, &tlry); - writePS("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n", - tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); + writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n", + tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); obj2.free(); } obj1.free(); @@ -2098,7 +3548,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { obj1.arrayGet(1, &obj2); vert = obj2.getNum(); obj2.free(); - writePS("%%ALDImageResoution: %g %g\n", horiz, vert); + writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert); obj2.free(); } obj1.free(); @@ -2111,7 +3561,7 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { obj1.arrayGet(1, &obj2); height = obj2.getInt(); obj2.free(); - writePS("%%ALDImageDimensions: %d %d\n", width, height); + writePSFmt("%%ALDImageDimensions: %d %d\n", width, height); } obj1.free(); @@ -2120,17 +3570,17 @@ void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { dict->lookup("Tint", &obj1); if (obj1.isNum()) { - writePS("%%ALDImageTint: %g\n", obj1.getNum()); + writePSFmt("%%ALDImageTint: %g\n", obj1.getNum()); } obj1.free(); dict->lookup("Transparency", &obj1); if (obj1.isBool()) { - writePS("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false"); + writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false"); } obj1.free(); - writePS("%%%%BeginObject: image\n"); + writePS("%%BeginObject: image\n"); writePS("opiMatrix2 setmatrix\n"); ++opi13Nest; } @@ -2145,10 +3595,17 @@ void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, state->transform(x0, y0, x1, y1); *x1 += tx; *y1 += ty; - if (landscape) { + 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; @@ -2157,11 +3614,11 @@ void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) { Object dict; - if (psOutOPI) { + if (globalParams->getPSOPI()) { opiDict->lookup("2.0", &dict); if (dict.isDict()) { - writePS("%%%%EndIncludedImage\n"); - writePS("%%%%EndOPI\n"); + writePS("%%EndIncludedImage\n"); + writePS("%%EndOPI\n"); writePS("grestore\n"); --opi20Nest; dict.free(); @@ -2169,7 +3626,7 @@ void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) { dict.free(); opiDict->lookup("1.3", &dict); if (dict.isDict()) { - writePS("%%%%EndObject\n"); + writePS("%%EndObject\n"); writePS("restore\n"); --opi13Nest; } @@ -2209,26 +3666,138 @@ GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) { } #endif // OPI_SUPPORT -void PSOutputDev::writePS(const char *fmt, ...) { +void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { + writePSFmt("%g %g setcharwidth\n", wx, wy); + writePS("q\n"); +} + +void PSOutputDev::type3D1(GfxState *state, double wx, double wy, + double llx, double lly, double urx, double ury) { + t3WX = wx; + t3WY = wy; + t3LLX = llx; + t3LLY = lly; + t3URX = urx; + t3URY = ury; + t3String = new GString(); + writePS("q\n"); + t3Cacheable = gTrue; +} + +void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) { + Stream *str; + int c; + + if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) { + str = level1Stream; + } else { + str = psStream; + } + str->reset(); + while ((c = str->getChar()) != EOF) { + writePSChar(c); + } + str->close(); +} + +void PSOutputDev::writePSChar(char c) { + if (t3String) { + t3String->append(c); + } else { + (*outputFunc)(outputStream, &c, 1); + } +} + +void PSOutputDev::writePS(char *s) { + if (t3String) { + t3String->append(s); + } else { + (*outputFunc)(outputStream, s, strlen(s)); + } +} + +void PSOutputDev::writePSFmt(const char *fmt, ...) { va_list args; + char buf[512]; va_start(args, fmt); - vfprintf(f, fmt, args); + vsprintf(buf, fmt, args); va_end(args); + if (t3String) { + t3String->append(buf); + } else { + (*outputFunc)(outputStream, buf, strlen(buf)); + } } void PSOutputDev::writePSString(GString *s) { Guchar *p; int n; + char buf[8]; - fputc('(', f); + writePSChar('('); for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) { - if (*p == '(' || *p == ')' || *p == '\\') - fprintf(f, "\\%c", *p); - else if (*p < 0x20 || *p >= 0x80) - fprintf(f, "\\%03o", *p); - else - fputc(*p, f); - } - fputc(')', f); + if (*p == '(' || *p == ')' || *p == '\\') { + writePSChar('\\'); + writePSChar((char)*p); + } else if (*p < 0x20 || *p >= 0x80) { + sprintf(buf, "\\%03o", *p); + if (t3String) { + t3String->append(buf); + } else { + (*outputFunc)(outputStream, buf, strlen(buf)); + } + } else { + writePSChar((char)*p); + } + } + writePSChar(')'); +} + +void PSOutputDev::writePSName(char *s) { + char *p; + char c; + + p = s; + while ((c = *p++)) { + if (c <= (char)0x20 || c >= (char)0x7f || + c == '(' || c == ')' || c == '<' || c == '>' || + c == '[' || c == ']' || c == '{' || c == '}' || + c == '/' || c == '%') { + writePSFmt("#%02x", c & 0xff); + } else { + writePSChar(c); + } + } +} + +GString *PSOutputDev::filterPSName(GString *name) { + GString *name2; + char buf[8]; + int i; + char c; + + name2 = new GString(); + + // ghostscript chokes on names that begin with out-of-limits + // numbers, e.g., 1e4foo is handled correctly (as a name), but + // 1e999foo generates a limitcheck error + c = name->getChar(0); + if (c >= '0' && c <= '9') { + name2->append('f'); + } + + for (i = 0; i < name->getLength(); ++i) { + c = name->getChar(i); + if (c <= (char)0x20 || c >= (char)0x7f || + c == '(' || c == ')' || c == '<' || c == '>' || + c == '[' || c == ']' || c == '{' || c == '}' || + c == '/' || c == '%') { + sprintf(buf, "#%02x", c & 0xff); + name2->append(buf); + } else { + name2->append(c); + } + } + return name2; }