Code

Added initial transparency group and softmask support
[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 // Max recursive depth for a function shading fill.
64 #define functionMaxDepth 6
66 // Max delta allowed in any color component for a function shading fill.
67 #define functionColorDelta (dblToCol(1 / 2.0))
69 // Max number of splits along the t axis for an axial shading fill.
70 #define axialMaxSplits 256
72 // Max delta allowed in any color component for an axial shading fill.
73 #define axialColorDelta (dblToCol(1 / 2.0))
75 // Max number of splits along the t axis for a radial shading fill.
76 #define radialMaxSplits 256
78 // Max delta allowed in any color component for a radial shading fill.
79 #define radialColorDelta (dblToCol(1 / 2.0))
81 // Max recursive depth for a Gouraud triangle shading fill.
82 #define gouraudMaxDepth 6
84 // Max delta allowed in any color component for a Gouraud triangle
85 // shading fill.
86 #define gouraudColorDelta (dblToCol(1 / 2.0))
88 // Max recursive depth for a patch mesh shading fill.
89 #define patchMaxDepth 6
91 // Max delta allowed in any color component for a patch mesh shading
92 // fill.
93 #define patchColorDelta (dblToCol(1 / 2.0))
95 // Max number of operators kept in the history list.
96 #define maxOperatorHistoryDepth 16
98 //------------------------------------------------------------------------
99 // Operator table
100 //------------------------------------------------------------------------
102 #ifdef WIN32 // this works around a bug in the VC7 compiler
103 #  pragma optimize("",off)
104 #endif
106 PdfOperator PdfParser::opTab[] = {
107   {"\"",  3, {tchkNum,    tchkNum,    tchkString},
108           &PdfParser::opMoveSetShowText},
109   {"'",   1, {tchkString},
110           &PdfParser::opMoveShowText},
111   {"B",   0, {tchkNone},
112           &PdfParser::opFillStroke},
113   {"B*",  0, {tchkNone},
114           &PdfParser::opEOFillStroke},
115   {"BDC", 2, {tchkName,   tchkProps},
116           &PdfParser::opBeginMarkedContent},
117   {"BI",  0, {tchkNone},
118           &PdfParser::opBeginImage},
119   {"BMC", 1, {tchkName},
120           &PdfParser::opBeginMarkedContent},
121   {"BT",  0, {tchkNone},
122           &PdfParser::opBeginText},
123   {"BX",  0, {tchkNone},
124           &PdfParser::opBeginIgnoreUndef},
125   {"CS",  1, {tchkName},
126           &PdfParser::opSetStrokeColorSpace},
127   {"DP",  2, {tchkName,   tchkProps},
128           &PdfParser::opMarkPoint},
129   {"Do",  1, {tchkName},
130           &PdfParser::opXObject},
131   {"EI",  0, {tchkNone},
132           &PdfParser::opEndImage},
133   {"EMC", 0, {tchkNone},
134           &PdfParser::opEndMarkedContent},
135   {"ET",  0, {tchkNone},
136           &PdfParser::opEndText},
137   {"EX",  0, {tchkNone},
138           &PdfParser::opEndIgnoreUndef},
139   {"F",   0, {tchkNone},
140           &PdfParser::opFill},
141   {"G",   1, {tchkNum},
142           &PdfParser::opSetStrokeGray},
143   {"ID",  0, {tchkNone},
144           &PdfParser::opImageData},
145   {"J",   1, {tchkInt},
146           &PdfParser::opSetLineCap},
147   {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
148           &PdfParser::opSetStrokeCMYKColor},
149   {"M",   1, {tchkNum},
150           &PdfParser::opSetMiterLimit},
151   {"MP",  1, {tchkName},
152           &PdfParser::opMarkPoint},
153   {"Q",   0, {tchkNone},
154           &PdfParser::opRestore},
155   {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
156           &PdfParser::opSetStrokeRGBColor},
157   {"S",   0, {tchkNone},
158           &PdfParser::opStroke},
159   {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
160           &PdfParser::opSetStrokeColor},
161   {"SCN", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
162                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
163                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
164                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
165                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
166                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
167                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
168                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
169                 tchkSCN},
170           &PdfParser::opSetStrokeColorN},
171   {"T*",  0, {tchkNone},
172           &PdfParser::opTextNextLine},
173   {"TD",  2, {tchkNum,    tchkNum},
174           &PdfParser::opTextMoveSet},
175   {"TJ",  1, {tchkArray},
176           &PdfParser::opShowSpaceText},
177   {"TL",  1, {tchkNum},
178           &PdfParser::opSetTextLeading},
179   {"Tc",  1, {tchkNum},
180           &PdfParser::opSetCharSpacing},
181   {"Td",  2, {tchkNum,    tchkNum},
182           &PdfParser::opTextMove},
183   {"Tf",  2, {tchkName,   tchkNum},
184           &PdfParser::opSetFont},
185   {"Tj",  1, {tchkString},
186           &PdfParser::opShowText},
187   {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
188               tchkNum,    tchkNum},
189           &PdfParser::opSetTextMatrix},
190   {"Tr",  1, {tchkInt},
191           &PdfParser::opSetTextRender},
192   {"Ts",  1, {tchkNum},
193           &PdfParser::opSetTextRise},
194   {"Tw",  1, {tchkNum},
195           &PdfParser::opSetWordSpacing},
196   {"Tz",  1, {tchkNum},
197           &PdfParser::opSetHorizScaling},
198   {"W",   0, {tchkNone},
199           &PdfParser::opClip},
200   {"W*",  0, {tchkNone},
201           &PdfParser::opEOClip},
202   {"b",   0, {tchkNone},
203           &PdfParser::opCloseFillStroke},
204   {"b*",  0, {tchkNone},
205           &PdfParser::opCloseEOFillStroke},
206   {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
207               tchkNum,    tchkNum},
208           &PdfParser::opCurveTo},
209   {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
210               tchkNum,    tchkNum},
211           &PdfParser::opConcat},
212   {"cs",  1, {tchkName},
213           &PdfParser::opSetFillColorSpace},
214   {"d",   2, {tchkArray,  tchkNum},
215           &PdfParser::opSetDash},
216   {"d0",  2, {tchkNum,    tchkNum},
217           &PdfParser::opSetCharWidth},
218   {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
219               tchkNum,    tchkNum},
220           &PdfParser::opSetCacheDevice},
221   {"f",   0, {tchkNone},
222           &PdfParser::opFill},
223   {"f*",  0, {tchkNone},
224           &PdfParser::opEOFill},
225   {"g",   1, {tchkNum},
226           &PdfParser::opSetFillGray},
227   {"gs",  1, {tchkName},
228           &PdfParser::opSetExtGState},
229   {"h",   0, {tchkNone},
230           &PdfParser::opClosePath},
231   {"i",   1, {tchkNum},
232           &PdfParser::opSetFlat},
233   {"j",   1, {tchkInt},
234           &PdfParser::opSetLineJoin},
235   {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
236           &PdfParser::opSetFillCMYKColor},
237   {"l",   2, {tchkNum,    tchkNum},
238           &PdfParser::opLineTo},
239   {"m",   2, {tchkNum,    tchkNum},
240           &PdfParser::opMoveTo},
241   {"n",   0, {tchkNone},
242           &PdfParser::opEndPath},
243   {"q",   0, {tchkNone},
244           &PdfParser::opSave},
245   {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
246           &PdfParser::opRectangle},
247   {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
248           &PdfParser::opSetFillRGBColor},
249   {"ri",  1, {tchkName},
250           &PdfParser::opSetRenderingIntent},
251   {"s",   0, {tchkNone},
252           &PdfParser::opCloseStroke},
253   {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
254           &PdfParser::opSetFillColor},
255   {"scn", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
256                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
257                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
258                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
259                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
260                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
261                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
262                 tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
263                 tchkSCN},
264           &PdfParser::opSetFillColorN},
265   {"sh",  1, {tchkName},
266           &PdfParser::opShFill},
267   {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
268           &PdfParser::opCurveTo1},
269   {"w",   1, {tchkNum},
270           &PdfParser::opSetLineWidth},
271   {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
272           &PdfParser::opCurveTo2}
273 };
275 #ifdef WIN32 // this works around a bug in the VC7 compiler
276 #  pragma optimize("",on)
277 #endif
279 #define numOps (sizeof(opTab) / sizeof(PdfOperator))
281 //------------------------------------------------------------------------
282 // PdfParser
283 //------------------------------------------------------------------------
285 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
286                      int pageNum, int rotate, Dict *resDict, PDFRectangle *cropBox) {
288   int i;
290   xref = xrefA;
291   subPage = gFalse;
292   printCommands = false;
294   // start the resource stack
295   res = new GfxResources(xref, resDict, NULL);
297   // initialize
298   state = new GfxState(72.0, 72.0, cropBox, rotate, gTrue);
299   clipHistory = new ClipHistoryEntry();
300   fontChanged = gFalse;
301   clip = clipNone;
302   ignoreUndef = 0;
303   operatorHistory = NULL;
304   builder = builderA;
305   builder->setDocumentSize(state->getPageWidth()*PX_PER_PT,
306                            state->getPageHeight()*PX_PER_PT);
308   double *ctm = state->getCTM();
309   double scaledCTM[6];
310   for (i = 0; i < 6; ++i) {
311     baseMatrix[i] = ctm[i];
312     scaledCTM[i] = PX_PER_PT * ctm[i];
313   }
314   saveState();
315   builder->setTransform((double*)&scaledCTM);
316   formDepth = 0;
318   // set crop box
319   if (cropBox) {
320     if (printCommands)
321         printf("cropBox: %f %f %f %f\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
322     // do not clip if it's not needed
323     if (cropBox->x1 != 0.0 || cropBox->y1 != 0.0 ||
324         cropBox->x2 != state->getPageWidth() || cropBox->y2 != state->getPageHeight()) {
325         
326         state->moveTo(cropBox->x1, cropBox->y1);
327         state->lineTo(cropBox->x2, cropBox->y1);
328         state->lineTo(cropBox->x2, cropBox->y2);
329         state->lineTo(cropBox->x1, cropBox->y2);
330         state->closePath();
331         state->clip();
332         clipHistory->setClip(state->getPath(), clipNormal);
333         builder->setClipPath(state);
334         state->clearPath();
335     }
336   }
337   pushOperator("startPage");
340 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
341                      Dict *resDict, PDFRectangle *box) {
343   int i;
345   xref = xrefA;
346   subPage = gTrue;
347   printCommands = false;
349   // start the resource stack
350   res = new GfxResources(xref, resDict, NULL);
352   // initialize
353   operatorHistory = NULL;
354   builder = builderA;
355   state = new GfxState(72, 72, box, 0, gFalse);
356   clipHistory = new ClipHistoryEntry();
357   fontChanged = gFalse;
358   clip = clipNone;
359   ignoreUndef = 0;
360   for (i = 0; i < 6; ++i) {
361     baseMatrix[i] = state->getCTM()[i];
362   }
363   formDepth = 0;
366 PdfParser::~PdfParser() {
367   while (state->hasSaves()) {
368     restoreState();
369   }
370   if (!subPage) {
371     //out->endPage();
372   }
373   while (res) {
374     popResources();
375   }
376   if (state) {
377     delete state;
378   }
379   if (clipHistory) {
380     delete clipHistory;
381   }
384 void PdfParser::parse(Object *obj, GBool topLevel) {
385   Object obj2;
386   int i;
388   if (obj->isArray()) {
389     for (i = 0; i < obj->arrayGetLength(); ++i) {
390       obj->arrayGet(i, &obj2);
391       if (!obj2.isStream()) {
392         error(-1, "Weird page contents");
393         obj2.free();
394         return;
395       }
396       obj2.free();
397     }
398   } else if (!obj->isStream()) {
399     error(-1, "Weird page contents");
400     return;
401   }
402   parser = new Parser(xref, new Lexer(xref, obj), gFalse);
403   go(topLevel);
404   delete parser;
405   parser = NULL;
408 void PdfParser::go(GBool topLevel) {
409   Object obj;
410   Object args[maxArgs];
411   int numArgs, i;
412   int lastAbortCheck;
414   // scan a sequence of objects
415   numArgs = 0;
416   parser->getObj(&obj);
417   while (!obj.isEOF()) {
419     // got a command - execute it
420     if (obj.isCmd()) {
421       if (printCommands) {
422         obj.print(stdout);
423         for (i = 0; i < numArgs; ++i) {
424           printf(" ");
425           args[i].print(stdout);
426         }
427         printf("\n");
428         fflush(stdout);
429       }
431       // Run the operation
432       execOp(&obj, args, numArgs);
434       obj.free();
435       for (i = 0; i < numArgs; ++i)
436         args[i].free();
437       numArgs = 0;
439     // got an argument - save it
440     } else if (numArgs < maxArgs) {
441       args[numArgs++] = obj;
443     // too many arguments - something is wrong
444     } else {
445       error(getPos(), "Too many args in content stream");
446       if (printCommands) {
447         printf("throwing away arg: ");
448         obj.print(stdout);
449         printf("\n");
450         fflush(stdout);
451       }
452       obj.free();
453     }
455     // grab the next object
456     parser->getObj(&obj);
457   }
458   obj.free();
460   // args at end with no command
461   if (numArgs > 0) {
462     error(getPos(), "Leftover args in content stream");
463     if (printCommands) {
464       printf("%d leftovers:", numArgs);
465       for (i = 0; i < numArgs; ++i) {
466         printf(" ");
467         args[i].print(stdout);
468       }
469       printf("\n");
470       fflush(stdout);
471     }
472     for (i = 0; i < numArgs; ++i)
473       args[i].free();
474   }
477 void PdfParser::pushOperator(const char *name) {
478     OpHistoryEntry *newEntry = new OpHistoryEntry;
479     newEntry->name = name;
480     newEntry->state = NULL;
481     newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
482     newEntry->next = operatorHistory;
483     operatorHistory = newEntry;
485     // Truncate list if needed
486     if (operatorHistory->depth > maxOperatorHistoryDepth) {
487         OpHistoryEntry *curr = operatorHistory;
488         OpHistoryEntry *prev = NULL;
489         while (curr && curr->next != NULL) {
490             curr->depth--;
491             prev = curr;
492             curr = curr->next;
493         }
494         if (prev) {
495             if (curr->state != NULL)
496                 delete curr->state;
497             delete curr;
498             prev->next = NULL;
499         }
500     }
503 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
504     OpHistoryEntry *prev = NULL;
505     if (operatorHistory != NULL && look_back > 0) {
506         prev = operatorHistory->next;
507         while (--look_back > 0 && prev != NULL) {
508             prev = prev->next;
509         }
510     }
511     if (prev != NULL) {
512         return prev->name;
513     } else {
514         return "";
515     }
518 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
519   PdfOperator *op;
520   char *name;
521   Object *argPtr;
522   int i;
524   // find operator
525   name = cmd->getCmd();
526   if (!(op = findOp(name))) {
527     if (ignoreUndef == 0)
528       error(getPos(), "Unknown operator '%s'", name);
529     return;
530   }
532   // type check args
533   argPtr = args;
534   if (op->numArgs >= 0) {
535     if (numArgs < op->numArgs) {
536       error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
537       return;
538     }
539     if (numArgs > op->numArgs) {
540 #if 0
541       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
542 #endif
543       argPtr += numArgs - op->numArgs;
544       numArgs = op->numArgs;
545     }
546   } else {
547     if (numArgs > -op->numArgs) {
548       error(getPos(), "Too many (%d) args to '%s' operator",
549             numArgs, name);
550       return;
551     }
552   }
553   for (i = 0; i < numArgs; ++i) {
554     if (!checkArg(&argPtr[i], op->tchk[i])) {
555       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
556             i, name, argPtr[i].getTypeName());
557       return;
558     }
559   }
561   // add to history
562   pushOperator((char*)&op->name);
564   // do it
565   (this->*op->func)(argPtr, numArgs);
568 PdfOperator *PdfParser::findOp(char *name) {
569   int a, b, m, cmp;
571   a = -1;
572   b = numOps;
573   // invariant: opTab[a] < name < opTab[b]
574   while (b - a > 1) {
575     m = (a + b) / 2;
576     cmp = strcmp(opTab[m].name, name);
577     if (cmp < 0)
578       a = m;
579     else if (cmp > 0)
580       b = m;
581     else
582       a = b = m;
583   }
584   if (cmp != 0)
585     return NULL;
586   return &opTab[a];
589 GBool PdfParser::checkArg(Object *arg, TchkType type) {
590   switch (type) {
591   case tchkBool:   return arg->isBool();
592   case tchkInt:    return arg->isInt();
593   case tchkNum:    return arg->isNum();
594   case tchkString: return arg->isString();
595   case tchkName:   return arg->isName();
596   case tchkArray:  return arg->isArray();
597   case tchkProps:  return arg->isDict() || arg->isName();
598   case tchkSCN:    return arg->isNum() || arg->isName();
599   case tchkNone:   return gFalse;
600   }
601   return gFalse;
604 int PdfParser::getPos() {
605   return parser ? parser->getPos() : -1;
608 //------------------------------------------------------------------------
609 // graphics state operators
610 //------------------------------------------------------------------------
612 void PdfParser::opSave(Object args[], int numArgs) {
613   saveState();
616 void PdfParser::opRestore(Object args[], int numArgs) {
617   restoreState();
620 void PdfParser::opConcat(Object args[], int numArgs) {
621   state->concatCTM(args[0].getNum(), args[1].getNum(),
622                    args[2].getNum(), args[3].getNum(),
623                    args[4].getNum(), args[5].getNum());
624   const char *prevOp = getPreviousOperator();
625   double a0 = args[0].getNum();
626   double a1 = args[1].getNum();
627   double a2 = args[2].getNum();
628   double a3 = args[3].getNum();
629   double a4 = args[4].getNum();
630   double a5 = args[5].getNum();
631   if (!strcmp(prevOp, "q")) {
632       builder->setTransform(a0, a1, a2, a3, a4, a5);
633   } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
634       // multiply it with the previous transform
635       double otherMatrix[6];
636       if (!builder->getTransform((double*)&otherMatrix)) { // invalid transform
637           // construct identity matrix
638           otherMatrix[0] = otherMatrix[3] = 1.0;
639           otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
640       }
641       double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
642       double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
643       double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
644       double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
645       double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
646       double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
647       builder->setTransform(c0, c1, c2, c3, c4, c5);
648   } else {
649       builder->pushGroup();
650       builder->setTransform(a0, a1, a2, a3, a4, a5);
651   }
652   fontChanged = gTrue;
655 void PdfParser::opSetDash(Object args[], int numArgs) {
656   Array *a;
657   int length;
658   Object obj;
659   double *dash;
660   int i;
662   a = args[0].getArray();
663   length = a->getLength();
664   if (length == 0) {
665     dash = NULL;
666   } else {
667     dash = (double *)gmallocn(length, sizeof(double));
668     for (i = 0; i < length; ++i) {
669       dash[i] = a->get(i, &obj)->getNum();
670       obj.free();
671     }
672   }
673   state->setLineDash(dash, length, args[1].getNum());
674   builder->updateStyle(state);
677 void PdfParser::opSetFlat(Object args[], int numArgs) {
678   state->setFlatness((int)args[0].getNum());
681 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
682   state->setLineJoin(args[0].getInt());
683   builder->updateStyle(state);
686 void PdfParser::opSetLineCap(Object args[], int numArgs) {
687   state->setLineCap(args[0].getInt());
688   builder->updateStyle(state);
691 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
692   state->setMiterLimit(args[0].getNum());
693   builder->updateStyle(state);
696 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
697   state->setLineWidth(args[0].getNum());
698   builder->updateStyle(state);
701 void PdfParser::opSetExtGState(Object args[], int numArgs) {
702   Object obj1, obj2, obj3, obj4, obj5;
703   GfxBlendMode mode;
704   GBool haveFillOP;
705   Function *funcs[4];
706   GfxColor backdropColor;
707   GBool haveBackdropColor;
708   GfxColorSpace *blendingColorSpace;
709   GBool alpha, isolated, knockout;
710   int i;
712   if (!res->lookupGState(args[0].getName(), &obj1)) {
713     return;
714   }
715   if (!obj1.isDict()) {
716     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
717     obj1.free();
718     return;
719   }
720   if (printCommands) {
721     printf("  gfx state dict: ");
722     obj1.print();
723     printf("\n");
724   }
726   // transparency support: blend mode, fill/stroke opacity
727   if (!obj1.dictLookup("BM", &obj2)->isNull()) {
728     if (state->parseBlendMode(&obj2, &mode)) {
729       state->setBlendMode(mode);
730     } else {
731       error(getPos(), "Invalid blend mode in ExtGState");
732     }
733   }
734   obj2.free();
735   if (obj1.dictLookup("ca", &obj2)->isNum()) {
736     state->setFillOpacity(obj2.getNum());
737   }
738   obj2.free();
739   if (obj1.dictLookup("CA", &obj2)->isNum()) {
740     state->setStrokeOpacity(obj2.getNum());
741   }
742   obj2.free();
744   // fill/stroke overprint
745   if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
746     state->setFillOverprint(obj2.getBool());
747   }
748   obj2.free();
749   if (obj1.dictLookup("OP", &obj2)->isBool()) {
750     state->setStrokeOverprint(obj2.getBool());
751     if (!haveFillOP) {
752       state->setFillOverprint(obj2.getBool());
753     }
754   }
755   obj2.free();
757   // stroke adjust
758   if (obj1.dictLookup("SA", &obj2)->isBool()) {
759     state->setStrokeAdjust(obj2.getBool());
760   }
761   obj2.free();
763   // transfer function
764   if (obj1.dictLookup("TR2", &obj2)->isNull()) {
765     obj2.free();
766     obj1.dictLookup("TR", &obj2);
767   }
768   if (obj2.isName("Default") ||
769       obj2.isName("Identity")) {
770     funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
771     state->setTransfer(funcs);
772   } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
773     for (i = 0; i < 4; ++i) {
774       obj2.arrayGet(i, &obj3);
775       funcs[i] = Function::parse(&obj3);
776       obj3.free();
777       if (!funcs[i]) {
778         break;
779       }
780     }
781     if (i == 4) {
782       state->setTransfer(funcs);
783     }
784   } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
785     if ((funcs[0] = Function::parse(&obj2))) {
786       funcs[1] = funcs[2] = funcs[3] = NULL;
787       state->setTransfer(funcs);
788     }
789   } else if (!obj2.isNull()) {
790     error(getPos(), "Invalid transfer function in ExtGState");
791   }
792   obj2.free();
794   // soft mask
795   if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
796     if (obj2.isName("None")) {
797       builder->clearSoftMask(state);
798     } else if (obj2.isDict()) {
799       if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
800         alpha = gTrue;
801       } else { // "Luminosity"
802         alpha = gFalse;
803       }
804       obj3.free();
805       funcs[0] = NULL;
806       if (!obj2.dictLookup("TR", &obj3)->isNull()) {
807         funcs[0] = Function::parse(&obj3);
808         if (funcs[0]->getInputSize() != 1 ||
809             funcs[0]->getOutputSize() != 1) {
810           error(getPos(),
811                 "Invalid transfer function in soft mask in ExtGState");
812           delete funcs[0];
813           funcs[0] = NULL;
814         }
815       }
816       obj3.free();
817       if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
818         for (i = 0; i < gfxColorMaxComps; ++i) {
819           backdropColor.c[i] = 0;
820         }
821         for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
822           obj3.arrayGet(i, &obj4);
823           if (obj4.isNum()) {
824             backdropColor.c[i] = dblToCol(obj4.getNum());
825           }
826           obj4.free();
827         }
828       }
829       obj3.free();
830       if (obj2.dictLookup("G", &obj3)->isStream()) {
831         if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
832           blendingColorSpace = NULL;
833           isolated = knockout = gFalse;
834           if (!obj4.dictLookup("CS", &obj5)->isNull()) {
835             blendingColorSpace = GfxColorSpace::parse(&obj5);
836           }
837           obj5.free();
838           if (obj4.dictLookup("I", &obj5)->isBool()) {
839             isolated = obj5.getBool();
840           }
841           obj5.free();
842           if (obj4.dictLookup("K", &obj5)->isBool()) {
843             knockout = obj5.getBool();
844           }
845           obj5.free();
846           if (!haveBackdropColor) {
847             if (blendingColorSpace) {
848               blendingColorSpace->getDefaultColor(&backdropColor);
849             } else {
850               //~ need to get the parent or default color space (?)
851               for (i = 0; i < gfxColorMaxComps; ++i) {
852                 backdropColor.c[i] = 0;
853               }
854             }
855           }
856           doSoftMask(&obj3, alpha, blendingColorSpace,
857                      isolated, knockout, funcs[0], &backdropColor);
858           if (funcs[0]) {
859             delete funcs[0];
860           }
861         } else {
862           error(getPos(), "Invalid soft mask in ExtGState - missing group");
863         }
864         obj4.free();
865       } else {
866         error(getPos(), "Invalid soft mask in ExtGState - missing group");
867       }
868       obj3.free();
869     } else if (!obj2.isNull()) {
870       error(getPos(), "Invalid soft mask in ExtGState");
871     }
872   }
873   obj2.free();
875   obj1.free();
878 void PdfParser::doSoftMask(Object *str, GBool alpha,
879                      GfxColorSpace *blendingColorSpace,
880                      GBool isolated, GBool knockout,
881                      Function *transferFunc, GfxColor *backdropColor) {
882   Dict *dict, *resDict;
883   double m[6], bbox[4];
884   Object obj1, obj2;
885   int i;
887   // check for excessive recursion
888   if (formDepth > 20) {
889     return;
890   }
892   // get stream dict
893   dict = str->streamGetDict();
895   // check form type
896   dict->lookup("FormType", &obj1);
897   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
898     error(getPos(), "Unknown form type");
899   }
900   obj1.free();
902   // get bounding box
903   dict->lookup("BBox", &obj1);
904   if (!obj1.isArray()) {
905     obj1.free();
906     error(getPos(), "Bad form bounding box");
907     return;
908   }
909   for (i = 0; i < 4; ++i) {
910     obj1.arrayGet(i, &obj2);
911     bbox[i] = obj2.getNum();
912     obj2.free();
913   }
914   obj1.free();
916   // get matrix
917   dict->lookup("Matrix", &obj1);
918   if (obj1.isArray()) {
919     for (i = 0; i < 6; ++i) {
920       obj1.arrayGet(i, &obj2);
921       m[i] = obj2.getNum();
922       obj2.free();
923     }
924   } else {
925     m[0] = 1; m[1] = 0;
926     m[2] = 0; m[3] = 1;
927     m[4] = 0; m[5] = 0;
928   }
929   obj1.free();
931   // get resources
932   dict->lookup("Resources", &obj1);
933   resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
935   // draw it
936   ++formDepth;
937   doForm1(str, resDict, m, bbox, gTrue, gTrue,
938           blendingColorSpace, isolated, knockout,
939           alpha, transferFunc, backdropColor);
940   --formDepth;
942   if (blendingColorSpace) {
943     delete blendingColorSpace;
944   }
945   obj1.free();
948 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
951 //------------------------------------------------------------------------
952 // color operators
953 //------------------------------------------------------------------------
955 void PdfParser::opSetFillGray(Object args[], int numArgs) {
956   GfxColor color;
958   state->setFillPattern(NULL);
959   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
960   color.c[0] = dblToCol(args[0].getNum());
961   state->setFillColor(&color);
962   builder->updateStyle(state);
965 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
966   GfxColor color;
968   state->setStrokePattern(NULL);
969   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
970   color.c[0] = dblToCol(args[0].getNum());
971   state->setStrokeColor(&color);
972   builder->updateStyle(state);
975 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
976   GfxColor color;
977   int i;
979   state->setFillPattern(NULL);
980   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
981   for (i = 0; i < 4; ++i) {
982     color.c[i] = dblToCol(args[i].getNum());
983   }
984   state->setFillColor(&color);
985   builder->updateStyle(state);
988 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
989   GfxColor color;
990   int i;
992   state->setStrokePattern(NULL);
993   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
994   for (i = 0; i < 4; ++i) {
995     color.c[i] = dblToCol(args[i].getNum());
996   }
997   state->setStrokeColor(&color);
998   builder->updateStyle(state);
1001 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
1002   GfxColor color;
1003   int i;
1005   state->setFillPattern(NULL);
1006   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
1007   for (i = 0; i < 3; ++i) {
1008     color.c[i] = dblToCol(args[i].getNum());
1009   }
1010   state->setFillColor(&color);
1011   builder->updateStyle(state);
1014 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
1015   GfxColor color;
1016   int i;
1018   state->setStrokePattern(NULL);
1019   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1020   for (i = 0; i < 3; ++i) {
1021     color.c[i] = dblToCol(args[i].getNum());
1022   }
1023   state->setStrokeColor(&color);
1024   builder->updateStyle(state);
1027 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1028   Object obj;
1029   GfxColorSpace *colorSpace;
1030   GfxColor color;
1032   state->setFillPattern(NULL);
1033   res->lookupColorSpace(args[0].getName(), &obj);
1034   if (obj.isNull()) {
1035     colorSpace = GfxColorSpace::parse(&args[0]);
1036   } else {
1037     colorSpace = GfxColorSpace::parse(&obj);
1038   }
1039   obj.free();
1040   if (colorSpace) {
1041     state->setFillColorSpace(colorSpace);
1042     colorSpace->getDefaultColor(&color);
1043     state->setFillColor(&color);
1044     builder->updateStyle(state);
1045   } else {
1046     error(getPos(), "Bad color space (fill)");
1047   }
1050 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1051   Object obj;
1052   GfxColorSpace *colorSpace;
1053   GfxColor color;
1055   state->setStrokePattern(NULL);
1056   res->lookupColorSpace(args[0].getName(), &obj);
1057   if (obj.isNull()) {
1058     colorSpace = GfxColorSpace::parse(&args[0]);
1059   } else {
1060     colorSpace = GfxColorSpace::parse(&obj);
1061   }
1062   obj.free();
1063   if (colorSpace) {
1064     state->setStrokeColorSpace(colorSpace);
1065     colorSpace->getDefaultColor(&color);
1066     state->setStrokeColor(&color);
1067     builder->updateStyle(state);
1068   } else {
1069     error(getPos(), "Bad color space (stroke)");
1070   }
1073 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1074   GfxColor color;
1075   int i;
1077   if (numArgs != state->getFillColorSpace()->getNComps()) {
1078     error(getPos(), "Incorrect number of arguments in 'sc' command");
1079     return;
1080   }
1081   state->setFillPattern(NULL);
1082   for (i = 0; i < numArgs; ++i) {
1083     color.c[i] = dblToCol(args[i].getNum());
1084   }
1085   state->setFillColor(&color);
1086   builder->updateStyle(state);
1089 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1090   GfxColor color;
1091   int i;
1093   if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1094     error(getPos(), "Incorrect number of arguments in 'SC' command");
1095     return;
1096   }
1097   state->setStrokePattern(NULL);
1098   for (i = 0; i < numArgs; ++i) {
1099     color.c[i] = dblToCol(args[i].getNum());
1100   }
1101   state->setStrokeColor(&color);
1102   builder->updateStyle(state);
1105 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1106   GfxColor color;
1107   GfxPattern *pattern;
1108   int i;
1110   if (state->getFillColorSpace()->getMode() == csPattern) {
1111     if (numArgs > 1) {
1112       if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1113           numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1114                              ->getUnder()->getNComps()) {
1115         error(getPos(), "Incorrect number of arguments in 'scn' command");
1116         return;
1117       }
1118       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1119         if (args[i].isNum()) {
1120           color.c[i] = dblToCol(args[i].getNum());
1121         }
1122       }
1123       state->setFillColor(&color);
1124       builder->updateStyle(state);
1125     }
1126     if (args[numArgs-1].isName() &&
1127         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1128       state->setFillPattern(pattern);
1129     }
1131   } else {
1132     if (numArgs != state->getFillColorSpace()->getNComps()) {
1133       error(getPos(), "Incorrect number of arguments in 'scn' command");
1134       return;
1135     }
1136     state->setFillPattern(NULL);
1137     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1138       if (args[i].isNum()) {
1139         color.c[i] = dblToCol(args[i].getNum());
1140       }
1141     }
1142     state->setFillColor(&color);
1143     builder->updateStyle(state);
1144   }
1147 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1148   GfxColor color;
1149   GfxPattern *pattern;
1150   int i;
1152   if (state->getStrokeColorSpace()->getMode() == csPattern) {
1153     if (numArgs > 1) {
1154       if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1155                ->getUnder() ||
1156           numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1157                              ->getUnder()->getNComps()) {
1158         error(getPos(), "Incorrect number of arguments in 'SCN' command");
1159         return;
1160       }
1161       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1162         if (args[i].isNum()) {
1163           color.c[i] = dblToCol(args[i].getNum());
1164         }
1165       }
1166       state->setStrokeColor(&color);
1167       builder->updateStyle(state);
1168     }
1169     if (args[numArgs-1].isName() &&
1170         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1171       state->setStrokePattern(pattern);
1172     }
1174   } else {
1175     if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1176       error(getPos(), "Incorrect number of arguments in 'SCN' command");
1177       return;
1178     }
1179     state->setStrokePattern(NULL);
1180     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1181       if (args[i].isNum()) {
1182         color.c[i] = dblToCol(args[i].getNum());
1183       }
1184     }
1185     state->setStrokeColor(&color);
1186     builder->updateStyle(state);
1187   }
1190 //------------------------------------------------------------------------
1191 // path segment operators
1192 //------------------------------------------------------------------------
1194 void PdfParser::opMoveTo(Object args[], int numArgs) {
1195   state->moveTo(args[0].getNum(), args[1].getNum());
1198 void PdfParser::opLineTo(Object args[], int numArgs) {
1199   if (!state->isCurPt()) {
1200     error(getPos(), "No current point in lineto");
1201     return;
1202   }
1203   state->lineTo(args[0].getNum(), args[1].getNum());
1206 void PdfParser::opCurveTo(Object args[], int numArgs) {
1207   double x1, y1, x2, y2, x3, y3;
1209   if (!state->isCurPt()) {
1210     error(getPos(), "No current point in curveto");
1211     return;
1212   }
1213   x1 = args[0].getNum();
1214   y1 = args[1].getNum();
1215   x2 = args[2].getNum();
1216   y2 = args[3].getNum();
1217   x3 = args[4].getNum();
1218   y3 = args[5].getNum();
1219   state->curveTo(x1, y1, x2, y2, x3, y3);
1222 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1223   double x1, y1, x2, y2, x3, y3;
1225   if (!state->isCurPt()) {
1226     error(getPos(), "No current point in curveto1");
1227     return;
1228   }
1229   x1 = state->getCurX();
1230   y1 = state->getCurY();
1231   x2 = args[0].getNum();
1232   y2 = args[1].getNum();
1233   x3 = args[2].getNum();
1234   y3 = args[3].getNum();
1235   state->curveTo(x1, y1, x2, y2, x3, y3);
1238 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1239   double x1, y1, x2, y2, x3, y3;
1241   if (!state->isCurPt()) {
1242     error(getPos(), "No current point in curveto2");
1243     return;
1244   }
1245   x1 = args[0].getNum();
1246   y1 = args[1].getNum();
1247   x2 = args[2].getNum();
1248   y2 = args[3].getNum();
1249   x3 = x2;
1250   y3 = y2;
1251   state->curveTo(x1, y1, x2, y2, x3, y3);
1254 void PdfParser::opRectangle(Object args[], int numArgs) {
1255   double x, y, w, h;
1257   x = args[0].getNum();
1258   y = args[1].getNum();
1259   w = args[2].getNum();
1260   h = args[3].getNum();
1261   state->moveTo(x, y);
1262   state->lineTo(x + w, y);
1263   state->lineTo(x + w, y + h);
1264   state->lineTo(x, y + h);
1265   state->closePath();
1268 void PdfParser::opClosePath(Object args[], int numArgs) {
1269   if (!state->isCurPt()) {
1270     error(getPos(), "No current point in closepath");
1271     return;
1272   }
1273   state->closePath();
1276 //------------------------------------------------------------------------
1277 // path painting operators
1278 //------------------------------------------------------------------------
1280 void PdfParser::opEndPath(Object args[], int numArgs) {
1281   doEndPath();
1284 void PdfParser::opStroke(Object args[], int numArgs) {
1285   if (!state->isCurPt()) {
1286     //error(getPos(), "No path in stroke");
1287     return;
1288   }
1289   if (state->isPath()) {
1290     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1291         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1292           doPatternStrokeFallback();
1293     } else {
1294       builder->addPath(state, false, true);
1295     }
1296   }
1297   doEndPath();
1300 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1301   if (!state->isCurPt()) {
1302     //error(getPos(), "No path in closepath/stroke");
1303     return;
1304   }
1305   state->closePath();
1306   if (state->isPath()) {
1307     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1308         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1309       doPatternStrokeFallback();
1310     } else {
1311       builder->addPath(state, false, true);
1312     }
1313   }
1314   doEndPath();
1317 void PdfParser::opFill(Object args[], int numArgs) {
1318   if (!state->isCurPt()) {
1319     //error(getPos(), "No path in fill");
1320     return;
1321   }
1322   if (state->isPath()) {
1323     if (state->getFillColorSpace()->getMode() == csPattern &&
1324         !builder->isPatternTypeSupported(state->getFillPattern())) {
1325       doPatternFillFallback(gFalse);
1326     } else {
1327       builder->addPath(state, true, false);
1328     }
1329   }
1330   doEndPath();
1333 void PdfParser::opEOFill(Object args[], int numArgs) {
1334   if (!state->isCurPt()) {
1335     //error(getPos(), "No path in eofill");
1336     return;
1337   }
1338   if (state->isPath()) {
1339     if (state->getFillColorSpace()->getMode() == csPattern &&
1340         !builder->isPatternTypeSupported(state->getFillPattern())) {
1341       doPatternFillFallback(gTrue);
1342     } else {
1343       builder->addPath(state, true, false, true);
1344     }
1345   }
1346   doEndPath();
1349 void PdfParser::opFillStroke(Object args[], int numArgs) {
1350   if (!state->isCurPt()) {
1351     //error(getPos(), "No path in fill/stroke");
1352     return;
1353   }
1354   if (state->isPath()) {
1355     doFillAndStroke(gFalse);
1356   } else {
1357     builder->addPath(state, true, true);
1358   }
1359   doEndPath();
1362 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1363   if (!state->isCurPt()) {
1364     //error(getPos(), "No path in closepath/fill/stroke");
1365     return;
1366   }
1367   if (state->isPath()) {
1368     state->closePath();
1369     doFillAndStroke(gFalse);
1370   }
1371   doEndPath();
1374 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1375   if (!state->isCurPt()) {
1376     //error(getPos(), "No path in eofill/stroke");
1377     return;
1378   }
1379   if (state->isPath()) {
1380     doFillAndStroke(gTrue);
1381   }
1382   doEndPath();
1385 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1386   if (!state->isCurPt()) {
1387     //error(getPos(), "No path in closepath/eofill/stroke");
1388     return;
1389   }
1390   if (state->isPath()) {
1391     state->closePath();
1392     doFillAndStroke(gTrue);
1393   }
1394   doEndPath();
1397 void PdfParser::doFillAndStroke(GBool eoFill) {
1398     GBool fillOk = gTrue, strokeOk = gTrue;
1399     if (state->getFillColorSpace()->getMode() == csPattern &&
1400         !builder->isPatternTypeSupported(state->getFillPattern())) {
1401         fillOk = gFalse;
1402     }
1403     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1404         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1405         strokeOk = gFalse;
1406     }
1407     if (fillOk && strokeOk) {
1408         builder->addPath(state, true, true, eoFill);
1409     } else {
1410         doPatternFillFallback(eoFill);
1411         doPatternStrokeFallback();
1412     }
1415 void PdfParser::doPatternFillFallback(GBool eoFill) {
1416   GfxPattern *pattern;
1418   if (!(pattern = state->getFillPattern())) {
1419     return;
1420   }
1421   switch (pattern->getType()) {
1422   case 1:
1423     break;
1424   case 2:
1425     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1426     break;
1427   default:
1428     error(getPos(), "Unimplemented pattern type (%d) in fill",
1429           pattern->getType());
1430     break;
1431   }
1434 void PdfParser::doPatternStrokeFallback() {
1435   GfxPattern *pattern;
1437   if (!(pattern = state->getStrokePattern())) {
1438     return;
1439   }
1440   switch (pattern->getType()) {
1441   case 1:
1442     break;
1443   case 2:
1444     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1445     break;
1446   default:
1447     error(getPos(), "Unimplemented pattern type (%d) in stroke",
1448           pattern->getType());
1449     break;
1450   }
1453 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1454                                              GBool stroke, GBool eoFill) {
1455   GfxShading *shading;
1456   GfxPath *savedPath;
1457   double *ctm, *btm, *ptm;
1458   double m[6], ictm[6], m1[6];
1459   double xMin, yMin, xMax, yMax;
1460   double det;
1462   shading = sPat->getShading();
1464   // save current graphics state
1465   savedPath = state->getPath()->copy();
1466   saveState();
1468   // clip to bbox
1469   if (0 ){//shading->getHasBBox()) {
1470     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1471     state->moveTo(xMin, yMin);
1472     state->lineTo(xMax, yMin);
1473     state->lineTo(xMax, yMax);
1474     state->lineTo(xMin, yMax);
1475     state->closePath();
1476     state->clip();
1477     //builder->clip(state);
1478     state->setPath(savedPath->copy());
1479   }
1481   // clip to current path
1482   if (stroke) {
1483     state->clipToStrokePath();
1484     //out->clipToStrokePath(state);
1485   } else {
1486     state->clip();
1487     if (eoFill) {
1488       builder->clip(state, true);
1489     } else {
1490       builder->clip(state);
1491     }
1492   }
1494   // set the color space
1495   state->setFillColorSpace(shading->getColorSpace()->copy());
1497   // background color fill
1498   if (shading->getHasBackground()) {
1499     state->setFillColor(shading->getBackground());
1500     builder->addPath(state, true, false);
1501   }
1502   state->clearPath();
1504   // construct a (pattern space) -> (current space) transform matrix
1505   ctm = state->getCTM();
1506   btm = baseMatrix;
1507   ptm = sPat->getMatrix();
1508   // iCTM = invert CTM
1509   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1510   ictm[0] = ctm[3] * det;
1511   ictm[1] = -ctm[1] * det;
1512   ictm[2] = -ctm[2] * det;
1513   ictm[3] = ctm[0] * det;
1514   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1515   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1516   // m1 = PTM * BTM = PTM * base transform matrix
1517   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1518   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1519   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1520   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1521   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1522   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1523   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1524   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1525   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1526   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1527   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1528   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1529   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1531   // set the new matrix
1532   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1533   builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1535   // do shading type-specific operations
1536   switch (shading->getType()) {
1537   case 1:
1538     doFunctionShFill((GfxFunctionShading *)shading);
1539     break;
1540   case 2:
1541   case 3:
1542     // no need to implement these
1543     break;
1544   case 4:
1545   case 5:
1546     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1547     break;
1548   case 6:
1549   case 7:
1550     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1551     break;
1552   }
1554   // restore graphics state
1555   restoreState();
1556   state->setPath(savedPath);
1559 void PdfParser::opShFill(Object args[], int numArgs) {
1560   GfxShading *shading;
1561   GfxPath *savedPath;
1562   double xMin, yMin, xMax, yMax;
1563   double gradientTransform[6];
1564   double *matrix = NULL;
1565   GBool savedState = gFalse;
1567   if (!(shading = res->lookupShading(args[0].getName()))) {
1568     return;
1569   }
1571   // save current graphics state
1572   if (shading->getType() != 2 && shading->getType() != 3) {
1573     savedPath = state->getPath()->copy();
1574     saveState();
1575     savedState = gTrue;
1576   } else {  // get gradient transform if possible
1577       // check proper operator sequence
1578       // first there should be one W(*) and then one 'cm' somewhere before 'sh'
1579       GBool seenClip, seenConcat;
1580       seenClip = (clipHistory->getClipPath() != NULL);
1581       seenConcat = gFalse;
1582       int i = 1;
1583       while (i <= maxOperatorHistoryDepth) {
1584         const char *opName = getPreviousOperator(i);
1585         if (!strcmp(opName, "cm")) {
1586           if (seenConcat) {   // more than one 'cm'
1587             break;
1588           } else {
1589             seenConcat = gTrue;
1590           }
1591         }
1592         i++;
1593       }
1595       if (seenConcat && seenClip) {
1596         if (builder->getTransform((double*)&gradientTransform)) {
1597           matrix = (double*)&gradientTransform;
1598           builder->setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);  // remove transform
1599         }
1600       }
1601   }
1603   // clip to bbox
1604   if (shading->getHasBBox()) {
1605     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1606     state->moveTo(xMin, yMin);
1607     state->lineTo(xMax, yMin);
1608     state->lineTo(xMax, yMax);
1609     state->lineTo(xMin, yMax);
1610     state->closePath();
1611     state->clip();
1612     if (savedState)
1613       builder->setClipPath(state);
1614     else
1615       builder->clip(state);
1616     state->clearPath();
1617   }
1619   // set the color space
1620   if (savedState)
1621     state->setFillColorSpace(shading->getColorSpace()->copy());
1623   // do shading type-specific operations
1624   switch (shading->getType()) {
1625   case 1:
1626     doFunctionShFill((GfxFunctionShading *)shading);
1627     break;
1628   case 2:
1629   case 3:
1630     if (clipHistory->getClipPath()) {
1631       builder->addShadedFill(shading, matrix, clipHistory->getClipPath(),
1632                              clipHistory->getClipType() == clipEO ? true : false);
1633     }
1634     break;
1635   case 4:
1636   case 5:
1637     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1638     break;
1639   case 6:
1640   case 7:
1641     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1642     break;
1643   }
1645   // restore graphics state
1646   if (savedState) {
1647     restoreState();
1648     state->setPath(savedPath);
1649   }
1651   delete shading;
1654 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1655   double x0, y0, x1, y1;
1656   GfxColor colors[4];
1658   shading->getDomain(&x0, &y0, &x1, &y1);
1659   shading->getColor(x0, y0, &colors[0]);
1660   shading->getColor(x0, y1, &colors[1]);
1661   shading->getColor(x1, y0, &colors[2]);
1662   shading->getColor(x1, y1, &colors[3]);
1663   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1666 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1667                             double x0, double y0,
1668                             double x1, double y1,
1669                             GfxColor *colors, int depth) {
1670   GfxColor fillColor;
1671   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1672   GfxColor colors2[4];
1673   double *matrix;
1674   double xM, yM;
1675   int nComps, i, j;
1677   nComps = shading->getColorSpace()->getNComps();
1678   matrix = shading->getMatrix();
1680   // compare the four corner colors
1681   for (i = 0; i < 4; ++i) {
1682     for (j = 0; j < nComps; ++j) {
1683       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1684         break;
1685       }
1686     }
1687     if (j < nComps) {
1688       break;
1689     }
1690   }
1692   // center of the rectangle
1693   xM = 0.5 * (x0 + x1);
1694   yM = 0.5 * (y0 + y1);
1696   // the four corner colors are close (or we hit the recursive limit)
1697   // -- fill the rectangle; but require at least one subdivision
1698   // (depth==0) to avoid problems when the four outer corners of the
1699   // shaded region are the same color
1700   if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1702     // use the center color
1703     shading->getColor(xM, yM, &fillColor);
1704     state->setFillColor(&fillColor);
1706     // fill the rectangle
1707     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1708                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1709     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1710                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1711     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1712                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1713     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1714                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1715     state->closePath();
1716     builder->addPath(state, true, false);
1717     state->clearPath();
1719   // the four corner colors are not close enough -- subdivide the
1720   // rectangle
1721   } else {
1723     // colors[0]       colorM0       colors[2]
1724     //   (x0,y0)       (xM,y0)       (x1,y0)
1725     //         +----------+----------+
1726     //         |          |          |
1727     //         |    UL    |    UR    |
1728     // color0M |       colorMM       | color1M
1729     // (x0,yM) +----------+----------+ (x1,yM)
1730     //         |       (xM,yM)       |
1731     //         |    LL    |    LR    |
1732     //         |          |          |
1733     //         +----------+----------+
1734     // colors[1]       colorM1       colors[3]
1735     //   (x0,y1)       (xM,y1)       (x1,y1)
1737     shading->getColor(x0, yM, &color0M);
1738     shading->getColor(x1, yM, &color1M);
1739     shading->getColor(xM, y0, &colorM0);
1740     shading->getColor(xM, y1, &colorM1);
1741     shading->getColor(xM, yM, &colorMM);
1743     // upper-left sub-rectangle
1744     colors2[0] = colors[0];
1745     colors2[1] = color0M;
1746     colors2[2] = colorM0;
1747     colors2[3] = colorMM;
1748     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1749     
1750     // lower-left sub-rectangle
1751     colors2[0] = color0M;
1752     colors2[1] = colors[1];
1753     colors2[2] = colorMM;
1754     colors2[3] = colorM1;
1755     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1756     
1757     // upper-right sub-rectangle
1758     colors2[0] = colorM0;
1759     colors2[1] = colorMM;
1760     colors2[2] = colors[2];
1761     colors2[3] = color1M;
1762     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1764     // lower-right sub-rectangle
1765     colors2[0] = colorMM;
1766     colors2[1] = colorM1;
1767     colors2[2] = color1M;
1768     colors2[3] = colors[3];
1769     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1770   }
1773 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1774   double x0, y0, x1, y1, x2, y2;
1775   GfxColor color0, color1, color2;
1776   int i;
1778   for (i = 0; i < shading->getNTriangles(); ++i) {
1779     shading->getTriangle(i, &x0, &y0, &color0,
1780                          &x1, &y1, &color1,
1781                          &x2, &y2, &color2);
1782     gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1783                         shading->getColorSpace()->getNComps(), 0);
1784   }
1787 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1788                               double x1, double y1, GfxColor *color1,
1789                               double x2, double y2, GfxColor *color2,
1790                               int nComps, int depth) {
1791   double x01, y01, x12, y12, x20, y20;
1792   GfxColor color01, color12, color20;
1793   int i;
1795   for (i = 0; i < nComps; ++i) {
1796     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1797         abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1798       break;
1799     }
1800   }
1801   if (i == nComps || depth == gouraudMaxDepth) {
1802     state->setFillColor(color0);
1803     state->moveTo(x0, y0);
1804     state->lineTo(x1, y1);
1805     state->lineTo(x2, y2);
1806     state->closePath();
1807     builder->addPath(state, true, false);
1808     state->clearPath();
1809   } else {
1810     x01 = 0.5 * (x0 + x1);
1811     y01 = 0.5 * (y0 + y1);
1812     x12 = 0.5 * (x1 + x2);
1813     y12 = 0.5 * (y1 + y2);
1814     x20 = 0.5 * (x2 + x0);
1815     y20 = 0.5 * (y2 + y0);
1816     //~ if the shading has a Function, this should interpolate on the
1817     //~ function parameter, not on the color components
1818     for (i = 0; i < nComps; ++i) {
1819       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1820       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1821       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1822     }
1823     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1824                         x20, y20, &color20, nComps, depth + 1);
1825     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1826                         x12, y12, &color12, nComps, depth + 1);
1827     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1828                         x20, y20, &color20, nComps, depth + 1);
1829     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1830                         x2, y2, color2, nComps, depth + 1);
1831   }
1834 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1835   int start, i;
1837   if (shading->getNPatches() > 128) {
1838     start = 3;
1839   } else if (shading->getNPatches() > 64) {
1840     start = 2;
1841   } else if (shading->getNPatches() > 16) {
1842     start = 1;
1843   } else {
1844     start = 0;
1845   }
1846   for (i = 0; i < shading->getNPatches(); ++i) {
1847     fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1848               start);
1849   }
1852 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1853   GfxPatch patch00, patch01, patch10, patch11;
1854   double xx[4][8], yy[4][8];
1855   double xxm, yym;
1856   int i;
1858   for (i = 0; i < nComps; ++i) {
1859     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1860           > patchColorDelta ||
1861         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1862           > patchColorDelta ||
1863         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1864           > patchColorDelta ||
1865         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1866           > patchColorDelta) {
1867       break;
1868     }
1869   }
1870   if (i == nComps || depth == patchMaxDepth) {
1871     state->setFillColor(&patch->color[0][0]);
1872     state->moveTo(patch->x[0][0], patch->y[0][0]);
1873     state->curveTo(patch->x[0][1], patch->y[0][1],
1874                    patch->x[0][2], patch->y[0][2],
1875                    patch->x[0][3], patch->y[0][3]);
1876     state->curveTo(patch->x[1][3], patch->y[1][3],
1877                    patch->x[2][3], patch->y[2][3],
1878                    patch->x[3][3], patch->y[3][3]);
1879     state->curveTo(patch->x[3][2], patch->y[3][2],
1880                    patch->x[3][1], patch->y[3][1],
1881                    patch->x[3][0], patch->y[3][0]);
1882     state->curveTo(patch->x[2][0], patch->y[2][0],
1883                    patch->x[1][0], patch->y[1][0],
1884                    patch->x[0][0], patch->y[0][0]);
1885     state->closePath();
1886     builder->addPath(state, true, false);
1887     state->clearPath();
1888   } else {
1889     for (i = 0; i < 4; ++i) {
1890       xx[i][0] = patch->x[i][0];
1891       yy[i][0] = patch->y[i][0];
1892       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1893       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1894       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1895       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1896       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1897       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1898       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1899       yy[i][2] = 0.5 * (yy[i][1] + yym);
1900       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1901       yy[i][5] = 0.5 * (yym + yy[i][6]);
1902       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1903       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1904       xx[i][7] = patch->x[i][3];
1905       yy[i][7] = patch->y[i][3];
1906     }
1907     for (i = 0; i < 4; ++i) {
1908       patch00.x[0][i] = xx[0][i];
1909       patch00.y[0][i] = yy[0][i];
1910       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1911       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1912       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1913       yym = 0.5 * (yy[1][i] + yy[2][i]);
1914       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1915       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1916       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1917       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1918       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1919       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1920       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1921       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1922       patch10.x[0][i] = patch00.x[3][i];
1923       patch10.y[0][i] = patch00.y[3][i];
1924       patch10.x[3][i] = xx[3][i];
1925       patch10.y[3][i] = yy[3][i];
1926     }
1927     for (i = 4; i < 8; ++i) {
1928       patch01.x[0][i-4] = xx[0][i];
1929       patch01.y[0][i-4] = yy[0][i];
1930       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1931       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1932       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1933       yym = 0.5 * (yy[1][i] + yy[2][i]);
1934       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1935       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1936       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1937       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1938       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1939       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1940       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1941       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1942       patch11.x[0][i-4] = patch01.x[3][i-4];
1943       patch11.y[0][i-4] = patch01.y[3][i-4];
1944       patch11.x[3][i-4] = xx[3][i];
1945       patch11.y[3][i-4] = yy[3][i];
1946     }
1947     //~ if the shading has a Function, this should interpolate on the
1948     //~ function parameter, not on the color components
1949     for (i = 0; i < nComps; ++i) {
1950       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1951       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1952                                   patch->color[0][1].c[i]) / 2;
1953       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1954       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1955       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1956                                   patch->color[1][1].c[i]) / 2;
1957       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1958       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1959       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1960                                   patch->color[1][0].c[i]) / 2;
1961       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1962       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1963       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1964                                   patch->color[0][0].c[i]) / 2;
1965       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1966       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1967                                   patch01.color[1][1].c[i]) / 2;
1968       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1969       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1970       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1971     }
1972     fillPatch(&patch00, nComps, depth + 1);
1973     fillPatch(&patch10, nComps, depth + 1);
1974     fillPatch(&patch01, nComps, depth + 1);
1975     fillPatch(&patch11, nComps, depth + 1);
1976   }
1979 void PdfParser::doEndPath() {
1980   if (state->isCurPt() && clip != clipNone) {
1981     state->clip();
1982     if (clip == clipNormal) {
1983       clipHistory->setClip(state->getPath(), clipNormal);
1984       builder->clip(state);
1985     } else {
1986       clipHistory->setClip(state->getPath(), clipEO);
1987       builder->clip(state, true);
1988     }
1989   }
1990   clip = clipNone;
1991   state->clearPath();
1994 //------------------------------------------------------------------------
1995 // path clipping operators
1996 //------------------------------------------------------------------------
1998 void PdfParser::opClip(Object args[], int numArgs) {
1999   clip = clipNormal;
2002 void PdfParser::opEOClip(Object args[], int numArgs) {
2003   clip = clipEO;
2006 //------------------------------------------------------------------------
2007 // text object operators
2008 //------------------------------------------------------------------------
2010 void PdfParser::opBeginText(Object args[], int numArgs) {
2011   state->setTextMat(1, 0, 0, 1, 0, 0);
2012   state->textMoveTo(0, 0);
2013   builder->updateTextPosition(0.0, 0.0);
2014   fontChanged = gTrue;
2015   builder->beginTextObject(state);
2018 void PdfParser::opEndText(Object args[], int numArgs) {
2019   builder->endTextObject(state);
2022 //------------------------------------------------------------------------
2023 // text state operators
2024 //------------------------------------------------------------------------
2026 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2027   state->setCharSpace(args[0].getNum());
2030 void PdfParser::opSetFont(Object args[], int numArgs) {
2031   GfxFont *font;
2033   if (!(font = res->lookupFont(args[0].getName()))) {
2034     return;
2035   }
2036   if (printCommands) {
2037     printf("  font: tag=%s name='%s' %g\n",
2038            font->getTag()->getCString(),
2039            font->getName() ? font->getName()->getCString() : "???",
2040            args[1].getNum());
2041     fflush(stdout);
2042   }
2044   font->incRefCnt();
2045   state->setFont(font, args[1].getNum());
2046   fontChanged = gTrue;
2049 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2050   state->setLeading(args[0].getNum());
2053 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2054   state->setRender(args[0].getInt());
2055   builder->updateStyle(state);
2058 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2059   state->setRise(args[0].getNum());
2062 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2063   state->setWordSpace(args[0].getNum());
2066 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2067   state->setHorizScaling(args[0].getNum());
2068   fontChanged = gTrue;
2071 //------------------------------------------------------------------------
2072 // text positioning operators
2073 //------------------------------------------------------------------------
2075 void PdfParser::opTextMove(Object args[], int numArgs) {
2076   double tx, ty;
2078   tx = state->getLineX() + args[0].getNum();
2079   ty = state->getLineY() + args[1].getNum();
2080   state->textMoveTo(tx, ty);
2081   builder->updateTextPosition(tx, ty);
2084 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2085   double tx, ty;
2087   tx = state->getLineX() + args[0].getNum();
2088   ty = args[1].getNum();
2089   state->setLeading(-ty);
2090   ty += state->getLineY();
2091   state->textMoveTo(tx, ty);
2092   builder->updateTextPosition(tx, ty);
2095 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2096   state->setTextMat(args[0].getNum(), args[1].getNum(),
2097                     args[2].getNum(), args[3].getNum(),
2098                     args[4].getNum(), args[5].getNum());
2099   state->textMoveTo(0, 0);
2100   builder->updateTextMatrix(state);
2101   builder->updateTextPosition(0.0, 0.0);
2102   fontChanged = gTrue;
2105 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2106   double tx, ty;
2108   tx = state->getLineX();
2109   ty = state->getLineY() - state->getLeading();
2110   state->textMoveTo(tx, ty);
2111   builder->updateTextPosition(tx, ty);
2114 //------------------------------------------------------------------------
2115 // text string operators
2116 //------------------------------------------------------------------------
2118 void PdfParser::opShowText(Object args[], int numArgs) {
2119   if (!state->getFont()) {
2120     error(getPos(), "No font in show");
2121     return;
2122   }
2123   if (fontChanged) {
2124     builder->updateFont(state);
2125     fontChanged = gFalse;
2126   }
2127   doShowText(args[0].getString());
2130 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2131   double tx, ty;
2133   if (!state->getFont()) {
2134     error(getPos(), "No font in move/show");
2135     return;
2136   }
2137   if (fontChanged) {
2138     builder->updateFont(state);
2139     fontChanged = gFalse;
2140   }
2141   tx = state->getLineX();
2142   ty = state->getLineY() - state->getLeading();
2143   state->textMoveTo(tx, ty);
2144   builder->updateTextPosition(tx, ty);
2145   doShowText(args[0].getString());
2148 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2149   double tx, ty;
2151   if (!state->getFont()) {
2152     error(getPos(), "No font in move/set/show");
2153     return;
2154   }
2155   if (fontChanged) {
2156     builder->updateFont(state);
2157     fontChanged = gFalse;
2158   }
2159   state->setWordSpace(args[0].getNum());
2160   state->setCharSpace(args[1].getNum());
2161   tx = state->getLineX();
2162   ty = state->getLineY() - state->getLeading();
2163   state->textMoveTo(tx, ty);
2164   builder->updateTextPosition(tx, ty);
2165   doShowText(args[2].getString());
2168 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2169   Array *a;
2170   Object obj;
2171   int wMode;
2172   int i;
2174   if (!state->getFont()) {
2175     error(getPos(), "No font in show/space");
2176     return;
2177   }
2178   if (fontChanged) {
2179     builder->updateFont(state);
2180     fontChanged = gFalse;
2181   }
2182   wMode = state->getFont()->getWMode();
2183   a = args[0].getArray();
2184   for (i = 0; i < a->getLength(); ++i) {
2185     a->get(i, &obj);
2186     if (obj.isNum()) {
2187       // this uses the absolute value of the font size to match
2188       // Acrobat's behavior
2189       if (wMode) {
2190         state->textShift(0, -obj.getNum() * 0.001 *
2191                             fabs(state->getFontSize()));
2192       } else {
2193         state->textShift(-obj.getNum() * 0.001 *
2194                          fabs(state->getFontSize()), 0);
2195       }
2196       builder->updateTextShift(state, obj.getNum());
2197     } else if (obj.isString()) {
2198       doShowText(obj.getString());
2199     } else {
2200       error(getPos(), "Element of show/space array must be number or string");
2201     }
2202     obj.free();
2203   }
2206 void PdfParser::doShowText(GooString *s) {
2207   GfxFont *font;
2208   int wMode;
2209   double riseX, riseY;
2210   CharCode code;
2211   Unicode u[8];
2212   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2213   double originX, originY, tOriginX, tOriginY;
2214   double oldCTM[6], newCTM[6];
2215   double *mat;
2216   Object charProc;
2217   Dict *resDict;
2218   Parser *oldParser;
2219   char *p;
2220   int len, n, uLen, nChars, nSpaces, i;
2222   font = state->getFont();
2223   wMode = font->getWMode();
2225   builder->beginString(state, s);
2227   // handle a Type 3 char
2228   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2229     mat = state->getCTM();
2230     for (i = 0; i < 6; ++i) {
2231       oldCTM[i] = mat[i];
2232     }
2233     mat = state->getTextMat();
2234     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2235     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2236     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2237     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2238     mat = font->getFontMatrix();
2239     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2240     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2241     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2242     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2243     newCTM[0] *= state->getFontSize();
2244     newCTM[1] *= state->getFontSize();
2245     newCTM[2] *= state->getFontSize();
2246     newCTM[3] *= state->getFontSize();
2247     newCTM[0] *= state->getHorizScaling();
2248     newCTM[2] *= state->getHorizScaling();
2249     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2250     curX = state->getCurX();
2251     curY = state->getCurY();
2252     lineX = state->getLineX();
2253     lineY = state->getLineY();
2254     oldParser = parser;
2255     p = s->getCString();
2256     len = s->getLength();
2257     while (len > 0) {
2258       n = font->getNextChar(p, len, &code,
2259                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2260                             &dx, &dy, &originX, &originY);
2261       dx = dx * state->getFontSize() + state->getCharSpace();
2262       if (n == 1 && *p == ' ') {
2263         dx += state->getWordSpace();
2264       }
2265       dx *= state->getHorizScaling();
2266       dy *= state->getFontSize();
2267       state->textTransformDelta(dx, dy, &tdx, &tdy);
2268       state->transform(curX + riseX, curY + riseY, &x, &y);
2269       saveState();
2270       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2271       //~ the CTM concat values here are wrong (but never used)
2272       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2273       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2274                                code, u, uLen)) {*/
2275         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2276         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2277           pushResources(resDict);
2278         }
2279         if (charProc.isStream()) {
2280           //parse(&charProc, gFalse); // TODO: parse into SVG font
2281         } else {
2282           error(getPos(), "Missing or bad Type3 CharProc entry");
2283         }
2284         //out->endType3Char(state);
2285         if (resDict) {
2286           popResources();
2287         }
2288         charProc.free();
2289       }
2290       restoreState();
2291       // GfxState::restore() does *not* restore the current position,
2292       // so we deal with it here using (curX, curY) and (lineX, lineY)
2293       curX += tdx;
2294       curY += tdy;
2295       state->moveTo(curX, curY);
2296       state->textSetPos(lineX, lineY);
2297       p += n;
2298       len -= n;
2299     }
2300     parser = oldParser;
2302   } else {
2303     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2304     p = s->getCString();
2305     len = s->getLength();
2306     while (len > 0) {
2307       n = font->getNextChar(p, len, &code,
2308                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2309                             &dx, &dy, &originX, &originY);
2310       
2311       if (wMode) {
2312         dx *= state->getFontSize();
2313         dy = dy * state->getFontSize() + state->getCharSpace();
2314         if (n == 1 && *p == ' ') {
2315           dy += state->getWordSpace();
2316         }
2317       } else {
2318         dx = dx * state->getFontSize() + state->getCharSpace();
2319         if (n == 1 && *p == ' ') {
2320           dx += state->getWordSpace();
2321         }
2322         dx *= state->getHorizScaling();
2323         dy *= state->getFontSize();
2324       }
2325       state->textTransformDelta(dx, dy, &tdx, &tdy);
2326       originX *= state->getFontSize();
2327       originY *= state->getFontSize();
2328       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2329       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2330                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2331       state->shift(tdx, tdy);
2332       p += n;
2333       len -= n;
2334     }
2335   }
2337   builder->endString(state);
2340 //------------------------------------------------------------------------
2341 // XObject operators
2342 //------------------------------------------------------------------------
2344 void PdfParser::opXObject(Object args[], int numArgs) {
2345   char *name;
2346   Object obj1, obj2, obj3, refObj;
2348   name = args[0].getName();
2349   if (!res->lookupXObject(name, &obj1)) {
2350     return;
2351   }
2352   if (!obj1.isStream()) {
2353     error(getPos(), "XObject '%s' is wrong type", name);
2354     obj1.free();
2355     return;
2356   }
2357   obj1.streamGetDict()->lookup("Subtype", &obj2);
2358   if (obj2.isName("Image")) {
2359     res->lookupXObjectNF(name, &refObj);
2360     doImage(&refObj, obj1.getStream(), gFalse);
2361     refObj.free();
2362   } else if (obj2.isName("Form")) {
2363     doForm(&obj1);
2364   } else if (obj2.isName("PS")) {
2365     obj1.streamGetDict()->lookup("Level1", &obj3);
2366 /*    out->psXObject(obj1.getStream(),
2367                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2368   } else if (obj2.isName()) {
2369     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2370   } else {
2371     error(getPos(), "XObject subtype is missing or wrong type");
2372   }
2373   obj2.free();
2374   obj1.free();
2377 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2378   Dict *dict, *maskDict;
2379   int width, height;
2380   int bits, maskBits;
2381   StreamColorSpaceMode csMode;
2382   GBool mask;
2383   GBool invert;
2384   GfxColorSpace *colorSpace, *maskColorSpace;
2385   GfxImageColorMap *colorMap, *maskColorMap;
2386   Object maskObj, smaskObj;
2387   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2388   int maskColors[2*gfxColorMaxComps];
2389   int maskWidth, maskHeight;
2390   GBool maskInvert;
2391   Stream *maskStr;
2392   Object obj1, obj2;
2393   int i;
2395   // get info from the stream
2396   bits = 0;
2397   csMode = streamCSNone;
2398   str->getImageParams(&bits, &csMode);
2400   // get stream dict
2401   dict = str->getDict();
2403   // get size
2404   dict->lookup("Width", &obj1);
2405   if (obj1.isNull()) {
2406     obj1.free();
2407     dict->lookup("W", &obj1);
2408   }
2409   if (obj1.isInt())
2410     width = obj1.getInt();
2411   else if (obj1.isReal())
2412     width = (int)obj1.getReal();
2413   else
2414     goto err2;
2415   obj1.free();
2416   dict->lookup("Height", &obj1);
2417   if (obj1.isNull()) {
2418     obj1.free();
2419     dict->lookup("H", &obj1);
2420   }
2421   if (obj1.isInt())
2422     height = obj1.getInt();
2423   else if (obj1.isReal())
2424     height = (int)obj1.getReal();
2425   else
2426     goto err2;
2427   obj1.free();
2429   // image or mask?
2430   dict->lookup("ImageMask", &obj1);
2431   if (obj1.isNull()) {
2432     obj1.free();
2433     dict->lookup("IM", &obj1);
2434   }
2435   mask = gFalse;
2436   if (obj1.isBool())
2437     mask = obj1.getBool();
2438   else if (!obj1.isNull())
2439     goto err2;
2440   obj1.free();
2442   // bit depth
2443   if (bits == 0) {
2444     dict->lookup("BitsPerComponent", &obj1);
2445     if (obj1.isNull()) {
2446       obj1.free();
2447       dict->lookup("BPC", &obj1);
2448     }
2449     if (obj1.isInt()) {
2450       bits = obj1.getInt();
2451     } else if (mask) {
2452       bits = 1;
2453     } else {
2454       goto err2;
2455     }
2456     obj1.free();
2457   }
2459   // display a mask
2460   if (mask) {
2462     // check for inverted mask
2463     if (bits != 1)
2464       goto err1;
2465     invert = gFalse;
2466     dict->lookup("Decode", &obj1);
2467     if (obj1.isNull()) {
2468       obj1.free();
2469       dict->lookup("D", &obj1);
2470     }
2471     if (obj1.isArray()) {
2472       obj1.arrayGet(0, &obj2);
2473       if (obj2.isInt() && obj2.getInt() == 1)
2474         invert = gTrue;
2475       obj2.free();
2476     } else if (!obj1.isNull()) {
2477       goto err2;
2478     }
2479     obj1.free();
2481     // draw it
2482     builder->addImageMask(state, str, width, height, invert);
2484   } else {
2486     // get color space and color map
2487     dict->lookup("ColorSpace", &obj1);
2488     if (obj1.isNull()) {
2489       obj1.free();
2490       dict->lookup("CS", &obj1);
2491     }
2492     if (obj1.isName()) {
2493       res->lookupColorSpace(obj1.getName(), &obj2);
2494       if (!obj2.isNull()) {
2495         obj1.free();
2496         obj1 = obj2;
2497       } else {
2498         obj2.free();
2499       }
2500     }
2501     if (!obj1.isNull()) {
2502       colorSpace = GfxColorSpace::parse(&obj1);
2503     } else if (csMode == streamCSDeviceGray) {
2504       colorSpace = new GfxDeviceGrayColorSpace();
2505     } else if (csMode == streamCSDeviceRGB) {
2506       colorSpace = new GfxDeviceRGBColorSpace();
2507     } else if (csMode == streamCSDeviceCMYK) {
2508       colorSpace = new GfxDeviceCMYKColorSpace();
2509     } else {
2510       colorSpace = NULL;
2511     }
2512     obj1.free();
2513     if (!colorSpace) {
2514       goto err1;
2515     }
2516     dict->lookup("Decode", &obj1);
2517     if (obj1.isNull()) {
2518       obj1.free();
2519       dict->lookup("D", &obj1);
2520     }
2521     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2522     obj1.free();
2523     if (!colorMap->isOk()) {
2524       delete colorMap;
2525       goto err1;
2526     }
2528     // get the mask
2529     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2530     maskStr = NULL; // make gcc happy
2531     maskWidth = maskHeight = 0; // make gcc happy
2532     maskInvert = gFalse; // make gcc happy
2533     maskColorMap = NULL; // make gcc happy
2534     dict->lookup("Mask", &maskObj);
2535     dict->lookup("SMask", &smaskObj);
2536     if (smaskObj.isStream()) {
2537       // soft mask
2538       if (inlineImg) {
2539         goto err1;
2540       }
2541       maskStr = smaskObj.getStream();
2542       maskDict = smaskObj.streamGetDict();
2543       maskDict->lookup("Width", &obj1);
2544       if (obj1.isNull()) {
2545         obj1.free();
2546         maskDict->lookup("W", &obj1);
2547       }
2548       if (!obj1.isInt()) {
2549         goto err2;
2550       }
2551       maskWidth = obj1.getInt();
2552       obj1.free();
2553       maskDict->lookup("Height", &obj1);
2554       if (obj1.isNull()) {
2555         obj1.free();
2556         maskDict->lookup("H", &obj1);
2557       }
2558       if (!obj1.isInt()) {
2559         goto err2;
2560       }
2561       maskHeight = obj1.getInt();
2562       obj1.free();
2563       maskDict->lookup("BitsPerComponent", &obj1);
2564       if (obj1.isNull()) {
2565         obj1.free();
2566         maskDict->lookup("BPC", &obj1);
2567       }
2568       if (!obj1.isInt()) {
2569         goto err2;
2570       }
2571       maskBits = obj1.getInt();
2572       obj1.free();
2573       maskDict->lookup("ColorSpace", &obj1);
2574       if (obj1.isNull()) {
2575         obj1.free();
2576         maskDict->lookup("CS", &obj1);
2577       }
2578       if (obj1.isName()) {
2579         res->lookupColorSpace(obj1.getName(), &obj2);
2580         if (!obj2.isNull()) {
2581           obj1.free();
2582           obj1 = obj2;
2583         } else {
2584           obj2.free();
2585         }
2586       }
2587       maskColorSpace = GfxColorSpace::parse(&obj1);
2588       obj1.free();
2589       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2590         goto err1;
2591       }
2592       maskDict->lookup("Decode", &obj1);
2593       if (obj1.isNull()) {
2594         obj1.free();
2595         maskDict->lookup("D", &obj1);
2596       }
2597       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2598       obj1.free();
2599       if (!maskColorMap->isOk()) {
2600         delete maskColorMap;
2601         goto err1;
2602       }
2603       //~ handle the Matte entry
2604       haveSoftMask = gTrue;
2605     } else if (maskObj.isArray()) {
2606       // color key mask
2607       for (i = 0;
2608            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2609            ++i) {
2610         maskObj.arrayGet(i, &obj1);
2611         maskColors[i] = obj1.getInt();
2612         obj1.free();
2613       }
2614       haveColorKeyMask = gTrue;
2615     } else if (maskObj.isStream()) {
2616       // explicit mask
2617       if (inlineImg) {
2618         goto err1;
2619       }
2620       maskStr = maskObj.getStream();
2621       maskDict = maskObj.streamGetDict();
2622       maskDict->lookup("Width", &obj1);
2623       if (obj1.isNull()) {
2624         obj1.free();
2625         maskDict->lookup("W", &obj1);
2626       }
2627       if (!obj1.isInt()) {
2628         goto err2;
2629       }
2630       maskWidth = obj1.getInt();
2631       obj1.free();
2632       maskDict->lookup("Height", &obj1);
2633       if (obj1.isNull()) {
2634         obj1.free();
2635         maskDict->lookup("H", &obj1);
2636       }
2637       if (!obj1.isInt()) {
2638         goto err2;
2639       }
2640       maskHeight = obj1.getInt();
2641       obj1.free();
2642       maskDict->lookup("ImageMask", &obj1);
2643       if (obj1.isNull()) {
2644         obj1.free();
2645         maskDict->lookup("IM", &obj1);
2646       }
2647       if (!obj1.isBool() || !obj1.getBool()) {
2648         goto err2;
2649       }
2650       obj1.free();
2651       maskInvert = gFalse;
2652       maskDict->lookup("Decode", &obj1);
2653       if (obj1.isNull()) {
2654         obj1.free();
2655         maskDict->lookup("D", &obj1);
2656       }
2657       if (obj1.isArray()) {
2658         obj1.arrayGet(0, &obj2);
2659         if (obj2.isInt() && obj2.getInt() == 1) {
2660           maskInvert = gTrue;
2661         }
2662         obj2.free();
2663       } else if (!obj1.isNull()) {
2664         goto err2;
2665       }
2666       obj1.free();
2667       haveExplicitMask = gTrue;
2668     }
2670     // draw it
2671     if (haveSoftMask) {
2672         builder->addSoftMaskedImage(state, str, width, height, colorMap,
2673                                     maskStr, maskWidth, maskHeight, maskColorMap);
2674       delete maskColorMap;
2675     } else if (haveExplicitMask) {
2676         builder->addMaskedImage(state, str, width, height, colorMap,
2677                                 maskStr, maskWidth, maskHeight, maskInvert);
2678     } else {
2679       builder->addImage(state, str, width, height, colorMap,
2680                         haveColorKeyMask ? maskColors : (int *)NULL);
2681     }
2682     delete colorMap;
2684     maskObj.free();
2685     smaskObj.free();
2686   }
2688   return;
2690  err2:
2691   obj1.free();
2692  err1:
2693   error(getPos(), "Bad image parameters");
2696 void PdfParser::doForm(Object *str) {
2697   Dict *dict;
2698   GBool transpGroup, isolated, knockout;
2699   GfxColorSpace *blendingColorSpace;
2700   Object matrixObj, bboxObj;
2701   double m[6], bbox[4];
2702   Object resObj;
2703   Dict *resDict;
2704   Object obj1, obj2, obj3;
2705   int i;
2707   // check for excessive recursion
2708   if (formDepth > 20) {
2709     return;
2710   }
2712   // get stream dict
2713   dict = str->streamGetDict();
2715   // check form type
2716   dict->lookup("FormType", &obj1);
2717   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2718     error(getPos(), "Unknown form type");
2719   }
2720   obj1.free();
2722   // get bounding box
2723   dict->lookup("BBox", &bboxObj);
2724   if (!bboxObj.isArray()) {
2725     bboxObj.free();
2726     error(getPos(), "Bad form bounding box");
2727     return;
2728   }
2729   for (i = 0; i < 4; ++i) {
2730     bboxObj.arrayGet(i, &obj1);
2731     bbox[i] = obj1.getNum();
2732     obj1.free();
2733   }
2734   bboxObj.free();
2736   // get matrix
2737   dict->lookup("Matrix", &matrixObj);
2738   if (matrixObj.isArray()) {
2739     for (i = 0; i < 6; ++i) {
2740       matrixObj.arrayGet(i, &obj1);
2741       m[i] = obj1.getNum();
2742       obj1.free();
2743     }
2744   } else {
2745     m[0] = 1; m[1] = 0;
2746     m[2] = 0; m[3] = 1;
2747     m[4] = 0; m[5] = 0;
2748   }
2749   matrixObj.free();
2751   // get resources
2752   dict->lookup("Resources", &resObj);
2753   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2755   // check for a transparency group
2756   transpGroup = isolated = knockout = gFalse;
2757   blendingColorSpace = NULL;
2758   if (dict->lookup("Group", &obj1)->isDict()) {
2759     if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2760       transpGroup = gTrue;
2761       if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2762         blendingColorSpace = GfxColorSpace::parse(&obj3);
2763       }
2764       obj3.free();
2765       if (obj1.dictLookup("I", &obj3)->isBool()) {
2766         isolated = obj3.getBool();
2767       }
2768       obj3.free();
2769       if (obj1.dictLookup("K", &obj3)->isBool()) {
2770         knockout = obj3.getBool();
2771       }
2772       obj3.free();
2773     }
2774     obj2.free();
2775   }
2776   obj1.free();
2778   // draw it
2779   ++formDepth;
2780   doForm1(str, resDict, m, bbox,
2781           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2782   --formDepth;
2784   if (blendingColorSpace) {
2785     delete blendingColorSpace;
2786   }
2787   resObj.free();
2790 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2791                   GBool transpGroup, GBool softMask,
2792                   GfxColorSpace *blendingColorSpace,
2793                   GBool isolated, GBool knockout,
2794                   GBool alpha, Function *transferFunc,
2795                   GfxColor *backdropColor) {
2796   Parser *oldParser;
2797   double oldBaseMatrix[6];
2798   int i;
2800   // push new resources on stack
2801   pushResources(resDict);
2803   // save current graphics state
2804   saveState();
2806   // kill any pre-existing path
2807   state->clearPath();
2809   if (softMask || transpGroup) {
2810     builder->clearSoftMask(state);
2811     builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2812                                    isolated, knockout, softMask);
2813   }
2815   // save current parser
2816   oldParser = parser;
2818   // set form transformation matrix
2819   state->concatCTM(matrix[0], matrix[1], matrix[2],
2820                    matrix[3], matrix[4], matrix[5]);
2821   builder->setTransform(matrix[0], matrix[1], matrix[2],
2822                         matrix[3], matrix[4], matrix[5]);
2824   // set form bounding box
2825   state->moveTo(bbox[0], bbox[1]);
2826   state->lineTo(bbox[2], bbox[1]);
2827   state->lineTo(bbox[2], bbox[3]);
2828   state->lineTo(bbox[0], bbox[3]);
2829   state->closePath();
2830   state->clip();
2831   clipHistory->setClip(state->getPath());
2832   builder->clip(state);
2833   state->clearPath();
2835   if (softMask || transpGroup) {
2836     if (state->getBlendMode() != gfxBlendNormal) {
2837       state->setBlendMode(gfxBlendNormal);
2838     }
2839     if (state->getFillOpacity() != 1) {
2840       builder->setGroupOpacity(state->getFillOpacity());
2841       state->setFillOpacity(1);
2842     }
2843     if (state->getStrokeOpacity() != 1) {
2844       state->setStrokeOpacity(1);
2845     }
2846   }
2848   // set new base matrix
2849   for (i = 0; i < 6; ++i) {
2850     oldBaseMatrix[i] = baseMatrix[i];
2851     baseMatrix[i] = state->getCTM()[i];
2852   }
2854   // draw the form
2855   parse(str, gFalse);
2857   // restore base matrix
2858   for (i = 0; i < 6; ++i) {
2859     baseMatrix[i] = oldBaseMatrix[i];
2860   }
2862   // restore parser
2863   parser = oldParser;
2865   if (softMask || transpGroup) {
2866       builder->popTransparencyGroup(state);
2867   }
2869   // restore graphics state
2870   restoreState();
2872   // pop resource stack
2873   popResources();
2875   if (softMask) {
2876     builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2877   } else if (transpGroup) {
2878     builder->paintTransparencyGroup(state, bbox);
2879   }
2881   return;
2884 //------------------------------------------------------------------------
2885 // in-line image operators
2886 //------------------------------------------------------------------------
2888 void PdfParser::opBeginImage(Object args[], int numArgs) {
2889   Stream *str;
2890   int c1, c2;
2892   // build dict/stream
2893   str = buildImageStream();
2895   // display the image
2896   if (str) {
2897     doImage(NULL, str, gTrue);
2898   
2899     // skip 'EI' tag
2900     c1 = str->getUndecodedStream()->getChar();
2901     c2 = str->getUndecodedStream()->getChar();
2902     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2903       c1 = c2;
2904       c2 = str->getUndecodedStream()->getChar();
2905     }
2906     delete str;
2907   }
2910 Stream *PdfParser::buildImageStream() {
2911   Object dict;
2912   Object obj;
2913   char *key;
2914   Stream *str;
2916   // build dictionary
2917   dict.initDict(xref);
2918   parser->getObj(&obj);
2919   while (!obj.isCmd("ID") && !obj.isEOF()) {
2920     if (!obj.isName()) {
2921       error(getPos(), "Inline image dictionary key must be a name object");
2922       obj.free();
2923     } else {
2924       key = copyString(obj.getName());
2925       obj.free();
2926       parser->getObj(&obj);
2927       if (obj.isEOF() || obj.isError()) {
2928         gfree(key);
2929         break;
2930       }
2931       dict.dictAdd(key, &obj);
2932     }
2933     parser->getObj(&obj);
2934   }
2935   if (obj.isEOF()) {
2936     error(getPos(), "End of file in inline image");
2937     obj.free();
2938     dict.free();
2939     return NULL;
2940   }
2941   obj.free();
2943   // make stream
2944   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2945   str = str->addFilters(&dict);
2947   return str;
2950 void PdfParser::opImageData(Object args[], int numArgs) {
2951   error(getPos(), "Internal: got 'ID' operator");
2954 void PdfParser::opEndImage(Object args[], int numArgs) {
2955   error(getPos(), "Internal: got 'EI' operator");
2958 //------------------------------------------------------------------------
2959 // type 3 font operators
2960 //------------------------------------------------------------------------
2962 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2965 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2968 //------------------------------------------------------------------------
2969 // compatibility operators
2970 //------------------------------------------------------------------------
2972 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2973   ++ignoreUndef;
2976 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2977   if (ignoreUndef > 0)
2978     --ignoreUndef;
2981 //------------------------------------------------------------------------
2982 // marked content operators
2983 //------------------------------------------------------------------------
2985 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2986   if (printCommands) {
2987     printf("  marked content: %s ", args[0].getName());
2988     if (numArgs == 2)
2989       args[2].print(stdout);
2990     printf("\n");
2991     fflush(stdout);
2992   }
2994   if(numArgs == 2) {
2995     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
2996   } else {
2997     //out->beginMarkedContent(args[0].getName());
2998   }
3001 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3002   //out->endMarkedContent();
3005 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3006   if (printCommands) {
3007     printf("  mark point: %s ", args[0].getName());
3008     if (numArgs == 2)
3009       args[2].print(stdout);
3010     printf("\n");
3011     fflush(stdout);
3012   }
3014   if(numArgs == 2) {
3015     //out->markPoint(args[0].getName(),args[1].getDict());
3016   } else {
3017     //out->markPoint(args[0].getName());
3018   }
3022 //------------------------------------------------------------------------
3023 // misc
3024 //------------------------------------------------------------------------
3026 void PdfParser::saveState() {
3027   builder->saveState();
3028   state = state->save();
3029   clipHistory = clipHistory->save();
3032 void PdfParser::restoreState() {
3033   clipHistory = clipHistory->restore();
3034   state = state->restore();
3035   builder->restoreState();
3038 void PdfParser::pushResources(Dict *resDict) {
3039   res = new GfxResources(xref, resDict, res);
3042 void PdfParser::popResources() {
3043   GfxResources *resPtr;
3045   resPtr = res->getNext();
3046   delete res;
3047   res = resPtr;
3050 //------------------------------------------------------------------------
3051 // ClipHistoryEntry
3052 //------------------------------------------------------------------------
3054 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3055     if (clipPathA) {
3056         clipPath = clipPathA->copy();
3057     } else {
3058         clipPath = NULL;
3059     }
3060     clipType = clipTypeA;
3061     saved = NULL;
3064 ClipHistoryEntry::~ClipHistoryEntry() {
3065     if (clipPath) {
3066         delete clipPath;
3067     }
3070 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3071     // Free previous clip path
3072     if (clipPath) {
3073         delete clipPath;
3074     }
3075     if (clipPathA) {
3076         clipPath = clipPathA->copy();
3077         clipType = clipTypeA;
3078     } else {
3079         clipPath = NULL;
3080     }
3083 ClipHistoryEntry *ClipHistoryEntry::save() {
3084     ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3085     newEntry->saved = this;
3087     return newEntry;
3090 ClipHistoryEntry *ClipHistoryEntry::restore() {
3091     ClipHistoryEntry *oldEntry;
3093     if (saved) {
3094         oldEntry = saved;
3095         saved = NULL;
3096         delete this;
3097     } else {
3098         oldEntry = this;
3099     }
3101     return oldEntry;
3104 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3105     if (other->clipPath) {
3106         this->clipPath = other->clipPath->copy();
3107         this->clipType = other->clipType;
3108     } else {
3109         this->clipPath = NULL;
3110     }
3111     saved = NULL;
3114 #endif /* HAVE_POPPLER */