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