summaryrefslogtreecommitdiff
path: root/noncore/unsupported/qpdf/xpdf/Gfx.cc
Unidiff
Diffstat (limited to 'noncore/unsupported/qpdf/xpdf/Gfx.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/unsupported/qpdf/xpdf/Gfx.cc2461
1 files changed, 2461 insertions, 0 deletions
diff --git a/noncore/unsupported/qpdf/xpdf/Gfx.cc b/noncore/unsupported/qpdf/xpdf/Gfx.cc
new file mode 100644
index 0000000..c19971c
--- a/dev/null
+++ b/noncore/unsupported/qpdf/xpdf/Gfx.cc
@@ -0,0 +1,2461 @@
1//========================================================================
2//
3// Gfx.cc
4//
5// Copyright 1996 Derek B. Noonburg
6//
7//========================================================================
8
9#ifdef __GNUC__
10#pragma implementation
11#endif
12
13#include <aconf.h>
14#include <stdio.h>
15#include <stddef.h>
16#include <string.h>
17#include <math.h>
18#include "gmem.h"
19#include "CharTypes.h"
20#include "Object.h"
21#include "Array.h"
22#include "Dict.h"
23#include "Stream.h"
24#include "Lexer.h"
25#include "Parser.h"
26#include "GfxFont.h"
27#include "GfxState.h"
28#include "OutputDev.h"
29#include "Page.h"
30#include "Error.h"
31#include "Gfx.h"
32
33//------------------------------------------------------------------------
34// constants
35//------------------------------------------------------------------------
36
37// Max number of splits along the t axis for an axial shading fill.
38#define axialMaxSplits 256
39
40// Max delta allowed in any color component for an axial shading fill.
41#define axialColorDelta (1 / 256.0)
42
43//------------------------------------------------------------------------
44// Operator table
45//------------------------------------------------------------------------
46
47Operator Gfx::opTab[] = {
48 {"\"", 3, {tchkNum, tchkNum, tchkString},
49 &Gfx::opMoveSetShowText},
50 {"'", 1, {tchkString},
51 &Gfx::opMoveShowText},
52 {"B", 0, {tchkNone},
53 &Gfx::opFillStroke},
54 {"B*", 0, {tchkNone},
55 &Gfx::opEOFillStroke},
56 {"BDC", 2, {tchkName, tchkProps},
57 &Gfx::opBeginMarkedContent},
58 {"BI", 0, {tchkNone},
59 &Gfx::opBeginImage},
60 {"BMC", 1, {tchkName},
61 &Gfx::opBeginMarkedContent},
62 {"BT", 0, {tchkNone},
63 &Gfx::opBeginText},
64 {"BX", 0, {tchkNone},
65 &Gfx::opBeginIgnoreUndef},
66 {"CS", 1, {tchkName},
67 &Gfx::opSetStrokeColorSpace},
68 {"DP", 2, {tchkName, tchkProps},
69 &Gfx::opMarkPoint},
70 {"Do", 1, {tchkName},
71 &Gfx::opXObject},
72 {"EI", 0, {tchkNone},
73 &Gfx::opEndImage},
74 {"EMC", 0, {tchkNone},
75 &Gfx::opEndMarkedContent},
76 {"ET", 0, {tchkNone},
77 &Gfx::opEndText},
78 {"EX", 0, {tchkNone},
79 &Gfx::opEndIgnoreUndef},
80 {"F", 0, {tchkNone},
81 &Gfx::opFill},
82 {"G", 1, {tchkNum},
83 &Gfx::opSetStrokeGray},
84 {"ID", 0, {tchkNone},
85 &Gfx::opImageData},
86 {"J", 1, {tchkInt},
87 &Gfx::opSetLineCap},
88 {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
89 &Gfx::opSetStrokeCMYKColor},
90 {"M", 1, {tchkNum},
91 &Gfx::opSetMiterLimit},
92 {"MP", 1, {tchkName},
93 &Gfx::opMarkPoint},
94 {"Q", 0, {tchkNone},
95 &Gfx::opRestore},
96 {"RG", 3, {tchkNum, tchkNum, tchkNum},
97 &Gfx::opSetStrokeRGBColor},
98 {"S", 0, {tchkNone},
99 &Gfx::opStroke},
100 {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
101 &Gfx::opSetStrokeColor},
102 {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
103 tchkSCN},
104 &Gfx::opSetStrokeColorN},
105 {"T*", 0, {tchkNone},
106 &Gfx::opTextNextLine},
107 {"TD", 2, {tchkNum, tchkNum},
108 &Gfx::opTextMoveSet},
109 {"TJ", 1, {tchkArray},
110 &Gfx::opShowSpaceText},
111 {"TL", 1, {tchkNum},
112 &Gfx::opSetTextLeading},
113 {"Tc", 1, {tchkNum},
114 &Gfx::opSetCharSpacing},
115 {"Td", 2, {tchkNum, tchkNum},
116 &Gfx::opTextMove},
117 {"Tf", 2, {tchkName, tchkNum},
118 &Gfx::opSetFont},
119 {"Tj", 1, {tchkString},
120 &Gfx::opShowText},
121 {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
122 tchkNum, tchkNum},
123 &Gfx::opSetTextMatrix},
124 {"Tr", 1, {tchkInt},
125 &Gfx::opSetTextRender},
126 {"Ts", 1, {tchkNum},
127 &Gfx::opSetTextRise},
128 {"Tw", 1, {tchkNum},
129 &Gfx::opSetWordSpacing},
130 {"Tz", 1, {tchkNum},
131 &Gfx::opSetHorizScaling},
132 {"W", 0, {tchkNone},
133 &Gfx::opClip},
134 {"W*", 0, {tchkNone},
135 &Gfx::opEOClip},
136 {"b", 0, {tchkNone},
137 &Gfx::opCloseFillStroke},
138 {"b*", 0, {tchkNone},
139 &Gfx::opCloseEOFillStroke},
140 {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
141 tchkNum, tchkNum},
142 &Gfx::opCurveTo},
143 {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
144 tchkNum, tchkNum},
145 &Gfx::opConcat},
146 {"cs", 1, {tchkName},
147 &Gfx::opSetFillColorSpace},
148 {"d", 2, {tchkArray, tchkNum},
149 &Gfx::opSetDash},
150 {"d0", 2, {tchkNum, tchkNum},
151 &Gfx::opSetCharWidth},
152 {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
153 tchkNum, tchkNum},
154 &Gfx::opSetCacheDevice},
155 {"f", 0, {tchkNone},
156 &Gfx::opFill},
157 {"f*", 0, {tchkNone},
158 &Gfx::opEOFill},
159 {"g", 1, {tchkNum},
160 &Gfx::opSetFillGray},
161 {"gs", 1, {tchkName},
162 &Gfx::opSetExtGState},
163 {"h", 0, {tchkNone},
164 &Gfx::opClosePath},
165 {"i", 1, {tchkNum},
166 &Gfx::opSetFlat},
167 {"j", 1, {tchkInt},
168 &Gfx::opSetLineJoin},
169 {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
170 &Gfx::opSetFillCMYKColor},
171 {"l", 2, {tchkNum, tchkNum},
172 &Gfx::opLineTo},
173 {"m", 2, {tchkNum, tchkNum},
174 &Gfx::opMoveTo},
175 {"n", 0, {tchkNone},
176 &Gfx::opEndPath},
177 {"q", 0, {tchkNone},
178 &Gfx::opSave},
179 {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
180 &Gfx::opRectangle},
181 {"rg", 3, {tchkNum, tchkNum, tchkNum},
182 &Gfx::opSetFillRGBColor},
183 {"ri", 1, {tchkName},
184 &Gfx::opSetRenderingIntent},
185 {"s", 0, {tchkNone},
186 &Gfx::opCloseStroke},
187 {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
188 &Gfx::opSetFillColor},
189 {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
190 tchkSCN},
191 &Gfx::opSetFillColorN},
192 {"sh", 1, {tchkName},
193 &Gfx::opShFill},
194 {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
195 &Gfx::opCurveTo1},
196 {"w", 1, {tchkNum},
197 &Gfx::opSetLineWidth},
198 {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
199 &Gfx::opCurveTo2},
200};
201
202#define numOps (sizeof(opTab) / sizeof(Operator))
203
204//------------------------------------------------------------------------
205// GfxResources
206//------------------------------------------------------------------------
207
208GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
209 Object obj1;
210
211 if (resDict) {
212
213 // build font dictionary
214 fonts = NULL;
215 resDict->lookup("Font", &obj1);
216 if (obj1.isDict()) {
217 fonts = new GfxFontDict(xref, obj1.getDict());
218 }
219 obj1.free();
220
221 // get XObject dictionary
222 resDict->lookup("XObject", &xObjDict);
223
224 // get color space dictionary
225 resDict->lookup("ColorSpace", &colorSpaceDict);
226
227 // get pattern dictionary
228 resDict->lookup("Pattern", &patternDict);
229
230 // get shading dictionary
231 resDict->lookup("Shading", &shadingDict);
232
233 // get graphics state parameter dictionary
234 resDict->lookup("ExtGState", &gStateDict);
235
236 } else {
237 fonts = NULL;
238 xObjDict.initNull();
239 colorSpaceDict.initNull();
240 patternDict.initNull();
241 gStateDict.initNull();
242 }
243
244 next = nextA;
245}
246
247GfxResources::~GfxResources() {
248 if (fonts) {
249 delete fonts;
250 }
251 xObjDict.free();
252 colorSpaceDict.free();
253 patternDict.free();
254 shadingDict.free();
255 gStateDict.free();
256}
257
258GfxFont *GfxResources::lookupFont(char *name) {
259 GfxFont *font;
260 GfxResources *resPtr;
261
262 for (resPtr = this; resPtr; resPtr = resPtr->next) {
263 if (resPtr->fonts) {
264 if ((font = resPtr->fonts->lookup(name)))
265 return font;
266 }
267 }
268 error(-1, "Unknown font tag '%s'", name);
269 return NULL;
270}
271
272GBool GfxResources::lookupXObject(char *name, Object *obj) {
273 GfxResources *resPtr;
274
275 for (resPtr = this; resPtr; resPtr = resPtr->next) {
276 if (resPtr->xObjDict.isDict()) {
277 if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
278 return gTrue;
279 obj->free();
280 }
281 }
282 error(-1, "XObject '%s' is unknown", name);
283 return gFalse;
284}
285
286GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
287 GfxResources *resPtr;
288
289 for (resPtr = this; resPtr; resPtr = resPtr->next) {
290 if (resPtr->xObjDict.isDict()) {
291 if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
292 return gTrue;
293 obj->free();
294 }
295 }
296 error(-1, "XObject '%s' is unknown", name);
297 return gFalse;
298}
299
300void GfxResources::lookupColorSpace(char *name, Object *obj) {
301 GfxResources *resPtr;
302
303 for (resPtr = this; resPtr; resPtr = resPtr->next) {
304 if (resPtr->colorSpaceDict.isDict()) {
305 if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
306 return;
307 }
308 obj->free();
309 }
310 }
311 obj->initNull();
312}
313
314GfxPattern *GfxResources::lookupPattern(char *name) {
315 GfxResources *resPtr;
316 GfxPattern *pattern;
317 Object obj;
318
319 for (resPtr = this; resPtr; resPtr = resPtr->next) {
320 if (resPtr->patternDict.isDict()) {
321 if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
322 pattern = GfxPattern::parse(&obj);
323 obj.free();
324 return pattern;
325 }
326 obj.free();
327 }
328 }
329 error(-1, "Unknown pattern '%s'", name);
330 return NULL;
331}
332
333GfxShading *GfxResources::lookupShading(char *name) {
334 GfxResources *resPtr;
335 GfxShading *shading;
336 Object obj;
337
338 for (resPtr = this; resPtr; resPtr = resPtr->next) {
339 if (resPtr->shadingDict.isDict()) {
340 if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
341 shading = GfxShading::parse(&obj);
342 obj.free();
343 return shading;
344 }
345 obj.free();
346 }
347 }
348 error(-1, "Unknown shading '%s'", name);
349 return NULL;
350}
351
352GBool GfxResources::lookupGState(char *name, Object *obj) {
353 GfxResources *resPtr;
354
355 for (resPtr = this; resPtr; resPtr = resPtr->next) {
356 if (resPtr->gStateDict.isDict()) {
357 if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
358 return gTrue;
359 }
360 obj->free();
361 }
362 }
363 error(-1, "ExtGState '%s' is unknown", name);
364 return gFalse;
365}
366
367//------------------------------------------------------------------------
368// Gfx
369//------------------------------------------------------------------------
370
371Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, fouble dpi,
372 PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
373 GBool printCommandsA) {
374 int i;
375
376 xref = xrefA;
377 printCommands = printCommandsA;
378
379 // start the resource stack
380 res = new GfxResources(xref, resDict, NULL);
381
382 // initialize
383 out = outA;
384 state = new GfxState(dpi, box, rotate, out->upsideDown());
385 fontChanged = gFalse;
386 clip = clipNone;
387 ignoreUndef = 0;
388 out->startPage(pageNum, state);
389 out->setDefaultCTM(state->getCTM());
390 out->updateAll(state);
391 for (i = 0; i < 6; ++i) {
392 baseMatrix[i] = state->getCTM()[i];
393 }
394
395 // set crop box
396 if (crop) {
397 state->moveTo(cropBox->x1, cropBox->y1);
398 state->lineTo(cropBox->x2, cropBox->y1);
399 state->lineTo(cropBox->x2, cropBox->y2);
400 state->lineTo(cropBox->x1, cropBox->y2);
401 state->closePath();
402 state->clip();
403 out->clip(state);
404 state->clearPath();
405 }
406}
407
408Gfx::~Gfx() {
409 GfxResources *resPtr;
410
411 while (state->hasSaves()) {
412 state = state->restore();
413 out->restoreState(state);
414 }
415 out->endPage();
416 while (res) {
417 resPtr = res->getNext();
418 delete res;
419 res = resPtr;
420 }
421 if (state)
422 delete state;
423}
424
425void Gfx::display(Object *obj, GBool topLevel) {
426 Object obj2;
427 int i;
428
429 if (obj->isArray()) {
430 for (i = 0; i < obj->arrayGetLength(); ++i) {
431 obj->arrayGet(i, &obj2);
432 if (!obj2.isStream()) {
433 error(-1, "Weird page contents");
434 obj2.free();
435 return;
436 }
437 obj2.free();
438 }
439 } else if (!obj->isStream()) {
440 error(-1, "Weird page contents");
441 return;
442 }
443 parser = new Parser(xref, new Lexer(xref, obj));
444 go(topLevel);
445 delete parser;
446 parser = NULL;
447}
448
449void Gfx::go(GBool topLevel) {
450 Object obj;
451 Object args[maxArgs];
452 int numCmds, numArgs;
453 int i;
454
455 // scan a sequence of objects
456 numCmds = 0;
457 numArgs = 0;
458 parser->getObj(&obj);
459 while (!obj.isEOF()) {
460
461 // got a command - execute it
462 if (obj.isCmd()) {
463 if (printCommands) {
464 obj.print(stdout);
465 for (i = 0; i < numArgs; ++i) {
466 printf(" ");
467 args[i].print(stdout);
468 }
469 printf("\n");
470 fflush(stdout);
471 }
472 execOp(&obj, args, numArgs);
473 obj.free();
474 for (i = 0; i < numArgs; ++i)
475 args[i].free();
476 numArgs = 0;
477
478 // periodically update display
479 if (++numCmds == 200) {
480 out->dump();
481 numCmds = 0;
482 }
483
484 // got an argument - save it
485 } else if (numArgs < maxArgs) {
486 args[numArgs++] = obj;
487
488 // too many arguments - something is wrong
489 } else {
490 error(getPos(), "Too many args in content stream");
491 if (printCommands) {
492 printf("throwing away arg: ");
493 obj.print(stdout);
494 printf("\n");
495 fflush(stdout);
496 }
497 obj.free();
498 }
499
500 // grab the next object
501 parser->getObj(&obj);
502 }
503 obj.free();
504
505 // args at end with no command
506 if (numArgs > 0) {
507 error(getPos(), "Leftover args in content stream");
508 if (printCommands) {
509 printf("%d leftovers:", numArgs);
510 for (i = 0; i < numArgs; ++i) {
511 printf(" ");
512 args[i].print(stdout);
513 }
514 printf("\n");
515 fflush(stdout);
516 }
517 for (i = 0; i < numArgs; ++i)
518 args[i].free();
519 }
520
521 // update display
522 if (topLevel && numCmds > 0) {
523 out->dump();
524 }
525}
526
527void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
528 Operator *op;
529 char *name;
530 int i;
531
532 // find operator
533 name = cmd->getName();
534 if (!(op = findOp(name))) {
535 if (ignoreUndef == 0)
536 error(getPos(), "Unknown operator '%s'", name);
537 return;
538 }
539
540 // type check args
541 if (op->numArgs >= 0) {
542 if (numArgs != op->numArgs) {
543 error(getPos(), "Wrong number (%d) of args to '%s' operator",
544 numArgs, name);
545 return;
546 }
547 } else {
548 if (numArgs > -op->numArgs) {
549 error(getPos(), "Too many (%d) args to '%s' operator",
550 numArgs, name);
551 return;
552 }
553 }
554 for (i = 0; i < numArgs; ++i) {
555 if (!checkArg(&args[i], op->tchk[i])) {
556 error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
557 i, name, args[i].getTypeName());
558 return;
559 }
560 }
561
562 // do it
563 (this->*op->func)(args, numArgs);
564}
565
566Operator *Gfx::findOp(char *name) {
567 int a, b, m, cmp;
568
569 a = -1;
570 b = numOps;
571 // invariant: opTab[a] < name < opTab[b]
572 while (b - a > 1) {
573 m = (a + b) / 2;
574 cmp = strcmp(opTab[m].name, name);
575 if (cmp < 0)
576 a = m;
577 else if (cmp > 0)
578 b = m;
579 else
580 a = b = m;
581 }
582 if (cmp != 0)
583 return NULL;
584 return &opTab[a];
585}
586
587GBool Gfx::checkArg(Object *arg, TchkType type) {
588 switch (type) {
589 case tchkBool: return arg->isBool();
590 case tchkInt: return arg->isInt();
591 case tchkNum: return arg->isNum();
592 case tchkString: return arg->isString();
593 case tchkName: return arg->isName();
594 case tchkArray: return arg->isArray();
595 case tchkProps: return arg->isDict() || arg->isName();
596 case tchkSCN: return arg->isNum() || arg->isName();
597 case tchkNone: return gFalse;
598 }
599 return gFalse;
600}
601
602int Gfx::getPos() {
603 return parser ? parser->getPos() : -1;
604}
605
606//------------------------------------------------------------------------
607// graphics state operators
608//------------------------------------------------------------------------
609
610void Gfx::opSave(Object args[], int numArgs) {
611 out->saveState(state);
612 state = state->save();
613}
614
615void Gfx::opRestore(Object args[], int numArgs) {
616 state = state->restore();
617 out->restoreState(state);
618}
619
620void Gfx::opConcat(Object args[], int numArgs) {
621 state->concatCTM(args[0].getNum(), args[1].getNum(),
622 args[2].getNum(), args[3].getNum(),
623 args[4].getNum(), args[5].getNum());
624 out->updateCTM(state, args[0].getNum(), args[1].getNum(),
625 args[2].getNum(), args[3].getNum(),
626 args[4].getNum(), args[5].getNum());
627 fontChanged = gTrue;
628}
629
630void Gfx::opSetDash(Object args[], int numArgs) {
631 Array *a;
632 int length;
633 Object obj;
634 fouble *dash;
635 int i;
636
637 a = args[0].getArray();
638 length = a->getLength();
639 if (length == 0) {
640 dash = NULL;
641 } else {
642 dash = (fouble *)gmalloc(length * sizeof(fouble));
643 for (i = 0; i < length; ++i) {
644 dash[i] = a->get(i, &obj)->getNum();
645 obj.free();
646 }
647 }
648 state->setLineDash(dash, length, args[1].getNum());
649 out->updateLineDash(state);
650}
651
652void Gfx::opSetFlat(Object args[], int numArgs) {
653 state->setFlatness((int)args[0].getNum());
654 out->updateFlatness(state);
655}
656
657void Gfx::opSetLineJoin(Object args[], int numArgs) {
658 state->setLineJoin(args[0].getInt());
659 out->updateLineJoin(state);
660}
661
662void Gfx::opSetLineCap(Object args[], int numArgs) {
663 state->setLineCap(args[0].getInt());
664 out->updateLineCap(state);
665}
666
667void Gfx::opSetMiterLimit(Object args[], int numArgs) {
668 state->setMiterLimit(args[0].getNum());
669 out->updateMiterLimit(state);
670}
671
672void Gfx::opSetLineWidth(Object args[], int numArgs) {
673 state->setLineWidth(args[0].getNum());
674 out->updateLineWidth(state);
675}
676
677void Gfx::opSetExtGState(Object args[], int numArgs) {
678 Object obj1, obj2;
679
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 (obj1.dictLookup("ca", &obj2)->isNum()) {
689 state->setFillOpacity(obj2.getNum());
690 out->updateFillOpacity(state);
691 }
692 obj2.free();
693 if (obj1.dictLookup("CA", &obj2)->isNum()) {
694 state->setStrokeOpacity(obj2.getNum());
695 out->updateStrokeOpacity(state);
696 }
697 obj2.free();
698 obj1.free();
699}
700
701void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
702}
703
704//------------------------------------------------------------------------
705// color operators
706//------------------------------------------------------------------------
707
708void Gfx::opSetFillGray(Object args[], int numArgs) {
709 GfxColor color;
710
711 state->setFillPattern(NULL);
712 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
713 color.c[0] = args[0].getNum();
714 state->setFillColor(&color);
715 out->updateFillColor(state);
716}
717
718void Gfx::opSetStrokeGray(Object args[], int numArgs) {
719 GfxColor color;
720
721 state->setStrokePattern(NULL);
722 state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
723 color.c[0] = args[0].getNum();
724 state->setStrokeColor(&color);
725 out->updateStrokeColor(state);
726}
727
728void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
729 GfxColor color;
730 int i;
731
732 state->setFillPattern(NULL);
733 state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
734 for (i = 0; i < 4; ++i) {
735 color.c[i] = args[i].getNum();
736 }
737 state->setFillColor(&color);
738 out->updateFillColor(state);
739}
740
741void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
742 GfxColor color;
743 int i;
744
745 state->setStrokePattern(NULL);
746 state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
747 for (i = 0; i < 4; ++i) {
748 color.c[i] = args[i].getNum();
749 }
750 state->setStrokeColor(&color);
751 out->updateStrokeColor(state);
752}
753
754void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
755 GfxColor color;
756 int i;
757
758 state->setFillPattern(NULL);
759 state->setFillColorSpace(new GfxDeviceRGBColorSpace());
760 for (i = 0; i < 3; ++i) {
761 color.c[i] = args[i].getNum();
762 }
763 state->setFillColor(&color);
764 out->updateFillColor(state);
765}
766
767void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
768 GfxColor color;
769 int i;
770
771 state->setStrokePattern(NULL);
772 state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
773 for (i = 0; i < 3; ++i) {
774 color.c[i] = args[i].getNum();
775 }
776 state->setStrokeColor(&color);
777 out->updateStrokeColor(state);
778}
779
780void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
781 Object obj;
782 GfxColorSpace *colorSpace;
783 GfxColor color;
784 int i;
785
786 state->setFillPattern(NULL);
787 res->lookupColorSpace(args[0].getName(), &obj);
788 if (obj.isNull()) {
789 colorSpace = GfxColorSpace::parse(&args[0]);
790 } else {
791 colorSpace = GfxColorSpace::parse(&obj);
792 }
793 obj.free();
794 if (colorSpace) {
795 state->setFillColorSpace(colorSpace);
796 } else {
797 error(getPos(), "Bad color space (fill)");
798 }
799 for (i = 0; i < gfxColorMaxComps; ++i) {
800 color.c[i] = 0;
801 }
802 state->setFillColor(&color);
803 out->updateFillColor(state);
804}
805
806void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
807 Object obj;
808 GfxColorSpace *colorSpace;
809 GfxColor color;
810 int i;
811
812 state->setStrokePattern(NULL);
813 res->lookupColorSpace(args[0].getName(), &obj);
814 if (obj.isNull()) {
815 colorSpace = GfxColorSpace::parse(&args[0]);
816 } else {
817 colorSpace = GfxColorSpace::parse(&obj);
818 }
819 obj.free();
820 if (colorSpace) {
821 state->setStrokeColorSpace(colorSpace);
822 } else {
823 error(getPos(), "Bad color space (stroke)");
824 }
825 for (i = 0; i < gfxColorMaxComps; ++i) {
826 color.c[i] = 0;
827 }
828 state->setStrokeColor(&color);
829 out->updateStrokeColor(state);
830}
831
832void Gfx::opSetFillColor(Object args[], int numArgs) {
833 GfxColor color;
834 int i;
835
836 state->setFillPattern(NULL);
837 for (i = 0; i < numArgs; ++i) {
838 color.c[i] = args[i].getNum();
839 }
840 state->setFillColor(&color);
841 out->updateFillColor(state);
842}
843
844void Gfx::opSetStrokeColor(Object args[], int numArgs) {
845 GfxColor color;
846 int i;
847
848 state->setStrokePattern(NULL);
849 for (i = 0; i < numArgs; ++i) {
850 color.c[i] = args[i].getNum();
851 }
852 state->setStrokeColor(&color);
853 out->updateStrokeColor(state);
854}
855
856void Gfx::opSetFillColorN(Object args[], int numArgs) {
857 GfxColor color;
858 GfxPattern *pattern;
859 int i;
860
861 if (state->getFillColorSpace()->getMode() == csPattern) {
862 if (numArgs > 1) {
863 for (i = 0; i < numArgs && i < 4; ++i) {
864 if (args[i].isNum()) {
865 color.c[i] = args[i].getNum();
866 }
867 }
868 state->setFillColor(&color);
869 out->updateFillColor(state);
870 }
871 if (args[numArgs-1].isName() &&
872 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
873 state->setFillPattern(pattern);
874 }
875
876 } else {
877 state->setFillPattern(NULL);
878 for (i = 0; i < numArgs && i < 4; ++i) {
879 if (args[i].isNum()) {
880 color.c[i] = args[i].getNum();
881 }
882 }
883 state->setFillColor(&color);
884 out->updateFillColor(state);
885 }
886}
887
888void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
889 GfxColor color;
890 GfxPattern *pattern;
891 int i;
892
893 if (state->getStrokeColorSpace()->getMode() == csPattern) {
894 if (numArgs > 1) {
895 for (i = 0; i < numArgs && i < 4; ++i) {
896 if (args[i].isNum()) {
897 color.c[i] = args[i].getNum();
898 }
899 }
900 state->setStrokeColor(&color);
901 out->updateStrokeColor(state);
902 }
903 if (args[numArgs-1].isName() &&
904 (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
905 state->setStrokePattern(pattern);
906 }
907
908 } else {
909 state->setStrokePattern(NULL);
910 for (i = 0; i < numArgs && i < 4; ++i) {
911 if (args[i].isNum()) {
912 color.c[i] = args[i].getNum();
913 }
914 }
915 state->setStrokeColor(&color);
916 out->updateStrokeColor(state);
917 }
918}
919
920//------------------------------------------------------------------------
921// path segment operators
922//------------------------------------------------------------------------
923
924void Gfx::opMoveTo(Object args[], int numArgs) {
925 state->moveTo(args[0].getNum(), args[1].getNum());
926}
927
928void Gfx::opLineTo(Object args[], int numArgs) {
929 if (!state->isCurPt()) {
930 error(getPos(), "No current point in lineto");
931 return;
932 }
933 state->lineTo(args[0].getNum(), args[1].getNum());
934}
935
936void Gfx::opCurveTo(Object args[], int numArgs) {
937 fouble x1, y1, x2, y2, x3, y3;
938
939 if (!state->isCurPt()) {
940 error(getPos(), "No current point in curveto");
941 return;
942 }
943 x1 = args[0].getNum();
944 y1 = args[1].getNum();
945 x2 = args[2].getNum();
946 y2 = args[3].getNum();
947 x3 = args[4].getNum();
948 y3 = args[5].getNum();
949 state->curveTo(x1, y1, x2, y2, x3, y3);
950}
951
952void Gfx::opCurveTo1(Object args[], int numArgs) {
953 fouble x1, y1, x2, y2, x3, y3;
954
955 if (!state->isCurPt()) {
956 error(getPos(), "No current point in curveto1");
957 return;
958 }
959 x1 = state->getCurX();
960 y1 = state->getCurY();
961 x2 = args[0].getNum();
962 y2 = args[1].getNum();
963 x3 = args[2].getNum();
964 y3 = args[3].getNum();
965 state->curveTo(x1, y1, x2, y2, x3, y3);
966}
967
968void Gfx::opCurveTo2(Object args[], int numArgs) {
969 fouble x1, y1, x2, y2, x3, y3;
970
971 if (!state->isCurPt()) {
972 error(getPos(), "No current point in curveto2");
973 return;
974 }
975 x1 = args[0].getNum();
976 y1 = args[1].getNum();
977 x2 = args[2].getNum();
978 y2 = args[3].getNum();
979 x3 = x2;
980 y3 = y2;
981 state->curveTo(x1, y1, x2, y2, x3, y3);
982}
983
984void Gfx::opRectangle(Object args[], int numArgs) {
985 fouble x, y, w, h;
986
987 x = args[0].getNum();
988 y = args[1].getNum();
989 w = args[2].getNum();
990 h = args[3].getNum();
991 state->moveTo(x, y);
992 state->lineTo(x + w, y);
993 state->lineTo(x + w, y + h);
994 state->lineTo(x, y + h);
995 state->closePath();
996}
997
998void Gfx::opClosePath(Object args[], int numArgs) {
999 if (!state->isCurPt()) {
1000 error(getPos(), "No current point in closepath");
1001 return;
1002 }
1003 state->closePath();
1004}
1005
1006//------------------------------------------------------------------------
1007// path painting operators
1008//------------------------------------------------------------------------
1009
1010void Gfx::opEndPath(Object args[], int numArgs) {
1011 doEndPath();
1012}
1013
1014void Gfx::opStroke(Object args[], int numArgs) {
1015 if (!state->isCurPt()) {
1016 //error(getPos(), "No path in stroke");
1017 return;
1018 }
1019 if (state->isPath())
1020 out->stroke(state);
1021 doEndPath();
1022}
1023
1024void Gfx::opCloseStroke(Object args[], int numArgs) {
1025 if (!state->isCurPt()) {
1026 //error(getPos(), "No path in closepath/stroke");
1027 return;
1028 }
1029 if (state->isPath()) {
1030 state->closePath();
1031 out->stroke(state);
1032 }
1033 doEndPath();
1034}
1035
1036void Gfx::opFill(Object args[], int numArgs) {
1037 if (!state->isCurPt()) {
1038 //error(getPos(), "No path in fill");
1039 return;
1040 }
1041 if (state->isPath()) {
1042 if (state->getFillColorSpace()->getMode() == csPattern) {
1043 doPatternFill(gFalse);
1044 } else {
1045 out->fill(state);
1046 }
1047 }
1048 doEndPath();
1049}
1050
1051void Gfx::opEOFill(Object args[], int numArgs) {
1052 if (!state->isCurPt()) {
1053 //error(getPos(), "No path in eofill");
1054 return;
1055 }
1056 if (state->isPath()) {
1057 if (state->getFillColorSpace()->getMode() == csPattern) {
1058 doPatternFill(gTrue);
1059 } else {
1060 out->eoFill(state);
1061 }
1062 }
1063 doEndPath();
1064}
1065
1066void Gfx::opFillStroke(Object args[], int numArgs) {
1067 if (!state->isCurPt()) {
1068 //error(getPos(), "No path in fill/stroke");
1069 return;
1070 }
1071 if (state->isPath()) {
1072 if (state->getFillColorSpace()->getMode() == csPattern) {
1073 doPatternFill(gFalse);
1074 } else {
1075 out->fill(state);
1076 }
1077 out->stroke(state);
1078 }
1079 doEndPath();
1080}
1081
1082void Gfx::opCloseFillStroke(Object args[], int numArgs) {
1083 if (!state->isCurPt()) {
1084 //error(getPos(), "No path in closepath/fill/stroke");
1085 return;
1086 }
1087 if (state->isPath()) {
1088 state->closePath();
1089 if (state->getFillColorSpace()->getMode() == csPattern) {
1090 doPatternFill(gFalse);
1091 } else {
1092 out->fill(state);
1093 }
1094 out->stroke(state);
1095 }
1096 doEndPath();
1097}
1098
1099void Gfx::opEOFillStroke(Object args[], int numArgs) {
1100 if (!state->isCurPt()) {
1101 //error(getPos(), "No path in eofill/stroke");
1102 return;
1103 }
1104 if (state->isPath()) {
1105 if (state->getFillColorSpace()->getMode() == csPattern) {
1106 doPatternFill(gTrue);
1107 } else {
1108 out->eoFill(state);
1109 }
1110 out->stroke(state);
1111 }
1112 doEndPath();
1113}
1114
1115void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
1116 if (!state->isCurPt()) {
1117 //error(getPos(), "No path in closepath/eofill/stroke");
1118 return;
1119 }
1120 if (state->isPath()) {
1121 state->closePath();
1122 if (state->getFillColorSpace()->getMode() == csPattern) {
1123 doPatternFill(gTrue);
1124 } else {
1125 out->eoFill(state);
1126 }
1127 out->stroke(state);
1128 }
1129 doEndPath();
1130}
1131
1132void Gfx::doPatternFill(GBool eoFill) {
1133 GfxPatternColorSpace *patCS;
1134 GfxPattern *pattern;
1135 GfxTilingPattern *tPat;
1136 GfxColorSpace *cs;
1137 fouble xMin, yMin, xMax, yMax, x, y, x1, y1;
1138 fouble cxMin, cyMin, cxMax, cyMax;
1139 int xi0, yi0, xi1, yi1, xi, yi;
1140 fouble *ctm, *btm, *ptm;
1141 fouble m[6], ictm[6], m1[6], im[6], imb[6];
1142 fouble det;
1143 fouble xstep, ystep;
1144 int i;
1145
1146 // this is a bit of a kludge -- patterns can be really slow, so we
1147 // skip them if we're only doing text extraction, since they almost
1148 // certainly don't contain any text
1149 if (!out->needNonText()) {
1150 return;
1151 }
1152
1153 // get color space
1154 patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
1155
1156 // get pattern
1157 if (!(pattern = state->getFillPattern())) {
1158 return;
1159 }
1160 if (pattern->getType() != 1) {
1161 return;
1162 }
1163 tPat = (GfxTilingPattern *)pattern;
1164
1165 // construct a (pattern space) -> (current space) transform matrix
1166 ctm = state->getCTM();
1167 btm = baseMatrix;
1168 ptm = tPat->getMatrix();
1169 // iCTM = invert CTM
1170 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1171 ictm[0] = ctm[3] * det;
1172 ictm[1] = -ctm[1] * det;
1173 ictm[2] = -ctm[2] * det;
1174 ictm[3] = ctm[0] * det;
1175 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1176 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1177 // m1 = PTM * BTM = PTM * base transform matrix
1178 m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
1179 m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
1180 m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
1181 m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
1182 m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
1183 m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
1184 // m = m1 * iCTM = (PTM * BTM) * (iCTM)
1185 m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
1186 m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
1187 m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
1188 m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
1189 m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
1190 m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
1191
1192 // construct a (current space) -> (pattern space) transform matrix
1193 det = 1 / (m[0] * m[3] - m[1] * m[2]);
1194 im[0] = m[3] * det;
1195 im[1] = -m[1] * det;
1196 im[2] = -m[2] * det;
1197 im[3] = m[0] * det;
1198 im[4] = (m[2] * m[5] - m[3] * m[4]) * det;
1199 im[5] = (m[1] * m[4] - m[0] * m[5]) * det;
1200
1201 // construct a (base space) -> (pattern space) transform matrix
1202 det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
1203 imb[0] = m1[3] * det;
1204 imb[1] = -m1[1] * det;
1205 imb[2] = -m1[2] * det;
1206 imb[3] = m1[0] * det;
1207 imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
1208 imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
1209
1210 // save current graphics state
1211 out->saveState(state);
1212 state = state->save();
1213
1214 // set underlying color space (for uncolored tiling patterns)
1215 if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
1216 state->setFillColorSpace(cs->copy());
1217 } else {
1218 state->setFillColorSpace(new GfxDeviceGrayColorSpace());
1219 }
1220 state->setFillPattern(NULL);
1221 out->updateFillColor(state);
1222
1223 // clip to current path
1224 state->clip();
1225 if (eoFill) {
1226 out->eoClip(state);
1227 } else {
1228 out->clip(state);
1229 }
1230 state->clearPath();
1231
1232 // transform clip region bbox to pattern space
1233 state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
1234 xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
1235 yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
1236 x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
1237 y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
1238 if (x1 < xMin) {
1239 xMin = x1;
1240 } else if (x1 > xMax) {
1241 xMax = x1;
1242 }
1243 if (y1 < yMin) {
1244 yMin = y1;
1245 } else if (y1 > yMax) {
1246 yMax = y1;
1247 }
1248 x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
1249 y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
1250 if (x1 < xMin) {
1251 xMin = x1;
1252 } else if (x1 > xMax) {
1253 xMax = x1;
1254 }
1255 if (y1 < yMin) {
1256 yMin = y1;
1257 } else if (y1 > yMax) {
1258 yMax = y1;
1259 }
1260 x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
1261 y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
1262 if (x1 < xMin) {
1263 xMin = x1;
1264 } else if (x1 > xMax) {
1265 xMax = x1;
1266 }
1267 if (y1 < yMin) {
1268 yMin = y1;
1269 } else if (y1 > yMax) {
1270 yMax = y1;
1271 }
1272
1273 // draw the pattern
1274 //~ this should treat negative steps differently -- start at right/top
1275 //~ edge instead of left/bottom (?)
1276 xstep = fabs(tPat->getXStep());
1277 ystep = fabs(tPat->getYStep());
1278 xi0 = (int)floor(xMin / xstep);
1279 xi1 = (int)ceil(xMax / xstep);
1280 yi0 = (int)floor(yMin / ystep);
1281 yi1 = (int)ceil(yMax / ystep);
1282 for (i = 0; i < 4; ++i) {
1283 m1[i] = m[i];
1284 }
1285 for (yi = yi0; yi < yi1; ++yi) {
1286 for (xi = xi0; xi < xi1; ++xi) {
1287 x = xi * xstep;
1288 y = yi * ystep;
1289 m1[4] = x * m[0] + y * m[2] + m[4];
1290 m1[5] = x * m[1] + y * m[3] + m[5];
1291 doForm1(tPat->getContentStream(), tPat->getResDict(),
1292 m1, tPat->getBBox());
1293 }
1294 }
1295
1296 // restore graphics state
1297 state = state->restore();
1298 out->restoreState(state);
1299}
1300
1301void Gfx::opShFill(Object args[], int numArgs) {
1302 GfxShading *shading;
1303 fouble xMin, yMin, xMax, yMax;
1304
1305 if (!(shading = res->lookupShading(args[0].getName()))) {
1306 return;
1307 }
1308
1309 // save current graphics state
1310 out->saveState(state);
1311 state = state->save();
1312
1313 // clip to bbox
1314 if (shading->getHasBBox()) {
1315 shading->getBBox(&xMin, &yMin, &xMax, &yMax);
1316 state->moveTo(xMin, yMin);
1317 state->lineTo(xMax, yMin);
1318 state->lineTo(xMax, yMax);
1319 state->lineTo(xMin, yMax);
1320 state->closePath();
1321 state->clip();
1322 out->clip(state);
1323 state->clearPath();
1324 }
1325
1326 // set the color space
1327 state->setFillColorSpace(shading->getColorSpace()->copy());
1328
1329 // do shading type-specific operations
1330 switch (shading->getType()) {
1331 case 2:
1332 doAxialShFill((GfxAxialShading *)shading);
1333 break;
1334 }
1335
1336 // restore graphics state
1337 state = state->restore();
1338 out->restoreState(state);
1339
1340 delete shading;
1341}
1342
1343void Gfx::doAxialShFill(GfxAxialShading *shading) {
1344 fouble xMin, yMin, xMax, yMax;
1345 fouble x0, y0, x1, y1;
1346 fouble det;
1347 fouble *ctm;
1348 fouble ictm[6];
1349 fouble dx, dy, mul;
1350 fouble tMin, tMax, t, tx, ty;
1351 fouble s[4], sMin, sMax, tmp;
1352 fouble ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
1353 fouble t0, t1, tt;
1354 fouble ta[axialMaxSplits + 1];
1355 int next[axialMaxSplits + 1];
1356 GfxColor color0, color1;
1357 int nComps;
1358 int i, j, k, kk;
1359
1360 // get clip region bbox and transform to current user space
1361 state->getClipBBox(&x0, &y0, &x1, &y1);
1362 ctm = state->getCTM();
1363 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
1364 ictm[0] = ctm[3] * det;
1365 ictm[1] = -ctm[1] * det;
1366 ictm[2] = -ctm[2] * det;
1367 ictm[3] = ctm[0] * det;
1368 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
1369 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
1370 xMin = xMax = x0 * ictm[0] + y0 * ictm[2] + ictm[4];
1371 yMin = yMax = x0 * ictm[1] + y0 * ictm[3] + ictm[5];
1372 tx = x0 * ictm[0] + y1 * ictm[2] + ictm[4];
1373 ty = x0 * ictm[1] + y1 * ictm[3] + ictm[5];
1374 if (tx < xMin) {
1375 xMin = tx;
1376 } else if (tx > xMax) {
1377 xMax = tx;
1378 }
1379 if (ty < yMin) {
1380 yMin = ty;
1381 } else if (ty > yMax) {
1382 yMax = ty;
1383 }
1384 tx = x1 * ictm[0] + y0 * ictm[2] + ictm[4];
1385 ty = x1 * ictm[1] + y0 * ictm[3] + ictm[5];
1386 if (tx < xMin) {
1387 xMin = tx;
1388 } else if (tx > xMax) {
1389 xMax = tx;
1390 }
1391 if (ty < yMin) {
1392 yMin = ty;
1393 } else if (ty > yMax) {
1394 yMax = ty;
1395 }
1396 tx = x1 * ictm[0] + y1 * ictm[2] + ictm[4];
1397 ty = x1 * ictm[1] + y1 * ictm[3] + ictm[5];
1398 if (tx < xMin) {
1399 xMin = tx;
1400 } else if (tx > xMax) {
1401 xMax = tx;
1402 }
1403 if (ty < yMin) {
1404 yMin = ty;
1405 } else if (ty > yMax) {
1406 yMax = ty;
1407 }
1408
1409 // compute min and max t values, based on the four corners of the
1410 // clip region bbox
1411 shading->getCoords(&x0, &y0, &x1, &y1);
1412 dx = x1 - x0;
1413 dy = y1 - y0;
1414 mul = 1 / (dx * dx + dy * dy);
1415 tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
1416 t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
1417 if (t < tMin) {
1418 tMin = t;
1419 } else if (t > tMax) {
1420 tMax = t;
1421 }
1422 t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
1423 if (t < tMin) {
1424 tMin = t;
1425 } else if (t > tMax) {
1426 tMax = t;
1427 }
1428 t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
1429 if (t < tMin) {
1430 tMin = t;
1431 } else if (t > tMax) {
1432 tMax = t;
1433 }
1434 if (tMin < 0 && !shading->getExtend0()) {
1435 tMin = 0;
1436 }
1437 if (tMax > 1 && !shading->getExtend1()) {
1438 tMax = 1;
1439 }
1440
1441 // get the function domain
1442 t0 = shading->getDomain0();
1443 t1 = shading->getDomain1();
1444
1445 // Traverse the t axis and do the shading.
1446 //
1447 // For each point (tx, ty) on the t axis, consider a line through
1448 // that point perpendicular to the t axis:
1449 //
1450 // x(s) = tx + s * -dy --> s = (x - tx) / -dy
1451 // y(s) = ty + s * dx --> s = (y - ty) / dx
1452 //
1453 // Then look at the intersection of this line with the bounding box
1454 // (xMin, yMin, xMax, yMax). In the general case, there are four
1455 // intersection points:
1456 //
1457 // s0 = (xMin - tx) / -dy
1458 // s1 = (xMax - tx) / -dy
1459 // s2 = (yMin - ty) / dx
1460 // s3 = (yMax - ty) / dx
1461 //
1462 // and we want the middle two s values.
1463 //
1464 // In the case where dx = 0, take s0 and s1; in the case where dy =
1465 // 0, take s2 and s3.
1466 //
1467 // Each filled polygon is bounded by two of these line segments
1468 // perpdendicular to the t axis.
1469 //
1470 // The t axis is bisected into smaller regions until the color
1471 // difference across a region is small enough, and then the region
1472 // is painted with a single color.
1473
1474 // set up
1475 nComps = shading->getColorSpace()->getNComps();
1476 ta[0] = tMin;
1477 ta[axialMaxSplits] = tMax;
1478 next[0] = axialMaxSplits;
1479
1480 // compute the color at t = tMin
1481 if (tMin < 0) {
1482 tt = t0;
1483 } else if (tMin > 1) {
1484 tt = t1;
1485 } else {
1486 tt = t0 + (t1 - t0) * tMin;
1487 }
1488 shading->getColor(tt, &color0);
1489
1490 // compute the coordinates of the point on the t axis at t = tMin;
1491 // then compute the intersection of the perpendicular line with the
1492 // bounding box
1493 tx = x0 + tMin * dx;
1494 ty = y0 + tMin * dy;
1495 if (dx == 0 && dy == 0) {
1496 sMin = sMax = 0;
1497 } if (dx == 0) {
1498 sMin = (xMin - tx) / -dy;
1499 sMax = (xMax - tx) / -dy;
1500 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1501 } else if (dy == 0) {
1502 sMin = (yMin - ty) / dx;
1503 sMax = (yMax - ty) / dx;
1504 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1505 } else {
1506 s[0] = (yMin - ty) / dx;
1507 s[1] = (yMax - ty) / dx;
1508 s[2] = (xMin - tx) / -dy;
1509 s[3] = (xMax - tx) / -dy;
1510 for (j = 0; j < 3; ++j) {
1511 kk = j;
1512 for (k = j + 1; k < 4; ++k) {
1513 if (s[k] < s[kk]) {
1514 kk = k;
1515 }
1516 }
1517 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1518 }
1519 sMin = s[1];
1520 sMax = s[2];
1521 }
1522 ux0 = tx - sMin * dy;
1523 uy0 = ty + sMin * dx;
1524 vx0 = tx - sMax * dy;
1525 vy0 = ty + sMax * dx;
1526
1527 i = 0;
1528 while (i < axialMaxSplits) {
1529
1530 // bisect until color difference is small enough or we hit the
1531 // bisection limit
1532 j = next[i];
1533 while (j > i + 1) {
1534 if (ta[j] < 0) {
1535 tt = t0;
1536 } else if (ta[j] > 1) {
1537 tt = t1;
1538 } else {
1539 tt = t0 + (t1 - t0) * ta[j];
1540 }
1541 shading->getColor(tt, &color1);
1542 for (k = 0; k < nComps; ++k) {
1543 if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
1544 break;
1545 }
1546 }
1547 if (k == nComps) {
1548 break;
1549 }
1550 k = (i + j) / 2;
1551 ta[k] = 0.5 * (ta[i] + ta[j]);
1552 next[i] = k;
1553 next[k] = j;
1554 j = k;
1555 }
1556
1557 // use the average of the colors of the two sides of the region
1558 for (k = 0; k < nComps; ++k) {
1559 color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
1560 }
1561
1562 // compute the coordinates of the point on the t axis; then
1563 // compute the intersection of the perpendicular line with the
1564 // bounding box
1565 tx = x0 + ta[j] * dx;
1566 ty = y0 + ta[j] * dy;
1567 if (dx == 0 && dy == 0) {
1568 sMin = sMax = 0;
1569 } if (dx == 0) {
1570 sMin = (xMin - tx) / -dy;
1571 sMax = (xMax - tx) / -dy;
1572 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1573 } else if (dy == 0) {
1574 sMin = (yMin - ty) / dx;
1575 sMax = (yMax - ty) / dx;
1576 if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
1577 } else {
1578 s[0] = (yMin - ty) / dx;
1579 s[1] = (yMax - ty) / dx;
1580 s[2] = (xMin - tx) / -dy;
1581 s[3] = (xMax - tx) / -dy;
1582 for (j = 0; j < 3; ++j) {
1583 kk = j;
1584 for (k = j + 1; k < 4; ++k) {
1585 if (s[k] < s[kk]) {
1586 kk = k;
1587 }
1588 }
1589 tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
1590 }
1591 sMin = s[1];
1592 sMax = s[2];
1593 }
1594 ux1 = tx - sMin * dy;
1595 uy1 = ty + sMin * dx;
1596 vx1 = tx - sMax * dy;
1597 vy1 = ty + sMax * dx;
1598
1599 // set the color
1600 state->setFillColor(&color0);
1601 out->updateFillColor(state);
1602
1603 // fill the region
1604 state->moveTo(ux0, uy0);
1605 state->lineTo(vx0, vy0);
1606 state->lineTo(vx1, vy1);
1607 state->lineTo(ux1, uy1);
1608 state->closePath();
1609 out->fill(state);
1610 state->clearPath();
1611
1612 // set up for next region
1613 ux0 = ux1;
1614 uy0 = uy1;
1615 vx0 = vx1;
1616 vy0 = vy1;
1617 color0 = color1;
1618 i = next[i];
1619 }
1620}
1621
1622void Gfx::doEndPath() {
1623 if (state->isPath() && clip != clipNone) {
1624 state->clip();
1625 if (clip == clipNormal) {
1626 out->clip(state);
1627 } else {
1628 out->eoClip(state);
1629 }
1630 }
1631 clip = clipNone;
1632 state->clearPath();
1633}
1634
1635//------------------------------------------------------------------------
1636// path clipping operators
1637//------------------------------------------------------------------------
1638
1639void Gfx::opClip(Object args[], int numArgs) {
1640 clip = clipNormal;
1641}
1642
1643void Gfx::opEOClip(Object args[], int numArgs) {
1644 clip = clipEO;
1645}
1646
1647//------------------------------------------------------------------------
1648// text object operators
1649//------------------------------------------------------------------------
1650
1651void Gfx::opBeginText(Object args[], int numArgs) {
1652 state->setTextMat(1, 0, 0, 1, 0, 0);
1653 state->textMoveTo(0, 0);
1654 out->updateTextMat(state);
1655 out->updateTextPos(state);
1656 fontChanged = gTrue;
1657}
1658
1659void Gfx::opEndText(Object args[], int numArgs) {
1660}
1661
1662//------------------------------------------------------------------------
1663// text state operators
1664//------------------------------------------------------------------------
1665
1666void Gfx::opSetCharSpacing(Object args[], int numArgs) {
1667 state->setCharSpace(args[0].getNum());
1668 out->updateCharSpace(state);
1669}
1670
1671void Gfx::opSetFont(Object args[], int numArgs) {
1672 GfxFont *font;
1673
1674 if (!(font = res->lookupFont(args[0].getName()))) {
1675 return;
1676 }
1677 if (printCommands) {
1678 printf(" font: tag=%s name='%s' %g\n",
1679 font->getTag()->getCString(),
1680 font->getName() ? font->getName()->getCString() : "???",
1681 args[1].getNum());
1682 fflush(stdout);
1683 }
1684 state->setFont(font, args[1].getNum());
1685 fontChanged = gTrue;
1686}
1687
1688void Gfx::opSetTextLeading(Object args[], int numArgs) {
1689 state->setLeading(args[0].getNum());
1690}
1691
1692void Gfx::opSetTextRender(Object args[], int numArgs) {
1693 state->setRender(args[0].getInt());
1694 out->updateRender(state);
1695}
1696
1697void Gfx::opSetTextRise(Object args[], int numArgs) {
1698 state->setRise(args[0].getNum());
1699 out->updateRise(state);
1700}
1701
1702void Gfx::opSetWordSpacing(Object args[], int numArgs) {
1703 state->setWordSpace(args[0].getNum());
1704 out->updateWordSpace(state);
1705}
1706
1707void Gfx::opSetHorizScaling(Object args[], int numArgs) {
1708 state->setHorizScaling(args[0].getNum());
1709 out->updateHorizScaling(state);
1710 fontChanged = gTrue;
1711}
1712
1713//------------------------------------------------------------------------
1714// text positioning operators
1715//------------------------------------------------------------------------
1716
1717void Gfx::opTextMove(Object args[], int numArgs) {
1718 fouble tx, ty;
1719
1720 tx = state->getLineX() + args[0].getNum();
1721 ty = state->getLineY() + args[1].getNum();
1722 state->textMoveTo(tx, ty);
1723 out->updateTextPos(state);
1724}
1725
1726void Gfx::opTextMoveSet(Object args[], int numArgs) {
1727 fouble tx, ty;
1728
1729 tx = state->getLineX() + args[0].getNum();
1730 ty = args[1].getNum();
1731 state->setLeading(-ty);
1732 ty += state->getLineY();
1733 state->textMoveTo(tx, ty);
1734 out->updateTextPos(state);
1735}
1736
1737void Gfx::opSetTextMatrix(Object args[], int numArgs) {
1738 state->setTextMat(args[0].getNum(), args[1].getNum(),
1739 args[2].getNum(), args[3].getNum(),
1740 args[4].getNum(), args[5].getNum());
1741 state->textMoveTo(0, 0);
1742 out->updateTextMat(state);
1743 out->updateTextPos(state);
1744 fontChanged = gTrue;
1745}
1746
1747void Gfx::opTextNextLine(Object args[], int numArgs) {
1748 fouble tx, ty;
1749
1750 tx = state->getLineX();
1751 ty = state->getLineY() - state->getLeading();
1752 state->textMoveTo(tx, ty);
1753 out->updateTextPos(state);
1754}
1755
1756//------------------------------------------------------------------------
1757// text string operators
1758//------------------------------------------------------------------------
1759
1760void Gfx::opShowText(Object args[], int numArgs) {
1761 if (!state->getFont()) {
1762 error(getPos(), "No font in show");
1763 return;
1764 }
1765 doShowText(args[0].getString());
1766}
1767
1768void Gfx::opMoveShowText(Object args[], int numArgs) {
1769 fouble tx, ty;
1770
1771 if (!state->getFont()) {
1772 error(getPos(), "No font in move/show");
1773 return;
1774 }
1775 tx = state->getLineX();
1776 ty = state->getLineY() - state->getLeading();
1777 state->textMoveTo(tx, ty);
1778 out->updateTextPos(state);
1779 doShowText(args[0].getString());
1780}
1781
1782void Gfx::opMoveSetShowText(Object args[], int numArgs) {
1783 fouble tx, ty;
1784
1785 if (!state->getFont()) {
1786 error(getPos(), "No font in move/set/show");
1787 return;
1788 }
1789 state->setWordSpace(args[0].getNum());
1790 state->setCharSpace(args[1].getNum());
1791 tx = state->getLineX();
1792 ty = state->getLineY() - state->getLeading();
1793 state->textMoveTo(tx, ty);
1794 out->updateWordSpace(state);
1795 out->updateCharSpace(state);
1796 out->updateTextPos(state);
1797 doShowText(args[2].getString());
1798}
1799
1800void Gfx::opShowSpaceText(Object args[], int numArgs) {
1801 Array *a;
1802 Object obj;
1803 int i;
1804
1805 if (!state->getFont()) {
1806 error(getPos(), "No font in show/space");
1807 return;
1808 }
1809 a = args[0].getArray();
1810 for (i = 0; i < a->getLength(); ++i) {
1811 a->get(i, &obj);
1812 if (obj.isNum()) {
1813 state->textShift(-obj.getNum() * 0.001 * state->getFontSize());
1814 out->updateTextShift(state, obj.getNum());
1815 } else if (obj.isString()) {
1816 doShowText(obj.getString());
1817 } else {
1818 error(getPos(), "Element of show/space array must be number or string");
1819 }
1820 obj.free();
1821 }
1822}
1823
1824void Gfx::doShowText(GString *s) {
1825 GfxFont *font;
1826 fouble riseX, riseY;
1827 CharCode code;
1828 Unicode u[8];
1829 fouble dx, dy, dx2, dy2, tdx, tdy;
1830 fouble originX, originY, tOriginX, tOriginY;
1831 char *p;
1832 int len, n, uLen, nChars, nSpaces;
1833
1834 if (fontChanged) {
1835 out->updateFont(state);
1836 fontChanged = gFalse;
1837 }
1838 font = state->getFont();
1839
1840#if 0 //~type3
1841 fouble x, y;
1842 fouble oldCTM[6], newCTM[6];
1843 fouble *mat;
1844 Object charProc;
1845 Parser *oldParser;
1846 int i;
1847
1848 //~ also check out->renderType3()
1849 if (font->getType() == fontType3) {
1850 out->beginString(state, s);
1851 mat = state->getCTM();
1852 for (i = 0; i < 6; ++i) {
1853 oldCTM[i] = mat[i];
1854 }
1855 mat = state->getTextMat();
1856 newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
1857 newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
1858 newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
1859 newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
1860 mat = font->getFontMatrix();
1861 newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
1862 newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
1863 newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
1864 newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
1865 newCTM[0] *= state->getFontSize();
1866 newCTM[3] *= state->getFontSize();
1867 newCTM[0] *= state->getHorizScaling();
1868 newCTM[2] *= state->getHorizScaling();
1869 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
1870 oldParser = parser;
1871 p = s->getCString();
1872 len = s->getLength();
1873 while (len > 0) {
1874 n = font->getNextChar(p, len, &code,
1875 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
1876 &dx, &dy, &originX, &originY);
1877 state->transform(state->getCurX() + riseX, state->getCurY() + riseY,
1878 &x, &y);
1879 out->saveState(state);
1880 state = state->save();
1881 state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
1882 //~ out->updateCTM(???)
1883 ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
1884 if (charProc.isStream()) {
1885 display(&charProc, gFalse);
1886 } else {
1887 error(getPos(), "Missing or bad Type3 CharProc entry");
1888 }
1889 state = state->restore();
1890 out->restoreState(state);
1891 charProc.free();
1892 dx = dx * state->getFontSize() + state->getCharSpace();
1893 if (n == 1 && *p == ' ') {
1894 dx += state->getWordSpace();
1895 }
1896 dx *= state->getHorizScaling();
1897 dy *= state->getFontSize();
1898 state->textTransformDelta(dx, dy, &tdx, &tdy);
1899 state->shift(tdx, tdy);
1900 p += n;
1901 len -= n;
1902 }
1903 parser = oldParser;
1904 out->endString(state);
1905 return;
1906 }
1907#endif
1908
1909 if (out->useDrawChar()) {
1910 state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
1911 out->beginString(state, s);
1912 p = s->getCString();
1913 len = s->getLength();
1914 while (len > 0) {
1915 n = font->getNextChar(p, len, &code,
1916 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
1917 &dx, &dy, &originX, &originY);
1918 dx = dx * state->getFontSize() + state->getCharSpace();
1919 if (n == 1 && *p == ' ') {
1920 dx += state->getWordSpace();
1921 }
1922 dx *= state->getHorizScaling();
1923 dy *= state->getFontSize();
1924 state->textTransformDelta(dx, dy, &tdx, &tdy);
1925 originX *= state->getFontSize();
1926 originY *= state->getFontSize();
1927 state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
1928 out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
1929 tdx, tdy, tOriginX, tOriginY, code, u, uLen);
1930 state->shift(tdx, tdy);
1931 p += n;
1932 len -= n;
1933 }
1934 out->endString(state);
1935
1936 } else {
1937 dx = dy = 0;
1938 p = s->getCString();
1939 len = s->getLength();
1940 nChars = nSpaces = 0;
1941 while (len > 0) {
1942 n = font->getNextChar(p, len, &code,
1943 u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
1944 &dx2, &dy2, &originX, &originY);
1945 dx += dx2;
1946 dy += dy2;
1947 if (n == 1 && *p == ' ') {
1948 ++nSpaces;
1949 }
1950 ++nChars;
1951 p += n;
1952 len -= n;
1953 }
1954 dx = dx * state->getFontSize()
1955 + nChars * state->getCharSpace()
1956 + nSpaces * state->getWordSpace();
1957 dx *= state->getHorizScaling();
1958 dy *= state->getFontSize();
1959 state->textTransformDelta(dx, dy, &tdx, &tdy);
1960 out->drawString(state, s);
1961 state->shift(tdx, tdy);
1962 }
1963}
1964
1965//------------------------------------------------------------------------
1966// XObject operators
1967//------------------------------------------------------------------------
1968
1969void Gfx::opXObject(Object args[], int numArgs) {
1970 Object obj1, obj2, refObj;
1971#if OPI_SUPPORT
1972 Object opiDict;
1973#endif
1974
1975 if (!res->lookupXObject(args[0].getName(), &obj1)) {
1976 return;
1977 }
1978 if (!obj1.isStream()) {
1979 error(getPos(), "XObject '%s' is wrong type", args[0].getName());
1980 obj1.free();
1981 return;
1982 }
1983#if OPI_SUPPORT
1984 obj1.streamGetDict()->lookup("OPI", &opiDict);
1985 if (opiDict.isDict()) {
1986 out->opiBegin(state, opiDict.getDict());
1987 }
1988#endif
1989 obj1.streamGetDict()->lookup("Subtype", &obj2);
1990 if (obj2.isName("Image")) {
1991 res->lookupXObjectNF(args[0].getName(), &refObj);
1992 doImage(&refObj, obj1.getStream(), gFalse);
1993 refObj.free();
1994 } else if (obj2.isName("Form")) {
1995 doForm(&obj1);
1996 } else if (obj2.isName()) {
1997 error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
1998 } else {
1999 error(getPos(), "XObject subtype is missing or wrong type");
2000 }
2001 obj2.free();
2002#if OPI_SUPPORT
2003 if (opiDict.isDict()) {
2004 out->opiEnd(state, opiDict.getDict());
2005 }
2006 opiDict.free();
2007#endif
2008 obj1.free();
2009}
2010
2011void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
2012 Dict *dict;
2013 int width, height;
2014 int bits;
2015 GBool mask;
2016 GBool invert;
2017 GfxColorSpace *colorSpace;
2018 GfxImageColorMap *colorMap;
2019 Object maskObj;
2020 GBool haveMask;
2021 int maskColors[2*gfxColorMaxComps];
2022 Object obj1, obj2;
2023 int i;
2024
2025 // get stream dict
2026 dict = str->getDict();
2027
2028 // get size
2029 dict->lookup("Width", &obj1);
2030 if (obj1.isNull()) {
2031 obj1.free();
2032 dict->lookup("W", &obj1);
2033 }
2034 if (!obj1.isInt())
2035 goto err2;
2036 width = obj1.getInt();
2037 obj1.free();
2038 dict->lookup("Height", &obj1);
2039 if (obj1.isNull()) {
2040 obj1.free();
2041 dict->lookup("H", &obj1);
2042 }
2043 if (!obj1.isInt())
2044 goto err2;
2045 height = obj1.getInt();
2046 obj1.free();
2047
2048 // image or mask?
2049 dict->lookup("ImageMask", &obj1);
2050 if (obj1.isNull()) {
2051 obj1.free();
2052 dict->lookup("IM", &obj1);
2053 }
2054 mask = gFalse;
2055 if (obj1.isBool())
2056 mask = obj1.getBool();
2057 else if (!obj1.isNull())
2058 goto err2;
2059 obj1.free();
2060
2061 // bit depth
2062 dict->lookup("BitsPerComponent", &obj1);
2063 if (obj1.isNull()) {
2064 obj1.free();
2065 dict->lookup("BPC", &obj1);
2066 }
2067 if (!obj1.isInt())
2068 goto err2;
2069 bits = obj1.getInt();
2070 obj1.free();
2071
2072 // display a mask
2073 if (mask) {
2074
2075 // check for inverted mask
2076 if (bits != 1)
2077 goto err1;
2078 invert = gFalse;
2079 dict->lookup("Decode", &obj1);
2080 if (obj1.isNull()) {
2081 obj1.free();
2082 dict->lookup("D", &obj1);
2083 }
2084 if (obj1.isArray()) {
2085 obj1.arrayGet(0, &obj2);
2086 if (obj2.isInt() && obj2.getInt() == 1)
2087 invert = gTrue;
2088 obj2.free();
2089 } else if (!obj1.isNull()) {
2090 goto err2;
2091 }
2092 obj1.free();
2093
2094 // draw it
2095 out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
2096
2097 } else {
2098
2099 // get color space and color map
2100 dict->lookup("ColorSpace", &obj1);
2101 if (obj1.isNull()) {
2102 obj1.free();
2103 dict->lookup("CS", &obj1);
2104 }
2105 if (obj1.isName()) {
2106 res->lookupColorSpace(obj1.getName(), &obj2);
2107 if (!obj2.isNull()) {
2108 obj1.free();
2109 obj1 = obj2;
2110 } else {
2111 obj2.free();
2112 }
2113 }
2114 colorSpace = GfxColorSpace::parse(&obj1);
2115 obj1.free();
2116 if (!colorSpace) {
2117 goto err1;
2118 }
2119 dict->lookup("Decode", &obj1);
2120 if (obj1.isNull()) {
2121 obj1.free();
2122 dict->lookup("D", &obj1);
2123 }
2124 colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
2125 obj1.free();
2126 if (!colorMap->isOk()) {
2127 delete colorMap;
2128 goto err1;
2129 }
2130
2131 // get the mask
2132 haveMask = gFalse;
2133 dict->lookup("Mask", &maskObj);
2134 if (maskObj.isArray()) {
2135 for (i = 0; i < maskObj.arrayGetLength(); ++i) {
2136 maskObj.arrayGet(i, &obj1);
2137 maskColors[i] = obj1.getInt();
2138 obj1.free();
2139 }
2140 haveMask = gTrue;
2141 }
2142
2143 // draw it
2144 out->drawImage(state, ref, str, width, height, colorMap,
2145 haveMask ? maskColors : (int *)NULL, inlineImg);
2146 delete colorMap;
2147
2148 maskObj.free();
2149 }
2150
2151 return;
2152
2153 err2:
2154 obj1.free();
2155 err1:
2156 error(getPos(), "Bad image parameters");
2157}
2158
2159void Gfx::doForm(Object *str) {
2160 Dict *dict;
2161 Object matrixObj, bboxObj;
2162 fouble m[6], bbox[6];
2163 Object resObj;
2164 Dict *resDict;
2165 Object obj1;
2166 int i;
2167
2168 // get stream dict
2169 dict = str->streamGetDict();
2170
2171 // check form type
2172 dict->lookup("FormType", &obj1);
2173 if (!(obj1.isInt() && obj1.getInt() == 1)) {
2174 error(getPos(), "Unknown form type");
2175 }
2176 obj1.free();
2177
2178 // get bounding box
2179 dict->lookup("BBox", &bboxObj);
2180 if (!bboxObj.isArray()) {
2181 matrixObj.free();
2182 bboxObj.free();
2183 error(getPos(), "Bad form bounding box");
2184 return;
2185 }
2186 for (i = 0; i < 4; ++i) {
2187 bboxObj.arrayGet(i, &obj1);
2188 bbox[i] = obj1.getNum();
2189 obj1.free();
2190 }
2191 bboxObj.free();
2192
2193 // get matrix
2194 dict->lookup("Matrix", &matrixObj);
2195 if (matrixObj.isArray()) {
2196 for (i = 0; i < 6; ++i) {
2197 matrixObj.arrayGet(i, &obj1);
2198 m[i] = obj1.getNum();
2199 obj1.free();
2200 }
2201 } else {
2202 m[0] = 1; m[1] = 0;
2203 m[2] = 0; m[3] = 1;
2204 m[4] = 0; m[5] = 0;
2205 }
2206 matrixObj.free();
2207
2208 // get resources
2209 dict->lookup("Resources", &resObj);
2210 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2211
2212 // draw it
2213 doForm1(str, resDict, m, bbox);
2214
2215 resObj.free();
2216}
2217
2218void Gfx::doWidgetForm(Object *str, fouble xMin, fouble yMin,
2219 fouble xMax, fouble yMax) {
2220 Dict *dict, *resDict;
2221 Object matrixObj, bboxObj, resObj;
2222 Object obj1;
2223 fouble m[6], bbox[6];
2224 fouble sx, sy;
2225 int i;
2226
2227 // get stream dict
2228 dict = str->streamGetDict();
2229
2230 // get bounding box
2231 dict->lookup("BBox", &bboxObj);
2232 if (!bboxObj.isArray()) {
2233 bboxObj.free();
2234 error(getPos(), "Bad form bounding box");
2235 return;
2236 }
2237 for (i = 0; i < 4; ++i) {
2238 bboxObj.arrayGet(i, &obj1);
2239 bbox[i] = obj1.getNum();
2240 obj1.free();
2241 }
2242 bboxObj.free();
2243
2244 // get matrix
2245 dict->lookup("Matrix", &matrixObj);
2246 if (matrixObj.isArray()) {
2247 for (i = 0; i < 6; ++i) {
2248 matrixObj.arrayGet(i, &obj1);
2249 m[i] = obj1.getNum();
2250 obj1.free();
2251 }
2252 } else {
2253 m[0] = 1; m[1] = 0;
2254 m[2] = 0; m[3] = 1;
2255 m[4] = 0; m[5] = 0;
2256 }
2257 matrixObj.free();
2258
2259 // scale form bbox to widget rectangle
2260 sx = fabs((xMax - xMin) / (bbox[2] - bbox[0]));
2261 sy = fabs((yMax - yMin) / (bbox[3] - bbox[1]));
2262 m[0] *= sx; m[1] *= sy;
2263 m[2] *= sx; m[3] *= sy;
2264 m[4] *= sx; m[5] *= sy;
2265
2266 // translate to widget rectangle
2267 m[4] += xMin;
2268 m[5] += yMin;
2269
2270 // get resources
2271 dict->lookup("Resources", &resObj);
2272 resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
2273
2274 // draw it
2275 doForm1(str, resDict, m, bbox);
2276
2277 resObj.free();
2278 bboxObj.free();
2279}
2280
2281void Gfx::doForm1(Object *str, Dict *resDict, fouble *matrix, fouble *bbox) {
2282 Parser *oldParser;
2283 fouble oldBaseMatrix[6];
2284 GfxResources *resPtr;
2285 int i;
2286
2287 // push new resources on stack
2288 res = new GfxResources(xref, resDict, res);
2289
2290 // save current graphics state
2291 out->saveState(state);
2292 state = state->save();
2293
2294 // save current parser
2295 oldParser = parser;
2296
2297 // set form transformation matrix
2298 state->concatCTM(matrix[0], matrix[1], matrix[2],
2299 matrix[3], matrix[4], matrix[5]);
2300 out->updateCTM(state, matrix[0], matrix[1], matrix[2],
2301 matrix[3], matrix[4], matrix[5]);
2302
2303 // set new base matrix
2304 for (i = 0; i < 6; ++i) {
2305 oldBaseMatrix[i] = baseMatrix[i];
2306 baseMatrix[i] = state->getCTM()[i];
2307 }
2308
2309 // set form bounding box
2310 state->moveTo(bbox[0], bbox[1]);
2311 state->lineTo(bbox[2], bbox[1]);
2312 state->lineTo(bbox[2], bbox[3]);
2313 state->lineTo(bbox[0], bbox[3]);
2314 state->closePath();
2315 state->clip();
2316 out->clip(state);
2317 state->clearPath();
2318
2319 // draw the form
2320 display(str, gFalse);
2321
2322 // restore base matrix
2323 for (i = 0; i < 6; ++i) {
2324 baseMatrix[i] = oldBaseMatrix[i];
2325 }
2326
2327 // restore parser
2328 parser = oldParser;
2329
2330 // restore graphics state
2331 state = state->restore();
2332 out->restoreState(state);
2333
2334 // pop resource stack
2335 resPtr = res->getNext();
2336 delete res;
2337 res = resPtr;
2338
2339 return;
2340}
2341
2342//------------------------------------------------------------------------
2343// in-line image operators
2344//------------------------------------------------------------------------
2345
2346void Gfx::opBeginImage(Object args[], int numArgs) {
2347 Stream *str;
2348 int c1, c2;
2349
2350 // build dict/stream
2351 str = buildImageStream();
2352
2353 // display the image
2354 if (str) {
2355 doImage(NULL, str, gTrue);
2356
2357 // skip 'EI' tag
2358 c1 = str->getBaseStream()->getChar();
2359 c2 = str->getBaseStream()->getChar();
2360 while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
2361 c1 = c2;
2362 c2 = str->getBaseStream()->getChar();
2363 }
2364 delete str;
2365 }
2366}
2367
2368Stream *Gfx::buildImageStream() {
2369 Object dict;
2370 Object obj;
2371 char *key;
2372 Stream *str;
2373
2374 // build dictionary
2375 dict.initDict(xref);
2376 parser->getObj(&obj);
2377 while (!obj.isCmd("ID") && !obj.isEOF()) {
2378 if (!obj.isName()) {
2379 error(getPos(), "Inline image dictionary key must be a name object");
2380 obj.free();
2381 parser->getObj(&obj);
2382 } else {
2383 key = copyString(obj.getName());
2384 obj.free();
2385 parser->getObj(&obj);
2386 if (obj.isEOF() || obj.isError())
2387 break;
2388 dict.dictAdd(key, &obj);
2389 }
2390 parser->getObj(&obj);
2391 }
2392 if (obj.isEOF())
2393 error(getPos(), "End of file in inline image");
2394 obj.free();
2395
2396 // make stream
2397 str = new EmbedStream(parser->getStream(), &dict);
2398 str = str->addFilters(&dict);
2399
2400 return str;
2401}
2402
2403void Gfx::opImageData(Object args[], int numArgs) {
2404 error(getPos(), "Internal: got 'ID' operator");
2405}
2406
2407void Gfx::opEndImage(Object args[], int numArgs) {
2408 error(getPos(), "Internal: got 'EI' operator");
2409}
2410
2411//------------------------------------------------------------------------
2412// type 3 font operators
2413//------------------------------------------------------------------------
2414
2415void Gfx::opSetCharWidth(Object args[], int numArgs) {
2416 error(getPos(), "Encountered 'd0' operator in content stream");
2417}
2418
2419void Gfx::opSetCacheDevice(Object args[], int numArgs) {
2420 error(getPos(), "Encountered 'd1' operator in content stream");
2421}
2422
2423//------------------------------------------------------------------------
2424// compatibility operators
2425//------------------------------------------------------------------------
2426
2427void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
2428 ++ignoreUndef;
2429}
2430
2431void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
2432 if (ignoreUndef > 0)
2433 --ignoreUndef;
2434}
2435
2436//------------------------------------------------------------------------
2437// marked content operators
2438//------------------------------------------------------------------------
2439
2440void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
2441 if (printCommands) {
2442 printf(" marked content: %s ", args[0].getName());
2443 if (numArgs == 2)
2444 args[2].print(stdout);
2445 printf("\n");
2446 fflush(stdout);
2447 }
2448}
2449
2450void Gfx::opEndMarkedContent(Object args[], int numArgs) {
2451}
2452
2453void Gfx::opMarkPoint(Object args[], int numArgs) {
2454 if (printCommands) {
2455 printf(" mark point: %s ", args[0].getName());
2456 if (numArgs == 2)
2457 args[2].print(stdout);
2458 printf("\n");
2459 fflush(stdout);
2460 }
2461}