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