Back to index

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