1#include "mupdf/fitz.h"
2#include "mupdf/pdf.h"
3
4#include <string.h>
5#include <math.h>
6
7/* Maximum number of errors before aborting */
8#define MAX_SYNTAX_ERRORS 100
9
10void *
11pdf_new_processor(fz_context *ctx, int size)
12{
13 return Memento_label(fz_calloc(ctx, 1, size), "pdf_processor");
14}
15
16void
17pdf_close_processor(fz_context *ctx, pdf_processor *proc)
18{
19 if (proc && proc->close_processor)
20 {
21 proc->close_processor(ctx, proc);
22 proc->close_processor = NULL;
23 }
24}
25
26void
27pdf_drop_processor(fz_context *ctx, pdf_processor *proc)
28{
29 if (proc)
30 {
31 if (proc->close_processor)
32 fz_warn(ctx, "dropping unclosed PDF processor");
33 if (proc->drop_processor)
34 proc->drop_processor(ctx, proc);
35 }
36 fz_free(ctx, proc);
37}
38
39static void
40pdf_init_csi(fz_context *ctx, pdf_csi *csi, pdf_document *doc, pdf_obj *rdb, pdf_lexbuf *buf, fz_cookie *cookie)
41{
42 memset(csi, 0, sizeof *csi);
43 csi->doc = doc;
44 csi->rdb = rdb;
45 csi->buf = buf;
46 csi->cookie = cookie;
47}
48
49static void
50pdf_clear_stack(fz_context *ctx, pdf_csi *csi)
51{
52 int i;
53
54 pdf_drop_obj(ctx, csi->obj);
55 csi->obj = NULL;
56
57 csi->name[0] = 0;
58 csi->string_len = 0;
59 for (i = 0; i < csi->top; i++)
60 csi->stack[i] = 0;
61
62 csi->top = 0;
63}
64
65static pdf_font_desc *
66pdf_try_load_font(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, pdf_obj *font, fz_cookie *cookie)
67{
68 pdf_font_desc *desc = NULL;
69 fz_try(ctx)
70 desc = pdf_load_font(ctx, doc, rdb, font);
71 fz_catch(ctx)
72 {
73 if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
74 if (cookie)
75 cookie->incomplete++;
76 }
77 if (desc == NULL)
78 desc = pdf_load_hail_mary_font(ctx, doc);
79 return desc;
80}
81
82static fz_image *
83parse_inline_image(fz_context *ctx, pdf_csi *csi, fz_stream *stm, char *csname, int cslen)
84{
85 pdf_document *doc = csi->doc;
86 pdf_obj *rdb = csi->rdb;
87 pdf_obj *obj = NULL;
88 pdf_obj *cs;
89 fz_image *img = NULL;
90 int ch, found;
91
92 fz_var(obj);
93 fz_var(img);
94
95 fz_try(ctx)
96 {
97 obj = pdf_parse_dict(ctx, doc, stm, &doc->lexbuf.base);
98
99 if (csname)
100 {
101 cs = pdf_dict_get(ctx, obj, PDF_NAME(CS));
102 if (!pdf_is_indirect(ctx, cs) && pdf_is_name(ctx, cs))
103 fz_strlcpy(csname, pdf_to_name(ctx, cs), cslen);
104 else
105 csname[0] = 0;
106 }
107
108 /* read whitespace after ID keyword */
109 ch = fz_read_byte(ctx, stm);
110 if (ch == '\r')
111 if (fz_peek_byte(ctx, stm) == '\n')
112 fz_read_byte(ctx, stm);
113
114 img = pdf_load_inline_image(ctx, doc, rdb, obj, stm);
115
116 /* find EI */
117 found = 0;
118 ch = fz_read_byte(ctx, stm);
119 do
120 {
121 while (ch != 'E' && ch != EOF)
122 ch = fz_read_byte(ctx, stm);
123 if (ch == 'E')
124 {
125 ch = fz_read_byte(ctx, stm);
126 if (ch == 'I')
127 {
128 ch = fz_peek_byte(ctx, stm);
129 if (ch == ' ' || ch <= 32 || ch == EOF || ch == '<' || ch == '/')
130 {
131 found = 1;
132 break;
133 }
134 }
135 }
136 } while (ch != EOF);
137 if (!found)
138 fz_throw(ctx, FZ_ERROR_SYNTAX, "syntax error after inline image");
139 }
140 fz_always(ctx)
141 {
142 pdf_drop_obj(ctx, obj);
143 }
144 fz_catch(ctx)
145 {
146 fz_drop_image(ctx, img);
147 fz_rethrow(ctx);
148 }
149
150 return img;
151}
152
153static void
154pdf_process_extgstate(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, pdf_obj *dict)
155{
156 pdf_obj *obj;
157
158 obj = pdf_dict_get(ctx, dict, PDF_NAME(LW));
159 if (pdf_is_number(ctx, obj) && proc->op_w)
160 proc->op_w(ctx, proc, pdf_to_real(ctx, obj));
161
162 obj = pdf_dict_get(ctx, dict, PDF_NAME(LC));
163 if (pdf_is_int(ctx, obj) && proc->op_J)
164 proc->op_J(ctx, proc, fz_clampi(pdf_to_int(ctx, obj), 0, 2));
165
166 obj = pdf_dict_get(ctx, dict, PDF_NAME(LJ));
167 if (pdf_is_int(ctx, obj) && proc->op_j)
168 proc->op_j(ctx, proc, fz_clampi(pdf_to_int(ctx, obj), 0, 2));
169
170 obj = pdf_dict_get(ctx, dict, PDF_NAME(ML));
171 if (pdf_is_number(ctx, obj) && proc->op_M)
172 proc->op_M(ctx, proc, pdf_to_real(ctx, obj));
173
174 obj = pdf_dict_get(ctx, dict, PDF_NAME(D));
175 if (pdf_is_array(ctx, obj) && proc->op_d)
176 {
177 pdf_obj *dash_array = pdf_array_get(ctx, obj, 0);
178 pdf_obj *dash_phase = pdf_array_get(ctx, obj, 1);
179 proc->op_d(ctx, proc, dash_array, pdf_to_real(ctx, dash_phase));
180 }
181
182 obj = pdf_dict_get(ctx, dict, PDF_NAME(RI));
183 if (pdf_is_name(ctx, obj) && proc->op_ri)
184 proc->op_ri(ctx, proc, pdf_to_name(ctx, obj));
185
186 obj = pdf_dict_get(ctx, dict, PDF_NAME(FL));
187 if (pdf_is_number(ctx, obj) && proc->op_i)
188 proc->op_i(ctx, proc, pdf_to_real(ctx, obj));
189
190 obj = pdf_dict_get(ctx, dict, PDF_NAME(Font));
191 if (pdf_is_array(ctx, obj) && proc->op_Tf)
192 {
193 pdf_obj *font_ref = pdf_array_get(ctx, obj, 0);
194 pdf_obj *font_size = pdf_array_get(ctx, obj, 1);
195 pdf_font_desc *font;
196 if (pdf_is_dict(ctx, font_ref))
197 font = pdf_try_load_font(ctx, csi->doc, csi->rdb, font_ref, csi->cookie);
198 else
199 font = pdf_load_hail_mary_font(ctx, csi->doc);
200 fz_try(ctx)
201 proc->op_Tf(ctx, proc, "ExtGState", font, pdf_to_real(ctx, font_size));
202 fz_always(ctx)
203 pdf_drop_font(ctx, font);
204 fz_catch(ctx)
205 fz_rethrow(ctx);
206 }
207
208 /* overprint and color management */
209
210 obj = pdf_dict_get(ctx, dict, PDF_NAME(OP));
211 if (pdf_is_bool(ctx, obj) && proc->op_gs_OP)
212 proc->op_gs_OP(ctx, proc, pdf_to_bool(ctx, obj));
213
214 obj = pdf_dict_get(ctx, dict, PDF_NAME(op));
215 if (pdf_is_bool(ctx, obj) && proc->op_gs_op)
216 proc->op_gs_op(ctx, proc, pdf_to_bool(ctx, obj));
217
218 obj = pdf_dict_get(ctx, dict, PDF_NAME(OPM));
219 if (pdf_is_int(ctx, obj) && proc->op_gs_OPM)
220 proc->op_gs_OPM(ctx, proc, pdf_to_int(ctx, obj));
221
222 obj = pdf_dict_get(ctx, dict, PDF_NAME(UseBlackPtComp));
223 if (pdf_is_name(ctx, obj) && proc->op_gs_UseBlackPtComp)
224 proc->op_gs_UseBlackPtComp(ctx, proc, obj);
225
226 /* transfer functions */
227
228 obj = pdf_dict_get(ctx, dict, PDF_NAME(TR2));
229 if (pdf_is_name(ctx, obj))
230 if (!pdf_name_eq(ctx, obj, PDF_NAME(Identity)) && !pdf_name_eq(ctx, obj, PDF_NAME(Default)))
231 fz_warn(ctx, "ignoring transfer function");
232 if (!obj) /* TR is ignored in the presence of TR2 */
233 {
234 pdf_obj *tr = pdf_dict_get(ctx, dict, PDF_NAME(TR));
235 if (pdf_is_name(ctx, tr))
236 if (!pdf_name_eq(ctx, tr, PDF_NAME(Identity)))
237 fz_warn(ctx, "ignoring transfer function");
238 }
239
240 /* transparency state */
241
242 obj = pdf_dict_get(ctx, dict, PDF_NAME(CA));
243 if (pdf_is_number(ctx, obj) && proc->op_gs_CA)
244 proc->op_gs_CA(ctx, proc, pdf_to_real(ctx, obj));
245
246 obj = pdf_dict_get(ctx, dict, PDF_NAME(ca));
247 if (pdf_is_number(ctx, obj) && proc->op_gs_ca)
248 proc->op_gs_ca(ctx, proc, pdf_to_real(ctx, obj));
249
250 obj = pdf_dict_get(ctx, dict, PDF_NAME(BM));
251 if (pdf_is_array(ctx, obj))
252 obj = pdf_array_get(ctx, obj, 0);
253 if (pdf_is_name(ctx, obj) && proc->op_gs_BM)
254 proc->op_gs_BM(ctx, proc, pdf_to_name(ctx, obj));
255
256 obj = pdf_dict_get(ctx, dict, PDF_NAME(SMask));
257 if (proc->op_gs_SMask)
258 {
259 if (pdf_is_dict(ctx, obj))
260 {
261 pdf_obj *xobj, *s, *bc, *tr;
262 float softmask_bc[FZ_MAX_COLORS];
263 fz_colorspace *colorspace;
264 int colorspace_n = 1;
265 int k, luminosity;
266
267 xobj = pdf_dict_get(ctx, obj, PDF_NAME(G));
268
269 colorspace = pdf_xobject_colorspace(ctx, xobj);
270 if (colorspace)
271 colorspace_n = fz_colorspace_n(ctx, colorspace);
272
273 /* Default background color is black. */
274 for (k = 0; k < colorspace_n; k++)
275 softmask_bc[k] = 0;
276 /* Which in CMYK means not all zeros! This should really be
277 * a test for subtractive color spaces, but this will have
278 * to do for now. */
279 if (fz_colorspace_is_cmyk(ctx, colorspace))
280 softmask_bc[3] = 1.0f;
281 fz_drop_colorspace(ctx, colorspace);
282
283 bc = pdf_dict_get(ctx, obj, PDF_NAME(BC));
284 if (pdf_is_array(ctx, bc))
285 {
286 for (k = 0; k < colorspace_n; k++)
287 softmask_bc[k] = pdf_array_get_real(ctx, bc, k);
288 }
289
290 s = pdf_dict_get(ctx, obj, PDF_NAME(S));
291 if (pdf_name_eq(ctx, s, PDF_NAME(Luminosity)))
292 luminosity = 1;
293 else
294 luminosity = 0;
295
296 tr = pdf_dict_get(ctx, obj, PDF_NAME(TR));
297 if (tr && !pdf_name_eq(ctx, tr, PDF_NAME(Identity)))
298 fz_warn(ctx, "ignoring transfer function");
299
300 proc->op_gs_SMask(ctx, proc, xobj, csi->rdb, softmask_bc, luminosity);
301 }
302 else if (pdf_is_name(ctx, obj) && pdf_name_eq(ctx, obj, PDF_NAME(None)))
303 {
304 proc->op_gs_SMask(ctx, proc, NULL, NULL, NULL, 0);
305 }
306 }
307}
308
309static void
310pdf_process_Do(fz_context *ctx, pdf_processor *proc, pdf_csi *csi)
311{
312 pdf_obj *xres, *xobj, *subtype;
313
314 xres = pdf_dict_get(ctx, csi->rdb, PDF_NAME(XObject));
315 xobj = pdf_dict_gets(ctx, xres, csi->name);
316 if (!xobj)
317 fz_throw(ctx, FZ_ERROR_MINOR, "cannot find XObject resource '%s'", csi->name);
318 subtype = pdf_dict_get(ctx, xobj, PDF_NAME(Subtype));
319 if (pdf_name_eq(ctx, subtype, PDF_NAME(Form)))
320 {
321 pdf_obj *st = pdf_dict_get(ctx, xobj, PDF_NAME(Subtype2));
322 if (st)
323 subtype = st;
324 }
325 if (!pdf_is_name(ctx, subtype))
326 fz_throw(ctx, FZ_ERROR_MINOR, "no XObject subtype specified");
327
328 if (pdf_is_hidden_ocg(ctx, csi->doc->ocg, csi->rdb, proc->usage, pdf_dict_get(ctx, xobj, PDF_NAME(OC))))
329 return;
330
331 if (pdf_name_eq(ctx, subtype, PDF_NAME(Form)))
332 {
333 if (proc->op_Do_form)
334 proc->op_Do_form(ctx, proc, csi->name, xobj, csi->rdb);
335 }
336
337 else if (pdf_name_eq(ctx, subtype, PDF_NAME(Image)))
338 {
339 if (proc->op_Do_image)
340 {
341 fz_image *image = pdf_load_image(ctx, csi->doc, xobj);
342 fz_try(ctx)
343 proc->op_Do_image(ctx, proc, csi->name, image);
344 fz_always(ctx)
345 fz_drop_image(ctx, image);
346 fz_catch(ctx)
347 fz_rethrow(ctx);
348 }
349 }
350
351 else if (!strcmp(pdf_to_name(ctx, subtype), "PS"))
352 fz_warn(ctx, "ignoring XObject with subtype PS");
353 else
354 fz_warn(ctx, "ignoring XObject with unknown subtype: '%s'", pdf_to_name(ctx, subtype));
355}
356
357static void
358pdf_process_CS(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, int stroke)
359{
360 if (!proc->op_CS || !proc->op_cs)
361 return;
362
363 if (!strcmp(csi->name, "Pattern"))
364 {
365 if (stroke)
366 proc->op_CS(ctx, proc, "Pattern", NULL);
367 else
368 proc->op_cs(ctx, proc, "Pattern", NULL);
369 }
370 else
371 {
372 fz_colorspace *cs;
373
374 if (!strcmp(csi->name, "DeviceGray"))
375 cs = fz_keep_colorspace(ctx, fz_device_gray(ctx));
376 else if (!strcmp(csi->name, "DeviceRGB"))
377 cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
378 else if (!strcmp(csi->name, "DeviceCMYK"))
379 cs = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
380 else
381 {
382 pdf_obj *csres, *csobj;
383 csres = pdf_dict_get(ctx, csi->rdb, PDF_NAME(ColorSpace));
384 csobj = pdf_dict_gets(ctx, csres, csi->name);
385 if (!csobj)
386 fz_throw(ctx, FZ_ERROR_MINOR, "cannot find ColorSpace resource '%s'", csi->name);
387 cs = pdf_load_colorspace(ctx, csobj);
388 }
389
390 fz_try(ctx)
391 {
392 if (stroke)
393 proc->op_CS(ctx, proc, csi->name, cs);
394 else
395 proc->op_cs(ctx, proc, csi->name, cs);
396 }
397 fz_always(ctx)
398 fz_drop_colorspace(ctx, cs);
399 fz_catch(ctx)
400 fz_rethrow(ctx);
401 }
402}
403
404static void
405pdf_process_SC(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, int stroke)
406{
407 if (csi->name[0])
408 {
409 pdf_obj *patres, *patobj, *type;
410
411 patres = pdf_dict_get(ctx, csi->rdb, PDF_NAME(Pattern));
412 patobj = pdf_dict_gets(ctx, patres, csi->name);
413 if (!patobj)
414 fz_throw(ctx, FZ_ERROR_MINOR, "cannot find Pattern resource '%s'", csi->name);
415
416 type = pdf_dict_get(ctx, patobj, PDF_NAME(PatternType));
417
418 if (pdf_to_int(ctx, type) == 1)
419 {
420 if (proc->op_SC_pattern && proc->op_sc_pattern)
421 {
422 pdf_pattern *pat = pdf_load_pattern(ctx, csi->doc, patobj);
423 fz_try(ctx)
424 {
425 if (stroke)
426 proc->op_SC_pattern(ctx, proc, csi->name, pat, csi->top, csi->stack);
427 else
428 proc->op_sc_pattern(ctx, proc, csi->name, pat, csi->top, csi->stack);
429 }
430 fz_always(ctx)
431 pdf_drop_pattern(ctx, pat);
432 fz_catch(ctx)
433 fz_rethrow(ctx);
434 }
435 }
436
437 else if (pdf_to_int(ctx, type) == 2)
438 {
439 if (proc->op_SC_shade && proc->op_sc_shade)
440 {
441 fz_shade *shade = pdf_load_shading(ctx, csi->doc, patobj);
442 fz_try(ctx)
443 {
444 if (stroke)
445 proc->op_SC_shade(ctx, proc, csi->name, shade);
446 else
447 proc->op_sc_shade(ctx, proc, csi->name, shade);
448 }
449 fz_always(ctx)
450 fz_drop_shade(ctx, shade);
451 fz_catch(ctx)
452 fz_rethrow(ctx);
453 }
454 }
455
456 else
457 {
458 fz_throw(ctx, FZ_ERROR_MINOR, "unknown pattern type: %d", pdf_to_int(ctx, type));
459 }
460 }
461
462 else
463 {
464 if (proc->op_SC_color && proc->op_sc_color)
465 {
466 if (stroke)
467 proc->op_SC_color(ctx, proc, csi->top, csi->stack);
468 else
469 proc->op_sc_color(ctx, proc, csi->top, csi->stack);
470 }
471 }
472}
473
474static pdf_obj *
475resolve_properties(fz_context *ctx, pdf_csi *csi, pdf_obj *obj)
476{
477 if (pdf_is_name(ctx, obj))
478 return pdf_dict_get(ctx, pdf_dict_get(ctx, csi->rdb, PDF_NAME(Properties)), obj);
479 else
480 return obj;
481}
482
483static void
484pdf_process_BDC(fz_context *ctx, pdf_processor *proc, pdf_csi *csi)
485{
486 if (proc->op_BDC)
487 proc->op_BDC(ctx, proc, csi->name, csi->obj, resolve_properties(ctx, csi, csi->obj));
488
489 /* Already hidden, no need to look further */
490 if (proc->hidden > 0)
491 {
492 ++proc->hidden;
493 return;
494 }
495
496 /* We only look at OC groups here */
497 if (strcmp(csi->name, "OC"))
498 return;
499
500 if (pdf_is_hidden_ocg(ctx, csi->doc->ocg, csi->rdb, proc->usage, csi->obj))
501 ++proc->hidden;
502}
503
504static void
505pdf_process_BMC(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, const char *name)
506{
507 if (proc->op_BMC)
508 proc->op_BMC(ctx, proc, name);
509 if (proc->hidden > 0)
510 ++proc->hidden;
511}
512
513static void
514pdf_process_EMC(fz_context *ctx, pdf_processor *proc, pdf_csi *csi)
515{
516 if (proc->op_EMC)
517 proc->op_EMC(ctx, proc);
518 if (proc->hidden > 0)
519 --proc->hidden;
520}
521
522static void
523pdf_process_gsave(fz_context *ctx, pdf_processor *proc, pdf_csi *csi)
524{
525 if (proc->op_q)
526 proc->op_q(ctx, proc);
527 ++csi->gstate;
528}
529
530static void
531pdf_process_grestore(fz_context *ctx, pdf_processor *proc, pdf_csi *csi)
532{
533 if (csi->gstate > 0)
534 {
535 if (proc->op_Q)
536 proc->op_Q(ctx, proc);
537 --csi->gstate;
538 }
539}
540
541static void
542pdf_process_end(fz_context *ctx, pdf_processor *proc, pdf_csi *csi)
543{
544 while (csi->gstate > 0)
545 pdf_process_grestore(ctx, proc, csi);
546 if (proc->op_END)
547 proc->op_END(ctx, proc);
548}
549
550#define A(a) (a)
551#define B(a,b) (a | b << 8)
552#define C(a,b,c) (a | b << 8 | c << 16)
553
554static void
555pdf_process_keyword(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, fz_stream *stm, char *word)
556{
557 float *s = csi->stack;
558 char csname[40];
559 int key;
560
561 key = word[0];
562 if (word[1])
563 {
564 key |= word[1] << 8;
565 if (word[2])
566 {
567 key |= word[2] << 16;
568 if (word[3])
569 key = 0;
570 }
571 }
572
573 switch (key)
574 {
575 default:
576 if (!csi->xbalance)
577 fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown keyword: '%s'", word);
578 break;
579
580 /* general graphics state */
581 case A('w'): if (proc->op_w) proc->op_w(ctx, proc, s[0]); break;
582 case A('j'): if (proc->op_j) proc->op_j(ctx, proc, fz_clampi(s[0], 0, 2)); break;
583 case A('J'): if (proc->op_J) proc->op_J(ctx, proc, fz_clampi(s[0], 0, 2)); break;
584 case A('M'): if (proc->op_M) proc->op_M(ctx, proc, s[0]); break;
585 case A('d'): if (proc->op_d) proc->op_d(ctx, proc, csi->obj, s[0]); break;
586 case B('r','i'): if (proc->op_ri) proc->op_ri(ctx, proc, csi->name); break;
587 case A('i'): if (proc->op_i) proc->op_i(ctx, proc, s[0]); break;
588
589 case B('g','s'):
590 {
591 pdf_obj *gsres, *gsobj;
592 gsres = pdf_dict_get(ctx, csi->rdb, PDF_NAME(ExtGState));
593 gsobj = pdf_dict_gets(ctx, gsres, csi->name);
594 if (!gsobj)
595 fz_throw(ctx, FZ_ERROR_MINOR, "cannot find ExtGState resource '%s'", csi->name);
596 if (proc->op_gs_begin)
597 proc->op_gs_begin(ctx, proc, csi->name, gsobj);
598 pdf_process_extgstate(ctx, proc, csi, gsobj);
599 if (proc->op_gs_end)
600 proc->op_gs_end(ctx, proc);
601 }
602 break;
603
604 /* special graphics state */
605 case A('q'): pdf_process_gsave(ctx, proc, csi); break;
606 case A('Q'): pdf_process_grestore(ctx, proc, csi); break;
607 case B('c','m'): if (proc->op_cm) proc->op_cm(ctx, proc, s[0], s[1], s[2], s[3], s[4], s[5]); break;
608
609 /* path construction */
610 case A('m'): if (proc->op_m) proc->op_m(ctx, proc, s[0], s[1]); break;
611 case A('l'): if (proc->op_l) proc->op_l(ctx, proc, s[0], s[1]); break;
612 case A('c'): if (proc->op_c) proc->op_c(ctx, proc, s[0], s[1], s[2], s[3], s[4], s[5]); break;
613 case A('v'): if (proc->op_v) proc->op_v(ctx, proc, s[0], s[1], s[2], s[3]); break;
614 case A('y'): if (proc->op_y) proc->op_y(ctx, proc, s[0], s[1], s[2], s[3]); break;
615 case A('h'): if (proc->op_h) proc->op_h(ctx, proc); break;
616 case B('r','e'): if (proc->op_re) proc->op_re(ctx, proc, s[0], s[1], s[2], s[3]); break;
617
618 /* path painting */
619 case A('S'): if (proc->op_S) proc->op_S(ctx, proc); break;
620 case A('s'): if (proc->op_s) proc->op_s(ctx, proc); break;
621 case A('F'): if (proc->op_F) proc->op_F(ctx, proc); break;
622 case A('f'): if (proc->op_f) proc->op_f(ctx, proc); break;
623 case B('f','*'): if (proc->op_fstar) proc->op_fstar(ctx, proc); break;
624 case A('B'): if (proc->op_B) proc->op_B(ctx, proc); break;
625 case B('B','*'): if (proc->op_Bstar) proc->op_Bstar(ctx, proc); break;
626 case A('b'): if (proc->op_b) proc->op_b(ctx, proc); break;
627 case B('b','*'): if (proc->op_bstar) proc->op_bstar(ctx, proc); break;
628 case A('n'): if (proc->op_n) proc->op_n(ctx, proc); break;
629
630 /* path clipping */
631 case A('W'): if (proc->op_W) proc->op_W(ctx, proc); break;
632 case B('W','*'): if (proc->op_Wstar) proc->op_Wstar(ctx, proc); break;
633
634 /* text objects */
635 case B('B','T'): csi->in_text = 1; if (proc->op_BT) proc->op_BT(ctx, proc); break;
636 case B('E','T'): csi->in_text = 0; if (proc->op_ET) proc->op_ET(ctx, proc); break;
637
638 /* text state */
639 case B('T','c'): if (proc->op_Tc) proc->op_Tc(ctx, proc, s[0]); break;
640 case B('T','w'): if (proc->op_Tw) proc->op_Tw(ctx, proc, s[0]); break;
641 case B('T','z'): if (proc->op_Tz) proc->op_Tz(ctx, proc, s[0]); break;
642 case B('T','L'): if (proc->op_TL) proc->op_TL(ctx, proc, s[0]); break;
643 case B('T','r'): if (proc->op_Tr) proc->op_Tr(ctx, proc, s[0]); break;
644 case B('T','s'): if (proc->op_Ts) proc->op_Ts(ctx, proc, s[0]); break;
645
646 case B('T','f'):
647 if (proc->op_Tf)
648 {
649 pdf_obj *fontres, *fontobj;
650 pdf_font_desc *font;
651 fontres = pdf_dict_get(ctx, csi->rdb, PDF_NAME(Font));
652 fontobj = pdf_dict_gets(ctx, fontres, csi->name);
653 if (pdf_is_dict(ctx, fontobj))
654 font = pdf_try_load_font(ctx, csi->doc, csi->rdb, fontobj, csi->cookie);
655 else
656 font = pdf_load_hail_mary_font(ctx, csi->doc);
657 fz_try(ctx)
658 proc->op_Tf(ctx, proc, csi->name, font, s[0]);
659 fz_always(ctx)
660 pdf_drop_font(ctx, font);
661 fz_catch(ctx)
662 fz_rethrow(ctx);
663 }
664 break;
665
666 /* text positioning */
667 case B('T','d'): if (proc->op_Td) proc->op_Td(ctx, proc, s[0], s[1]); break;
668 case B('T','D'): if (proc->op_TD) proc->op_TD(ctx, proc, s[0], s[1]); break;
669 case B('T','m'): if (proc->op_Tm) proc->op_Tm(ctx, proc, s[0], s[1], s[2], s[3], s[4], s[5]); break;
670 case B('T','*'): if (proc->op_Tstar) proc->op_Tstar(ctx, proc); break;
671
672 /* text showing */
673 case B('T','J'): if (proc->op_TJ) proc->op_TJ(ctx, proc, csi->obj); break;
674 case B('T','j'):
675 if (proc->op_Tj)
676 {
677 if (csi->string_len > 0)
678 proc->op_Tj(ctx, proc, csi->string, csi->string_len);
679 else
680 proc->op_Tj(ctx, proc, pdf_to_str_buf(ctx, csi->obj), pdf_to_str_len(ctx, csi->obj));
681 }
682 break;
683 case A('\''):
684 if (proc->op_squote)
685 {
686 if (csi->string_len > 0)
687 proc->op_squote(ctx, proc, csi->string, csi->string_len);
688 else
689 proc->op_squote(ctx, proc, pdf_to_str_buf(ctx, csi->obj), pdf_to_str_len(ctx, csi->obj));
690 }
691 break;
692 case A('"'):
693 if (proc->op_dquote)
694 {
695 if (csi->string_len > 0)
696 proc->op_dquote(ctx, proc, s[0], s[1], csi->string, csi->string_len);
697 else
698 proc->op_dquote(ctx, proc, s[0], s[1], pdf_to_str_buf(ctx, csi->obj), pdf_to_str_len(ctx, csi->obj));
699 }
700 break;
701
702 /* type 3 fonts */
703 case B('d','0'): if (proc->op_d0) proc->op_d0(ctx, proc, s[0], s[1]); break;
704 case B('d','1'): if (proc->op_d1) proc->op_d1(ctx, proc, s[0], s[1], s[2], s[3], s[4], s[5]); break;
705
706 /* color */
707 case B('C','S'): pdf_process_CS(ctx, proc, csi, 1); break;
708 case B('c','s'): pdf_process_CS(ctx, proc, csi, 0); break;
709 case B('S','C'): pdf_process_SC(ctx, proc, csi, 1); break;
710 case B('s','c'): pdf_process_SC(ctx, proc, csi, 0); break;
711 case C('S','C','N'): pdf_process_SC(ctx, proc, csi, 1); break;
712 case C('s','c','n'): pdf_process_SC(ctx, proc, csi, 0); break;
713
714 case A('G'): if (proc->op_G) proc->op_G(ctx, proc, s[0]); break;
715 case A('g'): if (proc->op_g) proc->op_g(ctx, proc, s[0]); break;
716 case B('R','G'): if (proc->op_RG) proc->op_RG(ctx, proc, s[0], s[1], s[2]); break;
717 case B('r','g'): if (proc->op_rg) proc->op_rg(ctx, proc, s[0], s[1], s[2]); break;
718 case A('K'): if (proc->op_K) proc->op_K(ctx, proc, s[0], s[1], s[2], s[3]); break;
719 case A('k'): if (proc->op_k) proc->op_k(ctx, proc, s[0], s[1], s[2], s[3]); break;
720
721 /* shadings, images, xobjects */
722 case B('B','I'):
723 {
724 fz_image *img = parse_inline_image(ctx, csi, stm, csname, sizeof csname);
725 fz_try(ctx)
726 {
727 if (proc->op_BI)
728 proc->op_BI(ctx, proc, img, csname[0] ? csname : NULL);
729 }
730 fz_always(ctx)
731 fz_drop_image(ctx, img);
732 fz_catch(ctx)
733 fz_rethrow(ctx);
734 }
735 break;
736
737 case B('s','h'):
738 if (proc->op_sh)
739 {
740 pdf_obj *shaderes, *shadeobj;
741 fz_shade *shade;
742 shaderes = pdf_dict_get(ctx, csi->rdb, PDF_NAME(Shading));
743 shadeobj = pdf_dict_gets(ctx, shaderes, csi->name);
744 if (!shadeobj)
745 fz_throw(ctx, FZ_ERROR_MINOR, "cannot find Shading resource '%s'", csi->name);
746 shade = pdf_load_shading(ctx, csi->doc, shadeobj);
747 fz_try(ctx)
748 proc->op_sh(ctx, proc, csi->name, shade);
749 fz_always(ctx)
750 fz_drop_shade(ctx, shade);
751 fz_catch(ctx)
752 fz_rethrow(ctx);
753 }
754 break;
755
756 case B('D','o'): pdf_process_Do(ctx, proc, csi); break;
757
758 /* marked content */
759 case B('M','P'): if (proc->op_MP) proc->op_MP(ctx, proc, csi->name); break;
760 case B('D','P'): if (proc->op_DP) proc->op_DP(ctx, proc, csi->name, csi->obj, resolve_properties(ctx, csi, csi->obj)); break;
761 case C('B','M','C'): pdf_process_BMC(ctx, proc, csi, csi->name); break;
762 case C('B','D','C'): pdf_process_BDC(ctx, proc, csi); break;
763 case C('E','M','C'): pdf_process_EMC(ctx, proc, csi); break;
764
765 /* compatibility */
766 case B('B','X'): ++csi->xbalance; if (proc->op_BX) proc->op_BX(ctx, proc); break;
767 case B('E','X'): --csi->xbalance; if (proc->op_EX) proc->op_EX(ctx, proc); break;
768 }
769}
770
771static void
772pdf_process_stream(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, fz_stream *stm)
773{
774 pdf_document *doc = csi->doc;
775 pdf_lexbuf *buf = csi->buf;
776 fz_cookie *cookie = csi->cookie;
777
778 pdf_token tok = PDF_TOK_ERROR;
779 int in_text_array = 0;
780 int syntax_errors = 0;
781
782 /* make sure we have a clean slate if we come here from flush_text */
783 pdf_clear_stack(ctx, csi);
784
785 fz_var(in_text_array);
786 fz_var(tok);
787
788 if (cookie)
789 {
790 cookie->progress_max = -1;
791 cookie->progress = 0;
792 }
793
794 do
795 {
796 fz_try(ctx)
797 {
798 do
799 {
800 /* Check the cookie */
801 if (cookie)
802 {
803 if (cookie->abort)
804 {
805 tok = PDF_TOK_EOF;
806 break;
807 }
808 cookie->progress++;
809 }
810
811 tok = pdf_lex(ctx, stm, buf);
812
813 if (in_text_array)
814 {
815 switch(tok)
816 {
817 case PDF_TOK_CLOSE_ARRAY:
818 in_text_array = 0;
819 break;
820 case PDF_TOK_REAL:
821 pdf_array_push_real(ctx, csi->obj, buf->f);
822 break;
823 case PDF_TOK_INT:
824 pdf_array_push_int(ctx, csi->obj, buf->i);
825 break;
826 case PDF_TOK_STRING:
827 pdf_array_push_string(ctx, csi->obj, buf->scratch, buf->len);
828 break;
829 case PDF_TOK_EOF:
830 break;
831 case PDF_TOK_KEYWORD:
832 if (buf->scratch[0] == 'T' && (buf->scratch[1] == 'w' || buf->scratch[1] == 'c') && buf->scratch[2] == 0)
833 {
834 int n = pdf_array_len(ctx, csi->obj);
835 if (n > 0)
836 {
837 pdf_obj *o = pdf_array_get(ctx, csi->obj, n-1);
838 if (pdf_is_number(ctx, o))
839 {
840 csi->stack[0] = pdf_to_real(ctx, o);
841 pdf_array_delete(ctx, csi->obj, n-1);
842 pdf_process_keyword(ctx, proc, csi, stm, buf->scratch);
843 }
844 }
845 }
846 /* Deliberate Fallthrough! */
847 default:
848 fz_throw(ctx, FZ_ERROR_SYNTAX, "syntax error in array");
849 }
850 }
851 else switch (tok)
852 {
853 case PDF_TOK_ENDSTREAM:
854 case PDF_TOK_EOF:
855 tok = PDF_TOK_EOF;
856 break;
857
858 case PDF_TOK_OPEN_ARRAY:
859 if (csi->obj)
860 {
861 pdf_drop_obj(ctx, csi->obj);
862 csi->obj = NULL;
863 }
864 if (csi->in_text)
865 {
866 in_text_array = 1;
867 csi->obj = pdf_new_array(ctx, doc, 4);
868 }
869 else
870 {
871 csi->obj = pdf_parse_array(ctx, doc, stm, buf);
872 }
873 break;
874
875 case PDF_TOK_OPEN_DICT:
876 if (csi->obj)
877 {
878 pdf_drop_obj(ctx, csi->obj);
879 csi->obj = NULL;
880 }
881 csi->obj = pdf_parse_dict(ctx, doc, stm, buf);
882 break;
883
884 case PDF_TOK_NAME:
885 if (csi->name[0])
886 {
887 pdf_drop_obj(ctx, csi->obj);
888 csi->obj = NULL;
889 csi->obj = pdf_new_name(ctx, buf->scratch);
890 }
891 else
892 fz_strlcpy(csi->name, buf->scratch, sizeof(csi->name));
893 break;
894
895 case PDF_TOK_INT:
896 if (csi->top < nelem(csi->stack)) {
897 csi->stack[csi->top] = buf->i;
898 csi->top ++;
899 }
900 else
901 fz_throw(ctx, FZ_ERROR_SYNTAX, "stack overflow");
902 break;
903
904 case PDF_TOK_REAL:
905 if (csi->top < nelem(csi->stack)) {
906 csi->stack[csi->top] = buf->f;
907 csi->top ++;
908 }
909 else
910 fz_throw(ctx, FZ_ERROR_SYNTAX, "stack overflow");
911 break;
912
913 case PDF_TOK_STRING:
914 if (buf->len <= sizeof(csi->string))
915 {
916 memcpy(csi->string, buf->scratch, buf->len);
917 csi->string_len = buf->len;
918 }
919 else
920 {
921 if (csi->obj)
922 {
923 pdf_drop_obj(ctx, csi->obj);
924 csi->obj = NULL;
925 }
926 csi->obj = pdf_new_string(ctx, buf->scratch, buf->len);
927 }
928 break;
929
930 case PDF_TOK_KEYWORD:
931 pdf_process_keyword(ctx, proc, csi, stm, buf->scratch);
932 pdf_clear_stack(ctx, csi);
933 break;
934
935 default:
936 fz_throw(ctx, FZ_ERROR_SYNTAX, "syntax error in content stream");
937 }
938 }
939 while (tok != PDF_TOK_EOF);
940 }
941 fz_always(ctx)
942 {
943 pdf_clear_stack(ctx, csi);
944 }
945 fz_catch(ctx)
946 {
947 int caught = fz_caught(ctx);
948 if (cookie)
949 {
950 if (caught == FZ_ERROR_TRYLATER)
951 {
952 cookie->incomplete++;
953 tok = PDF_TOK_EOF;
954 }
955 else if (caught == FZ_ERROR_ABORT)
956 {
957 fz_rethrow(ctx);
958 }
959 else if (caught == FZ_ERROR_MINOR)
960 {
961 cookie->errors++;
962 }
963 else if (caught == FZ_ERROR_SYNTAX)
964 {
965 cookie->errors++;
966 if (++syntax_errors >= MAX_SYNTAX_ERRORS)
967 {
968 fz_warn(ctx, "too many syntax errors; ignoring rest of page");
969 tok = PDF_TOK_EOF;
970 }
971 }
972 else
973 {
974 fz_rethrow(ctx);
975 }
976 }
977 else
978 {
979 if (caught == FZ_ERROR_TRYLATER)
980 tok = PDF_TOK_EOF;
981 else if (caught == FZ_ERROR_ABORT)
982 fz_rethrow(ctx);
983 else if (caught == FZ_ERROR_MINOR)
984 /* ignore minor errors */ ;
985 else if (caught == FZ_ERROR_SYNTAX)
986 {
987 if (++syntax_errors >= MAX_SYNTAX_ERRORS)
988 {
989 fz_warn(ctx, "too many syntax errors; ignoring rest of page");
990 tok = PDF_TOK_EOF;
991 }
992 }
993 else
994 {
995 fz_rethrow(ctx);
996 }
997 }
998
999 /* If we do catch an error, then reset ourselves to a base lexing state */
1000 in_text_array = 0;
1001 }
1002 }
1003 while (tok != PDF_TOK_EOF);
1004}
1005
1006/* Functions to actually process annotations, glyphs and general stream objects */
1007void
1008pdf_process_contents(fz_context *ctx, pdf_processor *proc, pdf_document *doc, pdf_obj *rdb, pdf_obj *stmobj, fz_cookie *cookie)
1009{
1010 pdf_csi csi;
1011 pdf_lexbuf buf;
1012 fz_stream *stm = NULL;
1013
1014 if (!stmobj)
1015 return;
1016
1017 fz_var(stm);
1018
1019 pdf_lexbuf_init(ctx, &buf, PDF_LEXBUF_SMALL);
1020 pdf_init_csi(ctx, &csi, doc, rdb, &buf, cookie);
1021
1022 fz_try(ctx)
1023 {
1024 fz_defer_reap_start(ctx);
1025 stm = pdf_open_contents_stream(ctx, doc, stmobj);
1026 pdf_process_stream(ctx, proc, &csi, stm);
1027 pdf_process_end(ctx, proc, &csi);
1028 }
1029 fz_always(ctx)
1030 {
1031 fz_defer_reap_end(ctx);
1032 fz_drop_stream(ctx, stm);
1033 pdf_clear_stack(ctx, &csi);
1034 pdf_lexbuf_fin(ctx, &buf);
1035 }
1036 fz_catch(ctx)
1037 {
1038 proc->close_processor = NULL; /* aborted run, don't warn about unclosed processor */
1039 fz_rethrow(ctx);
1040 }
1041}
1042
1043void
1044pdf_process_annot(fz_context *ctx, pdf_processor *proc, pdf_document *doc, pdf_page *page, pdf_annot *annot, fz_cookie *cookie)
1045{
1046 int flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
1047
1048 if (flags & (PDF_ANNOT_IS_INVISIBLE | PDF_ANNOT_IS_HIDDEN))
1049 return;
1050
1051 /* popup annotations should never be drawn */
1052 if (pdf_annot_type(ctx, annot) == PDF_ANNOT_POPUP)
1053 return;
1054
1055 if (proc->usage)
1056 {
1057 if (!strcmp(proc->usage, "Print") && !(flags & PDF_ANNOT_IS_PRINT))
1058 return;
1059 if (!strcmp(proc->usage, "View") && (flags & PDF_ANNOT_IS_NO_VIEW))
1060 return;
1061 }
1062
1063 /* TODO: NoZoom and NoRotate */
1064
1065 /* XXX what resources, if any, to use for this check? */
1066 if (pdf_is_hidden_ocg(ctx, doc->ocg, NULL, proc->usage, pdf_dict_get(ctx, annot->obj, PDF_NAME(OC))))
1067 return;
1068
1069 if (proc->op_q && proc->op_cm && proc->op_Do_form && proc->op_Q && annot->ap)
1070 {
1071 fz_matrix matrix = pdf_annot_transform(ctx, annot);
1072 proc->op_q(ctx, proc);
1073 proc->op_cm(ctx, proc,
1074 matrix.a, matrix.b,
1075 matrix.c, matrix.d,
1076 matrix.e, matrix.f);
1077 proc->op_Do_form(ctx, proc, NULL, annot->ap, pdf_page_resources(ctx, page));
1078 proc->op_Q(ctx, proc);
1079 }
1080}
1081
1082void
1083pdf_process_glyph(fz_context *ctx, pdf_processor *proc, pdf_document *doc, pdf_obj *rdb, fz_buffer *contents)
1084{
1085 pdf_csi csi;
1086 pdf_lexbuf buf;
1087 fz_stream *stm = NULL;
1088
1089 fz_var(stm);
1090
1091 if (!contents)
1092 return;
1093
1094 pdf_lexbuf_init(ctx, &buf, PDF_LEXBUF_SMALL);
1095 pdf_init_csi(ctx, &csi, doc, rdb, &buf, NULL);
1096
1097 fz_try(ctx)
1098 {
1099 stm = fz_open_buffer(ctx, contents);
1100 pdf_process_stream(ctx, proc, &csi, stm);
1101 pdf_process_end(ctx, proc, &csi);
1102 }
1103 fz_always(ctx)
1104 {
1105 fz_drop_stream(ctx, stm);
1106 pdf_clear_stack(ctx, &csi);
1107 pdf_lexbuf_fin(ctx, &buf);
1108 }
1109 fz_catch(ctx)
1110 {
1111 /* Note: Any SYNTAX errors should have been swallowed
1112 * by pdf_process_stream, but in case any escape from other
1113 * functions, recast the error type here to be safe. */
1114 if (fz_caught(ctx) == FZ_ERROR_SYNTAX)
1115 fz_throw(ctx, FZ_ERROR_GENERIC, "syntax error in content stream");
1116 fz_rethrow(ctx);
1117 }
1118}
1119
1120void
1121pdf_tos_save(fz_context *ctx, pdf_text_object_state *tos, fz_matrix save[2])
1122{
1123 save[0] = tos->tm;
1124 save[1] = tos->tlm;
1125}
1126
1127void
1128pdf_tos_restore(fz_context *ctx, pdf_text_object_state *tos, fz_matrix save[2])
1129{
1130 tos->tm = save[0];
1131 tos->tlm = save[1];
1132}
1133
1134fz_text *
1135pdf_tos_get_text(fz_context *ctx, pdf_text_object_state *tos)
1136{
1137 fz_text *text = tos->text;
1138
1139 tos->text = NULL;
1140
1141 return text;
1142}
1143
1144void
1145pdf_tos_reset(fz_context *ctx, pdf_text_object_state *tos, int render)
1146{
1147 tos->text = fz_new_text(ctx);
1148 tos->text_mode = render;
1149 tos->text_bbox = fz_empty_rect;
1150}
1151
1152int
1153pdf_tos_make_trm(fz_context *ctx, pdf_text_object_state *tos, pdf_text_state *text, pdf_font_desc *fontdesc, int cid, fz_matrix *trm)
1154{
1155 fz_matrix tsm;
1156
1157 tsm.a = text->size * text->scale;
1158 tsm.b = 0;
1159 tsm.c = 0;
1160 tsm.d = text->size;
1161 tsm.e = 0;
1162 tsm.f = text->rise;
1163
1164 if (fontdesc->wmode == 0)
1165 {
1166 pdf_hmtx h = pdf_lookup_hmtx(ctx, fontdesc, cid);
1167 float w0 = h.w * 0.001f;
1168 tos->char_tx = (w0 * text->size + text->char_space) * text->scale;
1169 tos->char_ty = 0;
1170 }
1171
1172 if (fontdesc->wmode == 1)
1173 {
1174 pdf_vmtx v = pdf_lookup_vmtx(ctx, fontdesc, cid);
1175 float w1 = v.w * 0.001f;
1176 tsm.e -= v.x * fabsf(text->size) * 0.001f;
1177 tsm.f -= v.y * text->size * 0.001f;
1178 tos->char_tx = 0;
1179 tos->char_ty = w1 * text->size + text->char_space;
1180 }
1181
1182 *trm = fz_concat(tsm, tos->tm);
1183
1184 tos->cid = cid;
1185 tos->gid = pdf_font_cid_to_gid(ctx, fontdesc, cid);
1186 tos->fontdesc = fontdesc;
1187
1188 /* Compensate for the glyph cache limited positioning precision */
1189 tos->char_bbox = fz_expand_rect(fz_bound_glyph(ctx, fontdesc->font, tos->gid, *trm), 1);
1190
1191 return tos->gid;
1192}
1193
1194void
1195pdf_tos_move_after_char(fz_context *ctx, pdf_text_object_state *tos)
1196{
1197 tos->text_bbox = fz_union_rect(tos->text_bbox, tos->char_bbox);
1198 tos->tm = fz_pre_translate(tos->tm, tos->char_tx, tos->char_ty);
1199}
1200
1201void
1202pdf_tos_translate(pdf_text_object_state *tos, float tx, float ty)
1203{
1204 tos->tlm = fz_pre_translate(tos->tlm, tx, ty);
1205 tos->tm = tos->tlm;
1206}
1207
1208void
1209pdf_tos_set_matrix(pdf_text_object_state *tos, float a, float b, float c, float d, float e, float f)
1210{
1211 tos->tm.a = a;
1212 tos->tm.b = b;
1213 tos->tm.c = c;
1214 tos->tm.d = d;
1215 tos->tm.e = e;
1216 tos->tm.f = f;
1217 tos->tlm = tos->tm;
1218}
1219
1220void
1221pdf_tos_newline(pdf_text_object_state *tos, float leading)
1222{
1223 tos->tlm = fz_pre_translate(tos->tlm, 0, -leading);
1224 tos->tm = tos->tlm;
1225}
1226