source: trunk/poppler/mypoppler/poppler/Gfx.cc @ 50

Last change on this file since 50 was 27, checked in by Eugene Romanenko, 16 years ago

poppler updated to version 0.5.2, also needed changes to be compatible with new poppler

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