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(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->setClipPath(state, true);
1469 } else {
1470 builder->setClipPath(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(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 // unsetting the font (drawing no text) is better than using the
2018 // previous one and drawing random glyphs from it
2019 state->setFont(NULL, args[1].getNum());
2020 fontChanged = gTrue;
2021 return;
2022 }
2023 if (printCommands) {
2024 printf(" font: tag=%s name='%s' %g\n",
2025 font->getTag()->getCString(),
2026 font->getName() ? font->getName()->getCString() : "???",
2027 args[1].getNum());
2028 fflush(stdout);
2029 }
2031 font->incRefCnt();
2032 state->setFont(font, args[1].getNum());
2033 fontChanged = gTrue;
2034 }
2036 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2037 state->setLeading(args[0].getNum());
2038 }
2040 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2041 state->setRender(args[0].getInt());
2042 builder->updateStyle(state);
2043 }
2045 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2046 state->setRise(args[0].getNum());
2047 }
2049 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2050 state->setWordSpace(args[0].getNum());
2051 }
2053 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2054 state->setHorizScaling(args[0].getNum());
2055 builder->updateTextMatrix(state);
2056 fontChanged = gTrue;
2057 }
2059 //------------------------------------------------------------------------
2060 // text positioning operators
2061 //------------------------------------------------------------------------
2063 void PdfParser::opTextMove(Object args[], int numArgs) {
2064 double tx, ty;
2066 tx = state->getLineX() + args[0].getNum();
2067 ty = state->getLineY() + args[1].getNum();
2068 state->textMoveTo(tx, ty);
2069 builder->updateTextPosition(tx, ty);
2070 }
2072 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2073 double tx, ty;
2075 tx = state->getLineX() + args[0].getNum();
2076 ty = args[1].getNum();
2077 state->setLeading(-ty);
2078 ty += state->getLineY();
2079 state->textMoveTo(tx, ty);
2080 builder->updateTextPosition(tx, ty);
2081 }
2083 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2084 state->setTextMat(args[0].getNum(), args[1].getNum(),
2085 args[2].getNum(), args[3].getNum(),
2086 args[4].getNum(), args[5].getNum());
2087 state->textMoveTo(0, 0);
2088 builder->updateTextMatrix(state);
2089 builder->updateTextPosition(0.0, 0.0);
2090 fontChanged = gTrue;
2091 }
2093 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2094 double tx, ty;
2096 tx = state->getLineX();
2097 ty = state->getLineY() - state->getLeading();
2098 state->textMoveTo(tx, ty);
2099 builder->updateTextPosition(tx, ty);
2100 }
2102 //------------------------------------------------------------------------
2103 // text string operators
2104 //------------------------------------------------------------------------
2106 void PdfParser::opShowText(Object args[], int numArgs) {
2107 if (!state->getFont()) {
2108 error(getPos(), "No font in show");
2109 return;
2110 }
2111 if (fontChanged) {
2112 builder->updateFont(state);
2113 fontChanged = gFalse;
2114 }
2115 doShowText(args[0].getString());
2116 }
2118 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2119 double tx, ty;
2121 if (!state->getFont()) {
2122 error(getPos(), "No font in move/show");
2123 return;
2124 }
2125 if (fontChanged) {
2126 builder->updateFont(state);
2127 fontChanged = gFalse;
2128 }
2129 tx = state->getLineX();
2130 ty = state->getLineY() - state->getLeading();
2131 state->textMoveTo(tx, ty);
2132 builder->updateTextPosition(tx, ty);
2133 doShowText(args[0].getString());
2134 }
2136 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2137 double tx, ty;
2139 if (!state->getFont()) {
2140 error(getPos(), "No font in move/set/show");
2141 return;
2142 }
2143 if (fontChanged) {
2144 builder->updateFont(state);
2145 fontChanged = gFalse;
2146 }
2147 state->setWordSpace(args[0].getNum());
2148 state->setCharSpace(args[1].getNum());
2149 tx = state->getLineX();
2150 ty = state->getLineY() - state->getLeading();
2151 state->textMoveTo(tx, ty);
2152 builder->updateTextPosition(tx, ty);
2153 doShowText(args[2].getString());
2154 }
2156 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2157 Array *a;
2158 Object obj;
2159 int wMode;
2160 int i;
2162 if (!state->getFont()) {
2163 error(getPos(), "No font in show/space");
2164 return;
2165 }
2166 if (fontChanged) {
2167 builder->updateFont(state);
2168 fontChanged = gFalse;
2169 }
2170 wMode = state->getFont()->getWMode();
2171 a = args[0].getArray();
2172 for (i = 0; i < a->getLength(); ++i) {
2173 a->get(i, &obj);
2174 if (obj.isNum()) {
2175 // this uses the absolute value of the font size to match
2176 // Acrobat's behavior
2177 if (wMode) {
2178 state->textShift(0, -obj.getNum() * 0.001 *
2179 fabs(state->getFontSize()));
2180 } else {
2181 state->textShift(-obj.getNum() * 0.001 *
2182 fabs(state->getFontSize()), 0);
2183 }
2184 builder->updateTextShift(state, obj.getNum());
2185 } else if (obj.isString()) {
2186 doShowText(obj.getString());
2187 } else {
2188 error(getPos(), "Element of show/space array must be number or string");
2189 }
2190 obj.free();
2191 }
2192 }
2194 void PdfParser::doShowText(GooString *s) {
2195 GfxFont *font;
2196 int wMode;
2197 double riseX, riseY;
2198 CharCode code;
2199 Unicode u[8];
2200 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2201 double originX, originY, tOriginX, tOriginY;
2202 double oldCTM[6], newCTM[6];
2203 double *mat;
2204 Object charProc;
2205 Dict *resDict;
2206 Parser *oldParser;
2207 char *p;
2208 int len, n, uLen, nChars, nSpaces, i;
2210 font = state->getFont();
2211 wMode = font->getWMode();
2213 builder->beginString(state, s);
2215 // handle a Type 3 char
2216 if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2217 mat = state->getCTM();
2218 for (i = 0; i < 6; ++i) {
2219 oldCTM[i] = mat[i];
2220 }
2221 mat = state->getTextMat();
2222 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2223 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2224 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2225 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2226 mat = font->getFontMatrix();
2227 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2228 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2229 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2230 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2231 newCTM[0] *= state->getFontSize();
2232 newCTM[1] *= state->getFontSize();
2233 newCTM[2] *= state->getFontSize();
2234 newCTM[3] *= state->getFontSize();
2235 newCTM[0] *= state->getHorizScaling();
2236 newCTM[2] *= state->getHorizScaling();
2237 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2238 curX = state->getCurX();
2239 curY = state->getCurY();
2240 lineX = state->getLineX();
2241 lineY = state->getLineY();
2242 oldParser = parser;
2243 p = s->getCString();
2244 len = s->getLength();
2245 while (len > 0) {
2246 n = font->getNextChar(p, len, &code,
2247 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2248 &dx, &dy, &originX, &originY);
2249 dx = dx * state->getFontSize() + state->getCharSpace();
2250 if (n == 1 && *p == ' ') {
2251 dx += state->getWordSpace();
2252 }
2253 dx *= state->getHorizScaling();
2254 dy *= state->getFontSize();
2255 state->textTransformDelta(dx, dy, &tdx, &tdy);
2256 state->transform(curX + riseX, curY + riseY, &x, &y);
2257 saveState();
2258 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2259 //~ the CTM concat values here are wrong (but never used)
2260 //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2261 if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2262 code, u, uLen)) {*/
2263 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2264 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2265 pushResources(resDict);
2266 }
2267 if (charProc.isStream()) {
2268 //parse(&charProc, gFalse); // TODO: parse into SVG font
2269 } else {
2270 error(getPos(), "Missing or bad Type3 CharProc entry");
2271 }
2272 //out->endType3Char(state);
2273 if (resDict) {
2274 popResources();
2275 }
2276 charProc.free();
2277 }
2278 restoreState();
2279 // GfxState::restore() does *not* restore the current position,
2280 // so we deal with it here using (curX, curY) and (lineX, lineY)
2281 curX += tdx;
2282 curY += tdy;
2283 state->moveTo(curX, curY);
2284 state->textSetPos(lineX, lineY);
2285 p += n;
2286 len -= n;
2287 }
2288 parser = oldParser;
2290 } else {
2291 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2292 p = s->getCString();
2293 len = s->getLength();
2294 while (len > 0) {
2295 n = font->getNextChar(p, len, &code,
2296 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2297 &dx, &dy, &originX, &originY);
2299 if (wMode) {
2300 dx *= state->getFontSize();
2301 dy = dy * state->getFontSize() + state->getCharSpace();
2302 if (n == 1 && *p == ' ') {
2303 dy += state->getWordSpace();
2304 }
2305 } else {
2306 dx = dx * state->getFontSize() + state->getCharSpace();
2307 if (n == 1 && *p == ' ') {
2308 dx += state->getWordSpace();
2309 }
2310 dx *= state->getHorizScaling();
2311 dy *= state->getFontSize();
2312 }
2313 state->textTransformDelta(dx, dy, &tdx, &tdy);
2314 originX *= state->getFontSize();
2315 originY *= state->getFontSize();
2316 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2317 builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2318 dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2319 state->shift(tdx, tdy);
2320 p += n;
2321 len -= n;
2322 }
2323 }
2325 builder->endString(state);
2326 }
2328 //------------------------------------------------------------------------
2329 // XObject operators
2330 //------------------------------------------------------------------------
2332 void PdfParser::opXObject(Object args[], int numArgs) {
2333 char *name;
2334 Object obj1, obj2, obj3, refObj;
2336 name = args[0].getName();
2337 if (!res->lookupXObject(name, &obj1)) {
2338 return;
2339 }
2340 if (!obj1.isStream()) {
2341 error(getPos(), "XObject '%s' is wrong type", name);
2342 obj1.free();
2343 return;
2344 }
2345 obj1.streamGetDict()->lookup("Subtype", &obj2);
2346 if (obj2.isName("Image")) {
2347 res->lookupXObjectNF(name, &refObj);
2348 doImage(&refObj, obj1.getStream(), gFalse);
2349 refObj.free();
2350 } else if (obj2.isName("Form")) {
2351 doForm(&obj1);
2352 } else if (obj2.isName("PS")) {
2353 obj1.streamGetDict()->lookup("Level1", &obj3);
2354 /* out->psXObject(obj1.getStream(),
2355 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2356 } else if (obj2.isName()) {
2357 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2358 } else {
2359 error(getPos(), "XObject subtype is missing or wrong type");
2360 }
2361 obj2.free();
2362 obj1.free();
2363 }
2365 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2366 Dict *dict, *maskDict;
2367 int width, height;
2368 int bits, maskBits;
2369 StreamColorSpaceMode csMode;
2370 GBool mask;
2371 GBool invert;
2372 GfxColorSpace *colorSpace, *maskColorSpace;
2373 GfxImageColorMap *colorMap, *maskColorMap;
2374 Object maskObj, smaskObj;
2375 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2376 int maskColors[2*gfxColorMaxComps];
2377 int maskWidth, maskHeight;
2378 GBool maskInvert;
2379 Stream *maskStr;
2380 Object obj1, obj2;
2381 int i;
2383 // get info from the stream
2384 bits = 0;
2385 csMode = streamCSNone;
2386 str->getImageParams(&bits, &csMode);
2388 // get stream dict
2389 dict = str->getDict();
2391 // get size
2392 dict->lookup("Width", &obj1);
2393 if (obj1.isNull()) {
2394 obj1.free();
2395 dict->lookup("W", &obj1);
2396 }
2397 if (obj1.isInt())
2398 width = obj1.getInt();
2399 else if (obj1.isReal())
2400 width = (int)obj1.getReal();
2401 else
2402 goto err2;
2403 obj1.free();
2404 dict->lookup("Height", &obj1);
2405 if (obj1.isNull()) {
2406 obj1.free();
2407 dict->lookup("H", &obj1);
2408 }
2409 if (obj1.isInt())
2410 height = obj1.getInt();
2411 else if (obj1.isReal())
2412 height = (int)obj1.getReal();
2413 else
2414 goto err2;
2415 obj1.free();
2417 // image or mask?
2418 dict->lookup("ImageMask", &obj1);
2419 if (obj1.isNull()) {
2420 obj1.free();
2421 dict->lookup("IM", &obj1);
2422 }
2423 mask = gFalse;
2424 if (obj1.isBool())
2425 mask = obj1.getBool();
2426 else if (!obj1.isNull())
2427 goto err2;
2428 obj1.free();
2430 // bit depth
2431 if (bits == 0) {
2432 dict->lookup("BitsPerComponent", &obj1);
2433 if (obj1.isNull()) {
2434 obj1.free();
2435 dict->lookup("BPC", &obj1);
2436 }
2437 if (obj1.isInt()) {
2438 bits = obj1.getInt();
2439 } else if (mask) {
2440 bits = 1;
2441 } else {
2442 goto err2;
2443 }
2444 obj1.free();
2445 }
2447 // display a mask
2448 if (mask) {
2450 // check for inverted mask
2451 if (bits != 1)
2452 goto err1;
2453 invert = gFalse;
2454 dict->lookup("Decode", &obj1);
2455 if (obj1.isNull()) {
2456 obj1.free();
2457 dict->lookup("D", &obj1);
2458 }
2459 if (obj1.isArray()) {
2460 obj1.arrayGet(0, &obj2);
2461 if (obj2.isInt() && obj2.getInt() == 1)
2462 invert = gTrue;
2463 obj2.free();
2464 } else if (!obj1.isNull()) {
2465 goto err2;
2466 }
2467 obj1.free();
2469 // draw it
2470 builder->addImageMask(state, str, width, height, invert);
2472 } else {
2474 // get color space and color map
2475 dict->lookup("ColorSpace", &obj1);
2476 if (obj1.isNull()) {
2477 obj1.free();
2478 dict->lookup("CS", &obj1);
2479 }
2480 if (obj1.isName()) {
2481 res->lookupColorSpace(obj1.getName(), &obj2);
2482 if (!obj2.isNull()) {
2483 obj1.free();
2484 obj1 = obj2;
2485 } else {
2486 obj2.free();
2487 }
2488 }
2489 if (!obj1.isNull()) {
2490 colorSpace = GfxColorSpace::parse(&obj1);
2491 } else if (csMode == streamCSDeviceGray) {
2492 colorSpace = new GfxDeviceGrayColorSpace();
2493 } else if (csMode == streamCSDeviceRGB) {
2494 colorSpace = new GfxDeviceRGBColorSpace();
2495 } else if (csMode == streamCSDeviceCMYK) {
2496 colorSpace = new GfxDeviceCMYKColorSpace();
2497 } else {
2498 colorSpace = NULL;
2499 }
2500 obj1.free();
2501 if (!colorSpace) {
2502 goto err1;
2503 }
2504 dict->lookup("Decode", &obj1);
2505 if (obj1.isNull()) {
2506 obj1.free();
2507 dict->lookup("D", &obj1);
2508 }
2509 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2510 obj1.free();
2511 if (!colorMap->isOk()) {
2512 delete colorMap;
2513 goto err1;
2514 }
2516 // get the mask
2517 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2518 maskStr = NULL; // make gcc happy
2519 maskWidth = maskHeight = 0; // make gcc happy
2520 maskInvert = gFalse; // make gcc happy
2521 maskColorMap = NULL; // make gcc happy
2522 dict->lookup("Mask", &maskObj);
2523 dict->lookup("SMask", &smaskObj);
2524 if (smaskObj.isStream()) {
2525 // soft mask
2526 if (inlineImg) {
2527 goto err1;
2528 }
2529 maskStr = smaskObj.getStream();
2530 maskDict = smaskObj.streamGetDict();
2531 maskDict->lookup("Width", &obj1);
2532 if (obj1.isNull()) {
2533 obj1.free();
2534 maskDict->lookup("W", &obj1);
2535 }
2536 if (!obj1.isInt()) {
2537 goto err2;
2538 }
2539 maskWidth = obj1.getInt();
2540 obj1.free();
2541 maskDict->lookup("Height", &obj1);
2542 if (obj1.isNull()) {
2543 obj1.free();
2544 maskDict->lookup("H", &obj1);
2545 }
2546 if (!obj1.isInt()) {
2547 goto err2;
2548 }
2549 maskHeight = obj1.getInt();
2550 obj1.free();
2551 maskDict->lookup("BitsPerComponent", &obj1);
2552 if (obj1.isNull()) {
2553 obj1.free();
2554 maskDict->lookup("BPC", &obj1);
2555 }
2556 if (!obj1.isInt()) {
2557 goto err2;
2558 }
2559 maskBits = obj1.getInt();
2560 obj1.free();
2561 maskDict->lookup("ColorSpace", &obj1);
2562 if (obj1.isNull()) {
2563 obj1.free();
2564 maskDict->lookup("CS", &obj1);
2565 }
2566 if (obj1.isName()) {
2567 res->lookupColorSpace(obj1.getName(), &obj2);
2568 if (!obj2.isNull()) {
2569 obj1.free();
2570 obj1 = obj2;
2571 } else {
2572 obj2.free();
2573 }
2574 }
2575 maskColorSpace = GfxColorSpace::parse(&obj1);
2576 obj1.free();
2577 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2578 goto err1;
2579 }
2580 maskDict->lookup("Decode", &obj1);
2581 if (obj1.isNull()) {
2582 obj1.free();
2583 maskDict->lookup("D", &obj1);
2584 }
2585 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2586 obj1.free();
2587 if (!maskColorMap->isOk()) {
2588 delete maskColorMap;
2589 goto err1;
2590 }
2591 //~ handle the Matte entry
2592 haveSoftMask = gTrue;
2593 } else if (maskObj.isArray()) {
2594 // color key mask
2595 for (i = 0;
2596 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2597 ++i) {
2598 maskObj.arrayGet(i, &obj1);
2599 maskColors[i] = obj1.getInt();
2600 obj1.free();
2601 }
2602 haveColorKeyMask = gTrue;
2603 } else if (maskObj.isStream()) {
2604 // explicit mask
2605 if (inlineImg) {
2606 goto err1;
2607 }
2608 maskStr = maskObj.getStream();
2609 maskDict = maskObj.streamGetDict();
2610 maskDict->lookup("Width", &obj1);
2611 if (obj1.isNull()) {
2612 obj1.free();
2613 maskDict->lookup("W", &obj1);
2614 }
2615 if (!obj1.isInt()) {
2616 goto err2;
2617 }
2618 maskWidth = obj1.getInt();
2619 obj1.free();
2620 maskDict->lookup("Height", &obj1);
2621 if (obj1.isNull()) {
2622 obj1.free();
2623 maskDict->lookup("H", &obj1);
2624 }
2625 if (!obj1.isInt()) {
2626 goto err2;
2627 }
2628 maskHeight = obj1.getInt();
2629 obj1.free();
2630 maskDict->lookup("ImageMask", &obj1);
2631 if (obj1.isNull()) {
2632 obj1.free();
2633 maskDict->lookup("IM", &obj1);
2634 }
2635 if (!obj1.isBool() || !obj1.getBool()) {
2636 goto err2;
2637 }
2638 obj1.free();
2639 maskInvert = gFalse;
2640 maskDict->lookup("Decode", &obj1);
2641 if (obj1.isNull()) {
2642 obj1.free();
2643 maskDict->lookup("D", &obj1);
2644 }
2645 if (obj1.isArray()) {
2646 obj1.arrayGet(0, &obj2);
2647 if (obj2.isInt() && obj2.getInt() == 1) {
2648 maskInvert = gTrue;
2649 }
2650 obj2.free();
2651 } else if (!obj1.isNull()) {
2652 goto err2;
2653 }
2654 obj1.free();
2655 haveExplicitMask = gTrue;
2656 }
2658 // draw it
2659 if (haveSoftMask) {
2660 builder->addSoftMaskedImage(state, str, width, height, colorMap,
2661 maskStr, maskWidth, maskHeight, maskColorMap);
2662 delete maskColorMap;
2663 } else if (haveExplicitMask) {
2664 builder->addMaskedImage(state, str, width, height, colorMap,
2665 maskStr, maskWidth, maskHeight, maskInvert);
2666 } else {
2667 builder->addImage(state, str, width, height, colorMap,
2668 haveColorKeyMask ? maskColors : (int *)NULL);
2669 }
2670 delete colorMap;
2672 maskObj.free();
2673 smaskObj.free();
2674 }
2676 return;
2678 err2:
2679 obj1.free();
2680 err1:
2681 error(getPos(), "Bad image parameters");
2682 }
2684 void PdfParser::doForm(Object *str) {
2685 Dict *dict;
2686 GBool transpGroup, isolated, knockout;
2687 GfxColorSpace *blendingColorSpace;
2688 Object matrixObj, bboxObj;
2689 double m[6], bbox[4];
2690 Object resObj;
2691 Dict *resDict;
2692 Object obj1, obj2, obj3;
2693 int i;
2695 // check for excessive recursion
2696 if (formDepth > 20) {
2697 return;
2698 }
2700 // get stream dict
2701 dict = str->streamGetDict();
2703 // check form type
2704 dict->lookup("FormType", &obj1);
2705 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2706 error(getPos(), "Unknown form type");
2707 }
2708 obj1.free();
2710 // get bounding box
2711 dict->lookup("BBox", &bboxObj);
2712 if (!bboxObj.isArray()) {
2713 bboxObj.free();
2714 error(getPos(), "Bad form bounding box");
2715 return;
2716 }
2717 for (i = 0; i < 4; ++i) {
2718 bboxObj.arrayGet(i, &obj1);
2719 bbox[i] = obj1.getNum();
2720 obj1.free();
2721 }
2722 bboxObj.free();
2724 // get matrix
2725 dict->lookup("Matrix", &matrixObj);
2726 if (matrixObj.isArray()) {
2727 for (i = 0; i < 6; ++i) {
2728 matrixObj.arrayGet(i, &obj1);
2729 m[i] = obj1.getNum();
2730 obj1.free();
2731 }
2732 } else {
2733 m[0] = 1; m[1] = 0;
2734 m[2] = 0; m[3] = 1;
2735 m[4] = 0; m[5] = 0;
2736 }
2737 matrixObj.free();
2739 // get resources
2740 dict->lookup("Resources", &resObj);
2741 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2743 // check for a transparency group
2744 transpGroup = isolated = knockout = gFalse;
2745 blendingColorSpace = NULL;
2746 if (dict->lookup("Group", &obj1)->isDict()) {
2747 if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2748 transpGroup = gTrue;
2749 if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2750 blendingColorSpace = GfxColorSpace::parse(&obj3);
2751 }
2752 obj3.free();
2753 if (obj1.dictLookup("I", &obj3)->isBool()) {
2754 isolated = obj3.getBool();
2755 }
2756 obj3.free();
2757 if (obj1.dictLookup("K", &obj3)->isBool()) {
2758 knockout = obj3.getBool();
2759 }
2760 obj3.free();
2761 }
2762 obj2.free();
2763 }
2764 obj1.free();
2766 // draw it
2767 ++formDepth;
2768 doForm1(str, resDict, m, bbox,
2769 transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2770 --formDepth;
2772 if (blendingColorSpace) {
2773 delete blendingColorSpace;
2774 }
2775 resObj.free();
2776 }
2778 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2779 GBool transpGroup, GBool softMask,
2780 GfxColorSpace *blendingColorSpace,
2781 GBool isolated, GBool knockout,
2782 GBool alpha, Function *transferFunc,
2783 GfxColor *backdropColor) {
2784 Parser *oldParser;
2785 double oldBaseMatrix[6];
2786 int i;
2788 // push new resources on stack
2789 pushResources(resDict);
2791 // save current graphics state
2792 saveState();
2794 // kill any pre-existing path
2795 state->clearPath();
2797 if (softMask || transpGroup) {
2798 builder->clearSoftMask(state);
2799 builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2800 isolated, knockout, softMask);
2801 }
2803 // save current parser
2804 oldParser = parser;
2806 // set form transformation matrix
2807 state->concatCTM(matrix[0], matrix[1], matrix[2],
2808 matrix[3], matrix[4], matrix[5]);
2809 builder->setTransform(matrix[0], matrix[1], matrix[2],
2810 matrix[3], matrix[4], matrix[5]);
2812 // set form bounding box
2813 state->moveTo(bbox[0], bbox[1]);
2814 state->lineTo(bbox[2], bbox[1]);
2815 state->lineTo(bbox[2], bbox[3]);
2816 state->lineTo(bbox[0], bbox[3]);
2817 state->closePath();
2818 state->clip();
2819 clipHistory->setClip(state->getPath());
2820 builder->clip(state);
2821 state->clearPath();
2823 if (softMask || transpGroup) {
2824 if (state->getBlendMode() != gfxBlendNormal) {
2825 state->setBlendMode(gfxBlendNormal);
2826 }
2827 if (state->getFillOpacity() != 1) {
2828 builder->setGroupOpacity(state->getFillOpacity());
2829 state->setFillOpacity(1);
2830 }
2831 if (state->getStrokeOpacity() != 1) {
2832 state->setStrokeOpacity(1);
2833 }
2834 }
2836 // set new base matrix
2837 for (i = 0; i < 6; ++i) {
2838 oldBaseMatrix[i] = baseMatrix[i];
2839 baseMatrix[i] = state->getCTM()[i];
2840 }
2842 // draw the form
2843 parse(str, gFalse);
2845 // restore base matrix
2846 for (i = 0; i < 6; ++i) {
2847 baseMatrix[i] = oldBaseMatrix[i];
2848 }
2850 // restore parser
2851 parser = oldParser;
2853 if (softMask || transpGroup) {
2854 builder->popTransparencyGroup(state);
2855 }
2857 // restore graphics state
2858 restoreState();
2860 // pop resource stack
2861 popResources();
2863 if (softMask) {
2864 builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2865 } else if (transpGroup) {
2866 builder->paintTransparencyGroup(state, bbox);
2867 }
2869 return;
2870 }
2872 //------------------------------------------------------------------------
2873 // in-line image operators
2874 //------------------------------------------------------------------------
2876 void PdfParser::opBeginImage(Object args[], int numArgs) {
2877 Stream *str;
2878 int c1, c2;
2880 // build dict/stream
2881 str = buildImageStream();
2883 // display the image
2884 if (str) {
2885 doImage(NULL, str, gTrue);
2887 // skip 'EI' tag
2888 c1 = str->getUndecodedStream()->getChar();
2889 c2 = str->getUndecodedStream()->getChar();
2890 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2891 c1 = c2;
2892 c2 = str->getUndecodedStream()->getChar();
2893 }
2894 delete str;
2895 }
2896 }
2898 Stream *PdfParser::buildImageStream() {
2899 Object dict;
2900 Object obj;
2901 char *key;
2902 Stream *str;
2904 // build dictionary
2905 dict.initDict(xref);
2906 parser->getObj(&obj);
2907 while (!obj.isCmd("ID") && !obj.isEOF()) {
2908 if (!obj.isName()) {
2909 error(getPos(), "Inline image dictionary key must be a name object");
2910 obj.free();
2911 } else {
2912 key = copyString(obj.getName());
2913 obj.free();
2914 parser->getObj(&obj);
2915 if (obj.isEOF() || obj.isError()) {
2916 gfree(key);
2917 break;
2918 }
2919 dict.dictAdd(key, &obj);
2920 }
2921 parser->getObj(&obj);
2922 }
2923 if (obj.isEOF()) {
2924 error(getPos(), "End of file in inline image");
2925 obj.free();
2926 dict.free();
2927 return NULL;
2928 }
2929 obj.free();
2931 // make stream
2932 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2933 str = str->addFilters(&dict);
2935 return str;
2936 }
2938 void PdfParser::opImageData(Object args[], int numArgs) {
2939 error(getPos(), "Internal: got 'ID' operator");
2940 }
2942 void PdfParser::opEndImage(Object args[], int numArgs) {
2943 error(getPos(), "Internal: got 'EI' operator");
2944 }
2946 //------------------------------------------------------------------------
2947 // type 3 font operators
2948 //------------------------------------------------------------------------
2950 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2951 }
2953 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2954 }
2956 //------------------------------------------------------------------------
2957 // compatibility operators
2958 //------------------------------------------------------------------------
2960 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2961 ++ignoreUndef;
2962 }
2964 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2965 if (ignoreUndef > 0)
2966 --ignoreUndef;
2967 }
2969 //------------------------------------------------------------------------
2970 // marked content operators
2971 //------------------------------------------------------------------------
2973 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2974 if (printCommands) {
2975 printf(" marked content: %s ", args[0].getName());
2976 if (numArgs == 2)
2977 args[2].print(stdout);
2978 printf("\n");
2979 fflush(stdout);
2980 }
2982 if(numArgs == 2) {
2983 //out->beginMarkedContent(args[0].getName(),args[1].getDict());
2984 } else {
2985 //out->beginMarkedContent(args[0].getName());
2986 }
2987 }
2989 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
2990 //out->endMarkedContent();
2991 }
2993 void PdfParser::opMarkPoint(Object args[], int numArgs) {
2994 if (printCommands) {
2995 printf(" mark point: %s ", args[0].getName());
2996 if (numArgs == 2)
2997 args[2].print(stdout);
2998 printf("\n");
2999 fflush(stdout);
3000 }
3002 if(numArgs == 2) {
3003 //out->markPoint(args[0].getName(),args[1].getDict());
3004 } else {
3005 //out->markPoint(args[0].getName());
3006 }
3008 }
3010 //------------------------------------------------------------------------
3011 // misc
3012 //------------------------------------------------------------------------
3014 void PdfParser::saveState() {
3015 builder->saveState();
3016 state = state->save();
3017 clipHistory = clipHistory->save();
3018 }
3020 void PdfParser::restoreState() {
3021 clipHistory = clipHistory->restore();
3022 state = state->restore();
3023 builder->restoreState();
3024 }
3026 void PdfParser::pushResources(Dict *resDict) {
3027 res = new GfxResources(xref, resDict, res);
3028 }
3030 void PdfParser::popResources() {
3031 GfxResources *resPtr;
3033 resPtr = res->getNext();
3034 delete res;
3035 res = resPtr;
3036 }
3038 void PdfParser::setDefaultApproximationPrecision() {
3039 int i;
3041 for (i = 1; i <= pdfNumShadingTypes; ++i) {
3042 setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3043 }
3044 }
3046 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3047 int maxDepth) {
3049 if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3050 return;
3051 }
3052 colorDeltas[shadingType-1] = dblToCol(colorDelta);
3053 maxDepths[shadingType-1] = maxDepth;
3054 }
3056 //------------------------------------------------------------------------
3057 // ClipHistoryEntry
3058 //------------------------------------------------------------------------
3060 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3061 if (clipPathA) {
3062 clipPath = clipPathA->copy();
3063 } else {
3064 clipPath = NULL;
3065 }
3066 clipType = clipTypeA;
3067 saved = NULL;
3068 }
3070 ClipHistoryEntry::~ClipHistoryEntry() {
3071 if (clipPath) {
3072 delete clipPath;
3073 }
3074 }
3076 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3077 // Free previous clip path
3078 if (clipPath) {
3079 delete clipPath;
3080 }
3081 if (clipPathA) {
3082 clipPath = clipPathA->copy();
3083 clipType = clipTypeA;
3084 } else {
3085 clipPath = NULL;
3086 }
3087 }
3089 ClipHistoryEntry *ClipHistoryEntry::save() {
3090 ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3091 newEntry->saved = this;
3093 return newEntry;
3094 }
3096 ClipHistoryEntry *ClipHistoryEntry::restore() {
3097 ClipHistoryEntry *oldEntry;
3099 if (saved) {
3100 oldEntry = saved;
3101 saved = NULL;
3102 delete this;
3103 } else {
3104 oldEntry = this;
3105 }
3107 return oldEntry;
3108 }
3110 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3111 if (other->clipPath) {
3112 this->clipPath = other->clipPath->copy();
3113 this->clipType = other->clipType;
3114 } else {
3115 this->clipPath = NULL;
3116 }
3117 saved = NULL;
3118 }
3120 #endif /* HAVE_POPPLER */