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