xref: /plan9/sys/src/cmd/gs/src/gdevpx.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gdevpx.c,v 1.16 2005/07/07 16:44:17 stefan Exp $ */
18 /* H-P PCL XL driver */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "string_.h"
22 #include "gx.h"
23 #include "gserrors.h"
24 #include "gsccolor.h"
25 #include "gsdcolor.h"
26 #include "gxcspace.h"		/* for color mapping for images */
27 #include "gxdevice.h"
28 #include "gxpath.h"
29 #include "gdevvec.h"
30 #include "strimpl.h"
31 #include "srlx.h"
32 #include "gdevpxat.h"
33 #include "gdevpxen.h"
34 #include "gdevpxop.h"
35 #include "gdevpxut.h"
36 
37 /* ---------------- Device definition ---------------- */
38 
39 /* Define the default resolution. */
40 #ifndef X_DPI
41 #  define X_DPI 600
42 #endif
43 #ifndef Y_DPI
44 #  define Y_DPI 600
45 #endif
46 
47 /* Structure definition */
48 #define NUM_POINTS 40		/* must be >= 3 and <= 255 */
49 typedef enum {
50     POINTS_NONE,
51     POINTS_LINES,
52     POINTS_CURVES
53 } point_type_t;
54 typedef struct gx_device_pclxl_s {
55     gx_device_vector_common;
56     /* Additional state information */
57     pxeMediaSize_t media_size;
58     bool ManualFeed;            /* map ps setpage commands to pxl */
59     bool ManualFeed_set;
60     int  MediaPosition;
61     int  MediaPosition_set;
62     gx_path_type_t fill_rule;	/* ...winding_number or ...even_odd  */
63     gx_path_type_t clip_rule;	/* ditto */
64     pxeColorSpace_t color_space;
65     struct pal_ {
66 	int size;		/* # of bytes */
67 	byte data[256 * 3];	/* up to 8-bit samples */
68     } palette;
69     struct pts_ {		/* buffer for accumulating path points */
70 	gs_int_point current;	/* current point as of start of data */
71 	point_type_t type;
72 	int count;
73 	gs_int_point data[NUM_POINTS];
74     } points;
75     struct ch_ {		/* cache for downloaded characters */
76 #define MAX_CACHED_CHARS 400
77 #define MAX_CHAR_DATA 500000
78 #define MAX_CHAR_SIZE 5000
79 #define CHAR_HASH_FACTOR 247
80 	ushort table[MAX_CACHED_CHARS * 3 / 2];
81 	struct cd_ {
82 	    gs_id id;		/* key */
83 	    uint size;
84 	} data[MAX_CACHED_CHARS];
85 	int next_in;		/* next data element to fill in */
86 	int next_out;		/* next data element to discard */
87 	int count;		/* of occupied data elements */
88 	ulong used;
89     } chars;
90     bool font_set;
91 } gx_device_pclxl;
92 
93 gs_public_st_suffix_add0_final(st_device_pclxl, gx_device_pclxl,
94 			       "gx_device_pclxl",
95 			       device_pclxl_enum_ptrs, device_pclxl_reloc_ptrs,
96 			       gx_device_finalize, st_device_vector);
97 
98 #define pclxl_device_body(dname, depth)\
99   std_device_dci_type_body(gx_device_pclxl, 0, dname, &st_device_pclxl,\
100 			   DEFAULT_WIDTH_10THS * X_DPI / 10,\
101 			   DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
102 			   X_DPI, Y_DPI,\
103 			   (depth > 8 ? 3 : 1), depth,\
104 			   (depth > 1 ? 255 : 1), (depth > 8 ? 255 : 0),\
105 			   (depth > 1 ? 256 : 2), (depth > 8 ? 256 : 1))
106 
107 /* Driver procedures */
108 private dev_proc_open_device(pclxl_open_device);
109 private dev_proc_output_page(pclxl_output_page);
110 private dev_proc_close_device(pclxl_close_device);
111 private dev_proc_copy_mono(pclxl_copy_mono);
112 private dev_proc_copy_color(pclxl_copy_color);
113 private dev_proc_fill_mask(pclxl_fill_mask);
114 
115 private dev_proc_get_params(pclxl_get_params);
116 private dev_proc_put_params(pclxl_put_params);
117 
118 /*private dev_proc_draw_thin_line(pclxl_draw_thin_line); */
119 private dev_proc_begin_image(pclxl_begin_image);
120 private dev_proc_strip_copy_rop(pclxl_strip_copy_rop);
121 
122 #define pclxl_device_procs(map_rgb_color, map_color_rgb)\
123 {\
124 	pclxl_open_device,\
125 	NULL,			/* get_initial_matrix */\
126 	NULL,			/* sync_output */\
127 	pclxl_output_page,\
128 	pclxl_close_device,\
129 	map_rgb_color,		/* differs */\
130 	map_color_rgb,		/* differs */\
131 	gdev_vector_fill_rectangle,\
132 	NULL,			/* tile_rectangle */\
133 	pclxl_copy_mono,\
134 	pclxl_copy_color,\
135 	NULL,			/* draw_line */\
136 	NULL,			/* get_bits */\
137 	pclxl_get_params,\
138 	pclxl_put_params,\
139 	NULL,			/* map_cmyk_color */\
140 	NULL,			/* get_xfont_procs */\
141 	NULL,			/* get_xfont_device */\
142 	NULL,			/* map_rgb_alpha_color */\
143 	gx_page_device_get_page_device,\
144 	NULL,			/* get_alpha_bits */\
145 	NULL,			/* copy_alpha */\
146 	NULL,			/* get_band */\
147 	NULL,			/* copy_rop */\
148 	gdev_vector_fill_path,\
149 	gdev_vector_stroke_path,\
150 	pclxl_fill_mask,\
151 	gdev_vector_fill_trapezoid,\
152 	gdev_vector_fill_parallelogram,\
153 	gdev_vector_fill_triangle,\
154 	NULL /****** WRONG ******/,	/* draw_thin_line */\
155 	pclxl_begin_image,\
156 	NULL,			/* image_data */\
157 	NULL,			/* end_image */\
158 	NULL,			/* strip_tile_rectangle */\
159 	pclxl_strip_copy_rop\
160 }
161 
162 const gx_device_pclxl gs_pxlmono_device = {
163     pclxl_device_body("pxlmono", 8),
164     pclxl_device_procs(gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb)
165 };
166 
167 const gx_device_pclxl gs_pxlcolor_device = {
168     pclxl_device_body("pxlcolor", 24),
169     pclxl_device_procs(gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb)
170 };
171 
172 /* ---------------- Other utilities ---------------- */
173 
174 inline private stream *
pclxl_stream(gx_device_pclxl * xdev)175 pclxl_stream(gx_device_pclxl *xdev)
176 {
177     return gdev_vector_stream((gx_device_vector *)xdev);
178 }
179 
180 /* Initialize for a page. */
181 private void
pclxl_page_init(gx_device_pclxl * xdev)182 pclxl_page_init(gx_device_pclxl * xdev)
183 {
184     gdev_vector_init((gx_device_vector *)xdev);
185     xdev->in_page = false;
186     xdev->fill_rule = gx_path_type_winding_number;
187     xdev->clip_rule = gx_path_type_winding_number;
188     xdev->color_space = eNoColorSpace;
189     xdev->palette.size = 0;
190     xdev->font_set = false;
191 }
192 
193 /* Test whether a RGB color is actually a gray shade. */
194 #define RGB_IS_GRAY(ci) ((ci) >> 8 == ((ci) & 0xffff))
195 
196 /* Set the color space and (optionally) palette. */
197 private void
pclxl_set_color_space(gx_device_pclxl * xdev,pxeColorSpace_t color_space)198 pclxl_set_color_space(gx_device_pclxl * xdev, pxeColorSpace_t color_space)
199 {
200     if (xdev->color_space != color_space) {
201 	stream *s = pclxl_stream(xdev);
202 
203 	px_put_ub(s, (byte)color_space);
204 	px_put_ac(s, pxaColorSpace, pxtSetColorSpace);
205 	xdev->color_space = color_space;
206     }
207 }
208 private void
pclxl_set_color_palette(gx_device_pclxl * xdev,pxeColorSpace_t color_space,const byte * palette,uint palette_size)209 pclxl_set_color_palette(gx_device_pclxl * xdev, pxeColorSpace_t color_space,
210 			const byte * palette, uint palette_size)
211 {
212     if (xdev->color_space != color_space ||
213 	xdev->palette.size != palette_size ||
214 	memcmp(xdev->palette.data, palette, palette_size)
215 	) {
216 	stream *s = pclxl_stream(xdev);
217 	static const byte csp_[] = {
218 	    DA(pxaColorSpace),
219 	    DUB(e8Bit), DA(pxaPaletteDepth),
220 	    pxt_ubyte_array
221 	};
222 
223 	px_put_ub(s, (byte)color_space);
224 	PX_PUT_LIT(s, csp_);
225 	px_put_u(s, palette_size);
226 	px_put_bytes(s, palette, palette_size);
227 	px_put_ac(s, pxaPaletteData, pxtSetColorSpace);
228 	xdev->color_space = color_space;
229 	xdev->palette.size = palette_size;
230 	memcpy(xdev->palette.data, palette, palette_size);
231     }
232 }
233 
234 /* Set a drawing RGB color. */
235 private int
pclxl_set_color(gx_device_pclxl * xdev,const gx_drawing_color * pdc,px_attribute_t null_source,px_tag_t op)236 pclxl_set_color(gx_device_pclxl * xdev, const gx_drawing_color * pdc,
237 		px_attribute_t null_source, px_tag_t op)
238 {
239     stream *s = pclxl_stream(xdev);
240 
241     if (gx_dc_is_pure(pdc)) {
242 	gx_color_index color = gx_dc_pure_color(pdc);
243 
244 	if (xdev->color_info.num_components == 1 || RGB_IS_GRAY(color)) {
245 	    pclxl_set_color_space(xdev, eGray);
246 	    px_put_uba(s, (byte) color, pxaGrayLevel);
247 	} else {
248 	    pclxl_set_color_space(xdev, eRGB);
249 	    spputc(s, pxt_ubyte_array);
250 	    px_put_ub(s, 3);
251 	    spputc(s, (byte) (color >> 16));
252 	    spputc(s, (byte) (color >> 8));
253 	    spputc(s, (byte) color);
254 	    px_put_a(s, pxaRGBColor);
255 	}
256     } else if (gx_dc_is_null(pdc) || !color_is_set(pdc))
257 	px_put_uba(s, 0, null_source);
258     else
259 	return_error(gs_error_rangecheck);
260     spputc(s, (byte)op);
261     return 0;
262 }
263 
264 /* Test whether we can handle a given color space in an image. */
265 /* We cannot handle ICCBased color spaces. */
266 private bool
pclxl_can_handle_color_space(const gs_color_space * pcs)267 pclxl_can_handle_color_space(const gs_color_space * pcs)
268 {
269     gs_color_space_index index = gs_color_space_get_index(pcs);
270 
271     if (index == gs_color_space_index_Indexed) {
272 	if (pcs->params.indexed.use_proc)
273 	    return false;
274 	index =
275 	    gs_color_space_get_index(gs_color_space_indexed_base_space(pcs));
276     }
277     return !(index == gs_color_space_index_Separation ||
278 	     index == gs_color_space_index_Pattern ||
279              index == gs_color_space_index_CIEICC);
280 }
281 
282 /* Set brush, pen, and mode for painting a path. */
283 private void
pclxl_set_paints(gx_device_pclxl * xdev,gx_path_type_t type)284 pclxl_set_paints(gx_device_pclxl * xdev, gx_path_type_t type)
285 {
286     stream *s = pclxl_stream(xdev);
287     gx_path_type_t rule = type & gx_path_type_rule;
288 
289     if (!(type & gx_path_type_fill) &&
290 	(color_is_set(&xdev->saved_fill_color.saved_dev_color) ||
291 	!gx_dc_is_null(&xdev->saved_fill_color.saved_dev_color)
292 	)
293 	) {
294 	static const byte nac_[] = {
295 	    DUB(0), DA(pxaNullBrush), pxtSetBrushSource
296 	};
297 
298 	PX_PUT_LIT(s, nac_);
299 	color_set_null(&xdev->saved_fill_color.saved_dev_color);
300 	if (rule != xdev->fill_rule) {
301 	    px_put_ub(s, (byte)(rule == gx_path_type_even_odd ? eEvenOdd :
302 		       eNonZeroWinding));
303 	    px_put_ac(s, pxaFillMode, pxtSetFillMode);
304 	    xdev->fill_rule = rule;
305 	}
306     }
307     if (!(type & gx_path_type_stroke) &&
308 	(color_is_set(&xdev->saved_stroke_color.saved_dev_color) ||
309 	!gx_dc_is_null(&xdev->saved_fill_color.saved_dev_color)
310 	 )
311 	) {
312 	static const byte nac_[] = {
313 	    DUB(0), DA(pxaNullPen), pxtSetPenSource
314 	};
315 
316 	PX_PUT_LIT(s, nac_);
317 	color_set_null(&xdev->saved_stroke_color.saved_dev_color);
318     }
319 }
320 
321 /* Set the cursor. */
322 private int
pclxl_set_cursor(gx_device_pclxl * xdev,int x,int y)323 pclxl_set_cursor(gx_device_pclxl * xdev, int x, int y)
324 {
325     stream *s = pclxl_stream(xdev);
326 
327     px_put_ssp(s, x, y);
328     px_put_ac(s, pxaPoint, pxtSetCursor);
329     return 0;
330 }
331 
332 /* ------ Paths ------ */
333 
334 /* Flush any buffered path points. */
335 private void
px_put_np(stream * s,int count,pxeDataType_t dtype)336 px_put_np(stream * s, int count, pxeDataType_t dtype)
337 {
338     px_put_uba(s, (byte)count, pxaNumberOfPoints);
339     px_put_uba(s, (byte)dtype, pxaPointType);
340 }
341 private int
pclxl_flush_points(gx_device_pclxl * xdev)342 pclxl_flush_points(gx_device_pclxl * xdev)
343 {
344     int count = xdev->points.count;
345 
346     if (count) {
347 	stream *s = pclxl_stream(xdev);
348 	px_tag_t op;
349 	int x = xdev->points.current.x, y = xdev->points.current.y;
350 	int uor = 0, sor = 0;
351 	pxeDataType_t data_type;
352 	int i, di;
353 	byte diffs[NUM_POINTS * 2];
354 
355 	/*
356 	 * Writing N lines using a point list requires 11 + 4*N or 11 +
357 	 * 2*N bytes, as opposed to 8*N bytes using separate commands;
358 	 * writing N curves requires 11 + 12*N or 11 + 6*N bytes
359 	 * vs. 22*N.  So it's always shorter to write curves with a
360 	 * list (except for N = 1 with full-size coordinates, but since
361 	 * the difference is only 1 byte, we don't bother to ever use
362 	 * the non-list form), but lines are shorter only if N >= 3
363 	 * (again, with a 1-byte difference if N = 2 and byte
364 	 * coordinates).
365 	 */
366 	switch (xdev->points.type) {
367 	    case POINTS_NONE:
368 		return 0;
369 	    case POINTS_LINES:
370 		op = pxtLinePath;
371 		if (count < 3) {
372 		    for (i = 0; i < count; ++i) {
373 			px_put_ssp(s, xdev->points.data[i].x,
374 				xdev->points.data[i].y);
375 			px_put_a(s, pxaEndPoint);
376 			spputc(s, (byte)op);
377 		    }
378 		    goto zap;
379 		}
380 		/* See if we can use byte values. */
381 		for (i = di = 0; i < count; ++i, di += 2) {
382 		    int dx = xdev->points.data[i].x - x;
383 		    int dy = xdev->points.data[i].y - y;
384 
385 		    diffs[di] = (byte) dx;
386 		    diffs[di + 1] = (byte) dy;
387 		    uor |= dx | dy;
388 		    sor |= (dx + 0x80) | (dy + 0x80);
389 		    x += dx, y += dy;
390 		}
391 		if (!(uor & ~0xff))
392 		    data_type = eUByte;
393 		else if (!(sor & ~0xff))
394 		    data_type = eSByte;
395 		else
396 		    break;
397 		op = pxtLineRelPath;
398 		/* Use byte values. */
399 	      useb:px_put_np(s, count, data_type);
400 		spputc(s, (byte)op);
401 		px_put_data_length(s, count * 2);	/* 2 bytes per point */
402 		px_put_bytes(s, diffs, count * 2);
403 		goto zap;
404 	    case POINTS_CURVES:
405 		op = pxtBezierPath;
406 		/* See if we can use byte values. */
407 		for (i = di = 0; i < count; i += 3, di += 6) {
408 		    int dx1 = xdev->points.data[i].x - x;
409 		    int dy1 = xdev->points.data[i].y - y;
410 		    int dx2 = xdev->points.data[i + 1].x - x;
411 		    int dy2 = xdev->points.data[i + 1].y - y;
412 		    int dx = xdev->points.data[i + 2].x - x;
413 		    int dy = xdev->points.data[i + 2].y - y;
414 
415 		    diffs[di] = (byte) dx1;
416 		    diffs[di + 1] = (byte) dy1;
417 		    diffs[di + 2] = (byte) dx2;
418 		    diffs[di + 3] = (byte) dy2;
419 		    diffs[di + 4] = (byte) dx;
420 		    diffs[di + 5] = (byte) dy;
421 		    uor |= dx1 | dy1 | dx2 | dy2 | dx | dy;
422 		    sor |= (dx1 + 0x80) | (dy1 + 0x80) |
423 			(dx2 + 0x80) | (dy2 + 0x80) |
424 			(dx + 0x80) | (dy + 0x80);
425 		    x += dx, y += dy;
426 		}
427 		if (!(uor & ~0xff))
428 		    data_type = eUByte;
429 		else if (!(sor & ~0xff))
430 		    data_type = eSByte;
431 		else
432 		    break;
433 		op = pxtBezierRelPath;
434 		goto useb;
435 	    default:		/* can't happen */
436 		return_error(gs_error_unknownerror);
437 	}
438 	px_put_np(s, count, eSInt16);
439 	spputc(s, (byte)op);
440 	px_put_data_length(s, count * 4);	/* 2 UInt16s per point */
441 	for (i = 0; i < count; ++i) {
442 	    px_put_s(s, xdev->points.data[i].x);
443 	    px_put_s(s, xdev->points.data[i].y);
444 	}
445       zap:xdev->points.type = POINTS_NONE;
446 	xdev->points.count = 0;
447     }
448     return 0;
449 }
450 
451 /* ------ Images ------ */
452 
453 private image_enum_proc_plane_data(pclxl_image_plane_data);
454 private image_enum_proc_end_image(pclxl_image_end_image);
455 private const gx_image_enum_procs_t pclxl_image_enum_procs = {
456     pclxl_image_plane_data, pclxl_image_end_image
457 };
458 
459 /* Begin an image. */
460 private void
pclxl_write_begin_image(gx_device_pclxl * xdev,uint width,uint height,uint dest_width,uint dest_height)461 pclxl_write_begin_image(gx_device_pclxl * xdev, uint width, uint height,
462 			uint dest_width, uint dest_height)
463 {
464     stream *s = pclxl_stream(xdev);
465 
466     px_put_usa(s, width, pxaSourceWidth);
467     px_put_usa(s, height, pxaSourceHeight);
468     px_put_usp(s, dest_width, dest_height);
469     px_put_ac(s, pxaDestinationSize, pxtBeginImage);
470 }
471 
472 /* Write rows of an image. */
473 /****** IGNORES data_bit ******/
474 private void
pclxl_write_image_data(gx_device_pclxl * xdev,const byte * data,int data_bit,uint raster,uint width_bits,int y,int height)475 pclxl_write_image_data(gx_device_pclxl * xdev, const byte * data, int data_bit,
476 		       uint raster, uint width_bits, int y, int height)
477 {
478     stream *s = pclxl_stream(xdev);
479     uint width_bytes = (width_bits + 7) >> 3;
480     uint num_bytes = ROUND_UP(width_bytes, 4) * height;
481     bool compress = num_bytes >= 8;
482     int i;
483 
484     px_put_usa(s, y, pxaStartLine);
485     px_put_usa(s, height, pxaBlockHeight);
486     if (compress) {
487 	stream_RLE_state rlstate;
488 	stream_cursor_write w;
489 	stream_cursor_read r;
490 
491 	/*
492 	 * H-P printers require that all the data for an operator be
493 	 * contained in a single data block.  Thus, we must allocate a
494 	 * temporary buffer for the compressed data.  Currently we don't go
495 	 * to the trouble of doing two passes if we can't allocate a buffer
496 	 * large enough for the entire transfer.
497 	 */
498 	byte *buf = gs_alloc_bytes(xdev->v_memory, num_bytes,
499 				   "pclxl_write_image_data");
500 
501 	if (buf == 0)
502 	    goto nc;
503 	s_RLE_set_defaults_inline(&rlstate);
504 	rlstate.EndOfData = false;
505 	s_RLE_init_inline(&rlstate);
506 	w.ptr = buf - 1;
507 	w.limit = w.ptr + num_bytes;
508 	/*
509 	 * If we ever overrun the buffer, it means that the compressed
510 	 * data was larger than the uncompressed.  If this happens,
511 	 * write the data uncompressed.
512 	 */
513 	for (i = 0; i < height; ++i) {
514 	    r.ptr = data + i * raster - 1;
515 	    r.limit = r.ptr + width_bytes;
516 	    if ((*s_RLE_template.process)
517 		((stream_state *) & rlstate, &r, &w, false) != 0 ||
518 		r.ptr != r.limit
519 		)
520 		goto ncfree;
521 	    r.ptr = (const byte *)"\000\000\000\000\000";
522 	    r.limit = r.ptr + (-(int)width_bytes & 3);
523 	    if ((*s_RLE_template.process)
524 		((stream_state *) & rlstate, &r, &w, false) != 0 ||
525 		r.ptr != r.limit
526 		)
527 		goto ncfree;
528 	}
529 	r.ptr = r.limit;
530 	if ((*s_RLE_template.process)
531 	    ((stream_state *) & rlstate, &r, &w, true) != 0
532 	    )
533 	    goto ncfree;
534 	{
535 	    uint count = w.ptr + 1 - buf;
536 
537 	    px_put_ub(s, eRLECompression);
538 	    px_put_ac(s, pxaCompressMode, pxtReadImage);
539 	    px_put_data_length(s, count);
540 	    px_put_bytes(s, buf, count);
541 	}
542 	gs_free_object(xdev->v_memory, buf, "pclxl_write_image_data");
543 	return;
544       ncfree:gs_free_object(xdev->v_memory, buf, "pclxl_write_image_data");
545     }
546  nc:
547     /* Write the data uncompressed. */
548     px_put_ub(s, eNoCompression);
549     px_put_ac(s, pxaCompressMode, pxtReadImage);
550     px_put_data_length(s, num_bytes);
551     for (i = 0; i < height; ++i) {
552 	px_put_bytes(s, data + i * raster, width_bytes);
553 	px_put_bytes(s, (const byte *)"\000\000\000\000", -(int)width_bytes & 3);
554     }
555 }
556 
557 /* End an image. */
558 private void
pclxl_write_end_image(gx_device_pclxl * xdev)559 pclxl_write_end_image(gx_device_pclxl * xdev)
560 {
561     spputc(xdev->strm, pxtEndImage);
562 }
563 
564 /* ------ Fonts ------ */
565 
566 /* Write a string (single- or double-byte). */
567 private void
px_put_string(stream * s,const byte * data,uint len,bool wide)568 px_put_string(stream * s, const byte * data, uint len, bool wide)
569 {
570     if (wide) {
571 	spputc(s, pxt_uint16_array);
572 	px_put_u(s, len);
573 	px_put_bytes(s, data, len * 2);
574     } else {
575 	spputc(s, pxt_ubyte_array);
576 	px_put_u(s, len);
577 	px_put_bytes(s, data, len);
578     }
579 }
580 
581 /* Write a 16-bit big-endian value. */
582 private void
px_put_us_be(stream * s,uint i)583 px_put_us_be(stream * s, uint i)
584 {
585     spputc(s, (byte) (i >> 8));
586     spputc(s, (byte) i);
587 }
588 
589 /* Define a bitmap font.  The client must call px_put_string */
590 /* with the font name immediately before calling this procedure. */
591 private void
pclxl_define_bitmap_font(gx_device_pclxl * xdev)592 pclxl_define_bitmap_font(gx_device_pclxl * xdev)
593 {
594     stream *s = pclxl_stream(xdev);
595     static const byte bfh_[] = {
596 	DA(pxaFontName), DUB(0), DA(pxaFontFormat),
597 	pxtBeginFontHeader,
598 	DUS(8 + 6 + 4 + 6), DA(pxaFontHeaderLength),
599 	pxtReadFontHeader,
600 	pxt_dataLengthByte, 8 + 6 + 4 + 6,
601 	0, 0, 0, 0,
602 	254, 0, (MAX_CACHED_CHARS + 255) >> 8, 0,
603 	'B', 'R', 0, 0, 0, 4
604     };
605     static const byte efh_[] = {
606 	0xff, 0xff, 0, 0, 0, 0,
607 	pxtEndFontHeader
608     };
609 
610     PX_PUT_LIT(s, bfh_);
611     px_put_us_be(s, (uint) (xdev->HWResolution[0] + 0.5));
612     px_put_us_be(s, (uint) (xdev->HWResolution[1] + 0.5));
613     PX_PUT_LIT(s, efh_);
614 }
615 
616 /* Set the font.  The client must call px_put_string */
617 /* with the font name immediately before calling this procedure. */
618 private void
pclxl_set_font(gx_device_pclxl * xdev)619 pclxl_set_font(gx_device_pclxl * xdev)
620 {
621     stream *s = pclxl_stream(xdev);
622     static const byte sf_[] = {
623 	DA(pxaFontName), DUB(1), DA(pxaCharSize), DUS(0), DA(pxaSymbolSet),
624 	pxtSetFont
625     };
626 
627     PX_PUT_LIT(s, sf_);
628 }
629 
630 /* Define a character in a bitmap font.  The client must call px_put_string */
631 /* with the font name immediately before calling this procedure. */
632 private void
pclxl_define_bitmap_char(gx_device_pclxl * xdev,uint ccode,const byte * data,uint raster,uint width_bits,uint height)633 pclxl_define_bitmap_char(gx_device_pclxl * xdev, uint ccode,
634 	       const byte * data, uint raster, uint width_bits, uint height)
635 {
636     stream *s = pclxl_stream(xdev);
637     uint width_bytes = (width_bits + 7) >> 3;
638     uint size = 10 + width_bytes * height;
639     uint i;
640 
641     px_put_ac(s, pxaFontName, pxtBeginChar);
642     px_put_u(s, ccode);
643     px_put_a(s, pxaCharCode);
644     if (size > 0xffff) {
645 	spputc(s, pxt_uint32);
646 	px_put_l(s, (ulong) size);
647     } else
648 	px_put_us(s, size);
649     px_put_ac(s, pxaCharDataSize, pxtReadChar);
650     px_put_data_length(s, size);
651     px_put_bytes(s, (const byte *)"\000\000\000\000\000\000", 6);
652     px_put_us_be(s, width_bits);
653     px_put_us_be(s, height);
654     for (i = 0; i < height; ++i)
655 	px_put_bytes(s, data + i * raster, width_bytes);
656     spputc(s, pxtEndChar);
657 }
658 
659 /* Write the name of the only font we define. */
660 private void
pclxl_write_font_name(gx_device_pclxl * xdev)661 pclxl_write_font_name(gx_device_pclxl * xdev)
662 {
663     stream *s = pclxl_stream(xdev);
664 
665     px_put_string(s, (const byte *)"@", 1, false);
666 }
667 
668 /* Look up a bitmap id, return the index in the character table. */
669 /* If the id is missing, return an index for inserting. */
670 private int
pclxl_char_index(gx_device_pclxl * xdev,gs_id id)671 pclxl_char_index(gx_device_pclxl * xdev, gs_id id)
672 {
673     int i, i_empty = -1;
674     uint ccode;
675 
676     for (i = (id * CHAR_HASH_FACTOR) % countof(xdev->chars.table);;
677 	 i = (i == 0 ? countof(xdev->chars.table) : i) - 1
678 	) {
679 	ccode = xdev->chars.table[i];
680 	if (ccode == 0)
681 	    return (i_empty >= 0 ? i_empty : i);
682 	else if (ccode == 1) {
683 	    if (i_empty < 0)
684 		i_empty = i;
685 	    else if (i == i_empty)	/* full table */
686 		return i;
687 	} else if (xdev->chars.data[ccode].id == id)
688 	    return i;
689     }
690 }
691 
692 /* Remove the character table entry at a given index. */
693 private void
pclxl_remove_char(gx_device_pclxl * xdev,int index)694 pclxl_remove_char(gx_device_pclxl * xdev, int index)
695 {
696     uint ccode = xdev->chars.table[index];
697     int i;
698 
699     if (ccode < 2)
700 	return;
701     xdev->chars.count--;
702     xdev->chars.used -= xdev->chars.data[ccode].size;
703     xdev->chars.table[index] = 1;	/* mark as deleted */
704     i = (index == 0 ? countof(xdev->chars.table) : index) - 1;
705     if (xdev->chars.table[i] == 0) {
706 	/* The next slot in probe order is empty. */
707 	/* Mark this slot and any deleted predecessors as empty. */
708 	for (i = index; xdev->chars.table[i] == 1;
709 	     i = (i == countof(xdev->chars.table) - 1 ? 0 : i + 1)
710 	    )
711 	    xdev->chars.table[i] = 0;
712     }
713 }
714 
715 /* Write a bitmap as a text character if possible. */
716 /* The caller must set the color, cursor, and RasterOp. */
717 /* We know id != gs_no_id. */
718 private int
pclxl_copy_text_char(gx_device_pclxl * xdev,const byte * data,int raster,gx_bitmap_id id,int w,int h)719 pclxl_copy_text_char(gx_device_pclxl * xdev, const byte * data,
720 		     int raster, gx_bitmap_id id, int w, int h)
721 {
722     uint width_bytes = (w + 7) >> 3;
723     uint size = width_bytes * h;
724     int index;
725     uint ccode;
726     stream *s = pclxl_stream(xdev);
727 
728     if (size > MAX_CHAR_SIZE)
729 	return -1;
730     index = pclxl_char_index(xdev, id);
731     if ((ccode = xdev->chars.table[index]) < 2) {
732 	/* Enter the character in the table. */
733 	while (xdev->chars.used + size > MAX_CHAR_DATA ||
734 	       xdev->chars.count >= MAX_CACHED_CHARS - 2
735 	    ) {
736 	    ccode = xdev->chars.next_out;
737 	    index = pclxl_char_index(xdev, xdev->chars.data[ccode].id);
738 	    pclxl_remove_char(xdev, index);
739 	    xdev->chars.next_out =
740 		(ccode == MAX_CACHED_CHARS - 1 ? 2 : ccode + 1);
741 	}
742 	index = pclxl_char_index(xdev, id);
743 	ccode = xdev->chars.next_in;
744 	xdev->chars.data[ccode].id = id;
745 	xdev->chars.data[ccode].size = size;
746 	xdev->chars.table[index] = ccode;
747 	xdev->chars.next_in =
748 	    (ccode == MAX_CACHED_CHARS - 1 ? 2 : ccode + 1);
749 	if (!xdev->chars.count++) {
750 	    /* This is the very first character. */
751 	    pclxl_write_font_name(xdev);
752 	    pclxl_define_bitmap_font(xdev);
753 	}
754 	xdev->chars.used += size;
755 	pclxl_write_font_name(xdev);
756 	pclxl_define_bitmap_char(xdev, ccode, data, raster, w, h);
757     }
758     if (!xdev->font_set) {
759 	pclxl_write_font_name(xdev);
760 	pclxl_set_font(xdev);
761 	xdev->font_set = true;
762     } {
763 	byte cc_bytes[2];
764 
765 	cc_bytes[0] = (byte) ccode;
766 	cc_bytes[1] = ccode >> 8;
767 	px_put_string(s, cc_bytes, 1, cc_bytes[1] != 0);
768     }
769     px_put_ac(s, pxaTextData, pxtText);
770     return 0;
771 }
772 
773 /* ---------------- Vector implementation procedures ---------------- */
774 
775 private int
pclxl_beginpage(gx_device_vector * vdev)776 pclxl_beginpage(gx_device_vector * vdev)
777 {
778     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
779     /*
780      * We can't use gdev_vector_stream here, because this may be called
781      * from there before in_page is set.
782      */
783     stream *s = vdev->strm;
784     byte media_source = eAutoSelect; /* default */
785 
786     px_write_page_header(s, (const gx_device *)vdev);
787 
788     if (xdev->ManualFeed_set && xdev->ManualFeed)
789 	media_source = 2;
790     else if (xdev->MediaPosition_set && xdev->MediaPosition >= 0 )
791 	media_source = xdev->MediaPosition;
792 
793     px_write_select_media(s, (const gx_device *)vdev, &xdev->media_size, &media_source );
794 
795     spputc(s, pxtBeginPage);
796     return 0;
797 }
798 
799 private int
pclxl_setlinewidth(gx_device_vector * vdev,floatp width)800 pclxl_setlinewidth(gx_device_vector * vdev, floatp width)
801 {
802     stream *s = gdev_vector_stream(vdev);
803 
804     px_put_us(s, (uint) width);
805     px_put_ac(s, pxaPenWidth, pxtSetPenWidth);
806     return 0;
807 }
808 
809 private int
pclxl_setlinecap(gx_device_vector * vdev,gs_line_cap cap)810 pclxl_setlinecap(gx_device_vector * vdev, gs_line_cap cap)
811 {
812     stream *s = gdev_vector_stream(vdev);
813 
814     /* The PCL XL cap styles just happen to be identical to PostScript. */
815     px_put_ub(s, (byte) cap);
816     px_put_ac(s, pxaLineCapStyle, pxtSetLineCap);
817     return 0;
818 }
819 
820 private int
pclxl_setlinejoin(gx_device_vector * vdev,gs_line_join join)821 pclxl_setlinejoin(gx_device_vector * vdev, gs_line_join join)
822 {
823     stream *s = gdev_vector_stream(vdev);
824 
825     /* The PCL XL join styles just happen to be identical to PostScript. */
826     px_put_ub(s, (byte) join);
827     px_put_ac(s, pxaLineJoinStyle, pxtSetLineJoin);
828     return 0;
829 }
830 
831 private int
pclxl_setmiterlimit(gx_device_vector * vdev,floatp limit)832 pclxl_setmiterlimit(gx_device_vector * vdev, floatp limit)
833 {
834     stream *s = gdev_vector_stream(vdev);
835     /*
836      * Amazingly enough, the PCL XL specification doesn't allow real
837      * numbers for the miter limit.
838      */
839     int i_limit = (int)(limit + 0.5);
840 
841     px_put_u(s, max(i_limit, 1));
842     px_put_ac(s, pxaMiterLength, pxtSetMiterLimit);
843     return 0;
844 }
845 
846 private int
pclxl_setdash(gx_device_vector * vdev,const float * pattern,uint count,floatp offset)847 pclxl_setdash(gx_device_vector * vdev, const float *pattern, uint count,
848 	      floatp offset)
849 {
850     stream *s = gdev_vector_stream(vdev);
851 
852     if (count == 0) {
853 	static const byte nac_[] = {
854 	    DUB(0), DA(pxaSolidLine)
855 	};
856 
857 	PX_PUT_LIT(s, nac_);
858     } else if (count > 255)
859 	return_error(gs_error_limitcheck);
860     else {
861 	uint i;
862 
863 	/*
864 	 * Astoundingly, PCL XL doesn't allow real numbers here.
865 	 * Do the best we can.
866 	 */
867 	spputc(s, pxt_uint16_array);
868 	px_put_ub(s, (byte)count);
869 	for (i = 0; i < count; ++i)
870 	    px_put_s(s, (uint)pattern[i]);
871 	px_put_a(s, pxaLineDashStyle);
872 	if (offset != 0)
873 	    px_put_usa(s, (uint)offset, pxaDashOffset);
874     }
875     spputc(s, pxtSetLineDash);
876     return 0;
877 }
878 
879 private int
pclxl_setlogop(gx_device_vector * vdev,gs_logical_operation_t lop,gs_logical_operation_t diff)880 pclxl_setlogop(gx_device_vector * vdev, gs_logical_operation_t lop,
881 	       gs_logical_operation_t diff)
882 {
883     stream *s = gdev_vector_stream(vdev);
884 
885     if (diff & lop_S_transparent) {
886 	px_put_ub(s, (byte)(lop & lop_S_transparent ? 1 : 0));
887 	px_put_ac(s, pxaTxMode, pxtSetSourceTxMode);
888     }
889     if (diff & lop_T_transparent) {
890 	px_put_ub(s, (byte)(lop & lop_T_transparent ? 1 : 0));
891 	px_put_ac(s, pxaTxMode, pxtSetPaintTxMode);
892     }
893     if (lop_rop(diff)) {
894 	px_put_ub(s, (byte)lop_rop(lop));
895 	px_put_ac(s, pxaROP3, pxtSetROP);
896     }
897     return 0;
898 }
899 
900 private int
pclxl_can_handle_hl_color(gx_device_vector * vdev,const gs_imager_state * pis,const gx_drawing_color * pdc)901 pclxl_can_handle_hl_color(gx_device_vector * vdev, const gs_imager_state * pis,
902                    const gx_drawing_color * pdc)
903 {
904     return false;
905 }
906 
907 private int
pclxl_setfillcolor(gx_device_vector * vdev,const gs_imager_state * pis,const gx_drawing_color * pdc)908 pclxl_setfillcolor(gx_device_vector * vdev, const gs_imager_state * pis,
909                    const gx_drawing_color * pdc)
910 {
911     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
912 
913     return pclxl_set_color(xdev, pdc, pxaNullBrush, pxtSetBrushSource);
914 }
915 
916 private int
pclxl_setstrokecolor(gx_device_vector * vdev,const gs_imager_state * pis,const gx_drawing_color * pdc)917 pclxl_setstrokecolor(gx_device_vector * vdev, const gs_imager_state * pis,
918                      const gx_drawing_color * pdc)
919 {
920     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
921 
922     return pclxl_set_color(xdev, pdc, pxaNullPen, pxtSetPenSource);
923 }
924 
925 private int
pclxl_dorect(gx_device_vector * vdev,fixed x0,fixed y0,fixed x1,fixed y1,gx_path_type_t type)926 pclxl_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1,
927 	     fixed y1, gx_path_type_t type)
928 {
929     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
930     stream *s = gdev_vector_stream(vdev);
931 
932     /* Check for out-of-range points. */
933 #define OUT_OF_RANGE(v) (v < 0 || v >= int2fixed(0x10000))
934     if (OUT_OF_RANGE(x0) || OUT_OF_RANGE(y0) ||
935 	OUT_OF_RANGE(x1) || OUT_OF_RANGE(y1)
936 	)
937 	return_error(gs_error_rangecheck);
938 #undef OUT_OF_RANGE
939     if (type & (gx_path_type_fill | gx_path_type_stroke)) {
940 	pclxl_set_paints(xdev, type);
941 	px_put_usq_fixed(s, x0, y0, x1, y1);
942 	px_put_ac(s, pxaBoundingBox, pxtRectangle);
943     }
944     if (type & gx_path_type_clip) {
945 	static const byte cr_[] = {
946 	    DA(pxaBoundingBox),
947 	    DUB(eInterior), DA(pxaClipRegion),
948 	    pxtSetClipRectangle
949 	};
950 
951 	px_put_usq_fixed(s, x0, y0, x1, y1);
952 	PX_PUT_LIT(s, cr_);
953     }
954     return 0;
955 }
956 
957 private int
pclxl_beginpath(gx_device_vector * vdev,gx_path_type_t type)958 pclxl_beginpath(gx_device_vector * vdev, gx_path_type_t type)
959 {
960     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
961     stream *s = gdev_vector_stream(vdev);
962 
963     spputc(s, pxtNewPath);
964     xdev->points.type = POINTS_NONE;
965     xdev->points.count = 0;
966     return 0;
967 }
968 
969 private int
pclxl_moveto(gx_device_vector * vdev,floatp x0,floatp y0,floatp x,floatp y,gx_path_type_t type)970 pclxl_moveto(gx_device_vector * vdev, floatp x0, floatp y0, floatp x, floatp y,
971 	     gx_path_type_t type)
972 {
973     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
974     int code = pclxl_flush_points(xdev);
975 
976     if (code < 0)
977 	return code;
978     return pclxl_set_cursor(xdev,
979 			    xdev->points.current.x = (int)x,
980 			    xdev->points.current.y = (int)y);
981 }
982 
983 private int
pclxl_lineto(gx_device_vector * vdev,floatp x0,floatp y0,floatp x,floatp y,gx_path_type_t type)984 pclxl_lineto(gx_device_vector * vdev, floatp x0, floatp y0, floatp x, floatp y,
985 	     gx_path_type_t type)
986 {
987     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
988 
989     if (xdev->points.type != POINTS_LINES ||
990 	xdev->points.count >= NUM_POINTS
991 	) {
992 	if (xdev->points.type != POINTS_NONE) {
993 	    int code = pclxl_flush_points(xdev);
994 
995 	    if (code < 0)
996 		return code;
997 	}
998 	xdev->points.current.x = (int)x0;
999 	xdev->points.current.y = (int)y0;
1000 	xdev->points.type = POINTS_LINES;
1001     } {
1002 	gs_int_point *ppt = &xdev->points.data[xdev->points.count++];
1003 
1004 	ppt->x = (int)x, ppt->y = (int)y;
1005     }
1006     return 0;
1007 }
1008 
1009 private int
pclxl_curveto(gx_device_vector * vdev,floatp x0,floatp y0,floatp x1,floatp y1,floatp x2,floatp y2,floatp x3,floatp y3,gx_path_type_t type)1010 pclxl_curveto(gx_device_vector * vdev, floatp x0, floatp y0,
1011 	   floatp x1, floatp y1, floatp x2, floatp y2, floatp x3, floatp y3,
1012 	      gx_path_type_t type)
1013 {
1014     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
1015 
1016     if (xdev->points.type != POINTS_CURVES ||
1017 	xdev->points.count >= NUM_POINTS - 2
1018 	) {
1019 	if (xdev->points.type != POINTS_NONE) {
1020 	    int code = pclxl_flush_points(xdev);
1021 
1022 	    if (code < 0)
1023 		return code;
1024 	}
1025 	xdev->points.current.x = (int)x0;
1026 	xdev->points.current.y = (int)y0;
1027 	xdev->points.type = POINTS_CURVES;
1028     }
1029     {
1030 	gs_int_point *ppt = &xdev->points.data[xdev->points.count];
1031 
1032 	ppt->x = (int)x1, ppt->y = (int)y1, ++ppt;
1033 	ppt->x = (int)x2, ppt->y = (int)y2, ++ppt;
1034 	ppt->x = (int)x3, ppt->y = (int)y3;
1035     }
1036     xdev->points.count += 3;
1037     return 0;
1038 }
1039 
1040 private int
pclxl_closepath(gx_device_vector * vdev,floatp x,floatp y,floatp x_start,floatp y_start,gx_path_type_t type)1041 pclxl_closepath(gx_device_vector * vdev, floatp x, floatp y,
1042 		floatp x_start, floatp y_start, gx_path_type_t type)
1043 {
1044     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
1045     stream *s = gdev_vector_stream(vdev);
1046     int code = pclxl_flush_points(xdev);
1047 
1048     if (code < 0)
1049 	return code;
1050     spputc(s, pxtCloseSubPath);
1051     xdev->points.current.x = (int)x_start;
1052     xdev->points.current.y = (int)y_start;
1053     return 0;
1054 }
1055 
1056 private int
pclxl_endpath(gx_device_vector * vdev,gx_path_type_t type)1057 pclxl_endpath(gx_device_vector * vdev, gx_path_type_t type)
1058 {
1059     gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
1060     stream *s = gdev_vector_stream(vdev);
1061     int code = pclxl_flush_points(xdev);
1062     gx_path_type_t rule = type & gx_path_type_rule;
1063 
1064     if (code < 0)
1065 	return code;
1066     if (type & (gx_path_type_fill | gx_path_type_stroke)) {
1067 	pclxl_set_paints(xdev, type);
1068 	spputc(s, pxtPaintPath);
1069     }
1070     if (type & gx_path_type_clip) {
1071 	static const byte scr_[] = {
1072 	    DUB(eInterior), DA(pxaClipRegion), pxtSetClipReplace
1073 	};
1074 
1075 	if (rule != xdev->clip_rule) {
1076 	    px_put_ub(s, (byte)(rule == gx_path_type_even_odd ? eEvenOdd :
1077 		       eNonZeroWinding));
1078 	    px_put_ac(s, pxaClipMode, pxtSetClipMode);
1079 	    xdev->clip_rule = rule;
1080 	}
1081 	PX_PUT_LIT(s, scr_);
1082     }
1083     return 0;
1084 }
1085 
1086 /* Vector implementation procedures */
1087 
1088 private const gx_device_vector_procs pclxl_vector_procs = {
1089 	/* Page management */
1090     pclxl_beginpage,
1091 	/* Imager state */
1092     pclxl_setlinewidth,
1093     pclxl_setlinecap,
1094     pclxl_setlinejoin,
1095     pclxl_setmiterlimit,
1096     pclxl_setdash,
1097     gdev_vector_setflat,
1098     pclxl_setlogop,
1099 	/* Other state */
1100     pclxl_can_handle_hl_color,
1101     pclxl_setfillcolor,
1102     pclxl_setstrokecolor,
1103 	/* Paths */
1104     gdev_vector_dopath,
1105     pclxl_dorect,
1106     pclxl_beginpath,
1107     pclxl_moveto,
1108     pclxl_lineto,
1109     pclxl_curveto,
1110     pclxl_closepath,
1111     pclxl_endpath
1112 };
1113 
1114 /* ---------------- Driver procedures ---------------- */
1115 
1116 /* ------ Open/close/page ------ */
1117 
1118 /* Open the device. */
1119 private int
pclxl_open_device(gx_device * dev)1120 pclxl_open_device(gx_device * dev)
1121 {
1122     gx_device_vector *const vdev = (gx_device_vector *)dev;
1123     gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
1124     int code;
1125 
1126     vdev->v_memory = dev->memory;	/****** WRONG ******/
1127     vdev->vec_procs = &pclxl_vector_procs;
1128     code = gdev_vector_open_file_options(vdev, 512,
1129 					 VECTOR_OPEN_FILE_SEQUENTIAL);
1130     if (code < 0)
1131 	return code;
1132     pclxl_page_init(xdev);
1133     px_write_file_header(vdev->strm, dev);
1134     xdev->media_size = pxeMediaSize_next;	/* no size selected */
1135     memset(&xdev->chars, 0, sizeof(xdev->chars));
1136     xdev->chars.next_in = xdev->chars.next_out = 2;
1137     return 0;
1138 }
1139 
1140 /* Wrap up ("output") a page. */
1141 /* We only support flush = true, and we don't support num_copies != 1. */
1142 private int
pclxl_output_page(gx_device * dev,int num_copies,int flush)1143 pclxl_output_page(gx_device * dev, int num_copies, int flush)
1144 {
1145     gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
1146     stream *s;
1147 
1148     /* Note that unlike close_device, end_page must not omit blank pages. */
1149     if (!xdev->in_page)
1150 	pclxl_beginpage((gx_device_vector *)dev);
1151     s = xdev->strm;
1152     spputc(s, pxtEndPage);
1153     sflush(s);
1154     pclxl_page_init(xdev);
1155     if (ferror(xdev->file))
1156 	return_error(gs_error_ioerror);
1157     return gx_finish_output_page(dev, num_copies, flush);
1158 }
1159 
1160 /* Close the device. */
1161 /* Note that if this is being called as a result of finalization, */
1162 /* the stream may no longer exist. */
1163 private int
pclxl_close_device(gx_device * dev)1164 pclxl_close_device(gx_device * dev)
1165 {
1166     gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
1167     FILE *file = xdev->file;
1168 
1169     if (xdev->in_page)
1170 	fputc(pxtEndPage, file);
1171     px_write_file_trailer(file);
1172     return gdev_vector_close_file((gx_device_vector *)dev);
1173 }
1174 
1175 /* ------ One-for-one images ------ */
1176 
1177 private const byte eBit_values[] = {
1178     0, e1Bit, 0, 0, e4Bit, 0, 0, 0, e8Bit
1179 };
1180 
1181 /* Copy a monochrome bitmap. */
1182 private int
pclxl_copy_mono(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index zero,gx_color_index one)1183 pclxl_copy_mono(gx_device * dev, const byte * data, int data_x, int raster,
1184 		gx_bitmap_id id, int x, int y, int w, int h,
1185 		gx_color_index zero, gx_color_index one)
1186 {
1187     gx_device_vector *const vdev = (gx_device_vector *)dev;
1188     gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
1189     int code;
1190     stream *s;
1191     gx_color_index color0 = zero, color1 = one;
1192     gs_logical_operation_t lop;
1193     byte palette[2 * 3];
1194     int palette_size;
1195     pxeColorSpace_t color_space;
1196 
1197     fit_copy(dev, data, data_x, raster, id, x, y, w, h);
1198     code = gdev_vector_update_clip_path(vdev, NULL);
1199     if (code < 0)
1200 	return code;
1201     pclxl_set_cursor(xdev, x, y);
1202     if (id != gs_no_id && zero == gx_no_color_index &&
1203 	one != gx_no_color_index && data_x == 0
1204 	) {
1205 	gx_drawing_color dcolor;
1206 
1207 	set_nonclient_dev_color(&dcolor, one);
1208 	pclxl_setfillcolor(vdev, NULL, &dcolor);
1209 	if (pclxl_copy_text_char(xdev, data, raster, id, w, h) >= 0)
1210 	    return 0;
1211     }
1212     /*
1213      * The following doesn't work if we're writing white with a mask.
1214      * We'll fix it eventually.
1215      */
1216     if (zero == gx_no_color_index) {
1217 	if (one == gx_no_color_index)
1218 	    return 0;
1219 	lop = rop3_S | lop_S_transparent;
1220 	color0 = (1 << dev->color_info.depth) - 1;
1221     } else if (one == gx_no_color_index) {
1222 	lop = rop3_S | lop_S_transparent;
1223 	color1 = (1 << dev->color_info.depth) - 1;
1224     } else {
1225 	lop = rop3_S;
1226     }
1227     if (dev->color_info.num_components == 1 ||
1228 	(RGB_IS_GRAY(color0) && RGB_IS_GRAY(color1))
1229 	) {
1230 	palette[0] = (byte) color0;
1231 	palette[1] = (byte) color1;
1232 	palette_size = 2;
1233 	color_space = eGray;
1234     } else {
1235 	palette[0] = (byte) (color0 >> 16);
1236 	palette[1] = (byte) (color0 >> 8);
1237 	palette[2] = (byte) color0;
1238 	palette[3] = (byte) (color1 >> 16);
1239 	palette[4] = (byte) (color1 >> 8);
1240 	palette[5] = (byte) color1;
1241 	palette_size = 6;
1242 	color_space = eRGB;
1243     }
1244     code = gdev_vector_update_log_op(vdev, lop);
1245     if (code < 0)
1246 	return 0;
1247     pclxl_set_color_palette(xdev, color_space, palette, palette_size);
1248     s = pclxl_stream(xdev);
1249     {
1250 	static const byte mi_[] = {
1251 	    DUB(e1Bit), DA(pxaColorDepth),
1252 	    DUB(eIndexedPixel), DA(pxaColorMapping)
1253 	};
1254 
1255 	PX_PUT_LIT(s, mi_);
1256     }
1257     pclxl_write_begin_image(xdev, w, h, w, h);
1258     pclxl_write_image_data(xdev, data, data_x, raster, w, 0, h);
1259     pclxl_write_end_image(xdev);
1260     return 0;
1261 }
1262 
1263 /* Copy a color bitmap. */
1264 private int
pclxl_copy_color(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h)1265 pclxl_copy_color(gx_device * dev,
1266 		 const byte * base, int sourcex, int raster, gx_bitmap_id id,
1267 		 int x, int y, int w, int h)
1268 {
1269     gx_device_vector *const vdev = (gx_device_vector *)dev;
1270     gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
1271     stream *s;
1272     uint source_bit;
1273     int code;
1274 
1275     fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
1276     code = gdev_vector_update_clip_path(vdev, NULL);
1277     if (code < 0)
1278 	return code;
1279     source_bit = sourcex * dev->color_info.depth;
1280     if ((source_bit & 7) != 0)
1281 	return gx_default_copy_color(dev, base, sourcex, raster, id,
1282 				     x, y, w, h);
1283     gdev_vector_update_log_op(vdev, rop3_S);
1284     pclxl_set_cursor(xdev, x, y);
1285     s = pclxl_stream(xdev);
1286     {
1287 	static const byte ci_[] = {
1288 	    DA(pxaColorDepth),
1289 	    DUB(eDirectPixel), DA(pxaColorMapping)
1290 	};
1291 
1292 	px_put_ub(s, eBit_values[dev->color_info.depth /
1293 				 dev->color_info.num_components]);
1294 	PX_PUT_LIT(s, ci_);
1295     }
1296     pclxl_write_begin_image(xdev, w, h, w, h);
1297     pclxl_write_image_data(xdev, base, source_bit, raster,
1298 			   w * dev->color_info.depth, 0, h);
1299     pclxl_write_end_image(xdev);
1300     return 0;
1301 }
1302 
1303 /* Fill a mask. */
1304 private int
pclxl_fill_mask(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int w,int h,const gx_drawing_color * pdcolor,int depth,gs_logical_operation_t lop,const gx_clip_path * pcpath)1305 pclxl_fill_mask(gx_device * dev,
1306 		const byte * data, int data_x, int raster, gx_bitmap_id id,
1307 		int x, int y, int w, int h,
1308 		const gx_drawing_color * pdcolor, int depth,
1309 		gs_logical_operation_t lop, const gx_clip_path * pcpath)
1310 {
1311     gx_device_vector *const vdev = (gx_device_vector *)dev;
1312     gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
1313     int code;
1314     stream *s;
1315 
1316     fit_copy(dev, data, data_x, raster, id, x, y, w, h);
1317     if ((data_x & 7) != 0 || !gx_dc_is_pure(pdcolor) || depth > 1)
1318 	return gx_default_fill_mask(dev, data, data_x, raster, id,
1319 				    x, y, w, h, pdcolor, depth,
1320 				    lop, pcpath);
1321     code = gdev_vector_update_clip_path(vdev, pcpath);
1322     if (code < 0)
1323 	return code;
1324     code = gdev_vector_update_fill_color(vdev, NULL, pdcolor);
1325     if (code < 0)
1326 	return 0;
1327     pclxl_set_cursor(xdev, x, y);
1328     if (id != gs_no_id && data_x == 0) {
1329 	code = gdev_vector_update_log_op(vdev, lop);
1330 	if (code < 0)
1331 	    return 0;
1332 	if (pclxl_copy_text_char(xdev, data, raster, id, w, h) >= 0)
1333 	    return 0;
1334     }
1335     code = gdev_vector_update_log_op(vdev,
1336 				     lop | rop3_S | lop_S_transparent);
1337     if (code < 0)
1338 	return 0;
1339     pclxl_set_color_palette(xdev, eGray, (const byte *)"\377\000", 2);
1340     s = pclxl_stream(xdev);
1341     {
1342 	static const byte mi_[] = {
1343 	    DUB(e1Bit), DA(pxaColorDepth),
1344 	    DUB(eIndexedPixel), DA(pxaColorMapping)
1345 	};
1346 
1347 	PX_PUT_LIT(s, mi_);
1348     }
1349     pclxl_write_begin_image(xdev, w, h, w, h);
1350     pclxl_write_image_data(xdev, data, data_x, raster, w, 0, h);
1351     pclxl_write_end_image(xdev);
1352     return 0;
1353 }
1354 
1355 /* Do a RasterOp. */
1356 private int
pclxl_strip_copy_rop(gx_device * dev,const byte * sdata,int sourcex,uint sraster,gx_bitmap_id id,const gx_color_index * scolors,const gx_strip_bitmap * textures,const gx_color_index * tcolors,int x,int y,int width,int height,int phase_x,int phase_y,gs_logical_operation_t lop)1357 pclxl_strip_copy_rop(gx_device * dev, const byte * sdata, int sourcex,
1358 		     uint sraster, gx_bitmap_id id,
1359 		     const gx_color_index * scolors,
1360 		     const gx_strip_bitmap * textures,
1361 		     const gx_color_index * tcolors,
1362 		     int x, int y, int width, int height,
1363 		     int phase_x, int phase_y, gs_logical_operation_t lop)
1364 {				/* We can't do general RasterOps yet. */
1365 /****** WORK IN PROGRESS ******/
1366     return 0;
1367 }
1368 
1369 /* ------ High-level images ------ */
1370 
1371 #define MAX_ROW_DATA 4000	/* arbitrary */
1372 typedef struct pclxl_image_enum_s {
1373     gdev_vector_image_enum_common;
1374     gs_matrix mat;
1375     struct ir_ {
1376 	byte *data;
1377 	int num_rows;		/* # of allocated rows */
1378 	int first_y;
1379 	uint raster;
1380     } rows;
1381 } pclxl_image_enum_t;
1382 gs_private_st_suffix_add1(st_pclxl_image_enum, pclxl_image_enum_t,
1383 			  "pclxl_image_enum_t", pclxl_image_enum_enum_ptrs,
1384 			  pclxl_image_enum_reloc_ptrs, st_vector_image_enum,
1385 			  rows.data);
1386 
1387 /* Start processing an image. */
1388 private int
pclxl_begin_image(gx_device * dev,const gs_imager_state * pis,const gs_image_t * pim,gs_image_format_t format,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)1389 pclxl_begin_image(gx_device * dev,
1390 		  const gs_imager_state * pis, const gs_image_t * pim,
1391 		  gs_image_format_t format, const gs_int_rect * prect,
1392 		  const gx_drawing_color * pdcolor,
1393 		  const gx_clip_path * pcpath, gs_memory_t * mem,
1394 		  gx_image_enum_common_t ** pinfo)
1395 {
1396     gx_device_vector *const vdev = (gx_device_vector *)dev;
1397     gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
1398     const gs_color_space *pcs = pim->ColorSpace;
1399     pclxl_image_enum_t *pie;
1400     byte *row_data;
1401     int num_rows;
1402     uint row_raster;
1403     /*
1404      * Following should divide by num_planes, but we only handle chunky
1405      * images, i.e., num_planes = 1.
1406      */
1407     int bits_per_pixel =
1408 	(pim->ImageMask ? 1 :
1409 	 pim->BitsPerComponent * gs_color_space_num_components(pcs));
1410     gs_matrix mat;
1411     int code;
1412 
1413     /*
1414      * Check whether we can handle this image.  PCL XL 1.0 and 2.0 only
1415      * handle orthogonal transformations.
1416      */
1417     gs_matrix_invert(&pim->ImageMatrix, &mat);
1418     gs_matrix_multiply(&mat, &ctm_only(pis), &mat);
1419     /* Currently we only handle portrait transformations. */
1420     if (mat.xx <= 0 || mat.xy != 0 || mat.yx != 0 || mat.yy <= 0 ||
1421 	(pim->ImageMask ?
1422 	 (!gx_dc_is_pure(pdcolor) || pim->CombineWithColor) :
1423 	 (!pclxl_can_handle_color_space(pim->ColorSpace) ||
1424 	  (bits_per_pixel != 1 && bits_per_pixel != 4 &&
1425 	   bits_per_pixel != 8))) ||
1426 	format != gs_image_format_chunky ||
1427 	prect
1428 	)
1429 	goto use_default;
1430     row_raster = (bits_per_pixel * pim->Width + 7) >> 3;
1431     num_rows = MAX_ROW_DATA / row_raster;
1432     if (num_rows > pim->Height)
1433 	num_rows = pim->Height;
1434     if (num_rows <= 0)
1435 	num_rows = 1;
1436     pie = gs_alloc_struct(mem, pclxl_image_enum_t, &st_pclxl_image_enum,
1437 			  "pclxl_begin_image");
1438     row_data = gs_alloc_bytes(mem, num_rows * row_raster,
1439 			      "pclxl_begin_image(rows)");
1440     if (pie == 0 || row_data == 0) {
1441 	code = gs_note_error(gs_error_VMerror);
1442 	goto fail;
1443     }
1444     code = gdev_vector_begin_image(vdev, pis, pim, format, prect,
1445 				   pdcolor, pcpath, mem,
1446 				   &pclxl_image_enum_procs,
1447 				   (gdev_vector_image_enum_t *)pie);
1448     if (code < 0)
1449 	return code;
1450     pie->mat = mat;
1451     pie->rows.data = row_data;
1452     pie->rows.num_rows = num_rows;
1453     pie->rows.first_y = 0;
1454     pie->rows.raster = row_raster;
1455     *pinfo = (gx_image_enum_common_t *) pie;
1456     {
1457 	gs_logical_operation_t lop = pis->log_op;
1458 
1459 	if (pim->ImageMask) {
1460 	    const byte *palette = (const byte *)
1461 		(pim->Decode[0] ? "\377\000" : "\000\377");
1462 
1463 	    code = gdev_vector_update_fill_color(vdev,
1464 	                             NULL, /* use process color */
1465 				     pdcolor);
1466 	    if (code < 0)
1467 		goto fail;
1468 	    code = gdev_vector_update_log_op
1469 		(vdev, lop | rop3_S | lop_S_transparent);
1470 	    if (code < 0)
1471 		goto fail;
1472 	    pclxl_set_color_palette(xdev, eGray, palette, 2);
1473 	} else {
1474 	    int bpc = pim->BitsPerComponent;
1475 	    int num_components = pie->plane_depths[0] * pie->num_planes / bpc;
1476 	    int sample_max = (1 << bpc) - 1;
1477 	    byte palette[256 * 3];
1478 	    int i;
1479 
1480 	    code = gdev_vector_update_log_op
1481 		(vdev, (pim->CombineWithColor ? lop : rop3_know_T_0(lop)));
1482 	    if (code < 0)
1483 		goto fail;
1484 	    for (i = 0; i < 1 << bits_per_pixel; ++i) {
1485 		gs_client_color cc;
1486 		gx_device_color devc;
1487 		int cv = i, j;
1488 		gx_color_index ci;
1489 
1490 		for (j = num_components - 1; j >= 0; cv >>= bpc, --j)
1491 		    cc.paint.values[j] = pim->Decode[j * 2] +
1492 			(cv & sample_max) *
1493 			(pim->Decode[j * 2 + 1] - pim->Decode[j * 2]) /
1494 			sample_max;
1495 		(*pcs->type->remap_color)
1496 		    (&cc, pcs, &devc, pis, dev, gs_color_select_source);
1497 		if (!gx_dc_is_pure(&devc))
1498 		    return_error(gs_error_Fatal);
1499 		ci = gx_dc_pure_color(&devc);
1500 		if (dev->color_info.num_components == 1) {
1501 		    palette[i] = (byte)ci;
1502 		} else {
1503 		    byte *ppal = &palette[i * 3];
1504 
1505 		    ppal[0] = (byte) (ci >> 16);
1506 		    ppal[1] = (byte) (ci >> 8);
1507 		    ppal[2] = (byte) ci;
1508 		}
1509 	    }
1510 	    if (dev->color_info.num_components == 1)
1511 		pclxl_set_color_palette(xdev, eGray, palette,
1512 					1 << bits_per_pixel);
1513 	    else
1514 		pclxl_set_color_palette(xdev, eRGB, palette,
1515 					3 << bits_per_pixel);
1516 	}
1517     }
1518     return 0;
1519  fail:
1520     gs_free_object(mem, row_data, "pclxl_begin_image(rows)");
1521     gs_free_object(mem, pie, "pclxl_begin_image");
1522  use_default:
1523     return gx_default_begin_image(dev, pis, pim, format, prect,
1524 				  pdcolor, pcpath, mem, pinfo);
1525 }
1526 
1527 /* Write one strip of an image, from pie->rows.first_y to pie->y. */
1528 private int
image_transform_x(const pclxl_image_enum_t * pie,int sx)1529 image_transform_x(const pclxl_image_enum_t *pie, int sx)
1530 {
1531     return (int)((pie->mat.tx + sx * pie->mat.xx + 0.5) /
1532 		 ((const gx_device_pclxl *)pie->dev)->scale.x);
1533 }
1534 private int
image_transform_y(const pclxl_image_enum_t * pie,int sy)1535 image_transform_y(const pclxl_image_enum_t *pie, int sy)
1536 {
1537     return (int)((pie->mat.ty + sy * pie->mat.yy + 0.5) /
1538 		 ((const gx_device_pclxl *)pie->dev)->scale.y);
1539 }
1540 private int
pclxl_image_write_rows(pclxl_image_enum_t * pie)1541 pclxl_image_write_rows(pclxl_image_enum_t *pie)
1542 {
1543     gx_device_pclxl *const xdev = (gx_device_pclxl *)pie->dev;
1544     stream *s = pclxl_stream(xdev);
1545     int y = pie->rows.first_y;
1546     int h = pie->y - y;
1547     int xo = image_transform_x(pie, 0);
1548     int yo = image_transform_y(pie, y);
1549     int dw = image_transform_x(pie, pie->width) - xo;
1550     int dh = image_transform_y(pie, y + h) - yo;
1551     static const byte ii_[] = {
1552 	DA(pxaColorDepth),
1553 	DUB(eIndexedPixel), DA(pxaColorMapping)
1554     };
1555 
1556     if (dw <= 0 || dh <= 0)
1557 	return 0;
1558     pclxl_set_cursor(xdev, xo, yo);
1559     px_put_ub(s, eBit_values[pie->bits_per_pixel]);
1560     PX_PUT_LIT(s, ii_);
1561     pclxl_write_begin_image(xdev, pie->width, h, dw, dh);
1562     pclxl_write_image_data(xdev, pie->rows.data, 0, pie->rows.raster,
1563 			   pie->rows.raster << 3, 0, h);
1564     pclxl_write_end_image(xdev);
1565     return 0;
1566 }
1567 
1568 /* Process the next piece of an image. */
1569 private int
pclxl_image_plane_data(gx_image_enum_common_t * info,const gx_image_plane_t * planes,int height,int * rows_used)1570 pclxl_image_plane_data(gx_image_enum_common_t * info,
1571 		       const gx_image_plane_t * planes, int height,
1572 		       int *rows_used)
1573 {
1574     pclxl_image_enum_t *pie = (pclxl_image_enum_t *) info;
1575     int data_bit = planes[0].data_x * info->plane_depths[0];
1576     int width_bits = pie->width * info->plane_depths[0];
1577     int i;
1578 
1579     /****** SHOULD HANDLE NON-BYTE-ALIGNED DATA ******/
1580     if (width_bits != pie->bits_per_row || (data_bit & 7) != 0)
1581 	return_error(gs_error_rangecheck);
1582     if (height > pie->height - pie->y)
1583 	height = pie->height - pie->y;
1584     for (i = 0; i < height; pie->y++, ++i) {
1585 	if (pie->y - pie->rows.first_y == pie->rows.num_rows) {
1586 	    int code = pclxl_image_write_rows(pie);
1587 
1588 	    if (code < 0)
1589 		return code;
1590 	    pie->rows.first_y = pie->y;
1591 	}
1592 	memcpy(pie->rows.data +
1593 	         pie->rows.raster * (pie->y - pie->rows.first_y),
1594 	       planes[0].data + planes[0].raster * i + (data_bit >> 3),
1595 	       pie->rows.raster);
1596     }
1597     *rows_used = height;
1598     return pie->y >= pie->height;
1599 }
1600 
1601 /* Clean up by releasing the buffers. */
1602 private int
pclxl_image_end_image(gx_image_enum_common_t * info,bool draw_last)1603 pclxl_image_end_image(gx_image_enum_common_t * info, bool draw_last)
1604 {
1605     pclxl_image_enum_t *pie = (pclxl_image_enum_t *) info;
1606     int code = 0;
1607 
1608     /* Write the final strip, if any. */
1609     if (pie->y > pie->rows.first_y && draw_last)
1610 	code = pclxl_image_write_rows(pie);
1611     gs_free_object(pie->memory, pie->rows.data, "pclxl_end_image(rows)");
1612     gs_free_object(pie->memory, pie, "pclxl_end_image");
1613     return code;
1614 }
1615 
1616 /* Get parameters. */
1617 int
pclxl_get_params(gx_device * dev,gs_param_list * plist)1618 pclxl_get_params(gx_device *dev, gs_param_list *plist)
1619 {
1620     gx_device_pclxl *pdev = (gx_device_pclxl *) dev;
1621     int code = gdev_vector_get_params(dev, plist);
1622 
1623     if (code < 0)
1624 	return code;
1625 
1626      if (code >= 0)
1627 	code = param_write_bool(plist, "ManualFeed", &pdev->ManualFeed);
1628     return code;
1629 }
1630 
1631 /* Put parameters. */
1632 int
pclxl_put_params(gx_device * dev,gs_param_list * plist)1633 pclxl_put_params(gx_device * dev, gs_param_list * plist)
1634 {
1635     gx_device_pclxl *pdev = (gx_device_pclxl *) dev;
1636     int code = 0;
1637     bool ManualFeed;
1638     bool ManualFeed_set = false;
1639     int MediaPosition;
1640     bool MediaPosition_set = false;
1641 
1642     code = param_read_bool(plist, "ManualFeed", &ManualFeed);
1643     if (code == 0)
1644 	ManualFeed_set = true;
1645     if (code >= 0) {
1646 	code = param_read_int(plist, "%MediaSource", &MediaPosition);
1647 	if (code == 0)
1648 	    MediaPosition_set = true;
1649 	else if (code < 0) {
1650 	    if (param_read_null(plist, "%MediaSource") == 0) {
1651 		code = 0;
1652 	    }
1653 	}
1654     }
1655 
1656     /* note this handles not opening/closing the device */
1657     code = gdev_vector_put_params(dev, plist);
1658     if (code < 0)
1659 	return code;
1660 
1661     if (code >= 0) {
1662 	if (ManualFeed_set) {
1663 	    pdev->ManualFeed = ManualFeed;
1664 	    pdev->ManualFeed_set = true;
1665 	}
1666 	if (MediaPosition_set) {
1667 	    pdev->MediaPosition = MediaPosition;
1668 	    pdev->MediaPosition_set = true;
1669 	}
1670     }
1671     return code;
1672 }
1673