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