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