/src/ghostpdl/devices/vector/gdevxps.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | /* XPS output device */ |
17 | | #include "string_.h" |
18 | | #include "stdio_.h" |
19 | | #include "zlib.h" |
20 | | #include "gx.h" |
21 | | #include "gserrors.h" |
22 | | #include "gdevvec.h" |
23 | | #include "gxpath.h" |
24 | | #include "gzcpath.h" |
25 | | #include "stream.h" |
26 | | #include "stdint_.h" |
27 | | #include "gdevtifs.h" |
28 | | #include "gsicc_create.h" |
29 | | #include "gsicc_cache.h" |
30 | | #include "gximdecode.h" /* Need so that we can unpack and decode */ |
31 | | #include "gxpaint.h" |
32 | | |
33 | 9.31k | #define MAXPRINTERNAME 64 |
34 | | |
35 | 4.07k | #define MAXNAME 64 |
36 | 2.03k | #define PROFILEPATH "Documents/1/Resources/Profiles/" |
37 | 2.03k | #define IMAGEPATH "Documents/1/Resources/Images/" |
38 | | |
39 | | /* default resolution. */ |
40 | | #ifndef X_DPI |
41 | | # define X_DPI 96 |
42 | | #endif |
43 | | #ifndef Y_DPI |
44 | | # define Y_DPI 96 |
45 | | #endif |
46 | | |
47 | | /* default constants */ |
48 | 9.31k | #define XPS_DEFAULT_LINEWIDTH 1.0 |
49 | 9.31k | #define XPS_DEFAULT_LINECAP gs_cap_butt |
50 | 9.31k | #define XPS_DEFAULT_LINEJOIN gs_join_miter |
51 | 9.31k | #define XPS_DEFAULT_MITERLIMIT 4.0 |
52 | | |
53 | | /* ---------------- Device definition ---------------- */ |
54 | | |
55 | | /* TODO graphics state definitions are not yet used */ |
56 | | |
57 | | /* xps join and cap values. */ |
58 | | static const char *join_str[] = {"Miter", "Bevel", "Round"}; |
59 | | static const char *cap_str[] = {"Flat", "Round", "Square", "Triangle"}; |
60 | | |
61 | | typedef enum {XPS_JOIN, XPS_START_CAP, XPS_END_CAP, XPS_THICK, XPS_MITER_LIMIT, |
62 | | XPS_STROKE_COLOR, XPS_FILL_COLOR} xps_attr_t; |
63 | | |
64 | | typedef struct gx_device_xps_vals { |
65 | | double f; |
66 | | uint64_t i; |
67 | | } val_t; |
68 | | |
69 | | /* current and default values of state atrributes */ |
70 | | typedef struct gx_device_xps_attr_state { |
71 | | val_t cur; |
72 | | val_t def; |
73 | | const char *fmt; |
74 | | const char **vals; |
75 | | } attr_t; |
76 | | |
77 | | /* xps proto state - a copy of this table is made on the heap when the |
78 | | device is opened and the current state values are filled in at run |
79 | | time. NB not currently used. */ |
80 | | attr_t xps_fixed_state[] = { |
81 | | /* attribute current default Format String values */ |
82 | | {/* XPS_JOIN */ {0.0, 0}, {0.0, 0}, "StrokeLineJoin=\"%s\"", join_str}, |
83 | | {/* XPS_START_CAP */ {0.0, 0}, {0.0, 0}, "StrokeStartLineCap=\"%s\"", cap_str}, |
84 | | {/* XPS_END_CAP */ {0.0, 0}, {0.0, 0}, "StrokeEndLineCap=\"%s\"", cap_str}, |
85 | | {/* XPS_THICK */ {0.0, 0}, {1.0, 0}, "StrokeThickness=\"%f\"", NULL}, |
86 | | {/* XPS_MITER_LIMIT */ {0.0, 0}, {10.0, 0}, "StrokeMiterLimit=\"%f\"", NULL}, |
87 | | {/* XPS_STROKE_COLOR */ {0.0, 0}, {0.0, 0}, "Stroke=\"%X\"", NULL}, |
88 | | {/* XPS_FILL_COLOR */ {0.0, 0}, {0.0, 0}, "Fill=\"%X\"", NULL} |
89 | | }; |
90 | | |
91 | | /* zip file management - a list of temporary files is maintained along |
92 | | with the final filename to be used in the archive. filenames with |
93 | | associated contents in temporary files are maintained until the end |
94 | | of the job, at which time we enumerate the list and write the zip |
95 | | archive. The exception to this is the image data and icc profiles. Image |
96 | | data are stored in temp files and upon end image transferred to the zip |
97 | | archive and the temp file closed (and removed). ICC profiles are written |
98 | | directly to the archive without a temp file. */ |
99 | | |
100 | | typedef struct gx_device_xps_zdata_s { |
101 | | gp_file *fp; |
102 | | ulong count; |
103 | | } gx_device_xps_zdata_t; |
104 | | |
105 | | /* Zip info for each file in the zip archive */ |
106 | | typedef struct gx_device_xps_zinfo_s { |
107 | | ulong CRC; |
108 | | ulong file_size; |
109 | | gx_device_xps_zdata_t data; |
110 | | long current_pos; |
111 | | ushort date; |
112 | | ushort time; |
113 | | bool saved; /* flag to indicate file was already saved (e.g. image or profile) */ |
114 | | } gx_device_xps_zinfo_t; |
115 | | |
116 | | /* a list of archive file names and their corresponding info |
117 | | (gx_device_xps_zinfo_t) */ |
118 | | typedef struct gx_device_xps_f2i_s { |
119 | | char *filename; |
120 | | gx_device_xps_zinfo_t *info; |
121 | | struct gx_device_xps_f2i_s *next; |
122 | | gs_memory_t *memory; |
123 | | } gx_device_xps_f2i_t; |
124 | | |
125 | | /* Used for keeping track of icc profiles that we have written. This way we |
126 | | avoid writing the same one multiple times. It would be nice to do this |
127 | | based upon the gs_id for images, but that id is really created after we |
128 | | have already drawn the path. Not clear to me how to get a source ID. */ |
129 | | typedef struct xps_icc_data_s { |
130 | | int64_t hash; |
131 | | int index; |
132 | | struct xps_icc_data_s *next; |
133 | | } xps_icc_data_t; |
134 | | |
135 | | /* Add new page relationships to this linked list, avoiding duplicates. |
136 | | When page is closed write out all the relationships */ |
137 | | typedef struct xps_relations_s xps_relations_t; |
138 | | |
139 | | struct xps_relations_s { |
140 | | char* relation; |
141 | | xps_relations_t *next; |
142 | | gs_memory_t *memory; |
143 | | }; |
144 | | |
145 | | typedef enum { |
146 | | xps_solidbrush, |
147 | | xps_imagebrush, |
148 | | xps_visualbrush |
149 | | } xps_brush_t; |
150 | | |
151 | | typedef struct xps_image_enum_s { |
152 | | gdev_vector_image_enum_common; |
153 | | gs_matrix mat; |
154 | | TIFF *tif; /* in non-GC memory */ |
155 | | char file_name[MAXNAME]; |
156 | | char icc_name[MAXNAME]; |
157 | | image_decode_t decode_st; |
158 | | int bytes_comp; |
159 | | byte *buffer; /* Needed for unpacking/decoding of image data */ |
160 | | byte *devc_buffer; /* Needed for case where we are mapping to device colors */ |
161 | | gs_color_space *pcs; /* Needed for Sep, DeviceN, Indexed */ |
162 | | gsicc_link_t *icc_link; /* Needed for CIELAB */ |
163 | | gp_file *fid; |
164 | | } xps_image_enum_t; |
165 | | |
166 | | static void |
167 | | xps_image_enum_finalize(const gs_memory_t *cmem, void *vptr); |
168 | | |
169 | | gs_private_st_suffix_add3_final(st_xps_image_enum, xps_image_enum_t, |
170 | | "xps_image_enum_t", xps_image_enum_enum_ptrs, |
171 | | xps_image_enum_reloc_ptrs, xps_image_enum_finalize, st_vector_image_enum, |
172 | | buffer, devc_buffer, pcs); |
173 | | |
174 | | typedef struct gx_device_xps_s { |
175 | | /* superclass state */ |
176 | | gx_device_vector_common; |
177 | | /* zip container bookkeeping */ |
178 | | gx_device_xps_f2i_t *f2i; |
179 | | gx_device_xps_f2i_t *f2i_tail; |
180 | | /* local state */ |
181 | | int page_count; /* how many output_page calls we've seen */ |
182 | | int image_count; /* number of images so far */ |
183 | | xps_relations_t *relations_head; |
184 | | xps_relations_t *relations_tail; |
185 | | xps_icc_data_t *icc_data; |
186 | | gx_color_index strokecolor, fillcolor; |
187 | | xps_brush_t stroketype, filltype; |
188 | | xps_image_enum_t *xps_pie; |
189 | | double linewidth; |
190 | | gs_line_cap linecap; |
191 | | gs_line_join linejoin; |
192 | | double miterlimit; |
193 | | bool can_stroke; |
194 | | unsigned char PrinterName[MAXPRINTERNAME]; |
195 | | bool in_path; |
196 | | bool in_clip; |
197 | | bool clip_written; |
198 | | bool rect_written; |
199 | | } gx_device_xps; |
200 | | |
201 | | gs_public_st_suffix_add1_final(st_device_xps, gx_device_xps, |
202 | | "gx_device_xps", device_xps_enum_ptrs, device_xps_reloc_ptrs, |
203 | | gx_device_finalize, st_device_vector, xps_pie); |
204 | | |
205 | | #define xps_device_body(dname, depth, init)\ |
206 | | std_device_dci_type_body(gx_device_xps, init, dname, &st_device_xps, \ |
207 | | DEFAULT_WIDTH_10THS * X_DPI / 10, \ |
208 | | DEFAULT_HEIGHT_10THS * Y_DPI / 10, \ |
209 | | X_DPI, Y_DPI, \ |
210 | | (depth > 8 ? 3 : 1), depth, \ |
211 | | (depth > 1 ? 255 : 1), (depth > 8 ? 255 : 0), \ |
212 | | (depth > 1 ? 256 : 2), (depth > 8 ? 256 : 1)) |
213 | | |
214 | | static dev_proc_open_device(xps_open_device); |
215 | | static dev_proc_output_page(xps_output_page); |
216 | | static dev_proc_close_device(xps_close_device); |
217 | | static dev_proc_get_params(xps_get_params); |
218 | | static dev_proc_put_params(xps_put_params); |
219 | | static dev_proc_fill_path(gdev_xps_fill_path); |
220 | | static dev_proc_stroke_path(gdev_xps_stroke_path); |
221 | | static dev_proc_initialize_device_procs(xps_initialize_device_procs); |
222 | | static dev_proc_begin_typed_image(xps_begin_typed_image); |
223 | | |
224 | | const gx_device_xps gs_xpswrite_device = { |
225 | | xps_device_body("xpswrite", 24, xps_initialize_device_procs), |
226 | | }; |
227 | | |
228 | | static int |
229 | | xps_initialize_device(gx_device *dev) |
230 | 9.31k | { |
231 | 9.31k | gx_device_xps *xps = (gx_device_xps*)dev; |
232 | | |
233 | 9.31k | memset(xps->PrinterName, 0x00, MAXPRINTERNAME); |
234 | | |
235 | 9.31k | return 0; |
236 | 9.31k | } |
237 | | |
238 | | static void |
239 | | xps_initialize_device_procs(gx_device *dev) |
240 | 9.31k | { |
241 | 9.31k | set_dev_proc(dev, initialize_device, xps_initialize_device); |
242 | 9.31k | set_dev_proc(dev, open_device, xps_open_device); |
243 | 9.31k | set_dev_proc(dev, output_page, xps_output_page); |
244 | 9.31k | set_dev_proc(dev, close_device, xps_close_device); |
245 | 9.31k | set_dev_proc(dev, map_rgb_color, gx_default_rgb_map_rgb_color); |
246 | 9.31k | set_dev_proc(dev, map_color_rgb, gx_default_rgb_map_color_rgb); |
247 | 9.31k | set_dev_proc(dev, fill_rectangle, gdev_vector_fill_rectangle); |
248 | 9.31k | set_dev_proc(dev, get_params, xps_get_params); |
249 | 9.31k | set_dev_proc(dev, put_params, xps_put_params); |
250 | 9.31k | set_dev_proc(dev, get_page_device, gx_page_device_get_page_device); |
251 | 9.31k | set_dev_proc(dev, fill_path, gdev_xps_fill_path); |
252 | 9.31k | set_dev_proc(dev, stroke_path, gdev_xps_stroke_path); |
253 | 9.31k | set_dev_proc(dev, begin_typed_image, xps_begin_typed_image); |
254 | 9.31k | } |
255 | | |
256 | | /* Vector device procedures */ |
257 | | static int |
258 | | xps_beginpage(gx_device_vector *vdev); |
259 | | static int |
260 | | xps_setlinewidth(gx_device_vector *vdev, double width); |
261 | | static int |
262 | | xps_setlinecap(gx_device_vector *vdev, gs_line_cap cap); |
263 | | static int |
264 | | xps_setlinejoin(gx_device_vector *vdev, gs_line_join join); |
265 | | static int |
266 | | xps_setmiterlimit(gx_device_vector *vdev, double limit); |
267 | | static int |
268 | | xps_setdash(gx_device_vector *vdev, const float *pattern, |
269 | | uint count, double offset); |
270 | | static int |
271 | | xps_setlogop(gx_device_vector *vdev, gs_logical_operation_t lop, |
272 | | gs_logical_operation_t diff); |
273 | | |
274 | | static bool |
275 | | xps_can_handle_hl_color(gx_device_vector *vdev, const gs_gstate *pgs, |
276 | | const gx_drawing_color * pdc); |
277 | | static int |
278 | | xps_setfillcolor(gx_device_vector *vdev, const gs_gstate *pgs, |
279 | | const gx_drawing_color *pdc); |
280 | | static int |
281 | | xps_setstrokecolor(gx_device_vector *vdev, const gs_gstate *pgs, |
282 | | const gx_drawing_color *pdc); |
283 | | |
284 | | static int |
285 | | xps_dorect(gx_device_vector *vdev, fixed x0, fixed y0, |
286 | | fixed x1, fixed y1, gx_path_type_t type); |
287 | | static int |
288 | | xps_beginpath(gx_device_vector *vdev, gx_path_type_t type); |
289 | | |
290 | | static int |
291 | | xps_moveto(gx_device_vector *vdev, double x0, double y0, |
292 | | double x, double y, gx_path_type_t type); |
293 | | static int |
294 | | xps_lineto(gx_device_vector *vdev, double x0, double y0, |
295 | | double x, double y, gx_path_type_t type); |
296 | | static int |
297 | | xps_curveto(gx_device_vector *vdev, double x0, double y0, |
298 | | double x1, double y1, double x2, double y2, |
299 | | double x3, double y3, gx_path_type_t type); |
300 | | static int |
301 | | xps_closepath(gx_device_vector *vdev, double x, double y, |
302 | | double x_start, double y_start, gx_path_type_t type); |
303 | | static int |
304 | | xps_endpath(gx_device_vector *vdev, gx_path_type_t type); |
305 | | |
306 | | /* Vector device function table */ |
307 | | static const gx_device_vector_procs xps_vector_procs = { |
308 | | xps_beginpage, |
309 | | xps_setlinewidth, |
310 | | xps_setlinecap, |
311 | | xps_setlinejoin, |
312 | | xps_setmiterlimit, |
313 | | xps_setdash, |
314 | | gdev_vector_setflat, |
315 | | xps_setlogop, |
316 | | xps_can_handle_hl_color, |
317 | | xps_setfillcolor, |
318 | | xps_setstrokecolor, |
319 | | gdev_vector_dopath, |
320 | | xps_dorect, |
321 | | xps_beginpath, |
322 | | xps_moveto, |
323 | | xps_lineto, |
324 | | xps_curveto, |
325 | | xps_closepath, |
326 | | xps_endpath |
327 | | }; |
328 | | |
329 | | /* Various static content we use in all xps jobs. We don't use all of |
330 | | these resource types */ |
331 | | static char *xps_content_types = (char *)"<?xml version=\"1.0\" encoding=\"utf-8\"?>" |
332 | | "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">" |
333 | | "<Default Extension=\"fdseq\" ContentType=\"application/vnd.ms-package.xps-fixeddocumentsequence+xml\" />" |
334 | | "<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\" />" |
335 | | "<Default Extension=\"fdoc\" ContentType=\"application/vnd.ms-package.xps-fixeddocument+xml\" />" |
336 | | "<Default Extension=\"fpage\" ContentType=\"application/vnd.ms-package.xps-fixedpage+xml\" />" |
337 | | "<Default Extension=\"ttf\" ContentType=\"application/vnd.ms-opentype\" />" |
338 | | "<Default Extension = \"icc\" ContentType = \"application/vnd.ms-color.iccprofile\" />" |
339 | | "<Default Extension=\"tif\" ContentType=\"image/tiff\" />" |
340 | | "<Default Extension=\"png\" ContentType=\"image/png\" /></Types>"; |
341 | | |
342 | | static char *fixed_document_sequence = (char *)"<?xml version=\"1.0\" encoding=\"utf-8\"?>" |
343 | | "<FixedDocumentSequence xmlns=\"http://schemas.microsoft.com/xps/2005/06\">" |
344 | | "<DocumentReference Source=\"Documents/1/FixedDocument.fdoc\" />" |
345 | | "</FixedDocumentSequence>"; |
346 | | |
347 | | static char *fixed_document_fdoc_header = (char *)"<?xml version=\"1.0\" encoding=\"utf-8\"?>" |
348 | | "<FixedDocument xmlns=\"http://schemas.microsoft.com/xps/2005/06\">"; |
349 | | |
350 | | static char *rels_header = (char *)"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
351 | | "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n"; |
352 | | |
353 | | static char *rels_fdseq = (char *)"<Relationship Type=\"http://schemas.microsoft.com/xps/2005/06/fixedrepresentation\" " |
354 | | "Target=\"/FixedDocumentSequence.fdseq\" Id=\"Rdd12fb46c1de43ab\" />\n" |
355 | | "</Relationships>\n"; |
356 | | |
357 | | static char *rels_req_type = (char *) "\"http://schemas.microsoft.com/xps/2005/06/required-resource\""; |
358 | | |
359 | | /* Procedures to manage the containing zip archive */ |
360 | | |
361 | | /* Append a node of info for this archive file */ |
362 | | static int |
363 | | zip_new_info_node(gx_device_xps *xps_dev, const char *filename) |
364 | 49.6k | { |
365 | 49.6k | gx_device *dev = (gx_device *)xps_dev; |
366 | 49.6k | gs_memory_t *mem = dev->memory; |
367 | 49.6k | int lenstr; |
368 | | |
369 | | /* NB should use GC */ |
370 | 49.6k | gx_device_xps_zinfo_t *info = |
371 | 49.6k | (gx_device_xps_zinfo_t *)gs_alloc_bytes(mem->non_gc_memory, sizeof(gx_device_xps_zinfo_t), "zinfo"); |
372 | 49.6k | gx_device_xps_f2i_t *f2i = |
373 | 49.6k | (gx_device_xps_f2i_t *)gs_alloc_bytes(mem->non_gc_memory, sizeof(gx_device_xps_f2i_t), "zinfo node"); |
374 | | |
375 | 49.6k | if_debug1m('_', dev->memory, "new node %s\n", filename); |
376 | | |
377 | 49.6k | if (info == NULL || f2i == NULL) |
378 | 0 | return gs_throw_code(gs_error_VMerror); |
379 | | |
380 | 49.6k | f2i->info = info; |
381 | 49.6k | f2i->next = NULL; |
382 | 49.6k | f2i->memory = mem->non_gc_memory; |
383 | | |
384 | 49.6k | if (xps_dev->f2i == 0) { /* no head */ |
385 | 9.31k | xps_dev->f2i = f2i; |
386 | 9.31k | xps_dev->f2i_tail = f2i; |
387 | 40.3k | } else { /* append the node */ |
388 | 40.3k | xps_dev->f2i_tail->next = f2i; |
389 | 40.3k | xps_dev->f2i_tail = f2i; |
390 | 40.3k | } |
391 | | |
392 | 49.6k | lenstr = strlen(filename); |
393 | 49.6k | f2i->filename = (char*)gs_alloc_bytes(mem->non_gc_memory, lenstr + 1, "zinfo_filename"); |
394 | 49.6k | if (f2i->filename == NULL) |
395 | 0 | return gs_throw_code(gs_error_VMerror); |
396 | 49.6k | strcpy(f2i->filename, filename); |
397 | | |
398 | 49.6k | info->data.fp = 0; |
399 | 49.6k | info->data.count = 0; |
400 | 49.6k | info->saved = false; |
401 | | |
402 | 49.6k | if (gs_debug_c('_')) { |
403 | 0 | gx_device_xps_f2i_t *f2i = xps_dev->f2i; |
404 | | #ifdef DEBUG |
405 | | int node = 1; |
406 | | gx_device_xps_f2i_t *prev_f2i; |
407 | | #endif |
408 | |
|
409 | 0 | while (f2i != NULL) { |
410 | 0 | if_debug2m('_', dev->memory, "node:%d %s\n", node++, f2i->filename); |
411 | | #ifdef DEBUG |
412 | | prev_f2i = f2i; |
413 | | #endif |
414 | 0 | f2i=f2i->next; |
415 | 0 | } |
416 | 0 | if_debug1m('_', dev->memory, "tail okay=%s\n", prev_f2i == xps_dev->f2i_tail ? "yes" : "no"); |
417 | 0 | } |
418 | 49.6k | return 0; |
419 | 49.6k | } |
420 | | |
421 | | /* add a new file to the zip archive */ |
422 | | static int |
423 | | zip_add_file(gx_device_xps *xps_dev, const char *filename) |
424 | 49.6k | { |
425 | 49.6k | int code = zip_new_info_node(xps_dev, filename); |
426 | 49.6k | if (code < 0) |
427 | 0 | return gs_throw_code(gs_error_Fatal); |
428 | 49.6k | return 0; |
429 | 49.6k | } |
430 | | |
431 | | /* look up the information associated with the zip archive [filename] */ |
432 | | static gx_device_xps_zinfo_t * |
433 | | zip_look_up_file_info(gx_device_xps *xps_dev, const char *filename) |
434 | 63.8M | { |
435 | 63.8M | gx_device_xps_f2i_t *cur = xps_dev->f2i; |
436 | 429M | while (cur) { |
437 | 429M | if (!strcmp(cur->filename, filename)) |
438 | 63.7M | break; |
439 | 365M | cur = cur->next; |
440 | 365M | } |
441 | 63.8M | return (cur ? cur->info : NULL); |
442 | 63.8M | } |
443 | | |
444 | | /* Add data to an archived zip file, create the file if it doesn't exist */ |
445 | | static int |
446 | | zip_append_data(gs_memory_t *mem, gx_device_xps_zinfo_t *info, byte *data, uint len) |
447 | 63.6M | { |
448 | 63.6M | uint count; |
449 | | |
450 | | /* if there is no data then this is the first call for this |
451 | | archive file, open a temporary file to store the data. */ |
452 | 63.6M | if (info->data.count == 0) { |
453 | 46.1k | char *filename = |
454 | 46.1k | (char *)gs_alloc_bytes(mem->non_gc_memory, gp_file_name_sizeof, |
455 | 46.1k | "zip_append_data(filename)"); |
456 | 46.1k | gp_file *fp; |
457 | | |
458 | 46.1k | if (!filename) { |
459 | 0 | return(gs_throw_code(gs_error_VMerror)); |
460 | 0 | } |
461 | | |
462 | 46.1k | fp = gp_open_scratch_file_rm(mem, "xpsdata-", |
463 | 46.1k | filename, "wb+"); |
464 | 46.1k | gs_free_object(mem->non_gc_memory, filename, "zip_append_data(filename)"); |
465 | 46.1k | info->data.fp = fp; |
466 | 46.1k | } |
467 | | |
468 | | /* shouldn't happen unless the first call opens the temporary file |
469 | | but writes no data. */ |
470 | 63.6M | if (info->data.fp == NULL) |
471 | 0 | return gs_throw_code(gs_error_Fatal); |
472 | | |
473 | 63.6M | count = gp_fwrite(data, 1, len, info->data.fp); |
474 | 63.6M | if (count != len) { |
475 | 0 | gp_fclose(info->data.fp); |
476 | 0 | return -1; |
477 | 0 | } |
478 | | /* probably unnecessary but makes debugging easier */ |
479 | 63.6M | gp_fflush(info->data.fp); |
480 | 63.6M | info->data.count += len; |
481 | | |
482 | 63.6M | return 0; |
483 | 63.6M | } |
484 | | |
485 | | /* write to one of the archives (filename) in the zip archive */ |
486 | | static int |
487 | | write_to_zip_file(gx_device_xps *xps_dev, const char *filename, |
488 | | byte *data, uint len) |
489 | 63.6M | { |
490 | 63.6M | gx_device *dev = (gx_device *)xps_dev; |
491 | 63.6M | gs_memory_t *mem = dev->memory; |
492 | | |
493 | 63.6M | gx_device_xps_zinfo_t *info = zip_look_up_file_info(xps_dev, filename); |
494 | 63.6M | int code = 0; |
495 | | |
496 | | /* No information on this archive file, create a new zip entry |
497 | | info node */ |
498 | 63.6M | if (info == NULL) { |
499 | 46.1k | code = zip_add_file(xps_dev, filename); |
500 | 46.1k | if (code < 0) |
501 | 0 | return gs_rethrow_code(code); |
502 | 46.1k | info = zip_look_up_file_info(xps_dev, filename); |
503 | 46.1k | } |
504 | | |
505 | 63.6M | if (info == NULL) |
506 | 0 | return gs_throw_code(gs_error_Fatal); |
507 | | |
508 | 63.6M | code = zip_append_data(mem, info, data, len); |
509 | 63.6M | if (code < 0) |
510 | 0 | return gs_rethrow_code(code); |
511 | | |
512 | 63.6M | return code; |
513 | 63.6M | } |
514 | | |
515 | | static void |
516 | | put_bytes(stream *zs, byte *buf, uint len) |
517 | 397M | { |
518 | 397M | uint used; |
519 | 397M | sputs(zs, buf, len, &used); |
520 | | /* NB return value */ |
521 | 397M | } |
522 | | |
523 | | static void |
524 | | put_u32(stream *zs, unsigned long l) |
525 | 524k | { |
526 | 524k | sputc(zs, (byte)(l)); |
527 | 524k | sputc(zs, (byte)(l >> 8)); |
528 | 524k | sputc(zs, (byte)(l >> 16)); |
529 | 524k | sputc(zs, (byte)(l >> 24)); |
530 | 524k | } |
531 | | |
532 | | static void |
533 | | put_u16(stream *zs, ushort s) |
534 | 940k | { |
535 | 940k | sputc(zs, (byte)(s)); |
536 | 940k | sputc(zs, (byte)(s >> 8)); |
537 | 940k | } |
538 | | |
539 | | /* TODO - the following 2 definitions need to done correctly */ |
540 | | static ushort |
541 | | make_dos_date(uint year, uint month, uint day) |
542 | 49.6k | { |
543 | 49.6k | uint delta_1980 = year - 1980; |
544 | 49.6k | return (delta_1980 << 9 | month << 5 | day); |
545 | 49.6k | } |
546 | | |
547 | | static ushort |
548 | | make_dos_time(uint hour, uint min, uint sec) |
549 | 49.6k | { |
550 | | /* note the seconds are multiplied by 2 */ |
551 | 49.6k | return (hour << 11 | min << 5 | sec >> 1); |
552 | 49.6k | } |
553 | | |
554 | | /* convenience routine. */ |
555 | | static int |
556 | | write_str_to_zip_file(gx_device_xps *xps_dev, const char *filename, |
557 | | const char *str) |
558 | 63.6M | { |
559 | 63.6M | return write_to_zip_file(xps_dev, filename, (byte *)str, strlen(str)); |
560 | 63.6M | } |
561 | | |
562 | | /* Used to add ICC profiles to the zip file. */ |
563 | | static int |
564 | | add_data_to_zip_file(gx_device_xps *xps_dev, const char *filename, byte *buf, long size) |
565 | 1.50k | { |
566 | 1.50k | gx_device_xps_zinfo_t *info = zip_look_up_file_info(xps_dev, filename); |
567 | 1.50k | int code; |
568 | 1.50k | long curr_pos; |
569 | 1.50k | unsigned long crc = 0; |
570 | 1.50k | stream *f; |
571 | 1.50k | ushort date, time; |
572 | | |
573 | | /* This file should not yet exist */ |
574 | 1.50k | if (info == NULL) { |
575 | 1.50k | code = zip_add_file(xps_dev, filename); |
576 | 1.50k | if (code < 0) |
577 | 0 | return gs_rethrow_code(code); |
578 | 1.50k | info = zip_look_up_file_info(xps_dev, filename); |
579 | 1.50k | } |
580 | 0 | else { |
581 | 0 | return gs_throw_code(gs_error_Fatal); |
582 | 0 | } |
583 | 1.50k | f = ((gx_device_vector*)xps_dev)->strm; |
584 | 1.50k | curr_pos = stell(f); |
585 | | |
586 | | /* Figure out the crc */ |
587 | 1.50k | crc = crc32(0L, Z_NULL, 0); |
588 | 1.50k | crc = crc32(crc, buf, size); |
589 | | |
590 | 1.50k | date = make_dos_date(2012, 2, 16); |
591 | 1.50k | time = make_dos_time(9, 15, 0); |
592 | | |
593 | 1.50k | put_u32(f, 0x04034b50); /* magic */ |
594 | 1.50k | put_u16(f, 20); /* version */ |
595 | 1.50k | put_u16(f, 0); /* flags */ |
596 | 1.50k | put_u16(f, 0); /* method */ |
597 | 1.50k | put_u16(f, time); |
598 | 1.50k | put_u16(f, date); |
599 | 1.50k | put_u32(f, crc); |
600 | 1.50k | put_u32(f, size); /* compressed */ |
601 | 1.50k | put_u32(f, size); /* uncompressed */ |
602 | 1.50k | put_u16(f, strlen(filename)); |
603 | 1.50k | put_u16(f, 0); /* extra field length */ |
604 | 1.50k | put_bytes(f, (byte *)filename, strlen(filename)); |
605 | 1.50k | put_bytes(f, buf, size); |
606 | 1.50k | put_bytes(f, 0, 0); /* extra field */ |
607 | | |
608 | | /* Now we need to add the info about this file */ |
609 | 1.50k | xps_dev->f2i_tail->info->CRC = crc; |
610 | 1.50k | xps_dev->f2i_tail->info->time = time; |
611 | 1.50k | xps_dev->f2i_tail->info->date = date; |
612 | 1.50k | xps_dev->f2i_tail->info->data.count = size; |
613 | 1.50k | xps_dev->f2i_tail->info->current_pos = curr_pos; |
614 | 1.50k | xps_dev->f2i_tail->info->file_size = size; |
615 | | /* Mark the file as already stored */ |
616 | 1.50k | xps_dev->f2i_tail->info->saved = true; |
617 | 1.50k | return 0; |
618 | 1.50k | } |
619 | | |
620 | | /* Used to add images to the zip file. This file is added now |
621 | | and not later like the other files */ |
622 | | static int |
623 | | add_file_to_zip_file(gx_device_xps *xps_dev, const char *filename, gp_file *src) |
624 | 2.03k | { |
625 | 2.03k | gx_device_xps_zinfo_t *info = zip_look_up_file_info(xps_dev, filename); |
626 | 2.03k | int code = 0; |
627 | 2.03k | long curr_pos; |
628 | 2.03k | unsigned long crc = 0; |
629 | 2.03k | byte buf[4]; |
630 | 2.03k | uint nread; |
631 | 2.03k | unsigned long count = 0; |
632 | 2.03k | stream *f; |
633 | 2.03k | ushort date, time; |
634 | | |
635 | | /* This file should not yet exist */ |
636 | 2.03k | if (info == NULL) { |
637 | 2.03k | code = zip_add_file(xps_dev, filename); |
638 | 2.03k | if (code < 0) |
639 | 0 | return gs_rethrow_code(code); |
640 | 2.03k | info = zip_look_up_file_info(xps_dev, filename); |
641 | 2.03k | } |
642 | 0 | else { |
643 | 0 | return gs_throw_code(gs_error_Fatal); |
644 | 0 | } |
645 | | |
646 | 2.03k | f = ((gx_device_vector*)xps_dev)->strm; |
647 | 2.03k | curr_pos = stell(f); |
648 | | |
649 | | /* Get to the start */ |
650 | 2.03k | if (gp_fseek(src, 0, 0) < 0) |
651 | 0 | return gs_throw_code(gs_error_Fatal); |
652 | | |
653 | | /* Figure out the crc */ |
654 | 2.03k | crc = crc32(0L, Z_NULL, 0); |
655 | | /* Chunks of 4 until we get to remainder */ |
656 | 59.9M | while (!gp_feof(src)) { |
657 | 59.9M | nread = gp_fread(buf, 1, sizeof(buf), src); |
658 | 59.9M | count = count + nread; |
659 | 59.9M | crc = crc32(crc, buf, nread); |
660 | 59.9M | } |
661 | | |
662 | 2.03k | date = make_dos_date(2012, 2, 16); |
663 | 2.03k | time = make_dos_time(9, 15, 0); |
664 | | |
665 | 2.03k | put_u32(f, 0x04034b50); /* magic */ |
666 | 2.03k | put_u16(f, 20); /* version */ |
667 | 2.03k | put_u16(f, 0); /* flags */ |
668 | 2.03k | put_u16(f, 0); /* method */ |
669 | 2.03k | put_u16(f, time); |
670 | 2.03k | put_u16(f, date); |
671 | 2.03k | put_u32(f, crc); |
672 | 2.03k | put_u32(f, count); /* compressed */ |
673 | 2.03k | put_u32(f, count); /* uncompressed */ |
674 | 2.03k | put_u16(f, strlen(filename)); |
675 | 2.03k | put_u16(f, 0); /* extra field length */ |
676 | 2.03k | put_bytes(f, (byte *)filename, strlen(filename)); |
677 | 2.03k | { |
678 | 2.03k | if (gp_fseek(src, (gs_offset_t)0, 0) < 0) |
679 | 0 | return gs_throw_code(gs_error_Fatal); |
680 | 59.9M | while (!gp_feof(src)) { |
681 | 59.9M | ulong nread = gp_fread(buf, 1, sizeof(buf), src); |
682 | 59.9M | put_bytes(f, buf, nread); |
683 | 59.9M | } |
684 | 2.03k | } |
685 | 0 | put_bytes(f, 0, 0); /* extra field */ |
686 | | |
687 | | /* Now we need to add the info about this file */ |
688 | 2.03k | xps_dev->f2i_tail->info->CRC = crc; |
689 | 2.03k | xps_dev->f2i_tail->info->time = time; |
690 | 2.03k | xps_dev->f2i_tail->info->date = date; |
691 | 2.03k | xps_dev->f2i_tail->info->data.count = count; |
692 | 2.03k | xps_dev->f2i_tail->info->current_pos = curr_pos; |
693 | 2.03k | xps_dev->f2i_tail->info->file_size = count; |
694 | | /* Mark the file as already stored */ |
695 | 2.03k | xps_dev->f2i_tail->info->saved = true; |
696 | 2.03k | return 0; |
697 | 2.03k | } |
698 | | |
699 | | /* zip up a single file moving its data from the temporary file to the |
700 | | zip container. */ |
701 | | static int |
702 | | zip_close_archive_file(gx_device_xps *xps_dev, const char *filename) |
703 | 49.6k | { |
704 | 49.6k | gx_device_xps_zinfo_t *info = zip_look_up_file_info(xps_dev, filename); |
705 | 49.6k | gx_device_xps_zdata_t data; |
706 | 49.6k | byte buf[4]; |
707 | 49.6k | unsigned long crc = 0; |
708 | 49.6k | int count = 0; |
709 | 49.6k | int len; |
710 | | /* we can't use the vector stream accessor because it calls beginpage */ |
711 | 49.6k | stream *f = ((gx_device_vector*)xps_dev)->strm; |
712 | | |
713 | 49.6k | if (info == NULL) |
714 | 0 | return -1; |
715 | | |
716 | | /* Already stored */ |
717 | 49.6k | if (info->saved) |
718 | 3.54k | return 0; |
719 | | |
720 | 46.1k | data = info->data; |
721 | 46.1k | if ((int)data.count >= 0) { |
722 | 46.1k | gp_file *fp = data.fp; |
723 | 46.1k | uint nread; |
724 | | |
725 | 46.1k | if (fp == NULL) |
726 | 0 | return gs_throw_code(gs_error_Fatal); |
727 | | |
728 | 46.1k | crc = crc32(0L, Z_NULL, 0); |
729 | 46.1k | gp_rewind(fp); |
730 | 337M | while (!gp_feof(fp)) { |
731 | 336M | nread = gp_fread(buf, 1, sizeof(buf), fp); |
732 | 336M | crc = crc32(crc, buf, nread); |
733 | 336M | count = count + nread; |
734 | 336M | } |
735 | | /* If this is a TIFF file, then update the data count information. |
736 | | During the writing of the TIFF directory there is seeking and |
737 | | relocations that occur, which make data.count incorrect. |
738 | | We could just do this for all the files and avoid the test. */ |
739 | 46.1k | len = strlen(filename); |
740 | 46.1k | if (len > 3 && (strncmp("tif", &(filename[len - 3]), 3) == 0)) { |
741 | 0 | info->data.count = count; |
742 | 0 | data = info->data; |
743 | 0 | data.fp = fp; |
744 | 0 | } |
745 | 46.1k | } |
746 | | |
747 | 46.1k | info->current_pos = stell(f); |
748 | 46.1k | info->CRC = crc; |
749 | | |
750 | | /* NB should use ghostscript "gp" time and date functions for |
751 | | this, for now we hardwire the dates. */ |
752 | 46.1k | info->date = make_dos_date(2012, 2, 16); |
753 | 46.1k | info->time = make_dos_time(9, 15, 0); |
754 | | |
755 | 46.1k | put_u32(f, 0x04034b50); /* magic */ |
756 | 46.1k | put_u16(f, 20); /* version */ |
757 | 46.1k | put_u16(f, 0); /* flags */ |
758 | 46.1k | put_u16(f, 0); /* method */ |
759 | 46.1k | put_u16(f, info->time); |
760 | 46.1k | put_u16(f, info->date); |
761 | 46.1k | put_u32(f, crc); |
762 | 46.1k | put_u32(f, data.count); /* compressed */ |
763 | 46.1k | put_u32(f, data.count); /* uncompressed */ |
764 | 46.1k | put_u16(f, strlen(filename)); |
765 | 46.1k | put_u16(f, 0); /* extra field length */ |
766 | 46.1k | put_bytes(f, (byte *)filename, strlen(filename)); |
767 | 46.1k | { |
768 | 46.1k | gp_file *fp = data.fp; |
769 | 46.1k | gp_rewind(fp); |
770 | 337M | while (!gp_feof(fp)) { |
771 | 336M | ulong nread = gp_fread(buf, 1, sizeof(buf), fp); |
772 | 336M | put_bytes(f, buf, nread); |
773 | 336M | } |
774 | 46.1k | gp_fclose(fp); |
775 | 46.1k | } |
776 | 46.1k | put_bytes(f, 0, 0); /* extra field */ |
777 | | |
778 | | /* Mark as saved */ |
779 | 46.1k | info->saved = true; |
780 | 46.1k | return 0; |
781 | 46.1k | } |
782 | | |
783 | | /* walk the file info node list writing all the files to the |
784 | | archive */ |
785 | | static int |
786 | | zip_close_all_archive_files(gx_device_xps *xps_dev) |
787 | 9.31k | { |
788 | 9.31k | gx_device_xps_f2i_t *f2i = xps_dev->f2i; |
789 | 58.9k | while (f2i) { |
790 | 49.6k | int code = zip_close_archive_file(xps_dev, f2i->filename); |
791 | 49.6k | if (code < 0) { |
792 | 0 | return code; |
793 | 0 | } |
794 | 49.6k | f2i = f2i->next; |
795 | 49.6k | } |
796 | 9.31k | return 0; |
797 | 9.31k | } |
798 | | |
799 | | /* write all files to the zip container and write the zip central |
800 | | directory */ |
801 | | static int |
802 | | zip_close_archive(gx_device_xps *xps_dev) |
803 | 9.31k | { |
804 | 9.31k | gx_device_xps_f2i_t *f2i = xps_dev->f2i; |
805 | | |
806 | 9.31k | int entry_count = 0; |
807 | 9.31k | long pos_before_cd; |
808 | 9.31k | long pos_after_cd; |
809 | | /* we can't use the vector stream accessor because it calls beginpage */ |
810 | 9.31k | stream *f = ((gx_device_vector*)xps_dev)->strm; |
811 | 9.31k | int code = zip_close_all_archive_files(xps_dev); |
812 | | |
813 | 9.31k | pos_before_cd = stell(f); |
814 | | |
815 | 9.31k | if (code < 0) |
816 | 0 | return code; |
817 | | |
818 | 58.9k | while (f2i) { |
819 | 49.6k | gx_device_xps_zinfo_t *info = f2i->info; |
820 | 49.6k | put_u32(f, 0x02014b50); /* magic */ |
821 | 49.6k | put_u16(f, 20); /* version made by */ |
822 | 49.6k | put_u16(f, 20); /* version required */ |
823 | 49.6k | put_u16(f, 0); /* bit flag */ |
824 | 49.6k | put_u16(f, 0); /* compression method */ |
825 | 49.6k | put_u16(f, info->time); |
826 | 49.6k | put_u16(f, info->date); |
827 | 49.6k | put_u32(f, info->CRC); |
828 | 49.6k | put_u32(f, info->data.count); /* compressed */ |
829 | 49.6k | put_u32(f, info->data.count); /* uncompressed */ |
830 | 49.6k | put_u16(f, strlen(f2i->filename)); |
831 | | |
832 | | /* probably will never use the next 6 so we just set them to |
833 | | zero here */ |
834 | 49.6k | put_u16(f, 0); /* extra field length */ |
835 | 49.6k | put_u16(f, 0); /* file comment length */ |
836 | 49.6k | put_u16(f, 0); /* disk number */ |
837 | 49.6k | put_u16(f, 0); /* internal file attributes */ |
838 | 49.6k | put_u32(f, 0); /* external file attributes */ |
839 | | |
840 | 49.6k | put_u32(f, info->current_pos); |
841 | 49.6k | put_bytes(f, (byte *)f2i->filename, strlen(f2i->filename)); |
842 | | |
843 | 49.6k | put_bytes(f, 0, 0); /* extra field */ |
844 | 49.6k | put_bytes(f, 0, 0); /* extra comment */ |
845 | | |
846 | 49.6k | entry_count++; |
847 | 49.6k | f2i = f2i->next; |
848 | 49.6k | } |
849 | | |
850 | 9.31k | pos_after_cd = stell(f); |
851 | | |
852 | 9.31k | put_u32(f, 0x06054b50); |
853 | 9.31k | put_u16(f, 0); /* number of disks */ |
854 | 9.31k | put_u16(f, 0); /* disk where central directory starts */ |
855 | 9.31k | put_u16(f, entry_count); /* # of records in cd */ |
856 | 9.31k | put_u16(f, entry_count); /* total # of records in cd */ |
857 | 9.31k | put_u32(f, pos_after_cd - pos_before_cd); /* size of central cd */ |
858 | 9.31k | put_u32(f, pos_before_cd); /* offset of central directory */ |
859 | 9.31k | put_u16(f, 0); /* comment length */ |
860 | 9.31k | put_bytes(f, 0, 0); /* comment */ |
861 | | |
862 | 9.31k | return 0; |
863 | 9.31k | } |
864 | | |
865 | | /* Brush management */ |
866 | | static void |
867 | | xps_setstrokebrush(gx_device_xps *xps, xps_brush_t type) |
868 | 13.3k | { |
869 | 13.3k | if_debug1m('_', xps->memory, "xps_setstrokebrush:%d\n", (int)type); |
870 | | |
871 | 13.3k | xps->stroketype = type; |
872 | 13.3k | } |
873 | | |
874 | | static void |
875 | | xps_setfillbrush(gx_device_xps *xps, xps_brush_t type) |
876 | 13.3k | { |
877 | 13.3k | if_debug1m('_', xps->memory, "xps_setfillbrush:%d\n", (int)type); |
878 | | |
879 | 13.3k | xps->filltype = type; |
880 | 13.3k | } |
881 | | |
882 | | /* Device procedures */ |
883 | | static int |
884 | | xps_open_device(gx_device *dev) |
885 | 9.31k | { |
886 | 9.31k | gx_device_vector * vdev = (gx_device_vector*)dev; |
887 | 9.31k | gx_device_xps * xps = (gx_device_xps*)dev; |
888 | 9.31k | int code = 0; |
889 | | |
890 | 9.31k | vdev->v_memory = dev->memory; |
891 | 9.31k | vdev->vec_procs = &xps_vector_procs; |
892 | 9.31k | gdev_vector_init(vdev); |
893 | 9.31k | code = gdev_vector_open_file_options(vdev, 512, |
894 | 9.31k | VECTOR_OPEN_FILE_SEQUENTIAL); |
895 | 9.31k | if (code < 0) |
896 | 0 | return gs_rethrow_code(code); |
897 | | |
898 | | /* In case ths device has been subclassed, descend to the terminal |
899 | | * of the chain. (Setting the variables in a subclassing device |
900 | | * doesn't do anythign useful.....) |
901 | | */ |
902 | 9.31k | while(vdev->child) |
903 | 0 | vdev = (gx_device_vector *)vdev->child; |
904 | 9.31k | xps = (gx_device_xps*)vdev; |
905 | | |
906 | | /* xps-specific initialization goes here */ |
907 | 9.31k | xps->page_count = 0; |
908 | 9.31k | xps->relations_head = NULL; |
909 | 9.31k | xps->relations_tail = NULL; |
910 | 9.31k | xps->strokecolor = gx_no_color_index; |
911 | 9.31k | xps->fillcolor = gx_no_color_index; |
912 | 9.31k | xps_setstrokebrush(xps, xps_solidbrush); |
913 | 9.31k | xps_setfillbrush(xps, xps_solidbrush); |
914 | | /* these should be the graphics library defaults instead? */ |
915 | 9.31k | xps->linewidth = XPS_DEFAULT_LINEWIDTH; |
916 | 9.31k | xps->linecap = XPS_DEFAULT_LINECAP; |
917 | 9.31k | xps->linejoin = XPS_DEFAULT_LINEJOIN; |
918 | 9.31k | xps->miterlimit = XPS_DEFAULT_MITERLIMIT; |
919 | 9.31k | xps->can_stroke = true; |
920 | 9.31k | xps->in_path = false; |
921 | 9.31k | xps->in_clip = false; |
922 | 9.31k | xps->clip_written = false; |
923 | 9.31k | xps->rect_written = false; |
924 | | /* zip info */ |
925 | 9.31k | xps->f2i = NULL; |
926 | 9.31k | xps->f2i_tail = NULL; |
927 | | |
928 | | /* Image related stuff */ |
929 | 9.31k | xps->image_count = 0; |
930 | 9.31k | xps->xps_pie = NULL; |
931 | | |
932 | | /* ICC related stuff */ |
933 | 9.31k | xps->icc_data = NULL; |
934 | | |
935 | 9.31k | code = write_str_to_zip_file(xps, (char *)"FixedDocumentSequence.fdseq", |
936 | 9.31k | fixed_document_sequence); |
937 | 9.31k | if (code < 0) |
938 | 0 | return gs_rethrow_code(code); |
939 | | |
940 | 9.31k | code = write_str_to_zip_file(xps, (char *)"[Content_Types].xml", |
941 | 9.31k | xps_content_types); |
942 | 9.31k | if (code < 0) |
943 | 0 | return gs_rethrow_code(code); |
944 | | |
945 | 9.31k | code = write_str_to_zip_file(xps, |
946 | 9.31k | (char *)"Documents/1/FixedDocument.fdoc", |
947 | 9.31k | fixed_document_fdoc_header); |
948 | 9.31k | if (code < 0) |
949 | 0 | return gs_rethrow_code(code); |
950 | | |
951 | | /* Right out the magical stuff related to the fix document sequence relationship */ |
952 | 9.31k | code = write_str_to_zip_file(xps, (char *)"_rels/.rels", rels_header); |
953 | 9.31k | if (code < 0) |
954 | 0 | return gs_rethrow_code(code); |
955 | | |
956 | 9.31k | code = write_str_to_zip_file(xps, (char *)"_rels/.rels", rels_fdseq); |
957 | 9.31k | if (code < 0) |
958 | 0 | return gs_rethrow_code(code); |
959 | | |
960 | 9.31k | return code; |
961 | 9.31k | } |
962 | | |
963 | | /* write xps commands to Pages/%d.fpage */ |
964 | | static int |
965 | | write_str_to_current_page(gx_device_xps *xps, const char *str) |
966 | 63.6M | { |
967 | 63.6M | const char *page_template = "Documents/1/Pages/%d.fpage"; |
968 | 63.6M | char buf[128]; /* easily enough to accommodate the string and a page number */ |
969 | | |
970 | | /* we're one ahead of the page count */ |
971 | 63.6M | int code = gs_snprintf(buf, sizeof(buf), page_template, xps->page_count+1); |
972 | 63.6M | if (code < 0) |
973 | 0 | return gs_rethrow_code(code); |
974 | | |
975 | 63.6M | return write_str_to_zip_file(xps, buf, str); |
976 | 63.6M | } |
977 | | |
978 | | /* add relationship to Pages/_rels/%d.fpage.rels */ |
979 | | static int |
980 | | add_new_relationship(gx_device_xps *xps, const char *str) |
981 | 4.07k | { |
982 | 4.07k | xps_relations_t *rel; |
983 | | |
984 | | /* See if we already have this one */ |
985 | 7.21k | for (rel = xps->relations_head; rel; rel = rel->next) |
986 | 3.45k | if (!strcmp(rel->relation, str)) |
987 | 315 | return 0; |
988 | | |
989 | 3.75k | rel = (xps_relations_t*)gs_alloc_bytes(xps->memory->non_gc_memory, |
990 | 3.75k | sizeof(xps_relations_t), "add_new_relationship"); |
991 | 3.75k | if (!rel) { |
992 | 0 | return gs_throw_code(gs_error_VMerror); |
993 | 0 | } |
994 | 3.75k | rel->memory = xps->memory->non_gc_memory; |
995 | 3.75k | rel->next = NULL; |
996 | | |
997 | 3.75k | rel->relation = (char*)gs_alloc_bytes(xps->memory->non_gc_memory, |
998 | 3.75k | strlen(str) + 1, "add_new_relationship"); |
999 | 3.75k | if (!rel->relation) { |
1000 | 0 | gs_free_object(rel->memory, rel, "add_new_relationship"); |
1001 | 0 | return gs_throw_code(gs_error_VMerror); |
1002 | 0 | } |
1003 | 3.75k | memcpy(rel->relation, str, strlen(str) + 1); |
1004 | | |
1005 | 3.75k | if (!xps->relations_head) { |
1006 | 1.69k | xps->relations_head = rel; |
1007 | 1.69k | xps->relations_tail = rel; |
1008 | 2.06k | } else { |
1009 | 2.06k | xps->relations_tail->next = rel; |
1010 | 2.06k | xps->relations_tail = rel; |
1011 | 2.06k | } |
1012 | | |
1013 | 3.75k | return 0; |
1014 | 3.75k | } |
1015 | | |
1016 | | static int |
1017 | | close_page_relationship(gx_device_xps *xps) |
1018 | 1.69k | { |
1019 | 1.69k | const char *rels_template = "Documents/1/Pages/_rels/%d.fpage.rels"; |
1020 | 1.69k | char buf[128]; /* easily enough to accommodate the string and a page number */ |
1021 | | |
1022 | 1.69k | int code = gs_snprintf(buf, sizeof(buf), rels_template, xps->page_count + 1); |
1023 | 1.69k | if (code < 0) |
1024 | 0 | return gs_rethrow_code(code); |
1025 | | |
1026 | 1.69k | write_str_to_zip_file(xps, buf, "</Relationships>"); |
1027 | 1.69k | return 0; |
1028 | 1.69k | } |
1029 | | |
1030 | | static int |
1031 | | write_page_relationship(gx_device_xps* xps) |
1032 | 1.69k | { |
1033 | 1.69k | const char* rels_template = "Documents/1/Pages/_rels/%d.fpage.rels"; |
1034 | 1.69k | char buf[128]; /* easily enough to accommodate the string and a page number */ |
1035 | 1.69k | char line[300]; |
1036 | 1.69k | const char* fmt; |
1037 | 1.69k | int count = 0; |
1038 | 1.69k | xps_relations_t *rel = xps->relations_head; |
1039 | | |
1040 | 1.69k | int code = gs_snprintf(buf, sizeof(buf), rels_template, xps->page_count + 1); |
1041 | 1.69k | if (code < 0) |
1042 | 0 | return gs_rethrow_code(code); |
1043 | | |
1044 | 1.69k | write_str_to_zip_file(xps, buf, rels_header); |
1045 | 1.69k | fmt = "<Relationship Target = \"/%s\" Id = \"R%d\" Type = %s/>\n"; |
1046 | | |
1047 | 5.44k | while (rel) { |
1048 | 3.75k | gs_snprintf(line, sizeof(line), fmt, rel->relation, count, rels_req_type); |
1049 | 3.75k | write_str_to_zip_file(xps, buf, line); |
1050 | 3.75k | rel = rel->next; |
1051 | 3.75k | count++; |
1052 | 3.75k | } |
1053 | | |
1054 | 1.69k | return 0; |
1055 | 1.69k | } |
1056 | | |
1057 | | static void |
1058 | | release_relationship(gx_device_xps* xps) |
1059 | 1.69k | { |
1060 | 1.69k | xps_relations_t *rel = xps->relations_head; |
1061 | 1.69k | xps_relations_t *old; |
1062 | | |
1063 | 5.44k | while (rel) { |
1064 | 3.75k | old = rel; |
1065 | 3.75k | rel = rel->next; |
1066 | 3.75k | gs_free_object(old->memory, old->relation, "release_relationship"); |
1067 | 3.75k | gs_free_object(old->memory, old, "release_relationship"); |
1068 | 3.75k | } |
1069 | | |
1070 | 1.69k | xps->relations_head = NULL; |
1071 | 1.69k | xps->relations_tail = NULL; |
1072 | 1.69k | return; |
1073 | 1.69k | } |
1074 | | |
1075 | | /* Page management */ |
1076 | | |
1077 | | /* Complete a page */ |
1078 | | static int |
1079 | | xps_output_page(gx_device *dev, int num_copies, int flush) |
1080 | 6.97k | { |
1081 | 6.97k | gx_device_xps *const xps = (gx_device_xps*)dev; |
1082 | 6.97k | gx_device_vector *vdev = (gx_device_vector *)dev; |
1083 | 6.97k | int code; |
1084 | | |
1085 | 6.97k | if (!vdev->in_page) { |
1086 | 2.49k | (*vdev_proc(vdev, beginpage)) (vdev); |
1087 | 2.49k | vdev->in_page = true; |
1088 | 2.49k | } |
1089 | 6.97k | write_str_to_current_page(xps, "</Canvas></FixedPage>"); |
1090 | | |
1091 | 6.97k | if (xps->relations_head) |
1092 | 1.69k | { |
1093 | | /* Write all the relations for the page */ |
1094 | 1.69k | code = write_page_relationship(xps); |
1095 | 1.69k | if (code < 0) |
1096 | 0 | return gs_rethrow_code(code); |
1097 | | |
1098 | | /* Close the relationship xml */ |
1099 | 1.69k | code = close_page_relationship(xps); |
1100 | 1.69k | if (code < 0) |
1101 | 0 | return gs_rethrow_code(code); |
1102 | | |
1103 | 1.69k | release_relationship(xps); |
1104 | 1.69k | } |
1105 | | |
1106 | 6.97k | xps->page_count++; |
1107 | | |
1108 | 6.97k | if (gp_ferror(xps->file)) |
1109 | 0 | return gs_throw_code(gs_error_ioerror); |
1110 | | |
1111 | 6.97k | if ((code=gx_finish_output_page(dev, num_copies, flush)) < 0) |
1112 | 0 | return code; |
1113 | | |
1114 | | /* Check if we need to change the output file for separate |
1115 | | pages. NB not sure if this will work correctly. */ |
1116 | 6.97k | if (gx_outputfile_is_separate_pages(((gx_device_vector *)dev)->fname, dev->memory)) { |
1117 | 0 | if ((code = xps_close_device(dev)) < 0) |
1118 | 0 | return code; |
1119 | 0 | code = xps_open_device(dev); |
1120 | 0 | } |
1121 | | |
1122 | 6.97k | if_debug1m('_', dev->memory, "xps_output_page - page=%d\n", xps->page_count); |
1123 | 6.97k | vdev->in_page = false; |
1124 | | |
1125 | 6.97k | return code; |
1126 | 6.97k | } |
1127 | | |
1128 | | static void |
1129 | | xps_release_icc_info(gx_device *dev) |
1130 | 9.31k | { |
1131 | 9.31k | gx_device_xps *xps = (gx_device_xps*)dev; |
1132 | 9.31k | xps_icc_data_t *curr; |
1133 | 9.31k | xps_icc_data_t *icc_data = xps->icc_data; |
1134 | | |
1135 | 10.8k | while (icc_data != NULL) { |
1136 | 1.50k | curr = icc_data; |
1137 | 1.50k | icc_data = icc_data->next; |
1138 | 1.50k | gs_free(dev->memory->non_gc_memory, curr, sizeof(xps_icc_data_t), 1, |
1139 | 1.50k | "xps_release_icc_info"); |
1140 | 1.50k | } |
1141 | 9.31k | return; |
1142 | 9.31k | } |
1143 | | |
1144 | | static void |
1145 | | xps_release_achive_file_names(gx_device* dev) |
1146 | 9.31k | { |
1147 | 9.31k | gx_device_xps* xps = (gx_device_xps*)dev; |
1148 | 9.31k | gx_device_xps_f2i_t *curr; |
1149 | 9.31k | gx_device_xps_f2i_t *f2i = xps->f2i; |
1150 | | |
1151 | 58.9k | while (f2i) { |
1152 | 49.6k | curr = f2i; |
1153 | 49.6k | f2i = f2i->next; |
1154 | 49.6k | gs_free_object(curr->memory, curr->info, "xps_release_achive_file_names(info)"); |
1155 | 49.6k | gs_free_object(curr->memory, curr->filename, "xps_release_achive_file_names(filename)"); |
1156 | 49.6k | gs_free_object(curr->memory, curr, "xps_release_achive_file_names(f2i)"); |
1157 | 49.6k | } |
1158 | 9.31k | return; |
1159 | 9.31k | } |
1160 | | |
1161 | | /* Close the device */ |
1162 | | static int |
1163 | | xps_close_device(gx_device *dev) |
1164 | 9.31k | { |
1165 | 9.31k | gx_device_xps *xps = (gx_device_xps*)dev; |
1166 | 9.31k | int code; |
1167 | | |
1168 | | /* closing for the FixedDocument */ |
1169 | 9.31k | code = write_str_to_zip_file(xps, "Documents/1/FixedDocument.fdoc", "</FixedDocument>"); |
1170 | 9.31k | if (code < 0) |
1171 | 0 | return gs_rethrow_code(code); |
1172 | | |
1173 | 9.31k | if (gp_ferror(xps->file)) |
1174 | 0 | return gs_throw_code(gs_error_ioerror); |
1175 | | |
1176 | 9.31k | code = zip_close_archive(xps); |
1177 | 9.31k | if (code < 0) |
1178 | 0 | return gs_rethrow_code(code); |
1179 | | |
1180 | | /* Release the icc info */ |
1181 | 9.31k | xps_release_icc_info(dev); |
1182 | | |
1183 | | /* Release the archive file names */ |
1184 | 9.31k | xps_release_achive_file_names(dev); |
1185 | | |
1186 | 9.31k | code = gdev_vector_close_file((gx_device_vector*)dev); |
1187 | 9.31k | if (code < 0) |
1188 | 0 | return gs_rethrow_code(code); |
1189 | | |
1190 | 9.31k | if (strlen((const char *)xps->PrinterName)) { |
1191 | 0 | int reason; |
1192 | 0 | code = gp_xpsprint(xps->fname, (char *)xps->PrinterName, &reason); |
1193 | 0 | if (code < 0) { |
1194 | 0 | switch(code) { |
1195 | 0 | case -1: |
1196 | 0 | break; |
1197 | 0 | case -2: |
1198 | 0 | eprintf1("ERROR: Could not create competion event: %08X\n", reason); |
1199 | 0 | break; |
1200 | 0 | case -3: |
1201 | 0 | eprintf1("ERROR: Could not create MultiByteString from PrinerName: %s\n", xps->PrinterName); |
1202 | 0 | break; |
1203 | 0 | case -4: |
1204 | 0 | eprintf1("ERROR: Could not start XPS print job: %08X\n", reason); |
1205 | 0 | break; |
1206 | 0 | case -5: |
1207 | 0 | eprintf1("ERROR: Could not create XPS OM Object Factory: %08X\n", reason); |
1208 | 0 | break; |
1209 | 0 | case -6: |
1210 | 0 | eprintf1("ERROR: Could not create MultiByteString from OutputFile: %s\n", xps->fname); |
1211 | 0 | break; |
1212 | 0 | case -7: |
1213 | 0 | eprintf1("ERROR: Could not create Package from File %08X\n", reason); |
1214 | 0 | break; |
1215 | 0 | case -8: |
1216 | 0 | eprintf1("ERROR: Could not write Package to stream %08X\n", reason); |
1217 | 0 | break; |
1218 | 0 | case -9: |
1219 | 0 | eprintf1("ERROR: Could not close job stream: %08X\n", reason); |
1220 | 0 | break; |
1221 | 0 | case -10: |
1222 | 0 | eprintf1("ERROR: Wait for completion event failed: %08X\n", reason); |
1223 | 0 | break; |
1224 | 0 | case -11: |
1225 | 0 | eprintf1("ERROR: Could not get job status: %08X\n", reason); |
1226 | 0 | break; |
1227 | 0 | case -12: |
1228 | 0 | eprintf("ERROR: job was cancelled\n"); |
1229 | 0 | break; |
1230 | 0 | case -13: |
1231 | 0 | eprintf1("ERROR: Print job failed: %08X\n", reason); |
1232 | 0 | break; |
1233 | 0 | case -14: |
1234 | 0 | eprintf("ERROR: unexpected failure\n"); |
1235 | 0 | break; |
1236 | 0 | case -15: |
1237 | 0 | case -16: |
1238 | 0 | eprintf("ERROR: XpsPrint.dll does not exist or is missing a required method\n"); |
1239 | 0 | break; |
1240 | 0 | } |
1241 | 0 | return(gs_throw_code(gs_error_invalidaccess)); |
1242 | 0 | } |
1243 | 0 | } |
1244 | 9.31k | return(0); |
1245 | 9.31k | } |
1246 | | |
1247 | | /* Respond to a device parameter query from the client */ |
1248 | | static int |
1249 | | xps_get_params(gx_device *dev, gs_param_list *plist) |
1250 | 186k | { |
1251 | 186k | int code = 0; |
1252 | | |
1253 | 186k | if_debug0m('_', dev->memory, "xps_get_params\n"); |
1254 | | |
1255 | | /* call our superclass to add its standard set */ |
1256 | 186k | code = gdev_vector_get_params(dev, plist); |
1257 | 186k | if (code < 0) |
1258 | 0 | return gs_rethrow_code(code); |
1259 | | |
1260 | | #if defined(__WIN32__) && XPSPRINT==1 |
1261 | | { |
1262 | | gs_param_string ofns; |
1263 | | gx_device_xps *const xps = (gx_device_xps*)dev; |
1264 | | |
1265 | | /* xps specific parameters are added to plist here */ |
1266 | | ofns.data = (const byte *)&xps->PrinterName; |
1267 | | ofns.size = strlen(xps->fname); |
1268 | | ofns.persistent = false; |
1269 | | if ((code = param_write_string(plist, "PrinterName", &ofns)) < 0) |
1270 | | return code; |
1271 | | } |
1272 | | #endif |
1273 | 186k | return code; |
1274 | 186k | } |
1275 | | |
1276 | | /* Read the device parameters passed to us by the client */ |
1277 | | static int |
1278 | | xps_put_params(gx_device *dev, gs_param_list *plist) |
1279 | 84.4k | { |
1280 | 84.4k | int code = 0; |
1281 | | |
1282 | 84.4k | if_debug0m('_', dev->memory, "xps_put_params\n"); |
1283 | | |
1284 | | /* xps specific parameters are parsed here */ |
1285 | | #if defined(__WIN32__) && XPSPRINT==1 |
1286 | | { /* NB: The following is not strictly correct since changes are */ |
1287 | | /* made even if 'gdev_vector_put_params' returns an error. */ |
1288 | | gs_param_name param_name; |
1289 | | gs_param_string pps; |
1290 | | gx_device_xps *const xps = (gx_device_xps*)dev; |
1291 | | switch (code = param_read_string(plist, (param_name = "PrinterName"), &pps)) { |
1292 | | case 0: |
1293 | | if (pps.size > 64) { |
1294 | | eprintf1("\nERROR: PrinterName too long (max %d)\n", MAXPRINTERNAME); |
1295 | | } else { |
1296 | | memcpy(xps->PrinterName, pps.data, pps.size); |
1297 | | xps->PrinterName[pps.size] = 0; |
1298 | | } |
1299 | | break; |
1300 | | default: |
1301 | | param_signal_error(plist, param_name, code); |
1302 | | case 1: |
1303 | | /* memset(&xps->PrinterName, 0x00, MAXPRINTERNAME);*/ |
1304 | | break; |
1305 | | } |
1306 | | } |
1307 | | #endif |
1308 | | /* call our superclass to get its parameters, like OutputFile */ |
1309 | 84.4k | code = gdev_vector_put_params(dev, plist); /* errors are handled by caller or setpagedevice */ |
1310 | | |
1311 | 84.4k | return code; |
1312 | 84.4k | } |
1313 | | |
1314 | | static int |
1315 | | set_state_color(gx_device_vector *vdev, const gx_drawing_color *pdc, gx_color_index *color) |
1316 | 2.01M | { |
1317 | 2.01M | gx_device_xps *xps = (gx_device_xps *)vdev; |
1318 | | |
1319 | | /* hack so beginpage is called */ |
1320 | 2.01M | (void)gdev_vector_stream((gx_device_vector*)xps); |
1321 | | |
1322 | | /* Usually this is not an actual error but a signal to the |
1323 | | graphics library to simplify the color */ |
1324 | 2.01M | if (!gx_dc_is_pure(pdc)) { |
1325 | 1.81k | return_error(gs_error_rangecheck); |
1326 | 1.81k | } |
1327 | | |
1328 | 2.00M | *color = gx_dc_pure_color(pdc); |
1329 | 2.00M | return 0; |
1330 | 2.01M | } |
1331 | | |
1332 | | static int |
1333 | | xps_setfillcolor(gx_device_vector *vdev, const gs_gstate *pgs, const gx_drawing_color *pdc) |
1334 | 2.00M | { |
1335 | 2.00M | gx_device_xps *xps = (gx_device_xps *)vdev; |
1336 | | |
1337 | 2.00M | if_debug1m('_', xps->memory, "xps_setfillcolor:%06X\n", (uint32_t)gx_dc_pure_color(pdc)); |
1338 | | |
1339 | 2.00M | return set_state_color(vdev, pdc, &xps->fillcolor); |
1340 | 2.00M | } |
1341 | | |
1342 | | static int |
1343 | | xps_setstrokecolor(gx_device_vector *vdev, const gs_gstate *pgs, const gx_drawing_color *pdc) |
1344 | 1.83k | { |
1345 | 1.83k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1346 | | |
1347 | 1.83k | if_debug1m('_', xps->memory, "xps_setstrokecolor:%06X\n", (uint32_t)gx_dc_pure_color(pdc)); |
1348 | | |
1349 | 1.83k | return set_state_color(vdev, pdc, &xps->strokecolor); |
1350 | 1.83k | } |
1351 | | |
1352 | | static int |
1353 | | xps_beginpage(gx_device_vector *vdev) |
1354 | 7.17k | { |
1355 | | |
1356 | 7.17k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1357 | 7.17k | char buf[128]; |
1358 | 7.17k | int code = 0; |
1359 | | |
1360 | 7.17k | if_debug0m('_', xps->memory, "xps_beginpage\n"); |
1361 | | |
1362 | 7.17k | { |
1363 | 7.17k | const char *template = "<PageContent Source=\"Pages/%d.fpage\" />"; |
1364 | | /* Note page count is 1 less than the current page */ |
1365 | 7.17k | code = gs_snprintf(buf, sizeof(buf), template, xps->page_count + 1); |
1366 | 7.17k | if (code < 0) |
1367 | 0 | return gs_rethrow_code(code); |
1368 | | |
1369 | | /* Put a reference to this new page in the FixedDocument */ |
1370 | 7.17k | code = write_str_to_zip_file(xps, "Documents/1/FixedDocument.fdoc", buf); |
1371 | 7.17k | if (code < 0) |
1372 | 0 | return gs_rethrow_code(code); |
1373 | | |
1374 | 7.17k | } |
1375 | | |
1376 | 7.17k | { |
1377 | 7.17k | const char *page_size_template = "<FixedPage Width=\"%d\" Height=\"%d\" " |
1378 | 7.17k | "xmlns=\"http://schemas.microsoft.com/xps/2005/06\" xml:lang=\"en-US\">\n"; |
1379 | 7.17k | code = gs_snprintf(buf, sizeof(buf), page_size_template, |
1380 | 7.17k | (int)(xps->MediaSize[0] * 4.0/3.0), /* pts -> 1/96 inch */ |
1381 | 7.17k | (int)(xps->MediaSize[1] * 4.0/3.0)); |
1382 | 7.17k | if (code < 0) |
1383 | 0 | return gs_rethrow_code(code); |
1384 | 7.17k | code = write_str_to_current_page(xps, buf); |
1385 | 7.17k | if (code < 0) |
1386 | 0 | return gs_rethrow_code(code); |
1387 | 7.17k | } |
1388 | 7.17k | { |
1389 | 7.17k | const char *canvas_template = "<Canvas RenderTransform=\"%g,%g,%g,%g,%g,%g\">\n"; |
1390 | 7.17k | code = gs_snprintf(buf, sizeof(buf), canvas_template, |
1391 | 7.17k | 96.0/xps->HWResolution[0], 0.0, 0.0, |
1392 | 7.17k | 96.0/xps->HWResolution[1], 0.0, 0.0); |
1393 | 7.17k | if (code < 0) |
1394 | 0 | return gs_rethrow_code(code); |
1395 | | |
1396 | 7.17k | code = write_str_to_current_page(xps, buf); |
1397 | 7.17k | if (code < 0) |
1398 | 0 | return gs_rethrow_code(code); |
1399 | 7.17k | } |
1400 | | |
1401 | 7.17k | if_debug4m('_', xps->memory, |
1402 | 7.17k | "page info: resx=%g resy=%g width=%d height=%d\n", |
1403 | 7.17k | xps->HWResolution[0], xps->HWResolution[1], |
1404 | 7.17k | (int)xps->MediaSize[0], (int)xps->MediaSize[1]); |
1405 | | |
1406 | 7.17k | return code; |
1407 | 7.17k | } |
1408 | | |
1409 | | static int |
1410 | | xps_setlinewidth(gx_device_vector *vdev, double width) |
1411 | 2.32k | { |
1412 | 2.32k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1413 | | |
1414 | 2.32k | if_debug1m('_', xps->memory, "xps_setlinewidth(%lf)\n", width); |
1415 | | |
1416 | 2.32k | xps->linewidth = width; |
1417 | | |
1418 | 2.32k | return 0; |
1419 | 2.32k | } |
1420 | | static int |
1421 | | xps_setlinecap(gx_device_vector *vdev, gs_line_cap cap) |
1422 | 320 | { |
1423 | 320 | gx_device_xps *xps = (gx_device_xps *)vdev; |
1424 | | #ifdef DEBUG |
1425 | | /* Only used for debug print, so guiard to prevent warnings on non-debug builds */ |
1426 | | const char *linecap_names[] = {"butt", "round", "square", |
1427 | | "triangle", "unknown"}; |
1428 | | #endif |
1429 | | |
1430 | 320 | if ((int)cap < 0 || (int)cap > gs_cap_unknown) |
1431 | 0 | return gs_throw_code(gs_error_rangecheck); |
1432 | 320 | if_debug1m('_', xps->memory, "xps_setlinecap(%s)\n", linecap_names[cap]); |
1433 | | |
1434 | 320 | xps->linecap = cap; |
1435 | | |
1436 | 320 | return 0; |
1437 | 320 | } |
1438 | | static int |
1439 | | xps_setlinejoin(gx_device_vector *vdev, gs_line_join join) |
1440 | 167 | { |
1441 | 167 | gx_device_xps *xps = (gx_device_xps *)vdev; |
1442 | | #ifdef DEBUG |
1443 | | /* Only used for debug print, so guiard to prevent warnings on non-debug builds */ |
1444 | | const char *linejoin_names[] = {"miter", "round", "bevel", |
1445 | | "none", "triangle", "unknown"}; |
1446 | | #endif |
1447 | | |
1448 | 167 | if ((int)join < 0 || (int)join > gs_join_unknown) |
1449 | 0 | return gs_throw_code(gs_error_rangecheck); |
1450 | 167 | if_debug1m('_', xps->memory, "xps_setlinejoin(%s)\n", linejoin_names[join]); |
1451 | | |
1452 | 167 | xps->linejoin = join; |
1453 | | |
1454 | 167 | return 0; |
1455 | 167 | } |
1456 | | static int |
1457 | | xps_setmiterlimit(gx_device_vector *vdev, double limit) |
1458 | 125 | { |
1459 | 125 | if_debug1m('_', vdev->memory, "xps_setmiterlimit(%lf)\n", limit); |
1460 | 125 | return 0; |
1461 | 125 | } |
1462 | | static int |
1463 | | xps_setdash(gx_device_vector *vdev, const float *pattern, |
1464 | | uint count, double offset) |
1465 | 269 | { |
1466 | 269 | gx_device_xps *xps = (gx_device_xps *)vdev; |
1467 | 269 | if_debug2m('_', vdev->memory, "xps_setdash count:%d offset:%g\n", count, offset); |
1468 | 269 | xps->can_stroke = (count == 0); |
1469 | 269 | return 0; |
1470 | 269 | } |
1471 | | static int |
1472 | | xps_setlogop(gx_device_vector *vdev, gs_logical_operation_t lop, |
1473 | | gs_logical_operation_t diff) |
1474 | 23.4k | { |
1475 | 23.4k | if_debug2m('_', vdev->memory, "xps_setlogop(%u,%u) set logical operation\n", |
1476 | 23.4k | lop, diff); |
1477 | | /* XPS can fake some simpler modes, but we ignore this for now. */ |
1478 | 23.4k | return 0; |
1479 | 23.4k | } |
1480 | | |
1481 | | /* Other state */ |
1482 | | |
1483 | | static bool |
1484 | | xps_can_handle_hl_color(gx_device_vector *vdev, const gs_gstate *pgs, |
1485 | | const gx_drawing_color *pdc) |
1486 | 20.1M | { |
1487 | 20.1M | if_debug0m('_', vdev->memory, "xps_can_handle_hl_color\n"); |
1488 | 20.1M | return false; |
1489 | 20.1M | } |
1490 | | |
1491 | | /* Paths */ |
1492 | | static bool |
1493 | | image_brush_fill(gx_path_type_t path_type, xps_brush_t brush_type) |
1494 | 22.3M | { |
1495 | 22.3M | return brush_type == xps_imagebrush; |
1496 | 22.3M | } |
1497 | | |
1498 | | static bool |
1499 | | drawing_path(gx_path_type_t path_type, xps_brush_t brush_type) |
1500 | 22.9M | { |
1501 | 22.9M | return ((path_type & gx_path_type_stroke) || (path_type & gx_path_type_fill) || |
1502 | 22.9M | (path_type & gx_path_type_clip) || image_brush_fill(path_type, brush_type)); |
1503 | 22.9M | } |
1504 | | |
1505 | | static void |
1506 | | xps_finish_image_path(gx_device_vector *vdev) |
1507 | 2.03k | { |
1508 | 2.03k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1509 | 2.03k | char line[300]; |
1510 | 2.03k | const char *fmt; |
1511 | 2.03k | gs_matrix matrix; |
1512 | | |
1513 | | /* If an error occurs during an image, we can get here after the enumerator |
1514 | | * has been freed - if that's the case, just bail out immediately |
1515 | | */ |
1516 | 2.03k | if (xps->xps_pie == NULL) |
1517 | 0 | return; |
1518 | | /* Path is started. Do the image brush image brush and close the path */ |
1519 | 2.03k | write_str_to_current_page(xps, "\t<Path.Fill>\n"); |
1520 | 2.03k | write_str_to_current_page(xps, "\t\t<ImageBrush "); |
1521 | 2.03k | fmt = "ImageSource = \"{ColorConvertedBitmap /%s /%s}\" Viewbox=\"%d, %d, %d, %d\" ViewboxUnits = \"Absolute\" Viewport = \"%d, %d, %d, %d\" ViewportUnits = \"Absolute\" TileMode = \"None\" >\n"; |
1522 | 2.03k | gs_snprintf(line, sizeof(line), fmt, xps->xps_pie->file_name, xps->xps_pie->icc_name, |
1523 | 2.03k | 0, 0, xps->xps_pie->width, xps->xps_pie->height, 0, 0, |
1524 | 2.03k | xps->xps_pie->width, xps->xps_pie->height); |
1525 | 2.03k | write_str_to_current_page(xps, line); |
1526 | | |
1527 | | /* Now the render transform. This is applied to the image brush. Path |
1528 | | is already transformed */ |
1529 | 2.03k | write_str_to_current_page(xps, "\t\t\t<ImageBrush.Transform>\n"); |
1530 | 2.03k | fmt = "\t\t\t\t<MatrixTransform Matrix = \"%g,%g,%g,%g,%g,%g\" />\n"; |
1531 | 2.03k | matrix = xps->xps_pie->mat; |
1532 | 2.03k | gs_snprintf(line, sizeof(line), fmt, |
1533 | 2.03k | matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.tx, matrix.ty); |
1534 | 2.03k | write_str_to_current_page(xps, line); |
1535 | 2.03k | write_str_to_current_page(xps, "\t\t\t</ImageBrush.Transform>\n"); |
1536 | 2.03k | write_str_to_current_page(xps, "\t\t</ImageBrush>\n"); |
1537 | 2.03k | write_str_to_current_page(xps, "\t</Path.Fill>\n"); |
1538 | | /* End this path */ |
1539 | 2.03k | write_str_to_current_page(xps, "</Path>\n"); |
1540 | 2.03k | } |
1541 | | |
1542 | | static int |
1543 | | xps_dorect(gx_device_vector *vdev, fixed x0, fixed y0, |
1544 | | fixed x1, fixed y1, gx_path_type_t type) |
1545 | 20.1M | { |
1546 | 20.1M | gx_device_xps *xps = (gx_device_xps *)vdev; |
1547 | 20.1M | char line[300]; |
1548 | 20.1M | const char *fmt; |
1549 | 20.1M | uint32_t c; |
1550 | | |
1551 | 20.1M | (void)gdev_vector_stream((gx_device_vector*)xps); |
1552 | | |
1553 | 20.1M | if_debug9m('_', xps->memory, |
1554 | 20.1M | "rect type=%d coords=%g,%g %g,%g %g,%g %g,%g\n", type, |
1555 | 20.1M | fixed2float(x0), fixed2float(y0), |
1556 | 20.1M | fixed2float(x0), fixed2float(y1), |
1557 | 20.1M | fixed2float(x1), fixed2float(y1), |
1558 | 20.1M | fixed2float(x1), fixed2float(y0)); |
1559 | | |
1560 | | |
1561 | | /* skip non-drawing paths for now */ |
1562 | 20.1M | if (!drawing_path(type, xps->filltype)) { |
1563 | 0 | if_debug1m('_', xps->memory, "xps_dorect: type not supported %x\n", type); |
1564 | 0 | return 0; |
1565 | 0 | } |
1566 | | |
1567 | | /* CLIP - we only write clips as an attribute of a path */ |
1568 | 20.1M | if (type & gx_path_type_clip && !image_brush_fill(type, xps->filltype)) { |
1569 | 223k | if (xps->in_path == false) |
1570 | 0 | return 0; |
1571 | 223k | fmt = "Clip=\"M %g,%g V %g H %g V %g Z\" "; |
1572 | 223k | gs_snprintf(line, sizeof(line), fmt, |
1573 | 223k | fixed2float(x0), fixed2float(y0), |
1574 | 223k | fixed2float(y1), fixed2float(x1), |
1575 | 223k | fixed2float(y0)); |
1576 | 223k | write_str_to_current_page(xps, line); |
1577 | 223k | xps->clip_written = true; |
1578 | 223k | return 0; |
1579 | 223k | } |
1580 | | |
1581 | 19.9M | if (xps->in_path && image_brush_fill(type, xps->filltype)) { |
1582 | 0 | write_str_to_current_page(xps, "/>\n"); |
1583 | 0 | xps->in_path = false; |
1584 | 0 | xps->in_clip = false; |
1585 | 0 | xps->clip_written = false; |
1586 | 0 | } |
1587 | | |
1588 | 19.9M | if (xps->in_path && xps->rect_written) { |
1589 | 143 | write_str_to_current_page(xps, "/>\n"); |
1590 | 143 | xps->in_path = false; |
1591 | 143 | xps->in_clip = false; |
1592 | 143 | xps->clip_written = false; |
1593 | 143 | xps->rect_written = false; |
1594 | 143 | } |
1595 | | |
1596 | 19.9M | if ((type & gx_path_type_stroke) && !xps->can_stroke) { |
1597 | 8 | return_error(gs_error_rangecheck); |
1598 | 8 | } |
1599 | | |
1600 | 19.9M | if (image_brush_fill(type, xps->filltype)) { |
1601 | | /* Do the path data */ |
1602 | 268 | fmt = "<Path Data=\"M %g, %g L %g, %g %g, %g %g, %g Z\" >\n"; |
1603 | 268 | gs_snprintf(line, sizeof(line), fmt, |
1604 | 268 | fixed2float(x0), fixed2float(y0), |
1605 | 268 | fixed2float(x0), fixed2float(y1), |
1606 | 268 | fixed2float(x1), fixed2float(y1), |
1607 | 268 | fixed2float(x1), fixed2float(y0)); |
1608 | 268 | write_str_to_current_page(xps, line); |
1609 | | /* And now the rest of the details */ |
1610 | 268 | xps_finish_image_path(vdev); |
1611 | 19.9M | } else if (type & gx_path_type_fill) { |
1612 | | /* Solid fill */ |
1613 | 19.9M | if (!xps->in_path) |
1614 | 19.8M | write_str_to_current_page(xps, "<Path "); |
1615 | | /* NB - F0 should be changed for a different winding type */ |
1616 | 19.9M | fmt = "Fill=\"#%06X\" Data=\"M %g,%g V %g H %g V %g Z\" "; |
1617 | 19.9M | c = xps->fillcolor & 0xffffffL; |
1618 | 19.9M | gs_snprintf(line, sizeof(line), fmt, c, |
1619 | 19.9M | fixed2float(x0), fixed2float(y0), |
1620 | 19.9M | fixed2float(y1), fixed2float(x1), |
1621 | 19.9M | fixed2float(y0)); |
1622 | 19.9M | write_str_to_current_page(xps, line); |
1623 | 19.9M | if (!xps->in_path) |
1624 | 19.8M | write_str_to_current_page(xps, "/>\n"); |
1625 | 77.6k | else |
1626 | 77.6k | xps->rect_written = true; |
1627 | 19.9M | } else { |
1628 | | /* Solid stroke */ |
1629 | 9.64k | if (!xps->in_path) |
1630 | 0 | write_str_to_current_page(xps, "<Path "); |
1631 | 9.64k | fmt = "Stroke=\"#%06X\" Data=\"M %g,%g V %g H %g V %g Z\" "; |
1632 | 9.64k | c = xps->strokecolor & 0xffffffL; |
1633 | 9.64k | gs_snprintf(line, sizeof(line), fmt, c, |
1634 | 9.64k | fixed2float(x0), fixed2float(y0), |
1635 | 9.64k | fixed2float(y1), fixed2float(x1), |
1636 | 9.64k | fixed2float(y0)); |
1637 | 9.64k | write_str_to_current_page(xps, line); |
1638 | | |
1639 | 9.64k | if (type & gx_path_type_stroke) { |
1640 | | /* NB format width. */ |
1641 | 9.64k | fmt = "StrokeThickness=\"%g\" "; |
1642 | 9.64k | gs_snprintf(line, sizeof(line), fmt, xps->linewidth); |
1643 | 9.64k | write_str_to_current_page(xps, line); |
1644 | 9.64k | } |
1645 | 9.64k | if (!xps->in_path) |
1646 | 0 | write_str_to_current_page(xps, "/>\n"); |
1647 | 9.64k | else |
1648 | 9.64k | xps->rect_written = true; |
1649 | 9.64k | } |
1650 | | /* end and close NB \n not necessary. */ |
1651 | 19.9M | return 0; |
1652 | 19.9M | } |
1653 | | |
1654 | | static int |
1655 | | gdev_xps_fill_path(gx_device * dev, const gs_gstate * pgs, gx_path * ppath, |
1656 | | const gx_fill_params * params, |
1657 | | const gx_drawing_color * pdcolor, const gx_clip_path * pcpath) |
1658 | 306k | { |
1659 | 306k | gx_device_xps *xps = (gx_device_xps *)dev; |
1660 | 306k | int code = 0; |
1661 | | |
1662 | 306k | if (gx_path_is_void(ppath)) { |
1663 | 20.6k | return 0; |
1664 | 20.6k | } |
1665 | | |
1666 | 285k | (void)gdev_vector_stream((gx_device_vector*)xps); |
1667 | | |
1668 | 285k | if (xps->in_path) { |
1669 | 4.04k | write_str_to_current_page(xps, "/>\n"); |
1670 | 4.04k | xps->in_clip = false; |
1671 | 4.04k | } |
1672 | | |
1673 | 285k | xps->clip_path_id = xps->no_clip_path_id; |
1674 | 285k | write_str_to_current_page(xps, "<Path "); |
1675 | 285k | xps->in_path = true; |
1676 | 285k | code = gdev_vector_fill_path(dev, pgs, ppath, params, pdcolor, pcpath); |
1677 | 285k | if (xps->in_path) { |
1678 | 285k | write_str_to_current_page(xps, "/>\n"); |
1679 | 285k | xps->in_path = false; |
1680 | 285k | } |
1681 | 285k | xps->clip_written = false; |
1682 | 285k | xps->rect_written = false; |
1683 | 285k | return code; |
1684 | 306k | } |
1685 | | |
1686 | | static int |
1687 | | gdev_xps_stroke_path(gx_device * dev, const gs_gstate * pgs, gx_path * ppath, |
1688 | | const gx_stroke_params * params, |
1689 | | const gx_drawing_color * pdcolor, const gx_clip_path * pcpath) |
1690 | 28.8k | { |
1691 | 28.8k | gx_device_xps *xps = (gx_device_xps *)dev; |
1692 | 28.8k | int code = 0; |
1693 | | |
1694 | 28.8k | if (gx_path_is_void(ppath)) { |
1695 | 2.81k | return 0; |
1696 | 2.81k | } |
1697 | | |
1698 | 26.0k | (void)gdev_vector_stream((gx_device_vector*)xps); |
1699 | | |
1700 | 26.0k | if (xps->in_path) { |
1701 | 0 | write_str_to_current_page(xps, "/>\n"); |
1702 | 0 | xps->in_clip = false; |
1703 | 0 | } |
1704 | | |
1705 | 26.0k | xps->clip_path_id = xps->no_clip_path_id; |
1706 | 26.0k | write_str_to_current_page(xps, "<Path "); |
1707 | 26.0k | xps->in_path = true; |
1708 | 26.0k | code = gdev_vector_stroke_path(dev, pgs, ppath, params, pdcolor, pcpath); |
1709 | 26.0k | if (xps->in_path) { |
1710 | 22.0k | write_str_to_current_page(xps, "/>\n"); |
1711 | 22.0k | xps->in_path = false; |
1712 | | |
1713 | 22.0k | } |
1714 | 26.0k | xps->clip_written = false; |
1715 | 26.0k | xps->rect_written = false; |
1716 | 26.0k | return code; |
1717 | 28.8k | } |
1718 | | |
1719 | | static int |
1720 | | xps_beginpath(gx_device_vector *vdev, gx_path_type_t type) |
1721 | 320k | { |
1722 | 320k | char line[300]; |
1723 | 320k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1724 | 320k | uint32_t c; |
1725 | 320k | const char *fmt; |
1726 | | |
1727 | 320k | (void)gdev_vector_stream((gx_device_vector*)xps); |
1728 | | |
1729 | | /* skip non-drawing paths for now */ |
1730 | 320k | if (!drawing_path(type, xps->filltype)) { |
1731 | 0 | if_debug1m('_', xps->memory, "xps_beginpath: type not supported %x\n", type); |
1732 | 0 | return 0; |
1733 | 0 | } |
1734 | | |
1735 | 320k | if ((type & gx_path_type_stroke) && !xps->can_stroke) { |
1736 | 434 | return_error(gs_error_rangecheck); |
1737 | 434 | } |
1738 | | |
1739 | 319k | c = type & gx_path_type_fill ? xps->fillcolor : xps->strokecolor; |
1740 | 319k | c &= 0xffffffL; |
1741 | | |
1742 | | /* NOTE the XPS code (ab)uses the clip to write the rectangle for the image! |
1743 | | */ |
1744 | 319k | if (type & gx_path_type_clip && !image_brush_fill(type, xps->filltype)) { |
1745 | 99.3k | if (xps->in_path == true && xps->clip_written == false) { |
1746 | 87.0k | write_str_to_current_page(xps, " Clip=\""); |
1747 | 87.0k | xps->in_clip = true; |
1748 | 87.0k | } |
1749 | 99.3k | goto exit; |
1750 | 99.3k | } |
1751 | | |
1752 | 220k | if (!image_brush_fill(type, xps->filltype)) { |
1753 | 218k | if (type & gx_path_type_fill) { |
1754 | 206k | if (type == gx_path_type_fill) |
1755 | 205k | fmt = "Fill=\"#%06X\" Data=\"F 1"; |
1756 | 562 | else |
1757 | 562 | fmt = "Fill=\"#%06X\" Data=\""; |
1758 | 206k | } |
1759 | 12.2k | else { |
1760 | 12.2k | fmt = "Stroke=\"#%06X\" Data=\""; |
1761 | 12.2k | } |
1762 | 218k | gs_snprintf(line, sizeof(line), fmt, c); |
1763 | 218k | write_str_to_current_page(xps, line); |
1764 | 218k | } |
1765 | 1.76k | else { |
1766 | 1.76k | if ( image_brush_fill(type, xps->filltype)) |
1767 | 1.76k | write_str_to_current_page(xps, "<Path Data=\""); |
1768 | 0 | else |
1769 | 0 | write_str_to_current_page(xps, " Data=\""); |
1770 | 1.76k | } |
1771 | | |
1772 | 319k | exit: |
1773 | 319k | if_debug1m('_', xps->memory, "xps_beginpath %s\n", line); |
1774 | | |
1775 | 319k | return 0; |
1776 | 220k | } |
1777 | | |
1778 | | static int |
1779 | | xps_moveto(gx_device_vector *vdev, double x0, double y0, |
1780 | | double x, double y, gx_path_type_t type) |
1781 | 435k | { |
1782 | 435k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1783 | 435k | char line[300]; |
1784 | | |
1785 | 435k | if_debug2m('_', xps->memory, "xps_moveto %g %g\n", x, y); |
1786 | | |
1787 | | /* skip non-drawing paths for now */ |
1788 | 435k | if (!drawing_path(type, xps->filltype)) { |
1789 | 182k | if (type == gx_path_type_none) { |
1790 | 182k | if (xps->in_path == false) { |
1791 | 11.0k | if_debug1m('_', xps->memory, "xps_moveto: type not supported %x\n", type); |
1792 | 11.0k | return 0; |
1793 | 11.0k | } |
1794 | 182k | } |
1795 | 182k | } |
1796 | | |
1797 | 424k | if ((type & gx_path_type_clip || type == gx_path_type_none) && !image_brush_fill(type, xps->filltype)) { |
1798 | 173k | if (xps->in_path == false || xps->clip_written) |
1799 | 1.39k | return 0; |
1800 | 173k | } |
1801 | 423k | gs_snprintf(line, sizeof(line), " M %g,%g", x, y); |
1802 | 423k | write_str_to_current_page(xps, line); |
1803 | 423k | if_debug1m('_', xps->memory, "xps_moveto %s", line); |
1804 | 423k | return 0; |
1805 | 424k | } |
1806 | | |
1807 | | static int |
1808 | | xps_lineto(gx_device_vector *vdev, double x0, double y0, |
1809 | | double x, double y, gx_path_type_t type) |
1810 | 1.54M | { |
1811 | 1.54M | gx_device_xps *xps = (gx_device_xps *)vdev; |
1812 | 1.54M | char line[200]; |
1813 | | |
1814 | 1.54M | if_debug2m('_', xps->memory, "xps_lineto %g %g\n", x, y); |
1815 | | |
1816 | | /* skip non-drawing paths for now */ |
1817 | 1.54M | if (!drawing_path(type, xps->filltype)) { |
1818 | 548k | if (type == gx_path_type_none) { |
1819 | 548k | if (xps->in_path == false) { |
1820 | 33.2k | if_debug1m('_', xps->memory, "xps_lineto: type not supported %x\n", type); |
1821 | 33.2k | return 0; |
1822 | 33.2k | } |
1823 | 548k | } |
1824 | 548k | } |
1825 | | |
1826 | 1.50M | if ((type & gx_path_type_clip || type == gx_path_type_none) && !image_brush_fill(type, xps->filltype)) { |
1827 | 521k | if (xps->in_path == false || xps->clip_written) |
1828 | 4.17k | return 0; |
1829 | 521k | } |
1830 | | |
1831 | 1.50M | gs_snprintf(line, sizeof(line), " L %g,%g", x, y); |
1832 | 1.50M | write_str_to_current_page(xps, line); |
1833 | 1.50M | if_debug1m('_', xps->memory, "xps_lineto %s\n", line); |
1834 | 1.50M | return 0; |
1835 | 1.50M | } |
1836 | | |
1837 | | static int |
1838 | | xps_curveto(gx_device_vector *vdev, double x0, double y0, |
1839 | | double x1, double y1, double x2, double y2, |
1840 | | double x3, double y3, gx_path_type_t type) |
1841 | 501k | { |
1842 | 501k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1843 | 501k | char line[200]; |
1844 | | |
1845 | | /* skip non-drawing paths for now */ |
1846 | 501k | if (!drawing_path(type, xps->filltype)) { |
1847 | 0 | if (type == gx_path_type_none) { |
1848 | 0 | if (xps->in_path == false) { |
1849 | 0 | if_debug1m('_', xps->memory, "xps_curveto: type not supported %x\n", type); |
1850 | 0 | return 0; |
1851 | 0 | } |
1852 | 0 | } |
1853 | 0 | } |
1854 | | |
1855 | 501k | if ((type & gx_path_type_clip || type == gx_path_type_none) && !image_brush_fill(type, xps->filltype)) { |
1856 | 361 | if (xps->in_path == false || xps->clip_written) |
1857 | 0 | return 0; |
1858 | 361 | } |
1859 | | |
1860 | 501k | gs_snprintf(line, sizeof(line), " C %g,%g %g,%g %g,%g", x1, y1, |
1861 | 501k | x2,y2,x3,y3); |
1862 | 501k | write_str_to_current_page(xps,line); |
1863 | 501k | if_debug1m('_', xps->memory, "xps_curveto %s\n", line); |
1864 | | |
1865 | 501k | return 0; |
1866 | 501k | } |
1867 | | |
1868 | | static int |
1869 | | xps_closepath(gx_device_vector *vdev, double x, double y, |
1870 | | double x_start, double y_start, gx_path_type_t type) |
1871 | 15.7k | { |
1872 | 15.7k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1873 | | |
1874 | | /* skip non-drawing paths for now */ |
1875 | 15.7k | if (!drawing_path(type, xps->filltype)) { |
1876 | 0 | if (type == gx_path_type_none) { |
1877 | 0 | if (xps->in_path == false) { |
1878 | 0 | if_debug1m('_', xps->memory, "xps_closepath: type not supported %x\n", type); |
1879 | 0 | return 0; |
1880 | 0 | } |
1881 | 0 | } |
1882 | 0 | } |
1883 | | |
1884 | 15.7k | if ((type & gx_path_type_clip || type == gx_path_type_none) && !image_brush_fill(type, xps->filltype)) { |
1885 | 52 | if (xps->in_path == false || xps->clip_written) |
1886 | 0 | return 0; |
1887 | 52 | } |
1888 | | |
1889 | 15.7k | write_str_to_current_page(xps, " Z"); |
1890 | 15.7k | if_debug0m('_', xps->memory, "xps_closepath"); |
1891 | | |
1892 | 15.7k | return 0; |
1893 | 15.7k | } |
1894 | | |
1895 | | static int |
1896 | | xps_endpath(gx_device_vector *vdev, gx_path_type_t type) |
1897 | 319k | { |
1898 | 319k | gx_device_xps *xps = (gx_device_xps *)vdev; |
1899 | 319k | char line[200]; |
1900 | 319k | const char *fmt; |
1901 | | |
1902 | 319k | if (xps->in_clip) { |
1903 | 87.0k | xps->in_clip = false; |
1904 | 87.0k | xps->clip_written = true; |
1905 | 87.0k | if (type & gx_path_type_clip) { |
1906 | 87.0k | if (xps->in_path == false) |
1907 | 0 | return 0; |
1908 | 87.0k | } |
1909 | 232k | } else { |
1910 | 232k | if (type & gx_path_type_clip && !image_brush_fill(type, xps->filltype)) { |
1911 | 12.2k | if (xps->in_path == false || xps->clip_written) |
1912 | 12.2k | return 0; |
1913 | 12.2k | } |
1914 | 232k | } |
1915 | | |
1916 | 307k | if (image_brush_fill(type, xps->filltype)) { |
1917 | 1.76k | write_str_to_current_page(xps, "\" >\n"); |
1918 | | /* And now the rest of the details */ |
1919 | 1.76k | xps_finish_image_path(vdev); |
1920 | 305k | } else if (type & gx_path_type_stroke) { |
1921 | | /* NB format width. */ |
1922 | 12.2k | fmt = "\" StrokeThickness=\"%g\" "; |
1923 | 12.2k | gs_snprintf(line, sizeof(line), fmt, xps->linewidth); |
1924 | 12.2k | write_str_to_current_page(xps, line); |
1925 | 12.2k | switch(xps->linecap) { |
1926 | 1.41k | case gs_cap_round: |
1927 | 1.41k | gs_snprintf(line, sizeof(line), "StrokeStartLineCap=\"Round\" StrokeEndLineCap=\"Round\" "); |
1928 | 1.41k | write_str_to_current_page(xps, line); |
1929 | 1.41k | break; |
1930 | 508 | case gs_cap_square: |
1931 | 508 | gs_snprintf(line, sizeof(line), "StrokeStartLineCap=\"Square\" StrokeEndLineCap=\"Square\" "); |
1932 | 508 | write_str_to_current_page(xps, line); |
1933 | 508 | break; |
1934 | 0 | case gs_cap_triangle: |
1935 | 0 | gs_snprintf(line, sizeof(line), "StrokeStartLineCap=\"Triangle\" StrokeEndLineCap=\"Triangle\" "); |
1936 | 0 | write_str_to_current_page(xps, line); |
1937 | 0 | break; |
1938 | 10.3k | case gs_cap_butt: |
1939 | 10.3k | case gs_cap_unknown: |
1940 | 10.3k | default: |
1941 | 10.3k | break; |
1942 | 12.2k | } |
1943 | 12.2k | switch(xps->linejoin) { |
1944 | 1.05k | case gs_join_round: |
1945 | 1.05k | gs_snprintf(line, sizeof(line), "StrokeLineJoin=\"Round\" "); |
1946 | 1.05k | write_str_to_current_page(xps, line); |
1947 | 1.05k | break; |
1948 | 11.2k | case gs_join_miter: |
1949 | 11.2k | gs_snprintf(line, sizeof(line), "StrokeLineJoin=\"Miter\" "); |
1950 | 11.2k | write_str_to_current_page(xps, line); |
1951 | 11.2k | gs_snprintf(line, sizeof(line), "StrokeMiterLimit=\"%g\" ", xps->miterlimit); |
1952 | 11.2k | write_str_to_current_page(xps, line); |
1953 | 11.2k | break; |
1954 | 15 | case gs_join_bevel: |
1955 | 15 | gs_snprintf(line, sizeof(line), "StrokeLineJoin=\"Bevel\" "); |
1956 | 15 | write_str_to_current_page(xps, line); |
1957 | 15 | break; |
1958 | 3 | case gs_join_none: |
1959 | 3 | case gs_join_triangle: |
1960 | 3 | case gs_join_unknown: |
1961 | 3 | default: |
1962 | 3 | break; |
1963 | 12.2k | } |
1964 | 293k | } else { /* fill */ |
1965 | | /* close the path data attribute */ |
1966 | 293k | write_str_to_current_page(xps, " Z\" "); |
1967 | 293k | } |
1968 | | |
1969 | 307k | return 0; |
1970 | 307k | } |
1971 | | |
1972 | | /* Image handling */ |
1973 | | static image_enum_proc_plane_data(xps_image_data); |
1974 | | static image_enum_proc_end_image(xps_image_end_image); |
1975 | | static const gx_image_enum_procs_t xps_image_enum_procs = { |
1976 | | xps_image_data, xps_image_end_image |
1977 | | }; |
1978 | | |
1979 | | /* High level image support */ |
1980 | | /* Prototypes */ |
1981 | | static TIFF* tiff_from_name(gx_device_xps *dev, const char *name, int big_endian, |
1982 | | bool usebigtiff); |
1983 | | static int tiff_set_values(xps_image_enum_t *pie, TIFF *tif, |
1984 | | cmm_profile_t *profile, bool force8bit); |
1985 | | static void xps_tiff_set_handlers(void); |
1986 | | static void xps_tiff_cleanup(xps_image_enum_t *xpie); |
1987 | | |
1988 | | /* Check if we have the ICC profile in the package */ |
1989 | | static xps_icc_data_t* |
1990 | | xps_find_icc(const gx_device_xps *xdev, cmm_profile_t *icc_profile) |
1991 | 4.07k | { |
1992 | 4.07k | xps_icc_data_t *icc_data = xdev->icc_data; |
1993 | | |
1994 | 4.15k | while (icc_data != NULL) { |
1995 | 2.64k | if (icc_data->hash == gsicc_get_hash(icc_profile)) { |
1996 | 2.56k | return icc_data; |
1997 | 2.56k | } |
1998 | 79 | icc_data = icc_data->next; |
1999 | 79 | } |
2000 | 1.50k | return NULL; |
2001 | 4.07k | } |
2002 | | |
2003 | | static int |
2004 | | xps_create_icc_name(const gx_device_xps *xps_dev, cmm_profile_t *profile, char *name) |
2005 | 2.03k | { |
2006 | 2.03k | xps_icc_data_t *icc_data; |
2007 | | |
2008 | 2.03k | icc_data = xps_find_icc(xps_dev, profile); |
2009 | 2.03k | if (icc_data == NULL) |
2010 | 0 | return gs_throw_code(gs_error_rangecheck); /* Should be there */ |
2011 | | |
2012 | 2.03k | snprintf(name, MAXNAME, "%sProfile_%d.icc", PROFILEPATH, icc_data->index); |
2013 | 2.03k | return 0; |
2014 | 2.03k | } |
2015 | | |
2016 | | static void |
2017 | | xps_create_image_name(gx_device *dev, char *name) |
2018 | 2.03k | { |
2019 | 2.03k | gx_device_xps *const xdev = (gx_device_xps *)dev; |
2020 | | |
2021 | 2.03k | snprintf(name, MAXNAME, "%s%d.tif", IMAGEPATH, xdev->image_count); |
2022 | 2.03k | xdev->image_count++; |
2023 | 2.03k | } |
2024 | | |
2025 | | static int |
2026 | | xps_add_icc_relationship(xps_image_enum_t *pie) |
2027 | 2.03k | { |
2028 | 2.03k | gx_device_xps *xps = (gx_device_xps*) (pie->dev); |
2029 | 2.03k | int code; |
2030 | | |
2031 | 2.03k | code = add_new_relationship(xps, pie->icc_name); |
2032 | 2.03k | if (code < 0) |
2033 | 0 | return gs_rethrow_code(code); |
2034 | | |
2035 | 2.03k | return 0; |
2036 | 2.03k | } |
2037 | | |
2038 | | static int |
2039 | | xps_add_image_relationship(xps_image_enum_t *pie) |
2040 | 2.03k | { |
2041 | 2.03k | gx_device_xps *xps = (gx_device_xps*) (pie->dev); |
2042 | 2.03k | int code; |
2043 | | |
2044 | 2.03k | code = add_new_relationship(xps, pie->file_name); |
2045 | 2.03k | if (code < 0) |
2046 | 0 | return gs_rethrow_code(code); |
2047 | 2.03k | return 0; |
2048 | 2.03k | } |
2049 | | |
2050 | | static int |
2051 | | xps_write_profile(const gs_gstate *pgs, char *name, cmm_profile_t *profile, gx_device_xps *xps_dev) |
2052 | 1.50k | { |
2053 | 1.50k | byte *profile_buffer; |
2054 | 1.50k | int size; |
2055 | | |
2056 | | /* Need V2 ICC Profile */ |
2057 | 1.50k | profile_buffer = gsicc_create_getv2buffer(pgs, profile, &size); |
2058 | | |
2059 | | /* Now go ahead and add to the zip archive */ |
2060 | 1.50k | return add_data_to_zip_file(xps_dev, name, profile_buffer, size); |
2061 | 1.50k | } |
2062 | | |
2063 | | static int |
2064 | | xps_begin_typed_image(gx_device *dev, |
2065 | | const gs_gstate *pgs, |
2066 | | const gs_matrix *pmat, |
2067 | | const gs_image_common_t *pic, |
2068 | | const gs_int_rect *prect, |
2069 | | const gx_drawing_color *pdcolor, |
2070 | | const gx_clip_path *pcpath, |
2071 | | gs_memory_t *mem, |
2072 | | gx_image_enum_common_t **pinfo) |
2073 | 3.78k | { |
2074 | 3.78k | gx_device_vector *vdev = (gx_device_vector *)dev; |
2075 | 3.78k | gx_device_xps *xdev = (gx_device_xps *)dev; |
2076 | 3.78k | const gs_image_t *pim = (const gs_image_t *)pic; |
2077 | 3.78k | gs_color_space *pcs; |
2078 | 3.78k | xps_image_enum_t *pie = NULL; |
2079 | 3.78k | xps_icc_data_t *icc_data; |
2080 | 3.78k | gs_matrix mat; |
2081 | 3.78k | int code; |
2082 | 3.78k | gx_clip_path cpath; |
2083 | 3.78k | gs_fixed_rect bbox; |
2084 | 3.78k | int bits_per_pixel; |
2085 | 3.78k | int num_components; |
2086 | 3.78k | int bsize; |
2087 | 3.78k | cmm_profile_t *icc_profile = NULL; |
2088 | 3.78k | gs_color_space_index csindex; |
2089 | 3.78k | float index_decode[2]; |
2090 | 3.78k | gsicc_rendering_param_t rendering_params; |
2091 | 3.78k | bool force8bit = false; |
2092 | | |
2093 | 3.78k | if (pic->type->index != 1) |
2094 | 16 | goto use_default; |
2095 | | |
2096 | 3.76k | pcs = pim->ColorSpace; |
2097 | | /* No image mask yet. Also, need a color space */ |
2098 | 3.76k | if (pcs == NULL || ((const gs_image1_t *)pim)->ImageMask) |
2099 | 1.69k | goto use_default; |
2100 | | |
2101 | | /* No indexed images that are not 8 bit. */ |
2102 | 2.07k | csindex = gs_color_space_get_index(pcs); |
2103 | 2.07k | if (csindex == gs_color_space_index_Indexed && pim->BitsPerComponent != 8) |
2104 | 32 | goto use_default; |
2105 | | |
2106 | | /* Also need gs_gstate for these color spaces */ |
2107 | 2.04k | if (pgs == NULL && (csindex == gs_color_space_index_Indexed || |
2108 | 0 | csindex == gs_color_space_index_Separation || |
2109 | 0 | csindex == gs_color_space_index_DeviceN)) |
2110 | 0 | goto use_default; |
2111 | | |
2112 | 2.04k | if (gs_matrix_invert(&pim->ImageMatrix, &mat) < 0) |
2113 | 4 | goto use_default; |
2114 | 2.03k | if (pmat == NULL) |
2115 | 2.03k | pmat = &ctm_only(pgs); |
2116 | 2.03k | if (pgs) |
2117 | 2.03k | gs_matrix_multiply(&mat, pmat, &mat); |
2118 | | |
2119 | 2.03k | pie = gs_alloc_struct(mem, xps_image_enum_t, &st_xps_image_enum, |
2120 | 2.03k | "xps_begin_image"); |
2121 | 2.03k | if (pie == 0) |
2122 | 0 | return_error(gs_error_VMerror); |
2123 | 2.03k | pie->buffer = NULL; |
2124 | 2.03k | pie->devc_buffer = NULL; |
2125 | 2.03k | pie->pgs = NULL; |
2126 | 2.03k | pie->tif = NULL; |
2127 | | |
2128 | | /* Set the brush types to image */ |
2129 | 2.03k | xps_setstrokebrush(xdev, xps_imagebrush); |
2130 | 2.03k | xps_setfillbrush(xdev, xps_imagebrush); |
2131 | 2.03k | pie->mat = mat; |
2132 | 2.03k | xdev->xps_pie = pie; |
2133 | | /* We need this set a bit early for the ICC relationship writing */ |
2134 | 2.03k | pie->dev = (gx_device*) xdev; |
2135 | | |
2136 | | /* If the color space is DeviceN, Sep or indexed these end up getting |
2137 | | mapped to the color space defined by the device profile. XPS only |
2138 | | support RGB indexed images so we just expand if for now. ICC link |
2139 | | creation etc is handled during the remap/concretization of the colors */ |
2140 | 2.03k | if (csindex == gs_color_space_index_Indexed || |
2141 | 2.03k | csindex == gs_color_space_index_Separation || |
2142 | 2.03k | csindex == gs_color_space_index_DeviceN) { |
2143 | 149 | cmm_dev_profile_t *dev_profile; |
2144 | 149 | pie->pcs = pcs; |
2145 | 149 | rc_increment(pcs); |
2146 | 149 | code = dev_proc(dev, get_profile)(dev, &(dev_profile)); |
2147 | | /* Just use the "default" profile for now */ |
2148 | 149 | icc_profile = dev_profile->device_profile[GS_DEFAULT_DEVICE_PROFILE]; |
2149 | 149 | force8bit = true; /* Output image is 8 bit regardless of source */ |
2150 | 1.88k | } else { |
2151 | | /* An ICC, RGB, CMYK, Gray color space */ |
2152 | 1.88k | pie->pcs = NULL; |
2153 | | /* Get the ICC profile */ |
2154 | 1.88k | if (gs_color_space_is_PSCIE(pcs)) { |
2155 | 0 | if (pcs->icc_equivalent == NULL) { |
2156 | 0 | bool is_lab; |
2157 | 0 | if (pgs == NULL) { |
2158 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2159 | 0 | return_error(gs_error_invalidaccess); |
2160 | 0 | } |
2161 | 0 | gs_colorspace_set_icc_equivalent(pcs, &is_lab, pgs->memory); |
2162 | 0 | } |
2163 | 0 | icc_profile = pcs->icc_equivalent->cmm_icc_profile_data; |
2164 | 1.88k | } else { |
2165 | 1.88k | icc_profile = pcs->cmm_icc_profile_data; |
2166 | 1.88k | } |
2167 | 1.88k | } |
2168 | | |
2169 | | /* Set up for handling case where we are in CIELAB. In this case, we are |
2170 | | going out to the default RGB color space */ |
2171 | 2.03k | if (icc_profile->islab) { |
2172 | | /* Create the link */ |
2173 | 0 | rendering_params.black_point_comp = gsBLACKPTCOMP_ON; |
2174 | 0 | rendering_params.graphics_type_tag = GS_IMAGE_TAG; |
2175 | 0 | rendering_params.override_icc = false; |
2176 | 0 | rendering_params.preserve_black = gsBKPRESNOTSPECIFIED; |
2177 | 0 | rendering_params.rendering_intent = gsPERCEPTUAL; |
2178 | 0 | rendering_params.cmm = gsCMM_DEFAULT; |
2179 | 0 | if (pgs == NULL) { |
2180 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2181 | 0 | return_error(gs_error_invalidaccess); |
2182 | 0 | } |
2183 | 0 | pie->icc_link = gsicc_get_link_profile(pgs, dev, icc_profile, |
2184 | 0 | pgs->icc_manager->default_rgb, &rendering_params, pgs->memory, false); |
2185 | 0 | icc_profile = pgs->icc_manager->default_rgb; |
2186 | 2.03k | } else { |
2187 | 2.03k | pie->icc_link = NULL; |
2188 | 2.03k | } |
2189 | | |
2190 | | /* Now we actually write out the image and icc profile data to the zip |
2191 | | package. Test if profile is already here. If not, add it. */ |
2192 | 2.03k | if (xps_find_icc(xdev, icc_profile) == NULL) { |
2193 | 1.50k | icc_data = (xps_icc_data_t*)gs_alloc_bytes(dev->memory->non_gc_memory, |
2194 | 1.50k | sizeof(xps_icc_data_t), "xps_begin_image"); |
2195 | 1.50k | if (icc_data == NULL) { |
2196 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2197 | 0 | gs_throw(gs_error_VMerror, "Allocation of icc_data failed"); |
2198 | 0 | return_error(gs_error_VMerror); |
2199 | 0 | } |
2200 | | |
2201 | 1.50k | icc_data->hash = gsicc_get_hash(icc_profile); |
2202 | 1.50k | if (xdev->icc_data == NULL) { |
2203 | 1.45k | icc_data->index = 0; |
2204 | 1.45k | xdev->icc_data = icc_data; |
2205 | 1.45k | xdev->icc_data->next = NULL; |
2206 | 1.45k | } else { |
2207 | 49 | icc_data->next = xdev->icc_data; |
2208 | 49 | icc_data->index = icc_data->next->index + 1; |
2209 | 49 | xdev->icc_data = icc_data; |
2210 | 49 | } |
2211 | | |
2212 | | /* Get name for mark up and for relationship. Have to wait and do |
2213 | | this after it is added to the package */ |
2214 | 1.50k | code = xps_create_icc_name(xdev, icc_profile, &(pie->icc_name[0])); |
2215 | 1.50k | if (code < 0) { |
2216 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2217 | 0 | return_error(gs_rethrow_code(code)); |
2218 | 0 | } |
2219 | | |
2220 | | /* Add profile to the package. Here like images we are going to write |
2221 | | the data now. Rather than later. */ |
2222 | 1.50k | if (pgs == NULL) { |
2223 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2224 | 0 | return_error(gs_error_invalidaccess); |
2225 | 0 | } |
2226 | 1.50k | code = xps_write_profile(pgs, &(pie->icc_name[0]), icc_profile, xdev); |
2227 | 1.50k | if (code < 0) { |
2228 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2229 | 0 | return_error(gs_rethrow_code(code)); |
2230 | 0 | } |
2231 | | |
2232 | | /* Add ICC relationship */ |
2233 | 1.50k | xps_add_icc_relationship(pie); |
2234 | 1.50k | } else { |
2235 | | /* Get name for mark up. We already have it in the resource list */ |
2236 | 530 | code = xps_create_icc_name(xdev, icc_profile, &(pie->icc_name[0])); |
2237 | 530 | if (code < 0) { |
2238 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2239 | 0 | return_error(gs_rethrow_code(code)); |
2240 | 0 | } |
2241 | | |
2242 | | /* Add ICC relationship. It may not yet be present for this page. */ |
2243 | 530 | xps_add_icc_relationship(pie); |
2244 | 530 | } |
2245 | | |
2246 | | /* Get image name for mark up */ |
2247 | 2.03k | xps_create_image_name(dev, &(pie->file_name[0])); |
2248 | | /* Set width and height here */ |
2249 | 2.03k | pie->width = pim->Width; |
2250 | 2.03k | pie->height = pim->Height; |
2251 | | |
2252 | 2.03k | if (pcpath == NULL) { |
2253 | 671 | (*dev_proc(dev, get_clipping_box)) (dev, &bbox); |
2254 | 671 | gx_cpath_init_local(&cpath, dev->memory); |
2255 | 671 | code = gx_cpath_from_rectangle(&cpath, &bbox); |
2256 | 671 | if (code < 0) { |
2257 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2258 | 0 | return_error(gs_rethrow_code(code)); |
2259 | 0 | } |
2260 | 671 | pcpath = &cpath; |
2261 | 1.36k | } else { |
2262 | | /* Force vector device to do new path as the clip path is the image |
2263 | | path. I had a case where the clip path ids were the same but the |
2264 | | CTM was changing which resulted in subsequent images coming up |
2265 | | missing on the page. i.e. only the first one was shown. */ |
2266 | 1.36k | ((gx_device_vector*) vdev)->clip_path_id = vdev->no_clip_path_id; |
2267 | 1.36k | } |
2268 | | |
2269 | 2.03k | if (pgs == NULL) { |
2270 | 0 | gs_free_object(mem, *pinfo, "xps_begin_image"); |
2271 | 0 | return_error(gs_error_invalidaccess); |
2272 | 0 | } |
2273 | 2.03k | code = gdev_vector_begin_image(vdev, pgs, pim, pim->format, prect, |
2274 | 2.03k | pdcolor, pcpath, mem, &xps_image_enum_procs, |
2275 | 2.03k | (gdev_vector_image_enum_t *)pie); |
2276 | 2.03k | if (code < 0) { |
2277 | 0 | gs_free_object(mem, pie, "xps_begin_image"); |
2278 | 0 | return_error(gs_rethrow_code(code)); |
2279 | 0 | } |
2280 | | |
2281 | 2.03k | if ((pie->tif = tiff_from_name(xdev, pie->file_name, false, false)) == NULL) { |
2282 | 0 | gs_free_object(mem, pie, "xps_begin_image"); |
2283 | 0 | return_error(gs_error_VMerror); |
2284 | 0 | } |
2285 | | |
2286 | | /* Null out pie. Only needed for the above vector command and tiff set up */ |
2287 | 2.03k | xdev->xps_pie = NULL; |
2288 | 2.03k | xps_tiff_set_handlers(); |
2289 | 2.03k | code = tiff_set_values(pie, pie->tif, icc_profile, force8bit); |
2290 | 2.03k | if (code < 0) { |
2291 | 0 | gs_free_object(mem, pie, "xps_begin_image"); |
2292 | 0 | return_error(gs_rethrow_code(code)); |
2293 | 0 | } |
2294 | 2.03k | code = TIFFCheckpointDirectory(pie->tif); |
2295 | | |
2296 | 2.03k | num_components = gs_color_space_num_components(pcs); |
2297 | 2.03k | bits_per_pixel = pim->BitsPerComponent * num_components; |
2298 | | |
2299 | 2.03k | { |
2300 | | /* This is a really hacky attempt to avoid running out of memory. This is |
2301 | | * inspired by various OSS-fuzz bugs but in particular 53398. This device |
2302 | | * uses the libtiff library to write images into the XPS file, libtiff won't |
2303 | | * let us use our own memory manager and have rejected patches to allow it |
2304 | | * to do so, so it uses system memory. If we have a badly broken file we |
2305 | | * might end up asking it to write a ridiculously large image which will |
2306 | | * cause it to eat all system memory. So here we try to limit it to any pre-defined |
2307 | | * limit. Also, if the width * height * 24 bits (RGB) goes negative then we know |
2308 | | * we've exceeded 2^(64 - 1) bytes, which is unreasonable too. |
2309 | | */ |
2310 | 2.03k | int64_t memory_needed = (int64_t)pim->Width * (int64_t)pim->Height * 3; |
2311 | 2.03k | gs_memory_status_t status; |
2312 | | |
2313 | 2.03k | if (memory_needed < 0) { |
2314 | 0 | gs_free_object(mem, pie, "xps_begin_image"); |
2315 | 0 | return_error(gs_error_VMerror); |
2316 | 0 | } |
2317 | | |
2318 | 2.03k | gs_memory_status(dev->memory->gs_lib_ctx->memory, &status); |
2319 | 2.03k | if (status.limit < (size_t)~1) { |
2320 | 2.03k | if ( memory_needed > status.limit) { |
2321 | 2 | gs_free_object(mem, pie, "xps_begin_image"); |
2322 | 2 | return_error(gs_error_VMerror); |
2323 | 2 | } |
2324 | 2.03k | } |
2325 | 2.03k | } |
2326 | | |
2327 | 2.03k | pie->decode_st.bps = bits_per_pixel / num_components; |
2328 | 2.03k | pie->bytes_comp = (pie->decode_st.bps > 8 ? 2 : 1); |
2329 | 2.03k | pie->decode_st.spp = num_components; |
2330 | 2.03k | pie->decode_st.unpack = NULL; |
2331 | 2.03k | get_unpack_proc((gx_image_enum_common_t*)pie, &(pie->decode_st), pim->format, |
2332 | 2.03k | pim->Decode); |
2333 | 2.03k | if (pie->decode_st.unpack == NULL){ |
2334 | 0 | gs_free_object(mem, pie, "xps_begin_image"); |
2335 | 0 | return_error(gs_rethrow_code(gs_error_rangecheck)); |
2336 | 0 | } |
2337 | | |
2338 | | /* The decode mapping for index colors needs an adjustment */ |
2339 | 2.03k | if (csindex == gs_color_space_index_Indexed) { |
2340 | 143 | if (pim->Decode[0] == 0 && |
2341 | 143 | pim->Decode[1] == 255) { |
2342 | 141 | index_decode[0] = 0; |
2343 | 141 | index_decode[1] = 1.0; |
2344 | 141 | } else { |
2345 | 2 | index_decode[0] = pim->Decode[0]; |
2346 | 2 | index_decode[1] = pim->Decode[1]; |
2347 | 2 | } |
2348 | 143 | get_map(&(pie->decode_st), pim->format, index_decode); |
2349 | 1.89k | } else { |
2350 | 1.89k | get_map(&(pie->decode_st), pim->format, pim->Decode); |
2351 | 1.89k | } |
2352 | | |
2353 | | /* Allocate our decode buffer. */ |
2354 | 2.03k | bsize = ((pie->decode_st.bps > 8 ? (pim->Width) * 2 : pim->Width) + 15) * num_components; |
2355 | 2.03k | pie->buffer = gs_alloc_bytes(mem, bsize, "xps_begin_typed_image(buffer)"); |
2356 | 2.03k | if (pie->buffer == 0) { |
2357 | 0 | gs_free_object(mem, pie, "xps_begin_typed_image"); |
2358 | 0 | *pinfo = NULL; |
2359 | 0 | return_error(gs_error_VMerror); |
2360 | 0 | } |
2361 | | |
2362 | | /* If needed, allocate our device color buffer. We will always do 8 bit here */ |
2363 | 2.03k | if (csindex == gs_color_space_index_Indexed || |
2364 | 2.03k | csindex == gs_color_space_index_Separation || |
2365 | 2.03k | csindex == gs_color_space_index_DeviceN) { |
2366 | 149 | bsize = (pim->Width + 15) * icc_profile->num_comps; |
2367 | 149 | pie->devc_buffer = gs_alloc_bytes(mem, bsize, "xps_begin_typed_image(devc_buffer)"); |
2368 | 149 | if (pie->devc_buffer == 0) { |
2369 | 0 | gs_free_object(mem, pie, "xps_begin_typed_image"); |
2370 | 0 | *pinfo = NULL; |
2371 | 0 | return_error(gs_error_VMerror); |
2372 | 0 | } |
2373 | 149 | } |
2374 | | |
2375 | | /* Also, the color remaps need the gs_gstate */ |
2376 | 2.03k | pie->pgs = pgs; |
2377 | 2.03k | if (pgs != NULL) |
2378 | 2.03k | pie->pgs_level = pgs->level; |
2379 | | |
2380 | 2.03k | *pinfo = (gx_image_enum_common_t *)pie; |
2381 | 2.03k | return 0; |
2382 | | |
2383 | 1.74k | use_default: |
2384 | 1.74k | return gx_default_begin_typed_image(dev, pgs, pmat, pic, prect, |
2385 | 1.74k | pdcolor, pcpath, mem, pinfo); |
2386 | 2.03k | } |
2387 | | |
2388 | | /* Handles conversion from decoded DeviceN, Sep or Indexed space to Device color |
2389 | | space. The encoding specified by the image object is already handled at this |
2390 | | point. Slow due to the multitude of conversions that take place. I.e. conv to |
2391 | | float, frac and back to byte, but it will get the job done. Since we can have |
2392 | | indexed spaces that reference DeviceN spaces etc, we really have to do it |
2393 | | this way or code up a bunch of optimized special cases. Note here we always |
2394 | | output 8 bit regardless of input */ |
2395 | | static int |
2396 | | set_device_colors(xps_image_enum_t *pie) |
2397 | 23.3k | { |
2398 | 23.3k | gx_device *pdev = pie->dev; |
2399 | 23.3k | const gs_gstate *pgs = pie->pgs; |
2400 | 23.3k | gs_color_space *pcs = pie->pcs; |
2401 | 23.3k | byte *src = pie->buffer; |
2402 | 23.3k | byte *des = pie->devc_buffer; |
2403 | 23.3k | int num_src = gs_color_space_num_components(pcs); |
2404 | 23.3k | int num_des = pdev->color_info.num_components; |
2405 | 23.3k | int width = pie->width; |
2406 | 23.3k | cs_proc_remap_color((*remap_color)) = pcs->type->remap_color; |
2407 | 23.3k | int i, j, code = 0; |
2408 | 23.3k | gs_client_color cc; |
2409 | 23.3k | gx_device_color devc; |
2410 | 23.3k | gx_color_value cm_values[GX_DEVICE_COLOR_MAX_COMPONENTS]; |
2411 | 23.3k | float scale = 1.0; |
2412 | | |
2413 | 23.3k | if (pie->decode_st.bps > 8) { |
2414 | 0 | unsigned short *src_ptr = (unsigned short*)src; |
2415 | 0 | int pos_src = 0; |
2416 | 0 | int pos_des = 0; |
2417 | 0 | for (i = 0; i < width; i++) { |
2418 | 0 | for (j = 0; j < num_src; j++, pos_src++) { |
2419 | 0 | cc.paint.values[j] = (float)(src_ptr[pos_src]) / 65535.0; |
2420 | 0 | } |
2421 | 0 | code = remap_color(&cc, pcs, &devc, pgs, pdev, gs_color_select_source); |
2422 | 0 | dev_proc(pdev, decode_color)(pdev, devc.colors.pure, cm_values); |
2423 | 0 | for (j = 0; j < num_des; j++, pos_des++) { |
2424 | 0 | des[pos_des] = (cm_values[j] >> 8); |
2425 | 0 | } |
2426 | 0 | } |
2427 | 23.3k | } else { |
2428 | 23.3k | int pos_src = 0; |
2429 | 23.3k | int pos_des = 0; |
2430 | 23.3k | if (gs_color_space_get_index(pcs) != gs_color_space_index_Indexed) |
2431 | 423 | scale = 255.0; |
2432 | 3.49M | for (i = 0; i < width; i++) { |
2433 | 7.03M | for (j = 0; j < num_src; j++, pos_src++) { |
2434 | 3.56M | cc.paint.values[j] = (float)(src[pos_src]) / scale; |
2435 | 3.56M | } |
2436 | 3.47M | code = remap_color(&cc, pcs, &devc, pgs, pdev, gs_color_select_source); |
2437 | 3.47M | dev_proc(pdev, decode_color)(pdev, devc.colors.pure, cm_values); |
2438 | 13.8M | for (j = 0; j < num_des; j++, pos_des++) { |
2439 | 10.4M | des[pos_des] = (cm_values[j] >> 8); |
2440 | 10.4M | } |
2441 | 3.47M | } |
2442 | 23.3k | } |
2443 | 23.3k | return code; |
2444 | 23.3k | } |
2445 | | |
2446 | | /* Chunky or planar in and chunky out */ |
2447 | | static int |
2448 | | xps_image_data(gx_image_enum_common_t *info, |
2449 | | const gx_image_plane_t *planes, int height, int *rows_used) |
2450 | 1.45M | { |
2451 | 1.45M | xps_image_enum_t *pie = (xps_image_enum_t *)info; |
2452 | 1.45M | int data_bit = planes[0].data_x * info->plane_depths[0]; |
2453 | 1.45M | int width_bits = pie->width * info->plane_depths[0]; |
2454 | 1.45M | int bytes_comp = pie->bytes_comp; |
2455 | 1.45M | int i, plane; |
2456 | 1.45M | int code = 0; |
2457 | 1.45M | int width = pie->width; |
2458 | 1.45M | int num_planes = pie->num_planes; |
2459 | 1.45M | int dsize = (((width + (planes[0]).data_x) * pie->decode_st.spp * |
2460 | 1.45M | pie->decode_st.bps / num_planes + 7) >> 3); |
2461 | 1.45M | void *bufend = (void*)(pie->buffer + width * bytes_comp * pie->decode_st.spp); |
2462 | 1.45M | byte *outbuffer; |
2463 | | |
2464 | 1.45M | if (info->pgs != NULL && info->pgs->level < info->pgs_level) |
2465 | 0 | return_error(gs_error_undefinedresult); |
2466 | | |
2467 | 1.45M | if (width_bits != pie->bits_per_row || (data_bit & 7) != 0) |
2468 | 0 | return_error(gs_error_rangecheck); |
2469 | 1.45M | if (height > pie->height - pie->y) |
2470 | 39 | height = pie->height - pie->y; |
2471 | | |
2472 | 2.98M | for (i = 0; i < height; pie->y++, i++) { |
2473 | 1.53M | int pdata_x; |
2474 | | /* Plane zero done here to get the pointer to the data */ |
2475 | 1.53M | const byte *data_ptr = planes[0].data + planes[0].raster * i + (data_bit >> 3); |
2476 | 1.53M | byte *des_ptr = pie->buffer; |
2477 | 1.53M | byte *buffer = (byte *)(*pie->decode_st.unpack)(des_ptr, &pdata_x, |
2478 | 1.53M | data_ptr, 0, dsize, &(pie->decode_st.map[0]), |
2479 | 1.53M | pie->decode_st.spread, pie->decode_st.spp); |
2480 | | |
2481 | | /* Step through the planes having decode do the repack to chunky as |
2482 | | well as any decoding needed */ |
2483 | 1.53M | for (plane = 1; plane < num_planes; plane++) { |
2484 | 0 | data_ptr = planes[plane].data + planes[plane].raster * i + (data_bit >> 3); |
2485 | 0 | des_ptr = pie->buffer + plane * pie->bytes_comp; |
2486 | | /* This does the planar to chunky conversion */ |
2487 | 0 | (*pie->decode_st.unpack)(des_ptr, &pdata_x, |
2488 | 0 | data_ptr, 0, dsize, &(pie->decode_st.map[plane]), |
2489 | 0 | pie->decode_st.spread, pie->decode_st.spp); |
2490 | 0 | } |
2491 | | |
2492 | | /* CIELAB does not get mapped. Handled in color management */ |
2493 | 1.53M | if (pie->icc_link == NULL) { |
2494 | 1.53M | pie->decode_st.applymap(pie->decode_st.map, (void*)buffer, |
2495 | 1.53M | pie->decode_st.spp, (void*)pie->buffer, bufend); |
2496 | | /* Index, Sep and DeviceN are mapped to color space defined by |
2497 | | device profile */ |
2498 | 1.53M | if (pie->pcs != NULL) { |
2499 | | /* In device color space */ |
2500 | 23.3k | code = set_device_colors(pie); |
2501 | 23.3k | if (code < 0) |
2502 | 0 | return gs_rethrow_code(code); |
2503 | 23.3k | outbuffer = pie->devc_buffer; |
2504 | 1.51M | } else { |
2505 | | /* In source color space */ |
2506 | 1.51M | outbuffer = pie->buffer; |
2507 | 1.51M | } |
2508 | 1.53M | } else { |
2509 | | /* CIELAB to default RGB */ |
2510 | 0 | gsicc_bufferdesc_t input_buff_desc; |
2511 | 0 | gsicc_bufferdesc_t output_buff_desc; |
2512 | 0 | gsicc_init_buffer(&input_buff_desc, 3, bytes_comp, |
2513 | 0 | false, false, false, 0, width * bytes_comp * 3, |
2514 | 0 | 1, width); |
2515 | 0 | gsicc_init_buffer(&output_buff_desc, 3, bytes_comp, |
2516 | 0 | false, false, false, 0, width * bytes_comp * 3, |
2517 | 0 | 1, width); |
2518 | 0 | code = (pie->icc_link->procs.map_buffer)(pie->dev, pie->icc_link, |
2519 | 0 | &input_buff_desc, &output_buff_desc, (void*)buffer, |
2520 | 0 | (void*)pie->buffer); |
2521 | 0 | if (code < 0) |
2522 | 0 | return code; |
2523 | 0 | outbuffer = pie->buffer; |
2524 | 0 | } |
2525 | 1.53M | code = TIFFWriteScanline(pie->tif, outbuffer, pie->y, 0); |
2526 | 1.53M | if (code < 0) |
2527 | 0 | return code; |
2528 | 1.53M | } |
2529 | 1.45M | *rows_used = height; |
2530 | 1.45M | return pie->y >= pie->height; |
2531 | 1.45M | } |
2532 | | |
2533 | | static int |
2534 | | xps_add_tiff_image(xps_image_enum_t *pie) |
2535 | 2.03k | { |
2536 | 2.03k | gx_device_xps *xdev = (gx_device_xps *)(pie->dev); |
2537 | 2.03k | int code; |
2538 | | |
2539 | 2.03k | code = add_file_to_zip_file(xdev, pie->file_name, pie->fid); |
2540 | 2.03k | gp_fclose(pie->fid); |
2541 | 2.03k | return code; |
2542 | 2.03k | } |
2543 | | |
2544 | | /* Clean up by releasing the buffers. */ |
2545 | | static int |
2546 | | xps_image_end_image(gx_image_enum_common_t * info, bool draw_last) |
2547 | 2.03k | { |
2548 | 2.03k | xps_image_enum_t *pie = (xps_image_enum_t *)info; |
2549 | 2.03k | int code = 0; |
2550 | | |
2551 | | /* N.B. Write the final strip, if any. */ |
2552 | | |
2553 | 2.03k | code = TIFFWriteDirectory(pie->tif); |
2554 | 2.03k | xps_tiff_cleanup(pie); |
2555 | | |
2556 | | /* Stuff the image into the zip archive and close the file */ |
2557 | 2.03k | code = xps_add_tiff_image(pie); |
2558 | 2.03k | if (code < 0) |
2559 | 0 | goto exit; |
2560 | | |
2561 | | /* Reset the brush type to solid */ |
2562 | 2.03k | xps_setstrokebrush((gx_device_xps *) (pie->dev), xps_solidbrush); |
2563 | 2.03k | xps_setfillbrush((gx_device_xps *) (pie->dev), xps_solidbrush); |
2564 | | |
2565 | | /* Add the image relationship */ |
2566 | 2.03k | code = xps_add_image_relationship(pie); |
2567 | | |
2568 | 2.03k | gs_free_object(pie->memory, pie, "xps_image_end_image"); |
2569 | 2.03k | exit: |
2570 | 2.03k | return code; |
2571 | 2.03k | } |
2572 | | |
2573 | | /* Tiff related code so that we can output all the image data in Tiff format. |
2574 | | Tiff has the advantage of supporting Gray, RGB, CMYK as well as multiple |
2575 | | bit depts and having lossless compression. Much of this was borrowed from |
2576 | | gstiffio.c */ |
2577 | | #define TIFF_PRINT_BUF_LENGTH 1024 |
2578 | | static const char tifs_msg_truncated[] = "\n*** Previous line has been truncated.\n"; |
2579 | | |
2580 | | static int |
2581 | | tiff_set_values(xps_image_enum_t *pie, TIFF *tif, cmm_profile_t *profile, |
2582 | | bool force8bit) |
2583 | 2.03k | { |
2584 | 2.03k | int bits = 8; |
2585 | | |
2586 | 2.03k | TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, pie->height); |
2587 | 2.03k | TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, pie->width); |
2588 | 2.03k | TIFFSetField(tif, TIFFTAG_IMAGELENGTH, pie->height); |
2589 | 2.03k | TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); |
2590 | 2.03k | TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); |
2591 | 2.03k | TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW); |
2592 | 2.03k | TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); |
2593 | 2.03k | TIFFSetField(tif, TIFFTAG_XRESOLUTION, 96.0); |
2594 | 2.03k | TIFFSetField(tif, TIFFTAG_YRESOLUTION, 96.0); |
2595 | | |
2596 | 2.03k | switch (profile->data_cs) { |
2597 | 396 | case gsGRAY: |
2598 | 396 | if (pie->bits_per_pixel > 8 && !force8bit) |
2599 | 0 | bits = 16; |
2600 | 396 | TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits); |
2601 | 396 | TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); |
2602 | 396 | TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); |
2603 | 396 | break; |
2604 | 1.57k | case gsRGB: |
2605 | 1.57k | case gsCIELAB: |
2606 | 1.57k | if ((pie->num_planes > 1 && pie->bits_per_pixel > 8 && !force8bit) || |
2607 | 1.57k | (pie->num_planes == 1 && pie->bits_per_pixel / 3 > 8 && !force8bit)) |
2608 | 0 | bits = 16; |
2609 | 1.57k | TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits); |
2610 | 1.57k | TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); |
2611 | 1.57k | TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); |
2612 | 1.57k | break; |
2613 | 67 | case gsCMYK: |
2614 | 67 | if ((pie->num_planes > 1 && pie->bits_per_pixel > 8 && !force8bit) || |
2615 | 67 | (pie->num_planes == 1 && pie->bits_per_pixel / 4 > 8 && !force8bit)) |
2616 | 0 | bits = 16; |
2617 | 67 | TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits); |
2618 | 67 | TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED); |
2619 | 67 | TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4); |
2620 | 67 | break; |
2621 | 0 | default: |
2622 | 0 | return gs_throw_code(gs_error_rangecheck); |
2623 | 2.03k | } |
2624 | 2.03k | return 0; |
2625 | 2.03k | } |
2626 | | |
2627 | | /* place to hold the data for our libtiff i/o hooks */ |
2628 | | typedef struct tifs_io_xps_t |
2629 | | { |
2630 | | gx_device_xps *pdev; |
2631 | | gp_file *fid; |
2632 | | } tifs_io_xps; |
2633 | | |
2634 | | /* libtiff i/o hooks */ |
2635 | | static size_t |
2636 | | xps_tifsWriteProc(thandle_t fd, void* buf, size_t size) |
2637 | 21.1k | { |
2638 | 21.1k | tifs_io_xps *tiffio = (tifs_io_xps *)fd; |
2639 | 21.1k | size_t size_io = (size_t)size; |
2640 | 21.1k | gp_file *fid = tiffio->fid; |
2641 | 21.1k | size_t count; |
2642 | | |
2643 | 21.1k | if ((size_t)size_io != size) { |
2644 | 0 | return (size_t)-1; |
2645 | 0 | } |
2646 | | |
2647 | 21.1k | if (fid == NULL) |
2648 | 0 | return gs_throw_code(gs_error_Fatal); |
2649 | | |
2650 | 21.1k | count = gp_fwrite(buf, 1, size, fid); |
2651 | 21.1k | if (count != size) { |
2652 | 0 | gp_fclose(fid); |
2653 | 0 | return gs_rethrow_code(-1); |
2654 | 0 | } |
2655 | 21.1k | gp_fflush(fid); |
2656 | 21.1k | return size; |
2657 | 21.1k | } |
2658 | | |
2659 | | static uint64_t |
2660 | | xps_tifsSeekProc(thandle_t fd, uint64_t off, int origin) |
2661 | 25.2k | { |
2662 | 25.2k | tifs_io_xps *tiffio = (tifs_io_xps *)fd; |
2663 | 25.2k | gs_offset_t off_io = (gs_offset_t)off; |
2664 | 25.2k | gp_file *fid = tiffio->fid; |
2665 | | |
2666 | 25.2k | if ((uint64_t)off_io != off) { |
2667 | 0 | return (uint64_t)-1; |
2668 | 0 | } |
2669 | | |
2670 | 25.2k | if (fid == NULL && off == 0) |
2671 | 0 | return 0; |
2672 | | |
2673 | 25.2k | if (fid == NULL) |
2674 | 0 | return (uint64_t)-1; |
2675 | | |
2676 | 25.2k | if (gp_fseek(fid, (gs_offset_t)off, origin) < 0) { |
2677 | 0 | return (uint64_t)-1; |
2678 | 0 | } |
2679 | 25.2k | return (gp_ftell(fid)); |
2680 | 25.2k | } |
2681 | | |
2682 | | static int |
2683 | | xps_tifsCloseProc(thandle_t fd) |
2684 | 0 | { |
2685 | 0 | tifs_io_xps *tiffio = (tifs_io_xps *)fd; |
2686 | 0 | gx_device_xps *pdev = tiffio->pdev; |
2687 | |
|
2688 | 0 | gs_free(pdev->memory->non_gc_memory, tiffio, sizeof(tifs_io_xps), 1, |
2689 | 0 | "xps_tifsCloseProc"); |
2690 | 0 | return 0; |
2691 | 0 | } |
2692 | | |
2693 | | static int |
2694 | | xps_tifsDummyMapProc(thandle_t fd, void** pbase, toff_t* psize) |
2695 | 0 | { |
2696 | 0 | (void)fd; |
2697 | 0 | (void)pbase; |
2698 | 0 | (void)psize; |
2699 | 0 | return (0); |
2700 | 0 | } |
2701 | | |
2702 | | static void |
2703 | | xps_tifsDummyUnmapProc(thandle_t fd, void* base, toff_t size) |
2704 | 0 | { |
2705 | 0 | (void)fd; |
2706 | 0 | (void)base; |
2707 | 0 | (void)size; |
2708 | 0 | } |
2709 | | |
2710 | | static size_t |
2711 | | xps_tifsReadProc(thandle_t fd, void* buf, size_t size) |
2712 | 0 | { |
2713 | 0 | size_t size_io = (size_t)size; |
2714 | |
|
2715 | 0 | if ((size_t)size_io != size) { |
2716 | 0 | return (size_t)-1; |
2717 | 0 | } |
2718 | 0 | return 0; |
2719 | 0 | } |
2720 | | |
2721 | | /* Could not see where this was getting used so basically a dummy proc |
2722 | | for now. */ |
2723 | | static uint64_t |
2724 | | xps_tifsSizeProc(thandle_t fd) |
2725 | 0 | { |
2726 | 0 | uint64_t length = 0; |
2727 | |
|
2728 | 0 | return length; |
2729 | 0 | } |
2730 | | |
2731 | | static void |
2732 | | xps_tifsWarningHandlerEx(thandle_t client_data, const char *module, |
2733 | | const char *fmt, va_list ap) |
2734 | 0 | { |
2735 | 0 | tifs_io_xps *tiffio = (tifs_io_xps *)client_data; |
2736 | 0 | gx_device_xps *pdev = tiffio->pdev; |
2737 | 0 | int count; |
2738 | 0 | char buf[TIFF_PRINT_BUF_LENGTH]; |
2739 | |
|
2740 | 0 | count = vsnprintf(buf, sizeof(buf), fmt, ap); |
2741 | 0 | if (count >= sizeof(buf) || count < 0) { /* C99 || MSVC */ |
2742 | 0 | dmlprintf1(pdev->memory, "%s", buf); |
2743 | 0 | dmlprintf1(pdev->memory, "%s\n", tifs_msg_truncated); |
2744 | 0 | } |
2745 | 0 | else { |
2746 | 0 | dmlprintf1(pdev->memory, "%s\n", buf); |
2747 | 0 | } |
2748 | 0 | } |
2749 | | |
2750 | | static void |
2751 | | xps_tifsErrorHandlerEx(thandle_t client_data, const char *module, |
2752 | | const char *fmt, va_list ap) |
2753 | 0 | { |
2754 | 0 | tifs_io_xps *tiffio = (tifs_io_xps *)client_data; |
2755 | 0 | gx_device_xps *pdev = tiffio->pdev; |
2756 | 0 | const char *max_size_error = "Maximum TIFF file size exceeded"; |
2757 | 0 | int count; |
2758 | 0 | char buf[TIFF_PRINT_BUF_LENGTH]; |
2759 | |
|
2760 | 0 | count = vsnprintf(buf, sizeof(buf), fmt, ap); |
2761 | 0 | if (count >= sizeof(buf) || count < 0) { /* C99 || MSVC */ |
2762 | 0 | dmlprintf1(pdev->memory, "%s\n", buf); |
2763 | 0 | dmlprintf1(pdev->memory, "%s", tifs_msg_truncated); |
2764 | 0 | } |
2765 | 0 | else { |
2766 | 0 | dmlprintf1(pdev->memory, "%s\n", buf); |
2767 | 0 | } |
2768 | |
|
2769 | 0 | #if (TIFFLIB_VERSION >= 20111221) |
2770 | 0 | if (!strncmp(fmt, max_size_error, strlen(max_size_error))) { |
2771 | 0 | dmlprintf(pdev->memory, "Use -dUseBigTIFF(=true) for BigTIFF output\n"); |
2772 | 0 | } |
2773 | 0 | #endif |
2774 | 0 | } |
2775 | | |
2776 | | static void |
2777 | | xps_tiff_set_handlers(void) |
2778 | 2.03k | { |
2779 | 2.03k | (void)TIFFSetErrorHandler(NULL); |
2780 | 2.03k | (void)TIFFSetWarningHandler(NULL); |
2781 | 2.03k | (void)TIFFSetErrorHandlerExt(xps_tifsErrorHandlerEx); |
2782 | 2.03k | (void)TIFFSetWarningHandlerExt(xps_tifsWarningHandlerEx); |
2783 | 2.03k | } |
2784 | | |
2785 | | static TIFF * |
2786 | | tiff_from_name(gx_device_xps *dev, const char *name, int big_endian, bool usebigtiff) |
2787 | 2.03k | { |
2788 | 2.03k | char mode[5] = "w"; |
2789 | 2.03k | int modelen = 1; |
2790 | 2.03k | TIFF *t; |
2791 | 2.03k | tifs_io_xps *tiffio; |
2792 | 2.03k | gs_memory_t *mem = dev->memory->non_gc_memory; |
2793 | 2.03k | char *filename; |
2794 | | |
2795 | 2.03k | if (big_endian) |
2796 | 0 | mode[modelen++] = 'b'; |
2797 | 2.03k | else |
2798 | 2.03k | mode[modelen++] = 'l'; |
2799 | | |
2800 | 2.03k | if (usebigtiff) |
2801 | | /* this should never happen for libtiff < 4.0 - see tiff_put_some_params() */ |
2802 | 0 | mode[modelen++] = '8'; |
2803 | | |
2804 | 2.03k | mode[modelen] = (char)0; |
2805 | | |
2806 | 2.03k | tiffio = (tifs_io_xps *)gs_malloc(dev->memory->non_gc_memory, |
2807 | 2.03k | sizeof(tifs_io_xps), 1, "tiff_from_name"); |
2808 | 2.03k | if (!tiffio) { |
2809 | 0 | return NULL; |
2810 | 0 | } |
2811 | 2.03k | tiffio->pdev = dev; |
2812 | | |
2813 | 2.03k | filename = (char *)gs_alloc_bytes(mem, gp_file_name_sizeof, |
2814 | 2.03k | "tiff_from_name(filename)"); |
2815 | 2.03k | if (!filename) |
2816 | 0 | return NULL; |
2817 | | |
2818 | 2.03k | tiffio->fid = gp_open_scratch_file_rm(mem, "tif-", filename, "wb+"); |
2819 | 2.03k | dev->xps_pie->fid = tiffio->fid; /* We will be closing it from here */ |
2820 | | |
2821 | 2.03k | gs_free_object(mem, filename, "tiff_from_name(filename)"); |
2822 | | |
2823 | 2.03k | t = TIFFClientOpen(name, mode, |
2824 | 2.03k | (thandle_t)tiffio, (TIFFReadWriteProc)xps_tifsReadProc, |
2825 | 2.03k | (TIFFReadWriteProc)xps_tifsWriteProc, (TIFFSeekProc)xps_tifsSeekProc, |
2826 | 2.03k | xps_tifsCloseProc, (TIFFSizeProc)xps_tifsSizeProc, xps_tifsDummyMapProc, |
2827 | 2.03k | xps_tifsDummyUnmapProc); |
2828 | 2.03k | return t; |
2829 | 2.03k | } |
2830 | | |
2831 | | static void xps_tiff_cleanup(xps_image_enum_t *xpie) |
2832 | 4.07k | { |
2833 | 4.07k | if (xpie->tif != NULL) { |
2834 | 2.03k | void *t = TIFFClientdata(xpie->tif); |
2835 | 2.03k | TIFFCleanup(xpie->tif); |
2836 | 2.03k | xpie->tif = NULL; |
2837 | 2.03k | gs_free_object(xpie->dev->memory->non_gc_memory, t, "xps_image_enum_finalize"); |
2838 | 2.03k | } |
2839 | 4.07k | } |
2840 | | |
2841 | | static void |
2842 | | xps_image_enum_finalize(const gs_memory_t *cmem, void *vptr) |
2843 | 2.03k | { |
2844 | 2.03k | xps_image_enum_t *xpie = (xps_image_enum_t *)vptr; |
2845 | 2.03k | gx_device_xps *xdev = (gx_device_xps *)xpie->dev; |
2846 | | |
2847 | 2.03k | xps_tiff_cleanup(xpie); |
2848 | | |
2849 | 2.03k | xpie->dev = NULL; |
2850 | 2.03k | if (xpie->pcs != NULL) |
2851 | 2.03k | rc_decrement(xpie->pcs, "xps_image_end_image (pcs)"); |
2852 | 2.03k | if (xpie->buffer != NULL) |
2853 | 2.03k | gs_free_object(xpie->memory, xpie->buffer, "xps_image_end_image"); |
2854 | 2.03k | if (xpie->devc_buffer != NULL) |
2855 | 149 | gs_free_object(xpie->memory, xpie->devc_buffer, "xps_image_end_image"); |
2856 | | |
2857 | | /* ICC clean up */ |
2858 | 2.03k | if (xpie->icc_link != NULL) |
2859 | 0 | gsicc_release_link(xpie->icc_link); |
2860 | 2.03k | xdev->xps_pie = NULL; |
2861 | 2.03k | } |