SmartPDF - lightweight pdf viewer app for rosapps
[reactos.git] / rosapps / smartpdf / fitz / mupdf / pdf_interpret.c
1 #include <fitz.h>
2 #include <mupdf.h>
3
4 fz_error *
5 pdf_newcsi(pdf_csi **csip, int maskonly)
6 {
7 fz_error *error;
8 pdf_csi *csi;
9 fz_node *node;
10
11 csi = *csip = fz_malloc(sizeof(pdf_csi));
12 if (!csi)
13 return fz_outofmem;
14
15 pdf_initgstate(&csi->gstate[0]);
16
17 csi->gtop = 0;
18 csi->top = 0;
19 csi->array = nil;
20 csi->xbalance = 0;
21
22 error = fz_newpathnode(&csi->path);
23 if (error) {
24 fz_free(csi);
25 return error;
26 }
27
28 error = fz_newtree(&csi->tree);
29 if (error) {
30 fz_dropnode((fz_node*)csi->path);
31 fz_free(csi);
32 return error;
33 }
34
35 error = fz_newovernode(&node);
36 csi->tree->root = node;
37 csi->gstate[0].head = node;
38
39 if (maskonly)
40 {
41 csi->gstate[0].fill.kind = PDF_MNONE;
42 csi->gstate[0].stroke.kind = PDF_MNONE;
43 }
44
45 csi->clip = 0;
46
47 csi->textclip = nil;
48 csi->textmode = 0;
49 csi->text = nil;
50 csi->tm = fz_identity();
51 csi->tlm = fz_identity();
52
53 return nil;
54 }
55
56 static void
57 clearstack(pdf_csi *csi)
58 {
59 int i;
60 for (i = 0; i < csi->top; i++)
61 fz_dropobj(csi->stack[i]);
62 csi->top = 0;
63 }
64
65 static fz_error *
66 gsave(pdf_csi *csi)
67 {
68 if (csi->gtop == 31)
69 return fz_throw("gstate overflow in content stream");
70
71 memcpy(&csi->gstate[csi->gtop + 1], &csi->gstate[csi->gtop], sizeof(pdf_gstate));
72
73 csi->gtop ++;
74
75 if (csi->gstate[csi->gtop].fill.cs)
76 fz_keepcolorspace(csi->gstate[csi->gtop].fill.cs);
77 if (csi->gstate[csi->gtop].stroke.cs)
78 fz_keepcolorspace(csi->gstate[csi->gtop].stroke.cs);
79
80 return nil;
81 }
82
83 static fz_error *
84 grestore(pdf_csi *csi)
85 {
86 if (csi->gtop == 0)
87 return fz_throw("gstate underflow in content stream");
88
89 if (csi->gstate[csi->gtop].fill.cs)
90 fz_dropcolorspace(csi->gstate[csi->gtop].fill.cs);
91 if (csi->gstate[csi->gtop].stroke.cs)
92 fz_dropcolorspace(csi->gstate[csi->gtop].stroke.cs);
93
94 csi->gtop --;
95
96 return nil;
97 }
98
99 void
100 pdf_dropcsi(pdf_csi *csi)
101 {
102 while (csi->gtop)
103 grestore(csi);
104
105 if (csi->gstate[csi->gtop].fill.cs)
106 fz_dropcolorspace(csi->gstate[csi->gtop].fill.cs);
107 if (csi->gstate[csi->gtop].stroke.cs)
108 fz_dropcolorspace(csi->gstate[csi->gtop].stroke.cs);
109
110 if (csi->path) fz_dropnode((fz_node*)csi->path);
111 if (csi->clip) fz_dropnode((fz_node*)csi->clip);
112 if (csi->textclip) fz_dropnode((fz_node*)csi->textclip);
113 if (csi->text) fz_dropnode((fz_node*)csi->text);
114 if (csi->array) fz_dropobj(csi->array);
115
116 clearstack(csi);
117
118 fz_free(csi);
119 }
120
121 /*
122 * Do some magic to call the xobject subroutine.
123 * Push gstate, set transform, clip, run, pop gstate.
124 */
125
126 static fz_error *
127 runxobject(pdf_csi *csi, pdf_xref *xref, pdf_xobject *xobj)
128 {
129 fz_error *error;
130 fz_node *transform;
131 fz_stream *file;
132
133 /* gsave */
134 error = gsave(csi);
135 if (error)
136 return error;
137
138 /* push transform */
139
140 error = fz_newtransformnode(&transform, xobj->matrix);
141 if (error)
142 return error;
143
144 error = pdf_addtransform(csi->gstate + csi->gtop, transform);
145 if (error)
146 {
147 fz_dropnode(transform);
148 return error;
149 }
150
151 /* run contents */
152
153 xobj->contents->rp = xobj->contents->bp;
154
155 error = fz_openrbuffer(&file, xobj->contents);
156 if (error)
157 return error;
158
159 error = pdf_runcsi(csi, xref, xobj->resources, file);
160
161 fz_dropstream(file);
162
163 if (error)
164 return error;
165
166 /* grestore */
167 error = grestore(csi);
168 if (error)
169 return error;
170
171 return nil;
172 }
173
174 /*
175 * Decode inline image and insert into page.
176 */
177
178 static fz_error *
179 runinlineimage(pdf_csi *csi, pdf_xref *xref, fz_obj *rdb, fz_stream *file, fz_obj *dict)
180 {
181 fz_error *error;
182 pdf_image *img = NULL;
183 char buf[256];
184 int token;
185 int len;
186
187 error = pdf_loadinlineimage(&img, xref, rdb, dict, file);
188 if (error)
189 return error;
190
191 token = pdf_lex(file, buf, sizeof buf, &len);
192 if (token != PDF_TKEYWORD || strcmp("EI", buf))
193 fz_warn("syntaxerror: corrupt inline image");
194
195 error = pdf_showimage(csi, img);
196 fz_dropimage((fz_image*)img);
197 return error;
198 }
199
200 /*
201 * Set gstate params from an ExtGState dictionary.
202 */
203
204 static fz_error *
205 runextgstate(pdf_gstate *gstate, pdf_xref *xref, fz_obj *extgstate)
206 {
207 int i, k;
208
209 for (i = 0; i < fz_dictlen(extgstate); i++)
210 {
211 fz_obj *key = fz_dictgetkey(extgstate, i);
212 fz_obj *val = fz_dictgetval(extgstate, i);
213 char *s = fz_toname(key);
214
215 if (!strcmp(s, "Font"))
216 {
217 if (fz_isarray(val) && fz_arraylen(val) == 2)
218 {
219 gstate->font = pdf_finditem(xref->store, PDF_KFONT, fz_arrayget(val, 0));
220 if (!gstate->font)
221 return fz_throw("syntaxerror: missing font resource");
222 gstate->size = fz_toreal(fz_arrayget(val, 1));
223 }
224 else
225 return fz_throw("syntaxerror in ExtGState/Font");
226 }
227
228 else if (!strcmp(s, "LW"))
229 gstate->linewidth = fz_toreal(val);
230 else if (!strcmp(s, "LC"))
231 gstate->linecap = fz_toint(val);
232 else if (!strcmp(s, "LJ"))
233 gstate->linejoin = fz_toint(val);
234 else if (!strcmp(s, "ML"))
235 gstate->miterlimit = fz_toreal(val);
236
237 else if (!strcmp(s, "D"))
238 {
239 if (fz_isarray(val) && fz_arraylen(val) == 2)
240 {
241 fz_obj *dashes = fz_arrayget(val, 0);
242 gstate->dashlen = MAX(fz_arraylen(dashes), 32);
243 for (k = 0; k < gstate->dashlen; k++)
244 gstate->dashlist[k] = fz_toreal(fz_arrayget(dashes, k));
245 gstate->dashphase = fz_toreal(fz_arrayget(val, 1));
246 }
247 else
248 return fz_throw("syntaxerror in ExtGState/D");
249 }
250 }
251
252 return nil;
253 }
254
255 /*
256 * The meat of the interpreter...
257 */
258
259 static fz_error *
260 runkeyword(pdf_csi *csi, pdf_xref *xref, fz_obj *rdb, char *buf)
261 {
262 pdf_gstate *gstate = csi->gstate + csi->gtop;
263 fz_error *error;
264 float a, b, c, d, e, f;
265 float x, y, w, h;
266 fz_matrix m;
267 float v[FZ_MAXCOLORS];
268 int what;
269 int i;
270
271 if (strlen(buf) > 1)
272 {
273 if (!strcmp(buf, "BX"))
274 {
275 if (csi->top != 0)
276 goto syntaxerror;
277 csi->xbalance ++;
278 }
279
280 else if (!strcmp(buf, "EX"))
281 {
282 if (csi->top != 0)
283 goto syntaxerror;
284 csi->xbalance --;
285 }
286
287 else if (!strcmp(buf, "MP"))
288 {
289 if (csi->top != 1)
290 goto syntaxerror;
291 }
292
293 else if (!strcmp(buf, "DP"))
294 {
295 if (csi->top != 2)
296 goto syntaxerror;
297 }
298
299 else if (!strcmp(buf, "BMC"))
300 {
301 if (csi->top != 1)
302 goto syntaxerror;
303 }
304
305 else if (!strcmp(buf, "BDC"))
306 {
307 if (csi->top != 2)
308 goto syntaxerror;
309 }
310
311 else if (!strcmp(buf, "EMC"))
312 {
313 if (csi->top != 0)
314 goto syntaxerror;
315 }
316
317 else if (!strcmp(buf, "cm"))
318 {
319 fz_matrix m;
320 fz_node *transform;
321
322 if (csi->top != 6)
323 goto syntaxerror;
324
325 m.a = fz_toreal(csi->stack[0]);
326 m.b = fz_toreal(csi->stack[1]);
327 m.c = fz_toreal(csi->stack[2]);
328 m.d = fz_toreal(csi->stack[3]);
329 m.e = fz_toreal(csi->stack[4]);
330 m.f = fz_toreal(csi->stack[5]);
331
332 error = fz_newtransformnode(&transform, m);
333 if (error) return error;
334 error = pdf_addtransform(gstate, transform);
335 if (error)
336 {
337 fz_dropnode(transform);
338 return error;
339 }
340 }
341
342 else if (!strcmp(buf, "ri"))
343 {
344 if (csi->top != 1)
345 goto syntaxerror;
346 }
347
348 else if (!strcmp(buf, "gs"))
349 {
350 fz_obj *dict;
351 fz_obj *obj;
352
353 if (csi->top != 1)
354 goto syntaxerror;
355
356 dict = fz_dictgets(rdb, "ExtGState");
357 if (!dict)
358 return fz_throw("syntaxerror: missing extgstate resource");
359
360 obj = fz_dictget(dict, csi->stack[0]);
361 if (!obj)
362 return fz_throw("syntaxerror: missing extgstate resource");
363
364 runextgstate(gstate, xref, obj);
365 }
366
367 else if (!strcmp(buf, "re"))
368 {
369 if (csi->top != 4)
370 goto syntaxerror;
371 x = fz_toreal(csi->stack[0]);
372 y = fz_toreal(csi->stack[1]);
373 w = fz_toreal(csi->stack[2]);
374 h = fz_toreal(csi->stack[3]);
375 error = fz_moveto(csi->path, x, y);
376 if (error) return error;
377 error = fz_lineto(csi->path, x + w, y);
378 if (error) return error;
379 error = fz_lineto(csi->path, x + w, y + h);
380 if (error) return error;
381 error = fz_lineto(csi->path, x, y + h);
382 if (error) return error;
383 error = fz_closepath(csi->path);
384 if (error) return error;
385 }
386
387 else if (!strcmp(buf, "f*"))
388 {
389 if (csi->top != 0)
390 goto syntaxerror;
391 error = pdf_showpath(csi, 0, 1, 0, 1);
392 if (error) return error;
393 }
394
395 else if (!strcmp(buf, "B*"))
396 {
397 if (csi->top != 0)
398 goto syntaxerror;
399 error = pdf_showpath(csi, 0, 1, 1, 1);
400 if (error) return error;
401 }
402
403 else if (!strcmp(buf, "b*"))
404 {
405 if (csi->top != 0)
406 goto syntaxerror;
407 error = pdf_showpath(csi, 1, 1, 1, 1);
408 if (error) return error;
409 }
410
411 else if (!strcmp(buf, "W*"))
412 {
413 if (csi->top != 0)
414 goto syntaxerror;
415 csi->clip = 1;
416 }
417
418 else if (!strcmp(buf, "cs"))
419 {
420 what = PDF_MFILL;
421 goto Lsetcolorspace;
422 }
423
424 else if (!strcmp(buf, "CS"))
425 {
426 fz_colorspace *cs;
427 fz_obj *obj;
428
429 what = PDF_MSTROKE;
430
431 Lsetcolorspace:
432 if (csi->top != 1)
433 goto syntaxerror;
434
435 obj = csi->stack[0];
436
437 if (!strcmp(fz_toname(obj), "Pattern"))
438 {
439 error = pdf_setpattern(csi, what, nil, nil);
440 if (error) return error;
441 }
442
443 else
444 {
445 if (!strcmp(fz_toname(obj), "DeviceGray"))
446 cs = pdf_devicegray;
447 else if (!strcmp(fz_toname(obj), "DeviceRGB"))
448 cs = pdf_devicergb;
449 else if (!strcmp(fz_toname(obj), "DeviceCMYK"))
450 cs = pdf_devicecmyk;
451 else
452 {
453 fz_obj *dict = fz_dictgets(rdb, "ColorSpace");
454 if (!dict)
455 return fz_throw("syntaxerror: missing colorspace resource");
456 obj = fz_dictget(dict, obj);
457 if (!obj)
458 return fz_throw("syntaxerror: missing colorspace resource");
459
460 cs = pdf_finditem(xref->store, PDF_KCOLORSPACE, obj);
461 if (!cs)
462 return fz_throw("syntaxerror: missing colorspace resource");
463 }
464
465 error = pdf_setcolorspace(csi, what, cs);
466 if (error) return error;
467 }
468 }
469
470 else if (!strcmp(buf, "sc") || !strcmp(buf, "scn"))
471 {
472 what = PDF_MFILL;
473 goto Lsetcolor;
474 }
475
476 else if (!strcmp(buf, "SC") || !strcmp(buf, "SCN"))
477 {
478 pdf_material *mat;
479 pdf_pattern *pat;
480 fz_shade *shd;
481 fz_obj *dict;
482 fz_obj *obj;
483
484 what = PDF_MSTROKE;
485
486 Lsetcolor:
487 mat = what == PDF_MSTROKE ? &gstate->stroke : &gstate->fill;
488
489 if (fz_isname(csi->stack[csi->top - 1]))
490 mat->kind = PDF_MPATTERN;
491
492 switch (mat->kind)
493 {
494 case PDF_MNONE:
495 return fz_throw("syntaxerror: cannot set color in mask objects");
496
497 case PDF_MINDEXED:
498 if (csi->top != 1)
499 goto syntaxerror;
500 v[0] = fz_toreal(csi->stack[0]);
501 error = pdf_setcolor(csi, what, v);
502 if (error) return error;
503 break;
504
505 case PDF_MCOLOR:
506 case PDF_MLAB:
507 if (csi->top != mat->cs->n)
508 goto syntaxerror;
509 for (i = 0; i < csi->top; i++)
510 v[i] = fz_toreal(csi->stack[i]);
511 error = pdf_setcolor(csi, what, v);
512 if (error) return error;
513 break;
514
515 case PDF_MPATTERN:
516 for (i = 0; i < csi->top - 1; i++)
517 v[i] = fz_toreal(csi->stack[i]);
518
519 dict = fz_dictgets(rdb, "Pattern");
520 if (!dict)
521 return fz_throw("syntaxerror: missing pattern resource");
522
523 obj = fz_dictget(dict, csi->stack[csi->top - 1]);
524 if (!obj)
525 return fz_throw("syntaxerror: missing pattern resource");
526
527 pat = pdf_finditem(xref->store, PDF_KPATTERN, obj);
528 if (pat)
529 {
530 error = pdf_setpattern(csi, what, pat, csi->top == 1 ? nil : v);
531 if (error) return error;
532 }
533
534 shd = pdf_finditem(xref->store, PDF_KSHADE, obj);
535 if (shd)
536 {
537 error = pdf_setshade(csi, what, shd);
538 if (error) return error;
539 }
540
541 if (!pat && !shd)
542 return fz_throw("syntaxerror: missing pattern resource");
543
544 break;
545
546 case PDF_MSHADE:
547 return fz_throw("syntaxerror: cannot set color in shade objects");
548 }
549 }
550
551 else if (!strcmp(buf, "rg"))
552 {
553 if (csi->top != 3)
554 goto syntaxerror;
555
556 v[0] = fz_toreal(csi->stack[0]);
557 v[1] = fz_toreal(csi->stack[1]);
558 v[2] = fz_toreal(csi->stack[2]);
559
560 error = pdf_setcolorspace(csi, PDF_MFILL, pdf_devicergb);
561 if (error) return error;
562 error = pdf_setcolor(csi, PDF_MFILL, v);
563 if (error) return error;
564 }
565
566 else if (!strcmp(buf, "RG"))
567 {
568 if (csi->top != 3)
569 goto syntaxerror;
570
571 v[0] = fz_toreal(csi->stack[0]);
572 v[1] = fz_toreal(csi->stack[1]);
573 v[2] = fz_toreal(csi->stack[2]);
574
575 error = pdf_setcolorspace(csi, PDF_MSTROKE, pdf_devicergb);
576 if (error) return error;
577 error = pdf_setcolor(csi, PDF_MSTROKE, v);
578 if (error) return error;
579 }
580
581 else if (!strcmp(buf, "BT"))
582 {
583 if (csi->top != 0)
584 goto syntaxerror;
585 csi->tm = fz_identity();
586 csi->tlm = fz_identity();
587 }
588
589 else if (!strcmp(buf, "ET"))
590 {
591 if (csi->top != 0)
592 goto syntaxerror;
593
594 error = pdf_flushtext(csi);
595 if (error)
596 return error;
597
598 if (csi->textclip)
599 {
600 error = pdf_addclipmask(gstate, csi->textclip);
601 if (error) return error;
602 csi->textclip = nil;
603 }
604 }
605
606 else if (!strcmp(buf, "Tc"))
607 {
608 if (csi->top != 1)
609 goto syntaxerror;
610 gstate->charspace = fz_toreal(csi->stack[0]);
611 }
612
613 else if (!strcmp(buf, "Tw"))
614 {
615 if (csi->top != 1)
616 goto syntaxerror;
617 gstate->wordspace = fz_toreal(csi->stack[0]);
618 }
619
620 else if (!strcmp(buf, "Tz"))
621 {
622 if (csi->top != 1)
623 goto syntaxerror;
624
625 error = pdf_flushtext(csi);
626 if (error) return error;
627
628 gstate->scale = fz_toreal(csi->stack[0]) / 100.0;
629 }
630
631 else if (!strcmp(buf, "TL"))
632 {
633 if (csi->top != 1)
634 goto syntaxerror;
635 gstate->leading = fz_toreal(csi->stack[0]);
636 }
637
638 else if (!strcmp(buf, "Tf"))
639 {
640 fz_obj *dict;
641 fz_obj *obj;
642
643 if (csi->top != 2)
644 goto syntaxerror;
645
646 dict = fz_dictgets(rdb, "Font");
647 if (!dict)
648 return fz_throw("syntaxerror: missing font resource");
649
650 obj = fz_dictget(dict, csi->stack[0]);
651 if (!obj)
652 return fz_throw("syntaxerror: missing font resource");
653
654 gstate->font = pdf_finditem(xref->store, PDF_KFONT, obj);
655 if (!gstate->font)
656 return fz_throw("syntaxerror: missing font resource");
657
658 gstate->size = fz_toreal(csi->stack[1]);
659 }
660
661 else if (!strcmp(buf, "Tr"))
662 {
663 if (csi->top != 1)
664 goto syntaxerror;
665 gstate->render = fz_toint(csi->stack[0]);
666 }
667
668 else if (!strcmp(buf, "Ts"))
669 {
670 if (csi->top != 1)
671 goto syntaxerror;
672 gstate->rise = fz_toreal(csi->stack[0]);
673 }
674
675 else if (!strcmp(buf, "Td"))
676 {
677 if (csi->top != 2)
678 goto syntaxerror;
679 m = fz_translate(fz_toreal(csi->stack[0]), fz_toreal(csi->stack[1]));
680 csi->tlm = fz_concat(m, csi->tlm);
681 csi->tm = csi->tlm;
682 }
683
684 else if (!strcmp(buf, "TD"))
685 {
686 if (csi->top != 2)
687 goto syntaxerror;
688 gstate->leading = -fz_toreal(csi->stack[1]);
689 m = fz_translate(fz_toreal(csi->stack[0]), fz_toreal(csi->stack[1]));
690 csi->tlm = fz_concat(m, csi->tlm);
691 csi->tm = csi->tlm;
692 }
693
694 else if (!strcmp(buf, "Tm"))
695 {
696 if (csi->top != 6)
697 goto syntaxerror;
698
699 error = pdf_flushtext(csi);
700 if (error) return error;
701
702 csi->tm.a = fz_toreal(csi->stack[0]);
703 csi->tm.b = fz_toreal(csi->stack[1]);
704 csi->tm.c = fz_toreal(csi->stack[2]);
705 csi->tm.d = fz_toreal(csi->stack[3]);
706 csi->tm.e = fz_toreal(csi->stack[4]);
707 csi->tm.f = fz_toreal(csi->stack[5]);
708 csi->tlm = csi->tm;
709 }
710
711 else if (!strcmp(buf, "T*"))
712 {
713 if (csi->top != 0)
714 goto syntaxerror;
715 m = fz_translate(0, -gstate->leading);
716 csi->tlm = fz_concat(m, csi->tlm);
717 csi->tm = csi->tlm;
718 }
719
720 else if (!strcmp(buf, "Tj"))
721 {
722 if (csi->top != 1)
723 goto syntaxerror;
724 error = pdf_showtext(csi, csi->stack[0]);
725 if (error) return error;
726 }
727
728 else if (!strcmp(buf, "TJ"))
729 {
730 if (csi->top != 1)
731 goto syntaxerror;
732 error = pdf_showtext(csi, csi->stack[0]);
733 if (error) return error;
734 }
735
736 else if (!strcmp(buf, "Do"))
737 {
738 fz_obj *dict;
739 fz_obj *obj;
740 pdf_image *img;
741 pdf_xobject *xobj;
742
743 if (csi->top != 1)
744 goto syntaxerror;
745
746 dict = fz_dictgets(rdb, "XObject");
747 if (!dict)
748 {
749 fz_debugobj(rdb);
750 return fz_throw("syntaxerror: missing xobject resource");
751 }
752
753 obj = fz_dictget(dict, csi->stack[0]);
754 if (!obj)
755 return fz_throw("syntaxerror: missing xobject resource");
756
757 img = pdf_finditem(xref->store, PDF_KIMAGE, obj);
758 xobj = pdf_finditem(xref->store, PDF_KXOBJECT, obj);
759
760 if (!img && !xobj)
761 return fz_throw("syntaxerror: missing xobject resource");
762
763 if (img)
764 {
765 error = pdf_showimage(csi, img);
766 if (error)
767 return error;
768 }
769
770 if (xobj)
771 {
772 clearstack(csi);
773 error = runxobject(csi, xref, xobj);
774 if (error)
775 return error;
776 }
777 }
778
779 else if (!strcmp(buf, "sh"))
780 {
781 fz_obj *dict;
782 fz_obj *obj;
783 fz_shade *shd;
784
785 if (csi->top != 1)
786 goto syntaxerror;
787
788 dict = fz_dictgets(rdb, "Shading");
789 if (!dict)
790 return fz_throw("syntaxerror: missing shading resource");
791
792 obj = fz_dictget(dict, csi->stack[csi->top - 1]);
793 if (!obj)
794 return fz_throw("syntaxerror: missing shading resource");
795
796 shd = pdf_finditem(xref->store, PDF_KSHADE, obj);
797 if (!shd)
798 return fz_throw("syntaxerror: missing shading resource");
799
800 error = pdf_addshade(gstate, shd);
801 if (error) return error;
802 }
803
804 else if (!strcmp(buf, "d0"))
805 {
806 fz_warn("unimplemented: d0 charprocs");
807 }
808
809 else if (!strcmp(buf, "d1"))
810 {
811 }
812
813 else
814 if (!csi->xbalance) goto syntaxerror;
815 }
816
817 else switch (buf[0])
818 {
819
820 case 'q':
821 if (csi->top != 0)
822 goto syntaxerror;
823 error = gsave(csi);
824 if (error)
825 return error;
826 break;
827
828 case 'Q':
829 if (csi->top != 0)
830 goto syntaxerror;
831 error = grestore(csi);
832 if (error)
833 return error;
834 break;
835
836 case 'w':
837 if (csi->top != 1)
838 goto syntaxerror;
839 gstate->linewidth = fz_toreal(csi->stack[0]);
840 break;
841
842 case 'J':
843 if (csi->top != 1)
844 goto syntaxerror;
845 gstate->linecap = fz_toint(csi->stack[0]);
846 break;
847
848 case 'j':
849 if (csi->top != 1)
850 goto syntaxerror;
851 gstate->linejoin = fz_toint(csi->stack[0]);
852 break;
853
854 case 'M':
855 if (csi->top != 1)
856 goto syntaxerror;
857 gstate->miterlimit = fz_toreal(csi->stack[0]);
858 break;
859
860 case 'd':
861 if (csi->top != 2)
862 goto syntaxerror;
863 {
864 int i;
865 fz_obj *array = csi->stack[0];
866 gstate->dashlen = fz_arraylen(array);
867 if (gstate->dashlen > 32)
868 return fz_throw("rangecheck: too large dash pattern");
869 for (i = 0; i < gstate->dashlen; i++)
870 gstate->dashlist[i] = fz_toreal(fz_arrayget(array, i));
871 gstate->dashphase = fz_toreal(csi->stack[1]);
872 }
873 break;
874
875 case 'i':
876 if (csi->top != 1)
877 goto syntaxerror;
878 /* flatness */
879 break;
880
881 case 'm':
882 if (csi->top != 2)
883 goto syntaxerror;
884 a = fz_toreal(csi->stack[0]);
885 b = fz_toreal(csi->stack[1]);
886 return fz_moveto(csi->path, a, b);
887
888 case 'l':
889 if (csi->top != 2)
890 goto syntaxerror;
891 a = fz_toreal(csi->stack[0]);
892 b = fz_toreal(csi->stack[1]);
893 return fz_lineto(csi->path, a, b);
894
895 case 'c':
896 if (csi->top != 6)
897 goto syntaxerror;
898 a = fz_toreal(csi->stack[0]);
899 b = fz_toreal(csi->stack[1]);
900 c = fz_toreal(csi->stack[2]);
901 d = fz_toreal(csi->stack[3]);
902 e = fz_toreal(csi->stack[4]);
903 f = fz_toreal(csi->stack[5]);
904 return fz_curveto(csi->path, a, b, c, d, e, f);
905
906 case 'v':
907 if (csi->top != 4)
908 goto syntaxerror;
909 a = fz_toreal(csi->stack[0]);
910 b = fz_toreal(csi->stack[1]);
911 c = fz_toreal(csi->stack[2]);
912 d = fz_toreal(csi->stack[3]);
913 return fz_curvetov(csi->path, a, b, c, d);
914
915 case 'y':
916 if (csi->top != 4)
917 goto syntaxerror;
918 a = fz_toreal(csi->stack[0]);
919 b = fz_toreal(csi->stack[1]);
920 c = fz_toreal(csi->stack[2]);
921 d = fz_toreal(csi->stack[3]);
922 return fz_curvetoy(csi->path, a, b, c, d);
923
924 case 'h':
925 if (csi->top != 0)
926 goto syntaxerror;
927 return fz_closepath(csi->path);
928
929 case 'S':
930 if (csi->top != 0)
931 goto syntaxerror;
932 error = pdf_showpath(csi, 0, 0, 1, 0);
933 if (error) return error;
934 break;
935
936 case 's':
937 if (csi->top != 0)
938 goto syntaxerror;
939 error = pdf_showpath(csi, 1, 0, 1, 0);
940 if (error) return error;
941 break;
942
943 case 'F':
944 case 'f':
945 if (csi->top != 0)
946 goto syntaxerror;
947 error = pdf_showpath(csi, 0, 1, 0, 0);
948 if (error) return error;
949 break;
950
951 case 'B':
952 if (csi->top != 0)
953 goto syntaxerror;
954 error = pdf_showpath(csi, 0, 1, 1, 0);
955 if (error) return error;
956 break;
957
958 case 'b':
959 if (csi->top != 0)
960 goto syntaxerror;
961 error = pdf_showpath(csi, 1, 1, 1, 0);
962 if (error) return error;
963 break;
964
965 case 'n':
966 if (csi->top != 0)
967 goto syntaxerror;
968 error = pdf_showpath(csi, 0, 0, 0, 0);
969 if (error) return error;
970 break;
971
972 case 'W':
973 if (csi->top != 0)
974 goto syntaxerror;
975 csi->clip = 1;
976 break;
977
978 case 'g':
979 if (csi->top != 1)
980 goto syntaxerror;
981
982 v[0] = fz_toreal(csi->stack[0]);
983 error = pdf_setcolorspace(csi, PDF_MFILL, pdf_devicegray);
984 if (error) return error;
985 error = pdf_setcolor(csi, PDF_MFILL, v);
986 if (error) return error;
987 break;
988
989 case 'G':
990 if (csi->top != 1)
991 goto syntaxerror;
992
993 v[0] = fz_toreal(csi->stack[0]);
994 error = pdf_setcolorspace(csi, PDF_MSTROKE, pdf_devicegray);
995 if (error) return error;
996 error = pdf_setcolor(csi, PDF_MSTROKE, v);
997 if (error) return error;
998 break;
999
1000 case 'k':
1001 if (csi->top != 4)
1002 goto syntaxerror;
1003
1004 v[0] = fz_toreal(csi->stack[0]);
1005 v[1] = fz_toreal(csi->stack[1]);
1006 v[2] = fz_toreal(csi->stack[2]);
1007 v[3] = fz_toreal(csi->stack[3]);
1008
1009 error = pdf_setcolorspace(csi, PDF_MFILL, pdf_devicecmyk);
1010 if (error) return error;
1011 error = pdf_setcolor(csi, PDF_MFILL, v);
1012 if (error) return error;
1013 break;
1014
1015 case 'K':
1016 if (csi->top != 4)
1017 goto syntaxerror;
1018
1019 v[0] = fz_toreal(csi->stack[0]);
1020 v[1] = fz_toreal(csi->stack[1]);
1021 v[2] = fz_toreal(csi->stack[2]);
1022 v[3] = fz_toreal(csi->stack[3]);
1023
1024 error = pdf_setcolorspace(csi, PDF_MSTROKE, pdf_devicecmyk);
1025 if (error) return error;
1026 error = pdf_setcolor(csi, PDF_MSTROKE, v);
1027 if (error) return error;
1028 break;
1029
1030 case '\'':
1031 if (csi->top != 1)
1032 goto syntaxerror;
1033
1034 m = fz_translate(0, -gstate->leading);
1035 csi->tlm = fz_concat(m, csi->tlm);
1036 csi->tm = csi->tlm;
1037
1038 error = pdf_showtext(csi, csi->stack[0]);
1039 if (error) return error;
1040 break;
1041
1042 case '"':
1043 if (csi->top != 3)
1044 goto syntaxerror;
1045
1046 gstate->wordspace = fz_toreal(csi->stack[0]);
1047 gstate->charspace = fz_toreal(csi->stack[1]);
1048
1049 m = fz_translate(0, -gstate->leading);
1050 csi->tlm = fz_concat(m, csi->tlm);
1051 csi->tm = csi->tlm;
1052
1053 error = pdf_showtext(csi, csi->stack[2]);
1054 if (error) return error;
1055 break;
1056
1057 default:
1058 if (!csi->xbalance) goto syntaxerror;
1059 }
1060
1061 return nil;
1062
1063 syntaxerror:
1064 return fz_throw("syntaxerror in content stream: '%s'", buf);
1065 }
1066
1067 fz_error *
1068 pdf_runcsi(pdf_csi *csi, pdf_xref *xref, fz_obj *rdb, fz_stream *file)
1069 {
1070 fz_error *error;
1071 char buf[65536];
1072 int token, len;
1073 fz_obj *obj;
1074
1075 while (1)
1076 {
1077 if (csi->top == 31)
1078 return fz_throw("stack overflow in content stream");
1079
1080 token = pdf_lex(file, buf, sizeof buf, &len);
1081
1082 if (csi->array)
1083 {
1084 if (token == PDF_TCARRAY)
1085 {
1086 csi->stack[csi->top] = csi->array;
1087 csi->array = nil;
1088 csi->top ++;
1089 }
1090 else if (token == PDF_TINT || token == PDF_TREAL)
1091 {
1092 error = fz_newreal(&obj, atof(buf));
1093 if (error) return error;
1094 error = fz_arraypush(csi->array, obj);
1095 fz_dropobj(obj);
1096 if (error) return error;
1097 }
1098 else if (token == PDF_TSTRING)
1099 {
1100 error = fz_newstring(&obj, buf, len);
1101 if (error) return error;
1102 error = fz_arraypush(csi->array, obj);
1103 fz_dropobj(obj);
1104 if (error) return error;
1105 }
1106 else if (token == PDF_TEOF)
1107 {
1108 return nil;
1109 }
1110 else
1111 {
1112 clearstack(csi);
1113 return fz_throw("syntaxerror in content stream");
1114 }
1115 }
1116
1117 else switch (token)
1118 {
1119 case PDF_TEOF:
1120 return nil;
1121
1122 /* optimize text-object array parsing */
1123 case PDF_TOARRAY:
1124 error = fz_newarray(&csi->array, 8);
1125 if (error) return error;
1126 break;
1127
1128 case PDF_TODICT:
1129 error = pdf_parsedict(&csi->stack[csi->top], file, buf, sizeof buf);
1130 if (error) return error;
1131 csi->top ++;
1132 break;
1133
1134 case PDF_TNAME:
1135 error = fz_newname(&csi->stack[csi->top], buf);
1136 if (error) return error;
1137 csi->top ++;
1138 break;
1139
1140 case PDF_TINT:
1141 error = fz_newint(&csi->stack[csi->top], atoi(buf));
1142 if (error) return error;
1143 csi->top ++;
1144 break;
1145
1146 case PDF_TREAL:
1147 error = fz_newreal(&csi->stack[csi->top], atof(buf));
1148 if (error) return error;
1149 csi->top ++;
1150 break;
1151
1152 case PDF_TSTRING:
1153 error = fz_newstring(&csi->stack[csi->top], buf, len);
1154 if (error) return error;
1155 csi->top ++;
1156 break;
1157
1158 case PDF_TTRUE:
1159 error = fz_newbool(&csi->stack[csi->top], 1);
1160 if (error) return error;
1161 csi->top ++;
1162 break;
1163
1164 case PDF_TFALSE:
1165 error = fz_newbool(&csi->stack[csi->top], 0);
1166 if (error) return error;
1167 csi->top ++;
1168 break;
1169
1170 case PDF_TNULL:
1171 error = fz_newnull(&csi->stack[csi->top]);
1172 if (error) return error;
1173 csi->top ++;
1174 break;
1175
1176 case PDF_TKEYWORD:
1177 if (!strcmp(buf, "BI"))
1178 {
1179 fz_obj *obj;
1180
1181 error = pdf_parsedict(&obj, file, buf, sizeof buf);
1182 if (error)
1183 return error;
1184
1185 /* read whitespace after ID keyword */
1186 fz_readbyte(file);
1187
1188 error = runinlineimage(csi, xref, rdb, file, obj);
1189 fz_dropobj(obj);
1190 if (error)
1191 return error;
1192 }
1193 else
1194 {
1195 error = runkeyword(csi, xref, rdb, buf);
1196 if (error) return error;
1197 clearstack(csi);
1198 }
1199 break;
1200
1201 default:
1202 clearstack(csi);
1203 return fz_throw("syntaxerror in content stream");
1204 }
1205 }
1206 }
1207