Code

Further development of the PDF import module: continued pruning of PdfParser.cpp...
[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 / 256.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 / 256.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 / 256.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 / 256.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 / 256.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   ignoreUndef = 0;
302   operatorHistory = NULL;
303   builder = builderA;
304   builder->setDocumentSize(state->getPageWidth()*PX_PER_PT,
305                            state->getPageHeight()*PX_PER_PT);
307   double *ctm = state->getCTM();
308   double scaledCTM[6];
309   for (i = 0; i < 6; ++i) {
310     baseMatrix[i] = ctm[i];
311     scaledCTM[i] = PX_PER_PT * ctm[i];
312   }
313   builder->setTransform((double*)&scaledCTM);
314   formDepth = 0;
316   // set crop box
317   if (cropBox) {
318     if (printCommands)
319         printf("cropBox: %f %f %f %f\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
320     // do not clip if it's not needed
321     if (cropBox->x1 != 0.0 || cropBox->y1 != 0.0 ||
322         cropBox->x2 != state->getPageWidth() || cropBox->y2 != state->getPageHeight()) {
323         
324         state->moveTo(cropBox->x1, cropBox->y1);
325         state->lineTo(cropBox->x2, cropBox->y1);
326         state->lineTo(cropBox->x2, cropBox->y2);
327         state->lineTo(cropBox->x1, cropBox->y2);
328         state->closePath();
329         state->clip();
330         builder->setClipPath(state);
331         state->clearPath();
332     }
333   }
334   pushOperator("startPage");
337 PdfParser::~PdfParser() {
338   while (state->hasSaves()) {
339     restoreState();
340   }
341   if (!subPage) {
342     //out->endPage();
343   }
344   while (res) {
345     popResources();
346   }
347   if (state) {
348     delete state;
349   }
352 void PdfParser::parse(Object *obj, GBool topLevel) {
353   Object obj2;
354   int i;
356   if (obj->isArray()) {
357     for (i = 0; i < obj->arrayGetLength(); ++i) {
358       obj->arrayGet(i, &obj2);
359       if (!obj2.isStream()) {
360         error(-1, "Weird page contents");
361         obj2.free();
362         return;
363       }
364       obj2.free();
365     }
366   } else if (!obj->isStream()) {
367     error(-1, "Weird page contents");
368     return;
369   }
370   parser = new Parser(xref, new Lexer(xref, obj), gFalse);
371   go(topLevel);
372   delete parser;
373   parser = NULL;
376 void PdfParser::go(GBool topLevel) {
377   Object obj;
378   Object args[maxArgs];
379   int numArgs, i;
380   int lastAbortCheck;
382   // scan a sequence of objects
383   numArgs = 0;
384   parser->getObj(&obj);
385   while (!obj.isEOF()) {
387     // got a command - execute it
388     if (obj.isCmd()) {
389       if (printCommands) {
390         obj.print(stdout);
391         for (i = 0; i < numArgs; ++i) {
392           printf(" ");
393           args[i].print(stdout);
394         }
395         printf("\n");
396         fflush(stdout);
397       }
399       // Run the operation
400       execOp(&obj, args, numArgs);
402       obj.free();
403       for (i = 0; i < numArgs; ++i)
404         args[i].free();
405       numArgs = 0;
407     // got an argument - save it
408     } else if (numArgs < maxArgs) {
409       args[numArgs++] = obj;
411     // too many arguments - something is wrong
412     } else {
413       error(getPos(), "Too many args in content stream");
414       if (printCommands) {
415         printf("throwing away arg: ");
416         obj.print(stdout);
417         printf("\n");
418         fflush(stdout);
419       }
420       obj.free();
421     }
423     // grab the next object
424     parser->getObj(&obj);
425   }
426   obj.free();
428   // args at end with no command
429   if (numArgs > 0) {
430     error(getPos(), "Leftover args in content stream");
431     if (printCommands) {
432       printf("%d leftovers:", numArgs);
433       for (i = 0; i < numArgs; ++i) {
434         printf(" ");
435         args[i].print(stdout);
436       }
437       printf("\n");
438       fflush(stdout);
439     }
440     for (i = 0; i < numArgs; ++i)
441       args[i].free();
442   }
445 void PdfParser::pushOperator(const char *name) {
446     OpHistoryEntry *newEntry = new OpHistoryEntry;
447     newEntry->name = name;
448     newEntry->state = NULL;
449     newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
450     newEntry->next = operatorHistory;
451     operatorHistory = newEntry;
453     // Truncate list if needed
454     if (operatorHistory->depth > maxOperatorHistoryDepth) {
455         OpHistoryEntry *curr = operatorHistory;
456         OpHistoryEntry *prev = NULL;
457         while (curr && curr->next != NULL) {
458             curr->depth--;
459             prev = curr;
460             curr = curr->next;
461         }
462         if (prev) {
463             if (curr->state != NULL)
464                 delete curr->state;
465             delete curr;
466             prev->next = NULL;
467         }
468     }
471 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
472     OpHistoryEntry *prev = NULL;
473     if (operatorHistory != NULL && look_back > 0) {
474         prev = operatorHistory->next;
475         while (--look_back > 0 && prev != NULL) {
476             prev = prev->next;
477         }
478     }
479     if (prev != NULL) {
480         return prev->name;
481     } else {
482         return "";
483     }
486 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
487   PdfOperator *op;
488   char *name;
489   Object *argPtr;
490   int i;
492   // find operator
493   name = cmd->getCmd();
494   if (!(op = findOp(name))) {
495     if (ignoreUndef == 0)
496       error(getPos(), "Unknown operator '%s'", name);
497     return;
498   }
500   // type check args
501   argPtr = args;
502   if (op->numArgs >= 0) {
503     if (numArgs < op->numArgs) {
504       error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
505       return;
506     }
507     if (numArgs > op->numArgs) {
508 #if 0
509       error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
510 #endif
511       argPtr += numArgs - op->numArgs;
512       numArgs = op->numArgs;
513     }
514   } else {
515     if (numArgs > -op->numArgs) {
516       error(getPos(), "Too many (%d) args to '%s' operator",
517             numArgs, name);
518       return;
519     }
520   }
521   for (i = 0; i < numArgs; ++i) {
522     if (!checkArg(&argPtr[i], op->tchk[i])) {
523       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
524             i, name, argPtr[i].getTypeName());
525       return;
526     }
527   }
529   // add to history
530   pushOperator((char*)&op->name);
532   // do it
533   (this->*op->func)(argPtr, numArgs);
536 PdfOperator *PdfParser::findOp(char *name) {
537   int a, b, m, cmp;
539   a = -1;
540   b = numOps;
541   // invariant: opTab[a] < name < opTab[b]
542   while (b - a > 1) {
543     m = (a + b) / 2;
544     cmp = strcmp(opTab[m].name, name);
545     if (cmp < 0)
546       a = m;
547     else if (cmp > 0)
548       b = m;
549     else
550       a = b = m;
551   }
552   if (cmp != 0)
553     return NULL;
554   return &opTab[a];
557 GBool PdfParser::checkArg(Object *arg, TchkType type) {
558   switch (type) {
559   case tchkBool:   return arg->isBool();
560   case tchkInt:    return arg->isInt();
561   case tchkNum:    return arg->isNum();
562   case tchkString: return arg->isString();
563   case tchkName:   return arg->isName();
564   case tchkArray:  return arg->isArray();
565   case tchkProps:  return arg->isDict() || arg->isName();
566   case tchkSCN:    return arg->isNum() || arg->isName();
567   case tchkNone:   return gFalse;
568   }
569   return gFalse;
572 int PdfParser::getPos() {
573   return parser ? parser->getPos() : -1;
576 //------------------------------------------------------------------------
577 // graphics state operators
578 //------------------------------------------------------------------------
580 void PdfParser::opSave(Object args[], int numArgs) {
581   saveState();
584 void PdfParser::opRestore(Object args[], int numArgs) {
585   restoreState();
588 void PdfParser::opConcat(Object args[], int numArgs) {
589   state->concatCTM(args[0].getNum(), args[1].getNum(),
590                    args[2].getNum(), args[3].getNum(),
591                    args[4].getNum(), args[5].getNum());
592   const char *prevOp = getPreviousOperator();
593   double a0 = args[0].getNum();
594   double a1 = args[1].getNum();
595   double a2 = args[2].getNum();
596   double a3 = args[3].getNum();
597   double a4 = args[4].getNum();
598   double a5 = args[5].getNum();
599   if (!strcmp(prevOp, "q")) {
600       builder->setTransform(a0, a1, a2, a3, a4, a5);
601   } else if (!strcmp(prevOp, "cm")) {
602       // multiply it with the previous transform
603       double otherMatrix[6];
604       if (!builder->getTransform((double*)&otherMatrix)) { // invalid transform
605           // construct identity matrix
606           otherMatrix[0] = otherMatrix[3] = 1.0;
607           otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
608       }
609       double c0 = otherMatrix[0]*a0 + otherMatrix[1]*a2;
610       double c1 = otherMatrix[0]*a1 + otherMatrix[1]*a3;
611       double c2 = otherMatrix[2]*a0 + otherMatrix[3]*a2;
612       double c3 = otherMatrix[2]*a1 + otherMatrix[3]*a3;
613       double c4 = otherMatrix[4]*a0 + otherMatrix[5]*a2 + a4;
614       double c5 = otherMatrix[4]*a1 + otherMatrix[5]*a3 + a5;
615       builder->setTransform(c0, c1, c2, c3, c4, c5);
616   } else {
617       builder->pushGroup();
618       builder->setTransform(a0, a1, a2, a3, a4, a5);
619   }
620   fontChanged = gTrue;
623 void PdfParser::opSetDash(Object args[], int numArgs) {
624   Array *a;
625   int length;
626   Object obj;
627   double *dash;
628   int i;
630   a = args[0].getArray();
631   length = a->getLength();
632   if (length == 0) {
633     dash = NULL;
634   } else {
635     dash = (double *)gmallocn(length, sizeof(double));
636     for (i = 0; i < length; ++i) {
637       dash[i] = a->get(i, &obj)->getNum();
638       obj.free();
639     }
640   }
641   state->setLineDash(dash, length, args[1].getNum());
642   builder->updateStyle(state);
645 void PdfParser::opSetFlat(Object args[], int numArgs) {
646   state->setFlatness((int)args[0].getNum());
649 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
650   state->setLineJoin(args[0].getInt());
651   builder->updateStyle(state);
654 void PdfParser::opSetLineCap(Object args[], int numArgs) {
655   state->setLineCap(args[0].getInt());
656   builder->updateStyle(state);
659 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
660   state->setMiterLimit(args[0].getNum());
661   builder->updateStyle(state);
664 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
665   state->setLineWidth(args[0].getNum());
666   builder->updateStyle(state);
669 void PdfParser::opSetExtGState(Object args[], int numArgs) {
670   Object obj1, obj2, obj3, obj4, obj5;
671   GfxBlendMode mode;
672   GBool haveFillOP;
673   Function *funcs[4];
674   GfxColor backdropColor;
675   GBool haveBackdropColor;
676   GfxColorSpace *blendingColorSpace;
677   GBool alpha, isolated, knockout;
678   int i;
680   if (!res->lookupGState(args[0].getName(), &obj1)) {
681     return;
682   }
683   if (!obj1.isDict()) {
684     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
685     obj1.free();
686     return;
687   }
688   if (printCommands) {
689     printf("  gfx state dict: ");
690     obj1.print();
691     printf("\n");
692   }
694   // transparency support: blend mode, fill/stroke opacity
695   if (!obj1.dictLookup("BM", &obj2)->isNull()) {
696     if (state->parseBlendMode(&obj2, &mode)) {
697       state->setBlendMode(mode);
698     } else {
699       error(getPos(), "Invalid blend mode in ExtGState");
700     }
701   }
702   obj2.free();
703   if (obj1.dictLookup("ca", &obj2)->isNum()) {
704     state->setFillOpacity(obj2.getNum());
705   }
706   obj2.free();
707   if (obj1.dictLookup("CA", &obj2)->isNum()) {
708     state->setStrokeOpacity(obj2.getNum());
709   }
710   obj2.free();
712   // fill/stroke overprint
713   if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
714     state->setFillOverprint(obj2.getBool());
715   }
716   obj2.free();
717   if (obj1.dictLookup("OP", &obj2)->isBool()) {
718     state->setStrokeOverprint(obj2.getBool());
719     if (!haveFillOP) {
720       state->setFillOverprint(obj2.getBool());
721     }
722   }
723   obj2.free();
725   // stroke adjust
726   if (obj1.dictLookup("SA", &obj2)->isBool()) {
727     state->setStrokeAdjust(obj2.getBool());
728   }
729   obj2.free();
731   // transfer function
732   if (obj1.dictLookup("TR2", &obj2)->isNull()) {
733     obj2.free();
734     obj1.dictLookup("TR", &obj2);
735   }
736   if (obj2.isName("Default") ||
737       obj2.isName("Identity")) {
738     funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
739     state->setTransfer(funcs);
740   } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
741     for (i = 0; i < 4; ++i) {
742       obj2.arrayGet(i, &obj3);
743       funcs[i] = Function::parse(&obj3);
744       obj3.free();
745       if (!funcs[i]) {
746         break;
747       }
748     }
749     if (i == 4) {
750       state->setTransfer(funcs);
751     }
752   } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
753     if ((funcs[0] = Function::parse(&obj2))) {
754       funcs[1] = funcs[2] = funcs[3] = NULL;
755       state->setTransfer(funcs);
756     }
757   } else if (!obj2.isNull()) {
758     error(getPos(), "Invalid transfer function in ExtGState");
759   }
760   obj2.free();
762   // soft mask
763   if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
764     if (obj2.isName("None")) {
765       //out->clearSoftMask(state);
766     } else if (obj2.isDict()) {
767       if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
768         alpha = gTrue;
769       } else { // "Luminosity"
770         alpha = gFalse;
771       }
772       obj3.free();
773       funcs[0] = NULL;
774       if (!obj2.dictLookup("TR", &obj3)->isNull()) {
775         funcs[0] = Function::parse(&obj3);
776         if (funcs[0]->getInputSize() != 1 ||
777             funcs[0]->getOutputSize() != 1) {
778           error(getPos(),
779                 "Invalid transfer function in soft mask in ExtGState");
780           delete funcs[0];
781           funcs[0] = NULL;
782         }
783       }
784       obj3.free();
785       if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
786         for (i = 0; i < gfxColorMaxComps; ++i) {
787           backdropColor.c[i] = 0;
788         }
789         for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
790           obj3.arrayGet(i, &obj4);
791           if (obj4.isNum()) {
792             backdropColor.c[i] = dblToCol(obj4.getNum());
793           }
794           obj4.free();
795         }
796       }
797       obj3.free();
798       if (obj2.dictLookup("G", &obj3)->isStream()) {
799         if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
800           blendingColorSpace = NULL;
801           isolated = knockout = gFalse;
802           if (!obj4.dictLookup("CS", &obj5)->isNull()) {
803             blendingColorSpace = GfxColorSpace::parse(&obj5);
804           }
805           obj5.free();
806           if (obj4.dictLookup("I", &obj5)->isBool()) {
807             isolated = obj5.getBool();
808           }
809           obj5.free();
810           if (obj4.dictLookup("K", &obj5)->isBool()) {
811             knockout = obj5.getBool();
812           }
813           obj5.free();
814           if (!haveBackdropColor) {
815             if (blendingColorSpace) {
816               blendingColorSpace->getDefaultColor(&backdropColor);
817             } else {
818               //~ need to get the parent or default color space (?)
819               for (i = 0; i < gfxColorMaxComps; ++i) {
820                 backdropColor.c[i] = 0;
821               }
822             }
823           }
824           doSoftMask(&obj3, alpha, blendingColorSpace,
825                      isolated, knockout, funcs[0], &backdropColor);
826           if (funcs[0]) {
827             delete funcs[0];
828           }
829         } else {
830           error(getPos(), "Invalid soft mask in ExtGState - missing group");
831         }
832         obj4.free();
833       } else {
834         error(getPos(), "Invalid soft mask in ExtGState - missing group");
835       }
836       obj3.free();
837     } else if (!obj2.isNull()) {
838       error(getPos(), "Invalid soft mask in ExtGState");
839     }
840   }
841   obj2.free();
843   obj1.free();
846 void PdfParser::doSoftMask(Object *str, GBool alpha,
847                      GfxColorSpace *blendingColorSpace,
848                      GBool isolated, GBool knockout,
849                      Function *transferFunc, GfxColor *backdropColor) {
850   Dict *dict, *resDict;
851   double m[6], bbox[4];
852   Object obj1, obj2;
853   int i;
855   // check for excessive recursion
856   if (formDepth > 20) {
857     return;
858   }
860   // get stream dict
861   dict = str->streamGetDict();
863   // check form type
864   dict->lookup("FormType", &obj1);
865   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
866     error(getPos(), "Unknown form type");
867   }
868   obj1.free();
870   // get bounding box
871   dict->lookup("BBox", &obj1);
872   if (!obj1.isArray()) {
873     obj1.free();
874     error(getPos(), "Bad form bounding box");
875     return;
876   }
877   for (i = 0; i < 4; ++i) {
878     obj1.arrayGet(i, &obj2);
879     bbox[i] = obj2.getNum();
880     obj2.free();
881   }
882   obj1.free();
884   // get matrix
885   dict->lookup("Matrix", &obj1);
886   if (obj1.isArray()) {
887     for (i = 0; i < 6; ++i) {
888       obj1.arrayGet(i, &obj2);
889       m[i] = obj2.getNum();
890       obj2.free();
891     }
892   } else {
893     m[0] = 1; m[1] = 0;
894     m[2] = 0; m[3] = 1;
895     m[4] = 0; m[5] = 0;
896   }
897   obj1.free();
899   // get resources
900   dict->lookup("Resources", &obj1);
901   resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
903   // draw it
904   ++formDepth;
905   doForm1(str, resDict, m, bbox, gTrue, gTrue,
906           blendingColorSpace, isolated, knockout,
907           alpha, transferFunc, backdropColor);
908   --formDepth;
910   if (blendingColorSpace) {
911     delete blendingColorSpace;
912   }
913   obj1.free();
916 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
919 //------------------------------------------------------------------------
920 // color operators
921 //------------------------------------------------------------------------
923 void PdfParser::opSetFillGray(Object args[], int numArgs) {
924   GfxColor color;
926   state->setFillPattern(NULL);
927   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
928   color.c[0] = dblToCol(args[0].getNum());
929   state->setFillColor(&color);
930   builder->updateStyle(state);
933 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
934   GfxColor color;
936   state->setStrokePattern(NULL);
937   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
938   color.c[0] = dblToCol(args[0].getNum());
939   state->setStrokeColor(&color);
940   builder->updateStyle(state);
943 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
944   GfxColor color;
945   int i;
947   state->setFillPattern(NULL);
948   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
949   for (i = 0; i < 4; ++i) {
950     color.c[i] = dblToCol(args[i].getNum());
951   }
952   state->setFillColor(&color);
953   builder->updateStyle(state);
956 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
957   GfxColor color;
958   int i;
960   state->setStrokePattern(NULL);
961   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
962   for (i = 0; i < 4; ++i) {
963     color.c[i] = dblToCol(args[i].getNum());
964   }
965   state->setStrokeColor(&color);
966   builder->updateStyle(state);
969 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
970   GfxColor color;
971   int i;
973   state->setFillPattern(NULL);
974   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
975   for (i = 0; i < 3; ++i) {
976     color.c[i] = dblToCol(args[i].getNum());
977   }
978   state->setFillColor(&color);
979   builder->updateStyle(state);
982 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
983   GfxColor color;
984   int i;
986   state->setStrokePattern(NULL);
987   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
988   for (i = 0; i < 3; ++i) {
989     color.c[i] = dblToCol(args[i].getNum());
990   }
991   state->setStrokeColor(&color);
992   builder->updateStyle(state);
995 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
996   Object obj;
997   GfxColorSpace *colorSpace;
998   GfxColor color;
1000   state->setFillPattern(NULL);
1001   res->lookupColorSpace(args[0].getName(), &obj);
1002   if (obj.isNull()) {
1003     colorSpace = GfxColorSpace::parse(&args[0]);
1004   } else {
1005     colorSpace = GfxColorSpace::parse(&obj);
1006   }
1007   obj.free();
1008   if (colorSpace) {
1009     state->setFillColorSpace(colorSpace);
1010     colorSpace->getDefaultColor(&color);
1011     state->setFillColor(&color);
1012     builder->updateStyle(state);
1013   } else {
1014     error(getPos(), "Bad color space (fill)");
1015   }
1018 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1019   Object obj;
1020   GfxColorSpace *colorSpace;
1021   GfxColor color;
1023   state->setStrokePattern(NULL);
1024   res->lookupColorSpace(args[0].getName(), &obj);
1025   if (obj.isNull()) {
1026     colorSpace = GfxColorSpace::parse(&args[0]);
1027   } else {
1028     colorSpace = GfxColorSpace::parse(&obj);
1029   }
1030   obj.free();
1031   if (colorSpace) {
1032     state->setStrokeColorSpace(colorSpace);
1033     colorSpace->getDefaultColor(&color);
1034     state->setStrokeColor(&color);
1035     builder->updateStyle(state);
1036   } else {
1037     error(getPos(), "Bad color space (stroke)");
1038   }
1041 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1042   GfxColor color;
1043   int i;
1045   if (numArgs != state->getFillColorSpace()->getNComps()) {
1046     error(getPos(), "Incorrect number of arguments in 'sc' command");
1047     return;
1048   }
1049   state->setFillPattern(NULL);
1050   for (i = 0; i < numArgs; ++i) {
1051     color.c[i] = dblToCol(args[i].getNum());
1052   }
1053   state->setFillColor(&color);
1054   builder->updateStyle(state);
1057 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1058   GfxColor color;
1059   int i;
1061   if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1062     error(getPos(), "Incorrect number of arguments in 'SC' command");
1063     return;
1064   }
1065   state->setStrokePattern(NULL);
1066   for (i = 0; i < numArgs; ++i) {
1067     color.c[i] = dblToCol(args[i].getNum());
1068   }
1069   state->setStrokeColor(&color);
1070   builder->updateStyle(state);
1073 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1074   GfxColor color;
1075   GfxPattern *pattern;
1076   int i;
1078   if (state->getFillColorSpace()->getMode() == csPattern) {
1079     if (numArgs > 1) {
1080       if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1081           numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1082                              ->getUnder()->getNComps()) {
1083         error(getPos(), "Incorrect number of arguments in 'scn' command");
1084         return;
1085       }
1086       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1087         if (args[i].isNum()) {
1088           color.c[i] = dblToCol(args[i].getNum());
1089         }
1090       }
1091       state->setFillColor(&color);
1092       builder->updateStyle(state);
1093     }
1094     if (args[numArgs-1].isName() &&
1095         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1096       state->setFillPattern(pattern);
1097     }
1099   } else {
1100     if (numArgs != state->getFillColorSpace()->getNComps()) {
1101       error(getPos(), "Incorrect number of arguments in 'scn' command");
1102       return;
1103     }
1104     state->setFillPattern(NULL);
1105     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1106       if (args[i].isNum()) {
1107         color.c[i] = dblToCol(args[i].getNum());
1108       }
1109     }
1110     state->setFillColor(&color);
1111     builder->updateStyle(state);
1112   }
1115 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1116   GfxColor color;
1117   GfxPattern *pattern;
1118   int i;
1120   if (state->getStrokeColorSpace()->getMode() == csPattern) {
1121     if (numArgs > 1) {
1122       if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1123                ->getUnder() ||
1124           numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1125                              ->getUnder()->getNComps()) {
1126         error(getPos(), "Incorrect number of arguments in 'SCN' command");
1127         return;
1128       }
1129       for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1130         if (args[i].isNum()) {
1131           color.c[i] = dblToCol(args[i].getNum());
1132         }
1133       }
1134       state->setStrokeColor(&color);
1135       builder->updateStyle(state);
1136     }
1137     if (args[numArgs-1].isName() &&
1138         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1139       state->setStrokePattern(pattern);
1140     }
1142   } else {
1143     if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1144       error(getPos(), "Incorrect number of arguments in 'SCN' command");
1145       return;
1146     }
1147     state->setStrokePattern(NULL);
1148     for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1149       if (args[i].isNum()) {
1150         color.c[i] = dblToCol(args[i].getNum());
1151       }
1152     }
1153     state->setStrokeColor(&color);
1154     builder->updateStyle(state);
1155   }
1158 //------------------------------------------------------------------------
1159 // path segment operators
1160 //------------------------------------------------------------------------
1162 void PdfParser::opMoveTo(Object args[], int numArgs) {
1163   state->moveTo(args[0].getNum(), args[1].getNum());
1166 void PdfParser::opLineTo(Object args[], int numArgs) {
1167   if (!state->isCurPt()) {
1168     error(getPos(), "No current point in lineto");
1169     return;
1170   }
1171   state->lineTo(args[0].getNum(), args[1].getNum());
1174 void PdfParser::opCurveTo(Object args[], int numArgs) {
1175   double x1, y1, x2, y2, x3, y3;
1177   if (!state->isCurPt()) {
1178     error(getPos(), "No current point in curveto");
1179     return;
1180   }
1181   x1 = args[0].getNum();
1182   y1 = args[1].getNum();
1183   x2 = args[2].getNum();
1184   y2 = args[3].getNum();
1185   x3 = args[4].getNum();
1186   y3 = args[5].getNum();
1187   state->curveTo(x1, y1, x2, y2, x3, y3);
1190 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1191   double x1, y1, x2, y2, x3, y3;
1193   if (!state->isCurPt()) {
1194     error(getPos(), "No current point in curveto1");
1195     return;
1196   }
1197   x1 = state->getCurX();
1198   y1 = state->getCurY();
1199   x2 = args[0].getNum();
1200   y2 = args[1].getNum();
1201   x3 = args[2].getNum();
1202   y3 = args[3].getNum();
1203   state->curveTo(x1, y1, x2, y2, x3, y3);
1206 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1207   double x1, y1, x2, y2, x3, y3;
1209   if (!state->isCurPt()) {
1210     error(getPos(), "No current point in curveto2");
1211     return;
1212   }
1213   x1 = args[0].getNum();
1214   y1 = args[1].getNum();
1215   x2 = args[2].getNum();
1216   y2 = args[3].getNum();
1217   x3 = x2;
1218   y3 = y2;
1219   state->curveTo(x1, y1, x2, y2, x3, y3);
1222 void PdfParser::opRectangle(Object args[], int numArgs) {
1223   double x, y, w, h;
1225   x = args[0].getNum();
1226   y = args[1].getNum();
1227   w = args[2].getNum();
1228   h = args[3].getNum();
1229   state->moveTo(x, y);
1230   state->lineTo(x + w, y);
1231   state->lineTo(x + w, y + h);
1232   state->lineTo(x, y + h);
1233   state->closePath();
1236 void PdfParser::opClosePath(Object args[], int numArgs) {
1237   if (!state->isCurPt()) {
1238     error(getPos(), "No current point in closepath");
1239     return;
1240   }
1241   state->closePath();
1244 //------------------------------------------------------------------------
1245 // path painting operators
1246 //------------------------------------------------------------------------
1248 void PdfParser::opEndPath(Object args[], int numArgs) {
1249   doEndPath();
1252 void PdfParser::opStroke(Object args[], int numArgs) {
1253   if (!state->isCurPt()) {
1254     //error(getPos(), "No path in stroke");
1255     return;
1256   }
1257   if (state->isPath()) {
1258     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1259         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1260           doPatternStrokeFallback();
1261     } else {
1262       builder->addPath(state, false, true);
1263     }
1264   }
1265   doEndPath();
1268 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1269   if (!state->isCurPt()) {
1270     //error(getPos(), "No path in closepath/stroke");
1271     return;
1272   }
1273   state->closePath();
1274   if (state->isPath()) {
1275     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1276         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1277       doPatternStrokeFallback();
1278     } else {
1279       builder->addPath(state, false, true);
1280     }
1281   }
1282   doEndPath();
1285 void PdfParser::opFill(Object args[], int numArgs) {
1286   if (!state->isCurPt()) {
1287     //error(getPos(), "No path in fill");
1288     return;
1289   }
1290   if (state->isPath()) {
1291     if (state->getFillColorSpace()->getMode() == csPattern &&
1292         !builder->isPatternTypeSupported(state->getFillPattern())) {
1293       doPatternFillFallback(gFalse);
1294     } else {
1295       builder->addPath(state, true, false);
1296     }
1297   }
1298   doEndPath();
1301 void PdfParser::opEOFill(Object args[], int numArgs) {
1302   if (!state->isCurPt()) {
1303     //error(getPos(), "No path in eofill");
1304     return;
1305   }
1306   if (state->isPath()) {
1307     if (state->getFillColorSpace()->getMode() == csPattern &&
1308         !builder->isPatternTypeSupported(state->getFillPattern())) {
1309       doPatternFillFallback(gTrue);
1310     } else {
1311       builder->addPath(state, true, false, true);
1312     }
1313   }
1314   doEndPath();
1317 void PdfParser::opFillStroke(Object args[], int numArgs) {
1318   if (!state->isCurPt()) {
1319     //error(getPos(), "No path in fill/stroke");
1320     return;
1321   }
1322   if (state->isPath()) {
1323     doFillAndStroke(gFalse);
1324   } else {
1325     builder->addPath(state, true, true);
1326   }
1327   doEndPath();
1330 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1331   if (!state->isCurPt()) {
1332     //error(getPos(), "No path in closepath/fill/stroke");
1333     return;
1334   }
1335   if (state->isPath()) {
1336     state->closePath();
1337     doFillAndStroke(gFalse);
1338   }
1339   doEndPath();
1342 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1343   if (!state->isCurPt()) {
1344     //error(getPos(), "No path in eofill/stroke");
1345     return;
1346   }
1347   if (state->isPath()) {
1348     doFillAndStroke(gTrue);
1349   }
1350   doEndPath();
1353 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1354   if (!state->isCurPt()) {
1355     //error(getPos(), "No path in closepath/eofill/stroke");
1356     return;
1357   }
1358   if (state->isPath()) {
1359     state->closePath();
1360     doFillAndStroke(gTrue);
1361   }
1362   doEndPath();
1365 void PdfParser::doFillAndStroke(GBool eoFill) {
1366     GBool fillOk = gTrue, strokeOk = gTrue;
1367     if (state->getFillColorSpace()->getMode() == csPattern &&
1368         !builder->isPatternTypeSupported(state->getFillPattern())) {
1369         fillOk = gFalse;
1370     }
1371     if (state->getStrokeColorSpace()->getMode() == csPattern &&
1372         !builder->isPatternTypeSupported(state->getStrokePattern())) {
1373         strokeOk = gFalse;
1374     }
1375     if (fillOk && strokeOk) {
1376         builder->addPath(state, true, true, eoFill);
1377     } else {
1378         doPatternFillFallback(eoFill);
1379         doPatternStrokeFallback();
1380     }
1383 void PdfParser::doPatternFillFallback(GBool eoFill) {
1384   GfxPattern *pattern;
1386   if (!(pattern = state->getFillPattern())) {
1387     return;
1388   }
1389   switch (pattern->getType()) {
1390   case 1:
1391     break;
1392   case 2:
1393     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1394     break;
1395   default:
1396     error(getPos(), "Unimplemented pattern type (%d) in fill",
1397           pattern->getType());
1398     break;
1399   }
1402 void PdfParser::doPatternStrokeFallback() {
1403   GfxPattern *pattern;
1405   if (!(pattern = state->getStrokePattern())) {
1406     return;
1407   }
1408   switch (pattern->getType()) {
1409   case 1:
1410     break;
1411   case 2:
1412     doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1413     break;
1414   default:
1415     error(getPos(), "Unimplemented pattern type (%d) in stroke",
1416           pattern->getType());
1417     break;
1418   }
1421 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1422                                              GBool stroke, GBool eoFill) {
1423   GfxShading *shading;
1424   GfxPath *savedPath;
1425   double *ctm, *btm, *ptm;
1426   double m[6], ictm[6], m1[6];
1427   double xMin, yMin, xMax, yMax;
1428   double det;
1430   shading = sPat->getShading();
1432   // save current graphics state
1433   savedPath = state->getPath()->copy();
1434   saveState();
1436   // clip to bbox
1437   if (0 ){//shading->getHasBBox()) {
1438     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1439     state->moveTo(xMin, yMin);
1440     state->lineTo(xMax, yMin);
1441     state->lineTo(xMax, yMax);
1442     state->lineTo(xMin, yMax);
1443     state->closePath();
1444     state->clip();
1445     //builder->clip(state);
1446     state->setPath(savedPath->copy());
1447   }
1449   // clip to current path
1450   if (stroke) {
1451     state->clipToStrokePath();
1452     //out->clipToStrokePath(state);
1453   } else {
1454     state->clip();
1455     if (eoFill) {
1456       builder->clip(state, true);
1457     } else {
1458       builder->clip(state);
1459     }
1460   }
1462   // set the color space
1463   state->setFillColorSpace(shading->getColorSpace()->copy());
1465   // background color fill
1466   if (shading->getHasBackground()) {
1467     state->setFillColor(shading->getBackground());
1468     builder->addPath(state, true, false);
1469   }
1470   state->clearPath();
1472   // construct a (pattern space) -> (current space) transform matrix
1473   ctm = state->getCTM();
1474   btm = baseMatrix;
1475   ptm = sPat->getMatrix();
1476   // iCTM = invert CTM
1477   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1478   ictm[0] = ctm[3] * det;
1479   ictm[1] = -ctm[1] * det;
1480   ictm[2] = -ctm[2] * det;
1481   ictm[3] = ctm[0] * det;
1482   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1483   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1484   // m1 = PTM * BTM = PTM * base transform matrix
1485   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1486   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1487   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1488   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1489   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1490   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1491   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1492   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1493   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1494   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1495   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1496   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1497   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1499   // set the new matrix
1500   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1501   builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1503   // do shading type-specific operations
1504   switch (shading->getType()) {
1505   case 1:
1506     doFunctionShFill((GfxFunctionShading *)shading);
1507     break;
1508   case 2:
1509   case 3:
1510     // no need to implement these
1511     break;
1512   case 4:
1513   case 5:
1514     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1515     break;
1516   case 6:
1517   case 7:
1518     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1519     break;
1520   }
1522   // restore graphics state
1523   restoreState();
1524   state->setPath(savedPath);
1527 void PdfParser::opShFill(Object args[], int numArgs) {
1528   GfxShading *shading;
1529   GfxPath *savedPath;
1530   double xMin, yMin, xMax, yMax;
1532   if (!(shading = res->lookupShading(args[0].getName()))) {
1533     return;
1534   }
1536   // save current graphics state
1537   savedPath = state->getPath()->copy();
1538   saveState();
1540   // clip to bbox
1541   if (shading->getHasBBox()) {
1542     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1543     state->moveTo(xMin, yMin);
1544     state->lineTo(xMax, yMin);
1545     state->lineTo(xMax, yMax);
1546     state->lineTo(xMin, yMax);
1547     state->closePath();
1548     state->clip();
1549     builder->clip(state);
1550     state->clearPath();
1551   }
1553   // set the color space
1554   state->setFillColorSpace(shading->getColorSpace()->copy());
1556   // do shading type-specific operations
1557   switch (shading->getType()) {
1558   case 1:
1559     doFunctionShFill((GfxFunctionShading *)shading);
1560     break;
1561   case 2:
1562   case 3:
1563     // no need to implement these
1564     break;
1565   case 4:
1566   case 5:
1567     doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1568     break;
1569   case 6:
1570   case 7:
1571     doPatchMeshShFill((GfxPatchMeshShading *)shading);
1572     break;
1573   }
1575   // restore graphics state
1576   restoreState();
1577   state->setPath(savedPath);
1579   delete shading;
1582 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1583   double x0, y0, x1, y1;
1584   GfxColor colors[4];
1586   shading->getDomain(&x0, &y0, &x1, &y1);
1587   shading->getColor(x0, y0, &colors[0]);
1588   shading->getColor(x0, y1, &colors[1]);
1589   shading->getColor(x1, y0, &colors[2]);
1590   shading->getColor(x1, y1, &colors[3]);
1591   doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1594 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1595                             double x0, double y0,
1596                             double x1, double y1,
1597                             GfxColor *colors, int depth) {
1598   GfxColor fillColor;
1599   GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1600   GfxColor colors2[4];
1601   double *matrix;
1602   double xM, yM;
1603   int nComps, i, j;
1605   nComps = shading->getColorSpace()->getNComps();
1606   matrix = shading->getMatrix();
1608   // compare the four corner colors
1609   for (i = 0; i < 4; ++i) {
1610     for (j = 0; j < nComps; ++j) {
1611       if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1612         break;
1613       }
1614     }
1615     if (j < nComps) {
1616       break;
1617     }
1618   }
1620   // center of the rectangle
1621   xM = 0.5 * (x0 + x1);
1622   yM = 0.5 * (y0 + y1);
1624   // the four corner colors are close (or we hit the recursive limit)
1625   // -- fill the rectangle; but require at least one subdivision
1626   // (depth==0) to avoid problems when the four outer corners of the
1627   // shaded region are the same color
1628   if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1630     // use the center color
1631     shading->getColor(xM, yM, &fillColor);
1632     state->setFillColor(&fillColor);
1634     // fill the rectangle
1635     state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1636                   x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1637     state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1638                   x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1639     state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1640                   x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1641     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1642                   x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1643     state->closePath();
1644     builder->addPath(state, true, false);
1645     state->clearPath();
1647   // the four corner colors are not close enough -- subdivide the
1648   // rectangle
1649   } else {
1651     // colors[0]       colorM0       colors[2]
1652     //   (x0,y0)       (xM,y0)       (x1,y0)
1653     //         +----------+----------+
1654     //         |          |          |
1655     //         |    UL    |    UR    |
1656     // color0M |       colorMM       | color1M
1657     // (x0,yM) +----------+----------+ (x1,yM)
1658     //         |       (xM,yM)       |
1659     //         |    LL    |    LR    |
1660     //         |          |          |
1661     //         +----------+----------+
1662     // colors[1]       colorM1       colors[3]
1663     //   (x0,y1)       (xM,y1)       (x1,y1)
1665     shading->getColor(x0, yM, &color0M);
1666     shading->getColor(x1, yM, &color1M);
1667     shading->getColor(xM, y0, &colorM0);
1668     shading->getColor(xM, y1, &colorM1);
1669     shading->getColor(xM, yM, &colorMM);
1671     // upper-left sub-rectangle
1672     colors2[0] = colors[0];
1673     colors2[1] = color0M;
1674     colors2[2] = colorM0;
1675     colors2[3] = colorMM;
1676     doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1677     
1678     // lower-left sub-rectangle
1679     colors2[0] = color0M;
1680     colors2[1] = colors[1];
1681     colors2[2] = colorMM;
1682     colors2[3] = colorM1;
1683     doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1684     
1685     // upper-right sub-rectangle
1686     colors2[0] = colorM0;
1687     colors2[1] = colorMM;
1688     colors2[2] = colors[2];
1689     colors2[3] = color1M;
1690     doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1692     // lower-right sub-rectangle
1693     colors2[0] = colorMM;
1694     colors2[1] = colorM1;
1695     colors2[2] = color1M;
1696     colors2[3] = colors[3];
1697     doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1698   }
1701 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1702   double x0, y0, x1, y1, x2, y2;
1703   GfxColor color0, color1, color2;
1704   int i;
1706   for (i = 0; i < shading->getNTriangles(); ++i) {
1707     shading->getTriangle(i, &x0, &y0, &color0,
1708                          &x1, &y1, &color1,
1709                          &x2, &y2, &color2);
1710     gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1711                         shading->getColorSpace()->getNComps(), 0);
1712   }
1715 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1716                               double x1, double y1, GfxColor *color1,
1717                               double x2, double y2, GfxColor *color2,
1718                               int nComps, int depth) {
1719   double x01, y01, x12, y12, x20, y20;
1720   GfxColor color01, color12, color20;
1721   int i;
1723   for (i = 0; i < nComps; ++i) {
1724     if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1725         abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1726       break;
1727     }
1728   }
1729   if (i == nComps || depth == gouraudMaxDepth) {
1730     state->setFillColor(color0);
1731     state->moveTo(x0, y0);
1732     state->lineTo(x1, y1);
1733     state->lineTo(x2, y2);
1734     state->closePath();
1735     builder->addPath(state, true, false);
1736     state->clearPath();
1737   } else {
1738     x01 = 0.5 * (x0 + x1);
1739     y01 = 0.5 * (y0 + y1);
1740     x12 = 0.5 * (x1 + x2);
1741     y12 = 0.5 * (y1 + y2);
1742     x20 = 0.5 * (x2 + x0);
1743     y20 = 0.5 * (y2 + y0);
1744     //~ if the shading has a Function, this should interpolate on the
1745     //~ function parameter, not on the color components
1746     for (i = 0; i < nComps; ++i) {
1747       color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1748       color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1749       color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1750     }
1751     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1752                         x20, y20, &color20, nComps, depth + 1);
1753     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1754                         x12, y12, &color12, nComps, depth + 1);
1755     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1756                         x20, y20, &color20, nComps, depth + 1);
1757     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1758                         x2, y2, color2, nComps, depth + 1);
1759   }
1762 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1763   int start, i;
1765   if (shading->getNPatches() > 128) {
1766     start = 3;
1767   } else if (shading->getNPatches() > 64) {
1768     start = 2;
1769   } else if (shading->getNPatches() > 16) {
1770     start = 1;
1771   } else {
1772     start = 0;
1773   }
1774   for (i = 0; i < shading->getNPatches(); ++i) {
1775     fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1776               start);
1777   }
1780 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1781   GfxPatch patch00, patch01, patch10, patch11;
1782   double xx[4][8], yy[4][8];
1783   double xxm, yym;
1784   int i;
1786   for (i = 0; i < nComps; ++i) {
1787     if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1788           > patchColorDelta ||
1789         abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1790           > patchColorDelta ||
1791         abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1792           > patchColorDelta ||
1793         abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1794           > patchColorDelta) {
1795       break;
1796     }
1797   }
1798   if (i == nComps || depth == patchMaxDepth) {
1799     state->setFillColor(&patch->color[0][0]);
1800     state->moveTo(patch->x[0][0], patch->y[0][0]);
1801     state->curveTo(patch->x[0][1], patch->y[0][1],
1802                    patch->x[0][2], patch->y[0][2],
1803                    patch->x[0][3], patch->y[0][3]);
1804     state->curveTo(patch->x[1][3], patch->y[1][3],
1805                    patch->x[2][3], patch->y[2][3],
1806                    patch->x[3][3], patch->y[3][3]);
1807     state->curveTo(patch->x[3][2], patch->y[3][2],
1808                    patch->x[3][1], patch->y[3][1],
1809                    patch->x[3][0], patch->y[3][0]);
1810     state->curveTo(patch->x[2][0], patch->y[2][0],
1811                    patch->x[1][0], patch->y[1][0],
1812                    patch->x[0][0], patch->y[0][0]);
1813     state->closePath();
1814     builder->addPath(state, true, false);
1815     state->clearPath();
1816   } else {
1817     for (i = 0; i < 4; ++i) {
1818       xx[i][0] = patch->x[i][0];
1819       yy[i][0] = patch->y[i][0];
1820       xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1821       yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1822       xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1823       yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1824       xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1825       yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1826       xx[i][2] = 0.5 * (xx[i][1] + xxm);
1827       yy[i][2] = 0.5 * (yy[i][1] + yym);
1828       xx[i][5] = 0.5 * (xxm + xx[i][6]);
1829       yy[i][5] = 0.5 * (yym + yy[i][6]);
1830       xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1831       yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1832       xx[i][7] = patch->x[i][3];
1833       yy[i][7] = patch->y[i][3];
1834     }
1835     for (i = 0; i < 4; ++i) {
1836       patch00.x[0][i] = xx[0][i];
1837       patch00.y[0][i] = yy[0][i];
1838       patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1839       patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1840       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1841       yym = 0.5 * (yy[1][i] + yy[2][i]);
1842       patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1843       patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1844       patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1845       patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1846       patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1847       patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1848       patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1849       patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1850       patch10.x[0][i] = patch00.x[3][i];
1851       patch10.y[0][i] = patch00.y[3][i];
1852       patch10.x[3][i] = xx[3][i];
1853       patch10.y[3][i] = yy[3][i];
1854     }
1855     for (i = 4; i < 8; ++i) {
1856       patch01.x[0][i-4] = xx[0][i];
1857       patch01.y[0][i-4] = yy[0][i];
1858       patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1859       patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1860       xxm = 0.5 * (xx[1][i] + xx[2][i]);
1861       yym = 0.5 * (yy[1][i] + yy[2][i]);
1862       patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1863       patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1864       patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1865       patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1866       patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1867       patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1868       patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1869       patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1870       patch11.x[0][i-4] = patch01.x[3][i-4];
1871       patch11.y[0][i-4] = patch01.y[3][i-4];
1872       patch11.x[3][i-4] = xx[3][i];
1873       patch11.y[3][i-4] = yy[3][i];
1874     }
1875     //~ if the shading has a Function, this should interpolate on the
1876     //~ function parameter, not on the color components
1877     for (i = 0; i < nComps; ++i) {
1878       patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1879       patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1880                                   patch->color[0][1].c[i]) / 2;
1881       patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1882       patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1883       patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1884                                   patch->color[1][1].c[i]) / 2;
1885       patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1886       patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1887       patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1888                                   patch->color[1][0].c[i]) / 2;
1889       patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1890       patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1891       patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1892                                   patch->color[0][0].c[i]) / 2;
1893       patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1894       patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1895                                   patch01.color[1][1].c[i]) / 2;
1896       patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1897       patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1898       patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1899     }
1900     fillPatch(&patch00, nComps, depth + 1);
1901     fillPatch(&patch10, nComps, depth + 1);
1902     fillPatch(&patch01, nComps, depth + 1);
1903     fillPatch(&patch11, nComps, depth + 1);
1904   }
1907 void PdfParser::doEndPath() {
1908   if (state->isCurPt() && clip != clipNone) {
1909     state->clip();
1910     if (clip == clipNormal) {
1911       builder->clip(state);
1912     } else {
1913       builder->clip(state, true);
1914     }
1915   }
1916   clip = clipNone;
1917   state->clearPath();
1920 //------------------------------------------------------------------------
1921 // path clipping operators
1922 //------------------------------------------------------------------------
1924 void PdfParser::opClip(Object args[], int numArgs) {
1925   clip = clipNormal;
1928 void PdfParser::opEOClip(Object args[], int numArgs) {
1929   clip = clipEO;
1932 //------------------------------------------------------------------------
1933 // text object operators
1934 //------------------------------------------------------------------------
1936 void PdfParser::opBeginText(Object args[], int numArgs) {
1937   state->setTextMat(1, 0, 0, 1, 0, 0);
1938   state->textMoveTo(0, 0);
1939   fontChanged = gTrue;
1940   builder->beginTextObject(state);
1943 void PdfParser::opEndText(Object args[], int numArgs) {
1944   builder->endTextObject(state);
1947 //------------------------------------------------------------------------
1948 // text state operators
1949 //------------------------------------------------------------------------
1951 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
1952   state->setCharSpace(args[0].getNum());
1955 void PdfParser::opSetFont(Object args[], int numArgs) {
1956   GfxFont *font;
1958   if (!(font = res->lookupFont(args[0].getName()))) {
1959     return;
1960   }
1961   if (printCommands) {
1962     printf("  font: tag=%s name='%s' %g\n",
1963            font->getTag()->getCString(),
1964            font->getName() ? font->getName()->getCString() : "???",
1965            args[1].getNum());
1966     fflush(stdout);
1967   }
1969   font->incRefCnt();
1970   state->setFont(font, args[1].getNum());
1971   fontChanged = gTrue;
1974 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
1975   state->setLeading(args[0].getNum());
1978 void PdfParser::opSetTextRender(Object args[], int numArgs) {
1979   state->setRender(args[0].getInt());
1980   builder->updateStyle(state);
1983 void PdfParser::opSetTextRise(Object args[], int numArgs) {
1984   state->setRise(args[0].getNum());
1987 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
1988   state->setWordSpace(args[0].getNum());
1991 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
1992   state->setHorizScaling(args[0].getNum());
1993   fontChanged = gTrue;
1996 //------------------------------------------------------------------------
1997 // text positioning operators
1998 //------------------------------------------------------------------------
2000 void PdfParser::opTextMove(Object args[], int numArgs) {
2001   double tx, ty;
2003   tx = state->getLineX() + args[0].getNum();
2004   ty = state->getLineY() + args[1].getNum();
2005   state->textMoveTo(tx, ty);
2008 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2009   double tx, ty;
2011   tx = state->getLineX() + args[0].getNum();
2012   ty = args[1].getNum();
2013   state->setLeading(-ty);
2014   ty += state->getLineY();
2015   state->textMoveTo(tx, ty);
2018 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2019   state->setTextMat(args[0].getNum(), args[1].getNum(),
2020                     args[2].getNum(), args[3].getNum(),
2021                     args[4].getNum(), args[5].getNum());
2022   state->textMoveTo(0, 0);
2023   fontChanged = gTrue;
2026 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2027   double tx, ty;
2029   tx = state->getLineX();
2030   ty = state->getLineY() - state->getLeading();
2031   state->textMoveTo(tx, ty);
2034 //------------------------------------------------------------------------
2035 // text string operators
2036 //------------------------------------------------------------------------
2038 void PdfParser::opShowText(Object args[], int numArgs) {
2039   if (!state->getFont()) {
2040     error(getPos(), "No font in show");
2041     return;
2042   }
2043   if (fontChanged) {
2044     builder->updateFont(state);
2045     fontChanged = gFalse;
2046   }
2047   doShowText(args[0].getString());
2050 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2051   double tx, ty;
2053   if (!state->getFont()) {
2054     error(getPos(), "No font in move/show");
2055     return;
2056   }
2057   if (fontChanged) {
2058     builder->updateFont(state);
2059     fontChanged = gFalse;
2060   }
2061   tx = state->getLineX();
2062   ty = state->getLineY() - state->getLeading();
2063   state->textMoveTo(tx, ty);
2064   doShowText(args[0].getString());
2067 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2068   double tx, ty;
2070   if (!state->getFont()) {
2071     error(getPos(), "No font in move/set/show");
2072     return;
2073   }
2074   if (fontChanged) {
2075     builder->updateFont(state);
2076     fontChanged = gFalse;
2077   }
2078   state->setWordSpace(args[0].getNum());
2079   state->setCharSpace(args[1].getNum());
2080   tx = state->getLineX();
2081   ty = state->getLineY() - state->getLeading();
2082   state->textMoveTo(tx, ty);
2083   doShowText(args[2].getString());
2086 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2087   Array *a;
2088   Object obj;
2089   int wMode;
2090   int i;
2092   if (!state->getFont()) {
2093     error(getPos(), "No font in show/space");
2094     return;
2095   }
2096   if (fontChanged) {
2097     builder->updateFont(state);
2098     fontChanged = gFalse;
2099   }
2100   wMode = state->getFont()->getWMode();
2101   a = args[0].getArray();
2102   for (i = 0; i < a->getLength(); ++i) {
2103     a->get(i, &obj);
2104     if (obj.isNum()) {
2105       // this uses the absolute value of the font size to match
2106       // Acrobat's behavior
2107       if (wMode) {
2108         state->textShift(0, -obj.getNum() * 0.001 *
2109                             fabs(state->getFontSize()));
2110       } else {
2111         state->textShift(-obj.getNum() * 0.001 *
2112                          fabs(state->getFontSize()), 0);
2113       }
2114     } else if (obj.isString()) {
2115       doShowText(obj.getString());
2116     } else {
2117       error(getPos(), "Element of show/space array must be number or string");
2118     }
2119     obj.free();
2120   }
2123 void PdfParser::doShowText(GooString *s) {
2124   GfxFont *font;
2125   int wMode;
2126   double riseX, riseY;
2127   CharCode code;
2128   Unicode u[8];
2129   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2130   double originX, originY, tOriginX, tOriginY;
2131   double oldCTM[6], newCTM[6];
2132   double *mat;
2133   Object charProc;
2134   Dict *resDict;
2135   Parser *oldParser;
2136   char *p;
2137   int len, n, uLen, nChars, nSpaces, i;
2139   font = state->getFont();
2140   wMode = font->getWMode();
2142   builder->beginString(state, s);
2144   // handle a Type 3 char
2145   if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2146     mat = state->getCTM();
2147     for (i = 0; i < 6; ++i) {
2148       oldCTM[i] = mat[i];
2149     }
2150     mat = state->getTextMat();
2151     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2152     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2153     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2154     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2155     mat = font->getFontMatrix();
2156     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2157     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2158     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2159     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2160     newCTM[0] *= state->getFontSize();
2161     newCTM[1] *= state->getFontSize();
2162     newCTM[2] *= state->getFontSize();
2163     newCTM[3] *= state->getFontSize();
2164     newCTM[0] *= state->getHorizScaling();
2165     newCTM[2] *= state->getHorizScaling();
2166     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2167     curX = state->getCurX();
2168     curY = state->getCurY();
2169     lineX = state->getLineX();
2170     lineY = state->getLineY();
2171     oldParser = parser;
2172     p = s->getCString();
2173     len = s->getLength();
2174     while (len > 0) {
2175       n = font->getNextChar(p, len, &code,
2176                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2177                             &dx, &dy, &originX, &originY);
2178       dx = dx * state->getFontSize() + state->getCharSpace();
2179       if (n == 1 && *p == ' ') {
2180         dx += state->getWordSpace();
2181       }
2182       dx *= state->getHorizScaling();
2183       dy *= state->getFontSize();
2184       state->textTransformDelta(dx, dy, &tdx, &tdy);
2185       state->transform(curX + riseX, curY + riseY, &x, &y);
2186       saveState();
2187       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2188       //~ the CTM concat values here are wrong (but never used)
2189       //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2190       if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2191                                code, u, uLen)) {*/
2192         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2193         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2194           pushResources(resDict);
2195         }
2196         if (charProc.isStream()) {
2197           //parse(&charProc, gFalse); // TODO: parse into SVG font
2198         } else {
2199           error(getPos(), "Missing or bad Type3 CharProc entry");
2200         }
2201         //out->endType3Char(state);
2202         if (resDict) {
2203           popResources();
2204         }
2205         charProc.free();
2206       }
2207       restoreState();
2208       // GfxState::restore() does *not* restore the current position,
2209       // so we deal with it here using (curX, curY) and (lineX, lineY)
2210       curX += tdx;
2211       curY += tdy;
2212       state->moveTo(curX, curY);
2213       state->textSetPos(lineX, lineY);
2214       p += n;
2215       len -= n;
2216     }
2217     parser = oldParser;
2219   } else {
2220     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2221     p = s->getCString();
2222     len = s->getLength();
2223     while (len > 0) {
2224       n = font->getNextChar(p, len, &code,
2225                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2226                             &dx, &dy, &originX, &originY);
2227       
2228       if (wMode) {
2229         dx *= state->getFontSize();
2230         dy = dy * state->getFontSize() + state->getCharSpace();
2231         if (n == 1 && *p == ' ') {
2232           dy += state->getWordSpace();
2233         }
2234       } else {
2235         dx = dx * state->getFontSize() + state->getCharSpace();
2236         if (n == 1 && *p == ' ') {
2237           dx += state->getWordSpace();
2238         }
2239         dx *= state->getHorizScaling();
2240         dy *= state->getFontSize();
2241       }
2242       state->textTransformDelta(dx, dy, &tdx, &tdy);
2243       originX *= state->getFontSize();
2244       originY *= state->getFontSize();
2245       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2246       builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2247                        tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
2248       state->shift(tdx, tdy);
2249       p += n;
2250       len -= n;
2251     }
2252   }
2254   builder->endString(state);
2257 //------------------------------------------------------------------------
2258 // XObject operators
2259 //------------------------------------------------------------------------
2261 void PdfParser::opXObject(Object args[], int numArgs) {
2262   char *name;
2263   Object obj1, obj2, obj3, refObj;
2265   name = args[0].getName();
2266   if (!res->lookupXObject(name, &obj1)) {
2267     return;
2268   }
2269   if (!obj1.isStream()) {
2270     error(getPos(), "XObject '%s' is wrong type", name);
2271     obj1.free();
2272     return;
2273   }
2274   obj1.streamGetDict()->lookup("Subtype", &obj2);
2275   if (obj2.isName("Image")) {
2276     res->lookupXObjectNF(name, &refObj);
2277     doImage(&refObj, obj1.getStream(), gFalse);
2278     refObj.free();
2279   } else if (obj2.isName("Form")) {
2280     doForm(&obj1);
2281   } else if (obj2.isName("PS")) {
2282     obj1.streamGetDict()->lookup("Level1", &obj3);
2283 /*    out->psXObject(obj1.getStream(),
2284                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2285   } else if (obj2.isName()) {
2286     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2287   } else {
2288     error(getPos(), "XObject subtype is missing or wrong type");
2289   }
2290   obj2.free();
2291   obj1.free();
2294 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2295   Dict *dict, *maskDict;
2296   int width, height;
2297   int bits, maskBits;
2298   StreamColorSpaceMode csMode;
2299   GBool mask;
2300   GBool invert;
2301   GfxColorSpace *colorSpace, *maskColorSpace;
2302   GfxImageColorMap *colorMap, *maskColorMap;
2303   Object maskObj, smaskObj;
2304   GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2305   int maskColors[2*gfxColorMaxComps];
2306   int maskWidth, maskHeight;
2307   GBool maskInvert;
2308   Stream *maskStr;
2309   Object obj1, obj2;
2310   int i;
2312   // get info from the stream
2313   bits = 0;
2314   csMode = streamCSNone;
2315   str->getImageParams(&bits, &csMode);
2317   // get stream dict
2318   dict = str->getDict();
2320   // get size
2321   dict->lookup("Width", &obj1);
2322   if (obj1.isNull()) {
2323     obj1.free();
2324     dict->lookup("W", &obj1);
2325   }
2326   if (obj1.isInt())
2327     width = obj1.getInt();
2328   else if (obj1.isReal())
2329     width = (int)obj1.getReal();
2330   else
2331     goto err2;
2332   obj1.free();
2333   dict->lookup("Height", &obj1);
2334   if (obj1.isNull()) {
2335     obj1.free();
2336     dict->lookup("H", &obj1);
2337   }
2338   if (obj1.isInt())
2339     height = obj1.getInt();
2340   else if (obj1.isReal())
2341     height = (int)obj1.getReal();
2342   else
2343     goto err2;
2344   obj1.free();
2346   // image or mask?
2347   dict->lookup("ImageMask", &obj1);
2348   if (obj1.isNull()) {
2349     obj1.free();
2350     dict->lookup("IM", &obj1);
2351   }
2352   mask = gFalse;
2353   if (obj1.isBool())
2354     mask = obj1.getBool();
2355   else if (!obj1.isNull())
2356     goto err2;
2357   obj1.free();
2359   // bit depth
2360   if (bits == 0) {
2361     dict->lookup("BitsPerComponent", &obj1);
2362     if (obj1.isNull()) {
2363       obj1.free();
2364       dict->lookup("BPC", &obj1);
2365     }
2366     if (obj1.isInt()) {
2367       bits = obj1.getInt();
2368     } else if (mask) {
2369       bits = 1;
2370     } else {
2371       goto err2;
2372     }
2373     obj1.free();
2374   }
2376   // display a mask
2377   if (mask) {
2379     // check for inverted mask
2380     if (bits != 1)
2381       goto err1;
2382     invert = gFalse;
2383     dict->lookup("Decode", &obj1);
2384     if (obj1.isNull()) {
2385       obj1.free();
2386       dict->lookup("D", &obj1);
2387     }
2388     if (obj1.isArray()) {
2389       obj1.arrayGet(0, &obj2);
2390       if (obj2.isInt() && obj2.getInt() == 1)
2391         invert = gTrue;
2392       obj2.free();
2393     } else if (!obj1.isNull()) {
2394       goto err2;
2395     }
2396     obj1.free();
2398     // draw it
2399     //out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2401   } else {
2403     // get color space and color map
2404     dict->lookup("ColorSpace", &obj1);
2405     if (obj1.isNull()) {
2406       obj1.free();
2407       dict->lookup("CS", &obj1);
2408     }
2409     if (obj1.isName()) {
2410       res->lookupColorSpace(obj1.getName(), &obj2);
2411       if (!obj2.isNull()) {
2412         obj1.free();
2413         obj1 = obj2;
2414       } else {
2415         obj2.free();
2416       }
2417     }
2418     if (!obj1.isNull()) {
2419       colorSpace = GfxColorSpace::parse(&obj1);
2420     } else if (csMode == streamCSDeviceGray) {
2421       colorSpace = new GfxDeviceGrayColorSpace();
2422     } else if (csMode == streamCSDeviceRGB) {
2423       colorSpace = new GfxDeviceRGBColorSpace();
2424     } else if (csMode == streamCSDeviceCMYK) {
2425       colorSpace = new GfxDeviceCMYKColorSpace();
2426     } else {
2427       colorSpace = NULL;
2428     }
2429     obj1.free();
2430     if (!colorSpace) {
2431       goto err1;
2432     }
2433     dict->lookup("Decode", &obj1);
2434     if (obj1.isNull()) {
2435       obj1.free();
2436       dict->lookup("D", &obj1);
2437     }
2438     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2439     obj1.free();
2440     if (!colorMap->isOk()) {
2441       delete colorMap;
2442       goto err1;
2443     }
2445     // get the mask
2446     haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2447     maskStr = NULL; // make gcc happy
2448     maskWidth = maskHeight = 0; // make gcc happy
2449     maskInvert = gFalse; // make gcc happy
2450     maskColorMap = NULL; // make gcc happy
2451     dict->lookup("Mask", &maskObj);
2452     dict->lookup("SMask", &smaskObj);
2453     if (smaskObj.isStream()) {
2454       // soft mask
2455       if (inlineImg) {
2456         goto err1;
2457       }
2458       maskStr = smaskObj.getStream();
2459       maskDict = smaskObj.streamGetDict();
2460       maskDict->lookup("Width", &obj1);
2461       if (obj1.isNull()) {
2462         obj1.free();
2463         maskDict->lookup("W", &obj1);
2464       }
2465       if (!obj1.isInt()) {
2466         goto err2;
2467       }
2468       maskWidth = obj1.getInt();
2469       obj1.free();
2470       maskDict->lookup("Height", &obj1);
2471       if (obj1.isNull()) {
2472         obj1.free();
2473         maskDict->lookup("H", &obj1);
2474       }
2475       if (!obj1.isInt()) {
2476         goto err2;
2477       }
2478       maskHeight = obj1.getInt();
2479       obj1.free();
2480       maskDict->lookup("BitsPerComponent", &obj1);
2481       if (obj1.isNull()) {
2482         obj1.free();
2483         maskDict->lookup("BPC", &obj1);
2484       }
2485       if (!obj1.isInt()) {
2486         goto err2;
2487       }
2488       maskBits = obj1.getInt();
2489       obj1.free();
2490       maskDict->lookup("ColorSpace", &obj1);
2491       if (obj1.isNull()) {
2492         obj1.free();
2493         maskDict->lookup("CS", &obj1);
2494       }
2495       if (obj1.isName()) {
2496         res->lookupColorSpace(obj1.getName(), &obj2);
2497         if (!obj2.isNull()) {
2498           obj1.free();
2499           obj1 = obj2;
2500         } else {
2501           obj2.free();
2502         }
2503       }
2504       maskColorSpace = GfxColorSpace::parse(&obj1);
2505       obj1.free();
2506       if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2507         goto err1;
2508       }
2509       maskDict->lookup("Decode", &obj1);
2510       if (obj1.isNull()) {
2511         obj1.free();
2512         maskDict->lookup("D", &obj1);
2513       }
2514       maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2515       obj1.free();
2516       if (!maskColorMap->isOk()) {
2517         delete maskColorMap;
2518         goto err1;
2519       }
2520       //~ handle the Matte entry
2521       haveSoftMask = gTrue;
2522     } else if (maskObj.isArray()) {
2523       // color key mask
2524       for (i = 0;
2525            i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2526            ++i) {
2527         maskObj.arrayGet(i, &obj1);
2528         maskColors[i] = obj1.getInt();
2529         obj1.free();
2530       }
2531       haveColorKeyMask = gTrue;
2532     } else if (maskObj.isStream()) {
2533       // explicit mask
2534       if (inlineImg) {
2535         goto err1;
2536       }
2537       maskStr = maskObj.getStream();
2538       maskDict = maskObj.streamGetDict();
2539       maskDict->lookup("Width", &obj1);
2540       if (obj1.isNull()) {
2541         obj1.free();
2542         maskDict->lookup("W", &obj1);
2543       }
2544       if (!obj1.isInt()) {
2545         goto err2;
2546       }
2547       maskWidth = obj1.getInt();
2548       obj1.free();
2549       maskDict->lookup("Height", &obj1);
2550       if (obj1.isNull()) {
2551         obj1.free();
2552         maskDict->lookup("H", &obj1);
2553       }
2554       if (!obj1.isInt()) {
2555         goto err2;
2556       }
2557       maskHeight = obj1.getInt();
2558       obj1.free();
2559       maskDict->lookup("ImageMask", &obj1);
2560       if (obj1.isNull()) {
2561         obj1.free();
2562         maskDict->lookup("IM", &obj1);
2563       }
2564       if (!obj1.isBool() || !obj1.getBool()) {
2565         goto err2;
2566       }
2567       obj1.free();
2568       maskInvert = gFalse;
2569       maskDict->lookup("Decode", &obj1);
2570       if (obj1.isNull()) {
2571         obj1.free();
2572         maskDict->lookup("D", &obj1);
2573       }
2574       if (obj1.isArray()) {
2575         obj1.arrayGet(0, &obj2);
2576         if (obj2.isInt() && obj2.getInt() == 1) {
2577           maskInvert = gTrue;
2578         }
2579         obj2.free();
2580       } else if (!obj1.isNull()) {
2581         goto err2;
2582       }
2583       obj1.free();
2584       haveExplicitMask = gTrue;
2585     }
2587     // draw it
2588     if (haveSoftMask) {
2589 /*      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
2590                                maskStr, maskWidth, maskHeight, maskColorMap);*/
2591       delete maskColorMap;
2592     } else if (haveExplicitMask) {
2593 /*      out->drawMaskedImage(state, ref, str, width, height, colorMap,
2594                            maskStr, maskWidth, maskHeight, maskInvert);*/
2595     } else {
2596       builder->addImage(state, str, width, height, colorMap,
2597                         haveColorKeyMask ? maskColors : (int *)NULL);
2598     }
2599     delete colorMap;
2601     maskObj.free();
2602     smaskObj.free();
2603   }
2605   return;
2607  err2:
2608   obj1.free();
2609  err1:
2610   error(getPos(), "Bad image parameters");
2613 void PdfParser::doForm(Object *str) {
2614   Dict *dict;
2615   GBool transpGroup, isolated, knockout;
2616   GfxColorSpace *blendingColorSpace;
2617   Object matrixObj, bboxObj;
2618   double m[6], bbox[4];
2619   Object resObj;
2620   Dict *resDict;
2621   Object obj1, obj2, obj3;
2622   int i;
2624   // check for excessive recursion
2625   if (formDepth > 20) {
2626     return;
2627   }
2629   // get stream dict
2630   dict = str->streamGetDict();
2632   // check form type
2633   dict->lookup("FormType", &obj1);
2634   if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2635     error(getPos(), "Unknown form type");
2636   }
2637   obj1.free();
2639   // get bounding box
2640   dict->lookup("BBox", &bboxObj);
2641   if (!bboxObj.isArray()) {
2642     bboxObj.free();
2643     error(getPos(), "Bad form bounding box");
2644     return;
2645   }
2646   for (i = 0; i < 4; ++i) {
2647     bboxObj.arrayGet(i, &obj1);
2648     bbox[i] = obj1.getNum();
2649     obj1.free();
2650   }
2651   bboxObj.free();
2653   // get matrix
2654   dict->lookup("Matrix", &matrixObj);
2655   if (matrixObj.isArray()) {
2656     for (i = 0; i < 6; ++i) {
2657       matrixObj.arrayGet(i, &obj1);
2658       m[i] = obj1.getNum();
2659       obj1.free();
2660     }
2661   } else {
2662     m[0] = 1; m[1] = 0;
2663     m[2] = 0; m[3] = 1;
2664     m[4] = 0; m[5] = 0;
2665   }
2666   matrixObj.free();
2668   // get resources
2669   dict->lookup("Resources", &resObj);
2670   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2672   // check for a transparency group
2673   transpGroup = isolated = knockout = gFalse;
2674   blendingColorSpace = NULL;
2675   if (dict->lookup("Group", &obj1)->isDict()) {
2676     if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2677       transpGroup = gTrue;
2678       if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2679         blendingColorSpace = GfxColorSpace::parse(&obj3);
2680       }
2681       obj3.free();
2682       if (obj1.dictLookup("I", &obj3)->isBool()) {
2683         isolated = obj3.getBool();
2684       }
2685       obj3.free();
2686       if (obj1.dictLookup("K", &obj3)->isBool()) {
2687         knockout = obj3.getBool();
2688       }
2689       obj3.free();
2690     }
2691     obj2.free();
2692   }
2693   obj1.free();
2695   // draw it
2696   ++formDepth;
2697   doForm1(str, resDict, m, bbox,
2698           transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2699   --formDepth;
2701   if (blendingColorSpace) {
2702     delete blendingColorSpace;
2703   }
2704   resObj.free();
2707 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2708                   GBool transpGroup, GBool softMask,
2709                   GfxColorSpace *blendingColorSpace,
2710                   GBool isolated, GBool knockout,
2711                   GBool alpha, Function *transferFunc,
2712                   GfxColor *backdropColor) {
2713   Parser *oldParser;
2714   double oldBaseMatrix[6];
2715   int i;
2717   // push new resources on stack
2718   pushResources(resDict);
2720   // save current graphics state
2721   saveState();
2723   // kill any pre-existing path
2724   state->clearPath();
2726   // save current parser
2727   oldParser = parser;
2729   // set form transformation matrix
2730   state->concatCTM(matrix[0], matrix[1], matrix[2],
2731                    matrix[3], matrix[4], matrix[5]);
2732   builder->setTransform(matrix[0], matrix[1], matrix[2],
2733                         matrix[3], matrix[4], matrix[5]);
2735   // set form bounding box
2736   state->moveTo(bbox[0], bbox[1]);
2737   state->lineTo(bbox[2], bbox[1]);
2738   state->lineTo(bbox[2], bbox[3]);
2739   state->lineTo(bbox[0], bbox[3]);
2740   state->closePath();
2741   state->clip();
2742   builder->clip(state);
2743   state->clearPath();
2745   if (softMask || transpGroup) {
2746     if (state->getBlendMode() != gfxBlendNormal) {
2747       state->setBlendMode(gfxBlendNormal);
2748     }
2749     if (state->getFillOpacity() != 1) {
2750       state->setFillOpacity(1);
2751     }
2752     if (state->getStrokeOpacity() != 1) {
2753       state->setStrokeOpacity(1);
2754     }
2755     //out->clearSoftMask(state);
2756     //out->beginTransparencyGroup(state, bbox, blendingColorSpace,
2757     //                          isolated, knockout, softMask);
2758   }
2760   // set new base matrix
2761   for (i = 0; i < 6; ++i) {
2762     oldBaseMatrix[i] = baseMatrix[i];
2763     baseMatrix[i] = state->getCTM()[i];
2764   }
2766   // draw the form
2767   parse(str, gFalse);
2769   if (softMask || transpGroup) {
2770     //out->endTransparencyGroup(state);
2771   }
2773   // restore base matrix
2774   for (i = 0; i < 6; ++i) {
2775     baseMatrix[i] = oldBaseMatrix[i];
2776   }
2778   // restore parser
2779   parser = oldParser;
2781   // restore graphics state
2782   restoreState();
2784   // pop resource stack
2785   popResources();
2787   if (softMask) {
2788     //out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2789   } else if (transpGroup) {
2790     //out->paintTransparencyGroup(state, bbox);
2791   }
2793   return;
2796 //------------------------------------------------------------------------
2797 // in-line image operators
2798 //------------------------------------------------------------------------
2800 void PdfParser::opBeginImage(Object args[], int numArgs) {
2801   Stream *str;
2802   int c1, c2;
2804   // build dict/stream
2805   str = buildImageStream();
2807   // display the image
2808   if (str) {
2809     doImage(NULL, str, gTrue);
2810   
2811     // skip 'EI' tag
2812     c1 = str->getUndecodedStream()->getChar();
2813     c2 = str->getUndecodedStream()->getChar();
2814     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2815       c1 = c2;
2816       c2 = str->getUndecodedStream()->getChar();
2817     }
2818     delete str;
2819   }
2822 Stream *PdfParser::buildImageStream() {
2823   Object dict;
2824   Object obj;
2825   char *key;
2826   Stream *str;
2828   // build dictionary
2829   dict.initDict(xref);
2830   parser->getObj(&obj);
2831   while (!obj.isCmd("ID") && !obj.isEOF()) {
2832     if (!obj.isName()) {
2833       error(getPos(), "Inline image dictionary key must be a name object");
2834       obj.free();
2835     } else {
2836       key = copyString(obj.getName());
2837       obj.free();
2838       parser->getObj(&obj);
2839       if (obj.isEOF() || obj.isError()) {
2840         gfree(key);
2841         break;
2842       }
2843       dict.dictAdd(key, &obj);
2844     }
2845     parser->getObj(&obj);
2846   }
2847   if (obj.isEOF()) {
2848     error(getPos(), "End of file in inline image");
2849     obj.free();
2850     dict.free();
2851     return NULL;
2852   }
2853   obj.free();
2855   // make stream
2856   str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2857   str = str->addFilters(&dict);
2859   return str;
2862 void PdfParser::opImageData(Object args[], int numArgs) {
2863   error(getPos(), "Internal: got 'ID' operator");
2866 void PdfParser::opEndImage(Object args[], int numArgs) {
2867   error(getPos(), "Internal: got 'EI' operator");
2870 //------------------------------------------------------------------------
2871 // type 3 font operators
2872 //------------------------------------------------------------------------
2874 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2877 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2880 //------------------------------------------------------------------------
2881 // compatibility operators
2882 //------------------------------------------------------------------------
2884 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2885   ++ignoreUndef;
2888 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2889   if (ignoreUndef > 0)
2890     --ignoreUndef;
2893 //------------------------------------------------------------------------
2894 // marked content operators
2895 //------------------------------------------------------------------------
2897 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2898   if (printCommands) {
2899     printf("  marked content: %s ", args[0].getName());
2900     if (numArgs == 2)
2901       args[2].print(stdout);
2902     printf("\n");
2903     fflush(stdout);
2904   }
2906   if(numArgs == 2) {
2907     //out->beginMarkedContent(args[0].getName(),args[1].getDict());
2908   } else {
2909     //out->beginMarkedContent(args[0].getName());
2910   }
2913 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
2914   //out->endMarkedContent();
2917 void PdfParser::opMarkPoint(Object args[], int numArgs) {
2918   if (printCommands) {
2919     printf("  mark point: %s ", args[0].getName());
2920     if (numArgs == 2)
2921       args[2].print(stdout);
2922     printf("\n");
2923     fflush(stdout);
2924   }
2926   if(numArgs == 2) {
2927     //out->markPoint(args[0].getName(),args[1].getDict());
2928   } else {
2929     //out->markPoint(args[0].getName());
2930   }
2934 //------------------------------------------------------------------------
2935 // misc
2936 //------------------------------------------------------------------------
2938 void PdfParser::saveState() {
2939   builder->saveState();
2940   state = state->save();
2943 void PdfParser::restoreState() {
2944   state = state->restore();
2945   builder->restoreState();
2948 void PdfParser::pushResources(Dict *resDict) {
2949   res = new GfxResources(xref, resDict, res);
2952 void PdfParser::popResources() {
2953   GfxResources *resPtr;
2955   resPtr = res->getNext();
2956   delete res;
2957   res = resPtr;
2960 #endif /* HAVE_POPPLER */