filters

Gfx.cc

00001 //========================================================================
00002 //
00003 // Gfx.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 <string.h>
00018 #include <math.h>
00019 #include "gmem.h"
00020 #include "GlobalParams.h"
00021 #include "CharTypes.h"
00022 #include "Object.h"
00023 #include "Array.h"
00024 #include "Dict.h"
00025 #include "Stream.h"
00026 #include "Lexer.h"
00027 #include "Parser.h"
00028 #include "GfxFont.h"
00029 #include "GfxState.h"
00030 #include "OutputDev.h"
00031 #include "Page.h"
00032 #include "Error.h"
00033 #include "Gfx.h"
00034 
00035 // the MSVC math.h doesn't define this
00036 #ifndef M_PI
00037 #define M_PI 3.14159265358979323846
00038 #endif
00039 
00040 //------------------------------------------------------------------------
00041 // constants
00042 //------------------------------------------------------------------------
00043 
00044 // Max number of splits along the t axis for an axial shading fill.
00045 #define axialMaxSplits 256
00046 
00047 // Max delta allowed in any color component for an axial shading fill.
00048 #define axialColorDelta (1 / 256.0)
00049 
00050 // Max number of splits along the t axis for a radial shading fill.
00051 #define radialMaxSplits 256
00052 
00053 // Max delta allowed in any color component for a radial shading fill.
00054 #define radialColorDelta (1 / 256.0)
00055 
00056 //------------------------------------------------------------------------
00057 // Operator table
00058 //------------------------------------------------------------------------
00059 
00060 Operator Gfx::opTab[] = {
00061   {"\"",  3, {tchkNum,    tchkNum,    tchkString},
00062           &Gfx::opMoveSetShowText},
00063   {"'",   1, {tchkString},
00064           &Gfx::opMoveShowText},
00065   {"B",   0, {tchkNone},
00066           &Gfx::opFillStroke},
00067   {"B*",  0, {tchkNone},
00068           &Gfx::opEOFillStroke},
00069   {"BDC", 2, {tchkName,   tchkProps},
00070           &Gfx::opBeginMarkedContent},
00071   {"BI",  0, {tchkNone},
00072           &Gfx::opBeginImage},
00073   {"BMC", 1, {tchkName},
00074           &Gfx::opBeginMarkedContent},
00075   {"BT",  0, {tchkNone},
00076           &Gfx::opBeginText},
00077   {"BX",  0, {tchkNone},
00078           &Gfx::opBeginIgnoreUndef},
00079   {"CS",  1, {tchkName},
00080           &Gfx::opSetStrokeColorSpace},
00081   {"DP",  2, {tchkName,   tchkProps},
00082           &Gfx::opMarkPoint},
00083   {"Do",  1, {tchkName},
00084           &Gfx::opXObject},
00085   {"EI",  0, {tchkNone},
00086           &Gfx::opEndImage},
00087   {"EMC", 0, {tchkNone},
00088           &Gfx::opEndMarkedContent},
00089   {"ET",  0, {tchkNone},
00090           &Gfx::opEndText},
00091   {"EX",  0, {tchkNone},
00092           &Gfx::opEndIgnoreUndef},
00093   {"F",   0, {tchkNone},
00094           &Gfx::opFill},
00095   {"G",   1, {tchkNum},
00096           &Gfx::opSetStrokeGray},
00097   {"ID",  0, {tchkNone},
00098           &Gfx::opImageData},
00099   {"J",   1, {tchkInt},
00100           &Gfx::opSetLineCap},
00101   {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
00102           &Gfx::opSetStrokeCMYKColor},
00103   {"M",   1, {tchkNum},
00104           &Gfx::opSetMiterLimit},
00105   {"MP",  1, {tchkName},
00106           &Gfx::opMarkPoint},
00107   {"Q",   0, {tchkNone},
00108           &Gfx::opRestore},
00109   {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
00110           &Gfx::opSetStrokeRGBColor},
00111   {"S",   0, {tchkNone},
00112           &Gfx::opStroke},
00113   {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
00114           &Gfx::opSetStrokeColor},
00115   {"SCN", -5, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
00116            tchkSCN},
00117           &Gfx::opSetStrokeColorN},
00118   {"T*",  0, {tchkNone},
00119           &Gfx::opTextNextLine},
00120   {"TD",  2, {tchkNum,    tchkNum},
00121           &Gfx::opTextMoveSet},
00122   {"TJ",  1, {tchkArray},
00123           &Gfx::opShowSpaceText},
00124   {"TL",  1, {tchkNum},
00125           &Gfx::opSetTextLeading},
00126   {"Tc",  1, {tchkNum},
00127           &Gfx::opSetCharSpacing},
00128   {"Td",  2, {tchkNum,    tchkNum},
00129           &Gfx::opTextMove},
00130   {"Tf",  2, {tchkName,   tchkNum},
00131           &Gfx::opSetFont},
00132   {"Tj",  1, {tchkString},
00133           &Gfx::opShowText},
00134   {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
00135           tchkNum,    tchkNum},
00136           &Gfx::opSetTextMatrix},
00137   {"Tr",  1, {tchkInt},
00138           &Gfx::opSetTextRender},
00139   {"Ts",  1, {tchkNum},
00140           &Gfx::opSetTextRise},
00141   {"Tw",  1, {tchkNum},
00142           &Gfx::opSetWordSpacing},
00143   {"Tz",  1, {tchkNum},
00144           &Gfx::opSetHorizScaling},
00145   {"W",   0, {tchkNone},
00146           &Gfx::opClip},
00147   {"W*",  0, {tchkNone},
00148           &Gfx::opEOClip},
00149   {"b",   0, {tchkNone},
00150           &Gfx::opCloseFillStroke},
00151   {"b*",  0, {tchkNone},
00152           &Gfx::opCloseEOFillStroke},
00153   {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
00154           tchkNum,    tchkNum},
00155           &Gfx::opCurveTo},
00156   {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
00157           tchkNum,    tchkNum},
00158           &Gfx::opConcat},
00159   {"cs",  1, {tchkName},
00160           &Gfx::opSetFillColorSpace},
00161   {"d",   2, {tchkArray,  tchkNum},
00162           &Gfx::opSetDash},
00163   {"d0",  2, {tchkNum,    tchkNum},
00164           &Gfx::opSetCharWidth},
00165   {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
00166           tchkNum,    tchkNum},
00167           &Gfx::opSetCacheDevice},
00168   {"f",   0, {tchkNone},
00169           &Gfx::opFill},
00170   {"f*",  0, {tchkNone},
00171           &Gfx::opEOFill},
00172   {"g",   1, {tchkNum},
00173           &Gfx::opSetFillGray},
00174   {"gs",  1, {tchkName},
00175           &Gfx::opSetExtGState},
00176   {"h",   0, {tchkNone},
00177           &Gfx::opClosePath},
00178   {"i",   1, {tchkNum},
00179           &Gfx::opSetFlat},
00180   {"j",   1, {tchkInt},
00181           &Gfx::opSetLineJoin},
00182   {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
00183           &Gfx::opSetFillCMYKColor},
00184   {"l",   2, {tchkNum,    tchkNum},
00185           &Gfx::opLineTo},
00186   {"m",   2, {tchkNum,    tchkNum},
00187           &Gfx::opMoveTo},
00188   {"n",   0, {tchkNone},
00189           &Gfx::opEndPath},
00190   {"q",   0, {tchkNone},
00191           &Gfx::opSave},
00192   {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
00193           &Gfx::opRectangle},
00194   {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
00195           &Gfx::opSetFillRGBColor},
00196   {"ri",  1, {tchkName},
00197           &Gfx::opSetRenderingIntent},
00198   {"s",   0, {tchkNone},
00199           &Gfx::opCloseStroke},
00200   {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
00201           &Gfx::opSetFillColor},
00202   {"scn", -5, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
00203            tchkSCN},
00204           &Gfx::opSetFillColorN},
00205   {"sh",  1, {tchkName},
00206           &Gfx::opShFill},
00207   {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
00208           &Gfx::opCurveTo1},
00209   {"w",   1, {tchkNum},
00210           &Gfx::opSetLineWidth},
00211   {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
00212           &Gfx::opCurveTo2},
00213 };
00214 
00215 #define numOps (sizeof(opTab) / sizeof(Operator))
00216 
00217 //------------------------------------------------------------------------
00218 // GfxResources
00219 //------------------------------------------------------------------------
00220 
00221 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
00222   Object obj1;
00223 
00224   if (resDict) {
00225 
00226     // build font dictionary
00227     fonts = NULL;
00228     resDict->lookup("Font", &obj1);
00229     if (obj1.isDict()) {
00230       fonts = new GfxFontDict(xref, obj1.getDict());
00231     }
00232     obj1.free();
00233 
00234     // get XObject dictionary
00235     resDict->lookup("XObject", &xObjDict);
00236 
00237     // get color space dictionary
00238     resDict->lookup("ColorSpace", &colorSpaceDict);
00239 
00240     // get pattern dictionary
00241     resDict->lookup("Pattern", &patternDict);
00242 
00243     // get shading dictionary
00244     resDict->lookup("Shading", &shadingDict);
00245 
00246     // get graphics state parameter dictionary
00247     resDict->lookup("ExtGState", &gStateDict);
00248 
00249   } else {
00250     fonts = NULL;
00251     xObjDict.initNull();
00252     colorSpaceDict.initNull();
00253     patternDict.initNull();
00254     gStateDict.initNull();
00255   }
00256 
00257   next = nextA;
00258 }
00259 
00260 GfxResources::~GfxResources() {
00261   if (fonts) {
00262     delete fonts;
00263   }
00264   xObjDict.free();
00265   colorSpaceDict.free();
00266   patternDict.free();
00267   shadingDict.free();
00268   gStateDict.free();
00269 }
00270 
00271 GfxFont *GfxResources::lookupFont(char *name) {
00272   GfxFont *font;
00273   GfxResources *resPtr;
00274 
00275   for (resPtr = this; resPtr; resPtr = resPtr->next) {
00276     if (resPtr->fonts) {
00277       if ((font = resPtr->fonts->lookup(name)))
00278     return font;
00279     }
00280   }
00281   error(-1, "Unknown font tag '%s'", name);
00282   return NULL;
00283 }
00284 
00285 GBool GfxResources::lookupXObject(char *name, Object *obj) {
00286   GfxResources *resPtr;
00287 
00288   for (resPtr = this; resPtr; resPtr = resPtr->next) {
00289     if (resPtr->xObjDict.isDict()) {
00290       if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
00291     return gTrue;
00292       obj->free();
00293     }
00294   }
00295   error(-1, "XObject '%s' is unknown", name);
00296   return gFalse;
00297 }
00298 
00299 GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
00300   GfxResources *resPtr;
00301 
00302   for (resPtr = this; resPtr; resPtr = resPtr->next) {
00303     if (resPtr->xObjDict.isDict()) {
00304       if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
00305     return gTrue;
00306       obj->free();
00307     }
00308   }
00309   error(-1, "XObject '%s' is unknown", name);
00310   return gFalse;
00311 }
00312 
00313 void GfxResources::lookupColorSpace(char *name, Object *obj) {
00314   GfxResources *resPtr;
00315 
00316   for (resPtr = this; resPtr; resPtr = resPtr->next) {
00317     if (resPtr->colorSpaceDict.isDict()) {
00318       if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
00319     return;
00320       }
00321       obj->free();
00322     }
00323   }
00324   obj->initNull();
00325 }
00326 
00327 GfxPattern *GfxResources::lookupPattern(char *name) {
00328   GfxResources *resPtr;
00329   GfxPattern *pattern;
00330   Object obj;
00331 
00332   for (resPtr = this; resPtr; resPtr = resPtr->next) {
00333     if (resPtr->patternDict.isDict()) {
00334       if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
00335     pattern = GfxPattern::parse(&obj);
00336     obj.free();
00337     return pattern;
00338       }
00339       obj.free();
00340     }
00341   }
00342   error(-1, "Unknown pattern '%s'", name);
00343   return NULL;
00344 }
00345 
00346 GfxShading *GfxResources::lookupShading(char *name) {
00347   GfxResources *resPtr;
00348   GfxShading *shading;
00349   Object obj;
00350 
00351   for (resPtr = this; resPtr; resPtr = resPtr->next) {
00352     if (resPtr->shadingDict.isDict()) {
00353       if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
00354     shading = GfxShading::parse(&obj);
00355     obj.free();
00356     return shading;
00357       }
00358       obj.free();
00359     }
00360   }
00361   error(-1, "Unknown shading '%s'", name);
00362   return NULL;
00363 }
00364 
00365 GBool GfxResources::lookupGState(char *name, Object *obj) {
00366   GfxResources *resPtr;
00367 
00368   for (resPtr = this; resPtr; resPtr = resPtr->next) {
00369     if (resPtr->gStateDict.isDict()) {
00370       if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
00371     return gTrue;
00372       }
00373       obj->free();
00374     }
00375   }
00376   error(-1, "ExtGState '%s' is unknown", name);
00377   return gFalse;
00378 }
00379 
00380 //------------------------------------------------------------------------
00381 // Gfx
00382 //------------------------------------------------------------------------
00383 
00384 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
00385      PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
00386      GBool (*abortCheckCbkA)(void *data),
00387      void *abortCheckCbkDataA) {
00388   int i;
00389 
00390   xref = xrefA;
00391   subPage = gFalse;
00392   printCommands = globalParams->getPrintCommands();
00393 
00394   // start the resource stack
00395   res = new GfxResources(xref, resDict, NULL);
00396 
00397   // initialize
00398   out = outA;
00399   state = new GfxState(dpi, box, rotate, out->upsideDown());
00400   fontChanged = gFalse;
00401   clip = clipNone;
00402   ignoreUndef = 0;
00403   out->startPage(pageNum, state);
00404   out->setDefaultCTM(state->getCTM());
00405   out->updateAll(state);
00406   for (i = 0; i < 6; ++i) {
00407     baseMatrix[i] = state->getCTM()[i];
00408   }
00409   abortCheckCbk = abortCheckCbkA;
00410   abortCheckCbkData = abortCheckCbkDataA;
00411 
00412   // set crop box
00413   if (crop) {
00414     state->moveTo(cropBox->x1, cropBox->y1);
00415     state->lineTo(cropBox->x2, cropBox->y1);
00416     state->lineTo(cropBox->x2, cropBox->y2);
00417     state->lineTo(cropBox->x1, cropBox->y2);
00418     state->closePath();
00419     state->clip();
00420     out->clip(state);
00421     state->clearPath();
00422   }
00423 }
00424 
00425 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
00426      PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
00427      GBool (*abortCheckCbkA)(void *data),
00428      void *abortCheckCbkDataA) {
00429   int i;
00430 
00431   xref = xrefA;
00432   subPage = gTrue;
00433   printCommands = globalParams->getPrintCommands();
00434 
00435   // start the resource stack
00436   res = new GfxResources(xref, resDict, NULL);
00437 
00438   // initialize
00439   out = outA;
00440   state = new GfxState(72, box, 0, gFalse);
00441   fontChanged = gFalse;
00442   clip = clipNone;
00443   ignoreUndef = 0;
00444   for (i = 0; i < 6; ++i) {
00445     baseMatrix[i] = state->getCTM()[i];
00446   }
00447   abortCheckCbk = abortCheckCbkA;
00448   abortCheckCbkData = abortCheckCbkDataA;
00449 
00450   // set crop box
00451   if (crop) {
00452     state->moveTo(cropBox->x1, cropBox->y1);
00453     state->lineTo(cropBox->x2, cropBox->y1);
00454     state->lineTo(cropBox->x2, cropBox->y2);
00455     state->lineTo(cropBox->x1, cropBox->y2);
00456     state->closePath();
00457     state->clip();
00458     out->clip(state);
00459     state->clearPath();
00460   }
00461 }
00462 
00463 Gfx::~Gfx() {
00464   while (state->hasSaves()) {
00465     state = state->restore();
00466     out->restoreState(state);
00467   }
00468   if (!subPage) {
00469     out->endPage();
00470   }
00471   while (res) {
00472     popResources();
00473   }
00474   if (state) {
00475     delete state;
00476   }
00477 }
00478 
00479 void Gfx::display(Object *obj, GBool topLevel) {
00480   Object obj2;
00481   int i;
00482 
00483   if (obj->isArray()) {
00484     for (i = 0; i < obj->arrayGetLength(); ++i) {
00485       obj->arrayGet(i, &obj2);
00486       if (!obj2.isStream()) {
00487     error(-1, "Weird page contents");
00488     obj2.free();
00489     return;
00490       }
00491       obj2.free();
00492     }
00493   } else if (!obj->isStream()) {
00494     error(-1, "Weird page contents");
00495     return;
00496   }
00497   parser = new Parser(xref, new Lexer(xref, obj));
00498   go(topLevel);
00499   delete parser;
00500   parser = NULL;
00501 }
00502 
00503 void Gfx::go(GBool topLevel) {
00504   Object obj;
00505   Object args[maxArgs];
00506   int numArgs, i;
00507   int lastAbortCheck;
00508 
00509   // scan a sequence of objects
00510   updateLevel = lastAbortCheck = 0;
00511   numArgs = 0;
00512   parser->getObj(&obj);
00513   while (!obj.isEOF()) {
00514 
00515     // got a command - execute it
00516     if (obj.isCmd()) {
00517       if (printCommands) {
00518     obj.print(stdout);
00519     for (i = 0; i < numArgs; ++i) {
00520       printf(" ");
00521       args[i].print(stdout);
00522     }
00523     printf("\n");
00524     fflush(stdout);
00525       }
00526       execOp(&obj, args, numArgs);
00527       obj.free();
00528       for (i = 0; i < numArgs; ++i)
00529     args[i].free();
00530       numArgs = 0;
00531 
00532       // periodically update display
00533       if (++updateLevel >= 20000) {
00534     out->dump();
00535     updateLevel = 0;
00536       }
00537 
00538       // check for an abort
00539       if (abortCheckCbk) {
00540     if (updateLevel - lastAbortCheck > 10) {
00541       if ((*abortCheckCbk)(abortCheckCbkData)) {
00542         break;
00543       }
00544       lastAbortCheck = updateLevel;
00545     }
00546       }
00547 
00548     // got an argument - save it
00549     } else if (numArgs < maxArgs) {
00550       args[numArgs++] = obj;
00551 
00552     // too many arguments - something is wrong
00553     } else {
00554       error(getPos(), "Too many args in content stream");
00555       if (printCommands) {
00556     printf("throwing away arg: ");
00557     obj.print(stdout);
00558     printf("\n");
00559     fflush(stdout);
00560       }
00561       obj.free();
00562     }
00563 
00564     // grab the next object
00565     parser->getObj(&obj);
00566   }
00567   obj.free();
00568 
00569   // args at end with no command
00570   if (numArgs > 0) {
00571     error(getPos(), "Leftover args in content stream");
00572     if (printCommands) {
00573       printf("%d leftovers:", numArgs);
00574       for (i = 0; i < numArgs; ++i) {
00575     printf(" ");
00576     args[i].print(stdout);
00577       }
00578       printf("\n");
00579       fflush(stdout);
00580     }
00581     for (i = 0; i < numArgs; ++i)
00582       args[i].free();
00583   }
00584 
00585   // update display
00586   if (topLevel && updateLevel > 0) {
00587     out->dump();
00588   }
00589 }
00590 
00591 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
00592   Operator *op;
00593   const char *name;
00594   int i;
00595 
00596   // find operator
00597   name = cmd->getCmd();
00598   if (!(op = findOp(name))) {
00599     if (ignoreUndef == 0)
00600       error(getPos(), "Unknown operator '%s'", name);
00601     return;
00602   }
00603 
00604   // type check args
00605   if (op->numArgs >= 0) {
00606     if (numArgs != op->numArgs) {
00607       error(getPos(), "Wrong number (%d) of args to '%s' operator",
00608         numArgs, name);
00609       return;
00610     }
00611   } else {
00612     if (numArgs > -op->numArgs) {
00613       error(getPos(), "Too many (%d) args to '%s' operator",
00614         numArgs, name);
00615       return;
00616     }
00617   }
00618   for (i = 0; i < numArgs; ++i) {
00619     if (!checkArg(&args[i], op->tchk[i])) {
00620       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
00621         i, name, args[i].getTypeName());
00622       return;
00623     }
00624   }
00625 
00626   // do it
00627   (this->*op->func)(args, numArgs);
00628 }
00629 
00630 Operator *Gfx::findOp(const char *name) {
00631   int a, b, m, cmp;
00632 
00633   a = -1;
00634   b = numOps;
00635   // invariant: opTab[a] < name < opTab[b]
00636   while (b - a > 1) {
00637     m = (a + b) / 2;
00638     cmp = strcmp(opTab[m].name, name);
00639     if (cmp < 0)
00640       a = m;
00641     else if (cmp > 0)
00642       b = m;
00643     else
00644       a = b = m;
00645   }
00646   if (cmp != 0)
00647     return NULL;
00648   return &opTab[a];
00649 }
00650 
00651 GBool Gfx::checkArg(Object *arg, TchkType type) {
00652   switch (type) {
00653   case tchkBool:   return arg->isBool();
00654   case tchkInt:    return arg->isInt();
00655   case tchkNum:    return arg->isNum();
00656   case tchkString: return arg->isString();
00657   case tchkName:   return arg->isName();
00658   case tchkArray:  return arg->isArray();
00659   case tchkProps:  return arg->isDict() || arg->isName();
00660   case tchkSCN:    return arg->isNum() || arg->isName();
00661   case tchkNone:   return gFalse;
00662   }
00663   return gFalse;
00664 }
00665 
00666 int Gfx::getPos() {
00667   return parser ? parser->getPos() : -1;
00668 }
00669 
00670 //------------------------------------------------------------------------
00671 // graphics state operators
00672 //------------------------------------------------------------------------
00673 
00674 void Gfx::opSave(Object /*args*/[], int /*numArgs*/) {
00675   out->saveState(state);
00676   state = state->save();
00677 }
00678 
00679 void Gfx::opRestore(Object /*args*/[], int /*numArgs*/) {
00680   state = state->restore();
00681   out->restoreState(state);
00682 }
00683 
00684 void Gfx::opConcat(Object args[], int /*numArgs*/) {
00685   state->concatCTM(args[0].getNum(), args[1].getNum(),
00686            args[2].getNum(), args[3].getNum(),
00687            args[4].getNum(), args[5].getNum());
00688   out->updateCTM(state, args[0].getNum(), args[1].getNum(),
00689          args[2].getNum(), args[3].getNum(),
00690          args[4].getNum(), args[5].getNum());
00691   fontChanged = gTrue;
00692 }
00693 
00694 void Gfx::opSetDash(Object args[], int /*numArgs*/) {
00695   Array *a;
00696   int length;
00697   Object obj;
00698   double *dash;
00699   int i;
00700 
00701   a = args[0].getArray();
00702   length = a->getLength();
00703   if (length == 0) {
00704     dash = NULL;
00705   } else {
00706     dash = (double *)gmalloc(length * sizeof(double));
00707     for (i = 0; i < length; ++i) {
00708       dash[i] = a->get(i, &obj)->getNum();
00709       obj.free();
00710     }
00711   }
00712   state->setLineDash(dash, length, args[1].getNum());
00713   out->updateLineDash(state);
00714 }
00715 
00716 void Gfx::opSetFlat(Object args[], int /*numArgs*/) {
00717   state->setFlatness((int)args[0].getNum());
00718   out->updateFlatness(state);
00719 }
00720 
00721 void Gfx::opSetLineJoin(Object args[], int /*numArgs*/) {
00722   state->setLineJoin(args[0].getInt());
00723   out->updateLineJoin(state);
00724 }
00725 
00726 void Gfx::opSetLineCap(Object args[], int /*numArgs*/) {
00727   state->setLineCap(args[0].getInt());
00728   out->updateLineCap(state);
00729 }
00730 
00731 void Gfx::opSetMiterLimit(Object args[], int /*numArgs*/) {
00732   state->setMiterLimit(args[0].getNum());
00733   out->updateMiterLimit(state);
00734 }
00735 
00736 void Gfx::opSetLineWidth(Object args[], int /*numArgs*/) {
00737   state->setLineWidth(args[0].getNum());
00738   out->updateLineWidth(state);
00739 }
00740 
00741 void Gfx::opSetExtGState(Object args[], int /*numArgs*/) {
00742   Object obj1, obj2;
00743 
00744   if (!res->lookupGState(args[0].getName(), &obj1)) {
00745     return;
00746   }
00747   if (!obj1.isDict()) {
00748     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
00749     obj1.free();
00750     return;
00751   }
00752   if (obj1.dictLookup("ca", &obj2)->isNum()) {
00753     state->setFillOpacity(obj2.getNum());
00754     out->updateFillOpacity(state);
00755   }
00756   obj2.free();
00757   if (obj1.dictLookup("CA", &obj2)->isNum()) {
00758     state->setStrokeOpacity(obj2.getNum());
00759     out->updateStrokeOpacity(state);
00760   }
00761   obj2.free();
00762   obj1.free();
00763 }
00764 
00765 void Gfx::opSetRenderingIntent(Object /*args*/[], int /*numArgs*/) {
00766 }
00767 
00768 //------------------------------------------------------------------------
00769 // color operators
00770 //------------------------------------------------------------------------
00771 
00772 void Gfx::opSetFillGray(Object args[], int /*numArgs*/) {
00773   GfxColor color;
00774 
00775   state->setFillPattern(NULL);
00776   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
00777   color.c[0] = args[0].getNum();
00778   state->setFillColor(&color);
00779   out->updateFillColor(state);
00780 }
00781 
00782 void Gfx::opSetStrokeGray(Object args[], int /*numArgs*/) {
00783   GfxColor color;
00784 
00785   state->setStrokePattern(NULL);
00786   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
00787   color.c[0] = args[0].getNum();
00788   state->setStrokeColor(&color);
00789   out->updateStrokeColor(state);
00790 }
00791 
00792 void Gfx::opSetFillCMYKColor(Object args[], int /*numArgs*/) {
00793   GfxColor color;
00794   int i;
00795 
00796   state->setFillPattern(NULL);
00797   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
00798   for (i = 0; i < 4; ++i) {
00799     color.c[i] = args[i].getNum();
00800   }
00801   state->setFillColor(&color);
00802   out->updateFillColor(state);
00803 }
00804 
00805 void Gfx::opSetStrokeCMYKColor(Object args[], int /*numArgs*/) {
00806   GfxColor color;
00807   int i;
00808 
00809   state->setStrokePattern(NULL);
00810   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
00811   for (i = 0; i < 4; ++i) {
00812     color.c[i] = args[i].getNum();
00813   }
00814   state->setStrokeColor(&color);
00815   out->updateStrokeColor(state);
00816 }
00817 
00818 void Gfx::opSetFillRGBColor(Object args[], int /*numArgs*/) {
00819   GfxColor color;
00820   int i;
00821 
00822   state->setFillPattern(NULL);
00823   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
00824   for (i = 0; i < 3; ++i) {
00825     color.c[i] = args[i].getNum();
00826   }
00827   state->setFillColor(&color);
00828   out->updateFillColor(state);
00829 }
00830 
00831 void Gfx::opSetStrokeRGBColor(Object args[], int /*numArgs*/) {
00832   GfxColor color;
00833   int i;
00834 
00835   state->setStrokePattern(NULL);
00836   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
00837   for (i = 0; i < 3; ++i) {
00838     color.c[i] = args[i].getNum();
00839   }
00840   state->setStrokeColor(&color);
00841   out->updateStrokeColor(state);
00842 }
00843 
00844 void Gfx::opSetFillColorSpace(Object args[], int /*numArgs*/) {
00845   Object obj;
00846   GfxColorSpace *colorSpace;
00847   GfxColor color;
00848   int i;
00849 
00850   state->setFillPattern(NULL);
00851   res->lookupColorSpace(args[0].getName(), &obj);
00852   if (obj.isNull()) {
00853     colorSpace = GfxColorSpace::parse(&args[0]);
00854   } else {
00855     colorSpace = GfxColorSpace::parse(&obj);
00856   }
00857   obj.free();
00858   if (colorSpace) {
00859     state->setFillColorSpace(colorSpace);
00860   } else {
00861     error(getPos(), "Bad color space (fill)");
00862   }
00863   for (i = 0; i < gfxColorMaxComps; ++i) {
00864     color.c[i] = 0;
00865   }
00866   state->setFillColor(&color);
00867   out->updateFillColor(state);
00868 }
00869 
00870 void Gfx::opSetStrokeColorSpace(Object args[], int /*numArgs*/) {
00871   Object obj;
00872   GfxColorSpace *colorSpace;
00873   GfxColor color;
00874   int i;
00875 
00876   state->setStrokePattern(NULL);
00877   res->lookupColorSpace(args[0].getName(), &obj);
00878   if (obj.isNull()) {
00879     colorSpace = GfxColorSpace::parse(&args[0]);
00880   } else {
00881     colorSpace = GfxColorSpace::parse(&obj);
00882   }
00883   obj.free();
00884   if (colorSpace) {
00885     state->setStrokeColorSpace(colorSpace);
00886   } else {
00887     error(getPos(), "Bad color space (stroke)");
00888   }
00889   for (i = 0; i < gfxColorMaxComps; ++i) {
00890     color.c[i] = 0;
00891   }
00892   state->setStrokeColor(&color);
00893   out->updateStrokeColor(state);
00894 }
00895 
00896 void Gfx::opSetFillColor(Object args[], int numArgs) {
00897   GfxColor color;
00898   int i;
00899 
00900   state->setFillPattern(NULL);
00901   for (i = 0; i < numArgs; ++i) {
00902     color.c[i] = args[i].getNum();
00903   }
00904   state->setFillColor(&color);
00905   out->updateFillColor(state);
00906 }
00907 
00908 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
00909   GfxColor color;
00910   int i;
00911 
00912   state->setStrokePattern(NULL);
00913   for (i = 0; i < numArgs; ++i) {
00914     color.c[i] = args[i].getNum();
00915   }
00916   state->setStrokeColor(&color);
00917   out->updateStrokeColor(state);
00918 }
00919 
00920 void Gfx::opSetFillColorN(Object args[], int numArgs) {
00921   GfxColor color;
00922   GfxPattern *pattern;
00923   int i;
00924 
00925   if (state->getFillColorSpace()->getMode() == csPattern) {
00926     if (numArgs > 1) {
00927       for (i = 0; i < numArgs && i < 4; ++i) {
00928     if (args[i].isNum()) {
00929       color.c[i] = args[i].getNum();
00930     }
00931       }
00932       state->setFillColor(&color);
00933       out->updateFillColor(state);
00934     }
00935     if (args[numArgs-1].isName() &&
00936     (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
00937       state->setFillPattern(pattern);
00938     }
00939 
00940   } else {
00941     state->setFillPattern(NULL);
00942     for (i = 0; i < numArgs && i < 4; ++i) {
00943       if (args[i].isNum()) {
00944     color.c[i] = args[i].getNum();
00945       }
00946     }
00947     state->setFillColor(&color);
00948     out->updateFillColor(state);
00949   }
00950 }
00951 
00952 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
00953   GfxColor color;
00954   GfxPattern *pattern;
00955   int i;
00956 
00957   if (state->getStrokeColorSpace()->getMode() == csPattern) {
00958     if (numArgs > 1) {
00959       for (i = 0; i < numArgs && i < 4; ++i) {
00960     if (args[i].isNum()) {
00961       color.c[i] = args[i].getNum();
00962     }
00963       }
00964       state->setStrokeColor(&color);
00965       out->updateStrokeColor(state);
00966     }
00967     if (args[numArgs-1].isName() &&
00968     (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
00969       state->setStrokePattern(pattern);
00970     }
00971 
00972   } else {
00973     state->setStrokePattern(NULL);
00974     for (i = 0; i < numArgs && i < 4; ++i) {
00975       if (args[i].isNum()) {
00976     color.c[i] = args[i].getNum();
00977       }
00978     }
00979     state->setStrokeColor(&color);
00980     out->updateStrokeColor(state);
00981   }
00982 }
00983 
00984 //------------------------------------------------------------------------
00985 // path segment operators
00986 //------------------------------------------------------------------------
00987 
00988 void Gfx::opMoveTo(Object args[], int /*numArgs*/) {
00989   state->moveTo(args[0].getNum(), args[1].getNum());
00990 }
00991 
00992 void Gfx::opLineTo(Object args[], int /*numArgs*/) {
00993   if (!state->isCurPt()) {
00994     error(getPos(), "No current point in lineto");
00995     return;
00996   }
00997   state->lineTo(args[0].getNum(), args[1].getNum());
00998 }
00999 
01000 void Gfx::opCurveTo(Object args[], int /*numArgs*/) {
01001   double x1, y1, x2, y2, x3, y3;
01002 
01003   if (!state->isCurPt()) {
01004     error(getPos(), "No current point in curveto");
01005     return;
01006   }
01007   x1 = args[0].getNum();
01008   y1 = args[1].getNum();
01009   x2 = args[2].getNum();
01010   y2 = args[3].getNum();
01011   x3 = args[4].getNum();
01012   y3 = args[5].getNum();
01013   state->curveTo(x1, y1, x2, y2, x3, y3);
01014 }
01015 
01016 void Gfx::opCurveTo1(Object args[], int /*numArgs*/) {
01017   double x1, y1, x2, y2, x3, y3;
01018 
01019   if (!state->isCurPt()) {
01020     error(getPos(), "No current point in curveto1");
01021     return;
01022   }
01023   x1 = state->getCurX();
01024   y1 = state->getCurY();
01025   x2 = args[0].getNum();
01026   y2 = args[1].getNum();
01027   x3 = args[2].getNum();
01028   y3 = args[3].getNum();
01029   state->curveTo(x1, y1, x2, y2, x3, y3);
01030 }
01031 
01032 void Gfx::opCurveTo2(Object args[], int /*numArgs*/) {
01033   double x1, y1, x2, y2, x3, y3;
01034 
01035   if (!state->isCurPt()) {
01036     error(getPos(), "No current point in curveto2");
01037     return;
01038   }
01039   x1 = args[0].getNum();
01040   y1 = args[1].getNum();
01041   x2 = args[2].getNum();
01042   y2 = args[3].getNum();
01043   x3 = x2;
01044   y3 = y2;
01045   state->curveTo(x1, y1, x2, y2, x3, y3);
01046 }
01047 
01048 void Gfx::opRectangle(Object args[], int /*numArgs*/) {
01049   double x, y, w, h;
01050 
01051   x = args[0].getNum();
01052   y = args[1].getNum();
01053   w = args[2].getNum();
01054   h = args[3].getNum();
01055   state->moveTo(x, y);
01056   state->lineTo(x + w, y);
01057   state->lineTo(x + w, y + h);
01058   state->lineTo(x, y + h);
01059   state->closePath();
01060 }
01061 
01062 void Gfx::opClosePath(Object /*args*/[], int /*numArgs*/) {
01063   if (!state->isCurPt()) {
01064     error(getPos(), "No current point in closepath");
01065     return;
01066   }
01067   state->closePath();
01068 }
01069 
01070 //------------------------------------------------------------------------
01071 // path painting operators
01072 //------------------------------------------------------------------------
01073 
01074 void Gfx::opEndPath(Object /*args*/[], int /*numArgs*/) {
01075   doEndPath();
01076 }
01077 
01078 void Gfx::opStroke(Object /*args*/[], int /*numArgs*/) {
01079   if (!state->isCurPt()) {
01080     //error(getPos(), "No path in stroke");
01081     return;
01082   }
01083   if (state->isPath())
01084     out->stroke(state);
01085   doEndPath();
01086 }
01087 
01088 void Gfx::opCloseStroke(Object /*args*/[], int /*numArgs*/) {
01089   if (!state->isCurPt()) {
01090     //error(getPos(), "No path in closepath/stroke");
01091     return;
01092   }
01093   if (state->isPath()) {
01094     state->closePath();
01095     out->stroke(state);
01096   }
01097   doEndPath();
01098 }
01099 
01100 void Gfx::opFill(Object /*args*/[], int /*numArgs*/) {
01101   if (!state->isCurPt()) {
01102     //error(getPos(), "No path in fill");
01103     return;
01104   }
01105   if (state->isPath()) {
01106     if (state->getFillColorSpace()->getMode() == csPattern) {
01107       doPatternFill(gFalse);
01108     } else {
01109       out->fill(state);
01110     }
01111   }
01112   doEndPath();
01113 }
01114 
01115 void Gfx::opEOFill(Object /*args*/[], int /*numArgs*/) {
01116   if (!state->isCurPt()) {
01117     //error(getPos(), "No path in eofill");
01118     return;
01119   }
01120   if (state->isPath()) {
01121     if (state->getFillColorSpace()->getMode() == csPattern) {
01122       doPatternFill(gTrue);
01123     } else {
01124       out->eoFill(state);
01125     }
01126   }
01127   doEndPath();
01128 }
01129 
01130 void Gfx::opFillStroke(Object /*args*/[], int /*numArgs*/) {
01131   if (!state->isCurPt()) {
01132     //error(getPos(), "No path in fill/stroke");
01133     return;
01134   }
01135   if (state->isPath()) {
01136     if (state->getFillColorSpace()->getMode() == csPattern) {
01137       doPatternFill(gFalse);
01138     } else {
01139       out->fill(state);
01140     }
01141     out->stroke(state);
01142   }
01143   doEndPath();
01144 }
01145 
01146 void Gfx::opCloseFillStroke(Object /*args*/[], int /*numArgs*/) {
01147   if (!state->isCurPt()) {
01148     //error(getPos(), "No path in closepath/fill/stroke");
01149     return;
01150   }
01151   if (state->isPath()) {
01152     state->closePath();
01153     if (state->getFillColorSpace()->getMode() == csPattern) {
01154       doPatternFill(gFalse);
01155     } else {
01156       out->fill(state);
01157     }
01158     out->stroke(state);
01159   }
01160   doEndPath();
01161 }
01162 
01163 void Gfx::opEOFillStroke(Object /*args*/[], int /*numArgs*/) {
01164   if (!state->isCurPt()) {
01165     //error(getPos(), "No path in eofill/stroke");
01166     return;
01167   }
01168   if (state->isPath()) {
01169     if (state->getFillColorSpace()->getMode() == csPattern) {
01170       doPatternFill(gTrue);
01171     } else {
01172       out->eoFill(state);
01173     }
01174     out->stroke(state);
01175   }
01176   doEndPath();
01177 }
01178 
01179 void Gfx::opCloseEOFillStroke(Object /*args*/[], int /*numArgs*/) {
01180   if (!state->isCurPt()) {
01181     //error(getPos(), "No path in closepath/eofill/stroke");
01182     return;
01183   }
01184   if (state->isPath()) {
01185     state->closePath();
01186     if (state->getFillColorSpace()->getMode() == csPattern) {
01187       doPatternFill(gTrue);
01188     } else {
01189       out->eoFill(state);
01190     }
01191     out->stroke(state);
01192   }
01193   doEndPath();
01194 }
01195 
01196 void Gfx::doPatternFill(GBool eoFill) {
01197   GfxPatternColorSpace *patCS;
01198   GfxPattern *pattern;
01199   GfxTilingPattern *tPat;
01200   const GfxColorSpace *cs;
01201   double xMin, yMin, xMax, yMax, x, y, x1, y1;
01202   double cxMin, cyMin, cxMax, cyMax;
01203   int xi0, yi0, xi1, yi1, xi, yi;
01204   const double *ctm;
01205   double *btm, *ptm;
01206   double m[6], ictm[6], m1[6], imb[6];
01207   double det;
01208   double xstep, ystep;
01209   int i;
01210 
01211   // this is a bit of a kludge -- patterns can be really slow, so we
01212   // skip them if we're only doing text extraction, since they almost
01213   // certainly don't contain any text
01214   if (!out->needNonText()) {
01215     return;
01216   }
01217 
01218   // get color space
01219   patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
01220 
01221   // get pattern
01222   if (!(pattern = state->getFillPattern())) {
01223     return;
01224   }
01225   if (pattern->getType() != 1) {
01226     return;
01227   }
01228   tPat = (GfxTilingPattern *)pattern;
01229 
01230   // construct a (pattern space) -> (current space) transform matrix
01231   ctm = state->getCTM();
01232   btm = baseMatrix;
01233   ptm = tPat->getMatrix();
01234   // iCTM = invert CTM
01235   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
01236   ictm[0] = ctm[3] * det;
01237   ictm[1] = -ctm[1] * det;
01238   ictm[2] = -ctm[2] * det;
01239   ictm[3] = ctm[0] * det;
01240   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
01241   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
01242   // m1 = PTM * BTM = PTM * base transform matrix
01243   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
01244   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
01245   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
01246   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
01247   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
01248   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
01249   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
01250   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
01251   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
01252   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
01253   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
01254   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
01255   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
01256 
01257   // construct a (base space) -> (pattern space) transform matrix
01258   det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
01259   imb[0] = m1[3] * det;
01260   imb[1] = -m1[1] * det;
01261   imb[2] = -m1[2] * det;
01262   imb[3] = m1[0] * det;
01263   imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
01264   imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
01265 
01266   // save current graphics state
01267   out->saveState(state);
01268   state = state->save();
01269 
01270   // set underlying color space (for uncolored tiling patterns)
01271   if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
01272     state->setFillColorSpace(cs->copy());
01273   } else {
01274     state->setFillColorSpace(new GfxDeviceGrayColorSpace());
01275   }
01276   state->setFillPattern(NULL);
01277   out->updateFillColor(state);
01278 
01279   // clip to current path
01280   state->clip();
01281   if (eoFill) {
01282     out->eoClip(state);
01283   } else {
01284     out->clip(state);
01285   }
01286   state->clearPath();
01287 
01288   // transform clip region bbox to pattern space
01289   state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
01290   xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
01291   yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
01292   x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
01293   y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
01294   if (x1 < xMin) {
01295     xMin = x1;
01296   } else if (x1 > xMax) {
01297     xMax = x1;
01298   }
01299   if (y1 < yMin) {
01300     yMin = y1;
01301   } else if (y1 > yMax) {
01302     yMax = y1;
01303   }
01304   x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
01305   y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
01306   if (x1 < xMin) {
01307     xMin = x1;
01308   } else if (x1 > xMax) {
01309     xMax = x1;
01310   }
01311   if (y1 < yMin) {
01312     yMin = y1;
01313   } else if (y1 > yMax) {
01314     yMax = y1;
01315   }
01316   x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
01317   y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
01318   if (x1 < xMin) {
01319     xMin = x1;
01320   } else if (x1 > xMax) {
01321     xMax = x1;
01322   }
01323   if (y1 < yMin) {
01324     yMin = y1;
01325   } else if (y1 > yMax) {
01326     yMax = y1;
01327   }
01328 
01329   // draw the pattern
01330   //~ this should treat negative steps differently -- start at right/top
01331   //~ edge instead of left/bottom (?)
01332   xstep = fabs(tPat->getXStep());
01333   ystep = fabs(tPat->getYStep());
01334   xi0 = (int)floor(xMin / xstep);
01335   xi1 = (int)ceil(xMax / xstep);
01336   yi0 = (int)floor(yMin / ystep);
01337   yi1 = (int)ceil(yMax / ystep);
01338   for (i = 0; i < 4; ++i) {
01339     m1[i] = m[i];
01340   }
01341   for (yi = yi0; yi < yi1; ++yi) {
01342     for (xi = xi0; xi < xi1; ++xi) {
01343       x = xi * xstep;
01344       y = yi * ystep;
01345       m1[4] = x * m[0] + y * m[2] + m[4];
01346       m1[5] = x * m[1] + y * m[3] + m[5];
01347       doForm1(tPat->getContentStream(), tPat->getResDict(),
01348           m1, tPat->getBBox());
01349     }
01350   }
01351 
01352   // restore graphics state
01353   state = state->restore();
01354   out->restoreState(state);
01355 }
01356 
01357 void Gfx::opShFill(Object args[], int /*numArgs*/) {
01358   GfxShading *shading;
01359   double xMin, yMin, xMax, yMax;
01360 
01361   if (!(shading = res->lookupShading(args[0].getName()))) {
01362     return;
01363   }
01364 
01365   // save current graphics state
01366   out->saveState(state);
01367   state = state->save();
01368 
01369   // clip to bbox
01370   if (shading->getHasBBox()) {
01371     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
01372     state->moveTo(xMin, yMin);
01373     state->lineTo(xMax, yMin);
01374     state->lineTo(xMax, yMax);
01375     state->lineTo(xMin, yMax);
01376     state->closePath();
01377     state->clip();
01378     out->clip(state);
01379     state->clearPath();
01380   }
01381 
01382   // set the color space
01383   state->setFillColorSpace(shading->getColorSpace()->copy());
01384 
01385   // do shading type-specific operations
01386   switch (shading->getType()) {
01387   case 2:
01388     doAxialShFill((GfxAxialShading *)shading);
01389     break;
01390   case 3:
01391     doRadialShFill((GfxRadialShading *)shading);
01392     break;
01393   }
01394 
01395   // restore graphics state
01396   state = state->restore();
01397   out->restoreState(state);
01398 
01399   delete shading;
01400 }
01401 
01402 void Gfx::doAxialShFill(GfxAxialShading *shading) {
01403   double xMin, yMin, xMax, yMax;
01404   double x0, y0, x1, y1;
01405   double dx, dy, mul;
01406   double tMin, tMax, t, tx, ty;
01407   double s[4], sMin, sMax, tmp;
01408   double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
01409   double t0, t1, tt;
01410   double ta[axialMaxSplits + 1];
01411   int next[axialMaxSplits + 1];
01412   GfxColor color0, color1;
01413   int nComps;
01414   int i, j, k, kk;
01415 
01416   // get the clip region bbox
01417   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
01418 
01419   // compute min and max t values, based on the four corners of the
01420   // clip region bbox
01421   shading->getCoords(&x0, &y0, &x1, &y1);
01422   dx = x1 - x0;
01423   dy = y1 - y0;
01424   mul = 1 / (dx * dx + dy * dy);
01425   tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
01426   t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
01427   if (t < tMin) {
01428     tMin = t;
01429   } else if (t > tMax) {
01430     tMax = t;
01431   }
01432   t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
01433   if (t < tMin) {
01434     tMin = t;
01435   } else if (t > tMax) {
01436     tMax = t;
01437   }
01438   t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
01439   if (t < tMin) {
01440     tMin = t;
01441   } else if (t > tMax) {
01442     tMax = t;
01443   }
01444   if (tMin < 0 && !shading->getExtend0()) {
01445     tMin = 0;
01446   }
01447   if (tMax > 1 && !shading->getExtend1()) {
01448     tMax = 1;
01449   }
01450 
01451   // get the function domain
01452   t0 = shading->getDomain0();
01453   t1 = shading->getDomain1();
01454 
01455   // Traverse the t axis and do the shading.
01456   //
01457   // For each point (tx, ty) on the t axis, consider a line through
01458   // that point perpendicular to the t axis:
01459   //
01460   //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
01461   //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
01462   //
01463   // Then look at the intersection of this line with the bounding box
01464   // (xMin, yMin, xMax, yMax).  In the general case, there are four
01465   // intersection points:
01466   //
01467   //     s0 = (xMin - tx) / -dy
01468   //     s1 = (xMax - tx) / -dy
01469   //     s2 = (yMin - ty) / dx
01470   //     s3 = (yMax - ty) / dx
01471   //
01472   // and we want the middle two s values.
01473   //
01474   // In the case where dx = 0, take s0 and s1; in the case where dy =
01475   // 0, take s2 and s3.
01476   //
01477   // Each filled polygon is bounded by two of these line segments
01478   // perpdendicular to the t axis.
01479   //
01480   // The t axis is bisected into smaller regions until the color
01481   // difference across a region is small enough, and then the region
01482   // is painted with a single color.
01483 
01484   // set up
01485   nComps = shading->getColorSpace()->getNComps();
01486   ta[0] = tMin;
01487   ta[axialMaxSplits] = tMax;
01488   next[0] = axialMaxSplits;
01489 
01490   // compute the color at t = tMin
01491   if (tMin < 0) {
01492     tt = t0;
01493   } else if (tMin > 1) {
01494     tt = t1;
01495   } else {
01496     tt = t0 + (t1 - t0) * tMin;
01497   }
01498   shading->getColor(tt, &color0);
01499 
01500   // compute the coordinates of the point on the t axis at t = tMin;
01501   // then compute the intersection of the perpendicular line with the
01502   // bounding box
01503   tx = x0 + tMin * dx;
01504   ty = y0 + tMin * dy;
01505   if (dx == 0 && dy == 0) {
01506     sMin = sMax = 0;
01507   } if (dx == 0) {
01508     sMin = (xMin - tx) / -dy;
01509     sMax = (xMax - tx) / -dy;
01510     if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
01511   } else if (dy == 0) {
01512     sMin = (yMin - ty) / dx;
01513     sMax = (yMax - ty) / dx;
01514     if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
01515   } else {
01516     s[0] = (yMin - ty) / dx;
01517     s[1] = (yMax - ty) / dx;
01518     s[2] = (xMin - tx) / -dy;
01519     s[3] = (xMax - tx) / -dy;
01520     for (j = 0; j < 3; ++j) {
01521       kk = j;
01522       for (k = j + 1; k < 4; ++k) {
01523     if (s[k] < s[kk]) {
01524       kk = k;
01525     }
01526       }
01527       tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
01528     }
01529     sMin = s[1];
01530     sMax = s[2];
01531   }
01532   ux0 = tx - sMin * dy;
01533   uy0 = ty + sMin * dx;
01534   vx0 = tx - sMax * dy;
01535   vy0 = ty + sMax * dx;
01536 
01537   i = 0;
01538   while (i < axialMaxSplits) {
01539 
01540     // bisect until color difference is small enough or we hit the
01541     // bisection limit
01542     j = next[i];
01543     while (j > i + 1) {
01544       if (ta[j] < 0) {
01545     tt = t0;
01546       } else if (ta[j] > 1) {
01547     tt = t1;
01548       } else {
01549     tt = t0 + (t1 - t0) * ta[j];
01550       }
01551       shading->getColor(tt, &color1);
01552       for (k = 0; k < nComps; ++k) {
01553     if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
01554       break;
01555     }
01556       }
01557       if (k == nComps) {
01558     break;
01559       }
01560       k = (i + j) / 2;
01561       ta[k] = 0.5 * (ta[i] + ta[j]);
01562       next[i] = k;
01563       next[k] = j;
01564       j = k;
01565     }
01566 
01567     // use the average of the colors of the two sides of the region
01568     for (k = 0; k < nComps; ++k) {
01569       color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
01570     }
01571 
01572     // compute the coordinates of the point on the t axis; then
01573     // compute the intersection of the perpendicular line with the
01574     // bounding box
01575     tx = x0 + ta[j] * dx;
01576     ty = y0 + ta[j] * dy;
01577     if (dx == 0 && dy == 0) {
01578       sMin = sMax = 0;
01579     } if (dx == 0) {
01580       sMin = (xMin - tx) / -dy;
01581       sMax = (xMax - tx) / -dy;
01582       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
01583     } else if (dy == 0) {
01584       sMin = (yMin - ty) / dx;
01585       sMax = (yMax - ty) / dx;
01586       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
01587     } else {
01588       s[0] = (yMin - ty) / dx;
01589       s[1] = (yMax - ty) / dx;
01590       s[2] = (xMin - tx) / -dy;
01591       s[3] = (xMax - tx) / -dy;
01592       for (j = 0; j < 3; ++j) {
01593     kk = j;
01594     for (k = j + 1; k < 4; ++k) {
01595       if (s[k] < s[kk]) {
01596         kk = k;
01597       }
01598     }
01599     tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
01600       }
01601       sMin = s[1];
01602       sMax = s[2];
01603     }
01604     ux1 = tx - sMin * dy;
01605     uy1 = ty + sMin * dx;
01606     vx1 = tx - sMax * dy;
01607     vy1 = ty + sMax * dx;
01608 
01609     // set the color
01610     state->setFillColor(&color0);
01611     out->updateFillColor(state);
01612 
01613     // fill the region
01614     state->moveTo(ux0, uy0);
01615     state->lineTo(vx0, vy0);
01616     state->lineTo(vx1, vy1);
01617     state->lineTo(ux1, uy1);
01618     state->closePath();
01619     out->fill(state);
01620     state->clearPath();
01621 
01622     // set up for next region
01623     ux0 = ux1;
01624     uy0 = uy1;
01625     vx0 = vx1;
01626     vy0 = vy1;
01627     color0 = color1;
01628     i = next[i];
01629   }
01630 }
01631 
01632 void Gfx::doRadialShFill(GfxRadialShading *shading) {
01633   double sMin, sMax, xMin, yMin, xMax, yMax;
01634   double x0, y0, r0, x1, y1, r1, t0, t1;
01635   int nComps;
01636   GfxColor colorA, colorB;
01637   double xa, ya, xb, yb, ra, rb;
01638   double ta, tb, sa, sb;
01639   int ia, ib, k, n;
01640   const double *ctm;
01641   double angle, t;
01642 
01643   // get the shading info
01644   shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
01645   t0 = shading->getDomain0();
01646   t1 = shading->getDomain1();
01647   nComps = shading->getColorSpace()->getNComps();
01648 
01649   // compute the (possibly extended) s range
01650   sMin = 0;
01651   sMax = 1;
01652   if (shading->getExtend0()) {
01653     if (r0 < r1) {
01654       // extend the smaller end
01655       sMin = -r0 / (r1 - r0);
01656     } else {
01657       // extend the larger end
01658       //~ this computes the diagonal of the bounding box -- we should
01659       //~ really compute the intersection of the moving/expanding
01660       //~ circles with each of the four corners and look for the max
01661       //~ radius
01662       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
01663       sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
01664            (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
01665       if (sMin > 0) {
01666     sMin = 0;
01667       } else if (sMin < -20) {
01668     // sanity check
01669     sMin = -20;
01670       }
01671     }
01672   }
01673   if (shading->getExtend1()) {
01674     if (r1 < r0) {
01675       // extend the smaller end
01676       sMax = -r0 / (r1 - r0);
01677     } else if (r1 > r0) {
01678       // extend the larger end
01679       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
01680       sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
01681            (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
01682       if (sMax < 1) {
01683     sMin = 1;
01684       } else if (sMax > 20) {
01685     // sanity check
01686     sMax = 20;
01687       }
01688     }
01689   }
01690 
01691   // compute the number of steps into which circles must be divided to
01692   // achieve a curve flatness of 0.1 pixel in device space for the
01693   // largest circle (note that "device space" is 72 dpi when generating
01694   // PostScript, hence the relatively small 0.1 pixel accuracy)
01695   ctm = state->getCTM();
01696   t = fabs(ctm[0]);
01697   if (fabs(ctm[1]) > t) {
01698     t = fabs(ctm[1]);
01699   }
01700   if (fabs(ctm[2]) > t) {
01701     t = fabs(ctm[2]);
01702   }
01703   if (fabs(ctm[3]) > t) {
01704     t = fabs(ctm[3]);
01705   }
01706   if (r0 > r1) {
01707     t *= r0;
01708   } else {
01709     t *= r1;
01710   }
01711   if (t < 1) {
01712     n = 3;
01713   } else {
01714     n = (int)(M_PI / acos(1 - 0.1 / t));
01715     if (n < 3) {
01716       n = 3;
01717     } else if (n > 200) {
01718       n = 200;
01719     }
01720   }
01721 
01722   // Traverse the t axis and do the shading.
01723   //
01724   // This generates and fills a series of rings.  Each ring is defined
01725   // by two circles:
01726   //   sa, ta, xa, ya, ra, colorA
01727   //   sb, tb, xb, yb, rb, colorB
01728   //
01729   // The s/t axis is divided into radialMaxSplits parts; these parts
01730   // are combined as much as possible while respecting the
01731   // radialColorDelta parameter.
01732 
01733   // setup for the start circle
01734   ia = 0;
01735   sa = sMin;
01736   ta = t0 + sa * (t1 - t0);
01737   xa = x0 + sa * (x1 - x0);
01738   ya = y0 + sa * (y1 - y0);
01739   ra = r0 + sa * (r1 - r0);
01740   if (ta < t0) {
01741     shading->getColor(t0, &colorA);
01742   } else if (ta > t1) {
01743     shading->getColor(t1, &colorA);
01744   } else {
01745     shading->getColor(ta, &colorA);
01746   }
01747 
01748   while (ia < radialMaxSplits) {
01749 
01750     // go as far along the t axis (toward t1) as we can, such that the
01751     // color difference is within the tolerance (radialColorDelta) --
01752     // this uses bisection (between the current value, t, and t1),
01753     // limited to radialMaxSplits points along the t axis
01754     ib = radialMaxSplits;
01755     sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
01756     tb = t0 + sb * (t1 - t0);
01757     if (tb < t0) {
01758       shading->getColor(t0, &colorB);
01759     } else if (tb > t1) {
01760       shading->getColor(t1, &colorB);
01761     } else {
01762       shading->getColor(tb, &colorB);
01763     }
01764     while (ib - ia > 1) {
01765       for (k = 0; k < nComps; ++k) {
01766     if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
01767       break;
01768     }
01769       }
01770       if (k == nComps) {
01771     break;
01772       }
01773       ib = (ia + ib) / 2;
01774       sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
01775       tb = t0 + sb * (t1 - t0);
01776       if (tb < t0) {
01777     shading->getColor(t0, &colorB);
01778       } else if (tb > t1) {
01779     shading->getColor(t1, &colorB);
01780       } else {
01781     shading->getColor(tb, &colorB);
01782       }
01783     }
01784 
01785     // compute center and radius of the circle
01786     xb = x0 + sb * (x1 - x0);
01787     yb = y0 + sb * (y1 - y0);
01788     rb = r0 + sb * (r1 - r0);
01789 
01790     // use the average of the colors at the two circles
01791     for (k = 0; k < nComps; ++k) {
01792       colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
01793     }
01794     state->setFillColor(&colorA);
01795     out->updateFillColor(state);
01796 
01797     // construct path for first circle
01798     state->moveTo(xa + ra, ya);
01799     for (k = 1; k < n; ++k) {
01800       angle = ((double)k / (double)n) * 2 * M_PI;
01801       state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
01802     }
01803     state->closePath();
01804 
01805     // construct and append path for second circle
01806     state->moveTo(xb + rb, yb);
01807     for (k = 1; k < n; ++k) {
01808       angle = ((double)k / (double)n) * 2 * M_PI;
01809       state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
01810     }
01811     state->closePath();
01812 
01813     // fill the ring
01814     out->eoFill(state);
01815     state->clearPath();
01816 
01817     // step to the next value of t
01818     ia = ib;
01819     sa = sb;
01820     ta = tb;
01821     xa = xb;
01822     ya = yb;
01823     ra = rb;
01824     colorA = colorB;
01825   }
01826 }
01827 
01828 void Gfx::doEndPath() {
01829   if (state->isPath() && clip != clipNone) {
01830     state->clip();
01831     if (clip == clipNormal) {
01832       out->clip(state);
01833     } else {
01834       out->eoClip(state);
01835     }
01836   }
01837   clip = clipNone;
01838   state->clearPath();
01839 }
01840 
01841 //------------------------------------------------------------------------
01842 // path clipping operators
01843 //------------------------------------------------------------------------
01844 
01845 void Gfx::opClip(Object /*args*/[], int /*numArgs*/) {
01846   clip = clipNormal;
01847 }
01848 
01849 void Gfx::opEOClip(Object /*args*/[], int /*numArgs*/) {
01850   clip = clipEO;
01851 }
01852 
01853 //------------------------------------------------------------------------
01854 // text object operators
01855 //------------------------------------------------------------------------
01856 
01857 void Gfx::opBeginText(Object /*args*/[], int /*numArgs*/) {
01858   state->setTextMat(1, 0, 0, 1, 0, 0);
01859   state->textMoveTo(0, 0);
01860   out->updateTextMat(state);
01861   out->updateTextPos(state);
01862   fontChanged = gTrue;
01863 }
01864 
01865 void Gfx::opEndText(Object /*args*/[], int /*numArgs*/) {
01866 }
01867 
01868 //------------------------------------------------------------------------
01869 // text state operators
01870 //------------------------------------------------------------------------
01871 
01872 void Gfx::opSetCharSpacing(Object args[], int /*numArgs*/) {
01873   state->setCharSpace(args[0].getNum());
01874   out->updateCharSpace(state);
01875 }
01876 
01877 void Gfx::opSetFont(Object args[], int /*numArgs*/) {
01878   GfxFont *font;
01879 
01880   if (!(font = res->lookupFont(args[0].getName()))) {
01881     return;
01882   }
01883   if (printCommands) {
01884     printf("  font: tag=%s name='%s' %g\n",
01885        font->getTag()->getCString(),
01886        font->getName() ? font->getName()->getCString() : "???",
01887        args[1].getNum());
01888     fflush(stdout);
01889   }
01890   state->setFont(font, args[1].getNum());
01891   fontChanged = gTrue;
01892 }
01893 
01894 void Gfx::opSetTextLeading(Object args[], int /*numArgs*/) {
01895   state->setLeading(args[0].getNum());
01896 }
01897 
01898 void Gfx::opSetTextRender(Object args[], int /*numArgs*/) {
01899   state->setRender(args[0].getInt());
01900   out->updateRender(state);
01901 }
01902 
01903 void Gfx::opSetTextRise(Object args[], int /*numArgs*/) {
01904   state->setRise(args[0].getNum());
01905   out->updateRise(state);
01906 }
01907 
01908 void Gfx::opSetWordSpacing(Object args[], int /*numArgs*/) {
01909   state->setWordSpace(args[0].getNum());
01910   out->updateWordSpace(state);
01911 }
01912 
01913 void Gfx::opSetHorizScaling(Object args[], int /*numArgs*/) {
01914   state->setHorizScaling(args[0].getNum());
01915   out->updateHorizScaling(state);
01916   fontChanged = gTrue;
01917 }
01918 
01919 //------------------------------------------------------------------------
01920 // text positioning operators
01921 //------------------------------------------------------------------------
01922 
01923 void Gfx::opTextMove(Object args[], int /*numArgs*/) {
01924   double tx, ty;
01925 
01926   tx = state->getLineX() + args[0].getNum();
01927   ty = state->getLineY() + args[1].getNum();
01928   state->textMoveTo(tx, ty);
01929   out->updateTextPos(state);
01930 }
01931 
01932 void Gfx::opTextMoveSet(Object args[], int /*numArgs*/) {
01933   double tx, ty;
01934 
01935   tx = state->getLineX() + args[0].getNum();
01936   ty = args[1].getNum();
01937   state->setLeading(-ty);
01938   ty += state->getLineY();
01939   state->textMoveTo(tx, ty);
01940   out->updateTextPos(state);
01941 }
01942 
01943 void Gfx::opSetTextMatrix(Object args[], int /*numArgs*/) {
01944   state->setTextMat(args[0].getNum(), args[1].getNum(),
01945             args[2].getNum(), args[3].getNum(),
01946             args[4].getNum(), args[5].getNum());
01947   state->textMoveTo(0, 0);
01948   out->updateTextMat(state);
01949   out->updateTextPos(state);
01950   fontChanged = gTrue;
01951 }
01952 
01953 void Gfx::opTextNextLine(Object /*args*/[], int /*numArgs*/) {
01954   double tx, ty;
01955 
01956   tx = state->getLineX();
01957   ty = state->getLineY() - state->getLeading();
01958   state->textMoveTo(tx, ty);
01959   out->updateTextPos(state);
01960 }
01961 
01962 //------------------------------------------------------------------------
01963 // text string operators
01964 //------------------------------------------------------------------------
01965 
01966 void Gfx::opShowText(Object args[], int /*numArgs*/) {
01967   if (!state->getFont()) {
01968     error(getPos(), "No font in show");
01969     return;
01970   }
01971   doShowText(args[0].getString());
01972 }
01973 
01974 void Gfx::opMoveShowText(Object args[], int /*numArgs*/) {
01975   double tx, ty;
01976 
01977   if (!state->getFont()) {
01978     error(getPos(), "No font in move/show");
01979     return;
01980   }
01981   tx = state->getLineX();
01982   ty = state->getLineY() - state->getLeading();
01983   state->textMoveTo(tx, ty);
01984   out->updateTextPos(state);
01985   doShowText(args[0].getString());
01986 }
01987 
01988 void Gfx::opMoveSetShowText(Object args[], int /*numArgs*/) {
01989   double tx, ty;
01990 
01991   if (!state->getFont()) {
01992     error(getPos(), "No font in move/set/show");
01993     return;
01994   }
01995   state->setWordSpace(args[0].getNum());
01996   state->setCharSpace(args[1].getNum());
01997   tx = state->getLineX();
01998   ty = state->getLineY() - state->getLeading();
01999   state->textMoveTo(tx, ty);
02000   out->updateWordSpace(state);
02001   out->updateCharSpace(state);
02002   out->updateTextPos(state);
02003   doShowText(args[2].getString());
02004 }
02005 
02006 void Gfx::opShowSpaceText(Object args[], int /*numArgs*/) {
02007   Array *a;
02008   Object obj;
02009   int wMode;
02010   int i;
02011 
02012   if (!state->getFont()) {
02013     error(getPos(), "No font in show/space");
02014     return;
02015   }
02016   wMode = state->getFont()->getWMode();
02017   a = args[0].getArray();
02018   for (i = 0; i < a->getLength(); ++i) {
02019     a->get(i, &obj);
02020     if (obj.isNum()) {
02021       if (wMode) {
02022     state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
02023       } else {
02024     state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
02025       }
02026       out->updateTextShift(state, obj.getNum());
02027     } else if (obj.isString()) {
02028       doShowText(obj.getString());
02029     } else {
02030       error(getPos(), "Element of show/space array must be number or string");
02031     }
02032     obj.free();
02033   }
02034 }
02035 
02036 void Gfx::doShowText(GString *s) {
02037   GfxFont *font;
02038   int wMode;
02039   double riseX, riseY;
02040   CharCode code;
02041   Unicode u[8];
02042   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy;
02043   double originX, originY, tOriginX, tOriginY;
02044   double oldCTM[6], newCTM[6];
02045   const double *mat;
02046   Object charProc;
02047   Dict *resDict;
02048   Parser *oldParser;
02049   char *p;
02050   int len, n, uLen, nChars, nSpaces, i;
02051 
02052   if (fontChanged) {
02053     out->updateFont(state);
02054     fontChanged = gFalse;
02055   }
02056   font = state->getFont();
02057   wMode = font->getWMode();
02058 
02059   if (out->useDrawChar()) {
02060     out->beginString(state, s);
02061   }
02062 
02063   // handle a Type 3 char
02064   if (font->getType() == fontType3 && out->interpretType3Chars()) {
02065     mat = state->getCTM();
02066     for (i = 0; i < 6; ++i) {
02067       oldCTM[i] = mat[i];
02068     }
02069     mat = state->getTextMat();
02070     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
02071     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
02072     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
02073     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
02074     mat = font->getFontMatrix();
02075     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
02076     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
02077     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
02078     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
02079     newCTM[0] *= state->getFontSize();
02080     newCTM[3] *= state->getFontSize();
02081     newCTM[0] *= state->getHorizScaling();
02082     newCTM[2] *= state->getHorizScaling();
02083     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
02084     curX = state->getCurX();
02085     curY = state->getCurY();
02086     oldParser = parser;
02087     p = s->getCString();
02088     len = s->getLength();
02089     while (len > 0) {
02090       n = font->getNextChar(p, len, &code,
02091                 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
02092                 &dx, &dy, &originX, &originY);
02093       dx = dx * state->getFontSize() + state->getCharSpace();
02094       if (n == 1 && *p == ' ') {
02095     dx += state->getWordSpace();
02096       }
02097       dx *= state->getHorizScaling();
02098       dy *= state->getFontSize();
02099       state->textTransformDelta(dx, dy, &tdx, &tdy);
02100       state->transform(curX + riseX, curY + riseY, &x, &y);
02101       out->saveState(state);
02102       state = state->save();
02103       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
02104       //~ out->updateCTM(???)
02105       if (!out->beginType3Char(state, code, u, uLen)) {
02106     ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
02107     if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
02108       pushResources(resDict);
02109     }
02110     if (charProc.isStream()) {
02111       display(&charProc, gFalse);
02112     } else {
02113       error(getPos(), "Missing or bad Type3 CharProc entry");
02114     }
02115     out->endType3Char(state);
02116     if (resDict) {
02117       popResources();
02118     }
02119     charProc.free();
02120       }
02121       state = state->restore();
02122       out->restoreState(state);
02123       // GfxState::restore() does *not* restore the current position,
02124       // so we track it here with (curX, curY)
02125       curX += tdx;
02126       curY += tdy;
02127       state->moveTo(curX, curY);
02128       p += n;
02129       len -= n;
02130     }
02131     parser = oldParser;
02132 
02133   } else if (out->useDrawChar()) {
02134     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
02135     p = s->getCString();
02136     len = s->getLength();
02137     while (len > 0) {
02138       n = font->getNextChar(p, len, &code,
02139                 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
02140                 &dx, &dy, &originX, &originY);
02141       if (wMode) {
02142     dx *= state->getFontSize();
02143     dy = dy * state->getFontSize() + state->getCharSpace();
02144     if (n == 1 && *p == ' ') {
02145       dy += state->getWordSpace();
02146     }
02147       } else {
02148     dx = dx * state->getFontSize() + state->getCharSpace();
02149     if (n == 1 && *p == ' ') {
02150       dx += state->getWordSpace();
02151     }
02152     dx *= state->getHorizScaling();
02153     dy *= state->getFontSize();
02154       }
02155       state->textTransformDelta(dx, dy, &tdx, &tdy);
02156       originX *= state->getFontSize();
02157       originY *= state->getFontSize();
02158       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
02159       out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
02160             tdx, tdy, tOriginX, tOriginY, code, u, uLen);
02161       state->shift(tdx, tdy);
02162       p += n;
02163       len -= n;
02164     }
02165 
02166   } else {
02167     dx = dy = 0;
02168     p = s->getCString();
02169     len = s->getLength();
02170     nChars = nSpaces = 0;
02171     while (len > 0) {
02172       n = font->getNextChar(p, len, &code,
02173                 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
02174                 &dx2, &dy2, &originX, &originY);
02175       dx += dx2;
02176       dy += dy2;
02177       if (n == 1 && *p == ' ') {
02178     ++nSpaces;
02179       }
02180       ++nChars;
02181       p += n;
02182       len -= n;
02183     }
02184     if (wMode) {
02185       dx *= state->getFontSize();
02186       dy = dy * state->getFontSize()
02187        + nChars * state->getCharSpace()
02188        + nSpaces * state->getWordSpace();
02189     } else {
02190       dx = dx * state->getFontSize()
02191        + nChars * state->getCharSpace()
02192        + nSpaces * state->getWordSpace();
02193       dx *= state->getHorizScaling();
02194       dy *= state->getFontSize();
02195     }
02196     state->textTransformDelta(dx, dy, &tdx, &tdy);
02197     out->drawString(state, s);
02198     state->shift(tdx, tdy);
02199   }
02200 
02201   if (out->useDrawChar()) {
02202     out->endString(state);
02203   }
02204 
02205   updateLevel += 10 * s->getLength();
02206 }
02207 
02208 //------------------------------------------------------------------------
02209 // XObject operators
02210 //------------------------------------------------------------------------
02211 
02212 void Gfx::opXObject(Object args[], int /*numArgs*/) {
02213   Object obj1, obj2, obj3, refObj;
02214 #ifdef OPI_SUPPORT
02215   Object opiDict;
02216 #endif
02217 
02218   if (!res->lookupXObject(args[0].getName(), &obj1)) {
02219     return;
02220   }
02221   if (!obj1.isStream()) {
02222     error(getPos(), "XObject '%s' is wrong type", args[0].getName());
02223     obj1.free();
02224     return;
02225   }
02226 #ifdef OPI_SUPPORT
02227   obj1.streamGetDict()->lookup("OPI", &opiDict);
02228   if (opiDict.isDict()) {
02229     out->opiBegin(state, opiDict.getDict());
02230   }
02231 #endif
02232   obj1.streamGetDict()->lookup("Subtype", &obj2);
02233   if (obj2.isName("Image")) {
02234     res->lookupXObjectNF(args[0].getName(), &refObj);
02235     doImage(&refObj, obj1.getStream(), gFalse);
02236     refObj.free();
02237   } else if (obj2.isName("Form")) {
02238     doForm(&obj1);
02239   } else if (obj2.isName("PS")) {
02240     obj1.streamGetDict()->lookup("Level1", &obj3);
02241     out->psXObject(obj1.getStream(),
02242            obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
02243   } else if (obj2.isName()) {
02244     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
02245   } else {
02246     error(getPos(), "XObject subtype is missing or wrong type");
02247   }
02248   obj2.free();
02249 #ifdef OPI_SUPPORT
02250   if (opiDict.isDict()) {
02251     out->opiEnd(state, opiDict.getDict());
02252   }
02253   opiDict.free();
02254 #endif
02255   obj1.free();
02256 }
02257 
02258 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
02259   Dict *dict;
02260   int width, height;
02261   int bits;
02262   GBool mask;
02263   GBool invert;
02264   GfxColorSpace *colorSpace;
02265   GfxImageColorMap *colorMap;
02266   Object maskObj;
02267   GBool haveMask;
02268   int maskColors[2*gfxColorMaxComps];
02269   Object obj1, obj2;
02270   int i;
02271 
02272   // get stream dict
02273   dict = str->getDict();
02274 
02275   // get size
02276   dict->lookup("Width", &obj1);
02277   if (obj1.isNull()) {
02278     obj1.free();
02279     dict->lookup("W", &obj1);
02280   }
02281   if (!obj1.isInt())
02282     goto err2;
02283   width = obj1.getInt();
02284   obj1.free();
02285   dict->lookup("Height", &obj1);
02286   if (obj1.isNull()) {
02287     obj1.free();
02288     dict->lookup("H", &obj1);
02289   }
02290   if (!obj1.isInt())
02291     goto err2;
02292   height = obj1.getInt();
02293   obj1.free();
02294 
02295   // image or mask?
02296   dict->lookup("ImageMask", &obj1);
02297   if (obj1.isNull()) {
02298     obj1.free();
02299     dict->lookup("IM", &obj1);
02300   }
02301   mask = gFalse;
02302   if (obj1.isBool())
02303     mask = obj1.getBool();
02304   else if (!obj1.isNull())
02305     goto err2;
02306   obj1.free();
02307 
02308   // bit depth
02309   dict->lookup("BitsPerComponent", &obj1);
02310   if (obj1.isNull()) {
02311     obj1.free();
02312     dict->lookup("BPC", &obj1);
02313   }
02314   if (!obj1.isInt())
02315     goto err2;
02316   bits = obj1.getInt();
02317   obj1.free();
02318 
02319   // display a mask
02320   if (mask) {
02321 
02322     // check for inverted mask
02323     if (bits != 1)
02324       goto err1;
02325     invert = gFalse;
02326     dict->lookup("Decode", &obj1);
02327     if (obj1.isNull()) {
02328       obj1.free();
02329       dict->lookup("D", &obj1);
02330     }
02331     if (obj1.isArray()) {
02332       obj1.arrayGet(0, &obj2);
02333       if (obj2.isInt() && obj2.getInt() == 1)
02334     invert = gTrue;
02335       obj2.free();
02336     } else if (!obj1.isNull()) {
02337       goto err2;
02338     }
02339     obj1.free();
02340 
02341     // draw it
02342     out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
02343 
02344   } else {
02345 
02346     // get color space and color map
02347     dict->lookup("ColorSpace", &obj1);
02348     if (obj1.isNull()) {
02349       obj1.free();
02350       dict->lookup("CS", &obj1);
02351     }
02352     if (obj1.isName()) {
02353       res->lookupColorSpace(obj1.getName(), &obj2);
02354       if (!obj2.isNull()) {
02355     obj1.free();
02356     obj1 = obj2;
02357       } else {
02358     obj2.free();
02359       }
02360     }
02361     colorSpace = GfxColorSpace::parse(&obj1);
02362     obj1.free();
02363     if (!colorSpace) {
02364       goto err1;
02365     }
02366     dict->lookup("Decode", &obj1);
02367     if (obj1.isNull()) {
02368       obj1.free();
02369       dict->lookup("D", &obj1);
02370     }
02371     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
02372     obj1.free();
02373     if (!colorMap->isOk()) {
02374       delete colorMap;
02375       goto err1;
02376     }
02377 
02378     // get the mask
02379     haveMask = gFalse;
02380     dict->lookup("Mask", &maskObj);
02381     if (maskObj.isArray()) {
02382       for (i = 0;
02383        i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
02384        ++i) {
02385     maskObj.arrayGet(i, &obj1);
02386     maskColors[i] = obj1.getInt();
02387     obj1.free();
02388       }
02389       haveMask = gTrue;
02390     }
02391 
02392     // draw it
02393     out->drawImage(state, ref, str, width, height, colorMap,
02394            haveMask ? maskColors : (int *)NULL,  inlineImg);
02395     delete colorMap;
02396 
02397     maskObj.free();
02398   }
02399 
02400   if ((i = width * height) > 1000) {
02401     i = 1000;
02402   }
02403   updateLevel += i;
02404 
02405   return;
02406 
02407  err2:
02408   obj1.free();
02409  err1:
02410   error(getPos(), "Bad image parameters");
02411 }
02412 
02413 void Gfx::doForm(Object *str) {
02414   Dict *dict;
02415   Object matrixObj, bboxObj;
02416   double m[6], bbox[6];
02417   Object resObj;
02418   Dict *resDict;
02419   Object obj1;
02420   int i;
02421 
02422   // get stream dict
02423   dict = str->streamGetDict();
02424 
02425   // check form type
02426   dict->lookup("FormType", &obj1);
02427   if (!(obj1.isInt() && obj1.getInt() == 1)) {
02428     error(getPos(), "Unknown form type");
02429   }
02430   obj1.free();
02431 
02432   // get bounding box
02433   dict->lookup("BBox", &bboxObj);
02434   if (!bboxObj.isArray()) {
02435     matrixObj.free();
02436     bboxObj.free();
02437     error(getPos(), "Bad form bounding box");
02438     return;
02439   }
02440   for (i = 0; i < 4; ++i) {
02441     bboxObj.arrayGet(i, &obj1);
02442     bbox[i] = obj1.getNum();
02443     obj1.free();
02444   }
02445   bboxObj.free();
02446 
02447   // get matrix
02448   dict->lookup("Matrix", &matrixObj);
02449   if (matrixObj.isArray()) {
02450     for (i = 0; i < 6; ++i) {
02451       matrixObj.arrayGet(i, &obj1);
02452       m[i] = obj1.getNum();
02453       obj1.free();
02454     }
02455   } else {
02456     m[0] = 1; m[1] = 0;
02457     m[2] = 0; m[3] = 1;
02458     m[4] = 0; m[5] = 0;
02459   }
02460   matrixObj.free();
02461 
02462   // get resources
02463   dict->lookup("Resources", &resObj);
02464   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
02465 
02466   // draw it
02467   doForm1(str, resDict, m, bbox);
02468 
02469   resObj.free();
02470 }
02471 
02472 void Gfx::doAnnot(Object *str, double xMin, double yMin,
02473           double xMax, double yMax) {
02474   Dict *dict, *resDict;
02475   Object matrixObj, bboxObj, resObj;
02476   Object obj1;
02477   double m[6], bbox[6], ictm[6];
02478   const double *ctm;
02479   double formX0, formY0, formX1, formY1;
02480   double annotX0, annotY0, annotX1, annotY1;
02481   double det, x, y, sx, sy;
02482   int i;
02483 
02484   // get stream dict
02485   dict = str->streamGetDict();
02486 
02487   // get the form bounding box
02488   dict->lookup("BBox", &bboxObj);
02489   if (!bboxObj.isArray()) {
02490     bboxObj.free();
02491     error(getPos(), "Bad form bounding box");
02492     return;
02493   }
02494   for (i = 0; i < 4; ++i) {
02495     bboxObj.arrayGet(i, &obj1);
02496     bbox[i] = obj1.getNum();
02497     obj1.free();
02498   }
02499   bboxObj.free();
02500 
02501   // get the form matrix
02502   dict->lookup("Matrix", &matrixObj);
02503   if (matrixObj.isArray()) {
02504     for (i = 0; i < 6; ++i) {
02505       matrixObj.arrayGet(i, &obj1);
02506       m[i] = obj1.getNum();
02507       obj1.free();
02508     }
02509   } else {
02510     m[0] = 1; m[1] = 0;
02511     m[2] = 0; m[3] = 1;
02512     m[4] = 0; m[5] = 0;
02513   }
02514   matrixObj.free();
02515 
02516   // transform the form bbox from form space to user space
02517   formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
02518   formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
02519   formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
02520   formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
02521 
02522   // transform the annotation bbox from default user space to user
02523   // space: (bbox * baseMatrix) * iCTM
02524   ctm = state->getCTM();
02525   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
02526   ictm[0] = ctm[3] * det;
02527   ictm[1] = -ctm[1] * det;
02528   ictm[2] = -ctm[2] * det;
02529   ictm[3] = ctm[0] * det;
02530   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
02531   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
02532   x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
02533   y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
02534   annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
02535   annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
02536   x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
02537   y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
02538   annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
02539   annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
02540 
02541   // swap min/max coords
02542   if (formX0 > formX1) {
02543     x = formX0; formX0 = formX1; formX1 = x;
02544   }
02545   if (formY0 > formY1) {
02546     y = formY0; formY0 = formY1; formY1 = y;
02547   }
02548   if (annotX0 > annotX1) {
02549     x = annotX0; annotX0 = annotX1; annotX1 = x;
02550   }
02551   if (annotY0 > annotY1) {
02552     y = annotY0; annotY0 = annotY1; annotY1 = y;
02553   }
02554 
02555   // scale the form to fit the annotation bbox
02556   if (formX1 == formX0) {
02557     // this shouldn't happen
02558     sx = 1;
02559   } else {
02560     sx = (annotX1 - annotX0) / (formX1 - formX0);
02561   }
02562   if (formY1 == formY0) {
02563     // this shouldn't happen
02564     sy = 1;
02565   } else {
02566     sy = (annotY1 - annotY0) / (formY1 - formY0);
02567   }
02568   m[0] *= sx;
02569   m[2] *= sx;
02570   m[4] = (m[4] - formX0) * sx + annotX0;
02571   m[1] *= sy;
02572   m[3] *= sy;
02573   m[5] = (m[5] - formY0) * sy + annotY0;
02574 
02575   // get resources
02576   dict->lookup("Resources", &resObj);
02577   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
02578 
02579   // draw it
02580   doForm1(str, resDict, m, bbox);
02581 
02582   resObj.free();
02583   bboxObj.free();
02584 }
02585 
02586 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
02587   Parser *oldParser;
02588   double oldBaseMatrix[6];
02589   int i;
02590 
02591   // push new resources on stack
02592   pushResources(resDict);
02593 
02594   // save current graphics state
02595   out->saveState(state);
02596   state = state->save();
02597 
02598   // save current parser
02599   oldParser = parser;
02600 
02601   // set form transformation matrix
02602   state->concatCTM(matrix[0], matrix[1], matrix[2],
02603            matrix[3], matrix[4], matrix[5]);
02604   out->updateCTM(state, matrix[0], matrix[1], matrix[2],
02605          matrix[3], matrix[4], matrix[5]);
02606 
02607   // set new base matrix
02608   for (i = 0; i < 6; ++i) {
02609     oldBaseMatrix[i] = baseMatrix[i];
02610     baseMatrix[i] = state->getCTM()[i];
02611   }
02612 
02613   // set form bounding box
02614   state->moveTo(bbox[0], bbox[1]);
02615   state->lineTo(bbox[2], bbox[1]);
02616   state->lineTo(bbox[2], bbox[3]);
02617   state->lineTo(bbox[0], bbox[3]);
02618   state->closePath();
02619   state->clip();
02620   out->clip(state);
02621   state->clearPath();
02622 
02623   // draw the form
02624   display(str, gFalse);
02625 
02626   // restore base matrix
02627   for (i = 0; i < 6; ++i) {
02628     baseMatrix[i] = oldBaseMatrix[i];
02629   }
02630 
02631   // restore parser
02632   parser = oldParser;
02633 
02634   // restore graphics state
02635   state = state->restore();
02636   out->restoreState(state);
02637 
02638   // pop resource stack
02639   popResources();
02640 
02641   return;
02642 }
02643 
02644 void Gfx::pushResources(Dict *resDict) {
02645   res = new GfxResources(xref, resDict, res);
02646 }
02647 
02648 void Gfx::popResources() {
02649   GfxResources *resPtr;
02650 
02651   resPtr = res->getNext();
02652   delete res;
02653   res = resPtr;
02654 }
02655 
02656 //------------------------------------------------------------------------
02657 // in-line image operators
02658 //------------------------------------------------------------------------
02659 
02660 void Gfx::opBeginImage(Object /*args*/[], int /*numArgs*/) {
02661   Stream *str;
02662   int c1, c2;
02663 
02664   // build dict/stream
02665   str = buildImageStream();
02666 
02667   // display the image
02668   if (str) {
02669     doImage(NULL, str, gTrue);
02670 
02671     // skip 'EI' tag
02672     c1 = str->getBaseStream()->getChar();
02673     c2 = str->getBaseStream()->getChar();
02674     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
02675       c1 = c2;
02676       c2 = str->getBaseStream()->getChar();
02677     }
02678     delete str;
02679   }
02680 }
02681 
02682 Stream *Gfx::buildImageStream() {
02683   Object dict;
02684   Object obj;
02685   char *key;
02686   Stream *str;
02687 
02688   // build dictionary
02689   dict.initDict(xref);
02690   parser->getObj(&obj);
02691   while (!obj.isCmd("ID") && !obj.isEOF()) {
02692     if (!obj.isName()) {
02693       error(getPos(), "Inline image dictionary key must be a name object");
02694       obj.free();
02695     } else {
02696       key = copyString(obj.getName());
02697       obj.free();
02698       parser->getObj(&obj);
02699       if (obj.isEOF() || obj.isError()) {
02700     gfree(key);
02701     break;
02702       }
02703       dict.dictAdd(key, &obj);
02704     }
02705     parser->getObj(&obj);
02706   }
02707   if (obj.isEOF()) {
02708     error(getPos(), "End of file in inline image");
02709     obj.free();
02710     dict.free();
02711     return NULL;
02712   }
02713   obj.free();
02714 
02715   // make stream
02716   str = new EmbedStream(parser->getStream(), &dict);
02717   str = str->addFilters(&dict);
02718 
02719   return str;
02720 }
02721 
02722 void Gfx::opImageData(Object /*args*/[], int /*numArgs*/) {
02723   error(getPos(), "Internal: got 'ID' operator");
02724 }
02725 
02726 void Gfx::opEndImage(Object /*args*/[], int /*numArgs*/) {
02727   error(getPos(), "Internal: got 'EI' operator");
02728 }
02729 
02730 //------------------------------------------------------------------------
02731 // type 3 font operators
02732 //------------------------------------------------------------------------
02733 
02734 void Gfx::opSetCharWidth(Object args[], int /*numArgs*/) {
02735   out->type3D0(state, args[0].getNum(), args[1].getNum());
02736 }
02737 
02738 void Gfx::opSetCacheDevice(Object args[], int /*numArgs*/) {
02739   out->type3D1(state, args[0].getNum(), args[1].getNum(),
02740            args[2].getNum(), args[3].getNum(),
02741            args[4].getNum(), args[5].getNum());
02742 }
02743 
02744 //------------------------------------------------------------------------
02745 // compatibility operators
02746 //------------------------------------------------------------------------
02747 
02748 void Gfx::opBeginIgnoreUndef(Object /*args*/[], int /*numArgs*/) {
02749   ++ignoreUndef;
02750 }
02751 
02752 void Gfx::opEndIgnoreUndef(Object /*args*/[], int /*numArgs*/) {
02753   if (ignoreUndef > 0)
02754     --ignoreUndef;
02755 }
02756 
02757 //------------------------------------------------------------------------
02758 // marked content operators
02759 //------------------------------------------------------------------------
02760 
02761 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
02762   if (printCommands) {
02763     printf("  marked content: %s ", args[0].getName());
02764     if (numArgs == 2)
02765       args[2].print(stdout);
02766     printf("\n");
02767     fflush(stdout);
02768   }
02769 }
02770 
02771 void Gfx::opEndMarkedContent(Object /*args*/[], int /*numArgs*/) {
02772 }
02773 
02774 void Gfx::opMarkPoint(Object args[], int numArgs) {
02775   if (printCommands) {
02776     printf("  mark point: %s ", args[0].getName());
02777     if (numArgs == 2)
02778       args[2].print(stdout);
02779     printf("\n");
02780     fflush(stdout);
02781   }
02782 }
KDE Home | KDE Accessibility Home | Description of Access Keys