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