1 /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
2
3 Copyright (C) 2007-2014 by Jin-Hwan Cho and Shunsaku Hirata,
4 the dvipdfmx project team.
5
6 Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 */
22
23 /*
24 * Concatinating content streams are only supported for streams that only uses
25 * single FlateDecode filter, i.e.,
26 *
27 * /Filter /FlateDecode or /Filter [/FlateDecode]
28 *
29 * TrimBox, BleedBox, ArtBox, Rotate ...
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35
36 #include "system.h"
37 #include "mem.h"
38 #include "mfileio.h"
39 #include "error.h"
40 #include "dvipdfmx.h"
41
42 #include "pdfobj.h"
43 #include "pdfdev.h"
44 #include "pdfdoc.h"
45
46 #include "pdfdraw.h"
47 #include "pdfparse.h"
48
49 #include "pdfximage.h"
50
51 #include "epdf.h"
52
53 static int rect_equal (pdf_obj *rect1, pdf_obj *rect2);
54 #if 0
55 #if HAVE_ZLIB
56 #include <zlib.h>
57 static int add_stream_flate (pdf_obj *dst, const void *data, long len);
58 #endif
59 static int concat_stream (pdf_obj *dst, pdf_obj *src);
60 #endif
61 /*
62 * From PDFReference15_v6.pdf (p.119 and p.834)
63 *
64 * MediaBox rectangle (Required; inheritable)
65 *
66 * The media box defines the boundaries of the physical medium on which the
67 * page is to be printed. It may include any extended area surrounding the
68 * finished page for bleed, printing marks, or other such purposes. It may
69 * also include areas close to the edges of the medium that cannot be marked
70 * because of physical limitations of the output device. Content falling
71 * outside this boundary can safely be discarded without affecting the
72 * meaning of the PDF file.
73 *
74 * CropBox rectangle (Optional; inheritable)
75 *
76 * The crop box defines the region to which the contents of the page are to be
77 * clipped (cropped) when displayed or printed. Unlike the other boxes, the
78 * crop box has no defined meaning in terms of physical page geometry or
79 * intended use; it merely imposes clipping on the page contents. However,
80 * in the absence of additional information (such as imposition instructions
81 * specified in a JDF or PJTF job ticket), the crop box will determine how
82 * the page’s contents are to be positioned on the output medium. The default
83 * value is the page’s media box.
84 *
85 * BleedBox rectangle (Optional; PDF 1.3)
86 *
87 * The bleed box (PDF 1.3) defines the region to which the contents of the
88 * page should be clipped when output in a production environment. This may
89 * include any extra “bleed area†needed to accommodate the physical
90 * limitations of cutting, folding, and trimming equipment. The actual printed
91 * page may include printing marks that fall outside the bleed box.
92 * The default value is the page’s crop box.
93 *
94 * TrimBox rectangle (Optional; PDF 1.3)
95 *
96 * The trim box (PDF 1.3) defines the intended dimensions of the finished page
97 * after trimming. It may be smaller than the media box, to allow for
98 * production-related content such as printing instructions, cut marks, or
99 * color bars. The default value is the page’s crop box.
100 *
101 * ArtBox rectangle (Optional; PDF 1.3)
102 *
103 * The art box (PDF 1.3) defines the extent of the page’s meaningful content
104 * (including potential white space) as intended by the page’s creator.
105 * The default value is the page’s crop box.
106 *
107 * Rotate integer (Optional; inheritable)
108 *
109 * The number of degrees by which the page should be rotated clockwise when
110 * displayed or printed. The value must be a multiple of 90. Default value: 0.
111 */
112
113 static int
rect_equal(pdf_obj * rect1,pdf_obj * rect2)114 rect_equal (pdf_obj *rect1, pdf_obj *rect2)
115 {
116 int i;
117
118 if (!rect1 || !rect2)
119 return 0;
120 for (i = 0; i < 4; i++) {
121 if (pdf_number_value(pdf_get_array(rect1, i)) !=
122 pdf_number_value(pdf_get_array(rect2, i)))
123 return 0;
124 }
125
126 return 1;
127 }
128
129 static pdf_obj*
pdf_get_page_obj(pdf_file * pf,long page_no,pdf_obj ** ret_bbox,pdf_obj ** ret_resources)130 pdf_get_page_obj (pdf_file *pf, long page_no,
131 pdf_obj **ret_bbox, pdf_obj **ret_resources)
132 {
133 pdf_obj *page_tree;
134 pdf_obj *bbox = NULL, *resources = NULL, *rotate = NULL;
135 long page_idx;
136
137 /*
138 * Get Page Tree.
139 */
140 page_tree = NULL;
141 {
142 pdf_obj *trailer, *catalog;
143 pdf_obj *markinfo, *tmp;
144
145 trailer = pdf_file_get_trailer(pf);
146
147 if (pdf_lookup_dict(trailer, "Encrypt")) {
148 WARN("This PDF document is encrypted.");
149 pdf_release_obj(trailer);
150 return NULL;
151 }
152
153 catalog = pdf_deref_obj(pdf_lookup_dict(trailer, "Root"));
154 if (!PDF_OBJ_DICTTYPE(catalog)) {
155 WARN("Can't read document catalog.");
156 pdf_release_obj(trailer);
157 if (catalog)
158 pdf_release_obj(catalog);
159 return NULL;
160 }
161 pdf_release_obj(trailer);
162
163 markinfo = pdf_deref_obj(pdf_lookup_dict(catalog, "MarkInfo"));
164 if (markinfo) {
165 tmp = pdf_lookup_dict(markinfo, "Marked");
166 if (PDF_OBJ_BOOLEANTYPE(tmp) && pdf_boolean_value(tmp))
167 WARN("File contains tagged PDF. Ignoring tags.");
168 pdf_release_obj(markinfo);
169 }
170
171 page_tree = pdf_deref_obj(pdf_lookup_dict(catalog, "Pages"));
172 pdf_release_obj(catalog);
173 }
174 if (!page_tree) {
175 WARN("Page tree not found.");
176 return NULL;
177 }
178
179 /*
180 * Negative page numbers are counted from the back.
181 */
182 {
183 long count = pdf_number_value(pdf_lookup_dict(page_tree, "Count"));
184 page_idx = page_no + (page_no >= 0 ? -1 : count);
185 if (page_idx < 0 || page_idx >= count) {
186 WARN("Page %ld does not exist.", page_no);
187 pdf_release_obj(page_tree);
188 return NULL;
189 }
190 page_no = page_idx+1;
191 }
192
193 /*
194 * Seek correct page. Get Media/Crop Box.
195 * Media box and resources can be inherited.
196 */
197 {
198 pdf_obj *kids_ref, *kids;
199 pdf_obj *crop_box = NULL;
200 pdf_obj *tmp;
201
202 tmp = pdf_lookup_dict(page_tree, "Resources");
203 resources = tmp ? pdf_deref_obj(tmp) : pdf_new_dict();
204
205 while (1) {
206 long kids_length, i;
207
208 if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "MediaBox")))) {
209 if (bbox)
210 pdf_release_obj(bbox);
211 bbox = tmp;
212 }
213 if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "BleedBox")))) {
214 if (!rect_equal(tmp, bbox)) {
215 if (bbox)
216 pdf_release_obj(bbox);
217 bbox = tmp;
218 } else
219 pdf_release_obj(tmp);
220 }
221 if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "TrimBox")))) {
222 if (!rect_equal(tmp, bbox)) {
223 if (bbox)
224 pdf_release_obj(bbox);
225 bbox = tmp;
226 } else
227 pdf_release_obj(tmp);
228 }
229 if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "ArtBox")))) {
230 if (!rect_equal(tmp, bbox)) {
231 if (bbox)
232 pdf_release_obj(bbox);
233 bbox = tmp;
234 } else
235 pdf_release_obj(tmp);
236 }
237 if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "CropBox")))) {
238 if (crop_box)
239 pdf_release_obj(crop_box);
240 crop_box = tmp;
241 }
242 if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Rotate")))) {
243 if (rotate)
244 pdf_release_obj(rotate);
245 rotate = tmp;
246 }
247 if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Resources")))) {
248 #if 0
249 pdf_merge_dict(tmp, resources);
250 #endif
251 if (resources)
252 pdf_release_obj(resources);
253 resources = tmp;
254 }
255
256 kids_ref = pdf_lookup_dict(page_tree, "Kids");
257 if (!kids_ref)
258 break;
259 kids = pdf_deref_obj(kids_ref);
260 kids_length = pdf_array_length(kids);
261
262 for (i = 0; i < kids_length; i++) {
263 long count;
264
265 pdf_release_obj(page_tree);
266 page_tree = pdf_deref_obj(pdf_get_array(kids, i));
267
268 tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Count"));
269 if (tmp) {
270 /* Pages object */
271 count = pdf_number_value(tmp);
272 pdf_release_obj(tmp);
273 } else
274 /* Page object */
275 count = 1;
276
277 if (page_idx < count)
278 break;
279
280 page_idx -= count;
281 }
282
283 pdf_release_obj(kids);
284
285 if (i == kids_length) {
286 WARN("Page %ld not found! Broken PDF file?", page_no);
287 if (bbox)
288 pdf_release_obj(bbox);
289 if (crop_box)
290 pdf_release_obj(crop_box);
291 if (rotate)
292 pdf_release_obj(rotate);
293 pdf_release_obj(resources);
294 pdf_release_obj(page_tree);
295 return NULL;
296 }
297 }
298 if (crop_box) {
299 pdf_release_obj(bbox);
300 bbox = crop_box;
301 }
302 }
303
304 if (!bbox) {
305 WARN("No BoundingBox information available.");
306 pdf_release_obj(page_tree);
307 pdf_release_obj(resources);
308 if (rotate)
309 pdf_release_obj(rotate);
310 return NULL;
311 }
312
313 if (rotate) {
314 if (pdf_number_value(rotate) != 0.0)
315 WARN("<< /Rotate %d >> found. (Not supported yet)", (int)pdf_number_value(rotate));
316 pdf_release_obj(rotate);
317 rotate = NULL;
318 }
319
320 if (ret_bbox != NULL)
321 *ret_bbox = bbox;
322 if (ret_resources != NULL)
323 *ret_resources = resources;
324
325 return page_tree;
326 }
327
328 static pdf_obj*
pdf_get_page_content(pdf_obj * page)329 pdf_get_page_content (pdf_obj* page)
330 {
331 pdf_obj *contents, *content_new;
332
333 contents = pdf_deref_obj(pdf_lookup_dict(page, "Contents"));
334 if (!contents)
335 return NULL;
336
337 if (pdf_obj_typeof(contents) == PDF_NULL) {
338 /* empty page */
339 pdf_release_obj(contents);
340 /* TODO: better don't include anything if the page is empty */
341 contents = pdf_new_stream(0);
342 } else if (PDF_OBJ_ARRAYTYPE(contents)) {
343 /*
344 * Concatenate all content streams.
345 */
346 pdf_obj *content_seg;
347 int idx = 0;
348 content_new = pdf_new_stream(STREAM_COMPRESS);
349 for (;;) {
350 content_seg = pdf_deref_obj(pdf_get_array(contents, idx));
351 if (!content_seg)
352 break;
353 else if (PDF_OBJ_NULLTYPE(content_seg)) {
354 /* Silently ignore. */
355 } else if (!PDF_OBJ_STREAMTYPE(content_seg)) {
356 WARN("Page content not a stream object. Broken PDF file?");
357 pdf_release_obj(content_seg);
358 pdf_release_obj(content_new);
359 pdf_release_obj(contents);
360 return NULL;
361 } else if (pdf_concat_stream(content_new, content_seg) < 0) {
362 WARN("Could not handle content stream with multiple segments.");
363 pdf_release_obj(content_seg);
364 pdf_release_obj(content_new);
365 pdf_release_obj(contents);
366 return NULL;
367 }
368 pdf_release_obj(content_seg);
369 idx++;
370 }
371 pdf_release_obj(contents);
372 contents = content_new;
373 } else {
374 if (!PDF_OBJ_STREAMTYPE(contents)) {
375 WARN("Page content not a stream object. Broken PDF file?");
376 pdf_release_obj(contents);
377 return NULL;
378 }
379 /* Flate the contents if necessary. */
380 content_new = pdf_new_stream(STREAM_COMPRESS);
381 if (pdf_concat_stream(content_new, contents) < 0) {
382 WARN("Could not handle a content stream.");
383 pdf_release_obj(contents);
384 pdf_release_obj(content_new);
385 return NULL;
386 }
387 pdf_release_obj(contents);
388 contents = content_new;
389 }
390
391 return contents;
392 }
393
394 int
pdf_include_page(pdf_ximage * ximage,FILE * image_file,const char * filename)395 pdf_include_page (pdf_ximage *ximage, FILE *image_file, const char *filename)
396 {
397 pdf_file *pf;
398 xform_info info;
399 pdf_obj *contents = NULL, *catalog;
400 pdf_obj *page = NULL, *resources = NULL, *markinfo = NULL;
401 long page_no;
402
403 pf = pdf_open(filename, image_file);
404 if (!pf)
405 return -1;
406
407 if (pdf_file_get_version(pf) > pdf_get_version())
408 goto too_recent;
409
410 pdf_ximage_init_form_info(&info);
411
412 /*
413 * Get Page.
414 */
415 page_no = pdf_ximage_get_page(ximage);
416 if (page_no == 0)
417 page_no = 1;
418
419 page = pdf_doc_get_page(pf, page_no, NULL, &info.bbox, &resources);
420 if(!page)
421 goto error_silent;
422
423 catalog = pdf_file_get_catalog(pf);
424 markinfo = pdf_deref_obj(pdf_lookup_dict(catalog, "MarkInfo"));
425 if (markinfo) {
426 pdf_obj *tmp = pdf_deref_obj(pdf_lookup_dict(markinfo, "Marked"));
427 pdf_release_obj(markinfo);
428 if (!PDF_OBJ_BOOLEANTYPE(tmp)) {
429 if (tmp)
430 pdf_release_obj(tmp);
431 goto error;
432 } else if (pdf_boolean_value(tmp))
433 WARN("File contains tagged PDF. Ignoring tags.");
434 pdf_release_obj(tmp);
435 }
436
437 contents = pdf_deref_obj(pdf_lookup_dict(page, "Contents"));
438 pdf_release_obj(page);
439 page = NULL;
440
441 /*
442 * Handle page content stream.
443 */
444 {
445 pdf_obj *content_new;
446
447 if (!contents) {
448 /*
449 * Empty page
450 */
451 content_new = pdf_new_stream(0);
452 /* TODO: better don't include anything if the page is empty */
453 } else if (PDF_OBJ_STREAMTYPE(contents)) {
454 /*
455 * We must import the stream because its dictionary
456 * may contain indirect references.
457 */
458 content_new = pdf_import_object(contents);
459 } else if (PDF_OBJ_ARRAYTYPE(contents)) {
460 /*
461 * Concatenate all content streams.
462 */
463 int idx, len = pdf_array_length(contents);
464 content_new = pdf_new_stream(STREAM_COMPRESS);
465 for (idx = 0; idx < len; idx++) {
466 pdf_obj *content_seg = pdf_deref_obj(pdf_get_array(contents, idx));
467 if (!PDF_OBJ_STREAMTYPE(content_seg) ||
468 pdf_concat_stream(content_new, content_seg) < 0) {
469 pdf_release_obj(content_seg);
470 pdf_release_obj(content_new);
471 goto error;
472 }
473 pdf_release_obj(content_seg);
474 }
475 } else
476 goto error;
477
478 if (contents)
479 pdf_release_obj(contents);
480 contents = content_new;
481 }
482
483 /*
484 * Add entries to contents stream dictionary.
485 */
486 {
487 pdf_obj *contents_dict, *bbox, *matrix;
488
489 contents_dict = pdf_stream_dict(contents);
490 pdf_add_dict(contents_dict,
491 pdf_new_name("Type"),
492 pdf_new_name("XObject"));
493 pdf_add_dict(contents_dict,
494 pdf_new_name("Subtype"),
495 pdf_new_name("Form"));
496 pdf_add_dict(contents_dict,
497 pdf_new_name("FormType"),
498 pdf_new_number(1.0));
499
500 bbox = pdf_new_array();
501 pdf_add_array(bbox, pdf_new_number(info.bbox.llx));
502 pdf_add_array(bbox, pdf_new_number(info.bbox.lly));
503 pdf_add_array(bbox, pdf_new_number(info.bbox.urx));
504 pdf_add_array(bbox, pdf_new_number(info.bbox.ury));
505
506 pdf_add_dict(contents_dict, pdf_new_name("BBox"), bbox);
507
508 matrix = pdf_new_array();
509 pdf_add_array(matrix, pdf_new_number(1.0));
510 pdf_add_array(matrix, pdf_new_number(0.0));
511 pdf_add_array(matrix, pdf_new_number(0.0));
512 pdf_add_array(matrix, pdf_new_number(1.0));
513 pdf_add_array(matrix, pdf_new_number(0.0));
514 pdf_add_array(matrix, pdf_new_number(0.0));
515
516 pdf_add_dict(contents_dict, pdf_new_name("Matrix"), matrix);
517
518 pdf_add_dict(contents_dict, pdf_new_name("Resources"),
519 pdf_import_object(resources));
520 pdf_release_obj(resources);
521 }
522
523 pdf_close(pf);
524
525 pdf_ximage_set_form(ximage, &info, contents);
526
527 return 0;
528
529 error:
530 WARN("Cannot parse document. Broken PDF file?");
531 error_silent:
532 if (resources)
533 pdf_release_obj(resources);
534 if (markinfo)
535 pdf_release_obj(markinfo);
536 if (page)
537 pdf_release_obj(page);
538 if (contents)
539 pdf_release_obj(contents);
540
541 pdf_close(pf);
542
543 return -1;
544
545 too_recent:
546 pdf_close(pf);
547 WARN("PDF version of input file more recent than in output file.");
548 if (compat_mode) {
549 WARN("Converting. Use \"-V\" switch to change output PDF version.");
550 return 1;
551 } else {
552 WARN("Use \"-V\" switch to change output PDF version.");
553 return -1;
554 }
555 }
556
557 typedef enum {
558 OP_SETCOLOR = 1,
559 OP_CLOSEandCLIP = 2,
560 OP_CLIP = 3,
561 OP_CONCATMATRIX = 4,
562 OP_SETCOLORSPACE = 5,
563 OP_RECTANGLE = 6,
564 OP_CURVETO = 7,
565 OP_CLOSEPATH = 8,
566 OP_LINETO = 9,
567 OP_MOVETO = 10,
568 OP_NOOP = 11,
569 OP_GSAVE = 12,
570 OP_GRESTORE = 13,
571 OP_CURVETO1 = 14,
572 OP_CURVETO2 = 15,
573 OP_UNKNOWN = 16
574 } pdf_opcode;
575
576 static struct operator
577 {
578 const char *token;
579 int opcode;
580 } pdf_operators[] = {
581 {"SCN", OP_SETCOLOR},
582 {"b*", OP_CLOSEandCLIP},
583 {"B*", OP_CLIP},
584 {"cm", OP_CONCATMATRIX},
585 {"CS", OP_SETCOLORSPACE},
586 {"f*", 0},
587 {"gs", -1},
588 {"re", OP_RECTANGLE},
589 {"rg", -3},
590 {"RG", -3},
591 {"sc", OP_SETCOLOR},
592 {"SC", OP_SETCOLOR},
593 {"W*", OP_CLIP},
594 {"b", OP_CLOSEandCLIP},
595 {"B", OP_CLIP},
596 {"c", OP_CURVETO},
597 {"d", -2},
598 {"f", 0},
599 {"F", 0},
600 {"g", -1},
601 {"G", -1},
602 {"h", OP_CLOSEPATH},
603 {"i", -1},
604 {"j", -1},
605 {"J", -1},
606 {"k", -4},
607 {"K", -4},
608 {"l", OP_LINETO},
609 {"m", OP_MOVETO},
610 {"M", -1},
611 {"n", OP_NOOP},
612 {"q", OP_GSAVE},
613 {"Q", OP_GRESTORE},
614 {"s", OP_CLOSEandCLIP},
615 {"S", OP_CLIP},
616 {"v", OP_CURVETO1},
617 {"w", -1},
618 {"W", OP_CLIP},
619 {"y", OP_CURVETO2}
620 };
621
622
623 int
pdf_copy_clip(FILE * image_file,int pageNo,double x_user,double y_user)624 pdf_copy_clip (FILE *image_file, int pageNo, double x_user, double y_user)
625 {
626 pdf_obj *page_tree, *contents;
627 int depth = 0, top = -1;
628 const char *clip_path, *end_path;
629 char *save_path, *temp;
630 pdf_tmatrix M;
631 double stack[6];
632 pdf_file *pf;
633
634 pf = pdf_open(NULL, image_file);
635 if (!pf)
636 return -1;
637
638 pdf_dev_currentmatrix(&M);
639 pdf_invertmatrix(&M);
640 M.e += x_user; M.f += y_user;
641 page_tree = pdf_get_page_obj (pf, pageNo, NULL, NULL);
642 if (!page_tree) {
643 pdf_close(pf);
644 return -1;
645 }
646
647 contents = pdf_get_page_content(page_tree);
648 pdf_release_obj(page_tree);
649 if (!contents) {
650 pdf_close(pf);
651 return -1;
652 }
653
654 pdf_doc_add_page_content(" ", 1);
655
656 save_path = malloc(pdf_stream_length(contents) + 1);
657 strncpy(save_path, (const char *) pdf_stream_dataptr(contents), pdf_stream_length(contents));
658 clip_path = save_path;
659 end_path = clip_path + pdf_stream_length(contents);
660 depth = 0;
661
662 for (; clip_path < end_path; clip_path++) {
663 int color_dimen = 0; /* silence uninitialized warning */
664 char *token;
665 skip_white(&clip_path, end_path);
666 if (clip_path == end_path)
667 break;
668 if (depth > 1) {
669 if (*clip_path == 'q')
670 depth++;
671 if (*clip_path == 'Q')
672 depth--;
673 parse_ident(&clip_path, end_path);
674 continue;
675 } else if (*clip_path == '-'
676 || *clip_path == '+'
677 || *clip_path == '.'
678 || isdigit((unsigned char)*clip_path)) {
679 stack[++top] = strtod(clip_path, &temp);
680 clip_path = temp;
681 } else if (*clip_path == '[') {
682 /* Ignore, but put a dummy value on the stack (in case of d operator) */
683 parse_pdf_array(&clip_path, end_path, pf);
684 stack[++top] = 0;
685 } else if (*clip_path == '/') {
686 if (strncmp("/DeviceGray", clip_path, 11) == 0
687 || strncmp("/Indexed", clip_path, 8) == 0
688 || strncmp("/CalGray", clip_path, 8) == 0) {
689 color_dimen = 1;
690 continue;
691 }
692 else if (strncmp("/DeviceRGB", clip_path, 10) == 0
693 || strncmp("/CalRGB", clip_path, 7) == 0
694 || strncmp("/Lab", clip_path, 4) == 0) {
695 color_dimen = 3;
696 continue;
697 }
698 else if (strncmp("/DeviceCMYK", clip_path, 11) == 0) {
699 color_dimen = 4;
700 continue;
701 }
702 else {
703 clip_path++;
704 parse_ident(&clip_path, end_path);
705 skip_white(&clip_path, end_path);
706 token = parse_ident(&clip_path, end_path);
707 if (strcmp(token, "gs") == 0) {
708 continue;
709 }
710 return -1;
711 }
712 } else {
713 int j;
714 pdf_tmatrix T;
715 pdf_coord p0, p1, p2, p3;
716
717 token = parse_ident(&clip_path, end_path);
718 for (j = 0; j < sizeof(pdf_operators) / sizeof(pdf_operators[0]); j++)
719 if (strcmp(token, pdf_operators[j].token) == 0)
720 break;
721 if (j == sizeof(pdf_operators) / sizeof(pdf_operators[0])) {
722 return -1;
723 }
724 switch (pdf_operators[j].opcode) {
725 case 0:
726 case -1:
727 case -2:
728 case -3:
729 case -4:
730 /* Just pop the stack and do nothing. */
731 top += pdf_operators[j].opcode;
732 if (top < -1)
733 return -1;
734 break;
735 case OP_SETCOLOR:
736 top -= color_dimen;
737 if (top < -1)
738 return -1;
739 break;
740 case OP_CLOSEandCLIP:
741 pdf_dev_closepath();
742 case OP_CLIP:
743 #if 0
744 pdf_dev_clip();
745 #else
746 pdf_dev_flushpath('W', PDF_FILL_RULE_NONZERO);
747 #endif
748 break;
749 case OP_CONCATMATRIX:
750 if (top < 5)
751 return -1;
752 T.f = stack[top--];
753 T.e = stack[top--];
754 T.d = stack[top--];
755 T.c = stack[top--];
756 T.b = stack[top--];
757 T.a = stack[top--];
758 pdf_concatmatrix(&M, &T);
759 break;
760 case OP_SETCOLORSPACE:
761 /* Do nothing. */
762 break;
763 case OP_RECTANGLE:
764 if (top < 3)
765 return -1;
766 p1.y = stack[top--];
767 p1.x = stack[top--];
768 p0.y = stack[top--];
769 p0.x = stack[top--];
770 if (M.b == 0 && M.c == 0) {
771 pdf_tmatrix M0;
772 M0.a = M.a; M0.b = M.b; M0.c = M.c; M0.d = M.d;
773 M0.e = 0; M0.f = 0;
774 pdf_dev_transform(&p0, &M);
775 pdf_dev_transform(&p1, &M0);
776 pdf_dev_rectadd(p0.x, p0.y, p1.x, p1.y);
777 } else {
778 p2.x = p0.x + p1.x; p2.y = p0.y + p1.y;
779 p3.x = p0.x; p3.y = p0.y + p1.y;
780 p1.x += p0.x; p1.y = p0.y;
781 pdf_dev_transform(&p0, &M);
782 pdf_dev_transform(&p1, &M);
783 pdf_dev_transform(&p2, &M);
784 pdf_dev_transform(&p3, &M);
785 pdf_dev_moveto(p0.x, p0.y);
786 pdf_dev_lineto(p1.x, p1.y);
787 pdf_dev_lineto(p2.x, p2.y);
788 pdf_dev_lineto(p3.x, p3.y);
789 pdf_dev_closepath();
790 }
791 break;
792 case OP_CURVETO:
793 if (top < 5)
794 return -1;
795 p0.y = stack[top--];
796 p0.x = stack[top--];
797 pdf_dev_transform(&p0, &M);
798 p1.y = stack[top--];
799 p1.x = stack[top--];
800 pdf_dev_transform(&p1, &M);
801 p2.y = stack[top--];
802 p2.x = stack[top--];
803 pdf_dev_transform(&p2, &M);
804 pdf_dev_curveto(p2.x, p2.y, p1.x, p1.y, p0.x, p0.y);
805 break;
806 case OP_CLOSEPATH:
807 pdf_dev_closepath();
808 break;
809 case OP_LINETO:
810 if (top < 1)
811 return -1;
812 p0.y = stack[top--];
813 p0.x = stack[top--];
814 pdf_dev_transform(&p0, &M);
815 pdf_dev_lineto(p0.x, p0.y);
816 break;
817 case OP_MOVETO:
818 if (top < 1)
819 return -1;
820 p0.y = stack[top--];
821 p0.x = stack[top--];
822 pdf_dev_transform(&p0, &M);
823 pdf_dev_moveto(p0.x, p0.y);
824 break;
825 case OP_NOOP:
826 pdf_doc_add_page_content(" n", 2);
827 break;
828 case OP_GSAVE:
829 depth++;
830 break;
831 case OP_GRESTORE:
832 depth--;
833 break;
834 case OP_CURVETO1:
835 if (top < 3)
836 return -1;
837 p0.y = stack[top--];
838 p0.x = stack[top--];
839 pdf_dev_transform(&p0, &M);
840 p1.y = stack[top--];
841 p1.x = stack[top--];
842 pdf_dev_transform(&p1, &M);
843 pdf_dev_vcurveto(p1.x, p1.y, p0.x, p0.y);
844 break;
845 case OP_CURVETO2:
846 if (top < 3)
847 return -1;
848 p0.y = stack[top--];
849 p0.x = stack[top--];
850 pdf_dev_transform(&p0, &M);
851 p1.y = stack[top--];
852 p1.x = stack[top--];
853 pdf_dev_transform(&p1, &M);
854 pdf_dev_ycurveto(p1.x, p1.y, p0.x, p0.y);
855 break;
856 default:
857 return -1;
858 }
859 }
860 }
861 free(save_path);
862
863 pdf_release_obj(contents);
864 pdf_close(pf);
865
866 return 0;
867 }
868
869 #if 0
870 #define WBUF_SIZE 4096
871 #if HAVE_ZLIB
872 static int
873 add_stream_flate (pdf_obj *dst, const void *data, long len)
874 {
875 z_stream z;
876 Bytef wbuf[WBUF_SIZE];
877
878 z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = Z_NULL;
879
880 z.next_in = (z_const Bytef *) data; z.avail_in = len;
881 z.next_out = (Bytef *) wbuf; z.avail_out = WBUF_SIZE;
882
883 if (inflateInit(&z) != Z_OK) {
884 WARN("inflateInit() failed.");
885 return -1;
886 }
887
888 for (;;) {
889 int status;
890 status = inflate(&z, Z_NO_FLUSH);
891 if (status == Z_STREAM_END)
892 break;
893 else if (status != Z_OK) {
894 WARN("inflate() failed. Broken PDF file?");
895 inflateEnd(&z);
896 return -1;
897 }
898
899 if (z.avail_out == 0) {
900 pdf_add_stream(dst, wbuf, WBUF_SIZE);
901 z.next_out = wbuf;
902 z.avail_out = WBUF_SIZE;
903 }
904 }
905
906 if (WBUF_SIZE - z.avail_out > 0)
907 pdf_add_stream(dst, wbuf, WBUF_SIZE - z.avail_out);
908
909 return (inflateEnd(&z) == Z_OK ? 0 : -1);
910 }
911 #endif
912
913 static int
914 concat_stream (pdf_obj *dst, pdf_obj *src)
915 {
916 const char *stream_data;
917 long stream_length;
918 pdf_obj *stream_dict;
919 pdf_obj *filter;
920
921 if (!PDF_OBJ_STREAMTYPE(dst) || !PDF_OBJ_STREAMTYPE(src))
922 ERROR("Invalid type.");
923
924 stream_data = pdf_stream_dataptr(src);
925 stream_length = pdf_stream_length (src);
926 stream_dict = pdf_stream_dict (src);
927
928 if (pdf_lookup_dict(stream_dict, "DecodeParms")) {
929 WARN("DecodeParams not supported.");
930 return -1;
931 }
932
933 filter = pdf_lookup_dict(stream_dict, "Filter");
934 if (!filter) {
935 pdf_add_stream(dst, stream_data, stream_length);
936 return 0;
937 #if HAVE_ZLIB
938 } else {
939 char *filter_name;
940 if (PDF_OBJ_NAMETYPE(filter)) {
941 filter_name = pdf_name_value(filter);
942 if (filter_name && !strcmp(filter_name, "FlateDecode"))
943 return add_stream_flate(dst, stream_data, stream_length);
944 else {
945 WARN("DecodeFilter \"%s\" not supported.", filter_name);
946 return -1;
947 }
948 } else if (PDF_OBJ_ARRAYTYPE(filter)) {
949 if (pdf_array_length(filter) > 1) {
950 WARN("Multiple DecodeFilter not supported.");
951 return -1;
952 } else {
953 filter_name = pdf_name_value(pdf_get_array(filter, 0));
954 if (filter_name && !strcmp(filter_name, "FlateDecode"))
955 return add_stream_flate(dst, stream_data, stream_length);
956 else {
957 WARN("DecodeFilter \"%s\" not supported.", filter_name);
958 return -1;
959 }
960 }
961 } else
962 ERROR("Broken PDF file?");
963 #endif /* HAVE_ZLIB */
964 }
965
966 return -1;
967 }
968 #endif