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 double xx[4][8], yy[4][8];
1878 double xxm, yym;
1879 double patchColorDelta = colorDeltas[pdfPatchMeshShading-1];
1880 int i;
1882 for (i = 0; i < nComps; ++i) {
1883 if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1884 > patchColorDelta ||
1885 abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1886 > patchColorDelta ||
1887 abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1888 > patchColorDelta ||
1889 abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1890 > patchColorDelta) {
1891 break;
1892 }
1893 }
1894 if (i == nComps || depth == maxDepths[pdfPatchMeshShading-1]) {
1895 state->setFillColor(&patch->color[0][0]);
1896 state->moveTo(patch->x[0][0], patch->y[0][0]);
1897 state->curveTo(patch->x[0][1], patch->y[0][1],
1898 patch->x[0][2], patch->y[0][2],
1899 patch->x[0][3], patch->y[0][3]);
1900 state->curveTo(patch->x[1][3], patch->y[1][3],
1901 patch->x[2][3], patch->y[2][3],
1902 patch->x[3][3], patch->y[3][3]);
1903 state->curveTo(patch->x[3][2], patch->y[3][2],
1904 patch->x[3][1], patch->y[3][1],
1905 patch->x[3][0], patch->y[3][0]);
1906 state->curveTo(patch->x[2][0], patch->y[2][0],
1907 patch->x[1][0], patch->y[1][0],
1908 patch->x[0][0], patch->y[0][0]);
1909 state->closePath();
1910 builder->addPath(state, true, false);
1911 state->clearPath();
1912 } else {
1913 for (i = 0; i < 4; ++i) {
1914 xx[i][0] = patch->x[i][0];
1915 yy[i][0] = patch->y[i][0];
1916 xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1917 yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1918 xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1919 yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1920 xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1921 yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1922 xx[i][2] = 0.5 * (xx[i][1] + xxm);
1923 yy[i][2] = 0.5 * (yy[i][1] + yym);
1924 xx[i][5] = 0.5 * (xxm + xx[i][6]);
1925 yy[i][5] = 0.5 * (yym + yy[i][6]);
1926 xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1927 yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1928 xx[i][7] = patch->x[i][3];
1929 yy[i][7] = patch->y[i][3];
1930 }
1931 for (i = 0; i < 4; ++i) {
1932 patch00.x[0][i] = xx[0][i];
1933 patch00.y[0][i] = yy[0][i];
1934 patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1935 patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1936 xxm = 0.5 * (xx[1][i] + xx[2][i]);
1937 yym = 0.5 * (yy[1][i] + yy[2][i]);
1938 patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1939 patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1940 patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1941 patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1942 patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1943 patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1944 patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1945 patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1946 patch10.x[0][i] = patch00.x[3][i];
1947 patch10.y[0][i] = patch00.y[3][i];
1948 patch10.x[3][i] = xx[3][i];
1949 patch10.y[3][i] = yy[3][i];
1950 }
1951 for (i = 4; i < 8; ++i) {
1952 patch01.x[0][i-4] = xx[0][i];
1953 patch01.y[0][i-4] = yy[0][i];
1954 patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1955 patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1956 xxm = 0.5 * (xx[1][i] + xx[2][i]);
1957 yym = 0.5 * (yy[1][i] + yy[2][i]);
1958 patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1959 patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1960 patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1961 patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1962 patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1963 patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1964 patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1965 patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1966 patch11.x[0][i-4] = patch01.x[3][i-4];
1967 patch11.y[0][i-4] = patch01.y[3][i-4];
1968 patch11.x[3][i-4] = xx[3][i];
1969 patch11.y[3][i-4] = yy[3][i];
1970 }
1971 //~ if the shading has a Function, this should interpolate on the
1972 //~ function parameter, not on the color components
1973 for (i = 0; i < nComps; ++i) {
1974 patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1975 patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1976 patch->color[0][1].c[i]) / 2;
1977 patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1978 patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1979 patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1980 patch->color[1][1].c[i]) / 2;
1981 patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1982 patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1983 patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1984 patch->color[1][0].c[i]) / 2;
1985 patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1986 patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1987 patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1988 patch->color[0][0].c[i]) / 2;
1989 patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1990 patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1991 patch01.color[1][1].c[i]) / 2;
1992 patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1993 patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1994 patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1995 }
1996 fillPatch(&patch00, nComps, depth + 1);
1997 fillPatch(&patch10, nComps, depth + 1);
1998 fillPatch(&patch01, nComps, depth + 1);
1999 fillPatch(&patch11, nComps, depth + 1);
2000 }
2001 }
2003 void PdfParser::doEndPath() {
2004 if (state->isCurPt() && clip != clipNone) {
2005 state->clip();
2006 if (clip == clipNormal) {
2007 clipHistory->setClip(state->getPath(), clipNormal);
2008 builder->clip(state);
2009 } else {
2010 clipHistory->setClip(state->getPath(), clipEO);
2011 builder->clip(state, true);
2012 }
2013 }
2014 clip = clipNone;
2015 state->clearPath();
2016 }
2018 //------------------------------------------------------------------------
2019 // path clipping operators
2020 //------------------------------------------------------------------------
2022 void PdfParser::opClip(Object args[], int numArgs) {
2023 clip = clipNormal;
2024 }
2026 void PdfParser::opEOClip(Object args[], int numArgs) {
2027 clip = clipEO;
2028 }
2030 //------------------------------------------------------------------------
2031 // text object operators
2032 //------------------------------------------------------------------------
2034 void PdfParser::opBeginText(Object args[], int numArgs) {
2035 state->setTextMat(1, 0, 0, 1, 0, 0);
2036 state->textMoveTo(0, 0);
2037 builder->updateTextPosition(0.0, 0.0);
2038 fontChanged = gTrue;
2039 builder->beginTextObject(state);
2040 }
2042 void PdfParser::opEndText(Object args[], int numArgs) {
2043 builder->endTextObject(state);
2044 }
2046 //------------------------------------------------------------------------
2047 // text state operators
2048 //------------------------------------------------------------------------
2050 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
2051 state->setCharSpace(args[0].getNum());
2052 }
2054 void PdfParser::opSetFont(Object args[], int numArgs) {
2055 GfxFont *font;
2057 if (!(font = res->lookupFont(args[0].getName()))) {
2058 // unsetting the font (drawing no text) is better than using the
2059 // previous one and drawing random glyphs from it
2060 state->setFont(NULL, args[1].getNum());
2061 fontChanged = gTrue;
2062 return;
2063 }
2064 if (printCommands) {
2065 printf(" font: tag=%s name='%s' %g\n",
2066 font->getTag()->getCString(),
2067 font->getName() ? font->getName()->getCString() : "???",
2068 args[1].getNum());
2069 fflush(stdout);
2070 }
2072 font->incRefCnt();
2073 state->setFont(font, args[1].getNum());
2074 fontChanged = gTrue;
2075 }
2077 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
2078 state->setLeading(args[0].getNum());
2079 }
2081 void PdfParser::opSetTextRender(Object args[], int numArgs) {
2082 state->setRender(args[0].getInt());
2083 builder->updateStyle(state);
2084 }
2086 void PdfParser::opSetTextRise(Object args[], int numArgs) {
2087 state->setRise(args[0].getNum());
2088 }
2090 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
2091 state->setWordSpace(args[0].getNum());
2092 }
2094 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
2095 state->setHorizScaling(args[0].getNum());
2096 builder->updateTextMatrix(state);
2097 fontChanged = gTrue;
2098 }
2100 //------------------------------------------------------------------------
2101 // text positioning operators
2102 //------------------------------------------------------------------------
2104 void PdfParser::opTextMove(Object args[], int numArgs) {
2105 double tx, ty;
2107 tx = state->getLineX() + args[0].getNum();
2108 ty = state->getLineY() + args[1].getNum();
2109 state->textMoveTo(tx, ty);
2110 builder->updateTextPosition(tx, ty);
2111 }
2113 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2114 double tx, ty;
2116 tx = state->getLineX() + args[0].getNum();
2117 ty = args[1].getNum();
2118 state->setLeading(-ty);
2119 ty += state->getLineY();
2120 state->textMoveTo(tx, ty);
2121 builder->updateTextPosition(tx, ty);
2122 }
2124 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2125 state->setTextMat(args[0].getNum(), args[1].getNum(),
2126 args[2].getNum(), args[3].getNum(),
2127 args[4].getNum(), args[5].getNum());
2128 state->textMoveTo(0, 0);
2129 builder->updateTextMatrix(state);
2130 builder->updateTextPosition(0.0, 0.0);
2131 fontChanged = gTrue;
2132 }
2134 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2135 double tx, ty;
2137 tx = state->getLineX();
2138 ty = state->getLineY() - state->getLeading();
2139 state->textMoveTo(tx, ty);
2140 builder->updateTextPosition(tx, ty);
2141 }
2143 //------------------------------------------------------------------------
2144 // text string operators
2145 //------------------------------------------------------------------------
2147 void PdfParser::opShowText(Object args[], int numArgs) {
2148 if (!state->getFont()) {
2149 error(getPos(), const_cast<char*>("No font in show"));
2150 return;
2151 }
2152 if (fontChanged) {
2153 builder->updateFont(state);
2154 fontChanged = gFalse;
2155 }
2156 doShowText(args[0].getString());
2157 }
2159 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2160 double tx, ty;
2162 if (!state->getFont()) {
2163 error(getPos(), const_cast<char*>("No font in move/show"));
2164 return;
2165 }
2166 if (fontChanged) {
2167 builder->updateFont(state);
2168 fontChanged = gFalse;
2169 }
2170 tx = state->getLineX();
2171 ty = state->getLineY() - state->getLeading();
2172 state->textMoveTo(tx, ty);
2173 builder->updateTextPosition(tx, ty);
2174 doShowText(args[0].getString());
2175 }
2177 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2178 double tx, ty;
2180 if (!state->getFont()) {
2181 error(getPos(), const_cast<char*>("No font in move/set/show"));
2182 return;
2183 }
2184 if (fontChanged) {
2185 builder->updateFont(state);
2186 fontChanged = gFalse;
2187 }
2188 state->setWordSpace(args[0].getNum());
2189 state->setCharSpace(args[1].getNum());
2190 tx = state->getLineX();
2191 ty = state->getLineY() - state->getLeading();
2192 state->textMoveTo(tx, ty);
2193 builder->updateTextPosition(tx, ty);
2194 doShowText(args[2].getString());
2195 }
2197 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2198 Array *a;
2199 Object obj;
2200 int wMode;
2201 int i;
2203 if (!state->getFont()) {
2204 error(getPos(), const_cast<char*>("No font in show/space"));
2205 return;
2206 }
2207 if (fontChanged) {
2208 builder->updateFont(state);
2209 fontChanged = gFalse;
2210 }
2211 wMode = state->getFont()->getWMode();
2212 a = args[0].getArray();
2213 for (i = 0; i < a->getLength(); ++i) {
2214 a->get(i, &obj);
2215 if (obj.isNum()) {
2216 // this uses the absolute value of the font size to match
2217 // Acrobat's behavior
2218 if (wMode) {
2219 state->textShift(0, -obj.getNum() * 0.001 *
2220 fabs(state->getFontSize()));
2221 } else {
2222 state->textShift(-obj.getNum() * 0.001 *
2223 fabs(state->getFontSize()), 0);
2224 }
2225 builder->updateTextShift(state, obj.getNum());
2226 } else if (obj.isString()) {
2227 doShowText(obj.getString());
2228 } else {
2229 error(getPos(), const_cast<char*>("Element of show/space array must be number or string"));
2230 }
2231 obj.free();
2232 }
2233 }
2237 /*
2238 * The `POPPLER_NEW_GFXFONT' stuff is for the change to GfxFont's getNextChar() call.
2239 * Thanks to tsdgeos for the fix.
2240 * Miklos, does this look ok?
2241 */
2243 void PdfParser::doShowText(GooString *s) {
2244 GfxFont *font;
2245 int wMode;
2246 double riseX, riseY;
2247 CharCode code;
2248 #ifdef POPPLER_NEW_GFXFONT
2249 Unicode *u = NULL;
2250 #else
2251 Unicode u[8];
2252 #endif
2253 double x, y, dx, dy, curX, curY, tdx, tdy, lineX, lineY;
2254 double originX, originY, tOriginX, tOriginY;
2255 double oldCTM[6], newCTM[6];
2256 double *mat;
2257 Object charProc;
2258 Dict *resDict;
2259 Parser *oldParser;
2260 char *p;
2261 int len, n, uLen, i;
2263 font = state->getFont();
2264 wMode = font->getWMode();
2266 builder->beginString(state, s);
2268 // handle a Type 3 char
2269 if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2270 mat = state->getCTM();
2271 for (i = 0; i < 6; ++i) {
2272 oldCTM[i] = mat[i];
2273 }
2274 mat = state->getTextMat();
2275 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2276 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2277 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2278 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2279 mat = font->getFontMatrix();
2280 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2281 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2282 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2283 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2284 newCTM[0] *= state->getFontSize();
2285 newCTM[1] *= state->getFontSize();
2286 newCTM[2] *= state->getFontSize();
2287 newCTM[3] *= state->getFontSize();
2288 newCTM[0] *= state->getHorizScaling();
2289 newCTM[2] *= state->getHorizScaling();
2290 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2291 curX = state->getCurX();
2292 curY = state->getCurY();
2293 lineX = state->getLineX();
2294 lineY = state->getLineY();
2295 oldParser = parser;
2296 p = s->getCString();
2297 len = s->getLength();
2298 while (len > 0) {
2299 n = font->getNextChar(p, len, &code,
2300 #ifdef POPPLER_NEW_GFXFONT
2301 &u, &uLen, /* TODO: This looks like a memory leak for u. */
2302 #else
2303 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2304 #endif
2305 &dx, &dy, &originX, &originY);
2306 dx = dx * state->getFontSize() + state->getCharSpace();
2307 if (n == 1 && *p == ' ') {
2308 dx += state->getWordSpace();
2309 }
2310 dx *= state->getHorizScaling();
2311 dy *= state->getFontSize();
2312 state->textTransformDelta(dx, dy, &tdx, &tdy);
2313 state->transform(curX + riseX, curY + riseY, &x, &y);
2314 saveState();
2315 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2316 //~ the CTM concat values here are wrong (but never used)
2317 //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2318 if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2319 code, u, uLen)) {*/
2320 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2321 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2322 pushResources(resDict);
2323 }
2324 if (charProc.isStream()) {
2325 //parse(&charProc, gFalse); // TODO: parse into SVG font
2326 } else {
2327 error(getPos(), const_cast<char*>("Missing or bad Type3 CharProc entry"));
2328 }
2329 //out->endType3Char(state);
2330 if (resDict) {
2331 popResources();
2332 }
2333 charProc.free();
2334 }
2335 restoreState();
2336 // GfxState::restore() does *not* restore the current position,
2337 // so we deal with it here using (curX, curY) and (lineX, lineY)
2338 curX += tdx;
2339 curY += tdy;
2340 state->moveTo(curX, curY);
2341 state->textSetPos(lineX, lineY);
2342 p += n;
2343 len -= n;
2344 }
2345 parser = oldParser;
2347 } else {
2348 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2349 p = s->getCString();
2350 len = s->getLength();
2351 while (len > 0) {
2352 n = font->getNextChar(p, len, &code,
2353 #ifdef POPPLER_NEW_GFXFONT
2354 &u, &uLen, /* TODO: This looks like a memory leak for u. */
2355 #else
2356 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2357 #endif
2358 &dx, &dy, &originX, &originY);
2360 if (wMode) {
2361 dx *= state->getFontSize();
2362 dy = dy * state->getFontSize() + state->getCharSpace();
2363 if (n == 1 && *p == ' ') {
2364 dy += state->getWordSpace();
2365 }
2366 } else {
2367 dx = dx * state->getFontSize() + state->getCharSpace();
2368 if (n == 1 && *p == ' ') {
2369 dx += state->getWordSpace();
2370 }
2371 dx *= state->getHorizScaling();
2372 dy *= state->getFontSize();
2373 }
2374 state->textTransformDelta(dx, dy, &tdx, &tdy);
2375 originX *= state->getFontSize();
2376 originY *= state->getFontSize();
2377 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2378 builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2379 dx, dy, tOriginX, tOriginY, code, n, u, uLen);
2380 state->shift(tdx, tdy);
2381 p += n;
2382 len -= n;
2383 }
2384 }
2386 builder->endString(state);
2387 }
2390 //------------------------------------------------------------------------
2391 // XObject operators
2392 //------------------------------------------------------------------------
2394 void PdfParser::opXObject(Object args[], int numArgs) {
2395 char *name;
2396 Object obj1, obj2, obj3, refObj;
2398 name = args[0].getName();
2399 if (!res->lookupXObject(name, &obj1)) {
2400 return;
2401 }
2402 if (!obj1.isStream()) {
2403 error(getPos(), const_cast<char*>("XObject '%s' is wrong type"), name);
2404 obj1.free();
2405 return;
2406 }
2407 obj1.streamGetDict()->lookup(const_cast<char*>("Subtype"), &obj2);
2408 if (obj2.isName(const_cast<char*>("Image"))) {
2409 res->lookupXObjectNF(name, &refObj);
2410 doImage(&refObj, obj1.getStream(), gFalse);
2411 refObj.free();
2412 } else if (obj2.isName(const_cast<char*>("Form"))) {
2413 doForm(&obj1);
2414 } else if (obj2.isName(const_cast<char*>("PS"))) {
2415 obj1.streamGetDict()->lookup(const_cast<char*>("Level1"), &obj3);
2416 /* out->psXObject(obj1.getStream(),
2417 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2418 } else if (obj2.isName()) {
2419 error(getPos(), const_cast<char*>("Unknown XObject subtype '%s'"), obj2.getName());
2420 } else {
2421 error(getPos(), const_cast<char*>("XObject subtype is missing or wrong type"));
2422 }
2423 obj2.free();
2424 obj1.free();
2425 }
2427 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2428 Dict *dict, *maskDict;
2429 int width, height;
2430 int bits, maskBits;
2431 StreamColorSpaceMode csMode;
2432 GBool mask;
2433 GBool invert;
2434 GfxColorSpace *colorSpace, *maskColorSpace;
2435 GfxImageColorMap *colorMap, *maskColorMap;
2436 Object maskObj, smaskObj;
2437 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2438 int maskColors[2*gfxColorMaxComps];
2439 int maskWidth, maskHeight;
2440 GBool maskInvert;
2441 Stream *maskStr;
2442 Object obj1, obj2;
2443 int i;
2445 // get info from the stream
2446 bits = 0;
2447 csMode = streamCSNone;
2448 str->getImageParams(&bits, &csMode);
2450 // get stream dict
2451 dict = str->getDict();
2453 // get size
2454 dict->lookup(const_cast<char*>("Width"), &obj1);
2455 if (obj1.isNull()) {
2456 obj1.free();
2457 dict->lookup(const_cast<char*>("W"), &obj1);
2458 }
2459 if (obj1.isInt())
2460 width = obj1.getInt();
2461 else if (obj1.isReal())
2462 width = (int)obj1.getReal();
2463 else
2464 goto err2;
2465 obj1.free();
2466 dict->lookup(const_cast<char*>("Height"), &obj1);
2467 if (obj1.isNull()) {
2468 obj1.free();
2469 dict->lookup(const_cast<char*>("H"), &obj1);
2470 }
2471 if (obj1.isInt())
2472 height = obj1.getInt();
2473 else if (obj1.isReal())
2474 height = (int)obj1.getReal();
2475 else
2476 goto err2;
2477 obj1.free();
2479 // image or mask?
2480 dict->lookup(const_cast<char*>("ImageMask"), &obj1);
2481 if (obj1.isNull()) {
2482 obj1.free();
2483 dict->lookup(const_cast<char*>("IM"), &obj1);
2484 }
2485 mask = gFalse;
2486 if (obj1.isBool())
2487 mask = obj1.getBool();
2488 else if (!obj1.isNull())
2489 goto err2;
2490 obj1.free();
2492 // bit depth
2493 if (bits == 0) {
2494 dict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2495 if (obj1.isNull()) {
2496 obj1.free();
2497 dict->lookup(const_cast<char*>("BPC"), &obj1);
2498 }
2499 if (obj1.isInt()) {
2500 bits = obj1.getInt();
2501 } else if (mask) {
2502 bits = 1;
2503 } else {
2504 goto err2;
2505 }
2506 obj1.free();
2507 }
2509 // display a mask
2510 if (mask) {
2512 // check for inverted mask
2513 if (bits != 1)
2514 goto err1;
2515 invert = gFalse;
2516 dict->lookup(const_cast<char*>("Decode"), &obj1);
2517 if (obj1.isNull()) {
2518 obj1.free();
2519 dict->lookup(const_cast<char*>("D"), &obj1);
2520 }
2521 if (obj1.isArray()) {
2522 obj1.arrayGet(0, &obj2);
2523 if (obj2.isInt() && obj2.getInt() == 1)
2524 invert = gTrue;
2525 obj2.free();
2526 } else if (!obj1.isNull()) {
2527 goto err2;
2528 }
2529 obj1.free();
2531 // draw it
2532 builder->addImageMask(state, str, width, height, invert);
2534 } else {
2536 // get color space and color map
2537 dict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2538 if (obj1.isNull()) {
2539 obj1.free();
2540 dict->lookup(const_cast<char*>("CS"), &obj1);
2541 }
2542 if (obj1.isName()) {
2543 res->lookupColorSpace(obj1.getName(), &obj2);
2544 if (!obj2.isNull()) {
2545 obj1.free();
2546 obj1 = obj2;
2547 } else {
2548 obj2.free();
2549 }
2550 }
2551 if (!obj1.isNull()) {
2552 #ifdef POPPLER_NEW_COLOR_SPACE_API
2553 colorSpace = GfxColorSpace::parse(&obj1, NULL);
2554 #else
2555 colorSpace = GfxColorSpace::parse(&obj1);
2556 #endif
2557 } else if (csMode == streamCSDeviceGray) {
2558 colorSpace = new GfxDeviceGrayColorSpace();
2559 } else if (csMode == streamCSDeviceRGB) {
2560 colorSpace = new GfxDeviceRGBColorSpace();
2561 } else if (csMode == streamCSDeviceCMYK) {
2562 colorSpace = new GfxDeviceCMYKColorSpace();
2563 } else {
2564 colorSpace = NULL;
2565 }
2566 obj1.free();
2567 if (!colorSpace) {
2568 goto err1;
2569 }
2570 dict->lookup(const_cast<char*>("Decode"), &obj1);
2571 if (obj1.isNull()) {
2572 obj1.free();
2573 dict->lookup(const_cast<char*>("D"), &obj1);
2574 }
2575 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2576 obj1.free();
2577 if (!colorMap->isOk()) {
2578 delete colorMap;
2579 goto err1;
2580 }
2582 // get the mask
2583 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2584 maskStr = NULL; // make gcc happy
2585 maskWidth = maskHeight = 0; // make gcc happy
2586 maskInvert = gFalse; // make gcc happy
2587 maskColorMap = NULL; // make gcc happy
2588 dict->lookup(const_cast<char*>("Mask"), &maskObj);
2589 dict->lookup(const_cast<char*>("SMask"), &smaskObj);
2590 if (smaskObj.isStream()) {
2591 // soft mask
2592 if (inlineImg) {
2593 goto err1;
2594 }
2595 maskStr = smaskObj.getStream();
2596 maskDict = smaskObj.streamGetDict();
2597 maskDict->lookup(const_cast<char*>("Width"), &obj1);
2598 if (obj1.isNull()) {
2599 obj1.free();
2600 maskDict->lookup(const_cast<char*>("W"), &obj1);
2601 }
2602 if (!obj1.isInt()) {
2603 goto err2;
2604 }
2605 maskWidth = obj1.getInt();
2606 obj1.free();
2607 maskDict->lookup(const_cast<char*>("Height"), &obj1);
2608 if (obj1.isNull()) {
2609 obj1.free();
2610 maskDict->lookup(const_cast<char*>("H"), &obj1);
2611 }
2612 if (!obj1.isInt()) {
2613 goto err2;
2614 }
2615 maskHeight = obj1.getInt();
2616 obj1.free();
2617 maskDict->lookup(const_cast<char*>("BitsPerComponent"), &obj1);
2618 if (obj1.isNull()) {
2619 obj1.free();
2620 maskDict->lookup(const_cast<char*>("BPC"), &obj1);
2621 }
2622 if (!obj1.isInt()) {
2623 goto err2;
2624 }
2625 maskBits = obj1.getInt();
2626 obj1.free();
2627 maskDict->lookup(const_cast<char*>("ColorSpace"), &obj1);
2628 if (obj1.isNull()) {
2629 obj1.free();
2630 maskDict->lookup(const_cast<char*>("CS"), &obj1);
2631 }
2632 if (obj1.isName()) {
2633 res->lookupColorSpace(obj1.getName(), &obj2);
2634 if (!obj2.isNull()) {
2635 obj1.free();
2636 obj1 = obj2;
2637 } else {
2638 obj2.free();
2639 }
2640 }
2641 #ifdef POPPLER_NEW_COLOR_SPACE_API
2642 maskColorSpace = GfxColorSpace::parse(&obj1, NULL);
2643 #else
2644 maskColorSpace = GfxColorSpace::parse(&obj1);
2645 #endif
2646 obj1.free();
2647 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2648 goto err1;
2649 }
2650 maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2651 if (obj1.isNull()) {
2652 obj1.free();
2653 maskDict->lookup(const_cast<char*>("D"), &obj1);
2654 }
2655 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2656 obj1.free();
2657 if (!maskColorMap->isOk()) {
2658 delete maskColorMap;
2659 goto err1;
2660 }
2661 //~ handle the Matte entry
2662 haveSoftMask = gTrue;
2663 } else if (maskObj.isArray()) {
2664 // color key mask
2665 for (i = 0;
2666 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2667 ++i) {
2668 maskObj.arrayGet(i, &obj1);
2669 maskColors[i] = obj1.getInt();
2670 obj1.free();
2671 }
2672 haveColorKeyMask = gTrue;
2673 } else if (maskObj.isStream()) {
2674 // explicit mask
2675 if (inlineImg) {
2676 goto err1;
2677 }
2678 maskStr = maskObj.getStream();
2679 maskDict = maskObj.streamGetDict();
2680 maskDict->lookup(const_cast<char*>("Width"), &obj1);
2681 if (obj1.isNull()) {
2682 obj1.free();
2683 maskDict->lookup(const_cast<char*>("W"), &obj1);
2684 }
2685 if (!obj1.isInt()) {
2686 goto err2;
2687 }
2688 maskWidth = obj1.getInt();
2689 obj1.free();
2690 maskDict->lookup(const_cast<char*>("Height"), &obj1);
2691 if (obj1.isNull()) {
2692 obj1.free();
2693 maskDict->lookup(const_cast<char*>("H"), &obj1);
2694 }
2695 if (!obj1.isInt()) {
2696 goto err2;
2697 }
2698 maskHeight = obj1.getInt();
2699 obj1.free();
2700 maskDict->lookup(const_cast<char*>("ImageMask"), &obj1);
2701 if (obj1.isNull()) {
2702 obj1.free();
2703 maskDict->lookup(const_cast<char*>("IM"), &obj1);
2704 }
2705 if (!obj1.isBool() || !obj1.getBool()) {
2706 goto err2;
2707 }
2708 obj1.free();
2709 maskInvert = gFalse;
2710 maskDict->lookup(const_cast<char*>("Decode"), &obj1);
2711 if (obj1.isNull()) {
2712 obj1.free();
2713 maskDict->lookup(const_cast<char*>("D"), &obj1);
2714 }
2715 if (obj1.isArray()) {
2716 obj1.arrayGet(0, &obj2);
2717 if (obj2.isInt() && obj2.getInt() == 1) {
2718 maskInvert = gTrue;
2719 }
2720 obj2.free();
2721 } else if (!obj1.isNull()) {
2722 goto err2;
2723 }
2724 obj1.free();
2725 haveExplicitMask = gTrue;
2726 }
2728 // draw it
2729 if (haveSoftMask) {
2730 builder->addSoftMaskedImage(state, str, width, height, colorMap,
2731 maskStr, maskWidth, maskHeight, maskColorMap);
2732 delete maskColorMap;
2733 } else if (haveExplicitMask) {
2734 builder->addMaskedImage(state, str, width, height, colorMap,
2735 maskStr, maskWidth, maskHeight, maskInvert);
2736 } else {
2737 builder->addImage(state, str, width, height, colorMap,
2738 haveColorKeyMask ? maskColors : (int *)NULL);
2739 }
2740 delete colorMap;
2742 maskObj.free();
2743 smaskObj.free();
2744 }
2746 return;
2748 err2:
2749 obj1.free();
2750 err1:
2751 error(getPos(), const_cast<char*>("Bad image parameters"));
2752 }
2754 void PdfParser::doForm(Object *str) {
2755 Dict *dict;
2756 GBool transpGroup, isolated, knockout;
2757 GfxColorSpace *blendingColorSpace;
2758 Object matrixObj, bboxObj;
2759 double m[6], bbox[4];
2760 Object resObj;
2761 Dict *resDict;
2762 Object obj1, obj2, obj3;
2763 int i;
2765 // check for excessive recursion
2766 if (formDepth > 20) {
2767 return;
2768 }
2770 // get stream dict
2771 dict = str->streamGetDict();
2773 // check form type
2774 dict->lookup(const_cast<char*>("FormType"), &obj1);
2775 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2776 error(getPos(), const_cast<char*>("Unknown form type"));
2777 }
2778 obj1.free();
2780 // get bounding box
2781 dict->lookup(const_cast<char*>("BBox"), &bboxObj);
2782 if (!bboxObj.isArray()) {
2783 bboxObj.free();
2784 error(getPos(), const_cast<char*>("Bad form bounding box"));
2785 return;
2786 }
2787 for (i = 0; i < 4; ++i) {
2788 bboxObj.arrayGet(i, &obj1);
2789 bbox[i] = obj1.getNum();
2790 obj1.free();
2791 }
2792 bboxObj.free();
2794 // get matrix
2795 dict->lookup(const_cast<char*>("Matrix"), &matrixObj);
2796 if (matrixObj.isArray()) {
2797 for (i = 0; i < 6; ++i) {
2798 matrixObj.arrayGet(i, &obj1);
2799 m[i] = obj1.getNum();
2800 obj1.free();
2801 }
2802 } else {
2803 m[0] = 1; m[1] = 0;
2804 m[2] = 0; m[3] = 1;
2805 m[4] = 0; m[5] = 0;
2806 }
2807 matrixObj.free();
2809 // get resources
2810 dict->lookup(const_cast<char*>("Resources"), &resObj);
2811 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2813 // check for a transparency group
2814 transpGroup = isolated = knockout = gFalse;
2815 blendingColorSpace = NULL;
2816 if (dict->lookup(const_cast<char*>("Group"), &obj1)->isDict()) {
2817 if (obj1.dictLookup(const_cast<char*>("S"), &obj2)->isName(const_cast<char*>("Transparency"))) {
2818 transpGroup = gTrue;
2819 if (!obj1.dictLookup(const_cast<char*>("CS"), &obj3)->isNull()) {
2820 #ifdef POPPLER_NEW_COLOR_SPACE_API
2821 blendingColorSpace = GfxColorSpace::parse(&obj3, NULL);
2822 #else
2823 blendingColorSpace = GfxColorSpace::parse(&obj3);
2824 #endif
2825 }
2826 obj3.free();
2827 if (obj1.dictLookup(const_cast<char*>("I"), &obj3)->isBool()) {
2828 isolated = obj3.getBool();
2829 }
2830 obj3.free();
2831 if (obj1.dictLookup(const_cast<char*>("K"), &obj3)->isBool()) {
2832 knockout = obj3.getBool();
2833 }
2834 obj3.free();
2835 }
2836 obj2.free();
2837 }
2838 obj1.free();
2840 // draw it
2841 ++formDepth;
2842 doForm1(str, resDict, m, bbox,
2843 transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2844 --formDepth;
2846 if (blendingColorSpace) {
2847 delete blendingColorSpace;
2848 }
2849 resObj.free();
2850 }
2852 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2853 GBool transpGroup, GBool softMask,
2854 GfxColorSpace *blendingColorSpace,
2855 GBool isolated, GBool knockout,
2856 GBool alpha, Function *transferFunc,
2857 GfxColor *backdropColor) {
2858 Parser *oldParser;
2859 double oldBaseMatrix[6];
2860 int i;
2862 // push new resources on stack
2863 pushResources(resDict);
2865 // save current graphics state
2866 saveState();
2868 // kill any pre-existing path
2869 state->clearPath();
2871 if (softMask || transpGroup) {
2872 builder->clearSoftMask(state);
2873 builder->pushTransparencyGroup(state, bbox, blendingColorSpace,
2874 isolated, knockout, softMask);
2875 }
2877 // save current parser
2878 oldParser = parser;
2880 // set form transformation matrix
2881 state->concatCTM(matrix[0], matrix[1], matrix[2],
2882 matrix[3], matrix[4], matrix[5]);
2883 builder->setTransform(matrix[0], matrix[1], matrix[2],
2884 matrix[3], matrix[4], matrix[5]);
2886 // set form bounding box
2887 state->moveTo(bbox[0], bbox[1]);
2888 state->lineTo(bbox[2], bbox[1]);
2889 state->lineTo(bbox[2], bbox[3]);
2890 state->lineTo(bbox[0], bbox[3]);
2891 state->closePath();
2892 state->clip();
2893 clipHistory->setClip(state->getPath());
2894 builder->clip(state);
2895 state->clearPath();
2897 if (softMask || transpGroup) {
2898 if (state->getBlendMode() != gfxBlendNormal) {
2899 state->setBlendMode(gfxBlendNormal);
2900 }
2901 if (state->getFillOpacity() != 1) {
2902 builder->setGroupOpacity(state->getFillOpacity());
2903 state->setFillOpacity(1);
2904 }
2905 if (state->getStrokeOpacity() != 1) {
2906 state->setStrokeOpacity(1);
2907 }
2908 }
2910 // set new base matrix
2911 for (i = 0; i < 6; ++i) {
2912 oldBaseMatrix[i] = baseMatrix[i];
2913 baseMatrix[i] = state->getCTM()[i];
2914 }
2916 // draw the form
2917 parse(str, gFalse);
2919 // restore base matrix
2920 for (i = 0; i < 6; ++i) {
2921 baseMatrix[i] = oldBaseMatrix[i];
2922 }
2924 // restore parser
2925 parser = oldParser;
2927 if (softMask || transpGroup) {
2928 builder->popTransparencyGroup(state);
2929 }
2931 // restore graphics state
2932 restoreState();
2934 // pop resource stack
2935 popResources();
2937 if (softMask) {
2938 builder->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2939 } else if (transpGroup) {
2940 builder->paintTransparencyGroup(state, bbox);
2941 }
2943 return;
2944 }
2946 //------------------------------------------------------------------------
2947 // in-line image operators
2948 //------------------------------------------------------------------------
2950 void PdfParser::opBeginImage(Object args[], int numArgs) {
2951 Stream *str;
2952 int c1, c2;
2954 // build dict/stream
2955 str = buildImageStream();
2957 // display the image
2958 if (str) {
2959 doImage(NULL, str, gTrue);
2961 // skip 'EI' tag
2962 c1 = str->getUndecodedStream()->getChar();
2963 c2 = str->getUndecodedStream()->getChar();
2964 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2965 c1 = c2;
2966 c2 = str->getUndecodedStream()->getChar();
2967 }
2968 delete str;
2969 }
2970 }
2972 Stream *PdfParser::buildImageStream() {
2973 Object dict;
2974 Object obj;
2975 char *key;
2976 Stream *str;
2978 // build dictionary
2979 dict.initDict(xref);
2980 parser->getObj(&obj);
2981 while (!obj.isCmd(const_cast<char*>("ID")) && !obj.isEOF()) {
2982 if (!obj.isName()) {
2983 error(getPos(), const_cast<char*>("Inline image dictionary key must be a name object"));
2984 obj.free();
2985 } else {
2986 key = copyString(obj.getName());
2987 obj.free();
2988 parser->getObj(&obj);
2989 if (obj.isEOF() || obj.isError()) {
2990 gfree(key);
2991 break;
2992 }
2993 dict.dictAdd(key, &obj);
2994 }
2995 parser->getObj(&obj);
2996 }
2997 if (obj.isEOF()) {
2998 error(getPos(), const_cast<char*>("End of file in inline image"));
2999 obj.free();
3000 dict.free();
3001 return NULL;
3002 }
3003 obj.free();
3005 // make stream
3006 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
3007 str = str->addFilters(&dict);
3009 return str;
3010 }
3012 void PdfParser::opImageData(Object args[], int numArgs) {
3013 error(getPos(), const_cast<char*>("Internal: got 'ID' operator"));
3014 }
3016 void PdfParser::opEndImage(Object args[], int numArgs) {
3017 error(getPos(), const_cast<char*>("Internal: got 'EI' operator"));
3018 }
3020 //------------------------------------------------------------------------
3021 // type 3 font operators
3022 //------------------------------------------------------------------------
3024 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
3025 }
3027 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
3028 }
3030 //------------------------------------------------------------------------
3031 // compatibility operators
3032 //------------------------------------------------------------------------
3034 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
3035 ++ignoreUndef;
3036 }
3038 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
3039 if (ignoreUndef > 0)
3040 --ignoreUndef;
3041 }
3043 //------------------------------------------------------------------------
3044 // marked content operators
3045 //------------------------------------------------------------------------
3047 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
3048 if (printCommands) {
3049 printf(" marked content: %s ", args[0].getName());
3050 if (numArgs == 2)
3051 args[2].print(stdout);
3052 printf("\n");
3053 fflush(stdout);
3054 }
3056 if(numArgs == 2) {
3057 //out->beginMarkedContent(args[0].getName(),args[1].getDict());
3058 } else {
3059 //out->beginMarkedContent(args[0].getName());
3060 }
3061 }
3063 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
3064 //out->endMarkedContent();
3065 }
3067 void PdfParser::opMarkPoint(Object args[], int numArgs) {
3068 if (printCommands) {
3069 printf(" mark point: %s ", args[0].getName());
3070 if (numArgs == 2)
3071 args[2].print(stdout);
3072 printf("\n");
3073 fflush(stdout);
3074 }
3076 if(numArgs == 2) {
3077 //out->markPoint(args[0].getName(),args[1].getDict());
3078 } else {
3079 //out->markPoint(args[0].getName());
3080 }
3082 }
3084 //------------------------------------------------------------------------
3085 // misc
3086 //------------------------------------------------------------------------
3088 void PdfParser::saveState() {
3089 builder->saveState();
3090 state = state->save();
3091 clipHistory = clipHistory->save();
3092 }
3094 void PdfParser::restoreState() {
3095 clipHistory = clipHistory->restore();
3096 state = state->restore();
3097 builder->restoreState();
3098 }
3100 void PdfParser::pushResources(Dict *resDict) {
3101 res = new GfxResources(xref, resDict, res);
3102 }
3104 void PdfParser::popResources() {
3105 GfxResources *resPtr;
3107 resPtr = res->getNext();
3108 delete res;
3109 res = resPtr;
3110 }
3112 void PdfParser::setDefaultApproximationPrecision() {
3113 int i;
3115 for (i = 1; i <= pdfNumShadingTypes; ++i) {
3116 setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth);
3117 }
3118 }
3120 void PdfParser::setApproximationPrecision(int shadingType, double colorDelta,
3121 int maxDepth) {
3123 if (shadingType > pdfNumShadingTypes || shadingType < 1) {
3124 return;
3125 }
3126 colorDeltas[shadingType-1] = dblToCol(colorDelta);
3127 maxDepths[shadingType-1] = maxDepth;
3128 }
3130 //------------------------------------------------------------------------
3131 // ClipHistoryEntry
3132 //------------------------------------------------------------------------
3134 ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) {
3135 if (clipPathA) {
3136 clipPath = clipPathA->copy();
3137 } else {
3138 clipPath = NULL;
3139 }
3140 clipType = clipTypeA;
3141 saved = NULL;
3142 }
3144 ClipHistoryEntry::~ClipHistoryEntry() {
3145 if (clipPath) {
3146 delete clipPath;
3147 }
3148 }
3150 void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) {
3151 // Free previous clip path
3152 if (clipPath) {
3153 delete clipPath;
3154 }
3155 if (clipPathA) {
3156 clipPath = clipPathA->copy();
3157 clipType = clipTypeA;
3158 } else {
3159 clipPath = NULL;
3160 }
3161 }
3163 ClipHistoryEntry *ClipHistoryEntry::save() {
3164 ClipHistoryEntry *newEntry = new ClipHistoryEntry(this);
3165 newEntry->saved = this;
3167 return newEntry;
3168 }
3170 ClipHistoryEntry *ClipHistoryEntry::restore() {
3171 ClipHistoryEntry *oldEntry;
3173 if (saved) {
3174 oldEntry = saved;
3175 saved = NULL;
3176 delete this;
3177 } else {
3178 oldEntry = this;
3179 }
3181 return oldEntry;
3182 }
3184 ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) {
3185 if (other->clipPath) {
3186 this->clipPath = other->clipPath->copy();
3187 this->clipType = other->clipType;
3188 } else {
3189 this->clipPath = NULL;
3190 }
3191 saved = NULL;
3192 }
3194 #endif /* HAVE_POPPLER */