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