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