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

Last change on this file since 461 was 461, checked in by Silvan Scherrer, 11 years ago

poppler update to 0.14.2

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