Code

To ease testing reduced fallback operations' precision for now
[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   fontChanged = gFalse;
300   clip = clipNone;
301   lastClipPath = NULL;
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         builder->setClipPath(state);
333         state->clearPath();
334     }
335   }
336   pushOperator("startPage");
339 PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA,
340                      Dict *resDict, PDFRectangle *box) {
342   int i;
344   xref = xrefA;
345   subPage = gTrue;
346   printCommands = false;
348   // start the resource stack
349   res = new GfxResources(xref, resDict, NULL);
351   // initialize
352   operatorHistory = NULL;
353   builder = builderA;
354   state = new GfxState(72, 72, box, 0, gFalse);
355   fontChanged = gFalse;
356   clip = clipNone;
357   lastClipPath = NULL;
358   ignoreUndef = 0;
359   for (i = 0; i < 6; ++i) {
360     baseMatrix[i] = state->getCTM()[i];
361   }
362   formDepth = 0;
365 PdfParser::~PdfParser() {
366   while (state->hasSaves()) {
367     restoreState();
368   }
369   if (!subPage) {
370     //out->endPage();
371   }
372   while (res) {
373     popResources();
374   }
375   if (state) {
376     delete state;
377   }
378   if (lastClipPath) {
379     delete lastClipPath;
380   }
383 void PdfParser::parse(Object *obj, GBool topLevel) {
384   Object obj2;
385   int i;
387   if (obj->isArray()) {
388     for (i = 0; i < obj->arrayGetLength(); ++i) {
389       obj->arrayGet(i, &obj2);
390       if (!obj2.isStream()) {
391         error(-1, "Weird page contents");
392         obj2.free();
393         return;
394       }
395       obj2.free();
396     }
397   } else if (!obj->isStream()) {
398     error(-1, "Weird page contents");
399     return;
400   }
401   parser = new Parser(xref, new Lexer(xref, obj), gFalse);
402   go(topLevel);
403   delete parser;
404   parser = NULL;
407 void PdfParser::go(GBool topLevel) {
408   Object obj;
409   Object args[maxArgs];
410   int numArgs, i;
411   int lastAbortCheck;
413   // scan a sequence of objects
414   numArgs = 0;
415   parser->getObj(&obj);
416   while (!obj.isEOF()) {
418     // got a command - execute it
419     if (obj.isCmd()) {
420       if (printCommands) {
421         obj.print(stdout);
422         for (i = 0; i < numArgs; ++i) {
423           printf(" ");
424           args[i].print(stdout);
425         }
426         printf("\n");
427         fflush(stdout);
428       }
430       // Run the operation
431       execOp(&obj, args, numArgs);
433       obj.free();
434       for (i = 0; i < numArgs; ++i)
435         args[i].free();
436       numArgs = 0;
438     // got an argument - save it
439     } else if (numArgs < maxArgs) {
440       args[numArgs++] = obj;
442     // too many arguments - something is wrong
443     } else {
444       error(getPos(), "Too many args in content stream");
445       if (printCommands) {
446         printf("throwing away arg: ");
447         obj.print(stdout);
448         printf("\n");
449         fflush(stdout);
450       }
451       obj.free();
452     }
454     // grab the next object
455     parser->getObj(&obj);
456   }
457   obj.free();
459   // args at end with no command
460   if (numArgs > 0) {
461     error(getPos(), "Leftover args in content stream");
462     if (printCommands) {
463       printf("%d leftovers:", numArgs);
464       for (i = 0; i < numArgs; ++i) {
465         printf(" ");
466         args[i].print(stdout);
467       }
468       printf("\n");
469       fflush(stdout);
470     }
471     for (i = 0; i < numArgs; ++i)
472       args[i].free();
473   }
476 void PdfParser::pushOperator(const char *name) {
477     OpHistoryEntry *newEntry = new OpHistoryEntry;
478     newEntry->name = name;
479     newEntry->state = NULL;
480     newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
481     newEntry->next = operatorHistory;
482     operatorHistory = newEntry;
484     // Truncate list if needed
485     if (operatorHistory->depth > maxOperatorHistoryDepth) {
486         OpHistoryEntry *curr = operatorHistory;
487         OpHistoryEntry *prev = NULL;
488         while (curr && curr->next != NULL) {
489             curr->depth--;
490             prev = curr;
491             curr = curr->next;
492         }
493         if (prev) {
494             if (curr->state != NULL)
495                 delete curr->state;
496             delete curr;
497             prev->next = NULL;
498         }
499     }
502 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
503     OpHistoryEntry *prev = NULL;
504     if (operatorHistory != NULL && look_back > 0) {
505         prev = operatorHistory->next;
506         while (--look_back > 0 && prev != NULL) {
507             prev = prev->next;
508         }
509     }
510     if (prev != NULL) {
511         return prev->name;
512     } else {
513         return "";
514     }
517 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
518   PdfOperator *op;
519   char *name;
520   Object *argPtr;
521   int i;
523   // find operator
524   name = cmd->getCmd();
525   if (!(op = findOp(name))) {
526     if (ignoreUndef == 0)
527       error(getPos(), "Unknown operator '%s'", name);
528     return;
529   }
531   // type check args
532   argPtr = args;
533   if (op->numArgs >= 0) {
534     if (numArgs < op->numArgs) {
535       error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
536       return;
537     }
538     if (numArgs > op->numArgs) {
539 #if 0
540       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
541 #endif
542       argPtr += numArgs - op->numArgs;
543       numArgs = op->numArgs;
544     }
545   } else {
546     if (numArgs > -op->numArgs) {
547       error(getPos(), "Too many (%d) args to '%s' operator",
548             numArgs, name);
549       return;
550     }
551   }
552   for (i = 0; i < numArgs; ++i) {
553     if (!checkArg(&argPtr[i], op->tchk[i])) {
554       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
555             i, name, argPtr[i].getTypeName());
556       return;
557     }
558   }
560   // add to history
561   pushOperator((char*)&op->name);
563   // do it
564   (this->*op->func)(argPtr, numArgs);
567 PdfOperator *PdfParser::findOp(char *name) {
568   int a, b, m, cmp;
570   a = -1;
571   b = numOps;
572   // invariant: opTab[a] < name < opTab[b]
573   while (b - a > 1) {
574     m = (a + b) / 2;
575     cmp = strcmp(opTab[m].name, name);
576     if (cmp < 0)
577       a = m;
578     else if (cmp > 0)
579       b = m;
580     else
581       a = b = m;
582   }
583   if (cmp != 0)
584     return NULL;
585   return &opTab[a];
588 GBool PdfParser::checkArg(Object *arg, TchkType type) {
589   switch (type) {
590   case tchkBool:   return arg->isBool();
591   case tchkInt:    return arg->isInt();
592   case tchkNum:    return arg->isNum();
593   case tchkString: return arg->isString();
594   case tchkName:   return arg->isName();
595   case tchkArray:  return arg->isArray();
596   case tchkProps:  return arg->isDict() || arg->isName();
597   case tchkSCN:    return arg->isNum() || arg->isName();
598   case tchkNone:   return gFalse;
599   }
600   return gFalse;
603 int PdfParser::getPos() {
604   return parser ? parser->getPos() : -1;
607 //------------------------------------------------------------------------
608 // graphics state operators
609 //------------------------------------------------------------------------
611 void PdfParser::opSave(Object args[], int numArgs) {
612   saveState();
615 void PdfParser::opRestore(Object args[], int numArgs) {
616   restoreState();
619 void PdfParser::opConcat(Object args[], int numArgs) {
620   state->concatCTM(args[0].getNum(), args[1].getNum(),
621                    args[2].getNum(), args[3].getNum(),
622                    args[4].getNum(), args[5].getNum());
623   const char *prevOp = getPreviousOperator();
624   double a0 = args[0].getNum();
625   double a1 = args[1].getNum();
626   double a2 = args[2].getNum();
627   double a3 = args[3].getNum();
628   double a4 = args[4].getNum();
629   double a5 = args[5].getNum();
630   if (!strcmp(prevOp, "q")) {
631       builder->setTransform(a0, a1, a2, a3, a4, a5);
632   } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
633       // multiply it with the previous transform
634       double otherMatrix[6];
635       if (!builder->getTransform((double*)&otherMatrix)) { // invalid transform
636           // construct identity matrix
637           otherMatrix[0] = otherMatrix[3] = 1.0;
638           otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
639       }
640       double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
641       double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
642       double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
643       double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
644       double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
645       double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
646       builder->setTransform(c0, c1, c2, c3, c4, c5);
647   } else {
648       builder->pushGroup();
649       builder->setTransform(a0, a1, a2, a3, a4, a5);
650   }
651   fontChanged = gTrue;
654 void PdfParser::opSetDash(Object args[], int numArgs) {
655   Array *a;
656   int length;
657   Object obj;
658   double *dash;
659   int i;
661   a = args[0].getArray();
662   length = a->getLength();
663   if (length == 0) {
664     dash = NULL;
665   } else {
666     dash = (double *)gmallocn(length, sizeof(double));
667     for (i = 0; i < length; ++i) {
668       dash[i] = a->get(i, &obj)->getNum();
669       obj.free();
670     }
671   }
672   state->setLineDash(dash, length, args[1].getNum());
673   builder->updateStyle(state);
676 void PdfParser::opSetFlat(Object args[], int numArgs) {
677   state->setFlatness((int)args[0].getNum());
680 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
681   state->setLineJoin(args[0].getInt());
682   builder->updateStyle(state);
685 void PdfParser::opSetLineCap(Object args[], int numArgs) {
686   state->setLineCap(args[0].getInt());
687   builder->updateStyle(state);
690 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
691   state->setMiterLimit(args[0].getNum());
692   builder->updateStyle(state);
695 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
696   state->setLineWidth(args[0].getNum());
697   builder->updateStyle(state);
700 void PdfParser::opSetExtGState(Object args[], int numArgs) {
701   Object obj1, obj2, obj3, obj4, obj5;
702   GfxBlendMode mode;
703   GBool haveFillOP;
704   Function *funcs[4];
705   GfxColor backdropColor;
706   GBool haveBackdropColor;
707   GfxColorSpace *blendingColorSpace;
708   GBool alpha, isolated, knockout;
709   int i;
711   if (!res->lookupGState(args[0].getName(), &obj1)) {
712     return;
713   }
714   if (!obj1.isDict()) {
715     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
716     obj1.free();
717     return;
718   }
719   if (printCommands) {
720     printf("  gfx state dict: ");
721     obj1.print();
722     printf("\n");
723   }
725   // transparency support: blend mode, fill/stroke opacity
726   if (!obj1.dictLookup("BM", &obj2)->isNull()) {
727     if (state->parseBlendMode(&obj2, &mode)) {
728       state->setBlendMode(mode);
729     } else {
730       error(getPos(), "Invalid blend mode in ExtGState");
731     }
732   }
733   obj2.free();
734   if (obj1.dictLookup("ca", &obj2)->isNum()) {
735     state->setFillOpacity(obj2.getNum());
736   }
737   obj2.free();
738   if (obj1.dictLookup("CA", &obj2)->isNum()) {
739     state->setStrokeOpacity(obj2.getNum());
740   }
741   obj2.free();
743   // fill/stroke overprint
744   if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
745     state->setFillOverprint(obj2.getBool());
746   }
747   obj2.free();
748   if (obj1.dictLookup("OP", &obj2)->isBool()) {
749     state->setStrokeOverprint(obj2.getBool());
750     if (!haveFillOP) {
751       state->setFillOverprint(obj2.getBool());
752     }
753   }
754   obj2.free();
756   // stroke adjust
757   if (obj1.dictLookup("SA", &obj2)->isBool()) {
758     state->setStrokeAdjust(obj2.getBool());
759   }
760   obj2.free();
762   // transfer function
763   if (obj1.dictLookup("TR2", &obj2)->isNull()) {
764     obj2.free();
765     obj1.dictLookup("TR", &obj2);
766   }
767   if (obj2.isName("Default") ||
768       obj2.isName("Identity")) {
769     funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
770     state->setTransfer(funcs);
771   } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
772     for (i = 0; i < 4; ++i) {
773       obj2.arrayGet(i, &obj3);
774       funcs[i] = Function::parse(&obj3);
775       obj3.free();
776       if (!funcs[i]) {
777         break;
778       }
779     }
780     if (i == 4) {
781       state->setTransfer(funcs);
782     }
783   } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
784     if ((funcs[0] = Function::parse(&obj2))) {
785       funcs[1] = funcs[2] = funcs[3] = NULL;
786       state->setTransfer(funcs);
787     }
788   } else if (!obj2.isNull()) {
789     error(getPos(), "Invalid transfer function in ExtGState");
790   }
791   obj2.free();
793   // soft mask
794   if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
795     if (obj2.isName("None")) {
796       //out->clearSoftMask(state);
797     } else if (obj2.isDict()) {
798       if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
799         alpha = gTrue;
800       } else { // "Luminosity"
801         alpha = gFalse;
802       }
803       obj3.free();
804       funcs[0] = NULL;
805       if (!obj2.dictLookup("TR", &obj3)->isNull()) {
806         funcs[0] = Function::parse(&obj3);
807         if (funcs[0]->getInputSize() != 1 ||
808             funcs[0]->getOutputSize() != 1) {
809           error(getPos(),
810                 "Invalid transfer function in soft mask in ExtGState");
811           delete funcs[0];
812           funcs[0] = NULL;
813         }
814       }
815       obj3.free();
816       if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
817         for (i = 0; i < gfxColorMaxComps; ++i) {
818           backdropColor.c[i] = 0;
819         }
820         for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
821           obj3.arrayGet(i, &obj4);
822           if (obj4.isNum()) {
823             backdropColor.c[i] = dblToCol(obj4.getNum());
824           }
825           obj4.free();
826         }
827       }
828       obj3.free();
829       if (obj2.dictLookup("G", &obj3)->isStream()) {
830         if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
831           blendingColorSpace = NULL;
832           isolated = knockout = gFalse;
833           if (!obj4.dictLookup("CS", &obj5)->isNull()) {
834             blendingColorSpace = GfxColorSpace::parse(&obj5);
835           }
836           obj5.free();
837           if (obj4.dictLookup("I", &obj5)->isBool()) {
838             isolated = obj5.getBool();
839           }
840           obj5.free();
841           if (obj4.dictLookup("K", &obj5)->isBool()) {
842             knockout = obj5.getBool();
843           }
844           obj5.free();
845           if (!haveBackdropColor) {
846             if (blendingColorSpace) {
847               blendingColorSpace->getDefaultColor(&backdropColor);
848             } else {
849               //~ need to get the parent or default color space (?)
850               for (i = 0; i < gfxColorMaxComps; ++i) {
851                 backdropColor.c[i] = 0;
852               }
853             }
854           }
855           doSoftMask(&obj3, alpha, blendingColorSpace,
856                      isolated, knockout, funcs[0], &backdropColor);
857           if (funcs[0]) {
858             delete funcs[0];
859           }
860         } else {
861           error(getPos(), "Invalid soft mask in ExtGState - missing group");
862         }
863         obj4.free();
864       } else {
865         error(getPos(), "Invalid soft mask in ExtGState - missing group");
866       }
867       obj3.free();
868     } else if (!obj2.isNull()) {
869       error(getPos(), "Invalid soft mask in ExtGState");
870     }
871   }
872   obj2.free();
874   obj1.free();
877 void PdfParser::doSoftMask(Object *str, GBool alpha,
878                      GfxColorSpace *blendingColorSpace,
879                      GBool isolated, GBool knockout,
880                      Function *transferFunc, GfxColor *backdropColor) {
881   Dict *dict, *resDict;
882   double m[6], bbox[4];
883   Object obj1, obj2;
884   int i;
886   // check for excessive recursion
887   if (formDepth > 20) {
888     return;
889   }
891   // get stream dict
892   dict = str->streamGetDict();
894   // check form type
895   dict->lookup("FormType", &obj1);
896   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
897     error(getPos(), "Unknown form type");
898   }
899   obj1.free();
901   // get bounding box
902   dict->lookup("BBox", &obj1);
903   if (!obj1.isArray()) {
904     obj1.free();
905     error(getPos(), "Bad form bounding box");
906     return;
907   }
908   for (i = 0; i < 4; ++i) {
909     obj1.arrayGet(i, &obj2);
910     bbox[i] = obj2.getNum();
911     obj2.free();
912   }
913   obj1.free();
915   // get matrix
916   dict->lookup("Matrix", &obj1);
917   if (obj1.isArray()) {
918     for (i = 0; i < 6; ++i) {
919       obj1.arrayGet(i, &obj2);
920       m[i] = obj2.getNum();
921       obj2.free();
922     }
923   } else {
924     m[0] = 1; m[1] = 0;
925     m[2] = 0; m[3] = 1;
926     m[4] = 0; m[5] = 0;
927   }
928   obj1.free();
930   // get resources
931   dict->lookup("Resources", &obj1);
932   resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
934   // draw it
935   ++formDepth;
936   doForm1(str, resDict, m, bbox, gTrue, gTrue,
937           blendingColorSpace, isolated, knockout,
938           alpha, transferFunc, backdropColor);
939   --formDepth;
941   if (blendingColorSpace) {
942     delete blendingColorSpace;
943   }
944   obj1.free();
947 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
950 //------------------------------------------------------------------------
951 // color operators
952 //------------------------------------------------------------------------
954 void PdfParser::opSetFillGray(Object args[], int numArgs) {
955   GfxColor color;
957   state->setFillPattern(NULL);
958   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
959   color.c[0] = dblToCol(args[0].getNum());
960   state->setFillColor(&color);
961   builder->updateStyle(state);
964 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
965   GfxColor color;
967   state->setStrokePattern(NULL);
968   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
969   color.c[0] = dblToCol(args[0].getNum());
970   state->setStrokeColor(&color);
971   builder->updateStyle(state);
974 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
975   GfxColor color;
976   int i;
978   state->setFillPattern(NULL);
979   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
980   for (i = 0; i < 4; ++i) {
981     color.c[i] = dblToCol(args[i].getNum());
982   }
983   state->setFillColor(&color);
984   builder->updateStyle(state);
987 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
988   GfxColor color;
989   int i;
991   state->setStrokePattern(NULL);
992   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
993   for (i = 0; i < 4; ++i) {
994     color.c[i] = dblToCol(args[i].getNum());
995   }
996   state->setStrokeColor(&color);
997   builder->updateStyle(state);
1000 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
1001   GfxColor color;
1002   int i;
1004   state->setFillPattern(NULL);
1005   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
1006   for (i = 0; i < 3; ++i) {
1007     color.c[i] = dblToCol(args[i].getNum());
1008   }
1009   state->setFillColor(&color);
1010   builder->updateStyle(state);
1013 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
1014   GfxColor color;
1015   int i;
1017   state->setStrokePattern(NULL);
1018   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1019   for (i = 0; i < 3; ++i) {
1020     color.c[i] = dblToCol(args[i].getNum());
1021   }
1022   state->setStrokeColor(&color);
1023   builder->updateStyle(state);
1026 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1027   Object obj;
1028   GfxColorSpace *colorSpace;
1029   GfxColor color;
1031   state->setFillPattern(NULL);
1032   res->lookupColorSpace(args[0].getName(), &obj);
1033   if (obj.isNull()) {
1034     colorSpace = GfxColorSpace::parse(&args[0]);
1035   } else {
1036     colorSpace = GfxColorSpace::parse(&obj);
1037   }
1038   obj.free();
1039   if (colorSpace) {
1040     state->setFillColorSpace(colorSpace);
1041     colorSpace->getDefaultColor(&color);
1042     state->setFillColor(&color);
1043     builder->updateStyle(state);
1044   } else {
1045     error(getPos(), "Bad color space (fill)");
1046   }
1049 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1050   Object obj;
1051   GfxColorSpace *colorSpace;
1052   GfxColor color;
1054   state->setStrokePattern(NULL);
1055   res->lookupColorSpace(args[0].getName(), &obj);
1056   if (obj.isNull()) {
1057     colorSpace = GfxColorSpace::parse(&args[0]);
1058   } else {
1059     colorSpace = GfxColorSpace::parse(&obj);
1060   }
1061   obj.free();
1062   if (colorSpace) {
1063     state->setStrokeColorSpace(colorSpace);
1064     colorSpace->getDefaultColor(&color);
1065     state->setStrokeColor(&color);
1066     builder->updateStyle(state);
1067   } else {
1068     error(getPos(), "Bad color space (stroke)");
1069   }
1072 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1073   GfxColor color;
1074   int i;
1076   if (numArgs != state->getFillColorSpace()->getNComps()) {
1077     error(getPos(), "Incorrect number of arguments in 'sc' command");
1078     return;
1079   }
1080   state->setFillPattern(NULL);
1081   for (i = 0; i < numArgs; ++i) {
1082     color.c[i] = dblToCol(args[i].getNum());
1083   }
1084   state->setFillColor(&color);
1085   builder->updateStyle(state);
1088 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1089   GfxColor color;
1090   int i;
1092   if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1093     error(getPos(), "Incorrect number of arguments in 'SC' command");
1094     return;
1095   }
1096   state->setStrokePattern(NULL);
1097   for (i = 0; i < numArgs; ++i) {
1098     color.c[i] = dblToCol(args[i].getNum());
1099   }
1100   state->setStrokeColor(&color);
1101   builder->updateStyle(state);
1104 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1105   GfxColor color;
1106   GfxPattern *pattern;
1107   int i;
1109   if (state->getFillColorSpace()->getMode() == csPattern) {
1110     if (numArgs > 1) {
1111       if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1112           numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1113                              ->getUnder()->getNComps()) {
1114         error(getPos(), "Incorrect number of arguments in 'scn' command");
1115         return;
1116       }
1117       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1118         if (args[i].isNum()) {
1119           color.c[i] = dblToCol(args[i].getNum());
1120         }
1121       }
1122       state->setFillColor(&color);
1123       builder->updateStyle(state);
1124     }
1125     if (args[numArgs-1].isName() &&
1126         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1127       state->setFillPattern(pattern);
1128     }
1130   } else {
1131     if (numArgs != state->getFillColorSpace()->getNComps()) {
1132       error(getPos(), "Incorrect number of arguments in 'scn' command");
1133       return;
1134     }
1135     state->setFillPattern(NULL);
1136     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1137       if (args[i].isNum()) {
1138         color.c[i] = dblToCol(args[i].getNum());
1139       }
1140     }
1141     state->setFillColor(&color);
1142     builder->updateStyle(state);
1143   }
1146 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1147   GfxColor color;
1148   GfxPattern *pattern;
1149   int i;
1151   if (state->getStrokeColorSpace()->getMode() == csPattern) {
1152     if (numArgs > 1) {
1153       if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1154                ->getUnder() ||
1155           numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1156                              ->getUnder()->getNComps()) {
1157         error(getPos(), "Incorrect number of arguments in 'SCN' command");
1158         return;
1159       }
1160       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1161         if (args[i].isNum()) {
1162           color.c[i] = dblToCol(args[i].getNum());
1163         }
1164       }
1165       state->setStrokeColor(&color);
1166       builder->updateStyle(state);
1167     }
1168     if (args[numArgs-1].isName() &&
1169         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1170       state->setStrokePattern(pattern);
1171     }
1173   } else {
1174     if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1175       error(getPos(), "Incorrect number of arguments in 'SCN' command");
1176       return;
1177     }
1178     state->setStrokePattern(NULL);
1179     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1180       if (args[i].isNum()) {
1181         color.c[i] = dblToCol(args[i].getNum());
1182       }
1183     }
1184     state->setStrokeColor(&color);
1185     builder->updateStyle(state);
1186   }
1189 //------------------------------------------------------------------------
1190 // path segment operators
1191 //------------------------------------------------------------------------
1193 void PdfParser::opMoveTo(Object args[], int numArgs) {
1194   state->moveTo(args[0].getNum(), args[1].getNum());
1197 void PdfParser::opLineTo(Object args[], int numArgs) {
1198   if (!state->isCurPt()) {
1199     error(getPos(), "No current point in lineto");
1200     return;
1201   }
1202   state->lineTo(args[0].getNum(), args[1].getNum());
1205 void PdfParser::opCurveTo(Object args[], int numArgs) {
1206   double x1, y1, x2, y2, x3, y3;
1208   if (!state->isCurPt()) {
1209     error(getPos(), "No current point in curveto");
1210     return;
1211   }
1212   x1 = args[0].getNum();
1213   y1 = args[1].getNum();
1214   x2 = args[2].getNum();
1215   y2 = args[3].getNum();
1216   x3 = args[4].getNum();
1217   y3 = args[5].getNum();
1218   state->curveTo(x1, y1, x2, y2, x3, y3);
1221 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1222   double x1, y1, x2, y2, x3, y3;
1224   if (!state->isCurPt()) {
1225     error(getPos(), "No current point in curveto1");
1226     return;
1227   }
1228   x1 = state->getCurX();
1229   y1 = state->getCurY();
1230   x2 = args[0].getNum();
1231   y2 = args[1].getNum();
1232   x3 = args[2].getNum();
1233   y3 = args[3].getNum();
1234   state->curveTo(x1, y1, x2, y2, x3, y3);
1237 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1238   double x1, y1, x2, y2, x3, y3;
1240   if (!state->isCurPt()) {
1241     error(getPos(), "No current point in curveto2");
1242     return;
1243   }
1244   x1 = args[0].getNum();
1245   y1 = args[1].getNum();
1246   x2 = args[2].getNum();
1247   y2 = args[3].getNum();
1248   x3 = x2;
1249   y3 = y2;
1250   state->curveTo(x1, y1, x2, y2, x3, y3);
1253 void PdfParser::opRectangle(Object args[], int numArgs) {
1254   double x, y, w, h;
1256   x = args[0].getNum();
1257   y = args[1].getNum();
1258   w = args[2].getNum();
1259   h = args[3].getNum();
1260   state->moveTo(x, y);
1261   state->lineTo(x + w, y);
1262   state->lineTo(x + w, y + h);
1263   state->lineTo(x, y + h);
1264   state->closePath();
1267 void PdfParser::opClosePath(Object args[], int numArgs) {
1268   if (!state->isCurPt()) {
1269     error(getPos(), "No current point in closepath");
1270     return;
1271   }
1272   state->closePath();
1275 //------------------------------------------------------------------------
1276 // path painting operators
1277 //------------------------------------------------------------------------
1279 void PdfParser::opEndPath(Object args[], int numArgs) {
1280   doEndPath();
1283 void PdfParser::opStroke(Object args[], int numArgs) {
1284   if (!state->isCurPt()) {
1285     //error(getPos(), "No path in stroke");
1286     return;
1287   }
1288   if (state->isPath()) {
1289     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1290         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1291           doPatternStrokeFallback();
1292     } else {
1293       builder->addPath(state, false, true);
1294     }
1295   }
1296   doEndPath();
1299 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1300   if (!state->isCurPt()) {
1301     //error(getPos(), "No path in closepath/stroke");
1302     return;
1303   }
1304   state->closePath();
1305   if (state->isPath()) {
1306     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1307         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1308       doPatternStrokeFallback();
1309     } else {
1310       builder->addPath(state, false, true);
1311     }
1312   }
1313   doEndPath();
1316 void PdfParser::opFill(Object args[], int numArgs) {
1317   if (!state->isCurPt()) {
1318     //error(getPos(), "No path in fill");
1319     return;
1320   }
1321   if (state->isPath()) {
1322     if (state->getFillColorSpace()->getMode() == csPattern &&
1323         !builder->isPatternTypeSupported(state->getFillPattern())) {
1324       doPatternFillFallback(gFalse);
1325     } else {
1326       builder->addPath(state, true, false);
1327     }
1328   }
1329   doEndPath();
1332 void PdfParser::opEOFill(Object args[], int numArgs) {
1333   if (!state->isCurPt()) {
1334     //error(getPos(), "No path in eofill");
1335     return;
1336   }
1337   if (state->isPath()) {
1338     if (state->getFillColorSpace()->getMode() == csPattern &&
1339         !builder->isPatternTypeSupported(state->getFillPattern())) {
1340       doPatternFillFallback(gTrue);
1341     } else {
1342       builder->addPath(state, true, false, true);
1343     }
1344   }
1345   doEndPath();
1348 void PdfParser::opFillStroke(Object args[], int numArgs) {
1349   if (!state->isCurPt()) {
1350     //error(getPos(), "No path in fill/stroke");
1351     return;
1352   }
1353   if (state->isPath()) {
1354     doFillAndStroke(gFalse);
1355   } else {
1356     builder->addPath(state, true, true);
1357   }
1358   doEndPath();
1361 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1362   if (!state->isCurPt()) {
1363     //error(getPos(), "No path in closepath/fill/stroke");
1364     return;
1365   }
1366   if (state->isPath()) {
1367     state->closePath();
1368     doFillAndStroke(gFalse);
1369   }
1370   doEndPath();
1373 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1374   if (!state->isCurPt()) {
1375     //error(getPos(), "No path in eofill/stroke");
1376     return;
1377   }
1378   if (state->isPath()) {
1379     doFillAndStroke(gTrue);
1380   }
1381   doEndPath();
1384 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1385   if (!state->isCurPt()) {
1386     //error(getPos(), "No path in closepath/eofill/stroke");
1387     return;
1388   }
1389   if (state->isPath()) {
1390     state->closePath();
1391     doFillAndStroke(gTrue);
1392   }
1393   doEndPath();
1396 void PdfParser::doFillAndStroke(GBool eoFill) {
1397     GBool fillOk = gTrue, strokeOk = gTrue;
1398     if (state->getFillColorSpace()->getMode() == csPattern &&
1399         !builder->isPatternTypeSupported(state->getFillPattern())) {
1400         fillOk = gFalse;
1401     }
1402     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1403         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1404         strokeOk = gFalse;
1405     }
1406     if (fillOk && strokeOk) {
1407         builder->addPath(state, true, true, eoFill);
1408     } else {
1409         doPatternFillFallback(eoFill);
1410         doPatternStrokeFallback();
1411     }
1414 void PdfParser::doPatternFillFallback(GBool eoFill) {
1415   GfxPattern *pattern;
1417   if (!(pattern = state->getFillPattern())) {
1418     return;
1419   }
1420   switch (pattern->getType()) {
1421   case 1:
1422     break;
1423   case 2:
1424     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1425     break;
1426   default:
1427     error(getPos(), "Unimplemented pattern type (%d) in fill",
1428           pattern->getType());
1429     break;
1430   }
1433 void PdfParser::doPatternStrokeFallback() {
1434   GfxPattern *pattern;
1436   if (!(pattern = state->getStrokePattern())) {
1437     return;
1438   }
1439   switch (pattern->getType()) {
1440   case 1:
1441     break;
1442   case 2:
1443     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1444     break;
1445   default:
1446     error(getPos(), "Unimplemented pattern type (%d) in stroke",
1447           pattern->getType());
1448     break;
1449   }
1452 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1453                                              GBool stroke, GBool eoFill) {
1454   GfxShading *shading;
1455   GfxPath *savedPath;
1456   double *ctm, *btm, *ptm;
1457   double m[6], ictm[6], m1[6];
1458   double xMin, yMin, xMax, yMax;
1459   double det;
1461   shading = sPat->getShading();
1463   // save current graphics state
1464   savedPath = state->getPath()->copy();
1465   saveState();
1467   // clip to bbox
1468   if (0 ){//shading->getHasBBox()) {
1469     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1470     state->moveTo(xMin, yMin);
1471     state->lineTo(xMax, yMin);
1472     state->lineTo(xMax, yMax);
1473     state->lineTo(xMin, yMax);
1474     state->closePath();
1475     state->clip();
1476     //builder->clip(state);
1477     state->setPath(savedPath->copy());
1478   }
1480   // clip to current path
1481   if (stroke) {
1482     state->clipToStrokePath();
1483     //out->clipToStrokePath(state);
1484   } else {
1485     state->clip();
1486     if (eoFill) {
1487       builder->clip(state, true);
1488     } else {
1489       builder->clip(state);
1490     }
1491   }
1493   // set the color space
1494   state->setFillColorSpace(shading->getColorSpace()->copy());
1496   // background color fill
1497   if (shading->getHasBackground()) {
1498     state->setFillColor(shading->getBackground());
1499     builder->addPath(state, true, false);
1500   }
1501   state->clearPath();
1503   // construct a (pattern space) -> (current space) transform matrix
1504   ctm = state->getCTM();
1505   btm = baseMatrix;
1506   ptm = sPat->getMatrix();
1507   // iCTM = invert CTM
1508   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1509   ictm[0] = ctm[3] * det;
1510   ictm[1] = -ctm[1] * det;
1511   ictm[2] = -ctm[2] * det;
1512   ictm[3] = ctm[0] * det;
1513   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1514   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1515   // m1 = PTM * BTM = PTM * base transform matrix
1516   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1517   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1518   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1519   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1520   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1521   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1522   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1523   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1524   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1525   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1526   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1527   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1528   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1530   // set the new matrix
1531   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1532   builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1534   // do shading type-specific operations
1535   switch (shading->getType()) {
1536   case 1:
1537     doFunctionShFill((GfxFunctionShading *)shading);
1538     break;
1539   case 2:
1540   case 3:
1541     // no need to implement these
1542     break;
1543   case 4:
1544   case 5:
1545     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1546     break;
1547   case 6:
1548   case 7:
1549     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1550     break;
1551   }
1553   // restore graphics state
1554   restoreState();
1555   state->setPath(savedPath);
1558 void PdfParser::opShFill(Object args[], int numArgs) {
1559   GfxShading *shading;
1560   GfxPath *savedPath;
1561   double xMin, yMin, xMax, yMax;
1562   double gradientTransform[6];
1563   double *matrix = NULL;
1564   GBool savedState = gFalse;
1566   if (!(shading = res->lookupShading(args[0].getName()))) {
1567     return;
1568   }
1570   // save current graphics state
1571   if (shading->getType() != 2 && shading->getType() != 3) {
1572     savedPath = state->getPath()->copy();
1573     saveState();
1574     savedState = gTrue;
1575   } else {  // get gradient transform if possible
1576       // check proper operator sequence
1577       // first there should be one W(*) and then one 'cm' somewhere before 'sh'
1578       GBool seenClip, seenConcat;
1579       seenClip = gFalse;
1580       seenConcat = gFalse;
1581       int i = 1;
1582       while (i <= maxOperatorHistoryDepth) {
1583         const char *opName = getPreviousOperator(i);
1584         if (!strcmp(opName, "cm")) {
1585           if (seenConcat) {   // more than one 'cm'
1586             break;
1587           } else {
1588             seenConcat = gTrue;
1589           }
1590         } else if (!strcmp(opName, "W") || !strcmp(opName, "W*")) {
1591           if (seenConcat) { // good sequence
1592             seenClip = gTrue;
1593             break;
1594           } else {  // no 'cm' so bail out
1595             break;
1596           }
1597         }
1598         i++;
1599       }
1601       if (seenConcat && seenClip) {
1602         if (builder->getTransform((double*)&gradientTransform)) {
1603           matrix = (double*)&gradientTransform;
1604           builder->setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);  // remove transform
1605         }
1606       }
1607   }
1609   // clip to bbox
1610   if (shading->getHasBBox()) {
1611     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1612     state->moveTo(xMin, yMin);
1613     state->lineTo(xMax, yMin);
1614     state->lineTo(xMax, yMax);
1615     state->lineTo(xMin, yMax);
1616     state->closePath();
1617     state->clip();
1618     if (savedState)
1619       builder->setClipPath(state);
1620     else
1621       builder->clip(state);
1622     state->clearPath();
1623   }
1625   // set the color space
1626   if (savedState)
1627     state->setFillColorSpace(shading->getColorSpace()->copy());
1629   // do shading type-specific operations
1630   switch (shading->getType()) {
1631   case 1:
1632     doFunctionShFill((GfxFunctionShading *)shading);
1633     break;
1634   case 2:
1635   case 3:
1636     if (lastClipPath) {
1637       builder->addShadedFill(shading, matrix, lastClipPath,
1638                              lastClipType == clipEO ? true : false);
1639     }
1640     break;
1641   case 4:
1642   case 5:
1643     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1644     break;
1645   case 6:
1646   case 7:
1647     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1648     break;
1649   }
1651   // restore graphics state
1652   if (savedState) {
1653     restoreState();
1654     state->setPath(savedPath);
1655   }
1657   delete shading;
1660 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1661   double x0, y0, x1, y1;
1662   GfxColor colors[4];
1664   shading->getDomain(&x0, &y0, &x1, &y1);
1665   shading->getColor(x0, y0, &colors[0]);
1666   shading->getColor(x0, y1, &colors[1]);
1667   shading->getColor(x1, y0, &colors[2]);
1668   shading->getColor(x1, y1, &colors[3]);
1669   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1672 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1673                             double x0, double y0,
1674                             double x1, double y1,
1675                             GfxColor *colors, int depth) {
1676   GfxColor fillColor;
1677   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1678   GfxColor colors2[4];
1679   double *matrix;
1680   double xM, yM;
1681   int nComps, i, j;
1683   nComps = shading->getColorSpace()->getNComps();
1684   matrix = shading->getMatrix();
1686   // compare the four corner colors
1687   for (i = 0; i < 4; ++i) {
1688     for (j = 0; j < nComps; ++j) {
1689       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1690         break;
1691       }
1692     }
1693     if (j < nComps) {
1694       break;
1695     }
1696   }
1698   // center of the rectangle
1699   xM = 0.5 * (x0 + x1);
1700   yM = 0.5 * (y0 + y1);
1702   // the four corner colors are close (or we hit the recursive limit)
1703   // -- fill the rectangle; but require at least one subdivision
1704   // (depth==0) to avoid problems when the four outer corners of the
1705   // shaded region are the same color
1706   if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1708     // use the center color
1709     shading->getColor(xM, yM, &fillColor);
1710     state->setFillColor(&fillColor);
1712     // fill the rectangle
1713     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1714                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1715     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1716                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1717     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1718                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1719     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1720                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1721     state->closePath();
1722     builder->addPath(state, true, false);
1723     state->clearPath();
1725   // the four corner colors are not close enough -- subdivide the
1726   // rectangle
1727   } else {
1729     // colors[0]       colorM0       colors[2]
1730     //   (x0,y0)       (xM,y0)       (x1,y0)
1731     //         +----------+----------+
1732     //         |          |          |
1733     //         |    UL    |    UR    |
1734     // color0M |       colorMM       | color1M
1735     // (x0,yM) +----------+----------+ (x1,yM)
1736     //         |       (xM,yM)       |
1737     //         |    LL    |    LR    |
1738     //         |          |          |
1739     //         +----------+----------+
1740     // colors[1]       colorM1       colors[3]
1741     //   (x0,y1)       (xM,y1)       (x1,y1)
1743     shading->getColor(x0, yM, &color0M);
1744     shading->getColor(x1, yM, &color1M);
1745     shading->getColor(xM, y0, &colorM0);
1746     shading->getColor(xM, y1, &colorM1);
1747     shading->getColor(xM, yM, &colorMM);
1749     // upper-left sub-rectangle
1750     colors2[0] = colors[0];
1751     colors2[1] = color0M;
1752     colors2[2] = colorM0;
1753     colors2[3] = colorMM;
1754     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1755     
1756     // lower-left sub-rectangle
1757     colors2[0] = color0M;
1758     colors2[1] = colors[1];
1759     colors2[2] = colorMM;
1760     colors2[3] = colorM1;
1761     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1762     
1763     // upper-right sub-rectangle
1764     colors2[0] = colorM0;
1765     colors2[1] = colorMM;
1766     colors2[2] = colors[2];
1767     colors2[3] = color1M;
1768     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1770     // lower-right sub-rectangle
1771     colors2[0] = colorMM;
1772     colors2[1] = colorM1;
1773     colors2[2] = color1M;
1774     colors2[3] = colors[3];
1775     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1776   }
1779 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1780   double x0, y0, x1, y1, x2, y2;
1781   GfxColor color0, color1, color2;
1782   int i;
1784   for (i = 0; i < shading->getNTriangles(); ++i) {
1785     shading->getTriangle(i, &x0, &y0, &color0,
1786                          &x1, &y1, &color1,
1787                          &x2, &y2, &color2);
1788     gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1789                         shading->getColorSpace()->getNComps(), 0);
1790   }
1793 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1794                               double x1, double y1, GfxColor *color1,
1795                               double x2, double y2, GfxColor *color2,
1796                               int nComps, int depth) {
1797   double x01, y01, x12, y12, x20, y20;
1798   GfxColor color01, color12, color20;
1799   int i;
1801   for (i = 0; i < nComps; ++i) {
1802     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1803         abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1804       break;
1805     }
1806   }
1807   if (i == nComps || depth == gouraudMaxDepth) {
1808     state->setFillColor(color0);
1809     state->moveTo(x0, y0);
1810     state->lineTo(x1, y1);
1811     state->lineTo(x2, y2);
1812     state->closePath();
1813     builder->addPath(state, true, false);
1814     state->clearPath();
1815   } else {
1816     x01 = 0.5 * (x0 + x1);
1817     y01 = 0.5 * (y0 + y1);
1818     x12 = 0.5 * (x1 + x2);
1819     y12 = 0.5 * (y1 + y2);
1820     x20 = 0.5 * (x2 + x0);
1821     y20 = 0.5 * (y2 + y0);
1822     //~ if the shading has a Function, this should interpolate on the
1823     //~ function parameter, not on the color components
1824     for (i = 0; i < nComps; ++i) {
1825       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1826       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1827       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1828     }
1829     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1830                         x20, y20, &color20, nComps, depth + 1);
1831     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1832                         x12, y12, &color12, nComps, depth + 1);
1833     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1834                         x20, y20, &color20, nComps, depth + 1);
1835     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1836                         x2, y2, color2, nComps, depth + 1);
1837   }
1840 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1841   int start, i;
1843   if (shading->getNPatches() > 128) {
1844     start = 3;
1845   } else if (shading->getNPatches() > 64) {
1846     start = 2;
1847   } else if (shading->getNPatches() > 16) {
1848     start = 1;
1849   } else {
1850     start = 0;
1851   }
1852   for (i = 0; i < shading->getNPatches(); ++i) {
1853     fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1854               start);
1855   }
1858 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1859   GfxPatch patch00, patch01, patch10, patch11;
1860   double xx[4][8], yy[4][8];
1861   double xxm, yym;
1862   int i;
1864   for (i = 0; i < nComps; ++i) {
1865     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1866           > patchColorDelta ||
1867         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1868           > patchColorDelta ||
1869         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1870           > patchColorDelta ||
1871         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1872           > patchColorDelta) {
1873       break;
1874     }
1875   }
1876   if (i == nComps || depth == patchMaxDepth) {
1877     state->setFillColor(&patch->color[0][0]);
1878     state->moveTo(patch->x[0][0], patch->y[0][0]);
1879     state->curveTo(patch->x[0][1], patch->y[0][1],
1880                    patch->x[0][2], patch->y[0][2],
1881                    patch->x[0][3], patch->y[0][3]);
1882     state->curveTo(patch->x[1][3], patch->y[1][3],
1883                    patch->x[2][3], patch->y[2][3],
1884                    patch->x[3][3], patch->y[3][3]);
1885     state->curveTo(patch->x[3][2], patch->y[3][2],
1886                    patch->x[3][1], patch->y[3][1],
1887                    patch->x[3][0], patch->y[3][0]);
1888     state->curveTo(patch->x[2][0], patch->y[2][0],
1889                    patch->x[1][0], patch->y[1][0],
1890                    patch->x[0][0], patch->y[0][0]);
1891     state->closePath();
1892     builder->addPath(state, true, false);
1893     state->clearPath();
1894   } else {
1895     for (i = 0; i < 4; ++i) {
1896       xx[i][0] = patch->x[i][0];
1897       yy[i][0] = patch->y[i][0];
1898       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1899       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1900       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1901       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1902       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1903       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1904       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1905       yy[i][2] = 0.5 * (yy[i][1] + yym);
1906       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1907       yy[i][5] = 0.5 * (yym + yy[i][6]);
1908       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1909       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1910       xx[i][7] = patch->x[i][3];
1911       yy[i][7] = patch->y[i][3];
1912     }
1913     for (i = 0; i < 4; ++i) {
1914       patch00.x[0][i] = xx[0][i];
1915       patch00.y[0][i] = yy[0][i];
1916       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1917       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1918       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1919       yym = 0.5 * (yy[1][i] + yy[2][i]);
1920       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1921       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1922       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1923       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1924       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1925       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1926       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1927       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1928       patch10.x[0][i] = patch00.x[3][i];
1929       patch10.y[0][i] = patch00.y[3][i];
1930       patch10.x[3][i] = xx[3][i];
1931       patch10.y[3][i] = yy[3][i];
1932     }
1933     for (i = 4; i < 8; ++i) {
1934       patch01.x[0][i-4] = xx[0][i];
1935       patch01.y[0][i-4] = yy[0][i];
1936       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1937       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1938       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1939       yym = 0.5 * (yy[1][i] + yy[2][i]);
1940       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1941       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1942       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1943       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1944       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1945       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1946       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1947       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1948       patch11.x[0][i-4] = patch01.x[3][i-4];
1949       patch11.y[0][i-4] = patch01.y[3][i-4];
1950       patch11.x[3][i-4] = xx[3][i];
1951       patch11.y[3][i-4] = yy[3][i];
1952     }
1953     //~ if the shading has a Function, this should interpolate on the
1954     //~ function parameter, not on the color components
1955     for (i = 0; i < nComps; ++i) {
1956       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1957       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1958                                   patch->color[0][1].c[i]) / 2;
1959       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1960       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1961       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1962                                   patch->color[1][1].c[i]) / 2;
1963       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1964       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1965       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1966                                   patch->color[1][0].c[i]) / 2;
1967       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1968       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1969       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1970                                   patch->color[0][0].c[i]) / 2;
1971       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1972       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1973                                   patch01.color[1][1].c[i]) / 2;
1974       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1975       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1976       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1977     }
1978     fillPatch(&patch00, nComps, depth + 1);
1979     fillPatch(&patch10, nComps, depth + 1);
1980     fillPatch(&patch01, nComps, depth + 1);
1981     fillPatch(&patch11, nComps, depth + 1);
1982   }
1985 void PdfParser::doEndPath() {
1986   if (state->isCurPt() && clip != clipNone) {
1987     state->clip();
1988     if (clip == clipNormal) {
1989       builder->clip(state);
1990     } else {
1991       builder->clip(state, true);
1992     }
1993   }
1994   clip = clipNone;
1995   state->clearPath();
1998 //------------------------------------------------------------------------
1999 // path clipping operators
2000 //------------------------------------------------------------------------
2002 void PdfParser::opClip(Object args[], int numArgs) {
2003   clip = clipNormal;
2004   lastClipType = clipNormal;
2005   if (lastClipPath)
2006       delete lastClipPath;
2007   lastClipPath = state->getPath()->copy();
2010 void PdfParser::opEOClip(Object args[], int numArgs) {
2011   clip = clipEO;
2012   lastClipType = clipEO;
2013   if (lastClipPath)
2014       delete lastClipPath;
2015   lastClipPath = state->getPath()->copy();
2018 //------------------------------------------------------------------------
2019 // text object operators
2020 //------------------------------------------------------------------------
2022 void PdfParser::opBeginText(Object args[], int numArgs) {
2023   state->setTextMat(1, 0, 0, 1, 0, 0);
2024   state->textMoveTo(0, 0);
2025   builder->updateTextPosition(0.0, 0.0);
2026   fontChanged = gTrue;
2027   builder->beginTextObject(state);
2030 void PdfParser::opEndText(Object args[], int numArgs) {
2031   builder->endTextObject(state);
2034 //------------------------------------------------------------------------
2035 // text state operators
2036 //------------------------------------------------------------------------
2038 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2039   state->setCharSpace(args[0].getNum());
2042 void PdfParser::opSetFont(Object args[], int numArgs) {
2043   GfxFont *font;
2045   if (!(font = res->lookupFont(args[0].getName()))) {
2046     return;
2047   }
2048   if (printCommands) {
2049     printf("  font: tag=%s name='%s' %g\n",
2050            font->getTag()->getCString(),
2051            font->getName() ? font->getName()->getCString() : "???",
2052            args[1].getNum());
2053     fflush(stdout);
2054   }
2056   font->incRefCnt();
2057   state->setFont(font, args[1].getNum());
2058   fontChanged = gTrue;
2061 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2062   state->setLeading(args[0].getNum());
2065 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2066   state->setRender(args[0].getInt());
2067   builder->updateStyle(state);
2070 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2071   state->setRise(args[0].getNum());
2074 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2075   state->setWordSpace(args[0].getNum());
2078 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2079   state->setHorizScaling(args[0].getNum());
2080   fontChanged = gTrue;
2083 //------------------------------------------------------------------------
2084 // text positioning operators
2085 //------------------------------------------------------------------------
2087 void PdfParser::opTextMove(Object args[], int numArgs) {
2088   double tx, ty;
2090   tx = state->getLineX() + args[0].getNum();
2091   ty = state->getLineY() + args[1].getNum();
2092   state->textMoveTo(tx, ty);
2093   builder->updateTextPosition(tx, ty);
2096 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2097   double tx, ty;
2099   tx = state->getLineX() + args[0].getNum();
2100   ty = args[1].getNum();
2101   state->setLeading(-ty);
2102   ty += state->getLineY();
2103   state->textMoveTo(tx, ty);
2104   builder->updateTextPosition(tx, ty);
2107 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2108   state->setTextMat(args[0].getNum(), args[1].getNum(),
2109                     args[2].getNum(), args[3].getNum(),
2110                     args[4].getNum(), args[5].getNum());
2111   state->textMoveTo(0, 0);
2112   builder->updateTextMatrix(state);
2113   builder->updateTextPosition(0.0, 0.0);
2114   fontChanged = gTrue;
2117 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2118   double tx, ty;
2120   tx = state->getLineX();
2121   ty = state->getLineY() - state->getLeading();
2122   state->textMoveTo(tx, ty);
2123   builder->updateTextPosition(tx, ty);
2126 //------------------------------------------------------------------------
2127 // text string operators
2128 //------------------------------------------------------------------------
2130 void PdfParser::opShowText(Object args[], int numArgs) {
2131   if (!state->getFont()) {
2132     error(getPos(), "No font in show");
2133     return;
2134   }
2135   if (fontChanged) {
2136     builder->updateFont(state);
2137     fontChanged = gFalse;
2138   }
2139   doShowText(args[0].getString());
2142 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2143   double tx, ty;
2145   if (!state->getFont()) {
2146     error(getPos(), "No font in move/show");
2147     return;
2148   }
2149   if (fontChanged) {
2150     builder->updateFont(state);
2151     fontChanged = gFalse;
2152   }
2153   tx = state->getLineX();
2154   ty = state->getLineY() - state->getLeading();
2155   state->textMoveTo(tx, ty);
2156   builder->updateTextPosition(tx, ty);
2157   doShowText(args[0].getString());
2160 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2161   double tx, ty;
2163   if (!state->getFont()) {
2164     error(getPos(), "No font in move/set/show");
2165     return;
2166   }
2167   if (fontChanged) {
2168     builder->updateFont(state);
2169     fontChanged = gFalse;
2170   }
2171   state->setWordSpace(args[0].getNum());
2172   state->setCharSpace(args[1].getNum());
2173   tx = state->getLineX();
2174   ty = state->getLineY() - state->getLeading();
2175   state->textMoveTo(tx, ty);
2176   builder->updateTextPosition(tx, ty);
2177   doShowText(args[2].getString());
2180 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2181   Array *a;
2182   Object obj;
2183   int wMode;
2184   int i;
2186   if (!state->getFont()) {
2187     error(getPos(), "No font in show/space");
2188     return;
2189   }
2190   if (fontChanged) {
2191     builder->updateFont(state);
2192     fontChanged = gFalse;
2193   }
2194   wMode = state->getFont()->getWMode();
2195   a = args[0].getArray();
2196   for (i = 0; i < a->getLength(); ++i) {
2197     a->get(i, &obj);
2198     if (obj.isNum()) {
2199       // this uses the absolute value of the font size to match
2200       // Acrobat's behavior
2201       if (wMode) {
2202         state->textShift(0, -obj.getNum() * 0.001 *
2203                             fabs(state->getFontSize()));
2204       } else {
2205         state->textShift(-obj.getNum() * 0.001 *
2206                          fabs(state->getFontSize()), 0);
2207       }
2208       builder->updateTextShift(state, obj.getNum());
2209     } else if (obj.isString()) {
2210       doShowText(obj.getString());
2211     } else {
2212       error(getPos(), "Element of show/space array must be number or string");
2213     }
2214     obj.free();
2215   }
2218 void PdfParser::doShowText(GooString *s) {
2219   GfxFont *font;
2220   int wMode;
2221   double riseX, riseY;
2222   CharCode code;
2223   Unicode u[8];
2224   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2225   double originX, originY, tOriginX, tOriginY;
2226   double oldCTM[6], newCTM[6];
2227   double *mat;
2228   Object charProc;
2229   Dict *resDict;
2230   Parser *oldParser;
2231   char *p;
2232   int len, n, uLen, nChars, nSpaces, i;
2234   font = state->getFont();
2235   wMode = font->getWMode();
2237   builder->beginString(state, s);
2239   // handle a Type 3 char
2240   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2241     mat = state->getCTM();
2242     for (i = 0; i < 6; ++i) {
2243       oldCTM[i] = mat[i];
2244     }
2245     mat = state->getTextMat();
2246     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2247     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2248     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2249     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2250     mat = font->getFontMatrix();
2251     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2252     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2253     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2254     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2255     newCTM[0] *= state->getFontSize();
2256     newCTM[1] *= state->getFontSize();
2257     newCTM[2] *= state->getFontSize();
2258     newCTM[3] *= state->getFontSize();
2259     newCTM[0] *= state->getHorizScaling();
2260     newCTM[2] *= state->getHorizScaling();
2261     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2262     curX = state->getCurX();
2263     curY = state->getCurY();
2264     lineX = state->getLineX();
2265     lineY = state->getLineY();
2266     oldParser = parser;
2267     p = s->getCString();
2268     len = s->getLength();
2269     while (len > 0) {
2270       n = font->getNextChar(p, len, &code,
2271                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2272                             &dx, &dy, &originX, &originY);
2273       dx = dx * state->getFontSize() + state->getCharSpace();
2274       if (n == 1 && *p == ' ') {
2275         dx += state->getWordSpace();
2276       }
2277       dx *= state->getHorizScaling();
2278       dy *= state->getFontSize();
2279       state->textTransformDelta(dx, dy, &tdx, &tdy);
2280       state->transform(curX + riseX, curY + riseY, &x, &y);
2281       saveState();
2282       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2283       //~ the CTM concat values here are wrong (but never used)
2284       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2285       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2286                                code, u, uLen)) {*/
2287         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2288         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2289           pushResources(resDict);
2290         }
2291         if (charProc.isStream()) {
2292           //parse(&charProc, gFalse); // TODO: parse into SVG font
2293         } else {
2294           error(getPos(), "Missing or bad Type3 CharProc entry");
2295         }
2296         //out->endType3Char(state);
2297         if (resDict) {
2298           popResources();
2299         }
2300         charProc.free();
2301       }
2302       restoreState();
2303       // GfxState::restore() does *not* restore the current position,
2304       // so we deal with it here using (curX, curY) and (lineX, lineY)
2305       curX += tdx;
2306       curY += tdy;
2307       state->moveTo(curX, curY);
2308       state->textSetPos(lineX, lineY);
2309       p += n;
2310       len -= n;
2311     }
2312     parser = oldParser;
2314   } else {
2315     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2316     p = s->getCString();
2317     len = s->getLength();
2318     while (len > 0) {
2319       n = font->getNextChar(p, len, &code,
2320                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2321                             &dx, &dy, &originX, &originY);
2322       
2323       if (wMode) {
2324         dx *= state->getFontSize();
2325         dy = dy * state->getFontSize() + state->getCharSpace();
2326         if (n == 1 && *p == ' ') {
2327           dy += state->getWordSpace();
2328         }
2329       } else {
2330         dx = dx * state->getFontSize() + state->getCharSpace();
2331         if (n == 1 && *p == ' ') {
2332           dx += state->getWordSpace();
2333         }
2334         dx *= state->getHorizScaling();
2335         dy *= state->getFontSize();
2336       }
2337       state->textTransformDelta(dx, dy, &tdx, &tdy);
2338       originX *= state->getFontSize();
2339       originY *= state->getFontSize();
2340       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2341       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2342                        dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2343       state->shift(tdx, tdy);
2344       p += n;
2345       len -= n;
2346     }
2347   }
2349   builder->endString(state);
2352 //------------------------------------------------------------------------
2353 // XObject operators
2354 //------------------------------------------------------------------------
2356 void PdfParser::opXObject(Object args[], int numArgs) {
2357   char *name;
2358   Object obj1, obj2, obj3, refObj;
2360   name = args[0].getName();
2361   if (!res->lookupXObject(name, &obj1)) {
2362     return;
2363   }
2364   if (!obj1.isStream()) {
2365     error(getPos(), "XObject '%s' is wrong type", name);
2366     obj1.free();
2367     return;
2368   }
2369   obj1.streamGetDict()->lookup("Subtype", &obj2);
2370   if (obj2.isName("Image")) {
2371     res->lookupXObjectNF(name, &refObj);
2372     doImage(&refObj, obj1.getStream(), gFalse);
2373     refObj.free();
2374   } else if (obj2.isName("Form")) {
2375     doForm(&obj1);
2376   } else if (obj2.isName("PS")) {
2377     obj1.streamGetDict()->lookup("Level1", &obj3);
2378 /*    out->psXObject(obj1.getStream(),
2379                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2380   } else if (obj2.isName()) {
2381     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2382   } else {
2383     error(getPos(), "XObject subtype is missing or wrong type");
2384   }
2385   obj2.free();
2386   obj1.free();
2389 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2390   Dict *dict, *maskDict;
2391   int width, height;
2392   int bits, maskBits;
2393   StreamColorSpaceMode csMode;
2394   GBool mask;
2395   GBool invert;
2396   GfxColorSpace *colorSpace, *maskColorSpace;
2397   GfxImageColorMap *colorMap, *maskColorMap;
2398   Object maskObj, smaskObj;
2399   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2400   int maskColors[2*gfxColorMaxComps];
2401   int maskWidth, maskHeight;
2402   GBool maskInvert;
2403   Stream *maskStr;
2404   Object obj1, obj2;
2405   int i;
2407   // get info from the stream
2408   bits = 0;
2409   csMode = streamCSNone;
2410   str->getImageParams(&bits, &csMode);
2412   // get stream dict
2413   dict = str->getDict();
2415   // get size
2416   dict->lookup("Width", &obj1);
2417   if (obj1.isNull()) {
2418     obj1.free();
2419     dict->lookup("W", &obj1);
2420   }
2421   if (obj1.isInt())
2422     width = obj1.getInt();
2423   else if (obj1.isReal())
2424     width = (int)obj1.getReal();
2425   else
2426     goto err2;
2427   obj1.free();
2428   dict->lookup("Height", &obj1);
2429   if (obj1.isNull()) {
2430     obj1.free();
2431     dict->lookup("H", &obj1);
2432   }
2433   if (obj1.isInt())
2434     height = obj1.getInt();
2435   else if (obj1.isReal())
2436     height = (int)obj1.getReal();
2437   else
2438     goto err2;
2439   obj1.free();
2441   // image or mask?
2442   dict->lookup("ImageMask", &obj1);
2443   if (obj1.isNull()) {
2444     obj1.free();
2445     dict->lookup("IM", &obj1);
2446   }
2447   mask = gFalse;
2448   if (obj1.isBool())
2449     mask = obj1.getBool();
2450   else if (!obj1.isNull())
2451     goto err2;
2452   obj1.free();
2454   // bit depth
2455   if (bits == 0) {
2456     dict->lookup("BitsPerComponent", &obj1);
2457     if (obj1.isNull()) {
2458       obj1.free();
2459       dict->lookup("BPC", &obj1);
2460     }
2461     if (obj1.isInt()) {
2462       bits = obj1.getInt();
2463     } else if (mask) {
2464       bits = 1;
2465     } else {
2466       goto err2;
2467     }
2468     obj1.free();
2469   }
2471   // display a mask
2472   if (mask) {
2474     // check for inverted mask
2475     if (bits != 1)
2476       goto err1;
2477     invert = gFalse;
2478     dict->lookup("Decode", &obj1);
2479     if (obj1.isNull()) {
2480       obj1.free();
2481       dict->lookup("D", &obj1);
2482     }
2483     if (obj1.isArray()) {
2484       obj1.arrayGet(0, &obj2);
2485       if (obj2.isInt() && obj2.getInt() == 1)
2486         invert = gTrue;
2487       obj2.free();
2488     } else if (!obj1.isNull()) {
2489       goto err2;
2490     }
2491     obj1.free();
2493     // draw it
2494     builder->addImageMask(state, str, width, height, invert);
2496   } else {
2498     // get color space and color map
2499     dict->lookup("ColorSpace", &obj1);
2500     if (obj1.isNull()) {
2501       obj1.free();
2502       dict->lookup("CS", &obj1);
2503     }
2504     if (obj1.isName()) {
2505       res->lookupColorSpace(obj1.getName(), &obj2);
2506       if (!obj2.isNull()) {
2507         obj1.free();
2508         obj1 = obj2;
2509       } else {
2510         obj2.free();
2511       }
2512     }
2513     if (!obj1.isNull()) {
2514       colorSpace = GfxColorSpace::parse(&obj1);
2515     } else if (csMode == streamCSDeviceGray) {
2516       colorSpace = new GfxDeviceGrayColorSpace();
2517     } else if (csMode == streamCSDeviceRGB) {
2518       colorSpace = new GfxDeviceRGBColorSpace();
2519     } else if (csMode == streamCSDeviceCMYK) {
2520       colorSpace = new GfxDeviceCMYKColorSpace();
2521     } else {
2522       colorSpace = NULL;
2523     }
2524     obj1.free();
2525     if (!colorSpace) {
2526       goto err1;
2527     }
2528     dict->lookup("Decode", &obj1);
2529     if (obj1.isNull()) {
2530       obj1.free();
2531       dict->lookup("D", &obj1);
2532     }
2533     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2534     obj1.free();
2535     if (!colorMap->isOk()) {
2536       delete colorMap;
2537       goto err1;
2538     }
2540     // get the mask
2541     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2542     maskStr = NULL; // make gcc happy
2543     maskWidth = maskHeight = 0; // make gcc happy
2544     maskInvert = gFalse; // make gcc happy
2545     maskColorMap = NULL; // make gcc happy
2546     dict->lookup("Mask", &maskObj);
2547     dict->lookup("SMask", &smaskObj);
2548     if (smaskObj.isStream()) {
2549       // soft mask
2550       if (inlineImg) {
2551         goto err1;
2552       }
2553       maskStr = smaskObj.getStream();
2554       maskDict = smaskObj.streamGetDict();
2555       maskDict->lookup("Width", &obj1);
2556       if (obj1.isNull()) {
2557         obj1.free();
2558         maskDict->lookup("W", &obj1);
2559       }
2560       if (!obj1.isInt()) {
2561         goto err2;
2562       }
2563       maskWidth = obj1.getInt();
2564       obj1.free();
2565       maskDict->lookup("Height", &obj1);
2566       if (obj1.isNull()) {
2567         obj1.free();
2568         maskDict->lookup("H", &obj1);
2569       }
2570       if (!obj1.isInt()) {
2571         goto err2;
2572       }
2573       maskHeight = obj1.getInt();
2574       obj1.free();
2575       maskDict->lookup("BitsPerComponent", &obj1);
2576       if (obj1.isNull()) {
2577         obj1.free();
2578         maskDict->lookup("BPC", &obj1);
2579       }
2580       if (!obj1.isInt()) {
2581         goto err2;
2582       }
2583       maskBits = obj1.getInt();
2584       obj1.free();
2585       maskDict->lookup("ColorSpace", &obj1);
2586       if (obj1.isNull()) {
2587         obj1.free();
2588         maskDict->lookup("CS", &obj1);
2589       }
2590       if (obj1.isName()) {
2591         res->lookupColorSpace(obj1.getName(), &obj2);
2592         if (!obj2.isNull()) {
2593           obj1.free();
2594           obj1 = obj2;
2595         } else {
2596           obj2.free();
2597         }
2598       }
2599       maskColorSpace = GfxColorSpace::parse(&obj1);
2600       obj1.free();
2601       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2602         goto err1;
2603       }
2604       maskDict->lookup("Decode", &obj1);
2605       if (obj1.isNull()) {
2606         obj1.free();
2607         maskDict->lookup("D", &obj1);
2608       }
2609       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2610       obj1.free();
2611       if (!maskColorMap->isOk()) {
2612         delete maskColorMap;
2613         goto err1;
2614       }
2615       //~ handle the Matte entry
2616       haveSoftMask = gTrue;
2617     } else if (maskObj.isArray()) {
2618       // color key mask
2619       for (i = 0;
2620            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2621            ++i) {
2622         maskObj.arrayGet(i, &obj1);
2623         maskColors[i] = obj1.getInt();
2624         obj1.free();
2625       }
2626       haveColorKeyMask = gTrue;
2627     } else if (maskObj.isStream()) {
2628       // explicit mask
2629       if (inlineImg) {
2630         goto err1;
2631       }
2632       maskStr = maskObj.getStream();
2633       maskDict = maskObj.streamGetDict();
2634       maskDict->lookup("Width", &obj1);
2635       if (obj1.isNull()) {
2636         obj1.free();
2637         maskDict->lookup("W", &obj1);
2638       }
2639       if (!obj1.isInt()) {
2640         goto err2;
2641       }
2642       maskWidth = obj1.getInt();
2643       obj1.free();
2644       maskDict->lookup("Height", &obj1);
2645       if (obj1.isNull()) {
2646         obj1.free();
2647         maskDict->lookup("H", &obj1);
2648       }
2649       if (!obj1.isInt()) {
2650         goto err2;
2651       }
2652       maskHeight = obj1.getInt();
2653       obj1.free();
2654       maskDict->lookup("ImageMask", &obj1);
2655       if (obj1.isNull()) {
2656         obj1.free();
2657         maskDict->lookup("IM", &obj1);
2658       }
2659       if (!obj1.isBool() || !obj1.getBool()) {
2660         goto err2;
2661       }
2662       obj1.free();
2663       maskInvert = gFalse;
2664       maskDict->lookup("Decode", &obj1);
2665       if (obj1.isNull()) {
2666         obj1.free();
2667         maskDict->lookup("D", &obj1);
2668       }
2669       if (obj1.isArray()) {
2670         obj1.arrayGet(0, &obj2);
2671         if (obj2.isInt() && obj2.getInt() == 1) {
2672           maskInvert = gTrue;
2673         }
2674         obj2.free();
2675       } else if (!obj1.isNull()) {
2676         goto err2;
2677       }
2678       obj1.free();
2679       haveExplicitMask = gTrue;
2680     }
2682     // draw it
2683     if (haveSoftMask) {
2684         builder->addSoftMaskedImage(state, str, width, height, colorMap,
2685                                     maskStr, maskWidth, maskHeight, maskColorMap);
2686       delete maskColorMap;
2687     } else if (haveExplicitMask) {
2688         builder->addMaskedImage(state, str, width, height, colorMap,
2689                                 maskStr, maskWidth, maskHeight, maskInvert);
2690     } else {
2691       builder->addImage(state, str, width, height, colorMap,
2692                         haveColorKeyMask ? maskColors : (int *)NULL);
2693     }
2694     delete colorMap;
2696     maskObj.free();
2697     smaskObj.free();
2698   }
2700   return;
2702  err2:
2703   obj1.free();
2704  err1:
2705   error(getPos(), "Bad image parameters");
2708 void PdfParser::doForm(Object *str) {
2709   Dict *dict;
2710   GBool transpGroup, isolated, knockout;
2711   GfxColorSpace *blendingColorSpace;
2712   Object matrixObj, bboxObj;
2713   double m[6], bbox[4];
2714   Object resObj;
2715   Dict *resDict;
2716   Object obj1, obj2, obj3;
2717   int i;
2719   // check for excessive recursion
2720   if (formDepth > 20) {
2721     return;
2722   }
2724   // get stream dict
2725   dict = str->streamGetDict();
2727   // check form type
2728   dict->lookup("FormType", &obj1);
2729   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2730     error(getPos(), "Unknown form type");
2731   }
2732   obj1.free();
2734   // get bounding box
2735   dict->lookup("BBox", &bboxObj);
2736   if (!bboxObj.isArray()) {
2737     bboxObj.free();
2738     error(getPos(), "Bad form bounding box");
2739     return;
2740   }
2741   for (i = 0; i < 4; ++i) {
2742     bboxObj.arrayGet(i, &obj1);
2743     bbox[i] = obj1.getNum();
2744     obj1.free();
2745   }
2746   bboxObj.free();
2748   // get matrix
2749   dict->lookup("Matrix", &matrixObj);
2750   if (matrixObj.isArray()) {
2751     for (i = 0; i < 6; ++i) {
2752       matrixObj.arrayGet(i, &obj1);
2753       m[i] = obj1.getNum();
2754       obj1.free();
2755     }
2756   } else {
2757     m[0] = 1; m[1] = 0;
2758     m[2] = 0; m[3] = 1;
2759     m[4] = 0; m[5] = 0;
2760   }
2761   matrixObj.free();
2763   // get resources
2764   dict->lookup("Resources", &resObj);
2765   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2767   // check for a transparency group
2768   transpGroup = isolated = knockout = gFalse;
2769   blendingColorSpace = NULL;
2770   if (dict->lookup("Group", &obj1)->isDict()) {
2771     if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2772       transpGroup = gTrue;
2773       if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2774         blendingColorSpace = GfxColorSpace::parse(&obj3);
2775       }
2776       obj3.free();
2777       if (obj1.dictLookup("I", &obj3)->isBool()) {
2778         isolated = obj3.getBool();
2779       }
2780       obj3.free();
2781       if (obj1.dictLookup("K", &obj3)->isBool()) {
2782         knockout = obj3.getBool();
2783       }
2784       obj3.free();
2785     }
2786     obj2.free();
2787   }
2788   obj1.free();
2790   // draw it
2791   ++formDepth;
2792   doForm1(str, resDict, m, bbox,
2793           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2794   --formDepth;
2796   if (blendingColorSpace) {
2797     delete blendingColorSpace;
2798   }
2799   resObj.free();
2802 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2803                   GBool transpGroup, GBool softMask,
2804                   GfxColorSpace *blendingColorSpace,
2805                   GBool isolated, GBool knockout,
2806                   GBool alpha, Function *transferFunc,
2807                   GfxColor *backdropColor) {
2808   Parser *oldParser;
2809   double oldBaseMatrix[6];
2810   int i;
2812   // push new resources on stack
2813   pushResources(resDict);
2815   // save current graphics state
2816   saveState();
2818   // kill any pre-existing path
2819   state->clearPath();
2821   // save current parser
2822   oldParser = parser;
2824   // set form transformation matrix
2825   state->concatCTM(matrix[0], matrix[1], matrix[2],
2826                    matrix[3], matrix[4], matrix[5]);
2827   builder->setTransform(matrix[0], matrix[1], matrix[2],
2828                         matrix[3], matrix[4], matrix[5]);
2830   // set form bounding box
2831   state->moveTo(bbox[0], bbox[1]);
2832   state->lineTo(bbox[2], bbox[1]);
2833   state->lineTo(bbox[2], bbox[3]);
2834   state->lineTo(bbox[0], bbox[3]);
2835   state->closePath();
2836   state->clip();
2837   builder->clip(state);
2838   state->clearPath();
2840   if (softMask || transpGroup) {
2841     if (state->getBlendMode() != gfxBlendNormal) {
2842       state->setBlendMode(gfxBlendNormal);
2843     }
2844     if (state->getFillOpacity() != 1) {
2845       state->setFillOpacity(1);
2846     }
2847     if (state->getStrokeOpacity() != 1) {
2848       state->setStrokeOpacity(1);
2849     }
2850     //out->clearSoftMask(state);
2851     //out->beginTransparencyGroup(state, bbox, blendingColorSpace,
2852     //                          isolated, knockout, softMask);
2853   }
2855   // set new base matrix
2856   for (i = 0; i < 6; ++i) {
2857     oldBaseMatrix[i] = baseMatrix[i];
2858     baseMatrix[i] = state->getCTM()[i];
2859   }
2861   // draw the form
2862   parse(str, gFalse);
2864   if (softMask || transpGroup) {
2865     //out->endTransparencyGroup(state);
2866   }
2868   // restore base matrix
2869   for (i = 0; i < 6; ++i) {
2870     baseMatrix[i] = oldBaseMatrix[i];
2871   }
2873   // restore parser
2874   parser = oldParser;
2876   // restore graphics state
2877   restoreState();
2879   // pop resource stack
2880   popResources();
2882   if (softMask) {
2883     //out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2884   } else if (transpGroup) {
2885     //out->paintTransparencyGroup(state, bbox);
2886   }
2888   return;
2891 //------------------------------------------------------------------------
2892 // in-line image operators
2893 //------------------------------------------------------------------------
2895 void PdfParser::opBeginImage(Object args[], int numArgs) {
2896   Stream *str;
2897   int c1, c2;
2899   // build dict/stream
2900   str = buildImageStream();
2902   // display the image
2903   if (str) {
2904     doImage(NULL, str, gTrue);
2905   
2906     // skip 'EI' tag
2907     c1 = str->getUndecodedStream()->getChar();
2908     c2 = str->getUndecodedStream()->getChar();
2909     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2910       c1 = c2;
2911       c2 = str->getUndecodedStream()->getChar();
2912     }
2913     delete str;
2914   }
2917 Stream *PdfParser::buildImageStream() {
2918   Object dict;
2919   Object obj;
2920   char *key;
2921   Stream *str;
2923   // build dictionary
2924   dict.initDict(xref);
2925   parser->getObj(&obj);
2926   while (!obj.isCmd("ID") && !obj.isEOF()) {
2927     if (!obj.isName()) {
2928       error(getPos(), "Inline image dictionary key must be a name object");
2929       obj.free();
2930     } else {
2931       key = copyString(obj.getName());
2932       obj.free();
2933       parser->getObj(&obj);
2934       if (obj.isEOF() || obj.isError()) {
2935         gfree(key);
2936         break;
2937       }
2938       dict.dictAdd(key, &obj);
2939     }
2940     parser->getObj(&obj);
2941   }
2942   if (obj.isEOF()) {
2943     error(getPos(), "End of file in inline image");
2944     obj.free();
2945     dict.free();
2946     return NULL;
2947   }
2948   obj.free();
2950   // make stream
2951   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2952   str = str->addFilters(&dict);
2954   return str;
2957 void PdfParser::opImageData(Object args[], int numArgs) {
2958   error(getPos(), "Internal: got 'ID' operator");
2961 void PdfParser::opEndImage(Object args[], int numArgs) {
2962   error(getPos(), "Internal: got 'EI' operator");
2965 //------------------------------------------------------------------------
2966 // type 3 font operators
2967 //------------------------------------------------------------------------
2969 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2972 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2975 //------------------------------------------------------------------------
2976 // compatibility operators
2977 //------------------------------------------------------------------------
2979 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2980   ++ignoreUndef;
2983 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2984   if (ignoreUndef > 0)
2985     --ignoreUndef;
2988 //------------------------------------------------------------------------
2989 // marked content operators
2990 //------------------------------------------------------------------------
2992 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2993   if (printCommands) {
2994     printf("  marked content: %s ", args[0].getName());
2995     if (numArgs == 2)
2996       args[2].print(stdout);
2997     printf("\n");
2998     fflush(stdout);
2999   }
3001   if(numArgs == 2) {
3002     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
3003   } else {
3004     //out->beginMarkedContent(args[0].getName());
3005   }
3008 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3009   //out->endMarkedContent();
3012 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3013   if (printCommands) {
3014     printf("  mark point: %s ", args[0].getName());
3015     if (numArgs == 2)
3016       args[2].print(stdout);
3017     printf("\n");
3018     fflush(stdout);
3019   }
3021   if(numArgs == 2) {
3022     //out->markPoint(args[0].getName(),args[1].getDict());
3023   } else {
3024     //out->markPoint(args[0].getName());
3025   }
3029 //------------------------------------------------------------------------
3030 // misc
3031 //------------------------------------------------------------------------
3033 void PdfParser::saveState() {
3034   builder->saveState();
3035   state = state->save();
3038 void PdfParser::restoreState() {
3039   state = state->restore();
3040   builder->restoreState();
3043 void PdfParser::pushResources(Dict *resDict) {
3044   res = new GfxResources(xref, resDict, res);
3047 void PdfParser::popResources() {
3048   GfxResources *resPtr;
3050   resPtr = res->getNext();
3051   delete res;
3052   res = resPtr;
3055 #endif /* HAVE_POPPLER */