Code

Restore files that shouldn't have been touched. Grrr
[inkscape.git] / src / extension / internal / pdfinput / pdf-parser.cpp
2  /** \file
3  * PDF parsing using libpoppler
4  *
5  * Derived from poppler's Gfx.cc
6  *
7  * Copyright 1996-2003 Glyph & Cog, LLC
8  *
9  */
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
15 #ifdef HAVE_POPPLER
17 #ifdef USE_GCC_PRAGMAS
18 #pragma implementation
19 #endif
21 extern "C" {
22         
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <math.h>
29 }
31 #include "svg-builder.h"
32 #include "Gfx.h"
33 #include "pdf-parser.h"
34 #include "unit-constants.h"
36 #include "goo/gmem.h"
37 #include "goo/GooTimer.h"
38 #include "goo/GooHash.h"
39 #include "GlobalParams.h"
40 #include "CharTypes.h"
41 #include "Object.h"
42 #include "Array.h"
43 #include "Dict.h"
44 #include "Stream.h"
45 #include "Lexer.h"
46 #include "Parser.h"
47 #include "GfxFont.h"
48 #include "GfxState.h"
49 #include "OutputDev.h"
50 #include "Page.h"
51 #include "Annot.h"
52 #include "Error.h"
54 // the MSVC math.h doesn't define this
55 #ifndef M_PI
56 #define M_PI 3.14159265358979323846
57 #endif
59 //------------------------------------------------------------------------
60 // constants
61 //------------------------------------------------------------------------
63 // Default max delta allowed in any color component for a shading fill.
64 #define defaultShadingColorDelta (dblToCol( 1 / 2.0 ))
66 // Default max recursive depth for a shading fill.
67 #define defaultShadingMaxDepth 6
69 // Max number of operators kept in the history list.
70 #define maxOperatorHistoryDepth 16
72 //------------------------------------------------------------------------
73 // Operator table
74 //------------------------------------------------------------------------
76 #ifdef WIN32 // this works around a bug in the VC7 compiler
77 #  pragma optimize("",off)
78 #endif
80 PdfOperator PdfParser::opTab[] = {
81   {"\"",  3, {tchkNum,    tchkNum,    tchkString},
82           &PdfParser::opMoveSetShowText},
83   {"'",   1, {tchkString},
84           &PdfParser::opMoveShowText},
85   {"B",   0, {tchkNone},
86           &PdfParser::opFillStroke},
87   {"B*",  0, {tchkNone},
88           &PdfParser::opEOFillStroke},
89   {"BDC", 2, {tchkName,   tchkProps},
90           &PdfParser::opBeginMarkedContent},
91   {"BI",  0, {tchkNone},
92           &PdfParser::opBeginImage},
93   {"BMC", 1, {tchkName},
94           &PdfParser::opBeginMarkedContent},
95   {"BT",  0, {tchkNone},
96           &PdfParser::opBeginText},
97   {"BX",  0, {tchkNone},
98           &PdfParser::opBeginIgnoreUndef},
99   {"CS",  1, {tchkName},
100           &PdfParser::opSetStrokeColorSpace},
101   {"DP",  2, {tchkName,   tchkProps},
102           &PdfParser::opMarkPoint},
103   {"Do",  1, {tchkName},
104           &PdfParser::opXObject},
105   {"EI",  0, {tchkNone},
106           &PdfParser::opEndImage},
107   {"EMC", 0, {tchkNone},
108           &PdfParser::opEndMarkedContent},
109   {"ET",  0, {tchkNone},
110           &PdfParser::opEndText},
111   {"EX",  0, {tchkNone},
112           &PdfParser::opEndIgnoreUndef},
113   {"F",   0, {tchkNone},
114           &PdfParser::opFill},
115   {"G",   1, {tchkNum},
116           &PdfParser::opSetStrokeGray},
117   {"ID",  0, {tchkNone},
118           &PdfParser::opImageData},
119   {"J",   1, {tchkInt},
120           &PdfParser::opSetLineCap},
121   {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
122           &PdfParser::opSetStrokeCMYKColor},
123   {"M",   1, {tchkNum},
124           &PdfParser::opSetMiterLimit},
125   {"MP",  1, {tchkName},
126           &PdfParser::opMarkPoint},
127   {"Q",   0, {tchkNone},
128           &PdfParser::opRestore},
129   {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
130           &PdfParser::opSetStrokeRGBColor},
131   {"S",   0, {tchkNone},
132           &PdfParser::opStroke},
133   {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
134           &PdfParser::opSetStrokeColor},
135   {"SCN", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
136                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
137                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
138                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
139                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
140                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
141                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
142                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
143                 tchkSCN},
144           &PdfParser::opSetStrokeColorN},
145   {"T*",  0, {tchkNone},
146           &PdfParser::opTextNextLine},
147   {"TD",  2, {tchkNum,    tchkNum},
148           &PdfParser::opTextMoveSet},
149   {"TJ",  1, {tchkArray},
150           &PdfParser::opShowSpaceText},
151   {"TL",  1, {tchkNum},
152           &PdfParser::opSetTextLeading},
153   {"Tc",  1, {tchkNum},
154           &PdfParser::opSetCharSpacing},
155   {"Td",  2, {tchkNum,    tchkNum},
156           &PdfParser::opTextMove},
157   {"Tf",  2, {tchkName,   tchkNum},
158           &PdfParser::opSetFont},
159   {"Tj",  1, {tchkString},
160           &PdfParser::opShowText},
161   {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
162               tchkNum,    tchkNum},
163           &PdfParser::opSetTextMatrix},
164   {"Tr",  1, {tchkInt},
165           &PdfParser::opSetTextRender},
166   {"Ts",  1, {tchkNum},
167           &PdfParser::opSetTextRise},
168   {"Tw",  1, {tchkNum},
169           &PdfParser::opSetWordSpacing},
170   {"Tz",  1, {tchkNum},
171           &PdfParser::opSetHorizScaling},
172   {"W",   0, {tchkNone},
173           &PdfParser::opClip},
174   {"W*",  0, {tchkNone},
175           &PdfParser::opEOClip},
176   {"b",   0, {tchkNone},
177           &PdfParser::opCloseFillStroke},
178   {"b*",  0, {tchkNone},
179           &PdfParser::opCloseEOFillStroke},
180   {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
181               tchkNum,    tchkNum},
182           &PdfParser::opCurveTo},
183   {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
184               tchkNum,    tchkNum},
185           &PdfParser::opConcat},
186   {"cs",  1, {tchkName},
187           &PdfParser::opSetFillColorSpace},
188   {"d",   2, {tchkArray,  tchkNum},
189           &PdfParser::opSetDash},
190   {"d0",  2, {tchkNum,    tchkNum},
191           &PdfParser::opSetCharWidth},
192   {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
193               tchkNum,    tchkNum},
194           &PdfParser::opSetCacheDevice},
195   {"f",   0, {tchkNone},
196           &PdfParser::opFill},
197   {"f*",  0, {tchkNone},
198           &PdfParser::opEOFill},
199   {"g",   1, {tchkNum},
200           &PdfParser::opSetFillGray},
201   {"gs",  1, {tchkName},
202           &PdfParser::opSetExtGState},
203   {"h",   0, {tchkNone},
204           &PdfParser::opClosePath},
205   {"i",   1, {tchkNum},
206           &PdfParser::opSetFlat},
207   {"j",   1, {tchkInt},
208           &PdfParser::opSetLineJoin},
209   {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
210           &PdfParser::opSetFillCMYKColor},
211   {"l",   2, {tchkNum,    tchkNum},
212           &PdfParser::opLineTo},
213   {"m",   2, {tchkNum,    tchkNum},
214           &PdfParser::opMoveTo},
215   {"n",   0, {tchkNone},
216           &PdfParser::opEndPath},
217   {"q",   0, {tchkNone},
218           &PdfParser::opSave},
219   {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
220           &PdfParser::opRectangle},
221   {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
222           &PdfParser::opSetFillRGBColor},
223   {"ri",  1, {tchkName},
224           &PdfParser::opSetRenderingIntent},
225   {"s",   0, {tchkNone},
226           &PdfParser::opCloseStroke},
227   {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
228           &PdfParser::opSetFillColor},
229   {"scn", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
230                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
231                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
232                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
233                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
234                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
235                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
236                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
237                 tchkSCN},
238           &PdfParser::opSetFillColorN},
239   {"sh",  1, {tchkName},
240           &PdfParser::opShFill},
241   {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
242           &PdfParser::opCurveTo1},
243   {"w",   1, {tchkNum},
244           &PdfParser::opSetLineWidth},
245   {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
246           &PdfParser::opCurveTo2}
247 };
249 #ifdef WIN32 // this works around a bug in the VC7 compiler
250 #  pragma optimize("",on)
251 #endif
253 #define numOps (sizeof(opTab) / sizeof(PdfOperator))
255 //------------------------------------------------------------------------
256 // PdfParser
257 //------------------------------------------------------------------------
259 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
260                      int pageNum, int rotate, Dict *resDict,
261                      PDFRectangle *box, PDFRectangle *cropBox) {
263   int i;
265   xref = xrefA;
266   subPage = gFalse;
267   printCommands = false;
269   // start the resource stack
270   res = new GfxResources(xref, resDict, NULL);
272   // initialize
273   state = new GfxState(72.0, 72.0, box, rotate, gTrue);
274   clipHistory = new ClipHistoryEntry();
275   setDefaultApproximationPrecision();
276   fontChanged = gFalse;
277   clip = clipNone;
278   ignoreUndef = 0;
279   operatorHistory = NULL;
280   builder = builderA;
281   builder->setDocumentSize(state->getPageWidth()*PX_PER_PT,
282                            state->getPageHeight()*PX_PER_PT);
284   double *ctm = state->getCTM();
285   double scaledCTM[6];
286   for (i = 0; i < 6; ++i) {
287     baseMatrix[i] = ctm[i];
288     scaledCTM[i] = PX_PER_PT * ctm[i];
289   }
290   saveState();
291   builder->setTransform((double*)&scaledCTM);
292   formDepth = 0;
294   // set crop box
295   if (cropBox) {
296     if (printCommands)
297         printf("cropBox: %f %f %f %f\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
298     // do not clip if it's not needed
299     if (cropBox->x1 != 0.0 || cropBox->y1 != 0.0 ||
300         cropBox->x2 != state->getPageWidth() || cropBox->y2 != state->getPageHeight()) {
301         
302         state->moveTo(cropBox->x1, cropBox->y1);
303         state->lineTo(cropBox->x2, cropBox->y1);
304         state->lineTo(cropBox->x2, cropBox->y2);
305         state->lineTo(cropBox->x1, cropBox->y2);
306         state->closePath();
307         state->clip();
308         clipHistory->setClip(state->getPath(), clipNormal);
309         builder->setClipPath(state);
310         state->clearPath();
311     }
312   }
313   pushOperator("startPage");
316 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
317                      Dict *resDict, PDFRectangle *box) {
319   int i;
321   xref = xrefA;
322   subPage = gTrue;
323   printCommands = false;
325   // start the resource stack
326   res = new GfxResources(xref, resDict, NULL);
328   // initialize
329   operatorHistory = NULL;
330   builder = builderA;
331   state = new GfxState(72, 72, box, 0, gFalse);
332   clipHistory = new ClipHistoryEntry();
333   setDefaultApproximationPrecision();
334   
335   fontChanged = gFalse;
336   clip = clipNone;
337   ignoreUndef = 0;
338   for (i = 0; i < 6; ++i) {
339     baseMatrix[i] = state->getCTM()[i];
340   }
341   formDepth = 0;
344 PdfParser::~PdfParser() {
345   while (state->hasSaves()) {
346     restoreState();
347   }
348   if (!subPage) {
349     //out->endPage();
350   }
351   while (res) {
352     popResources();
353   }
354   if (state) {
355     delete state;
356   }
357   if (clipHistory) {
358     delete clipHistory;
359   }
362 void PdfParser::parse(Object *obj, GBool topLevel) {
363   Object obj2;
364   int i;
366   if (obj->isArray()) {
367     for (i = 0; i < obj->arrayGetLength(); ++i) {
368       obj->arrayGet(i, &obj2);
369       if (!obj2.isStream()) {
370         error(-1, const_cast<char*>("Weird page contents"));
371         obj2.free();
372         return;
373       }
374       obj2.free();
375     }
376   } else if (!obj->isStream()) {
377     error(-1, const_cast<char*>("Weird page contents"));
378     return;
379   }
380   parser = new Parser(xref, new Lexer(xref, obj), gFalse);
381   go(topLevel);
382   delete parser;
383   parser = NULL;
386 void PdfParser::go(GBool topLevel) {
387   Object obj;
388   Object args[maxArgs];
389   int numArgs, i;
391   // scan a sequence of objects
392   numArgs = 0;
393   parser->getObj(&obj);
394   while (!obj.isEOF()) {
396     // got a command - execute it
397     if (obj.isCmd()) {
398       if (printCommands) {
399         obj.print(stdout);
400         for (i = 0; i < numArgs; ++i) {
401           printf(" ");
402           args[i].print(stdout);
403         }
404         printf("\n");
405         fflush(stdout);
406       }
408       // Run the operation
409       execOp(&obj, args, numArgs);
411       obj.free();
412       for (i = 0; i < numArgs; ++i)
413         args[i].free();
414       numArgs = 0;
416     // got an argument - save it
417     } else if (numArgs < maxArgs) {
418       args[numArgs++] = obj;
420     // too many arguments - something is wrong
421     } else {
422       error(getPos(), const_cast<char*>("Too many args in content stream"));
423       if (printCommands) {
424         printf("throwing away arg: ");
425         obj.print(stdout);
426         printf("\n");
427         fflush(stdout);
428       }
429       obj.free();
430     }
432     // grab the next object
433     parser->getObj(&obj);
434   }
435   obj.free();
437   // args at end with no command
438   if (numArgs > 0) {
439     error(getPos(), const_cast<char*>("Leftover args in content stream"));
440     if (printCommands) {
441       printf("%d leftovers:", numArgs);
442       for (i = 0; i < numArgs; ++i) {
443         printf(" ");
444         args[i].print(stdout);
445       }
446       printf("\n");
447       fflush(stdout);
448     }
449     for (i = 0; i < numArgs; ++i)
450       args[i].free();
451   }
454 void PdfParser::pushOperator(const char *name) {
455     OpHistoryEntry *newEntry = new OpHistoryEntry;
456     newEntry->name = name;
457     newEntry->state = NULL;
458     newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
459     newEntry->next = operatorHistory;
460     operatorHistory = newEntry;
462     // Truncate list if needed
463     if (operatorHistory->depth > maxOperatorHistoryDepth) {
464         OpHistoryEntry *curr = operatorHistory;
465         OpHistoryEntry *prev = NULL;
466         while (curr && curr->next != NULL) {
467             curr->depth--;
468             prev = curr;
469             curr = curr->next;
470         }
471         if (prev) {
472             if (curr->state != NULL)
473                 delete curr->state;
474             delete curr;
475             prev->next = NULL;
476         }
477     }
480 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
481     OpHistoryEntry *prev = NULL;
482     if (operatorHistory != NULL && look_back > 0) {
483         prev = operatorHistory->next;
484         while (--look_back > 0 && prev != NULL) {
485             prev = prev->next;
486         }
487     }
488     if (prev != NULL) {
489         return prev->name;
490     } else {
491         return "";
492     }
495 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
496   PdfOperator *op;
497   char *name;
498   Object *argPtr;
499   int i;
501   // find operator
502   name = cmd->getCmd();
503   if (!(op = findOp(name))) {
504     if (ignoreUndef == 0)
505       error(getPos(), const_cast<char*>("Unknown operator '%s'"), name);
506     return;
507   }
509   // type check args
510   argPtr = args;
511   if (op->numArgs >= 0) {
512     if (numArgs < op->numArgs) {
513       error(getPos(), const_cast<char*>("Too few (%d) args to '%s' operator"), numArgs, name);
514       return;
515     }
516     if (numArgs > op->numArgs) {
517 #if 0
518       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
519 #endif
520       argPtr += numArgs - op->numArgs;
521       numArgs = op->numArgs;
522     }
523   } else {
524     if (numArgs > -op->numArgs) {
525       error(getPos(), const_cast<char*>("Too many (%d) args to '%s' operator"),
526             numArgs, name);
527       return;
528     }
529   }
530   for (i = 0; i < numArgs; ++i) {
531     if (!checkArg(&argPtr[i], op->tchk[i])) {
532       error(getPos(), const_cast<char*>("Arg #%d to '%s' operator is wrong type (%s)"),
533             i, name, argPtr[i].getTypeName());
534       return;
535     }
536   }
538   // add to history
539   pushOperator((char*)&op->name);
541   // do it
542   (this->*op->func)(argPtr, numArgs);
545 PdfOperator *PdfParser::findOp(char *name) {
546   int a, b, m, cmp;
548   a = -1;
549   b = numOps;
550   // invariant: opTab[a] < name < opTab[b]
551   while (b - a > 1) {
552     m = (a + b) / 2;
553     cmp = strcmp(opTab[m].name, name);
554     if (cmp < 0)
555       a = m;
556     else if (cmp > 0)
557       b = m;
558     else
559       a = b = m;
560   }
561   if (cmp != 0)
562     return NULL;
563   return &opTab[a];
566 GBool PdfParser::checkArg(Object *arg, TchkType type) {
567   switch (type) {
568   case tchkBool:   return arg->isBool();
569   case tchkInt:    return arg->isInt();
570   case tchkNum:    return arg->isNum();
571   case tchkString: return arg->isString();
572   case tchkName:   return arg->isName();
573   case tchkArray:  return arg->isArray();
574   case tchkProps:  return arg->isDict() || arg->isName();
575   case tchkSCN:    return arg->isNum() || arg->isName();
576   case tchkNone:   return gFalse;
577   }
578   return gFalse;
581 int PdfParser::getPos() {
582   return parser ? parser->getPos() : -1;
585 //------------------------------------------------------------------------
586 // graphics state operators
587 //------------------------------------------------------------------------
589 void PdfParser::opSave(Object args[], int numArgs) {
590   saveState();
593 void PdfParser::opRestore(Object args[], int numArgs) {
594   restoreState();
597 void PdfParser::opConcat(Object args[], int numArgs) {
598   state->concatCTM(args[0].getNum(), args[1].getNum(),
599                    args[2].getNum(), args[3].getNum(),
600                    args[4].getNum(), args[5].getNum());
601   const char *prevOp = getPreviousOperator();
602   double a0 = args[0].getNum();
603   double a1 = args[1].getNum();
604   double a2 = args[2].getNum();
605   double a3 = args[3].getNum();
606   double a4 = args[4].getNum();
607   double a5 = args[5].getNum();
608   if (!strcmp(prevOp, "q")) {
609       builder->setTransform(a0, a1, a2, a3, a4, a5);
610   } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
611       // multiply it with the previous transform
612       double otherMatrix[6];
613       if (!builder->getTransform(otherMatrix)) { // invalid transform
614           // construct identity matrix
615           otherMatrix[0] = otherMatrix[3] = 1.0;
616           otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
617       }
618       double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
619       double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
620       double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
621       double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
622       double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
623       double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
624       builder->setTransform(c0, c1, c2, c3, c4, c5);
625   } else {
626       builder->pushGroup();
627       builder->setTransform(a0, a1, a2, a3, a4, a5);
628   }
629   fontChanged = gTrue;
632 void PdfParser::opSetDash(Object args[], int numArgs) {
633   Array *a;
634   int length;
635   Object obj;
636   double *dash;
637   int i;
639   a = args[0].getArray();
640   length = a->getLength();
641   if (length == 0) {
642     dash = NULL;
643   } else {
644     dash = (double *)gmallocn(length, sizeof(double));
645     for (i = 0; i < length; ++i) {
646       dash[i] = a->get(i, &obj)->getNum();
647       obj.free();
648     }
649   }
650   state->setLineDash(dash, length, args[1].getNum());
651   builder->updateStyle(state);
654 void PdfParser::opSetFlat(Object args[], int numArgs) {
655   state->setFlatness((int)args[0].getNum());
658 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
659   state->setLineJoin(args[0].getInt());
660   builder->updateStyle(state);
663 void PdfParser::opSetLineCap(Object args[], int numArgs) {
664   state->setLineCap(args[0].getInt());
665   builder->updateStyle(state);
668 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
669   state->setMiterLimit(args[0].getNum());
670   builder->updateStyle(state);
673 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
674   state->setLineWidth(args[0].getNum());
675   builder->updateStyle(state);
678 void PdfParser::opSetExtGState(Object args[], int numArgs) {
679   Object obj1, obj2, obj3, obj4, obj5;
680   GfxBlendMode mode;
681   GBool haveFillOP;
682   Function *funcs[4];
683   GfxColor backdropColor;
684   GBool haveBackdropColor;
685   GfxColorSpace *blendingColorSpace;
686   GBool alpha, isolated, knockout;
687   int i;
689   if (!res->lookupGState(args[0].getName(), &obj1)) {
690     return;
691   }
692   if (!obj1.isDict()) {
693     error(getPos(), const_cast<char*>("ExtGState '%s' is wrong type"), args[0].getName());
694     obj1.free();
695     return;
696   }
697   if (printCommands) {
698     printf("  gfx state dict: ");
699     obj1.print();
700     printf("\n");
701   }
703   // transparency support: blend mode, fill/stroke opacity
704   if (!obj1.dictLookup(const_cast<char*>("BM"), &obj2)->isNull()) {
705     if (state->parseBlendMode(&obj2, &mode)) {
706       state->setBlendMode(mode);
707     } else {
708       error(getPos(), const_cast<char*>("Invalid blend mode in ExtGState"));
709     }
710   }
711   obj2.free();
712   if (obj1.dictLookup(const_cast<char*>("ca"), &obj2)->isNum()) {
713     state->setFillOpacity(obj2.getNum());
714   }
715   obj2.free();
716   if (obj1.dictLookup(const_cast<char*>("CA"), &obj2)->isNum()) {
717     state->setStrokeOpacity(obj2.getNum());
718   }
719   obj2.free();
721   // fill/stroke overprint
722   if ((haveFillOP = (obj1.dictLookup(const_cast<char*>("op"), &obj2)->isBool()))) {
723     state->setFillOverprint(obj2.getBool());
724   }
725   obj2.free();
726   if (obj1.dictLookup(const_cast<char*>("OP"), &obj2)->isBool()) {
727     state->setStrokeOverprint(obj2.getBool());
728     if (!haveFillOP) {
729       state->setFillOverprint(obj2.getBool());
730     }
731   }
732   obj2.free();
734   // stroke adjust
735   if (obj1.dictLookup(const_cast<char*>("SA"), &obj2)->isBool()) {
736     state->setStrokeAdjust(obj2.getBool());
737   }
738   obj2.free();
740   // transfer function
741   if (obj1.dictLookup(const_cast<char*>("TR2"), &obj2)->isNull()) {
742     obj2.free();
743     obj1.dictLookup(const_cast<char*>("TR"), &obj2);
744   }
745   if (obj2.isName(const_cast<char*>("Default")) ||
746       obj2.isName(const_cast<char*>("Identity"))) {
747     funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
748     state->setTransfer(funcs);
749   } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
750     for (i = 0; i < 4; ++i) {
751       obj2.arrayGet(i, &obj3);
752       funcs[i] = Function::parse(&obj3);
753       obj3.free();
754       if (!funcs[i]) {
755         break;
756       }
757     }
758     if (i == 4) {
759       state->setTransfer(funcs);
760     }
761   } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
762     if ((funcs[0] = Function::parse(&obj2))) {
763       funcs[1] = funcs[2] = funcs[3] = NULL;
764       state->setTransfer(funcs);
765     }
766   } else if (!obj2.isNull()) {
767     error(getPos(), const_cast<char*>("Invalid transfer function in ExtGState"));
768   }
769   obj2.free();
771   // soft mask
772   if (!obj1.dictLookup(const_cast<char*>("SMask"), &obj2)->isNull()) {
773     if (obj2.isName(const_cast<char*>("None"))) {
774       builder->clearSoftMask(state);
775     } else if (obj2.isDict()) {
776       if (obj2.dictLookup(const_cast<char*>("S"), &obj3)->isName(const_cast<char*>("Alpha"))) {
777         alpha = gTrue;
778       } else { // "Luminosity"
779         alpha = gFalse;
780       }
781       obj3.free();
782       funcs[0] = NULL;
783       if (!obj2.dictLookup(const_cast<char*>("TR"), &obj3)->isNull()) {
784         funcs[0] = Function::parse(&obj3);
785         if (funcs[0]->getInputSize() != 1 ||
786             funcs[0]->getOutputSize() != 1) {
787           error(getPos(),
788                 const_cast<char*>("Invalid transfer function in soft mask in ExtGState"));
789           delete funcs[0];
790           funcs[0] = NULL;
791         }
792       }
793       obj3.free();
794       if ((haveBackdropColor = obj2.dictLookup(const_cast<char*>("BC"), &obj3)->isArray())) {
795         for (i = 0; i < gfxColorMaxComps; ++i) {
796           backdropColor.c[i] = 0;
797         }
798         for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
799           obj3.arrayGet(i, &obj4);
800           if (obj4.isNum()) {
801             backdropColor.c[i] = dblToCol(obj4.getNum());
802           }
803           obj4.free();
804         }
805       }
806       obj3.free();
807       if (obj2.dictLookup(const_cast<char*>("G"), &obj3)->isStream()) {
808         if (obj3.streamGetDict()->lookup(const_cast<char*>("Group"), &obj4)->isDict()) {
809           blendingColorSpace = NULL;
810           isolated = knockout = gFalse;
811           if (!obj4.dictLookup(const_cast<char*>("CS"), &obj5)->isNull()) {
812             blendingColorSpace = GfxColorSpace::parse(&obj5);
813           }
814           obj5.free();
815           if (obj4.dictLookup(const_cast<char*>("I"), &obj5)->isBool()) {
816             isolated = obj5.getBool();
817           }
818           obj5.free();
819           if (obj4.dictLookup(const_cast<char*>("K"), &obj5)->isBool()) {
820             knockout = obj5.getBool();
821           }
822           obj5.free();
823           if (!haveBackdropColor) {
824             if (blendingColorSpace) {
825               blendingColorSpace->getDefaultColor(&backdropColor);
826             } else {
827               //~ need to get the parent or default color space (?)
828               for (i = 0; i < gfxColorMaxComps; ++i) {
829                 backdropColor.c[i] = 0;
830               }
831             }
832           }
833           doSoftMask(&obj3, alpha, blendingColorSpace,
834                      isolated, knockout, funcs[0], &backdropColor);
835           if (funcs[0]) {
836             delete funcs[0];
837           }
838         } else {
839           error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState - missing group"));
840         }
841         obj4.free();
842       } else {
843         error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState - missing group"));
844       }
845       obj3.free();
846     } else if (!obj2.isNull()) {
847       error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState"));
848     }
849   }
850   obj2.free();
852   obj1.free();
855 void PdfParser::doSoftMask(Object *str, GBool alpha,
856                      GfxColorSpace *blendingColorSpace,
857                      GBool isolated, GBool knockout,
858                      Function *transferFunc, GfxColor *backdropColor) {
859   Dict *dict, *resDict;
860   double m[6], bbox[4];
861   Object obj1, obj2;
862   int i;
864   // check for excessive recursion
865   if (formDepth > 20) {
866     return;
867   }
869   // get stream dict
870   dict = str->streamGetDict();
872   // check form type
873   dict->lookup(const_cast<char*>("FormType"), &obj1);
874   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
875     error(getPos(), const_cast<char*>("Unknown form type"));
876   }
877   obj1.free();
879   // get bounding box
880   dict->lookup(const_cast<char*>("BBox"), &obj1);
881   if (!obj1.isArray()) {
882     obj1.free();
883     error(getPos(), const_cast<char*>("Bad form bounding box"));
884     return;
885   }
886   for (i = 0; i < 4; ++i) {
887     obj1.arrayGet(i, &obj2);
888     bbox[i] = obj2.getNum();
889     obj2.free();
890   }
891   obj1.free();
893   // get matrix
894   dict->lookup(const_cast<char*>("Matrix"), &obj1);
895   if (obj1.isArray()) {
896     for (i = 0; i < 6; ++i) {
897       obj1.arrayGet(i, &obj2);
898       m[i] = obj2.getNum();
899       obj2.free();
900     }
901   } else {
902     m[0] = 1; m[1] = 0;
903     m[2] = 0; m[3] = 1;
904     m[4] = 0; m[5] = 0;
905   }
906   obj1.free();
908   // get resources
909   dict->lookup(const_cast<char*>("Resources"), &obj1);
910   resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
912   // draw it
913   ++formDepth;
914   doForm1(str, resDict, m, bbox, gTrue, gTrue,
915           blendingColorSpace, isolated, knockout,
916           alpha, transferFunc, backdropColor);
917   --formDepth;
919   if (blendingColorSpace) {
920     delete blendingColorSpace;
921   }
922   obj1.free();
925 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
928 //------------------------------------------------------------------------
929 // color operators
930 //------------------------------------------------------------------------
932 void PdfParser::opSetFillGray(Object args[], int numArgs) {
933   GfxColor color;
935   state->setFillPattern(NULL);
936   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
937   color.c[0] = dblToCol(args[0].getNum());
938   state->setFillColor(&color);
939   builder->updateStyle(state);
942 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
943   GfxColor color;
945   state->setStrokePattern(NULL);
946   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
947   color.c[0] = dblToCol(args[0].getNum());
948   state->setStrokeColor(&color);
949   builder->updateStyle(state);
952 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
953   GfxColor color;
954   int i;
956   state->setFillPattern(NULL);
957   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
958   for (i = 0; i < 4; ++i) {
959     color.c[i] = dblToCol(args[i].getNum());
960   }
961   state->setFillColor(&color);
962   builder->updateStyle(state);
965 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
966   GfxColor color;
967   int i;
969   state->setStrokePattern(NULL);
970   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
971   for (i = 0; i < 4; ++i) {
972     color.c[i] = dblToCol(args[i].getNum());
973   }
974   state->setStrokeColor(&color);
975   builder->updateStyle(state);
978 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
979   GfxColor color;
980   int i;
982   state->setFillPattern(NULL);
983   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
984   for (i = 0; i < 3; ++i) {
985     color.c[i] = dblToCol(args[i].getNum());
986   }
987   state->setFillColor(&color);
988   builder->updateStyle(state);
991 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
992   GfxColor color;
993   int i;
995   state->setStrokePattern(NULL);
996   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
997   for (i = 0; i < 3; ++i) {
998     color.c[i] = dblToCol(args[i].getNum());
999   }
1000   state->setStrokeColor(&color);
1001   builder->updateStyle(state);
1004 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1005   Object obj;
1006   GfxColorSpace *colorSpace;
1007   GfxColor color;
1009   state->setFillPattern(NULL);
1010   res->lookupColorSpace(args[0].getName(), &obj);
1011   if (obj.isNull()) {
1012     colorSpace = GfxColorSpace::parse(&args[0]);
1013   } else {
1014     colorSpace = GfxColorSpace::parse(&obj);
1015   }
1016   obj.free();
1017   if (colorSpace) {
1018     state->setFillColorSpace(colorSpace);
1019     colorSpace->getDefaultColor(&color);
1020     state->setFillColor(&color);
1021     builder->updateStyle(state);
1022   } else {
1023     error(getPos(), const_cast<char*>("Bad color space (fill)"));
1024   }
1027 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1028   Object obj;
1029   GfxColorSpace *colorSpace;
1030   GfxColor color;
1032   state->setStrokePattern(NULL);
1033   res->lookupColorSpace(args[0].getName(), &obj);
1034   if (obj.isNull()) {
1035     colorSpace = GfxColorSpace::parse(&args[0]);
1036   } else {
1037     colorSpace = GfxColorSpace::parse(&obj);
1038   }
1039   obj.free();
1040   if (colorSpace) {
1041     state->setStrokeColorSpace(colorSpace);
1042     colorSpace->getDefaultColor(&color);
1043     state->setStrokeColor(&color);
1044     builder->updateStyle(state);
1045   } else {
1046     error(getPos(), const_cast<char*>("Bad color space (stroke)"));
1047   }
1050 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1051   GfxColor color;
1052   int i;
1054   if (numArgs != state->getFillColorSpace()->getNComps()) {
1055     error(getPos(), const_cast<char*>("Incorrect number of arguments in 'sc' command"));
1056     return;
1057   }
1058   state->setFillPattern(NULL);
1059   for (i = 0; i < numArgs; ++i) {
1060     color.c[i] = dblToCol(args[i].getNum());
1061   }
1062   state->setFillColor(&color);
1063   builder->updateStyle(state);
1066 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1067   GfxColor color;
1068   int i;
1070   if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1071     error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SC' command"));
1072     return;
1073   }
1074   state->setStrokePattern(NULL);
1075   for (i = 0; i < numArgs; ++i) {
1076     color.c[i] = dblToCol(args[i].getNum());
1077   }
1078   state->setStrokeColor(&color);
1079   builder->updateStyle(state);
1082 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1083   GfxColor color;
1084   GfxPattern *pattern;
1085   int i;
1087   if (state->getFillColorSpace()->getMode() == csPattern) {
1088     if (numArgs > 1) {
1089       if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1090           numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1091                              ->getUnder()->getNComps()) {
1092         error(getPos(), const_cast<char*>("Incorrect number of arguments in 'scn' command"));
1093         return;
1094       }
1095       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1096         if (args[i].isNum()) {
1097           color.c[i] = dblToCol(args[i].getNum());
1098         }
1099       }
1100       state->setFillColor(&color);
1101       builder->updateStyle(state);
1102     }
1103     if (args[numArgs-1].isName() &&
1104         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1105       state->setFillPattern(pattern);
1106       builder->updateStyle(state);
1107     }
1109   } else {
1110     if (numArgs != state->getFillColorSpace()->getNComps()) {
1111       error(getPos(), const_cast<char*>("Incorrect number of arguments in 'scn' command"));
1112       return;
1113     }
1114     state->setFillPattern(NULL);
1115     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1116       if (args[i].isNum()) {
1117         color.c[i] = dblToCol(args[i].getNum());
1118       }
1119     }
1120     state->setFillColor(&color);
1121     builder->updateStyle(state);
1122   }
1125 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1126   GfxColor color;
1127   GfxPattern *pattern;
1128   int i;
1130   if (state->getStrokeColorSpace()->getMode() == csPattern) {
1131     if (numArgs > 1) {
1132       if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1133                ->getUnder() ||
1134           numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1135                              ->getUnder()->getNComps()) {
1136         error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SCN' command"));
1137         return;
1138       }
1139       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1140         if (args[i].isNum()) {
1141           color.c[i] = dblToCol(args[i].getNum());
1142         }
1143       }
1144       state->setStrokeColor(&color);
1145       builder->updateStyle(state);
1146     }
1147     if (args[numArgs-1].isName() &&
1148         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1149       state->setStrokePattern(pattern);
1150       builder->updateStyle(state);
1151     }
1153   } else {
1154     if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1155       error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SCN' command"));
1156       return;
1157     }
1158     state->setStrokePattern(NULL);
1159     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1160       if (args[i].isNum()) {
1161         color.c[i] = dblToCol(args[i].getNum());
1162       }
1163     }
1164     state->setStrokeColor(&color);
1165     builder->updateStyle(state);
1166   }
1169 //------------------------------------------------------------------------
1170 // path segment operators
1171 //------------------------------------------------------------------------
1173 void PdfParser::opMoveTo(Object args[], int numArgs) {
1174   state->moveTo(args[0].getNum(), args[1].getNum());
1177 void PdfParser::opLineTo(Object args[], int numArgs) {
1178   if (!state->isCurPt()) {
1179     error(getPos(), const_cast<char*>("No current point in lineto"));
1180     return;
1181   }
1182   state->lineTo(args[0].getNum(), args[1].getNum());
1185 void PdfParser::opCurveTo(Object args[], int numArgs) {
1186   double x1, y1, x2, y2, x3, y3;
1188   if (!state->isCurPt()) {
1189     error(getPos(), const_cast<char*>("No current point in curveto"));
1190     return;
1191   }
1192   x1 = args[0].getNum();
1193   y1 = args[1].getNum();
1194   x2 = args[2].getNum();
1195   y2 = args[3].getNum();
1196   x3 = args[4].getNum();
1197   y3 = args[5].getNum();
1198   state->curveTo(x1, y1, x2, y2, x3, y3);
1201 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1202   double x1, y1, x2, y2, x3, y3;
1204   if (!state->isCurPt()) {
1205     error(getPos(), const_cast<char*>("No current point in curveto1"));
1206     return;
1207   }
1208   x1 = state->getCurX();
1209   y1 = state->getCurY();
1210   x2 = args[0].getNum();
1211   y2 = args[1].getNum();
1212   x3 = args[2].getNum();
1213   y3 = args[3].getNum();
1214   state->curveTo(x1, y1, x2, y2, x3, y3);
1217 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1218   double x1, y1, x2, y2, x3, y3;
1220   if (!state->isCurPt()) {
1221     error(getPos(), const_cast<char*>("No current point in curveto2"));
1222     return;
1223   }
1224   x1 = args[0].getNum();
1225   y1 = args[1].getNum();
1226   x2 = args[2].getNum();
1227   y2 = args[3].getNum();
1228   x3 = x2;
1229   y3 = y2;
1230   state->curveTo(x1, y1, x2, y2, x3, y3);
1233 void PdfParser::opRectangle(Object args[], int numArgs) {
1234   double x, y, w, h;
1236   x = args[0].getNum();
1237   y = args[1].getNum();
1238   w = args[2].getNum();
1239   h = args[3].getNum();
1240   state->moveTo(x, y);
1241   state->lineTo(x + w, y);
1242   state->lineTo(x + w, y + h);
1243   state->lineTo(x, y + h);
1244   state->closePath();
1247 void PdfParser::opClosePath(Object args[], int numArgs) {
1248   if (!state->isCurPt()) {
1249     error(getPos(), const_cast<char*>("No current point in closepath"));
1250     return;
1251   }
1252   state->closePath();
1255 //------------------------------------------------------------------------
1256 // path painting operators
1257 //------------------------------------------------------------------------
1259 void PdfParser::opEndPath(Object args[], int numArgs) {
1260   doEndPath();
1263 void PdfParser::opStroke(Object args[], int numArgs) {
1264   if (!state->isCurPt()) {
1265     //error(getPos(), const_cast<char*>("No path in stroke"));
1266     return;
1267   }
1268   if (state->isPath()) {
1269     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1270         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1271           doPatternStrokeFallback();
1272     } else {
1273       builder->addPath(state, false, true);
1274     }
1275   }
1276   doEndPath();
1279 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1280   if (!state->isCurPt()) {
1281     //error(getPos(), const_cast<char*>("No path in closepath/stroke"));
1282     return;
1283   }
1284   state->closePath();
1285   if (state->isPath()) {
1286     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1287         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1288       doPatternStrokeFallback();
1289     } else {
1290       builder->addPath(state, false, true);
1291     }
1292   }
1293   doEndPath();
1296 void PdfParser::opFill(Object args[], int numArgs) {
1297   if (!state->isCurPt()) {
1298     //error(getPos(), const_cast<char*>("No path in fill"));
1299     return;
1300   }
1301   if (state->isPath()) {
1302     if (state->getFillColorSpace()->getMode() == csPattern &&
1303         !builder->isPatternTypeSupported(state->getFillPattern())) {
1304       doPatternFillFallback(gFalse);
1305     } else {
1306       builder->addPath(state, true, false);
1307     }
1308   }
1309   doEndPath();
1312 void PdfParser::opEOFill(Object args[], int numArgs) {
1313   if (!state->isCurPt()) {
1314     //error(getPos(), const_cast<char*>("No path in eofill"));
1315     return;
1316   }
1317   if (state->isPath()) {
1318     if (state->getFillColorSpace()->getMode() == csPattern &&
1319         !builder->isPatternTypeSupported(state->getFillPattern())) {
1320       doPatternFillFallback(gTrue);
1321     } else {
1322       builder->addPath(state, true, false, true);
1323     }
1324   }
1325   doEndPath();
1328 void PdfParser::opFillStroke(Object args[], int numArgs) {
1329   if (!state->isCurPt()) {
1330     //error(getPos(), const_cast<char*>("No path in fill/stroke"));
1331     return;
1332   }
1333   if (state->isPath()) {
1334     doFillAndStroke(gFalse);
1335   } else {
1336     builder->addPath(state, true, true);
1337   }
1338   doEndPath();
1341 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1342   if (!state->isCurPt()) {
1343     //error(getPos(), const_cast<char*>("No path in closepath/fill/stroke"));
1344     return;
1345   }
1346   if (state->isPath()) {
1347     state->closePath();
1348     doFillAndStroke(gFalse);
1349   }
1350   doEndPath();
1353 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1354   if (!state->isCurPt()) {
1355     //error(getPos(), const_cast<char*>("No path in eofill/stroke"));
1356     return;
1357   }
1358   if (state->isPath()) {
1359     doFillAndStroke(gTrue);
1360   }
1361   doEndPath();
1364 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1365   if (!state->isCurPt()) {
1366     //error(getPos(), const_cast<char*>("No path in closepath/eofill/stroke"));
1367     return;
1368   }
1369   if (state->isPath()) {
1370     state->closePath();
1371     doFillAndStroke(gTrue);
1372   }
1373   doEndPath();
1376 void PdfParser::doFillAndStroke(GBool eoFill) {
1377     GBool fillOk = gTrue, strokeOk = gTrue;
1378     if (state->getFillColorSpace()->getMode() == csPattern &&
1379         !builder->isPatternTypeSupported(state->getFillPattern())) {
1380         fillOk = gFalse;
1381     }
1382     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1383         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1384         strokeOk = gFalse;
1385     }
1386     if (fillOk && strokeOk) {
1387         builder->addPath(state, true, true, eoFill);
1388     } else {
1389         doPatternFillFallback(eoFill);
1390         doPatternStrokeFallback();
1391     }
1394 void PdfParser::doPatternFillFallback(GBool eoFill) {
1395   GfxPattern *pattern;
1397   if (!(pattern = state->getFillPattern())) {
1398     return;
1399   }
1400   switch (pattern->getType()) {
1401   case 1:
1402     break;
1403   case 2:
1404     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1405     break;
1406   default:
1407     error(getPos(), const_cast<char*>("Unimplemented pattern type (%d) in fill"),
1408           pattern->getType());
1409     break;
1410   }
1413 void PdfParser::doPatternStrokeFallback() {
1414   GfxPattern *pattern;
1416   if (!(pattern = state->getStrokePattern())) {
1417     return;
1418   }
1419   switch (pattern->getType()) {
1420   case 1:
1421     break;
1422   case 2:
1423     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1424     break;
1425   default:
1426     error(getPos(), const_cast<char*>("Unimplemented pattern type (%d) in stroke"),
1427           pattern->getType());
1428     break;
1429   }
1432 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1433                                              GBool stroke, GBool eoFill) {
1434   GfxShading *shading;
1435   GfxPath *savedPath;
1436   double *ctm, *btm, *ptm;
1437   double m[6], ictm[6], m1[6];
1438   double xMin, yMin, xMax, yMax;
1439   double det;
1441   shading = sPat->getShading();
1443   // save current graphics state
1444   savedPath = state->getPath()->copy();
1445   saveState();
1447   // clip to bbox
1448   if (0 ){//shading->getHasBBox()) {
1449     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1450     state->moveTo(xMin, yMin);
1451     state->lineTo(xMax, yMin);
1452     state->lineTo(xMax, yMax);
1453     state->lineTo(xMin, yMax);
1454     state->closePath();
1455     state->clip();
1456     //builder->clip(state);
1457     state->setPath(savedPath->copy());
1458   }
1460   // clip to current path
1461   if (stroke) {
1462     state->clipToStrokePath();
1463     //out->clipToStrokePath(state);
1464   } else {
1465     state->clip();
1466     if (eoFill) {
1467       builder->setClipPath(state, true);
1468     } else {
1469       builder->setClipPath(state);
1470     }
1471   }
1473   // set the color space
1474   state->setFillColorSpace(shading->getColorSpace()->copy());
1476   // background color fill
1477   if (shading->getHasBackground()) {
1478     state->setFillColor(shading->getBackground());
1479     builder->addPath(state, true, false);
1480   }
1481   state->clearPath();
1483   // construct a (pattern space) -> (current space) transform matrix
1484   ctm = state->getCTM();
1485   btm = baseMatrix;
1486   ptm = sPat->getMatrix();
1487   // iCTM = invert CTM
1488   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1489   ictm[0] = ctm[3] * det;
1490   ictm[1] = -ctm[1] * det;
1491   ictm[2] = -ctm[2] * det;
1492   ictm[3] = ctm[0] * det;
1493   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1494   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1495   // m1 = PTM * BTM = PTM * base transform matrix
1496   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1497   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1498   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1499   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1500   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1501   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1502   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1503   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1504   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1505   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1506   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1507   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1508   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1510   // set the new matrix
1511   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1512   builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1514   // do shading type-specific operations
1515   switch (shading->getType()) {
1516   case 1:
1517     doFunctionShFill((GfxFunctionShading *)shading);
1518     break;
1519   case 2:
1520   case 3:
1521     // no need to implement these
1522     break;
1523   case 4:
1524   case 5:
1525     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1526     break;
1527   case 6:
1528   case 7:
1529     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1530     break;
1531   }
1533   // restore graphics state
1534   restoreState();
1535   state->setPath(savedPath);
1538 void PdfParser::opShFill(Object args[], int numArgs) {
1539   GfxShading *shading;
1540   GfxPath *savedPath = NULL;
1541   double xMin, yMin, xMax, yMax;
1542   double gradientTransform[6];
1543   double *matrix = NULL;
1544   GBool savedState = gFalse;
1546   if (!(shading = res->lookupShading(args[0].getName()))) {
1547     return;
1548   }
1550   // save current graphics state
1551   if (shading->getType() != 2 && shading->getType() != 3) {
1552     savedPath = state->getPath()->copy();
1553     saveState();
1554     savedState = gTrue;
1555   } else {  // get gradient transform if possible
1556       // check proper operator sequence
1557       // first there should be one W(*) and then one 'cm' somewhere before 'sh'
1558       GBool seenClip, seenConcat;
1559       seenClip = (clipHistory->getClipPath() != NULL);
1560       seenConcat = gFalse;
1561       int i = 1;
1562       while (i <= maxOperatorHistoryDepth) {
1563         const char *opName = getPreviousOperator(i);
1564         if (!strcmp(opName, "cm")) {
1565           if (seenConcat) {   // more than one 'cm'
1566             break;
1567           } else {
1568             seenConcat = gTrue;
1569           }
1570         }
1571         i++;
1572       }
1574       if (seenConcat && seenClip) {
1575         if (builder->getTransform(gradientTransform)) {
1576           matrix = (double*)&gradientTransform;
1577           builder->setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);  // remove transform
1578         }
1579       }
1580   }
1582   // clip to bbox
1583   if (shading->getHasBBox()) {
1584     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1585     state->moveTo(xMin, yMin);
1586     state->lineTo(xMax, yMin);
1587     state->lineTo(xMax, yMax);
1588     state->lineTo(xMin, yMax);
1589     state->closePath();
1590     state->clip();
1591     if (savedState)
1592       builder->setClipPath(state);
1593     else
1594       builder->clip(state);
1595     state->clearPath();
1596   }
1598   // set the color space
1599   if (savedState)
1600     state->setFillColorSpace(shading->getColorSpace()->copy());
1602   // do shading type-specific operations
1603   switch (shading->getType()) {
1604   case 1:
1605     doFunctionShFill((GfxFunctionShading *)shading);
1606     break;
1607   case 2:
1608   case 3:
1609     if (clipHistory->getClipPath()) {
1610       builder->addShadedFill(shading, matrix, clipHistory->getClipPath(),
1611                              clipHistory->getClipType() == clipEO ? true : false);
1612     }
1613     break;
1614   case 4:
1615   case 5:
1616     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1617     break;
1618   case 6:
1619   case 7:
1620     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1621     break;
1622   }
1624   // restore graphics state
1625   if (savedState) {
1626     restoreState();
1627     state->setPath(savedPath);
1628   }
1630   delete shading;
1633 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1634   double x0, y0, x1, y1;
1635   GfxColor colors[4];
1637   shading->getDomain(&x0, &y0, &x1, &y1);
1638   shading->getColor(x0, y0, &colors[0]);
1639   shading->getColor(x0, y1, &colors[1]);
1640   shading->getColor(x1, y0, &colors[2]);
1641   shading->getColor(x1, y1, &colors[3]);
1642   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1645 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1646                             double x0, double y0,
1647                             double x1, double y1,
1648                             GfxColor *colors, int depth) {
1649   GfxColor fillColor;
1650   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1651   GfxColor colors2[4];
1652   double functionColorDelta = colorDeltas[pdfFunctionShading-1];
1653   double *matrix;
1654   double xM, yM;
1655   int nComps, i, j;
1657   nComps = shading->getColorSpace()->getNComps();
1658   matrix = shading->getMatrix();
1660   // compare the four corner colors
1661   for (i = 0; i < 4; ++i) {
1662     for (j = 0; j < nComps; ++j) {
1663       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1664         break;
1665       }
1666     }
1667     if (j < nComps) {
1668       break;
1669     }
1670   }
1672   // center of the rectangle
1673   xM = 0.5 * (x0 + x1);
1674   yM = 0.5 * (y0 + y1);
1676   // the four corner colors are close (or we hit the recursive limit)
1677   // -- fill the rectangle; but require at least one subdivision
1678   // (depth==0) to avoid problems when the four outer corners of the
1679   // shaded region are the same color
1680   if ((i == 4 && depth > 0) || depth == maxDepths[pdfFunctionShading-1]) {
1682     // use the center color
1683     shading->getColor(xM, yM, &fillColor);
1684     state->setFillColor(&fillColor);
1686     // fill the rectangle
1687     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1688                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1689     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1690                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1691     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1692                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1693     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1694                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1695     state->closePath();
1696     builder->addPath(state, true, false);
1697     state->clearPath();
1699   // the four corner colors are not close enough -- subdivide the
1700   // rectangle
1701   } else {
1703     // colors[0]       colorM0       colors[2]
1704     //   (x0,y0)       (xM,y0)       (x1,y0)
1705     //         +----------+----------+
1706     //         |          |          |
1707     //         |    UL    |    UR    |
1708     // color0M |       colorMM       | color1M
1709     // (x0,yM) +----------+----------+ (x1,yM)
1710     //         |       (xM,yM)       |
1711     //         |    LL    |    LR    |
1712     //         |          |          |
1713     //         +----------+----------+
1714     // colors[1]       colorM1       colors[3]
1715     //   (x0,y1)       (xM,y1)       (x1,y1)
1717     shading->getColor(x0, yM, &color0M);
1718     shading->getColor(x1, yM, &color1M);
1719     shading->getColor(xM, y0, &colorM0);
1720     shading->getColor(xM, y1, &colorM1);
1721     shading->getColor(xM, yM, &colorMM);
1723     // upper-left sub-rectangle
1724     colors2[0] = colors[0];
1725     colors2[1] = color0M;
1726     colors2[2] = colorM0;
1727     colors2[3] = colorMM;
1728     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1729     
1730     // lower-left sub-rectangle
1731     colors2[0] = color0M;
1732     colors2[1] = colors[1];
1733     colors2[2] = colorMM;
1734     colors2[3] = colorM1;
1735     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1736     
1737     // upper-right sub-rectangle
1738     colors2[0] = colorM0;
1739     colors2[1] = colorMM;
1740     colors2[2] = colors[2];
1741     colors2[3] = color1M;
1742     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1744     // lower-right sub-rectangle
1745     colors2[0] = colorMM;
1746     colors2[1] = colorM1;
1747     colors2[2] = color1M;
1748     colors2[3] = colors[3];
1749     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1750   }
1753 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1754   double x0, y0, x1, y1, x2, y2;
1755   GfxColor color0, color1, color2;
1756   int i;
1758   for (i = 0; i < shading->getNTriangles(); ++i) {
1759     shading->getTriangle(i, &x0, &y0, &color0,
1760                          &x1, &y1, &color1,
1761                          &x2, &y2, &color2);
1762     gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1763                         shading->getColorSpace()->getNComps(), 0);
1764   }
1767 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1768                               double x1, double y1, GfxColor *color1,
1769                               double x2, double y2, GfxColor *color2,
1770                               int nComps, int depth) {
1771   double x01, y01, x12, y12, x20, y20;
1772   double gouraudColorDelta = colorDeltas[pdfGouraudTriangleShading-1];
1773   GfxColor color01, color12, color20;
1774   int i;
1776   for (i = 0; i < nComps; ++i) {
1777     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1778        abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1779       break;
1780     }
1781   }
1782   if (i == nComps || depth == maxDepths[pdfGouraudTriangleShading-1]) {
1783     state->setFillColor(color0);
1784     state->moveTo(x0, y0);
1785     state->lineTo(x1, y1);
1786     state->lineTo(x2, y2);
1787     state->closePath();
1788     builder->addPath(state, true, false);
1789     state->clearPath();
1790   } else {
1791     x01 = 0.5 * (x0 + x1);
1792     y01 = 0.5 * (y0 + y1);
1793     x12 = 0.5 * (x1 + x2);
1794     y12 = 0.5 * (y1 + y2);
1795     x20 = 0.5 * (x2 + x0);
1796     y20 = 0.5 * (y2 + y0);
1797     //~ if the shading has a Function, this should interpolate on the
1798     //~ function parameter, not on the color components
1799     for (i = 0; i < nComps; ++i) {
1800       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1801       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1802       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1803     }
1804     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1805                         x20, y20, &color20, nComps, depth + 1);
1806     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1807                         x12, y12, &color12, nComps, depth + 1);
1808     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1809                         x20, y20, &color20, nComps, depth + 1);
1810     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1811                         x2, y2, color2, nComps, depth + 1);
1812   }
1815 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1816   int start, i;
1818   if (shading->getNPatches() > 128) {
1819     start = 3;
1820   } else if (shading->getNPatches() > 64) {
1821     start = 2;
1822   } else if (shading->getNPatches() > 16) {
1823     start = 1;
1824   } else {
1825     start = 0;
1826   }
1827   for (i = 0; i < shading->getNPatches(); ++i) {
1828     fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1829               start);
1830   }
1833 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1834   GfxPatch patch00, patch01, patch10, patch11;
1835   double xx[4][8], yy[4][8];
1836   double xxm, yym;
1837   double patchColorDelta = colorDeltas[pdfPatchMeshShading-1];
1838   int i;
1840   for (i = 0; i < nComps; ++i) {
1841     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1842           > patchColorDelta ||
1843         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1844           > patchColorDelta ||
1845         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1846           > patchColorDelta ||
1847         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1848           > patchColorDelta) {
1849       break;
1850     }
1851   }
1852   if (i == nComps || depth == maxDepths[pdfPatchMeshShading-1]) {
1853     state->setFillColor(&patch->color[0][0]);
1854     state->moveTo(patch->x[0][0], patch->y[0][0]);
1855     state->curveTo(patch->x[0][1], patch->y[0][1],
1856                    patch->x[0][2], patch->y[0][2],
1857                    patch->x[0][3], patch->y[0][3]);
1858     state->curveTo(patch->x[1][3], patch->y[1][3],
1859                    patch->x[2][3], patch->y[2][3],
1860                    patch->x[3][3], patch->y[3][3]);
1861     state->curveTo(patch->x[3][2], patch->y[3][2],
1862                    patch->x[3][1], patch->y[3][1],
1863                    patch->x[3][0], patch->y[3][0]);
1864     state->curveTo(patch->x[2][0], patch->y[2][0],
1865                    patch->x[1][0], patch->y[1][0],
1866                    patch->x[0][0], patch->y[0][0]);
1867     state->closePath();
1868     builder->addPath(state, true, false);
1869     state->clearPath();
1870   } else {
1871     for (i = 0; i < 4; ++i) {
1872       xx[i][0] = patch->x[i][0];
1873       yy[i][0] = patch->y[i][0];
1874       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1875       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1876       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1877       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1878       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1879       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1880       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1881       yy[i][2] = 0.5 * (yy[i][1] + yym);
1882       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1883       yy[i][5] = 0.5 * (yym + yy[i][6]);
1884       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1885       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1886       xx[i][7] = patch->x[i][3];
1887       yy[i][7] = patch->y[i][3];
1888     }
1889     for (i = 0; i < 4; ++i) {
1890       patch00.x[0][i] = xx[0][i];
1891       patch00.y[0][i] = yy[0][i];
1892       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1893       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1894       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1895       yym = 0.5 * (yy[1][i] + yy[2][i]);
1896       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1897       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1898       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1899       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1900       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1901       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1902       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1903       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1904       patch10.x[0][i] = patch00.x[3][i];
1905       patch10.y[0][i] = patch00.y[3][i];
1906       patch10.x[3][i] = xx[3][i];
1907       patch10.y[3][i] = yy[3][i];
1908     }
1909     for (i = 4; i < 8; ++i) {
1910       patch01.x[0][i-4] = xx[0][i];
1911       patch01.y[0][i-4] = yy[0][i];
1912       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1913       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1914       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1915       yym = 0.5 * (yy[1][i] + yy[2][i]);
1916       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1917       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1918       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1919       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1920       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1921       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1922       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1923       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1924       patch11.x[0][i-4] = patch01.x[3][i-4];
1925       patch11.y[0][i-4] = patch01.y[3][i-4];
1926       patch11.x[3][i-4] = xx[3][i];
1927       patch11.y[3][i-4] = yy[3][i];
1928     }
1929     //~ if the shading has a Function, this should interpolate on the
1930     //~ function parameter, not on the color components
1931     for (i = 0; i < nComps; ++i) {
1932       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1933       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1934                                   patch->color[0][1].c[i]) / 2;
1935       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1936       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1937       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1938                                   patch->color[1][1].c[i]) / 2;
1939       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1940       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1941       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1942                                   patch->color[1][0].c[i]) / 2;
1943       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1944       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1945       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1946                                   patch->color[0][0].c[i]) / 2;
1947       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1948       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1949                                   patch01.color[1][1].c[i]) / 2;
1950       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1951       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1952       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1953     }
1954     fillPatch(&patch00, nComps, depth + 1);
1955     fillPatch(&patch10, nComps, depth + 1);
1956     fillPatch(&patch01, nComps, depth + 1);
1957     fillPatch(&patch11, nComps, depth + 1);
1958   }
1961 void PdfParser::doEndPath() {
1962   if (state->isCurPt() && clip != clipNone) {
1963     state->clip();
1964     if (clip == clipNormal) {
1965       clipHistory->setClip(state->getPath(), clipNormal);
1966       builder->clip(state);
1967     } else {
1968       clipHistory->setClip(state->getPath(), clipEO);
1969       builder->clip(state, true);
1970     }
1971   }
1972   clip = clipNone;
1973   state->clearPath();
1976 //------------------------------------------------------------------------
1977 // path clipping operators
1978 //------------------------------------------------------------------------
1980 void PdfParser::opClip(Object args[], int numArgs) {
1981   clip = clipNormal;
1984 void PdfParser::opEOClip(Object args[], int numArgs) {
1985   clip = clipEO;
1988 //------------------------------------------------------------------------
1989 // text object operators
1990 //------------------------------------------------------------------------
1992 void PdfParser::opBeginText(Object args[], int numArgs) {
1993   state->setTextMat(1, 0, 0, 1, 0, 0);
1994   state->textMoveTo(0, 0);
1995   builder->updateTextPosition(0.0, 0.0);
1996   fontChanged = gTrue;
1997   builder->beginTextObject(state);
2000 void PdfParser::opEndText(Object args[], int numArgs) {
2001   builder->endTextObject(state);
2004 //------------------------------------------------------------------------
2005 // text state operators
2006 //------------------------------------------------------------------------
2008 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2009   state->setCharSpace(args[0].getNum());
2012 void PdfParser::opSetFont(Object args[], int numArgs) {
2013   GfxFont *font;
2015   if (!(font = res->lookupFont(args[0].getName()))) {
2016     // unsetting the font (drawing no text) is better than using the
2017     // previous one and drawing random glyphs from it
2018     state->setFont(NULL, args[1].getNum());
2019     fontChanged = gTrue;
2020     return;
2021   }
2022   if (printCommands) {
2023     printf("  font: tag=%s name='%s' %g\n",
2024            font->getTag()->getCString(),
2025            font->getName() ? font->getName()->getCString() : "???",
2026            args[1].getNum());
2027     fflush(stdout);
2028   }
2030   font->incRefCnt();
2031   state->setFont(font, args[1].getNum());
2032   fontChanged = gTrue;
2035 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2036   state->setLeading(args[0].getNum());
2039 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2040   state->setRender(args[0].getInt());
2041   builder->updateStyle(state);
2044 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2045   state->setRise(args[0].getNum());
2048 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2049   state->setWordSpace(args[0].getNum());
2052 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2053   state->setHorizScaling(args[0].getNum());
2054   builder->updateTextMatrix(state);
2055   fontChanged = gTrue;
2058 //------------------------------------------------------------------------
2059 // text positioning operators
2060 //------------------------------------------------------------------------
2062 void PdfParser::opTextMove(Object args[], int numArgs) {
2063   double tx, ty;
2065   tx = state->getLineX() + args[0].getNum();
2066   ty = state->getLineY() + args[1].getNum();
2067   state->textMoveTo(tx, ty);
2068   builder->updateTextPosition(tx, ty);
2071 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2072   double tx, ty;
2074   tx = state->getLineX() + args[0].getNum();
2075   ty = args[1].getNum();
2076   state->setLeading(-ty);
2077   ty += state->getLineY();
2078   state->textMoveTo(tx, ty);
2079   builder->updateTextPosition(tx, ty);
2082 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2083   state->setTextMat(args[0].getNum(), args[1].getNum(),
2084                     args[2].getNum(), args[3].getNum(),
2085                     args[4].getNum(), args[5].getNum());
2086   state->textMoveTo(0, 0);
2087   builder->updateTextMatrix(state);
2088   builder->updateTextPosition(0.0, 0.0);
2089   fontChanged = gTrue;
2092 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2093   double tx, ty;
2095   tx = state->getLineX();
2096   ty = state->getLineY() - state->getLeading();
2097   state->textMoveTo(tx, ty);
2098   builder->updateTextPosition(tx, ty);
2101 //------------------------------------------------------------------------
2102 // text string operators
2103 //------------------------------------------------------------------------
2105 void PdfParser::opShowText(Object args[], int numArgs) {
2106   if (!state->getFont()) {
2107     error(getPos(), const_cast<char*>("No font in show"));
2108     return;
2109   }
2110   if (fontChanged) {
2111     builder->updateFont(state);
2112     fontChanged = gFalse;
2113   }
2114   doShowText(args[0].getString());
2117 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2118   double tx, ty;
2120   if (!state->getFont()) {
2121     error(getPos(), const_cast<char*>("No font in move/show"));
2122     return;
2123   }
2124   if (fontChanged) {
2125     builder->updateFont(state);
2126     fontChanged = gFalse;
2127   }
2128   tx = state->getLineX();
2129   ty = state->getLineY() - state->getLeading();
2130   state->textMoveTo(tx, ty);
2131   builder->updateTextPosition(tx, ty);
2132   doShowText(args[0].getString());
2135 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2136   double tx, ty;
2138   if (!state->getFont()) {
2139     error(getPos(), const_cast<char*>("No font in move/set/show"));
2140     return;
2141   }
2142   if (fontChanged) {
2143     builder->updateFont(state);
2144     fontChanged = gFalse;
2145   }
2146   state->setWordSpace(args[0].getNum());
2147   state->setCharSpace(args[1].getNum());
2148   tx = state->getLineX();
2149   ty = state->getLineY() - state->getLeading();
2150   state->textMoveTo(tx, ty);
2151   builder->updateTextPosition(tx, ty);
2152   doShowText(args[2].getString());
2155 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2156   Array *a;
2157   Object obj;
2158   int wMode;
2159   int i;
2161   if (!state->getFont()) {
2162     error(getPos(), const_cast<char*>("No font in show/space"));
2163     return;
2164   }
2165   if (fontChanged) {
2166     builder->updateFont(state);
2167     fontChanged = gFalse;
2168   }
2169   wMode = state->getFont()->getWMode();
2170   a = args[0].getArray();
2171   for (i = 0; i < a->getLength(); ++i) {
2172     a->get(i, &obj);
2173     if (obj.isNum()) {
2174       // this uses the absolute value of the font size to match
2175       // Acrobat's behavior
2176       if (wMode) {
2177         state->textShift(0, -obj.getNum() * 0.001 *
2178                             fabs(state->getFontSize()));
2179       } else {
2180         state->textShift(-obj.getNum() * 0.001 *
2181                          fabs(state->getFontSize()), 0);
2182       }
2183       builder->updateTextShift(state, obj.getNum());
2184     } else if (obj.isString()) {
2185       doShowText(obj.getString());
2186     } else {
2187       error(getPos(), const_cast<char*>("Element of show/space array must be number or string"));
2188     }
2189     obj.free();
2190   }
2195 /*
2196  * The `POPPLER_NEW_GFXFONT' stuff is for the change to GfxFont's getNextChar() call.
2197  * Thanks to tsdgeos for the fix.
2198  * Miklos, does this look ok?
2199  */   
2201 void PdfParser::doShowText(GooString *s) {
2202   GfxFont *font;
2203   int wMode;
2204   double riseX, riseY;
2205   CharCode code;
2206 #ifdef POPPLER_NEW_GFXFONT
2207   Unicode *u = NULL;
2208 #else
2209   Unicode u[8];
2210 #endif
2211   double x, y, dx, dy, curX, curY, tdx, tdy, lineX, lineY;
2212   double originX, originY, tOriginX, tOriginY;
2213   double oldCTM[6], newCTM[6];
2214   double *mat;
2215   Object charProc;
2216   Dict *resDict;
2217   Parser *oldParser;
2218   char *p;
2219   int len, n, uLen, i;
2221   font = state->getFont();
2222   wMode = font->getWMode();
2224   builder->beginString(state, s);
2226   // handle a Type 3 char
2227   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2228     mat = state->getCTM();
2229     for (i = 0; i < 6; ++i) {
2230       oldCTM[i] = mat[i];
2231     }
2232     mat = state->getTextMat();
2233     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2234     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2235     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2236     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2237     mat = font->getFontMatrix();
2238     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2239     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2240     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2241     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2242     newCTM[0] *= state->getFontSize();
2243     newCTM[1] *= state->getFontSize();
2244     newCTM[2] *= state->getFontSize();
2245     newCTM[3] *= state->getFontSize();
2246     newCTM[0] *= state->getHorizScaling();
2247     newCTM[2] *= state->getHorizScaling();
2248     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2249     curX = state->getCurX();
2250     curY = state->getCurY();
2251     lineX = state->getLineX();
2252     lineY = state->getLineY();
2253     oldParser = parser;
2254     p = s->getCString();
2255     len = s->getLength();
2256     while (len > 0) {
2257       n = font->getNextChar(p, len, &code,
2258 #ifdef POPPLER_NEW_GFXFONT
2259                             &u, &uLen,  /* TODO: This looks like a memory leak for u. */
2260 #else
2261                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2262 #endif
2263                             &dx, &dy, &originX, &originY);
2264       dx = dx * state->getFontSize() + state->getCharSpace();
2265       if (n == 1 && *p == ' ') {
2266         dx += state->getWordSpace();
2267       }
2268       dx *= state->getHorizScaling();
2269       dy *= state->getFontSize();
2270       state->textTransformDelta(dx, dy, &tdx, &tdy);
2271       state->transform(curX + riseX, curY + riseY, &x, &y);
2272       saveState();
2273       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2274       //~ the CTM concat values here are wrong (but never used)
2275       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2276       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2277                                code, u, uLen)) {*/
2278         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2279         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2280           pushResources(resDict);
2281         }
2282         if (charProc.isStream()) {
2283           //parse(&charProc, gFalse); // TODO: parse into SVG font
2284         } else {
2285           error(getPos(), const_cast<char*>("Missing or bad Type3 CharProc entry"));
2286         }
2287         //out->endType3Char(state);
2288         if (resDict) {
2289           popResources();
2290         }
2291         charProc.free();
2292       }
2293       restoreState();
2294       // GfxState::restore() does *not* restore the current position,
2295       // so we deal with it here using (curX, curY) and (lineX, lineY)
2296       curX += tdx;
2297       curY += tdy;
2298       state->moveTo(curX, curY);
2299       state->textSetPos(lineX, lineY);
2300       p += n;
2301       len -= n;
2302     }
2303     parser = oldParser;
2305   } else {
2306     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2307     p = s->getCString();
2308     len = s->getLength();
2309     while (len > 0) {
2310       n = font->getNextChar(p, len, &code,
2311 #ifdef POPPLER_NEW_GFXFONT
2312                             &u, &uLen,  /* TODO: This looks like a memory leak for u. */
2313 #else
2314                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2315 #endif
2316                             &dx, &dy, &originX, &originY);
2317       
2318       if (wMode) {
2319         dx *= state->getFontSize();
2320         dy = dy * state->getFontSize() + state->getCharSpace();
2321         if (n == 1 && *p == ' ') {
2322           dy += state->getWordSpace();
2323         }
2324       } else {
2325         dx = dx * state->getFontSize() + state->getCharSpace();
2326         if (n == 1 && *p == ' ') {
2327           dx += state->getWordSpace();
2328         }
2329         dx *= state->getHorizScaling();
2330         dy *= state->getFontSize();
2331       }
2332       state->textTransformDelta(dx, dy, &tdx, &tdy);
2333       originX *= state->getFontSize();
2334       originY *= state->getFontSize();
2335       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2336       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2337                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2338       state->shift(tdx, tdy);
2339       p += n;
2340       len -= n;
2341     }
2342   }
2344   builder->endString(state);
2348 //------------------------------------------------------------------------
2349 // XObject operators
2350 //------------------------------------------------------------------------
2352 void PdfParser::opXObject(Object args[], int numArgs) {
2353   char *name;
2354   Object obj1, obj2, obj3, refObj;
2356   name = args[0].getName();
2357   if (!res->lookupXObject(name, &obj1)) {
2358     return;
2359   }
2360   if (!obj1.isStream()) {
2361     error(getPos(), const_cast<char*>("XObject '%s' is wrong type"), name);
2362     obj1.free();
2363     return;
2364   }
2365   obj1.streamGetDict()->lookup(const_cast<char*>("Subtype"), &obj2);
2366   if (obj2.isName(const_cast<char*>("Image"))) {
2367     res->lookupXObjectNF(name, &refObj);
2368     doImage(&refObj, obj1.getStream(), gFalse);
2369     refObj.free();
2370   } else if (obj2.isName(const_cast<char*>("Form"))) {
2371     doForm(&obj1);
2372   } else if (obj2.isName(const_cast<char*>("PS"))) {
2373     obj1.streamGetDict()->lookup(const_cast<char*>("Level1"), &obj3);
2374 /*    out->psXObject(obj1.getStream(),
2375                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2376   } else if (obj2.isName()) {
2377     error(getPos(), const_cast<char*>("Unknown XObject subtype '%s'"), obj2.getName());
2378   } else {
2379     error(getPos(), const_cast<char*>("XObject subtype is missing or wrong type"));
2380   }
2381   obj2.free();
2382   obj1.free();
2385 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2386   Dict *dict, *maskDict;
2387   int width, height;
2388   int bits, maskBits;
2389   StreamColorSpaceMode csMode;
2390   GBool mask;
2391   GBool invert;
2392   GfxColorSpace *colorSpace, *maskColorSpace;
2393   GfxImageColorMap *colorMap, *maskColorMap;
2394   Object maskObj, smaskObj;
2395   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2396   int maskColors[2*gfxColorMaxComps];
2397   int maskWidth, maskHeight;
2398   GBool maskInvert;
2399   Stream *maskStr;
2400   Object obj1, obj2;
2401   int i;
2403   // get info from the stream
2404   bits = 0;
2405   csMode = streamCSNone;
2406   str->getImageParams(&bits, &csMode);
2408   // get stream dict
2409   dict = str->getDict();
2411   // get size
2412   dict->lookup(const_cast<char*>("Width"), &obj1);
2413   if (obj1.isNull()) {
2414     obj1.free();
2415     dict->lookup(const_cast<char*>("W"), &obj1);
2416   }
2417   if (obj1.isInt())
2418     width = obj1.getInt();
2419   else if (obj1.isReal())
2420     width = (int)obj1.getReal();
2421   else
2422     goto err2;
2423   obj1.free();
2424   dict->lookup(const_cast<char*>("Height"), &obj1);
2425   if (obj1.isNull()) {
2426     obj1.free();
2427     dict->lookup(const_cast<char*>("H"), &obj1);
2428   }
2429   if (obj1.isInt())
2430     height = obj1.getInt();
2431   else if (obj1.isReal())
2432     height = (int)obj1.getReal();
2433   else
2434     goto err2;
2435   obj1.free();
2437   // image or mask?
2438   dict->lookup(const_cast<char*>("ImageMask"), &obj1);
2439   if (obj1.isNull()) {
2440     obj1.free();
2441     dict->lookup(const_cast<char*>("IM"), &obj1);
2442   }
2443   mask = gFalse;
2444   if (obj1.isBool())
2445     mask = obj1.getBool();
2446   else if (!obj1.isNull())
2447     goto err2;
2448   obj1.free();
2450   // bit depth
2451   if (bits == 0) {
2452     dict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2453     if (obj1.isNull()) {
2454       obj1.free();
2455       dict->lookup(const_cast<char*>("BPC"), &obj1);
2456     }
2457     if (obj1.isInt()) {
2458       bits = obj1.getInt();
2459     } else if (mask) {
2460       bits = 1;
2461     } else {
2462       goto err2;
2463     }
2464     obj1.free();
2465   }
2467   // display a mask
2468   if (mask) {
2470     // check for inverted mask
2471     if (bits != 1)
2472       goto err1;
2473     invert = gFalse;
2474     dict->lookup(const_cast<char*>("Decode"), &obj1);
2475     if (obj1.isNull()) {
2476       obj1.free();
2477       dict->lookup(const_cast<char*>("D"), &obj1);
2478     }
2479     if (obj1.isArray()) {
2480       obj1.arrayGet(0, &obj2);
2481       if (obj2.isInt() && obj2.getInt() == 1)
2482         invert = gTrue;
2483       obj2.free();
2484     } else if (!obj1.isNull()) {
2485       goto err2;
2486     }
2487     obj1.free();
2489     // draw it
2490     builder->addImageMask(state, str, width, height, invert);
2492   } else {
2494     // get color space and color map
2495     dict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2496     if (obj1.isNull()) {
2497       obj1.free();
2498       dict->lookup(const_cast<char*>("CS"), &obj1);
2499     }
2500     if (obj1.isName()) {
2501       res->lookupColorSpace(obj1.getName(), &obj2);
2502       if (!obj2.isNull()) {
2503         obj1.free();
2504         obj1 = obj2;
2505       } else {
2506         obj2.free();
2507       }
2508     }
2509     if (!obj1.isNull()) {
2510       colorSpace = GfxColorSpace::parse(&obj1);
2511     } else if (csMode == streamCSDeviceGray) {
2512       colorSpace = new GfxDeviceGrayColorSpace();
2513     } else if (csMode == streamCSDeviceRGB) {
2514       colorSpace = new GfxDeviceRGBColorSpace();
2515     } else if (csMode == streamCSDeviceCMYK) {
2516       colorSpace = new GfxDeviceCMYKColorSpace();
2517     } else {
2518       colorSpace = NULL;
2519     }
2520     obj1.free();
2521     if (!colorSpace) {
2522       goto err1;
2523     }
2524     dict->lookup(const_cast<char*>("Decode"), &obj1);
2525     if (obj1.isNull()) {
2526       obj1.free();
2527       dict->lookup(const_cast<char*>("D"), &obj1);
2528     }
2529     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2530     obj1.free();
2531     if (!colorMap->isOk()) {
2532       delete colorMap;
2533       goto err1;
2534     }
2536     // get the mask
2537     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2538     maskStr = NULL; // make gcc happy
2539     maskWidth = maskHeight = 0; // make gcc happy
2540     maskInvert = gFalse; // make gcc happy
2541     maskColorMap = NULL; // make gcc happy
2542     dict->lookup(const_cast<char*>("Mask"), &maskObj);
2543     dict->lookup(const_cast<char*>("SMask"), &smaskObj);
2544     if (smaskObj.isStream()) {
2545       // soft mask
2546       if (inlineImg) {
2547         goto err1;
2548       }
2549       maskStr = smaskObj.getStream();
2550       maskDict = smaskObj.streamGetDict();
2551       maskDict->lookup(const_cast<char*>("Width"), &obj1);
2552       if (obj1.isNull()) {
2553         obj1.free();
2554         maskDict->lookup(const_cast<char*>("W"), &obj1);
2555       }
2556       if (!obj1.isInt()) {
2557         goto err2;
2558       }
2559       maskWidth = obj1.getInt();
2560       obj1.free();
2561       maskDict->lookup(const_cast<char*>("Height"), &obj1);
2562       if (obj1.isNull()) {
2563         obj1.free();
2564         maskDict->lookup(const_cast<char*>("H"), &obj1);
2565       }
2566       if (!obj1.isInt()) {
2567         goto err2;
2568       }
2569       maskHeight = obj1.getInt();
2570       obj1.free();
2571       maskDict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2572       if (obj1.isNull()) {
2573         obj1.free();
2574         maskDict->lookup(const_cast<char*>("BPC"), &obj1);
2575       }
2576       if (!obj1.isInt()) {
2577         goto err2;
2578       }
2579       maskBits = obj1.getInt();
2580       obj1.free();
2581       maskDict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2582       if (obj1.isNull()) {
2583         obj1.free();
2584         maskDict->lookup(const_cast<char*>("CS"), &obj1);
2585       }
2586       if (obj1.isName()) {
2587         res->lookupColorSpace(obj1.getName(), &obj2);
2588         if (!obj2.isNull()) {
2589           obj1.free();
2590           obj1 = obj2;
2591         } else {
2592           obj2.free();
2593         }
2594       }
2595       maskColorSpace = GfxColorSpace::parse(&obj1);
2596       obj1.free();
2597       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2598         goto err1;
2599       }
2600       maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2601       if (obj1.isNull()) {
2602         obj1.free();
2603         maskDict->lookup(const_cast<char*>("D"), &obj1);
2604       }
2605       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2606       obj1.free();
2607       if (!maskColorMap->isOk()) {
2608         delete maskColorMap;
2609         goto err1;
2610       }
2611       //~ handle the Matte entry
2612       haveSoftMask = gTrue;
2613     } else if (maskObj.isArray()) {
2614       // color key mask
2615       for (i = 0;
2616            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2617            ++i) {
2618         maskObj.arrayGet(i, &obj1);
2619         maskColors[i] = obj1.getInt();
2620         obj1.free();
2621       }
2622       haveColorKeyMask = gTrue;
2623     } else if (maskObj.isStream()) {
2624       // explicit mask
2625       if (inlineImg) {
2626         goto err1;
2627       }
2628       maskStr = maskObj.getStream();
2629       maskDict = maskObj.streamGetDict();
2630       maskDict->lookup(const_cast<char*>("Width"), &obj1);
2631       if (obj1.isNull()) {
2632         obj1.free();
2633         maskDict->lookup(const_cast<char*>("W"), &obj1);
2634       }
2635       if (!obj1.isInt()) {
2636         goto err2;
2637       }
2638       maskWidth = obj1.getInt();
2639       obj1.free();
2640       maskDict->lookup(const_cast<char*>("Height"), &obj1);
2641       if (obj1.isNull()) {
2642         obj1.free();
2643         maskDict->lookup(const_cast<char*>("H"), &obj1);
2644       }
2645       if (!obj1.isInt()) {
2646         goto err2;
2647       }
2648       maskHeight = obj1.getInt();
2649       obj1.free();
2650       maskDict->lookup(const_cast<char*>("ImageMask"), &obj1);
2651       if (obj1.isNull()) {
2652         obj1.free();
2653         maskDict->lookup(const_cast<char*>("IM"), &obj1);
2654       }
2655       if (!obj1.isBool() || !obj1.getBool()) {
2656         goto err2;
2657       }
2658       obj1.free();
2659       maskInvert = gFalse;
2660       maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2661       if (obj1.isNull()) {
2662         obj1.free();
2663         maskDict->lookup(const_cast<char*>("D"), &obj1);
2664       }
2665       if (obj1.isArray()) {
2666         obj1.arrayGet(0, &obj2);
2667         if (obj2.isInt() && obj2.getInt() == 1) {
2668           maskInvert = gTrue;
2669         }
2670         obj2.free();
2671       } else if (!obj1.isNull()) {
2672         goto err2;
2673       }
2674       obj1.free();
2675       haveExplicitMask = gTrue;
2676     }
2678     // draw it
2679     if (haveSoftMask) {
2680         builder->addSoftMaskedImage(state, str, width, height, colorMap,
2681                                     maskStr, maskWidth, maskHeight, maskColorMap);
2682       delete maskColorMap;
2683     } else if (haveExplicitMask) {
2684         builder->addMaskedImage(state, str, width, height, colorMap,
2685                                 maskStr, maskWidth, maskHeight, maskInvert);
2686     } else {
2687       builder->addImage(state, str, width, height, colorMap,
2688                         haveColorKeyMask ? maskColors : (int *)NULL);
2689     }
2690     delete colorMap;
2692     maskObj.free();
2693     smaskObj.free();
2694   }
2696   return;
2698  err2:
2699   obj1.free();
2700  err1:
2701   error(getPos(), const_cast<char*>("Bad image parameters"));
2704 void PdfParser::doForm(Object *str) {
2705   Dict *dict;
2706   GBool transpGroup, isolated, knockout;
2707   GfxColorSpace *blendingColorSpace;
2708   Object matrixObj, bboxObj;
2709   double m[6], bbox[4];
2710   Object resObj;
2711   Dict *resDict;
2712   Object obj1, obj2, obj3;
2713   int i;
2715   // check for excessive recursion
2716   if (formDepth > 20) {
2717     return;
2718   }
2720   // get stream dict
2721   dict = str->streamGetDict();
2723   // check form type
2724   dict->lookup(const_cast<char*>("FormType"), &obj1);
2725   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2726     error(getPos(), const_cast<char*>("Unknown form type"));
2727   }
2728   obj1.free();
2730   // get bounding box
2731   dict->lookup(const_cast<char*>("BBox"), &bboxObj);
2732   if (!bboxObj.isArray()) {
2733     bboxObj.free();
2734     error(getPos(), const_cast<char*>("Bad form bounding box"));
2735     return;
2736   }
2737   for (i = 0; i < 4; ++i) {
2738     bboxObj.arrayGet(i, &obj1);
2739     bbox[i] = obj1.getNum();
2740     obj1.free();
2741   }
2742   bboxObj.free();
2744   // get matrix
2745   dict->lookup(const_cast<char*>("Matrix"), &matrixObj);
2746   if (matrixObj.isArray()) {
2747     for (i = 0; i < 6; ++i) {
2748       matrixObj.arrayGet(i, &obj1);
2749       m[i] = obj1.getNum();
2750       obj1.free();
2751     }
2752   } else {
2753     m[0] = 1; m[1] = 0;
2754     m[2] = 0; m[3] = 1;
2755     m[4] = 0; m[5] = 0;
2756   }
2757   matrixObj.free();
2759   // get resources
2760   dict->lookup(const_cast<char*>("Resources"), &resObj);
2761   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2763   // check for a transparency group
2764   transpGroup = isolated = knockout = gFalse;
2765   blendingColorSpace = NULL;
2766   if (dict->lookup(const_cast<char*>("Group"), &obj1)->isDict()) {
2767     if (obj1.dictLookup(const_cast<char*>("S"), &obj2)->isName(const_cast<char*>("Transparency"))) {
2768       transpGroup = gTrue;
2769       if (!obj1.dictLookup(const_cast<char*>("CS"), &obj3)->isNull()) {
2770         blendingColorSpace = GfxColorSpace::parse(&obj3);
2771       }
2772       obj3.free();
2773       if (obj1.dictLookup(const_cast<char*>("I"), &obj3)->isBool()) {
2774         isolated = obj3.getBool();
2775       }
2776       obj3.free();
2777       if (obj1.dictLookup(const_cast<char*>("K"), &obj3)->isBool()) {
2778         knockout = obj3.getBool();
2779       }
2780       obj3.free();
2781     }
2782     obj2.free();
2783   }
2784   obj1.free();
2786   // draw it
2787   ++formDepth;
2788   doForm1(str, resDict, m, bbox,
2789           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2790   --formDepth;
2792   if (blendingColorSpace) {
2793     delete blendingColorSpace;
2794   }
2795   resObj.free();
2798 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2799                   GBool transpGroup, GBool softMask,
2800                   GfxColorSpace *blendingColorSpace,
2801                   GBool isolated, GBool knockout,
2802                   GBool alpha, Function *transferFunc,
2803                   GfxColor *backdropColor) {
2804   Parser *oldParser;
2805   double oldBaseMatrix[6];
2806   int i;
2808   // push new resources on stack
2809   pushResources(resDict);
2811   // save current graphics state
2812   saveState();
2814   // kill any pre-existing path
2815   state->clearPath();
2817   if (softMask || transpGroup) {
2818     builder->clearSoftMask(state);
2819     builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2820                                    isolated, knockout, softMask);
2821   }
2823   // save current parser
2824   oldParser = parser;
2826   // set form transformation matrix
2827   state->concatCTM(matrix[0], matrix[1], matrix[2],
2828                    matrix[3], matrix[4], matrix[5]);
2829   builder->setTransform(matrix[0], matrix[1], matrix[2],
2830                         matrix[3], matrix[4], matrix[5]);
2832   // set form bounding box
2833   state->moveTo(bbox[0], bbox[1]);
2834   state->lineTo(bbox[2], bbox[1]);
2835   state->lineTo(bbox[2], bbox[3]);
2836   state->lineTo(bbox[0], bbox[3]);
2837   state->closePath();
2838   state->clip();
2839   clipHistory->setClip(state->getPath());
2840   builder->clip(state);
2841   state->clearPath();
2843   if (softMask || transpGroup) {
2844     if (state->getBlendMode() != gfxBlendNormal) {
2845       state->setBlendMode(gfxBlendNormal);
2846     }
2847     if (state->getFillOpacity() != 1) {
2848       builder->setGroupOpacity(state->getFillOpacity());
2849       state->setFillOpacity(1);
2850     }
2851     if (state->getStrokeOpacity() != 1) {
2852       state->setStrokeOpacity(1);
2853     }
2854   }
2856   // set new base matrix
2857   for (i = 0; i < 6; ++i) {
2858     oldBaseMatrix[i] = baseMatrix[i];
2859     baseMatrix[i] = state->getCTM()[i];
2860   }
2862   // draw the form
2863   parse(str, gFalse);
2865   // restore base matrix
2866   for (i = 0; i < 6; ++i) {
2867     baseMatrix[i] = oldBaseMatrix[i];
2868   }
2870   // restore parser
2871   parser = oldParser;
2873   if (softMask || transpGroup) {
2874       builder->popTransparencyGroup(state);
2875   }
2877   // restore graphics state
2878   restoreState();
2880   // pop resource stack
2881   popResources();
2883   if (softMask) {
2884     builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2885   } else if (transpGroup) {
2886     builder->paintTransparencyGroup(state, bbox);
2887   }
2889   return;
2892 //------------------------------------------------------------------------
2893 // in-line image operators
2894 //------------------------------------------------------------------------
2896 void PdfParser::opBeginImage(Object args[], int numArgs) {
2897   Stream *str;
2898   int c1, c2;
2900   // build dict/stream
2901   str = buildImageStream();
2903   // display the image
2904   if (str) {
2905     doImage(NULL, str, gTrue);
2906   
2907     // skip 'EI' tag
2908     c1 = str->getUndecodedStream()->getChar();
2909     c2 = str->getUndecodedStream()->getChar();
2910     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2911       c1 = c2;
2912       c2 = str->getUndecodedStream()->getChar();
2913     }
2914     delete str;
2915   }
2918 Stream *PdfParser::buildImageStream() {
2919   Object dict;
2920   Object obj;
2921   char *key;
2922   Stream *str;
2924   // build dictionary
2925   dict.initDict(xref);
2926   parser->getObj(&obj);
2927   while (!obj.isCmd(const_cast<char*>("ID")) && !obj.isEOF()) {
2928     if (!obj.isName()) {
2929       error(getPos(), const_cast<char*>("Inline image dictionary key must be a name object"));
2930       obj.free();
2931     } else {
2932       key = copyString(obj.getName());
2933       obj.free();
2934       parser->getObj(&obj);
2935       if (obj.isEOF() || obj.isError()) {
2936         gfree(key);
2937         break;
2938       }
2939       dict.dictAdd(key, &obj);
2940     }
2941     parser->getObj(&obj);
2942   }
2943   if (obj.isEOF()) {
2944     error(getPos(), const_cast<char*>("End of file in inline image"));
2945     obj.free();
2946     dict.free();
2947     return NULL;
2948   }
2949   obj.free();
2951   // make stream
2952   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2953   str = str->addFilters(&dict);
2955   return str;
2958 void PdfParser::opImageData(Object args[], int numArgs) {
2959   error(getPos(), const_cast<char*>("Internal: got 'ID' operator"));
2962 void PdfParser::opEndImage(Object args[], int numArgs) {
2963   error(getPos(), const_cast<char*>("Internal: got 'EI' operator"));
2966 //------------------------------------------------------------------------
2967 // type 3 font operators
2968 //------------------------------------------------------------------------
2970 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2973 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2976 //------------------------------------------------------------------------
2977 // compatibility operators
2978 //------------------------------------------------------------------------
2980 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2981   ++ignoreUndef;
2984 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2985   if (ignoreUndef > 0)
2986     --ignoreUndef;
2989 //------------------------------------------------------------------------
2990 // marked content operators
2991 //------------------------------------------------------------------------
2993 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2994   if (printCommands) {
2995     printf("  marked content: %s ", args[0].getName());
2996     if (numArgs == 2)
2997       args[2].print(stdout);
2998     printf("\n");
2999     fflush(stdout);
3000   }
3002   if(numArgs == 2) {
3003     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
3004   } else {
3005     //out->beginMarkedContent(args[0].getName());
3006   }
3009 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3010   //out->endMarkedContent();
3013 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3014   if (printCommands) {
3015     printf("  mark point: %s ", args[0].getName());
3016     if (numArgs == 2)
3017       args[2].print(stdout);
3018     printf("\n");
3019     fflush(stdout);
3020   }
3022   if(numArgs == 2) {
3023     //out->markPoint(args[0].getName(),args[1].getDict());
3024   } else {
3025     //out->markPoint(args[0].getName());
3026   }
3030 //------------------------------------------------------------------------
3031 // misc
3032 //------------------------------------------------------------------------
3034 void PdfParser::saveState() {
3035   builder->saveState();
3036   state = state->save();
3037   clipHistory = clipHistory->save();
3040 void PdfParser::restoreState() {
3041   clipHistory = clipHistory->restore();
3042   state = state->restore();
3043   builder->restoreState();
3046 void PdfParser::pushResources(Dict *resDict) {
3047   res = new GfxResources(xref, resDict, res);
3050 void PdfParser::popResources() {
3051   GfxResources *resPtr;
3053   resPtr = res->getNext();
3054   delete res;
3055   res = resPtr;
3058 void PdfParser::setDefaultApproximationPrecision() {
3059   int i;
3061   for (i = 1; i <= pdfNumShadingTypes; ++i) {
3062     setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3063   }
3066 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3067                                           int maxDepth) {
3069   if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3070     return;
3071   }
3072   colorDeltas[shadingType-1] = dblToCol(colorDelta);
3073   maxDepths[shadingType-1] = maxDepth;
3076 //------------------------------------------------------------------------
3077 // ClipHistoryEntry
3078 //------------------------------------------------------------------------
3080 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3081     if (clipPathA) {
3082         clipPath = clipPathA->copy();
3083     } else {
3084         clipPath = NULL;
3085     }
3086     clipType = clipTypeA;
3087     saved = NULL;
3090 ClipHistoryEntry::~ClipHistoryEntry() {
3091     if (clipPath) {
3092         delete clipPath;
3093     }
3096 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3097     // Free previous clip path
3098     if (clipPath) {
3099         delete clipPath;
3100     }
3101     if (clipPathA) {
3102         clipPath = clipPathA->copy();
3103         clipType = clipTypeA;
3104     } else {
3105         clipPath = NULL;
3106     }
3109 ClipHistoryEntry *ClipHistoryEntry::save() {
3110     ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3111     newEntry->saved = this;
3113     return newEntry;
3116 ClipHistoryEntry *ClipHistoryEntry::restore() {
3117     ClipHistoryEntry *oldEntry;
3119     if (saved) {
3120         oldEntry = saved;
3121         saved = NULL;
3122         delete this;
3123     } else {
3124         oldEntry = this;
3125     }
3127     return oldEntry;
3130 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3131     if (other->clipPath) {
3132         this->clipPath = other->clipPath->copy();
3133         this->clipType = other->clipType;
3134     } else {
3135         this->clipPath = NULL;
3136     }
3137     saved = NULL;
3140 #endif /* HAVE_POPPLER */