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, const_cast<char*>("Weird page contents"));
371 obj2.free();
372 return;
373 }
374 obj2.free();
375 }
376 } else if (!obj->isStream()) {
377 error(-1, const_cast<char*>("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;
391 // scan a sequence of objects
392 numArgs = 0;
393 parser->getObj(&obj);
394 while (!obj.isEOF()) {
396 // got a command - execute it
397 if (obj.isCmd()) {
398 if (printCommands) {
399 obj.print(stdout);
400 for (i = 0; i < numArgs; ++i) {
401 printf(" ");
402 args[i].print(stdout);
403 }
404 printf("\n");
405 fflush(stdout);
406 }
408 // Run the operation
409 execOp(&obj, args, numArgs);
411 obj.free();
412 for (i = 0; i < numArgs; ++i)
413 args[i].free();
414 numArgs = 0;
416 // got an argument - save it
417 } else if (numArgs < maxArgs) {
418 args[numArgs++] = obj;
420 // too many arguments - something is wrong
421 } else {
422 error(getPos(), const_cast<char*>("Too many args in content stream"));
423 if (printCommands) {
424 printf("throwing away arg: ");
425 obj.print(stdout);
426 printf("\n");
427 fflush(stdout);
428 }
429 obj.free();
430 }
432 // grab the next object
433 parser->getObj(&obj);
434 }
435 obj.free();
437 // args at end with no command
438 if (numArgs > 0) {
439 error(getPos(), const_cast<char*>("Leftover args in content stream"));
440 if (printCommands) {
441 printf("%d leftovers:", numArgs);
442 for (i = 0; i < numArgs; ++i) {
443 printf(" ");
444 args[i].print(stdout);
445 }
446 printf("\n");
447 fflush(stdout);
448 }
449 for (i = 0; i < numArgs; ++i)
450 args[i].free();
451 }
452 }
454 void PdfParser::pushOperator(const char *name) {
455 OpHistoryEntry *newEntry = new OpHistoryEntry;
456 newEntry->name = name;
457 newEntry->state = NULL;
458 newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
459 newEntry->next = operatorHistory;
460 operatorHistory = newEntry;
462 // Truncate list if needed
463 if (operatorHistory->depth > maxOperatorHistoryDepth) {
464 OpHistoryEntry *curr = operatorHistory;
465 OpHistoryEntry *prev = NULL;
466 while (curr && curr->next != NULL) {
467 curr->depth--;
468 prev = curr;
469 curr = curr->next;
470 }
471 if (prev) {
472 if (curr->state != NULL)
473 delete curr->state;
474 delete curr;
475 prev->next = NULL;
476 }
477 }
478 }
480 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
481 OpHistoryEntry *prev = NULL;
482 if (operatorHistory != NULL && look_back > 0) {
483 prev = operatorHistory->next;
484 while (--look_back > 0 && prev != NULL) {
485 prev = prev->next;
486 }
487 }
488 if (prev != NULL) {
489 return prev->name;
490 } else {
491 return "";
492 }
493 }
495 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
496 PdfOperator *op;
497 char *name;
498 Object *argPtr;
499 int i;
501 // find operator
502 name = cmd->getCmd();
503 if (!(op = findOp(name))) {
504 if (ignoreUndef == 0)
505 error(getPos(), const_cast<char*>("Unknown operator '%s'"), name);
506 return;
507 }
509 // type check args
510 argPtr = args;
511 if (op->numArgs >= 0) {
512 if (numArgs < op->numArgs) {
513 error(getPos(), const_cast<char*>("Too few (%d) args to '%s' operator"), numArgs, name);
514 return;
515 }
516 if (numArgs > op->numArgs) {
517 #if 0
518 error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
519 #endif
520 argPtr += numArgs - op->numArgs;
521 numArgs = op->numArgs;
522 }
523 } else {
524 if (numArgs > -op->numArgs) {
525 error(getPos(), const_cast<char*>("Too many (%d) args to '%s' operator"),
526 numArgs, name);
527 return;
528 }
529 }
530 for (i = 0; i < numArgs; ++i) {
531 if (!checkArg(&argPtr[i], op->tchk[i])) {
532 error(getPos(), const_cast<char*>("Arg #%d to '%s' operator is wrong type (%s)"),
533 i, name, argPtr[i].getTypeName());
534 return;
535 }
536 }
538 // add to history
539 pushOperator((char*)&op->name);
541 // do it
542 (this->*op->func)(argPtr, numArgs);
543 }
545 PdfOperator *PdfParser::findOp(char *name) {
546 int a, b, m, cmp;
548 a = -1;
549 b = numOps;
550 // invariant: opTab[a] < name < opTab[b]
551 while (b - a > 1) {
552 m = (a + b) / 2;
553 cmp = strcmp(opTab[m].name, name);
554 if (cmp < 0)
555 a = m;
556 else if (cmp > 0)
557 b = m;
558 else
559 a = b = m;
560 }
561 if (cmp != 0)
562 return NULL;
563 return &opTab[a];
564 }
566 GBool PdfParser::checkArg(Object *arg, TchkType type) {
567 switch (type) {
568 case tchkBool: return arg->isBool();
569 case tchkInt: return arg->isInt();
570 case tchkNum: return arg->isNum();
571 case tchkString: return arg->isString();
572 case tchkName: return arg->isName();
573 case tchkArray: return arg->isArray();
574 case tchkProps: return arg->isDict() || arg->isName();
575 case tchkSCN: return arg->isNum() || arg->isName();
576 case tchkNone: return gFalse;
577 }
578 return gFalse;
579 }
581 int PdfParser::getPos() {
582 return parser ? parser->getPos() : -1;
583 }
585 //------------------------------------------------------------------------
586 // graphics state operators
587 //------------------------------------------------------------------------
589 void PdfParser::opSave(Object args[], int numArgs) {
590 saveState();
591 }
593 void PdfParser::opRestore(Object args[], int numArgs) {
594 restoreState();
595 }
597 void PdfParser::opConcat(Object args[], int numArgs) {
598 state->concatCTM(args[0].getNum(), args[1].getNum(),
599 args[2].getNum(), args[3].getNum(),
600 args[4].getNum(), args[5].getNum());
601 const char *prevOp = getPreviousOperator();
602 double a0 = args[0].getNum();
603 double a1 = args[1].getNum();
604 double a2 = args[2].getNum();
605 double a3 = args[3].getNum();
606 double a4 = args[4].getNum();
607 double a5 = args[5].getNum();
608 if (!strcmp(prevOp, "q")) {
609 builder->setTransform(a0, a1, a2, a3, a4, a5);
610 } else if (!strcmp(prevOp, "cm") || !strcmp(prevOp, "startPage")) {
611 // multiply it with the previous transform
612 double otherMatrix[6];
613 if (!builder->getTransform(otherMatrix)) { // invalid transform
614 // construct identity matrix
615 otherMatrix[0] = otherMatrix[3] = 1.0;
616 otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
617 }
618 double c0 = a0*otherMatrix[0] + a1*otherMatrix[2];
619 double c1 = a0*otherMatrix[1] + a1*otherMatrix[3];
620 double c2 = a2*otherMatrix[0] + a3*otherMatrix[2];
621 double c3 = a2*otherMatrix[1] + a3*otherMatrix[3];
622 double c4 = a4*otherMatrix[0] + a5*otherMatrix[2] + otherMatrix[4];
623 double c5 = a4*otherMatrix[1] + a5*otherMatrix[3] + otherMatrix[5];
624 builder->setTransform(c0, c1, c2, c3, c4, c5);
625 } else {
626 builder->pushGroup();
627 builder->setTransform(a0, a1, a2, a3, a4, a5);
628 }
629 fontChanged = gTrue;
630 }
632 void PdfParser::opSetDash(Object args[], int numArgs) {
633 Array *a;
634 int length;
635 Object obj;
636 double *dash;
637 int i;
639 a = args[0].getArray();
640 length = a->getLength();
641 if (length == 0) {
642 dash = NULL;
643 } else {
644 dash = (double *)gmallocn(length, sizeof(double));
645 for (i = 0; i < length; ++i) {
646 dash[i] = a->get(i, &obj)->getNum();
647 obj.free();
648 }
649 }
650 state->setLineDash(dash, length, args[1].getNum());
651 builder->updateStyle(state);
652 }
654 void PdfParser::opSetFlat(Object args[], int numArgs) {
655 state->setFlatness((int)args[0].getNum());
656 }
658 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
659 state->setLineJoin(args[0].getInt());
660 builder->updateStyle(state);
661 }
663 void PdfParser::opSetLineCap(Object args[], int numArgs) {
664 state->setLineCap(args[0].getInt());
665 builder->updateStyle(state);
666 }
668 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
669 state->setMiterLimit(args[0].getNum());
670 builder->updateStyle(state);
671 }
673 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
674 state->setLineWidth(args[0].getNum());
675 builder->updateStyle(state);
676 }
678 void PdfParser::opSetExtGState(Object args[], int numArgs) {
679 Object obj1, obj2, obj3, obj4, obj5;
680 GfxBlendMode mode;
681 GBool haveFillOP;
682 Function *funcs[4];
683 GfxColor backdropColor;
684 GBool haveBackdropColor;
685 GfxColorSpace *blendingColorSpace;
686 GBool alpha, isolated, knockout;
687 int i;
689 if (!res->lookupGState(args[0].getName(), &obj1)) {
690 return;
691 }
692 if (!obj1.isDict()) {
693 error(getPos(), const_cast<char*>("ExtGState '%s' is wrong type"), args[0].getName());
694 obj1.free();
695 return;
696 }
697 if (printCommands) {
698 printf(" gfx state dict: ");
699 obj1.print();
700 printf("\n");
701 }
703 // transparency support: blend mode, fill/stroke opacity
704 if (!obj1.dictLookup(const_cast<char*>("BM"), &obj2)->isNull()) {
705 if (state->parseBlendMode(&obj2, &mode)) {
706 state->setBlendMode(mode);
707 } else {
708 error(getPos(), const_cast<char*>("Invalid blend mode in ExtGState"));
709 }
710 }
711 obj2.free();
712 if (obj1.dictLookup(const_cast<char*>("ca"), &obj2)->isNum()) {
713 state->setFillOpacity(obj2.getNum());
714 }
715 obj2.free();
716 if (obj1.dictLookup(const_cast<char*>("CA"), &obj2)->isNum()) {
717 state->setStrokeOpacity(obj2.getNum());
718 }
719 obj2.free();
721 // fill/stroke overprint
722 if ((haveFillOP = (obj1.dictLookup(const_cast<char*>("op"), &obj2)->isBool()))) {
723 state->setFillOverprint(obj2.getBool());
724 }
725 obj2.free();
726 if (obj1.dictLookup(const_cast<char*>("OP"), &obj2)->isBool()) {
727 state->setStrokeOverprint(obj2.getBool());
728 if (!haveFillOP) {
729 state->setFillOverprint(obj2.getBool());
730 }
731 }
732 obj2.free();
734 // stroke adjust
735 if (obj1.dictLookup(const_cast<char*>("SA"), &obj2)->isBool()) {
736 state->setStrokeAdjust(obj2.getBool());
737 }
738 obj2.free();
740 // transfer function
741 if (obj1.dictLookup(const_cast<char*>("TR2"), &obj2)->isNull()) {
742 obj2.free();
743 obj1.dictLookup(const_cast<char*>("TR"), &obj2);
744 }
745 if (obj2.isName(const_cast<char*>("Default")) ||
746 obj2.isName(const_cast<char*>("Identity"))) {
747 funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
748 state->setTransfer(funcs);
749 } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
750 for (i = 0; i < 4; ++i) {
751 obj2.arrayGet(i, &obj3);
752 funcs[i] = Function::parse(&obj3);
753 obj3.free();
754 if (!funcs[i]) {
755 break;
756 }
757 }
758 if (i == 4) {
759 state->setTransfer(funcs);
760 }
761 } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
762 if ((funcs[0] = Function::parse(&obj2))) {
763 funcs[1] = funcs[2] = funcs[3] = NULL;
764 state->setTransfer(funcs);
765 }
766 } else if (!obj2.isNull()) {
767 error(getPos(), const_cast<char*>("Invalid transfer function in ExtGState"));
768 }
769 obj2.free();
771 // soft mask
772 if (!obj1.dictLookup(const_cast<char*>("SMask"), &obj2)->isNull()) {
773 if (obj2.isName(const_cast<char*>("None"))) {
774 builder->clearSoftMask(state);
775 } else if (obj2.isDict()) {
776 if (obj2.dictLookup(const_cast<char*>("S"), &obj3)->isName(const_cast<char*>("Alpha"))) {
777 alpha = gTrue;
778 } else { // "Luminosity"
779 alpha = gFalse;
780 }
781 obj3.free();
782 funcs[0] = NULL;
783 if (!obj2.dictLookup(const_cast<char*>("TR"), &obj3)->isNull()) {
784 funcs[0] = Function::parse(&obj3);
785 if (funcs[0]->getInputSize() != 1 ||
786 funcs[0]->getOutputSize() != 1) {
787 error(getPos(),
788 const_cast<char*>("Invalid transfer function in soft mask in ExtGState"));
789 delete funcs[0];
790 funcs[0] = NULL;
791 }
792 }
793 obj3.free();
794 if ((haveBackdropColor = obj2.dictLookup(const_cast<char*>("BC"), &obj3)->isArray())) {
795 for (i = 0; i < gfxColorMaxComps; ++i) {
796 backdropColor.c[i] = 0;
797 }
798 for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
799 obj3.arrayGet(i, &obj4);
800 if (obj4.isNum()) {
801 backdropColor.c[i] = dblToCol(obj4.getNum());
802 }
803 obj4.free();
804 }
805 }
806 obj3.free();
807 if (obj2.dictLookup(const_cast<char*>("G"), &obj3)->isStream()) {
808 if (obj3.streamGetDict()->lookup(const_cast<char*>("Group"), &obj4)->isDict()) {
809 blendingColorSpace = NULL;
810 isolated = knockout = gFalse;
811 if (!obj4.dictLookup(const_cast<char*>("CS"), &obj5)->isNull()) {
812 #ifdef POPPLER_NEW_COLOR_SPACE_API
813 blendingColorSpace = GfxColorSpace::parse(&obj5, NULL);
814 #else
815 blendingColorSpace = GfxColorSpace::parse(&obj5);
816 #endif
817 }
818 obj5.free();
819 if (obj4.dictLookup(const_cast<char*>("I"), &obj5)->isBool()) {
820 isolated = obj5.getBool();
821 }
822 obj5.free();
823 if (obj4.dictLookup(const_cast<char*>("K"), &obj5)->isBool()) {
824 knockout = obj5.getBool();
825 }
826 obj5.free();
827 if (!haveBackdropColor) {
828 if (blendingColorSpace) {
829 blendingColorSpace->getDefaultColor(&backdropColor);
830 } else {
831 //~ need to get the parent or default color space (?)
832 for (i = 0; i < gfxColorMaxComps; ++i) {
833 backdropColor.c[i] = 0;
834 }
835 }
836 }
837 doSoftMask(&obj3, alpha, blendingColorSpace,
838 isolated, knockout, funcs[0], &backdropColor);
839 if (funcs[0]) {
840 delete funcs[0];
841 }
842 } else {
843 error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState - missing group"));
844 }
845 obj4.free();
846 } else {
847 error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState - missing group"));
848 }
849 obj3.free();
850 } else if (!obj2.isNull()) {
851 error(getPos(), const_cast<char*>("Invalid soft mask in ExtGState"));
852 }
853 }
854 obj2.free();
856 obj1.free();
857 }
859 void PdfParser::doSoftMask(Object *str, GBool alpha,
860 GfxColorSpace *blendingColorSpace,
861 GBool isolated, GBool knockout,
862 Function *transferFunc, GfxColor *backdropColor) {
863 Dict *dict, *resDict;
864 double m[6], bbox[4];
865 Object obj1, obj2;
866 int i;
868 // check for excessive recursion
869 if (formDepth > 20) {
870 return;
871 }
873 // get stream dict
874 dict = str->streamGetDict();
876 // check form type
877 dict->lookup(const_cast<char*>("FormType"), &obj1);
878 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
879 error(getPos(), const_cast<char*>("Unknown form type"));
880 }
881 obj1.free();
883 // get bounding box
884 dict->lookup(const_cast<char*>("BBox"), &obj1);
885 if (!obj1.isArray()) {
886 obj1.free();
887 error(getPos(), const_cast<char*>("Bad form bounding box"));
888 return;
889 }
890 for (i = 0; i < 4; ++i) {
891 obj1.arrayGet(i, &obj2);
892 bbox[i] = obj2.getNum();
893 obj2.free();
894 }
895 obj1.free();
897 // get matrix
898 dict->lookup(const_cast<char*>("Matrix"), &obj1);
899 if (obj1.isArray()) {
900 for (i = 0; i < 6; ++i) {
901 obj1.arrayGet(i, &obj2);
902 m[i] = obj2.getNum();
903 obj2.free();
904 }
905 } else {
906 m[0] = 1; m[1] = 0;
907 m[2] = 0; m[3] = 1;
908 m[4] = 0; m[5] = 0;
909 }
910 obj1.free();
912 // get resources
913 dict->lookup(const_cast<char*>("Resources"), &obj1);
914 resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
916 // draw it
917 ++formDepth;
918 doForm1(str, resDict, m, bbox, gTrue, gTrue,
919 blendingColorSpace, isolated, knockout,
920 alpha, transferFunc, backdropColor);
921 --formDepth;
923 if (blendingColorSpace) {
924 delete blendingColorSpace;
925 }
926 obj1.free();
927 }
929 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
930 }
932 //------------------------------------------------------------------------
933 // color operators
934 //------------------------------------------------------------------------
936 void PdfParser::opSetFillGray(Object args[], int numArgs) {
937 GfxColor color;
939 state->setFillPattern(NULL);
940 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
941 color.c[0] = dblToCol(args[0].getNum());
942 state->setFillColor(&color);
943 builder->updateStyle(state);
944 }
946 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
947 GfxColor color;
949 state->setStrokePattern(NULL);
950 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
951 color.c[0] = dblToCol(args[0].getNum());
952 state->setStrokeColor(&color);
953 builder->updateStyle(state);
954 }
956 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
957 GfxColor color;
958 int i;
960 state->setFillPattern(NULL);
961 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
962 for (i = 0; i < 4; ++i) {
963 color.c[i] = dblToCol(args[i].getNum());
964 }
965 state->setFillColor(&color);
966 builder->updateStyle(state);
967 }
969 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
970 GfxColor color;
971 int i;
973 state->setStrokePattern(NULL);
974 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
975 for (i = 0; i < 4; ++i) {
976 color.c[i] = dblToCol(args[i].getNum());
977 }
978 state->setStrokeColor(&color);
979 builder->updateStyle(state);
980 }
982 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
983 GfxColor color;
984 int i;
986 state->setFillPattern(NULL);
987 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
988 for (i = 0; i < 3; ++i) {
989 color.c[i] = dblToCol(args[i].getNum());
990 }
991 state->setFillColor(&color);
992 builder->updateStyle(state);
993 }
995 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
996 GfxColor color;
997 int i;
999 state->setStrokePattern(NULL);
1000 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
1001 for (i = 0; i < 3; ++i) {
1002 color.c[i] = dblToCol(args[i].getNum());
1003 }
1004 state->setStrokeColor(&color);
1005 builder->updateStyle(state);
1006 }
1008 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
1009 Object obj;
1010 GfxColorSpace *colorSpace;
1011 GfxColor color;
1013 state->setFillPattern(NULL);
1014 res->lookupColorSpace(args[0].getName(), &obj);
1015 #ifdef POPPLER_NEW_COLOR_SPACE_API
1016 if (obj.isNull()) {
1017 colorSpace = GfxColorSpace::parse(&args[0], NULL);
1018 } else {
1019 colorSpace = GfxColorSpace::parse(&obj, NULL);
1020 }
1021 #else
1022 if (obj.isNull()) {
1023 colorSpace = GfxColorSpace::parse(&args[0]);
1024 } else {
1025 colorSpace = GfxColorSpace::parse(&obj);
1026 }
1027 #endif
1028 obj.free();
1029 if (colorSpace) {
1030 state->setFillColorSpace(colorSpace);
1031 colorSpace->getDefaultColor(&color);
1032 state->setFillColor(&color);
1033 builder->updateStyle(state);
1034 } else {
1035 error(getPos(), const_cast<char*>("Bad color space (fill)"));
1036 }
1037 }
1039 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1040 Object obj;
1041 GfxColorSpace *colorSpace;
1042 GfxColor color;
1044 state->setStrokePattern(NULL);
1045 res->lookupColorSpace(args[0].getName(), &obj);
1046 #ifdef POPPLER_NEW_COLOR_SPACE_API
1047 if (obj.isNull()) {
1048 colorSpace = GfxColorSpace::parse(&args[0], NULL);
1049 } else {
1050 colorSpace = GfxColorSpace::parse(&obj, NULL);
1051 }
1052 #else
1053 if (obj.isNull()) {
1054 colorSpace = GfxColorSpace::parse(&args[0]);
1055 } else {
1056 colorSpace = GfxColorSpace::parse(&obj);
1057 }
1058 #endif
1059 obj.free();
1060 if (colorSpace) {
1061 state->setStrokeColorSpace(colorSpace);
1062 colorSpace->getDefaultColor(&color);
1063 state->setStrokeColor(&color);
1064 builder->updateStyle(state);
1065 } else {
1066 error(getPos(), const_cast<char*>("Bad color space (stroke)"));
1067 }
1068 }
1070 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1071 GfxColor color;
1072 int i;
1074 if (numArgs != state->getFillColorSpace()->getNComps()) {
1075 error(getPos(), const_cast<char*>("Incorrect number of arguments in 'sc' command"));
1076 return;
1077 }
1078 state->setFillPattern(NULL);
1079 for (i = 0; i < numArgs; ++i) {
1080 color.c[i] = dblToCol(args[i].getNum());
1081 }
1082 state->setFillColor(&color);
1083 builder->updateStyle(state);
1084 }
1086 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1087 GfxColor color;
1088 int i;
1090 if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1091 error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SC' command"));
1092 return;
1093 }
1094 state->setStrokePattern(NULL);
1095 for (i = 0; i < numArgs; ++i) {
1096 color.c[i] = dblToCol(args[i].getNum());
1097 }
1098 state->setStrokeColor(&color);
1099 builder->updateStyle(state);
1100 }
1102 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1103 GfxColor color;
1104 GfxPattern *pattern;
1105 int i;
1107 if (state->getFillColorSpace()->getMode() == csPattern) {
1108 if (numArgs > 1) {
1109 if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1110 numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1111 ->getUnder()->getNComps()) {
1112 error(getPos(), const_cast<char*>("Incorrect number of arguments in 'scn' command"));
1113 return;
1114 }
1115 for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1116 if (args[i].isNum()) {
1117 color.c[i] = dblToCol(args[i].getNum());
1118 }
1119 }
1120 state->setFillColor(&color);
1121 builder->updateStyle(state);
1122 }
1123 #ifdef POPPLER_NEW_COLOR_SPACE_API
1124 if (args[numArgs-1].isName() &&
1125 (pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) {
1126 state->setFillPattern(pattern);
1127 builder->updateStyle(state);
1128 }
1129 #else
1130 if (args[numArgs-1].isName() &&
1131 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1132 state->setFillPattern(pattern);
1133 builder->updateStyle(state);
1134 }
1135 #endif
1137 } else {
1138 if (numArgs != state->getFillColorSpace()->getNComps()) {
1139 error(getPos(), const_cast<char*>("Incorrect number of arguments in 'scn' command"));
1140 return;
1141 }
1142 state->setFillPattern(NULL);
1143 for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1144 if (args[i].isNum()) {
1145 color.c[i] = dblToCol(args[i].getNum());
1146 }
1147 }
1148 state->setFillColor(&color);
1149 builder->updateStyle(state);
1150 }
1151 }
1153 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1154 GfxColor color;
1155 GfxPattern *pattern;
1156 int i;
1158 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1159 if (numArgs > 1) {
1160 if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1161 ->getUnder() ||
1162 numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1163 ->getUnder()->getNComps()) {
1164 error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SCN' command"));
1165 return;
1166 }
1167 for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1168 if (args[i].isNum()) {
1169 color.c[i] = dblToCol(args[i].getNum());
1170 }
1171 }
1172 state->setStrokeColor(&color);
1173 builder->updateStyle(state);
1174 }
1175 #ifdef POPPLER_NEW_COLOR_SPACE_API
1176 if (args[numArgs-1].isName() &&
1177 (pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) {
1178 state->setStrokePattern(pattern);
1179 builder->updateStyle(state);
1180 }
1181 #else
1182 if (args[numArgs-1].isName() &&
1183 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1184 state->setStrokePattern(pattern);
1185 builder->updateStyle(state);
1186 }
1187 #endif
1189 } else {
1190 if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1191 error(getPos(), const_cast<char*>("Incorrect number of arguments in 'SCN' command"));
1192 return;
1193 }
1194 state->setStrokePattern(NULL);
1195 for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1196 if (args[i].isNum()) {
1197 color.c[i] = dblToCol(args[i].getNum());
1198 }
1199 }
1200 state->setStrokeColor(&color);
1201 builder->updateStyle(state);
1202 }
1203 }
1205 //------------------------------------------------------------------------
1206 // path segment operators
1207 //------------------------------------------------------------------------
1209 void PdfParser::opMoveTo(Object args[], int numArgs) {
1210 state->moveTo(args[0].getNum(), args[1].getNum());
1211 }
1213 void PdfParser::opLineTo(Object args[], int numArgs) {
1214 if (!state->isCurPt()) {
1215 error(getPos(), const_cast<char*>("No current point in lineto"));
1216 return;
1217 }
1218 state->lineTo(args[0].getNum(), args[1].getNum());
1219 }
1221 void PdfParser::opCurveTo(Object args[], int numArgs) {
1222 double x1, y1, x2, y2, x3, y3;
1224 if (!state->isCurPt()) {
1225 error(getPos(), const_cast<char*>("No current point in curveto"));
1226 return;
1227 }
1228 x1 = args[0].getNum();
1229 y1 = args[1].getNum();
1230 x2 = args[2].getNum();
1231 y2 = args[3].getNum();
1232 x3 = args[4].getNum();
1233 y3 = args[5].getNum();
1234 state->curveTo(x1, y1, x2, y2, x3, y3);
1235 }
1237 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1238 double x1, y1, x2, y2, x3, y3;
1240 if (!state->isCurPt()) {
1241 error(getPos(), const_cast<char*>("No current point in curveto1"));
1242 return;
1243 }
1244 x1 = state->getCurX();
1245 y1 = state->getCurY();
1246 x2 = args[0].getNum();
1247 y2 = args[1].getNum();
1248 x3 = args[2].getNum();
1249 y3 = args[3].getNum();
1250 state->curveTo(x1, y1, x2, y2, x3, y3);
1251 }
1253 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1254 double x1, y1, x2, y2, x3, y3;
1256 if (!state->isCurPt()) {
1257 error(getPos(), const_cast<char*>("No current point in curveto2"));
1258 return;
1259 }
1260 x1 = args[0].getNum();
1261 y1 = args[1].getNum();
1262 x2 = args[2].getNum();
1263 y2 = args[3].getNum();
1264 x3 = x2;
1265 y3 = y2;
1266 state->curveTo(x1, y1, x2, y2, x3, y3);
1267 }
1269 void PdfParser::opRectangle(Object args[], int numArgs) {
1270 double x, y, w, h;
1272 x = args[0].getNum();
1273 y = args[1].getNum();
1274 w = args[2].getNum();
1275 h = args[3].getNum();
1276 state->moveTo(x, y);
1277 state->lineTo(x + w, y);
1278 state->lineTo(x + w, y + h);
1279 state->lineTo(x, y + h);
1280 state->closePath();
1281 }
1283 void PdfParser::opClosePath(Object args[], int numArgs) {
1284 if (!state->isCurPt()) {
1285 error(getPos(), const_cast<char*>("No current point in closepath"));
1286 return;
1287 }
1288 state->closePath();
1289 }
1291 //------------------------------------------------------------------------
1292 // path painting operators
1293 //------------------------------------------------------------------------
1295 void PdfParser::opEndPath(Object args[], int numArgs) {
1296 doEndPath();
1297 }
1299 void PdfParser::opStroke(Object args[], int numArgs) {
1300 if (!state->isCurPt()) {
1301 //error(getPos(), const_cast<char*>("No path in stroke"));
1302 return;
1303 }
1304 if (state->isPath()) {
1305 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1306 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1307 doPatternStrokeFallback();
1308 } else {
1309 builder->addPath(state, false, true);
1310 }
1311 }
1312 doEndPath();
1313 }
1315 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1316 if (!state->isCurPt()) {
1317 //error(getPos(), const_cast<char*>("No path in closepath/stroke"));
1318 return;
1319 }
1320 state->closePath();
1321 if (state->isPath()) {
1322 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1323 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1324 doPatternStrokeFallback();
1325 } else {
1326 builder->addPath(state, false, true);
1327 }
1328 }
1329 doEndPath();
1330 }
1332 void PdfParser::opFill(Object args[], int numArgs) {
1333 if (!state->isCurPt()) {
1334 //error(getPos(), const_cast<char*>("No path in fill"));
1335 return;
1336 }
1337 if (state->isPath()) {
1338 if (state->getFillColorSpace()->getMode() == csPattern &&
1339 !builder->isPatternTypeSupported(state->getFillPattern())) {
1340 doPatternFillFallback(gFalse);
1341 } else {
1342 builder->addPath(state, true, false);
1343 }
1344 }
1345 doEndPath();
1346 }
1348 void PdfParser::opEOFill(Object args[], int numArgs) {
1349 if (!state->isCurPt()) {
1350 //error(getPos(), const_cast<char*>("No path in eofill"));
1351 return;
1352 }
1353 if (state->isPath()) {
1354 if (state->getFillColorSpace()->getMode() == csPattern &&
1355 !builder->isPatternTypeSupported(state->getFillPattern())) {
1356 doPatternFillFallback(gTrue);
1357 } else {
1358 builder->addPath(state, true, false, true);
1359 }
1360 }
1361 doEndPath();
1362 }
1364 void PdfParser::opFillStroke(Object args[], int numArgs) {
1365 if (!state->isCurPt()) {
1366 //error(getPos(), const_cast<char*>("No path in fill/stroke"));
1367 return;
1368 }
1369 if (state->isPath()) {
1370 doFillAndStroke(gFalse);
1371 } else {
1372 builder->addPath(state, true, true);
1373 }
1374 doEndPath();
1375 }
1377 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1378 if (!state->isCurPt()) {
1379 //error(getPos(), const_cast<char*>("No path in closepath/fill/stroke"));
1380 return;
1381 }
1382 if (state->isPath()) {
1383 state->closePath();
1384 doFillAndStroke(gFalse);
1385 }
1386 doEndPath();
1387 }
1389 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1390 if (!state->isCurPt()) {
1391 //error(getPos(), const_cast<char*>("No path in eofill/stroke"));
1392 return;
1393 }
1394 if (state->isPath()) {
1395 doFillAndStroke(gTrue);
1396 }
1397 doEndPath();
1398 }
1400 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1401 if (!state->isCurPt()) {
1402 //error(getPos(), const_cast<char*>("No path in closepath/eofill/stroke"));
1403 return;
1404 }
1405 if (state->isPath()) {
1406 state->closePath();
1407 doFillAndStroke(gTrue);
1408 }
1409 doEndPath();
1410 }
1412 void PdfParser::doFillAndStroke(GBool eoFill) {
1413 GBool fillOk = gTrue, strokeOk = gTrue;
1414 if (state->getFillColorSpace()->getMode() == csPattern &&
1415 !builder->isPatternTypeSupported(state->getFillPattern())) {
1416 fillOk = gFalse;
1417 }
1418 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1419 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1420 strokeOk = gFalse;
1421 }
1422 if (fillOk && strokeOk) {
1423 builder->addPath(state, true, true, eoFill);
1424 } else {
1425 doPatternFillFallback(eoFill);
1426 doPatternStrokeFallback();
1427 }
1428 }
1430 void PdfParser::doPatternFillFallback(GBool eoFill) {
1431 GfxPattern *pattern;
1433 if (!(pattern = state->getFillPattern())) {
1434 return;
1435 }
1436 switch (pattern->getType()) {
1437 case 1:
1438 break;
1439 case 2:
1440 doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1441 break;
1442 default:
1443 error(getPos(), const_cast<char*>("Unimplemented pattern type (%d) in fill"),
1444 pattern->getType());
1445 break;
1446 }
1447 }
1449 void PdfParser::doPatternStrokeFallback() {
1450 GfxPattern *pattern;
1452 if (!(pattern = state->getStrokePattern())) {
1453 return;
1454 }
1455 switch (pattern->getType()) {
1456 case 1:
1457 break;
1458 case 2:
1459 doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1460 break;
1461 default:
1462 error(getPos(), const_cast<char*>("Unimplemented pattern type (%d) in stroke"),
1463 pattern->getType());
1464 break;
1465 }
1466 }
1468 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1469 GBool stroke, GBool eoFill) {
1470 GfxShading *shading;
1471 GfxPath *savedPath;
1472 double *ctm, *btm, *ptm;
1473 double m[6], ictm[6], m1[6];
1474 double xMin, yMin, xMax, yMax;
1475 double det;
1477 shading = sPat->getShading();
1479 // save current graphics state
1480 savedPath = state->getPath()->copy();
1481 saveState();
1483 // clip to bbox
1484 if (0 ){//shading->getHasBBox()) {
1485 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1486 state->moveTo(xMin, yMin);
1487 state->lineTo(xMax, yMin);
1488 state->lineTo(xMax, yMax);
1489 state->lineTo(xMin, yMax);
1490 state->closePath();
1491 state->clip();
1492 //builder->clip(state);
1493 state->setPath(savedPath->copy());
1494 }
1496 // clip to current path
1497 if (stroke) {
1498 state->clipToStrokePath();
1499 //out->clipToStrokePath(state);
1500 } else {
1501 state->clip();
1502 if (eoFill) {
1503 builder->setClipPath(state, true);
1504 } else {
1505 builder->setClipPath(state);
1506 }
1507 }
1509 // set the color space
1510 state->setFillColorSpace(shading->getColorSpace()->copy());
1512 // background color fill
1513 if (shading->getHasBackground()) {
1514 state->setFillColor(shading->getBackground());
1515 builder->addPath(state, true, false);
1516 }
1517 state->clearPath();
1519 // construct a (pattern space) -> (current space) transform matrix
1520 ctm = state->getCTM();
1521 btm = baseMatrix;
1522 ptm = sPat->getMatrix();
1523 // iCTM = invert CTM
1524 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1525 ictm[0] = ctm[3] * det;
1526 ictm[1] = -ctm[1] * det;
1527 ictm[2] = -ctm[2] * det;
1528 ictm[3] = ctm[0] * det;
1529 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1530 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1531 // m1 = PTM * BTM = PTM * base transform matrix
1532 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1533 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1534 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1535 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1536 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1537 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1538 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1539 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1540 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1541 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1542 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1543 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1544 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1546 // set the new matrix
1547 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1548 builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1550 // do shading type-specific operations
1551 switch (shading->getType()) {
1552 case 1:
1553 doFunctionShFill((GfxFunctionShading *)shading);
1554 break;
1555 case 2:
1556 case 3:
1557 // no need to implement these
1558 break;
1559 case 4:
1560 case 5:
1561 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1562 break;
1563 case 6:
1564 case 7:
1565 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1566 break;
1567 }
1569 // restore graphics state
1570 restoreState();
1571 state->setPath(savedPath);
1572 }
1574 void PdfParser::opShFill(Object args[], int numArgs) {
1575 GfxShading *shading;
1576 GfxPath *savedPath = NULL;
1577 double xMin, yMin, xMax, yMax;
1578 double gradientTransform[6];
1579 double *matrix = NULL;
1580 GBool savedState = gFalse;
1582 #ifdef POPPLER_NEW_COLOR_SPACE_API
1583 if (!(shading = res->lookupShading(args[0].getName(), NULL))) {
1584 return;
1585 }
1586 #else
1587 if (!(shading = res->lookupShading(args[0].getName()))) {
1588 return;
1589 }
1590 #endif
1592 // save current graphics state
1593 if (shading->getType() != 2 && shading->getType() != 3) {
1594 savedPath = state->getPath()->copy();
1595 saveState();
1596 savedState = gTrue;
1597 } else { // get gradient transform if possible
1598 // check proper operator sequence
1599 // first there should be one W(*) and then one 'cm' somewhere before 'sh'
1600 GBool seenClip, seenConcat;
1601 seenClip = (clipHistory->getClipPath() != NULL);
1602 seenConcat = gFalse;
1603 int i = 1;
1604 while (i <= maxOperatorHistoryDepth) {
1605 const char *opName = getPreviousOperator(i);
1606 if (!strcmp(opName, "cm")) {
1607 if (seenConcat) { // more than one 'cm'
1608 break;
1609 } else {
1610 seenConcat = gTrue;
1611 }
1612 }
1613 i++;
1614 }
1616 if (seenConcat && seenClip) {
1617 if (builder->getTransform(gradientTransform)) {
1618 matrix = (double*)&gradientTransform;
1619 builder->setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); // remove transform
1620 }
1621 }
1622 }
1624 // clip to bbox
1625 if (shading->getHasBBox()) {
1626 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1627 state->moveTo(xMin, yMin);
1628 state->lineTo(xMax, yMin);
1629 state->lineTo(xMax, yMax);
1630 state->lineTo(xMin, yMax);
1631 state->closePath();
1632 state->clip();
1633 if (savedState)
1634 builder->setClipPath(state);
1635 else
1636 builder->clip(state);
1637 state->clearPath();
1638 }
1640 // set the color space
1641 if (savedState)
1642 state->setFillColorSpace(shading->getColorSpace()->copy());
1644 // do shading type-specific operations
1645 switch (shading->getType()) {
1646 case 1:
1647 doFunctionShFill((GfxFunctionShading *)shading);
1648 break;
1649 case 2:
1650 case 3:
1651 if (clipHistory->getClipPath()) {
1652 builder->addShadedFill(shading, matrix, clipHistory->getClipPath(),
1653 clipHistory->getClipType() == clipEO ? true : false);
1654 }
1655 break;
1656 case 4:
1657 case 5:
1658 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1659 break;
1660 case 6:
1661 case 7:
1662 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1663 break;
1664 }
1666 // restore graphics state
1667 if (savedState) {
1668 restoreState();
1669 state->setPath(savedPath);
1670 }
1672 delete shading;
1673 }
1675 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1676 double x0, y0, x1, y1;
1677 GfxColor colors[4];
1679 shading->getDomain(&x0, &y0, &x1, &y1);
1680 shading->getColor(x0, y0, &colors[0]);
1681 shading->getColor(x0, y1, &colors[1]);
1682 shading->getColor(x1, y0, &colors[2]);
1683 shading->getColor(x1, y1, &colors[3]);
1684 doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1685 }
1687 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1688 double x0, double y0,
1689 double x1, double y1,
1690 GfxColor *colors, int depth) {
1691 GfxColor fillColor;
1692 GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1693 GfxColor colors2[4];
1694 double functionColorDelta = colorDeltas[pdfFunctionShading-1];
1695 double *matrix;
1696 double xM, yM;
1697 int nComps, i, j;
1699 nComps = shading->getColorSpace()->getNComps();
1700 matrix = shading->getMatrix();
1702 // compare the four corner colors
1703 for (i = 0; i < 4; ++i) {
1704 for (j = 0; j < nComps; ++j) {
1705 if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1706 break;
1707 }
1708 }
1709 if (j < nComps) {
1710 break;
1711 }
1712 }
1714 // center of the rectangle
1715 xM = 0.5 * (x0 + x1);
1716 yM = 0.5 * (y0 + y1);
1718 // the four corner colors are close (or we hit the recursive limit)
1719 // -- fill the rectangle; but require at least one subdivision
1720 // (depth==0) to avoid problems when the four outer corners of the
1721 // shaded region are the same color
1722 if ((i == 4 && depth > 0) || depth == maxDepths[pdfFunctionShading-1]) {
1724 // use the center color
1725 shading->getColor(xM, yM, &fillColor);
1726 state->setFillColor(&fillColor);
1728 // fill the rectangle
1729 state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1730 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1731 state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1732 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1733 state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1734 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1735 state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1736 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1737 state->closePath();
1738 builder->addPath(state, true, false);
1739 state->clearPath();
1741 // the four corner colors are not close enough -- subdivide the
1742 // rectangle
1743 } else {
1745 // colors[0] colorM0 colors[2]
1746 // (x0,y0) (xM,y0) (x1,y0)
1747 // +----------+----------+
1748 // | | |
1749 // | UL | UR |
1750 // color0M | colorMM | color1M
1751 // (x0,yM) +----------+----------+ (x1,yM)
1752 // | (xM,yM) |
1753 // | LL | LR |
1754 // | | |
1755 // +----------+----------+
1756 // colors[1] colorM1 colors[3]
1757 // (x0,y1) (xM,y1) (x1,y1)
1759 shading->getColor(x0, yM, &color0M);
1760 shading->getColor(x1, yM, &color1M);
1761 shading->getColor(xM, y0, &colorM0);
1762 shading->getColor(xM, y1, &colorM1);
1763 shading->getColor(xM, yM, &colorMM);
1765 // upper-left sub-rectangle
1766 colors2[0] = colors[0];
1767 colors2[1] = color0M;
1768 colors2[2] = colorM0;
1769 colors2[3] = colorMM;
1770 doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1772 // lower-left sub-rectangle
1773 colors2[0] = color0M;
1774 colors2[1] = colors[1];
1775 colors2[2] = colorMM;
1776 colors2[3] = colorM1;
1777 doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1779 // upper-right sub-rectangle
1780 colors2[0] = colorM0;
1781 colors2[1] = colorMM;
1782 colors2[2] = colors[2];
1783 colors2[3] = color1M;
1784 doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1786 // lower-right sub-rectangle
1787 colors2[0] = colorMM;
1788 colors2[1] = colorM1;
1789 colors2[2] = color1M;
1790 colors2[3] = colors[3];
1791 doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1792 }
1793 }
1795 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1796 double x0, y0, x1, y1, x2, y2;
1797 GfxColor color0, color1, color2;
1798 int i;
1800 for (i = 0; i < shading->getNTriangles(); ++i) {
1801 shading->getTriangle(i, &x0, &y0, &color0,
1802 &x1, &y1, &color1,
1803 &x2, &y2, &color2);
1804 gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1805 shading->getColorSpace()->getNComps(), 0);
1806 }
1807 }
1809 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1810 double x1, double y1, GfxColor *color1,
1811 double x2, double y2, GfxColor *color2,
1812 int nComps, int depth) {
1813 double x01, y01, x12, y12, x20, y20;
1814 double gouraudColorDelta = colorDeltas[pdfGouraudTriangleShading-1];
1815 GfxColor color01, color12, color20;
1816 int i;
1818 for (i = 0; i < nComps; ++i) {
1819 if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1820 abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1821 break;
1822 }
1823 }
1824 if (i == nComps || depth == maxDepths[pdfGouraudTriangleShading-1]) {
1825 state->setFillColor(color0);
1826 state->moveTo(x0, y0);
1827 state->lineTo(x1, y1);
1828 state->lineTo(x2, y2);
1829 state->closePath();
1830 builder->addPath(state, true, false);
1831 state->clearPath();
1832 } else {
1833 x01 = 0.5 * (x0 + x1);
1834 y01 = 0.5 * (y0 + y1);
1835 x12 = 0.5 * (x1 + x2);
1836 y12 = 0.5 * (y1 + y2);
1837 x20 = 0.5 * (x2 + x0);
1838 y20 = 0.5 * (y2 + y0);
1839 //~ if the shading has a Function, this should interpolate on the
1840 //~ function parameter, not on the color components
1841 for (i = 0; i < nComps; ++i) {
1842 color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1843 color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1844 color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1845 }
1846 gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1847 x20, y20, &color20, nComps, depth + 1);
1848 gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1849 x12, y12, &color12, nComps, depth + 1);
1850 gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1851 x20, y20, &color20, nComps, depth + 1);
1852 gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1853 x2, y2, color2, nComps, depth + 1);
1854 }
1855 }
1857 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1858 int start, i;
1860 if (shading->getNPatches() > 128) {
1861 start = 3;
1862 } else if (shading->getNPatches() > 64) {
1863 start = 2;
1864 } else if (shading->getNPatches() > 16) {
1865 start = 1;
1866 } else {
1867 start = 0;
1868 }
1869 for (i = 0; i < shading->getNPatches(); ++i) {
1870 fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1871 start);
1872 }
1873 }
1875 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1876 GfxPatch patch00, patch01, patch10, patch11;
1877 #ifdef POPPLER_NEW_GFXPATCH
1878 GfxColor color;
1879 #endif
1880 double xx[4][8], yy[4][8];
1881 double xxm, yym;
1882 double patchColorDelta = colorDeltas[pdfPatchMeshShading-1];
1883 int i;
1885 for (i = 0; i < nComps; ++i) {
1886 if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1887 > patchColorDelta ||
1888 abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1889 > patchColorDelta ||
1890 abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1891 > patchColorDelta ||
1892 abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1893 > patchColorDelta) {
1894 break;
1895 }
1896 #ifdef POPPLER_NEW_GFXPATCH
1897 color.c[i] = GfxColorComp(patch->color[0][0].c[i]);
1898 #endif
1899 }
1900 if (i == nComps || depth == maxDepths[pdfPatchMeshShading-1]) {
1901 #ifdef POPPLER_NEW_GFXPATCH
1902 state->setFillColor(&color);
1903 #else
1904 state->setFillColor(&patch->color[0][0]);
1905 #endif
1906 state->moveTo(patch->x[0][0], patch->y[0][0]);
1907 state->curveTo(patch->x[0][1], patch->y[0][1],
1908 patch->x[0][2], patch->y[0][2],
1909 patch->x[0][3], patch->y[0][3]);
1910 state->curveTo(patch->x[1][3], patch->y[1][3],
1911 patch->x[2][3], patch->y[2][3],
1912 patch->x[3][3], patch->y[3][3]);
1913 state->curveTo(patch->x[3][2], patch->y[3][2],
1914 patch->x[3][1], patch->y[3][1],
1915 patch->x[3][0], patch->y[3][0]);
1916 state->curveTo(patch->x[2][0], patch->y[2][0],
1917 patch->x[1][0], patch->y[1][0],
1918 patch->x[0][0], patch->y[0][0]);
1919 state->closePath();
1920 builder->addPath(state, true, false);
1921 state->clearPath();
1922 } else {
1923 for (i = 0; i < 4; ++i) {
1924 xx[i][0] = patch->x[i][0];
1925 yy[i][0] = patch->y[i][0];
1926 xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1927 yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1928 xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1929 yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1930 xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1931 yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1932 xx[i][2] = 0.5 * (xx[i][1] + xxm);
1933 yy[i][2] = 0.5 * (yy[i][1] + yym);
1934 xx[i][5] = 0.5 * (xxm + xx[i][6]);
1935 yy[i][5] = 0.5 * (yym + yy[i][6]);
1936 xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1937 yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1938 xx[i][7] = patch->x[i][3];
1939 yy[i][7] = patch->y[i][3];
1940 }
1941 for (i = 0; i < 4; ++i) {
1942 patch00.x[0][i] = xx[0][i];
1943 patch00.y[0][i] = yy[0][i];
1944 patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1945 patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1946 xxm = 0.5 * (xx[1][i] + xx[2][i]);
1947 yym = 0.5 * (yy[1][i] + yy[2][i]);
1948 patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1949 patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1950 patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1951 patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1952 patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1953 patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1954 patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1955 patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1956 patch10.x[0][i] = patch00.x[3][i];
1957 patch10.y[0][i] = patch00.y[3][i];
1958 patch10.x[3][i] = xx[3][i];
1959 patch10.y[3][i] = yy[3][i];
1960 }
1961 for (i = 4; i < 8; ++i) {
1962 patch01.x[0][i-4] = xx[0][i];
1963 patch01.y[0][i-4] = yy[0][i];
1964 patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1965 patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1966 xxm = 0.5 * (xx[1][i] + xx[2][i]);
1967 yym = 0.5 * (yy[1][i] + yy[2][i]);
1968 patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1969 patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1970 patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1971 patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1972 patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1973 patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1974 patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1975 patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1976 patch11.x[0][i-4] = patch01.x[3][i-4];
1977 patch11.y[0][i-4] = patch01.y[3][i-4];
1978 patch11.x[3][i-4] = xx[3][i];
1979 patch11.y[3][i-4] = yy[3][i];
1980 }
1981 //~ if the shading has a Function, this should interpolate on the
1982 //~ function parameter, not on the color components
1983 for (i = 0; i < nComps; ++i) {
1984 patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1985 patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1986 patch->color[0][1].c[i]) / 2;
1987 patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1988 patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1989 patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1990 patch->color[1][1].c[i]) / 2;
1991 patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1992 patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1993 patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1994 patch->color[1][0].c[i]) / 2;
1995 patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1996 patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1997 patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1998 patch->color[0][0].c[i]) / 2;
1999 patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
2000 patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
2001 patch01.color[1][1].c[i]) / 2;
2002 patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
2003 patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
2004 patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
2005 }
2006 fillPatch(&patch00, nComps, depth + 1);
2007 fillPatch(&patch10, nComps, depth + 1);
2008 fillPatch(&patch01, nComps, depth + 1);
2009 fillPatch(&patch11, nComps, depth + 1);
2010 }
2011 }
2013 void PdfParser::doEndPath() {
2014 if (state->isCurPt() && clip != clipNone) {
2015 state->clip();
2016 if (clip == clipNormal) {
2017 clipHistory->setClip(state->getPath(), clipNormal);
2018 builder->clip(state);
2019 } else {
2020 clipHistory->setClip(state->getPath(), clipEO);
2021 builder->clip(state, true);
2022 }
2023 }
2024 clip = clipNone;
2025 state->clearPath();
2026 }
2028 //------------------------------------------------------------------------
2029 // path clipping operators
2030 //------------------------------------------------------------------------
2032 void PdfParser::opClip(Object args[], int numArgs) {
2033 clip = clipNormal;
2034 }
2036 void PdfParser::opEOClip(Object args[], int numArgs) {
2037 clip = clipEO;
2038 }
2040 //------------------------------------------------------------------------
2041 // text object operators
2042 //------------------------------------------------------------------------
2044 void PdfParser::opBeginText(Object args[], int numArgs) {
2045 state->setTextMat(1, 0, 0, 1, 0, 0);
2046 state->textMoveTo(0, 0);
2047 builder->updateTextPosition(0.0, 0.0);
2048 fontChanged = gTrue;
2049 builder->beginTextObject(state);
2050 }
2052 void PdfParser::opEndText(Object args[], int numArgs) {
2053 builder->endTextObject(state);
2054 }
2056 //------------------------------------------------------------------------
2057 // text state operators
2058 //------------------------------------------------------------------------
2060 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2061 state->setCharSpace(args[0].getNum());
2062 }
2064 void PdfParser::opSetFont(Object args[], int numArgs) {
2065 GfxFont *font;
2067 if (!(font = res->lookupFont(args[0].getName()))) {
2068 // unsetting the font (drawing no text) is better than using the
2069 // previous one and drawing random glyphs from it
2070 state->setFont(NULL, args[1].getNum());
2071 fontChanged = gTrue;
2072 return;
2073 }
2074 if (printCommands) {
2075 printf(" font: tag=%s name='%s' %g\n",
2076 font->getTag()->getCString(),
2077 font->getName() ? font->getName()->getCString() : "???",
2078 args[1].getNum());
2079 fflush(stdout);
2080 }
2082 font->incRefCnt();
2083 state->setFont(font, args[1].getNum());
2084 fontChanged = gTrue;
2085 }
2087 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2088 state->setLeading(args[0].getNum());
2089 }
2091 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2092 state->setRender(args[0].getInt());
2093 builder->updateStyle(state);
2094 }
2096 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2097 state->setRise(args[0].getNum());
2098 }
2100 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2101 state->setWordSpace(args[0].getNum());
2102 }
2104 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2105 state->setHorizScaling(args[0].getNum());
2106 builder->updateTextMatrix(state);
2107 fontChanged = gTrue;
2108 }
2110 //------------------------------------------------------------------------
2111 // text positioning operators
2112 //------------------------------------------------------------------------
2114 void PdfParser::opTextMove(Object args[], int numArgs) {
2115 double tx, ty;
2117 tx = state->getLineX() + args[0].getNum();
2118 ty = state->getLineY() + args[1].getNum();
2119 state->textMoveTo(tx, ty);
2120 builder->updateTextPosition(tx, ty);
2121 }
2123 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2124 double tx, ty;
2126 tx = state->getLineX() + args[0].getNum();
2127 ty = args[1].getNum();
2128 state->setLeading(-ty);
2129 ty += state->getLineY();
2130 state->textMoveTo(tx, ty);
2131 builder->updateTextPosition(tx, ty);
2132 }
2134 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2135 state->setTextMat(args[0].getNum(), args[1].getNum(),
2136 args[2].getNum(), args[3].getNum(),
2137 args[4].getNum(), args[5].getNum());
2138 state->textMoveTo(0, 0);
2139 builder->updateTextMatrix(state);
2140 builder->updateTextPosition(0.0, 0.0);
2141 fontChanged = gTrue;
2142 }
2144 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2145 double tx, ty;
2147 tx = state->getLineX();
2148 ty = state->getLineY() - state->getLeading();
2149 state->textMoveTo(tx, ty);
2150 builder->updateTextPosition(tx, ty);
2151 }
2153 //------------------------------------------------------------------------
2154 // text string operators
2155 //------------------------------------------------------------------------
2157 void PdfParser::opShowText(Object args[], int numArgs) {
2158 if (!state->getFont()) {
2159 error(getPos(), const_cast<char*>("No font in show"));
2160 return;
2161 }
2162 if (fontChanged) {
2163 builder->updateFont(state);
2164 fontChanged = gFalse;
2165 }
2166 doShowText(args[0].getString());
2167 }
2169 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2170 double tx, ty;
2172 if (!state->getFont()) {
2173 error(getPos(), const_cast<char*>("No font in move/show"));
2174 return;
2175 }
2176 if (fontChanged) {
2177 builder->updateFont(state);
2178 fontChanged = gFalse;
2179 }
2180 tx = state->getLineX();
2181 ty = state->getLineY() - state->getLeading();
2182 state->textMoveTo(tx, ty);
2183 builder->updateTextPosition(tx, ty);
2184 doShowText(args[0].getString());
2185 }
2187 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2188 double tx, ty;
2190 if (!state->getFont()) {
2191 error(getPos(), const_cast<char*>("No font in move/set/show"));
2192 return;
2193 }
2194 if (fontChanged) {
2195 builder->updateFont(state);
2196 fontChanged = gFalse;
2197 }
2198 state->setWordSpace(args[0].getNum());
2199 state->setCharSpace(args[1].getNum());
2200 tx = state->getLineX();
2201 ty = state->getLineY() - state->getLeading();
2202 state->textMoveTo(tx, ty);
2203 builder->updateTextPosition(tx, ty);
2204 doShowText(args[2].getString());
2205 }
2207 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2208 Array *a;
2209 Object obj;
2210 int wMode;
2211 int i;
2213 if (!state->getFont()) {
2214 error(getPos(), const_cast<char*>("No font in show/space"));
2215 return;
2216 }
2217 if (fontChanged) {
2218 builder->updateFont(state);
2219 fontChanged = gFalse;
2220 }
2221 wMode = state->getFont()->getWMode();
2222 a = args[0].getArray();
2223 for (i = 0; i < a->getLength(); ++i) {
2224 a->get(i, &obj);
2225 if (obj.isNum()) {
2226 // this uses the absolute value of the font size to match
2227 // Acrobat's behavior
2228 if (wMode) {
2229 state->textShift(0, -obj.getNum() * 0.001 *
2230 fabs(state->getFontSize()));
2231 } else {
2232 state->textShift(-obj.getNum() * 0.001 *
2233 fabs(state->getFontSize()), 0);
2234 }
2235 builder->updateTextShift(state, obj.getNum());
2236 } else if (obj.isString()) {
2237 doShowText(obj.getString());
2238 } else {
2239 error(getPos(), const_cast<char*>("Element of show/space array must be number or string"));
2240 }
2241 obj.free();
2242 }
2243 }
2247 /*
2248 * The `POPPLER_NEW_GFXFONT' stuff is for the change to GfxFont's getNextChar() call.
2249 * Thanks to tsdgeos for the fix.
2250 * Miklos, does this look ok?
2251 */
2253 void PdfParser::doShowText(GooString *s) {
2254 GfxFont *font;
2255 int wMode;
2256 double riseX, riseY;
2257 CharCode code;
2258 #ifdef POPPLER_NEW_GFXFONT
2259 Unicode *u = NULL;
2260 #else
2261 Unicode u[8];
2262 #endif
2263 double x, y, dx, dy, curX, curY, tdx, tdy, lineX, lineY;
2264 double originX, originY, tOriginX, tOriginY;
2265 double oldCTM[6], newCTM[6];
2266 double *mat;
2267 Object charProc;
2268 Dict *resDict;
2269 Parser *oldParser;
2270 char *p;
2271 int len, n, uLen, i;
2273 font = state->getFont();
2274 wMode = font->getWMode();
2276 builder->beginString(state, s);
2278 // handle a Type 3 char
2279 if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2280 mat = state->getCTM();
2281 for (i = 0; i < 6; ++i) {
2282 oldCTM[i] = mat[i];
2283 }
2284 mat = state->getTextMat();
2285 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2286 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2287 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2288 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2289 mat = font->getFontMatrix();
2290 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2291 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2292 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2293 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2294 newCTM[0] *= state->getFontSize();
2295 newCTM[1] *= state->getFontSize();
2296 newCTM[2] *= state->getFontSize();
2297 newCTM[3] *= state->getFontSize();
2298 newCTM[0] *= state->getHorizScaling();
2299 newCTM[2] *= state->getHorizScaling();
2300 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2301 curX = state->getCurX();
2302 curY = state->getCurY();
2303 lineX = state->getLineX();
2304 lineY = state->getLineY();
2305 oldParser = parser;
2306 p = s->getCString();
2307 len = s->getLength();
2308 while (len > 0) {
2309 n = font->getNextChar(p, len, &code,
2310 #ifdef POPPLER_NEW_GFXFONT
2311 &u, &uLen, /* TODO: This looks like a memory leak for u. */
2312 #else
2313 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2314 #endif
2315 &dx, &dy, &originX, &originY);
2316 dx = dx * state->getFontSize() + state->getCharSpace();
2317 if (n == 1 && *p == ' ') {
2318 dx += state->getWordSpace();
2319 }
2320 dx *= state->getHorizScaling();
2321 dy *= state->getFontSize();
2322 state->textTransformDelta(dx, dy, &tdx, &tdy);
2323 state->transform(curX + riseX, curY + riseY, &x, &y);
2324 saveState();
2325 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2326 //~ the CTM concat values here are wrong (but never used)
2327 //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2328 if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2329 code, u, uLen)) {*/
2330 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2331 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2332 pushResources(resDict);
2333 }
2334 if (charProc.isStream()) {
2335 //parse(&charProc, gFalse); // TODO: parse into SVG font
2336 } else {
2337 error(getPos(), const_cast<char*>("Missing or bad Type3 CharProc entry"));
2338 }
2339 //out->endType3Char(state);
2340 if (resDict) {
2341 popResources();
2342 }
2343 charProc.free();
2344 }
2345 restoreState();
2346 // GfxState::restore() does *not* restore the current position,
2347 // so we deal with it here using (curX, curY) and (lineX, lineY)
2348 curX += tdx;
2349 curY += tdy;
2350 state->moveTo(curX, curY);
2351 state->textSetPos(lineX, lineY);
2352 p += n;
2353 len -= n;
2354 }
2355 parser = oldParser;
2357 } else {
2358 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2359 p = s->getCString();
2360 len = s->getLength();
2361 while (len > 0) {
2362 n = font->getNextChar(p, len, &code,
2363 #ifdef POPPLER_NEW_GFXFONT
2364 &u, &uLen, /* TODO: This looks like a memory leak for u. */
2365 #else
2366 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2367 #endif
2368 &dx, &dy, &originX, &originY);
2370 if (wMode) {
2371 dx *= state->getFontSize();
2372 dy = dy * state->getFontSize() + state->getCharSpace();
2373 if (n == 1 && *p == ' ') {
2374 dy += state->getWordSpace();
2375 }
2376 } else {
2377 dx = dx * state->getFontSize() + state->getCharSpace();
2378 if (n == 1 && *p == ' ') {
2379 dx += state->getWordSpace();
2380 }
2381 dx *= state->getHorizScaling();
2382 dy *= state->getFontSize();
2383 }
2384 state->textTransformDelta(dx, dy, &tdx, &tdy);
2385 originX *= state->getFontSize();
2386 originY *= state->getFontSize();
2387 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2388 builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2389 dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2390 state->shift(tdx, tdy);
2391 p += n;
2392 len -= n;
2393 }
2394 }
2396 builder->endString(state);
2397 }
2400 //------------------------------------------------------------------------
2401 // XObject operators
2402 //------------------------------------------------------------------------
2404 void PdfParser::opXObject(Object args[], int numArgs) {
2405 char *name;
2406 Object obj1, obj2, obj3, refObj;
2408 name = args[0].getName();
2409 if (!res->lookupXObject(name, &obj1)) {
2410 return;
2411 }
2412 if (!obj1.isStream()) {
2413 error(getPos(), const_cast<char*>("XObject '%s' is wrong type"), name);
2414 obj1.free();
2415 return;
2416 }
2417 obj1.streamGetDict()->lookup(const_cast<char*>("Subtype"), &obj2);
2418 if (obj2.isName(const_cast<char*>("Image"))) {
2419 res->lookupXObjectNF(name, &refObj);
2420 doImage(&refObj, obj1.getStream(), gFalse);
2421 refObj.free();
2422 } else if (obj2.isName(const_cast<char*>("Form"))) {
2423 doForm(&obj1);
2424 } else if (obj2.isName(const_cast<char*>("PS"))) {
2425 obj1.streamGetDict()->lookup(const_cast<char*>("Level1"), &obj3);
2426 /* out->psXObject(obj1.getStream(),
2427 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2428 } else if (obj2.isName()) {
2429 error(getPos(), const_cast<char*>("Unknown XObject subtype '%s'"), obj2.getName());
2430 } else {
2431 error(getPos(), const_cast<char*>("XObject subtype is missing or wrong type"));
2432 }
2433 obj2.free();
2434 obj1.free();
2435 }
2437 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2438 Dict *dict, *maskDict;
2439 int width, height;
2440 int bits, maskBits;
2441 StreamColorSpaceMode csMode;
2442 GBool mask;
2443 GBool invert;
2444 GfxColorSpace *colorSpace, *maskColorSpace;
2445 GfxImageColorMap *colorMap, *maskColorMap;
2446 Object maskObj, smaskObj;
2447 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2448 int maskColors[2*gfxColorMaxComps];
2449 int maskWidth, maskHeight;
2450 GBool maskInvert;
2451 Stream *maskStr;
2452 Object obj1, obj2;
2453 int i;
2455 // get info from the stream
2456 bits = 0;
2457 csMode = streamCSNone;
2458 str->getImageParams(&bits, &csMode);
2460 // get stream dict
2461 dict = str->getDict();
2463 // get size
2464 dict->lookup(const_cast<char*>("Width"), &obj1);
2465 if (obj1.isNull()) {
2466 obj1.free();
2467 dict->lookup(const_cast<char*>("W"), &obj1);
2468 }
2469 if (obj1.isInt())
2470 width = obj1.getInt();
2471 else if (obj1.isReal())
2472 width = (int)obj1.getReal();
2473 else
2474 goto err2;
2475 obj1.free();
2476 dict->lookup(const_cast<char*>("Height"), &obj1);
2477 if (obj1.isNull()) {
2478 obj1.free();
2479 dict->lookup(const_cast<char*>("H"), &obj1);
2480 }
2481 if (obj1.isInt())
2482 height = obj1.getInt();
2483 else if (obj1.isReal())
2484 height = (int)obj1.getReal();
2485 else
2486 goto err2;
2487 obj1.free();
2489 // image or mask?
2490 dict->lookup(const_cast<char*>("ImageMask"), &obj1);
2491 if (obj1.isNull()) {
2492 obj1.free();
2493 dict->lookup(const_cast<char*>("IM"), &obj1);
2494 }
2495 mask = gFalse;
2496 if (obj1.isBool())
2497 mask = obj1.getBool();
2498 else if (!obj1.isNull())
2499 goto err2;
2500 obj1.free();
2502 // bit depth
2503 if (bits == 0) {
2504 dict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2505 if (obj1.isNull()) {
2506 obj1.free();
2507 dict->lookup(const_cast<char*>("BPC"), &obj1);
2508 }
2509 if (obj1.isInt()) {
2510 bits = obj1.getInt();
2511 } else if (mask) {
2512 bits = 1;
2513 } else {
2514 goto err2;
2515 }
2516 obj1.free();
2517 }
2519 // display a mask
2520 if (mask) {
2522 // check for inverted mask
2523 if (bits != 1)
2524 goto err1;
2525 invert = gFalse;
2526 dict->lookup(const_cast<char*>("Decode"), &obj1);
2527 if (obj1.isNull()) {
2528 obj1.free();
2529 dict->lookup(const_cast<char*>("D"), &obj1);
2530 }
2531 if (obj1.isArray()) {
2532 obj1.arrayGet(0, &obj2);
2533 if (obj2.isInt() && obj2.getInt() == 1)
2534 invert = gTrue;
2535 obj2.free();
2536 } else if (!obj1.isNull()) {
2537 goto err2;
2538 }
2539 obj1.free();
2541 // draw it
2542 builder->addImageMask(state, str, width, height, invert);
2544 } else {
2546 // get color space and color map
2547 dict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2548 if (obj1.isNull()) {
2549 obj1.free();
2550 dict->lookup(const_cast<char*>("CS"), &obj1);
2551 }
2552 if (obj1.isName()) {
2553 res->lookupColorSpace(obj1.getName(), &obj2);
2554 if (!obj2.isNull()) {
2555 obj1.free();
2556 obj1 = obj2;
2557 } else {
2558 obj2.free();
2559 }
2560 }
2561 if (!obj1.isNull()) {
2562 #ifdef POPPLER_NEW_COLOR_SPACE_API
2563 colorSpace = GfxColorSpace::parse(&obj1, NULL);
2564 #else
2565 colorSpace = GfxColorSpace::parse(&obj1);
2566 #endif
2567 } else if (csMode == streamCSDeviceGray) {
2568 colorSpace = new GfxDeviceGrayColorSpace();
2569 } else if (csMode == streamCSDeviceRGB) {
2570 colorSpace = new GfxDeviceRGBColorSpace();
2571 } else if (csMode == streamCSDeviceCMYK) {
2572 colorSpace = new GfxDeviceCMYKColorSpace();
2573 } else {
2574 colorSpace = NULL;
2575 }
2576 obj1.free();
2577 if (!colorSpace) {
2578 goto err1;
2579 }
2580 dict->lookup(const_cast<char*>("Decode"), &obj1);
2581 if (obj1.isNull()) {
2582 obj1.free();
2583 dict->lookup(const_cast<char*>("D"), &obj1);
2584 }
2585 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2586 obj1.free();
2587 if (!colorMap->isOk()) {
2588 delete colorMap;
2589 goto err1;
2590 }
2592 // get the mask
2593 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2594 maskStr = NULL; // make gcc happy
2595 maskWidth = maskHeight = 0; // make gcc happy
2596 maskInvert = gFalse; // make gcc happy
2597 maskColorMap = NULL; // make gcc happy
2598 dict->lookup(const_cast<char*>("Mask"), &maskObj);
2599 dict->lookup(const_cast<char*>("SMask"), &smaskObj);
2600 if (smaskObj.isStream()) {
2601 // soft mask
2602 if (inlineImg) {
2603 goto err1;
2604 }
2605 maskStr = smaskObj.getStream();
2606 maskDict = smaskObj.streamGetDict();
2607 maskDict->lookup(const_cast<char*>("Width"), &obj1);
2608 if (obj1.isNull()) {
2609 obj1.free();
2610 maskDict->lookup(const_cast<char*>("W"), &obj1);
2611 }
2612 if (!obj1.isInt()) {
2613 goto err2;
2614 }
2615 maskWidth = obj1.getInt();
2616 obj1.free();
2617 maskDict->lookup(const_cast<char*>("Height"), &obj1);
2618 if (obj1.isNull()) {
2619 obj1.free();
2620 maskDict->lookup(const_cast<char*>("H"), &obj1);
2621 }
2622 if (!obj1.isInt()) {
2623 goto err2;
2624 }
2625 maskHeight = obj1.getInt();
2626 obj1.free();
2627 maskDict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2628 if (obj1.isNull()) {
2629 obj1.free();
2630 maskDict->lookup(const_cast<char*>("BPC"), &obj1);
2631 }
2632 if (!obj1.isInt()) {
2633 goto err2;
2634 }
2635 maskBits = obj1.getInt();
2636 obj1.free();
2637 maskDict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2638 if (obj1.isNull()) {
2639 obj1.free();
2640 maskDict->lookup(const_cast<char*>("CS"), &obj1);
2641 }
2642 if (obj1.isName()) {
2643 res->lookupColorSpace(obj1.getName(), &obj2);
2644 if (!obj2.isNull()) {
2645 obj1.free();
2646 obj1 = obj2;
2647 } else {
2648 obj2.free();
2649 }
2650 }
2651 #ifdef POPPLER_NEW_COLOR_SPACE_API
2652 maskColorSpace = GfxColorSpace::parse(&obj1, NULL);
2653 #else
2654 maskColorSpace = GfxColorSpace::parse(&obj1);
2655 #endif
2656 obj1.free();
2657 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2658 goto err1;
2659 }
2660 maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2661 if (obj1.isNull()) {
2662 obj1.free();
2663 maskDict->lookup(const_cast<char*>("D"), &obj1);
2664 }
2665 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2666 obj1.free();
2667 if (!maskColorMap->isOk()) {
2668 delete maskColorMap;
2669 goto err1;
2670 }
2671 //~ handle the Matte entry
2672 haveSoftMask = gTrue;
2673 } else if (maskObj.isArray()) {
2674 // color key mask
2675 for (i = 0;
2676 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2677 ++i) {
2678 maskObj.arrayGet(i, &obj1);
2679 maskColors[i] = obj1.getInt();
2680 obj1.free();
2681 }
2682 haveColorKeyMask = gTrue;
2683 } else if (maskObj.isStream()) {
2684 // explicit mask
2685 if (inlineImg) {
2686 goto err1;
2687 }
2688 maskStr = maskObj.getStream();
2689 maskDict = maskObj.streamGetDict();
2690 maskDict->lookup(const_cast<char*>("Width"), &obj1);
2691 if (obj1.isNull()) {
2692 obj1.free();
2693 maskDict->lookup(const_cast<char*>("W"), &obj1);
2694 }
2695 if (!obj1.isInt()) {
2696 goto err2;
2697 }
2698 maskWidth = obj1.getInt();
2699 obj1.free();
2700 maskDict->lookup(const_cast<char*>("Height"), &obj1);
2701 if (obj1.isNull()) {
2702 obj1.free();
2703 maskDict->lookup(const_cast<char*>("H"), &obj1);
2704 }
2705 if (!obj1.isInt()) {
2706 goto err2;
2707 }
2708 maskHeight = obj1.getInt();
2709 obj1.free();
2710 maskDict->lookup(const_cast<char*>("ImageMask"), &obj1);
2711 if (obj1.isNull()) {
2712 obj1.free();
2713 maskDict->lookup(const_cast<char*>("IM"), &obj1);
2714 }
2715 if (!obj1.isBool() || !obj1.getBool()) {
2716 goto err2;
2717 }
2718 obj1.free();
2719 maskInvert = gFalse;
2720 maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2721 if (obj1.isNull()) {
2722 obj1.free();
2723 maskDict->lookup(const_cast<char*>("D"), &obj1);
2724 }
2725 if (obj1.isArray()) {
2726 obj1.arrayGet(0, &obj2);
2727 if (obj2.isInt() && obj2.getInt() == 1) {
2728 maskInvert = gTrue;
2729 }
2730 obj2.free();
2731 } else if (!obj1.isNull()) {
2732 goto err2;
2733 }
2734 obj1.free();
2735 haveExplicitMask = gTrue;
2736 }
2738 // draw it
2739 if (haveSoftMask) {
2740 builder->addSoftMaskedImage(state, str, width, height, colorMap,
2741 maskStr, maskWidth, maskHeight, maskColorMap);
2742 delete maskColorMap;
2743 } else if (haveExplicitMask) {
2744 builder->addMaskedImage(state, str, width, height, colorMap,
2745 maskStr, maskWidth, maskHeight, maskInvert);
2746 } else {
2747 builder->addImage(state, str, width, height, colorMap,
2748 haveColorKeyMask ? maskColors : (int *)NULL);
2749 }
2750 delete colorMap;
2752 maskObj.free();
2753 smaskObj.free();
2754 }
2756 return;
2758 err2:
2759 obj1.free();
2760 err1:
2761 error(getPos(), const_cast<char*>("Bad image parameters"));
2762 }
2764 void PdfParser::doForm(Object *str) {
2765 Dict *dict;
2766 GBool transpGroup, isolated, knockout;
2767 GfxColorSpace *blendingColorSpace;
2768 Object matrixObj, bboxObj;
2769 double m[6], bbox[4];
2770 Object resObj;
2771 Dict *resDict;
2772 Object obj1, obj2, obj3;
2773 int i;
2775 // check for excessive recursion
2776 if (formDepth > 20) {
2777 return;
2778 }
2780 // get stream dict
2781 dict = str->streamGetDict();
2783 // check form type
2784 dict->lookup(const_cast<char*>("FormType"), &obj1);
2785 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2786 error(getPos(), const_cast<char*>("Unknown form type"));
2787 }
2788 obj1.free();
2790 // get bounding box
2791 dict->lookup(const_cast<char*>("BBox"), &bboxObj);
2792 if (!bboxObj.isArray()) {
2793 bboxObj.free();
2794 error(getPos(), const_cast<char*>("Bad form bounding box"));
2795 return;
2796 }
2797 for (i = 0; i < 4; ++i) {
2798 bboxObj.arrayGet(i, &obj1);
2799 bbox[i] = obj1.getNum();
2800 obj1.free();
2801 }
2802 bboxObj.free();
2804 // get matrix
2805 dict->lookup(const_cast<char*>("Matrix"), &matrixObj);
2806 if (matrixObj.isArray()) {
2807 for (i = 0; i < 6; ++i) {
2808 matrixObj.arrayGet(i, &obj1);
2809 m[i] = obj1.getNum();
2810 obj1.free();
2811 }
2812 } else {
2813 m[0] = 1; m[1] = 0;
2814 m[2] = 0; m[3] = 1;
2815 m[4] = 0; m[5] = 0;
2816 }
2817 matrixObj.free();
2819 // get resources
2820 dict->lookup(const_cast<char*>("Resources"), &resObj);
2821 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2823 // check for a transparency group
2824 transpGroup = isolated = knockout = gFalse;
2825 blendingColorSpace = NULL;
2826 if (dict->lookup(const_cast<char*>("Group"), &obj1)->isDict()) {
2827 if (obj1.dictLookup(const_cast<char*>("S"), &obj2)->isName(const_cast<char*>("Transparency"))) {
2828 transpGroup = gTrue;
2829 if (!obj1.dictLookup(const_cast<char*>("CS"), &obj3)->isNull()) {
2830 #ifdef POPPLER_NEW_COLOR_SPACE_API
2831 blendingColorSpace = GfxColorSpace::parse(&obj3, NULL);
2832 #else
2833 blendingColorSpace = GfxColorSpace::parse(&obj3);
2834 #endif
2835 }
2836 obj3.free();
2837 if (obj1.dictLookup(const_cast<char*>("I"), &obj3)->isBool()) {
2838 isolated = obj3.getBool();
2839 }
2840 obj3.free();
2841 if (obj1.dictLookup(const_cast<char*>("K"), &obj3)->isBool()) {
2842 knockout = obj3.getBool();
2843 }
2844 obj3.free();
2845 }
2846 obj2.free();
2847 }
2848 obj1.free();
2850 // draw it
2851 ++formDepth;
2852 doForm1(str, resDict, m, bbox,
2853 transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2854 --formDepth;
2856 if (blendingColorSpace) {
2857 delete blendingColorSpace;
2858 }
2859 resObj.free();
2860 }
2862 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2863 GBool transpGroup, GBool softMask,
2864 GfxColorSpace *blendingColorSpace,
2865 GBool isolated, GBool knockout,
2866 GBool alpha, Function *transferFunc,
2867 GfxColor *backdropColor) {
2868 Parser *oldParser;
2869 double oldBaseMatrix[6];
2870 int i;
2872 // push new resources on stack
2873 pushResources(resDict);
2875 // save current graphics state
2876 saveState();
2878 // kill any pre-existing path
2879 state->clearPath();
2881 if (softMask || transpGroup) {
2882 builder->clearSoftMask(state);
2883 builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2884 isolated, knockout, softMask);
2885 }
2887 // save current parser
2888 oldParser = parser;
2890 // set form transformation matrix
2891 state->concatCTM(matrix[0], matrix[1], matrix[2],
2892 matrix[3], matrix[4], matrix[5]);
2893 builder->setTransform(matrix[0], matrix[1], matrix[2],
2894 matrix[3], matrix[4], matrix[5]);
2896 // set form bounding box
2897 state->moveTo(bbox[0], bbox[1]);
2898 state->lineTo(bbox[2], bbox[1]);
2899 state->lineTo(bbox[2], bbox[3]);
2900 state->lineTo(bbox[0], bbox[3]);
2901 state->closePath();
2902 state->clip();
2903 clipHistory->setClip(state->getPath());
2904 builder->clip(state);
2905 state->clearPath();
2907 if (softMask || transpGroup) {
2908 if (state->getBlendMode() != gfxBlendNormal) {
2909 state->setBlendMode(gfxBlendNormal);
2910 }
2911 if (state->getFillOpacity() != 1) {
2912 builder->setGroupOpacity(state->getFillOpacity());
2913 state->setFillOpacity(1);
2914 }
2915 if (state->getStrokeOpacity() != 1) {
2916 state->setStrokeOpacity(1);
2917 }
2918 }
2920 // set new base matrix
2921 for (i = 0; i < 6; ++i) {
2922 oldBaseMatrix[i] = baseMatrix[i];
2923 baseMatrix[i] = state->getCTM()[i];
2924 }
2926 // draw the form
2927 parse(str, gFalse);
2929 // restore base matrix
2930 for (i = 0; i < 6; ++i) {
2931 baseMatrix[i] = oldBaseMatrix[i];
2932 }
2934 // restore parser
2935 parser = oldParser;
2937 if (softMask || transpGroup) {
2938 builder->popTransparencyGroup(state);
2939 }
2941 // restore graphics state
2942 restoreState();
2944 // pop resource stack
2945 popResources();
2947 if (softMask) {
2948 builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2949 } else if (transpGroup) {
2950 builder->paintTransparencyGroup(state, bbox);
2951 }
2953 return;
2954 }
2956 //------------------------------------------------------------------------
2957 // in-line image operators
2958 //------------------------------------------------------------------------
2960 void PdfParser::opBeginImage(Object args[], int numArgs) {
2961 Stream *str;
2962 int c1, c2;
2964 // build dict/stream
2965 str = buildImageStream();
2967 // display the image
2968 if (str) {
2969 doImage(NULL, str, gTrue);
2971 // skip 'EI' tag
2972 c1 = str->getUndecodedStream()->getChar();
2973 c2 = str->getUndecodedStream()->getChar();
2974 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2975 c1 = c2;
2976 c2 = str->getUndecodedStream()->getChar();
2977 }
2978 delete str;
2979 }
2980 }
2982 Stream *PdfParser::buildImageStream() {
2983 Object dict;
2984 Object obj;
2985 char *key;
2986 Stream *str;
2988 // build dictionary
2989 dict.initDict(xref);
2990 parser->getObj(&obj);
2991 while (!obj.isCmd(const_cast<char*>("ID")) && !obj.isEOF()) {
2992 if (!obj.isName()) {
2993 error(getPos(), const_cast<char*>("Inline image dictionary key must be a name object"));
2994 obj.free();
2995 } else {
2996 key = copyString(obj.getName());
2997 obj.free();
2998 parser->getObj(&obj);
2999 if (obj.isEOF() || obj.isError()) {
3000 gfree(key);
3001 break;
3002 }
3003 dict.dictAdd(key, &obj);
3004 }
3005 parser->getObj(&obj);
3006 }
3007 if (obj.isEOF()) {
3008 error(getPos(), const_cast<char*>("End of file in inline image"));
3009 obj.free();
3010 dict.free();
3011 return NULL;
3012 }
3013 obj.free();
3015 // make stream
3016 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
3017 str = str->addFilters(&dict);
3019 return str;
3020 }
3022 void PdfParser::opImageData(Object args[], int numArgs) {
3023 error(getPos(), const_cast<char*>("Internal: got 'ID' operator"));
3024 }
3026 void PdfParser::opEndImage(Object args[], int numArgs) {
3027 error(getPos(), const_cast<char*>("Internal: got 'EI' operator"));
3028 }
3030 //------------------------------------------------------------------------
3031 // type 3 font operators
3032 //------------------------------------------------------------------------
3034 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
3035 }
3037 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
3038 }
3040 //------------------------------------------------------------------------
3041 // compatibility operators
3042 //------------------------------------------------------------------------
3044 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
3045 ++ignoreUndef;
3046 }
3048 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
3049 if (ignoreUndef > 0)
3050 --ignoreUndef;
3051 }
3053 //------------------------------------------------------------------------
3054 // marked content operators
3055 //------------------------------------------------------------------------
3057 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
3058 if (printCommands) {
3059 printf(" marked content: %s ", args[0].getName());
3060 if (numArgs == 2)
3061 args[2].print(stdout);
3062 printf("\n");
3063 fflush(stdout);
3064 }
3066 if(numArgs == 2) {
3067 //out->beginMarkedContent(args[0].getName(),args[1].getDict());
3068 } else {
3069 //out->beginMarkedContent(args[0].getName());
3070 }
3071 }
3073 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3074 //out->endMarkedContent();
3075 }
3077 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3078 if (printCommands) {
3079 printf(" mark point: %s ", args[0].getName());
3080 if (numArgs == 2)
3081 args[2].print(stdout);
3082 printf("\n");
3083 fflush(stdout);
3084 }
3086 if(numArgs == 2) {
3087 //out->markPoint(args[0].getName(),args[1].getDict());
3088 } else {
3089 //out->markPoint(args[0].getName());
3090 }
3092 }
3094 //------------------------------------------------------------------------
3095 // misc
3096 //------------------------------------------------------------------------
3098 void PdfParser::saveState() {
3099 builder->saveState();
3100 state = state->save();
3101 clipHistory = clipHistory->save();
3102 }
3104 void PdfParser::restoreState() {
3105 clipHistory = clipHistory->restore();
3106 state = state->restore();
3107 builder->restoreState();
3108 }
3110 void PdfParser::pushResources(Dict *resDict) {
3111 res = new GfxResources(xref, resDict, res);
3112 }
3114 void PdfParser::popResources() {
3115 GfxResources *resPtr;
3117 resPtr = res->getNext();
3118 delete res;
3119 res = resPtr;
3120 }
3122 void PdfParser::setDefaultApproximationPrecision() {
3123 int i;
3125 for (i = 1; i <= pdfNumShadingTypes; ++i) {
3126 setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3127 }
3128 }
3130 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3131 int maxDepth) {
3133 if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3134 return;
3135 }
3136 colorDeltas[shadingType-1] = dblToCol(colorDelta);
3137 maxDepths[shadingType-1] = maxDepth;
3138 }
3140 //------------------------------------------------------------------------
3141 // ClipHistoryEntry
3142 //------------------------------------------------------------------------
3144 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3145 if (clipPathA) {
3146 clipPath = clipPathA->copy();
3147 } else {
3148 clipPath = NULL;
3149 }
3150 clipType = clipTypeA;
3151 saved = NULL;
3152 }
3154 ClipHistoryEntry::~ClipHistoryEntry() {
3155 if (clipPath) {
3156 delete clipPath;
3157 }
3158 }
3160 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3161 // Free previous clip path
3162 if (clipPath) {
3163 delete clipPath;
3164 }
3165 if (clipPathA) {
3166 clipPath = clipPathA->copy();
3167 clipType = clipTypeA;
3168 } else {
3169 clipPath = NULL;
3170 }
3171 }
3173 ClipHistoryEntry *ClipHistoryEntry::save() {
3174 ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3175 newEntry->saved = this;
3177 return newEntry;
3178 }
3180 ClipHistoryEntry *ClipHistoryEntry::restore() {
3181 ClipHistoryEntry *oldEntry;
3183 if (saved) {
3184 oldEntry = saved;
3185 saved = NULL;
3186 delete this;
3187 } else {
3188 oldEntry = this;
3189 }
3191 return oldEntry;
3192 }
3194 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3195 if (other->clipPath) {
3196 this->clipPath = other->clipPath->copy();
3197 this->clipType = other->clipType;
3198 } else {
3199 this->clipPath = NULL;
3200 }
3201 saved = NULL;
3202 }
3204 #endif /* HAVE_POPPLER */