Code

Require a pageBox in the constructor for GfxState so that we do not always need to...
[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, "Weird page contents");
371         obj2.free();
372         return;
373       }
374       obj2.free();
375     }
376   } else if (!obj->isStream()) {
377     error(-1, "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;
390   int lastAbortCheck;
392   // scan a sequence of objects
393   numArgs = 0;
394   parser->getObj(&obj);
395   while (!obj.isEOF()) {
397     // got a command - execute it
398     if (obj.isCmd()) {
399       if (printCommands) {
400         obj.print(stdout);
401         for (i = 0; i < numArgs; ++i) {
402           printf(" ");
403           args[i].print(stdout);
404         }
405         printf("\n");
406         fflush(stdout);
407       }
409       // Run the operation
410       execOp(&obj, args, numArgs);
412       obj.free();
413       for (i = 0; i < numArgs; ++i)
414         args[i].free();
415       numArgs = 0;
417     // got an argument - save it
418     } else if (numArgs < maxArgs) {
419       args[numArgs++] = obj;
421     // too many arguments - something is wrong
422     } else {
423       error(getPos(), "Too many args in content stream");
424       if (printCommands) {
425         printf("throwing away arg: ");
426         obj.print(stdout);
427         printf("\n");
428         fflush(stdout);
429       }
430       obj.free();
431     }
433     // grab the next object
434     parser->getObj(&obj);
435   }
436   obj.free();
438   // args at end with no command
439   if (numArgs > 0) {
440     error(getPos(), "Leftover args in content stream");
441     if (printCommands) {
442       printf("%d leftovers:", numArgs);
443       for (i = 0; i < numArgs; ++i) {
444         printf(" ");
445         args[i].print(stdout);
446       }
447       printf("\n");
448       fflush(stdout);
449     }
450     for (i = 0; i < numArgs; ++i)
451       args[i].free();
452   }
455 void PdfParser::pushOperator(const char *name) {
456     OpHistoryEntry *newEntry = new OpHistoryEntry;
457     newEntry->name = name;
458     newEntry->state = NULL;
459     newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
460     newEntry->next = operatorHistory;
461     operatorHistory = newEntry;
463     // Truncate list if needed
464     if (operatorHistory->depth > maxOperatorHistoryDepth) {
465         OpHistoryEntry *curr = operatorHistory;
466         OpHistoryEntry *prev = NULL;
467         while (curr && curr->next != NULL) {
468             curr->depth--;
469             prev = curr;
470             curr = curr->next;
471         }
472         if (prev) {
473             if (curr->state != NULL)
474                 delete curr->state;
475             delete curr;
476             prev->next = NULL;
477         }
478     }
481 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
482     OpHistoryEntry *prev = NULL;
483     if (operatorHistory != NULL && look_back > 0) {
484         prev = operatorHistory->next;
485         while (--look_back > 0 && prev != NULL) {
486             prev = prev->next;
487         }
488     }
489     if (prev != NULL) {
490         return prev->name;
491     } else {
492         return "";
493     }
496 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
497   PdfOperator *op;
498   char *name;
499   Object *argPtr;
500   int i;
502   // find operator
503   name = cmd->getCmd();
504   if (!(op = findOp(name))) {
505     if (ignoreUndef == 0)
506       error(getPos(), "Unknown operator '%s'", name);
507     return;
508   }
510   // type check args
511   argPtr = args;
512   if (op->numArgs >= 0) {
513     if (numArgs < op->numArgs) {
514       error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
515       return;
516     }
517     if (numArgs > op->numArgs) {
518 #if 0
519       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
520 #endif
521       argPtr += numArgs - op->numArgs;
522       numArgs = op->numArgs;
523     }
524   } else {
525     if (numArgs > -op->numArgs) {
526       error(getPos(), "Too many (%d) args to '%s' operator",
527             numArgs, name);
528       return;
529     }
530   }
531   for (i = 0; i < numArgs; ++i) {
532     if (!checkArg(&argPtr[i], op->tchk[i])) {
533       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
534             i, name, argPtr[i].getTypeName());
535       return;
536     }
537   }
539   // add to history
540   pushOperator((char*)&op->name);
542   // do it
543   (this->*op->func)(argPtr, numArgs);
546 PdfOperator *PdfParser::findOp(char *name) {
547   int a, b, m, cmp;
549   a = -1;
550   b = numOps;
551   // invariant: opTab[a] < name < opTab[b]
552   while (b - a > 1) {
553     m = (a + b) / 2;
554     cmp = strcmp(opTab[m].name, name);
555     if (cmp < 0)
556       a = m;
557     else if (cmp > 0)
558       b = m;
559     else
560       a = b = m;
561   }
562   if (cmp != 0)
563     return NULL;
564   return &opTab[a];
567 GBool PdfParser::checkArg(Object *arg, TchkType type) {
568   switch (type) {
569   case tchkBool:   return arg->isBool();
570   case tchkInt:    return arg->isInt();
571   case tchkNum:    return arg->isNum();
572   case tchkString: return arg->isString();
573   case tchkName:   return arg->isName();
574   case tchkArray:  return arg->isArray();
575   case tchkProps:  return arg->isDict() || arg->isName();
576   case tchkSCN:    return arg->isNum() || arg->isName();
577   case tchkNone:   return gFalse;
578   }
579   return gFalse;
582 int PdfParser::getPos() {
583   return parser ? parser->getPos() : -1;
586 //------------------------------------------------------------------------
587 // graphics state operators
588 //------------------------------------------------------------------------
590 void PdfParser::opSave(Object args[], int numArgs) {
591   saveState();
594 void PdfParser::opRestore(Object args[], int numArgs) {
595   restoreState();
598 void PdfParser::opConcat(Object args[], int numArgs) {
599   state->concatCTM(args[0].getNum(), args[1].getNum(),
600                    args[2].getNum(), args[3].getNum(),
601                    args[4].getNum(), args[5].getNum());
602   const char *prevOp = getPreviousOperator();
603   double a0 = args[0].getNum();
604   double a1 = args[1].getNum();
605   double a2 = args[2].getNum();
606   double a3 = args[3].getNum();
607   double a4 = args[4].getNum();
608   double a5 = args[5].getNum();
609   if (!strcmp(prevOp, "q")) {
610       builder->setTransform(a0, a1, a2, a3, a4, a5);
611   } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
612       // multiply it with the previous transform
613       double otherMatrix[6];
614       if (!builder->getTransform((double*)&otherMatrix)) { // invalid transform
615           // construct identity matrix
616           otherMatrix[0] = otherMatrix[3] = 1.0;
617           otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
618       }
619       double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
620       double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
621       double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
622       double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
623       double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
624       double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
625       builder->setTransform(c0, c1, c2, c3, c4, c5);
626   } else {
627       builder->pushGroup();
628       builder->setTransform(a0, a1, a2, a3, a4, a5);
629   }
630   fontChanged = gTrue;
633 void PdfParser::opSetDash(Object args[], int numArgs) {
634   Array *a;
635   int length;
636   Object obj;
637   double *dash;
638   int i;
640   a = args[0].getArray();
641   length = a->getLength();
642   if (length == 0) {
643     dash = NULL;
644   } else {
645     dash = (double *)gmallocn(length, sizeof(double));
646     for (i = 0; i < length; ++i) {
647       dash[i] = a->get(i, &obj)->getNum();
648       obj.free();
649     }
650   }
651   state->setLineDash(dash, length, args[1].getNum());
652   builder->updateStyle(state);
655 void PdfParser::opSetFlat(Object args[], int numArgs) {
656   state->setFlatness((int)args[0].getNum());
659 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
660   state->setLineJoin(args[0].getInt());
661   builder->updateStyle(state);
664 void PdfParser::opSetLineCap(Object args[], int numArgs) {
665   state->setLineCap(args[0].getInt());
666   builder->updateStyle(state);
669 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
670   state->setMiterLimit(args[0].getNum());
671   builder->updateStyle(state);
674 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
675   state->setLineWidth(args[0].getNum());
676   builder->updateStyle(state);
679 void PdfParser::opSetExtGState(Object args[], int numArgs) {
680   Object obj1, obj2, obj3, obj4, obj5;
681   GfxBlendMode mode;
682   GBool haveFillOP;
683   Function *funcs[4];
684   GfxColor backdropColor;
685   GBool haveBackdropColor;
686   GfxColorSpace *blendingColorSpace;
687   GBool alpha, isolated, knockout;
688   int i;
690   if (!res->lookupGState(args[0].getName(), &obj1)) {
691     return;
692   }
693   if (!obj1.isDict()) {
694     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
695     obj1.free();
696     return;
697   }
698   if (printCommands) {
699     printf("  gfx state dict: ");
700     obj1.print();
701     printf("\n");
702   }
704   // transparency support: blend mode, fill/stroke opacity
705   if (!obj1.dictLookup("BM", &obj2)->isNull()) {
706     if (state->parseBlendMode(&obj2, &mode)) {
707       state->setBlendMode(mode);
708     } else {
709       error(getPos(), "Invalid blend mode in ExtGState");
710     }
711   }
712   obj2.free();
713   if (obj1.dictLookup("ca", &obj2)->isNum()) {
714     state->setFillOpacity(obj2.getNum());
715   }
716   obj2.free();
717   if (obj1.dictLookup("CA", &obj2)->isNum()) {
718     state->setStrokeOpacity(obj2.getNum());
719   }
720   obj2.free();
722   // fill/stroke overprint
723   if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
724     state->setFillOverprint(obj2.getBool());
725   }
726   obj2.free();
727   if (obj1.dictLookup("OP", &obj2)->isBool()) {
728     state->setStrokeOverprint(obj2.getBool());
729     if (!haveFillOP) {
730       state->setFillOverprint(obj2.getBool());
731     }
732   }
733   obj2.free();
735   // stroke adjust
736   if (obj1.dictLookup("SA", &obj2)->isBool()) {
737     state->setStrokeAdjust(obj2.getBool());
738   }
739   obj2.free();
741   // transfer function
742   if (obj1.dictLookup("TR2", &obj2)->isNull()) {
743     obj2.free();
744     obj1.dictLookup("TR", &obj2);
745   }
746   if (obj2.isName("Default") ||
747       obj2.isName("Identity")) {
748     funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
749     state->setTransfer(funcs);
750   } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
751     for (i = 0; i < 4; ++i) {
752       obj2.arrayGet(i, &obj3);
753       funcs[i] = Function::parse(&obj3);
754       obj3.free();
755       if (!funcs[i]) {
756         break;
757       }
758     }
759     if (i == 4) {
760       state->setTransfer(funcs);
761     }
762   } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
763     if ((funcs[0] = Function::parse(&obj2))) {
764       funcs[1] = funcs[2] = funcs[3] = NULL;
765       state->setTransfer(funcs);
766     }
767   } else if (!obj2.isNull()) {
768     error(getPos(), "Invalid transfer function in ExtGState");
769   }
770   obj2.free();
772   // soft mask
773   if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
774     if (obj2.isName("None")) {
775       builder->clearSoftMask(state);
776     } else if (obj2.isDict()) {
777       if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
778         alpha = gTrue;
779       } else { // "Luminosity"
780         alpha = gFalse;
781       }
782       obj3.free();
783       funcs[0] = NULL;
784       if (!obj2.dictLookup("TR", &obj3)->isNull()) {
785         funcs[0] = Function::parse(&obj3);
786         if (funcs[0]->getInputSize() != 1 ||
787             funcs[0]->getOutputSize() != 1) {
788           error(getPos(),
789                 "Invalid transfer function in soft mask in ExtGState");
790           delete funcs[0];
791           funcs[0] = NULL;
792         }
793       }
794       obj3.free();
795       if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
796         for (i = 0; i < gfxColorMaxComps; ++i) {
797           backdropColor.c[i] = 0;
798         }
799         for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
800           obj3.arrayGet(i, &obj4);
801           if (obj4.isNum()) {
802             backdropColor.c[i] = dblToCol(obj4.getNum());
803           }
804           obj4.free();
805         }
806       }
807       obj3.free();
808       if (obj2.dictLookup("G", &obj3)->isStream()) {
809         if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
810           blendingColorSpace = NULL;
811           isolated = knockout = gFalse;
812           if (!obj4.dictLookup("CS", &obj5)->isNull()) {
813             blendingColorSpace = GfxColorSpace::parse(&obj5);
814           }
815           obj5.free();
816           if (obj4.dictLookup("I", &obj5)->isBool()) {
817             isolated = obj5.getBool();
818           }
819           obj5.free();
820           if (obj4.dictLookup("K", &obj5)->isBool()) {
821             knockout = obj5.getBool();
822           }
823           obj5.free();
824           if (!haveBackdropColor) {
825             if (blendingColorSpace) {
826               blendingColorSpace->getDefaultColor(&backdropColor);
827             } else {
828               //~ need to get the parent or default color space (?)
829               for (i = 0; i < gfxColorMaxComps; ++i) {
830                 backdropColor.c[i] = 0;
831               }
832             }
833           }
834           doSoftMask(&obj3, alpha, blendingColorSpace,
835                      isolated, knockout, funcs[0], &backdropColor);
836           if (funcs[0]) {
837             delete funcs[0];
838           }
839         } else {
840           error(getPos(), "Invalid soft mask in ExtGState - missing group");
841         }
842         obj4.free();
843       } else {
844         error(getPos(), "Invalid soft mask in ExtGState - missing group");
845       }
846       obj3.free();
847     } else if (!obj2.isNull()) {
848       error(getPos(), "Invalid soft mask in ExtGState");
849     }
850   }
851   obj2.free();
853   obj1.free();
856 void PdfParser::doSoftMask(Object *str, GBool alpha,
857                      GfxColorSpace *blendingColorSpace,
858                      GBool isolated, GBool knockout,
859                      Function *transferFunc, GfxColor *backdropColor) {
860   Dict *dict, *resDict;
861   double m[6], bbox[4];
862   Object obj1, obj2;
863   int i;
865   // check for excessive recursion
866   if (formDepth > 20) {
867     return;
868   }
870   // get stream dict
871   dict = str->streamGetDict();
873   // check form type
874   dict->lookup("FormType", &obj1);
875   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
876     error(getPos(), "Unknown form type");
877   }
878   obj1.free();
880   // get bounding box
881   dict->lookup("BBox", &obj1);
882   if (!obj1.isArray()) {
883     obj1.free();
884     error(getPos(), "Bad form bounding box");
885     return;
886   }
887   for (i = 0; i < 4; ++i) {
888     obj1.arrayGet(i, &obj2);
889     bbox[i] = obj2.getNum();
890     obj2.free();
891   }
892   obj1.free();
894   // get matrix
895   dict->lookup("Matrix", &obj1);
896   if (obj1.isArray()) {
897     for (i = 0; i < 6; ++i) {
898       obj1.arrayGet(i, &obj2);
899       m[i] = obj2.getNum();
900       obj2.free();
901     }
902   } else {
903     m[0] = 1; m[1] = 0;
904     m[2] = 0; m[3] = 1;
905     m[4] = 0; m[5] = 0;
906   }
907   obj1.free();
909   // get resources
910   dict->lookup("Resources", &obj1);
911   resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
913   // draw it
914   ++formDepth;
915   doForm1(str, resDict, m, bbox, gTrue, gTrue,
916           blendingColorSpace, isolated, knockout,
917           alpha, transferFunc, backdropColor);
918   --formDepth;
920   if (blendingColorSpace) {
921     delete blendingColorSpace;
922   }
923   obj1.free();
926 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
929 //------------------------------------------------------------------------
930 // color operators
931 //------------------------------------------------------------------------
933 void PdfParser::opSetFillGray(Object args[], int numArgs) {
934   GfxColor color;
936   state->setFillPattern(NULL);
937   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
938   color.c[0] = dblToCol(args[0].getNum());
939   state->setFillColor(&color);
940   builder->updateStyle(state);
943 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
944   GfxColor color;
946   state->setStrokePattern(NULL);
947   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
948   color.c[0] = dblToCol(args[0].getNum());
949   state->setStrokeColor(&color);
950   builder->updateStyle(state);
953 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
954   GfxColor color;
955   int i;
957   state->setFillPattern(NULL);
958   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
959   for (i = 0; i < 4; ++i) {
960     color.c[i] = dblToCol(args[i].getNum());
961   }
962   state->setFillColor(&color);
963   builder->updateStyle(state);
966 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
967   GfxColor color;
968   int i;
970   state->setStrokePattern(NULL);
971   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
972   for (i = 0; i < 4; ++i) {
973     color.c[i] = dblToCol(args[i].getNum());
974   }
975   state->setStrokeColor(&color);
976   builder->updateStyle(state);
979 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
980   GfxColor color;
981   int i;
983   state->setFillPattern(NULL);
984   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
985   for (i = 0; i < 3; ++i) {
986     color.c[i] = dblToCol(args[i].getNum());
987   }
988   state->setFillColor(&color);
989   builder->updateStyle(state);
992 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
993   GfxColor color;
994   int i;
996   state->setStrokePattern(NULL);
997   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
998   for (i = 0; i < 3; ++i) {
999     color.c[i] = dblToCol(args[i].getNum());
1000   }
1001   state->setStrokeColor(&color);
1002   builder->updateStyle(state);
1005 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1006   Object obj;
1007   GfxColorSpace *colorSpace;
1008   GfxColor color;
1010   state->setFillPattern(NULL);
1011   res->lookupColorSpace(args[0].getName(), &obj);
1012   if (obj.isNull()) {
1013     colorSpace = GfxColorSpace::parse(&args[0]);
1014   } else {
1015     colorSpace = GfxColorSpace::parse(&obj);
1016   }
1017   obj.free();
1018   if (colorSpace) {
1019     state->setFillColorSpace(colorSpace);
1020     colorSpace->getDefaultColor(&color);
1021     state->setFillColor(&color);
1022     builder->updateStyle(state);
1023   } else {
1024     error(getPos(), "Bad color space (fill)");
1025   }
1028 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1029   Object obj;
1030   GfxColorSpace *colorSpace;
1031   GfxColor color;
1033   state->setStrokePattern(NULL);
1034   res->lookupColorSpace(args[0].getName(), &obj);
1035   if (obj.isNull()) {
1036     colorSpace = GfxColorSpace::parse(&args[0]);
1037   } else {
1038     colorSpace = GfxColorSpace::parse(&obj);
1039   }
1040   obj.free();
1041   if (colorSpace) {
1042     state->setStrokeColorSpace(colorSpace);
1043     colorSpace->getDefaultColor(&color);
1044     state->setStrokeColor(&color);
1045     builder->updateStyle(state);
1046   } else {
1047     error(getPos(), "Bad color space (stroke)");
1048   }
1051 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1052   GfxColor color;
1053   int i;
1055   if (numArgs != state->getFillColorSpace()->getNComps()) {
1056     error(getPos(), "Incorrect number of arguments in 'sc' command");
1057     return;
1058   }
1059   state->setFillPattern(NULL);
1060   for (i = 0; i < numArgs; ++i) {
1061     color.c[i] = dblToCol(args[i].getNum());
1062   }
1063   state->setFillColor(&color);
1064   builder->updateStyle(state);
1067 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1068   GfxColor color;
1069   int i;
1071   if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1072     error(getPos(), "Incorrect number of arguments in 'SC' command");
1073     return;
1074   }
1075   state->setStrokePattern(NULL);
1076   for (i = 0; i < numArgs; ++i) {
1077     color.c[i] = dblToCol(args[i].getNum());
1078   }
1079   state->setStrokeColor(&color);
1080   builder->updateStyle(state);
1083 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1084   GfxColor color;
1085   GfxPattern *pattern;
1086   int i;
1088   if (state->getFillColorSpace()->getMode() == csPattern) {
1089     if (numArgs > 1) {
1090       if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1091           numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1092                              ->getUnder()->getNComps()) {
1093         error(getPos(), "Incorrect number of arguments in 'scn' command");
1094         return;
1095       }
1096       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1097         if (args[i].isNum()) {
1098           color.c[i] = dblToCol(args[i].getNum());
1099         }
1100       }
1101       state->setFillColor(&color);
1102       builder->updateStyle(state);
1103     }
1104     if (args[numArgs-1].isName() &&
1105         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1106       state->setFillPattern(pattern);
1107       builder->updateStyle(state);
1108     }
1110   } else {
1111     if (numArgs != state->getFillColorSpace()->getNComps()) {
1112       error(getPos(), "Incorrect number of arguments in 'scn' command");
1113       return;
1114     }
1115     state->setFillPattern(NULL);
1116     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1117       if (args[i].isNum()) {
1118         color.c[i] = dblToCol(args[i].getNum());
1119       }
1120     }
1121     state->setFillColor(&color);
1122     builder->updateStyle(state);
1123   }
1126 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1127   GfxColor color;
1128   GfxPattern *pattern;
1129   int i;
1131   if (state->getStrokeColorSpace()->getMode() == csPattern) {
1132     if (numArgs > 1) {
1133       if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1134                ->getUnder() ||
1135           numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1136                              ->getUnder()->getNComps()) {
1137         error(getPos(), "Incorrect number of arguments in 'SCN' command");
1138         return;
1139       }
1140       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1141         if (args[i].isNum()) {
1142           color.c[i] = dblToCol(args[i].getNum());
1143         }
1144       }
1145       state->setStrokeColor(&color);
1146       builder->updateStyle(state);
1147     }
1148     if (args[numArgs-1].isName() &&
1149         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1150       state->setStrokePattern(pattern);
1151       builder->updateStyle(state);
1152     }
1154   } else {
1155     if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1156       error(getPos(), "Incorrect number of arguments in 'SCN' command");
1157       return;
1158     }
1159     state->setStrokePattern(NULL);
1160     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1161       if (args[i].isNum()) {
1162         color.c[i] = dblToCol(args[i].getNum());
1163       }
1164     }
1165     state->setStrokeColor(&color);
1166     builder->updateStyle(state);
1167   }
1170 //------------------------------------------------------------------------
1171 // path segment operators
1172 //------------------------------------------------------------------------
1174 void PdfParser::opMoveTo(Object args[], int numArgs) {
1175   state->moveTo(args[0].getNum(), args[1].getNum());
1178 void PdfParser::opLineTo(Object args[], int numArgs) {
1179   if (!state->isCurPt()) {
1180     error(getPos(), "No current point in lineto");
1181     return;
1182   }
1183   state->lineTo(args[0].getNum(), args[1].getNum());
1186 void PdfParser::opCurveTo(Object args[], int numArgs) {
1187   double x1, y1, x2, y2, x3, y3;
1189   if (!state->isCurPt()) {
1190     error(getPos(), "No current point in curveto");
1191     return;
1192   }
1193   x1 = args[0].getNum();
1194   y1 = args[1].getNum();
1195   x2 = args[2].getNum();
1196   y2 = args[3].getNum();
1197   x3 = args[4].getNum();
1198   y3 = args[5].getNum();
1199   state->curveTo(x1, y1, x2, y2, x3, y3);
1202 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1203   double x1, y1, x2, y2, x3, y3;
1205   if (!state->isCurPt()) {
1206     error(getPos(), "No current point in curveto1");
1207     return;
1208   }
1209   x1 = state->getCurX();
1210   y1 = state->getCurY();
1211   x2 = args[0].getNum();
1212   y2 = args[1].getNum();
1213   x3 = args[2].getNum();
1214   y3 = args[3].getNum();
1215   state->curveTo(x1, y1, x2, y2, x3, y3);
1218 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1219   double x1, y1, x2, y2, x3, y3;
1221   if (!state->isCurPt()) {
1222     error(getPos(), "No current point in curveto2");
1223     return;
1224   }
1225   x1 = args[0].getNum();
1226   y1 = args[1].getNum();
1227   x2 = args[2].getNum();
1228   y2 = args[3].getNum();
1229   x3 = x2;
1230   y3 = y2;
1231   state->curveTo(x1, y1, x2, y2, x3, y3);
1234 void PdfParser::opRectangle(Object args[], int numArgs) {
1235   double x, y, w, h;
1237   x = args[0].getNum();
1238   y = args[1].getNum();
1239   w = args[2].getNum();
1240   h = args[3].getNum();
1241   state->moveTo(x, y);
1242   state->lineTo(x + w, y);
1243   state->lineTo(x + w, y + h);
1244   state->lineTo(x, y + h);
1245   state->closePath();
1248 void PdfParser::opClosePath(Object args[], int numArgs) {
1249   if (!state->isCurPt()) {
1250     error(getPos(), "No current point in closepath");
1251     return;
1252   }
1253   state->closePath();
1256 //------------------------------------------------------------------------
1257 // path painting operators
1258 //------------------------------------------------------------------------
1260 void PdfParser::opEndPath(Object args[], int numArgs) {
1261   doEndPath();
1264 void PdfParser::opStroke(Object args[], int numArgs) {
1265   if (!state->isCurPt()) {
1266     //error(getPos(), "No path in stroke");
1267     return;
1268   }
1269   if (state->isPath()) {
1270     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1271         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1272           doPatternStrokeFallback();
1273     } else {
1274       builder->addPath(state, false, true);
1275     }
1276   }
1277   doEndPath();
1280 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1281   if (!state->isCurPt()) {
1282     //error(getPos(), "No path in closepath/stroke");
1283     return;
1284   }
1285   state->closePath();
1286   if (state->isPath()) {
1287     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1288         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1289       doPatternStrokeFallback();
1290     } else {
1291       builder->addPath(state, false, true);
1292     }
1293   }
1294   doEndPath();
1297 void PdfParser::opFill(Object args[], int numArgs) {
1298   if (!state->isCurPt()) {
1299     //error(getPos(), "No path in fill");
1300     return;
1301   }
1302   if (state->isPath()) {
1303     if (state->getFillColorSpace()->getMode() == csPattern &&
1304         !builder->isPatternTypeSupported(state->getFillPattern())) {
1305       doPatternFillFallback(gFalse);
1306     } else {
1307       builder->addPath(state, true, false);
1308     }
1309   }
1310   doEndPath();
1313 void PdfParser::opEOFill(Object args[], int numArgs) {
1314   if (!state->isCurPt()) {
1315     //error(getPos(), "No path in eofill");
1316     return;
1317   }
1318   if (state->isPath()) {
1319     if (state->getFillColorSpace()->getMode() == csPattern &&
1320         !builder->isPatternTypeSupported(state->getFillPattern())) {
1321       doPatternFillFallback(gTrue);
1322     } else {
1323       builder->addPath(state, true, false, true);
1324     }
1325   }
1326   doEndPath();
1329 void PdfParser::opFillStroke(Object args[], int numArgs) {
1330   if (!state->isCurPt()) {
1331     //error(getPos(), "No path in fill/stroke");
1332     return;
1333   }
1334   if (state->isPath()) {
1335     doFillAndStroke(gFalse);
1336   } else {
1337     builder->addPath(state, true, true);
1338   }
1339   doEndPath();
1342 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1343   if (!state->isCurPt()) {
1344     //error(getPos(), "No path in closepath/fill/stroke");
1345     return;
1346   }
1347   if (state->isPath()) {
1348     state->closePath();
1349     doFillAndStroke(gFalse);
1350   }
1351   doEndPath();
1354 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1355   if (!state->isCurPt()) {
1356     //error(getPos(), "No path in eofill/stroke");
1357     return;
1358   }
1359   if (state->isPath()) {
1360     doFillAndStroke(gTrue);
1361   }
1362   doEndPath();
1365 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1366   if (!state->isCurPt()) {
1367     //error(getPos(), "No path in closepath/eofill/stroke");
1368     return;
1369   }
1370   if (state->isPath()) {
1371     state->closePath();
1372     doFillAndStroke(gTrue);
1373   }
1374   doEndPath();
1377 void PdfParser::doFillAndStroke(GBool eoFill) {
1378     GBool fillOk = gTrue, strokeOk = gTrue;
1379     if (state->getFillColorSpace()->getMode() == csPattern &&
1380         !builder->isPatternTypeSupported(state->getFillPattern())) {
1381         fillOk = gFalse;
1382     }
1383     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1384         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1385         strokeOk = gFalse;
1386     }
1387     if (fillOk && strokeOk) {
1388         builder->addPath(state, true, true, eoFill);
1389     } else {
1390         doPatternFillFallback(eoFill);
1391         doPatternStrokeFallback();
1392     }
1395 void PdfParser::doPatternFillFallback(GBool eoFill) {
1396   GfxPattern *pattern;
1398   if (!(pattern = state->getFillPattern())) {
1399     return;
1400   }
1401   switch (pattern->getType()) {
1402   case 1:
1403     break;
1404   case 2:
1405     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1406     break;
1407   default:
1408     error(getPos(), "Unimplemented pattern type (%d) in fill",
1409           pattern->getType());
1410     break;
1411   }
1414 void PdfParser::doPatternStrokeFallback() {
1415   GfxPattern *pattern;
1417   if (!(pattern = state->getStrokePattern())) {
1418     return;
1419   }
1420   switch (pattern->getType()) {
1421   case 1:
1422     break;
1423   case 2:
1424     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1425     break;
1426   default:
1427     error(getPos(), "Unimplemented pattern type (%d) in stroke",
1428           pattern->getType());
1429     break;
1430   }
1433 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1434                                              GBool stroke, GBool eoFill) {
1435   GfxShading *shading;
1436   GfxPath *savedPath;
1437   double *ctm, *btm, *ptm;
1438   double m[6], ictm[6], m1[6];
1439   double xMin, yMin, xMax, yMax;
1440   double det;
1442   shading = sPat->getShading();
1444   // save current graphics state
1445   savedPath = state->getPath()->copy();
1446   saveState();
1448   // clip to bbox
1449   if (0 ){//shading->getHasBBox()) {
1450     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1451     state->moveTo(xMin, yMin);
1452     state->lineTo(xMax, yMin);
1453     state->lineTo(xMax, yMax);
1454     state->lineTo(xMin, yMax);
1455     state->closePath();
1456     state->clip();
1457     //builder->clip(state);
1458     state->setPath(savedPath->copy());
1459   }
1461   // clip to current path
1462   if (stroke) {
1463     state->clipToStrokePath();
1464     //out->clipToStrokePath(state);
1465   } else {
1466     state->clip();
1467     if (eoFill) {
1468       builder->clip(state, true);
1469     } else {
1470       builder->clip(state);
1471     }
1472   }
1474   // set the color space
1475   state->setFillColorSpace(shading->getColorSpace()->copy());
1477   // background color fill
1478   if (shading->getHasBackground()) {
1479     state->setFillColor(shading->getBackground());
1480     builder->addPath(state, true, false);
1481   }
1482   state->clearPath();
1484   // construct a (pattern space) -> (current space) transform matrix
1485   ctm = state->getCTM();
1486   btm = baseMatrix;
1487   ptm = sPat->getMatrix();
1488   // iCTM = invert CTM
1489   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1490   ictm[0] = ctm[3] * det;
1491   ictm[1] = -ctm[1] * det;
1492   ictm[2] = -ctm[2] * det;
1493   ictm[3] = ctm[0] * det;
1494   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1495   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1496   // m1 = PTM * BTM = PTM * base transform matrix
1497   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1498   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1499   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1500   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1501   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1502   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1503   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1504   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1505   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1506   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1507   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1508   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1509   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1511   // set the new matrix
1512   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1513   builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1515   // do shading type-specific operations
1516   switch (shading->getType()) {
1517   case 1:
1518     doFunctionShFill((GfxFunctionShading *)shading);
1519     break;
1520   case 2:
1521   case 3:
1522     // no need to implement these
1523     break;
1524   case 4:
1525   case 5:
1526     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1527     break;
1528   case 6:
1529   case 7:
1530     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1531     break;
1532   }
1534   // restore graphics state
1535   restoreState();
1536   state->setPath(savedPath);
1539 void PdfParser::opShFill(Object args[], int numArgs) {
1540   GfxShading *shading;
1541   GfxPath *savedPath;
1542   double xMin, yMin, xMax, yMax;
1543   double gradientTransform[6];
1544   double *matrix = NULL;
1545   GBool savedState = gFalse;
1547   if (!(shading = res->lookupShading(args[0].getName()))) {
1548     return;
1549   }
1551   // save current graphics state
1552   if (shading->getType() != 2 && shading->getType() != 3) {
1553     savedPath = state->getPath()->copy();
1554     saveState();
1555     savedState = gTrue;
1556   } else {  // get gradient transform if possible
1557       // check proper operator sequence
1558       // first there should be one W(*) and then one 'cm' somewhere before 'sh'
1559       GBool seenClip, seenConcat;
1560       seenClip = (clipHistory->getClipPath() != NULL);
1561       seenConcat = gFalse;
1562       int i = 1;
1563       while (i <= maxOperatorHistoryDepth) {
1564         const char *opName = getPreviousOperator(i);
1565         if (!strcmp(opName, "cm")) {
1566           if (seenConcat) {   // more than one 'cm'
1567             break;
1568           } else {
1569             seenConcat = gTrue;
1570           }
1571         }
1572         i++;
1573       }
1575       if (seenConcat && seenClip) {
1576         if (builder->getTransform((double*)&gradientTransform)) {
1577           matrix = (double*)&gradientTransform;
1578           builder->setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);  // remove transform
1579         }
1580       }
1581   }
1583   // clip to bbox
1584   if (shading->getHasBBox()) {
1585     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1586     state->moveTo(xMin, yMin);
1587     state->lineTo(xMax, yMin);
1588     state->lineTo(xMax, yMax);
1589     state->lineTo(xMin, yMax);
1590     state->closePath();
1591     state->clip();
1592     if (savedState)
1593       builder->setClipPath(state);
1594     else
1595       builder->clip(state);
1596     state->clearPath();
1597   }
1599   // set the color space
1600   if (savedState)
1601     state->setFillColorSpace(shading->getColorSpace()->copy());
1603   // do shading type-specific operations
1604   switch (shading->getType()) {
1605   case 1:
1606     doFunctionShFill((GfxFunctionShading *)shading);
1607     break;
1608   case 2:
1609   case 3:
1610     if (clipHistory->getClipPath()) {
1611       builder->addShadedFill(shading, matrix, clipHistory->getClipPath(),
1612                              clipHistory->getClipType() == clipEO ? true : false);
1613     }
1614     break;
1615   case 4:
1616   case 5:
1617     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1618     break;
1619   case 6:
1620   case 7:
1621     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1622     break;
1623   }
1625   // restore graphics state
1626   if (savedState) {
1627     restoreState();
1628     state->setPath(savedPath);
1629   }
1631   delete shading;
1634 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1635   double x0, y0, x1, y1;
1636   GfxColor colors[4];
1638   shading->getDomain(&x0, &y0, &x1, &y1);
1639   shading->getColor(x0, y0, &colors[0]);
1640   shading->getColor(x0, y1, &colors[1]);
1641   shading->getColor(x1, y0, &colors[2]);
1642   shading->getColor(x1, y1, &colors[3]);
1643   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1646 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1647                             double x0, double y0,
1648                             double x1, double y1,
1649                             GfxColor *colors, int depth) {
1650   GfxColor fillColor;
1651   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1652   GfxColor colors2[4];
1653   double functionColorDelta = colorDeltas[pdfFunctionShading-1];
1654   double *matrix;
1655   double xM, yM;
1656   int nComps, i, j;
1658   nComps = shading->getColorSpace()->getNComps();
1659   matrix = shading->getMatrix();
1661   // compare the four corner colors
1662   for (i = 0; i < 4; ++i) {
1663     for (j = 0; j < nComps; ++j) {
1664       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1665         break;
1666       }
1667     }
1668     if (j < nComps) {
1669       break;
1670     }
1671   }
1673   // center of the rectangle
1674   xM = 0.5 * (x0 + x1);
1675   yM = 0.5 * (y0 + y1);
1677   // the four corner colors are close (or we hit the recursive limit)
1678   // -- fill the rectangle; but require at least one subdivision
1679   // (depth==0) to avoid problems when the four outer corners of the
1680   // shaded region are the same color
1681   if ((i == 4 && depth > 0) || depth == maxDepths[pdfFunctionShading-1]) {
1683     // use the center color
1684     shading->getColor(xM, yM, &fillColor);
1685     state->setFillColor(&fillColor);
1687     // fill the rectangle
1688     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1689                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1690     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1691                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1692     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1693                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1694     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1695                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1696     state->closePath();
1697     builder->addPath(state, true, false);
1698     state->clearPath();
1700   // the four corner colors are not close enough -- subdivide the
1701   // rectangle
1702   } else {
1704     // colors[0]       colorM0       colors[2]
1705     //   (x0,y0)       (xM,y0)       (x1,y0)
1706     //         +----------+----------+
1707     //         |          |          |
1708     //         |    UL    |    UR    |
1709     // color0M |       colorMM       | color1M
1710     // (x0,yM) +----------+----------+ (x1,yM)
1711     //         |       (xM,yM)       |
1712     //         |    LL    |    LR    |
1713     //         |          |          |
1714     //         +----------+----------+
1715     // colors[1]       colorM1       colors[3]
1716     //   (x0,y1)       (xM,y1)       (x1,y1)
1718     shading->getColor(x0, yM, &color0M);
1719     shading->getColor(x1, yM, &color1M);
1720     shading->getColor(xM, y0, &colorM0);
1721     shading->getColor(xM, y1, &colorM1);
1722     shading->getColor(xM, yM, &colorMM);
1724     // upper-left sub-rectangle
1725     colors2[0] = colors[0];
1726     colors2[1] = color0M;
1727     colors2[2] = colorM0;
1728     colors2[3] = colorMM;
1729     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1730     
1731     // lower-left sub-rectangle
1732     colors2[0] = color0M;
1733     colors2[1] = colors[1];
1734     colors2[2] = colorMM;
1735     colors2[3] = colorM1;
1736     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1737     
1738     // upper-right sub-rectangle
1739     colors2[0] = colorM0;
1740     colors2[1] = colorMM;
1741     colors2[2] = colors[2];
1742     colors2[3] = color1M;
1743     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1745     // lower-right sub-rectangle
1746     colors2[0] = colorMM;
1747     colors2[1] = colorM1;
1748     colors2[2] = color1M;
1749     colors2[3] = colors[3];
1750     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1751   }
1754 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1755   double x0, y0, x1, y1, x2, y2;
1756   GfxColor color0, color1, color2;
1757   int i;
1759   for (i = 0; i < shading->getNTriangles(); ++i) {
1760     shading->getTriangle(i, &x0, &y0, &color0,
1761                          &x1, &y1, &color1,
1762                          &x2, &y2, &color2);
1763     gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1764                         shading->getColorSpace()->getNComps(), 0);
1765   }
1768 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1769                               double x1, double y1, GfxColor *color1,
1770                               double x2, double y2, GfxColor *color2,
1771                               int nComps, int depth) {
1772   double x01, y01, x12, y12, x20, y20;
1773   double gouraudColorDelta = colorDeltas[pdfGouraudTriangleShading-1];
1774   GfxColor color01, color12, color20;
1775   int i;
1777   for (i = 0; i < nComps; ++i) {
1778     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1779        abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1780       break;
1781     }
1782   }
1783   if (i == nComps || depth == maxDepths[pdfGouraudTriangleShading-1]) {
1784     state->setFillColor(color0);
1785     state->moveTo(x0, y0);
1786     state->lineTo(x1, y1);
1787     state->lineTo(x2, y2);
1788     state->closePath();
1789     builder->addPath(state, true, false);
1790     state->clearPath();
1791   } else {
1792     x01 = 0.5 * (x0 + x1);
1793     y01 = 0.5 * (y0 + y1);
1794     x12 = 0.5 * (x1 + x2);
1795     y12 = 0.5 * (y1 + y2);
1796     x20 = 0.5 * (x2 + x0);
1797     y20 = 0.5 * (y2 + y0);
1798     //~ if the shading has a Function, this should interpolate on the
1799     //~ function parameter, not on the color components
1800     for (i = 0; i < nComps; ++i) {
1801       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1802       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1803       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1804     }
1805     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1806                         x20, y20, &color20, nComps, depth + 1);
1807     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1808                         x12, y12, &color12, nComps, depth + 1);
1809     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1810                         x20, y20, &color20, nComps, depth + 1);
1811     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1812                         x2, y2, color2, nComps, depth + 1);
1813   }
1816 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1817   int start, i;
1819   if (shading->getNPatches() > 128) {
1820     start = 3;
1821   } else if (shading->getNPatches() > 64) {
1822     start = 2;
1823   } else if (shading->getNPatches() > 16) {
1824     start = 1;
1825   } else {
1826     start = 0;
1827   }
1828   for (i = 0; i < shading->getNPatches(); ++i) {
1829     fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1830               start);
1831   }
1834 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1835   GfxPatch patch00, patch01, patch10, patch11;
1836   double xx[4][8], yy[4][8];
1837   double xxm, yym;
1838   double patchColorDelta = colorDeltas[pdfPatchMeshShading-1];
1839   int i;
1841   for (i = 0; i < nComps; ++i) {
1842     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1843           > patchColorDelta ||
1844         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1845           > patchColorDelta ||
1846         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1847           > patchColorDelta ||
1848         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1849           > patchColorDelta) {
1850       break;
1851     }
1852   }
1853   if (i == nComps || depth == maxDepths[pdfPatchMeshShading]-1) {
1854     state->setFillColor(&patch->color[0][0]);
1855     state->moveTo(patch->x[0][0], patch->y[0][0]);
1856     state->curveTo(patch->x[0][1], patch->y[0][1],
1857                    patch->x[0][2], patch->y[0][2],
1858                    patch->x[0][3], patch->y[0][3]);
1859     state->curveTo(patch->x[1][3], patch->y[1][3],
1860                    patch->x[2][3], patch->y[2][3],
1861                    patch->x[3][3], patch->y[3][3]);
1862     state->curveTo(patch->x[3][2], patch->y[3][2],
1863                    patch->x[3][1], patch->y[3][1],
1864                    patch->x[3][0], patch->y[3][0]);
1865     state->curveTo(patch->x[2][0], patch->y[2][0],
1866                    patch->x[1][0], patch->y[1][0],
1867                    patch->x[0][0], patch->y[0][0]);
1868     state->closePath();
1869     builder->addPath(state, true, false);
1870     state->clearPath();
1871   } else {
1872     for (i = 0; i < 4; ++i) {
1873       xx[i][0] = patch->x[i][0];
1874       yy[i][0] = patch->y[i][0];
1875       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1876       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1877       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1878       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1879       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1880       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1881       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1882       yy[i][2] = 0.5 * (yy[i][1] + yym);
1883       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1884       yy[i][5] = 0.5 * (yym + yy[i][6]);
1885       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1886       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1887       xx[i][7] = patch->x[i][3];
1888       yy[i][7] = patch->y[i][3];
1889     }
1890     for (i = 0; i < 4; ++i) {
1891       patch00.x[0][i] = xx[0][i];
1892       patch00.y[0][i] = yy[0][i];
1893       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1894       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1895       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1896       yym = 0.5 * (yy[1][i] + yy[2][i]);
1897       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1898       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1899       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1900       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1901       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1902       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1903       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1904       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1905       patch10.x[0][i] = patch00.x[3][i];
1906       patch10.y[0][i] = patch00.y[3][i];
1907       patch10.x[3][i] = xx[3][i];
1908       patch10.y[3][i] = yy[3][i];
1909     }
1910     for (i = 4; i < 8; ++i) {
1911       patch01.x[0][i-4] = xx[0][i];
1912       patch01.y[0][i-4] = yy[0][i];
1913       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1914       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1915       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1916       yym = 0.5 * (yy[1][i] + yy[2][i]);
1917       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1918       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1919       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1920       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1921       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1922       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1923       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1924       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1925       patch11.x[0][i-4] = patch01.x[3][i-4];
1926       patch11.y[0][i-4] = patch01.y[3][i-4];
1927       patch11.x[3][i-4] = xx[3][i];
1928       patch11.y[3][i-4] = yy[3][i];
1929     }
1930     //~ if the shading has a Function, this should interpolate on the
1931     //~ function parameter, not on the color components
1932     for (i = 0; i < nComps; ++i) {
1933       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1934       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1935                                   patch->color[0][1].c[i]) / 2;
1936       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1937       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1938       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1939                                   patch->color[1][1].c[i]) / 2;
1940       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1941       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1942       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1943                                   patch->color[1][0].c[i]) / 2;
1944       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1945       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1946       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1947                                   patch->color[0][0].c[i]) / 2;
1948       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1949       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1950                                   patch01.color[1][1].c[i]) / 2;
1951       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1952       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1953       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1954     }
1955     fillPatch(&patch00, nComps, depth + 1);
1956     fillPatch(&patch10, nComps, depth + 1);
1957     fillPatch(&patch01, nComps, depth + 1);
1958     fillPatch(&patch11, nComps, depth + 1);
1959   }
1962 void PdfParser::doEndPath() {
1963   if (state->isCurPt() && clip != clipNone) {
1964     state->clip();
1965     if (clip == clipNormal) {
1966       clipHistory->setClip(state->getPath(), clipNormal);
1967       builder->clip(state);
1968     } else {
1969       clipHistory->setClip(state->getPath(), clipEO);
1970       builder->clip(state, true);
1971     }
1972   }
1973   clip = clipNone;
1974   state->clearPath();
1977 //------------------------------------------------------------------------
1978 // path clipping operators
1979 //------------------------------------------------------------------------
1981 void PdfParser::opClip(Object args[], int numArgs) {
1982   clip = clipNormal;
1985 void PdfParser::opEOClip(Object args[], int numArgs) {
1986   clip = clipEO;
1989 //------------------------------------------------------------------------
1990 // text object operators
1991 //------------------------------------------------------------------------
1993 void PdfParser::opBeginText(Object args[], int numArgs) {
1994   state->setTextMat(1, 0, 0, 1, 0, 0);
1995   state->textMoveTo(0, 0);
1996   builder->updateTextPosition(0.0, 0.0);
1997   fontChanged = gTrue;
1998   builder->beginTextObject(state);
2001 void PdfParser::opEndText(Object args[], int numArgs) {
2002   builder->endTextObject(state);
2005 //------------------------------------------------------------------------
2006 // text state operators
2007 //------------------------------------------------------------------------
2009 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2010   state->setCharSpace(args[0].getNum());
2013 void PdfParser::opSetFont(Object args[], int numArgs) {
2014   GfxFont *font;
2016   if (!(font = res->lookupFont(args[0].getName()))) {
2017     return;
2018   }
2019   if (printCommands) {
2020     printf("  font: tag=%s name='%s' %g\n",
2021            font->getTag()->getCString(),
2022            font->getName() ? font->getName()->getCString() : "???",
2023            args[1].getNum());
2024     fflush(stdout);
2025   }
2027   font->incRefCnt();
2028   state->setFont(font, args[1].getNum());
2029   fontChanged = gTrue;
2032 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2033   state->setLeading(args[0].getNum());
2036 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2037   state->setRender(args[0].getInt());
2038   builder->updateStyle(state);
2041 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2042   state->setRise(args[0].getNum());
2045 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2046   state->setWordSpace(args[0].getNum());
2049 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2050   state->setHorizScaling(args[0].getNum());
2051   builder->updateTextMatrix(state);
2052   fontChanged = gTrue;
2055 //------------------------------------------------------------------------
2056 // text positioning operators
2057 //------------------------------------------------------------------------
2059 void PdfParser::opTextMove(Object args[], int numArgs) {
2060   double tx, ty;
2062   tx = state->getLineX() + args[0].getNum();
2063   ty = state->getLineY() + args[1].getNum();
2064   state->textMoveTo(tx, ty);
2065   builder->updateTextPosition(tx, ty);
2068 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2069   double tx, ty;
2071   tx = state->getLineX() + args[0].getNum();
2072   ty = args[1].getNum();
2073   state->setLeading(-ty);
2074   ty += state->getLineY();
2075   state->textMoveTo(tx, ty);
2076   builder->updateTextPosition(tx, ty);
2079 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2080   state->setTextMat(args[0].getNum(), args[1].getNum(),
2081                     args[2].getNum(), args[3].getNum(),
2082                     args[4].getNum(), args[5].getNum());
2083   state->textMoveTo(0, 0);
2084   builder->updateTextMatrix(state);
2085   builder->updateTextPosition(0.0, 0.0);
2086   fontChanged = gTrue;
2089 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2090   double tx, ty;
2092   tx = state->getLineX();
2093   ty = state->getLineY() - state->getLeading();
2094   state->textMoveTo(tx, ty);
2095   builder->updateTextPosition(tx, ty);
2098 //------------------------------------------------------------------------
2099 // text string operators
2100 //------------------------------------------------------------------------
2102 void PdfParser::opShowText(Object args[], int numArgs) {
2103   if (!state->getFont()) {
2104     error(getPos(), "No font in show");
2105     return;
2106   }
2107   if (fontChanged) {
2108     builder->updateFont(state);
2109     fontChanged = gFalse;
2110   }
2111   doShowText(args[0].getString());
2114 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2115   double tx, ty;
2117   if (!state->getFont()) {
2118     error(getPos(), "No font in move/show");
2119     return;
2120   }
2121   if (fontChanged) {
2122     builder->updateFont(state);
2123     fontChanged = gFalse;
2124   }
2125   tx = state->getLineX();
2126   ty = state->getLineY() - state->getLeading();
2127   state->textMoveTo(tx, ty);
2128   builder->updateTextPosition(tx, ty);
2129   doShowText(args[0].getString());
2132 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2133   double tx, ty;
2135   if (!state->getFont()) {
2136     error(getPos(), "No font in move/set/show");
2137     return;
2138   }
2139   if (fontChanged) {
2140     builder->updateFont(state);
2141     fontChanged = gFalse;
2142   }
2143   state->setWordSpace(args[0].getNum());
2144   state->setCharSpace(args[1].getNum());
2145   tx = state->getLineX();
2146   ty = state->getLineY() - state->getLeading();
2147   state->textMoveTo(tx, ty);
2148   builder->updateTextPosition(tx, ty);
2149   doShowText(args[2].getString());
2152 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2153   Array *a;
2154   Object obj;
2155   int wMode;
2156   int i;
2158   if (!state->getFont()) {
2159     error(getPos(), "No font in show/space");
2160     return;
2161   }
2162   if (fontChanged) {
2163     builder->updateFont(state);
2164     fontChanged = gFalse;
2165   }
2166   wMode = state->getFont()->getWMode();
2167   a = args[0].getArray();
2168   for (i = 0; i < a->getLength(); ++i) {
2169     a->get(i, &obj);
2170     if (obj.isNum()) {
2171       // this uses the absolute value of the font size to match
2172       // Acrobat's behavior
2173       if (wMode) {
2174         state->textShift(0, -obj.getNum() * 0.001 *
2175                             fabs(state->getFontSize()));
2176       } else {
2177         state->textShift(-obj.getNum() * 0.001 *
2178                          fabs(state->getFontSize()), 0);
2179       }
2180       builder->updateTextShift(state, obj.getNum());
2181     } else if (obj.isString()) {
2182       doShowText(obj.getString());
2183     } else {
2184       error(getPos(), "Element of show/space array must be number or string");
2185     }
2186     obj.free();
2187   }
2190 void PdfParser::doShowText(GooString *s) {
2191   GfxFont *font;
2192   int wMode;
2193   double riseX, riseY;
2194   CharCode code;
2195   Unicode u[8];
2196   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2197   double originX, originY, tOriginX, tOriginY;
2198   double oldCTM[6], newCTM[6];
2199   double *mat;
2200   Object charProc;
2201   Dict *resDict;
2202   Parser *oldParser;
2203   char *p;
2204   int len, n, uLen, nChars, nSpaces, i;
2206   font = state->getFont();
2207   wMode = font->getWMode();
2209   builder->beginString(state, s);
2211   // handle a Type 3 char
2212   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2213     mat = state->getCTM();
2214     for (i = 0; i < 6; ++i) {
2215       oldCTM[i] = mat[i];
2216     }
2217     mat = state->getTextMat();
2218     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2219     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2220     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2221     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2222     mat = font->getFontMatrix();
2223     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2224     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2225     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2226     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2227     newCTM[0] *= state->getFontSize();
2228     newCTM[1] *= state->getFontSize();
2229     newCTM[2] *= state->getFontSize();
2230     newCTM[3] *= state->getFontSize();
2231     newCTM[0] *= state->getHorizScaling();
2232     newCTM[2] *= state->getHorizScaling();
2233     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2234     curX = state->getCurX();
2235     curY = state->getCurY();
2236     lineX = state->getLineX();
2237     lineY = state->getLineY();
2238     oldParser = parser;
2239     p = s->getCString();
2240     len = s->getLength();
2241     while (len > 0) {
2242       n = font->getNextChar(p, len, &code,
2243                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2244                             &dx, &dy, &originX, &originY);
2245       dx = dx * state->getFontSize() + state->getCharSpace();
2246       if (n == 1 && *p == ' ') {
2247         dx += state->getWordSpace();
2248       }
2249       dx *= state->getHorizScaling();
2250       dy *= state->getFontSize();
2251       state->textTransformDelta(dx, dy, &tdx, &tdy);
2252       state->transform(curX + riseX, curY + riseY, &x, &y);
2253       saveState();
2254       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2255       //~ the CTM concat values here are wrong (but never used)
2256       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2257       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2258                                code, u, uLen)) {*/
2259         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2260         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2261           pushResources(resDict);
2262         }
2263         if (charProc.isStream()) {
2264           //parse(&charProc, gFalse); // TODO: parse into SVG font
2265         } else {
2266           error(getPos(), "Missing or bad Type3 CharProc entry");
2267         }
2268         //out->endType3Char(state);
2269         if (resDict) {
2270           popResources();
2271         }
2272         charProc.free();
2273       }
2274       restoreState();
2275       // GfxState::restore() does *not* restore the current position,
2276       // so we deal with it here using (curX, curY) and (lineX, lineY)
2277       curX += tdx;
2278       curY += tdy;
2279       state->moveTo(curX, curY);
2280       state->textSetPos(lineX, lineY);
2281       p += n;
2282       len -= n;
2283     }
2284     parser = oldParser;
2286   } else {
2287     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2288     p = s->getCString();
2289     len = s->getLength();
2290     while (len > 0) {
2291       n = font->getNextChar(p, len, &code,
2292                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2293                             &dx, &dy, &originX, &originY);
2294       
2295       if (wMode) {
2296         dx *= state->getFontSize();
2297         dy = dy * state->getFontSize() + state->getCharSpace();
2298         if (n == 1 && *p == ' ') {
2299           dy += state->getWordSpace();
2300         }
2301       } else {
2302         dx = dx * state->getFontSize() + state->getCharSpace();
2303         if (n == 1 && *p == ' ') {
2304           dx += state->getWordSpace();
2305         }
2306         dx *= state->getHorizScaling();
2307         dy *= state->getFontSize();
2308       }
2309       state->textTransformDelta(dx, dy, &tdx, &tdy);
2310       originX *= state->getFontSize();
2311       originY *= state->getFontSize();
2312       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2313       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2314                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2315       state->shift(tdx, tdy);
2316       p += n;
2317       len -= n;
2318     }
2319   }
2321   builder->endString(state);
2324 //------------------------------------------------------------------------
2325 // XObject operators
2326 //------------------------------------------------------------------------
2328 void PdfParser::opXObject(Object args[], int numArgs) {
2329   char *name;
2330   Object obj1, obj2, obj3, refObj;
2332   name = args[0].getName();
2333   if (!res->lookupXObject(name, &obj1)) {
2334     return;
2335   }
2336   if (!obj1.isStream()) {
2337     error(getPos(), "XObject '%s' is wrong type", name);
2338     obj1.free();
2339     return;
2340   }
2341   obj1.streamGetDict()->lookup("Subtype", &obj2);
2342   if (obj2.isName("Image")) {
2343     res->lookupXObjectNF(name, &refObj);
2344     doImage(&refObj, obj1.getStream(), gFalse);
2345     refObj.free();
2346   } else if (obj2.isName("Form")) {
2347     doForm(&obj1);
2348   } else if (obj2.isName("PS")) {
2349     obj1.streamGetDict()->lookup("Level1", &obj3);
2350 /*    out->psXObject(obj1.getStream(),
2351                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2352   } else if (obj2.isName()) {
2353     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2354   } else {
2355     error(getPos(), "XObject subtype is missing or wrong type");
2356   }
2357   obj2.free();
2358   obj1.free();
2361 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2362   Dict *dict, *maskDict;
2363   int width, height;
2364   int bits, maskBits;
2365   StreamColorSpaceMode csMode;
2366   GBool mask;
2367   GBool invert;
2368   GfxColorSpace *colorSpace, *maskColorSpace;
2369   GfxImageColorMap *colorMap, *maskColorMap;
2370   Object maskObj, smaskObj;
2371   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2372   int maskColors[2*gfxColorMaxComps];
2373   int maskWidth, maskHeight;
2374   GBool maskInvert;
2375   Stream *maskStr;
2376   Object obj1, obj2;
2377   int i;
2379   // get info from the stream
2380   bits = 0;
2381   csMode = streamCSNone;
2382   str->getImageParams(&bits, &csMode);
2384   // get stream dict
2385   dict = str->getDict();
2387   // get size
2388   dict->lookup("Width", &obj1);
2389   if (obj1.isNull()) {
2390     obj1.free();
2391     dict->lookup("W", &obj1);
2392   }
2393   if (obj1.isInt())
2394     width = obj1.getInt();
2395   else if (obj1.isReal())
2396     width = (int)obj1.getReal();
2397   else
2398     goto err2;
2399   obj1.free();
2400   dict->lookup("Height", &obj1);
2401   if (obj1.isNull()) {
2402     obj1.free();
2403     dict->lookup("H", &obj1);
2404   }
2405   if (obj1.isInt())
2406     height = obj1.getInt();
2407   else if (obj1.isReal())
2408     height = (int)obj1.getReal();
2409   else
2410     goto err2;
2411   obj1.free();
2413   // image or mask?
2414   dict->lookup("ImageMask", &obj1);
2415   if (obj1.isNull()) {
2416     obj1.free();
2417     dict->lookup("IM", &obj1);
2418   }
2419   mask = gFalse;
2420   if (obj1.isBool())
2421     mask = obj1.getBool();
2422   else if (!obj1.isNull())
2423     goto err2;
2424   obj1.free();
2426   // bit depth
2427   if (bits == 0) {
2428     dict->lookup("BitsPerComponent", &obj1);
2429     if (obj1.isNull()) {
2430       obj1.free();
2431       dict->lookup("BPC", &obj1);
2432     }
2433     if (obj1.isInt()) {
2434       bits = obj1.getInt();
2435     } else if (mask) {
2436       bits = 1;
2437     } else {
2438       goto err2;
2439     }
2440     obj1.free();
2441   }
2443   // display a mask
2444   if (mask) {
2446     // check for inverted mask
2447     if (bits != 1)
2448       goto err1;
2449     invert = gFalse;
2450     dict->lookup("Decode", &obj1);
2451     if (obj1.isNull()) {
2452       obj1.free();
2453       dict->lookup("D", &obj1);
2454     }
2455     if (obj1.isArray()) {
2456       obj1.arrayGet(0, &obj2);
2457       if (obj2.isInt() && obj2.getInt() == 1)
2458         invert = gTrue;
2459       obj2.free();
2460     } else if (!obj1.isNull()) {
2461       goto err2;
2462     }
2463     obj1.free();
2465     // draw it
2466     builder->addImageMask(state, str, width, height, invert);
2468   } else {
2470     // get color space and color map
2471     dict->lookup("ColorSpace", &obj1);
2472     if (obj1.isNull()) {
2473       obj1.free();
2474       dict->lookup("CS", &obj1);
2475     }
2476     if (obj1.isName()) {
2477       res->lookupColorSpace(obj1.getName(), &obj2);
2478       if (!obj2.isNull()) {
2479         obj1.free();
2480         obj1 = obj2;
2481       } else {
2482         obj2.free();
2483       }
2484     }
2485     if (!obj1.isNull()) {
2486       colorSpace = GfxColorSpace::parse(&obj1);
2487     } else if (csMode == streamCSDeviceGray) {
2488       colorSpace = new GfxDeviceGrayColorSpace();
2489     } else if (csMode == streamCSDeviceRGB) {
2490       colorSpace = new GfxDeviceRGBColorSpace();
2491     } else if (csMode == streamCSDeviceCMYK) {
2492       colorSpace = new GfxDeviceCMYKColorSpace();
2493     } else {
2494       colorSpace = NULL;
2495     }
2496     obj1.free();
2497     if (!colorSpace) {
2498       goto err1;
2499     }
2500     dict->lookup("Decode", &obj1);
2501     if (obj1.isNull()) {
2502       obj1.free();
2503       dict->lookup("D", &obj1);
2504     }
2505     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2506     obj1.free();
2507     if (!colorMap->isOk()) {
2508       delete colorMap;
2509       goto err1;
2510     }
2512     // get the mask
2513     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2514     maskStr = NULL; // make gcc happy
2515     maskWidth = maskHeight = 0; // make gcc happy
2516     maskInvert = gFalse; // make gcc happy
2517     maskColorMap = NULL; // make gcc happy
2518     dict->lookup("Mask", &maskObj);
2519     dict->lookup("SMask", &smaskObj);
2520     if (smaskObj.isStream()) {
2521       // soft mask
2522       if (inlineImg) {
2523         goto err1;
2524       }
2525       maskStr = smaskObj.getStream();
2526       maskDict = smaskObj.streamGetDict();
2527       maskDict->lookup("Width", &obj1);
2528       if (obj1.isNull()) {
2529         obj1.free();
2530         maskDict->lookup("W", &obj1);
2531       }
2532       if (!obj1.isInt()) {
2533         goto err2;
2534       }
2535       maskWidth = obj1.getInt();
2536       obj1.free();
2537       maskDict->lookup("Height", &obj1);
2538       if (obj1.isNull()) {
2539         obj1.free();
2540         maskDict->lookup("H", &obj1);
2541       }
2542       if (!obj1.isInt()) {
2543         goto err2;
2544       }
2545       maskHeight = obj1.getInt();
2546       obj1.free();
2547       maskDict->lookup("BitsPerComponent", &obj1);
2548       if (obj1.isNull()) {
2549         obj1.free();
2550         maskDict->lookup("BPC", &obj1);
2551       }
2552       if (!obj1.isInt()) {
2553         goto err2;
2554       }
2555       maskBits = obj1.getInt();
2556       obj1.free();
2557       maskDict->lookup("ColorSpace", &obj1);
2558       if (obj1.isNull()) {
2559         obj1.free();
2560         maskDict->lookup("CS", &obj1);
2561       }
2562       if (obj1.isName()) {
2563         res->lookupColorSpace(obj1.getName(), &obj2);
2564         if (!obj2.isNull()) {
2565           obj1.free();
2566           obj1 = obj2;
2567         } else {
2568           obj2.free();
2569         }
2570       }
2571       maskColorSpace = GfxColorSpace::parse(&obj1);
2572       obj1.free();
2573       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2574         goto err1;
2575       }
2576       maskDict->lookup("Decode", &obj1);
2577       if (obj1.isNull()) {
2578         obj1.free();
2579         maskDict->lookup("D", &obj1);
2580       }
2581       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2582       obj1.free();
2583       if (!maskColorMap->isOk()) {
2584         delete maskColorMap;
2585         goto err1;
2586       }
2587       //~ handle the Matte entry
2588       haveSoftMask = gTrue;
2589     } else if (maskObj.isArray()) {
2590       // color key mask
2591       for (i = 0;
2592            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2593            ++i) {
2594         maskObj.arrayGet(i, &obj1);
2595         maskColors[i] = obj1.getInt();
2596         obj1.free();
2597       }
2598       haveColorKeyMask = gTrue;
2599     } else if (maskObj.isStream()) {
2600       // explicit mask
2601       if (inlineImg) {
2602         goto err1;
2603       }
2604       maskStr = maskObj.getStream();
2605       maskDict = maskObj.streamGetDict();
2606       maskDict->lookup("Width", &obj1);
2607       if (obj1.isNull()) {
2608         obj1.free();
2609         maskDict->lookup("W", &obj1);
2610       }
2611       if (!obj1.isInt()) {
2612         goto err2;
2613       }
2614       maskWidth = obj1.getInt();
2615       obj1.free();
2616       maskDict->lookup("Height", &obj1);
2617       if (obj1.isNull()) {
2618         obj1.free();
2619         maskDict->lookup("H", &obj1);
2620       }
2621       if (!obj1.isInt()) {
2622         goto err2;
2623       }
2624       maskHeight = obj1.getInt();
2625       obj1.free();
2626       maskDict->lookup("ImageMask", &obj1);
2627       if (obj1.isNull()) {
2628         obj1.free();
2629         maskDict->lookup("IM", &obj1);
2630       }
2631       if (!obj1.isBool() || !obj1.getBool()) {
2632         goto err2;
2633       }
2634       obj1.free();
2635       maskInvert = gFalse;
2636       maskDict->lookup("Decode", &obj1);
2637       if (obj1.isNull()) {
2638         obj1.free();
2639         maskDict->lookup("D", &obj1);
2640       }
2641       if (obj1.isArray()) {
2642         obj1.arrayGet(0, &obj2);
2643         if (obj2.isInt() && obj2.getInt() == 1) {
2644           maskInvert = gTrue;
2645         }
2646         obj2.free();
2647       } else if (!obj1.isNull()) {
2648         goto err2;
2649       }
2650       obj1.free();
2651       haveExplicitMask = gTrue;
2652     }
2654     // draw it
2655     if (haveSoftMask) {
2656         builder->addSoftMaskedImage(state, str, width, height, colorMap,
2657                                     maskStr, maskWidth, maskHeight, maskColorMap);
2658       delete maskColorMap;
2659     } else if (haveExplicitMask) {
2660         builder->addMaskedImage(state, str, width, height, colorMap,
2661                                 maskStr, maskWidth, maskHeight, maskInvert);
2662     } else {
2663       builder->addImage(state, str, width, height, colorMap,
2664                         haveColorKeyMask ? maskColors : (int *)NULL);
2665     }
2666     delete colorMap;
2668     maskObj.free();
2669     smaskObj.free();
2670   }
2672   return;
2674  err2:
2675   obj1.free();
2676  err1:
2677   error(getPos(), "Bad image parameters");
2680 void PdfParser::doForm(Object *str) {
2681   Dict *dict;
2682   GBool transpGroup, isolated, knockout;
2683   GfxColorSpace *blendingColorSpace;
2684   Object matrixObj, bboxObj;
2685   double m[6], bbox[4];
2686   Object resObj;
2687   Dict *resDict;
2688   Object obj1, obj2, obj3;
2689   int i;
2691   // check for excessive recursion
2692   if (formDepth > 20) {
2693     return;
2694   }
2696   // get stream dict
2697   dict = str->streamGetDict();
2699   // check form type
2700   dict->lookup("FormType", &obj1);
2701   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2702     error(getPos(), "Unknown form type");
2703   }
2704   obj1.free();
2706   // get bounding box
2707   dict->lookup("BBox", &bboxObj);
2708   if (!bboxObj.isArray()) {
2709     bboxObj.free();
2710     error(getPos(), "Bad form bounding box");
2711     return;
2712   }
2713   for (i = 0; i < 4; ++i) {
2714     bboxObj.arrayGet(i, &obj1);
2715     bbox[i] = obj1.getNum();
2716     obj1.free();
2717   }
2718   bboxObj.free();
2720   // get matrix
2721   dict->lookup("Matrix", &matrixObj);
2722   if (matrixObj.isArray()) {
2723     for (i = 0; i < 6; ++i) {
2724       matrixObj.arrayGet(i, &obj1);
2725       m[i] = obj1.getNum();
2726       obj1.free();
2727     }
2728   } else {
2729     m[0] = 1; m[1] = 0;
2730     m[2] = 0; m[3] = 1;
2731     m[4] = 0; m[5] = 0;
2732   }
2733   matrixObj.free();
2735   // get resources
2736   dict->lookup("Resources", &resObj);
2737   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2739   // check for a transparency group
2740   transpGroup = isolated = knockout = gFalse;
2741   blendingColorSpace = NULL;
2742   if (dict->lookup("Group", &obj1)->isDict()) {
2743     if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2744       transpGroup = gTrue;
2745       if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2746         blendingColorSpace = GfxColorSpace::parse(&obj3);
2747       }
2748       obj3.free();
2749       if (obj1.dictLookup("I", &obj3)->isBool()) {
2750         isolated = obj3.getBool();
2751       }
2752       obj3.free();
2753       if (obj1.dictLookup("K", &obj3)->isBool()) {
2754         knockout = obj3.getBool();
2755       }
2756       obj3.free();
2757     }
2758     obj2.free();
2759   }
2760   obj1.free();
2762   // draw it
2763   ++formDepth;
2764   doForm1(str, resDict, m, bbox,
2765           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2766   --formDepth;
2768   if (blendingColorSpace) {
2769     delete blendingColorSpace;
2770   }
2771   resObj.free();
2774 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2775                   GBool transpGroup, GBool softMask,
2776                   GfxColorSpace *blendingColorSpace,
2777                   GBool isolated, GBool knockout,
2778                   GBool alpha, Function *transferFunc,
2779                   GfxColor *backdropColor) {
2780   Parser *oldParser;
2781   double oldBaseMatrix[6];
2782   int i;
2784   // push new resources on stack
2785   pushResources(resDict);
2787   // save current graphics state
2788   saveState();
2790   // kill any pre-existing path
2791   state->clearPath();
2793   if (softMask || transpGroup) {
2794     builder->clearSoftMask(state);
2795     builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2796                                    isolated, knockout, softMask);
2797   }
2799   // save current parser
2800   oldParser = parser;
2802   // set form transformation matrix
2803   state->concatCTM(matrix[0], matrix[1], matrix[2],
2804                    matrix[3], matrix[4], matrix[5]);
2805   builder->setTransform(matrix[0], matrix[1], matrix[2],
2806                         matrix[3], matrix[4], matrix[5]);
2808   // set form bounding box
2809   state->moveTo(bbox[0], bbox[1]);
2810   state->lineTo(bbox[2], bbox[1]);
2811   state->lineTo(bbox[2], bbox[3]);
2812   state->lineTo(bbox[0], bbox[3]);
2813   state->closePath();
2814   state->clip();
2815   clipHistory->setClip(state->getPath());
2816   builder->clip(state);
2817   state->clearPath();
2819   if (softMask || transpGroup) {
2820     if (state->getBlendMode() != gfxBlendNormal) {
2821       state->setBlendMode(gfxBlendNormal);
2822     }
2823     if (state->getFillOpacity() != 1) {
2824       builder->setGroupOpacity(state->getFillOpacity());
2825       state->setFillOpacity(1);
2826     }
2827     if (state->getStrokeOpacity() != 1) {
2828       state->setStrokeOpacity(1);
2829     }
2830   }
2832   // set new base matrix
2833   for (i = 0; i < 6; ++i) {
2834     oldBaseMatrix[i] = baseMatrix[i];
2835     baseMatrix[i] = state->getCTM()[i];
2836   }
2838   // draw the form
2839   parse(str, gFalse);
2841   // restore base matrix
2842   for (i = 0; i < 6; ++i) {
2843     baseMatrix[i] = oldBaseMatrix[i];
2844   }
2846   // restore parser
2847   parser = oldParser;
2849   if (softMask || transpGroup) {
2850       builder->popTransparencyGroup(state);
2851   }
2853   // restore graphics state
2854   restoreState();
2856   // pop resource stack
2857   popResources();
2859   if (softMask) {
2860     builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2861   } else if (transpGroup) {
2862     builder->paintTransparencyGroup(state, bbox);
2863   }
2865   return;
2868 //------------------------------------------------------------------------
2869 // in-line image operators
2870 //------------------------------------------------------------------------
2872 void PdfParser::opBeginImage(Object args[], int numArgs) {
2873   Stream *str;
2874   int c1, c2;
2876   // build dict/stream
2877   str = buildImageStream();
2879   // display the image
2880   if (str) {
2881     doImage(NULL, str, gTrue);
2882   
2883     // skip 'EI' tag
2884     c1 = str->getUndecodedStream()->getChar();
2885     c2 = str->getUndecodedStream()->getChar();
2886     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2887       c1 = c2;
2888       c2 = str->getUndecodedStream()->getChar();
2889     }
2890     delete str;
2891   }
2894 Stream *PdfParser::buildImageStream() {
2895   Object dict;
2896   Object obj;
2897   char *key;
2898   Stream *str;
2900   // build dictionary
2901   dict.initDict(xref);
2902   parser->getObj(&obj);
2903   while (!obj.isCmd("ID") && !obj.isEOF()) {
2904     if (!obj.isName()) {
2905       error(getPos(), "Inline image dictionary key must be a name object");
2906       obj.free();
2907     } else {
2908       key = copyString(obj.getName());
2909       obj.free();
2910       parser->getObj(&obj);
2911       if (obj.isEOF() || obj.isError()) {
2912         gfree(key);
2913         break;
2914       }
2915       dict.dictAdd(key, &obj);
2916     }
2917     parser->getObj(&obj);
2918   }
2919   if (obj.isEOF()) {
2920     error(getPos(), "End of file in inline image");
2921     obj.free();
2922     dict.free();
2923     return NULL;
2924   }
2925   obj.free();
2927   // make stream
2928   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2929   str = str->addFilters(&dict);
2931   return str;
2934 void PdfParser::opImageData(Object args[], int numArgs) {
2935   error(getPos(), "Internal: got 'ID' operator");
2938 void PdfParser::opEndImage(Object args[], int numArgs) {
2939   error(getPos(), "Internal: got 'EI' operator");
2942 //------------------------------------------------------------------------
2943 // type 3 font operators
2944 //------------------------------------------------------------------------
2946 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2949 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2952 //------------------------------------------------------------------------
2953 // compatibility operators
2954 //------------------------------------------------------------------------
2956 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2957   ++ignoreUndef;
2960 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2961   if (ignoreUndef > 0)
2962     --ignoreUndef;
2965 //------------------------------------------------------------------------
2966 // marked content operators
2967 //------------------------------------------------------------------------
2969 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2970   if (printCommands) {
2971     printf("  marked content: %s ", args[0].getName());
2972     if (numArgs == 2)
2973       args[2].print(stdout);
2974     printf("\n");
2975     fflush(stdout);
2976   }
2978   if(numArgs == 2) {
2979     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
2980   } else {
2981     //out->beginMarkedContent(args[0].getName());
2982   }
2985 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
2986   //out->endMarkedContent();
2989 void PdfParser::opMarkPoint(Object args[], int numArgs) {
2990   if (printCommands) {
2991     printf("  mark point: %s ", args[0].getName());
2992     if (numArgs == 2)
2993       args[2].print(stdout);
2994     printf("\n");
2995     fflush(stdout);
2996   }
2998   if(numArgs == 2) {
2999     //out->markPoint(args[0].getName(),args[1].getDict());
3000   } else {
3001     //out->markPoint(args[0].getName());
3002   }
3006 //------------------------------------------------------------------------
3007 // misc
3008 //------------------------------------------------------------------------
3010 void PdfParser::saveState() {
3011   builder->saveState();
3012   state = state->save();
3013   clipHistory = clipHistory->save();
3016 void PdfParser::restoreState() {
3017   clipHistory = clipHistory->restore();
3018   state = state->restore();
3019   builder->restoreState();
3022 void PdfParser::pushResources(Dict *resDict) {
3023   res = new GfxResources(xref, resDict, res);
3026 void PdfParser::popResources() {
3027   GfxResources *resPtr;
3029   resPtr = res->getNext();
3030   delete res;
3031   res = resPtr;
3034 void PdfParser::setDefaultApproximationPrecision() {
3035   int i;
3037   for (i = 0; i < pdfNumShadingTypes; ++i) {
3038     setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3039   }
3042 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3043                                           int maxDepth) {
3045   if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3046     return;
3047   }
3048   colorDeltas[shadingType-1] = dblToCol(colorDelta);
3049   maxDepths[shadingType-1] = maxDepth;
3052 //------------------------------------------------------------------------
3053 // ClipHistoryEntry
3054 //------------------------------------------------------------------------
3056 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3057     if (clipPathA) {
3058         clipPath = clipPathA->copy();
3059     } else {
3060         clipPath = NULL;
3061     }
3062     clipType = clipTypeA;
3063     saved = NULL;
3066 ClipHistoryEntry::~ClipHistoryEntry() {
3067     if (clipPath) {
3068         delete clipPath;
3069     }
3072 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3073     // Free previous clip path
3074     if (clipPath) {
3075         delete clipPath;
3076     }
3077     if (clipPathA) {
3078         clipPath = clipPathA->copy();
3079         clipType = clipTypeA;
3080     } else {
3081         clipPath = NULL;
3082     }
3085 ClipHistoryEntry *ClipHistoryEntry::save() {
3086     ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3087     newEntry->saved = this;
3089     return newEntry;
3092 ClipHistoryEntry *ClipHistoryEntry::restore() {
3093     ClipHistoryEntry *oldEntry;
3095     if (saved) {
3096         oldEntry = saved;
3097         saved = NULL;
3098         delete this;
3099     } else {
3100         oldEntry = this;
3101     }
3103     return oldEntry;
3106 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3107     if (other->clipPath) {
3108         this->clipPath = other->clipPath->copy();
3109         this->clipType = other->clipType;
3110     } else {
3111         this->clipPath = NULL;
3112     }
3113     saved = NULL;
3116 #endif /* HAVE_POPPLER */