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