added (call to) useGradients().
[swftools.git] / pdf2swf / xpdf / Gfx.cc
1 //========================================================================
2 //
3 // Gfx.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stdio.h>
16 #include <stddef.h>
17 #include <string.h>
18 #include <math.h>
19 #include "gmem.h"
20 #include "GlobalParams.h"
21 #include "CharTypes.h"
22 #include "Object.h"
23 #include "Array.h"
24 #include "Dict.h"
25 #include "Stream.h"
26 #include "Lexer.h"
27 #include "Parser.h"
28 #include "GfxFont.h"
29 #include "GfxState.h"
30 #include "OutputDev.h"
31 #include "Page.h"
32 #include "Error.h"
33 #include "Gfx.h"
34
35 // the MSVC math.h doesn't define this
36 #ifndef M_PI
37 #define M_PI 3.14159265358979323846
38 #endif
39
40 //------------------------------------------------------------------------
41 // constants
42 //------------------------------------------------------------------------
43
44 // Max recursive depth for a function shading fill.
45 #define functionMaxDepth 6
46
47 // Max delta allowed in any color component for a function shading fill.
48 #define functionColorDelta (1 / 256.0)
49
50 // Max number of splits along the t axis for an axial shading fill.
51 #define axialMaxSplits 256
52
53 // Max delta allowed in any color component for an axial shading fill.
54 #define axialColorDelta (1 / 256.0)
55
56 // Max number of splits along the t axis for a radial shading fill.
57 #define radialMaxSplits 256
58
59 // Max delta allowed in any color component for a radial shading fill.
60 #define radialColorDelta (1 / 256.0)
61
62 //------------------------------------------------------------------------
63 // Operator table
64 //------------------------------------------------------------------------
65
66 #ifdef WIN32 // this works around a bug in the VC7 compiler
67 #  pragma optimize("",off)
68 #endif
69
70 Operator Gfx::opTab[] = {
71   {"\"",  3, {tchkNum,    tchkNum,    tchkString},
72           &Gfx::opMoveSetShowText},
73   {"'",   1, {tchkString},
74           &Gfx::opMoveShowText},
75   {"B",   0, {tchkNone},
76           &Gfx::opFillStroke},
77   {"B*",  0, {tchkNone},
78           &Gfx::opEOFillStroke},
79   {"BDC", 2, {tchkName,   tchkProps},
80           &Gfx::opBeginMarkedContent},
81   {"BI",  0, {tchkNone},
82           &Gfx::opBeginImage},
83   {"BMC", 1, {tchkName},
84           &Gfx::opBeginMarkedContent},
85   {"BT",  0, {tchkNone},
86           &Gfx::opBeginText},
87   {"BX",  0, {tchkNone},
88           &Gfx::opBeginIgnoreUndef},
89   {"CS",  1, {tchkName},
90           &Gfx::opSetStrokeColorSpace},
91   {"DP",  2, {tchkName,   tchkProps},
92           &Gfx::opMarkPoint},
93   {"Do",  1, {tchkName},
94           &Gfx::opXObject},
95   {"EI",  0, {tchkNone},
96           &Gfx::opEndImage},
97   {"EMC", 0, {tchkNone},
98           &Gfx::opEndMarkedContent},
99   {"ET",  0, {tchkNone},
100           &Gfx::opEndText},
101   {"EX",  0, {tchkNone},
102           &Gfx::opEndIgnoreUndef},
103   {"F",   0, {tchkNone},
104           &Gfx::opFill},
105   {"G",   1, {tchkNum},
106           &Gfx::opSetStrokeGray},
107   {"ID",  0, {tchkNone},
108           &Gfx::opImageData},
109   {"J",   1, {tchkInt},
110           &Gfx::opSetLineCap},
111   {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
112           &Gfx::opSetStrokeCMYKColor},
113   {"M",   1, {tchkNum},
114           &Gfx::opSetMiterLimit},
115   {"MP",  1, {tchkName},
116           &Gfx::opMarkPoint},
117   {"Q",   0, {tchkNone},
118           &Gfx::opRestore},
119   {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
120           &Gfx::opSetStrokeRGBColor},
121   {"S",   0, {tchkNone},
122           &Gfx::opStroke},
123   {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
124           &Gfx::opSetStrokeColor},
125   {"SCN", -5, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
126                tchkSCN},
127           &Gfx::opSetStrokeColorN},
128   {"T*",  0, {tchkNone},
129           &Gfx::opTextNextLine},
130   {"TD",  2, {tchkNum,    tchkNum},
131           &Gfx::opTextMoveSet},
132   {"TJ",  1, {tchkArray},
133           &Gfx::opShowSpaceText},
134   {"TL",  1, {tchkNum},
135           &Gfx::opSetTextLeading},
136   {"Tc",  1, {tchkNum},
137           &Gfx::opSetCharSpacing},
138   {"Td",  2, {tchkNum,    tchkNum},
139           &Gfx::opTextMove},
140   {"Tf",  2, {tchkName,   tchkNum},
141           &Gfx::opSetFont},
142   {"Tj",  1, {tchkString},
143           &Gfx::opShowText},
144   {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
145               tchkNum,    tchkNum},
146           &Gfx::opSetTextMatrix},
147   {"Tr",  1, {tchkInt},
148           &Gfx::opSetTextRender},
149   {"Ts",  1, {tchkNum},
150           &Gfx::opSetTextRise},
151   {"Tw",  1, {tchkNum},
152           &Gfx::opSetWordSpacing},
153   {"Tz",  1, {tchkNum},
154           &Gfx::opSetHorizScaling},
155   {"W",   0, {tchkNone},
156           &Gfx::opClip},
157   {"W*",  0, {tchkNone},
158           &Gfx::opEOClip},
159   {"b",   0, {tchkNone},
160           &Gfx::opCloseFillStroke},
161   {"b*",  0, {tchkNone},
162           &Gfx::opCloseEOFillStroke},
163   {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
164               tchkNum,    tchkNum},
165           &Gfx::opCurveTo},
166   {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
167               tchkNum,    tchkNum},
168           &Gfx::opConcat},
169   {"cs",  1, {tchkName},
170           &Gfx::opSetFillColorSpace},
171   {"d",   2, {tchkArray,  tchkNum},
172           &Gfx::opSetDash},
173   {"d0",  2, {tchkNum,    tchkNum},
174           &Gfx::opSetCharWidth},
175   {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
176               tchkNum,    tchkNum},
177           &Gfx::opSetCacheDevice},
178   {"f",   0, {tchkNone},
179           &Gfx::opFill},
180   {"f*",  0, {tchkNone},
181           &Gfx::opEOFill},
182   {"g",   1, {tchkNum},
183           &Gfx::opSetFillGray},
184   {"gs",  1, {tchkName},
185           &Gfx::opSetExtGState},
186   {"h",   0, {tchkNone},
187           &Gfx::opClosePath},
188   {"i",   1, {tchkNum},
189           &Gfx::opSetFlat},
190   {"j",   1, {tchkInt},
191           &Gfx::opSetLineJoin},
192   {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
193           &Gfx::opSetFillCMYKColor},
194   {"l",   2, {tchkNum,    tchkNum},
195           &Gfx::opLineTo},
196   {"m",   2, {tchkNum,    tchkNum},
197           &Gfx::opMoveTo},
198   {"n",   0, {tchkNone},
199           &Gfx::opEndPath},
200   {"q",   0, {tchkNone},
201           &Gfx::opSave},
202   {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
203           &Gfx::opRectangle},
204   {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
205           &Gfx::opSetFillRGBColor},
206   {"ri",  1, {tchkName},
207           &Gfx::opSetRenderingIntent},
208   {"s",   0, {tchkNone},
209           &Gfx::opCloseStroke},
210   {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
211           &Gfx::opSetFillColor},
212   {"scn", -5, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
213                tchkSCN},
214           &Gfx::opSetFillColorN},
215   {"sh",  1, {tchkName},
216           &Gfx::opShFill},
217   {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
218           &Gfx::opCurveTo1},
219   {"w",   1, {tchkNum},
220           &Gfx::opSetLineWidth},
221   {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
222           &Gfx::opCurveTo2},
223 };
224
225 #ifdef WIN32 // this works around a bug in the VC7 compiler
226 #  pragma optimize("",on)
227 #endif
228
229 #define numOps (sizeof(opTab) / sizeof(Operator))
230
231 //------------------------------------------------------------------------
232 // GfxResources
233 //------------------------------------------------------------------------
234
235 GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
236   Object obj1, obj2;
237   Ref r;
238
239   if (resDict) {
240
241     // build font dictionary
242     fonts = NULL;
243     resDict->lookupNF("Font", &obj1);
244     if (obj1.isRef()) {
245       obj1.fetch(xref, &obj2);
246       if (obj2.isDict()) {
247         r = obj1.getRef();
248         fonts = new GfxFontDict(xref, &r, obj2.getDict());
249       }
250       obj2.free();
251     } else if (obj1.isDict()) {
252       fonts = new GfxFontDict(xref, NULL, obj1.getDict());
253     }
254     obj1.free();
255
256     // get XObject dictionary
257     resDict->lookup("XObject", &xObjDict);
258
259     // get color space dictionary
260     resDict->lookup("ColorSpace", &colorSpaceDict);
261
262     // get pattern dictionary
263     resDict->lookup("Pattern", &patternDict);
264
265     // get shading dictionary
266     resDict->lookup("Shading", &shadingDict);
267
268     // get graphics state parameter dictionary
269     resDict->lookup("ExtGState", &gStateDict);
270
271   } else {
272     fonts = NULL;
273     xObjDict.initNull();
274     colorSpaceDict.initNull();
275     patternDict.initNull();
276     shadingDict.initNull();
277     gStateDict.initNull();
278   }
279
280   next = nextA;
281 }
282
283 GfxResources::~GfxResources() {
284   if (fonts) {
285     delete fonts;
286   }
287   xObjDict.free();
288   colorSpaceDict.free();
289   patternDict.free();
290   shadingDict.free();
291   gStateDict.free();
292 }
293
294 GfxFont *GfxResources::lookupFont(char *name) {
295   GfxFont *font;
296   GfxResources *resPtr;
297
298   for (resPtr = this; resPtr; resPtr = resPtr->next) {
299     if (resPtr->fonts) {
300       if ((font = resPtr->fonts->lookup(name)))
301         return font;
302     }
303   }
304   error(-1, "Unknown font tag '%s'", name);
305   return NULL;
306 }
307
308 GBool GfxResources::lookupXObject(char *name, Object *obj) {
309   GfxResources *resPtr;
310
311   for (resPtr = this; resPtr; resPtr = resPtr->next) {
312     if (resPtr->xObjDict.isDict()) {
313       if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
314         return gTrue;
315       obj->free();
316     }
317   }
318   error(-1, "XObject '%s' is unknown", name);
319   return gFalse;
320 }
321
322 GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
323   GfxResources *resPtr;
324
325   for (resPtr = this; resPtr; resPtr = resPtr->next) {
326     if (resPtr->xObjDict.isDict()) {
327       if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
328         return gTrue;
329       obj->free();
330     }
331   }
332   error(-1, "XObject '%s' is unknown", name);
333   return gFalse;
334 }
335
336 void GfxResources::lookupColorSpace(char *name, Object *obj) {
337   GfxResources *resPtr;
338
339   for (resPtr = this; resPtr; resPtr = resPtr->next) {
340     if (resPtr->colorSpaceDict.isDict()) {
341       if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
342         return;
343       }
344       obj->free();
345     }
346   }
347   obj->initNull();
348 }
349
350 GfxPattern *GfxResources::lookupPattern(char *name) {
351   GfxResources *resPtr;
352   GfxPattern *pattern;
353   Object obj;
354
355   for (resPtr = this; resPtr; resPtr = resPtr->next) {
356     if (resPtr->patternDict.isDict()) {
357       if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
358         pattern = GfxPattern::parse(&obj);
359         obj.free();
360         return pattern;
361       }
362       obj.free();
363     }
364   }
365   error(-1, "Unknown pattern '%s'", name);
366   return NULL;
367 }
368
369 GfxShading *GfxResources::lookupShading(char *name) {
370   GfxResources *resPtr;
371   GfxShading *shading;
372   Object obj;
373
374   for (resPtr = this; resPtr; resPtr = resPtr->next) {
375     if (resPtr->shadingDict.isDict()) {
376       if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
377         shading = GfxShading::parse(&obj);
378         obj.free();
379         return shading;
380       }
381       obj.free();
382     }
383   }
384   error(-1, "Unknown shading '%s'", name);
385   return NULL;
386 }
387
388 GBool GfxResources::lookupGState(char *name, Object *obj) {
389   GfxResources *resPtr;
390
391   for (resPtr = this; resPtr; resPtr = resPtr->next) {
392     if (resPtr->gStateDict.isDict()) {
393       if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
394         return gTrue;
395       }
396       obj->free();
397     }
398   }
399   error(-1, "ExtGState '%s' is unknown", name);
400   return gFalse;
401 }
402
403 //------------------------------------------------------------------------
404 // Gfx
405 //------------------------------------------------------------------------
406
407 Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
408          double hDPI, double vDPI, PDFRectangle *box, GBool crop,
409          PDFRectangle *cropBox, int rotate,
410          GBool (*abortCheckCbkA)(void *data),
411          void *abortCheckCbkDataA) {
412   int i;
413
414   xref = xrefA;
415   subPage = gFalse;
416   printCommands = globalParams->getPrintCommands();
417
418   // start the resource stack
419   res = new GfxResources(xref, resDict, NULL);
420
421   // initialize
422   out = outA;
423   state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
424   fontChanged = gFalse;
425   clip = clipNone;
426   ignoreUndef = 0;
427   out->startPage(pageNum, state, cropBox->x1,cropBox->y1,cropBox->x2,cropBox->y2);
428   out->setDefaultCTM(state->getCTM());
429   out->updateAll(state);
430   for (i = 0; i < 6; ++i) {
431     baseMatrix[i] = state->getCTM()[i];
432   }
433   formDepth = 0;
434   abortCheckCbk = abortCheckCbkA;
435   abortCheckCbkData = abortCheckCbkDataA;
436
437   // set crop box
438   /*if (crop) {
439     state->moveTo(cropBox->x1, cropBox->y1);
440     state->lineTo(cropBox->x2, cropBox->y1);
441     state->lineTo(cropBox->x2, cropBox->y2);
442     state->lineTo(cropBox->x1, cropBox->y2);
443     state->closePath();
444     state->clip();
445     out->clip(state);
446     state->clearPath();
447   }*/
448 }
449
450 Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
451          PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
452          GBool (*abortCheckCbkA)(void *data),
453          void *abortCheckCbkDataA) {
454   int i;
455
456   xref = xrefA;
457   subPage = gTrue;
458   printCommands = globalParams->getPrintCommands();
459
460   // start the resource stack
461   res = new GfxResources(xref, resDict, NULL);
462
463   // initialize
464   out = outA;
465   state = new GfxState(72, 72, box, 0, gFalse);
466   fontChanged = gFalse;
467   clip = clipNone;
468   ignoreUndef = 0;
469   for (i = 0; i < 6; ++i) {
470     baseMatrix[i] = state->getCTM()[i];
471   }
472   formDepth = 0;
473   abortCheckCbk = abortCheckCbkA;
474   abortCheckCbkData = abortCheckCbkDataA;
475
476   // set crop box
477   if (crop) {
478     state->moveTo(cropBox->x1, cropBox->y1);
479     state->lineTo(cropBox->x2, cropBox->y1);
480     state->lineTo(cropBox->x2, cropBox->y2);
481     state->lineTo(cropBox->x1, cropBox->y2);
482     state->closePath();
483     state->clip();
484     out->clip(state);
485     state->clearPath();
486   }
487 }
488
489 Gfx::~Gfx() {
490   while (state->hasSaves()) {
491     restoreState();
492   }
493   if (!subPage) {
494     out->endPage();
495   }
496   while (res) {
497     popResources();
498   }
499   if (state) {
500     delete state;
501   }
502 }
503
504 void Gfx::display(Object *obj, GBool topLevel) {
505   Object obj2;
506   int i;
507
508   if (obj->isArray()) {
509     for (i = 0; i < obj->arrayGetLength(); ++i) {
510       obj->arrayGet(i, &obj2);
511       if (!obj2.isStream()) {
512         error(-1, "Weird page contents");
513         obj2.free();
514         return;
515       }
516       obj2.free();
517     }
518   } else if (!obj->isStream()) {
519     error(-1, "Weird page contents");
520     return;
521   }
522   parser = new Parser(xref, new Lexer(xref, obj));
523   go(topLevel);
524   delete parser;
525   parser = NULL;
526 }
527
528 void Gfx::go(GBool topLevel) {
529   Object obj;
530   Object args[maxArgs];
531   int numArgs, i;
532   int lastAbortCheck;
533
534   // scan a sequence of objects
535   updateLevel = lastAbortCheck = 0;
536   numArgs = 0;
537   parser->getObj(&obj);
538   while (!obj.isEOF()) {
539
540     // got a command - execute it
541     if (obj.isCmd()) {
542       if (printCommands) {
543         obj.print(stdout);
544         for (i = 0; i < numArgs; ++i) {
545           printf(" ");
546           args[i].print(stdout);
547         }
548         printf("\n");
549         fflush(stdout);
550       }
551       execOp(&obj, args, numArgs);
552       obj.free();
553       for (i = 0; i < numArgs; ++i)
554         args[i].free();
555       numArgs = 0;
556
557       // periodically update display
558       if (++updateLevel >= 20000) {
559         out->dump();
560         updateLevel = 0;
561       }
562
563       // check for an abort
564       if (abortCheckCbk) {
565         if (updateLevel - lastAbortCheck > 10) {
566           if ((*abortCheckCbk)(abortCheckCbkData)) {
567             break;
568           }
569           lastAbortCheck = updateLevel;
570         }
571       }
572
573     // got an argument - save it
574     } else if (numArgs < maxArgs) {
575       args[numArgs++] = obj;
576
577     // too many arguments - something is wrong
578     } else {
579       error(getPos(), "Too many args in content stream");
580       if (printCommands) {
581         printf("throwing away arg: ");
582         obj.print(stdout);
583         printf("\n");
584         fflush(stdout);
585       }
586       obj.free();
587     }
588
589     // grab the next object
590     parser->getObj(&obj);
591   }
592   obj.free();
593
594   // args at end with no command
595   if (numArgs > 0) {
596     error(getPos(), "Leftover args in content stream");
597     if (printCommands) {
598       printf("%d leftovers:", numArgs);
599       for (i = 0; i < numArgs; ++i) {
600         printf(" ");
601         args[i].print(stdout);
602       }
603       printf("\n");
604       fflush(stdout);
605     }
606     for (i = 0; i < numArgs; ++i)
607       args[i].free();
608   }
609
610   // update display
611   if (topLevel && updateLevel > 0) {
612     out->dump();
613   }
614 }
615
616 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
617   Operator *op;
618   char *name;
619   Object *argPtr;
620   int i;
621
622   // find operator
623   name = cmd->getCmd();
624   if (!(op = findOp(name))) {
625     if (ignoreUndef == 0)
626       error(getPos(), "Unknown operator '%s'", name);
627     return;
628   }
629
630   // type check args
631   argPtr = args;
632   if (op->numArgs >= 0) {
633     if (numArgs < op->numArgs) {
634       error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
635       return;
636     }
637     if (numArgs > op->numArgs) {
638 #if 0
639       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
640 #endif
641       argPtr += numArgs - op->numArgs;
642       numArgs = op->numArgs;
643     }
644   } else {
645     if (numArgs > -op->numArgs) {
646       error(getPos(), "Too many (%d) args to '%s' operator",
647             numArgs, name);
648       return;
649     }
650   }
651   for (i = 0; i < numArgs; ++i) {
652     if (!checkArg(&argPtr[i], op->tchk[i])) {
653       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
654             i, name, argPtr[i].getTypeName());
655       return;
656     }
657   }
658
659   // do it
660   (this->*op->func)(argPtr, numArgs);
661 }
662
663 Operator *Gfx::findOp(char *name) {
664   int a, b, m, cmp;
665
666   a = -1;
667   b = numOps;
668   // invariant: opTab[a] < name < opTab[b]
669   while (b - a > 1) {
670     m = (a + b) / 2;
671     cmp = strcmp(opTab[m].name, name);
672     if (cmp < 0)
673       a = m;
674     else if (cmp > 0)
675       b = m;
676     else
677       a = b = m;
678   }
679   if (cmp != 0)
680     return NULL;
681   return &opTab[a];
682 }
683
684 GBool Gfx::checkArg(Object *arg, TchkType type) {
685   switch (type) {
686   case tchkBool:   return arg->isBool();
687   case tchkInt:    return arg->isInt();
688   case tchkNum:    return arg->isNum();
689   case tchkString: return arg->isString();
690   case tchkName:   return arg->isName();
691   case tchkArray:  return arg->isArray();
692   case tchkProps:  return arg->isDict() || arg->isName();
693   case tchkSCN:    return arg->isNum() || arg->isName();
694   case tchkNone:   return gFalse;
695   }
696   return gFalse;
697 }
698
699 int Gfx::getPos() {
700   return parser ? parser->getPos() : -1;
701 }
702
703 //------------------------------------------------------------------------
704 // graphics state operators
705 //------------------------------------------------------------------------
706
707 void Gfx::opSave(Object args[], int numArgs) {
708   saveState();
709 }
710
711 void Gfx::opRestore(Object args[], int numArgs) {
712   restoreState();
713 }
714
715 void Gfx::opConcat(Object args[], int numArgs) {
716   state->concatCTM(args[0].getNum(), args[1].getNum(),
717                    args[2].getNum(), args[3].getNum(),
718                    args[4].getNum(), args[5].getNum());
719   out->updateCTM(state, args[0].getNum(), args[1].getNum(),
720                  args[2].getNum(), args[3].getNum(),
721                  args[4].getNum(), args[5].getNum());
722   fontChanged = gTrue;
723 }
724
725 void Gfx::opSetDash(Object args[], int numArgs) {
726   Array *a;
727   int length;
728   Object obj;
729   double *dash;
730   int i;
731
732   a = args[0].getArray();
733   length = a->getLength();
734   if (length == 0) {
735     dash = NULL;
736   } else {
737     dash = (double *)gmalloc(length * sizeof(double));
738     for (i = 0; i < length; ++i) {
739       dash[i] = a->get(i, &obj)->getNum();
740       obj.free();
741     }
742   }
743   state->setLineDash(dash, length, args[1].getNum());
744   out->updateLineDash(state);
745 }
746
747 void Gfx::opSetFlat(Object args[], int numArgs) {
748   state->setFlatness((int)args[0].getNum());
749   out->updateFlatness(state);
750 }
751
752 void Gfx::opSetLineJoin(Object args[], int numArgs) {
753   state->setLineJoin(args[0].getInt());
754   out->updateLineJoin(state);
755 }
756
757 void Gfx::opSetLineCap(Object args[], int numArgs) {
758   state->setLineCap(args[0].getInt());
759   out->updateLineCap(state);
760 }
761
762 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
763   state->setMiterLimit(args[0].getNum());
764   out->updateMiterLimit(state);
765 }
766
767 void Gfx::opSetLineWidth(Object args[], int numArgs) {
768   state->setLineWidth(args[0].getNum());
769   out->updateLineWidth(state);
770 }
771
772 void Gfx::opSetExtGState(Object args[], int numArgs) {
773   Object obj1, obj2;
774
775   if (!res->lookupGState(args[0].getName(), &obj1)) {
776     return;
777   }
778   if (!obj1.isDict()) {
779     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
780     obj1.free();
781     return;
782   }
783   if (obj1.dictLookup("ca", &obj2)->isNum()) {
784     state->setFillOpacity(obj2.getNum());
785     out->updateFillOpacity(state);
786   }
787   obj2.free();
788   if (obj1.dictLookup("CA", &obj2)->isNum()) {
789     state->setStrokeOpacity(obj2.getNum());
790     out->updateStrokeOpacity(state);
791   }
792   obj2.free();
793   obj1.free();
794 }
795
796 void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
797 }
798
799 //------------------------------------------------------------------------
800 // color operators
801 //------------------------------------------------------------------------
802
803 void Gfx::opSetFillGray(Object args[], int numArgs) {
804   GfxColor color;
805
806   state->setFillPattern(NULL);
807   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
808   color.c[0] = args[0].getNum();
809   state->setFillColor(&color);
810   out->updateFillColor(state);
811 }
812
813 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
814   GfxColor color;
815
816   state->setStrokePattern(NULL);
817   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
818   color.c[0] = args[0].getNum();
819   state->setStrokeColor(&color);
820   out->updateStrokeColor(state);
821 }
822
823 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
824   GfxColor color;
825   int i;
826
827   state->setFillPattern(NULL);
828   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
829   for (i = 0; i < 4; ++i) {
830     color.c[i] = args[i].getNum();
831   }
832   state->setFillColor(&color);
833   out->updateFillColor(state);
834 }
835
836 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
837   GfxColor color;
838   int i;
839
840   state->setStrokePattern(NULL);
841   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
842   for (i = 0; i < 4; ++i) {
843     color.c[i] = args[i].getNum();
844   }
845   state->setStrokeColor(&color);
846   out->updateStrokeColor(state);
847 }
848
849 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
850   GfxColor color;
851   int i;
852
853   state->setFillPattern(NULL);
854   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
855   for (i = 0; i < 3; ++i) {
856     color.c[i] = args[i].getNum();
857   }
858   state->setFillColor(&color);
859   out->updateFillColor(state);
860 }
861
862 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
863   GfxColor color;
864   int i;
865
866   state->setStrokePattern(NULL);
867   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
868   for (i = 0; i < 3; ++i) {
869     color.c[i] = args[i].getNum();
870   }
871   state->setStrokeColor(&color);
872   out->updateStrokeColor(state);
873 }
874
875 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
876   Object obj;
877   GfxColorSpace *colorSpace;
878   GfxColor color;
879   int i;
880
881   state->setFillPattern(NULL);
882   res->lookupColorSpace(args[0].getName(), &obj);
883   if (obj.isNull()) {
884     colorSpace = GfxColorSpace::parse(&args[0]);
885   } else {
886     colorSpace = GfxColorSpace::parse(&obj);
887   }
888   obj.free();
889   if (colorSpace) {
890     state->setFillColorSpace(colorSpace);
891   } else {
892     error(getPos(), "Bad color space (fill)");
893   }
894   for (i = 0; i < gfxColorMaxComps; ++i) {
895     color.c[i] = 0;
896   }
897   state->setFillColor(&color);
898   out->updateFillColor(state);
899 }
900
901 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
902   Object obj;
903   GfxColorSpace *colorSpace;
904   GfxColor color;
905   int i;
906
907   state->setStrokePattern(NULL);
908   res->lookupColorSpace(args[0].getName(), &obj);
909   if (obj.isNull()) {
910     colorSpace = GfxColorSpace::parse(&args[0]);
911   } else {
912     colorSpace = GfxColorSpace::parse(&obj);
913   }
914   obj.free();
915   if (colorSpace) {
916     state->setStrokeColorSpace(colorSpace);
917   } else {
918     error(getPos(), "Bad color space (stroke)");
919   }
920   for (i = 0; i < gfxColorMaxComps; ++i) {
921     color.c[i] = 0;
922   }
923   state->setStrokeColor(&color);
924   out->updateStrokeColor(state);
925 }
926
927 void Gfx::opSetFillColor(Object args[], int numArgs) {
928   GfxColor color;
929   int i;
930
931   state->setFillPattern(NULL);
932   for (i = 0; i < numArgs; ++i) {
933     color.c[i] = args[i].getNum();
934   }
935   state->setFillColor(&color);
936   out->updateFillColor(state);
937 }
938
939 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
940   GfxColor color;
941   int i;
942
943   state->setStrokePattern(NULL);
944   for (i = 0; i < numArgs; ++i) {
945     color.c[i] = args[i].getNum();
946   }
947   state->setStrokeColor(&color);
948   out->updateStrokeColor(state);
949 }
950
951 void Gfx::opSetFillColorN(Object args[], int numArgs) {
952   GfxColor color;
953   GfxPattern *pattern;
954   int i;
955
956   if (state->getFillColorSpace()->getMode() == csPattern) {
957     if (numArgs > 1) {
958       for (i = 0; i < numArgs && i < 4; ++i) {
959         if (args[i].isNum()) {
960           color.c[i] = args[i].getNum();
961         }
962       }
963       state->setFillColor(&color);
964       out->updateFillColor(state);
965     }
966     if (args[numArgs-1].isName() &&
967         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
968       state->setFillPattern(pattern);
969     }
970
971   } else {
972     state->setFillPattern(NULL);
973     for (i = 0; i < numArgs && i < 4; ++i) {
974       if (args[i].isNum()) {
975         color.c[i] = args[i].getNum();
976       }
977     }
978     state->setFillColor(&color);
979     out->updateFillColor(state);
980   }
981 }
982
983 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
984   GfxColor color;
985   GfxPattern *pattern;
986   int i;
987
988   if (state->getStrokeColorSpace()->getMode() == csPattern) {
989     if (numArgs > 1) {
990       for (i = 0; i < numArgs && i < 4; ++i) {
991         if (args[i].isNum()) {
992           color.c[i] = args[i].getNum();
993         }
994       }
995       state->setStrokeColor(&color);
996       out->updateStrokeColor(state);
997     }
998     if (args[numArgs-1].isName() &&
999         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1000       state->setStrokePattern(pattern);
1001     }
1002
1003   } else {
1004     state->setStrokePattern(NULL);
1005     for (i = 0; i < numArgs && i < 4; ++i) {
1006       if (args[i].isNum()) {
1007         color.c[i] = args[i].getNum();
1008       }
1009     }
1010     state->setStrokeColor(&color);
1011     out->updateStrokeColor(state);
1012   }
1013 }
1014
1015 //------------------------------------------------------------------------
1016 // path segment operators
1017 //------------------------------------------------------------------------
1018
1019 void Gfx::opMoveTo(Object args[], int numArgs) {
1020   state->moveTo(args[0].getNum(), args[1].getNum());
1021 }
1022
1023 void Gfx::opLineTo(Object args[], int numArgs) {
1024   if (!state->isCurPt()) {
1025     error(getPos(), "No current point in lineto");
1026     return;
1027   }
1028   state->lineTo(args[0].getNum(), args[1].getNum());
1029 }
1030
1031 void Gfx::opCurveTo(Object args[], int numArgs) {
1032   double x1, y1, x2, y2, x3, y3;
1033
1034   if (!state->isCurPt()) {
1035     error(getPos(), "No current point in curveto");
1036     return;
1037   }
1038   x1 = args[0].getNum();
1039   y1 = args[1].getNum();
1040   x2 = args[2].getNum();
1041   y2 = args[3].getNum();
1042   x3 = args[4].getNum();
1043   y3 = args[5].getNum();
1044   state->curveTo(x1, y1, x2, y2, x3, y3);
1045 }
1046
1047 void Gfx::opCurveTo1(Object args[], int numArgs) {
1048   double x1, y1, x2, y2, x3, y3;
1049
1050   if (!state->isCurPt()) {
1051     error(getPos(), "No current point in curveto1");
1052     return;
1053   }
1054   x1 = state->getCurX();
1055   y1 = state->getCurY();
1056   x2 = args[0].getNum();
1057   y2 = args[1].getNum();
1058   x3 = args[2].getNum();
1059   y3 = args[3].getNum();
1060   state->curveTo(x1, y1, x2, y2, x3, y3);
1061 }
1062
1063 void Gfx::opCurveTo2(Object args[], int numArgs) {
1064   double x1, y1, x2, y2, x3, y3;
1065
1066   if (!state->isCurPt()) {
1067     error(getPos(), "No current point in curveto2");
1068     return;
1069   }
1070   x1 = args[0].getNum();
1071   y1 = args[1].getNum();
1072   x2 = args[2].getNum();
1073   y2 = args[3].getNum();
1074   x3 = x2;
1075   y3 = y2;
1076   state->curveTo(x1, y1, x2, y2, x3, y3);
1077 }
1078
1079 void Gfx::opRectangle(Object args[], int numArgs) {
1080   double x, y, w, h;
1081
1082   x = args[0].getNum();
1083   y = args[1].getNum();
1084   w = args[2].getNum();
1085   h = args[3].getNum();
1086   state->moveTo(x, y);
1087   state->lineTo(x + w, y);
1088   state->lineTo(x + w, y + h);
1089   state->lineTo(x, y + h);
1090   state->closePath();
1091 }
1092
1093 void Gfx::opClosePath(Object args[], int numArgs) {
1094   if (!state->isCurPt()) {
1095     error(getPos(), "No current point in closepath");
1096     return;
1097   }
1098   state->closePath();
1099 }
1100
1101 //------------------------------------------------------------------------
1102 // path painting operators
1103 //------------------------------------------------------------------------
1104
1105 void Gfx::opEndPath(Object args[], int numArgs) {
1106   doEndPath();
1107 }
1108
1109 void Gfx::opStroke(Object args[], int numArgs) {
1110   if (!state->isCurPt()) {
1111     //error(getPos(), "No path in stroke");
1112     return;
1113   }
1114   if (state->isPath())
1115     out->stroke(state);
1116   doEndPath();
1117 }
1118
1119 void Gfx::opCloseStroke(Object args[], int numArgs) {
1120   if (!state->isCurPt()) {
1121     //error(getPos(), "No path in closepath/stroke");
1122     return;
1123   }
1124   if (state->isPath()) {
1125     state->closePath();
1126     out->stroke(state);
1127   }
1128   doEndPath();
1129 }
1130
1131 void Gfx::opFill(Object args[], int numArgs) {
1132   if (!state->isCurPt()) {
1133     //error(getPos(), "No path in fill");
1134     return;
1135   }
1136   if (state->isPath()) {
1137     if (state->getFillColorSpace()->getMode() == csPattern) {
1138       doPatternFill(gFalse);
1139     } else {
1140       out->fill(state);
1141     }
1142   }
1143   doEndPath();
1144 }
1145
1146 void Gfx::opEOFill(Object args[], int numArgs) {
1147   if (!state->isCurPt()) {
1148     //error(getPos(), "No path in eofill");
1149     return;
1150   }
1151   if (state->isPath()) {
1152     if (state->getFillColorSpace()->getMode() == csPattern) {
1153       doPatternFill(gTrue);
1154     } else {
1155       out->eoFill(state);
1156     }
1157   }
1158   doEndPath();
1159 }
1160
1161 void Gfx::opFillStroke(Object args[], int numArgs) {
1162   if (!state->isCurPt()) {
1163     //error(getPos(), "No path in fill/stroke");
1164     return;
1165   }
1166   if (state->isPath()) {
1167     if (state->getFillColorSpace()->getMode() == csPattern) {
1168       doPatternFill(gFalse);
1169     } else {
1170       out->fill(state);
1171     }
1172     out->stroke(state);
1173   }
1174   doEndPath();
1175 }
1176
1177 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1178   if (!state->isCurPt()) {
1179     //error(getPos(), "No path in closepath/fill/stroke");
1180     return;
1181   }
1182   if (state->isPath()) {
1183     state->closePath();
1184     if (state->getFillColorSpace()->getMode() == csPattern) {
1185       doPatternFill(gFalse);
1186     } else {
1187       out->fill(state);
1188     }
1189     out->stroke(state);
1190   }
1191   doEndPath();
1192 }
1193
1194 void Gfx::opEOFillStroke(Object args[], int numArgs) {
1195   if (!state->isCurPt()) {
1196     //error(getPos(), "No path in eofill/stroke");
1197     return;
1198   }
1199   if (state->isPath()) {
1200     if (state->getFillColorSpace()->getMode() == csPattern) {
1201       doPatternFill(gTrue);
1202     } else {
1203       out->eoFill(state);
1204     }
1205     out->stroke(state);
1206   }
1207   doEndPath();
1208 }
1209
1210 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1211   if (!state->isCurPt()) {
1212     //error(getPos(), "No path in closepath/eofill/stroke");
1213     return;
1214   }
1215   if (state->isPath()) {
1216     state->closePath();
1217     if (state->getFillColorSpace()->getMode() == csPattern) {
1218       doPatternFill(gTrue);
1219     } else {
1220       out->eoFill(state);
1221     }
1222     out->stroke(state);
1223   }
1224   doEndPath();
1225 }
1226
1227 void Gfx::doPatternFill(GBool eoFill) {
1228   GfxPattern *pattern;
1229
1230   // this is a bit of a kludge -- patterns can be really slow, so we
1231   // skip them if we're only doing text extraction, since they almost
1232   // certainly don't contain any text
1233   if (!out->needNonText()) {
1234     return;
1235   }
1236
1237   if (!(pattern = state->getFillPattern())) {
1238     return;
1239   }
1240   switch (pattern->getType()) {
1241   case 1:
1242     doTilingPatternFill((GfxTilingPattern *)pattern, eoFill);
1243     break;
1244   case 2:
1245     doShadingPatternFill((GfxShadingPattern *)pattern, eoFill);
1246     break;
1247   default:
1248     error(getPos(), "Unimplemented pattern type (%d) in fill",
1249           pattern->getType());
1250     break;
1251   }
1252 }
1253
1254 void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
1255   GfxPatternColorSpace *patCS;
1256   GfxColorSpace *cs;
1257   GfxPath *savedPath;
1258   double xMin, yMin, xMax, yMax, x, y, x1, y1;
1259   double cxMin, cyMin, cxMax, cyMax;
1260   int xi0, yi0, xi1, yi1, xi, yi;
1261   double *ctm, *btm, *ptm;
1262   double m[6], ictm[6], m1[6], imb[6];
1263   double det;
1264   double xstep, ystep;
1265   int i;
1266
1267   // get color space
1268   patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
1269
1270   // construct a (pattern space) -> (current space) transform matrix
1271   ctm = state->getCTM();
1272   btm = baseMatrix;
1273   ptm = tPat->getMatrix();
1274   // iCTM = invert CTM
1275   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1276   ictm[0] = ctm[3] * det;
1277   ictm[1] = -ctm[1] * det;
1278   ictm[2] = -ctm[2] * det;
1279   ictm[3] = ctm[0] * det;
1280   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1281   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1282   // m1 = PTM * BTM = PTM * base transform matrix
1283   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1284   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1285   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1286   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1287   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1288   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1289   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1290   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1291   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1292   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1293   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1294   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1295   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1296
1297   // construct a (base space) -> (pattern space) transform matrix
1298   det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1299   imb[0] = m1[3] * det;
1300   imb[1] = -m1[1] * det;
1301   imb[2] = -m1[2] * det;
1302   imb[3] = m1[0] * det;
1303   imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1304   imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1305
1306   // save current graphics state
1307   savedPath = state->getPath()->copy();
1308   saveState();
1309
1310   // set underlying color space (for uncolored tiling patterns); set
1311   // various other parameters (stroke color, line width) to match
1312   // Adobe's behavior
1313   if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1314     state->setFillColorSpace(cs->copy());
1315     state->setStrokeColorSpace(cs->copy());
1316     state->setStrokeColor(state->getFillColor());
1317   } else {
1318     state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1319     state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
1320   }
1321   state->setFillPattern(NULL);
1322   out->updateFillColor(state);
1323   state->setStrokePattern(NULL);
1324   out->updateStrokeColor(state);
1325   state->setLineWidth(0);
1326   out->updateLineWidth(state);
1327
1328   // clip to current path
1329   state->clip();
1330   if (eoFill) {
1331     out->eoClip(state);
1332   } else {
1333     out->clip(state);
1334   }
1335   state->clearPath();
1336
1337   // transform clip region bbox to pattern space
1338   state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1339   xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1340   yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1341   x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1342   y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1343   if (x1 < xMin) {
1344     xMin = x1;
1345   } else if (x1 > xMax) {
1346     xMax = x1;
1347   }
1348   if (y1 < yMin) {
1349     yMin = y1;
1350   } else if (y1 > yMax) {
1351     yMax = y1;
1352   }
1353   x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1354   y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1355   if (x1 < xMin) {
1356     xMin = x1;
1357   } else if (x1 > xMax) {
1358     xMax = x1;
1359   }
1360   if (y1 < yMin) {
1361     yMin = y1;
1362   } else if (y1 > yMax) {
1363     yMax = y1;
1364   }
1365   x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1366   y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1367   if (x1 < xMin) {
1368     xMin = x1;
1369   } else if (x1 > xMax) {
1370     xMax = x1;
1371   }
1372   if (y1 < yMin) {
1373     yMin = y1;
1374   } else if (y1 > yMax) {
1375     yMax = y1;
1376   }
1377
1378   // draw the pattern
1379   //~ this should treat negative steps differently -- start at right/top
1380   //~ edge instead of left/bottom (?)
1381   xstep = fabs(tPat->getXStep());
1382   ystep = fabs(tPat->getYStep());
1383   xi0 = (int)floor((xMin - tPat->getBBox()[0]) / xstep);
1384   xi1 = (int)ceil((xMax - tPat->getBBox()[0]) / xstep);
1385   yi0 = (int)floor((yMin - tPat->getBBox()[1]) / ystep);
1386   yi1 = (int)ceil((yMax - tPat->getBBox()[1]) / ystep);
1387   for (i = 0; i < 4; ++i) {
1388     m1[i] = m[i];
1389   }
1390   for (yi = yi0; yi < yi1; ++yi) {
1391     for (xi = xi0; xi < xi1; ++xi) {
1392       x = xi * xstep;
1393       y = yi * ystep;
1394       m1[4] = x * m[0] + y * m[2] + m[4];
1395       m1[5] = x * m[1] + y * m[3] + m[5];
1396       doForm1(tPat->getContentStream(), tPat->getResDict(),
1397               m1, tPat->getBBox());
1398     }
1399   }
1400
1401   // restore graphics state
1402   restoreState();
1403   state->setPath(savedPath);
1404 }
1405
1406 void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
1407   GfxShading *shading;
1408   GfxPath *savedPath;
1409   double *ctm, *btm, *ptm;
1410   double m[6], ictm[6], m1[6];
1411   double xMin, yMin, xMax, yMax;
1412   double det;
1413
1414   shading = sPat->getShading();
1415
1416   // save current graphics state
1417   savedPath = state->getPath()->copy();
1418   saveState();
1419
1420   // clip to bbox
1421   if (shading->getHasBBox()) {
1422     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1423     state->moveTo(xMin, yMin);
1424     state->lineTo(xMax, yMin);
1425     state->lineTo(xMax, yMax);
1426     state->lineTo(xMin, yMax);
1427     state->closePath();
1428     state->clip();
1429     out->clip(state);
1430     state->clearPath();
1431   }
1432
1433   // clip to current path
1434   state->clip();
1435   if (eoFill) {
1436     out->eoClip(state);
1437   } else {
1438     out->clip(state);
1439   }
1440   state->clearPath();
1441
1442   // construct a (pattern space) -> (current space) transform matrix
1443   ctm = state->getCTM();
1444   btm = baseMatrix;
1445   ptm = sPat->getMatrix();
1446   // iCTM = invert CTM
1447   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1448   ictm[0] = ctm[3] * det;
1449   ictm[1] = -ctm[1] * det;
1450   ictm[2] = -ctm[2] * det;
1451   ictm[3] = ctm[0] * det;
1452   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1453   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1454   // m1 = PTM * BTM = PTM * base transform matrix
1455   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1456   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1457   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1458   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1459   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1460   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1461   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1462   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1463   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1464   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1465   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1466   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1467   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1468
1469   // set the new matrix
1470   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1471   out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1472
1473   // set the color space
1474   state->setFillColorSpace(shading->getColorSpace()->copy());
1475
1476   // do shading type-specific operations
1477   switch (shading->getType()) {
1478   case 1:
1479     doFunctionShFill((GfxFunctionShading *)shading);
1480     break;
1481   case 2:
1482     doAxialShFill((GfxAxialShading *)shading);
1483     break;
1484   case 3:
1485     doRadialShFill((GfxRadialShading *)shading);
1486     break;
1487   }
1488
1489   // restore graphics state
1490   restoreState();
1491   state->setPath(savedPath);
1492 }
1493
1494 void Gfx::opShFill(Object args[], int numArgs) {
1495   GfxShading *shading;
1496   GfxPath *savedPath;
1497   double xMin, yMin, xMax, yMax;
1498
1499   if (!(shading = res->lookupShading(args[0].getName()))) {
1500     return;
1501   }
1502
1503   // save current graphics state
1504   savedPath = state->getPath()->copy();
1505   saveState();
1506
1507   // clip to bbox
1508   if (shading->getHasBBox()) {
1509     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1510     state->moveTo(xMin, yMin);
1511     state->lineTo(xMax, yMin);
1512     state->lineTo(xMax, yMax);
1513     state->lineTo(xMin, yMax);
1514     state->closePath();
1515     state->clip();
1516     out->clip(state);
1517     state->clearPath();
1518   }
1519
1520   // set the color space
1521   state->setFillColorSpace(shading->getColorSpace()->copy());
1522
1523   // do shading type-specific operations
1524   switch (shading->getType()) {
1525   case 1:
1526     doFunctionShFill((GfxFunctionShading *)shading);
1527     break;
1528   case 2:
1529     doAxialShFill((GfxAxialShading *)shading);
1530     break;
1531   case 3:
1532     doRadialShFill((GfxRadialShading *)shading);
1533     break;
1534   }
1535
1536   // restore graphics state
1537   restoreState();
1538   state->setPath(savedPath);
1539
1540   delete shading;
1541 }
1542
1543 void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
1544   double x0, y0, x1, y1;
1545   GfxColor colors[4];
1546
1547   shading->getDomain(&x0, &y0, &x1, &y1);
1548   shading->getColor(x0, y0, &colors[0]);
1549   shading->getColor(x0, y1, &colors[1]);
1550   shading->getColor(x1, y0, &colors[2]);
1551   shading->getColor(x1, y1, &colors[3]);
1552   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1553 }
1554
1555 void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
1556                             double x0, double y0,
1557                             double x1, double y1,
1558                             GfxColor *colors, int depth) {
1559   GfxColor fillColor;
1560   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1561   GfxColor colors2[4];
1562   double *matrix;
1563   double xM, yM;
1564   int nComps, i, j;
1565
1566   nComps = shading->getColorSpace()->getNComps();
1567   matrix = shading->getMatrix();
1568
1569   // compare the four corner colors
1570   for (i = 0; i < 4; ++i) {
1571     for (j = 0; j < nComps; ++j) {
1572       if (fabs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1573         break;
1574       }
1575     }
1576     if (j < nComps) {
1577       break;
1578     }
1579   }
1580
1581   // center of the rectangle
1582   xM = 0.5 * (x0 + x1);
1583   yM = 0.5 * (y0 + y1);
1584   
1585   out->useGradients();
1586
1587   // the four corner colors are close (or we hit the recursive limit)
1588   // -- fill the rectangle; but require at least one subdivision
1589   // (depth==0) to avoid problems when the four outer corners of the
1590   // shaded region are the same color
1591   if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1592
1593     // use the center color
1594     shading->getColor(xM, yM, &fillColor);
1595     state->setFillColor(&fillColor);
1596     out->updateFillColor(state);
1597
1598     // fill the rectangle
1599     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1600                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1601     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1602                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1603     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1604                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1605     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1606                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1607     state->closePath();
1608     out->fill(state);
1609     state->clearPath();
1610
1611   // the four corner colors are not close enough -- subdivide the
1612   // rectangle
1613   } else {
1614
1615     // colors[0]       colorM0       colors[2]
1616     //   (x0,y0)       (xM,y0)       (x1,y0)
1617     //         +----------+----------+
1618     //         |          |          |
1619     //         |    UL    |    UR    |
1620     // color0M |       colorMM       | color1M
1621     // (x0,yM) +----------+----------+ (x1,yM)
1622     //         |       (xM,yM)       |
1623     //         |    LL    |    LR    |
1624     //         |          |          |
1625     //         +----------+----------+
1626     // colors[1]       colorM1       colors[3]
1627     //   (x0,y1)       (xM,y1)       (x1,y1)
1628
1629     shading->getColor(x0, yM, &color0M);
1630     shading->getColor(x1, yM, &color1M);
1631     shading->getColor(xM, y0, &colorM0);
1632     shading->getColor(xM, y1, &colorM1);
1633     shading->getColor(xM, yM, &colorMM);
1634
1635     // upper-left sub-rectangle
1636     colors2[0] = colors[0];
1637     colors2[1] = color0M;
1638     colors2[2] = colorM0;
1639     colors2[3] = colorMM;
1640     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1641     
1642     // lower-left sub-rectangle
1643     colors2[0] = color0M;
1644     colors2[1] = colors[1];
1645     colors2[2] = colorMM;
1646     colors2[3] = colorM1;
1647     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1648     
1649     // upper-right sub-rectangle
1650     colors2[0] = colorM0;
1651     colors2[1] = colorMM;
1652     colors2[2] = colors[2];
1653     colors2[3] = color1M;
1654     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1655
1656     // lower-right sub-rectangle
1657     colors2[0] = colorMM;
1658     colors2[1] = colorM1;
1659     colors2[2] = color1M;
1660     colors2[3] = colors[3];
1661     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1662   }
1663 }
1664
1665 void Gfx::doAxialShFill(GfxAxialShading *shading) {
1666   double xMin, yMin, xMax, yMax;
1667   double x0, y0, x1, y1;
1668   double dx, dy, mul;
1669   double tMin, tMax, t, tx, ty;
1670   double s[4], sMin, sMax, tmp;
1671   double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
1672   double t0, t1, tt;
1673   double ta[axialMaxSplits + 1];
1674   int next[axialMaxSplits + 1];
1675   GfxColor color0, color1;
1676   int nComps;
1677   int i, j, k, kk;
1678
1679   // get the clip region bbox
1680   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1681
1682   // compute min and max t values, based on the four corners of the
1683   // clip region bbox
1684   shading->getCoords(&x0, &y0, &x1, &y1);
1685   dx = x1 - x0;
1686   dy = y1 - y0;
1687   mul = 1 / (dx * dx + dy * dy);
1688   tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
1689   t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
1690   if (t < tMin) {
1691     tMin = t;
1692   } else if (t > tMax) {
1693     tMax = t;
1694   }
1695   t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
1696   if (t < tMin) {
1697     tMin = t;
1698   } else if (t > tMax) {
1699     tMax = t;
1700   }
1701   t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
1702   if (t < tMin) {
1703     tMin = t;
1704   } else if (t > tMax) {
1705     tMax = t;
1706   }
1707   if (tMin < 0 && !shading->getExtend0()) {
1708     tMin = 0;
1709   }
1710   if (tMax > 1 && !shading->getExtend1()) {
1711     tMax = 1;
1712   }
1713
1714   // get the function domain
1715   t0 = shading->getDomain0();
1716   t1 = shading->getDomain1();
1717
1718   // Traverse the t axis and do the shading.
1719   //
1720   // For each point (tx, ty) on the t axis, consider a line through
1721   // that point perpendicular to the t axis:
1722   //
1723   //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
1724   //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
1725   //
1726   // Then look at the intersection of this line with the bounding box
1727   // (xMin, yMin, xMax, yMax).  In the general case, there are four
1728   // intersection points:
1729   //
1730   //     s0 = (xMin - tx) / -dy
1731   //     s1 = (xMax - tx) / -dy
1732   //     s2 = (yMin - ty) / dx
1733   //     s3 = (yMax - ty) / dx
1734   //
1735   // and we want the middle two s values.
1736   //
1737   // In the case where dx = 0, take s0 and s1; in the case where dy =
1738   // 0, take s2 and s3.
1739   //
1740   // Each filled polygon is bounded by two of these line segments
1741   // perpdendicular to the t axis.
1742   //
1743   // The t axis is bisected into smaller regions until the color
1744   // difference across a region is small enough, and then the region
1745   // is painted with a single color.
1746
1747   // set up: require at least one split to avoid problems when the two
1748   // ends of the t axis have the same color
1749   nComps = shading->getColorSpace()->getNComps();
1750   ta[0] = tMin;
1751   next[0] = axialMaxSplits / 2;
1752   ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
1753   next[axialMaxSplits / 2] = axialMaxSplits;
1754   ta[axialMaxSplits] = tMax;
1755
1756   // compute the color at t = tMin
1757   if (tMin < 0) {
1758     tt = t0;
1759   } else if (tMin > 1) {
1760     tt = t1;
1761   } else {
1762     tt = t0 + (t1 - t0) * tMin;
1763   }
1764   shading->getColor(tt, &color0);
1765
1766   // compute the coordinates of the point on the t axis at t = tMin;
1767   // then compute the intersection of the perpendicular line with the
1768   // bounding box
1769   tx = x0 + tMin * dx;
1770   ty = y0 + tMin * dy;
1771   if (dx == 0 && dy == 0) {
1772     sMin = sMax = 0;
1773   } if (dx == 0) {
1774     sMin = (xMin - tx) / -dy;
1775     sMax = (xMax - tx) / -dy;
1776     if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1777   } else if (dy == 0) {
1778     sMin = (yMin - ty) / dx;
1779     sMax = (yMax - ty) / dx;
1780     if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1781   } else {
1782     s[0] = (yMin - ty) / dx;
1783     s[1] = (yMax - ty) / dx;
1784     s[2] = (xMin - tx) / -dy;
1785     s[3] = (xMax - tx) / -dy;
1786     for (j = 0; j < 3; ++j) {
1787       kk = j;
1788       for (k = j + 1; k < 4; ++k) {
1789         if (s[k] < s[kk]) {
1790           kk = k;
1791         }
1792       }
1793       tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1794     }
1795     sMin = s[1];
1796     sMax = s[2];
1797   }
1798   ux0 = tx - sMin * dy;
1799   uy0 = ty + sMin * dx;
1800   vx0 = tx - sMax * dy;
1801   vy0 = ty + sMax * dx;
1802
1803   i = 0;
1804   if(i < axialMaxSplits)
1805     out->useGradients();
1806
1807   while (i < axialMaxSplits) {
1808
1809     // bisect until color difference is small enough or we hit the
1810     // bisection limit
1811     j = next[i];
1812     while (j > i + 1) {
1813       if (ta[j] < 0) {
1814         tt = t0;
1815       } else if (ta[j] > 1) {
1816         tt = t1;
1817       } else {
1818         tt = t0 + (t1 - t0) * ta[j];
1819       }
1820       shading->getColor(tt, &color1);
1821       for (k = 0; k < nComps; ++k) {
1822         if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
1823           break;
1824         }
1825       }
1826       if (k == nComps) {
1827         break;
1828       }
1829       k = (i + j) / 2;
1830       ta[k] = 0.5 * (ta[i] + ta[j]);
1831       next[i] = k;
1832       next[k] = j;
1833       j = k;
1834     }
1835
1836     // use the average of the colors of the two sides of the region
1837     for (k = 0; k < nComps; ++k) {
1838       color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
1839     }
1840
1841     // compute the coordinates of the point on the t axis; then
1842     // compute the intersection of the perpendicular line with the
1843     // bounding box
1844     tx = x0 + ta[j] * dx;
1845     ty = y0 + ta[j] * dy;
1846     if (dx == 0 && dy == 0) {
1847       sMin = sMax = 0;
1848     } if (dx == 0) {
1849       sMin = (xMin - tx) / -dy;
1850       sMax = (xMax - tx) / -dy;
1851       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1852     } else if (dy == 0) {
1853       sMin = (yMin - ty) / dx;
1854       sMax = (yMax - ty) / dx;
1855       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1856     } else {
1857       s[0] = (yMin - ty) / dx;
1858       s[1] = (yMax - ty) / dx;
1859       s[2] = (xMin - tx) / -dy;
1860       s[3] = (xMax - tx) / -dy;
1861       for (j = 0; j < 3; ++j) {
1862         kk = j;
1863         for (k = j + 1; k < 4; ++k) {
1864           if (s[k] < s[kk]) {
1865             kk = k;
1866           }
1867         }
1868         tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1869       }
1870       sMin = s[1];
1871       sMax = s[2];
1872     }
1873     ux1 = tx - sMin * dy;
1874     uy1 = ty + sMin * dx;
1875     vx1 = tx - sMax * dy;
1876     vy1 = ty + sMax * dx;
1877
1878     // set the color
1879     state->setFillColor(&color0);
1880     out->updateFillColor(state);
1881
1882     // fill the region
1883     state->moveTo(ux0, uy0);
1884     state->lineTo(vx0, vy0);
1885     state->lineTo(vx1, vy1);
1886     state->lineTo(ux1, uy1);
1887     state->closePath();
1888     out->fill(state);
1889     state->clearPath();
1890
1891     // set up for next region
1892     ux0 = ux1;
1893     uy0 = uy1;
1894     vx0 = vx1;
1895     vy0 = vy1;
1896     color0 = color1;
1897     i = next[i];
1898   }
1899 }
1900
1901 void Gfx::doRadialShFill(GfxRadialShading *shading) {
1902   double sMin, sMax, xMin, yMin, xMax, yMax;
1903   double x0, y0, r0, x1, y1, r1, t0, t1;
1904   int nComps;
1905   GfxColor colorA, colorB;
1906   double xa, ya, xb, yb, ra, rb;
1907   double ta, tb, sa, sb;
1908   int ia, ib, k, n;
1909   double *ctm;
1910   double angle, t;
1911
1912   // get the shading info
1913   shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
1914   t0 = shading->getDomain0();
1915   t1 = shading->getDomain1();
1916   nComps = shading->getColorSpace()->getNComps();
1917
1918   // compute the (possibly extended) s range
1919   sMin = 0;
1920   sMax = 1;
1921   if (shading->getExtend0()) {
1922     if (r0 < r1) {
1923       // extend the smaller end
1924       sMin = -r0 / (r1 - r0);
1925     } else {
1926       // extend the larger end
1927       //~ this computes the diagonal of the bounding box -- we should
1928       //~ really compute the intersection of the moving/expanding
1929       //~ circles with each of the four corners and look for the max
1930       //~ radius
1931       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1932       sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
1933                    (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1934       if (sMin > 0) {
1935         sMin = 0;
1936       } else if (sMin < -20) {
1937         // sanity check
1938         sMin = -20;
1939       }
1940     }
1941   }
1942   if (shading->getExtend1()) {
1943     if (r1 < r0) {
1944       // extend the smaller end
1945       sMax = -r0 / (r1 - r0);
1946     } else if (r1 > r0) {
1947       // extend the larger end
1948       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1949       sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
1950                    (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
1951       if (sMax < 1) {
1952         sMin = 1;
1953       } else if (sMax > 20) {
1954         // sanity check
1955         sMax = 20;
1956       }
1957     }
1958   }
1959
1960   // compute the number of steps into which circles must be divided to
1961   // achieve a curve flatness of 0.1 pixel in device space for the
1962   // largest circle (note that "device space" is 72 dpi when generating
1963   // PostScript, hence the relatively small 0.1 pixel accuracy)
1964   ctm = state->getCTM();
1965   t = fabs(ctm[0]);
1966   if (fabs(ctm[1]) > t) {
1967     t = fabs(ctm[1]);
1968   }
1969   if (fabs(ctm[2]) > t) {
1970     t = fabs(ctm[2]);
1971   }
1972   if (fabs(ctm[3]) > t) {
1973     t = fabs(ctm[3]);
1974   }
1975   if (r0 > r1) {
1976     t *= r0;
1977   } else {
1978     t *= r1;
1979   }
1980   if (t < 1) {
1981     n = 3;
1982   } else {
1983     n = (int)(M_PI / acos(1 - 0.1 / t));
1984     if (n < 3) {
1985       n = 3;
1986     } else if (n > 200) {
1987       n = 200;
1988     }
1989   }
1990
1991   // Traverse the t axis and do the shading.
1992   //
1993   // This generates and fills a series of rings.  Each ring is defined
1994   // by two circles:
1995   //   sa, ta, xa, ya, ra, colorA
1996   //   sb, tb, xb, yb, rb, colorB
1997   //
1998   // The s/t axis is divided into radialMaxSplits parts; these parts
1999   // are combined as much as possible while respecting the
2000   // radialColorDelta parameter.
2001
2002   // setup for the start circle
2003   ia = 0;
2004   sa = sMin;
2005   ta = t0 + sa * (t1 - t0);
2006   xa = x0 + sa * (x1 - x0);
2007   ya = y0 + sa * (y1 - y0);
2008   ra = r0 + sa * (r1 - r0);
2009   if (ta < t0) {
2010     shading->getColor(t0, &colorA);
2011   } else if (ta > t1) {
2012     shading->getColor(t1, &colorA);
2013   } else {
2014     shading->getColor(ta, &colorA);
2015   }
2016
2017   if(ia < radialMaxSplits)
2018     out->useGradients();
2019
2020   while (ia < radialMaxSplits) {
2021
2022     // go as far along the t axis (toward t1) as we can, such that the
2023     // color difference is within the tolerance (radialColorDelta) --
2024     // this uses bisection (between the current value, t, and t1),
2025     // limited to radialMaxSplits points along the t axis; require at
2026     // least one split to avoid problems when the innermost and
2027     // outermost colors are the same
2028     ib = radialMaxSplits;
2029     sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2030     tb = t0 + sb * (t1 - t0);
2031     if (tb < t0) {
2032       shading->getColor(t0, &colorB);
2033     } else if (tb > t1) {
2034       shading->getColor(t1, &colorB);
2035     } else {
2036       shading->getColor(tb, &colorB);
2037     }
2038     while (ib - ia > 1) {
2039       for (k = 0; k < nComps; ++k) {
2040         if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
2041           break;
2042         }
2043       }
2044       if (k == nComps && ib < radialMaxSplits) {
2045         break;
2046       }
2047       ib = (ia + ib) / 2;
2048       sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
2049       tb = t0 + sb * (t1 - t0);
2050       if (tb < t0) {
2051         shading->getColor(t0, &colorB);
2052       } else if (tb > t1) {
2053         shading->getColor(t1, &colorB);
2054       } else {
2055         shading->getColor(tb, &colorB);
2056       }
2057     }
2058
2059     // compute center and radius of the circle
2060     xb = x0 + sb * (x1 - x0);
2061     yb = y0 + sb * (y1 - y0);
2062     rb = r0 + sb * (r1 - r0);
2063
2064     // use the average of the colors at the two circles
2065     for (k = 0; k < nComps; ++k) {
2066       colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
2067     }
2068     state->setFillColor(&colorA);
2069     out->updateFillColor(state);
2070
2071     // construct path for first circle
2072     state->moveTo(xa + ra, ya);
2073     for (k = 1; k < n; ++k) {
2074       angle = ((double)k / (double)n) * 2 * M_PI;
2075       state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
2076     }
2077     state->closePath();
2078
2079     // construct and append path for second circle
2080     state->moveTo(xb + rb, yb);
2081     for (k = 1; k < n; ++k) {
2082       angle = ((double)k / (double)n) * 2 * M_PI;
2083       state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
2084     }
2085     state->closePath();
2086
2087     // fill the ring
2088     out->eoFill(state);
2089     state->clearPath();
2090
2091     // step to the next value of t
2092     ia = ib;
2093     sa = sb;
2094     ta = tb;
2095     xa = xb;
2096     ya = yb;
2097     ra = rb;
2098     colorA = colorB;
2099   }
2100 }
2101
2102 void Gfx::doEndPath() {
2103   if (state->isCurPt() && clip != clipNone) {
2104     state->clip();
2105     if (clip == clipNormal) {
2106       out->clip(state);
2107     } else {
2108       out->eoClip(state);
2109     }
2110   }
2111   clip = clipNone;
2112   state->clearPath();
2113 }
2114
2115 //------------------------------------------------------------------------
2116 // path clipping operators
2117 //------------------------------------------------------------------------
2118
2119 void Gfx::opClip(Object args[], int numArgs) {
2120   clip = clipNormal;
2121 }
2122
2123 void Gfx::opEOClip(Object args[], int numArgs) {
2124   clip = clipEO;
2125 }
2126
2127 //------------------------------------------------------------------------
2128 // text object operators
2129 //------------------------------------------------------------------------
2130
2131 void Gfx::opBeginText(Object args[], int numArgs) {
2132   state->setTextMat(1, 0, 0, 1, 0, 0);
2133   state->textMoveTo(0, 0);
2134   out->updateTextMat(state);
2135   out->updateTextPos(state);
2136   fontChanged = gTrue;
2137 }
2138
2139 void Gfx::opEndText(Object args[], int numArgs) {
2140   out->endTextObject(state);
2141 }
2142
2143 //------------------------------------------------------------------------
2144 // text state operators
2145 //------------------------------------------------------------------------
2146
2147 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
2148   state->setCharSpace(args[0].getNum());
2149   out->updateCharSpace(state);
2150 }
2151
2152 void Gfx::opSetFont(Object args[], int numArgs) {
2153   GfxFont *font;
2154
2155   if (!(font = res->lookupFont(args[0].getName()))) {
2156     return;
2157   }
2158   if (printCommands) {
2159     printf("  font: tag=%s name='%s' %g\n",
2160            font->getTag()->getCString(),
2161            font->getName() ? font->getName()->getCString() : "???",
2162            args[1].getNum());
2163     fflush(stdout);
2164   }
2165   state->setFont(font, args[1].getNum());
2166   fontChanged = gTrue;
2167 }
2168
2169 void Gfx::opSetTextLeading(Object args[], int numArgs) {
2170   state->setLeading(args[0].getNum());
2171 }
2172
2173 void Gfx::opSetTextRender(Object args[], int numArgs) {
2174   state->setRender(args[0].getInt());
2175   out->updateRender(state);
2176 }
2177
2178 void Gfx::opSetTextRise(Object args[], int numArgs) {
2179   state->setRise(args[0].getNum());
2180   out->updateRise(state);
2181 }
2182
2183 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
2184   state->setWordSpace(args[0].getNum());
2185   out->updateWordSpace(state);
2186 }
2187
2188 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
2189   state->setHorizScaling(args[0].getNum());
2190   out->updateHorizScaling(state);
2191   fontChanged = gTrue;
2192 }
2193
2194 //------------------------------------------------------------------------
2195 // text positioning operators
2196 //------------------------------------------------------------------------
2197
2198 void Gfx::opTextMove(Object args[], int numArgs) {
2199   double tx, ty;
2200
2201   tx = state->getLineX() + args[0].getNum();
2202   ty = state->getLineY() + args[1].getNum();
2203   state->textMoveTo(tx, ty);
2204   out->updateTextPos(state);
2205 }
2206
2207 void Gfx::opTextMoveSet(Object args[], int numArgs) {
2208   double tx, ty;
2209
2210   tx = state->getLineX() + args[0].getNum();
2211   ty = args[1].getNum();
2212   state->setLeading(-ty);
2213   ty += state->getLineY();
2214   state->textMoveTo(tx, ty);
2215   out->updateTextPos(state);
2216 }
2217
2218 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
2219   state->setTextMat(args[0].getNum(), args[1].getNum(),
2220                     args[2].getNum(), args[3].getNum(),
2221                     args[4].getNum(), args[5].getNum());
2222   state->textMoveTo(0, 0);
2223   out->updateTextMat(state);
2224   out->updateTextPos(state);
2225   fontChanged = gTrue;
2226 }
2227
2228 void Gfx::opTextNextLine(Object args[], int numArgs) {
2229   double tx, ty;
2230
2231   tx = state->getLineX();
2232   ty = state->getLineY() - state->getLeading();
2233   state->textMoveTo(tx, ty);
2234   out->updateTextPos(state);
2235 }
2236
2237 //------------------------------------------------------------------------
2238 // text string operators
2239 //------------------------------------------------------------------------
2240
2241 void Gfx::opShowText(Object args[], int numArgs) {
2242   if (!state->getFont()) {
2243     error(getPos(), "No font in show");
2244     return;
2245   }
2246   doShowText(args[0].getString());
2247 }
2248
2249 void Gfx::opMoveShowText(Object args[], int numArgs) {
2250   double tx, ty;
2251
2252   if (!state->getFont()) {
2253     error(getPos(), "No font in move/show");
2254     return;
2255   }
2256   tx = state->getLineX();
2257   ty = state->getLineY() - state->getLeading();
2258   state->textMoveTo(tx, ty);
2259   out->updateTextPos(state);
2260   doShowText(args[0].getString());
2261 }
2262
2263 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
2264   double tx, ty;
2265
2266   if (!state->getFont()) {
2267     error(getPos(), "No font in move/set/show");
2268     return;
2269   }
2270   state->setWordSpace(args[0].getNum());
2271   state->setCharSpace(args[1].getNum());
2272   tx = state->getLineX();
2273   ty = state->getLineY() - state->getLeading();
2274   state->textMoveTo(tx, ty);
2275   out->updateWordSpace(state);
2276   out->updateCharSpace(state);
2277   out->updateTextPos(state);
2278   doShowText(args[2].getString());
2279 }
2280
2281 void Gfx::opShowSpaceText(Object args[], int numArgs) {
2282   Array *a;
2283   Object obj;
2284   int wMode;
2285   int i;
2286
2287   if (!state->getFont()) {
2288     error(getPos(), "No font in show/space");
2289     return;
2290   }
2291   wMode = state->getFont()->getWMode();
2292   a = args[0].getArray();
2293   for (i = 0; i < a->getLength(); ++i) {
2294     a->get(i, &obj);
2295     if (obj.isNum()) {
2296       if (wMode) {
2297         state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
2298       } else {
2299         state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
2300       }
2301       out->updateTextShift(state, obj.getNum());
2302     } else if (obj.isString()) {
2303       doShowText(obj.getString());
2304     } else {
2305       error(getPos(), "Element of show/space array must be number or string");
2306     }
2307     obj.free();
2308   }
2309 }
2310
2311 void Gfx::doShowText(GString *s) {
2312   GfxFont *font;
2313   int wMode;
2314   double riseX, riseY;
2315   CharCode code;
2316   Unicode u[8];
2317   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2318   double originX, originY, tOriginX, tOriginY;
2319   double oldCTM[6], newCTM[6];
2320   double *mat;
2321   Object charProc;
2322   Dict *resDict;
2323   Parser *oldParser;
2324   char *p;
2325   int len, n, uLen, nChars, nSpaces, i;
2326
2327   if (fontChanged) {
2328     out->updateFont(state);
2329     fontChanged = gFalse;
2330   }
2331   font = state->getFont();
2332   wMode = font->getWMode();
2333
2334   if (out->useDrawChar()) {
2335     out->beginString(state, s);
2336   }
2337
2338   // handle a Type 3 char
2339   if (font->getType() == fontType3 && out->interpretType3Chars()) {
2340     mat = state->getCTM();
2341     for (i = 0; i < 6; ++i) {
2342       oldCTM[i] = mat[i];
2343     }
2344     mat = state->getTextMat();
2345     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2346     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2347     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2348     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2349     mat = font->getFontMatrix();
2350     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2351     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2352     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2353     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2354     newCTM[0] *= state->getFontSize();
2355     newCTM[1] *= state->getFontSize();
2356     newCTM[2] *= state->getFontSize();
2357     newCTM[3] *= state->getFontSize();
2358     newCTM[0] *= state->getHorizScaling();
2359     newCTM[2] *= state->getHorizScaling();
2360     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2361     curX = state->getCurX();
2362     curY = state->getCurY();
2363     lineX = state->getLineX();
2364     lineY = state->getLineY();
2365     oldParser = parser;
2366     p = s->getCString();
2367     len = s->getLength();
2368     while (len > 0) {
2369       n = font->getNextChar(p, len, &code,
2370                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2371                             &dx, &dy, &originX, &originY);
2372       dx = dx * state->getFontSize() + state->getCharSpace();
2373       if (n == 1 && *p == ' ') {
2374         dx += state->getWordSpace();
2375       }
2376       dx *= state->getHorizScaling();
2377       dy *= state->getFontSize();
2378       state->textTransformDelta(dx, dy, &tdx, &tdy);
2379       state->transform(curX + riseX, curY + riseY, &x, &y);
2380       saveState();
2381       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2382       //~ out->updateCTM(???)
2383       if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2384                                code, u, uLen)) {
2385         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2386         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2387           pushResources(resDict);
2388         }
2389         if (charProc.isStream()) {
2390           display(&charProc, gFalse);
2391         } else {
2392           error(getPos(), "Missing or bad Type3 CharProc entry");
2393         }
2394         out->endType3Char(state);
2395         if (resDict) {
2396           popResources();
2397         }
2398         charProc.free();
2399       }
2400       restoreState();
2401       // GfxState::restore() does *not* restore the current position,
2402       // so we deal with it here using (curX, curY) and (lineX, lineY)
2403       curX += tdx;
2404       curY += tdy;
2405       state->moveTo(curX, curY);
2406       state->textSetPos(lineX, lineY);
2407       p += n;
2408       len -= n;
2409     }
2410     parser = oldParser;
2411
2412   } else if (out->useDrawChar()) {
2413     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2414     p = s->getCString();
2415     len = s->getLength();
2416     while (len > 0) {
2417       n = font->getNextChar(p, len, &code,
2418                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2419                             &dx, &dy, &originX, &originY);
2420       if (wMode) {
2421         dx *= state->getFontSize();
2422         dy = dy * state->getFontSize() + state->getCharSpace();
2423         if (n == 1 && *p == ' ') {
2424           dy += state->getWordSpace();
2425         }
2426       } else {
2427         dx = dx * state->getFontSize() + state->getCharSpace();
2428         if (n == 1 && *p == ' ') {
2429           dx += state->getWordSpace();
2430         }
2431         dx *= state->getHorizScaling();
2432         dy *= state->getFontSize();
2433       }
2434       state->textTransformDelta(dx, dy, &tdx, &tdy);
2435       originX *= state->getFontSize();
2436       originY *= state->getFontSize();
2437       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2438       out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2439                     tdx, tdy, tOriginX, tOriginY, code, u, uLen);
2440       state->shift(tdx, tdy);
2441       p += n;
2442       len -= n;
2443     }
2444
2445   } else {
2446     dx = dy = 0;
2447     p = s->getCString();
2448     len = s->getLength();
2449     nChars = nSpaces = 0;
2450     while (len > 0) {
2451       n = font->getNextChar(p, len, &code,
2452                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2453                             &dx2, &dy2, &originX, &originY);
2454       dx += dx2;
2455       dy += dy2;
2456       if (n == 1 && *p == ' ') {
2457         ++nSpaces;
2458       }
2459       ++nChars;
2460       p += n;
2461       len -= n;
2462     }
2463     if (wMode) {
2464       dx *= state->getFontSize();
2465       dy = dy * state->getFontSize()
2466            + nChars * state->getCharSpace()
2467            + nSpaces * state->getWordSpace();
2468     } else {
2469       dx = dx * state->getFontSize()
2470            + nChars * state->getCharSpace()
2471            + nSpaces * state->getWordSpace();
2472       dx *= state->getHorizScaling();
2473       dy *= state->getFontSize();
2474     }
2475     state->textTransformDelta(dx, dy, &tdx, &tdy);
2476     out->drawString(state, s);
2477     state->shift(tdx, tdy);
2478   }
2479
2480   if (out->useDrawChar()) {
2481     out->endString(state);
2482   }
2483
2484   updateLevel += 10 * s->getLength();
2485 }
2486
2487 //------------------------------------------------------------------------
2488 // XObject operators
2489 //------------------------------------------------------------------------
2490
2491 void Gfx::opXObject(Object args[], int numArgs) {
2492   Object obj1, obj2, obj3, refObj;
2493 #if OPI_SUPPORT
2494   Object opiDict;
2495 #endif
2496
2497   if (!res->lookupXObject(args[0].getName(), &obj1)) {
2498     return;
2499   }
2500   if (!obj1.isStream()) {
2501     error(getPos(), "XObject '%s' is wrong type", args[0].getName());
2502     obj1.free();
2503     return;
2504   }
2505 #if OPI_SUPPORT
2506   obj1.streamGetDict()->lookup("OPI", &opiDict);
2507   if (opiDict.isDict()) {
2508     out->opiBegin(state, opiDict.getDict());
2509   }
2510 #endif
2511   obj1.streamGetDict()->lookup("Subtype", &obj2);
2512   if (obj2.isName("Image")) {
2513     res->lookupXObjectNF(args[0].getName(), &refObj);
2514     doImage(&refObj, obj1.getStream(), gFalse);
2515     refObj.free();
2516   } else if (obj2.isName("Form")) {
2517     doForm(&obj1);
2518   } else if (obj2.isName("PS")) {
2519     obj1.streamGetDict()->lookup("Level1", &obj3);
2520     out->psXObject(obj1.getStream(),
2521                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
2522   } else if (obj2.isName()) {
2523     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2524   } else {
2525     error(getPos(), "XObject subtype is missing or wrong type");
2526   }
2527   obj2.free();
2528 #if OPI_SUPPORT
2529   if (opiDict.isDict()) {
2530     out->opiEnd(state, opiDict.getDict());
2531   }
2532   opiDict.free();
2533 #endif
2534   obj1.free();
2535 }
2536
2537 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2538   Dict *dict;
2539   int width, height;
2540   int bits;
2541   GBool mask;
2542   GBool invert;
2543   GfxColorSpace *colorSpace;
2544   GfxImageColorMap *colorMap;
2545   Object maskObj;
2546   GBool haveMask;
2547   int maskColors[2*gfxColorMaxComps];
2548   Object obj1, obj2;
2549   int i;
2550
2551   // get stream dict
2552   dict = str->getDict();
2553
2554   // get size
2555   dict->lookup("Width", &obj1);
2556   if (obj1.isNull()) {
2557     obj1.free();
2558     dict->lookup("W", &obj1);
2559   }
2560   if (!obj1.isInt())
2561     goto err2;
2562   width = obj1.getInt();
2563   obj1.free();
2564   dict->lookup("Height", &obj1);
2565   if (obj1.isNull()) {
2566     obj1.free();
2567     dict->lookup("H", &obj1);
2568   }
2569   if (!obj1.isInt())
2570     goto err2;
2571   height = obj1.getInt();
2572   obj1.free();
2573
2574   // image or mask?
2575   dict->lookup("ImageMask", &obj1);
2576   if (obj1.isNull()) {
2577     obj1.free();
2578     dict->lookup("IM", &obj1);
2579   }
2580   mask = gFalse;
2581   if (obj1.isBool())
2582     mask = obj1.getBool();
2583   else if (!obj1.isNull())
2584     goto err2;
2585   obj1.free();
2586
2587   // bit depth
2588   dict->lookup("BitsPerComponent", &obj1);
2589   if (obj1.isNull()) {
2590     obj1.free();
2591     dict->lookup("BPC", &obj1);
2592   }
2593   if (obj1.isInt()) {
2594     bits = obj1.getInt();
2595   } else if (mask) {
2596     bits = 1;
2597   } else {
2598     goto err2;
2599   }
2600   obj1.free();
2601
2602   // display a mask
2603   if (mask) {
2604
2605     // check for inverted mask
2606     if (bits != 1)
2607       goto err1;
2608     invert = gFalse;
2609     dict->lookup("Decode", &obj1);
2610     if (obj1.isNull()) {
2611       obj1.free();
2612       dict->lookup("D", &obj1);
2613     }
2614     if (obj1.isArray()) {
2615       obj1.arrayGet(0, &obj2);
2616       if (obj2.isInt() && obj2.getInt() == 1)
2617         invert = gTrue;
2618       obj2.free();
2619     } else if (!obj1.isNull()) {
2620       goto err2;
2621     }
2622     obj1.free();
2623
2624     // draw it
2625     out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2626
2627   } else {
2628
2629     // get color space and color map
2630     dict->lookup("ColorSpace", &obj1);
2631     if (obj1.isNull()) {
2632       obj1.free();
2633       dict->lookup("CS", &obj1);
2634     }
2635     if (obj1.isName()) {
2636       res->lookupColorSpace(obj1.getName(), &obj2);
2637       if (!obj2.isNull()) {
2638         obj1.free();
2639         obj1 = obj2;
2640       } else {
2641         obj2.free();
2642       }
2643     }
2644     colorSpace = GfxColorSpace::parse(&obj1);
2645     obj1.free();
2646     if (!colorSpace) {
2647       goto err1;
2648     }
2649     dict->lookup("Decode", &obj1);
2650     if (obj1.isNull()) {
2651       obj1.free();
2652       dict->lookup("D", &obj1);
2653     }
2654     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2655     obj1.free();
2656     if (!colorMap->isOk()) {
2657       delete colorMap;
2658       goto err1;
2659     }
2660
2661     // get the mask
2662     haveMask = gFalse;
2663     dict->lookup("Mask", &maskObj);
2664     if (maskObj.isArray()) {
2665       for (i = 0; i < maskObj.arrayGetLength(); ++i) {
2666         maskObj.arrayGet(i, &obj1);
2667         maskColors[i] = obj1.getInt();
2668         obj1.free();
2669       }
2670       haveMask = gTrue;
2671     }
2672
2673     // draw it
2674     out->drawImage(state, ref, str, width, height, colorMap,
2675                    haveMask ? maskColors : (int *)NULL,  inlineImg);
2676     delete colorMap;
2677
2678     maskObj.free();
2679   }
2680
2681   if ((i = width * height) > 1000) {
2682     i = 1000;
2683   }
2684   updateLevel += i;
2685
2686   return;
2687
2688  err2:
2689   obj1.free();
2690  err1:
2691   error(getPos(), "Bad image parameters");
2692 }
2693
2694 void Gfx::doForm(Object *str) {
2695   Dict *dict;
2696   Object matrixObj, bboxObj;
2697   double m[6], bbox[6];
2698   Object resObj;
2699   Dict *resDict;
2700   Object obj1;
2701   int i;
2702
2703   // check for excessive recursion
2704   if (formDepth > 20) {
2705     return;
2706   }
2707
2708   // get stream dict
2709   dict = str->streamGetDict();
2710
2711   // check form type
2712   dict->lookup("FormType", &obj1);
2713   if (!(obj1.isInt() && obj1.getInt() == 1)) {
2714     error(getPos(), "Unknown form type");
2715   }
2716   obj1.free();
2717
2718   // get bounding box
2719   dict->lookup("BBox", &bboxObj);
2720   if (!bboxObj.isArray()) {
2721     matrixObj.free();
2722     bboxObj.free();
2723     error(getPos(), "Bad form bounding box");
2724     return;
2725   }
2726   for (i = 0; i < 4; ++i) {
2727     bboxObj.arrayGet(i, &obj1);
2728     bbox[i] = obj1.getNum();
2729     obj1.free();
2730   }
2731   bboxObj.free();
2732
2733   // get matrix
2734   dict->lookup("Matrix", &matrixObj);
2735   if (matrixObj.isArray()) {
2736     for (i = 0; i < 6; ++i) {
2737       matrixObj.arrayGet(i, &obj1);
2738       m[i] = obj1.getNum();
2739       obj1.free();
2740     }
2741   } else {
2742     m[0] = 1; m[1] = 0;
2743     m[2] = 0; m[3] = 1;
2744     m[4] = 0; m[5] = 0;
2745   }
2746   matrixObj.free();
2747
2748   // get resources
2749   dict->lookup("Resources", &resObj);
2750   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2751
2752   // draw it
2753   ++formDepth;
2754   doForm1(str, resDict, m, bbox);
2755   --formDepth;
2756
2757   resObj.free();
2758 }
2759
2760 void Gfx::doAnnot(Object *str, double xMin, double yMin,
2761                   double xMax, double yMax) {
2762   Dict *dict, *resDict;
2763   Object matrixObj, bboxObj, resObj;
2764   Object obj1;
2765   double m[6], bbox[6], ictm[6];
2766   double *ctm;
2767   double formX0, formY0, formX1, formY1;
2768   double annotX0, annotY0, annotX1, annotY1;
2769   double det, x, y, sx, sy;
2770   int i;
2771
2772   // get stream dict
2773   dict = str->streamGetDict();
2774
2775   // get the form bounding box
2776   dict->lookup("BBox", &bboxObj);
2777   if (!bboxObj.isArray()) {
2778     bboxObj.free();
2779     error(getPos(), "Bad form bounding box");
2780     return;
2781   }
2782   for (i = 0; i < 4; ++i) {
2783     bboxObj.arrayGet(i, &obj1);
2784     bbox[i] = obj1.getNum();
2785     obj1.free();
2786   }
2787   bboxObj.free();
2788
2789   // get the form matrix
2790   dict->lookup("Matrix", &matrixObj);
2791   if (matrixObj.isArray()) {
2792     for (i = 0; i < 6; ++i) {
2793       matrixObj.arrayGet(i, &obj1);
2794       m[i] = obj1.getNum();
2795       obj1.free();
2796     }
2797   } else {
2798     m[0] = 1; m[1] = 0;
2799     m[2] = 0; m[3] = 1;
2800     m[4] = 0; m[5] = 0;
2801   }
2802   matrixObj.free();
2803
2804   // transform the form bbox from form space to user space
2805   formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
2806   formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
2807   formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
2808   formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
2809
2810   // transform the annotation bbox from default user space to user
2811   // space: (bbox * baseMatrix) * iCTM
2812   ctm = state->getCTM();
2813   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2814   ictm[0] = ctm[3] * det;
2815   ictm[1] = -ctm[1] * det;
2816   ictm[2] = -ctm[2] * det;
2817   ictm[3] = ctm[0] * det;
2818   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2819   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2820   x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
2821   y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
2822   annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
2823   annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
2824   x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
2825   y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
2826   annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
2827   annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
2828
2829   // swap min/max coords
2830   if (formX0 > formX1) {
2831     x = formX0; formX0 = formX1; formX1 = x;
2832   }
2833   if (formY0 > formY1) {
2834     y = formY0; formY0 = formY1; formY1 = y;
2835   }
2836   if (annotX0 > annotX1) {
2837     x = annotX0; annotX0 = annotX1; annotX1 = x;
2838   }
2839   if (annotY0 > annotY1) {
2840     y = annotY0; annotY0 = annotY1; annotY1 = y;
2841   }
2842
2843   // scale the form to fit the annotation bbox
2844   if (formX1 == formX0) {
2845     // this shouldn't happen
2846     sx = 1;
2847   } else {
2848     sx = (annotX1 - annotX0) / (formX1 - formX0);
2849   }
2850   if (formY1 == formY0) {
2851     // this shouldn't happen
2852     sy = 1;
2853   } else {
2854     sy = (annotY1 - annotY0) / (formY1 - formY0);
2855   }
2856   m[0] *= sx;
2857   m[2] *= sx;
2858   m[4] = (m[4] - formX0) * sx + annotX0;
2859   m[1] *= sy;
2860   m[3] *= sy;
2861   m[5] = (m[5] - formY0) * sy + annotY0;
2862
2863   // get resources
2864   dict->lookup("Resources", &resObj);
2865   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2866
2867   // draw it
2868   doForm1(str, resDict, m, bbox);
2869
2870   resObj.free();
2871   bboxObj.free();
2872 }
2873
2874 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
2875   Parser *oldParser;
2876   double oldBaseMatrix[6];
2877   int i;
2878
2879   // push new resources on stack
2880   pushResources(resDict);
2881
2882   // save current graphics state
2883   saveState();
2884
2885   // kill any pre-existing path
2886   state->clearPath();
2887
2888   // save current parser
2889   oldParser = parser;
2890
2891   // set form transformation matrix
2892   state->concatCTM(matrix[0], matrix[1], matrix[2],
2893                    matrix[3], matrix[4], matrix[5]);
2894   out->updateCTM(state, matrix[0], matrix[1], matrix[2],
2895                  matrix[3], matrix[4], matrix[5]);
2896
2897   // set new base matrix
2898   for (i = 0; i < 6; ++i) {
2899     oldBaseMatrix[i] = baseMatrix[i];
2900     baseMatrix[i] = state->getCTM()[i];
2901   }
2902
2903   // set form bounding box
2904   state->moveTo(bbox[0], bbox[1]);
2905   state->lineTo(bbox[2], bbox[1]);
2906   state->lineTo(bbox[2], bbox[3]);
2907   state->lineTo(bbox[0], bbox[3]);
2908   state->closePath();
2909   state->clip();
2910   out->clip(state);
2911   state->clearPath();
2912
2913   // draw the form
2914   display(str, gFalse);
2915
2916   // restore base matrix
2917   for (i = 0; i < 6; ++i) {
2918     baseMatrix[i] = oldBaseMatrix[i];
2919   }
2920
2921   // restore parser
2922   parser = oldParser;
2923
2924   // restore graphics state
2925   restoreState();
2926
2927   // pop resource stack
2928   popResources();
2929
2930   return;
2931 }
2932
2933 //------------------------------------------------------------------------
2934 // in-line image operators
2935 //------------------------------------------------------------------------
2936
2937 void Gfx::opBeginImage(Object args[], int numArgs) {
2938   Stream *str;
2939   int c1, c2;
2940
2941   // build dict/stream
2942   str = buildImageStream();
2943
2944   // display the image
2945   if (str) {
2946     doImage(NULL, str, gTrue);
2947   
2948     // skip 'EI' tag
2949     c1 = str->getBaseStream()->getChar();
2950     c2 = str->getBaseStream()->getChar();
2951     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2952       c1 = c2;
2953       c2 = str->getBaseStream()->getChar();
2954     }
2955     delete str;
2956   }
2957 }
2958
2959 Stream *Gfx::buildImageStream() {
2960   Object dict;
2961   Object obj;
2962   char *key;
2963   Stream *str;
2964
2965   // build dictionary
2966   dict.initDict(xref);
2967   parser->getObj(&obj);
2968   while (!obj.isCmd("ID") && !obj.isEOF()) {
2969     if (!obj.isName()) {
2970       error(getPos(), "Inline image dictionary key must be a name object");
2971       obj.free();
2972     } else {
2973       key = copyString(obj.getName());
2974       obj.free();
2975       parser->getObj(&obj);
2976       if (obj.isEOF() || obj.isError()) {
2977         gfree(key);
2978         break;
2979       }
2980       dict.dictAdd(key, &obj);
2981     }
2982     parser->getObj(&obj);
2983   }
2984   if (obj.isEOF()) {
2985     error(getPos(), "End of file in inline image");
2986     obj.free();
2987     dict.free();
2988     return NULL;
2989   }
2990   obj.free();
2991
2992   // make stream
2993   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2994   str = str->addFilters(&dict);
2995
2996   return str;
2997 }
2998
2999 void Gfx::opImageData(Object args[], int numArgs) {
3000   error(getPos(), "Internal: got 'ID' operator");
3001 }
3002
3003 void Gfx::opEndImage(Object args[], int numArgs) {
3004   error(getPos(), "Internal: got 'EI' operator");
3005 }
3006
3007 //------------------------------------------------------------------------
3008 // type 3 font operators
3009 //------------------------------------------------------------------------
3010
3011 void Gfx::opSetCharWidth(Object args[], int numArgs) {
3012   out->type3D0(state, args[0].getNum(), args[1].getNum());
3013 }
3014
3015 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
3016   out->type3D1(state, args[0].getNum(), args[1].getNum(),
3017                args[2].getNum(), args[3].getNum(),
3018                args[4].getNum(), args[5].getNum());
3019 }
3020
3021 //------------------------------------------------------------------------
3022 // compatibility operators
3023 //------------------------------------------------------------------------
3024
3025 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
3026   ++ignoreUndef;
3027 }
3028
3029 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
3030   if (ignoreUndef > 0)
3031     --ignoreUndef;
3032 }
3033
3034 //------------------------------------------------------------------------
3035 // marked content operators
3036 //------------------------------------------------------------------------
3037
3038 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
3039   if (printCommands) {
3040     printf("  marked content: %s ", args[0].getName());
3041     if (numArgs == 2)
3042       args[2].print(stdout);
3043     printf("\n");
3044     fflush(stdout);
3045   }
3046 }
3047
3048 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
3049 }
3050
3051 void Gfx::opMarkPoint(Object args[], int numArgs) {
3052   if (printCommands) {
3053     printf("  mark point: %s ", args[0].getName());
3054     if (numArgs == 2)
3055       args[2].print(stdout);
3056     printf("\n");
3057     fflush(stdout);
3058   }
3059 }
3060
3061 //------------------------------------------------------------------------
3062 // misc
3063 //------------------------------------------------------------------------
3064
3065 void Gfx::saveState() {
3066   out->saveState(state);
3067   state = state->save();
3068 }
3069
3070 void Gfx::restoreState() {
3071   state = state->restore();
3072   out->restoreState(state);
3073 }
3074
3075 void Gfx::pushResources(Dict *resDict) {
3076   res = new GfxResources(xref, resDict, res);
3077 }
3078
3079 void Gfx::popResources() {
3080   GfxResources *resPtr;
3081
3082   resPtr = res->getNext();
3083   delete res;
3084   res = resPtr;
3085 }