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

Last change on this file since 250 was 250, checked in by Eugene Romanenko, 13 years ago

PDF plugin: poppler library updated to version 0.8.3

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