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