Coverage Report

Created: 2026-02-14 07:09

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