]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/Gfx.cc
kill traces of ltk, incorporate new sources
[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->isPath() && 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;
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     oldParser = parser;
2086     p = s->getCString();
2087     len = s->getLength();
2088     while (len > 0) {
2089       n = font->getNextChar(p, len, &code,
2090                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2091                             &dx, &dy, &originX, &originY);
2092       dx = dx * state->getFontSize() + state->getCharSpace();
2093       if (n == 1 && *p == ' ') {
2094         dx += state->getWordSpace();
2095       }
2096       dx *= state->getHorizScaling();
2097       dy *= state->getFontSize();
2098       state->textTransformDelta(dx, dy, &tdx, &tdy);
2099       state->transform(curX + riseX, curY + riseY, &x, &y);
2100       out->saveState(state);
2101       state = state->save();
2102       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2103       //~ out->updateCTM(???)
2104       if (!out->beginType3Char(state, code, u, uLen)) {
2105         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2106         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2107           pushResources(resDict);
2108         }
2109         if (charProc.isStream()) {
2110           display(&charProc, gFalse);
2111         } else {
2112           error(getPos(), "Missing or bad Type3 CharProc entry");
2113         }
2114         out->endType3Char(state);
2115         if (resDict) {
2116           popResources();
2117         }
2118         charProc.free();
2119       }
2120       state = state->restore();
2121       out->restoreState(state);
2122       // GfxState::restore() does *not* restore the current position,
2123       // so we track it here with (curX, curY)
2124       curX += tdx;
2125       curY += tdy;
2126       state->moveTo(curX, curY);
2127       p += n;
2128       len -= n;
2129     }
2130     parser = oldParser;
2131
2132   } else if (out->useDrawChar()) {
2133     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2134     p = s->getCString();
2135     len = s->getLength();
2136     while (len > 0) {
2137       n = font->getNextChar(p, len, &code,
2138                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2139                             &dx, &dy, &originX, &originY);
2140       if (wMode) {
2141         dx *= state->getFontSize();
2142         dy = dy * state->getFontSize() + state->getCharSpace();
2143         if (n == 1 && *p == ' ') {
2144           dy += state->getWordSpace();
2145         }
2146       } else {
2147         dx = dx * state->getFontSize() + state->getCharSpace();
2148         if (n == 1 && *p == ' ') {
2149           dx += state->getWordSpace();
2150         }
2151         dx *= state->getHorizScaling();
2152         dy *= state->getFontSize();
2153       }
2154       state->textTransformDelta(dx, dy, &tdx, &tdy);
2155       originX *= state->getFontSize();
2156       originY *= state->getFontSize();
2157       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2158       out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2159                     tdx, tdy, tOriginX, tOriginY, code, u, uLen);
2160       state->shift(tdx, tdy);
2161       p += n;
2162       len -= n;
2163     }
2164
2165   } else {
2166     dx = dy = 0;
2167     p = s->getCString();
2168     len = s->getLength();
2169     nChars = nSpaces = 0;
2170     while (len > 0) {
2171       n = font->getNextChar(p, len, &code,
2172                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2173                             &dx2, &dy2, &originX, &originY);
2174       dx += dx2;
2175       dy += dy2;
2176       if (n == 1 && *p == ' ') {
2177         ++nSpaces;
2178       }
2179       ++nChars;
2180       p += n;
2181       len -= n;
2182     }
2183     if (wMode) {
2184       dx *= state->getFontSize();
2185       dy = dy * state->getFontSize()
2186            + nChars * state->getCharSpace()
2187            + nSpaces * state->getWordSpace();
2188     } else {
2189       dx = dx * state->getFontSize()
2190            + nChars * state->getCharSpace()
2191            + nSpaces * state->getWordSpace();
2192       dx *= state->getHorizScaling();
2193       dy *= state->getFontSize();
2194     }
2195     state->textTransformDelta(dx, dy, &tdx, &tdy);
2196     out->drawString(state, s);
2197     state->shift(tdx, tdy);
2198   }
2199
2200   if (out->useDrawChar()) {
2201     out->endString(state);
2202   }
2203
2204   updateLevel += 10 * s->getLength();
2205 }
2206
2207 //------------------------------------------------------------------------
2208 // XObject operators
2209 //------------------------------------------------------------------------
2210
2211 void Gfx::opXObject(Object args[], int numArgs) {
2212   Object obj1, obj2, obj3, refObj;
2213 #if OPI_SUPPORT
2214   Object opiDict;
2215 #endif
2216
2217   if (!res->lookupXObject(args[0].getName(), &obj1)) {
2218     return;
2219   }
2220   if (!obj1.isStream()) {
2221     error(getPos(), "XObject '%s' is wrong type", args[0].getName());
2222     obj1.free();
2223     return;
2224   }
2225 #if OPI_SUPPORT
2226   obj1.streamGetDict()->lookup("OPI", &opiDict);
2227   if (opiDict.isDict()) {
2228     out->opiBegin(state, opiDict.getDict());
2229   }
2230 #endif
2231   obj1.streamGetDict()->lookup("Subtype", &obj2);
2232   if (obj2.isName("Image")) {
2233     res->lookupXObjectNF(args[0].getName(), &refObj);
2234     doImage(&refObj, obj1.getStream(), gFalse);
2235     refObj.free();
2236   } else if (obj2.isName("Form")) {
2237     doForm(&obj1);
2238   } else if (obj2.isName("PS")) {
2239     obj1.streamGetDict()->lookup("Level1", &obj3);
2240     out->psXObject(obj1.getStream(),
2241                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
2242   } else if (obj2.isName()) {
2243     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2244   } else {
2245     error(getPos(), "XObject subtype is missing or wrong type");
2246   }
2247   obj2.free();
2248 #if OPI_SUPPORT
2249   if (opiDict.isDict()) {
2250     out->opiEnd(state, opiDict.getDict());
2251   }
2252   opiDict.free();
2253 #endif
2254   obj1.free();
2255 }
2256
2257 void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2258   Dict *dict;
2259   int width, height;
2260   int bits;
2261   GBool mask;
2262   GBool invert;
2263   GfxColorSpace *colorSpace;
2264   GfxImageColorMap *colorMap;
2265   Object maskObj;
2266   GBool haveMask;
2267   int maskColors[2*gfxColorMaxComps];
2268   Object obj1, obj2;
2269   int i;
2270
2271   // get stream dict
2272   dict = str->getDict();
2273
2274   // get size
2275   dict->lookup("Width", &obj1);
2276   if (obj1.isNull()) {
2277     obj1.free();
2278     dict->lookup("W", &obj1);
2279   }
2280   if (!obj1.isInt())
2281     goto err2;
2282   width = obj1.getInt();
2283   obj1.free();
2284   dict->lookup("Height", &obj1);
2285   if (obj1.isNull()) {
2286     obj1.free();
2287     dict->lookup("H", &obj1);
2288   }
2289   if (!obj1.isInt())
2290     goto err2;
2291   height = obj1.getInt();
2292   obj1.free();
2293
2294   // image or mask?
2295   dict->lookup("ImageMask", &obj1);
2296   if (obj1.isNull()) {
2297     obj1.free();
2298     dict->lookup("IM", &obj1);
2299   }
2300   mask = gFalse;
2301   if (obj1.isBool())
2302     mask = obj1.getBool();
2303   else if (!obj1.isNull())
2304     goto err2;
2305   obj1.free();
2306
2307   // bit depth
2308   dict->lookup("BitsPerComponent", &obj1);
2309   if (obj1.isNull()) {
2310     obj1.free();
2311     dict->lookup("BPC", &obj1);
2312   }
2313   if (!obj1.isInt())
2314     goto err2;
2315   bits = obj1.getInt();
2316   obj1.free();
2317
2318   // display a mask
2319   if (mask) {
2320
2321     // check for inverted mask
2322     if (bits != 1)
2323       goto err1;
2324     invert = gFalse;
2325     dict->lookup("Decode", &obj1);
2326     if (obj1.isNull()) {
2327       obj1.free();
2328       dict->lookup("D", &obj1);
2329     }
2330     if (obj1.isArray()) {
2331       obj1.arrayGet(0, &obj2);
2332       if (obj2.isInt() && obj2.getInt() == 1)
2333         invert = gTrue;
2334       obj2.free();
2335     } else if (!obj1.isNull()) {
2336       goto err2;
2337     }
2338     obj1.free();
2339
2340     // draw it
2341     out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2342
2343   } else {
2344
2345     // get color space and color map
2346     dict->lookup("ColorSpace", &obj1);
2347     if (obj1.isNull()) {
2348       obj1.free();
2349       dict->lookup("CS", &obj1);
2350     }
2351     if (obj1.isName()) {
2352       res->lookupColorSpace(obj1.getName(), &obj2);
2353       if (!obj2.isNull()) {
2354         obj1.free();
2355         obj1 = obj2;
2356       } else {
2357         obj2.free();
2358       }
2359     }
2360     colorSpace = GfxColorSpace::parse(&obj1);
2361     obj1.free();
2362     if (!colorSpace) {
2363       goto err1;
2364     }
2365     dict->lookup("Decode", &obj1);
2366     if (obj1.isNull()) {
2367       obj1.free();
2368       dict->lookup("D", &obj1);
2369     }
2370     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2371     obj1.free();
2372     if (!colorMap->isOk()) {
2373       delete colorMap;
2374       goto err1;
2375     }
2376
2377     // get the mask
2378     haveMask = gFalse;
2379     dict->lookup("Mask", &maskObj);
2380     if (maskObj.isArray()) {
2381       for (i = 0; i < maskObj.arrayGetLength(); ++i) {
2382         maskObj.arrayGet(i, &obj1);
2383         maskColors[i] = obj1.getInt();
2384         obj1.free();
2385       }
2386       haveMask = gTrue;
2387     }
2388
2389     // draw it
2390     out->drawImage(state, ref, str, width, height, colorMap,
2391                    haveMask ? maskColors : (int *)NULL,  inlineImg);
2392     delete colorMap;
2393
2394     maskObj.free();
2395   }
2396
2397   if ((i = width * height) > 1000) {
2398     i = 1000;
2399   }
2400   updateLevel += i;
2401
2402   return;
2403
2404  err2:
2405   obj1.free();
2406  err1:
2407   error(getPos(), "Bad image parameters");
2408 }
2409
2410 void Gfx::doForm(Object *str) {
2411   Dict *dict;
2412   Object matrixObj, bboxObj;
2413   double m[6], bbox[6];
2414   Object resObj;
2415   Dict *resDict;
2416   Object obj1;
2417   int i;
2418
2419   // get stream dict
2420   dict = str->streamGetDict();
2421
2422   // check form type
2423   dict->lookup("FormType", &obj1);
2424   if (!(obj1.isInt() && obj1.getInt() == 1)) {
2425     error(getPos(), "Unknown form type");
2426   }
2427   obj1.free();
2428
2429   // get bounding box
2430   dict->lookup("BBox", &bboxObj);
2431   if (!bboxObj.isArray()) {
2432     matrixObj.free();
2433     bboxObj.free();
2434     error(getPos(), "Bad form bounding box");
2435     return;
2436   }
2437   for (i = 0; i < 4; ++i) {
2438     bboxObj.arrayGet(i, &obj1);
2439     bbox[i] = obj1.getNum();
2440     obj1.free();
2441   }
2442   bboxObj.free();
2443
2444   // get matrix
2445   dict->lookup("Matrix", &matrixObj);
2446   if (matrixObj.isArray()) {
2447     for (i = 0; i < 6; ++i) {
2448       matrixObj.arrayGet(i, &obj1);
2449       m[i] = obj1.getNum();
2450       obj1.free();
2451     }
2452   } else {
2453     m[0] = 1; m[1] = 0;
2454     m[2] = 0; m[3] = 1;
2455     m[4] = 0; m[5] = 0;
2456   }
2457   matrixObj.free();
2458
2459   // get resources
2460   dict->lookup("Resources", &resObj);
2461   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2462
2463   // draw it
2464   doForm1(str, resDict, m, bbox);
2465
2466   resObj.free();
2467 }
2468
2469 void Gfx::doAnnot(Object *str, double xMin, double yMin,
2470                   double xMax, double yMax) {
2471   Dict *dict, *resDict;
2472   Object matrixObj, bboxObj, resObj;
2473   Object obj1;
2474   double m[6], bbox[6], ictm[6];
2475   double *ctm;
2476   double formX0, formY0, formX1, formY1;
2477   double annotX0, annotY0, annotX1, annotY1;
2478   double det, x, y, sx, sy;
2479   int i;
2480
2481   // get stream dict
2482   dict = str->streamGetDict();
2483
2484   // get the form bounding box
2485   dict->lookup("BBox", &bboxObj);
2486   if (!bboxObj.isArray()) {
2487     bboxObj.free();
2488     error(getPos(), "Bad form bounding box");
2489     return;
2490   }
2491   for (i = 0; i < 4; ++i) {
2492     bboxObj.arrayGet(i, &obj1);
2493     bbox[i] = obj1.getNum();
2494     obj1.free();
2495   }
2496   bboxObj.free();
2497
2498   // get the form matrix
2499   dict->lookup("Matrix", &matrixObj);
2500   if (matrixObj.isArray()) {
2501     for (i = 0; i < 6; ++i) {
2502       matrixObj.arrayGet(i, &obj1);
2503       m[i] = obj1.getNum();
2504       obj1.free();
2505     }
2506   } else {
2507     m[0] = 1; m[1] = 0;
2508     m[2] = 0; m[3] = 1;
2509     m[4] = 0; m[5] = 0;
2510   }
2511   matrixObj.free();
2512
2513   // transform the form bbox from form space to user space
2514   formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
2515   formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
2516   formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
2517   formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
2518
2519   // transform the annotation bbox from default user space to user
2520   // space: (bbox * baseMatrix) * iCTM
2521   ctm = state->getCTM();
2522   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
2523   ictm[0] = ctm[3] * det;
2524   ictm[1] = -ctm[1] * det;
2525   ictm[2] = -ctm[2] * det;
2526   ictm[3] = ctm[0] * det;
2527   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
2528   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
2529   x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
2530   y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
2531   annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
2532   annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
2533   x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
2534   y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
2535   annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
2536   annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
2537
2538   // swap min/max coords
2539   if (formX0 > formX1) {
2540     x = formX0; formX0 = formX1; formX1 = x;
2541   }
2542   if (formY0 > formY1) {
2543     y = formY0; formY0 = formY1; formY1 = y;
2544   }
2545   if (annotX0 > annotX1) {
2546     x = annotX0; annotX0 = annotX1; annotX1 = x;
2547   }
2548   if (annotY0 > annotY1) {
2549     y = annotY0; annotY0 = annotY1; annotY1 = y;
2550   }
2551
2552   // scale the form to fit the annotation bbox
2553   if (formX1 == formX0) {
2554     // this shouldn't happen
2555     sx = 1;
2556   } else {
2557     sx = (annotX1 - annotX0) / (formX1 - formX0);
2558   }
2559   if (formY1 == formY0) {
2560     // this shouldn't happen
2561     sy = 1;
2562   } else {
2563     sy = (annotY1 - annotY0) / (formY1 - formY0);
2564   }
2565   m[0] *= sx;
2566   m[2] *= sx;
2567   m[4] = (m[4] - formX0) * sx + annotX0;
2568   m[1] *= sy;
2569   m[3] *= sy;
2570   m[5] = (m[5] - formY0) * sy + annotY0;
2571
2572   // get resources
2573   dict->lookup("Resources", &resObj);
2574   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2575
2576   // draw it
2577   doForm1(str, resDict, m, bbox);
2578
2579   resObj.free();
2580   bboxObj.free();
2581 }
2582
2583 void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
2584   Parser *oldParser;
2585   double oldBaseMatrix[6];
2586   int i;
2587
2588   // push new resources on stack
2589   pushResources(resDict);
2590
2591   // save current graphics state
2592   out->saveState(state);
2593   state = state->save();
2594
2595   // save current parser
2596   oldParser = parser;
2597
2598   // set form transformation matrix
2599   state->concatCTM(matrix[0], matrix[1], matrix[2],
2600                    matrix[3], matrix[4], matrix[5]);
2601   out->updateCTM(state, matrix[0], matrix[1], matrix[2],
2602                  matrix[3], matrix[4], matrix[5]);
2603
2604   // set new base matrix
2605   for (i = 0; i < 6; ++i) {
2606     oldBaseMatrix[i] = baseMatrix[i];
2607     baseMatrix[i] = state->getCTM()[i];
2608   }
2609
2610   // set form bounding box
2611   state->moveTo(bbox[0], bbox[1]);
2612   state->lineTo(bbox[2], bbox[1]);
2613   state->lineTo(bbox[2], bbox[3]);
2614   state->lineTo(bbox[0], bbox[3]);
2615   state->closePath();
2616   state->clip();
2617   out->clip(state);
2618   state->clearPath();
2619
2620   // draw the form
2621   display(str, gFalse);
2622
2623   // restore base matrix
2624   for (i = 0; i < 6; ++i) {
2625     baseMatrix[i] = oldBaseMatrix[i];
2626   }
2627
2628   // restore parser
2629   parser = oldParser;
2630
2631   // restore graphics state
2632   state = state->restore();
2633   out->restoreState(state);
2634
2635   // pop resource stack
2636   popResources();
2637
2638   return;
2639 }
2640
2641 void Gfx::pushResources(Dict *resDict) {
2642   res = new GfxResources(xref, resDict, res);
2643 }
2644
2645 void Gfx::popResources() {
2646   GfxResources *resPtr;
2647
2648   resPtr = res->getNext();
2649   delete res;
2650   res = resPtr;
2651 }
2652
2653 //------------------------------------------------------------------------
2654 // in-line image operators
2655 //------------------------------------------------------------------------
2656
2657 void Gfx::opBeginImage(Object args[], int numArgs) {
2658   Stream *str;
2659   int c1, c2;
2660
2661   // build dict/stream
2662   str = buildImageStream();
2663
2664   // display the image
2665   if (str) {
2666     doImage(NULL, str, gTrue);
2667   
2668     // skip 'EI' tag
2669     c1 = str->getBaseStream()->getChar();
2670     c2 = str->getBaseStream()->getChar();
2671     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2672       c1 = c2;
2673       c2 = str->getBaseStream()->getChar();
2674     }
2675     delete str;
2676   }
2677 }
2678
2679 Stream *Gfx::buildImageStream() {
2680   Object dict;
2681   Object obj;
2682   char *key;
2683   Stream *str;
2684
2685   // build dictionary
2686   dict.initDict(xref);
2687   parser->getObj(&obj);
2688   while (!obj.isCmd("ID") && !obj.isEOF()) {
2689     if (!obj.isName()) {
2690       error(getPos(), "Inline image dictionary key must be a name object");
2691       obj.free();
2692     } else {
2693       key = copyString(obj.getName());
2694       obj.free();
2695       parser->getObj(&obj);
2696       if (obj.isEOF() || obj.isError()) {
2697         gfree(key);
2698         break;
2699       }
2700       dict.dictAdd(key, &obj);
2701     }
2702     parser->getObj(&obj);
2703   }
2704   if (obj.isEOF()) {
2705     error(getPos(), "End of file in inline image");
2706     obj.free();
2707     dict.free();
2708     return NULL;
2709   }
2710   obj.free();
2711
2712   // make stream
2713   str = new EmbedStream(parser->getStream(), &dict);
2714   str = str->addFilters(&dict);
2715
2716   return str;
2717 }
2718
2719 void Gfx::opImageData(Object args[], int numArgs) {
2720   error(getPos(), "Internal: got 'ID' operator");
2721 }
2722
2723 void Gfx::opEndImage(Object args[], int numArgs) {
2724   error(getPos(), "Internal: got 'EI' operator");
2725 }
2726
2727 //------------------------------------------------------------------------
2728 // type 3 font operators
2729 //------------------------------------------------------------------------
2730
2731 void Gfx::opSetCharWidth(Object args[], int numArgs) {
2732   out->type3D0(state, args[0].getNum(), args[1].getNum());
2733 }
2734
2735 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
2736   out->type3D1(state, args[0].getNum(), args[1].getNum(),
2737                args[2].getNum(), args[3].getNum(),
2738                args[4].getNum(), args[5].getNum());
2739 }
2740
2741 //------------------------------------------------------------------------
2742 // compatibility operators
2743 //------------------------------------------------------------------------
2744
2745 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
2746   ++ignoreUndef;
2747 }
2748
2749 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
2750   if (ignoreUndef > 0)
2751     --ignoreUndef;
2752 }
2753
2754 //------------------------------------------------------------------------
2755 // marked content operators
2756 //------------------------------------------------------------------------
2757
2758 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
2759   if (printCommands) {
2760     printf("  marked content: %s ", args[0].getName());
2761     if (numArgs == 2)
2762       args[2].print(stdout);
2763     printf("\n");
2764     fflush(stdout);
2765   }
2766 }
2767
2768 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
2769 }
2770
2771 void Gfx::opMarkPoint(Object args[], int numArgs) {
2772   if (printCommands) {
2773     printf("  mark point: %s ", args[0].getName());
2774     if (numArgs == 2)
2775       args[2].print(stdout);
2776     printf("\n");
2777     fflush(stdout);
2778   }
2779 }