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 }
2196 /*
2197 * The `POPPLER_NEW_GFXFONT' stuff is for the change to GfxFont's getNextChar() call.
2198 * Thanks to tsdgeos for the fix.
2199 * Miklos, does this look ok?
2200 */
2202 void PdfParser::doShowText(GooString *s) {
2203 GfxFont *font;
2204 int wMode;
2205 double riseX, riseY;
2206 CharCode code;
2207 #ifdef POPPLER_NEW_GFXFONT
2208 Unicode *u = NULL;
2209 #else
2210 Unicode u[8];
2211 #endif
2212 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2213 double originX, originY, tOriginX, tOriginY;
2214 double oldCTM[6], newCTM[6];
2215 double *mat;
2216 Object charProc;
2217 Dict *resDict;
2218 Parser *oldParser;
2219 char *p;
2220 int len, n, uLen, nChars, nSpaces, i;
2222 font = state->getFont();
2223 wMode = font->getWMode();
2225 builder->beginString(state, s);
2227 // handle a Type 3 char
2228 if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2229 mat = state->getCTM();
2230 for (i = 0; i < 6; ++i) {
2231 oldCTM[i] = mat[i];
2232 }
2233 mat = state->getTextMat();
2234 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2235 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2236 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2237 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2238 mat = font->getFontMatrix();
2239 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2240 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2241 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2242 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2243 newCTM[0] *= state->getFontSize();
2244 newCTM[1] *= state->getFontSize();
2245 newCTM[2] *= state->getFontSize();
2246 newCTM[3] *= state->getFontSize();
2247 newCTM[0] *= state->getHorizScaling();
2248 newCTM[2] *= state->getHorizScaling();
2249 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2250 curX = state->getCurX();
2251 curY = state->getCurY();
2252 lineX = state->getLineX();
2253 lineY = state->getLineY();
2254 oldParser = parser;
2255 p = s->getCString();
2256 len = s->getLength();
2257 while (len > 0) {
2258 n = font->getNextChar(p, len, &code,
2259 #ifdef POPPLER_NEW_GFXFONT
2260 &u, &uLen, /* TODO: This looks like a memory leak for u. */
2261 #else
2262 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2263 #endif
2264 &dx, &dy, &originX, &originY);
2265 dx = dx * state->getFontSize() + state->getCharSpace();
2266 if (n == 1 && *p == ' ') {
2267 dx += state->getWordSpace();
2268 }
2269 dx *= state->getHorizScaling();
2270 dy *= state->getFontSize();
2271 state->textTransformDelta(dx, dy, &tdx, &tdy);
2272 state->transform(curX + riseX, curY + riseY, &x, &y);
2273 saveState();
2274 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2275 //~ the CTM concat values here are wrong (but never used)
2276 //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2277 if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2278 code, u, uLen)) {*/
2279 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2280 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2281 pushResources(resDict);
2282 }
2283 if (charProc.isStream()) {
2284 //parse(&charProc, gFalse); // TODO: parse into SVG font
2285 } else {
2286 error(getPos(), "Missing or bad Type3 CharProc entry");
2287 }
2288 //out->endType3Char(state);
2289 if (resDict) {
2290 popResources();
2291 }
2292 charProc.free();
2293 }
2294 restoreState();
2295 // GfxState::restore() does *not* restore the current position,
2296 // so we deal with it here using (curX, curY) and (lineX, lineY)
2297 curX += tdx;
2298 curY += tdy;
2299 state->moveTo(curX, curY);
2300 state->textSetPos(lineX, lineY);
2301 p += n;
2302 len -= n;
2303 }
2304 parser = oldParser;
2306 } else {
2307 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2308 p = s->getCString();
2309 len = s->getLength();
2310 while (len > 0) {
2311 n = font->getNextChar(p, len, &code,
2312 #ifdef POPPLER_NEW_GFXFONT
2313 &u, &uLen, /* TODO: This looks like a memory leak for u. */
2314 #else
2315 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2316 #endif
2317 &dx, &dy, &originX, &originY);
2319 if (wMode) {
2320 dx *= state->getFontSize();
2321 dy = dy * state->getFontSize() + state->getCharSpace();
2322 if (n == 1 && *p == ' ') {
2323 dy += state->getWordSpace();
2324 }
2325 } else {
2326 dx = dx * state->getFontSize() + state->getCharSpace();
2327 if (n == 1 && *p == ' ') {
2328 dx += state->getWordSpace();
2329 }
2330 dx *= state->getHorizScaling();
2331 dy *= state->getFontSize();
2332 }
2333 state->textTransformDelta(dx, dy, &tdx, &tdy);
2334 originX *= state->getFontSize();
2335 originY *= state->getFontSize();
2336 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2337 builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2338 dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2339 state->shift(tdx, tdy);
2340 p += n;
2341 len -= n;
2342 }
2343 }
2345 builder->endString(state);
2346 }
2349 //------------------------------------------------------------------------
2350 // XObject operators
2351 //------------------------------------------------------------------------
2353 void PdfParser::opXObject(Object args[], int numArgs) {
2354 char *name;
2355 Object obj1, obj2, obj3, refObj;
2357 name = args[0].getName();
2358 if (!res->lookupXObject(name, &obj1)) {
2359 return;
2360 }
2361 if (!obj1.isStream()) {
2362 error(getPos(), "XObject '%s' is wrong type", name);
2363 obj1.free();
2364 return;
2365 }
2366 obj1.streamGetDict()->lookup("Subtype", &obj2);
2367 if (obj2.isName("Image")) {
2368 res->lookupXObjectNF(name, &refObj);
2369 doImage(&refObj, obj1.getStream(), gFalse);
2370 refObj.free();
2371 } else if (obj2.isName("Form")) {
2372 doForm(&obj1);
2373 } else if (obj2.isName("PS")) {
2374 obj1.streamGetDict()->lookup("Level1", &obj3);
2375 /* out->psXObject(obj1.getStream(),
2376 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2377 } else if (obj2.isName()) {
2378 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2379 } else {
2380 error(getPos(), "XObject subtype is missing or wrong type");
2381 }
2382 obj2.free();
2383 obj1.free();
2384 }
2386 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2387 Dict *dict, *maskDict;
2388 int width, height;
2389 int bits, maskBits;
2390 StreamColorSpaceMode csMode;
2391 GBool mask;
2392 GBool invert;
2393 GfxColorSpace *colorSpace, *maskColorSpace;
2394 GfxImageColorMap *colorMap, *maskColorMap;
2395 Object maskObj, smaskObj;
2396 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2397 int maskColors[2*gfxColorMaxComps];
2398 int maskWidth, maskHeight;
2399 GBool maskInvert;
2400 Stream *maskStr;
2401 Object obj1, obj2;
2402 int i;
2404 // get info from the stream
2405 bits = 0;
2406 csMode = streamCSNone;
2407 str->getImageParams(&bits, &csMode);
2409 // get stream dict
2410 dict = str->getDict();
2412 // get size
2413 dict->lookup("Width", &obj1);
2414 if (obj1.isNull()) {
2415 obj1.free();
2416 dict->lookup("W", &obj1);
2417 }
2418 if (obj1.isInt())
2419 width = obj1.getInt();
2420 else if (obj1.isReal())
2421 width = (int)obj1.getReal();
2422 else
2423 goto err2;
2424 obj1.free();
2425 dict->lookup("Height", &obj1);
2426 if (obj1.isNull()) {
2427 obj1.free();
2428 dict->lookup("H", &obj1);
2429 }
2430 if (obj1.isInt())
2431 height = obj1.getInt();
2432 else if (obj1.isReal())
2433 height = (int)obj1.getReal();
2434 else
2435 goto err2;
2436 obj1.free();
2438 // image or mask?
2439 dict->lookup("ImageMask", &obj1);
2440 if (obj1.isNull()) {
2441 obj1.free();
2442 dict->lookup("IM", &obj1);
2443 }
2444 mask = gFalse;
2445 if (obj1.isBool())
2446 mask = obj1.getBool();
2447 else if (!obj1.isNull())
2448 goto err2;
2449 obj1.free();
2451 // bit depth
2452 if (bits == 0) {
2453 dict->lookup("BitsPerComponent", &obj1);
2454 if (obj1.isNull()) {
2455 obj1.free();
2456 dict->lookup("BPC", &obj1);
2457 }
2458 if (obj1.isInt()) {
2459 bits = obj1.getInt();
2460 } else if (mask) {
2461 bits = 1;
2462 } else {
2463 goto err2;
2464 }
2465 obj1.free();
2466 }
2468 // display a mask
2469 if (mask) {
2471 // check for inverted mask
2472 if (bits != 1)
2473 goto err1;
2474 invert = gFalse;
2475 dict->lookup("Decode", &obj1);
2476 if (obj1.isNull()) {
2477 obj1.free();
2478 dict->lookup("D", &obj1);
2479 }
2480 if (obj1.isArray()) {
2481 obj1.arrayGet(0, &obj2);
2482 if (obj2.isInt() && obj2.getInt() == 1)
2483 invert = gTrue;
2484 obj2.free();
2485 } else if (!obj1.isNull()) {
2486 goto err2;
2487 }
2488 obj1.free();
2490 // draw it
2491 builder->addImageMask(state, str, width, height, invert);
2493 } else {
2495 // get color space and color map
2496 dict->lookup("ColorSpace", &obj1);
2497 if (obj1.isNull()) {
2498 obj1.free();
2499 dict->lookup("CS", &obj1);
2500 }
2501 if (obj1.isName()) {
2502 res->lookupColorSpace(obj1.getName(), &obj2);
2503 if (!obj2.isNull()) {
2504 obj1.free();
2505 obj1 = obj2;
2506 } else {
2507 obj2.free();
2508 }
2509 }
2510 if (!obj1.isNull()) {
2511 colorSpace = GfxColorSpace::parse(&obj1);
2512 } else if (csMode == streamCSDeviceGray) {
2513 colorSpace = new GfxDeviceGrayColorSpace();
2514 } else if (csMode == streamCSDeviceRGB) {
2515 colorSpace = new GfxDeviceRGBColorSpace();
2516 } else if (csMode == streamCSDeviceCMYK) {
2517 colorSpace = new GfxDeviceCMYKColorSpace();
2518 } else {
2519 colorSpace = NULL;
2520 }
2521 obj1.free();
2522 if (!colorSpace) {
2523 goto err1;
2524 }
2525 dict->lookup("Decode", &obj1);
2526 if (obj1.isNull()) {
2527 obj1.free();
2528 dict->lookup("D", &obj1);
2529 }
2530 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2531 obj1.free();
2532 if (!colorMap->isOk()) {
2533 delete colorMap;
2534 goto err1;
2535 }
2537 // get the mask
2538 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2539 maskStr = NULL; // make gcc happy
2540 maskWidth = maskHeight = 0; // make gcc happy
2541 maskInvert = gFalse; // make gcc happy
2542 maskColorMap = NULL; // make gcc happy
2543 dict->lookup("Mask", &maskObj);
2544 dict->lookup("SMask", &smaskObj);
2545 if (smaskObj.isStream()) {
2546 // soft mask
2547 if (inlineImg) {
2548 goto err1;
2549 }
2550 maskStr = smaskObj.getStream();
2551 maskDict = smaskObj.streamGetDict();
2552 maskDict->lookup("Width", &obj1);
2553 if (obj1.isNull()) {
2554 obj1.free();
2555 maskDict->lookup("W", &obj1);
2556 }
2557 if (!obj1.isInt()) {
2558 goto err2;
2559 }
2560 maskWidth = obj1.getInt();
2561 obj1.free();
2562 maskDict->lookup("Height", &obj1);
2563 if (obj1.isNull()) {
2564 obj1.free();
2565 maskDict->lookup("H", &obj1);
2566 }
2567 if (!obj1.isInt()) {
2568 goto err2;
2569 }
2570 maskHeight = obj1.getInt();
2571 obj1.free();
2572 maskDict->lookup("BitsPerComponent", &obj1);
2573 if (obj1.isNull()) {
2574 obj1.free();
2575 maskDict->lookup("BPC", &obj1);
2576 }
2577 if (!obj1.isInt()) {
2578 goto err2;
2579 }
2580 maskBits = obj1.getInt();
2581 obj1.free();
2582 maskDict->lookup("ColorSpace", &obj1);
2583 if (obj1.isNull()) {
2584 obj1.free();
2585 maskDict->lookup("CS", &obj1);
2586 }
2587 if (obj1.isName()) {
2588 res->lookupColorSpace(obj1.getName(), &obj2);
2589 if (!obj2.isNull()) {
2590 obj1.free();
2591 obj1 = obj2;
2592 } else {
2593 obj2.free();
2594 }
2595 }
2596 maskColorSpace = GfxColorSpace::parse(&obj1);
2597 obj1.free();
2598 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2599 goto err1;
2600 }
2601 maskDict->lookup("Decode", &obj1);
2602 if (obj1.isNull()) {
2603 obj1.free();
2604 maskDict->lookup("D", &obj1);
2605 }
2606 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2607 obj1.free();
2608 if (!maskColorMap->isOk()) {
2609 delete maskColorMap;
2610 goto err1;
2611 }
2612 //~ handle the Matte entry
2613 haveSoftMask = gTrue;
2614 } else if (maskObj.isArray()) {
2615 // color key mask
2616 for (i = 0;
2617 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2618 ++i) {
2619 maskObj.arrayGet(i, &obj1);
2620 maskColors[i] = obj1.getInt();
2621 obj1.free();
2622 }
2623 haveColorKeyMask = gTrue;
2624 } else if (maskObj.isStream()) {
2625 // explicit mask
2626 if (inlineImg) {
2627 goto err1;
2628 }
2629 maskStr = maskObj.getStream();
2630 maskDict = maskObj.streamGetDict();
2631 maskDict->lookup("Width", &obj1);
2632 if (obj1.isNull()) {
2633 obj1.free();
2634 maskDict->lookup("W", &obj1);
2635 }
2636 if (!obj1.isInt()) {
2637 goto err2;
2638 }
2639 maskWidth = obj1.getInt();
2640 obj1.free();
2641 maskDict->lookup("Height", &obj1);
2642 if (obj1.isNull()) {
2643 obj1.free();
2644 maskDict->lookup("H", &obj1);
2645 }
2646 if (!obj1.isInt()) {
2647 goto err2;
2648 }
2649 maskHeight = obj1.getInt();
2650 obj1.free();
2651 maskDict->lookup("ImageMask", &obj1);
2652 if (obj1.isNull()) {
2653 obj1.free();
2654 maskDict->lookup("IM", &obj1);
2655 }
2656 if (!obj1.isBool() || !obj1.getBool()) {
2657 goto err2;
2658 }
2659 obj1.free();
2660 maskInvert = gFalse;
2661 maskDict->lookup("Decode", &obj1);
2662 if (obj1.isNull()) {
2663 obj1.free();
2664 maskDict->lookup("D", &obj1);
2665 }
2666 if (obj1.isArray()) {
2667 obj1.arrayGet(0, &obj2);
2668 if (obj2.isInt() && obj2.getInt() == 1) {
2669 maskInvert = gTrue;
2670 }
2671 obj2.free();
2672 } else if (!obj1.isNull()) {
2673 goto err2;
2674 }
2675 obj1.free();
2676 haveExplicitMask = gTrue;
2677 }
2679 // draw it
2680 if (haveSoftMask) {
2681 builder->addSoftMaskedImage(state, str, width, height, colorMap,
2682 maskStr, maskWidth, maskHeight, maskColorMap);
2683 delete maskColorMap;
2684 } else if (haveExplicitMask) {
2685 builder->addMaskedImage(state, str, width, height, colorMap,
2686 maskStr, maskWidth, maskHeight, maskInvert);
2687 } else {
2688 builder->addImage(state, str, width, height, colorMap,
2689 haveColorKeyMask ? maskColors : (int *)NULL);
2690 }
2691 delete colorMap;
2693 maskObj.free();
2694 smaskObj.free();
2695 }
2697 return;
2699 err2:
2700 obj1.free();
2701 err1:
2702 error(getPos(), "Bad image parameters");
2703 }
2705 void PdfParser::doForm(Object *str) {
2706 Dict *dict;
2707 GBool transpGroup, isolated, knockout;
2708 GfxColorSpace *blendingColorSpace;
2709 Object matrixObj, bboxObj;
2710 double m[6], bbox[4];
2711 Object resObj;
2712 Dict *resDict;
2713 Object obj1, obj2, obj3;
2714 int i;
2716 // check for excessive recursion
2717 if (formDepth > 20) {
2718 return;
2719 }
2721 // get stream dict
2722 dict = str->streamGetDict();
2724 // check form type
2725 dict->lookup("FormType", &obj1);
2726 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2727 error(getPos(), "Unknown form type");
2728 }
2729 obj1.free();
2731 // get bounding box
2732 dict->lookup("BBox", &bboxObj);
2733 if (!bboxObj.isArray()) {
2734 bboxObj.free();
2735 error(getPos(), "Bad form bounding box");
2736 return;
2737 }
2738 for (i = 0; i < 4; ++i) {
2739 bboxObj.arrayGet(i, &obj1);
2740 bbox[i] = obj1.getNum();
2741 obj1.free();
2742 }
2743 bboxObj.free();
2745 // get matrix
2746 dict->lookup("Matrix", &matrixObj);
2747 if (matrixObj.isArray()) {
2748 for (i = 0; i < 6; ++i) {
2749 matrixObj.arrayGet(i, &obj1);
2750 m[i] = obj1.getNum();
2751 obj1.free();
2752 }
2753 } else {
2754 m[0] = 1; m[1] = 0;
2755 m[2] = 0; m[3] = 1;
2756 m[4] = 0; m[5] = 0;
2757 }
2758 matrixObj.free();
2760 // get resources
2761 dict->lookup("Resources", &resObj);
2762 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2764 // check for a transparency group
2765 transpGroup = isolated = knockout = gFalse;
2766 blendingColorSpace = NULL;
2767 if (dict->lookup("Group", &obj1)->isDict()) {
2768 if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2769 transpGroup = gTrue;
2770 if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2771 blendingColorSpace = GfxColorSpace::parse(&obj3);
2772 }
2773 obj3.free();
2774 if (obj1.dictLookup("I", &obj3)->isBool()) {
2775 isolated = obj3.getBool();
2776 }
2777 obj3.free();
2778 if (obj1.dictLookup("K", &obj3)->isBool()) {
2779 knockout = obj3.getBool();
2780 }
2781 obj3.free();
2782 }
2783 obj2.free();
2784 }
2785 obj1.free();
2787 // draw it
2788 ++formDepth;
2789 doForm1(str, resDict, m, bbox,
2790 transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2791 --formDepth;
2793 if (blendingColorSpace) {
2794 delete blendingColorSpace;
2795 }
2796 resObj.free();
2797 }
2799 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2800 GBool transpGroup, GBool softMask,
2801 GfxColorSpace *blendingColorSpace,
2802 GBool isolated, GBool knockout,
2803 GBool alpha, Function *transferFunc,
2804 GfxColor *backdropColor) {
2805 Parser *oldParser;
2806 double oldBaseMatrix[6];
2807 int i;
2809 // push new resources on stack
2810 pushResources(resDict);
2812 // save current graphics state
2813 saveState();
2815 // kill any pre-existing path
2816 state->clearPath();
2818 if (softMask || transpGroup) {
2819 builder->clearSoftMask(state);
2820 builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2821 isolated, knockout, softMask);
2822 }
2824 // save current parser
2825 oldParser = parser;
2827 // set form transformation matrix
2828 state->concatCTM(matrix[0], matrix[1], matrix[2],
2829 matrix[3], matrix[4], matrix[5]);
2830 builder->setTransform(matrix[0], matrix[1], matrix[2],
2831 matrix[3], matrix[4], matrix[5]);
2833 // set form bounding box
2834 state->moveTo(bbox[0], bbox[1]);
2835 state->lineTo(bbox[2], bbox[1]);
2836 state->lineTo(bbox[2], bbox[3]);
2837 state->lineTo(bbox[0], bbox[3]);
2838 state->closePath();
2839 state->clip();
2840 clipHistory->setClip(state->getPath());
2841 builder->clip(state);
2842 state->clearPath();
2844 if (softMask || transpGroup) {
2845 if (state->getBlendMode() != gfxBlendNormal) {
2846 state->setBlendMode(gfxBlendNormal);
2847 }
2848 if (state->getFillOpacity() != 1) {
2849 builder->setGroupOpacity(state->getFillOpacity());
2850 state->setFillOpacity(1);
2851 }
2852 if (state->getStrokeOpacity() != 1) {
2853 state->setStrokeOpacity(1);
2854 }
2855 }
2857 // set new base matrix
2858 for (i = 0; i < 6; ++i) {
2859 oldBaseMatrix[i] = baseMatrix[i];
2860 baseMatrix[i] = state->getCTM()[i];
2861 }
2863 // draw the form
2864 parse(str, gFalse);
2866 // restore base matrix
2867 for (i = 0; i < 6; ++i) {
2868 baseMatrix[i] = oldBaseMatrix[i];
2869 }
2871 // restore parser
2872 parser = oldParser;
2874 if (softMask || transpGroup) {
2875 builder->popTransparencyGroup(state);
2876 }
2878 // restore graphics state
2879 restoreState();
2881 // pop resource stack
2882 popResources();
2884 if (softMask) {
2885 builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2886 } else if (transpGroup) {
2887 builder->paintTransparencyGroup(state, bbox);
2888 }
2890 return;
2891 }
2893 //------------------------------------------------------------------------
2894 // in-line image operators
2895 //------------------------------------------------------------------------
2897 void PdfParser::opBeginImage(Object args[], int numArgs) {
2898 Stream *str;
2899 int c1, c2;
2901 // build dict/stream
2902 str = buildImageStream();
2904 // display the image
2905 if (str) {
2906 doImage(NULL, str, gTrue);
2908 // skip 'EI' tag
2909 c1 = str->getUndecodedStream()->getChar();
2910 c2 = str->getUndecodedStream()->getChar();
2911 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2912 c1 = c2;
2913 c2 = str->getUndecodedStream()->getChar();
2914 }
2915 delete str;
2916 }
2917 }
2919 Stream *PdfParser::buildImageStream() {
2920 Object dict;
2921 Object obj;
2922 char *key;
2923 Stream *str;
2925 // build dictionary
2926 dict.initDict(xref);
2927 parser->getObj(&obj);
2928 while (!obj.isCmd("ID") && !obj.isEOF()) {
2929 if (!obj.isName()) {
2930 error(getPos(), "Inline image dictionary key must be a name object");
2931 obj.free();
2932 } else {
2933 key = copyString(obj.getName());
2934 obj.free();
2935 parser->getObj(&obj);
2936 if (obj.isEOF() || obj.isError()) {
2937 gfree(key);
2938 break;
2939 }
2940 dict.dictAdd(key, &obj);
2941 }
2942 parser->getObj(&obj);
2943 }
2944 if (obj.isEOF()) {
2945 error(getPos(), "End of file in inline image");
2946 obj.free();
2947 dict.free();
2948 return NULL;
2949 }
2950 obj.free();
2952 // make stream
2953 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2954 str = str->addFilters(&dict);
2956 return str;
2957 }
2959 void PdfParser::opImageData(Object args[], int numArgs) {
2960 error(getPos(), "Internal: got 'ID' operator");
2961 }
2963 void PdfParser::opEndImage(Object args[], int numArgs) {
2964 error(getPos(), "Internal: got 'EI' operator");
2965 }
2967 //------------------------------------------------------------------------
2968 // type 3 font operators
2969 //------------------------------------------------------------------------
2971 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2972 }
2974 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2975 }
2977 //------------------------------------------------------------------------
2978 // compatibility operators
2979 //------------------------------------------------------------------------
2981 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2982 ++ignoreUndef;
2983 }
2985 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2986 if (ignoreUndef > 0)
2987 --ignoreUndef;
2988 }
2990 //------------------------------------------------------------------------
2991 // marked content operators
2992 //------------------------------------------------------------------------
2994 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2995 if (printCommands) {
2996 printf(" marked content: %s ", args[0].getName());
2997 if (numArgs == 2)
2998 args[2].print(stdout);
2999 printf("\n");
3000 fflush(stdout);
3001 }
3003 if(numArgs == 2) {
3004 //out->beginMarkedContent(args[0].getName(),args[1].getDict());
3005 } else {
3006 //out->beginMarkedContent(args[0].getName());
3007 }
3008 }
3010 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3011 //out->endMarkedContent();
3012 }
3014 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3015 if (printCommands) {
3016 printf(" mark point: %s ", args[0].getName());
3017 if (numArgs == 2)
3018 args[2].print(stdout);
3019 printf("\n");
3020 fflush(stdout);
3021 }
3023 if(numArgs == 2) {
3024 //out->markPoint(args[0].getName(),args[1].getDict());
3025 } else {
3026 //out->markPoint(args[0].getName());
3027 }
3029 }
3031 //------------------------------------------------------------------------
3032 // misc
3033 //------------------------------------------------------------------------
3035 void PdfParser::saveState() {
3036 builder->saveState();
3037 state = state->save();
3038 clipHistory = clipHistory->save();
3039 }
3041 void PdfParser::restoreState() {
3042 clipHistory = clipHistory->restore();
3043 state = state->restore();
3044 builder->restoreState();
3045 }
3047 void PdfParser::pushResources(Dict *resDict) {
3048 res = new GfxResources(xref, resDict, res);
3049 }
3051 void PdfParser::popResources() {
3052 GfxResources *resPtr;
3054 resPtr = res->getNext();
3055 delete res;
3056 res = resPtr;
3057 }
3059 void PdfParser::setDefaultApproximationPrecision() {
3060 int i;
3062 for (i = 1; i <= pdfNumShadingTypes; ++i) {
3063 setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3064 }
3065 }
3067 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3068 int maxDepth) {
3070 if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3071 return;
3072 }
3073 colorDeltas[shadingType-1] = dblToCol(colorDelta);
3074 maxDepths[shadingType-1] = maxDepth;
3075 }
3077 //------------------------------------------------------------------------
3078 // ClipHistoryEntry
3079 //------------------------------------------------------------------------
3081 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3082 if (clipPathA) {
3083 clipPath = clipPathA->copy();
3084 } else {
3085 clipPath = NULL;
3086 }
3087 clipType = clipTypeA;
3088 saved = NULL;
3089 }
3091 ClipHistoryEntry::~ClipHistoryEntry() {
3092 if (clipPath) {
3093 delete clipPath;
3094 }
3095 }
3097 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3098 // Free previous clip path
3099 if (clipPath) {
3100 delete clipPath;
3101 }
3102 if (clipPathA) {
3103 clipPath = clipPathA->copy();
3104 clipType = clipTypeA;
3105 } else {
3106 clipPath = NULL;
3107 }
3108 }
3110 ClipHistoryEntry *ClipHistoryEntry::save() {
3111 ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3112 newEntry->saved = this;
3114 return newEntry;
3115 }
3117 ClipHistoryEntry *ClipHistoryEntry::restore() {
3118 ClipHistoryEntry *oldEntry;
3120 if (saved) {
3121 oldEntry = saved;
3122 saved = NULL;
3123 delete this;
3124 } else {
3125 oldEntry = this;
3126 }
3128 return oldEntry;
3129 }
3131 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3132 if (other->clipPath) {
3133 this->clipPath = other->clipPath->copy();
3134 this->clipType = other->clipType;
3135 } else {
3136 this->clipPath = NULL;
3137 }
3138 saved = NULL;
3139 }
3141 #endif /* HAVE_POPPLER */