1 /*
2   Copyright (C) 2001 artofcode LLC.
3 
4   This program is free software; you can redistribute it and/or modify it
5   under the terms of the GNU General Public License as published by the
6   Free Software Foundation; either version 2 of the License, or (at your
7   option) any later version.
8 
9   This program is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   General Public License for more details.
13 
14   You should have received a copy of the GNU General Public License along
15   with this program; if not, write to the Free Software Foundation, Inc.,
16   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
17 
18 
19   Author: Raph Levien <raph@artofcode.com>
20 */
21 /*$Id: gdevp14.c,v 1.4.2.3.2.1 2003/01/17 00:49:01 giles Exp $ */
22 /* Device filter implementing PDF 1.4 imaging model */
23 
24 #include "math_.h"
25 #include "memory_.h"
26 #include "gx.h"
27 #include "gserrors.h"
28 #include "gscdefs.h"
29 #include "gxdevice.h"
30 #include "gsdevice.h"
31 #include "gsstruct.h"
32 #include "gxistate.h"
33 #include "gxdcolor.h"
34 #include "gxiparam.h"
35 #include "gstparam.h"
36 #include "gxblend.h"
37 #include "gxtext.h"
38 #include "gsdfilt.h"
39 #include "gsimage.h"
40 #include "gzstate.h"
41 #include "gdevp14.h"
42 
43 # define INCR(v) DO_NOTHING
44 
45 /* Buffer stack data structure */
46 
47 #define PDF14_MAX_PLANES 16
48 
49 typedef struct pdf14_buf_s pdf14_buf;
50 typedef struct pdf14_ctx_s pdf14_ctx;
51 
52 struct pdf14_buf_s {
53     pdf14_buf *saved;
54 
55     bool isolated;
56     bool knockout;
57     byte alpha;
58     byte shape;
59     gs_blend_mode_t blend_mode;
60 
61     bool has_alpha_g;
62     bool has_shape;
63 
64     gs_int_rect rect;
65     /* Note: the traditional GS name for rowstride is "raster" */
66 
67     /* Data is stored in planar format. Order of planes is: pixel values,
68        alpha, shape if present, alpha_g if present. */
69 
70     int rowstride;
71     int planestride;
72     int n_chan; /* number of pixel planes including alpha */
73     int n_planes; /* total number of planes including alpha, shape, alpha_g */
74     byte *data;
75 };
76 
77 struct pdf14_ctx_s {
78     pdf14_buf *stack;
79     gs_memory_t *memory;
80     gs_int_rect rect;
81     int n_chan;
82 };
83 
84 /* GC procedures for buffer stack */
85 
86 private
87 ENUM_PTRS_WITH(pdf14_buf_enum_ptrs, pdf14_buf *buf)
88     return 0;
89     case 0: return ENUM_OBJ(buf->saved);
90     case 1: return ENUM_OBJ(buf->data);
91 ENUM_PTRS_END
92 
93 private
RELOC_PTRS_WITH(pdf14_buf_reloc_ptrs,pdf14_buf * buf)94 RELOC_PTRS_WITH(pdf14_buf_reloc_ptrs, pdf14_buf *buf)
95 {
96     RELOC_VAR(buf->saved);
97     RELOC_VAR(buf->data);
98 }
99 RELOC_PTRS_END
100 
101 gs_private_st_composite(st_pdf14_buf, pdf14_buf, "pdf14_buf",
102 			pdf14_buf_enum_ptrs, pdf14_buf_reloc_ptrs);
103 
104 gs_private_st_ptrs1(st_pdf14_ctx, pdf14_ctx, "pdf14_ctx",
105 		    pdf14_ctx_enum_ptrs, pdf14_ctx_reloc_ptrs,
106 		    stack);
107 
108 /* ------ The device descriptors ------ */
109 
110 /*
111  * Default X and Y resolution.
112  */
113 #define X_DPI 72
114 #define Y_DPI 72
115 
116 private int pdf14_open(gx_device * pdev);
117 private dev_proc_close_device(pdf14_close);
118 private int pdf14_output_page(gx_device * pdev, int num_copies, int flush);
119 private dev_proc_fill_rectangle(pdf14_fill_rectangle);
120 private dev_proc_fill_path(pdf14_fill_path);
121 private dev_proc_stroke_path(pdf14_stroke_path);
122 private dev_proc_begin_typed_image(pdf14_begin_typed_image);
123 private dev_proc_text_begin(pdf14_text_begin);
124 private dev_proc_begin_transparency_group(pdf14_begin_transparency_group);
125 private dev_proc_end_transparency_group(pdf14_end_transparency_group);
126 
127 #define XSIZE (int)(8.5 * X_DPI)	/* 8.5 x 11 inch page, by default */
128 #define YSIZE (int)(11 * Y_DPI)
129 
130 /* 24-bit color. */
131 
132 private const gx_device_procs pdf14_procs =
133 {
134 	pdf14_open,			/* open */
135 	NULL,	/* get_initial_matrix */
136 	NULL,	/* sync_output */
137 	pdf14_output_page,		/* output_page */
138 	pdf14_close,			/* close */
139 	gx_default_rgb_map_rgb_color,
140 	gx_default_rgb_map_color_rgb,
141 	pdf14_fill_rectangle,	/* fill_rectangle */
142 	NULL,	/* tile_rectangle */
143 	NULL,	/* copy_mono */
144 	NULL,	/* copy_color */
145 	NULL,	/* draw_line */
146 	NULL,	/* get_bits */
147 	NULL,   /* get_params */
148 	NULL,   /* put_params */
149 	NULL,	/* map_cmyk_color */
150 	NULL,	/* get_xfont_procs */
151 	NULL,	/* get_xfont_device */
152 	NULL,	/* map_rgb_alpha_color */
153 #if 0
154 	gx_page_device_get_page_device,	/* get_page_device */
155 #else
156 	NULL,   /* get_page_device */
157 #endif
158 	NULL,	/* get_alpha_bits */
159 	NULL,	/* copy_alpha */
160 	NULL,	/* get_band */
161 	NULL,	/* copy_rop */
162 	pdf14_fill_path,		/* fill_path */
163 	pdf14_stroke_path,	/* stroke_path */
164 	NULL,	/* fill_mask */
165 	NULL,	/* fill_trapezoid */
166 	NULL,	/* fill_parallelogram */
167 	NULL,	/* fill_triangle */
168 	NULL,	/* draw_thin_line */
169 	NULL,	/* begin_image */
170 	NULL,	/* image_data */
171 	NULL,	/* end_image */
172 	NULL,	/* strip_tile_rectangle */
173 	NULL,	/* strip_copy_rop, */
174 	NULL,	/* get_clipping_box */
175 	pdf14_begin_typed_image,	/* begin_typed_image */
176 	NULL,	/* get_bits_rectangle */
177 	NULL,	/* map_color_rgb_alpha */
178 	NULL,	/* create_compositor */
179 	NULL,	/* get_hardware_params */
180 	pdf14_text_begin,	/* text_begin */
181 	NULL,	/* finish_copydevice */
182 	pdf14_begin_transparency_group,
183 	pdf14_end_transparency_group
184 };
185 
186 typedef struct pdf14_device_s {
187     gx_device_common;
188 
189     pdf14_ctx *ctx;
190     gx_device *target;
191 
192 } pdf14_device;
193 
194 gs_private_st_composite_use_final(st_pdf14_device, pdf14_device, "pdf14_device",
195 				  pdf14_device_enum_ptrs, pdf14_device_reloc_ptrs,
196 			  gx_device_finalize);
197 
198 const pdf14_device gs_pdf14_device = {
199     std_device_color_stype_body(pdf14_device, &pdf14_procs, "pdf14",
200 				&st_pdf14_device,
201 				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 0),
202     { 0 }
203 };
204 
205 /* GC procedures */
206 private
207 ENUM_PTRS_WITH(pdf14_device_enum_ptrs, pdf14_device *pdev) return 0;
208 case 0: return ENUM_OBJ(pdev->ctx);
209 case 1: ENUM_RETURN(gx_device_enum_ptr(pdev->target));
210 ENUM_PTRS_END
RELOC_PTRS_WITH(pdf14_device_reloc_ptrs,pdf14_device * pdev)211 private RELOC_PTRS_WITH(pdf14_device_reloc_ptrs, pdf14_device *pdev)
212 {
213     RELOC_VAR(pdev->ctx);
214     pdev->target = gx_device_reloc_ptr(pdev->target, gcst);
215 }
216 RELOC_PTRS_END
217 
218 /* ------ The device descriptors for the marking device ------ */
219 
220 private dev_proc_fill_rectangle(pdf14_mark_fill_rectangle);
221 private dev_proc_fill_rectangle(pdf14_mark_fill_rectangle_ko_simple);
222 
223 private const gx_device_procs pdf14_mark_procs =
224 {
225 	NULL,	/* open */
226 	NULL,	/* get_initial_matrix */
227 	NULL,	/* sync_output */
228 	NULL,	/* output_page */
229 	NULL,	/* close */
230 	gx_default_rgb_map_rgb_color,
231 	gx_default_rgb_map_color_rgb,
232 	NULL,	/* fill_rectangle */
233 	NULL,	/* tile_rectangle */
234 	NULL,	/* copy_mono */
235 	NULL,	/* copy_color */
236 	NULL,	/* draw_line */
237 	NULL,	/* get_bits */
238 	NULL,   /* get_params */
239 	NULL,   /* put_params */
240 	NULL,	/* map_cmyk_color */
241 	NULL,	/* get_xfont_procs */
242 	NULL,	/* get_xfont_device */
243 	NULL,	/* map_rgb_alpha_color */
244 #if 0
245 	gx_page_device_get_page_device,	/* get_page_device */
246 #else
247 	NULL,   /* get_page_device */
248 #endif
249 	NULL,	/* get_alpha_bits */
250 	NULL,	/* copy_alpha */
251 	NULL,	/* get_band */
252 	NULL,	/* copy_rop */
253 	NULL,	/* fill_path */
254 	NULL,	/* stroke_path */
255 	NULL,	/* fill_mask */
256 	NULL,	/* fill_trapezoid */
257 	NULL,	/* fill_parallelogram */
258 	NULL,	/* fill_triangle */
259 	NULL,	/* draw_thin_line */
260 	NULL,	/* begin_image */
261 	NULL,	/* image_data */
262 	NULL,	/* end_image */
263 	NULL,	/* strip_tile_rectangle */
264 	NULL,	/* strip_copy_rop, */
265 	NULL,	/* get_clipping_box */
266 	NULL,	/* begin_typed_image */
267 	NULL,	/* get_bits_rectangle */
268 	NULL,	/* map_color_rgb_alpha */
269 	NULL,	/* create_compositor */
270 	NULL,	/* get_hardware_params */
271 	NULL,	/* text_begin */
272 	NULL	/* finish_copydevice */
273 };
274 
275 typedef struct pdf14_mark_device_s {
276     gx_device_common;
277 
278     pdf14_device *pdf14_dev;
279     float opacity;
280     float shape;
281     float alpha; /* alpha = opacity * shape */
282     gs_blend_mode_t blend_mode;
283 } pdf14_mark_device;
284 
285 gs_private_st_simple_final(st_pdf14_mark_device, pdf14_mark_device,
286 			   "pdf14_mark_device", gx_device_finalize);
287 
288 const pdf14_mark_device gs_pdf14_mark_device = {
289     std_device_color_stype_body(pdf14_mark_device, &pdf14_mark_procs,
290 				"pdf14_mark",
291 				&st_pdf14_mark_device,
292 				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 0),
293     { 0 }
294 };
295 
296 typedef struct pdf14_text_enum_s {
297     gs_text_enum_common;
298     gs_text_enum_t *target_enum;
299 } pdf14_text_enum_t;
300 extern_st(st_gs_text_enum);
301 gs_private_st_suffix_add1(st_pdf14_text_enum, pdf14_text_enum_t,
302 			  "pdf14_text_enum_t", pdf14_text_enum_enum_ptrs,
303 			  pdf14_text_enum_reloc_ptrs, st_gs_text_enum,
304 			  target_enum);
305 
306 /* ------ Private definitions ------ */
307 
308 /**
309  * pdf14_buf_new: Allocate a new PDF 1.4 buffer.
310  * @n_chan: Number of pixel channels including alpha.
311  *
312  * Return value: Newly allocated buffer, or NULL on failure.
313  **/
314 private pdf14_buf *
pdf14_buf_new(gs_int_rect * rect,bool has_alpha_g,bool has_shape,int n_chan,gs_memory_t * memory)315 pdf14_buf_new(gs_int_rect *rect, bool has_alpha_g, bool has_shape,
316 	       int n_chan,
317 	       gs_memory_t *memory)
318 {
319     pdf14_buf *result;
320     int rowstride = (rect->q.x - rect->p.x + 3) & -4;
321     int planestride = rowstride * (rect->q.y - rect->p.y);
322     int n_planes = n_chan + (has_shape ? 1 : 0) + (has_alpha_g ? 1 : 0);
323 
324     result = gs_alloc_struct(memory, pdf14_buf, &st_pdf14_buf,
325 			     "pdf14_buf_new");
326     if (result == NULL)
327 	return result;
328 
329     result->isolated = false;
330     result->knockout = false;
331     result->has_alpha_g = has_alpha_g;
332     result->has_shape = has_shape;
333     result->rect = *rect;
334     result->n_chan = n_chan;
335     result->n_planes = n_planes;
336     result->rowstride = rowstride;
337     result->planestride = planestride;
338     result->data = gs_alloc_bytes(memory, planestride * n_planes,
339 				  "pdf14_buf_new");
340     if (result->data == NULL) {
341 	gs_free_object(memory, result, "pdf_buf_new");
342 	return NULL;
343     }
344     if (has_alpha_g) {
345 	int alpha_g_plane = n_chan + (has_shape ? 1 : 0);
346 	memset (result->data + alpha_g_plane * planestride, 0, planestride);
347     }
348     return result;
349 }
350 
351 private void
pdf14_buf_free(pdf14_buf * buf,gs_memory_t * memory)352 pdf14_buf_free(pdf14_buf *buf, gs_memory_t *memory)
353 {
354     gs_free_object(memory, buf->data, "pdf14_buf_free");
355     gs_free_object(memory, buf, "pdf14_buf_free");
356 }
357 
358 private pdf14_ctx *
pdf14_ctx_new(gs_int_rect * rect,int n_chan,gs_memory_t * memory)359 pdf14_ctx_new(gs_int_rect *rect, int n_chan, gs_memory_t *memory)
360 {
361     pdf14_ctx *result;
362     pdf14_buf *buf;
363 
364     result = gs_alloc_struct(memory, pdf14_ctx, &st_pdf14_ctx,
365 			     "pdf14_ctx_new");
366     if (result == NULL)
367 	return result;
368 
369     buf = pdf14_buf_new(rect, false, false, n_chan, memory);
370     if (buf == NULL) {
371 	gs_free_object(memory, result, "pdf14_ctx_new");
372 	return NULL;
373     }
374     if_debug3('v', "[v]base buf: %d x %d, %d channels\n",
375 	      buf->rect.q.x, buf->rect.q.y, buf->n_chan);
376     memset(buf->data, 0, buf->planestride * buf->n_planes);
377     buf->saved = NULL;
378     result->stack = buf;
379     result->n_chan = n_chan;
380     result->memory = memory;
381     result->rect = *rect;
382     return result;
383 }
384 
385 private void
pdf14_ctx_free(pdf14_ctx * ctx)386 pdf14_ctx_free(pdf14_ctx *ctx)
387 {
388     pdf14_buf *buf, *next;
389 
390     for (buf = ctx->stack; buf != NULL; buf = next) {
391 	next = buf->saved;
392 	pdf14_buf_free(buf, ctx->memory);
393     }
394     gs_free_object (ctx->memory, ctx, "pdf14_ctx_free");
395 }
396 
397 /**
398  * pdf14_find_backdrop_buf: Find backdrop buffer.
399  *
400  * Return value: Backdrop buffer for current group operation, or NULL
401  * if backdrop is fully transparent.
402  **/
403 private pdf14_buf *
pdf14_find_backdrop_buf(pdf14_ctx * ctx)404 pdf14_find_backdrop_buf(pdf14_ctx *ctx)
405 {
406     pdf14_buf *buf = ctx->stack;
407 
408     while (buf != NULL) {
409 	if (buf->isolated) return NULL;
410 	if (!buf->knockout) return buf->saved;
411 	buf = buf->saved;
412     }
413     /* this really shouldn't happen, as bottom-most buf should be
414        non-knockout */
415     return NULL;
416 }
417 
418 private int
pdf14_push_transparency_group(pdf14_ctx * ctx,gs_int_rect * rect,bool isolated,bool knockout,byte alpha,byte shape,gs_blend_mode_t blend_mode)419 pdf14_push_transparency_group(pdf14_ctx *ctx, gs_int_rect *rect,
420 			      bool isolated, bool knockout,
421 			      byte alpha, byte shape,
422 			      gs_blend_mode_t blend_mode)
423 {
424     pdf14_buf *tos = ctx->stack;
425     pdf14_buf *buf, *backdrop;
426     bool has_shape;
427 
428     /* todo: fix this hack, which makes all knockout groups isolated.
429        For the vast majority of files, there won't be any visible
430        effects, but it still isn't correct. The pixel compositing code
431        for non-isolated knockout groups gets pretty hairy, which is
432        why this is here. */
433     if (knockout) isolated = true;
434 
435     has_shape = tos->has_shape || tos->knockout;
436 
437     buf = pdf14_buf_new(rect, !isolated, has_shape, ctx->n_chan, ctx->memory);
438     if_debug3('v', "[v]push buf: %d x %d, %d channels\n", buf->rect.p.x, buf->rect.p.y, buf->n_chan);
439     if (buf == NULL)
440 	return_error(gs_error_VMerror);
441     buf->isolated = isolated;
442     buf->knockout = knockout;
443     buf->alpha = alpha;
444     buf->shape = shape;
445     buf->blend_mode = blend_mode;
446 
447     buf->saved = tos;
448     ctx->stack = buf;
449 
450     backdrop = pdf14_find_backdrop_buf(ctx);
451     if (backdrop == NULL) {
452 	memset(buf->data, 0, buf->planestride * (buf->n_chan +
453 						 (buf->has_shape ? 1 : 0)));
454     } else {
455 	/* make copy of backdrop for compositing */
456 	byte *buf_plane = buf->data;
457 	byte *tos_plane = tos->data + buf->rect.p.x - tos->rect.p.x +
458 	    (buf->rect.p.y - tos->rect.p.y) * tos->rowstride;
459 	int width = buf->rect.q.x - buf->rect.p.x;
460 	int y0 = buf->rect.p.y;
461 	int y1 = buf->rect.q.y;
462 	int i;
463 	int n_chan_copy = buf->n_chan + (tos->has_shape ? 1 : 0);
464 
465 	for (i = 0; i < n_chan_copy; i++) {
466 	    byte *buf_ptr = buf_plane;
467 	    byte *tos_ptr = tos_plane;
468 	    int y;
469 
470 	    for (y = y0; y < y1; ++y) {
471 		memcpy (buf_ptr, tos_ptr, width);
472 		buf_ptr += buf->rowstride;
473 		tos_ptr += tos->rowstride;
474 	    }
475 	    buf_plane += buf->planestride;
476 	    tos_plane += tos->planestride;
477 	}
478 	if (has_shape && !tos->has_shape)
479 	    memset (buf_plane, 0, buf->planestride);
480     }
481 
482     return 0;
483 }
484 
485 private int
pdf14_pop_transparency_group(pdf14_ctx * ctx)486 pdf14_pop_transparency_group(pdf14_ctx *ctx)
487 {
488     pdf14_buf *tos = ctx->stack;
489     pdf14_buf *nos = tos->saved;
490     int n_chan = ctx->n_chan;
491     byte alpha = tos->alpha;
492     byte shape = tos->shape;
493     byte blend_mode = tos->blend_mode;
494     int x0 = tos->rect.p.x;
495     int y0 = tos->rect.p.y;
496     int x1 = tos->rect.q.x;
497     int y1 = tos->rect.q.y;
498     byte *tos_ptr = tos->data;
499     byte *nos_ptr = nos->data + x0 - nos->rect.p.x +
500 	(y0 - nos->rect.p.y) * nos->rowstride;
501     int tos_planestride = tos->planestride;
502     int nos_planestride = nos->planestride;
503     int width = x1 - x0;
504     int x, y;
505     int i;
506     byte tos_pixel[PDF14_MAX_PLANES];
507     byte nos_pixel[PDF14_MAX_PLANES];
508     bool tos_isolated = tos->isolated;
509     bool nos_knockout = nos->knockout;
510     byte *nos_alpha_g_ptr;
511     int tos_shape_offset = n_chan * tos_planestride;
512     int tos_alpha_g_offset = tos_shape_offset +
513 	(tos->has_shape ? tos_planestride : 0);
514     int nos_shape_offset = n_chan * nos_planestride;
515     bool nos_has_shape = nos->has_shape;
516 
517     if (nos == NULL)
518 	return_error(gs_error_rangecheck);
519 
520     /* for now, only simple non-knockout */
521 
522     if (nos->has_alpha_g)
523 	nos_alpha_g_ptr = nos_ptr + n_chan * nos_planestride;
524     else
525 	nos_alpha_g_ptr = NULL;
526 
527     for (y = y0; y < y1; ++y) {
528 	for (x = 0; x < width; ++x) {
529 	    for (i = 0; i < n_chan; ++i) {
530 		tos_pixel[i] = tos_ptr[x + i * tos_planestride];
531 		nos_pixel[i] = nos_ptr[x + i * nos_planestride];
532 	    }
533 
534 	    if (nos_knockout) {
535 		byte *nos_shape_ptr = nos_has_shape ?
536 		    &nos_ptr[x + nos_shape_offset] : NULL;
537 		byte tos_shape = tos_ptr[x + tos_shape_offset];
538 
539 #if 1
540 		art_pdf_composite_knockout_isolated_8 (nos_pixel,
541 						       nos_shape_ptr,
542 						       tos_pixel,
543 						       n_chan - 1,
544 						       tos_shape,
545 						       alpha, shape);
546 #else
547 		tos_pixel[3] = tos_ptr[x + tos_shape_offset];
548 		art_pdf_composite_group_8(nos_pixel, nos_alpha_g_ptr,
549 					  tos_pixel,
550 					  n_chan - 1,
551 					  alpha, blend_mode);
552 #endif
553 	    } else if (tos_isolated) {
554 		art_pdf_composite_group_8(nos_pixel, nos_alpha_g_ptr,
555 					  tos_pixel,
556 					  n_chan - 1,
557 					  alpha, blend_mode);
558 	    } else {
559 		byte tos_alpha_g = tos_ptr[x + tos_alpha_g_offset];
560 		art_pdf_recomposite_group_8(nos_pixel, nos_alpha_g_ptr,
561 					    tos_pixel, tos_alpha_g,
562 					    n_chan - 1,
563 					    alpha, blend_mode);
564 	    }
565 	    if (nos_has_shape) {
566 		nos_ptr[x + nos_shape_offset] =
567 		    art_pdf_union_mul_8 (nos_ptr[x + nos_shape_offset],
568 					 tos_ptr[x + tos_shape_offset],
569 					 shape);
570 	    }
571 	    /* todo: knockout cases */
572 
573 	    for (i = 0; i < n_chan; ++i) {
574 		nos_ptr[x + i * nos_planestride] = nos_pixel[i];
575 	    }
576 	    if (nos_alpha_g_ptr != NULL)
577 		++nos_alpha_g_ptr;
578 	}
579 	tos_ptr += tos->rowstride;
580 	nos_ptr += nos->rowstride;
581 	if (nos_alpha_g_ptr != NULL)
582 	    nos_alpha_g_ptr += nos->rowstride - width;
583     }
584 
585     ctx->stack = nos;
586     if_debug0('v', "[v]pop buf\n");
587     pdf14_buf_free(tos, ctx->memory);
588     return 0;
589 }
590 
591 private int
pdf14_open(gx_device * dev)592 pdf14_open(gx_device *dev)
593 {
594     pdf14_device *pdev = (pdf14_device *)dev;
595     gs_int_rect rect;
596 
597     if_debug2('v', "[v]pdf14_open: width = %d, height = %d\n",
598 	     dev->width, dev->height);
599 
600     rect.p.x = 0;
601     rect.p.y = 0;
602     rect.q.x = dev->width;
603     rect.q.y = dev->height;
604     pdev->ctx = pdf14_ctx_new(&rect, 4, dev->memory);
605     if (pdev->ctx == NULL)
606 	return_error(gs_error_VMerror);
607 
608     return 0;
609 }
610 
611 /**
612  * pdf14_put_image: Put rendered image to target device.
613  * @pdev: The PDF 1.4 rendering device.
614  * @pgs: State for image draw operation.
615  * @target: The target device.
616  *
617  * Puts the rendered image in @pdev's buffer to @target. This is called
618  * as part of the sequence of popping the PDF 1.4 device filter.
619  *
620  * Return code: negative on error.
621  **/
622 private int
pdf14_put_image(pdf14_device * pdev,gs_state * pgs,gx_device * target)623 pdf14_put_image(pdf14_device *pdev, gs_state *pgs, gx_device *target)
624 {
625     int code;
626     gs_image1_t image;
627     gs_matrix pmat;
628     gx_image_enum_common_t *info;
629     int width = pdev->width;
630     int height = pdev->height;
631     gs_imager_state *pis = (gs_imager_state *)pgs;
632     int y;
633     pdf14_buf *buf = pdev->ctx->stack;
634 
635     int planestride = buf->planestride;
636     byte *buf_ptr = buf->data;
637     byte *linebuf;
638 
639 #ifdef TEST_CODE
640     code = dev_proc(target, fill_rectangle) (target, 10, 10, 100, 100, 0);
641 #endif
642 
643     gx_set_dev_color(pgs);
644     gs_image_t_init_adjust(&image, pis->shared->device_color_spaces.named.RGB,
645 			   false);
646     image.ImageMatrix.xx = (float)width;
647     image.ImageMatrix.yy = (float)height;
648     image.Width = width;
649     image.Height = height;
650     image.BitsPerComponent = 8;
651     pmat.xx = (float)width;
652     pmat.xy = 0;
653     pmat.yx = 0;
654     pmat.yy = (float)height;
655     pmat.tx = 0;
656     pmat.ty = 0;
657     code = dev_proc(target, begin_typed_image) (target,
658 						pis, &pmat,
659 						(gs_image_common_t *)&image,
660 						NULL, NULL, NULL,
661 						pgs->memory, &info);
662     if (code < 0)
663 	return code;
664 
665     linebuf = gs_alloc_bytes(pdev->memory, width * 3, "pdf14_put_image");
666     for (y = 0; y < height; y++) {
667 	gx_image_plane_t planes;
668 	int x;
669 	int rows_used;
670 
671 	for (x = 0; x < width; x++) {
672 	    const byte bg_r = 0xff, bg_g = 0xff, bg_b = 0xff;
673 	    byte r, g, b, a;
674 	    int tmp;
675 
676 	    /* composite RGBA pixel with over solid background */
677 	    a = buf_ptr[x + planestride * 3];
678 
679 	    if ((a + 1) & 0xfe) {
680 		r = buf_ptr[x];
681 		g = buf_ptr[x + planestride];
682 		b = buf_ptr[x + planestride * 2];
683 		a ^= 0xff;
684 
685 		tmp = ((bg_r - r) * a) + 0x80;
686 		r += (tmp + (tmp >> 8)) >> 8;
687 		linebuf[x * 3] = r;
688 
689 		tmp = ((bg_g - g) * a) + 0x80;
690 		g += (tmp + (tmp >> 8)) >> 8;
691 		linebuf[x * 3 + 1] = g;
692 
693 		tmp = ((bg_b - b) * a) + 0x80;
694 		b += (tmp + (tmp >> 8)) >> 8;
695 		linebuf[x * 3 + 2] = b;
696 	    } else if (a == 0) {
697 		linebuf[x * 3] = bg_r;
698 		linebuf[x * 3 + 1] = bg_g;
699 		linebuf[x * 3 + 2] = bg_b;
700 	    } else {
701 		r = buf_ptr[x];
702 		g = buf_ptr[x + planestride];
703 		b = buf_ptr[x + planestride * 2];
704 		linebuf[x * 3 + 0] = r;
705 		linebuf[x * 3 + 1] = g;
706 		linebuf[x * 3 + 2] = b;
707 	    }
708 	}
709 
710 	planes.data = linebuf;
711 	planes.data_x = 0;
712 	planes.raster = width * 3;
713 	info->procs->plane_data(info, &planes, 1, &rows_used);
714 	/* todo: check return value */
715 
716 	buf_ptr += buf->rowstride;
717     }
718     gs_free_object(pdev->memory, linebuf, "pdf14_put_image");
719 
720     info->procs->end_image(info, true);
721     return code;
722 }
723 
724 private int
pdf14_close(gx_device * dev)725 pdf14_close(gx_device *dev)
726 {
727     pdf14_device *pdev = (pdf14_device *)dev;
728 
729     if (pdev->ctx)
730 	pdf14_ctx_free(pdev->ctx);
731     return 0;
732 }
733 
734 private int
pdf14_output_page(gx_device * dev,int num_copies,int flush)735 pdf14_output_page(gx_device *dev, int num_copies, int flush)
736 {
737     /* todo: actually forward page */
738 
739     /* Hmm, actually I think the filter should be popped before the
740        output_page operation. In that case, this should probably
741        rangecheck. */
742     return 0;
743 }
744 
745 private void
pdf14_finalize(gx_device * dev)746 pdf14_finalize(gx_device *dev)
747 {
748     if_debug1('v', "[v]finalizing %lx\n", dev);
749 }
750 
751 #define COPY_PARAM(p) dev->p = target->p
752 #define COPY_ARRAY_PARAM(p) memcpy(dev->p, target->p, sizeof(dev->p))
753 
754 /*
755  * Copy device parameters back from a target.  This copies all standard
756  * parameters related to page size and resolution, but not any of the
757  * color-related parameters, as the pdf14 device retains its own color
758  * handling. This routine is parallel to gx_device_copy_params().
759  */
760 private void
gs_pdf14_device_copy_params(gx_device * dev,const gx_device * target)761 gs_pdf14_device_copy_params(gx_device *dev, const gx_device *target)
762 {
763 	COPY_PARAM(width);
764 	COPY_PARAM(height);
765 	COPY_ARRAY_PARAM(MediaSize);
766 	COPY_ARRAY_PARAM(ImagingBBox);
767 	COPY_PARAM(ImagingBBox_set);
768 	COPY_ARRAY_PARAM(HWResolution);
769 	COPY_ARRAY_PARAM(MarginsHWResolution);
770 	COPY_ARRAY_PARAM(Margins);
771 	COPY_ARRAY_PARAM(HWMargins);
772 	COPY_PARAM(PageCount);
773 #undef COPY_ARRAY_PARAM
774 #undef COPY_PARAM
775 }
776 
777 /**
778  * pdf14_get_marking_device: Obtain a marking device.
779  * @dev: Original device.
780  * @pis: Imager state.
781  *
782  * The current implementation creates a marking device each time this
783  * routine is called. A potential optimization is to cache a single
784  * instance in the original device.
785  *
786  * Return value: Marking device, or NULL on error.
787  **/
788 private gx_device *
pdf14_get_marking_device(gx_device * dev,const gs_imager_state * pis)789 pdf14_get_marking_device(gx_device *dev, const gs_imager_state *pis)
790 {
791     pdf14_device *pdev = (pdf14_device *)dev;
792     pdf14_buf *buf = pdev->ctx->stack;
793     pdf14_mark_device *mdev;
794     int code = gs_copydevice((gx_device **)&mdev,
795 			     (const gx_device *)&gs_pdf14_mark_device,
796 			     dev->memory);
797 
798     if (code < 0)
799 	return NULL;
800 
801     gx_device_fill_in_procs((gx_device *)mdev);
802     mdev->pdf14_dev = pdev;
803     mdev->opacity = pis->opacity.alpha;
804     mdev->shape = pis->shape.alpha;
805     mdev->alpha = pis->opacity.alpha * pis->shape.alpha;
806     mdev->blend_mode = pis->blend_mode;
807 
808     if (buf->knockout) {
809 	fill_dev_proc((gx_device *)mdev, fill_rectangle,
810 		      pdf14_mark_fill_rectangle_ko_simple);
811     } else {
812 	fill_dev_proc((gx_device *)mdev, fill_rectangle,
813 		      pdf14_mark_fill_rectangle);
814     }
815 
816     if_debug1('v', "[v]creating %lx\n", mdev);
817     gs_pdf14_device_copy_params((gx_device *)mdev, dev);
818     mdev->finalize = pdf14_finalize;
819     return (gx_device *)mdev;
820 }
821 
822 private void
pdf14_release_marking_device(gx_device * marking_dev)823 pdf14_release_marking_device(gx_device *marking_dev)
824 {
825     rc_decrement_only(marking_dev, "pdf14_release_marking_device");
826 }
827 
828 private int
pdf14_fill_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)829 pdf14_fill_path(gx_device *dev, const gs_imager_state *pis,
830 			   gx_path *ppath, const gx_fill_params *params,
831 			   const gx_drawing_color *pdcolor,
832 			   const gx_clip_path *pcpath)
833 {
834     int code;
835     gx_device *mdev = pdf14_get_marking_device(dev, pis);
836     gs_imager_state new_is = *pis;
837 
838     if (mdev == 0)
839 	return_error(gs_error_VMerror);
840     new_is.log_op |= lop_pdf14;
841     code = gx_default_fill_path(mdev, &new_is, ppath, params, pdcolor, pcpath);
842     pdf14_release_marking_device(mdev);
843     return code;
844 }
845 
846 private int
pdf14_stroke_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_stroke_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)847 pdf14_stroke_path(gx_device *dev, const gs_imager_state *pis,
848 			     gx_path *ppath, const gx_stroke_params *params,
849 			     const gx_drawing_color *pdcolor,
850 			     const gx_clip_path *pcpath)
851 {
852     int code;
853     gx_device *mdev = pdf14_get_marking_device(dev, pis);
854     gs_imager_state new_is = *pis;
855 
856     if (mdev == 0)
857 	return_error(gs_error_VMerror);
858     new_is.log_op |= lop_pdf14;
859     code = gx_default_stroke_path(mdev, &new_is, ppath, params, pdcolor,
860 				  pcpath);
861     pdf14_release_marking_device(mdev);
862     return code;
863 }
864 
865 private int
pdf14_begin_typed_image(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * mem,gx_image_enum_common_t ** pinfo)866 pdf14_begin_typed_image(gx_device * dev, const gs_imager_state * pis,
867 			   const gs_matrix *pmat, const gs_image_common_t *pic,
868 			   const gs_int_rect * prect,
869 			   const gx_drawing_color * pdcolor,
870 			   const gx_clip_path * pcpath, gs_memory_t * mem,
871 			   gx_image_enum_common_t ** pinfo)
872 {
873     gx_device *mdev = pdf14_get_marking_device(dev, pis);
874     int code;
875 
876     if (mdev == 0)
877 	return_error(gs_error_VMerror);
878 
879     code = gx_default_begin_typed_image(mdev, pis, pmat, pic, prect, pdcolor,
880 					pcpath, mem, pinfo);
881 
882     /* We need to free the marking device on end of image. This probably
883        means implementing our own image enum, which primarily forwards
884        requests, but also frees the marking device on end_image. For
885        now, we'll just leak this - it will get cleaned up by the GC. */
886 #if 0
887     pdf14_release_marking_device(mdev);
888 #endif
889 
890     return code;
891 }
892 
893 private int
pdf14_text_resync(gs_text_enum_t * pte,const gs_text_enum_t * pfrom)894 pdf14_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
895 {
896     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
897 
898     if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY)
899 	return_error(gs_error_rangecheck);
900     if (penum->target_enum) {
901 	int code = gs_text_resync(penum->target_enum, pfrom);
902 
903 	if (code < 0)
904 	    return code;
905     }
906     pte->text = pfrom->text;
907     gs_text_enum_copy_dynamic(pte, pfrom, false);
908     return 0;
909 }
910 
911 private int
pdf14_text_process(gs_text_enum_t * pte)912 pdf14_text_process(gs_text_enum_t *pte)
913 {
914     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
915     int code;
916 
917     code = gs_text_process(penum->target_enum);
918     gs_text_enum_copy_dynamic(pte, penum->target_enum, true);
919     return code;
920 }
921 
922 private bool
pdf14_text_is_width_only(const gs_text_enum_t * pte)923 pdf14_text_is_width_only(const gs_text_enum_t *pte)
924 {
925     const pdf14_text_enum_t *const penum = (const pdf14_text_enum_t *)pte;
926 
927     if (penum->target_enum)
928 	return gs_text_is_width_only(penum->target_enum);
929     return false;
930 }
931 
932 private int
pdf14_text_current_width(const gs_text_enum_t * pte,gs_point * pwidth)933 pdf14_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
934 {
935     const pdf14_text_enum_t *const penum = (const pdf14_text_enum_t *)pte;
936 
937     if (penum->target_enum)
938 	return gs_text_current_width(penum->target_enum, pwidth);
939     return_error(gs_error_rangecheck); /* can't happen */
940 }
941 
942 private int
pdf14_text_set_cache(gs_text_enum_t * pte,const double * pw,gs_text_cache_control_t control)943 pdf14_text_set_cache(gs_text_enum_t *pte, const double *pw,
944 		   gs_text_cache_control_t control)
945 {
946     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
947 
948     if (penum->target_enum)
949 	return gs_text_set_cache(penum->target_enum, pw, control);
950     return_error(gs_error_rangecheck); /* can't happen */
951 }
952 
953 private int
pdf14_text_retry(gs_text_enum_t * pte)954 pdf14_text_retry(gs_text_enum_t *pte)
955 {
956     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
957 
958     if (penum->target_enum)
959 	return gs_text_retry(penum->target_enum);
960     return_error(gs_error_rangecheck); /* can't happen */
961 }
962 
963 private void
pdf14_text_release(gs_text_enum_t * pte,client_name_t cname)964 pdf14_text_release(gs_text_enum_t *pte, client_name_t cname)
965 {
966     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
967 
968     if (penum->target_enum) {
969 	gs_text_release(penum->target_enum, cname);
970 	penum->target_enum = 0;
971     }
972     gx_default_text_release(pte, cname);
973 }
974 
975 private const gs_text_enum_procs_t pdf14_text_procs = {
976     pdf14_text_resync, pdf14_text_process,
977     pdf14_text_is_width_only, pdf14_text_current_width,
978     pdf14_text_set_cache, pdf14_text_retry,
979     pdf14_text_release
980 };
981 
982 private int
pdf14_text_begin(gx_device * dev,gs_imager_state * pis,const gs_text_params_t * text,gs_font * font,gx_path * path,const gx_device_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * memory,gs_text_enum_t ** ppenum)983 pdf14_text_begin(gx_device * dev, gs_imager_state * pis,
984 		 const gs_text_params_t * text, gs_font * font,
985 		 gx_path * path, const gx_device_color * pdcolor,
986 		 const gx_clip_path * pcpath, gs_memory_t * memory,
987 		 gs_text_enum_t ** ppenum)
988 {
989     int code;
990     gx_device *mdev = pdf14_get_marking_device(dev, pis);
991     pdf14_text_enum_t *penum;
992     gs_text_enum_t *target_enum;
993 
994     if (mdev == 0)
995 	return_error(gs_error_VMerror);
996     if_debug0('v', "[v]pdf14_text_begin\n");
997     code = gx_default_text_begin(mdev, pis, text, font, path, pdcolor, pcpath,
998 				 memory, &target_enum);
999 
1000     rc_alloc_struct_1(penum, pdf14_text_enum_t, &st_pdf14_text_enum, memory,
1001 		      return_error(gs_error_VMerror), "pdf14_text_begin");
1002     penum->rc.free = rc_free_text_enum;
1003     penum->target_enum = target_enum;
1004     code = gs_text_enum_init((gs_text_enum_t *)penum, &pdf14_text_procs,
1005 			     dev, pis, text, font, path, pdcolor, pcpath,
1006 			     memory);
1007     if (code < 0) {
1008 	gs_free_object(memory, penum, "pdf14_text_begin");
1009 	return code;
1010     }
1011     *ppenum = (gs_text_enum_t *)penum;
1012     rc_decrement_only(mdev, "pdf14_text_begin");
1013     return code;
1014 }
1015 
1016 private int
pdf14_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)1017 pdf14_fill_rectangle(gx_device * dev,
1018 		    int x, int y, int w, int h, gx_color_index color)
1019 {
1020     if_debug4('v', "[v]pdf14_fill_rectangle, (%d, %d), %d x %d\n", x, y, w, h);
1021     return 0;
1022 }
1023 
1024 
1025 private int
pdf14_begin_transparency_group(gx_device * dev,const gs_transparency_group_params_t * ptgp,const gs_rect * pbbox,gs_imager_state * pis,gs_transparency_state_t ** ppts,gs_memory_t * mem)1026 pdf14_begin_transparency_group(gx_device *dev,
1027 			      const gs_transparency_group_params_t *ptgp,
1028 			      const gs_rect *pbbox,
1029 			      gs_imager_state *pis,
1030 			      gs_transparency_state_t **ppts,
1031 			      gs_memory_t *mem)
1032 {
1033     pdf14_device *pdev = (pdf14_device *)dev;
1034     double alpha = pis->opacity.alpha * pis->shape.alpha;
1035     int code;
1036 
1037     if_debug4('v', "[v]begin_transparency_group, I = %d, K = %d, alpha = %g, bm = %d\n",
1038 	      ptgp->Isolated, ptgp->Knockout, alpha, pis->blend_mode);
1039 
1040     code = pdf14_push_transparency_group(pdev->ctx, &pdev->ctx->rect,
1041 					 ptgp->Isolated, ptgp->Knockout,
1042 					 floor (255 * alpha + 0.5),
1043 					 floor (255 * pis->shape.alpha + 0.5),
1044 					 pis->blend_mode);
1045     return code;
1046 }
1047 
1048 private int
pdf14_end_transparency_group(gx_device * dev,gs_imager_state * pis,gs_transparency_state_t ** ppts)1049 pdf14_end_transparency_group(gx_device *dev,
1050 			      gs_imager_state *pis,
1051 			      gs_transparency_state_t **ppts)
1052 {
1053     pdf14_device *pdev = (pdf14_device *)dev;
1054     int code;
1055 
1056     if_debug0('v', "[v]end_transparency_group\n");
1057     code = pdf14_pop_transparency_group(pdev->ctx);
1058     return code;
1059 }
1060 
1061 private int
pdf14_mark_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)1062 pdf14_mark_fill_rectangle(gx_device * dev,
1063 			 int x, int y, int w, int h, gx_color_index color)
1064 {
1065     pdf14_mark_device *mdev = (pdf14_mark_device *)dev;
1066     pdf14_device *pdev = (pdf14_device *)mdev->pdf14_dev;
1067     pdf14_buf *buf = pdev->ctx->stack;
1068     int i, j, k;
1069     byte *line, *dst_ptr;
1070     byte src[PDF14_MAX_PLANES];
1071     byte dst[PDF14_MAX_PLANES];
1072     gs_blend_mode_t blend_mode = mdev->blend_mode;
1073     int rowstride = buf->rowstride;
1074     int planestride = buf->planestride;
1075     bool has_alpha_g = buf->has_alpha_g;
1076     bool has_shape = buf->has_shape;
1077     int shape_off = buf->n_chan * planestride;
1078     int alpha_g_off = shape_off + (has_shape ? planestride : 0);
1079     byte shape;
1080 
1081     src[0] = color >> 16;
1082     src[1] = (color >> 8) & 0xff;
1083     src[2] = color & 0xff;
1084     src[3] = (byte)floor (255 * mdev->alpha + 0.5);
1085     if (has_shape)
1086 	shape = (byte)floor (255 * mdev->shape + 0.5);
1087 
1088     if (x < buf->rect.p.x) x = buf->rect.p.x;
1089     if (y < buf->rect.p.x) y = buf->rect.p.y;
1090     if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
1091     if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
1092 
1093     line = buf->data + (x - buf->rect.p.x) + (y - buf->rect.p.y) * rowstride;
1094 
1095     for (j = 0; j < h; ++j) {
1096 	dst_ptr = line;
1097 	for (i = 0; i < w; ++i) {
1098 	    for (k = 0; k < 4; ++k)
1099 		dst[k] = dst_ptr[k * planestride];
1100 	    art_pdf_composite_pixel_alpha_8(dst, src, 3, blend_mode);
1101 	    for (k = 0; k < 4; ++k)
1102 		dst_ptr[k * planestride] = dst[k];
1103 	    if (has_alpha_g) {
1104 		int tmp = (255 - dst_ptr[alpha_g_off]) * (255 - src[3]) + 0x80;
1105 		dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
1106 	    }
1107 	    if (has_shape) {
1108 		int tmp = (255 - dst_ptr[shape_off]) * (255 - shape) + 0x80;
1109 		dst_ptr[shape_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
1110 	    }
1111 	    ++dst_ptr;
1112 	}
1113 	line += rowstride;
1114     }
1115     return 0;
1116 }
1117 
1118 private int
pdf14_mark_fill_rectangle_ko_simple(gx_device * dev,int x,int y,int w,int h,gx_color_index color)1119 pdf14_mark_fill_rectangle_ko_simple(gx_device * dev,
1120 				   int x, int y, int w, int h, gx_color_index color)
1121 {
1122     pdf14_mark_device *mdev = (pdf14_mark_device *)dev;
1123     pdf14_device *pdev = (pdf14_device *)mdev->pdf14_dev;
1124     pdf14_buf *buf = pdev->ctx->stack;
1125     int i, j, k;
1126     byte *line, *dst_ptr;
1127     byte src[PDF14_MAX_PLANES];
1128     byte dst[PDF14_MAX_PLANES];
1129     int rowstride = buf->rowstride;
1130     int planestride = buf->planestride;
1131     int shape_off = buf->n_chan * planestride;
1132     bool has_shape = buf->has_shape;
1133     byte opacity;
1134 
1135     src[0] = color >> 16;
1136     src[1] = (color >> 8) & 0xff;
1137     src[2] = color & 0xff;
1138     src[3] = (byte)floor (255 * mdev->shape + 0.5);
1139     opacity = (byte)floor (255 * mdev->opacity + 0.5);
1140 
1141     if (x < buf->rect.p.x) x = buf->rect.p.x;
1142     if (y < buf->rect.p.x) y = buf->rect.p.y;
1143     if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
1144     if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
1145 
1146     line = buf->data + (x - buf->rect.p.x) + (y - buf->rect.p.y) * rowstride;
1147 
1148     for (j = 0; j < h; ++j) {
1149 	dst_ptr = line;
1150 	for (i = 0; i < w; ++i) {
1151 	    for (k = 0; k < 4; ++k)
1152 		dst[k] = dst_ptr[k * planestride];
1153 	    art_pdf_composite_knockout_simple_8(dst, has_shape ? dst_ptr + shape_off : NULL,
1154 						src, 3, opacity);
1155 	    for (k = 0; k < 4; ++k)
1156 		dst_ptr[k * planestride] = dst[k];
1157 	    ++dst_ptr;
1158 	}
1159 	line += rowstride;
1160     }
1161     return 0;
1162 }
1163 
1164 private int
gs_pdf14_device_filter_push(gs_device_filter_t * self,gs_memory_t * mem,gx_device ** pdev,gx_device * target)1165 gs_pdf14_device_filter_push(gs_device_filter_t *self, gs_memory_t *mem,
1166 			   gx_device **pdev, gx_device *target)
1167 {
1168     pdf14_device *p14dev;
1169     int code;
1170 
1171     code = gs_copydevice((gx_device **) &p14dev,
1172 			 (const gx_device *) &gs_pdf14_device,
1173 			 mem);
1174     if (code < 0)
1175 	return code;
1176 
1177     gx_device_fill_in_procs((gx_device *)p14dev);
1178 
1179     gs_pdf14_device_copy_params((gx_device *)p14dev, target);
1180 
1181     rc_assign(p14dev->target, target, "gs_pdf14_device_filter_push");
1182 
1183     dev_proc((gx_device *) p14dev, open_device) ((gx_device *) p14dev);
1184     *pdev = (gx_device *) p14dev;
1185     return 0;
1186 }
1187 
1188 private int
gs_pdf14_device_filter_pop(gs_device_filter_t * self,gs_memory_t * mem,gs_state * pgs,gx_device * dev)1189 gs_pdf14_device_filter_pop(gs_device_filter_t *self, gs_memory_t *mem,
1190 			   gs_state *pgs, gx_device *dev)
1191 {
1192     gx_device *target = ((pdf14_device *)dev)->target;
1193     int code;
1194 
1195     code = pdf14_put_image((pdf14_device *)dev, pgs, target);
1196     if (code < 0)
1197 	return code;
1198 
1199     code = dev_proc(dev, close_device) (dev);
1200     if (code < 0)
1201 	return code;
1202 
1203     ((pdf14_device *)dev)->target = 0;
1204     rc_decrement_only(target, "gs_pdf14_device_filter_pop");
1205 
1206     gs_free_object(mem, self, "gs_pdf14_device_filter_pop");
1207     return 0;
1208 }
1209 
1210 int
gs_pdf14_device_filter(gs_device_filter_t ** pdf,int depth,gs_memory_t * mem)1211 gs_pdf14_device_filter(gs_device_filter_t **pdf, int depth, gs_memory_t *mem)
1212 {
1213     gs_device_filter_t *df;
1214 
1215     df = gs_alloc_struct(mem, gs_device_filter_t,
1216 			 &st_gs_device_filter, "gs_pdf14_device_filter");
1217     if (df == 0)
1218 	return_error(gs_error_VMerror);
1219     df->push = gs_pdf14_device_filter_push;
1220     df->pop = gs_pdf14_device_filter_pop;
1221     *pdf = df;
1222     return 0;
1223 }
1224