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