filters

PSOutputDev.cc

00001 //========================================================================
00002 //
00003 // PSOutputDev.cc
00004 //
00005 // Copyright 1996-2002 Glyph & Cog, LLC
00006 //
00007 //========================================================================
00008 
00009 #include <aconf.h>
00010 
00011 #ifdef USE_GCC_PRAGMAS
00012 #pragma implementation
00013 #endif
00014 
00015 #include <stdio.h>
00016 #include <stddef.h>
00017 #include <stdarg.h>
00018 #include <signal.h>
00019 #include <math.h>
00020 #include "GString.h"
00021 #include "config.h"
00022 #include "GlobalParams.h"
00023 #include "Object.h"
00024 #include "Error.h"
00025 #include "Function.h"
00026 #include "Gfx.h"
00027 #include "GfxState.h"
00028 #include "GfxFont.h"
00029 #include "CharCodeToUnicode.h"
00030 #include "UnicodeMap.h"
00031 #include "FontFile.h"
00032 #include "Catalog.h"
00033 #include "Page.h"
00034 #include "Stream.h"
00035 #include "Annot.h"
00036 #include "PSOutputDev.h"
00037 
00038 #ifdef MACOS
00039 // needed for setting type/creator of MacOS files
00040 #include "ICSupport.h"
00041 #endif
00042 
00043 //------------------------------------------------------------------------
00044 // PostScript prolog and setup
00045 //------------------------------------------------------------------------
00046 
00047 static char *prolog[] = {
00048   "/xpdf 75 dict def xpdf begin",
00049   "% PDF special state",
00050   "/pdfDictSize 14 def",
00051   "/pdfSetup {",
00052   "  3 1 roll 2 array astore",
00053   "  /setpagedevice where {",
00054   "    pop 3 dict begin",
00055   "      /PageSize exch def",
00056   "      /ImagingBBox null def",
00057   "      /Policies 1 dict dup begin /PageSize 3 def end def",
00058   "      { /Duplex true def } if",
00059   "    currentdict end setpagedevice",
00060   "  } {",
00061   "    pop pop",
00062   "  } ifelse",
00063   "} def",
00064   "/pdfStartPage {",
00065   "  pdfDictSize dict begin",
00066   "  /pdfFill [0] def",
00067   "  /pdfStroke [0] def",
00068   "  /pdfLastFill false def",
00069   "  /pdfLastStroke false def",
00070   "  /pdfTextMat [1 0 0 1 0 0] def",
00071   "  /pdfFontSize 0 def",
00072   "  /pdfCharSpacing 0 def",
00073   "  /pdfTextRender 0 def",
00074   "  /pdfTextRise 0 def",
00075   "  /pdfWordSpacing 0 def",
00076   "  /pdfHorizScaling 1 def",
00077   "} def",
00078   "/pdfEndPage { end } def",
00079   "% separation convention operators",
00080   "/findcmykcustomcolor where {",
00081   "  pop",
00082   "}{",
00083   "  /findcmykcustomcolor { 5 array astore } def",
00084   "} ifelse",
00085   "/setcustomcolor where {",
00086   "  pop",
00087   "}{",
00088   "  /setcustomcolor {",
00089   "    exch",
00090   "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
00091   "      0 4 getinterval cvx",
00092   "      [ exch /dup load exch { mul exch dup } /forall load",
00093   "        /pop load dup ] cvx",
00094   "    ] setcolorspace setcolor",
00095   "  } def",
00096   "} ifelse",
00097   "/customcolorimage where {",
00098   "  pop",
00099   "}{",
00100   "  /customcolorimage {",
00101   "    gsave",
00102   "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
00103   "      0 4 getinterval cvx",
00104   "      [ exch /dup load exch { mul exch dup } /forall load",
00105   "        /pop load dup ] cvx",
00106   "    ] setcolorspace",
00107   "    10 dict begin",
00108   "      /ImageType 1 def",
00109   "      /DataSource exch def",
00110   "      /ImageMatrix exch def",
00111   "      /BitsPerComponent exch def",
00112   "      /Height exch def",
00113   "      /Width exch def",
00114   "      /Decode [1 0] def",
00115   "    currentdict end",
00116   "    image",
00117   "    grestore",
00118   "  } def",
00119   "} ifelse",
00120   "% PDF color state",
00121   "/sCol {",
00122   "  pdfLastStroke not {",
00123   "    pdfStroke aload length",
00124   "    dup 1 eq {",
00125   "      pop setgray",
00126   "    }{",
00127   "      dup 3 eq {",
00128   "        pop setrgbcolor",
00129   "      }{",
00130   "        4 eq {",
00131   "          setcmykcolor",
00132   "        }{",
00133   "          findcmykcustomcolor exch setcustomcolor",
00134   "        } ifelse",
00135   "      } ifelse",
00136   "    } ifelse",
00137   "    /pdfLastStroke true def /pdfLastFill false def",
00138   "  } if",
00139   "} def",
00140   "/fCol {",
00141   "  pdfLastFill not {",
00142   "    pdfFill aload length",
00143   "    dup 1 eq {",
00144   "      pop setgray",
00145   "    }{",
00146   "      dup 3 eq {",
00147   "        pop setrgbcolor",
00148   "      }{",
00149   "        4 eq {",
00150   "          setcmykcolor",
00151   "        }{",
00152   "          findcmykcustomcolor exch setcustomcolor",
00153   "        } ifelse",
00154   "      } ifelse",
00155   "    } ifelse",
00156   "    /pdfLastFill true def /pdfLastStroke false def",
00157   "  } if",
00158   "} def",
00159   "% build a font",
00160   "/pdfMakeFont {",
00161   "  4 3 roll findfont",
00162   "  4 2 roll matrix scale makefont",
00163   "  dup length dict begin",
00164   "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
00165   "    /Encoding exch def",
00166   "    currentdict",
00167   "  end",
00168   "  definefont pop",
00169   "} def",
00170   "/pdfMakeFont16 {",
00171   "  exch findfont",
00172   "  dup length dict begin",
00173   "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
00174   "    /WMode exch def",
00175   "    currentdict",
00176   "  end",
00177   "  definefont pop",
00178   "} def",
00179   "/pdfMakeFont16L3 {",
00180   "  1 index /CIDFont resourcestatus {",
00181   "    pop pop 1 index /CIDFont findresource /CIDFontType known",
00182   "  } {",
00183   "    false",
00184   "  } ifelse",
00185   "  {",
00186   "    0 eq { /Identity-H } { /Identity-V } ifelse",
00187   "    exch 1 array astore composefont pop",
00188   "  } {",
00189   "    pdfMakeFont16",
00190   "  } ifelse",
00191   "} def",
00192   "% graphics state operators",
00193   "/q { gsave pdfDictSize dict begin } def",
00194   "/Q { end grestore } def",
00195   "/cm { concat } def",
00196   "/d { setdash } def",
00197   "/i { setflat } def",
00198   "/j { setlinejoin } def",
00199   "/J { setlinecap } def",
00200   "/M { setmiterlimit } def",
00201   "/w { setlinewidth } def",
00202   "% color operators",
00203   "/g { dup 1 array astore /pdfFill exch def setgray",
00204   "     /pdfLastFill true def /pdfLastStroke false def } def",
00205   "/G { dup 1 array astore /pdfStroke exch def setgray",
00206   "     /pdfLastStroke true def /pdfLastFill false def } def",
00207   "/rg { 3 copy 3 array astore /pdfFill exch def setrgbcolor",
00208   "      /pdfLastFill true def /pdfLastStroke false def } def",
00209   "/RG { 3 copy 3 array astore /pdfStroke exch def setrgbcolor",
00210   "      /pdfLastStroke true def /pdfLastFill false def } def",
00211   "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
00212   "     /pdfLastFill true def /pdfLastStroke false def } def",
00213   "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
00214   "     /pdfLastStroke true def /pdfLastFill false def } def",
00215   "/ck { 6 copy 6 array astore /pdfFill exch def",
00216   "      findcmykcustomcolor exch setcustomcolor",
00217   "      /pdfLastFill true def /pdfLastStroke false def } def",
00218   "/CK { 6 copy 6 array astore /pdfStroke exch def",
00219   "      findcmykcustomcolor exch setcustomcolor",
00220   "      /pdfLastStroke true def /pdfLastFill false def } def",
00221   "% path segment operators",
00222   "/m { moveto } def",
00223   "/l { lineto } def",
00224   "/c { curveto } def",
00225   "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
00226   "      neg 0 rlineto closepath } def",
00227   "/h { closepath } def",
00228   "% path painting operators",
00229   "/S { sCol stroke } def",
00230   "/Sf { fCol stroke } def",
00231   "/f { fCol fill } def",
00232   "/f* { fCol eofill } def",
00233   "% clipping operators",
00234   "/W { clip newpath } def",
00235   "/W* { eoclip newpath } def",
00236   "% text state operators",
00237   "/Tc { /pdfCharSpacing exch def } def",
00238   "/Tf { dup /pdfFontSize exch def",
00239   "      dup pdfHorizScaling mul exch matrix scale",
00240   "      pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
00241   "      exch findfont exch makefont setfont } def",
00242   "/Tr { /pdfTextRender exch def } def",
00243   "/Ts { /pdfTextRise exch def } def",
00244   "/Tw { /pdfWordSpacing exch def } def",
00245   "/Tz { /pdfHorizScaling exch def } def",
00246   "% text positioning operators",
00247   "/Td { pdfTextMat transform moveto } def",
00248   "/Tm { /pdfTextMat exch def } def",
00249   "% text string operators",
00250   "/awcp { % awidthcharpath",
00251   "  exch {",
00252   "    1 string dup 0 3 index put 2 index charpath",
00253   "    3 index 3 index rmoveto",
00254   "    4 index eq { 5 index 5 index rmoveto } if",
00255   "  } forall",
00256   "  6 {pop} repeat",
00257   "} def",
00258   "/Tj { fCol",  // because stringwidth has to draw Type 3 chars
00259   "      0 pdfTextRise pdfTextMat dtransform rmoveto",
00260   "      1 index stringwidth pdfTextMat idtransform pop",
00261   "      sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
00262   "      pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
00263   "      4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
00264   "      pdfTextMat dtransform",
00265   "      6 5 roll",
00266   "      currentpoint 8 2 roll",
00267   "      pdfTextRender 1 and 0 eq {",
00268   "        6 copy awidthshow",
00269   "      } if",
00270   "      pdfTextRender 3 and dup 1 eq exch 2 eq or {",
00271   "        8 6 roll moveto",
00272   "        currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
00273   "        false awcp currentpoint stroke moveto",
00274   "      } {",
00275   "        8 {pop} repeat",
00276   "      } ifelse",
00277   "      0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
00278   "/Tj16 { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
00279   "        0 pdfTextRise pdfTextMat dtransform rmoveto",
00280   "        2 index stringwidth pdfTextMat idtransform pop",
00281   "        sub exch div",
00282   "        pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
00283   "        4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
00284   "        pdfTextMat dtransform",
00285   "        6 5 roll awidthshow",
00286   "        0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
00287   "/Tj16V { pdfTextRender 1 and 0 eq { fCol } { sCol } ifelse",
00288   "         0 pdfTextRise pdfTextMat dtransform rmoveto",
00289   "         2 index stringwidth pdfTextMat idtransform exch pop",
00290   "         sub exch div",
00291   "         0 pdfWordSpacing pdfTextMat dtransform 32",
00292   "         4 3 roll pdfCharSpacing add 0 exch",
00293   "         pdfTextMat dtransform",
00294   "         6 5 roll awidthshow",
00295   "         0 pdfTextRise neg pdfTextMat dtransform rmoveto } def",
00296   "/TJm { pdfFontSize 0.001 mul mul neg 0",
00297   "       pdfTextMat dtransform rmoveto } def",
00298   "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
00299   "        pdfTextMat dtransform rmoveto } def",
00300   "% Level 1 image operators",
00301   "/pdfIm1 {",
00302   "  /pdfImBuf1 4 index string def",
00303   "  { currentfile pdfImBuf1 readhexstring pop } image",
00304   "} def",
00305   "/pdfIm1Sep {",
00306   "  /pdfImBuf1 4 index string def",
00307   "  /pdfImBuf2 4 index string def",
00308   "  /pdfImBuf3 4 index string def",
00309   "  /pdfImBuf4 4 index string def",
00310   "  { currentfile pdfImBuf1 readhexstring pop }",
00311   "  { currentfile pdfImBuf2 readhexstring pop }",
00312   "  { currentfile pdfImBuf3 readhexstring pop }",
00313   "  { currentfile pdfImBuf4 readhexstring pop }",
00314   "  true 4 colorimage",
00315   "} def",
00316   "/pdfImM1 {",
00317   "  /pdfImBuf1 4 index 7 add 8 idiv string def",
00318   "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
00319   "} def",
00320   "% Level 2 image operators",
00321   "/pdfImBuf 100 string def",
00322   "/pdfIm {",
00323   "  image",
00324   "  { currentfile pdfImBuf readline",
00325   "    not { pop exit } if",
00326   "    (%-EOD-) eq { exit } if } loop",
00327   "} def",
00328   "/pdfImSep {",
00329   "  findcmykcustomcolor exch",
00330   "  dup /Width get /pdfImBuf1 exch string def",
00331   "  begin Width Height BitsPerComponent ImageMatrix DataSource end",
00332   "  /pdfImData exch def",
00333   "  { pdfImData pdfImBuf1 readstring pop",
00334   "    0 1 2 index length 1 sub {",
00335   "      1 index exch 2 copy get 255 exch sub put",
00336   "    } for }",
00337   "  6 5 roll customcolorimage",
00338   "  { currentfile pdfImBuf readline",
00339   "    not { pop exit } if",
00340   "    (%-EOD-) eq { exit } if } loop",
00341   "} def",
00342   "/pdfImM {",
00343   "  fCol imagemask",
00344   "  { currentfile pdfImBuf readline",
00345   "    not { pop exit } if",
00346   "    (%-EOD-) eq { exit } if } loop",
00347   "} def",
00348   "end",
00349   NULL
00350 };
00351 
00352 static char *cmapProlog[] = {
00353   "/CIDInit /ProcSet findresource begin",
00354   "10 dict begin",
00355   "  begincmap",
00356   "  /CMapType 1 def",
00357   "  /CMapName /Identity-H def",
00358   "  /CIDSystemInfo 3 dict dup begin",
00359   "    /Registry (Adobe) def",
00360   "    /Ordering (Identity) def",
00361   "    /Supplement 0 def",
00362   "  end def",
00363   "  1 begincodespacerange",
00364   "    <0000> <ffff>",
00365   "  endcodespacerange",
00366   "  0 usefont",
00367   "  1 begincidrange",
00368   "    <0000> <ffff> 0",
00369   "  endcidrange",
00370   "  endcmap",
00371   "  currentdict CMapName exch /CMap defineresource pop",
00372   "end",
00373   "10 dict begin",
00374   "  begincmap",
00375   "  /CMapType 1 def",
00376   "  /CMapName /Identity-V def",
00377   "  /CIDSystemInfo 3 dict dup begin",
00378   "    /Registry (Adobe) def",
00379   "    /Ordering (Identity) def",
00380   "    /Supplement 0 def",
00381   "  end def",
00382   "  /WMode 1 def",
00383   "  1 begincodespacerange",
00384   "    <0000> <ffff>",
00385   "  endcodespacerange",
00386   "  0 usefont",
00387   "  1 begincidrange",
00388   "    <0000> <ffff> 0",
00389   "  endcidrange",
00390   "  endcmap",
00391   "  currentdict CMapName exch /CMap defineresource pop",
00392   "end",
00393   "end",
00394   NULL
00395 };
00396 
00397 //------------------------------------------------------------------------
00398 // Fonts
00399 //------------------------------------------------------------------------
00400 
00401 struct PSSubstFont {
00402   char *psName;         // PostScript name
00403   double mWidth;        // width of 'm' character
00404 };
00405 
00406 static char *psFonts[] = {
00407   "Courier",
00408   "Courier-Bold",
00409   "Courier-Oblique",
00410   "Courier-BoldOblique",
00411   "Helvetica",
00412   "Helvetica-Bold",
00413   "Helvetica-Oblique",
00414   "Helvetica-BoldOblique",
00415   "Symbol",
00416   "Times-Roman",
00417   "Times-Bold",
00418   "Times-Italic",
00419   "Times-BoldItalic",
00420   "ZapfDingbats",
00421   NULL
00422 };
00423 
00424 static PSSubstFont psSubstFonts[] = {
00425   {"Helvetica",             0.833},
00426   {"Helvetica-Oblique",     0.833},
00427   {"Helvetica-Bold",        0.889},
00428   {"Helvetica-BoldOblique", 0.889},
00429   {"Times-Roman",           0.788},
00430   {"Times-Italic",          0.722},
00431   {"Times-Bold",            0.833},
00432   {"Times-BoldItalic",      0.778},
00433   {"Courier",               0.600},
00434   {"Courier-Oblique",       0.600},
00435   {"Courier-Bold",          0.600},
00436   {"Courier-BoldOblique",   0.600}
00437 };
00438 
00439 // Encoding info for substitute 16-bit font
00440 struct PSFont16Enc {
00441   Ref fontID;
00442   GString *enc;
00443 };
00444 
00445 //------------------------------------------------------------------------
00446 // process colors
00447 //------------------------------------------------------------------------
00448 
00449 #define psProcessCyan     1
00450 #define psProcessMagenta  2
00451 #define psProcessYellow   4
00452 #define psProcessBlack    8
00453 #define psProcessCMYK    15
00454 
00455 //------------------------------------------------------------------------
00456 // PSOutCustomColor
00457 //------------------------------------------------------------------------
00458 
00459 class PSOutCustomColor {
00460 public:
00461 
00462   PSOutCustomColor(double cA, double mA,
00463            double yA, double kA, GString *nameA);
00464   ~PSOutCustomColor();
00465 
00466   double c, m, y, k;
00467   GString *name;
00468   PSOutCustomColor *next;
00469 };
00470 
00471 PSOutCustomColor::PSOutCustomColor(double cA, double mA,
00472                    double yA, double kA, GString *nameA) {
00473   c = cA;
00474   m = mA;
00475   y = yA;
00476   k = kA;
00477   name = nameA;
00478   next = NULL;
00479 }
00480 
00481 PSOutCustomColor::~PSOutCustomColor() {
00482   delete name;
00483 }
00484 
00485 //------------------------------------------------------------------------
00486 // PSOutputDev
00487 //------------------------------------------------------------------------
00488 
00489 extern "C" {
00490 typedef void (*SignalFunc)(int);
00491 }
00492 
00493 static void outputToFile(void *stream, char *data, int len) {
00494   fwrite(data, 1, len, (FILE *)stream);
00495 }
00496 
00497 PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
00498              int firstPage, int lastPage, PSOutMode modeA) {
00499   FILE *f;
00500   PSFileType fileTypeA;
00501 
00502   fontIDs = NULL;
00503   fontFileIDs = NULL;
00504   fontFileNames = NULL;
00505   font16Enc = NULL;
00506   embFontList = NULL;
00507   customColors = NULL;
00508   t3String = NULL;
00509 
00510   // open file or pipe
00511   if (!strcmp(fileName, "-")) {
00512     fileTypeA = psStdout;
00513     f = stdout;
00514   } else if (fileName[0] == '|') {
00515     fileTypeA = psPipe;
00516 #ifdef HAVE_POPEN
00517 #ifndef WIN32
00518     signal(SIGPIPE, (SignalFunc)SIG_IGN);
00519 #endif
00520     if (!(f = popen(fileName + 1, "w"))) {
00521       error(-1, "Couldn't run print command '%s'", fileName);
00522       ok = gFalse;
00523       return;
00524     }
00525 #else
00526     error(-1, "Print commands are not supported ('%s')", fileName);
00527     ok = gFalse;
00528     return;
00529 #endif
00530   } else {
00531     fileTypeA = psFile;
00532     if (!(f = fopen(fileName, "w"))) {
00533       error(-1, "Couldn't open PostScript file '%s'", fileName);
00534       ok = gFalse;
00535       return;
00536     }
00537   }
00538 
00539   init(outputToFile, f, fileTypeA,
00540        xrefA, catalog, firstPage, lastPage, modeA);
00541 }
00542 
00543 PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
00544              XRef *xrefA, Catalog *catalog,
00545              int firstPage, int lastPage, PSOutMode modeA) {
00546   fontIDs = NULL;
00547   fontFileIDs = NULL;
00548   fontFileNames = NULL;
00549   font16Enc = NULL;
00550   embFontList = NULL;
00551   customColors = NULL;
00552   t3String = NULL;
00553 
00554   init(outputFuncA, outputStreamA, psGeneric,
00555        xrefA, catalog, firstPage, lastPage, modeA);
00556 }
00557 
00558 void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
00559                PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
00560                int firstPage, int lastPage, PSOutMode modeA) {
00561   Page *page;
00562   PDFRectangle *box;
00563   Dict *resDict;
00564   Annots *annots;
00565   char **p;
00566   int pg;
00567   Object obj1, obj2;
00568   int i;
00569 
00570   // initialize
00571   ok = gTrue;
00572   outputFunc = outputFuncA;
00573   outputStream = outputStreamA;
00574   fileType = fileTypeA;
00575   xref = xrefA;
00576   level = globalParams->getPSLevel();
00577   mode = modeA;
00578   paperWidth = globalParams->getPSPaperWidth();
00579   paperHeight = globalParams->getPSPaperHeight();
00580   if (mode == psModeForm) {
00581     lastPage = firstPage;
00582   }
00583   processColors = 0;
00584   inType3Char = gFalse;
00585 
00586 #if OPI_SUPPORT
00587   // initialize OPI nesting levels
00588   opi13Nest = 0;
00589   opi20Nest = 0;
00590 #endif
00591 
00592   // initialize fontIDs, fontFileIDs, and fontFileNames lists
00593   fontIDSize = 64;
00594   fontIDLen = 0;
00595   fontIDs = (Ref *)gmalloc(fontIDSize * sizeof(Ref));
00596   fontFileIDSize = 64;
00597   fontFileIDLen = 0;
00598   fontFileIDs = (Ref *)gmalloc(fontFileIDSize * sizeof(Ref));
00599   fontFileNameSize = 64;
00600   fontFileNameLen = 0;
00601   fontFileNames = (GString **)gmalloc(fontFileNameSize * sizeof(GString *));
00602   font16EncLen = 0;
00603   font16EncSize = 0;
00604 
00605   // initialize embedded font resource comment list
00606   embFontList = new GString();
00607 
00608   // write header
00609   switch (mode) {
00610   case psModePS:
00611     writePS("%!PS-Adobe-3.0\n");
00612     writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
00613     writePSFmt("%%%%LanguageLevel: %d\n",
00614            (level == psLevel1 || level == psLevel1Sep) ? 1 :
00615            (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
00616     if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
00617       writePS("%%DocumentProcessColors: (atend)\n");
00618       writePS("%%DocumentCustomColors: (atend)\n");
00619     }
00620     writePS("%%DocumentSuppliedResources: (atend)\n");
00621     writePSFmt("%%%%DocumentMedia: plain %d %d 0 () ()\n",
00622            paperWidth, paperHeight);
00623     writePSFmt("%%%%Pages: %d\n", lastPage - firstPage + 1);
00624     writePS("%%EndComments\n");
00625     writePS("%%BeginDefaults\n");
00626     writePS("%%PageMedia: plain\n");
00627     writePS("%%EndDefaults\n");
00628     break;
00629   case psModeEPS:
00630     writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
00631     writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
00632     writePSFmt("%%%%LanguageLevel: %d\n",
00633            (level == psLevel1 || level == psLevel1Sep) ? 1 :
00634            (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
00635     if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
00636       writePS("%%DocumentProcessColors: (atend)\n");
00637       writePS("%%DocumentCustomColors: (atend)\n");
00638     }
00639     page = catalog->getPage(firstPage);
00640     box = page->getBox();
00641     writePSFmt("%%%%BoundingBox: %d %d %d %d\n",
00642            (int)floor(box->x1), (int)floor(box->y1),
00643            (int)ceil(box->x2), (int)ceil(box->y2));
00644     if (floor(box->x1) != ceil(box->x1) ||
00645     floor(box->y1) != ceil(box->y1) ||
00646     floor(box->x2) != ceil(box->x2) ||
00647     floor(box->y2) != ceil(box->y2)) {
00648       writePSFmt("%%%%HiResBoundingBox: %g %g %g %g\n",
00649          box->x1, box->y1, box->x2, box->y2);
00650     }
00651     writePS("%%DocumentSuppliedResources: (atend)\n");
00652     writePS("%%EndComments\n");
00653     break;
00654   case psModeForm:
00655     writePS("%!PS-Adobe-3.0 Resource-Form\n");
00656     writePSFmt("%%%%Creator: xpdf/pdftops %s\n", xpdfVersion);
00657     writePSFmt("%%%%LanguageLevel: %d\n",
00658            (level == psLevel1 || level == psLevel1Sep) ? 1 :
00659            (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
00660     if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
00661       writePS("%%DocumentProcessColors: (atend)\n");
00662       writePS("%%DocumentCustomColors: (atend)\n");
00663     }
00664     writePS("%%DocumentSuppliedResources: (atend)\n");
00665     writePS("%%EndComments\n");
00666     page = catalog->getPage(firstPage);
00667     box = page->getBox();
00668     writePS("32 dict dup begin\n");
00669     writePSFmt("/BBox [%d %d %d %d] def\n",
00670            (int)box->x1, (int)box->y1, (int)box->x2, (int)box->y2);
00671     writePS("/FormType 1 def\n");
00672     writePS("/Matrix [1 0 0 1 0 0] def\n");
00673     break;
00674   }
00675 
00676   // write prolog
00677   if (mode != psModeForm) {
00678     writePS("%%BeginProlog\n");
00679   }
00680   writePSFmt("%%%%BeginResource: procset xpdf %s 0\n", xpdfVersion);
00681   for (p = prolog; *p; ++p) {
00682     writePSFmt("%s\n", *p);
00683   }
00684   writePS("%%EndResource\n");
00685   if (level >= psLevel3) {
00686     for (p = cmapProlog; *p; ++p) {
00687       writePSFmt("%s\n", *p);
00688     }
00689   }
00690   if (mode != psModeForm) {
00691     writePS("%%EndProlog\n");
00692   }
00693 
00694   // set up fonts and images
00695   if (mode == psModeForm) {
00696     // swap the form and xpdf dicts
00697     writePS("xpdf end begin dup begin\n");
00698   } else {
00699     writePS("%%BeginSetup\n");
00700     writePS("xpdf begin\n");
00701   }
00702   for (pg = firstPage; pg <= lastPage; ++pg) {
00703     page = catalog->getPage(pg);
00704     if ((resDict = page->getResourceDict())) {
00705       setupResources(resDict);
00706     }
00707     annots = new Annots(xref, page->getAnnots(&obj1));
00708     obj1.free();
00709     for (i = 0; i < annots->getNumAnnots(); ++i) {
00710       if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
00711     obj1.streamGetDict()->lookup("Resources", &obj2);
00712     if (obj2.isDict()) {
00713       setupResources(obj2.getDict());
00714     }
00715     obj2.free();
00716       }
00717       obj1.free();
00718     }
00719     delete annots;
00720   }
00721   if (mode != psModeForm) {
00722     if (mode != psModeEPS) {
00723       writePSFmt("%d %d %s pdfSetup\n",
00724          paperWidth, paperHeight,
00725          globalParams->getPSDuplex() ? "true" : "false");
00726     }
00727 #if OPI_SUPPORT
00728     if (globalParams->getPSOPI()) {
00729       writePS("/opiMatrix matrix currentmatrix def\n");
00730     }
00731 #endif
00732     writePS("%%EndSetup\n");
00733   }
00734 
00735   // initialize sequential page number
00736   seqPage = 1;
00737 }
00738 
00739 PSOutputDev::~PSOutputDev() {
00740   PSOutCustomColor *cc;
00741   int i;
00742 
00743   if (ok) {
00744     if (mode == psModeForm) {
00745       writePS("/Foo exch /Form defineresource pop\n");
00746     } else {
00747       writePS("%%Trailer\n");
00748       writePS("end\n");
00749       writePS("%%DocumentSuppliedResources:\n");
00750       writePS(embFontList->getCString());
00751       if (level == psLevel1Sep || level == psLevel2Sep ||
00752       level == psLevel3Sep) {
00753          writePS("%%DocumentProcessColors:");
00754          if (processColors & psProcessCyan) {
00755        writePS(" Cyan");
00756      }
00757          if (processColors & psProcessMagenta) {
00758        writePS(" Magenta");
00759      }
00760          if (processColors & psProcessYellow) {
00761        writePS(" Yellow");
00762      }
00763          if (processColors & psProcessBlack) {
00764        writePS(" Black");
00765      }
00766          writePS("\n");
00767          writePS("%%DocumentCustomColors:");
00768      for (cc = customColors; cc; cc = cc->next) {
00769        writePSFmt(" (%s)", cc->name->getCString());
00770      }
00771          writePS("\n");
00772          writePS("%%CMYKCustomColor:\n");
00773      for (cc = customColors; cc; cc = cc->next) {
00774        writePSFmt("%%%%+ %g %g %g %g (%s)\n",
00775            cc->c, cc->m, cc->y, cc->k, cc->name->getCString());
00776      }
00777       }
00778       writePS("%%EOF\n");
00779     }
00780     if (fileType == psFile) {
00781 #ifdef MACOS
00782       ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
00783 #endif
00784       fclose((FILE *)outputStream);
00785     }
00786 #ifdef HAVE_POPEN
00787     else if (fileType == psPipe) {
00788       pclose((FILE *)outputStream);
00789 #ifndef WIN32
00790       signal(SIGPIPE, (SignalFunc)SIG_DFL);
00791 #endif
00792     }
00793 #endif
00794   }
00795   if (embFontList) {
00796     delete embFontList;
00797   }
00798   if (fontIDs) {
00799     gfree(fontIDs);
00800   }
00801   if (fontFileIDs) {
00802     gfree(fontFileIDs);
00803   }
00804   if (fontFileNames) {
00805     for (i = 0; i < fontFileNameLen; ++i) {
00806       delete fontFileNames[i];
00807     }
00808     gfree(fontFileNames);
00809   }
00810   if (font16Enc) {
00811     for (i = 0; i < font16EncLen; ++i) {
00812       delete font16Enc[i].enc;
00813     }
00814     gfree(font16Enc);
00815   }
00816   while (customColors) {
00817     cc = customColors;
00818     customColors = cc->next;
00819     delete cc;
00820   }
00821 }
00822 
00823 void PSOutputDev::setupResources(Dict *resDict) {
00824   Object xObjDict, xObj, resObj;
00825   int i;
00826 
00827   setupFonts(resDict);
00828   setupImages(resDict);
00829 
00830   resDict->lookup("XObject", &xObjDict);
00831   if (xObjDict.isDict()) {
00832     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
00833       xObjDict.dictGetVal(i, &xObj);
00834       if (xObj.isStream()) {
00835     xObj.streamGetDict()->lookup("Resources", &resObj);
00836     if (resObj.isDict()) {
00837       setupResources(resObj.getDict());
00838     }
00839     resObj.free();
00840       }
00841       xObj.free();
00842     }
00843   }
00844   xObjDict.free();
00845 }
00846 
00847 void PSOutputDev::setupFonts(Dict *resDict) {
00848   Object fontDict;
00849   GfxFontDict *gfxFontDict;
00850   GfxFont *font;
00851   int i;
00852 
00853   resDict->lookup("Font", &fontDict);
00854   if (fontDict.isDict()) {
00855     gfxFontDict = new GfxFontDict(xref, fontDict.getDict());
00856     for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
00857       font = gfxFontDict->getFont(i);
00858       setupFont(font, resDict);
00859     }
00860     delete gfxFontDict;
00861   }
00862   fontDict.free();
00863 }
00864 
00865 void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
00866   Ref fontFileID;
00867   GString *name;
00868   PSFontParam *fontParam;
00869   GString *psNameStr;
00870   char *psName;
00871   char type3Name[64], buf[16];
00872   UnicodeMap *uMap;
00873   char *charName;
00874   double xs, ys;
00875   int code;
00876   double w1, w2;
00877   double *fm;
00878   int i, j;
00879 
00880   // check if font is already set up
00881   for (i = 0; i < fontIDLen; ++i) {
00882     if (fontIDs[i].num == font->getID()->num &&
00883     fontIDs[i].gen == font->getID()->gen) {
00884       return;
00885     }
00886   }
00887 
00888   // add entry to fontIDs list
00889   if (fontIDLen >= fontIDSize) {
00890     fontIDSize += 64;
00891     fontIDs = (Ref *)grealloc(fontIDs, fontIDSize * sizeof(Ref));
00892   }
00893   fontIDs[fontIDLen++] = *font->getID();
00894 
00895   xs = ys = 1;
00896   psNameStr = NULL;
00897 
00898   // check for resident 8-bit font
00899   if (font->getName() &&
00900       (fontParam = globalParams->getPSFont(font->getName()))) {
00901     psName = fontParam->psFontName->getCString();
00902 
00903   // check for embedded Type 1 font
00904   } else if (globalParams->getPSEmbedType1() &&
00905          font->getType() == fontType1 &&
00906          font->getEmbeddedFontID(&fontFileID)) {
00907     psNameStr = filterPSName(font->getEmbeddedFontName());
00908     psName = psNameStr->getCString();
00909     setupEmbeddedType1Font(&fontFileID, psName);
00910 
00911   // check for embedded Type 1C font
00912   } else if (globalParams->getPSEmbedType1() &&
00913          font->getType() == fontType1C &&
00914          font->getEmbeddedFontID(&fontFileID)) {
00915     psNameStr = filterPSName(font->getEmbeddedFontName());
00916     psName = psNameStr->getCString();
00917     setupEmbeddedType1CFont(font, &fontFileID, psName);
00918 
00919   // check for external Type 1 font file
00920   } else if (globalParams->getPSEmbedType1() &&
00921          font->getType() == fontType1 &&
00922          font->getExtFontFile()) {
00923     // this assumes that the PS font name matches the PDF font name
00924     psName = font->getName()->getCString();
00925     setupExternalType1Font(font->getExtFontFile(), psName);
00926 
00927   // check for embedded TrueType font
00928   } else if (globalParams->getPSEmbedTrueType() &&
00929          font->getType() == fontTrueType &&
00930          font->getEmbeddedFontID(&fontFileID)) {
00931     psNameStr = filterPSName(font->getEmbeddedFontName());
00932     psName = psNameStr->getCString();
00933     setupEmbeddedTrueTypeFont(font, &fontFileID, psName);
00934 
00935   // check for external TrueType font file
00936   } else if (globalParams->getPSEmbedTrueType() &&
00937          font->getType() == fontTrueType &&
00938          font->getExtFontFile()) {
00939     psNameStr = filterPSName(font->getName());
00940     psName = psNameStr->getCString();
00941     setupExternalTrueTypeFont(font, psName);
00942 
00943   // check for embedded CID PostScript font
00944   } else if (globalParams->getPSEmbedCIDPostScript() &&
00945          font->getType() == fontCIDType0C &&
00946          font->getEmbeddedFontID(&fontFileID)) {
00947     psNameStr = filterPSName(font->getEmbeddedFontName());
00948     psName = psNameStr->getCString();
00949     setupEmbeddedCIDType0Font(font, &fontFileID, psName);
00950 
00951   // check for embedded CID TrueType font
00952   } else if (globalParams->getPSEmbedCIDTrueType() &&
00953          font->getType() == fontCIDType2 &&
00954          font->getEmbeddedFontID(&fontFileID)) {
00955     psNameStr = filterPSName(font->getEmbeddedFontName());
00956     psName = psNameStr->getCString();
00957     setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName);
00958 
00959   } else if (font->getType() == fontType3) {
00960     sprintf(type3Name, "T3_%d_%d",
00961         font->getID()->num, font->getID()->gen);
00962     psName = type3Name;
00963     setupType3Font(font, psName, parentResDict);
00964 
00965   // do 8-bit font substitution
00966   } else if (!font->isCIDFont()) {
00967     name = font->getName();
00968     psName = NULL;
00969     if (name) {
00970       for (i = 0; psFonts[i]; ++i) {
00971     if (name->cmp(psFonts[i]) == 0) {
00972       psName = psFonts[i];
00973       break;
00974     }
00975       }
00976     }
00977     if (!psName) {
00978       if (font->isFixedWidth()) {
00979     i = 8;
00980       } else if (font->isSerif()) {
00981     i = 4;
00982       } else {
00983     i = 0;
00984       }
00985       if (font->isBold()) {
00986     i += 2;
00987       }
00988       if (font->isItalic()) {
00989     i += 1;
00990       }
00991       psName = psSubstFonts[i].psName;
00992       for (code = 0; code < 256; ++code) {
00993     if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
00994         charName[0] == 'm' && charName[1] == '\0') {
00995       break;
00996     }
00997       }
00998       if (code < 256) {
00999     w1 = ((Gfx8BitFont *)font)->getWidth(code);
01000       } else {
01001     w1 = 0;
01002       }
01003       w2 = psSubstFonts[i].mWidth;
01004       xs = w1 / w2;
01005       if (xs < 0.1) {
01006     xs = 1;
01007       }
01008       if (font->getType() == fontType3) {
01009     // This is a hack which makes it possible to substitute for some
01010     // Type 3 fonts.  The problem is that it's impossible to know what
01011     // the base coordinate system used in the font is without actually
01012     // rendering the font.
01013     ys = xs;
01014     fm = font->getFontMatrix();
01015     if (fm[0] != 0) {
01016       ys *= fm[3] / fm[0];
01017     }
01018       } else {
01019     ys = 1;
01020       }
01021     }
01022 
01023   // do 16-bit font substitution
01024   } else if ((fontParam = globalParams->
01025             getPSFont16(font->getName(),
01026                 ((GfxCIDFont *)font)->getCollection(),
01027                 font->getWMode()))) {
01028     psName = fontParam->psFontName->getCString();
01029     if (font16EncLen >= font16EncSize) {
01030       font16EncSize += 16;
01031       font16Enc = (PSFont16Enc *)grealloc(font16Enc,
01032                       font16EncSize * sizeof(PSFont16Enc));
01033     }
01034     font16Enc[font16EncLen].fontID = *font->getID();
01035     font16Enc[font16EncLen].enc = fontParam->encoding->copy();
01036     if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
01037       uMap->decRefCnt();
01038       ++font16EncLen;
01039     } else {
01040       error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
01041         font16Enc[font16EncLen].enc->getCString());
01042     }
01043 
01044   // give up - can't do anything with this font
01045   } else {
01046     error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
01047       font->getName() ? font->getName()->getCString() : "(unnamed)",
01048       ((GfxCIDFont *)font)->getCollection()
01049         ? ((GfxCIDFont *)font)->getCollection()->getCString()
01050         : "(unknown)");
01051     return;
01052   }
01053 
01054   // generate PostScript code to set up the font
01055   if (font->isCIDFont()) {
01056     if (level == psLevel3 || level == psLevel3Sep) {
01057       writePSFmt("/F%d_%d /%s %d pdfMakeFont16L3\n",
01058          font->getID()->num, font->getID()->gen, psName,
01059          font->getWMode());
01060     } else {
01061       writePSFmt("/F%d_%d /%s %d pdfMakeFont16\n",
01062          font->getID()->num, font->getID()->gen, psName,
01063          font->getWMode());
01064     }
01065   } else {
01066     writePSFmt("/F%d_%d /%s %g %g\n",
01067            font->getID()->num, font->getID()->gen, psName, xs, ys);
01068     for (i = 0; i < 256; i += 8) {
01069       writePSFmt((i == 0) ? "[ " : "  ");
01070       for (j = 0; j < 8; ++j) {
01071     if (font->getType() == fontTrueType &&
01072         !((Gfx8BitFont *)font)->getHasEncoding()) {
01073       sprintf(buf, "c%02x", i+j);
01074       charName = buf;
01075     } else {
01076       charName = ((Gfx8BitFont *)font)->getCharName(i+j);
01077       // this is a kludge for broken PDF files that encode char 32
01078       // as .notdef
01079       if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
01080         charName = "space";
01081       }
01082     }
01083     writePS("/");
01084     writePSName(charName ? charName : (char *)".notdef");
01085       }
01086       writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
01087     }
01088     writePS("pdfMakeFont\n");
01089   }
01090 
01091   if (psNameStr) {
01092     delete psNameStr;
01093   }
01094 }
01095 
01096 void PSOutputDev::setupEmbeddedType1Font(Ref *id, char *psName) {
01097   static char hexChar[17] = "0123456789abcdef";
01098   Object refObj, strObj, obj1, obj2;
01099   Dict *dict;
01100   int length1, length2;
01101   int c;
01102   int start[4];
01103   GBool binMode;
01104   int i;
01105 
01106   // check if font is already embedded
01107   for (i = 0; i < fontFileIDLen; ++i) {
01108     if (fontFileIDs[i].num == id->num &&
01109     fontFileIDs[i].gen == id->gen)
01110       return;
01111   }
01112 
01113   // add entry to fontFileIDs list
01114   if (fontFileIDLen >= fontFileIDSize) {
01115     fontFileIDSize += 64;
01116     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
01117   }
01118   fontFileIDs[fontFileIDLen++] = *id;
01119 
01120   // get the font stream and info
01121   refObj.initRef(id->num, id->gen);
01122   refObj.fetch(xref, &strObj);
01123   refObj.free();
01124   if (!strObj.isStream()) {
01125     error(-1, "Embedded font file object is not a stream");
01126     goto err1;
01127   }
01128   if (!(dict = strObj.streamGetDict())) {
01129     error(-1, "Embedded font stream is missing its dictionary");
01130     goto err1;
01131   }
01132   dict->lookup("Length1", &obj1);
01133   dict->lookup("Length2", &obj2);
01134   if (!obj1.isInt() || !obj2.isInt()) {
01135     error(-1, "Missing length fields in embedded font stream dictionary");
01136     obj1.free();
01137     obj2.free();
01138     goto err1;
01139   }
01140   length1 = obj1.getInt();
01141   length2 = obj2.getInt();
01142   obj1.free();
01143   obj2.free();
01144 
01145   // beginning comment
01146   writePSFmt("%%%%BeginResource: font %s\n", psName);
01147   embFontList->append("%%+ font ");
01148   embFontList->append(psName);
01149   embFontList->append("\n");
01150 
01151   // copy ASCII portion of font
01152   strObj.streamReset();
01153   for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
01154     writePSChar(c);
01155   }
01156 
01157   // figure out if encrypted portion is binary or ASCII
01158   binMode = gFalse;
01159   for (i = 0; i < 4; ++i) {
01160     start[i] = strObj.streamGetChar();
01161     if (start[i] == EOF) {
01162       error(-1, "Unexpected end of file in embedded font stream");
01163       goto err1;
01164     }
01165     if (!((start[i] >= '0' && start[i] <= '9') ||
01166       (start[i] >= 'A' && start[i] <= 'F') ||
01167       (start[i] >= 'a' && start[i] <= 'f')))
01168       binMode = gTrue;
01169   }
01170 
01171   // convert binary data to ASCII
01172   if (binMode) {
01173     for (i = 0; i < 4; ++i) {
01174       writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
01175       writePSChar(hexChar[start[i] & 0x0f]);
01176     }
01177     while (i < length2) {
01178       if ((c = strObj.streamGetChar()) == EOF) {
01179     break;
01180       }
01181       writePSChar(hexChar[(c >> 4) & 0x0f]);
01182       writePSChar(hexChar[c & 0x0f]);
01183       if (++i % 32 == 0) {
01184     writePSChar('\n');
01185       }
01186     }
01187     if (i % 32 > 0) {
01188       writePSChar('\n');
01189     }
01190 
01191   // already in ASCII format -- just copy it
01192   } else {
01193     for (i = 0; i < 4; ++i) {
01194       writePSChar(start[i]);
01195     }
01196     for (i = 4; i < length2; ++i) {
01197       if ((c = strObj.streamGetChar()) == EOF) {
01198     break;
01199       }
01200       writePSChar(c);
01201     }
01202   }
01203 
01204   // write padding and "cleartomark"
01205   for (i = 0; i < 8; ++i) {
01206     writePS("00000000000000000000000000000000"
01207         "00000000000000000000000000000000\n");
01208   }
01209   writePS("cleartomark\n");
01210 
01211   // ending comment
01212   writePS("%%EndResource\n");
01213 
01214  err1:
01215   strObj.streamClose();
01216   strObj.free();
01217 }
01218 
01219 //~ This doesn't handle .pfb files or binary eexec data (which only
01220 //~ happens in pfb files?).
01221 void PSOutputDev::setupExternalType1Font(GString *fileName, char *psName) {
01222   FILE *fontFile;
01223   int c;
01224   int i;
01225 
01226   // check if font is already embedded
01227   for (i = 0; i < fontFileNameLen; ++i) {
01228     if (!fontFileNames[i]->cmp(fileName)) {
01229       return;
01230     }
01231   }
01232 
01233   // add entry to fontFileNames list
01234   if (fontFileNameLen >= fontFileNameSize) {
01235     fontFileNameSize += 64;
01236     fontFileNames = (GString **)grealloc(fontFileNames,
01237                      fontFileNameSize * sizeof(GString *));
01238   }
01239   fontFileNames[fontFileNameLen++] = fileName->copy();
01240 
01241   // beginning comment
01242   writePSFmt("%%%%BeginResource: font %s\n", psName);
01243   embFontList->append("%%+ font ");
01244   embFontList->append(psName);
01245   embFontList->append("\n");
01246 
01247   // copy the font file
01248   if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
01249     error(-1, "Couldn't open external font file");
01250     return;
01251   }
01252   while ((c = fgetc(fontFile)) != EOF) {
01253     writePSChar(c);
01254   }
01255   fclose(fontFile);
01256 
01257   // ending comment
01258   writePS("%%EndResource\n");
01259 }
01260 
01261 void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
01262                       char *psName) {
01263   char *fontBuf;
01264   int fontLen;
01265   Type1CFontFile *t1cFile;
01266   int i;
01267 
01268   // check if font is already embedded
01269   for (i = 0; i < fontFileIDLen; ++i) {
01270     if (fontFileIDs[i].num == id->num &&
01271     fontFileIDs[i].gen == id->gen)
01272       return;
01273   }
01274 
01275   // add entry to fontFileIDs list
01276   if (fontFileIDLen >= fontFileIDSize) {
01277     fontFileIDSize += 64;
01278     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
01279   }
01280   fontFileIDs[fontFileIDLen++] = *id;
01281 
01282   // beginning comment
01283   writePSFmt("%%%%BeginResource: font %s\n", psName);
01284   embFontList->append("%%+ font ");
01285   embFontList->append(psName);
01286   embFontList->append("\n");
01287 
01288   // convert it to a Type 1 font
01289   fontBuf = font->readEmbFontFile(xref, &fontLen);
01290   t1cFile = new Type1CFontFile(fontBuf, fontLen);
01291   t1cFile->convertToType1(outputFunc, outputStream);
01292   delete t1cFile;
01293   gfree(fontBuf);
01294 
01295   // ending comment
01296   writePS("%%EndResource\n");
01297 }
01298 
01299 void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
01300                         char *psName) {
01301   char *fontBuf;
01302   int fontLen;
01303   TrueTypeFontFile *ttFile;
01304   CharCodeToUnicode *ctu;
01305   int i;
01306 
01307   // check if font is already embedded
01308   for (i = 0; i < fontFileIDLen; ++i) {
01309     if (fontFileIDs[i].num == id->num &&
01310     fontFileIDs[i].gen == id->gen)
01311       return;
01312   }
01313 
01314   // add entry to fontFileIDs list
01315   if (fontFileIDLen >= fontFileIDSize) {
01316     fontFileIDSize += 64;
01317     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
01318   }
01319   fontFileIDs[fontFileIDLen++] = *id;
01320 
01321   // beginning comment
01322   writePSFmt("%%%%BeginResource: font %s\n", psName);
01323   embFontList->append("%%+ font ");
01324   embFontList->append(psName);
01325   embFontList->append("\n");
01326 
01327   // convert it to a Type 42 font
01328   fontBuf = font->readEmbFontFile(xref, &fontLen);
01329   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
01330   ctu = ((Gfx8BitFont *)font)->getToUnicode();
01331   ttFile->convertToType42(psName, ((Gfx8BitFont *)font)->getEncoding(),
01332               ctu, ((Gfx8BitFont *)font)->getHasEncoding(),
01333               outputFunc, outputStream);
01334   ctu->decRefCnt();
01335   delete ttFile;
01336   gfree(fontBuf);
01337 
01338   // ending comment
01339   writePS("%%EndResource\n");
01340 }
01341 
01342 void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, char *psName) {
01343   GString *fileName;
01344   char *fontBuf;
01345   int fontLen;
01346   TrueTypeFontFile *ttFile;
01347   CharCodeToUnicode *ctu;
01348   int i;
01349 
01350   // check if font is already embedded
01351   fileName = font->getExtFontFile();
01352   for (i = 0; i < fontFileNameLen; ++i) {
01353     if (!fontFileNames[i]->cmp(fileName)) {
01354       return;
01355     }
01356   }
01357 
01358   // add entry to fontFileNames list
01359   if (fontFileNameLen >= fontFileNameSize) {
01360     fontFileNameSize += 64;
01361     fontFileNames = (GString **)grealloc(fontFileNames,
01362                      fontFileNameSize * sizeof(GString *));
01363   }
01364   fontFileNames[fontFileNameLen++] = fileName->copy();
01365 
01366   // beginning comment
01367   writePSFmt("%%%%BeginResource: font %s\n", psName);
01368   embFontList->append("%%+ font ");
01369   embFontList->append(psName);
01370   embFontList->append("\n");
01371 
01372   // convert it to a Type 42 font
01373   fontBuf = font->readExtFontFile(&fontLen);
01374   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
01375   ctu = ((Gfx8BitFont *)font)->getToUnicode();
01376   ttFile->convertToType42(psName, ((Gfx8BitFont *)font)->getEncoding(),
01377               ctu, ((Gfx8BitFont *)font)->getHasEncoding(),
01378               outputFunc, outputStream);
01379   ctu->decRefCnt();
01380   delete ttFile;
01381   gfree(fontBuf);
01382 
01383   // ending comment
01384   writePS("%%EndResource\n");
01385 }
01386 
01387 void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
01388                         char *psName) {
01389   char *fontBuf;
01390   int fontLen;
01391   Type1CFontFile *t1cFile;
01392   int i;
01393 
01394   // check if font is already embedded
01395   for (i = 0; i < fontFileIDLen; ++i) {
01396     if (fontFileIDs[i].num == id->num &&
01397     fontFileIDs[i].gen == id->gen)
01398       return;
01399   }
01400 
01401   // add entry to fontFileIDs list
01402   if (fontFileIDLen >= fontFileIDSize) {
01403     fontFileIDSize += 64;
01404     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
01405   }
01406   fontFileIDs[fontFileIDLen++] = *id;
01407 
01408   // beginning comment
01409   writePSFmt("%%%%BeginResource: font %s\n", psName);
01410   embFontList->append("%%+ font ");
01411   embFontList->append(psName);
01412   embFontList->append("\n");
01413 
01414   // convert it to a Type 0 font
01415   fontBuf = font->readEmbFontFile(xref, &fontLen);
01416   t1cFile = new Type1CFontFile(fontBuf, fontLen);
01417   if (globalParams->getPSLevel() >= psLevel3) {
01418     // Level 3: use a CID font
01419     t1cFile->convertToCIDType0(psName, outputFunc, outputStream);
01420   } else {
01421     // otherwise: use a non-CID composite font
01422     t1cFile->convertToType0(psName, outputFunc, outputStream);
01423   }
01424   delete t1cFile;
01425   gfree(fontBuf);
01426 
01427   // ending comment
01428   writePS("%%EndResource\n");
01429 }
01430 
01431 void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
01432                            char *psName) {
01433   char *fontBuf;
01434   int fontLen;
01435   TrueTypeFontFile *ttFile;
01436   int i;
01437 
01438   // check if font is already embedded
01439   for (i = 0; i < fontFileIDLen; ++i) {
01440     if (fontFileIDs[i].num == id->num &&
01441     fontFileIDs[i].gen == id->gen)
01442       return;
01443   }
01444 
01445   // add entry to fontFileIDs list
01446   if (fontFileIDLen >= fontFileIDSize) {
01447     fontFileIDSize += 64;
01448     fontFileIDs = (Ref *)grealloc(fontFileIDs, fontFileIDSize * sizeof(Ref));
01449   }
01450   fontFileIDs[fontFileIDLen++] = *id;
01451 
01452   // beginning comment
01453   writePSFmt("%%%%BeginResource: font %s\n", psName);
01454   embFontList->append("%%+ font ");
01455   embFontList->append(psName);
01456   embFontList->append("\n");
01457 
01458   // convert it to a Type 0 font
01459   fontBuf = font->readEmbFontFile(xref, &fontLen);
01460   ttFile = new TrueTypeFontFile(fontBuf, fontLen);
01461   if (globalParams->getPSLevel() >= psLevel3) {
01462     ttFile->convertToCIDType2(psName,
01463                   ((GfxCIDFont *)font)->getCIDToGID(),
01464                   ((GfxCIDFont *)font)->getCIDToGIDLen(),
01465                   outputFunc, outputStream);
01466   } else {
01467     // otherwise: use a non-CID composite font
01468     ttFile->convertToType0(psName, ((GfxCIDFont *)font)->getCIDToGID(),
01469                ((GfxCIDFont *)font)->getCIDToGIDLen(),
01470                outputFunc, outputStream);
01471   }
01472   delete ttFile;
01473   gfree(fontBuf);
01474 
01475   // ending comment
01476   writePS("%%EndResource\n");
01477 }
01478 
01479 void PSOutputDev::setupType3Font(GfxFont *font, char *psName,
01480                  Dict *parentResDict) {
01481   Dict *resDict;
01482   Dict *charProcs;
01483   Object charProc;
01484   Gfx *gfx;
01485   PDFRectangle box;
01486   double *m;
01487   char buf[256];
01488   int i;
01489 
01490   // set up resources used by font
01491   if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
01492     setupResources(resDict);
01493   } else {
01494     resDict = parentResDict;
01495   }
01496 
01497   // beginning comment
01498   writePSFmt("%%%%BeginResource: font %s\n", psName);
01499   embFontList->append("%%+ font ");
01500   embFontList->append(psName);
01501   embFontList->append("\n");
01502 
01503   // font dictionary
01504   writePS("7 dict begin\n");
01505   writePS("/FontType 3 def\n");
01506   m = font->getFontMatrix();
01507   writePSFmt("/FontMatrix [%g %g %g %g %g %g] def\n",
01508          m[0], m[1], m[2], m[3], m[4], m[5]);
01509   m = font->getFontBBox();
01510   writePSFmt("/FontBBox [%g %g %g %g] def\n",
01511          m[0], m[1], m[2], m[3]);
01512   writePS("/Encoding 256 array def\n");
01513   writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
01514   writePS("/BuildGlyph {\n");
01515   writePS("  exch /CharProcs get exch\n");
01516   writePS("  2 copy known not { pop /.notdef } if\n");
01517   writePS("  get exec\n");
01518   writePS("} bind def\n");
01519   writePS("/BuildChar {\n");
01520   writePS("  1 index /Encoding get exch get\n");
01521   writePS("  1 index /BuildGlyph get exec\n");
01522   writePS("} bind def\n");
01523   if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
01524     writePSFmt("/CharProcs %d dict def\n", charProcs->getLength());
01525     writePS("CharProcs begin\n");
01526     box.x1 = m[0];
01527     box.y1 = m[1];
01528     box.x2 = m[2];
01529     box.y2 = m[3];
01530     gfx = new Gfx(xref, this, resDict, &box, gFalse, NULL);
01531     inType3Char = gTrue;
01532     t3Cacheable = gFalse;
01533     for (i = 0; i < charProcs->getLength(); ++i) {
01534       writePS("/");
01535       writePSName(charProcs->getKey(i));
01536       writePS(" {\n");
01537       gfx->display(charProcs->getVal(i, &charProc));
01538       charProc.free();
01539       if (t3String) {
01540     if (t3Cacheable) {
01541       sprintf(buf, "%g %g %g %g %g %g setcachedevice\n",
01542           t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
01543     } else {
01544       sprintf(buf, "%g %g setcharwidth\n", t3WX, t3WY);
01545     }
01546     (*outputFunc)(outputStream, buf, strlen(buf));
01547     (*outputFunc)(outputStream, t3String->getCString(),
01548               t3String->getLength());
01549     delete t3String;
01550     t3String = NULL;
01551       }
01552       (*outputFunc)(outputStream, "Q\n", 2);
01553       writePS("} def\n");
01554     }
01555     inType3Char = gFalse;
01556     delete gfx;
01557     writePS("end\n");
01558   }
01559   writePS("currentdict end\n");
01560   writePSFmt("/%s exch definefont pop\n", psName);
01561 
01562   // ending comment
01563   writePS("%%EndResource\n");
01564 }
01565 
01566 void PSOutputDev::setupImages(Dict *resDict) {
01567   Object xObjDict, xObj, xObjRef, subtypeObj;
01568   int i;
01569 
01570   if (mode != psModeForm) {
01571     return;
01572   }
01573 
01574   resDict->lookup("XObject", &xObjDict);
01575   if (xObjDict.isDict()) {
01576     for (i = 0; i < xObjDict.dictGetLength(); ++i) {
01577       xObjDict.dictGetValNF(i, &xObjRef);
01578       xObjDict.dictGetVal(i, &xObj);
01579       if (xObj.isStream()) {
01580     xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
01581     if (subtypeObj.isName("Image")) {
01582       if (xObjRef.isRef()) {
01583         setupImage(xObjRef.getRef(), xObj.getStream());
01584       } else {
01585         error(-1, "Image in resource dict is not an indirect reference");
01586       }
01587     }
01588     subtypeObj.free();
01589       }
01590       xObj.free();
01591       xObjRef.free();
01592     }
01593   }
01594   xObjDict.free();
01595 }
01596 
01597 void PSOutputDev::setupImage(Ref id, Stream *str) {
01598   int c;
01599   int size, line, col, i;
01600 
01601   // construct an encoder stream
01602   if (globalParams->getPSASCIIHex()) {
01603     str = new ASCIIHexEncoder(str);
01604   } else {
01605     str = new ASCII85Encoder(str);
01606   }
01607 
01608   // compute image data size
01609   str->reset();
01610   col = size = 0;
01611   do {
01612     do {
01613       c = str->getChar();
01614     } while (c == '\n' || c == '\r');
01615     if (c == '~' || c == EOF) {
01616       break;
01617     }
01618     if (c == 'z') {
01619       ++col;
01620     } else {
01621       ++col;
01622       for (i = 1; i <= 4; ++i) {
01623     do {
01624       c = str->getChar();
01625     } while (c == '\n' || c == '\r');
01626     if (c == '~' || c == EOF) {
01627       break;
01628     }
01629     ++col;
01630       }
01631     }
01632     if (col > 225) {
01633       ++size;
01634       col = 0;
01635     }
01636   } while (c != '~' && c != EOF);
01637   ++size;
01638   writePSFmt("%d array dup /ImData_%d_%d exch def\n", size, id.num, id.gen);
01639 
01640   // write the data into the array
01641   str->reset();
01642   line = col = 0;
01643   writePS("dup 0 <~");
01644   do {
01645     do {
01646       c = str->getChar();
01647     } while (c == '\n' || c == '\r');
01648     if (c == '~' || c == EOF) {
01649       break;
01650     }
01651     if (c == 'z') {
01652       writePSChar(c);
01653       ++col;
01654     } else {
01655       writePSChar(c);
01656       ++col;
01657       for (i = 1; i <= 4; ++i) {
01658     do {
01659       c = str->getChar();
01660     } while (c == '\n' || c == '\r');
01661     if (c == '~' || c == EOF) {
01662       break;
01663     }
01664     writePSChar(c);
01665     ++col;
01666       }
01667     }
01668     // each line is: "dup nnnnn <~...data...~> put<eol>"
01669     // so max data length = 255 - 20 = 235
01670     // chunks are 1 or 4 bytes each, so we have to stop at 232
01671     // but make it 225 just to be safe
01672     if (col > 225) {
01673       writePS("~> put\n");
01674       ++line;
01675       writePSFmt("dup %d <~", line);
01676       col = 0;
01677     }
01678   } while (c != '~' && c != EOF);
01679   writePS("~> put\n");
01680   writePS("pop\n");
01681 
01682   delete str;
01683 }
01684 
01685 void PSOutputDev::startPage(int pageNum, GfxState *state) {
01686   int x1, y1, x2, y2, width, height, t;
01687 
01688 
01689   switch (mode) {
01690 
01691   case psModePS:
01692     writePSFmt("%%%%Page: %d %d\n", pageNum, seqPage);
01693     writePS("%%BeginPageSetup\n");
01694 
01695     // rotate, translate, and scale page
01696     x1 = (int)(state->getX1() + 0.5);
01697     y1 = (int)(state->getY1() + 0.5);
01698     x2 = (int)(state->getX2() + 0.5);
01699     y2 = (int)(state->getY2() + 0.5);
01700     width = x2 - x1;
01701     height = y2 - y1;
01702     if (width > height && width > paperWidth) {
01703       landscape = gTrue;
01704       writePSFmt("%%%%PageOrientation: %s\n",
01705          state->getCTM()[0] ? "Landscape" : "Portrait");
01706       writePS("pdfStartPage\n");
01707       writePS("90 rotate\n");
01708       tx = -x1;
01709       ty = -(y1 + paperWidth);
01710       t = width;
01711       width = height;
01712       height = t;
01713     } else {
01714       landscape = gFalse;
01715       writePSFmt("%%%%PageOrientation: %s\n",
01716          state->getCTM()[0] ? "Portrait" : "Landscape");
01717       writePS("pdfStartPage\n");
01718       tx = -x1;
01719       ty = -y1;
01720     }
01721     if (width < paperWidth) {
01722       tx += (paperWidth - width) / 2;
01723     }
01724     if (height < paperHeight) {
01725       ty += (paperHeight - height) / 2;
01726     }
01727     if (tx != 0 || ty != 0) {
01728       writePSFmt("%g %g translate\n", tx, ty);
01729     }
01730     if (width > paperWidth || height > paperHeight) {
01731       xScale = (double)paperWidth / (double)width;
01732       yScale = (double)paperHeight / (double)height;
01733       if (yScale < xScale) {
01734     xScale = yScale;
01735       } else {
01736     yScale = xScale;
01737       }
01738       writePSFmt("%0.4f %0.4f scale\n", xScale, xScale);
01739     } else {
01740       xScale = yScale = 1;
01741     }
01742 
01743     writePS("%%EndPageSetup\n");
01744     ++seqPage;
01745     break;
01746 
01747   case psModeEPS:
01748     writePS("pdfStartPage\n");
01749     tx = ty = 0;
01750     xScale = yScale = 1;
01751     landscape = gFalse;
01752     break;
01753 
01754   case psModeForm:
01755     writePS("/PaintProc {\n");
01756     writePS("begin xpdf begin\n");
01757     writePS("pdfStartPage\n");
01758     tx = ty = 0;
01759     xScale = yScale = 1;
01760     landscape = gFalse;
01761     break;
01762   }
01763 }
01764 
01765 void PSOutputDev::endPage() {
01766 
01767   if (mode == psModeForm) {
01768     writePS("pdfEndPage\n");
01769     writePS("end end\n");
01770     writePS("} def\n");
01771     writePS("end end\n");
01772   } else {
01773     writePS("showpage\n");
01774     writePS("%%PageTrailer\n");
01775     writePS("pdfEndPage\n");
01776   }
01777 }
01778 
01779 void PSOutputDev::saveState(GfxState *state) {
01780   writePS("q\n");
01781 }
01782 
01783 void PSOutputDev::restoreState(GfxState *state) {
01784   writePS("Q\n");
01785 }
01786 
01787 void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
01788                 double m21, double m22, double m31, double m32) {
01789   writePSFmt("[%g %g %g %g %g %g] cm\n", m11, m12, m21, m22, m31, m32);
01790 }
01791 
01792 void PSOutputDev::updateLineDash(GfxState *state) {
01793   double *dash;
01794   double start;
01795   int length, i;
01796 
01797   state->getLineDash(&dash, &length, &start);
01798   writePS("[");
01799   for (i = 0; i < length; ++i)
01800     writePSFmt("%g%s", dash[i], (i == length-1) ? "" : " ");
01801   writePSFmt("] %g d\n", start);
01802 }
01803 
01804 void PSOutputDev::updateFlatness(GfxState *state) {
01805   writePSFmt("%d i\n", state->getFlatness());
01806 }
01807 
01808 void PSOutputDev::updateLineJoin(GfxState *state) {
01809   writePSFmt("%d j\n", state->getLineJoin());
01810 }
01811 
01812 void PSOutputDev::updateLineCap(GfxState *state) {
01813   writePSFmt("%d J\n", state->getLineCap());
01814 }
01815 
01816 void PSOutputDev::updateMiterLimit(GfxState *state) {
01817   writePSFmt("%g M\n", state->getMiterLimit());
01818 }
01819 
01820 void PSOutputDev::updateLineWidth(GfxState *state) {
01821   writePSFmt("%g w\n", state->getLineWidth());
01822 }
01823 
01824 void PSOutputDev::updateFillColor(GfxState *state) {
01825   GfxColor color;
01826   double gray;
01827   GfxRGB rgb;
01828   GfxCMYK cmyk;
01829   GfxSeparationColorSpace *sepCS;
01830 
01831   switch (level) {
01832   case psLevel1:
01833     state->getFillGray(&gray);
01834     writePSFmt("%g g\n", gray);
01835     break;
01836   case psLevel1Sep:
01837     state->getFillCMYK(&cmyk);
01838     writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01839     addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01840     break;
01841   case psLevel2:
01842   case psLevel3:
01843     if (state->getFillColorSpace()->getMode() == csDeviceCMYK) {
01844       state->getFillCMYK(&cmyk);
01845       writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01846     } else {
01847       state->getFillRGB(&rgb);
01848       if (rgb.r == rgb.g && rgb.g == rgb.b) {
01849     writePSFmt("%g g\n", rgb.r);
01850       } else {
01851     writePSFmt("%g %g %g rg\n", rgb.r, rgb.g, rgb.b);
01852       }
01853     }
01854     break;
01855   case psLevel2Sep:
01856   case psLevel3Sep:
01857     if (state->getFillColorSpace()->getMode() == csSeparation) {
01858       sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
01859       color.c[0] = 1;
01860       sepCS->getCMYK(&color, &cmyk);
01861       writePSFmt("%g %g %g %g %g (%s) ck\n",
01862          state->getFillColor()->c[0],
01863          cmyk.c, cmyk.m, cmyk.y, cmyk.k,
01864          sepCS->getName()->getCString());
01865       addCustomColor(sepCS);
01866     } else {
01867       state->getFillCMYK(&cmyk);
01868       writePSFmt("%g %g %g %g k\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01869       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01870     }
01871     break;
01872   }
01873   t3Cacheable = gFalse;
01874 }
01875 
01876 void PSOutputDev::updateStrokeColor(GfxState *state) {
01877   GfxColor color;
01878   double gray;
01879   GfxRGB rgb;
01880   GfxCMYK cmyk;
01881   GfxSeparationColorSpace *sepCS;
01882 
01883   switch (level) {
01884   case psLevel1:
01885     state->getStrokeGray(&gray);
01886     writePSFmt("%g G\n", gray);
01887     break;
01888   case psLevel1Sep:
01889     state->getStrokeCMYK(&cmyk);
01890     writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01891     addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01892     break;
01893   case psLevel2:
01894   case psLevel3:
01895     if (state->getStrokeColorSpace()->getMode() == csDeviceCMYK) {
01896       state->getStrokeCMYK(&cmyk);
01897       writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01898     } else {
01899       state->getStrokeRGB(&rgb);
01900       if (rgb.r == rgb.g && rgb.g == rgb.b) {
01901     writePSFmt("%g G\n", rgb.r);
01902       } else {
01903     writePSFmt("%g %g %g RG\n", rgb.r, rgb.g, rgb.b);
01904       }
01905     }
01906     break;
01907   case psLevel2Sep:
01908   case psLevel3Sep:
01909     if (state->getStrokeColorSpace()->getMode() == csSeparation) {
01910       sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
01911       color.c[0] = 1;
01912       sepCS->getCMYK(&color, &cmyk);
01913       writePSFmt("%g %g %g %g %g (%s) CK\n",
01914          state->getStrokeColor()->c[0],
01915          cmyk.c, cmyk.m, cmyk.y, cmyk.k,
01916          sepCS->getName()->getCString());
01917       addCustomColor(sepCS);
01918     } else {
01919       state->getStrokeCMYK(&cmyk);
01920       writePSFmt("%g %g %g %g K\n", cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01921       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
01922     }
01923     break;
01924   }
01925   t3Cacheable = gFalse;
01926 }
01927 
01928 void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
01929   if (c > 0) {
01930     processColors |= psProcessCyan;
01931   }
01932   if (m > 0) {
01933     processColors |= psProcessMagenta;
01934   }
01935   if (y > 0) {
01936     processColors |= psProcessYellow;
01937   }
01938   if (k > 0) {
01939     processColors |= psProcessBlack;
01940   }
01941 }
01942 
01943 void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
01944   PSOutCustomColor *cc;
01945   GfxColor color;
01946   GfxCMYK cmyk;
01947 
01948   for (cc = customColors; cc; cc = cc->next) {
01949     if (!cc->name->cmp(sepCS->getName())) {
01950       return;
01951     }
01952   }
01953   color.c[0] = 1;
01954   sepCS->getCMYK(&color, &cmyk);
01955   cc = new PSOutCustomColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k,
01956                 sepCS->getName()->copy());
01957   cc->next = customColors;
01958   customColors = cc;
01959 }
01960 
01961 void PSOutputDev::updateFont(GfxState *state) {
01962   if (state->getFont()) {
01963     writePSFmt("/F%d_%d %g Tf\n",
01964            state->getFont()->getID()->num, state->getFont()->getID()->gen,
01965            state->getFontSize());
01966   }
01967 }
01968 
01969 void PSOutputDev::updateTextMat(GfxState *state) {
01970   double *mat;
01971 
01972   mat = state->getTextMat();
01973   writePSFmt("[%g %g %g %g %g %g] Tm\n",
01974          mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
01975 }
01976 
01977 void PSOutputDev::updateCharSpace(GfxState *state) {
01978   writePSFmt("%g Tc\n", state->getCharSpace());
01979 }
01980 
01981 void PSOutputDev::updateRender(GfxState *state) {
01982   int rm;
01983 
01984   rm = state->getRender();
01985   writePSFmt("%d Tr\n", rm);
01986   rm &= 3;
01987   if (rm != 0 && rm != 3) {
01988     t3Cacheable = gFalse;
01989   }
01990 }
01991 
01992 void PSOutputDev::updateRise(GfxState *state) {
01993   writePSFmt("%g Ts\n", state->getRise());
01994 }
01995 
01996 void PSOutputDev::updateWordSpace(GfxState *state) {
01997   writePSFmt("%g Tw\n", state->getWordSpace());
01998 }
01999 
02000 void PSOutputDev::updateHorizScaling(GfxState *state) {
02001   writePSFmt("%g Tz\n", state->getHorizScaling());
02002 }
02003 
02004 void PSOutputDev::updateTextPos(GfxState *state) {
02005   writePSFmt("%g %g Td\n", state->getLineX(), state->getLineY());
02006 }
02007 
02008 void PSOutputDev::updateTextShift(GfxState *state, double shift) {
02009   if (state->getFont()->getWMode()) {
02010     writePSFmt("%g TJmV\n", shift);
02011   } else {
02012     writePSFmt("%g TJm\n", shift);
02013   }
02014 }
02015 
02016 void PSOutputDev::stroke(GfxState *state) {
02017   doPath(state->getPath());
02018   if (t3String) {
02019     // if we're construct a cacheable Type 3 glyph, we need to do
02020     // everything in the fill color
02021     writePS("Sf\n");
02022   } else {
02023     writePS("S\n");
02024   }
02025 }
02026 
02027 void PSOutputDev::fill(GfxState *state) {
02028   doPath(state->getPath());
02029   writePS("f\n");
02030 }
02031 
02032 void PSOutputDev::eoFill(GfxState *state) {
02033   doPath(state->getPath());
02034   writePS("f*\n");
02035 }
02036 
02037 void PSOutputDev::clip(GfxState *state) {
02038   doPath(state->getPath());
02039   writePS("W\n");
02040 }
02041 
02042 void PSOutputDev::eoClip(GfxState *state) {
02043   doPath(state->getPath());
02044   writePS("W*\n");
02045 }
02046 
02047 void PSOutputDev::doPath(GfxPath *path) {
02048   GfxSubpath *subpath;
02049   double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
02050   int n, m, i, j;
02051 
02052   n = path->getNumSubpaths();
02053 
02054   if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
02055     subpath = path->getSubpath(0);
02056     x0 = subpath->getX(0);
02057     y0 = subpath->getY(0);
02058     x4 = subpath->getX(4);
02059     y4 = subpath->getY(4);
02060     if (x4 == x0 && y4 == y0) {
02061       x1 = subpath->getX(1);
02062       y1 = subpath->getY(1);
02063       x2 = subpath->getX(2);
02064       y2 = subpath->getY(2);
02065       x3 = subpath->getX(3);
02066       y3 = subpath->getY(3);
02067       if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
02068     writePSFmt("%g %g %g %g re\n",
02069            x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
02070            fabs(x2 - x0), fabs(y1 - y0));
02071     return;
02072       } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
02073     writePSFmt("%g %g %g %g re\n",
02074            x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
02075            fabs(x1 - x0), fabs(y2 - y0));
02076     return;
02077       }
02078     }
02079   }
02080 
02081   for (i = 0; i < n; ++i) {
02082     subpath = path->getSubpath(i);
02083     m = subpath->getNumPoints();
02084     writePSFmt("%g %g m\n", subpath->getX(0), subpath->getY(0));
02085     j = 1;
02086     while (j < m) {
02087       if (subpath->getCurve(j)) {
02088     writePSFmt("%g %g %g %g %g %g c\n", subpath->getX(j), subpath->getY(j),
02089            subpath->getX(j+1), subpath->getY(j+1),
02090            subpath->getX(j+2), subpath->getY(j+2));
02091     j += 3;
02092       } else {
02093     writePSFmt("%g %g l\n", subpath->getX(j), subpath->getY(j));
02094     ++j;
02095       }
02096     }
02097     if (subpath->isClosed()) {
02098       writePS("h\n");
02099     }
02100   }
02101 }
02102 
02103 void PSOutputDev::drawString(GfxState *state, GString *s) {
02104   GfxFont *font;
02105   int wMode;
02106   GString *s2;
02107   double dx, dy, dx2, dy2, originX, originY;
02108   char *p;
02109   UnicodeMap *uMap;
02110   CharCode code;
02111   Unicode u[8];
02112   char buf[8];
02113   int len, nChars, uLen, n, m, i, j;
02114 
02115   // check for invisible text -- this is used by Acrobat Capture
02116   if ((state->getRender() & 3) == 3) {
02117     return;
02118   }
02119 
02120   // ignore empty strings
02121   if (s->getLength() == 0) {
02122     return;
02123   }
02124 
02125   // get the font
02126   if (!(font = state->getFont())) {
02127     return;
02128   }
02129   wMode = font->getWMode();
02130 
02131   // check for a subtitute 16-bit font
02132   uMap = NULL;
02133   if (font->isCIDFont()) {
02134     for (i = 0; i < font16EncLen; ++i) {
02135       if (font->getID()->num == font16Enc[i].fontID.num &&
02136       font->getID()->gen == font16Enc[i].fontID.gen) {
02137     uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
02138     break;
02139       }
02140     }
02141   }
02142 
02143   // compute width of chars in string, ignoring char spacing and word
02144   // spacing -- the Tj operator will adjust for the metrics of the
02145   // font that's actually used
02146   dx = dy = 0;
02147   nChars = 0;
02148   p = s->getCString();
02149   len = s->getLength();
02150   if (font->isCIDFont()) {
02151     s2 = new GString();
02152   } else {
02153     s2 = s;
02154   }
02155   while (len > 0) {
02156     n = font->getNextChar(p, len, &code,
02157               u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
02158               &dx2, &dy2, &originX, &originY);
02159     if (font->isCIDFont()) {
02160       if (uMap) {
02161     for (i = 0; i < uLen; ++i) {
02162       m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
02163       for (j = 0; j < m; ++j) {
02164         s2->append(buf[j]);
02165       }
02166     }
02167     //~ this really needs to get the number of chars in the target
02168     //~ encoding - which may be more than the number of Unicode
02169     //~ chars
02170     nChars += uLen;
02171       } else {
02172     s2->append((char)((code >> 8) & 0xff));
02173     s2->append((char)(code & 0xff));
02174     ++nChars;
02175       }
02176     }
02177     dx += dx2;
02178     dy += dy2;
02179     p += n;
02180     len -= n;
02181   }
02182   dx *= state->getFontSize() * state->getHorizScaling();
02183   dy *= state->getFontSize();
02184   if (uMap) {
02185     uMap->decRefCnt();
02186   }
02187 
02188   if (s2->getLength() > 0) {
02189     writePSString(s2);
02190     if (font->isCIDFont()) {
02191       if (wMode) {
02192     writePSFmt(" %d %g Tj16V\n", nChars, dy);
02193       } else {
02194     writePSFmt(" %d %g Tj16\n", nChars, dx);
02195       }
02196     } else {
02197       writePSFmt(" %g Tj\n", dx);
02198     }
02199   }
02200   if (font->isCIDFont()) {
02201     delete s2;
02202   }
02203 }
02204 
02205 void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
02206                 int width, int height, GBool invert,
02207                 GBool inlineImg) {
02208   int len;
02209 
02210   len = height * ((width + 7) / 8);
02211   if (level == psLevel1 || level == psLevel1Sep) {
02212     doImageL1(NULL, invert, inlineImg, str, width, height, len);
02213   } else {
02214     doImageL2(ref, NULL, invert, inlineImg, str, width, height, len);
02215   }
02216 }
02217 
02218 void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
02219                 int width, int height, GfxImageColorMap *colorMap,
02220                 int *maskColors, GBool inlineImg) {
02221   int len;
02222 
02223   len = height * ((width * colorMap->getNumPixelComps() *
02224            colorMap->getBits() + 7) / 8);
02225   switch (level) {
02226   case psLevel1:
02227     doImageL1(colorMap, gFalse, inlineImg, str, width, height, len);
02228     break;
02229   case psLevel1Sep:
02230     //~ handle indexed, separation, ... color spaces
02231     doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
02232     break;
02233   case psLevel2:
02234   case psLevel2Sep:
02235   case psLevel3:
02236   case psLevel3Sep:
02237     doImageL2(ref, colorMap, gFalse, inlineImg, str, width, height, len);
02238     break;
02239   }
02240   t3Cacheable = gFalse;
02241 }
02242 
02243 void PSOutputDev::doImageL1(GfxImageColorMap *colorMap,
02244                 GBool invert, GBool inlineImg,
02245                 Stream *str, int width, int height, int len) {
02246   ImageStream *imgStr;
02247   Guchar pixBuf[gfxColorMaxComps];
02248   double gray;
02249   int x, y, i;
02250 
02251   // width, height, matrix, bits per component
02252   if (colorMap) {
02253     writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1\n",
02254            width, height,
02255            width, -height, height);
02256   } else {
02257     writePSFmt("%d %d %s [%d 0 0 %d 0 %d] pdfImM1\n",
02258            width, height, invert ? "true" : "false",
02259            width, -height, height);
02260   }
02261 
02262   // image
02263   if (colorMap) {
02264 
02265     // set up to process the data stream
02266     imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
02267                  colorMap->getBits());
02268     imgStr->reset();
02269 
02270     // process the data stream
02271     i = 0;
02272     for (y = 0; y < height; ++y) {
02273 
02274       // write the line
02275       for (x = 0; x < width; ++x) {
02276     imgStr->getPixel(pixBuf);
02277     colorMap->getGray(pixBuf, &gray);
02278     writePSFmt("%02x", (int)(gray * 255 + 0.5));
02279     if (++i == 32) {
02280       writePSChar('\n');
02281       i = 0;
02282     }
02283       }
02284     }
02285     if (i != 0) {
02286       writePSChar('\n');
02287     }
02288     delete imgStr;
02289 
02290   // imagemask
02291   } else {
02292     str->reset();
02293     i = 0;
02294     for (y = 0; y < height; ++y) {
02295       for (x = 0; x < width; x += 8) {
02296     writePSFmt("%02x", str->getChar() & 0xff);
02297     if (++i == 32) {
02298       writePSChar('\n');
02299       i = 0;
02300     }
02301       }
02302     }
02303     if (i != 0) {
02304       writePSChar('\n');
02305     }
02306     str->close();
02307   }
02308 }
02309 
02310 void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
02311                    GBool invert, GBool inlineImg,
02312                    Stream *str, int width, int height, int len) {
02313   ImageStream *imgStr;
02314   Guchar *lineBuf;
02315   Guchar pixBuf[gfxColorMaxComps];
02316   GfxCMYK cmyk;
02317   int x, y, i, comp;
02318 
02319   // width, height, matrix, bits per component
02320   writePSFmt("%d %d 8 [%d 0 0 %d 0 %d] pdfIm1Sep\n",
02321          width, height,
02322          width, -height, height);
02323 
02324   // allocate a line buffer
02325   lineBuf = (Guchar *)gmalloc(4 * width);
02326 
02327   // set up to process the data stream
02328   imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
02329                colorMap->getBits());
02330   imgStr->reset();
02331 
02332   // process the data stream
02333   i = 0;
02334   for (y = 0; y < height; ++y) {
02335 
02336     // read the line
02337     for (x = 0; x < width; ++x) {
02338       imgStr->getPixel(pixBuf);
02339       colorMap->getCMYK(pixBuf, &cmyk);
02340       lineBuf[4*x+0] = (int)(255 * cmyk.c + 0.5);
02341       lineBuf[4*x+1] = (int)(255 * cmyk.m + 0.5);
02342       lineBuf[4*x+2] = (int)(255 * cmyk.y + 0.5);
02343       lineBuf[4*x+3] = (int)(255 * cmyk.k + 0.5);
02344       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
02345     }
02346 
02347     // write one line of each color component
02348     for (comp = 0; comp < 4; ++comp) {
02349       for (x = 0; x < width; ++x) {
02350     writePSFmt("%02x", lineBuf[4*x + comp]);
02351     if (++i == 32) {
02352       writePSChar('\n');
02353       i = 0;
02354     }
02355       }
02356     }
02357   }
02358 
02359   if (i != 0) {
02360     writePSChar('\n');
02361   }
02362 
02363   delete imgStr;
02364   gfree(lineBuf);
02365 }
02366 
02367 void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
02368                 GBool invert, GBool inlineImg,
02369                 Stream *str, int width, int height, int len) {
02370   GString *s;
02371   int n, numComps;
02372   GBool useRLE, useASCII, useCompressed;
02373   GfxSeparationColorSpace *sepCS;
02374   GfxColor color;
02375   GfxCMYK cmyk;
02376   int c;
02377   int line, col, i;
02378 
02379   // color space
02380   if (colorMap) {
02381     dumpColorSpaceL2(colorMap->getColorSpace());
02382     writePS(" setcolorspace\n");
02383   }
02384 
02385   // set up the image data
02386   if (mode == psModeForm || inType3Char) {
02387     if (inlineImg) {
02388       // create an array
02389       str = new FixedLengthEncoder(str, len);
02390       if (globalParams->getPSASCIIHex()) {
02391     str = new ASCIIHexEncoder(str);
02392       } else {
02393     str = new ASCII85Encoder(str);
02394       }
02395       str->reset();
02396       line = col = 0;
02397       writePS("[<~");
02398       do {
02399     do {
02400       c = str->getChar();
02401     } while (c == '\n' || c == '\r');
02402     if (c == '~' || c == EOF) {
02403       break;
02404     }
02405     if (c == 'z') {
02406       writePSChar(c);
02407       ++col;
02408     } else {
02409       writePSChar(c);
02410       ++col;
02411       for (i = 1; i <= 4; ++i) {
02412         do {
02413           c = str->getChar();
02414         } while (c == '\n' || c == '\r');
02415         if (c == '~' || c == EOF) {
02416           break;
02417         }
02418         writePSChar(c);
02419         ++col;
02420       }
02421     }
02422     // each line is: "dup nnnnn <~...data...~> put<eol>"
02423     // so max data length = 255 - 20 = 235
02424     // chunks are 1 or 4 bytes each, so we have to stop at 232
02425     // but make it 225 just to be safe
02426     if (col > 225) {
02427       writePS("~>\n");
02428       ++line;
02429       writePSFmt("<~", line);
02430       col = 0;
02431     }
02432       } while (c != '~' && c != EOF);
02433       writePS("~>]\n");
02434       writePS("0\n");
02435       delete str;
02436     } else {
02437       // set up to use the array already created by setupImages()
02438       writePSFmt("ImData_%d_%d 0\n", ref->getRefNum(), ref->getRefGen());
02439     }
02440   }
02441 
02442   // image dictionary
02443   writePS("<<\n  /ImageType 1\n");
02444 
02445   // width, height, matrix, bits per component
02446   writePSFmt("  /Width %d\n", width);
02447   writePSFmt("  /Height %d\n", height);
02448   writePSFmt("  /ImageMatrix [%d 0 0 %d 0 %d]\n", width, -height, height);
02449   writePSFmt("  /BitsPerComponent %d\n",
02450          colorMap ? colorMap->getBits() : 1);
02451 
02452   // decode 
02453   if (colorMap) {
02454     writePS("  /Decode [");
02455     if (colorMap->getColorSpace()->getMode() == csSeparation) {
02456       //~ this is a kludge -- see comment in dumpColorSpaceL2
02457       n = (1 << colorMap->getBits()) - 1;
02458       writePSFmt("%g %g", colorMap->getDecodeLow(0) * n,
02459          colorMap->getDecodeHigh(0) * n);
02460     } else {
02461       numComps = colorMap->getNumPixelComps();
02462       for (i = 0; i < numComps; ++i) {
02463     if (i > 0) {
02464       writePS(" ");
02465     }
02466     writePSFmt("%g %g", colorMap->getDecodeLow(i),
02467            colorMap->getDecodeHigh(i));
02468       }
02469     }
02470     writePS("]\n");
02471   } else {
02472     writePSFmt("  /Decode [%d %d]\n", invert ? 1 : 0, invert ? 0 : 1);
02473   }
02474 
02475   if (mode == psModeForm || inType3Char) {
02476 
02477     // data source
02478     writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
02479 
02480     // end of image dictionary
02481     writePSFmt(">>\n%s\n", colorMap ? "image" : "imagemask");
02482 
02483     // get rid of the array and index
02484     writePS("pop pop\n");
02485 
02486   } else {
02487 
02488     // data source
02489     writePS("  /DataSource currentfile\n");
02490     s = str->getPSFilter("    ");
02491     if (inlineImg || !s) {
02492       useRLE = gTrue;
02493       useASCII = gTrue;
02494       useCompressed = gFalse;
02495     } else {
02496       useRLE = gFalse;
02497       useASCII = str->isBinary();
02498       useCompressed = gTrue;
02499     }
02500     if (useASCII) {
02501       writePSFmt("    /ASCII%sDecode filter\n",
02502          globalParams->getPSASCIIHex() ? "Hex" : "85");
02503     }
02504     if (useRLE) {
02505       writePS("    /RunLengthDecode filter\n");
02506     }
02507     if (useCompressed) {
02508       writePS(s->getCString());
02509     }
02510     if (s) {
02511       delete s;
02512     }
02513 
02514     // cut off inline image streams at appropriate length
02515     if (inlineImg) {
02516       str = new FixedLengthEncoder(str, len);
02517     } else if (useCompressed) {
02518       str = str->getBaseStream();
02519     }
02520 
02521     // add RunLengthEncode and ASCIIHex/85 encode filters
02522     if (useRLE) {
02523       str = new RunLengthEncoder(str);
02524     }
02525     if (useASCII) {
02526       if (globalParams->getPSASCIIHex()) {
02527     str = new ASCIIHexEncoder(str);
02528       } else {
02529     str = new ASCII85Encoder(str);
02530       }
02531     }
02532 
02533     // end of image dictionary
02534     writePS(">>\n");
02535 #if OPI_SUPPORT
02536     if (opi13Nest) {
02537       if (inlineImg) {
02538     // this can't happen -- OPI dictionaries are in XObjects
02539     error(-1, "Internal: OPI in inline image");
02540     n = 0;
02541       } else {
02542     // need to read the stream to count characters -- the length
02543     // is data-dependent (because of ASCII and RLE filters)
02544     str->reset();
02545     n = 0;
02546     while ((c = str->getChar()) != EOF) {
02547       ++n;
02548     }
02549     str->close();
02550       }
02551       // +6/7 for "pdfIm\n" / "pdfImM\n"
02552       // +8 for newline + trailer
02553       n += colorMap ? 14 : 15;
02554       writePSFmt("%%%%BeginData: %d Hex Bytes\n", n);
02555     }
02556 #endif
02557     if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
02558     colorMap->getColorSpace()->getMode() == csSeparation) {
02559       color.c[0] = 1;
02560       sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
02561       sepCS->getCMYK(&color, &cmyk);
02562       writePSFmt("%g %g %g %g (%s) pdfImSep\n",
02563          cmyk.c, cmyk.m, cmyk.y, cmyk.k,
02564          sepCS->getName()->getCString());
02565     } else {
02566       writePSFmt("%s\n", colorMap ? "pdfIm" : "pdfImM");
02567     }
02568 
02569     // copy the stream data
02570     str->reset();
02571     while ((c = str->getChar()) != EOF) {
02572       writePSChar(c);
02573     }
02574     str->close();
02575 
02576     // add newline and trailer to the end
02577     writePSChar('\n');
02578     writePS("%-EOD-\n");
02579 #if OPI_SUPPORT
02580     if (opi13Nest) {
02581       writePS("%%EndData\n");
02582     }
02583 #endif
02584 
02585     // delete encoders
02586     if (useRLE || useASCII || inlineImg) {
02587       delete str;
02588     }
02589   }
02590 }
02591 
02592 void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace) {
02593   GfxCalGrayColorSpace *calGrayCS;
02594   GfxCalRGBColorSpace *calRGBCS;
02595   GfxLabColorSpace *labCS;
02596   GfxIndexedColorSpace *indexedCS;
02597   GfxSeparationColorSpace *separationCS;
02598   GfxColorSpace *baseCS;
02599   Guchar *lookup, *p;
02600   double x[gfxColorMaxComps], y[gfxColorMaxComps];
02601   GfxColor color;
02602   GfxCMYK cmyk;
02603   Function *func;
02604   int n, numComps, numAltComps;
02605   int byte;
02606   int i, j, k;
02607 
02608   switch (colorSpace->getMode()) {
02609 
02610   case csDeviceGray:
02611     writePS("/DeviceGray");
02612     processColors |= psProcessBlack;
02613     break;
02614 
02615   case csCalGray:
02616     calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
02617     writePS("[/CIEBasedA <<\n");
02618     writePSFmt(" /DecodeA {%g exp} bind\n", calGrayCS->getGamma());
02619     writePSFmt(" /MatrixA [%g %g %g]\n",
02620            calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
02621            calGrayCS->getWhiteZ());
02622     writePSFmt(" /WhitePoint [%g %g %g]\n",
02623            calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
02624            calGrayCS->getWhiteZ());
02625     writePSFmt(" /BlackPoint [%g %g %g]\n",
02626            calGrayCS->getBlackX(), calGrayCS->getBlackY(),
02627            calGrayCS->getBlackZ());
02628     writePS(">>]");
02629     processColors |= psProcessBlack;
02630     break;
02631 
02632   case csDeviceRGB:
02633     writePS("/DeviceRGB");
02634     processColors |= psProcessCMYK;
02635     break;
02636 
02637   case csCalRGB:
02638     calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
02639     writePS("[/CIEBasedABC <<\n");
02640     writePSFmt(" /DecodeABC [{%g exp} bind {%g exp} bind {%g exp} bind]\n",
02641            calRGBCS->getGammaR(), calRGBCS->getGammaG(),
02642            calRGBCS->getGammaB());
02643     writePSFmt(" /MatrixABC [%g %g %g %g %g %g %g %g %g]\n",
02644            calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
02645            calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
02646            calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
02647            calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
02648            calRGBCS->getMatrix()[8]);
02649     writePSFmt(" /WhitePoint [%g %g %g]\n",
02650            calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
02651            calRGBCS->getWhiteZ());
02652     writePSFmt(" /BlackPoint [%g %g %g]\n",
02653            calRGBCS->getBlackX(), calRGBCS->getBlackY(),
02654            calRGBCS->getBlackZ());
02655     writePS(">>]");
02656     processColors |= psProcessCMYK;
02657     break;
02658 
02659   case csDeviceCMYK:
02660     writePS("/DeviceCMYK");
02661     processColors |= psProcessCMYK;
02662     break;
02663 
02664   case csLab:
02665     labCS = (GfxLabColorSpace *)colorSpace;
02666     writePS("[/CIEBasedABC <<\n");
02667     writePSFmt(" /RangeABC [0 100 %g %g %g %g]\n",
02668            labCS->getAMin(), labCS->getAMax(),
02669            labCS->getBMin(), labCS->getBMax());
02670     writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
02671     writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
02672     writePS(" /DecodeLMN\n");
02673     writePS("   [{dup 6 29 div ge {dup dup mul mul}\n");
02674     writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
02675            labCS->getWhiteX());
02676     writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
02677     writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind\n",
02678            labCS->getWhiteY());
02679     writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
02680     writePSFmt("     {4 29 div sub 108 841 div mul } ifelse %g mul} bind]\n",
02681            labCS->getWhiteZ());
02682     writePSFmt(" /WhitePoint [%g %g %g]\n",
02683            labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
02684     writePSFmt(" /BlackPoint [%g %g %g]\n",
02685            labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
02686     writePS(">>]");
02687     processColors |= psProcessCMYK;
02688     break;
02689 
02690   case csICCBased:
02691     dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt());
02692     break;
02693 
02694   case csIndexed:
02695     indexedCS = (GfxIndexedColorSpace *)colorSpace;
02696     baseCS = indexedCS->getBase();
02697     writePS("[/Indexed ");
02698     dumpColorSpaceL2(baseCS);
02699     n = indexedCS->getIndexHigh();
02700     numComps = baseCS->getNComps();
02701     lookup = indexedCS->getLookup();
02702     writePSFmt(" %d <\n", n);
02703     if (baseCS->getMode() == csDeviceN) {
02704       func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
02705       numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
02706       p = lookup;
02707       for (i = 0; i <= n; i += 8) {
02708     writePS("  ");
02709     for (j = i; j < i+8 && j <= n; ++j) {
02710       for (k = 0; k < numComps; ++k) {
02711         x[k] = *p++ / 255.0;
02712       }
02713       func->transform(x, y);
02714       for (k = 0; k < numAltComps; ++k) {
02715         byte = (int)(y[k] * 255 + 0.5);
02716         if (byte < 0) {
02717           byte = 0;
02718         } else if (byte > 255) {
02719           byte = 255;
02720         }
02721         writePSFmt("%02x", byte);
02722       }
02723       color.c[0] = j;
02724       indexedCS->getCMYK(&color, &cmyk);
02725       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
02726     }
02727     writePS("\n");
02728       }
02729     } else {
02730       for (i = 0; i <= n; i += 8) {
02731     writePS("  ");
02732     for (j = i; j < i+8 && j <= n; ++j) {
02733       for (k = 0; k < numComps; ++k) {
02734         writePSFmt("%02x", lookup[j * numComps + k]);
02735       }
02736       color.c[0] = j;
02737       indexedCS->getCMYK(&color, &cmyk);
02738       addProcessColor(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
02739     }
02740     writePS("\n");
02741       }
02742     }
02743     writePS(">]");
02744     break;
02745 
02746   case csSeparation:
02747     //~ this is a kludge -- the correct thing would to output a
02748     //~ separation color space, with the specified alternate color
02749     //~ space and tint transform
02750     separationCS = (GfxSeparationColorSpace *)colorSpace;
02751     writePS("[/Indexed ");
02752     dumpColorSpaceL2(separationCS->getAlt());
02753     writePS(" 255 <\n");
02754     numComps = separationCS->getAlt()->getNComps();
02755     for (i = 0; i <= 255; i += 8) {
02756       writePS("  ");
02757       for (j = i; j < i+8 && j <= 255; ++j) {
02758     x[0] = (double)j / 255.0;
02759     separationCS->getFunc()->transform(x, y);
02760     for (k = 0; k < numComps; ++k) {
02761       writePSFmt("%02x", (int)(255 * y[k] + 0.5));
02762     }
02763       }
02764       writePS("\n");
02765     }
02766     writePS(">]");
02767     addCustomColor(separationCS);
02768     break;
02769 
02770   case csDeviceN:
02771     // DeviceN color spaces are a Level 3 PostScript feature.
02772     dumpColorSpaceL2(((GfxDeviceNColorSpace *)colorSpace)->getAlt());
02773     break;
02774 
02775   case csPattern:
02776     //~ unimplemented
02777     break;
02778 
02779   }
02780 }
02781 
02782 #if OPI_SUPPORT
02783 void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
02784   Object dict;
02785 
02786   if (globalParams->getPSOPI()) {
02787     opiDict->lookup("2.0", &dict);
02788     if (dict.isDict()) {
02789       opiBegin20(state, dict.getDict());
02790       dict.free();
02791     } else {
02792       dict.free();
02793       opiDict->lookup("1.3", &dict);
02794       if (dict.isDict()) {
02795     opiBegin13(state, dict.getDict());
02796       }
02797       dict.free();
02798     }
02799   }
02800 }
02801 
02802 void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
02803   Object obj1, obj2, obj3, obj4;
02804   double width, height, left, right, top, bottom;
02805   int w, h;
02806   int i;
02807 
02808   writePS("%%BeginOPI: 2.0\n");
02809   writePS("%%Distilled\n");
02810 
02811   dict->lookup("F", &obj1);
02812   if (getFileSpec(&obj1, &obj2)) {
02813     writePSFmt("%%%%ImageFileName: %s\n",
02814            obj2.getString()->getCString());
02815     obj2.free();
02816   }
02817   obj1.free();
02818 
02819   dict->lookup("MainImage", &obj1);
02820   if (obj1.isString()) {
02821     writePSFmt("%%%%MainImage: %s\n", obj1.getString()->getCString());
02822   }
02823   obj1.free();
02824 
02825   //~ ignoring 'Tags' entry
02826   //~ need to use writePSString() and deal with >255-char lines
02827 
02828   dict->lookup("Size", &obj1);
02829   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
02830     obj1.arrayGet(0, &obj2);
02831     width = obj2.getNum();
02832     obj2.free();
02833     obj1.arrayGet(1, &obj2);
02834     height = obj2.getNum();
02835     obj2.free();
02836     writePSFmt("%%%%ImageDimensions: %g %g\n", width, height);
02837   }
02838   obj1.free();
02839 
02840   dict->lookup("CropRect", &obj1);
02841   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
02842     obj1.arrayGet(0, &obj2);
02843     left = obj2.getNum();
02844     obj2.free();
02845     obj1.arrayGet(1, &obj2);
02846     top = obj2.getNum();
02847     obj2.free();
02848     obj1.arrayGet(2, &obj2);
02849     right = obj2.getNum();
02850     obj2.free();
02851     obj1.arrayGet(3, &obj2);
02852     bottom = obj2.getNum();
02853     obj2.free();
02854     writePSFmt("%%%%ImageCropRect: %g %g %g %g\n", left, top, right, bottom);
02855   }
02856   obj1.free();
02857 
02858   dict->lookup("Overprint", &obj1);
02859   if (obj1.isBool()) {
02860     writePSFmt("%%%%ImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
02861   }
02862   obj1.free();
02863 
02864   dict->lookup("Inks", &obj1);
02865   if (obj1.isName()) {
02866     writePSFmt("%%%%ImageInks: %s\n", obj1.getName());
02867   } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
02868     obj1.arrayGet(0, &obj2);
02869     if (obj2.isName()) {
02870       writePSFmt("%%%%ImageInks: %s %d",
02871          obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
02872       for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
02873     obj1.arrayGet(i, &obj3);
02874     obj1.arrayGet(i+1, &obj4);
02875     if (obj3.isString() && obj4.isNum()) {
02876       writePS(" ");
02877       writePSString(obj3.getString());
02878       writePSFmt(" %g", obj4.getNum());
02879     }
02880     obj3.free();
02881     obj4.free();
02882       }
02883       writePS("\n");
02884     }
02885     obj2.free();
02886   }
02887   obj1.free();
02888 
02889   writePS("gsave\n");
02890 
02891   writePS("%%BeginIncludedImage\n");
02892 
02893   dict->lookup("IncludedImageDimensions", &obj1);
02894   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
02895     obj1.arrayGet(0, &obj2);
02896     w = obj2.getInt();
02897     obj2.free();
02898     obj1.arrayGet(1, &obj2);
02899     h = obj2.getInt();
02900     obj2.free();
02901     writePSFmt("%%%%IncludedImageDimensions: %d %d\n", w, h);
02902   }
02903   obj1.free();
02904 
02905   dict->lookup("IncludedImageQuality", &obj1);
02906   if (obj1.isNum()) {
02907     writePSFmt("%%%%IncludedImageQuality: %g\n", obj1.getNum());
02908   }
02909   obj1.free();
02910 
02911   ++opi20Nest;
02912 }
02913 
02914 void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
02915   Object obj1, obj2;
02916   int left, right, top, bottom, samples, bits, width, height;
02917   double c, m, y, k;
02918   double llx, lly, ulx, uly, urx, ury, lrx, lry;
02919   double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
02920   double horiz, vert;
02921   int i, j;
02922 
02923   writePS("save\n");
02924   writePS("/opiMatrix2 matrix currentmatrix def\n");
02925   writePS("opiMatrix setmatrix\n");
02926 
02927   dict->lookup("F", &obj1);
02928   if (getFileSpec(&obj1, &obj2)) {
02929     writePSFmt("%%ALDImageFileName: %s\n",
02930            obj2.getString()->getCString());
02931     obj2.free();
02932   }
02933   obj1.free();
02934 
02935   dict->lookup("CropRect", &obj1);
02936   if (obj1.isArray() && obj1.arrayGetLength() == 4) {
02937     obj1.arrayGet(0, &obj2);
02938     left = obj2.getInt();
02939     obj2.free();
02940     obj1.arrayGet(1, &obj2);
02941     top = obj2.getInt();
02942     obj2.free();
02943     obj1.arrayGet(2, &obj2);
02944     right = obj2.getInt();
02945     obj2.free();
02946     obj1.arrayGet(3, &obj2);
02947     bottom = obj2.getInt();
02948     obj2.free();
02949     writePSFmt("%%ALDImageCropRect: %d %d %d %d\n", left, top, right, bottom);
02950   }
02951   obj1.free();
02952 
02953   dict->lookup("Color", &obj1);
02954   if (obj1.isArray() && obj1.arrayGetLength() == 5) {
02955     obj1.arrayGet(0, &obj2);
02956     c = obj2.getNum();
02957     obj2.free();
02958     obj1.arrayGet(1, &obj2);
02959     m = obj2.getNum();
02960     obj2.free();
02961     obj1.arrayGet(2, &obj2);
02962     y = obj2.getNum();
02963     obj2.free();
02964     obj1.arrayGet(3, &obj2);
02965     k = obj2.getNum();
02966     obj2.free();
02967     obj1.arrayGet(4, &obj2);
02968     if (obj2.isString()) {
02969       writePSFmt("%%ALDImageColor: %g %g %g %g ", c, m, y, k);
02970       writePSString(obj2.getString());
02971       writePS("\n");
02972     }
02973     obj2.free();
02974   }
02975   obj1.free();
02976 
02977   dict->lookup("ColorType", &obj1);
02978   if (obj1.isName()) {
02979     writePSFmt("%%ALDImageColorType: %s\n", obj1.getName());
02980   }
02981   obj1.free();
02982 
02983   //~ ignores 'Comments' entry
02984   //~ need to handle multiple lines
02985 
02986   dict->lookup("CropFixed", &obj1);
02987   if (obj1.isArray()) {
02988     obj1.arrayGet(0, &obj2);
02989     ulx = obj2.getNum();
02990     obj2.free();
02991     obj1.arrayGet(1, &obj2);
02992     uly = obj2.getNum();
02993     obj2.free();
02994     obj1.arrayGet(2, &obj2);
02995     lrx = obj2.getNum();
02996     obj2.free();
02997     obj1.arrayGet(3, &obj2);
02998     lry = obj2.getNum();
02999     obj2.free();
03000     writePSFmt("%%ALDImageCropFixed: %g %g %g %g\n", ulx, uly, lrx, lry);
03001   }
03002   obj1.free();
03003 
03004   dict->lookup("GrayMap", &obj1);
03005   if (obj1.isArray()) {
03006     writePS("%ALDImageGrayMap:");
03007     for (i = 0; i < obj1.arrayGetLength(); i += 16) {
03008       if (i > 0) {
03009     writePS("\n%%+");
03010       }
03011       for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
03012     obj1.arrayGet(i+j, &obj2);
03013     writePSFmt(" %d", obj2.getInt());
03014     obj2.free();
03015       }
03016     }
03017     writePS("\n");
03018   }
03019   obj1.free();
03020 
03021   dict->lookup("ID", &obj1);
03022   if (obj1.isString()) {
03023     writePSFmt("%%ALDImageID: %s\n", obj1.getString()->getCString());
03024   }
03025   obj1.free();
03026 
03027   dict->lookup("ImageType", &obj1);
03028   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
03029     obj1.arrayGet(0, &obj2);
03030     samples = obj2.getInt();
03031     obj2.free();
03032     obj1.arrayGet(1, &obj2);
03033     bits = obj2.getInt();
03034     obj2.free();
03035     writePSFmt("%%ALDImageType: %d %d\n", samples, bits);
03036   }
03037   obj1.free();
03038 
03039   dict->lookup("Overprint", &obj1);
03040   if (obj1.isBool()) {
03041     writePSFmt("%%ALDImageOverprint: %s\n", obj1.getBool() ? "true" : "false");
03042   }
03043   obj1.free();
03044 
03045   dict->lookup("Position", &obj1);
03046   if (obj1.isArray() && obj1.arrayGetLength() == 8) {
03047     obj1.arrayGet(0, &obj2);
03048     llx = obj2.getNum();
03049     obj2.free();
03050     obj1.arrayGet(1, &obj2);
03051     lly = obj2.getNum();
03052     obj2.free();
03053     obj1.arrayGet(2, &obj2);
03054     ulx = obj2.getNum();
03055     obj2.free();
03056     obj1.arrayGet(3, &obj2);
03057     uly = obj2.getNum();
03058     obj2.free();
03059     obj1.arrayGet(4, &obj2);
03060     urx = obj2.getNum();
03061     obj2.free();
03062     obj1.arrayGet(5, &obj2);
03063     ury = obj2.getNum();
03064     obj2.free();
03065     obj1.arrayGet(6, &obj2);
03066     lrx = obj2.getNum();
03067     obj2.free();
03068     obj1.arrayGet(7, &obj2);
03069     lry = obj2.getNum();
03070     obj2.free();
03071     opiTransform(state, llx, lly, &tllx, &tlly);
03072     opiTransform(state, ulx, uly, &tulx, &tuly);
03073     opiTransform(state, urx, ury, &turx, &tury);
03074     opiTransform(state, lrx, lry, &tlrx, &tlry);
03075     writePSFmt("%%ALDImagePosition: %g %g %g %g %g %g %g %g\n",
03076            tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
03077     obj2.free();
03078   }
03079   obj1.free();
03080 
03081   dict->lookup("Resolution", &obj1);
03082   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
03083     obj1.arrayGet(0, &obj2);
03084     horiz = obj2.getNum();
03085     obj2.free();
03086     obj1.arrayGet(1, &obj2);
03087     vert = obj2.getNum();
03088     obj2.free();
03089     writePSFmt("%%ALDImageResoution: %g %g\n", horiz, vert);
03090     obj2.free();
03091   }
03092   obj1.free();
03093 
03094   dict->lookup("Size", &obj1);
03095   if (obj1.isArray() && obj1.arrayGetLength() == 2) {
03096     obj1.arrayGet(0, &obj2);
03097     width = obj2.getInt();
03098     obj2.free();
03099     obj1.arrayGet(1, &obj2);
03100     height = obj2.getInt();
03101     obj2.free();
03102     writePSFmt("%%ALDImageDimensions: %d %d\n", width, height);
03103   }
03104   obj1.free();
03105 
03106   //~ ignoring 'Tags' entry
03107   //~ need to use writePSString() and deal with >255-char lines
03108 
03109   dict->lookup("Tint", &obj1);
03110   if (obj1.isNum()) {
03111     writePSFmt("%%ALDImageTint: %g\n", obj1.getNum());
03112   }
03113   obj1.free();
03114 
03115   dict->lookup("Transparency", &obj1);
03116   if (obj1.isBool()) {
03117     writePSFmt("%%ALDImageTransparency: %s\n", obj1.getBool() ? "true" : "false");
03118   }
03119   obj1.free();
03120 
03121   writePS("%%BeginObject: image\n");
03122   writePS("opiMatrix2 setmatrix\n");
03123   ++opi13Nest;
03124 }
03125 
03126 // Convert PDF user space coordinates to PostScript default user space
03127 // coordinates.  This has to account for both the PDF CTM and the
03128 // PSOutputDev page-fitting transform.
03129 void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
03130                    double *x1, double *y1) {
03131   double t;
03132 
03133   state->transform(x0, y0, x1, y1);
03134   *x1 += tx;
03135   *y1 += ty;
03136   if (landscape) {
03137     t = *x1;
03138     *x1 = -*y1;
03139     *y1 = t;
03140   }
03141   *x1 *= xScale;
03142   *y1 *= yScale;
03143 }
03144 
03145 void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
03146   Object dict;
03147 
03148   if (globalParams->getPSOPI()) {
03149     opiDict->lookup("2.0", &dict);
03150     if (dict.isDict()) {
03151       writePS("%%EndIncludedImage\n");
03152       writePS("%%EndOPI\n");
03153       writePS("grestore\n");
03154       --opi20Nest;
03155       dict.free();
03156     } else {
03157       dict.free();
03158       opiDict->lookup("1.3", &dict);
03159       if (dict.isDict()) {
03160     writePS("%%EndObject\n");
03161     writePS("restore\n");
03162     --opi13Nest;
03163       }
03164       dict.free();
03165     }
03166   }
03167 }
03168 
03169 GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
03170   if (fileSpec->isString()) {
03171     fileSpec->copy(fileName);
03172     return gTrue;
03173   }
03174   if (fileSpec->isDict()) {
03175     fileSpec->dictLookup("DOS", fileName);
03176     if (fileName->isString()) {
03177       return gTrue;
03178     }
03179     fileName->free();
03180     fileSpec->dictLookup("Mac", fileName);
03181     if (fileName->isString()) {
03182       return gTrue;
03183     }
03184     fileName->free();
03185     fileSpec->dictLookup("Unix", fileName);
03186     if (fileName->isString()) {
03187       return gTrue;
03188     }
03189     fileName->free();
03190     fileSpec->dictLookup("F", fileName);
03191     if (fileName->isString()) {
03192       return gTrue;
03193     }
03194     fileName->free();
03195   }
03196   return gFalse;
03197 }
03198 #endif // OPI_SUPPORT
03199 
03200 void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
03201   writePSFmt("%g %g setcharwidth\n", wx, wy);
03202   writePS("q\n");
03203 }
03204 
03205 void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
03206               double llx, double lly, double urx, double ury) {
03207   t3WX = wx;
03208   t3WY = wy;
03209   t3LLX = llx;
03210   t3LLY = lly;
03211   t3URX = urx;
03212   t3URY = ury;
03213   t3String = new GString();
03214   writePS("q\n");
03215   t3Cacheable = gTrue;
03216 }
03217 
03218 void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
03219   Stream *str;
03220   int c;
03221 
03222   if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
03223     str = level1Stream;
03224   } else {
03225     str = psStream;
03226   }
03227   str->reset();
03228   while ((c = str->getChar()) != EOF) {
03229     writePSChar(c);
03230   }
03231   str->close();
03232 }
03233 
03234 void PSOutputDev::writePSChar(char c) {
03235   if (t3String) {
03236     t3String->append(c);
03237   } else {
03238     (*outputFunc)(outputStream, &c, 1);
03239   }
03240 }
03241 
03242 void PSOutputDev::writePS(char *s) {
03243   if (t3String) {
03244     t3String->append(s);
03245   } else {
03246     (*outputFunc)(outputStream, s, strlen(s));
03247   }
03248 }
03249 
03250 void PSOutputDev::writePSFmt(const char *fmt, ...) {
03251   va_list args;
03252   char buf[512];
03253 
03254   va_start(args, fmt);
03255   vsprintf(buf, fmt, args);
03256   va_end(args);
03257   if (t3String) {
03258     t3String->append(buf);
03259   } else {
03260     (*outputFunc)(outputStream, buf, strlen(buf));
03261   }
03262 }
03263 
03264 void PSOutputDev::writePSString(GString *s) {
03265   Guchar *p;
03266   int n;
03267   char buf[8];
03268 
03269   writePSChar('(');
03270   for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
03271     if (*p == '(' || *p == ')' || *p == '\\') {
03272       writePSChar('\\');
03273       writePSChar((char)*p);
03274     } else if (*p < 0x20 || *p >= 0x80) {
03275       sprintf(buf, "\\%03o", *p);
03276       if (t3String) {
03277     t3String->append(buf);
03278       } else {
03279     (*outputFunc)(outputStream, buf, strlen(buf));
03280       }
03281     } else {
03282       writePSChar((char)*p);
03283     }
03284   }
03285   writePSChar(')');
03286 }
03287 
03288 void PSOutputDev::writePSName(char *s) {
03289   char *p;
03290   char c;
03291 
03292   p = s;
03293   while ((c = *p++)) {
03294     if (c <= (char)0x20 || c >= (char)0x7f ||
03295     c == '(' || c == ')' || c == '<' || c == '>' ||
03296     c == '[' || c == ']' || c == '{' || c == '}' ||
03297     c == '/' || c == '%') {
03298       writePSFmt("#%02x", c & 0xff);
03299     } else {
03300       writePSChar(c);
03301     }
03302   }
03303 }
03304 
03305 GString *PSOutputDev::filterPSName(GString *name) {
03306   GString *name2;
03307   char buf[8];
03308   int i;
03309   char c;
03310 
03311   name2 = new GString();
03312   for (i = 0; i < name->getLength(); ++i) {
03313     c = name->getChar(i);
03314     if (c <= (char)0x20 || c >= (char)0x7f ||
03315     c == '(' || c == ')' || c == '<' || c == '>' ||
03316     c == '[' || c == ']' || c == '{' || c == '}' ||
03317     c == '/' || c == '%') {
03318       sprintf(buf, "#%02x", c & 0xff);
03319       name2->append(buf);
03320     } else {
03321       name2->append(c);
03322     }
03323   }
03324   return name2;
03325 }
KDE Home | KDE Accessibility Home | Description of Access Keys