1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
23 #include "GlobalParams.h"
30 #include "UnicodeMap.h"
36 #include "PSOutputDev.h"
39 // needed for setting type/creator of MacOS files
40 #include "ICSupport.h"
43 //------------------------------------------------------------------------
44 // PostScript prolog and setup
45 //------------------------------------------------------------------------
47 static char *prolog[] = {
48 "/xpdf 75 dict def xpdf begin",
49 "% PDF special state",
50 "/pdfDictSize 15 def",
52 "/pdfStates 64 array def",
54 " pdfStates exch pdfDictSize dict",
55 " dup /pdfStateIdx 3 index put",
60 " 3 1 roll 2 array astore",
61 " /setpagedevice where {",
63 " /PageSize exch def",
64 " /ImagingBBox null def",
65 " /Policies 1 dict dup begin /PageSize 3 def end def",
66 " { /Duplex true def } if",
67 " currentdict end setpagedevice",
74 " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
75 " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
76 " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
81 " pdfStates 0 get begin",
83 " pdfDictSize dict begin",
86 " /pdfStroke [0] def",
87 " /pdfLastFill false def",
88 " /pdfLastStroke false def",
89 " /pdfTextMat [1 0 0 1 0 0] def",
90 " /pdfFontSize 0 def",
91 " /pdfCharSpacing 0 def",
92 " /pdfTextRender 0 def",
93 " /pdfTextRise 0 def",
94 " /pdfWordSpacing 0 def",
95 " /pdfHorizScaling 1 def",
96 " /pdfTextClipPath [] def",
98 "/pdfEndPage { end } def",
99 "% separation convention operators",
100 "/findcmykcustomcolor where {",
103 " /findcmykcustomcolor { 5 array astore } def",
105 "/setcustomcolor where {",
108 " /setcustomcolor {",
110 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
111 " 0 4 getinterval cvx",
112 " [ exch /dup load exch { mul exch dup } /forall load",
113 " /pop load dup ] cvx",
114 " ] setcolorspace setcolor",
117 "/customcolorimage where {",
120 " /customcolorimage {",
122 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
124 " [ exch /dup load exch { mul exch dup } /forall load",
125 " /pop load dup ] cvx",
129 " /DataSource exch def",
130 " /ImageMatrix exch def",
131 " /BitsPerComponent exch def",
134 " /Decode [1 0] def",
142 " pdfLastStroke not {",
143 " pdfStroke aload length",
153 " findcmykcustomcolor exch setcustomcolor",
157 " /pdfLastStroke true def /pdfLastFill false def",
161 " pdfLastFill not {",
162 " pdfFill aload length",
172 " findcmykcustomcolor exch setcustomcolor",
176 " /pdfLastFill true def /pdfLastStroke false def",
181 " 4 3 roll findfont",
182 " 4 2 roll matrix scale makefont",
183 " dup length dict begin",
184 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
185 " /Encoding exch def",
192 " dup length dict begin",
193 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
199 "/pdfMakeFont16L3 {",
200 " 1 index /CIDFont resourcestatus {",
201 " pop pop 1 index /CIDFont findresource /CIDFontType known",
206 " 0 eq { /Identity-H } { /Identity-V } ifelse",
207 " exch 1 array astore composefont pop",
212 "% graphics state operators",
216 " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
217 " pdfStates pdfStateIdx 1 add get begin",
218 " pdfOpNames { exch def } forall",
221 "/q { gsave pdfDictSize dict begin } def",
223 "/Q { end grestore } def",
224 "/cm { concat } def",
225 "/d { setdash } def",
226 "/i { setflat } def",
227 "/j { setlinejoin } def",
228 "/J { setlinecap } def",
229 "/M { setmiterlimit } def",
230 "/w { setlinewidth } def",
232 "/g { dup 1 array astore /pdfFill exch def setgray",
233 " /pdfLastFill true def /pdfLastStroke false def } def",
234 "/G { dup 1 array astore /pdfStroke exch def setgray",
235 " /pdfLastStroke true def /pdfLastFill false def } def",
236 "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor",
237 " /pdfLastFill true def /pdfLastStroke false def } def",
238 "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor",
239 " /pdfLastStroke true def /pdfLastFill false def } def",
240 "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
241 " /pdfLastFill true def /pdfLastStroke false def } def",
242 "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
243 " /pdfLastStroke true def /pdfLastFill false def } def",
244 "/ck { 6 copy 6 array astore /pdfFill exch def",
245 " findcmykcustomcolor exch setcustomcolor",
246 " /pdfLastFill true def /pdfLastStroke false def } def",
247 "/CK { 6 copy 6 array astore /pdfStroke exch def",
248 " findcmykcustomcolor exch setcustomcolor",
249 " /pdfLastStroke true def /pdfLastFill false def } def",
250 "% path segment operators",
253 "/c { curveto } def",
254 "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
255 " neg 0 rlineto closepath } def",
256 "/h { closepath } def",
257 "% path painting operators",
258 "/S { sCol stroke } def",
259 "/Sf { fCol stroke } def",
260 "/f { fCol fill } def",
261 "/f* { fCol eofill } def",
262 "% clipping operators",
263 "/W { clip newpath } def",
264 "/W* { eoclip newpath } def",
265 "% text state operators",
266 "/Tc { /pdfCharSpacing exch def } def",
267 "/Tf { dup /pdfFontSize exch def",
268 " dup pdfHorizScaling mul exch matrix scale",
269 " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
270 " exch findfont exch makefont setfont } def",
271 "/Tr { /pdfTextRender exch def } def",
272 "/Ts { /pdfTextRise exch def } def",
273 "/Tw { /pdfWordSpacing exch def } def",
274 "/Tz { /pdfHorizScaling exch def } def",
275 "% text positioning operators",
276 "/Td { pdfTextMat transform moveto } def",
277 "/Tm { /pdfTextMat exch def } def",
278 "% text string operators",
284 " 1 string dup 0 3 index put 3 index exec",
290 " currentfont /FontType get 0 eq {",
291 " 0 2 2 index length 1 sub {",
292 " 2 copy get exch 1 add 2 index exch get",
293 " 2 copy exch 256 mul add",
294 " 2 string dup 0 6 5 roll put dup 1 5 4 roll put",
299 " 1 string dup 0 3 index put 3 index exec",
305 "/awcp {", // awidthcharpath
308 " 5 index 5 index rmoveto",
309 " 6 index eq { 7 index 7 index rmoveto } if",
314 " fCol", // because stringwidth has to draw Type 3 chars
315 " 1 index stringwidth pdfTextMat idtransform pop",
316 " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
317 " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
318 " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
319 " pdfTextMat dtransform",
323 " fCol", // because stringwidth has to draw Type 3 chars
324 " 2 index stringwidth pdfTextMat idtransform pop",
326 " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
327 " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
328 " pdfTextMat dtransform",
332 " fCol", // because stringwidth has to draw Type 3 chars
333 " 2 index stringwidth pdfTextMat idtransform exch pop",
335 " 0 pdfWordSpacing pdfTextMat dtransform 32",
336 " 4 3 roll pdfCharSpacing add 0 exch",
337 " pdfTextMat dtransform",
341 " 0 pdfTextRise pdfTextMat dtransform rmoveto",
342 " currentpoint 8 2 roll",
343 " pdfTextRender 1 and 0 eq {",
344 " 6 copy awidthshow",
346 " pdfTextRender 3 and dup 1 eq exch 2 eq or {",
347 " 7 index 7 index moveto",
349 " currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
350 " false awcp currentpoint stroke moveto",
352 " pdfTextRender 4 and 0 ne {",
355 " /pdfTextClipPath [ pdfTextClipPath aload pop",
361 " currentpoint newpath moveto",
365 " 0 pdfTextRise neg pdfTextMat dtransform rmoveto",
367 "/TJm { pdfFontSize 0.001 mul mul neg 0",
368 " pdfTextMat dtransform rmoveto } def",
369 "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
370 " pdfTextMat dtransform rmoveto } def",
371 "/Tclip { pdfTextClipPath cvx exec clip",
372 " /pdfTextClipPath [] def } def",
373 "% Level 1 image operators",
375 " /pdfImBuf1 4 index string def",
376 " { currentfile pdfImBuf1 readhexstring pop } image",
379 " /pdfImBuf1 4 index string def",
380 " /pdfImBuf2 4 index string def",
381 " /pdfImBuf3 4 index string def",
382 " /pdfImBuf4 4 index string def",
383 " { currentfile pdfImBuf1 readhexstring pop }",
384 " { currentfile pdfImBuf2 readhexstring pop }",
385 " { currentfile pdfImBuf3 readhexstring pop }",
386 " { currentfile pdfImBuf4 readhexstring pop }",
387 " true 4 colorimage",
390 " /pdfImBuf1 4 index 7 add 8 idiv string def",
391 " { currentfile pdfImBuf1 readhexstring pop } imagemask",
393 "% Level 2 image operators",
394 "/pdfImBuf 100 string def",
397 " { currentfile pdfImBuf readline",
398 " not { pop exit } if",
399 " (%-EOD-) eq { exit } if } loop",
402 " findcmykcustomcolor exch",
403 " dup /Width get /pdfImBuf1 exch string def",
404 " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
405 " /pdfImDecodeLow exch def",
406 " begin Width Height BitsPerComponent ImageMatrix DataSource end",
407 " /pdfImData exch def",
408 " { pdfImData pdfImBuf1 readstring pop",
409 " 0 1 2 index length 1 sub {",
410 " 1 index exch 2 copy get",
411 " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
414 " 6 5 roll customcolorimage",
415 " { currentfile pdfImBuf readline",
416 " not { pop exit } if",
417 " (%-EOD-) eq { exit } if } loop",
421 " { currentfile pdfImBuf readline",
422 " not { pop exit } if",
423 " (%-EOD-) eq { exit } if } loop",
429 static char *cmapProlog[] = {
430 "/CIDInit /ProcSet findresource begin",
434 " /CMapName /Identity-H def",
435 " /CIDSystemInfo 3 dict dup begin",
436 " /Registry (Adobe) def",
437 " /Ordering (Identity) def",
438 " /Supplement 0 def",
440 " 1 begincodespacerange",
442 " endcodespacerange",
448 " currentdict CMapName exch /CMap defineresource pop",
453 " /CMapName /Identity-V def",
454 " /CIDSystemInfo 3 dict dup begin",
455 " /Registry (Adobe) def",
456 " /Ordering (Identity) def",
457 " /Supplement 0 def",
460 " 1 begincodespacerange",
462 " endcodespacerange",
468 " currentdict CMapName exch /CMap defineresource pop",
474 //------------------------------------------------------------------------
476 //------------------------------------------------------------------------
479 char *psName; // PostScript name
480 double mWidth; // width of 'm' character
483 static char *psFonts[] = {
487 "Courier-BoldOblique",
491 "Helvetica-BoldOblique",
501 static PSSubstFont psSubstFonts[] = {
502 {"Helvetica", 0.833},
503 {"Helvetica-Oblique", 0.833},
504 {"Helvetica-Bold", 0.889},
505 {"Helvetica-BoldOblique", 0.889},
506 {"Times-Roman", 0.788},
507 {"Times-Italic", 0.722},
508 {"Times-Bold", 0.833},
509 {"Times-BoldItalic", 0.778},
511 {"Courier-Oblique", 0.600},
512 {"Courier-Bold", 0.600},
513 {"Courier-BoldOblique", 0.600}
516 // Encoding info for substitute 16-bit font
522 //------------------------------------------------------------------------
524 //------------------------------------------------------------------------
526 #define psProcessCyan 1
527 #define psProcessMagenta 2
528 #define psProcessYellow 4
529 #define psProcessBlack 8
530 #define psProcessCMYK 15
532 //------------------------------------------------------------------------
534 //------------------------------------------------------------------------
536 class PSOutCustomColor {
539 PSOutCustomColor(double cA, double mA,
540 double yA, double kA, GString *nameA);
545 PSOutCustomColor *next;
548 PSOutCustomColor::PSOutCustomColor(double cA, double mA,
549 double yA, double kA, GString *nameA) {
558 PSOutCustomColor::~PSOutCustomColor() {
562 //------------------------------------------------------------------------
564 //------------------------------------------------------------------------
567 typedef void (*SignalFunc)(int);
570 static void outputToFile(void *stream, char *data, int len) {
571 fwrite(data, 1, len, (FILE *)stream);
574 PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
575 int firstPage, int lastPage, PSOutMode modeA,
576 int paperWidthA, int paperHeightA,
579 PSFileType fileTypeA;
582 underlayCbkData = NULL;
584 overlayCbkData = NULL;
588 fontFileNames = NULL;
593 haveTextClip = gFalse;
597 if (!strcmp(fileName, "-")) {
598 fileTypeA = psStdout;
600 } else if (fileName[0] == '|') {
604 signal(SIGPIPE, (SignalFunc)SIG_IGN);
606 if (!(f = popen(fileName + 1, "w"))) {
607 error(-1, "Couldn't run print command '%s'", fileName);
612 error(-1, "Print commands are not supported ('%s')", fileName);
618 if (!(f = fopen(fileName, "w"))) {
619 error(-1, "Couldn't open PostScript file '%s'", fileName);
625 init(outputToFile, f, fileTypeA,
626 xrefA, catalog, firstPage, lastPage, modeA,
627 paperWidthA, paperHeightA, manualCtrlA);
630 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
631 XRef *xrefA, Catalog *catalog,
632 int firstPage, int lastPage, PSOutMode modeA,
633 int paperWidthA, int paperHeightA,
637 fontFileNames = NULL;
642 haveTextClip = gFalse;
645 init(outputFuncA, outputStreamA, psGeneric,
646 xrefA, catalog, firstPage, lastPage, modeA,
647 paperWidthA, paperHeightA, manualCtrlA);
650 void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
651 PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
652 int firstPage, int lastPage, PSOutMode modeA,
653 int paperWidthA, int paperHeightA,
659 outputFunc = outputFuncA;
660 outputStream = outputStreamA;
661 fileType = fileTypeA;
663 level = globalParams->getPSLevel();
665 paperWidth = paperWidthA;
666 paperHeight = paperHeightA;
667 if (paperWidth == 0) {
668 paperWidth = globalParams->getPSPaperWidth();
670 if (paperHeight == 0) {
671 paperHeight = globalParams->getPSPaperHeight();
673 if (paperWidth < 0 || paperHeight < 0) {
674 page = catalog->getPage(firstPage);
675 paperWidth = (int)(page->getWidth() + 0.5);
676 paperHeight = (int)(page->getHeight() + 0.5);
678 manualCtrl = manualCtrlA;
679 if (mode == psModeForm) {
680 lastPage = firstPage;
683 inType3Char = gFalse;
686 // initialize OPI nesting levels
691 // initialize fontIDs, fontFileIDs, and fontFileNames lists
694 fontIDs = (Ref *)gmalloc(fontIDSize * sizeof(Ref));
697 fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref));
698 fontFileNameSize = 64;
700 fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *));
705 xobjStack = new GList();
708 // initialize embedded font resource comment list
709 embFontList = new GString();
712 writeHeader(firstPage, lastPage, catalog->getPage(firstPage)->getBox());
713 if (mode != psModeForm) {
714 writePS("%%BeginProlog\n");
717 if (mode != psModeForm) {
718 writePS("%%EndProlog\n");
719 writePS("%%BeginSetup\n");
721 writeDocSetup(catalog, firstPage, lastPage);
722 if (mode != psModeForm) {
723 writePS("%%EndSetup\n");
727 // initialize sequential page number
731 PSOutputDev::~PSOutputDev() {
732 PSOutCustomColor *cc;
737 writePS("%%Trailer\n");
739 if (mode != psModeForm) {
743 if (fileType == psFile) {
745 ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
747 fclose((FILE *)outputStream);
750 else if (fileType == psPipe) {
751 pclose((FILE *)outputStream);
753 signal(SIGPIPE, (SignalFunc)SIG_DFL);
768 for (i = 0; i < fontFileNameLen; ++i) {
769 delete fontFileNames[i];
771 gfree(fontFileNames);
774 for (i = 0; i < font16EncLen; ++i) {
775 delete font16Enc[i].enc;
782 while (customColors) {
784 customColors = cc->next;
789 void PSOutputDev::writeHeader(int firstPage, int lastPage, PDFRectangle *box) {
792 writePS("%!PS-Adobe-3.0\n");
793 writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
794 writePSFmt("%%%%LanguageLevel: %d\n",
795 (level == psLevel1 || level == psLevel1Sep) ? 1 :
796 (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
797 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
798 writePS("%%DocumentProcessColors: (atend)\n");
799 writePS("%%DocumentCustomColors: (atend)\n");
801 writePS("%%DocumentSuppliedResources: (atend)\n");
802 writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
803 paperWidth, paperHeight);
804 writePSFmt("%%%%BoundingBox: 0 0 %d %d\n", paperWidth, paperHeight);
805 writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
806 writePS("%%EndComments\n");
807 writePS("%%BeginDefaults\n");
808 writePS("%%PageMedia: plain\n");
809 writePS("%%EndDefaults\n");
812 writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
813 writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
814 writePSFmt("%%%%LanguageLevel: %d\n",
815 (level == psLevel1 || level == psLevel1Sep) ? 1 :
816 (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
817 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
818 writePS("%%DocumentProcessColors: (atend)\n");
819 writePS("%%DocumentCustomColors: (atend)\n");
821 writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
822 (int)floor(box->x1), (int)floor(box->y1),
823 (int)ceil(box->x2), (int)ceil(box->y2));
824 if (floor(box->x1) != ceil(box->x1) ||
825 floor(box->y1) != ceil(box->y1) ||
826 floor(box->x2) != ceil(box->x2) ||
827 floor(box->y2) != ceil(box->y2)) {
828 writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n",
829 box->x1, box->y1, box->x2, box->y2);
831 writePS("%%DocumentSuppliedResources: (atend)\n");
832 writePS("%%EndComments\n");
835 writePS("%!PS-Adobe-3.0 Resource-Form\n");
836 writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
837 writePSFmt("%%%%LanguageLevel: %d\n",
838 (level == psLevel1 || level == psLevel1Sep) ? 1 :
839 (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
840 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
841 writePS("%%DocumentProcessColors: (atend)\n");
842 writePS("%%DocumentCustomColors: (atend)\n");
844 writePS("%%DocumentSuppliedResources: (atend)\n");
845 writePS("%%EndComments\n");
846 writePS("32 dict dup begin\n");
847 writePSFmt("/BBox [%d %d %d %d] def\n",
848 (int)box->x1, (int)box->y1, (int)box->x2, (int)box->y2);
849 writePS("/FormType 1 def\n");
850 writePS("/Matrix [1 0 0 1 0 0] def\n");
855 void PSOutputDev::writeXpdfProcset() {
859 writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
861 for (p = prolog; *p; ++p) {
862 if ((*p)[0] == '~' && (*p)[1] == '1') {
864 } else if ((*p)[0] == '~' && (*p)[1] == '2') {
866 } else if ((*p)[0] == '~' && (*p)[1] == 'a') {
868 } else if (prologLevel == 'a' ||
869 (prologLevel == '1' && level < psLevel2) ||
870 (prologLevel == '2' && level >= psLevel2)) {
871 writePSFmt("%s\n", *p);
874 writePS("%%EndResource\n");
876 if (level >= psLevel3) {
877 for (p = cmapProlog; *p; ++p) {
878 writePSFmt("%s\n", *p);
883 void PSOutputDev::writeDocSetup(Catalog *catalog,
884 int firstPage, int lastPage) {
891 if (mode == psModeForm) {
892 // swap the form and xpdf dicts
893 writePS("xpdf end begin dup begin\n");
895 writePS("xpdf begin\n");
897 for (pg = firstPage; pg <= lastPage; ++pg) {
898 page = catalog->getPage(pg);
899 if ((resDict = page->getResourceDict())) {
900 setupResources(resDict);
902 annots = new Annots(xref, page->getAnnots(&obj1));
904 for (i = 0; i < annots->getNumAnnots(); ++i) {
905 if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
906 obj1.streamGetDict()->lookup("Resources", &obj2);
908 setupResources(obj2.getDict());
916 if (mode != psModeForm) {
917 if (mode != psModeEPS && !manualCtrl) {
918 writePSFmt("%d %d %s pdfSetup\n",
919 paperWidth, paperHeight,
920 globalParams->getPSDuplex() ? "true" : "false");
923 if (globalParams->getPSOPI()) {
924 writePS("/opiMatrix matrix currentmatrix def\n");
930 void PSOutputDev::writePageTrailer() {
931 if (mode != psModeForm) {
932 writePS("pdfEndPage\n");
936 void PSOutputDev::writeTrailer() {
937 PSOutCustomColor *cc;
939 if (mode == psModeForm) {
940 writePS("/Foo exch /Form defineresource pop\n");
943 writePS("%%DocumentSuppliedResources:\n");
944 writePS(embFontList->getCString());
945 if (level == psLevel1Sep || level == psLevel2Sep ||
946 level == psLevel3Sep) {
947 writePS("%%DocumentProcessColors:");
948 if (processColors & psProcessCyan) {
951 if (processColors & psProcessMagenta) {
954 if (processColors & psProcessYellow) {
957 if (processColors & psProcessBlack) {
961 writePS("%%DocumentCustomColors:");
962 for (cc = customColors; cc; cc = cc->next) {
963 writePSFmt(" (%s)", cc->name->getCString());
966 writePS("%%CMYKCustomColor:\n");
967 for (cc = customColors; cc; cc = cc->next) {
968 writePSFmt("%%%%+ %g %g %g %g (%s)\n",
969 cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
975 void PSOutputDev::setupResources(Dict *resDict) {
976 Object xObjDict, xObjRef, xObj, resObj;
982 setupImages(resDict);
984 resDict->lookup("XObject", &xObjDict);
985 if (xObjDict.isDict()) {
986 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
988 // avoid infinite recursion on XObjects
990 if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
991 ref0 = xObjRef.getRef();
992 for (j = 0; j < xobjStack->getLength(); ++j) {
993 ref1 = *(Ref *)xobjStack->get(j);
994 if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
1000 xobjStack->append(&ref0);
1005 // process the XObject's resource dictionary
1006 xObjDict.dictGetVal(i, &xObj);
1007 if (xObj.isStream()) {
1008 xObj.streamGetDict()->lookup("Resources", &resObj);
1009 if (resObj.isDict()) {
1010 setupResources(resObj.getDict());
1017 if (xObjRef.isRef() && !skip) {
1018 xobjStack->del(xobjStack->getLength() - 1);
1026 void PSOutputDev::setupFonts(Dict *resDict) {
1029 GfxFontDict *gfxFontDict;
1034 resDict->lookupNF("Font", &obj1);
1036 obj1.fetch(xref, &obj2);
1037 if (obj2.isDict()) {
1039 gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
1042 } else if (obj1.isDict()) {
1043 gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
1046 for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
1047 font = gfxFontDict->getFont(i);
1048 setupFont(font, resDict);
1055 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
1058 PSFontParam *fontParam;
1060 char type3Name[64], buf[16];
1070 // check if font is already set up
1071 for (i = 0; i < fontIDLen; ++i) {
1072 if (fontIDs[i].num == font->getID()->num &&
1073 fontIDs[i].gen == font->getID()->gen) {
1078 // add entry to fontIDs list
1079 if (fontIDLen >= fontIDSize) {
1081 fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref));
1083 fontIDs[fontIDLen++] = *font->getID();
1088 // check for resident 8-bit font
1089 if (font->getName() &&
1090 (fontParam = globalParams->getPSFont(font->getName()))) {
1091 psName = new GString(fontParam->psFontName->getCString());
1093 // check for embedded Type 1 font
1094 } else if (globalParams->getPSEmbedType1() &&
1095 font->getType() == fontType1 &&
1096 font->getEmbeddedFontID(&fontFileID)) {
1097 psName = filterPSName(font->getEmbeddedFontName());
1098 setupEmbeddedType1Font(&fontFileID, psName);
1100 // check for embedded Type 1C font
1101 } else if (globalParams->getPSEmbedType1() &&
1102 font->getType() == fontType1C &&
1103 font->getEmbeddedFontID(&fontFileID)) {
1104 psName = filterPSName(font->getEmbeddedFontName());
1105 setupEmbeddedType1CFont(font, &fontFileID, psName);
1107 // check for external Type 1 font file
1108 } else if (globalParams->getPSEmbedType1() &&
1109 font->getType() == fontType1 &&
1110 font->getExtFontFile()) {
1111 // this assumes that the PS font name matches the PDF font name
1112 psName = font->getName()->copy();
1113 setupExternalType1Font(font->getExtFontFile(), psName);
1115 // check for embedded TrueType font
1116 } else if (globalParams->getPSEmbedTrueType() &&
1117 font->getType() == fontTrueType &&
1118 font->getEmbeddedFontID(&fontFileID)) {
1119 psName = filterPSName(font->getEmbeddedFontName());
1120 setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
1122 // check for external TrueType font file
1123 } else if (globalParams->getPSEmbedTrueType() &&
1124 font->getType() == fontTrueType &&
1125 font->getExtFontFile()) {
1126 psName = filterPSName(font->getName());
1127 setupExternalTrueTypeFont(font, psName);
1129 // check for embedded CID PostScript font
1130 } else if (globalParams->getPSEmbedCIDPostScript() &&
1131 font->getType() == fontCIDType0C &&
1132 font->getEmbeddedFontID(&fontFileID)) {
1133 psName = filterPSName(font->getEmbeddedFontName());
1134 setupEmbeddedCIDType0Font(font, &fontFileID, psName);
1136 // check for embedded CID TrueType font
1137 } else if (globalParams->getPSEmbedCIDTrueType() &&
1138 font->getType() == fontCIDType2 &&
1139 font->getEmbeddedFontID(&fontFileID)) {
1140 psName = filterPSName(font->getEmbeddedFontName());
1141 setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName);
1143 } else if (font->getType() == fontType3) {
1144 sprintf(type3Name, "T3_%d_%d",
1145 font->getID()->num, font->getID()->gen);
1146 psName = new GString(type3Name);
1147 setupType3Font(font, psName, parentResDict);
1149 // do 8-bit font substitution
1150 } else if (!font->isCIDFont()) {
1152 name = font->getName();
1155 for (i = 0; psFonts[i]; ++i) {
1156 if (name->cmp(psFonts[i]) == 0) {
1157 psName = new GString(psFonts[i]);
1163 if (font->isFixedWidth()) {
1165 } else if (font->isSerif()) {
1170 if (font->isBold()) {
1173 if (font->isItalic()) {
1176 psName = new GString(psSubstFonts[i].psName);
1177 for (code = 0; code < 256; ++code) {
1178 if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
1179 charName[0] == 'm' && charName[1] == '\0') {
1184 w1 = ((Gfx8BitFont *)font)->getWidth(code);
1188 w2 = psSubstFonts[i].mWidth;
1193 if (font->getType() == fontType3) {
1194 // This is a hack which makes it possible to substitute for some
1195 // Type 3 fonts. The problem is that it's impossible to know what
1196 // the base coordinate system used in the font is without actually
1197 // rendering the font.
1199 fm = font->getFontMatrix();
1201 ys *= fm[3] / fm[0];
1208 // do 16-bit font substitution
1209 } else if ((fontParam = globalParams->
1210 getPSFont16(font->getName(),
1211 ((GfxCIDFont *)font)->getCollection(),
1212 font->getWMode()))) {
1214 psName = fontParam->psFontName->copy();
1215 if (font16EncLen >= font16EncSize) {
1216 font16EncSize += 16;
1217 font16Enc = (PSFont16Enc *)grealloc(font16Enc,
1218 font16EncSize * sizeof(PSFont16Enc));
1220 font16Enc[font16EncLen].fontID = *font->getID();
1221 font16Enc[font16EncLen].enc = fontParam->encoding->copy();
1222 if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
1226 error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
1227 font16Enc[font16EncLen].enc->getCString());
1230 // give up - can't do anything with this font
1232 error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
1233 font->getName() ? font->getName()->getCString() : "(unnamed)",
1234 ((GfxCIDFont *)font)->getCollection()
1235 ? ((GfxCIDFont *)font)->getCollection()->getCString()
1240 // generate PostScript code to set up the font
1241 if (font->isCIDFont()) {
1242 if (level == psLevel3 || level == psLevel3Sep) {
1243 writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
1244 font->getID()->num, font->getID()->gen, psName->getCString(),
1247 writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
1248 font->getID()->num, font->getID()->gen, psName->getCString(),
1252 writePSFmt("/F%d_%d /%s %g %g\n",
1253 font->getID()->num, font->getID()->gen, psName->getCString(),
1255 for (i = 0; i < 256; i += 8) {
1256 writePSFmt((i == 0) ? "[ " : " ");
1257 for (j = 0; j < 8; ++j) {
1258 if (font->getType() == fontTrueType &&
1260 !((Gfx8BitFont *)font)->getHasEncoding()) {
1261 sprintf(buf, "c%02x", i+j);
1264 charName = ((Gfx8BitFont *)font)->getCharName(i+j);
1265 // this is a kludge for broken PDF files that encode char 32
1267 if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
1272 writePSName(charName ? charName : (char *)".notdef");
1274 writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
1276 writePS("pdfMakeFont\n");
1282 void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
1283 static char hexChar[17] = "0123456789abcdef";
1284 Object refObj, strObj, obj1, obj2, obj3;
1286 int length1, length2, length3;
1292 // check if font is already embedded
1293 for (i = 0; i < fontFileIDLen; ++i) {
1294 if (fontFileIDs[i].num == id->num &&
1295 fontFileIDs[i].gen == id->gen)
1299 // add entry to fontFileIDs list
1300 if (fontFileIDLen >= fontFileIDSize) {
1301 fontFileIDSize += 64;
1302 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1304 fontFileIDs[fontFileIDLen++] = *id;
1306 // get the font stream and info
1307 refObj.initRef(id->num, id->gen);
1308 refObj.fetch(xref, &strObj);
1310 if (!strObj.isStream()) {
1311 error(-1, "Embedded font file object is not a stream");
1314 if (!(dict = strObj.streamGetDict())) {
1315 error(-1, "Embedded font stream is missing its dictionary");
1318 dict->lookup("Length1", &obj1);
1319 dict->lookup("Length2", &obj2);
1320 dict->lookup("Length3", &obj3);
1321 if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
1322 error(-1, "Missing length fields in embedded font stream dictionary");
1328 length1 = obj1.getInt();
1329 length2 = obj2.getInt();
1330 length3 = obj3.getInt();
1335 // beginning comment
1336 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1337 embFontList->append("%%+ font ");
1338 embFontList->append(psName->getCString());
1339 embFontList->append("\n");
1341 // copy ASCII portion of font
1342 strObj.streamReset();
1343 for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
1347 // figure out if encrypted portion is binary or ASCII
1349 for (i = 0; i < 4; ++i) {
1350 start[i] = strObj.streamGetChar();
1351 if (start[i] == EOF) {
1352 error(-1, "Unexpected end of file in embedded font stream");
1355 if (!((start[i] >= '0' && start[i] <= '9') ||
1356 (start[i] >= 'A' && start[i] <= 'F') ||
1357 (start[i] >= 'a' && start[i] <= 'f')))
1361 // convert binary data to ASCII
1363 for (i = 0; i < 4; ++i) {
1364 writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
1365 writePSChar(hexChar[start[i] & 0x0f]);
1367 // if Length2 is incorrect (too small), font data gets chopped, so
1368 // we take a few extra characters from the trailer just in case
1369 length2 += length3 >= 8 ? 8 : length3;
1370 while (i < length2) {
1371 if ((c = strObj.streamGetChar()) == EOF) {
1374 writePSChar(hexChar[(c >> 4) & 0x0f]);
1375 writePSChar(hexChar[c & 0x0f]);
1376 if (++i % 32 == 0) {
1384 // already in ASCII format -- just copy it
1386 for (i = 0; i < 4; ++i) {
1387 writePSChar(start[i]);
1389 for (i = 4; i < length2; ++i) {
1390 if ((c = strObj.streamGetChar()) == EOF) {
1397 // write padding and "cleartomark"
1398 for (i = 0; i < 8; ++i) {
1399 writePS("00000000000000000000000000000000"
1400 "00000000000000000000000000000000\n");
1402 writePS("cleartomark\n");
1405 writePS("%%EndResource\n");
1408 strObj.streamClose();
1412 //~ This doesn't handle .pfb files or binary eexec data (which only
1413 //~ happens in pfb files?).
1414 void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
1419 // check if font is already embedded
1420 for (i = 0; i < fontFileNameLen; ++i) {
1421 if (!fontFileNames[i]->cmp(fileName)) {
1426 // add entry to fontFileNames list
1427 if (fontFileNameLen >= fontFileNameSize) {
1428 fontFileNameSize += 64;
1429 fontFileNames = (GString **)grealloc(fontFileNames,
1430 fontFileNameSize * sizeof(GString *));
1432 fontFileNames[fontFileNameLen++] = fileName->copy();
1434 // beginning comment
1435 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1436 embFontList->append("%%+ font ");
1437 embFontList->append(psName->getCString());
1438 embFontList->append("\n");
1440 // copy the font file
1441 if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
1442 error(-1, "Couldn't open external font file");
1445 while ((c = fgetc(fontFile)) != EOF) {
1451 writePS("%%EndResource\n");
1454 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
1458 Type1CFontFile *t1cFile;
1461 // check if font is already embedded
1462 for (i = 0; i < fontFileIDLen; ++i) {
1463 if (fontFileIDs[i].num == id->num &&
1464 fontFileIDs[i].gen == id->gen)
1468 // add entry to fontFileIDs list
1469 if (fontFileIDLen >= fontFileIDSize) {
1470 fontFileIDSize += 64;
1471 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1473 fontFileIDs[fontFileIDLen++] = *id;
1475 // beginning comment
1476 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1477 embFontList->append("%%+ font ");
1478 embFontList->append(psName->getCString());
1479 embFontList->append("\n");
1481 // convert it to a Type 1 font
1482 fontBuf = font->readEmbFontFile(xref, &fontLen);
1483 t1cFile = new Type1CFontFile(fontBuf, fontLen);
1484 if (t1cFile->isOk()) {
1485 t1cFile->convertToType1(outputFunc, outputStream);
1491 writePS("%%EndResource\n");
1494 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
1499 TrueTypeFontFile *ttFile;
1503 // check if font is already embedded
1504 for (i = 0; i < fontFileIDLen; ++i) {
1505 if (fontFileIDs[i].num == id->num &&
1506 fontFileIDs[i].gen == id->gen) {
1507 sprintf(unique, "_%d", nextTrueTypeNum++);
1508 psName->append(unique);
1513 // add entry to fontFileIDs list
1514 if (i == fontFileIDLen) {
1515 if (fontFileIDLen >= fontFileIDSize) {
1516 fontFileIDSize += 64;
1517 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1519 fontFileIDs[fontFileIDLen++] = *id;
1522 // beginning comment
1523 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1524 embFontList->append("%%+ font ");
1525 embFontList->append(psName->getCString());
1526 embFontList->append("\n");
1528 // convert it to a Type 42 font
1529 fontBuf = font->readEmbFontFile(xref, &fontLen);
1530 ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1531 codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ttFile);
1532 ttFile->convertToType42(psName->getCString(),
1533 ((Gfx8BitFont *)font)->getEncoding(),
1534 ((Gfx8BitFont *)font)->getHasEncoding(),
1536 outputFunc, outputStream);
1542 writePS("%%EndResource\n");
1545 void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) {
1550 TrueTypeFontFile *ttFile;
1554 // check if font is already embedded
1555 fileName = font->getExtFontFile();
1556 for (i = 0; i < fontFileNameLen; ++i) {
1557 if (!fontFileNames[i]->cmp(fileName)) {
1558 sprintf(unique, "_%d", nextTrueTypeNum++);
1559 psName->append(unique);
1564 // add entry to fontFileNames list
1565 if (i == fontFileNameLen) {
1566 if (fontFileNameLen >= fontFileNameSize) {
1567 fontFileNameSize += 64;
1569 (GString **)grealloc(fontFileNames,
1570 fontFileNameSize * sizeof(GString *));
1573 fontFileNames[fontFileNameLen++] = fileName->copy();
1575 // beginning comment
1576 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1577 embFontList->append("%%+ font ");
1578 embFontList->append(psName->getCString());
1579 embFontList->append("\n");
1581 // convert it to a Type 42 font
1582 fontBuf = font->readExtFontFile(&fontLen);
1583 ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1584 codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ttFile);
1585 ttFile->convertToType42(psName->getCString(),
1586 ((Gfx8BitFont *)font)->getEncoding(),
1587 ((Gfx8BitFont *)font)->getHasEncoding(),
1589 outputFunc, outputStream);
1594 writePS("%%EndResource\n");
1597 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
1601 Type1CFontFile *t1cFile;
1604 // check if font is already embedded
1605 for (i = 0; i < fontFileIDLen; ++i) {
1606 if (fontFileIDs[i].num == id->num &&
1607 fontFileIDs[i].gen == id->gen)
1611 // add entry to fontFileIDs list
1612 if (fontFileIDLen >= fontFileIDSize) {
1613 fontFileIDSize += 64;
1614 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1616 fontFileIDs[fontFileIDLen++] = *id;
1618 // beginning comment
1619 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1620 embFontList->append("%%+ font ");
1621 embFontList->append(psName->getCString());
1622 embFontList->append("\n");
1624 // convert it to a Type 0 font
1625 fontBuf = font->readEmbFontFile(xref, &fontLen);
1626 t1cFile = new Type1CFontFile(fontBuf, fontLen);
1627 if (t1cFile->isOk()) {
1628 if (globalParams->getPSLevel() >= psLevel3) {
1629 // Level 3: use a CID font
1630 t1cFile->convertToCIDType0(psName->getCString(),
1631 outputFunc, outputStream);
1633 // otherwise: use a non-CID composite font
1634 t1cFile->convertToType0(psName->getCString(), outputFunc, outputStream);
1641 writePS("%%EndResource\n");
1644 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
1648 TrueTypeFontFile *ttFile;
1651 // check if font is already embedded
1652 for (i = 0; i < fontFileIDLen; ++i) {
1653 if (fontFileIDs[i].num == id->num &&
1654 fontFileIDs[i].gen == id->gen)
1658 // add entry to fontFileIDs list
1659 if (fontFileIDLen >= fontFileIDSize) {
1660 fontFileIDSize += 64;
1661 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1663 fontFileIDs[fontFileIDLen++] = *id;
1665 // beginning comment
1666 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1667 embFontList->append("%%+ font ");
1668 embFontList->append(psName->getCString());
1669 embFontList->append("\n");
1671 // convert it to a Type 0 font
1672 fontBuf = font->readEmbFontFile(xref, &fontLen);
1673 ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1674 if (globalParams->getPSLevel() >= psLevel3) {
1675 ttFile->convertToCIDType2(psName->getCString(),
1676 ((GfxCIDFont *)font)->getCIDToGID(),
1677 ((GfxCIDFont *)font)->getCIDToGIDLen(),
1678 outputFunc, outputStream);
1680 // otherwise: use a non-CID composite font
1681 ttFile->convertToType0(psName->getCString(),
1682 ((GfxCIDFont *)font)->getCIDToGID(),
1683 ((GfxCIDFont *)font)->getCIDToGIDLen(),
1684 outputFunc, outputStream);
1690 writePS("%%EndResource\n");
1693 void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
1694 Dict *parentResDict) {
1704 // set up resources used by font
1705 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
1706 inType3Char = gTrue;
1707 setupResources(resDict);
1708 inType3Char = gFalse;
1710 resDict = parentResDict;
1713 // beginning comment
1714 writePSFmt("%%%%BeginResource: font %s\n", psName->getCString());
1715 embFontList->append("%%+ font ");
1716 embFontList->append(psName->getCString());
1717 embFontList->append("\n");
1720 writePS("7 dict begin\n");
1721 writePS("/FontType 3 def\n");
1722 m = font->getFontMatrix();
1723 writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n",
1724 m[0], m[1], m[2], m[3], m[4], m[5]);
1725 m = font->getFontBBox();
1726 writePSFmt("/FontBBox [%g %g %g %g] def\n",
1727 m[0], m[1], m[2], m[3]);
1728 writePS("/Encoding 256 array def\n");
1729 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
1730 writePS("/BuildGlyph {\n");
1731 writePS(" exch /CharProcs get exch\n");
1732 writePS(" 2 copy known not { pop /.notdef } if\n");
1733 writePS(" get exec\n");
1734 writePS("} bind def\n");
1735 writePS("/BuildChar {\n");
1736 writePS(" 1 index /Encoding get exch get\n");
1737 writePS(" 1 index /BuildGlyph get exec\n");
1738 writePS("} bind def\n");
1739 if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
1740 writePSFmt("/CharProcs %d dict def\n", charProcs->getLength());
1741 writePS("CharProcs begin\n");
1746 gfx = new Gfx(xref, this, resDict, &box, gFalse, NULL);
1747 inType3Char = gTrue;
1748 t3Cacheable = gFalse;
1749 for (i = 0; i < charProcs->getLength(); ++i) {
1751 writePSName(charProcs->getKey(i));
1753 gfx->display(charProcs->getVal(i, &charProc));
1757 sprintf(buf, "%g %g %g %g %g %g setcachedevice\n",
1758 t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
1760 sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY);
1762 (*outputFunc)(outputStream, buf, strlen(buf));
1763 (*outputFunc)(outputStream, t3String->getCString(),
1764 t3String->getLength());
1768 (*outputFunc)(outputStream, "Q\n", 2);
1771 inType3Char = gFalse;
1775 writePS("currentdict end\n");
1776 writePSFmt("/%s exch definefont pop\n", psName->getCString());
1779 writePS("%%EndResource\n");
1782 void PSOutputDev::setupImages(Dict *resDict) {
1783 Object xObjDict, xObj, xObjRef, subtypeObj;
1786 if (!(mode == psModeForm || inType3Char)) {
1790 resDict->lookup("XObject", &xObjDict);
1791 if (xObjDict.isDict()) {
1792 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
1793 xObjDict.dictGetValNF(i, &xObjRef);
1794 xObjDict.dictGetVal(i, &xObj);
1795 if (xObj.isStream()) {
1796 xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
1797 if (subtypeObj.isName("Image")) {
1798 if (xObjRef.isRef()) {
1799 setupImage(xObjRef.getRef(), xObj.getStream());
1801 error(-1, "Image in resource dict is not an indirect reference");
1813 void PSOutputDev::setupImage(Ref id, Stream *str) {
1815 int size, line, col, i;
1817 // construct an encoder stream
1818 if (globalParams->getPSASCIIHex()) {
1819 str = new ASCIIHexEncoder(str);
1821 str = new ASCII85Encoder(str);
1824 // compute image data size
1830 } while (c == '\n' || c == '\r');
1831 if (c == '~' || c == EOF) {
1838 for (i = 1; i <= 4; ++i) {
1841 } while (c == '\n' || c == '\r');
1842 if (c == '~' || c == EOF) {
1852 } while (c != '~' && c != EOF);
1854 writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
1857 // write the data into the array
1860 writePS("dup 0 <~");
1864 } while (c == '\n' || c == '\r');
1865 if (c == '~' || c == EOF) {
1874 for (i = 1; i <= 4; ++i) {
1877 } while (c == '\n' || c == '\r');
1878 if (c == '~' || c == EOF) {
1885 // each line is: "dup nnnnn <~...data...~> put<eol>"
1886 // so max data length = 255 - 20 = 235
1887 // chunks are 1 or 4 bytes each, so we have to stop at 232
1888 // but make it 225 just to be safe
1890 writePS("~> put\n");
1892 writePSFmt("dup %d <~", line);
1895 } while (c != '~' && c != EOF);
1896 writePS("~> put\n");
1903 void PSOutputDev::startPage(int pageNum, GfxState *state) {
1904 int x1, y1, x2, y2, width, height, paperWidth2, paperHeight2;
1910 writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage);
1911 writePS("%%BeginPageSetup\n");
1913 // rotate, translate, and scale page
1914 x1 = (int)(state->getX1() + 0.5);
1915 y1 = (int)(state->getY1() + 0.5);
1916 x2 = (int)(state->getX2() + 0.5);
1917 y2 = (int)(state->getY2() + 0.5);
1920 if (width > height && width > paperWidth) {
1922 writePSFmt("%%%%PageOrientation: %s\n",
1923 state->getCTM()[0] ? "Landscape" : "Portrait");
1924 writePS("pdfStartPage\n");
1925 writePS("90 rotate\n");
1927 ty = -(y1 + paperWidth);
1928 paperWidth2 = paperHeight;
1929 paperHeight2 = paperWidth;
1932 writePSFmt("%%%%PageOrientation: %s\n",
1933 state->getCTM()[0] ? "Portrait" : "Landscape");
1934 writePS("pdfStartPage\n");
1937 paperWidth2 = paperWidth;
1938 paperHeight2 = paperHeight;
1940 if (width < paperWidth2) {
1941 tx += (paperWidth2 - width) / 2;
1943 if (height < paperHeight2) {
1944 ty += (paperHeight2 - height) / 2;
1946 if (tx != 0 || ty != 0) {
1947 writePSFmt("%g %g translate\n", tx, ty);
1949 if (width > paperWidth2 || height > paperHeight2) {
1950 xScale = (double)paperWidth2 / (double)width;
1951 yScale = (double)paperHeight2 / (double)height;
1952 if (yScale < xScale) {
1957 writePSFmt("%0.4f %0.4f scale\n", xScale, xScale);
1959 xScale = yScale = 1;
1962 writePS("%%EndPageSetup\n");
1967 writePS("pdfStartPage\n");
1969 xScale = yScale = 1;
1974 writePS("/PaintProc {\n");
1975 writePS("begin xpdf begin\n");
1976 writePS("pdfStartPage\n");
1978 xScale = yScale = 1;
1984 (*underlayCbk)(this, underlayCbkData);
1988 void PSOutputDev::endPage() {
1990 (*overlayCbk)(this, overlayCbkData);
1994 if (mode == psModeForm) {
1995 writePS("pdfEndPage\n");
1996 writePS("end end\n");
1998 writePS("end end\n");
2001 writePS("showpage\n");
2002 writePS("%%PageTrailer\n");
2008 void PSOutputDev::saveState(GfxState *state) {
2013 void PSOutputDev::restoreState(GfxState *state) {
2018 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
2019 double m21, double m22, double m31, double m32) {
2020 writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
2023 void PSOutputDev::updateLineDash(GfxState *state) {
2028 state->getLineDash(&dash, &length, &start);
2030 for (i = 0; i < length; ++i)
2031 writePSFmt("%g%s", dash[i], (i == length-1) ? "" : " ");
2032 writePSFmt("] %g d\n", start);
2035 void PSOutputDev::updateFlatness(GfxState *state) {
2036 writePSFmt("%d i\n", state->getFlatness());
2039 void PSOutputDev::updateLineJoin(GfxState *state) {
2040 writePSFmt("%d j\n", state->getLineJoin());
2043 void PSOutputDev::updateLineCap(GfxState *state) {
2044 writePSFmt("%d J\n", state->getLineCap());
2047 void PSOutputDev::updateMiterLimit(GfxState *state) {
2048 writePSFmt("%g M\n", state->getMiterLimit());
2051 void PSOutputDev::updateLineWidth(GfxState *state) {
2052 writePSFmt("%g w\n", state->getLineWidth());
2055 void PSOutputDev::updateFillColor(GfxState *state) {
2060 GfxSeparationColorSpace *sepCS;
2064 state->getFillGray(&gray);
2065 writePSFmt("%g g\n", gray);
2068 state->getFillCMYK(&cmyk);
2069 writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2070 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2074 if (state->getFillColorSpace()->getMode() == csDeviceCMYK) {
2075 state->getFillCMYK(&cmyk);
2076 writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2078 state->getFillRGB(&rgb);
2079 if (rgb.r == rgb.g && rgb.g == rgb.b) {
2080 writePSFmt("%g g\n", rgb.r);
2082 writePSFmt("%g %g %g rg\n", rgb.r, rgb.g, rgb.b);
2088 if (state->getFillColorSpace()->getMode() == csSeparation) {
2089 sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
2091 sepCS->getCMYK(&color, &cmyk);
2092 writePSFmt("%g %g %g %g %g (%s) ck\n",
2093 state->getFillColor()->c[0],
2094 cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2095 sepCS->getName()->getCString());
2096 addCustomColor(sepCS);
2098 state->getFillCMYK(&cmyk);
2099 writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2100 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2104 t3Cacheable = gFalse;
2107 void PSOutputDev::updateStrokeColor(GfxState *state) {
2112 GfxSeparationColorSpace *sepCS;
2116 state->getStrokeGray(&gray);
2117 writePSFmt("%g G\n", gray);
2120 state->getStrokeCMYK(&cmyk);
2121 writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2122 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2126 if (state->getStrokeColorSpace()->getMode() == csDeviceCMYK) {
2127 state->getStrokeCMYK(&cmyk);
2128 writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2130 state->getStrokeRGB(&rgb);
2131 if (rgb.r == rgb.g && rgb.g == rgb.b) {
2132 writePSFmt("%g G\n", rgb.r);
2134 writePSFmt("%g %g %g RG\n", rgb.r, rgb.g, rgb.b);
2140 if (state->getStrokeColorSpace()->getMode() == csSeparation) {
2141 sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
2143 sepCS->getCMYK(&color, &cmyk);
2144 writePSFmt("%g %g %g %g %g (%s) CK\n",
2145 state->getStrokeColor()->c[0],
2146 cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2147 sepCS->getName()->getCString());
2148 addCustomColor(sepCS);
2150 state->getStrokeCMYK(&cmyk);
2151 writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2152 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2156 t3Cacheable = gFalse;
2159 void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
2161 processColors |= psProcessCyan;
2164 processColors |= psProcessMagenta;
2167 processColors |= psProcessYellow;
2170 processColors |= psProcessBlack;
2174 void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
2175 PSOutCustomColor *cc;
2179 for (cc = customColors; cc; cc = cc->next) {
2180 if (!cc->name->cmp(sepCS->getName())) {
2185 sepCS->getCMYK(&color, &cmyk);
2186 cc = new PSOutCustomColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2187 sepCS->getName()->copy());
2188 cc->next = customColors;
2192 void PSOutputDev::updateFont(GfxState *state) {
2193 if (state->getFont()) {
2194 writePSFmt("/F%d_%d %g Tf\n",
2195 state->getFont()->getID()->num, state->getFont()->getID()->gen,
2196 state->getFontSize());
2200 void PSOutputDev::updateTextMat(GfxState *state) {
2203 mat = state->getTextMat();
2204 writePSFmt("[%g %g %g %g %g %g] Tm\n",
2205 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
2208 void PSOutputDev::updateCharSpace(GfxState *state) {
2209 writePSFmt("%g Tc\n", state->getCharSpace());
2212 void PSOutputDev::updateRender(GfxState *state) {
2215 rm = state->getRender();
2216 writePSFmt("%d Tr\n", rm);
2218 if (rm != 0 && rm != 3) {
2219 t3Cacheable = gFalse;
2223 void PSOutputDev::updateRise(GfxState *state) {
2224 writePSFmt("%g Ts\n", state->getRise());
2227 void PSOutputDev::updateWordSpace(GfxState *state) {
2228 writePSFmt("%g Tw\n", state->getWordSpace());
2231 void PSOutputDev::updateHorizScaling(GfxState *state) {
2232 writePSFmt("%g Tz\n", state->getHorizScaling());
2235 void PSOutputDev::updateTextPos(GfxState *state) {
2236 writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
2239 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
2240 if (state->getFont()->getWMode()) {
2241 writePSFmt("%g TJmV\n", shift);
2243 writePSFmt("%g TJm\n", shift);
2247 void PSOutputDev::stroke(GfxState *state) {
2248 doPath(state->getPath());
2250 // if we're construct a cacheable Type 3 glyph, we need to do
2251 // everything in the fill color
2258 void PSOutputDev::fill(GfxState *state) {
2259 doPath(state->getPath());
2263 void PSOutputDev::eoFill(GfxState *state) {
2264 doPath(state->getPath());
2268 void PSOutputDev::clip(GfxState *state) {
2269 doPath(state->getPath());
2273 void PSOutputDev::eoClip(GfxState *state) {
2274 doPath(state->getPath());
2278 void PSOutputDev::doPath(GfxPath *path) {
2279 GfxSubpath *subpath;
2280 double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
2283 n = path->getNumSubpaths();
2285 if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
2286 subpath = path->getSubpath(0);
2287 x0 = subpath->getX(0);
2288 y0 = subpath->getY(0);
2289 x4 = subpath->getX(4);
2290 y4 = subpath->getY(4);
2291 if (x4 == x0 && y4 == y0) {
2292 x1 = subpath->getX(1);
2293 y1 = subpath->getY(1);
2294 x2 = subpath->getX(2);
2295 y2 = subpath->getY(2);
2296 x3 = subpath->getX(3);
2297 y3 = subpath->getY(3);
2298 if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
2299 writePSFmt("%g %g %g %g re\n",
2300 x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
2301 fabs(x2 - x0), fabs(y1 - y0));
2303 } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
2304 writePSFmt("%g %g %g %g re\n",
2305 x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
2306 fabs(x1 - x0), fabs(y2 - y0));
2312 for (i = 0; i < n; ++i) {
2313 subpath = path->getSubpath(i);
2314 m = subpath->getNumPoints();
2315 writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0));
2318 if (subpath->getCurve(j)) {
2319 writePSFmt("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
2320 subpath->getX(j+1), subpath->getY(j+1),
2321 subpath->getX(j+2), subpath->getY(j+2));
2324 writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j));
2328 if (subpath->isClosed()) {
2334 void PSOutputDev::drawString(GfxState *state, GString *s) {
2338 double dx, dy, dx2, dy2, originX, originY;
2344 int len, nChars, uLen, n, m, i, j;
2346 // check for invisible text -- this is used by Acrobat Capture
2347 if (state->getRender() == 3) {
2351 // ignore empty strings
2352 if (s->getLength() == 0) {
2357 if (!(font = state->getFont())) {
2360 wMode = font->getWMode();
2362 // check for a subtitute 16-bit font
2364 if (font->isCIDFont()) {
2365 for (i = 0; i < font16EncLen; ++i) {
2366 if (font->getID()->num == font16Enc[i].fontID.num &&
2367 font->getID()->gen == font16Enc[i].fontID.gen) {
2368 uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
2374 // compute width of chars in string, ignoring char spacing and word
2375 // spacing -- the Tj operator will adjust for the metrics of the
2376 // font that's actually used
2379 p = s->getCString();
2380 len = s->getLength();
2381 if (font->isCIDFont()) {
2387 n = font->getNextChar(p, len, &code,
2388 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2389 &dx2, &dy2, &originX, &originY);
2390 if (font->isCIDFont()) {
2392 for (i = 0; i < uLen; ++i) {
2393 m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
2394 for (j = 0; j < m; ++j) {
2398 //~ this really needs to get the number of chars in the target
2399 //~ encoding - which may be more than the number of Unicode
2403 s2->append((char)((code >> 8) & 0xff));
2404 s2->append((char)(code & 0xff));
2413 dx *= state->getFontSize() * state->getHorizScaling();
2414 dy *= state->getFontSize();
2419 if (s2->getLength() > 0) {
2421 if (font->isCIDFont()) {
2423 writePSFmt(" %d %g Tj16V\n", nChars, dy);
2425 writePSFmt(" %d %g Tj16\n", nChars, dx);
2428 writePSFmt(" %g Tj\n", dx);
2431 if (font->isCIDFont()) {
2435 if (state->getRender() & 4) {
2436 haveTextClip = gTrue;
2440 void PSOutputDev::endTextObject(GfxState *state) {
2443 haveTextClip = gFalse;
2447 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2448 int width, int height, GBool invert,
2452 len = height * ((width + 7) / 8);
2453 if (level == psLevel1 || level == psLevel1Sep) {
2454 doImageL1(NULL, invert, inlineImg, str, width, height, len);
2456 doImageL2(ref, NULL, invert, inlineImg, str, width, height, len);
2460 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2461 int width, int height, GfxImageColorMap *colorMap,
2462 int *maskColors, GBool inlineImg) {
2465 len = height * ((width * colorMap->getNumPixelComps() *
2466 colorMap->getBits() + 7) / 8);
2469 doImageL1(colorMap, gFalse, inlineImg, str, width, height, len);
2472 //~ handle indexed, separation, ... color spaces
2473 doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
2479 doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len);
2482 t3Cacheable = gFalse;
2485 void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
2486 GBool invert, GBool inlineImg,
2487 Stream *str, int width, int height, int len) {
2488 ImageStream *imgStr;
2489 Guchar pixBuf[gfxColorMaxComps];
2493 // width, height, matrix, bits per component
2495 writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
2497 width, -height, height);
2499 writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
2500 width, height, invert ? "true" : "false",
2501 width, -height, height);
2507 // set up to process the data stream
2508 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
2509 colorMap->getBits());
2512 // process the data stream
2514 for (y = 0; y < height; ++y) {
2517 for (x = 0; x < width; ++x) {
2518 imgStr->getPixel(pixBuf);
2519 colorMap->getGray(pixBuf, &gray);
2520 writePSFmt("%02x", (int)(gray * 255 + 0.5));
2536 for (y = 0; y < height; ++y) {
2537 for (x = 0; x < width; x += 8) {
2538 writePSFmt("%02x", str->getChar() & 0xff);
2552 void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
2553 GBool invert, GBool inlineImg,
2554 Stream *str, int width, int height, int len) {
2555 ImageStream *imgStr;
2557 Guchar pixBuf[gfxColorMaxComps];
2561 // width, height, matrix, bits per component
2562 writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n",
2564 width, -height, height);
2566 // allocate a line buffer
2567 lineBuf = (Guchar *)gmalloc(4 * width);
2569 // set up to process the data stream
2570 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
2571 colorMap->getBits());
2574 // process the data stream
2576 for (y = 0; y < height; ++y) {
2579 for (x = 0; x < width; ++x) {
2580 imgStr->getPixel(pixBuf);
2581 colorMap->getCMYK(pixBuf, &cmyk);
2582 lineBuf[4*x+0] = (int)(255 * cmyk.c + 0.5);
2583 lineBuf[4*x+1] = (int)(255 * cmyk.m + 0.5);
2584 lineBuf[4*x+2] = (int)(255 * cmyk.y + 0.5);
2585 lineBuf[4*x+3] = (int)(255 * cmyk.k + 0.5);
2586 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2589 // write one line of each color component
2590 for (comp = 0; comp < 4; ++comp) {
2591 for (x = 0; x < width; ++x) {
2592 writePSFmt("%02x", lineBuf[4*x + comp]);
2609 void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
2610 GBool invert, GBool inlineImg,
2611 Stream *str, int width, int height, int len) {
2614 GBool useRLE, useASCII, useCompressed;
2615 GfxSeparationColorSpace *sepCS;
2623 dumpColorSpaceL2(colorMap->getColorSpace());
2624 writePS(" setcolorspace\n");
2627 // set up the image data
2628 if (mode == psModeForm || inType3Char) {
2631 str = new FixedLengthEncoder(str, len);
2632 if (globalParams->getPSASCIIHex()) {
2633 str = new ASCIIHexEncoder(str);
2635 str = new ASCII85Encoder(str);
2643 } while (c == '\n' || c == '\r');
2644 if (c == '~' || c == EOF) {
2653 for (i = 1; i <= 4; ++i) {
2656 } while (c == '\n' || c == '\r');
2657 if (c == '~' || c == EOF) {
2664 // each line is: "dup nnnnn <~...data...~> put<eol>"
2665 // so max data length = 255 - 20 = 235
2666 // chunks are 1 or 4 bytes each, so we have to stop at 232
2667 // but make it 225 just to be safe
2671 writePSFmt("<~", line);
2674 } while (c != '~' && c != EOF);
2680 // set up to use the array already created by setupImages()
2681 writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
2686 writePS("<<\n /ImageType 1\n");
2688 // width, height, matrix, bits per component
2689 writePSFmt(" /Width %d\n", width);
2690 writePSFmt(" /Height %d\n", height);
2691 writePSFmt(" /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
2692 writePSFmt(" /BitsPerComponent %d\n",
2693 colorMap ? colorMap->getBits() : 1);
2697 writePS(" /Decode [");
2698 if (colorMap->getColorSpace()->getMode() == csSeparation) {
2699 //~ this is a kludge -- see comment in dumpColorSpaceL2
2700 n = (1 << colorMap->getBits()) - 1;
2701 writePSFmt("%g %g", colorMap->getDecodeLow(0) * n,
2702 colorMap->getDecodeHigh(0) * n);
2704 numComps = colorMap->getNumPixelComps();
2705 for (i = 0; i < numComps; ++i) {
2709 writePSFmt("%g %g", colorMap->getDecodeLow(i),
2710 colorMap->getDecodeHigh(i));
2715 writePSFmt(" /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
2718 if (mode == psModeForm || inType3Char) {
2721 writePS(" /DataSource { 2 copy get exch 1 add exch }\n");
2723 // end of image dictionary
2724 writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask");
2726 // get rid of the array and index
2727 writePS("pop pop\n");
2732 writePS(" /DataSource currentfile\n");
2733 s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
2735 if (inlineImg || !s) {
2738 useCompressed = gFalse;
2741 useASCII = str->isBinary();
2742 useCompressed = gTrue;
2745 writePSFmt(" /ASCII%sDecode filter\n",
2746 globalParams->getPSASCIIHex() ? "Hex" : "85");
2749 writePS(" /RunLengthDecode filter\n");
2751 if (useCompressed) {
2752 writePS(s->getCString());
2758 // cut off inline image streams at appropriate length
2760 str = new FixedLengthEncoder(str, len);
2761 } else if (useCompressed) {
2762 str = str->getBaseStream();
2765 // add RunLengthEncode and ASCIIHex/85 encode filters
2767 str = new RunLengthEncoder(str);
2770 if (globalParams->getPSASCIIHex()) {
2771 str = new ASCIIHexEncoder(str);
2773 str = new ASCII85Encoder(str);
2777 // end of image dictionary
2782 // this can't happen -- OPI dictionaries are in XObjects
2783 error(-1, "Internal: OPI in inline image");
2786 // need to read the stream to count characters -- the length
2787 // is data-dependent (because of ASCII and RLE filters)
2790 while ((c = str->getChar()) != EOF) {
2795 // +6/7 for "pdfIm\n" / "pdfImM\n"
2796 // +8 for newline + trailer
2797 n += colorMap ? 14 : 15;
2798 writePSFmt("%%%%BeginData: %d Hex Bytes\n", n);
2801 if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
2802 colorMap->getColorSpace()->getMode() == csSeparation) {
2804 sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
2805 sepCS->getCMYK(&color, &cmyk);
2806 writePSFmt("%g %g %g %g (%s) pdfImSep\n",
2807 cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2808 sepCS->getName()->getCString());
2810 writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM");
2813 // copy the stream data
2815 while ((c = str->getChar()) != EOF) {
2820 // add newline and trailer to the end
2822 writePS("%-EOD-\n");
2825 writePS("%%EndData\n");
2830 if (useRLE || useASCII || inlineImg) {
2836 void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
2837 GfxCalGrayColorSpace *calGrayCS;
2838 GfxCalRGBColorSpace *calRGBCS;
2839 GfxLabColorSpace *labCS;
2840 GfxIndexedColorSpace *indexedCS;
2841 GfxSeparationColorSpace *separationCS;
2842 GfxColorSpace *baseCS;
2844 double x[gfxColorMaxComps], y[gfxColorMaxComps];
2848 int n, numComps, numAltComps;
2852 switch (colorSpace->getMode()) {
2855 writePS("/DeviceGray");
2856 processColors |= psProcessBlack;
2860 calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
2861 writePS("[/CIEBasedA <<\n");
2862 writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
2863 writePSFmt(" /MatrixA [%g %g %g]\n",
2864 calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
2865 calGrayCS->getWhiteZ());
2866 writePSFmt(" /WhitePoint [%g %g %g]\n",
2867 calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
2868 calGrayCS->getWhiteZ());
2869 writePSFmt(" /BlackPoint [%g %g %g]\n",
2870 calGrayCS->getBlackX(), calGrayCS->getBlackY(),
2871 calGrayCS->getBlackZ());
2873 processColors |= psProcessBlack;
2877 writePS("/DeviceRGB");
2878 processColors |= psProcessCMYK;
2882 calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
2883 writePS("[/CIEBasedABC <<\n");
2884 writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
2885 calRGBCS->getGammaR(), calRGBCS->getGammaG(),
2886 calRGBCS->getGammaB());
2887 writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n",
2888 calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
2889 calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
2890 calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
2891 calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
2892 calRGBCS->getMatrix()[8]);
2893 writePSFmt(" /WhitePoint [%g %g %g]\n",
2894 calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
2895 calRGBCS->getWhiteZ());
2896 writePSFmt(" /BlackPoint [%g %g %g]\n",
2897 calRGBCS->getBlackX(), calRGBCS->getBlackY(),
2898 calRGBCS->getBlackZ());
2900 processColors |= psProcessCMYK;
2904 writePS("/DeviceCMYK");
2905 processColors |= psProcessCMYK;
2909 labCS = (GfxLabColorSpace *)colorSpace;
2910 writePS("[/CIEBasedABC <<\n");
2911 writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n",
2912 labCS->getAMin(), labCS->getAMax(),
2913 labCS->getBMin(), labCS->getBMax());
2914 writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
2915 writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
2916 writePS(" /DecodeLMN\n");
2917 writePS(" [{dup 6 29 div ge {dup dup mul mul}\n");
2918 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
2919 labCS->getWhiteX());
2920 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
2921 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
2922 labCS->getWhiteY());
2923 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
2924 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
2925 labCS->getWhiteZ());
2926 writePSFmt(" /WhitePoint [%g %g %g]\n",
2927 labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
2928 writePSFmt(" /BlackPoint [%g %g %g]\n",
2929 labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
2931 processColors |= psProcessCMYK;
2935 dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt());
2939 indexedCS = (GfxIndexedColorSpace *)colorSpace;
2940 baseCS = indexedCS->getBase();
2941 writePS("[/Indexed ");
2942 dumpColorSpaceL2(baseCS);
2943 n = indexedCS->getIndexHigh();
2944 numComps = baseCS->getNComps();
2945 lookup = indexedCS->getLookup();
2946 writePSFmt(" %d <\n", n);
2947 if (baseCS->getMode() == csDeviceN) {
2948 func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
2949 numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
2951 for (i = 0; i <= n; i += 8) {
2953 for (j = i; j < i+8 && j <= n; ++j) {
2954 for (k = 0; k < numComps; ++k) {
2955 x[k] = *p++ / 255.0;
2957 func->transform(x, y);
2958 for (k = 0; k < numAltComps; ++k) {
2959 byte = (int)(y[k] * 255 + 0.5);
2962 } else if (byte > 255) {
2965 writePSFmt("%02x", byte);
2968 indexedCS->getCMYK(&color, &cmyk);
2969 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2974 for (i = 0; i <= n; i += 8) {
2976 for (j = i; j < i+8 && j <= n; ++j) {
2977 for (k = 0; k < numComps; ++k) {
2978 writePSFmt("%02x", lookup[j * numComps + k]);
2981 indexedCS->getCMYK(&color, &cmyk);
2982 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2991 //~ this is a kludge -- the correct thing would to ouput a
2992 //~ separation color space, with the specified alternate color
2993 //~ space and tint transform
2994 separationCS = (GfxSeparationColorSpace *)colorSpace;
2995 writePS("[/Indexed ");
2996 dumpColorSpaceL2(separationCS->getAlt());
2997 writePS(" 255 <\n");
2998 numComps = separationCS->getAlt()->getNComps();
2999 for (i = 0; i <= 255; i += 8) {
3001 for (j = i; j < i+8 && j <= 255; ++j) {
3002 x[0] = (double)j / 255.0;
3003 separationCS->getFunc()->transform(x, y);
3004 for (k = 0; k < numComps; ++k) {
3005 writePSFmt("%02x", (int)(255 * y[k] + 0.5));
3011 #if 0 //~ this shouldn't be here since the PS file doesn't actually refer
3012 //~ to this colorant (it's converted to CMYK instead)
3013 addCustomColor(separationCS);
3018 // DeviceN color spaces are a Level 3 PostScript feature.
3019 dumpColorSpaceL2(((GfxDeviceNColorSpace *)colorSpace)->getAlt());
3030 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
3033 if (globalParams->getPSOPI()) {
3034 opiDict->lookup("2.0", &dict);
3035 if (dict.isDict()) {
3036 opiBegin20(state, dict.getDict());
3040 opiDict->lookup("1.3", &dict);
3041 if (dict.isDict()) {
3042 opiBegin13(state, dict.getDict());
3049 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
3050 Object obj1, obj2, obj3, obj4;
3051 double width, height, left, right, top, bottom;
3055 writePS("%%BeginOPI: 2.0\n");
3056 writePS("%%Distilled\n");
3058 dict->lookup("F", &obj1);
3059 if (getFileSpec(&obj1, &obj2)) {
3060 writePSFmt("%%%%ImageFileName: %s\n",
3061 obj2.getString()->getCString());
3066 dict->lookup("MainImage", &obj1);
3067 if (obj1.isString()) {
3068 writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString());
3072 //~ ignoring 'Tags' entry
3073 //~ need to use writePSString() and deal with >255-char lines
3075 dict->lookup("Size", &obj1);
3076 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3077 obj1.arrayGet(0, &obj2);
3078 width = obj2.getNum();
3080 obj1.arrayGet(1, &obj2);
3081 height = obj2.getNum();
3083 writePSFmt("%%%%ImageDimensions: %g %g\n", width, height);
3087 dict->lookup("CropRect", &obj1);
3088 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
3089 obj1.arrayGet(0, &obj2);
3090 left = obj2.getNum();
3092 obj1.arrayGet(1, &obj2);
3093 top = obj2.getNum();
3095 obj1.arrayGet(2, &obj2);
3096 right = obj2.getNum();
3098 obj1.arrayGet(3, &obj2);
3099 bottom = obj2.getNum();
3101 writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
3105 dict->lookup("Overprint", &obj1);
3106 if (obj1.isBool()) {
3107 writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
3111 dict->lookup("Inks", &obj1);
3112 if (obj1.isName()) {
3113 writePSFmt("%%%%ImageInks: %s\n", obj1.getName());
3114 } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
3115 obj1.arrayGet(0, &obj2);
3116 if (obj2.isName()) {
3117 writePSFmt("%%%%ImageInks: %s %d",
3118 obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
3119 for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
3120 obj1.arrayGet(i, &obj3);
3121 obj1.arrayGet(i+1, &obj4);
3122 if (obj3.isString() && obj4.isNum()) {
3124 writePSString(obj3.getString());
3125 writePSFmt(" %g", obj4.getNum());
3138 writePS("%%BeginIncludedImage\n");
3140 dict->lookup("IncludedImageDimensions", &obj1);
3141 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3142 obj1.arrayGet(0, &obj2);
3145 obj1.arrayGet(1, &obj2);
3148 writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h);
3152 dict->lookup("IncludedImageQuality", &obj1);
3154 writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum());
3161 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
3163 int left, right, top, bottom, samples, bits, width, height;
3165 double llx, lly, ulx, uly, urx, ury, lrx, lry;
3166 double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
3171 writePS("/opiMatrix2 matrix currentmatrix def\n");
3172 writePS("opiMatrix setmatrix\n");
3174 dict->lookup("F", &obj1);
3175 if (getFileSpec(&obj1, &obj2)) {
3176 writePSFmt("%%ALDImageFileName: %s\n",
3177 obj2.getString()->getCString());
3182 dict->lookup("CropRect", &obj1);
3183 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
3184 obj1.arrayGet(0, &obj2);
3185 left = obj2.getInt();
3187 obj1.arrayGet(1, &obj2);
3188 top = obj2.getInt();
3190 obj1.arrayGet(2, &obj2);
3191 right = obj2.getInt();
3193 obj1.arrayGet(3, &obj2);
3194 bottom = obj2.getInt();
3196 writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
3200 dict->lookup("Color", &obj1);
3201 if (obj1.isArray() && obj1.arrayGetLength() == 5) {
3202 obj1.arrayGet(0, &obj2);
3205 obj1.arrayGet(1, &obj2);
3208 obj1.arrayGet(2, &obj2);
3211 obj1.arrayGet(3, &obj2);
3214 obj1.arrayGet(4, &obj2);
3215 if (obj2.isString()) {
3216 writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
3217 writePSString(obj2.getString());
3224 dict->lookup("ColorType", &obj1);
3225 if (obj1.isName()) {
3226 writePSFmt("%%ALDImageColorType: %s\n", obj1.getName());
3230 //~ ignores 'Comments' entry
3231 //~ need to handle multiple lines
3233 dict->lookup("CropFixed", &obj1);
3234 if (obj1.isArray()) {
3235 obj1.arrayGet(0, &obj2);
3236 ulx = obj2.getNum();
3238 obj1.arrayGet(1, &obj2);
3239 uly = obj2.getNum();
3241 obj1.arrayGet(2, &obj2);
3242 lrx = obj2.getNum();
3244 obj1.arrayGet(3, &obj2);
3245 lry = obj2.getNum();
3247 writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
3251 dict->lookup("GrayMap", &obj1);
3252 if (obj1.isArray()) {
3253 writePS("%ALDImageGrayMap:");
3254 for (i = 0; i < obj1.arrayGetLength(); i += 16) {
3258 for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
3259 obj1.arrayGet(i+j, &obj2);
3260 writePSFmt(" %d", obj2.getInt());
3268 dict->lookup("ID", &obj1);
3269 if (obj1.isString()) {
3270 writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString());
3274 dict->lookup("ImageType", &obj1);
3275 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3276 obj1.arrayGet(0, &obj2);
3277 samples = obj2.getInt();
3279 obj1.arrayGet(1, &obj2);
3280 bits = obj2.getInt();
3282 writePSFmt("%%ALDImageType: %d %d\n", samples, bits);
3286 dict->lookup("Overprint", &obj1);
3287 if (obj1.isBool()) {
3288 writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
3292 dict->lookup("Position", &obj1);
3293 if (obj1.isArray() && obj1.arrayGetLength() == 8) {
3294 obj1.arrayGet(0, &obj2);
3295 llx = obj2.getNum();
3297 obj1.arrayGet(1, &obj2);
3298 lly = obj2.getNum();
3300 obj1.arrayGet(2, &obj2);
3301 ulx = obj2.getNum();
3303 obj1.arrayGet(3, &obj2);
3304 uly = obj2.getNum();
3306 obj1.arrayGet(4, &obj2);
3307 urx = obj2.getNum();
3309 obj1.arrayGet(5, &obj2);
3310 ury = obj2.getNum();
3312 obj1.arrayGet(6, &obj2);
3313 lrx = obj2.getNum();
3315 obj1.arrayGet(7, &obj2);
3316 lry = obj2.getNum();
3318 opiTransform(state, llx, lly, &tllx, &tlly);
3319 opiTransform(state, ulx, uly, &tulx, &tuly);
3320 opiTransform(state, urx, ury, &turx, &tury);
3321 opiTransform(state, lrx, lry, &tlrx, &tlry);
3322 writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
3323 tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
3328 dict->lookup("Resolution", &obj1);
3329 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3330 obj1.arrayGet(0, &obj2);
3331 horiz = obj2.getNum();
3333 obj1.arrayGet(1, &obj2);
3334 vert = obj2.getNum();
3336 writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert);
3341 dict->lookup("Size", &obj1);
3342 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3343 obj1.arrayGet(0, &obj2);
3344 width = obj2.getInt();
3346 obj1.arrayGet(1, &obj2);
3347 height = obj2.getInt();
3349 writePSFmt("%%ALDImageDimensions: %d %d\n", width, height);
3353 //~ ignoring 'Tags' entry
3354 //~ need to use writePSString() and deal with >255-char lines
3356 dict->lookup("Tint", &obj1);
3358 writePSFmt("%%ALDImageTint: %g\n", obj1.getNum());
3362 dict->lookup("Transparency", &obj1);
3363 if (obj1.isBool()) {
3364 writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
3368 writePS("%%BeginObject: image\n");
3369 writePS("opiMatrix2 setmatrix\n");
3373 // Convert PDF user space coordinates to PostScript default user space
3374 // coordinates. This has to account for both the PDF CTM and the
3375 // PSOutputDev page-fitting transform.
3376 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
3377 double *x1, double *y1) {
3380 state->transform(x0, y0, x1, y1);
3392 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
3395 if (globalParams->getPSOPI()) {
3396 opiDict->lookup("2.0", &dict);
3397 if (dict.isDict()) {
3398 writePS("%%EndIncludedImage\n");
3399 writePS("%%EndOPI\n");
3400 writePS("grestore\n");
3405 opiDict->lookup("1.3", &dict);
3406 if (dict.isDict()) {
3407 writePS("%%EndObject\n");
3408 writePS("restore\n");
3416 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
3417 if (fileSpec->isString()) {
3418 fileSpec->copy(fileName);
3421 if (fileSpec->isDict()) {
3422 fileSpec->dictLookup("DOS", fileName);
3423 if (fileName->isString()) {
3427 fileSpec->dictLookup("Mac", fileName);
3428 if (fileName->isString()) {
3432 fileSpec->dictLookup("Unix", fileName);
3433 if (fileName->isString()) {
3437 fileSpec->dictLookup("F", fileName);
3438 if (fileName->isString()) {
3445 #endif // OPI_SUPPORT
3447 void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
3448 writePSFmt("%g %g setcharwidth\n", wx, wy);
3452 void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
3453 double llx, double lly, double urx, double ury) {
3460 t3String = new GString();
3462 t3Cacheable = gTrue;
3465 void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
3469 if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
3475 while ((c = str->getChar()) != EOF) {
3481 void PSOutputDev::writePSChar(char c) {
3483 t3String->append(c);
3485 (*outputFunc)(outputStream, &c, 1);
3489 void PSOutputDev::writePS(char *s) {
3491 t3String->append(s);
3493 (*outputFunc)(outputStream, s, strlen(s));
3497 void PSOutputDev::writePSFmt(const char *fmt, ...) {
3501 va_start(args, fmt);
3502 vsprintf(buf, fmt, args);
3505 t3String->append(buf);
3507 (*outputFunc)(outputStream, buf, strlen(buf));
3511 void PSOutputDev::writePSString(GString *s) {
3517 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
3518 if (*p == '(' || *p == ')' || *p == '\\') {
3520 writePSChar((char)*p);
3521 } else if (*p < 0x20 || *p >= 0x80) {
3522 sprintf(buf, "\\%03o", *p);
3524 t3String->append(buf);
3526 (*outputFunc)(outputStream, buf, strlen(buf));
3529 writePSChar((char)*p);
3535 void PSOutputDev::writePSName(char *s) {
3540 while ((c = *p++)) {
3541 if (c <= (char)0x20 || c >= (char)0x7f ||
3542 c == '(' || c == ')' || c == '<' || c == '>' ||
3543 c == '[' || c == ']' || c == '{' || c == '}' ||
3544 c == '/' || c == '%') {
3545 writePSFmt("#%02x", c & 0xff);
3552 GString *PSOutputDev::filterPSName(GString *name) {
3558 name2 = new GString();
3560 // ghostscript chokes on names that begin with out-of-limits
3561 // numbers, e.g., 1e4foo is handled correctly (as a name), but
3562 // 1e999foo generates a limitcheck error
3563 c = name->getChar(0);
3564 if (c >= '0' && c <= '9') {
3568 for (i = 0; i < name->getLength(); ++i) {
3569 c = name->getChar(i);
3570 if (c <= (char)0x20 || c >= (char)0x7f ||
3571 c == '(' || c == ')' || c == '<' || c == '>' ||
3572 c == '[' || c == ']' || c == '{' || c == '}' ||
3573 c == '/' || c == '%') {
3574 sprintf(buf, "#%02x", c & 0xff);