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 / 256.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 / 256.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 / 256.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 / 256.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 / 256.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 ignoreUndef = 0;
302 operatorHistory = NULL;
303 builder = builderA;
304 builder->setDocumentSize(state->getPageWidth()*PX_PER_PT,
305 state->getPageHeight()*PX_PER_PT);
307 double *ctm = state->getCTM();
308 double scaledCTM[6];
309 for (i = 0; i < 6; ++i) {
310 baseMatrix[i] = ctm[i];
311 scaledCTM[i] = PX_PER_PT * ctm[i];
312 }
313 builder->setTransform((double*)&scaledCTM);
314 formDepth = 0;
316 // set crop box
317 if (cropBox) {
318 if (printCommands)
319 printf("cropBox: %f %f %f %f\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
320 // do not clip if it's not needed
321 if (cropBox->x1 != 0.0 || cropBox->y1 != 0.0 ||
322 cropBox->x2 != state->getPageWidth() || cropBox->y2 != state->getPageHeight()) {
324 state->moveTo(cropBox->x1, cropBox->y1);
325 state->lineTo(cropBox->x2, cropBox->y1);
326 state->lineTo(cropBox->x2, cropBox->y2);
327 state->lineTo(cropBox->x1, cropBox->y2);
328 state->closePath();
329 state->clip();
330 builder->setClipPath(state);
331 state->clearPath();
332 }
333 }
334 pushOperator("startPage");
335 }
337 PdfParser::~PdfParser() {
338 while (state->hasSaves()) {
339 restoreState();
340 }
341 if (!subPage) {
342 //out->endPage();
343 }
344 while (res) {
345 popResources();
346 }
347 if (state) {
348 delete state;
349 }
350 }
352 void PdfParser::parse(Object *obj, GBool topLevel) {
353 Object obj2;
354 int i;
356 if (obj->isArray()) {
357 for (i = 0; i < obj->arrayGetLength(); ++i) {
358 obj->arrayGet(i, &obj2);
359 if (!obj2.isStream()) {
360 error(-1, "Weird page contents");
361 obj2.free();
362 return;
363 }
364 obj2.free();
365 }
366 } else if (!obj->isStream()) {
367 error(-1, "Weird page contents");
368 return;
369 }
370 parser = new Parser(xref, new Lexer(xref, obj), gFalse);
371 go(topLevel);
372 delete parser;
373 parser = NULL;
374 }
376 void PdfParser::go(GBool topLevel) {
377 Object obj;
378 Object args[maxArgs];
379 int numArgs, i;
380 int lastAbortCheck;
382 // scan a sequence of objects
383 numArgs = 0;
384 parser->getObj(&obj);
385 while (!obj.isEOF()) {
387 // got a command - execute it
388 if (obj.isCmd()) {
389 if (printCommands) {
390 obj.print(stdout);
391 for (i = 0; i < numArgs; ++i) {
392 printf(" ");
393 args[i].print(stdout);
394 }
395 printf("\n");
396 fflush(stdout);
397 }
399 // Run the operation
400 execOp(&obj, args, numArgs);
402 obj.free();
403 for (i = 0; i < numArgs; ++i)
404 args[i].free();
405 numArgs = 0;
407 // got an argument - save it
408 } else if (numArgs < maxArgs) {
409 args[numArgs++] = obj;
411 // too many arguments - something is wrong
412 } else {
413 error(getPos(), "Too many args in content stream");
414 if (printCommands) {
415 printf("throwing away arg: ");
416 obj.print(stdout);
417 printf("\n");
418 fflush(stdout);
419 }
420 obj.free();
421 }
423 // grab the next object
424 parser->getObj(&obj);
425 }
426 obj.free();
428 // args at end with no command
429 if (numArgs > 0) {
430 error(getPos(), "Leftover args in content stream");
431 if (printCommands) {
432 printf("%d leftovers:", numArgs);
433 for (i = 0; i < numArgs; ++i) {
434 printf(" ");
435 args[i].print(stdout);
436 }
437 printf("\n");
438 fflush(stdout);
439 }
440 for (i = 0; i < numArgs; ++i)
441 args[i].free();
442 }
443 }
445 void PdfParser::pushOperator(const char *name) {
446 OpHistoryEntry *newEntry = new OpHistoryEntry;
447 newEntry->name = name;
448 newEntry->state = NULL;
449 newEntry->depth = (operatorHistory != NULL ? (operatorHistory->depth+1) : 0);
450 newEntry->next = operatorHistory;
451 operatorHistory = newEntry;
453 // Truncate list if needed
454 if (operatorHistory->depth > maxOperatorHistoryDepth) {
455 OpHistoryEntry *curr = operatorHistory;
456 OpHistoryEntry *prev = NULL;
457 while (curr && curr->next != NULL) {
458 curr->depth--;
459 prev = curr;
460 curr = curr->next;
461 }
462 if (prev) {
463 if (curr->state != NULL)
464 delete curr->state;
465 delete curr;
466 prev->next = NULL;
467 }
468 }
469 }
471 const char *PdfParser::getPreviousOperator(unsigned int look_back) {
472 OpHistoryEntry *prev = NULL;
473 if (operatorHistory != NULL && look_back > 0) {
474 prev = operatorHistory->next;
475 while (--look_back > 0 && prev != NULL) {
476 prev = prev->next;
477 }
478 }
479 if (prev != NULL) {
480 return prev->name;
481 } else {
482 return "";
483 }
484 }
486 void PdfParser::execOp(Object *cmd, Object args[], int numArgs) {
487 PdfOperator *op;
488 char *name;
489 Object *argPtr;
490 int i;
492 // find operator
493 name = cmd->getCmd();
494 if (!(op = findOp(name))) {
495 if (ignoreUndef == 0)
496 error(getPos(), "Unknown operator '%s'", name);
497 return;
498 }
500 // type check args
501 argPtr = args;
502 if (op->numArgs >= 0) {
503 if (numArgs < op->numArgs) {
504 error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
505 return;
506 }
507 if (numArgs > op->numArgs) {
508 #if 0
509 error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
510 #endif
511 argPtr += numArgs - op->numArgs;
512 numArgs = op->numArgs;
513 }
514 } else {
515 if (numArgs > -op->numArgs) {
516 error(getPos(), "Too many (%d) args to '%s' operator",
517 numArgs, name);
518 return;
519 }
520 }
521 for (i = 0; i < numArgs; ++i) {
522 if (!checkArg(&argPtr[i], op->tchk[i])) {
523 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
524 i, name, argPtr[i].getTypeName());
525 return;
526 }
527 }
529 // add to history
530 pushOperator((char*)&op->name);
532 // do it
533 (this->*op->func)(argPtr, numArgs);
534 }
536 PdfOperator *PdfParser::findOp(char *name) {
537 int a, b, m, cmp;
539 a = -1;
540 b = numOps;
541 // invariant: opTab[a] < name < opTab[b]
542 while (b - a > 1) {
543 m = (a + b) / 2;
544 cmp = strcmp(opTab[m].name, name);
545 if (cmp < 0)
546 a = m;
547 else if (cmp > 0)
548 b = m;
549 else
550 a = b = m;
551 }
552 if (cmp != 0)
553 return NULL;
554 return &opTab[a];
555 }
557 GBool PdfParser::checkArg(Object *arg, TchkType type) {
558 switch (type) {
559 case tchkBool: return arg->isBool();
560 case tchkInt: return arg->isInt();
561 case tchkNum: return arg->isNum();
562 case tchkString: return arg->isString();
563 case tchkName: return arg->isName();
564 case tchkArray: return arg->isArray();
565 case tchkProps: return arg->isDict() || arg->isName();
566 case tchkSCN: return arg->isNum() || arg->isName();
567 case tchkNone: return gFalse;
568 }
569 return gFalse;
570 }
572 int PdfParser::getPos() {
573 return parser ? parser->getPos() : -1;
574 }
576 //------------------------------------------------------------------------
577 // graphics state operators
578 //------------------------------------------------------------------------
580 void PdfParser::opSave(Object args[], int numArgs) {
581 saveState();
582 }
584 void PdfParser::opRestore(Object args[], int numArgs) {
585 restoreState();
586 }
588 void PdfParser::opConcat(Object args[], int numArgs) {
589 state->concatCTM(args[0].getNum(), args[1].getNum(),
590 args[2].getNum(), args[3].getNum(),
591 args[4].getNum(), args[5].getNum());
592 const char *prevOp = getPreviousOperator();
593 double a0 = args[0].getNum();
594 double a1 = args[1].getNum();
595 double a2 = args[2].getNum();
596 double a3 = args[3].getNum();
597 double a4 = args[4].getNum();
598 double a5 = args[5].getNum();
599 if (!strcmp(prevOp, "q")) {
600 builder->setTransform(a0, a1, a2, a3, a4, a5);
601 } else if (!strcmp(prevOp, "cm")) {
602 // multiply it with the previous transform
603 double otherMatrix[6];
604 if (!builder->getTransform((double*)&otherMatrix)) { // invalid transform
605 // construct identity matrix
606 otherMatrix[0] = otherMatrix[3] = 1.0;
607 otherMatrix[1] = otherMatrix[2] = otherMatrix[4] = otherMatrix[5] = 0.0;
608 }
609 double c0 = otherMatrix[0]*a0 + otherMatrix[1]*a2;
610 double c1 = otherMatrix[0]*a1 + otherMatrix[1]*a3;
611 double c2 = otherMatrix[2]*a0 + otherMatrix[3]*a2;
612 double c3 = otherMatrix[2]*a1 + otherMatrix[3]*a3;
613 double c4 = otherMatrix[4]*a0 + otherMatrix[5]*a2 + a4;
614 double c5 = otherMatrix[4]*a1 + otherMatrix[5]*a3 + a5;
615 builder->setTransform(c0, c1, c2, c3, c4, c5);
616 } else {
617 builder->pushGroup();
618 builder->setTransform(a0, a1, a2, a3, a4, a5);
619 }
620 fontChanged = gTrue;
621 }
623 void PdfParser::opSetDash(Object args[], int numArgs) {
624 Array *a;
625 int length;
626 Object obj;
627 double *dash;
628 int i;
630 a = args[0].getArray();
631 length = a->getLength();
632 if (length == 0) {
633 dash = NULL;
634 } else {
635 dash = (double *)gmallocn(length, sizeof(double));
636 for (i = 0; i < length; ++i) {
637 dash[i] = a->get(i, &obj)->getNum();
638 obj.free();
639 }
640 }
641 state->setLineDash(dash, length, args[1].getNum());
642 builder->updateStyle(state);
643 }
645 void PdfParser::opSetFlat(Object args[], int numArgs) {
646 state->setFlatness((int)args[0].getNum());
647 }
649 void PdfParser::opSetLineJoin(Object args[], int numArgs) {
650 state->setLineJoin(args[0].getInt());
651 builder->updateStyle(state);
652 }
654 void PdfParser::opSetLineCap(Object args[], int numArgs) {
655 state->setLineCap(args[0].getInt());
656 builder->updateStyle(state);
657 }
659 void PdfParser::opSetMiterLimit(Object args[], int numArgs) {
660 state->setMiterLimit(args[0].getNum());
661 builder->updateStyle(state);
662 }
664 void PdfParser::opSetLineWidth(Object args[], int numArgs) {
665 state->setLineWidth(args[0].getNum());
666 builder->updateStyle(state);
667 }
669 void PdfParser::opSetExtGState(Object args[], int numArgs) {
670 Object obj1, obj2, obj3, obj4, obj5;
671 GfxBlendMode mode;
672 GBool haveFillOP;
673 Function *funcs[4];
674 GfxColor backdropColor;
675 GBool haveBackdropColor;
676 GfxColorSpace *blendingColorSpace;
677 GBool alpha, isolated, knockout;
678 int i;
680 if (!res->lookupGState(args[0].getName(), &obj1)) {
681 return;
682 }
683 if (!obj1.isDict()) {
684 error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
685 obj1.free();
686 return;
687 }
688 if (printCommands) {
689 printf(" gfx state dict: ");
690 obj1.print();
691 printf("\n");
692 }
694 // transparency support: blend mode, fill/stroke opacity
695 if (!obj1.dictLookup("BM", &obj2)->isNull()) {
696 if (state->parseBlendMode(&obj2, &mode)) {
697 state->setBlendMode(mode);
698 } else {
699 error(getPos(), "Invalid blend mode in ExtGState");
700 }
701 }
702 obj2.free();
703 if (obj1.dictLookup("ca", &obj2)->isNum()) {
704 state->setFillOpacity(obj2.getNum());
705 }
706 obj2.free();
707 if (obj1.dictLookup("CA", &obj2)->isNum()) {
708 state->setStrokeOpacity(obj2.getNum());
709 }
710 obj2.free();
712 // fill/stroke overprint
713 if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
714 state->setFillOverprint(obj2.getBool());
715 }
716 obj2.free();
717 if (obj1.dictLookup("OP", &obj2)->isBool()) {
718 state->setStrokeOverprint(obj2.getBool());
719 if (!haveFillOP) {
720 state->setFillOverprint(obj2.getBool());
721 }
722 }
723 obj2.free();
725 // stroke adjust
726 if (obj1.dictLookup("SA", &obj2)->isBool()) {
727 state->setStrokeAdjust(obj2.getBool());
728 }
729 obj2.free();
731 // transfer function
732 if (obj1.dictLookup("TR2", &obj2)->isNull()) {
733 obj2.free();
734 obj1.dictLookup("TR", &obj2);
735 }
736 if (obj2.isName("Default") ||
737 obj2.isName("Identity")) {
738 funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
739 state->setTransfer(funcs);
740 } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
741 for (i = 0; i < 4; ++i) {
742 obj2.arrayGet(i, &obj3);
743 funcs[i] = Function::parse(&obj3);
744 obj3.free();
745 if (!funcs[i]) {
746 break;
747 }
748 }
749 if (i == 4) {
750 state->setTransfer(funcs);
751 }
752 } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
753 if ((funcs[0] = Function::parse(&obj2))) {
754 funcs[1] = funcs[2] = funcs[3] = NULL;
755 state->setTransfer(funcs);
756 }
757 } else if (!obj2.isNull()) {
758 error(getPos(), "Invalid transfer function in ExtGState");
759 }
760 obj2.free();
762 // soft mask
763 if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
764 if (obj2.isName("None")) {
765 //out->clearSoftMask(state);
766 } else if (obj2.isDict()) {
767 if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
768 alpha = gTrue;
769 } else { // "Luminosity"
770 alpha = gFalse;
771 }
772 obj3.free();
773 funcs[0] = NULL;
774 if (!obj2.dictLookup("TR", &obj3)->isNull()) {
775 funcs[0] = Function::parse(&obj3);
776 if (funcs[0]->getInputSize() != 1 ||
777 funcs[0]->getOutputSize() != 1) {
778 error(getPos(),
779 "Invalid transfer function in soft mask in ExtGState");
780 delete funcs[0];
781 funcs[0] = NULL;
782 }
783 }
784 obj3.free();
785 if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
786 for (i = 0; i < gfxColorMaxComps; ++i) {
787 backdropColor.c[i] = 0;
788 }
789 for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
790 obj3.arrayGet(i, &obj4);
791 if (obj4.isNum()) {
792 backdropColor.c[i] = dblToCol(obj4.getNum());
793 }
794 obj4.free();
795 }
796 }
797 obj3.free();
798 if (obj2.dictLookup("G", &obj3)->isStream()) {
799 if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
800 blendingColorSpace = NULL;
801 isolated = knockout = gFalse;
802 if (!obj4.dictLookup("CS", &obj5)->isNull()) {
803 blendingColorSpace = GfxColorSpace::parse(&obj5);
804 }
805 obj5.free();
806 if (obj4.dictLookup("I", &obj5)->isBool()) {
807 isolated = obj5.getBool();
808 }
809 obj5.free();
810 if (obj4.dictLookup("K", &obj5)->isBool()) {
811 knockout = obj5.getBool();
812 }
813 obj5.free();
814 if (!haveBackdropColor) {
815 if (blendingColorSpace) {
816 blendingColorSpace->getDefaultColor(&backdropColor);
817 } else {
818 //~ need to get the parent or default color space (?)
819 for (i = 0; i < gfxColorMaxComps; ++i) {
820 backdropColor.c[i] = 0;
821 }
822 }
823 }
824 doSoftMask(&obj3, alpha, blendingColorSpace,
825 isolated, knockout, funcs[0], &backdropColor);
826 if (funcs[0]) {
827 delete funcs[0];
828 }
829 } else {
830 error(getPos(), "Invalid soft mask in ExtGState - missing group");
831 }
832 obj4.free();
833 } else {
834 error(getPos(), "Invalid soft mask in ExtGState - missing group");
835 }
836 obj3.free();
837 } else if (!obj2.isNull()) {
838 error(getPos(), "Invalid soft mask in ExtGState");
839 }
840 }
841 obj2.free();
843 obj1.free();
844 }
846 void PdfParser::doSoftMask(Object *str, GBool alpha,
847 GfxColorSpace *blendingColorSpace,
848 GBool isolated, GBool knockout,
849 Function *transferFunc, GfxColor *backdropColor) {
850 Dict *dict, *resDict;
851 double m[6], bbox[4];
852 Object obj1, obj2;
853 int i;
855 // check for excessive recursion
856 if (formDepth > 20) {
857 return;
858 }
860 // get stream dict
861 dict = str->streamGetDict();
863 // check form type
864 dict->lookup("FormType", &obj1);
865 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
866 error(getPos(), "Unknown form type");
867 }
868 obj1.free();
870 // get bounding box
871 dict->lookup("BBox", &obj1);
872 if (!obj1.isArray()) {
873 obj1.free();
874 error(getPos(), "Bad form bounding box");
875 return;
876 }
877 for (i = 0; i < 4; ++i) {
878 obj1.arrayGet(i, &obj2);
879 bbox[i] = obj2.getNum();
880 obj2.free();
881 }
882 obj1.free();
884 // get matrix
885 dict->lookup("Matrix", &obj1);
886 if (obj1.isArray()) {
887 for (i = 0; i < 6; ++i) {
888 obj1.arrayGet(i, &obj2);
889 m[i] = obj2.getNum();
890 obj2.free();
891 }
892 } else {
893 m[0] = 1; m[1] = 0;
894 m[2] = 0; m[3] = 1;
895 m[4] = 0; m[5] = 0;
896 }
897 obj1.free();
899 // get resources
900 dict->lookup("Resources", &obj1);
901 resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
903 // draw it
904 ++formDepth;
905 doForm1(str, resDict, m, bbox, gTrue, gTrue,
906 blendingColorSpace, isolated, knockout,
907 alpha, transferFunc, backdropColor);
908 --formDepth;
910 if (blendingColorSpace) {
911 delete blendingColorSpace;
912 }
913 obj1.free();
914 }
916 void PdfParser::opSetRenderingIntent(Object args[], int numArgs) {
917 }
919 //------------------------------------------------------------------------
920 // color operators
921 //------------------------------------------------------------------------
923 void PdfParser::opSetFillGray(Object args[], int numArgs) {
924 GfxColor color;
926 state->setFillPattern(NULL);
927 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
928 color.c[0] = dblToCol(args[0].getNum());
929 state->setFillColor(&color);
930 builder->updateStyle(state);
931 }
933 void PdfParser::opSetStrokeGray(Object args[], int numArgs) {
934 GfxColor color;
936 state->setStrokePattern(NULL);
937 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
938 color.c[0] = dblToCol(args[0].getNum());
939 state->setStrokeColor(&color);
940 builder->updateStyle(state);
941 }
943 void PdfParser::opSetFillCMYKColor(Object args[], int numArgs) {
944 GfxColor color;
945 int i;
947 state->setFillPattern(NULL);
948 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
949 for (i = 0; i < 4; ++i) {
950 color.c[i] = dblToCol(args[i].getNum());
951 }
952 state->setFillColor(&color);
953 builder->updateStyle(state);
954 }
956 void PdfParser::opSetStrokeCMYKColor(Object args[], int numArgs) {
957 GfxColor color;
958 int i;
960 state->setStrokePattern(NULL);
961 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
962 for (i = 0; i < 4; ++i) {
963 color.c[i] = dblToCol(args[i].getNum());
964 }
965 state->setStrokeColor(&color);
966 builder->updateStyle(state);
967 }
969 void PdfParser::opSetFillRGBColor(Object args[], int numArgs) {
970 GfxColor color;
971 int i;
973 state->setFillPattern(NULL);
974 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
975 for (i = 0; i < 3; ++i) {
976 color.c[i] = dblToCol(args[i].getNum());
977 }
978 state->setFillColor(&color);
979 builder->updateStyle(state);
980 }
982 void PdfParser::opSetStrokeRGBColor(Object args[], int numArgs) {
983 GfxColor color;
984 int i;
986 state->setStrokePattern(NULL);
987 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
988 for (i = 0; i < 3; ++i) {
989 color.c[i] = dblToCol(args[i].getNum());
990 }
991 state->setStrokeColor(&color);
992 builder->updateStyle(state);
993 }
995 void PdfParser::opSetFillColorSpace(Object args[], int numArgs) {
996 Object obj;
997 GfxColorSpace *colorSpace;
998 GfxColor color;
1000 state->setFillPattern(NULL);
1001 res->lookupColorSpace(args[0].getName(), &obj);
1002 if (obj.isNull()) {
1003 colorSpace = GfxColorSpace::parse(&args[0]);
1004 } else {
1005 colorSpace = GfxColorSpace::parse(&obj);
1006 }
1007 obj.free();
1008 if (colorSpace) {
1009 state->setFillColorSpace(colorSpace);
1010 colorSpace->getDefaultColor(&color);
1011 state->setFillColor(&color);
1012 builder->updateStyle(state);
1013 } else {
1014 error(getPos(), "Bad color space (fill)");
1015 }
1016 }
1018 void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) {
1019 Object obj;
1020 GfxColorSpace *colorSpace;
1021 GfxColor color;
1023 state->setStrokePattern(NULL);
1024 res->lookupColorSpace(args[0].getName(), &obj);
1025 if (obj.isNull()) {
1026 colorSpace = GfxColorSpace::parse(&args[0]);
1027 } else {
1028 colorSpace = GfxColorSpace::parse(&obj);
1029 }
1030 obj.free();
1031 if (colorSpace) {
1032 state->setStrokeColorSpace(colorSpace);
1033 colorSpace->getDefaultColor(&color);
1034 state->setStrokeColor(&color);
1035 builder->updateStyle(state);
1036 } else {
1037 error(getPos(), "Bad color space (stroke)");
1038 }
1039 }
1041 void PdfParser::opSetFillColor(Object args[], int numArgs) {
1042 GfxColor color;
1043 int i;
1045 if (numArgs != state->getFillColorSpace()->getNComps()) {
1046 error(getPos(), "Incorrect number of arguments in 'sc' command");
1047 return;
1048 }
1049 state->setFillPattern(NULL);
1050 for (i = 0; i < numArgs; ++i) {
1051 color.c[i] = dblToCol(args[i].getNum());
1052 }
1053 state->setFillColor(&color);
1054 builder->updateStyle(state);
1055 }
1057 void PdfParser::opSetStrokeColor(Object args[], int numArgs) {
1058 GfxColor color;
1059 int i;
1061 if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1062 error(getPos(), "Incorrect number of arguments in 'SC' command");
1063 return;
1064 }
1065 state->setStrokePattern(NULL);
1066 for (i = 0; i < numArgs; ++i) {
1067 color.c[i] = dblToCol(args[i].getNum());
1068 }
1069 state->setStrokeColor(&color);
1070 builder->updateStyle(state);
1071 }
1073 void PdfParser::opSetFillColorN(Object args[], int numArgs) {
1074 GfxColor color;
1075 GfxPattern *pattern;
1076 int i;
1078 if (state->getFillColorSpace()->getMode() == csPattern) {
1079 if (numArgs > 1) {
1080 if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
1081 numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
1082 ->getUnder()->getNComps()) {
1083 error(getPos(), "Incorrect number of arguments in 'scn' command");
1084 return;
1085 }
1086 for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1087 if (args[i].isNum()) {
1088 color.c[i] = dblToCol(args[i].getNum());
1089 }
1090 }
1091 state->setFillColor(&color);
1092 builder->updateStyle(state);
1093 }
1094 if (args[numArgs-1].isName() &&
1095 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1096 state->setFillPattern(pattern);
1097 }
1099 } else {
1100 if (numArgs != state->getFillColorSpace()->getNComps()) {
1101 error(getPos(), "Incorrect number of arguments in 'scn' command");
1102 return;
1103 }
1104 state->setFillPattern(NULL);
1105 for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1106 if (args[i].isNum()) {
1107 color.c[i] = dblToCol(args[i].getNum());
1108 }
1109 }
1110 state->setFillColor(&color);
1111 builder->updateStyle(state);
1112 }
1113 }
1115 void PdfParser::opSetStrokeColorN(Object args[], int numArgs) {
1116 GfxColor color;
1117 GfxPattern *pattern;
1118 int i;
1120 if (state->getStrokeColorSpace()->getMode() == csPattern) {
1121 if (numArgs > 1) {
1122 if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
1123 ->getUnder() ||
1124 numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
1125 ->getUnder()->getNComps()) {
1126 error(getPos(), "Incorrect number of arguments in 'SCN' command");
1127 return;
1128 }
1129 for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
1130 if (args[i].isNum()) {
1131 color.c[i] = dblToCol(args[i].getNum());
1132 }
1133 }
1134 state->setStrokeColor(&color);
1135 builder->updateStyle(state);
1136 }
1137 if (args[numArgs-1].isName() &&
1138 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
1139 state->setStrokePattern(pattern);
1140 }
1142 } else {
1143 if (numArgs != state->getStrokeColorSpace()->getNComps()) {
1144 error(getPos(), "Incorrect number of arguments in 'SCN' command");
1145 return;
1146 }
1147 state->setStrokePattern(NULL);
1148 for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
1149 if (args[i].isNum()) {
1150 color.c[i] = dblToCol(args[i].getNum());
1151 }
1152 }
1153 state->setStrokeColor(&color);
1154 builder->updateStyle(state);
1155 }
1156 }
1158 //------------------------------------------------------------------------
1159 // path segment operators
1160 //------------------------------------------------------------------------
1162 void PdfParser::opMoveTo(Object args[], int numArgs) {
1163 state->moveTo(args[0].getNum(), args[1].getNum());
1164 }
1166 void PdfParser::opLineTo(Object args[], int numArgs) {
1167 if (!state->isCurPt()) {
1168 error(getPos(), "No current point in lineto");
1169 return;
1170 }
1171 state->lineTo(args[0].getNum(), args[1].getNum());
1172 }
1174 void PdfParser::opCurveTo(Object args[], int numArgs) {
1175 double x1, y1, x2, y2, x3, y3;
1177 if (!state->isCurPt()) {
1178 error(getPos(), "No current point in curveto");
1179 return;
1180 }
1181 x1 = args[0].getNum();
1182 y1 = args[1].getNum();
1183 x2 = args[2].getNum();
1184 y2 = args[3].getNum();
1185 x3 = args[4].getNum();
1186 y3 = args[5].getNum();
1187 state->curveTo(x1, y1, x2, y2, x3, y3);
1188 }
1190 void PdfParser::opCurveTo1(Object args[], int numArgs) {
1191 double x1, y1, x2, y2, x3, y3;
1193 if (!state->isCurPt()) {
1194 error(getPos(), "No current point in curveto1");
1195 return;
1196 }
1197 x1 = state->getCurX();
1198 y1 = state->getCurY();
1199 x2 = args[0].getNum();
1200 y2 = args[1].getNum();
1201 x3 = args[2].getNum();
1202 y3 = args[3].getNum();
1203 state->curveTo(x1, y1, x2, y2, x3, y3);
1204 }
1206 void PdfParser::opCurveTo2(Object args[], int numArgs) {
1207 double x1, y1, x2, y2, x3, y3;
1209 if (!state->isCurPt()) {
1210 error(getPos(), "No current point in curveto2");
1211 return;
1212 }
1213 x1 = args[0].getNum();
1214 y1 = args[1].getNum();
1215 x2 = args[2].getNum();
1216 y2 = args[3].getNum();
1217 x3 = x2;
1218 y3 = y2;
1219 state->curveTo(x1, y1, x2, y2, x3, y3);
1220 }
1222 void PdfParser::opRectangle(Object args[], int numArgs) {
1223 double x, y, w, h;
1225 x = args[0].getNum();
1226 y = args[1].getNum();
1227 w = args[2].getNum();
1228 h = args[3].getNum();
1229 state->moveTo(x, y);
1230 state->lineTo(x + w, y);
1231 state->lineTo(x + w, y + h);
1232 state->lineTo(x, y + h);
1233 state->closePath();
1234 }
1236 void PdfParser::opClosePath(Object args[], int numArgs) {
1237 if (!state->isCurPt()) {
1238 error(getPos(), "No current point in closepath");
1239 return;
1240 }
1241 state->closePath();
1242 }
1244 //------------------------------------------------------------------------
1245 // path painting operators
1246 //------------------------------------------------------------------------
1248 void PdfParser::opEndPath(Object args[], int numArgs) {
1249 doEndPath();
1250 }
1252 void PdfParser::opStroke(Object args[], int numArgs) {
1253 if (!state->isCurPt()) {
1254 //error(getPos(), "No path in stroke");
1255 return;
1256 }
1257 if (state->isPath()) {
1258 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1259 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1260 doPatternStrokeFallback();
1261 } else {
1262 builder->addPath(state, false, true);
1263 }
1264 }
1265 doEndPath();
1266 }
1268 void PdfParser::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
1269 if (!state->isCurPt()) {
1270 //error(getPos(), "No path in closepath/stroke");
1271 return;
1272 }
1273 state->closePath();
1274 if (state->isPath()) {
1275 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1276 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1277 doPatternStrokeFallback();
1278 } else {
1279 builder->addPath(state, false, true);
1280 }
1281 }
1282 doEndPath();
1283 }
1285 void PdfParser::opFill(Object args[], int numArgs) {
1286 if (!state->isCurPt()) {
1287 //error(getPos(), "No path in fill");
1288 return;
1289 }
1290 if (state->isPath()) {
1291 if (state->getFillColorSpace()->getMode() == csPattern &&
1292 !builder->isPatternTypeSupported(state->getFillPattern())) {
1293 doPatternFillFallback(gFalse);
1294 } else {
1295 builder->addPath(state, true, false);
1296 }
1297 }
1298 doEndPath();
1299 }
1301 void PdfParser::opEOFill(Object args[], int numArgs) {
1302 if (!state->isCurPt()) {
1303 //error(getPos(), "No path in eofill");
1304 return;
1305 }
1306 if (state->isPath()) {
1307 if (state->getFillColorSpace()->getMode() == csPattern &&
1308 !builder->isPatternTypeSupported(state->getFillPattern())) {
1309 doPatternFillFallback(gTrue);
1310 } else {
1311 builder->addPath(state, true, false, true);
1312 }
1313 }
1314 doEndPath();
1315 }
1317 void PdfParser::opFillStroke(Object args[], int numArgs) {
1318 if (!state->isCurPt()) {
1319 //error(getPos(), "No path in fill/stroke");
1320 return;
1321 }
1322 if (state->isPath()) {
1323 doFillAndStroke(gFalse);
1324 } else {
1325 builder->addPath(state, true, true);
1326 }
1327 doEndPath();
1328 }
1330 void PdfParser::opCloseFillStroke(Object args[], int numArgs) {
1331 if (!state->isCurPt()) {
1332 //error(getPos(), "No path in closepath/fill/stroke");
1333 return;
1334 }
1335 if (state->isPath()) {
1336 state->closePath();
1337 doFillAndStroke(gFalse);
1338 }
1339 doEndPath();
1340 }
1342 void PdfParser::opEOFillStroke(Object args[], int numArgs) {
1343 if (!state->isCurPt()) {
1344 //error(getPos(), "No path in eofill/stroke");
1345 return;
1346 }
1347 if (state->isPath()) {
1348 doFillAndStroke(gTrue);
1349 }
1350 doEndPath();
1351 }
1353 void PdfParser::opCloseEOFillStroke(Object args[], int numArgs) {
1354 if (!state->isCurPt()) {
1355 //error(getPos(), "No path in closepath/eofill/stroke");
1356 return;
1357 }
1358 if (state->isPath()) {
1359 state->closePath();
1360 doFillAndStroke(gTrue);
1361 }
1362 doEndPath();
1363 }
1365 void PdfParser::doFillAndStroke(GBool eoFill) {
1366 GBool fillOk = gTrue, strokeOk = gTrue;
1367 if (state->getFillColorSpace()->getMode() == csPattern &&
1368 !builder->isPatternTypeSupported(state->getFillPattern())) {
1369 fillOk = gFalse;
1370 }
1371 if (state->getStrokeColorSpace()->getMode() == csPattern &&
1372 !builder->isPatternTypeSupported(state->getStrokePattern())) {
1373 strokeOk = gFalse;
1374 }
1375 if (fillOk && strokeOk) {
1376 builder->addPath(state, true, true, eoFill);
1377 } else {
1378 doPatternFillFallback(eoFill);
1379 doPatternStrokeFallback();
1380 }
1381 }
1383 void PdfParser::doPatternFillFallback(GBool eoFill) {
1384 GfxPattern *pattern;
1386 if (!(pattern = state->getFillPattern())) {
1387 return;
1388 }
1389 switch (pattern->getType()) {
1390 case 1:
1391 break;
1392 case 2:
1393 doShadingPatternFillFallback((GfxShadingPattern *)pattern, gFalse, eoFill);
1394 break;
1395 default:
1396 error(getPos(), "Unimplemented pattern type (%d) in fill",
1397 pattern->getType());
1398 break;
1399 }
1400 }
1402 void PdfParser::doPatternStrokeFallback() {
1403 GfxPattern *pattern;
1405 if (!(pattern = state->getStrokePattern())) {
1406 return;
1407 }
1408 switch (pattern->getType()) {
1409 case 1:
1410 break;
1411 case 2:
1412 doShadingPatternFillFallback((GfxShadingPattern *)pattern, gTrue, gFalse);
1413 break;
1414 default:
1415 error(getPos(), "Unimplemented pattern type (%d) in stroke",
1416 pattern->getType());
1417 break;
1418 }
1419 }
1421 void PdfParser::doShadingPatternFillFallback(GfxShadingPattern *sPat,
1422 GBool stroke, GBool eoFill) {
1423 GfxShading *shading;
1424 GfxPath *savedPath;
1425 double *ctm, *btm, *ptm;
1426 double m[6], ictm[6], m1[6];
1427 double xMin, yMin, xMax, yMax;
1428 double det;
1430 shading = sPat->getShading();
1432 // save current graphics state
1433 savedPath = state->getPath()->copy();
1434 saveState();
1436 // clip to bbox
1437 if (0 ){//shading->getHasBBox()) {
1438 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1439 state->moveTo(xMin, yMin);
1440 state->lineTo(xMax, yMin);
1441 state->lineTo(xMax, yMax);
1442 state->lineTo(xMin, yMax);
1443 state->closePath();
1444 state->clip();
1445 //builder->clip(state);
1446 state->setPath(savedPath->copy());
1447 }
1449 // clip to current path
1450 if (stroke) {
1451 state->clipToStrokePath();
1452 //out->clipToStrokePath(state);
1453 } else {
1454 state->clip();
1455 if (eoFill) {
1456 builder->clip(state, true);
1457 } else {
1458 builder->clip(state);
1459 }
1460 }
1462 // set the color space
1463 state->setFillColorSpace(shading->getColorSpace()->copy());
1465 // background color fill
1466 if (shading->getHasBackground()) {
1467 state->setFillColor(shading->getBackground());
1468 builder->addPath(state, true, false);
1469 }
1470 state->clearPath();
1472 // construct a (pattern space) -> (current space) transform matrix
1473 ctm = state->getCTM();
1474 btm = baseMatrix;
1475 ptm = sPat->getMatrix();
1476 // iCTM = invert CTM
1477 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1478 ictm[0] = ctm[3] * det;
1479 ictm[1] = -ctm[1] * det;
1480 ictm[2] = -ctm[2] * det;
1481 ictm[3] = ctm[0] * det;
1482 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1483 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1484 // m1 = PTM * BTM = PTM * base transform matrix
1485 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1486 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1487 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1488 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1489 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1490 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1491 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1492 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1493 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1494 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1495 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1496 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1497 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1499 // set the new matrix
1500 state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1501 builder->setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
1503 // do shading type-specific operations
1504 switch (shading->getType()) {
1505 case 1:
1506 doFunctionShFill((GfxFunctionShading *)shading);
1507 break;
1508 case 2:
1509 case 3:
1510 // no need to implement these
1511 break;
1512 case 4:
1513 case 5:
1514 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1515 break;
1516 case 6:
1517 case 7:
1518 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1519 break;
1520 }
1522 // restore graphics state
1523 restoreState();
1524 state->setPath(savedPath);
1525 }
1527 void PdfParser::opShFill(Object args[], int numArgs) {
1528 GfxShading *shading;
1529 GfxPath *savedPath;
1530 double xMin, yMin, xMax, yMax;
1532 if (!(shading = res->lookupShading(args[0].getName()))) {
1533 return;
1534 }
1536 // save current graphics state
1537 savedPath = state->getPath()->copy();
1538 saveState();
1540 // clip to bbox
1541 if (shading->getHasBBox()) {
1542 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1543 state->moveTo(xMin, yMin);
1544 state->lineTo(xMax, yMin);
1545 state->lineTo(xMax, yMax);
1546 state->lineTo(xMin, yMax);
1547 state->closePath();
1548 state->clip();
1549 builder->clip(state);
1550 state->clearPath();
1551 }
1553 // set the color space
1554 state->setFillColorSpace(shading->getColorSpace()->copy());
1556 // do shading type-specific operations
1557 switch (shading->getType()) {
1558 case 1:
1559 doFunctionShFill((GfxFunctionShading *)shading);
1560 break;
1561 case 2:
1562 case 3:
1563 // no need to implement these
1564 break;
1565 case 4:
1566 case 5:
1567 doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
1568 break;
1569 case 6:
1570 case 7:
1571 doPatchMeshShFill((GfxPatchMeshShading *)shading);
1572 break;
1573 }
1575 // restore graphics state
1576 restoreState();
1577 state->setPath(savedPath);
1579 delete shading;
1580 }
1582 void PdfParser::doFunctionShFill(GfxFunctionShading *shading) {
1583 double x0, y0, x1, y1;
1584 GfxColor colors[4];
1586 shading->getDomain(&x0, &y0, &x1, &y1);
1587 shading->getColor(x0, y0, &colors[0]);
1588 shading->getColor(x0, y1, &colors[1]);
1589 shading->getColor(x1, y0, &colors[2]);
1590 shading->getColor(x1, y1, &colors[3]);
1591 doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
1592 }
1594 void PdfParser::doFunctionShFill1(GfxFunctionShading *shading,
1595 double x0, double y0,
1596 double x1, double y1,
1597 GfxColor *colors, int depth) {
1598 GfxColor fillColor;
1599 GfxColor color0M, color1M, colorM0, colorM1, colorMM;
1600 GfxColor colors2[4];
1601 double *matrix;
1602 double xM, yM;
1603 int nComps, i, j;
1605 nComps = shading->getColorSpace()->getNComps();
1606 matrix = shading->getMatrix();
1608 // compare the four corner colors
1609 for (i = 0; i < 4; ++i) {
1610 for (j = 0; j < nComps; ++j) {
1611 if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
1612 break;
1613 }
1614 }
1615 if (j < nComps) {
1616 break;
1617 }
1618 }
1620 // center of the rectangle
1621 xM = 0.5 * (x0 + x1);
1622 yM = 0.5 * (y0 + y1);
1624 // the four corner colors are close (or we hit the recursive limit)
1625 // -- fill the rectangle; but require at least one subdivision
1626 // (depth==0) to avoid problems when the four outer corners of the
1627 // shaded region are the same color
1628 if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
1630 // use the center color
1631 shading->getColor(xM, yM, &fillColor);
1632 state->setFillColor(&fillColor);
1634 // fill the rectangle
1635 state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
1636 x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
1637 state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
1638 x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
1639 state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
1640 x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
1641 state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
1642 x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
1643 state->closePath();
1644 builder->addPath(state, true, false);
1645 state->clearPath();
1647 // the four corner colors are not close enough -- subdivide the
1648 // rectangle
1649 } else {
1651 // colors[0] colorM0 colors[2]
1652 // (x0,y0) (xM,y0) (x1,y0)
1653 // +----------+----------+
1654 // | | |
1655 // | UL | UR |
1656 // color0M | colorMM | color1M
1657 // (x0,yM) +----------+----------+ (x1,yM)
1658 // | (xM,yM) |
1659 // | LL | LR |
1660 // | | |
1661 // +----------+----------+
1662 // colors[1] colorM1 colors[3]
1663 // (x0,y1) (xM,y1) (x1,y1)
1665 shading->getColor(x0, yM, &color0M);
1666 shading->getColor(x1, yM, &color1M);
1667 shading->getColor(xM, y0, &colorM0);
1668 shading->getColor(xM, y1, &colorM1);
1669 shading->getColor(xM, yM, &colorMM);
1671 // upper-left sub-rectangle
1672 colors2[0] = colors[0];
1673 colors2[1] = color0M;
1674 colors2[2] = colorM0;
1675 colors2[3] = colorMM;
1676 doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
1678 // lower-left sub-rectangle
1679 colors2[0] = color0M;
1680 colors2[1] = colors[1];
1681 colors2[2] = colorMM;
1682 colors2[3] = colorM1;
1683 doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
1685 // upper-right sub-rectangle
1686 colors2[0] = colorM0;
1687 colors2[1] = colorMM;
1688 colors2[2] = colors[2];
1689 colors2[3] = color1M;
1690 doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
1692 // lower-right sub-rectangle
1693 colors2[0] = colorMM;
1694 colors2[1] = colorM1;
1695 colors2[2] = color1M;
1696 colors2[3] = colors[3];
1697 doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
1698 }
1699 }
1701 void PdfParser::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
1702 double x0, y0, x1, y1, x2, y2;
1703 GfxColor color0, color1, color2;
1704 int i;
1706 for (i = 0; i < shading->getNTriangles(); ++i) {
1707 shading->getTriangle(i, &x0, &y0, &color0,
1708 &x1, &y1, &color1,
1709 &x2, &y2, &color2);
1710 gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
1711 shading->getColorSpace()->getNComps(), 0);
1712 }
1713 }
1715 void PdfParser::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
1716 double x1, double y1, GfxColor *color1,
1717 double x2, double y2, GfxColor *color2,
1718 int nComps, int depth) {
1719 double x01, y01, x12, y12, x20, y20;
1720 GfxColor color01, color12, color20;
1721 int i;
1723 for (i = 0; i < nComps; ++i) {
1724 if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
1725 abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
1726 break;
1727 }
1728 }
1729 if (i == nComps || depth == gouraudMaxDepth) {
1730 state->setFillColor(color0);
1731 state->moveTo(x0, y0);
1732 state->lineTo(x1, y1);
1733 state->lineTo(x2, y2);
1734 state->closePath();
1735 builder->addPath(state, true, false);
1736 state->clearPath();
1737 } else {
1738 x01 = 0.5 * (x0 + x1);
1739 y01 = 0.5 * (y0 + y1);
1740 x12 = 0.5 * (x1 + x2);
1741 y12 = 0.5 * (y1 + y2);
1742 x20 = 0.5 * (x2 + x0);
1743 y20 = 0.5 * (y2 + y0);
1744 //~ if the shading has a Function, this should interpolate on the
1745 //~ function parameter, not on the color components
1746 for (i = 0; i < nComps; ++i) {
1747 color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
1748 color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
1749 color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
1750 }
1751 gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
1752 x20, y20, &color20, nComps, depth + 1);
1753 gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
1754 x12, y12, &color12, nComps, depth + 1);
1755 gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
1756 x20, y20, &color20, nComps, depth + 1);
1757 gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
1758 x2, y2, color2, nComps, depth + 1);
1759 }
1760 }
1762 void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) {
1763 int start, i;
1765 if (shading->getNPatches() > 128) {
1766 start = 3;
1767 } else if (shading->getNPatches() > 64) {
1768 start = 2;
1769 } else if (shading->getNPatches() > 16) {
1770 start = 1;
1771 } else {
1772 start = 0;
1773 }
1774 for (i = 0; i < shading->getNPatches(); ++i) {
1775 fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
1776 start);
1777 }
1778 }
1780 void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) {
1781 GfxPatch patch00, patch01, patch10, patch11;
1782 double xx[4][8], yy[4][8];
1783 double xxm, yym;
1784 int i;
1786 for (i = 0; i < nComps; ++i) {
1787 if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
1788 > patchColorDelta ||
1789 abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
1790 > patchColorDelta ||
1791 abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
1792 > patchColorDelta ||
1793 abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
1794 > patchColorDelta) {
1795 break;
1796 }
1797 }
1798 if (i == nComps || depth == patchMaxDepth) {
1799 state->setFillColor(&patch->color[0][0]);
1800 state->moveTo(patch->x[0][0], patch->y[0][0]);
1801 state->curveTo(patch->x[0][1], patch->y[0][1],
1802 patch->x[0][2], patch->y[0][2],
1803 patch->x[0][3], patch->y[0][3]);
1804 state->curveTo(patch->x[1][3], patch->y[1][3],
1805 patch->x[2][3], patch->y[2][3],
1806 patch->x[3][3], patch->y[3][3]);
1807 state->curveTo(patch->x[3][2], patch->y[3][2],
1808 patch->x[3][1], patch->y[3][1],
1809 patch->x[3][0], patch->y[3][0]);
1810 state->curveTo(patch->x[2][0], patch->y[2][0],
1811 patch->x[1][0], patch->y[1][0],
1812 patch->x[0][0], patch->y[0][0]);
1813 state->closePath();
1814 builder->addPath(state, true, false);
1815 state->clearPath();
1816 } else {
1817 for (i = 0; i < 4; ++i) {
1818 xx[i][0] = patch->x[i][0];
1819 yy[i][0] = patch->y[i][0];
1820 xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
1821 yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
1822 xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
1823 yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
1824 xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
1825 yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
1826 xx[i][2] = 0.5 * (xx[i][1] + xxm);
1827 yy[i][2] = 0.5 * (yy[i][1] + yym);
1828 xx[i][5] = 0.5 * (xxm + xx[i][6]);
1829 yy[i][5] = 0.5 * (yym + yy[i][6]);
1830 xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
1831 yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
1832 xx[i][7] = patch->x[i][3];
1833 yy[i][7] = patch->y[i][3];
1834 }
1835 for (i = 0; i < 4; ++i) {
1836 patch00.x[0][i] = xx[0][i];
1837 patch00.y[0][i] = yy[0][i];
1838 patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
1839 patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
1840 xxm = 0.5 * (xx[1][i] + xx[2][i]);
1841 yym = 0.5 * (yy[1][i] + yy[2][i]);
1842 patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
1843 patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
1844 patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
1845 patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
1846 patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
1847 patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
1848 patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
1849 patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
1850 patch10.x[0][i] = patch00.x[3][i];
1851 patch10.y[0][i] = patch00.y[3][i];
1852 patch10.x[3][i] = xx[3][i];
1853 patch10.y[3][i] = yy[3][i];
1854 }
1855 for (i = 4; i < 8; ++i) {
1856 patch01.x[0][i-4] = xx[0][i];
1857 patch01.y[0][i-4] = yy[0][i];
1858 patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
1859 patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
1860 xxm = 0.5 * (xx[1][i] + xx[2][i]);
1861 yym = 0.5 * (yy[1][i] + yy[2][i]);
1862 patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
1863 patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
1864 patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
1865 patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
1866 patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
1867 patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
1868 patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
1869 patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
1870 patch11.x[0][i-4] = patch01.x[3][i-4];
1871 patch11.y[0][i-4] = patch01.y[3][i-4];
1872 patch11.x[3][i-4] = xx[3][i];
1873 patch11.y[3][i-4] = yy[3][i];
1874 }
1875 //~ if the shading has a Function, this should interpolate on the
1876 //~ function parameter, not on the color components
1877 for (i = 0; i < nComps; ++i) {
1878 patch00.color[0][0].c[i] = patch->color[0][0].c[i];
1879 patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
1880 patch->color[0][1].c[i]) / 2;
1881 patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
1882 patch01.color[0][1].c[i] = patch->color[0][1].c[i];
1883 patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
1884 patch->color[1][1].c[i]) / 2;
1885 patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
1886 patch11.color[1][1].c[i] = patch->color[1][1].c[i];
1887 patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
1888 patch->color[1][0].c[i]) / 2;
1889 patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
1890 patch10.color[1][0].c[i] = patch->color[1][0].c[i];
1891 patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
1892 patch->color[0][0].c[i]) / 2;
1893 patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
1894 patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
1895 patch01.color[1][1].c[i]) / 2;
1896 patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
1897 patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
1898 patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
1899 }
1900 fillPatch(&patch00, nComps, depth + 1);
1901 fillPatch(&patch10, nComps, depth + 1);
1902 fillPatch(&patch01, nComps, depth + 1);
1903 fillPatch(&patch11, nComps, depth + 1);
1904 }
1905 }
1907 void PdfParser::doEndPath() {
1908 if (state->isCurPt() && clip != clipNone) {
1909 state->clip();
1910 if (clip == clipNormal) {
1911 builder->clip(state);
1912 } else {
1913 builder->clip(state, true);
1914 }
1915 }
1916 clip = clipNone;
1917 state->clearPath();
1918 }
1920 //------------------------------------------------------------------------
1921 // path clipping operators
1922 //------------------------------------------------------------------------
1924 void PdfParser::opClip(Object args[], int numArgs) {
1925 clip = clipNormal;
1926 }
1928 void PdfParser::opEOClip(Object args[], int numArgs) {
1929 clip = clipEO;
1930 }
1932 //------------------------------------------------------------------------
1933 // text object operators
1934 //------------------------------------------------------------------------
1936 void PdfParser::opBeginText(Object args[], int numArgs) {
1937 state->setTextMat(1, 0, 0, 1, 0, 0);
1938 state->textMoveTo(0, 0);
1939 fontChanged = gTrue;
1940 builder->beginTextObject(state);
1941 }
1943 void PdfParser::opEndText(Object args[], int numArgs) {
1944 builder->endTextObject(state);
1945 }
1947 //------------------------------------------------------------------------
1948 // text state operators
1949 //------------------------------------------------------------------------
1951 void PdfParser::opSetCharSpacing(Object args[], int numArgs) {
1952 state->setCharSpace(args[0].getNum());
1953 }
1955 void PdfParser::opSetFont(Object args[], int numArgs) {
1956 GfxFont *font;
1958 if (!(font = res->lookupFont(args[0].getName()))) {
1959 return;
1960 }
1961 if (printCommands) {
1962 printf(" font: tag=%s name='%s' %g\n",
1963 font->getTag()->getCString(),
1964 font->getName() ? font->getName()->getCString() : "???",
1965 args[1].getNum());
1966 fflush(stdout);
1967 }
1969 font->incRefCnt();
1970 state->setFont(font, args[1].getNum());
1971 fontChanged = gTrue;
1972 }
1974 void PdfParser::opSetTextLeading(Object args[], int numArgs) {
1975 state->setLeading(args[0].getNum());
1976 }
1978 void PdfParser::opSetTextRender(Object args[], int numArgs) {
1979 state->setRender(args[0].getInt());
1980 builder->updateStyle(state);
1981 }
1983 void PdfParser::opSetTextRise(Object args[], int numArgs) {
1984 state->setRise(args[0].getNum());
1985 }
1987 void PdfParser::opSetWordSpacing(Object args[], int numArgs) {
1988 state->setWordSpace(args[0].getNum());
1989 }
1991 void PdfParser::opSetHorizScaling(Object args[], int numArgs) {
1992 state->setHorizScaling(args[0].getNum());
1993 fontChanged = gTrue;
1994 }
1996 //------------------------------------------------------------------------
1997 // text positioning operators
1998 //------------------------------------------------------------------------
2000 void PdfParser::opTextMove(Object args[], int numArgs) {
2001 double tx, ty;
2003 tx = state->getLineX() + args[0].getNum();
2004 ty = state->getLineY() + args[1].getNum();
2005 state->textMoveTo(tx, ty);
2006 }
2008 void PdfParser::opTextMoveSet(Object args[], int numArgs) {
2009 double tx, ty;
2011 tx = state->getLineX() + args[0].getNum();
2012 ty = args[1].getNum();
2013 state->setLeading(-ty);
2014 ty += state->getLineY();
2015 state->textMoveTo(tx, ty);
2016 }
2018 void PdfParser::opSetTextMatrix(Object args[], int numArgs) {
2019 state->setTextMat(args[0].getNum(), args[1].getNum(),
2020 args[2].getNum(), args[3].getNum(),
2021 args[4].getNum(), args[5].getNum());
2022 state->textMoveTo(0, 0);
2023 fontChanged = gTrue;
2024 }
2026 void PdfParser::opTextNextLine(Object args[], int numArgs) {
2027 double tx, ty;
2029 tx = state->getLineX();
2030 ty = state->getLineY() - state->getLeading();
2031 state->textMoveTo(tx, ty);
2032 }
2034 //------------------------------------------------------------------------
2035 // text string operators
2036 //------------------------------------------------------------------------
2038 void PdfParser::opShowText(Object args[], int numArgs) {
2039 if (!state->getFont()) {
2040 error(getPos(), "No font in show");
2041 return;
2042 }
2043 if (fontChanged) {
2044 builder->updateFont(state);
2045 fontChanged = gFalse;
2046 }
2047 doShowText(args[0].getString());
2048 }
2050 void PdfParser::opMoveShowText(Object args[], int numArgs) {
2051 double tx, ty;
2053 if (!state->getFont()) {
2054 error(getPos(), "No font in move/show");
2055 return;
2056 }
2057 if (fontChanged) {
2058 builder->updateFont(state);
2059 fontChanged = gFalse;
2060 }
2061 tx = state->getLineX();
2062 ty = state->getLineY() - state->getLeading();
2063 state->textMoveTo(tx, ty);
2064 doShowText(args[0].getString());
2065 }
2067 void PdfParser::opMoveSetShowText(Object args[], int numArgs) {
2068 double tx, ty;
2070 if (!state->getFont()) {
2071 error(getPos(), "No font in move/set/show");
2072 return;
2073 }
2074 if (fontChanged) {
2075 builder->updateFont(state);
2076 fontChanged = gFalse;
2077 }
2078 state->setWordSpace(args[0].getNum());
2079 state->setCharSpace(args[1].getNum());
2080 tx = state->getLineX();
2081 ty = state->getLineY() - state->getLeading();
2082 state->textMoveTo(tx, ty);
2083 doShowText(args[2].getString());
2084 }
2086 void PdfParser::opShowSpaceText(Object args[], int numArgs) {
2087 Array *a;
2088 Object obj;
2089 int wMode;
2090 int i;
2092 if (!state->getFont()) {
2093 error(getPos(), "No font in show/space");
2094 return;
2095 }
2096 if (fontChanged) {
2097 builder->updateFont(state);
2098 fontChanged = gFalse;
2099 }
2100 wMode = state->getFont()->getWMode();
2101 a = args[0].getArray();
2102 for (i = 0; i < a->getLength(); ++i) {
2103 a->get(i, &obj);
2104 if (obj.isNum()) {
2105 // this uses the absolute value of the font size to match
2106 // Acrobat's behavior
2107 if (wMode) {
2108 state->textShift(0, -obj.getNum() * 0.001 *
2109 fabs(state->getFontSize()));
2110 } else {
2111 state->textShift(-obj.getNum() * 0.001 *
2112 fabs(state->getFontSize()), 0);
2113 }
2114 } else if (obj.isString()) {
2115 doShowText(obj.getString());
2116 } else {
2117 error(getPos(), "Element of show/space array must be number or string");
2118 }
2119 obj.free();
2120 }
2121 }
2123 void PdfParser::doShowText(GooString *s) {
2124 GfxFont *font;
2125 int wMode;
2126 double riseX, riseY;
2127 CharCode code;
2128 Unicode u[8];
2129 double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
2130 double originX, originY, tOriginX, tOriginY;
2131 double oldCTM[6], newCTM[6];
2132 double *mat;
2133 Object charProc;
2134 Dict *resDict;
2135 Parser *oldParser;
2136 char *p;
2137 int len, n, uLen, nChars, nSpaces, i;
2139 font = state->getFont();
2140 wMode = font->getWMode();
2142 builder->beginString(state, s);
2144 // handle a Type 3 char
2145 if (font->getType() == fontType3 && 0) {//out->interpretType3Chars()) {
2146 mat = state->getCTM();
2147 for (i = 0; i < 6; ++i) {
2148 oldCTM[i] = mat[i];
2149 }
2150 mat = state->getTextMat();
2151 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
2152 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
2153 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
2154 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
2155 mat = font->getFontMatrix();
2156 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
2157 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
2158 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
2159 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
2160 newCTM[0] *= state->getFontSize();
2161 newCTM[1] *= state->getFontSize();
2162 newCTM[2] *= state->getFontSize();
2163 newCTM[3] *= state->getFontSize();
2164 newCTM[0] *= state->getHorizScaling();
2165 newCTM[2] *= state->getHorizScaling();
2166 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2167 curX = state->getCurX();
2168 curY = state->getCurY();
2169 lineX = state->getLineX();
2170 lineY = state->getLineY();
2171 oldParser = parser;
2172 p = s->getCString();
2173 len = s->getLength();
2174 while (len > 0) {
2175 n = font->getNextChar(p, len, &code,
2176 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2177 &dx, &dy, &originX, &originY);
2178 dx = dx * state->getFontSize() + state->getCharSpace();
2179 if (n == 1 && *p == ' ') {
2180 dx += state->getWordSpace();
2181 }
2182 dx *= state->getHorizScaling();
2183 dy *= state->getFontSize();
2184 state->textTransformDelta(dx, dy, &tdx, &tdy);
2185 state->transform(curX + riseX, curY + riseY, &x, &y);
2186 saveState();
2187 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
2188 //~ the CTM concat values here are wrong (but never used)
2189 //out->updateCTM(state, 1, 0, 0, 1, 0, 0);
2190 if (0){ /*!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
2191 code, u, uLen)) {*/
2192 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
2193 if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
2194 pushResources(resDict);
2195 }
2196 if (charProc.isStream()) {
2197 //parse(&charProc, gFalse); // TODO: parse into SVG font
2198 } else {
2199 error(getPos(), "Missing or bad Type3 CharProc entry");
2200 }
2201 //out->endType3Char(state);
2202 if (resDict) {
2203 popResources();
2204 }
2205 charProc.free();
2206 }
2207 restoreState();
2208 // GfxState::restore() does *not* restore the current position,
2209 // so we deal with it here using (curX, curY) and (lineX, lineY)
2210 curX += tdx;
2211 curY += tdy;
2212 state->moveTo(curX, curY);
2213 state->textSetPos(lineX, lineY);
2214 p += n;
2215 len -= n;
2216 }
2217 parser = oldParser;
2219 } else {
2220 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
2221 p = s->getCString();
2222 len = s->getLength();
2223 while (len > 0) {
2224 n = font->getNextChar(p, len, &code,
2225 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
2226 &dx, &dy, &originX, &originY);
2228 if (wMode) {
2229 dx *= state->getFontSize();
2230 dy = dy * state->getFontSize() + state->getCharSpace();
2231 if (n == 1 && *p == ' ') {
2232 dy += state->getWordSpace();
2233 }
2234 } else {
2235 dx = dx * state->getFontSize() + state->getCharSpace();
2236 if (n == 1 && *p == ' ') {
2237 dx += state->getWordSpace();
2238 }
2239 dx *= state->getHorizScaling();
2240 dy *= state->getFontSize();
2241 }
2242 state->textTransformDelta(dx, dy, &tdx, &tdy);
2243 originX *= state->getFontSize();
2244 originY *= state->getFontSize();
2245 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
2246 builder->addChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
2247 tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
2248 state->shift(tdx, tdy);
2249 p += n;
2250 len -= n;
2251 }
2252 }
2254 builder->endString(state);
2255 }
2257 //------------------------------------------------------------------------
2258 // XObject operators
2259 //------------------------------------------------------------------------
2261 void PdfParser::opXObject(Object args[], int numArgs) {
2262 char *name;
2263 Object obj1, obj2, obj3, refObj;
2265 name = args[0].getName();
2266 if (!res->lookupXObject(name, &obj1)) {
2267 return;
2268 }
2269 if (!obj1.isStream()) {
2270 error(getPos(), "XObject '%s' is wrong type", name);
2271 obj1.free();
2272 return;
2273 }
2274 obj1.streamGetDict()->lookup("Subtype", &obj2);
2275 if (obj2.isName("Image")) {
2276 res->lookupXObjectNF(name, &refObj);
2277 doImage(&refObj, obj1.getStream(), gFalse);
2278 refObj.free();
2279 } else if (obj2.isName("Form")) {
2280 doForm(&obj1);
2281 } else if (obj2.isName("PS")) {
2282 obj1.streamGetDict()->lookup("Level1", &obj3);
2283 /* out->psXObject(obj1.getStream(),
2284 obj3.isStream() ? obj3.getStream() : (Stream *)NULL);*/
2285 } else if (obj2.isName()) {
2286 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
2287 } else {
2288 error(getPos(), "XObject subtype is missing or wrong type");
2289 }
2290 obj2.free();
2291 obj1.free();
2292 }
2294 void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) {
2295 Dict *dict, *maskDict;
2296 int width, height;
2297 int bits, maskBits;
2298 StreamColorSpaceMode csMode;
2299 GBool mask;
2300 GBool invert;
2301 GfxColorSpace *colorSpace, *maskColorSpace;
2302 GfxImageColorMap *colorMap, *maskColorMap;
2303 Object maskObj, smaskObj;
2304 GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
2305 int maskColors[2*gfxColorMaxComps];
2306 int maskWidth, maskHeight;
2307 GBool maskInvert;
2308 Stream *maskStr;
2309 Object obj1, obj2;
2310 int i;
2312 // get info from the stream
2313 bits = 0;
2314 csMode = streamCSNone;
2315 str->getImageParams(&bits, &csMode);
2317 // get stream dict
2318 dict = str->getDict();
2320 // get size
2321 dict->lookup("Width", &obj1);
2322 if (obj1.isNull()) {
2323 obj1.free();
2324 dict->lookup("W", &obj1);
2325 }
2326 if (obj1.isInt())
2327 width = obj1.getInt();
2328 else if (obj1.isReal())
2329 width = (int)obj1.getReal();
2330 else
2331 goto err2;
2332 obj1.free();
2333 dict->lookup("Height", &obj1);
2334 if (obj1.isNull()) {
2335 obj1.free();
2336 dict->lookup("H", &obj1);
2337 }
2338 if (obj1.isInt())
2339 height = obj1.getInt();
2340 else if (obj1.isReal())
2341 height = (int)obj1.getReal();
2342 else
2343 goto err2;
2344 obj1.free();
2346 // image or mask?
2347 dict->lookup("ImageMask", &obj1);
2348 if (obj1.isNull()) {
2349 obj1.free();
2350 dict->lookup("IM", &obj1);
2351 }
2352 mask = gFalse;
2353 if (obj1.isBool())
2354 mask = obj1.getBool();
2355 else if (!obj1.isNull())
2356 goto err2;
2357 obj1.free();
2359 // bit depth
2360 if (bits == 0) {
2361 dict->lookup("BitsPerComponent", &obj1);
2362 if (obj1.isNull()) {
2363 obj1.free();
2364 dict->lookup("BPC", &obj1);
2365 }
2366 if (obj1.isInt()) {
2367 bits = obj1.getInt();
2368 } else if (mask) {
2369 bits = 1;
2370 } else {
2371 goto err2;
2372 }
2373 obj1.free();
2374 }
2376 // display a mask
2377 if (mask) {
2379 // check for inverted mask
2380 if (bits != 1)
2381 goto err1;
2382 invert = gFalse;
2383 dict->lookup("Decode", &obj1);
2384 if (obj1.isNull()) {
2385 obj1.free();
2386 dict->lookup("D", &obj1);
2387 }
2388 if (obj1.isArray()) {
2389 obj1.arrayGet(0, &obj2);
2390 if (obj2.isInt() && obj2.getInt() == 1)
2391 invert = gTrue;
2392 obj2.free();
2393 } else if (!obj1.isNull()) {
2394 goto err2;
2395 }
2396 obj1.free();
2398 // draw it
2399 //out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2401 } else {
2403 // get color space and color map
2404 dict->lookup("ColorSpace", &obj1);
2405 if (obj1.isNull()) {
2406 obj1.free();
2407 dict->lookup("CS", &obj1);
2408 }
2409 if (obj1.isName()) {
2410 res->lookupColorSpace(obj1.getName(), &obj2);
2411 if (!obj2.isNull()) {
2412 obj1.free();
2413 obj1 = obj2;
2414 } else {
2415 obj2.free();
2416 }
2417 }
2418 if (!obj1.isNull()) {
2419 colorSpace = GfxColorSpace::parse(&obj1);
2420 } else if (csMode == streamCSDeviceGray) {
2421 colorSpace = new GfxDeviceGrayColorSpace();
2422 } else if (csMode == streamCSDeviceRGB) {
2423 colorSpace = new GfxDeviceRGBColorSpace();
2424 } else if (csMode == streamCSDeviceCMYK) {
2425 colorSpace = new GfxDeviceCMYKColorSpace();
2426 } else {
2427 colorSpace = NULL;
2428 }
2429 obj1.free();
2430 if (!colorSpace) {
2431 goto err1;
2432 }
2433 dict->lookup("Decode", &obj1);
2434 if (obj1.isNull()) {
2435 obj1.free();
2436 dict->lookup("D", &obj1);
2437 }
2438 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2439 obj1.free();
2440 if (!colorMap->isOk()) {
2441 delete colorMap;
2442 goto err1;
2443 }
2445 // get the mask
2446 haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
2447 maskStr = NULL; // make gcc happy
2448 maskWidth = maskHeight = 0; // make gcc happy
2449 maskInvert = gFalse; // make gcc happy
2450 maskColorMap = NULL; // make gcc happy
2451 dict->lookup("Mask", &maskObj);
2452 dict->lookup("SMask", &smaskObj);
2453 if (smaskObj.isStream()) {
2454 // soft mask
2455 if (inlineImg) {
2456 goto err1;
2457 }
2458 maskStr = smaskObj.getStream();
2459 maskDict = smaskObj.streamGetDict();
2460 maskDict->lookup("Width", &obj1);
2461 if (obj1.isNull()) {
2462 obj1.free();
2463 maskDict->lookup("W", &obj1);
2464 }
2465 if (!obj1.isInt()) {
2466 goto err2;
2467 }
2468 maskWidth = obj1.getInt();
2469 obj1.free();
2470 maskDict->lookup("Height", &obj1);
2471 if (obj1.isNull()) {
2472 obj1.free();
2473 maskDict->lookup("H", &obj1);
2474 }
2475 if (!obj1.isInt()) {
2476 goto err2;
2477 }
2478 maskHeight = obj1.getInt();
2479 obj1.free();
2480 maskDict->lookup("BitsPerComponent", &obj1);
2481 if (obj1.isNull()) {
2482 obj1.free();
2483 maskDict->lookup("BPC", &obj1);
2484 }
2485 if (!obj1.isInt()) {
2486 goto err2;
2487 }
2488 maskBits = obj1.getInt();
2489 obj1.free();
2490 maskDict->lookup("ColorSpace", &obj1);
2491 if (obj1.isNull()) {
2492 obj1.free();
2493 maskDict->lookup("CS", &obj1);
2494 }
2495 if (obj1.isName()) {
2496 res->lookupColorSpace(obj1.getName(), &obj2);
2497 if (!obj2.isNull()) {
2498 obj1.free();
2499 obj1 = obj2;
2500 } else {
2501 obj2.free();
2502 }
2503 }
2504 maskColorSpace = GfxColorSpace::parse(&obj1);
2505 obj1.free();
2506 if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
2507 goto err1;
2508 }
2509 maskDict->lookup("Decode", &obj1);
2510 if (obj1.isNull()) {
2511 obj1.free();
2512 maskDict->lookup("D", &obj1);
2513 }
2514 maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
2515 obj1.free();
2516 if (!maskColorMap->isOk()) {
2517 delete maskColorMap;
2518 goto err1;
2519 }
2520 //~ handle the Matte entry
2521 haveSoftMask = gTrue;
2522 } else if (maskObj.isArray()) {
2523 // color key mask
2524 for (i = 0;
2525 i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
2526 ++i) {
2527 maskObj.arrayGet(i, &obj1);
2528 maskColors[i] = obj1.getInt();
2529 obj1.free();
2530 }
2531 haveColorKeyMask = gTrue;
2532 } else if (maskObj.isStream()) {
2533 // explicit mask
2534 if (inlineImg) {
2535 goto err1;
2536 }
2537 maskStr = maskObj.getStream();
2538 maskDict = maskObj.streamGetDict();
2539 maskDict->lookup("Width", &obj1);
2540 if (obj1.isNull()) {
2541 obj1.free();
2542 maskDict->lookup("W", &obj1);
2543 }
2544 if (!obj1.isInt()) {
2545 goto err2;
2546 }
2547 maskWidth = obj1.getInt();
2548 obj1.free();
2549 maskDict->lookup("Height", &obj1);
2550 if (obj1.isNull()) {
2551 obj1.free();
2552 maskDict->lookup("H", &obj1);
2553 }
2554 if (!obj1.isInt()) {
2555 goto err2;
2556 }
2557 maskHeight = obj1.getInt();
2558 obj1.free();
2559 maskDict->lookup("ImageMask", &obj1);
2560 if (obj1.isNull()) {
2561 obj1.free();
2562 maskDict->lookup("IM", &obj1);
2563 }
2564 if (!obj1.isBool() || !obj1.getBool()) {
2565 goto err2;
2566 }
2567 obj1.free();
2568 maskInvert = gFalse;
2569 maskDict->lookup("Decode", &obj1);
2570 if (obj1.isNull()) {
2571 obj1.free();
2572 maskDict->lookup("D", &obj1);
2573 }
2574 if (obj1.isArray()) {
2575 obj1.arrayGet(0, &obj2);
2576 if (obj2.isInt() && obj2.getInt() == 1) {
2577 maskInvert = gTrue;
2578 }
2579 obj2.free();
2580 } else if (!obj1.isNull()) {
2581 goto err2;
2582 }
2583 obj1.free();
2584 haveExplicitMask = gTrue;
2585 }
2587 // draw it
2588 if (haveSoftMask) {
2589 /* out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
2590 maskStr, maskWidth, maskHeight, maskColorMap);*/
2591 delete maskColorMap;
2592 } else if (haveExplicitMask) {
2593 /* out->drawMaskedImage(state, ref, str, width, height, colorMap,
2594 maskStr, maskWidth, maskHeight, maskInvert);*/
2595 } else {
2596 builder->addImage(state, str, width, height, colorMap,
2597 haveColorKeyMask ? maskColors : (int *)NULL);
2598 }
2599 delete colorMap;
2601 maskObj.free();
2602 smaskObj.free();
2603 }
2605 return;
2607 err2:
2608 obj1.free();
2609 err1:
2610 error(getPos(), "Bad image parameters");
2611 }
2613 void PdfParser::doForm(Object *str) {
2614 Dict *dict;
2615 GBool transpGroup, isolated, knockout;
2616 GfxColorSpace *blendingColorSpace;
2617 Object matrixObj, bboxObj;
2618 double m[6], bbox[4];
2619 Object resObj;
2620 Dict *resDict;
2621 Object obj1, obj2, obj3;
2622 int i;
2624 // check for excessive recursion
2625 if (formDepth > 20) {
2626 return;
2627 }
2629 // get stream dict
2630 dict = str->streamGetDict();
2632 // check form type
2633 dict->lookup("FormType", &obj1);
2634 if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
2635 error(getPos(), "Unknown form type");
2636 }
2637 obj1.free();
2639 // get bounding box
2640 dict->lookup("BBox", &bboxObj);
2641 if (!bboxObj.isArray()) {
2642 bboxObj.free();
2643 error(getPos(), "Bad form bounding box");
2644 return;
2645 }
2646 for (i = 0; i < 4; ++i) {
2647 bboxObj.arrayGet(i, &obj1);
2648 bbox[i] = obj1.getNum();
2649 obj1.free();
2650 }
2651 bboxObj.free();
2653 // get matrix
2654 dict->lookup("Matrix", &matrixObj);
2655 if (matrixObj.isArray()) {
2656 for (i = 0; i < 6; ++i) {
2657 matrixObj.arrayGet(i, &obj1);
2658 m[i] = obj1.getNum();
2659 obj1.free();
2660 }
2661 } else {
2662 m[0] = 1; m[1] = 0;
2663 m[2] = 0; m[3] = 1;
2664 m[4] = 0; m[5] = 0;
2665 }
2666 matrixObj.free();
2668 // get resources
2669 dict->lookup("Resources", &resObj);
2670 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2672 // check for a transparency group
2673 transpGroup = isolated = knockout = gFalse;
2674 blendingColorSpace = NULL;
2675 if (dict->lookup("Group", &obj1)->isDict()) {
2676 if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
2677 transpGroup = gTrue;
2678 if (!obj1.dictLookup("CS", &obj3)->isNull()) {
2679 blendingColorSpace = GfxColorSpace::parse(&obj3);
2680 }
2681 obj3.free();
2682 if (obj1.dictLookup("I", &obj3)->isBool()) {
2683 isolated = obj3.getBool();
2684 }
2685 obj3.free();
2686 if (obj1.dictLookup("K", &obj3)->isBool()) {
2687 knockout = obj3.getBool();
2688 }
2689 obj3.free();
2690 }
2691 obj2.free();
2692 }
2693 obj1.free();
2695 // draw it
2696 ++formDepth;
2697 doForm1(str, resDict, m, bbox,
2698 transpGroup, gFalse, blendingColorSpace, isolated, knockout);
2699 --formDepth;
2701 if (blendingColorSpace) {
2702 delete blendingColorSpace;
2703 }
2704 resObj.free();
2705 }
2707 void PdfParser::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
2708 GBool transpGroup, GBool softMask,
2709 GfxColorSpace *blendingColorSpace,
2710 GBool isolated, GBool knockout,
2711 GBool alpha, Function *transferFunc,
2712 GfxColor *backdropColor) {
2713 Parser *oldParser;
2714 double oldBaseMatrix[6];
2715 int i;
2717 // push new resources on stack
2718 pushResources(resDict);
2720 // save current graphics state
2721 saveState();
2723 // kill any pre-existing path
2724 state->clearPath();
2726 // save current parser
2727 oldParser = parser;
2729 // set form transformation matrix
2730 state->concatCTM(matrix[0], matrix[1], matrix[2],
2731 matrix[3], matrix[4], matrix[5]);
2732 builder->setTransform(matrix[0], matrix[1], matrix[2],
2733 matrix[3], matrix[4], matrix[5]);
2735 // set form bounding box
2736 state->moveTo(bbox[0], bbox[1]);
2737 state->lineTo(bbox[2], bbox[1]);
2738 state->lineTo(bbox[2], bbox[3]);
2739 state->lineTo(bbox[0], bbox[3]);
2740 state->closePath();
2741 state->clip();
2742 builder->clip(state);
2743 state->clearPath();
2745 if (softMask || transpGroup) {
2746 if (state->getBlendMode() != gfxBlendNormal) {
2747 state->setBlendMode(gfxBlendNormal);
2748 }
2749 if (state->getFillOpacity() != 1) {
2750 state->setFillOpacity(1);
2751 }
2752 if (state->getStrokeOpacity() != 1) {
2753 state->setStrokeOpacity(1);
2754 }
2755 //out->clearSoftMask(state);
2756 //out->beginTransparencyGroup(state, bbox, blendingColorSpace,
2757 // isolated, knockout, softMask);
2758 }
2760 // set new base matrix
2761 for (i = 0; i < 6; ++i) {
2762 oldBaseMatrix[i] = baseMatrix[i];
2763 baseMatrix[i] = state->getCTM()[i];
2764 }
2766 // draw the form
2767 parse(str, gFalse);
2769 if (softMask || transpGroup) {
2770 //out->endTransparencyGroup(state);
2771 }
2773 // restore base matrix
2774 for (i = 0; i < 6; ++i) {
2775 baseMatrix[i] = oldBaseMatrix[i];
2776 }
2778 // restore parser
2779 parser = oldParser;
2781 // restore graphics state
2782 restoreState();
2784 // pop resource stack
2785 popResources();
2787 if (softMask) {
2788 //out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
2789 } else if (transpGroup) {
2790 //out->paintTransparencyGroup(state, bbox);
2791 }
2793 return;
2794 }
2796 //------------------------------------------------------------------------
2797 // in-line image operators
2798 //------------------------------------------------------------------------
2800 void PdfParser::opBeginImage(Object args[], int numArgs) {
2801 Stream *str;
2802 int c1, c2;
2804 // build dict/stream
2805 str = buildImageStream();
2807 // display the image
2808 if (str) {
2809 doImage(NULL, str, gTrue);
2811 // skip 'EI' tag
2812 c1 = str->getUndecodedStream()->getChar();
2813 c2 = str->getUndecodedStream()->getChar();
2814 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2815 c1 = c2;
2816 c2 = str->getUndecodedStream()->getChar();
2817 }
2818 delete str;
2819 }
2820 }
2822 Stream *PdfParser::buildImageStream() {
2823 Object dict;
2824 Object obj;
2825 char *key;
2826 Stream *str;
2828 // build dictionary
2829 dict.initDict(xref);
2830 parser->getObj(&obj);
2831 while (!obj.isCmd("ID") && !obj.isEOF()) {
2832 if (!obj.isName()) {
2833 error(getPos(), "Inline image dictionary key must be a name object");
2834 obj.free();
2835 } else {
2836 key = copyString(obj.getName());
2837 obj.free();
2838 parser->getObj(&obj);
2839 if (obj.isEOF() || obj.isError()) {
2840 gfree(key);
2841 break;
2842 }
2843 dict.dictAdd(key, &obj);
2844 }
2845 parser->getObj(&obj);
2846 }
2847 if (obj.isEOF()) {
2848 error(getPos(), "End of file in inline image");
2849 obj.free();
2850 dict.free();
2851 return NULL;
2852 }
2853 obj.free();
2855 // make stream
2856 str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
2857 str = str->addFilters(&dict);
2859 return str;
2860 }
2862 void PdfParser::opImageData(Object args[], int numArgs) {
2863 error(getPos(), "Internal: got 'ID' operator");
2864 }
2866 void PdfParser::opEndImage(Object args[], int numArgs) {
2867 error(getPos(), "Internal: got 'EI' operator");
2868 }
2870 //------------------------------------------------------------------------
2871 // type 3 font operators
2872 //------------------------------------------------------------------------
2874 void PdfParser::opSetCharWidth(Object args[], int numArgs) {
2875 }
2877 void PdfParser::opSetCacheDevice(Object args[], int numArgs) {
2878 }
2880 //------------------------------------------------------------------------
2881 // compatibility operators
2882 //------------------------------------------------------------------------
2884 void PdfParser::opBeginIgnoreUndef(Object args[], int numArgs) {
2885 ++ignoreUndef;
2886 }
2888 void PdfParser::opEndIgnoreUndef(Object args[], int numArgs) {
2889 if (ignoreUndef > 0)
2890 --ignoreUndef;
2891 }
2893 //------------------------------------------------------------------------
2894 // marked content operators
2895 //------------------------------------------------------------------------
2897 void PdfParser::opBeginMarkedContent(Object args[], int numArgs) {
2898 if (printCommands) {
2899 printf(" marked content: %s ", args[0].getName());
2900 if (numArgs == 2)
2901 args[2].print(stdout);
2902 printf("\n");
2903 fflush(stdout);
2904 }
2906 if(numArgs == 2) {
2907 //out->beginMarkedContent(args[0].getName(),args[1].getDict());
2908 } else {
2909 //out->beginMarkedContent(args[0].getName());
2910 }
2911 }
2913 void PdfParser::opEndMarkedContent(Object args[], int numArgs) {
2914 //out->endMarkedContent();
2915 }
2917 void PdfParser::opMarkPoint(Object args[], int numArgs) {
2918 if (printCommands) {
2919 printf(" mark point: %s ", args[0].getName());
2920 if (numArgs == 2)
2921 args[2].print(stdout);
2922 printf("\n");
2923 fflush(stdout);
2924 }
2926 if(numArgs == 2) {
2927 //out->markPoint(args[0].getName(),args[1].getDict());
2928 } else {
2929 //out->markPoint(args[0].getName());
2930 }
2932 }
2934 //------------------------------------------------------------------------
2935 // misc
2936 //------------------------------------------------------------------------
2938 void PdfParser::saveState() {
2939 builder->saveState();
2940 state = state->save();
2941 }
2943 void PdfParser::restoreState() {
2944 state = state->restore();
2945 builder->restoreState();
2946 }
2948 void PdfParser::pushResources(Dict *resDict) {
2949 res = new GfxResources(xref, resDict, res);
2950 }
2952 void PdfParser::popResources() {
2953 GfxResources *resPtr;
2955 resPtr = res->getNext();
2956 delete res;
2957 res = resPtr;
2958 }
2960 #endif /* HAVE_POPPLER */