1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
23 #include "GlobalParams.h"
30 #include "CharCodeToUnicode.h"
31 #include "UnicodeMap.h"
37 #include "PSOutputDev.h"
40 // needed for setting type/creator of MacOS files
41 #include "ICSupport.h"
44 //------------------------------------------------------------------------
45 // PostScript prolog and setup
46 //------------------------------------------------------------------------
48 static char *prolog[] = {
49 "/xpdf 75 dict def xpdf begin",
50 "% PDF special state",
51 "/pdfDictSize 14 def",
53 " 3 1 roll 2 array astore",
54 " /setpagedevice where {",
56 " /PageSize exch def",
57 " /ImagingBBox null def",
58 " /Policies 1 dict dup begin /PageSize 3 def end def",
59 " { /Duplex true def } if",
60 " currentdict end setpagedevice",
66 " pdfDictSize dict begin",
68 " /pdfStroke [0] def",
69 " /pdfLastFill false def",
70 " /pdfLastStroke false def",
71 " /pdfTextMat [1 0 0 1 0 0] def",
72 " /pdfFontSize 0 def",
73 " /pdfCharSpacing 0 def",
74 " /pdfTextRender 0 def",
75 " /pdfTextRise 0 def",
76 " /pdfWordSpacing 0 def",
77 " /pdfHorizScaling 1 def",
79 "/pdfEndPage { end } def",
80 "% separation convention operators",
81 "/findcmykcustomcolor where {",
84 " /findcmykcustomcolor { 5 array astore } def",
86 "/setcustomcolor where {",
91 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
92 " 0 4 getinterval cvx",
93 " [ exch /dup load exch { mul exch dup } /forall load",
94 " /pop load dup ] cvx",
95 " ] setcolorspace setcolor",
98 "/customcolorimage where {",
101 " /customcolorimage {",
103 " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
105 " [ exch /dup load exch { mul exch dup } /forall load",
106 " /pop load dup ] cvx",
110 " /DataSource exch def",
111 " /ImageMatrix exch def",
112 " /BitsPerComponent exch def",
115 " /Decode [1 0] def",
123 " pdfLastStroke not {",
124 " pdfStroke aload length",
134 " findcmykcustomcolor exch setcustomcolor",
138 " /pdfLastStroke true def /pdfLastFill false def",
142 " pdfLastFill not {",
143 " pdfFill aload length",
153 " findcmykcustomcolor exch setcustomcolor",
157 " /pdfLastFill true def /pdfLastStroke false def",
162 " 4 3 roll findfont",
163 " 4 2 roll matrix scale makefont",
164 " dup length dict begin",
165 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
166 " /Encoding exch def",
173 " dup length dict begin",
174 " { 1 index /FID ne { def } { pop pop } ifelse } forall",
180 "/pdfMakeFont16L3 {",
181 " 1 index /CIDFont resourcestatus {",
182 " pop pop 1 index /CIDFont findresource /CIDFontType known",
187 " 0 eq { /Identity-H } { /Identity-V } ifelse",
188 " exch 1 array astore composefont pop",
193 "% graphics state operators",
194 "/q { gsave pdfDictSize dict begin } def",
195 "/Q { end grestore } def",
196 "/cm { concat } def",
197 "/d { setdash } def",
198 "/i { setflat } def",
199 "/j { setlinejoin } def",
200 "/J { setlinecap } def",
201 "/M { setmiterlimit } def",
202 "/w { setlinewidth } def",
204 "/g { dup 1 array astore /pdfFill exch def setgray",
205 " /pdfLastFill true def /pdfLastStroke false def } def",
206 "/G { dup 1 array astore /pdfStroke exch def setgray",
207 " /pdfLastStroke true def /pdfLastFill false def } def",
208 "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor",
209 " /pdfLastFill true def /pdfLastStroke false def } def",
210 "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor",
211 " /pdfLastStroke true def /pdfLastFill false def } def",
212 "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
213 " /pdfLastFill true def /pdfLastStroke false def } def",
214 "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
215 " /pdfLastStroke true def /pdfLastFill false def } def",
216 "/ck { 6 copy 6 array astore /pdfFill exch def",
217 " findcmykcustomcolor exch setcustomcolor",
218 " /pdfLastFill true def /pdfLastStroke false def } def",
219 "/CK { 6 copy 6 array astore /pdfStroke exch def",
220 " findcmykcustomcolor exch setcustomcolor",
221 " /pdfLastStroke true def /pdfLastFill false def } def",
222 "% path segment operators",
225 "/c { curveto } def",
226 "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
227 " neg 0 rlineto closepath } def",
228 "/h { closepath } def",
229 "% path painting operators",
230 "/S { sCol stroke } def",
231 "/Sf { fCol stroke } def",
232 "/f { fCol fill } def",
233 "/f* { fCol eofill } def",
234 "% clipping operators",
235 "/W { clip newpath } def",
236 "/W* { eoclip newpath } def",
237 "% text state operators",
238 "/Tc { /pdfCharSpacing exch def } def",
239 "/Tf { dup /pdfFontSize exch def",
240 " dup pdfHorizScaling mul exch matrix scale",
241 " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
242 " exch findfont exch makefont setfont } def",
243 "/Tr { /pdfTextRender exch def } def",
244 "/Ts { /pdfTextRise exch def } def",
245 "/Tw { /pdfWordSpacing exch def } def",
246 "/Tz { /pdfHorizScaling exch def } def",
247 "% text positioning operators",
248 "/Td { pdfTextMat transform moveto } def",
249 "/Tm { /pdfTextMat exch def } def",
250 "% text string operators",
251 "/awcp { % awidthcharpath",
253 " 1 string dup 0 3 index put 2 index charpath",
254 " 3 index 3 index rmoveto",
255 " 4 index eq { 5 index 5 index rmoveto } if",
259 "/Tj { fCol", // because stringwidth has to draw Type 3 chars
260 " 0 pdfTextRise pdfTextMat dtransform rmoveto",
261 " 1 index stringwidth pdfTextMat idtransform pop",
262 " sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
263 " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
264 " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
265 " pdfTextMat dtransform",
267 " currentpoint 8 2 roll",
268 " pdfTextRender 1 and 0 eq {",
269 " 6 copy awidthshow",
271 " pdfTextRender 3 and dup 1 eq exch 2 eq or {",
273 " currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
274 " false awcp currentpoint stroke moveto",
278 " 0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
279 "/Tj16 { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
280 " 0 pdfTextRise pdfTextMat dtransform rmoveto",
281 " 2 index stringwidth pdfTextMat idtransform pop",
283 " pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
284 " 4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
285 " pdfTextMat dtransform",
286 " 6 5 roll awidthshow",
287 " 0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
288 "/Tj16V { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
289 " 0 pdfTextRise pdfTextMat dtransform rmoveto",
290 " 2 index stringwidth pdfTextMat idtransform exch pop",
292 " 0 pdfWordSpacing pdfTextMat dtransform 32",
293 " 4 3 roll pdfCharSpacing add 0 exch",
294 " pdfTextMat dtransform",
295 " 6 5 roll awidthshow",
296 " 0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
297 "/TJm { pdfFontSize 0.001 mul mul neg 0",
298 " pdfTextMat dtransform rmoveto } def",
299 "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
300 " pdfTextMat dtransform rmoveto } def",
301 "% Level 1 image operators",
303 " /pdfImBuf1 4 index string def",
304 " { currentfile pdfImBuf1 readhexstring pop } image",
307 " /pdfImBuf1 4 index string def",
308 " /pdfImBuf2 4 index string def",
309 " /pdfImBuf3 4 index string def",
310 " /pdfImBuf4 4 index string def",
311 " { currentfile pdfImBuf1 readhexstring pop }",
312 " { currentfile pdfImBuf2 readhexstring pop }",
313 " { currentfile pdfImBuf3 readhexstring pop }",
314 " { currentfile pdfImBuf4 readhexstring pop }",
315 " true 4 colorimage",
318 " /pdfImBuf1 4 index 7 add 8 idiv string def",
319 " { currentfile pdfImBuf1 readhexstring pop } imagemask",
321 "% Level 2 image operators",
322 "/pdfImBuf 100 string def",
325 " { currentfile pdfImBuf readline",
326 " not { pop exit } if",
327 " (%-EOD-) eq { exit } if } loop",
330 " findcmykcustomcolor exch",
331 " dup /Width get /pdfImBuf1 exch string def",
332 " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
333 " /pdfImDecodeLow exch def",
334 " begin Width Height BitsPerComponent ImageMatrix DataSource end",
335 " /pdfImData exch def",
336 " { pdfImData pdfImBuf1 readstring pop",
337 " 0 1 2 index length 1 sub {",
338 " 1 index exch 2 copy get",
339 " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
342 " 6 5 roll customcolorimage",
343 " { currentfile pdfImBuf readline",
344 " not { pop exit } if",
345 " (%-EOD-) eq { exit } if } loop",
349 " { currentfile pdfImBuf readline",
350 " not { pop exit } if",
351 " (%-EOD-) eq { exit } if } loop",
357 static char *cmapProlog[] = {
358 "/CIDInit /ProcSet findresource begin",
362 " /CMapName /Identity-H def",
363 " /CIDSystemInfo 3 dict dup begin",
364 " /Registry (Adobe) def",
365 " /Ordering (Identity) def",
366 " /Supplement 0 def",
368 " 1 begincodespacerange",
370 " endcodespacerange",
376 " currentdict CMapName exch /CMap defineresource pop",
381 " /CMapName /Identity-V def",
382 " /CIDSystemInfo 3 dict dup begin",
383 " /Registry (Adobe) def",
384 " /Ordering (Identity) def",
385 " /Supplement 0 def",
388 " 1 begincodespacerange",
390 " endcodespacerange",
396 " currentdict CMapName exch /CMap defineresource pop",
402 //------------------------------------------------------------------------
404 //------------------------------------------------------------------------
407 char *psName; // PostScript name
408 double mWidth; // width of 'm' character
411 static char *psFonts[] = {
415 "Courier-BoldOblique",
419 "Helvetica-BoldOblique",
429 static PSSubstFont psSubstFonts[] = {
430 {"Helvetica", 0.833},
431 {"Helvetica-Oblique", 0.833},
432 {"Helvetica-Bold", 0.889},
433 {"Helvetica-BoldOblique", 0.889},
434 {"Times-Roman", 0.788},
435 {"Times-Italic", 0.722},
436 {"Times-Bold", 0.833},
437 {"Times-BoldItalic", 0.778},
439 {"Courier-Oblique", 0.600},
440 {"Courier-Bold", 0.600},
441 {"Courier-BoldOblique", 0.600}
444 // Encoding info for substitute 16-bit font
450 //------------------------------------------------------------------------
452 //------------------------------------------------------------------------
454 #define psProcessCyan 1
455 #define psProcessMagenta 2
456 #define psProcessYellow 4
457 #define psProcessBlack 8
458 #define psProcessCMYK 15
460 //------------------------------------------------------------------------
462 //------------------------------------------------------------------------
464 class PSOutCustomColor {
467 PSOutCustomColor(double cA, double mA,
468 double yA, double kA, GString *nameA);
473 PSOutCustomColor *next;
476 PSOutCustomColor::PSOutCustomColor(double cA, double mA,
477 double yA, double kA, GString *nameA) {
486 PSOutCustomColor::~PSOutCustomColor() {
490 //------------------------------------------------------------------------
492 //------------------------------------------------------------------------
495 typedef void (*SignalFunc)(int);
498 static void outputToFile(void *stream, char *data, int len) {
499 fwrite(data, 1, len, (FILE *)stream);
502 PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
503 int firstPage, int lastPage, PSOutMode modeA) {
505 PSFileType fileTypeA;
509 fontFileNames = NULL;
517 if (!strcmp(fileName, "-")) {
518 fileTypeA = psStdout;
520 } else if (fileName[0] == '|') {
524 signal(SIGPIPE, (SignalFunc)SIG_IGN);
526 if (!(f = popen(fileName + 1, "w"))) {
527 error(-1, "Couldn't run print command '%s'", fileName);
532 error(-1, "Print commands are not supported ('%s')", fileName);
538 if (!(f = fopen(fileName, "w"))) {
539 error(-1, "Couldn't open PostScript file '%s'", fileName);
545 init(outputToFile, f, fileTypeA,
546 xrefA, catalog, firstPage, lastPage, modeA);
549 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
550 XRef *xrefA, Catalog *catalog,
551 int firstPage, int lastPage, PSOutMode modeA) {
554 fontFileNames = NULL;
561 init(outputFuncA, outputStreamA, psGeneric,
562 xrefA, catalog, firstPage, lastPage, modeA);
565 void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
566 PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
567 int firstPage, int lastPage, PSOutMode modeA) {
579 outputFunc = outputFuncA;
580 outputStream = outputStreamA;
581 fileType = fileTypeA;
583 level = globalParams->getPSLevel();
585 paperWidth = globalParams->getPSPaperWidth();
586 paperHeight = globalParams->getPSPaperHeight();
587 if (paperWidth < 0 || paperHeight < 0) {
588 page = catalog->getPage(firstPage);
589 paperWidth = (int)(page->getWidth() + 0.5);
590 paperHeight = (int)(page->getHeight() + 0.5);
592 if (mode == psModeForm) {
593 lastPage = firstPage;
596 inType3Char = gFalse;
599 // initialize OPI nesting levels
604 // initialize fontIDs, fontFileIDs, and fontFileNames lists
607 fontIDs = (Ref *)gmalloc(fontIDSize * sizeof(Ref));
610 fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref));
611 fontFileNameSize = 64;
613 fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *));
616 xobjStack = new GList();
618 // initialize embedded font resource comment list
619 embFontList = new GString();
624 writePS("%!PS-Adobe-3.0\n");
625 writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
626 writePSFmt("%%%%LanguageLevel: %d\n",
627 (level == psLevel1 || level == psLevel1Sep) ? 1 :
628 (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
629 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
630 writePS("%%DocumentProcessColors: (atend)\n");
631 writePS("%%DocumentCustomColors: (atend)\n");
633 writePS("%%DocumentSuppliedResources: (atend)\n");
634 writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
635 paperWidth, paperHeight);
636 writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
637 writePS("%%EndComments\n");
638 writePS("%%BeginDefaults\n");
639 writePS("%%PageMedia: plain\n");
640 writePS("%%EndDefaults\n");
643 writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
644 writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
645 writePSFmt("%%%%LanguageLevel: %d\n",
646 (level == psLevel1 || level == psLevel1Sep) ? 1 :
647 (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
648 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
649 writePS("%%DocumentProcessColors: (atend)\n");
650 writePS("%%DocumentCustomColors: (atend)\n");
652 page = catalog->getPage(firstPage);
653 box = page->getBox();
654 writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
655 (int)floor(box->x1), (int)floor(box->y1),
656 (int)ceil(box->x2), (int)ceil(box->y2));
657 if (floor(box->x1) != ceil(box->x1) ||
658 floor(box->y1) != ceil(box->y1) ||
659 floor(box->x2) != ceil(box->x2) ||
660 floor(box->y2) != ceil(box->y2)) {
661 writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n",
662 box->x1, box->y1, box->x2, box->y2);
664 writePS("%%DocumentSuppliedResources: (atend)\n");
665 writePS("%%EndComments\n");
668 writePS("%!PS-Adobe-3.0 Resource-Form\n");
669 writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
670 writePSFmt("%%%%LanguageLevel: %d\n",
671 (level == psLevel1 || level == psLevel1Sep) ? 1 :
672 (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
673 if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
674 writePS("%%DocumentProcessColors: (atend)\n");
675 writePS("%%DocumentCustomColors: (atend)\n");
677 writePS("%%DocumentSuppliedResources: (atend)\n");
678 writePS("%%EndComments\n");
679 page = catalog->getPage(firstPage);
680 box = page->getBox();
681 writePS("32 dict dup begin\n");
682 writePSFmt("/BBox [%d %d %d %d] def\n",
683 (int)box->x1, (int)box->y1, (int)box->x2, (int)box->y2);
684 writePS("/FormType 1 def\n");
685 writePS("/Matrix [1 0 0 1 0 0] def\n");
690 if (mode != psModeForm) {
691 writePS("%%BeginProlog\n");
693 writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
694 for (p = prolog; *p; ++p) {
695 writePSFmt("%s\n", *p);
697 writePS("%%EndResource\n");
698 if (level >= psLevel3) {
699 for (p = cmapProlog; *p; ++p) {
700 writePSFmt("%s\n", *p);
703 if (mode != psModeForm) {
704 writePS("%%EndProlog\n");
707 // set up fonts and images
708 if (mode == psModeForm) {
709 // swap the form and xpdf dicts
710 writePS("xpdf end begin dup begin\n");
712 writePS("%%BeginSetup\n");
713 writePS("xpdf begin\n");
715 for (pg = firstPage; pg <= lastPage; ++pg) {
716 page = catalog->getPage(pg);
717 if ((resDict = page->getResourceDict())) {
718 setupResources(resDict);
720 annots = new Annots(xref, page->getAnnots(&obj1));
722 for (i = 0; i < annots->getNumAnnots(); ++i) {
723 if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
724 obj1.streamGetDict()->lookup("Resources", &obj2);
726 setupResources(obj2.getDict());
734 if (mode != psModeForm) {
735 if (mode != psModeEPS) {
736 writePSFmt("%d %d %s pdfSetup\n",
737 paperWidth, paperHeight,
738 globalParams->getPSDuplex() ? "true" : "false");
741 if (globalParams->getPSOPI()) {
742 writePS("/opiMatrix matrix currentmatrix def\n");
745 writePS("%%EndSetup\n");
748 // initialize sequential page number
752 PSOutputDev::~PSOutputDev() {
753 PSOutCustomColor *cc;
757 if (mode == psModeForm) {
758 writePS("/Foo exch /Form defineresource pop\n");
760 writePS("%%Trailer\n");
762 writePS("%%DocumentSuppliedResources:\n");
763 writePS(embFontList->getCString());
764 if (level == psLevel1Sep || level == psLevel2Sep ||
765 level == psLevel3Sep) {
766 writePS("%%DocumentProcessColors:");
767 if (processColors & psProcessCyan) {
770 if (processColors & psProcessMagenta) {
773 if (processColors & psProcessYellow) {
776 if (processColors & psProcessBlack) {
780 writePS("%%DocumentCustomColors:");
781 for (cc = customColors; cc; cc = cc->next) {
782 writePSFmt(" (%s)", cc->name->getCString());
785 writePS("%%CMYKCustomColor:\n");
786 for (cc = customColors; cc; cc = cc->next) {
787 writePSFmt("%%%%+ %g %g %g %g (%s)\n",
788 cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
793 if (fileType == psFile) {
795 ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
797 fclose((FILE *)outputStream);
800 else if (fileType == psPipe) {
801 pclose((FILE *)outputStream);
803 signal(SIGPIPE, (SignalFunc)SIG_DFL);
818 for (i = 0; i < fontFileNameLen; ++i) {
819 delete fontFileNames[i];
821 gfree(fontFileNames);
824 for (i = 0; i < font16EncLen; ++i) {
825 delete font16Enc[i].enc;
832 while (customColors) {
834 customColors = cc->next;
839 void PSOutputDev::setupResources(Dict *resDict) {
840 Object xObjDict, xObjRef, xObj, resObj;
846 setupImages(resDict);
848 resDict->lookup("XObject", &xObjDict);
849 if (xObjDict.isDict()) {
850 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
852 // avoid infinite recursion on XObjects
854 if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
855 ref0 = xObjRef.getRef();
856 for (j = 0; j < xobjStack->getLength(); ++j) {
857 ref1 = *(Ref *)xobjStack->get(j);
858 if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
864 xobjStack->append(&ref0);
869 // process the XObject's resource dictionary
870 xObjDict.dictGetVal(i, &xObj);
871 if (xObj.isStream()) {
872 xObj.streamGetDict()->lookup("Resources", &resObj);
873 if (resObj.isDict()) {
874 setupResources(resObj.getDict());
881 if (xObjRef.isRef() && !skip) {
882 xobjStack->del(xobjStack->getLength() - 1);
890 void PSOutputDev::setupFonts(Dict *resDict) {
892 GfxFontDict *gfxFontDict;
896 resDict->lookup("Font", &fontDict);
897 if (fontDict.isDict()) {
898 gfxFontDict = new GfxFontDict(xref, fontDict.getDict());
899 for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
900 font = gfxFontDict->getFont(i);
901 setupFont(font, resDict);
908 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
911 PSFontParam *fontParam;
914 char type3Name[64], buf[16];
924 // check if font is already set up
925 for (i = 0; i < fontIDLen; ++i) {
926 if (fontIDs[i].num == font->getID()->num &&
927 fontIDs[i].gen == font->getID()->gen) {
932 // add entry to fontIDs list
933 if (fontIDLen >= fontIDSize) {
935 fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref));
937 fontIDs[fontIDLen++] = *font->getID();
943 // check for resident 8-bit font
944 if (font->getName() &&
945 (fontParam = globalParams->getPSFont(font->getName()))) {
946 psName = fontParam->psFontName->getCString();
948 // check for embedded Type 1 font
949 } else if (globalParams->getPSEmbedType1() &&
950 font->getType() == fontType1 &&
951 font->getEmbeddedFontID(&fontFileID)) {
952 psNameStr = filterPSName(font->getEmbeddedFontName());
953 psName = psNameStr->getCString();
954 setupEmbeddedType1Font(&fontFileID, psName);
956 // check for embedded Type 1C font
957 } else if (globalParams->getPSEmbedType1() &&
958 font->getType() == fontType1C &&
959 font->getEmbeddedFontID(&fontFileID)) {
960 psNameStr = filterPSName(font->getEmbeddedFontName());
961 psName = psNameStr->getCString();
962 setupEmbeddedType1CFont(font, &fontFileID, psName);
964 // check for external Type 1 font file
965 } else if (globalParams->getPSEmbedType1() &&
966 font->getType() == fontType1 &&
967 font->getExtFontFile()) {
968 // this assumes that the PS font name matches the PDF font name
969 psName = font->getName()->getCString();
970 setupExternalType1Font(font->getExtFontFile(), psName);
972 // check for embedded TrueType font
973 } else if (globalParams->getPSEmbedTrueType() &&
974 font->getType() == fontTrueType &&
975 font->getEmbeddedFontID(&fontFileID)) {
976 psNameStr = filterPSName(font->getEmbeddedFontName());
977 psName = psNameStr->getCString();
978 setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
980 // check for external TrueType font file
981 } else if (globalParams->getPSEmbedTrueType() &&
982 font->getType() == fontTrueType &&
983 font->getExtFontFile()) {
984 psNameStr = filterPSName(font->getName());
985 psName = psNameStr->getCString();
986 setupExternalTrueTypeFont(font, psName);
988 // check for embedded CID PostScript font
989 } else if (globalParams->getPSEmbedCIDPostScript() &&
990 font->getType() == fontCIDType0C &&
991 font->getEmbeddedFontID(&fontFileID)) {
992 psNameStr = filterPSName(font->getEmbeddedFontName());
993 psName = psNameStr->getCString();
994 setupEmbeddedCIDType0Font(font, &fontFileID, psName);
996 // check for embedded CID TrueType font
997 } else if (globalParams->getPSEmbedCIDTrueType() &&
998 font->getType() == fontCIDType2 &&
999 font->getEmbeddedFontID(&fontFileID)) {
1000 psNameStr = filterPSName(font->getEmbeddedFontName());
1001 psName = psNameStr->getCString();
1002 setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName);
1004 } else if (font->getType() == fontType3) {
1005 sprintf(type3Name, "T3_%d_%d",
1006 font->getID()->num, font->getID()->gen);
1008 setupType3Font(font, psName, parentResDict);
1010 // do 8-bit font substitution
1011 } else if (!font->isCIDFont()) {
1013 name = font->getName();
1016 for (i = 0; psFonts[i]; ++i) {
1017 if (name->cmp(psFonts[i]) == 0) {
1018 psName = psFonts[i];
1024 if (font->isFixedWidth()) {
1026 } else if (font->isSerif()) {
1031 if (font->isBold()) {
1034 if (font->isItalic()) {
1037 psName = psSubstFonts[i].psName;
1038 for (code = 0; code < 256; ++code) {
1039 if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
1040 charName[0] == 'm' && charName[1] == '\0') {
1045 w1 = ((Gfx8BitFont *)font)->getWidth(code);
1049 w2 = psSubstFonts[i].mWidth;
1054 if (font->getType() == fontType3) {
1055 // This is a hack which makes it possible to substitute for some
1056 // Type 3 fonts. The problem is that it's impossible to know what
1057 // the base coordinate system used in the font is without actually
1058 // rendering the font.
1060 fm = font->getFontMatrix();
1062 ys *= fm[3] / fm[0];
1069 // do 16-bit font substitution
1070 } else if ((fontParam = globalParams->
1071 getPSFont16(font->getName(),
1072 ((GfxCIDFont *)font)->getCollection(),
1073 font->getWMode()))) {
1075 psName = fontParam->psFontName->getCString();
1076 if (font16EncLen >= font16EncSize) {
1077 font16EncSize += 16;
1078 font16Enc = (PSFont16Enc *)grealloc(font16Enc,
1079 font16EncSize * sizeof(PSFont16Enc));
1081 font16Enc[font16EncLen].fontID = *font->getID();
1082 font16Enc[font16EncLen].enc = fontParam->encoding->copy();
1083 if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
1087 error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
1088 font16Enc[font16EncLen].enc->getCString());
1091 // give up - can't do anything with this font
1093 error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
1094 font->getName() ? font->getName()->getCString() : "(unnamed)",
1095 ((GfxCIDFont *)font)->getCollection()
1096 ? ((GfxCIDFont *)font)->getCollection()->getCString()
1101 // generate PostScript code to set up the font
1102 if (font->isCIDFont()) {
1103 if (level == psLevel3 || level == psLevel3Sep) {
1104 writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
1105 font->getID()->num, font->getID()->gen, psName,
1108 writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
1109 font->getID()->num, font->getID()->gen, psName,
1113 writePSFmt("/F%d_%d /%s %g %g\n",
1114 font->getID()->num, font->getID()->gen, psName, xs, ys);
1115 for (i = 0; i < 256; i += 8) {
1116 writePSFmt((i == 0) ? "[ " : " ");
1117 for (j = 0; j < 8; ++j) {
1118 if (font->getType() == fontTrueType &&
1120 !((Gfx8BitFont *)font)->getHasEncoding()) {
1121 sprintf(buf, "c%02x", i+j);
1124 charName = ((Gfx8BitFont *)font)->getCharName(i+j);
1125 // this is a kludge for broken PDF files that encode char 32
1127 if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
1132 writePSName(charName ? charName : (char *)".notdef");
1134 writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
1136 writePS("pdfMakeFont\n");
1144 void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
1145 static char hexChar[17] = "0123456789abcdef";
1146 Object refObj, strObj, obj1, obj2;
1148 int length1, length2;
1154 // check if font is already embedded
1155 for (i = 0; i < fontFileIDLen; ++i) {
1156 if (fontFileIDs[i].num == id->num &&
1157 fontFileIDs[i].gen == id->gen)
1161 // add entry to fontFileIDs list
1162 if (fontFileIDLen >= fontFileIDSize) {
1163 fontFileIDSize += 64;
1164 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1166 fontFileIDs[fontFileIDLen++] = *id;
1168 // get the font stream and info
1169 refObj.initRef(id->num, id->gen);
1170 refObj.fetch(xref, &strObj);
1172 if (!strObj.isStream()) {
1173 error(-1, "Embedded font file object is not a stream");
1176 if (!(dict = strObj.streamGetDict())) {
1177 error(-1, "Embedded font stream is missing its dictionary");
1180 dict->lookup("Length1", &obj1);
1181 dict->lookup("Length2", &obj2);
1182 if (!obj1.isInt() || !obj2.isInt()) {
1183 error(-1, "Missing length fields in embedded font stream dictionary");
1188 length1 = obj1.getInt();
1189 length2 = obj2.getInt();
1193 // beginning comment
1194 writePSFmt("%%%%BeginResource: font %s\n", psName);
1195 embFontList->append("%%+ font ");
1196 embFontList->append(psName);
1197 embFontList->append("\n");
1199 // copy ASCII portion of font
1200 strObj.streamReset();
1201 for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
1205 // figure out if encrypted portion is binary or ASCII
1207 for (i = 0; i < 4; ++i) {
1208 start[i] = strObj.streamGetChar();
1209 if (start[i] == EOF) {
1210 error(-1, "Unexpected end of file in embedded font stream");
1213 if (!((start[i] >= '0' && start[i] <= '9') ||
1214 (start[i] >= 'A' && start[i] <= 'F') ||
1215 (start[i] >= 'a' && start[i] <= 'f')))
1219 // convert binary data to ASCII
1221 for (i = 0; i < 4; ++i) {
1222 writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
1223 writePSChar(hexChar[start[i] & 0x0f]);
1225 while (i < length2) {
1226 if ((c = strObj.streamGetChar()) == EOF) {
1229 writePSChar(hexChar[(c >> 4) & 0x0f]);
1230 writePSChar(hexChar[c & 0x0f]);
1231 if (++i % 32 == 0) {
1239 // already in ASCII format -- just copy it
1241 for (i = 0; i < 4; ++i) {
1242 writePSChar(start[i]);
1244 for (i = 4; i < length2; ++i) {
1245 if ((c = strObj.streamGetChar()) == EOF) {
1252 // write padding and "cleartomark"
1253 for (i = 0; i < 8; ++i) {
1254 writePS("00000000000000000000000000000000"
1255 "00000000000000000000000000000000\n");
1257 writePS("cleartomark\n");
1260 writePS("%%EndResource\n");
1263 strObj.streamClose();
1267 //~ This doesn't handle .pfb files or binary eexec data (which only
1268 //~ happens in pfb files?).
1269 void PSOutputDev::setupExternalType1Font(GString *fileName, char *psName) {
1274 // check if font is already embedded
1275 for (i = 0; i < fontFileNameLen; ++i) {
1276 if (!fontFileNames[i]->cmp(fileName)) {
1281 // add entry to fontFileNames list
1282 if (fontFileNameLen >= fontFileNameSize) {
1283 fontFileNameSize += 64;
1284 fontFileNames = (GString **)grealloc(fontFileNames,
1285 fontFileNameSize * sizeof(GString *));
1287 fontFileNames[fontFileNameLen++] = fileName->copy();
1289 // beginning comment
1290 writePSFmt("%%%%BeginResource: font %s\n", psName);
1291 embFontList->append("%%+ font ");
1292 embFontList->append(psName);
1293 embFontList->append("\n");
1295 // copy the font file
1296 if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
1297 error(-1, "Couldn't open external font file");
1300 while ((c = fgetc(fontFile)) != EOF) {
1306 writePS("%%EndResource\n");
1309 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
1313 Type1CFontFile *t1cFile;
1316 // check if font is already embedded
1317 for (i = 0; i < fontFileIDLen; ++i) {
1318 if (fontFileIDs[i].num == id->num &&
1319 fontFileIDs[i].gen == id->gen)
1323 // add entry to fontFileIDs list
1324 if (fontFileIDLen >= fontFileIDSize) {
1325 fontFileIDSize += 64;
1326 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1328 fontFileIDs[fontFileIDLen++] = *id;
1330 // beginning comment
1331 writePSFmt("%%%%BeginResource: font %s\n", psName);
1332 embFontList->append("%%+ font ");
1333 embFontList->append(psName);
1334 embFontList->append("\n");
1336 // convert it to a Type 1 font
1337 fontBuf = font->readEmbFontFile(xref, &fontLen);
1338 t1cFile = new Type1CFontFile(fontBuf, fontLen);
1339 if (t1cFile->isOk()) {
1340 t1cFile->convertToType1(outputFunc, outputStream);
1346 writePS("%%EndResource\n");
1349 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
1353 TrueTypeFontFile *ttFile;
1354 CharCodeToUnicode *ctu;
1357 // check if font is already embedded
1358 for (i = 0; i < fontFileIDLen; ++i) {
1359 if (fontFileIDs[i].num == id->num &&
1360 fontFileIDs[i].gen == id->gen)
1364 // add entry to fontFileIDs list
1365 if (fontFileIDLen >= fontFileIDSize) {
1366 fontFileIDSize += 64;
1367 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1369 fontFileIDs[fontFileIDLen++] = *id;
1371 // beginning comment
1372 writePSFmt("%%%%BeginResource: font %s\n", psName);
1373 embFontList->append("%%+ font ");
1374 embFontList->append(psName);
1375 embFontList->append("\n");
1377 // convert it to a Type 42 font
1378 fontBuf = font->readEmbFontFile(xref, &fontLen);
1379 ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1380 ctu = ((Gfx8BitFont *)font)->getToUnicode();
1381 ttFile->convertToType42(psName, ((Gfx8BitFont *)font)->getEncoding(),
1382 ctu, ((Gfx8BitFont *)font)->getHasEncoding(),
1383 ((Gfx8BitFont *)font)->isSymbolic(),
1384 outputFunc, outputStream);
1390 writePS("%%EndResource\n");
1393 void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, char *psName) {
1397 TrueTypeFontFile *ttFile;
1398 CharCodeToUnicode *ctu;
1401 // check if font is already embedded
1402 fileName = font->getExtFontFile();
1403 for (i = 0; i < fontFileNameLen; ++i) {
1404 if (!fontFileNames[i]->cmp(fileName)) {
1409 // add entry to fontFileNames list
1410 if (fontFileNameLen >= fontFileNameSize) {
1411 fontFileNameSize += 64;
1412 fontFileNames = (GString **)grealloc(fontFileNames,
1413 fontFileNameSize * sizeof(GString *));
1415 fontFileNames[fontFileNameLen++] = fileName->copy();
1417 // beginning comment
1418 writePSFmt("%%%%BeginResource: font %s\n", psName);
1419 embFontList->append("%%+ font ");
1420 embFontList->append(psName);
1421 embFontList->append("\n");
1423 // convert it to a Type 42 font
1424 fontBuf = font->readExtFontFile(&fontLen);
1425 ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1426 ctu = ((Gfx8BitFont *)font)->getToUnicode();
1427 ttFile->convertToType42(psName, ((Gfx8BitFont *)font)->getEncoding(),
1428 ctu, ((Gfx8BitFont *)font)->getHasEncoding(),
1429 ((Gfx8BitFont *)font)->isSymbolic(),
1430 outputFunc, outputStream);
1436 writePS("%%EndResource\n");
1439 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
1443 Type1CFontFile *t1cFile;
1446 // check if font is already embedded
1447 for (i = 0; i < fontFileIDLen; ++i) {
1448 if (fontFileIDs[i].num == id->num &&
1449 fontFileIDs[i].gen == id->gen)
1453 // add entry to fontFileIDs list
1454 if (fontFileIDLen >= fontFileIDSize) {
1455 fontFileIDSize += 64;
1456 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1458 fontFileIDs[fontFileIDLen++] = *id;
1460 // beginning comment
1461 writePSFmt("%%%%BeginResource: font %s\n", psName);
1462 embFontList->append("%%+ font ");
1463 embFontList->append(psName);
1464 embFontList->append("\n");
1466 // convert it to a Type 0 font
1467 fontBuf = font->readEmbFontFile(xref, &fontLen);
1468 t1cFile = new Type1CFontFile(fontBuf, fontLen);
1469 if (t1cFile->isOk()) {
1470 if (globalParams->getPSLevel() >= psLevel3) {
1471 // Level 3: use a CID font
1472 t1cFile->convertToCIDType0(psName, outputFunc, outputStream);
1474 // otherwise: use a non-CID composite font
1475 t1cFile->convertToType0(psName, outputFunc, outputStream);
1482 writePS("%%EndResource\n");
1485 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
1489 TrueTypeFontFile *ttFile;
1492 // check if font is already embedded
1493 for (i = 0; i < fontFileIDLen; ++i) {
1494 if (fontFileIDs[i].num == id->num &&
1495 fontFileIDs[i].gen == id->gen)
1499 // add entry to fontFileIDs list
1500 if (fontFileIDLen >= fontFileIDSize) {
1501 fontFileIDSize += 64;
1502 fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
1504 fontFileIDs[fontFileIDLen++] = *id;
1506 // beginning comment
1507 writePSFmt("%%%%BeginResource: font %s\n", psName);
1508 embFontList->append("%%+ font ");
1509 embFontList->append(psName);
1510 embFontList->append("\n");
1512 // convert it to a Type 0 font
1513 fontBuf = font->readEmbFontFile(xref, &fontLen);
1514 ttFile = new TrueTypeFontFile(fontBuf, fontLen);
1515 if (globalParams->getPSLevel() >= psLevel3) {
1516 ttFile->convertToCIDType2(psName,
1517 ((GfxCIDFont *)font)->getCIDToGID(),
1518 ((GfxCIDFont *)font)->getCIDToGIDLen(),
1519 outputFunc, outputStream);
1521 // otherwise: use a non-CID composite font
1522 ttFile->convertToType0(psName, ((GfxCIDFont *)font)->getCIDToGID(),
1523 ((GfxCIDFont *)font)->getCIDToGIDLen(),
1524 outputFunc, outputStream);
1530 writePS("%%EndResource\n");
1533 void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
1534 Dict *parentResDict) {
1544 // set up resources used by font
1545 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
1546 setupResources(resDict);
1548 resDict = parentResDict;
1551 // beginning comment
1552 writePSFmt("%%%%BeginResource: font %s\n", psName);
1553 embFontList->append("%%+ font ");
1554 embFontList->append(psName);
1555 embFontList->append("\n");
1558 writePS("7 dict begin\n");
1559 writePS("/FontType 3 def\n");
1560 m = font->getFontMatrix();
1561 writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n",
1562 m[0], m[1], m[2], m[3], m[4], m[5]);
1563 m = font->getFontBBox();
1564 writePSFmt("/FontBBox [%g %g %g %g] def\n",
1565 m[0], m[1], m[2], m[3]);
1566 writePS("/Encoding 256 array def\n");
1567 writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
1568 writePS("/BuildGlyph {\n");
1569 writePS(" exch /CharProcs get exch\n");
1570 writePS(" 2 copy known not { pop /.notdef } if\n");
1571 writePS(" get exec\n");
1572 writePS("} bind def\n");
1573 writePS("/BuildChar {\n");
1574 writePS(" 1 index /Encoding get exch get\n");
1575 writePS(" 1 index /BuildGlyph get exec\n");
1576 writePS("} bind def\n");
1577 if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
1578 writePSFmt("/CharProcs %d dict def\n", charProcs->getLength());
1579 writePS("CharProcs begin\n");
1584 gfx = new Gfx(xref, this, resDict, &box, gFalse, NULL);
1585 inType3Char = gTrue;
1586 t3Cacheable = gFalse;
1587 for (i = 0; i < charProcs->getLength(); ++i) {
1589 writePSName(charProcs->getKey(i));
1591 gfx->display(charProcs->getVal(i, &charProc));
1595 sprintf(buf, "%g %g %g %g %g %g setcachedevice\n",
1596 t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
1598 sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY);
1600 (*outputFunc)(outputStream, buf, strlen(buf));
1601 (*outputFunc)(outputStream, t3String->getCString(),
1602 t3String->getLength());
1606 (*outputFunc)(outputStream, "Q\n", 2);
1609 inType3Char = gFalse;
1613 writePS("currentdict end\n");
1614 writePSFmt("/%s exch definefont pop\n", psName);
1617 writePS("%%EndResource\n");
1620 void PSOutputDev::setupImages(Dict *resDict) {
1621 Object xObjDict, xObj, xObjRef, subtypeObj;
1624 if (mode != psModeForm) {
1628 resDict->lookup("XObject", &xObjDict);
1629 if (xObjDict.isDict()) {
1630 for (i = 0; i < xObjDict.dictGetLength(); ++i) {
1631 xObjDict.dictGetValNF(i, &xObjRef);
1632 xObjDict.dictGetVal(i, &xObj);
1633 if (xObj.isStream()) {
1634 xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
1635 if (subtypeObj.isName("Image")) {
1636 if (xObjRef.isRef()) {
1637 setupImage(xObjRef.getRef(), xObj.getStream());
1639 error(-1, "Image in resource dict is not an indirect reference");
1651 void PSOutputDev::setupImage(Ref id, Stream *str) {
1653 int size, line, col, i;
1655 // construct an encoder stream
1656 if (globalParams->getPSASCIIHex()) {
1657 str = new ASCIIHexEncoder(str);
1659 str = new ASCII85Encoder(str);
1662 // compute image data size
1668 } while (c == '\n' || c == '\r');
1669 if (c == '~' || c == EOF) {
1676 for (i = 1; i <= 4; ++i) {
1679 } while (c == '\n' || c == '\r');
1680 if (c == '~' || c == EOF) {
1690 } while (c != '~' && c != EOF);
1692 writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
1694 // write the data into the array
1697 writePS("dup 0 <~");
1701 } while (c == '\n' || c == '\r');
1702 if (c == '~' || c == EOF) {
1711 for (i = 1; i <= 4; ++i) {
1714 } while (c == '\n' || c == '\r');
1715 if (c == '~' || c == EOF) {
1722 // each line is: "dup nnnnn <~...data...~> put<eol>"
1723 // so max data length = 255 - 20 = 235
1724 // chunks are 1 or 4 bytes each, so we have to stop at 232
1725 // but make it 225 just to be safe
1727 writePS("~> put\n");
1729 writePSFmt("dup %d <~", line);
1732 } while (c != '~' && c != EOF);
1733 writePS("~> put\n");
1739 void PSOutputDev::startPage(int pageNum, GfxState *state) {
1740 int x1, y1, x2, y2, width, height, t;
1746 writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage);
1747 writePS("%%BeginPageSetup\n");
1749 // rotate, translate, and scale page
1750 x1 = (int)(state->getX1() + 0.5);
1751 y1 = (int)(state->getY1() + 0.5);
1752 x2 = (int)(state->getX2() + 0.5);
1753 y2 = (int)(state->getY2() + 0.5);
1756 if (width > height && width > paperWidth) {
1758 writePSFmt("%%%%PageOrientation: %s\n",
1759 state->getCTM()[0] ? "Landscape" : "Portrait");
1760 writePS("pdfStartPage\n");
1761 writePS("90 rotate\n");
1763 ty = -(y1 + paperWidth);
1769 writePSFmt("%%%%PageOrientation: %s\n",
1770 state->getCTM()[0] ? "Portrait" : "Landscape");
1771 writePS("pdfStartPage\n");
1775 if (width < paperWidth) {
1776 tx += (paperWidth - width) / 2;
1778 if (height < paperHeight) {
1779 ty += (paperHeight - height) / 2;
1781 if (tx != 0 || ty != 0) {
1782 writePSFmt("%g %g translate\n", tx, ty);
1784 if (width > paperWidth || height > paperHeight) {
1785 xScale = (double)paperWidth / (double)width;
1786 yScale = (double)paperHeight / (double)height;
1787 if (yScale < xScale) {
1792 writePSFmt("%0.4f %0.4f scale\n", xScale, xScale);
1794 xScale = yScale = 1;
1797 writePS("%%EndPageSetup\n");
1802 writePS("pdfStartPage\n");
1804 xScale = yScale = 1;
1809 writePS("/PaintProc {\n");
1810 writePS("begin xpdf begin\n");
1811 writePS("pdfStartPage\n");
1813 xScale = yScale = 1;
1819 void PSOutputDev::endPage() {
1821 if (mode == psModeForm) {
1822 writePS("pdfEndPage\n");
1823 writePS("end end\n");
1825 writePS("end end\n");
1827 writePS("showpage\n");
1828 writePS("%%PageTrailer\n");
1829 writePS("pdfEndPage\n");
1833 void PSOutputDev::saveState(GfxState *state) {
1837 void PSOutputDev::restoreState(GfxState *state) {
1841 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
1842 double m21, double m22, double m31, double m32) {
1843 writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
1846 void PSOutputDev::updateLineDash(GfxState *state) {
1851 state->getLineDash(&dash, &length, &start);
1853 for (i = 0; i < length; ++i)
1854 writePSFmt("%g%s", dash[i], (i == length-1) ? "" : " ");
1855 writePSFmt("] %g d\n", start);
1858 void PSOutputDev::updateFlatness(GfxState *state) {
1859 writePSFmt("%d i\n", state->getFlatness());
1862 void PSOutputDev::updateLineJoin(GfxState *state) {
1863 writePSFmt("%d j\n", state->getLineJoin());
1866 void PSOutputDev::updateLineCap(GfxState *state) {
1867 writePSFmt("%d J\n", state->getLineCap());
1870 void PSOutputDev::updateMiterLimit(GfxState *state) {
1871 writePSFmt("%g M\n", state->getMiterLimit());
1874 void PSOutputDev::updateLineWidth(GfxState *state) {
1875 writePSFmt("%g w\n", state->getLineWidth());
1878 void PSOutputDev::updateFillColor(GfxState *state) {
1883 GfxSeparationColorSpace *sepCS;
1887 state->getFillGray(&gray);
1888 writePSFmt("%g g\n", gray);
1891 state->getFillCMYK(&cmyk);
1892 writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1893 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1897 if (state->getFillColorSpace()->getMode() == csDeviceCMYK) {
1898 state->getFillCMYK(&cmyk);
1899 writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1901 state->getFillRGB(&rgb);
1902 if (rgb.r == rgb.g && rgb.g == rgb.b) {
1903 writePSFmt("%g g\n", rgb.r);
1905 writePSFmt("%g %g %g rg\n", rgb.r, rgb.g, rgb.b);
1911 if (state->getFillColorSpace()->getMode() == csSeparation) {
1912 sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
1914 sepCS->getCMYK(&color, &cmyk);
1915 writePSFmt("%g %g %g %g %g (%s) ck\n",
1916 state->getFillColor()->c[0],
1917 cmyk.c, cmyk.m, cmyk.y, cmyk.k,
1918 sepCS->getName()->getCString());
1919 addCustomColor(sepCS);
1921 state->getFillCMYK(&cmyk);
1922 writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1923 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1927 t3Cacheable = gFalse;
1930 void PSOutputDev::updateStrokeColor(GfxState *state) {
1935 GfxSeparationColorSpace *sepCS;
1939 state->getStrokeGray(&gray);
1940 writePSFmt("%g G\n", gray);
1943 state->getStrokeCMYK(&cmyk);
1944 writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1945 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1949 if (state->getStrokeColorSpace()->getMode() == csDeviceCMYK) {
1950 state->getStrokeCMYK(&cmyk);
1951 writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1953 state->getStrokeRGB(&rgb);
1954 if (rgb.r == rgb.g && rgb.g == rgb.b) {
1955 writePSFmt("%g G\n", rgb.r);
1957 writePSFmt("%g %g %g RG\n", rgb.r, rgb.g, rgb.b);
1963 if (state->getStrokeColorSpace()->getMode() == csSeparation) {
1964 sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
1966 sepCS->getCMYK(&color, &cmyk);
1967 writePSFmt("%g %g %g %g %g (%s) CK\n",
1968 state->getStrokeColor()->c[0],
1969 cmyk.c, cmyk.m, cmyk.y, cmyk.k,
1970 sepCS->getName()->getCString());
1971 addCustomColor(sepCS);
1973 state->getStrokeCMYK(&cmyk);
1974 writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1975 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
1979 t3Cacheable = gFalse;
1982 void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
1984 processColors |= psProcessCyan;
1987 processColors |= psProcessMagenta;
1990 processColors |= psProcessYellow;
1993 processColors |= psProcessBlack;
1997 void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
1998 PSOutCustomColor *cc;
2002 for (cc = customColors; cc; cc = cc->next) {
2003 if (!cc->name->cmp(sepCS->getName())) {
2008 sepCS->getCMYK(&color, &cmyk);
2009 cc = new PSOutCustomColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2010 sepCS->getName()->copy());
2011 cc->next = customColors;
2015 void PSOutputDev::updateFont(GfxState *state) {
2016 if (state->getFont()) {
2017 writePSFmt("/F%d_%d %g Tf\n",
2018 state->getFont()->getID()->num, state->getFont()->getID()->gen,
2019 state->getFontSize());
2023 void PSOutputDev::updateTextMat(GfxState *state) {
2026 mat = state->getTextMat();
2027 writePSFmt("[%g %g %g %g %g %g] Tm\n",
2028 mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
2031 void PSOutputDev::updateCharSpace(GfxState *state) {
2032 writePSFmt("%g Tc\n", state->getCharSpace());
2035 void PSOutputDev::updateRender(GfxState *state) {
2038 rm = state->getRender();
2039 writePSFmt("%d Tr\n", rm);
2041 if (rm != 0 && rm != 3) {
2042 t3Cacheable = gFalse;
2046 void PSOutputDev::updateRise(GfxState *state) {
2047 writePSFmt("%g Ts\n", state->getRise());
2050 void PSOutputDev::updateWordSpace(GfxState *state) {
2051 writePSFmt("%g Tw\n", state->getWordSpace());
2054 void PSOutputDev::updateHorizScaling(GfxState *state) {
2055 writePSFmt("%g Tz\n", state->getHorizScaling());
2058 void PSOutputDev::updateTextPos(GfxState *state) {
2059 writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
2062 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
2063 if (state->getFont()->getWMode()) {
2064 writePSFmt("%g TJmV\n", shift);
2066 writePSFmt("%g TJm\n", shift);
2070 void PSOutputDev::stroke(GfxState *state) {
2071 doPath(state->getPath());
2073 // if we're construct a cacheable Type 3 glyph, we need to do
2074 // everything in the fill color
2081 void PSOutputDev::fill(GfxState *state) {
2082 doPath(state->getPath());
2086 void PSOutputDev::eoFill(GfxState *state) {
2087 doPath(state->getPath());
2091 void PSOutputDev::clip(GfxState *state) {
2092 doPath(state->getPath());
2096 void PSOutputDev::eoClip(GfxState *state) {
2097 doPath(state->getPath());
2101 void PSOutputDev::doPath(GfxPath *path) {
2102 GfxSubpath *subpath;
2103 double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
2106 n = path->getNumSubpaths();
2108 if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
2109 subpath = path->getSubpath(0);
2110 x0 = subpath->getX(0);
2111 y0 = subpath->getY(0);
2112 x4 = subpath->getX(4);
2113 y4 = subpath->getY(4);
2114 if (x4 == x0 && y4 == y0) {
2115 x1 = subpath->getX(1);
2116 y1 = subpath->getY(1);
2117 x2 = subpath->getX(2);
2118 y2 = subpath->getY(2);
2119 x3 = subpath->getX(3);
2120 y3 = subpath->getY(3);
2121 if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
2122 writePSFmt("%g %g %g %g re\n",
2123 x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
2124 fabs(x2 - x0), fabs(y1 - y0));
2126 } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
2127 writePSFmt("%g %g %g %g re\n",
2128 x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
2129 fabs(x1 - x0), fabs(y2 - y0));
2135 for (i = 0; i < n; ++i) {
2136 subpath = path->getSubpath(i);
2137 m = subpath->getNumPoints();
2138 writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0));
2141 if (subpath->getCurve(j)) {
2142 writePSFmt("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
2143 subpath->getX(j+1), subpath->getY(j+1),
2144 subpath->getX(j+2), subpath->getY(j+2));
2147 writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j));
2151 if (subpath->isClosed()) {
2157 void PSOutputDev::drawString(GfxState *state, GString *s) {
2161 double dx, dy, dx2, dy2, originX, originY;
2167 int len, nChars, uLen, n, m, i, j;
2169 // check for invisible text -- this is used by Acrobat Capture
2170 if ((state->getRender() & 3) == 3) {
2174 // ignore empty strings
2175 if (s->getLength() == 0) {
2180 if (!(font = state->getFont())) {
2183 wMode = font->getWMode();
2185 // check for a subtitute 16-bit font
2187 if (font->isCIDFont()) {
2188 for (i = 0; i < font16EncLen; ++i) {
2189 if (font->getID()->num == font16Enc[i].fontID.num &&
2190 font->getID()->gen == font16Enc[i].fontID.gen) {
2191 uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
2197 // compute width of chars in string, ignoring char spacing and word
2198 // spacing -- the Tj operator will adjust for the metrics of the
2199 // font that's actually used
2202 p = s->getCString();
2203 len = s->getLength();
2204 if (font->isCIDFont()) {
2210 n = font->getNextChar(p, len, &code,
2211 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2212 &dx2, &dy2, &originX, &originY);
2213 if (font->isCIDFont()) {
2215 for (i = 0; i < uLen; ++i) {
2216 m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
2217 for (j = 0; j < m; ++j) {
2221 //~ this really needs to get the number of chars in the target
2222 //~ encoding - which may be more than the number of Unicode
2226 s2->append((char)((code >> 8) & 0xff));
2227 s2->append((char)(code & 0xff));
2236 dx *= state->getFontSize() * state->getHorizScaling();
2237 dy *= state->getFontSize();
2242 if (s2->getLength() > 0) {
2244 if (font->isCIDFont()) {
2246 writePSFmt(" %d %g Tj16V\n", nChars, dy);
2248 writePSFmt(" %d %g Tj16\n", nChars, dx);
2251 writePSFmt(" %g Tj\n", dx);
2254 if (font->isCIDFont()) {
2259 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2260 int width, int height, GBool invert,
2264 len = height * ((width + 7) / 8);
2265 if (level == psLevel1 || level == psLevel1Sep) {
2266 doImageL1(NULL, invert, inlineImg, str, width, height, len);
2268 doImageL2(ref, NULL, invert, inlineImg, str, width, height, len);
2272 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2273 int width, int height, GfxImageColorMap *colorMap,
2274 int *maskColors, GBool inlineImg) {
2277 len = height * ((width * colorMap->getNumPixelComps() *
2278 colorMap->getBits() + 7) / 8);
2281 doImageL1(colorMap, gFalse, inlineImg, str, width, height, len);
2284 //~ handle indexed, separation, ... color spaces
2285 doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
2291 doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len);
2294 t3Cacheable = gFalse;
2297 void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
2298 GBool invert, GBool inlineImg,
2299 Stream *str, int width, int height, int len) {
2300 ImageStream *imgStr;
2301 Guchar pixBuf[gfxColorMaxComps];
2305 // width, height, matrix, bits per component
2307 writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
2309 width, -height, height);
2311 writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
2312 width, height, invert ? "true" : "false",
2313 width, -height, height);
2319 // set up to process the data stream
2320 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
2321 colorMap->getBits());
2324 // process the data stream
2326 for (y = 0; y < height; ++y) {
2329 for (x = 0; x < width; ++x) {
2330 imgStr->getPixel(pixBuf);
2331 colorMap->getGray(pixBuf, &gray);
2332 writePSFmt("%02x", (int)(gray * 255 + 0.5));
2348 for (y = 0; y < height; ++y) {
2349 for (x = 0; x < width; x += 8) {
2350 writePSFmt("%02x", str->getChar() & 0xff);
2364 void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
2365 GBool invert, GBool inlineImg,
2366 Stream *str, int width, int height, int len) {
2367 ImageStream *imgStr;
2369 Guchar pixBuf[gfxColorMaxComps];
2373 // width, height, matrix, bits per component
2374 writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n",
2376 width, -height, height);
2378 // allocate a line buffer
2379 lineBuf = (Guchar *)gmalloc(4 * width);
2381 // set up to process the data stream
2382 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
2383 colorMap->getBits());
2386 // process the data stream
2388 for (y = 0; y < height; ++y) {
2391 for (x = 0; x < width; ++x) {
2392 imgStr->getPixel(pixBuf);
2393 colorMap->getCMYK(pixBuf, &cmyk);
2394 lineBuf[4*x+0] = (int)(255 * cmyk.c + 0.5);
2395 lineBuf[4*x+1] = (int)(255 * cmyk.m + 0.5);
2396 lineBuf[4*x+2] = (int)(255 * cmyk.y + 0.5);
2397 lineBuf[4*x+3] = (int)(255 * cmyk.k + 0.5);
2398 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2401 // write one line of each color component
2402 for (comp = 0; comp < 4; ++comp) {
2403 for (x = 0; x < width; ++x) {
2404 writePSFmt("%02x", lineBuf[4*x + comp]);
2421 void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
2422 GBool invert, GBool inlineImg,
2423 Stream *str, int width, int height, int len) {
2426 GBool useRLE, useASCII, useCompressed;
2427 GfxSeparationColorSpace *sepCS;
2435 dumpColorSpaceL2(colorMap->getColorSpace());
2436 writePS(" setcolorspace\n");
2439 // set up the image data
2440 if (mode == psModeForm || inType3Char) {
2443 str = new FixedLengthEncoder(str, len);
2444 if (globalParams->getPSASCIIHex()) {
2445 str = new ASCIIHexEncoder(str);
2447 str = new ASCII85Encoder(str);
2455 } while (c == '\n' || c == '\r');
2456 if (c == '~' || c == EOF) {
2465 for (i = 1; i <= 4; ++i) {
2468 } while (c == '\n' || c == '\r');
2469 if (c == '~' || c == EOF) {
2476 // each line is: "dup nnnnn <~...data...~> put<eol>"
2477 // so max data length = 255 - 20 = 235
2478 // chunks are 1 or 4 bytes each, so we have to stop at 232
2479 // but make it 225 just to be safe
2483 writePSFmt("<~", line);
2486 } while (c != '~' && c != EOF);
2491 // set up to use the array already created by setupImages()
2492 writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
2497 writePS("<<\n /ImageType 1\n");
2499 // width, height, matrix, bits per component
2500 writePSFmt(" /Width %d\n", width);
2501 writePSFmt(" /Height %d\n", height);
2502 writePSFmt(" /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
2503 writePSFmt(" /BitsPerComponent %d\n",
2504 colorMap ? colorMap->getBits() : 1);
2508 writePS(" /Decode [");
2509 if (colorMap->getColorSpace()->getMode() == csSeparation) {
2510 //~ this is a kludge -- see comment in dumpColorSpaceL2
2511 n = (1 << colorMap->getBits()) - 1;
2512 writePSFmt("%g %g", colorMap->getDecodeLow(0) * n,
2513 colorMap->getDecodeHigh(0) * n);
2515 numComps = colorMap->getNumPixelComps();
2516 for (i = 0; i < numComps; ++i) {
2520 writePSFmt("%g %g", colorMap->getDecodeLow(i),
2521 colorMap->getDecodeHigh(i));
2526 writePSFmt(" /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
2529 if (mode == psModeForm || inType3Char) {
2532 writePS(" /DataSource { 2 copy get exch 1 add exch }\n");
2534 // end of image dictionary
2535 writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask");
2537 // get rid of the array and index
2538 writePS("pop pop\n");
2543 writePS(" /DataSource currentfile\n");
2544 s = str->getPSFilter(" ");
2545 if (inlineImg || !s) {
2548 useCompressed = gFalse;
2551 useASCII = str->isBinary();
2552 useCompressed = gTrue;
2555 writePSFmt(" /ASCII%sDecode filter\n",
2556 globalParams->getPSASCIIHex() ? "Hex" : "85");
2559 writePS(" /RunLengthDecode filter\n");
2561 if (useCompressed) {
2562 writePS(s->getCString());
2568 // cut off inline image streams at appropriate length
2570 str = new FixedLengthEncoder(str, len);
2571 } else if (useCompressed) {
2572 str = str->getBaseStream();
2575 // add RunLengthEncode and ASCIIHex/85 encode filters
2577 str = new RunLengthEncoder(str);
2580 if (globalParams->getPSASCIIHex()) {
2581 str = new ASCIIHexEncoder(str);
2583 str = new ASCII85Encoder(str);
2587 // end of image dictionary
2592 // this can't happen -- OPI dictionaries are in XObjects
2593 error(-1, "Internal: OPI in inline image");
2596 // need to read the stream to count characters -- the length
2597 // is data-dependent (because of ASCII and RLE filters)
2600 while ((c = str->getChar()) != EOF) {
2605 // +6/7 for "pdfIm\n" / "pdfImM\n"
2606 // +8 for newline + trailer
2607 n += colorMap ? 14 : 15;
2608 writePSFmt("%%%%BeginData: %d Hex Bytes\n", n);
2611 if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
2612 colorMap->getColorSpace()->getMode() == csSeparation) {
2614 sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
2615 sepCS->getCMYK(&color, &cmyk);
2616 writePSFmt("%g %g %g %g (%s) pdfImSep\n",
2617 cmyk.c, cmyk.m, cmyk.y, cmyk.k,
2618 sepCS->getName()->getCString());
2620 writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM");
2623 // copy the stream data
2625 while ((c = str->getChar()) != EOF) {
2630 // add newline and trailer to the end
2632 writePS("%-EOD-\n");
2635 writePS("%%EndData\n");
2640 if (useRLE || useASCII || inlineImg) {
2646 void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
2647 GfxCalGrayColorSpace *calGrayCS;
2648 GfxCalRGBColorSpace *calRGBCS;
2649 GfxLabColorSpace *labCS;
2650 GfxIndexedColorSpace *indexedCS;
2651 GfxSeparationColorSpace *separationCS;
2652 GfxColorSpace *baseCS;
2654 double x[gfxColorMaxComps], y[gfxColorMaxComps];
2658 int n, numComps, numAltComps;
2662 switch (colorSpace->getMode()) {
2665 writePS("/DeviceGray");
2666 processColors |= psProcessBlack;
2670 calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
2671 writePS("[/CIEBasedA <<\n");
2672 writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
2673 writePSFmt(" /MatrixA [%g %g %g]\n",
2674 calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
2675 calGrayCS->getWhiteZ());
2676 writePSFmt(" /WhitePoint [%g %g %g]\n",
2677 calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
2678 calGrayCS->getWhiteZ());
2679 writePSFmt(" /BlackPoint [%g %g %g]\n",
2680 calGrayCS->getBlackX(), calGrayCS->getBlackY(),
2681 calGrayCS->getBlackZ());
2683 processColors |= psProcessBlack;
2687 writePS("/DeviceRGB");
2688 processColors |= psProcessCMYK;
2692 calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
2693 writePS("[/CIEBasedABC <<\n");
2694 writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
2695 calRGBCS->getGammaR(), calRGBCS->getGammaG(),
2696 calRGBCS->getGammaB());
2697 writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n",
2698 calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
2699 calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
2700 calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
2701 calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
2702 calRGBCS->getMatrix()[8]);
2703 writePSFmt(" /WhitePoint [%g %g %g]\n",
2704 calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
2705 calRGBCS->getWhiteZ());
2706 writePSFmt(" /BlackPoint [%g %g %g]\n",
2707 calRGBCS->getBlackX(), calRGBCS->getBlackY(),
2708 calRGBCS->getBlackZ());
2710 processColors |= psProcessCMYK;
2714 writePS("/DeviceCMYK");
2715 processColors |= psProcessCMYK;
2719 labCS = (GfxLabColorSpace *)colorSpace;
2720 writePS("[/CIEBasedABC <<\n");
2721 writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n",
2722 labCS->getAMin(), labCS->getAMax(),
2723 labCS->getBMin(), labCS->getBMax());
2724 writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
2725 writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
2726 writePS(" /DecodeLMN\n");
2727 writePS(" [{dup 6 29 div ge {dup dup mul mul}\n");
2728 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
2729 labCS->getWhiteX());
2730 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
2731 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
2732 labCS->getWhiteY());
2733 writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
2734 writePSFmt(" {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
2735 labCS->getWhiteZ());
2736 writePSFmt(" /WhitePoint [%g %g %g]\n",
2737 labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
2738 writePSFmt(" /BlackPoint [%g %g %g]\n",
2739 labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
2741 processColors |= psProcessCMYK;
2745 dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt());
2749 indexedCS = (GfxIndexedColorSpace *)colorSpace;
2750 baseCS = indexedCS->getBase();
2751 writePS("[/Indexed ");
2752 dumpColorSpaceL2(baseCS);
2753 n = indexedCS->getIndexHigh();
2754 numComps = baseCS->getNComps();
2755 lookup = indexedCS->getLookup();
2756 writePSFmt(" %d <\n", n);
2757 if (baseCS->getMode() == csDeviceN) {
2758 func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
2759 numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
2761 for (i = 0; i <= n; i += 8) {
2763 for (j = i; j < i+8 && j <= n; ++j) {
2764 for (k = 0; k < numComps; ++k) {
2765 x[k] = *p++ / 255.0;
2767 func->transform(x, y);
2768 for (k = 0; k < numAltComps; ++k) {
2769 byte = (int)(y[k] * 255 + 0.5);
2772 } else if (byte > 255) {
2775 writePSFmt("%02x", byte);
2778 indexedCS->getCMYK(&color, &cmyk);
2779 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2784 for (i = 0; i <= n; i += 8) {
2786 for (j = i; j < i+8 && j <= n; ++j) {
2787 for (k = 0; k < numComps; ++k) {
2788 writePSFmt("%02x", lookup[j * numComps + k]);
2791 indexedCS->getCMYK(&color, &cmyk);
2792 addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
2801 //~ this is a kludge -- the correct thing would to ouput a
2802 //~ separation color space, with the specified alternate color
2803 //~ space and tint transform
2804 separationCS = (GfxSeparationColorSpace *)colorSpace;
2805 writePS("[/Indexed ");
2806 dumpColorSpaceL2(separationCS->getAlt());
2807 writePS(" 255 <\n");
2808 numComps = separationCS->getAlt()->getNComps();
2809 for (i = 0; i <= 255; i += 8) {
2811 for (j = i; j < i+8 && j <= 255; ++j) {
2812 x[0] = (double)j / 255.0;
2813 separationCS->getFunc()->transform(x, y);
2814 for (k = 0; k < numComps; ++k) {
2815 writePSFmt("%02x", (int)(255 * y[k] + 0.5));
2821 addCustomColor(separationCS);
2825 // DeviceN color spaces are a Level 3 PostScript feature.
2826 dumpColorSpaceL2(((GfxDeviceNColorSpace *)colorSpace)->getAlt());
2837 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
2840 if (globalParams->getPSOPI()) {
2841 opiDict->lookup("2.0", &dict);
2842 if (dict.isDict()) {
2843 opiBegin20(state, dict.getDict());
2847 opiDict->lookup("1.3", &dict);
2848 if (dict.isDict()) {
2849 opiBegin13(state, dict.getDict());
2856 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
2857 Object obj1, obj2, obj3, obj4;
2858 double width, height, left, right, top, bottom;
2862 writePS("%%BeginOPI: 2.0\n");
2863 writePS("%%Distilled\n");
2865 dict->lookup("F", &obj1);
2866 if (getFileSpec(&obj1, &obj2)) {
2867 writePSFmt("%%%%ImageFileName: %s\n",
2868 obj2.getString()->getCString());
2873 dict->lookup("MainImage", &obj1);
2874 if (obj1.isString()) {
2875 writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString());
2879 //~ ignoring 'Tags' entry
2880 //~ need to use writePSString() and deal with >255-char lines
2882 dict->lookup("Size", &obj1);
2883 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
2884 obj1.arrayGet(0, &obj2);
2885 width = obj2.getNum();
2887 obj1.arrayGet(1, &obj2);
2888 height = obj2.getNum();
2890 writePSFmt("%%%%ImageDimensions: %g %g\n", width, height);
2894 dict->lookup("CropRect", &obj1);
2895 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
2896 obj1.arrayGet(0, &obj2);
2897 left = obj2.getNum();
2899 obj1.arrayGet(1, &obj2);
2900 top = obj2.getNum();
2902 obj1.arrayGet(2, &obj2);
2903 right = obj2.getNum();
2905 obj1.arrayGet(3, &obj2);
2906 bottom = obj2.getNum();
2908 writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
2912 dict->lookup("Overprint", &obj1);
2913 if (obj1.isBool()) {
2914 writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
2918 dict->lookup("Inks", &obj1);
2919 if (obj1.isName()) {
2920 writePSFmt("%%%%ImageInks: %s\n", obj1.getName());
2921 } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
2922 obj1.arrayGet(0, &obj2);
2923 if (obj2.isName()) {
2924 writePSFmt("%%%%ImageInks: %s %d",
2925 obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
2926 for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
2927 obj1.arrayGet(i, &obj3);
2928 obj1.arrayGet(i+1, &obj4);
2929 if (obj3.isString() && obj4.isNum()) {
2931 writePSString(obj3.getString());
2932 writePSFmt(" %g", obj4.getNum());
2945 writePS("%%BeginIncludedImage\n");
2947 dict->lookup("IncludedImageDimensions", &obj1);
2948 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
2949 obj1.arrayGet(0, &obj2);
2952 obj1.arrayGet(1, &obj2);
2955 writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h);
2959 dict->lookup("IncludedImageQuality", &obj1);
2961 writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum());
2968 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
2970 int left, right, top, bottom, samples, bits, width, height;
2972 double llx, lly, ulx, uly, urx, ury, lrx, lry;
2973 double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
2978 writePS("/opiMatrix2 matrix currentmatrix def\n");
2979 writePS("opiMatrix setmatrix\n");
2981 dict->lookup("F", &obj1);
2982 if (getFileSpec(&obj1, &obj2)) {
2983 writePSFmt("%%ALDImageFileName: %s\n",
2984 obj2.getString()->getCString());
2989 dict->lookup("CropRect", &obj1);
2990 if (obj1.isArray() && obj1.arrayGetLength() == 4) {
2991 obj1.arrayGet(0, &obj2);
2992 left = obj2.getInt();
2994 obj1.arrayGet(1, &obj2);
2995 top = obj2.getInt();
2997 obj1.arrayGet(2, &obj2);
2998 right = obj2.getInt();
3000 obj1.arrayGet(3, &obj2);
3001 bottom = obj2.getInt();
3003 writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
3007 dict->lookup("Color", &obj1);
3008 if (obj1.isArray() && obj1.arrayGetLength() == 5) {
3009 obj1.arrayGet(0, &obj2);
3012 obj1.arrayGet(1, &obj2);
3015 obj1.arrayGet(2, &obj2);
3018 obj1.arrayGet(3, &obj2);
3021 obj1.arrayGet(4, &obj2);
3022 if (obj2.isString()) {
3023 writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
3024 writePSString(obj2.getString());
3031 dict->lookup("ColorType", &obj1);
3032 if (obj1.isName()) {
3033 writePSFmt("%%ALDImageColorType: %s\n", obj1.getName());
3037 //~ ignores 'Comments' entry
3038 //~ need to handle multiple lines
3040 dict->lookup("CropFixed", &obj1);
3041 if (obj1.isArray()) {
3042 obj1.arrayGet(0, &obj2);
3043 ulx = obj2.getNum();
3045 obj1.arrayGet(1, &obj2);
3046 uly = obj2.getNum();
3048 obj1.arrayGet(2, &obj2);
3049 lrx = obj2.getNum();
3051 obj1.arrayGet(3, &obj2);
3052 lry = obj2.getNum();
3054 writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
3058 dict->lookup("GrayMap", &obj1);
3059 if (obj1.isArray()) {
3060 writePS("%ALDImageGrayMap:");
3061 for (i = 0; i < obj1.arrayGetLength(); i += 16) {
3065 for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
3066 obj1.arrayGet(i+j, &obj2);
3067 writePSFmt(" %d", obj2.getInt());
3075 dict->lookup("ID", &obj1);
3076 if (obj1.isString()) {
3077 writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString());
3081 dict->lookup("ImageType", &obj1);
3082 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3083 obj1.arrayGet(0, &obj2);
3084 samples = obj2.getInt();
3086 obj1.arrayGet(1, &obj2);
3087 bits = obj2.getInt();
3089 writePSFmt("%%ALDImageType: %d %d\n", samples, bits);
3093 dict->lookup("Overprint", &obj1);
3094 if (obj1.isBool()) {
3095 writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
3099 dict->lookup("Position", &obj1);
3100 if (obj1.isArray() && obj1.arrayGetLength() == 8) {
3101 obj1.arrayGet(0, &obj2);
3102 llx = obj2.getNum();
3104 obj1.arrayGet(1, &obj2);
3105 lly = obj2.getNum();
3107 obj1.arrayGet(2, &obj2);
3108 ulx = obj2.getNum();
3110 obj1.arrayGet(3, &obj2);
3111 uly = obj2.getNum();
3113 obj1.arrayGet(4, &obj2);
3114 urx = obj2.getNum();
3116 obj1.arrayGet(5, &obj2);
3117 ury = obj2.getNum();
3119 obj1.arrayGet(6, &obj2);
3120 lrx = obj2.getNum();
3122 obj1.arrayGet(7, &obj2);
3123 lry = obj2.getNum();
3125 opiTransform(state, llx, lly, &tllx, &tlly);
3126 opiTransform(state, ulx, uly, &tulx, &tuly);
3127 opiTransform(state, urx, ury, &turx, &tury);
3128 opiTransform(state, lrx, lry, &tlrx, &tlry);
3129 writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
3130 tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
3135 dict->lookup("Resolution", &obj1);
3136 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3137 obj1.arrayGet(0, &obj2);
3138 horiz = obj2.getNum();
3140 obj1.arrayGet(1, &obj2);
3141 vert = obj2.getNum();
3143 writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert);
3148 dict->lookup("Size", &obj1);
3149 if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3150 obj1.arrayGet(0, &obj2);
3151 width = obj2.getInt();
3153 obj1.arrayGet(1, &obj2);
3154 height = obj2.getInt();
3156 writePSFmt("%%ALDImageDimensions: %d %d\n", width, height);
3160 //~ ignoring 'Tags' entry
3161 //~ need to use writePSString() and deal with >255-char lines
3163 dict->lookup("Tint", &obj1);
3165 writePSFmt("%%ALDImageTint: %g\n", obj1.getNum());
3169 dict->lookup("Transparency", &obj1);
3170 if (obj1.isBool()) {
3171 writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
3175 writePS("%%BeginObject: image\n");
3176 writePS("opiMatrix2 setmatrix\n");
3180 // Convert PDF user space coordinates to PostScript default user space
3181 // coordinates. This has to account for both the PDF CTM and the
3182 // PSOutputDev page-fitting transform.
3183 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
3184 double *x1, double *y1) {
3187 state->transform(x0, y0, x1, y1);
3199 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
3202 if (globalParams->getPSOPI()) {
3203 opiDict->lookup("2.0", &dict);
3204 if (dict.isDict()) {
3205 writePS("%%EndIncludedImage\n");
3206 writePS("%%EndOPI\n");
3207 writePS("grestore\n");
3212 opiDict->lookup("1.3", &dict);
3213 if (dict.isDict()) {
3214 writePS("%%EndObject\n");
3215 writePS("restore\n");
3223 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
3224 if (fileSpec->isString()) {
3225 fileSpec->copy(fileName);
3228 if (fileSpec->isDict()) {
3229 fileSpec->dictLookup("DOS", fileName);
3230 if (fileName->isString()) {
3234 fileSpec->dictLookup("Mac", fileName);
3235 if (fileName->isString()) {
3239 fileSpec->dictLookup("Unix", fileName);
3240 if (fileName->isString()) {
3244 fileSpec->dictLookup("F", fileName);
3245 if (fileName->isString()) {
3252 #endif // OPI_SUPPORT
3254 void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
3255 writePSFmt("%g %g setcharwidth\n", wx, wy);
3259 void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
3260 double llx, double lly, double urx, double ury) {
3267 t3String = new GString();
3269 t3Cacheable = gTrue;
3272 void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
3276 if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
3282 while ((c = str->getChar()) != EOF) {
3288 void PSOutputDev::writePSChar(char c) {
3290 t3String->append(c);
3292 (*outputFunc)(outputStream, &c, 1);
3296 void PSOutputDev::writePS(char *s) {
3298 t3String->append(s);
3300 (*outputFunc)(outputStream, s, strlen(s));
3304 void PSOutputDev::writePSFmt(const char *fmt, ...) {
3308 va_start(args, fmt);
3309 vsprintf(buf, fmt, args);
3312 t3String->append(buf);
3314 (*outputFunc)(outputStream, buf, strlen(buf));
3318 void PSOutputDev::writePSString(GString *s) {
3324 for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
3325 if (*p == '(' || *p == ')' || *p == '\\') {
3327 writePSChar((char)*p);
3328 } else if (*p < 0x20 || *p >= 0x80) {
3329 sprintf(buf, "\\%03o", *p);
3331 t3String->append(buf);
3333 (*outputFunc)(outputStream, buf, strlen(buf));
3336 writePSChar((char)*p);
3342 void PSOutputDev::writePSName(char *s) {
3347 while ((c = *p++)) {
3348 if (c <= (char)0x20 || c >= (char)0x7f ||
3349 c == '(' || c == ')' || c == '<' || c == '>' ||
3350 c == '[' || c == ']' || c == '{' || c == '}' ||
3351 c == '/' || c == '%') {
3352 writePSFmt("#%02x", c & 0xff);
3359 GString *PSOutputDev::filterPSName(GString *name) {
3365 name2 = new GString();
3367 // ghostscript chokes on names that begin with out-of-limits
3368 // numbers, e.g., 1e4foo is handled correctly (as a name), but
3369 // 1e999foo generates a limitcheck error
3370 c = name->getChar(0);
3371 if (c >= '0' && c <= '9') {
3375 for (i = 0; i < name->getLength(); ++i) {
3376 c = name->getChar(i);
3377 if (c <= (char)0x20 || c >= (char)0x7f ||
3378 c == '(' || c == ')' || c == '<' || c == '>' ||
3379 c == '[' || c == ']' || c == '{' || c == '}' ||
3380 c == '/' || c == '%') {
3381 sprintf(buf, "#%02x", c & 0xff);