]> www.fi.muni.cz Git - evince.git/blob - xpdf/Gfx.cc
[dualscreen] fix crash on ctrl+w and fix control window closing
[evince.git] / xpdf / Gfx.cc
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 // Gfx
193 //------------------------------------------------------------------------
194
195 Gfx::Gfx(OutputDev *out1, int pageNum, Dict *resDict,
196          int dpi, double x1, double y1, double x2, double y2, GBool crop,
197          double cropX1, double cropY1, double cropX2, double cropY2,
198          int rotate) {
199   Object obj1;
200
201   // start the resource stack
202   res = new GfxResources(NULL);
203
204   // build font dictionary
205   res->fonts = NULL;
206   if (resDict) {
207     resDict->lookup("Font", &obj1);
208     if (obj1.isDict())
209       res->fonts = new GfxFontDict(obj1.getDict());
210     obj1.free();
211   }
212
213   // get XObject dictionary
214   if (resDict)
215     resDict->lookup("XObject", &res->xObjDict);
216   else
217     res->xObjDict.initNull();
218
219   // get colorspace dictionary
220   if (resDict)
221     resDict->lookup("ColorSpace", &res->colorSpaceDict);
222   else
223     res->colorSpaceDict.initNull();
224
225   // initialize
226   out = out1;
227   state = new GfxState(dpi, x1, y1, x2, y2, rotate, out->upsideDown());
228   fontChanged = gFalse;
229   clip = clipNone;
230   ignoreUndef = 0;
231   out->startPage(pageNum, state);
232   out->setDefaultCTM(state->getCTM());
233   out->updateAll(state);
234
235   // set crop box
236   if (crop) {
237     state->moveTo(cropX1, cropY1);
238     state->lineTo(cropX2, cropY1);
239     state->lineTo(cropX2, cropY2);
240     state->lineTo(cropX1, cropY2);
241     state->closePath();
242     out->clip(state);
243     state->clearPath();
244   }
245 }
246
247 Gfx::~Gfx() {
248   GfxResources *resPtr;
249
250   while (state->hasSaves()) {
251     state = state->restore();
252     out->restoreState(state);
253   }
254   out->endPage();
255   while (res) {
256     resPtr = res->next;
257     delete res;
258     res = resPtr;
259   }
260   if (state)
261     delete state;
262 }
263
264 GfxResources::~GfxResources() {
265   if (fonts)
266     delete fonts;
267   xObjDict.free();
268   colorSpaceDict.free();
269 }
270
271 void Gfx::display(Object *obj) {
272   Object obj2;
273   int i;
274
275   if (obj->isArray()) {
276     for (i = 0; i < obj->arrayGetLength(); ++i) {
277       obj->arrayGet(i, &obj2);
278       if (!obj2.isStream()) {
279         error(-1, "Weird page contents");
280         obj2.free();
281         return;
282       }
283       obj2.free();
284     }
285   } else if (!obj->isStream()) {
286     error(-1, "Weird page contents");
287     return;
288   }
289   parser = new Parser(new Lexer(obj));
290   go();
291 }
292
293 void Gfx::go() {
294   Object obj;
295   Object args[maxArgs];
296   int numCmds, numArgs;
297   int i;
298
299   // scan a sequence of objects
300   numCmds = 0;
301   numArgs = 0;
302   parser->getObj(&obj);
303   while (!obj.isEOF()) {
304
305     // got a command - execute it
306     if (obj.isCmd()) {
307       if (printCommands) {
308         obj.print(stdout);
309         for (i = 0; i < numArgs; ++i) {
310           printf(" ");
311           args[i].print(stdout);
312         }
313         printf("\n");
314       }
315       execOp(&obj, args, numArgs);
316       obj.free();
317       for (i = 0; i < numArgs; ++i)
318         args[i].free();
319       numArgs = 0;
320
321       // periodically update display
322       if (++numCmds == 200) {
323         out->dump();
324         numCmds = 0;
325       }
326
327     // got an argument - save it
328     } else if (numArgs < maxArgs) {
329       args[numArgs++] = obj;
330
331     // too many arguments - something is wrong
332     } else {
333       error(getPos(), "Too many args in content stream");
334       if (printCommands) {
335         printf("throwing away arg: ");
336         obj.print(stdout);
337         printf("\n");
338       }
339       obj.free();
340     }
341
342     // grab the next object
343     parser->getObj(&obj);
344   }
345   obj.free();
346
347   // args at end with no command
348   if (numArgs > 0) {
349     error(getPos(), "Leftover args in content stream");
350     if (printCommands) {
351       printf("%d leftovers:", numArgs);
352       for (i = 0; i < numArgs; ++i) {
353         printf(" ");
354         args[i].print(stdout);
355       }
356       printf("\n");
357     }
358     for (i = 0; i < numArgs; ++i)
359       args[i].free();
360   }
361
362   // update display
363   if (numCmds > 0)
364     out->dump();
365
366   // clean up
367   if (parser)
368     delete parser;
369   if (printCommands)
370     fflush(stdout);
371 }
372
373 void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
374   Operator *op;
375   char *name;
376   int i;
377
378   // find operator
379   name = cmd->getName();
380   if (!(op = findOp(name))) {
381     if (ignoreUndef == 0)
382       error(getPos(), "Unknown operator '%s'", name);
383     return;
384   }
385
386   // type check args
387   if (op->numArgs >= 0) {
388     if (numArgs != op->numArgs) {
389       error(getPos(), "Wrong number (%d) of args to '%s' operator",
390             numArgs, name);
391       return;
392     }
393   } else {
394     if (numArgs > -op->numArgs) {
395       error(getPos(), "Too many (%d) args to '%s' operator",
396             numArgs, name);
397       return;
398     }
399   }
400   for (i = 0; i < numArgs; ++i) {
401     if (!checkArg(&args[i], op->tchk[i])) {
402       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
403             i, name, args[i].getTypeName());
404       return;
405     }
406   }
407
408   // do it
409   (this->*op->func)(args, numArgs);
410 }
411
412 Operator *Gfx::findOp(char *name) {
413   int a, b, m, cmp;
414
415   a = -1;
416   b = numOps;
417   // invariant: opTab[a] < name < opTab[b]
418   while (b - a > 1) {
419     m = (a + b) / 2;
420     cmp = strcmp(opTab[m].name, name);
421     if (cmp < 0)
422       a = m;
423     else if (cmp > 0)
424       b = m;
425     else
426       a = b = m;
427   }
428   if (cmp != 0)
429     return NULL;
430   return &opTab[a];
431 }
432
433 GBool Gfx::checkArg(Object *arg, TchkType type) {
434   switch (type) {
435   case tchkBool:   return arg->isBool();
436   case tchkInt:    return arg->isInt();
437   case tchkNum:    return arg->isNum();
438   case tchkString: return arg->isString();
439   case tchkName:   return arg->isName();
440   case tchkArray:  return arg->isArray();
441   case tchkProps:  return arg->isDict() || arg->isName();
442   case tchkSCN:    return arg->isNum() || arg->isName();
443   case tchkNone:   return gFalse;
444   }
445   return gFalse;
446 }
447
448 int Gfx::getPos() {
449   return parser->getPos();
450 }
451
452 GfxFont *Gfx::lookupFont(char *name) {
453   GfxFont *font;
454   GfxResources *resPtr;
455
456   for (resPtr = res; resPtr; resPtr = resPtr->next) {
457     if (resPtr->fonts) {
458       if ((font = resPtr->fonts->lookup(name)))
459         return font;
460     }
461   }
462   error(getPos(), "unknown font tag '%s'", name);
463   return NULL;
464 }
465
466 GBool Gfx::lookupXObject(char *name, Object *obj) {
467   GfxResources *resPtr;
468
469   for (resPtr = res; resPtr; resPtr = resPtr->next) {
470     if (resPtr->xObjDict.isDict()) {
471       if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
472         return gTrue;
473       obj->free();
474     }
475   }
476   error(getPos(), "XObject '%s' is unknown", name);
477   return gFalse;
478 }
479
480 void Gfx::lookupColorSpace(char *name, Object *obj) {
481   GfxResources *resPtr;
482
483   for (resPtr = res; resPtr; resPtr = resPtr->next) {
484     if (resPtr->colorSpaceDict.isDict()) {
485       if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull())
486         return;
487       obj->free();
488     }
489   }
490   obj->initNull();
491 }
492
493 //------------------------------------------------------------------------
494 // graphics state operators
495 //------------------------------------------------------------------------
496
497 void Gfx::opSave(Object args[], int numArgs) {
498   out->saveState(state);
499   state = state->save();
500 }
501
502 void Gfx::opRestore(Object args[], int numArgs) {
503   state = state->restore();
504   out->restoreState(state);
505 }
506
507 void Gfx::opConcat(Object args[], int numArgs) {
508   state->concatCTM(args[0].getNum(), args[1].getNum(),
509                    args[2].getNum(), args[3].getNum(),
510                    args[4].getNum(), args[5].getNum());
511   out->updateCTM(state, args[0].getNum(), args[1].getNum(),
512                  args[2].getNum(), args[3].getNum(),
513                  args[4].getNum(), args[5].getNum());
514   fontChanged = gTrue;
515 }
516
517 void Gfx::opSetDash(Object args[], int numArgs) {
518   Array *a;
519   int length;
520   Object obj;
521   double *dash;
522   int i;
523
524   a = args[0].getArray();
525   length = a->getLength();
526   if (length == 0) {
527     dash = NULL;
528   } else {
529     dash = (double *)gmalloc(length * sizeof(double));
530     for (i = 0; i < length; ++i) {
531       dash[i] = a->get(i, &obj)->getNum();
532       obj.free();
533     }
534   }
535   state->setLineDash(dash, length, args[1].getNum());
536   out->updateLineDash(state);
537 }
538
539 void Gfx::opSetFlat(Object args[], int numArgs) {
540   state->setFlatness((int)args[0].getNum());
541   out->updateFlatness(state);
542 }
543
544 void Gfx::opSetLineJoin(Object args[], int numArgs) {
545   state->setLineJoin(args[0].getInt());
546   out->updateLineJoin(state);
547 }
548
549 void Gfx::opSetLineCap(Object args[], int numArgs) {
550   state->setLineCap(args[0].getInt());
551   out->updateLineCap(state);
552 }
553
554 void Gfx::opSetMiterLimit(Object args[], int numArgs) {
555   state->setMiterLimit(args[0].getNum());
556   out->updateMiterLimit(state);
557 }
558
559 void Gfx::opSetLineWidth(Object args[], int numArgs) {
560   state->setLineWidth(args[0].getNum());
561   out->updateLineWidth(state);
562 }
563
564 void Gfx::opSetExtGState(Object args[], int numArgs) {
565 }
566
567 void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
568 }
569
570 //------------------------------------------------------------------------
571 // color operators
572 //------------------------------------------------------------------------
573
574 void Gfx::opSetFillGray(Object args[], int numArgs) {
575   state->setFillColorSpace(new GfxColorSpace(colorGray));
576   state->setFillGray(args[0].getNum());
577   out->updateFillColor(state);
578 }
579
580 void Gfx::opSetStrokeGray(Object args[], int numArgs) {
581   state->setStrokeColorSpace(new GfxColorSpace(colorGray));
582   state->setStrokeGray(args[0].getNum());
583   out->updateStrokeColor(state);
584 }
585
586 void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
587   state->setFillColorSpace(new GfxColorSpace(colorCMYK));
588   state->setFillCMYK(args[0].getNum(), args[1].getNum(),
589                      args[2].getNum(), args[3].getNum());
590   out->updateFillColor(state);
591 }
592
593 void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
594   state->setStrokeColorSpace(new GfxColorSpace(colorCMYK));
595   state->setStrokeCMYK(args[0].getNum(), args[1].getNum(),
596                        args[2].getNum(), args[3].getNum());
597   out->updateStrokeColor(state);
598 }
599
600 void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
601   state->setFillColorSpace(new GfxColorSpace(colorRGB));
602   state->setFillRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
603   out->updateFillColor(state);
604 }
605
606 void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
607   state->setStrokeColorSpace(new GfxColorSpace(colorRGB));
608   state->setStrokeRGB(args[0].getNum(), args[1].getNum(), args[2].getNum());
609   out->updateStrokeColor(state);
610 }
611
612 void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
613   Object obj;
614   GfxColorSpace *colorSpace;
615   double x[4];
616
617   lookupColorSpace(args[0].getName(), &obj);
618   if (obj.isNull())
619     colorSpace = new GfxColorSpace(&args[0]);
620   else
621     colorSpace = new GfxColorSpace(&obj);
622   obj.free();
623   if (colorSpace->isOk()) {
624     state->setFillColorSpace(colorSpace);
625   } else {
626     delete colorSpace;
627     error(getPos(), "Bad colorspace");
628   }
629   x[0] = x[1] = x[2] = x[3] = 0;
630   state->setFillColor(x);
631   out->updateFillColor(state);
632 }
633
634 void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
635   Object obj;
636   GfxColorSpace *colorSpace;
637   double x[4];
638
639   lookupColorSpace(args[0].getName(), &obj);
640   if (obj.isNull())
641     colorSpace = new GfxColorSpace(&args[0]);
642   else
643     colorSpace = new GfxColorSpace(&obj);
644   obj.free();
645   if (colorSpace->isOk()) {
646     state->setStrokeColorSpace(colorSpace);
647   } else {
648     delete colorSpace;
649     error(getPos(), "Bad colorspace");
650   }
651   x[0] = x[1] = x[2] = x[3] = 0;
652   state->setStrokeColor(x);
653   out->updateStrokeColor(state);
654 }
655
656 void Gfx::opSetFillColor(Object args[], int numArgs) {
657   double x[4];
658   int i;
659
660   x[0] = x[1] = x[2] = x[3] = 0;
661   for (i = 0; i < numArgs; ++i)
662     x[i] = args[i].getNum();
663   state->setFillColor(x);
664   out->updateFillColor(state);
665 }
666
667 void Gfx::opSetStrokeColor(Object args[], int numArgs) {
668   double x[4];
669   int i;
670
671   x[0] = x[1] = x[2] = x[3] = 0;
672   for (i = 0; i < numArgs; ++i)
673     x[i] = args[i].getNum();
674   state->setStrokeColor(x);
675   out->updateStrokeColor(state);
676 }
677
678 void Gfx::opSetFillColorN(Object args[], int numArgs) {
679   double x[4];
680   int i;
681
682   x[0] = x[1] = x[2] = x[3] = 0;
683   for (i = 0; i < numArgs && i < 4; ++i) {
684     if (args[i].isNum())
685       x[i] = args[i].getNum();
686     else
687       break;
688   }
689   state->setFillColor(x);
690   out->updateFillColor(state);
691 }
692
693 void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
694   double x[4];
695   int i;
696
697   x[0] = x[1] = x[2] = x[3] = 0;
698   for (i = 0; i < numArgs && i < 4; ++i) {
699     if (args[i].isNum())
700       x[i] = args[i].getNum();
701     else
702       break;
703   }
704   state->setStrokeColor(x);
705   out->updateStrokeColor(state);
706 }
707
708 //------------------------------------------------------------------------
709 // path segment operators
710 //------------------------------------------------------------------------
711
712 void Gfx::opMoveTo(Object args[], int numArgs) {
713   state->moveTo(args[0].getNum(), args[1].getNum());
714 }
715
716 void Gfx::opLineTo(Object args[], int numArgs) {
717   if (!state->isCurPt()) {
718     error(getPos(), "No current point in lineto");
719     return;
720   }
721   state->lineTo(args[0].getNum(), args[1].getNum());
722 }
723
724 void Gfx::opCurveTo(Object args[], int numArgs) {
725   double x1, y1, x2, y2, x3, y3;
726
727   if (!state->isCurPt()) {
728     error(getPos(), "No current point in curveto");
729     return;
730   }
731   x1 = args[0].getNum();
732   y1 = args[1].getNum();
733   x2 = args[2].getNum();
734   y2 = args[3].getNum();
735   x3 = args[4].getNum();
736   y3 = args[5].getNum();
737   state->curveTo(x1, y1, x2, y2, x3, y3);
738 }
739
740 void Gfx::opCurveTo1(Object args[], int numArgs) {
741   double x1, y1, x2, y2, x3, y3;
742
743   if (!state->isCurPt()) {
744     error(getPos(), "No current point in curveto1");
745     return;
746   }
747   x1 = state->getCurX();
748   y1 = state->getCurY();
749   x2 = args[0].getNum();
750   y2 = args[1].getNum();
751   x3 = args[2].getNum();
752   y3 = args[3].getNum();
753   state->curveTo(x1, y1, x2, y2, x3, y3);
754 }
755
756 void Gfx::opCurveTo2(Object args[], int numArgs) {
757   double x1, y1, x2, y2, x3, y3;
758
759   if (!state->isCurPt()) {
760     error(getPos(), "No current point in curveto2");
761     return;
762   }
763   x1 = args[0].getNum();
764   y1 = args[1].getNum();
765   x2 = args[2].getNum();
766   y2 = args[3].getNum();
767   x3 = x2;
768   y3 = y2;
769   state->curveTo(x1, y1, x2, y2, x3, y3);
770 }
771
772 void Gfx::opRectangle(Object args[], int numArgs) {
773   double x, y, w, h;
774
775   x = args[0].getNum();
776   y = args[1].getNum();
777   w = args[2].getNum();
778   h = args[3].getNum();
779   state->moveTo(x, y);
780   state->lineTo(x + w, y);
781   state->lineTo(x + w, y + h);
782   state->lineTo(x, y + h);
783   state->closePath();
784 }
785
786 void Gfx::opClosePath(Object args[], int numArgs) {
787   if (!state->isPath()) {
788     error(getPos(), "No current point in closepath");
789     return;
790   }
791   state->closePath();
792 }
793
794 //------------------------------------------------------------------------
795 // path painting operators
796 //------------------------------------------------------------------------
797
798 void Gfx::opEndPath(Object args[], int numArgs) {
799   doEndPath();
800 }
801
802 void Gfx::opStroke(Object args[], int numArgs) {
803   if (!state->isCurPt()) {
804     //error(getPos(), "No path in stroke");
805     return;
806   }
807   if (state->isPath())
808     out->stroke(state);
809   doEndPath();
810 }
811
812 void Gfx::opCloseStroke(Object args[], int numArgs) {
813   if (!state->isCurPt()) {
814     //error(getPos(), "No path in closepath/stroke");
815     return;
816   }
817   if (state->isPath()) {
818     state->closePath();
819     out->stroke(state);
820   }
821   doEndPath();
822 }
823
824 void Gfx::opFill(Object args[], int numArgs) {
825   if (!state->isCurPt()) {
826     //error(getPos(), "No path in fill");
827     return;
828   }
829   if (state->isPath())
830     out->fill(state);
831   doEndPath();
832 }
833
834 void Gfx::opEOFill(Object args[], int numArgs) {
835   if (!state->isCurPt()) {
836     //error(getPos(), "No path in eofill");
837     return;
838   }
839   if (state->isPath())
840     out->eoFill(state);
841   doEndPath();
842 }
843
844 void Gfx::opFillStroke(Object args[], int numArgs) {
845   if (!state->isCurPt()) {
846     //error(getPos(), "No path in fill/stroke");
847     return;
848   }
849   if (state->isPath()) {
850     out->fill(state);
851     out->stroke(state);
852   }
853   doEndPath();
854 }
855
856 void Gfx::opCloseFillStroke(Object args[], int numArgs) {
857   if (!state->isCurPt()) {
858     //error(getPos(), "No path in closepath/fill/stroke");
859     return;
860   }
861   if (state->isPath()) {
862     state->closePath();
863     out->fill(state);
864     out->stroke(state);
865   }
866   doEndPath();
867 }
868
869 void Gfx::opEOFillStroke(Object args[], int numArgs) {
870   if (!state->isCurPt()) {
871     //error(getPos(), "No path in eofill/stroke");
872     return;
873   }
874   if (state->isPath()) {
875     out->eoFill(state);
876     out->stroke(state);
877   }
878   doEndPath();
879 }
880
881 void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
882   if (!state->isCurPt()) {
883     //error(getPos(), "No path in closepath/eofill/stroke");
884     return;
885   }
886   if (state->isPath()) {
887     state->closePath();
888     out->eoFill(state);
889     out->stroke(state);
890   }
891   doEndPath();
892 }
893
894 void Gfx::opShFill(Object args[], int numArgs) {
895 }
896
897 void Gfx::doEndPath() {
898   if (state->isPath()) {
899     if (clip == clipNormal)
900       out->clip(state);
901     else if (clip == clipEO)
902       out->eoClip(state);
903   }
904   clip = clipNone;
905   state->clearPath();
906 }
907
908 //------------------------------------------------------------------------
909 // path clipping operators
910 //------------------------------------------------------------------------
911
912 void Gfx::opClip(Object args[], int numArgs) {
913   clip = clipNormal;
914 }
915
916 void Gfx::opEOClip(Object args[], int numArgs) {
917   clip = clipEO;
918 }
919
920 //------------------------------------------------------------------------
921 // text object operators
922 //------------------------------------------------------------------------
923
924 void Gfx::opBeginText(Object args[], int numArgs) {
925   state->setTextMat(1, 0, 0, 1, 0, 0);
926   state->textMoveTo(0, 0);
927   out->updateTextMat(state);
928   out->updateTextPos(state);
929   fontChanged = gTrue;
930 }
931
932 void Gfx::opEndText(Object args[], int numArgs) {
933 }
934
935 //------------------------------------------------------------------------
936 // text state operators
937 //------------------------------------------------------------------------
938
939 void Gfx::opSetCharSpacing(Object args[], int numArgs) {
940   state->setCharSpace(args[0].getNum());
941   out->updateCharSpace(state);
942 }
943
944 void Gfx::opSetFont(Object args[], int numArgs) {
945   GfxFont *font;
946
947   if (!(font = lookupFont(args[0].getName())))
948     return;
949   if (printCommands) {
950     printf("  font: '%s' %g\n",
951            font->getName() ? font->getName()->getCString() : "???",
952            args[1].getNum());
953   }
954   state->setFont(font, args[1].getNum());
955   fontChanged = gTrue;
956 }
957
958 void Gfx::opSetTextLeading(Object args[], int numArgs) {
959   state->setLeading(args[0].getNum());
960 }
961
962 void Gfx::opSetTextRender(Object args[], int numArgs) {
963   state->setRender(args[0].getInt());
964   out->updateRender(state);
965 }
966
967 void Gfx::opSetTextRise(Object args[], int numArgs) {
968   state->setRise(args[0].getNum());
969   out->updateRise(state);
970 }
971
972 void Gfx::opSetWordSpacing(Object args[], int numArgs) {
973   state->setWordSpace(args[0].getNum());
974   out->updateWordSpace(state);
975 }
976
977 void Gfx::opSetHorizScaling(Object args[], int numArgs) {
978   state->setHorizScaling(args[0].getNum());
979   out->updateHorizScaling(state);
980 }
981
982 //------------------------------------------------------------------------
983 // text positioning operators
984 //------------------------------------------------------------------------
985
986 void Gfx::opTextMove(Object args[], int numArgs) {
987   double tx, ty;
988
989   tx = state->getLineX() + args[0].getNum();
990   ty = state->getLineY() + args[1].getNum();
991   state->textMoveTo(tx, ty);
992   out->updateTextPos(state);
993 }
994
995 void Gfx::opTextMoveSet(Object args[], int numArgs) {
996   double tx, ty;
997
998   tx = state->getLineX() + args[0].getNum();
999   ty = args[1].getNum();
1000   state->setLeading(-ty);
1001   ty += state->getLineY();
1002   state->textMoveTo(tx, ty);
1003   out->updateTextPos(state);
1004 }
1005
1006 void Gfx::opSetTextMatrix(Object args[], int numArgs) {
1007   state->setTextMat(args[0].getNum(), args[1].getNum(),
1008                     args[2].getNum(), args[3].getNum(),
1009                     args[4].getNum(), args[5].getNum());
1010   state->textMoveTo(0, 0);
1011   out->updateTextMat(state);
1012   out->updateTextPos(state);
1013   fontChanged = gTrue;
1014 }
1015
1016 void Gfx::opTextNextLine(Object args[], int numArgs) {
1017   double tx, ty;
1018
1019   tx = state->getLineX();
1020   ty = state->getLineY() - state->getLeading();
1021   state->textMoveTo(tx, ty);
1022   out->updateTextPos(state);
1023 }
1024
1025 //------------------------------------------------------------------------
1026 // text string operators
1027 //------------------------------------------------------------------------
1028
1029 void Gfx::opShowText(Object args[], int numArgs) {
1030   if (!state->getFont()) {
1031     error(getPos(), "No font in show");
1032     return;
1033   }
1034   doShowText(args[0].getString());
1035 }
1036
1037 void Gfx::opMoveShowText(Object args[], int numArgs) {
1038   double tx, ty;
1039
1040   if (!state->getFont()) {
1041     error(getPos(), "No font in move/show");
1042     return;
1043   }
1044   tx = state->getLineX();
1045   ty = state->getLineY() - state->getLeading();
1046   state->textMoveTo(tx, ty);
1047   out->updateTextPos(state);
1048   doShowText(args[0].getString());
1049 }
1050
1051 void Gfx::opMoveSetShowText(Object args[], int numArgs) {
1052   double tx, ty;
1053
1054   if (!state->getFont()) {
1055     error(getPos(), "No font in move/set/show");
1056     return;
1057   }
1058   state->setWordSpace(args[0].getNum());
1059   state->setCharSpace(args[1].getNum());
1060   tx = state->getLineX();
1061   ty = state->getLineY() - state->getLeading();
1062   state->textMoveTo(tx, ty);
1063   out->updateWordSpace(state);
1064   out->updateCharSpace(state);
1065   out->updateTextPos(state);
1066   doShowText(args[2].getString());
1067 }
1068
1069 void Gfx::opShowSpaceText(Object args[], int numArgs) {
1070   Array *a;
1071   Object obj;
1072   int i;
1073
1074   if (!state->getFont()) {
1075     error(getPos(), "No font in show/space");
1076     return;
1077   }
1078   a = args[0].getArray();
1079   for (i = 0; i < a->getLength(); ++i) {
1080     a->get(i, &obj);
1081     if (obj.isNum()) {
1082       state->textShift(-obj.getNum() * 0.001 * state->getFontSize());
1083       out->updateTextShift(state, obj.getNum());
1084     } else if (obj.isString()) {
1085       doShowText(obj.getString());
1086     } else {
1087       error(getPos(), "Element of show/space array must be number or string");
1088     }
1089     obj.free();
1090   }
1091 }
1092
1093 void Gfx::doShowText(GString *s) {
1094   GfxFont *font;
1095   GfxFontEncoding16 *enc;
1096   Guchar *p;
1097   Guchar c8;
1098   int c16;
1099   GString *s16;
1100   int m, n;
1101   double dx, dy, width, height, w, h;
1102
1103   if (fontChanged) {
1104     out->updateFont(state);
1105     fontChanged = gFalse;
1106   }
1107   font = state->getFont();
1108
1109   //----- 16-bit font
1110   if (font->is16Bit()) {
1111     enc = font->getEncoding16();
1112     if (out->useDrawChar()) {
1113       out->beginString(state, s);
1114       s16 = NULL;
1115     } else {
1116       s16 = new GString("  ");
1117     }
1118     state->textTransformDelta(0, state->getRise(), &dx, &dy);
1119     p = (Guchar *)s->getCString();
1120     n = s->getLength();
1121     while (n > 0) {
1122       m = getNextChar16(enc, p, &c16);
1123       if (enc->wMode == 0) {
1124         width = state->getFontSize() * state->getHorizScaling() *
1125                 font->getWidth16(c16) +
1126                 state->getCharSpace();
1127         if (c16 == ' ') {
1128           width += state->getWordSpace();
1129         }
1130         height = 0;
1131       } else {
1132         width = 0;
1133         height = state->getFontSize() * font->getHeight16(c16);
1134       }
1135       state->textTransformDelta(width, height, &w, &h);
1136       if (out->useDrawChar()) {
1137         out->drawChar16(state, state->getCurX() + dx, state->getCurY() + dy,
1138                         w, h, c16);
1139       } else {
1140         s16->setChar(0, (char)(c16 >> 8));
1141         s16->setChar(1, (char)c16);
1142         out->drawString16(state, s16);
1143       }
1144       state->textShift(width, height);
1145       n -= m;
1146       p += m;
1147     }
1148     if (out->useDrawChar())
1149       out->endString(state);
1150     else
1151       delete s16;
1152
1153   //----- 8-bit font
1154   } else {
1155     if (out->useDrawChar()) {
1156       out->beginString(state, s);
1157       state->textTransformDelta(0, state->getRise(), &dx, &dy);
1158       for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1159         c8 = *p;
1160         width = state->getFontSize() * state->getHorizScaling() *
1161                 font->getWidth(c8) +
1162                 state->getCharSpace();
1163         if (c8 == ' ')
1164           width += state->getWordSpace();
1165         state->textTransformDelta(width, 0, &w, &h);
1166         out->drawChar(state, state->getCurX() + dx, state->getCurY() + dy,
1167                       w, h, c8);
1168         state->textShift(width);
1169       }
1170       out->endString(state);
1171     } else {
1172       out->drawString(state, s);
1173       width = state->getFontSize() * state->getHorizScaling() *
1174               font->getWidth(s) +
1175               s->getLength() * state->getCharSpace();
1176       for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
1177         if (*p == ' ')
1178           width += state->getWordSpace();
1179       }
1180       state->textShift(width);
1181     }
1182   }
1183 }
1184
1185 int Gfx::getNextChar16(GfxFontEncoding16 *enc, Guchar *p, int *c16) {
1186   int n;
1187   int code;
1188   int a, b, m;
1189
1190   n = enc->codeLen[*p];
1191   if (n == 1) {
1192     *c16 = enc->map1[*p];
1193   } else {
1194     code = (p[0] << 8) + p[1];
1195     a = 0;
1196     b = enc->map2Len;
1197     // invariant: map2[2*a] <= code < map2[2*b]
1198     while (b - a > 1) {
1199       m = (a + b) / 2;
1200       if (enc->map2[2*m] <= code)
1201         a = m;
1202       else if (enc->map2[2*m] > code)
1203         b = m;
1204       else
1205         break;
1206     }
1207     *c16 = enc->map2[2*a+1] + (code - enc->map2[2*a]);
1208   }
1209   return n;
1210 }
1211
1212 //------------------------------------------------------------------------
1213 // XObject operators
1214 //------------------------------------------------------------------------
1215
1216 void Gfx::opXObject(Object args[], int numArgs) {
1217   Object obj1, obj2;
1218
1219   if (!lookupXObject(args[0].getName(), &obj1))
1220     return;
1221   if (!obj1.isStream("XObject")) {
1222     error(getPos(), "XObject '%s' is wrong type", args[0].getName());
1223     obj1.free();
1224     return;
1225   }
1226   obj1.streamGetDict()->lookup("Subtype", &obj2);
1227   if (obj2.isName("Image"))
1228     doImage(obj1.getStream(), gFalse);
1229   else if (obj2.isName("Form"))
1230     doForm(&obj1);
1231   else if (obj2.isName())
1232     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
1233   else
1234     error(getPos(), "XObject subtype is missing or wrong type");
1235   obj2.free();
1236   obj1.free();
1237 }
1238
1239 void Gfx::doImage(Stream *str, GBool inlineImg) {
1240   Dict *dict;
1241   Object obj1, obj2;
1242   int width, height;
1243   int bits;
1244   GBool mask;
1245   GfxColorSpace *colorSpace;
1246   GfxImageColorMap *colorMap;
1247   GBool invert;
1248
1249   // get stream dict
1250   dict = str->getDict();
1251
1252   // get size
1253   dict->lookup("Width", &obj1);
1254   if (obj1.isNull()) {
1255     obj1.free();
1256     dict->lookup("W", &obj1);
1257   }
1258   if (!obj1.isInt())
1259     goto err2;
1260   width = obj1.getInt();
1261   obj1.free();
1262   dict->lookup("Height", &obj1);
1263   if (obj1.isNull()) {
1264     obj1.free();
1265     dict->lookup("H", &obj1);
1266   }
1267   if (!obj1.isInt())
1268     goto err2;
1269   height = obj1.getInt();
1270   obj1.free();
1271
1272   // image or mask?
1273   dict->lookup("ImageMask", &obj1);
1274   if (obj1.isNull()) {
1275     obj1.free();
1276     dict->lookup("IM", &obj1);
1277   }
1278   mask = gFalse;
1279   if (obj1.isBool())
1280     mask = obj1.getBool();
1281   else if (!obj1.isNull())
1282     goto err2;
1283   obj1.free();
1284
1285   // bit depth
1286   dict->lookup("BitsPerComponent", &obj1);
1287   if (obj1.isNull()) {
1288     obj1.free();
1289     dict->lookup("BPC", &obj1);
1290   }
1291   if (!obj1.isInt())
1292     goto err2;
1293   bits = obj1.getInt();
1294   obj1.free();
1295
1296   // display a mask
1297   if (mask) {
1298
1299     // check for inverted mask
1300     if (bits != 1)
1301       goto err1;
1302     invert = gFalse;
1303     dict->lookup("Decode", &obj1);
1304     if (obj1.isNull()) {
1305       obj1.free();
1306       dict->lookup("D", &obj1);
1307     }
1308     if (obj1.isArray()) {
1309       obj1.arrayGet(0, &obj2);
1310       if (obj2.isInt() && obj2.getInt() == 1)
1311         invert = gTrue;
1312       obj2.free();
1313     } else if (!obj1.isNull()) {
1314       goto err2;
1315     }
1316     obj1.free();
1317
1318     // draw it
1319     out->drawImageMask(state, str, width, height, invert, inlineImg);
1320
1321   } else {
1322
1323     // get color space and color map
1324     dict->lookup("ColorSpace", &obj1);
1325     if (obj1.isNull()) {
1326       obj1.free();
1327       dict->lookup("CS", &obj1);
1328     }
1329     if (obj1.isName()) {
1330       lookupColorSpace(obj1.getName(), &obj2);
1331       if (!obj2.isNull()) {
1332         obj1.free();
1333         obj1 = obj2;
1334       } else {
1335         obj2.free();
1336       }
1337     }
1338     colorSpace = new GfxColorSpace(&obj1);
1339     obj1.free();
1340     if (!colorSpace->isOk()) {
1341       delete colorSpace;
1342       goto err1;
1343     }
1344     dict->lookup("Decode", &obj1);
1345     if (obj1.isNull()) {
1346       obj1.free();
1347       dict->lookup("D", &obj1);
1348     }
1349     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
1350     obj1.free();
1351     if (!colorMap->isOk()) {
1352       delete colorSpace;
1353       goto err1;
1354     }
1355
1356     // draw it
1357     out->drawImage(state, str, width, height, colorMap, inlineImg);
1358     delete colorMap;
1359   }
1360
1361   return;
1362
1363  err2:
1364   obj1.free();
1365  err1:
1366   error(getPos(), "Bad image parameters");
1367 }
1368
1369 void Gfx::doForm(Object *str) {
1370   Parser *oldParser;
1371   GfxResources *resPtr;
1372   Dict *dict;
1373   Dict *resDict;
1374   Object matrixObj, bboxObj;
1375   double m[6];
1376   Object obj1, obj2;
1377   int i;
1378
1379   // get stream dict
1380   dict = str->streamGetDict();
1381
1382   // check form type
1383   dict->lookup("FormType", &obj1);
1384   if (!(obj1.isInt() && obj1.getInt() == 1)) {
1385     error(getPos(), "Unknown form type");
1386   }
1387   obj1.free();
1388
1389   // get matrix and bounding box
1390   dict->lookup("Matrix", &matrixObj);
1391   if (!matrixObj.isArray()) {
1392     matrixObj.free();
1393     error(getPos(), "Bad form matrix");
1394     return;
1395   }
1396   dict->lookup("BBox", &bboxObj);
1397   if (!bboxObj.isArray()) {
1398     matrixObj.free();
1399     bboxObj.free();
1400     error(getPos(), "Bad form bounding box");
1401     return;
1402   }
1403
1404   // push new resources on stack
1405   res = new GfxResources(res);
1406   dict->lookup("Resources", &obj1);
1407   if (obj1.isDict()) {
1408     resDict = obj1.getDict();
1409     res->fonts = NULL;
1410     resDict->lookup("Font", &obj2);
1411     if (obj2.isDict())
1412       res->fonts = new GfxFontDict(obj2.getDict());
1413     obj2.free();
1414     resDict->lookup("XObject", &res->xObjDict);
1415     resDict->lookup("ColorSpace", &res->colorSpaceDict);
1416     obj1.free();
1417   }
1418
1419   // save current graphics state
1420   out->saveState(state);
1421   state = state->save();
1422
1423   // save current parser
1424   oldParser = parser;
1425
1426   // set form transformation matrix
1427   for (i = 0; i < 6; ++i) {
1428     matrixObj.arrayGet(i, &obj1);
1429     m[i] = obj1.getNum();
1430     obj1.free();
1431   }
1432   state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
1433   out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
1434
1435   // set form bounding box
1436   for (i = 0; i < 4; ++i) {
1437     bboxObj.arrayGet(i, &obj1);
1438     m[i] = obj1.getNum();
1439     obj1.free();
1440   }
1441   state->moveTo(m[0], m[1]);
1442   state->lineTo(m[0]+m[2], m[1]);
1443   state->lineTo(m[0]+m[2], m[1]+m[3]);
1444   state->lineTo(m[0], m[1]+m[3]);
1445   state->closePath();
1446   out->clip(state);
1447   state->clearPath();
1448
1449   // draw the form
1450   display(str);
1451
1452   // free matrix and bounding box
1453   matrixObj.free();
1454   bboxObj.free();
1455
1456   // restore parser
1457   parser = oldParser;
1458
1459   // restore graphics state
1460   state = state->restore();
1461   out->restoreState(state);
1462
1463   // pop resource stack
1464   resPtr = res->next;
1465   delete res;
1466   res = resPtr;
1467
1468   return;
1469 }
1470
1471 //------------------------------------------------------------------------
1472 // in-line image operators
1473 //------------------------------------------------------------------------
1474
1475 void Gfx::opBeginImage(Object args[], int numArgs) {
1476   Stream *str;
1477   int c1, c2;
1478
1479   // build dict/stream
1480   str = buildImageStream();
1481
1482   // display the image
1483   if (str) {
1484     doImage(str, gTrue);
1485   
1486     // skip 'EI' tag
1487     c1 = str->getBaseStream()->getChar();
1488     c2 = str->getBaseStream()->getChar();
1489     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
1490       c1 = c2;
1491       c2 = str->getBaseStream()->getChar();
1492     }
1493     delete str;
1494   }
1495 }
1496
1497 Stream *Gfx::buildImageStream() {
1498   Object dict;
1499   Object obj;
1500   char *key;
1501   Stream *str;
1502
1503   // build dictionary
1504   dict.initDict();
1505   parser->getObj(&obj);
1506   while (!obj.isCmd("ID") && !obj.isEOF()) {
1507     if (!obj.isName()) {
1508       error(getPos(), "Inline image dictionary key must be a name object");
1509       obj.free();
1510       parser->getObj(&obj);
1511     } else {
1512       key = copyString(obj.getName());
1513       obj.free();
1514       parser->getObj(&obj);
1515       if (obj.isEOF() || obj.isError())
1516         break;
1517       dict.dictAdd(key, &obj);
1518     }
1519     parser->getObj(&obj);
1520   }
1521   if (obj.isEOF())
1522     error(getPos(), "End of file in inline image");
1523   obj.free();
1524
1525   // make stream
1526   str = new EmbedStream(parser->getStream(), &dict);
1527   str = str->addFilters(&dict);
1528
1529   return str;
1530 }
1531
1532 void Gfx::opImageData(Object args[], int numArgs) {
1533   error(getPos(), "Internal: got 'ID' operator");
1534 }
1535
1536 void Gfx::opEndImage(Object args[], int numArgs) {
1537   error(getPos(), "Internal: got 'EI' operator");
1538 }
1539
1540 //------------------------------------------------------------------------
1541 // type 3 font operators
1542 //------------------------------------------------------------------------
1543
1544 void Gfx::opSetCharWidth(Object args[], int numArgs) {
1545   error(getPos(), "Encountered 'd0' operator in content stream");
1546 }
1547
1548 void Gfx::opSetCacheDevice(Object args[], int numArgs) {
1549   error(getPos(), "Encountered 'd1' operator in content stream");
1550 }
1551
1552 //------------------------------------------------------------------------
1553 // compatibility operators
1554 //------------------------------------------------------------------------
1555
1556 void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
1557   ++ignoreUndef;
1558 }
1559
1560 void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
1561   if (ignoreUndef > 0)
1562     --ignoreUndef;
1563 }
1564
1565 //------------------------------------------------------------------------
1566 // marked content operators
1567 //------------------------------------------------------------------------
1568
1569 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
1570   if (printCommands) {
1571     printf("  marked content: %s ", args[0].getName());
1572     if (numArgs == 2)
1573       args[2].print(stdout);
1574     printf("\n");
1575   }
1576 }
1577
1578 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
1579 }
1580
1581 void Gfx::opMarkPoint(Object args[], int numArgs) {
1582   if (printCommands) {
1583     printf("  mark point: %s ", args[0].getName());
1584     if (numArgs == 2)
1585       args[2].print(stdout);
1586     printf("\n");
1587   }
1588 }