Coverage Report

Created: 2026-08-08 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpstop.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
/* Top-level API implementation of XML Paper Specification */
17
18
/* Language wrapper implementation (see pltop.h) */
19
20
#include "ghostxps.h"
21
22
#include "pltop.h"
23
#include "plmain.h"
24
25
#include "plparse.h" /* for e_ExitLanguage */
26
#include "plmain.h"
27
#include "gxdevice.h" /* so we can include gxht.h below */
28
#include "gxht.h" /* gsht1.h is incomplete, we need storage size of gs_halftone */
29
#include "gsht1.h"
30
#include "gsparam.h"
31
32
#include <assert.h>
33
34
static int xps_install_halftone(xps_context_t *ctx, gx_device *pdevice);
35
36
/*
37
 * The XPS interpeter is identical to pl_interp_t.
38
 * The XPS interpreter instance is derived from pl_interp_implementation_t.
39
 */
40
41
typedef struct xps_interp_instance_s xps_interp_instance_t;
42
43
struct xps_interp_instance_s
44
{
45
    gs_memory_t *memory;                /* memory allocator to use */
46
47
    xps_context_t *ctx;
48
    gp_file *scratch_file;
49
    char scratch_name[gp_file_name_sizeof];
50
};
51
52
static int
53
xps_detect_language(const char *s, int len)
54
28.1k
{
55
28.1k
    if (len < 2)
56
23
        return 0;
57
28.1k
    if (memcmp(s, "PK", 2) == 0)
58
301
        return 80; /* Pretty sure, but not 100, so the SO one can override us. */
59
27.8k
    return 0;
60
28.1k
}
61
62
static const pl_interp_characteristics_t *
63
xps_impl_characteristics(const pl_interp_implementation_t *pimpl)
64
59.4k
{
65
59.4k
    static pl_interp_characteristics_t xps_characteristics =
66
59.4k
    {
67
59.4k
        "XPS",
68
59.4k
        xps_detect_language,
69
59.4k
    };
70
59.4k
    return &xps_characteristics;
71
59.4k
}
72
73
static void
74
xps_set_nocache(pl_interp_implementation_t *impl, gs_font_dir *font_dir)
75
149
{
76
149
    bool nocache;
77
149
    xps_interp_instance_t *xpsi  = impl->interp_client_data;
78
149
    nocache = pl_main_get_nocache(xpsi->memory);
79
149
    if (nocache)
80
0
        gs_setcachelimit(font_dir, 0);
81
149
    return;
82
149
}
83
84
85
static int
86
xps_set_icc_user_params(pl_interp_implementation_t *impl, gs_gstate *pgs)
87
149
{
88
149
    xps_interp_instance_t *xpsi  = impl->interp_client_data;
89
149
    return pl_set_icc_params(xpsi->memory, pgs);
90
149
}
91
92
/* Do per-instance interpreter allocation/init. No device is set yet */
93
static int
94
xps_impl_allocate_interp_instance(pl_interp_implementation_t *impl,
95
                                  gs_memory_t *pmem)
96
16.1k
{
97
16.1k
    int code = 0;
98
16.1k
    xps_interp_instance_t *instance;
99
16.1k
    xps_context_t *ctx;
100
16.1k
    gs_gstate *pgs;
101
102
16.1k
    instance = (xps_interp_instance_t *) gs_alloc_bytes(pmem,
103
16.1k
            sizeof(xps_interp_instance_t), "xps_impl_allocate_interp_instance");
104
105
16.1k
    ctx = (xps_context_t *) gs_alloc_bytes(pmem,
106
16.1k
            sizeof(xps_context_t), "xps_impl_allocate_interp_instance");
107
108
16.1k
    pgs = gs_gstate_alloc(pmem);
109
110
16.1k
    if (!instance || !ctx || !pgs)
111
0
    {
112
0
        code = gs_error_VMerror;
113
0
        goto end;
114
0
    }
115
116
    /* FIXME: check return value */
117
16.1k
    gsicc_init_iccmanager(pgs);
118
16.1k
    memset(ctx, 0, sizeof(xps_context_t));
119
120
16.1k
    ctx->instance = instance;
121
16.1k
    ctx->memory = pmem;
122
16.1k
    ctx->pgs = pgs;
123
    /* Declare PDL client support for high level patterns, for the benefit
124
     * of pdfwrite and other high-level devices
125
     */
126
16.1k
    ctx->pgs->have_pattern_streams = true;
127
16.1k
    ctx->fontdir = NULL;
128
16.1k
    ctx->preserve_tr_mode = 0;
129
130
16.1k
    ctx->file = NULL;
131
16.1k
    ctx->zip_count = 0;
132
16.1k
    ctx->zip_table = NULL;
133
16.1k
    ctx->in_high_level_pattern = false;
134
135
    /* Gray, RGB and CMYK profiles set when color spaces installed in graphics lib */
136
16.1k
    ctx->gray_lin = gs_cspace_new_ICC(ctx->memory, ctx->pgs, -1);
137
16.1k
    ctx->gray = gs_cspace_new_ICC(ctx->memory, ctx->pgs, 1);
138
16.1k
    ctx->cmyk = gs_cspace_new_ICC(ctx->memory, ctx->pgs, 4);
139
16.1k
    ctx->srgb = gs_cspace_new_ICC(ctx->memory, ctx->pgs, 3);
140
141
    /* scrgb needs special treatment */
142
16.1k
    ctx->scrgb = gs_cspace_new_scrgb(ctx->memory, ctx->pgs);
143
144
16.1k
    instance->ctx = ctx;
145
16.1k
    instance->scratch_file = NULL;
146
16.1k
    instance->scratch_name[0] = 0;
147
16.1k
    instance->memory = pmem;
148
149
    /* NB needs error handling */
150
16.1k
    ctx->fontdir = gs_font_dir_alloc(ctx->memory);
151
16.1k
    if (!ctx->fontdir) {
152
0
        code = gs_error_VMerror;
153
0
        goto end;
154
0
    }
155
16.1k
    gs_setaligntopixels(ctx->fontdir, 1); /* no subpixels */
156
16.1k
    gs_setgridfittt(ctx->fontdir, 1); /* see gx_ttf_outline in gxttfn.c for values */
157
158
16.1k
    impl->interp_client_data = instance;
159
160
16.1k
    end:
161
16.1k
    if (code < 0) {
162
0
        if (instance) {
163
0
            gs_free_object(pmem, instance, "xps_impl_allocate_interp_instance");
164
0
        }
165
0
        if (ctx) {
166
0
            assert(!ctx->fontdir);
167
0
            rc_decrement(ctx->gray_lin, "gs_cspace_new_ICC");
168
0
            rc_decrement(ctx->gray, "gs_cspace_new_ICC");
169
0
            rc_decrement(ctx->cmyk, "gs_cspace_new_ICC");
170
0
            rc_decrement(ctx->srgb, "gs_cspace_new_ICC");
171
0
            rc_decrement(ctx->scrgb, "gs_cspace_new_ICC");
172
0
            gs_free_object(pmem, ctx, "xps_impl_allocate_interp_instance");
173
0
        }
174
0
        if (pgs) {
175
0
            gs_gstate_free(pgs);
176
0
        }
177
0
    }
178
16.1k
    return code;
179
16.1k
}
180
181
/* Prepare interp instance for the next "job" */
182
static int
183
xps_impl_init_job(pl_interp_implementation_t *impl,
184
                 gx_device                  *pdevice)
185
149
{
186
149
    xps_interp_instance_t *instance = impl->interp_client_data;
187
149
    xps_context_t *ctx = instance->ctx;
188
149
    gs_c_param_list list;
189
149
    int code;
190
149
    bool disable_page_handler = false;
191
149
    bool true_val = true;
192
149
    gs_memory_t* mem = ctx->memory;
193
194
149
    ctx->font_table = xps_hash_new(ctx);
195
149
    ctx->colorspace_table = xps_hash_new(ctx);
196
197
149
    ctx->start_part = NULL;
198
199
149
    ctx->use_transparency = 1;
200
149
    if (getenv("XPS_DISABLE_TRANSPARENCY"))
201
0
        ctx->use_transparency = 0;
202
203
149
    ctx->opacity_only = 0;
204
149
    ctx->fill_rule = 0;
205
206
149
    code = gs_setdevice_no_erase(ctx->pgs, pdevice);
207
149
    if (code < 0)
208
0
        goto cleanup_setdevice;
209
210
    /* Check if the device wants PreserveTrMode (pdfwrite) */
211
149
    gs_c_param_list_write(&list, pdevice->memory);
212
149
    code = gs_getdeviceparams(pdevice, (gs_param_list *)&list);
213
149
    if (code >= 0) {
214
149
        bool trm = false;
215
149
        gs_c_param_list_read(&list);
216
149
        code = param_read_bool((gs_param_list *)&list, "PreserveTrMode", &trm);
217
149
        ctx->preserve_tr_mode = (int)trm;
218
149
    }
219
149
    gs_c_param_list_release(&list);
220
149
    if (code < 0)
221
0
        return code;
222
223
149
    gs_setaccuratecurves(ctx->pgs, true); /* NB not sure */
224
149
    gs_setfilladjust(ctx->pgs, 0, 0);
225
149
    (void)xps_set_icc_user_params(impl, ctx->pgs);
226
149
    xps_set_nocache(impl, ctx->fontdir);
227
228
149
    gs_setscanconverter(ctx->pgs, pl_main_get_scanconverter(ctx->memory));
229
230
    /* Disable the page handler as the XPS interpreter will handle page range.
231
       List takes precedent over firstpage lastpage */
232
149
    if (pdevice->PageList != NULL && pdevice->PageHandlerPushed)
233
0
    {
234
0
        ctx->page_range = xps_alloc(ctx, sizeof(xps_page_range_t));
235
0
        if (!ctx->page_range)
236
0
        {
237
0
            return gs_rethrow(gs_error_VMerror, "out of memory: page_range struct");
238
0
        }
239
240
0
        ctx->page_range->page_list = xps_strdup(ctx, pdevice->PageList->Pages);
241
0
        if (!ctx->page_range->page_list)
242
0
        {
243
0
            return gs_rethrow(gs_error_VMerror, "out of memory: page_list");
244
0
        }
245
0
        disable_page_handler = true;
246
0
        ctx->page_range->reverse = false;
247
0
    }
248
149
    else if ((pdevice->FirstPage > 0 || pdevice->LastPage > 0) &&
249
0
        pdevice->PageHandlerPushed)
250
0
    {
251
0
        disable_page_handler = true;
252
253
0
        ctx->page_range = xps_alloc(ctx, sizeof(xps_page_range_t));
254
0
        if (!ctx->page_range)
255
0
        {
256
0
            return gs_throw(gs_error_VMerror, "out of memory: page_range struct");
257
0
        }
258
0
        ctx->page_range->first = pdevice->FirstPage;
259
0
        ctx->page_range->last = pdevice->LastPage;
260
0
        ctx->page_range->current = 0;
261
0
        ctx->page_range->page_list = NULL;
262
263
0
        if (ctx->page_range->first != 0 &&
264
0
            ctx->page_range->last != 0 &&
265
0
            ctx->page_range->first > ctx->page_range->last)
266
0
            ctx->page_range->reverse = true;
267
0
        else
268
0
            ctx->page_range->reverse = false;
269
0
    }
270
271
149
    if (disable_page_handler)
272
0
    {
273
0
        gs_c_param_list_write(&list, mem);
274
0
        code = param_write_bool((gs_param_list*)&list, "DisablePageHandler", &(true_val));
275
0
        if (code >= 0)
276
0
        {
277
0
            gs_c_param_list_read(&list);
278
0
            code = gs_putdeviceparams(pdevice, (gs_param_list*)&list);
279
0
            gs_c_param_list_release(&list);
280
0
            if (code < 0) {
281
0
                return gs_rethrow(code, "cannot set device parameters");
282
0
            }
283
0
        }
284
0
    }
285
286
    /* gsave and grestore (among other places) assume that */
287
    /* there are at least 2 gstates on the graphics stack. */
288
    /* Ensure that now. */
289
149
    code = gs_gsave(ctx->pgs);
290
149
    if (code < 0)
291
0
        goto cleanup_gsave;
292
293
149
    code = gs_erasepage(ctx->pgs);
294
149
    if (code < 0)
295
0
        goto cleanup_erase;
296
297
149
    code = xps_install_halftone(ctx, pdevice);
298
149
    if (code < 0)
299
0
        goto cleanup_halftone;
300
301
149
    return 0;
302
303
0
cleanup_halftone:
304
0
cleanup_erase:
305
    /* undo gsave */
306
0
    gs_grestore_only(ctx->pgs);     /* destroys gs_save stack */
307
308
0
cleanup_gsave:
309
    /* undo setdevice */
310
0
    gs_nulldevice(ctx->pgs);
311
312
0
cleanup_setdevice:
313
    /* nothing to undo */
314
0
    return code;
315
0
}
316
317
/* Parse an entire random access file */
318
static int
319
xps_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
320
149
{
321
149
    xps_interp_instance_t *instance = impl->interp_client_data;
322
149
    xps_context_t *ctx = instance->ctx;
323
149
    int code;
324
325
149
    code = xps_process_file(ctx, filename);
326
149
    if (code)
327
126
        gs_catch1(code, "cannot process xps file '%s'", filename);
328
329
149
    return code;
330
149
}
331
332
/* Do any setup for parser per-cursor */
333
static int                      /* ret 0 or +ve if ok, else -ve error code */
334
xps_impl_process_begin(pl_interp_implementation_t * impl)
335
0
{
336
0
    return 0;
337
0
}
338
339
/* Parse a cursor-full of data */
340
static int
341
xps_impl_process(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
342
0
{
343
0
    xps_interp_instance_t *instance = impl->interp_client_data;
344
0
    xps_context_t *ctx = instance->ctx;
345
0
    int avail, n;
346
347
0
    if (!instance->scratch_file)
348
0
    {
349
0
        instance->scratch_file = gp_open_scratch_file(ctx->memory,
350
0
            "ghostxps-scratch-", instance->scratch_name, "wb");
351
0
        if (!instance->scratch_file)
352
0
        {
353
0
            gs_catch(gs_error_invalidfileaccess, "cannot open scratch file");
354
0
            return e_ExitLanguage;
355
0
        }
356
0
        if_debug1m('|', ctx->memory, "xps: open scratch file '%s'\n", instance->scratch_name);
357
0
    }
358
359
0
    avail = cursor->limit - cursor->ptr;
360
0
    n = gp_fwrite(cursor->ptr + 1, 1, avail, instance->scratch_file);
361
0
    if (n != avail)
362
0
    {
363
0
        gs_catch(gs_error_invalidfileaccess, "cannot write to scratch file");
364
0
        return e_ExitLanguage;
365
0
    }
366
0
    cursor->ptr = cursor->limit;
367
368
0
    return 0;
369
0
}
370
371
static int                      /* ret 0 or +ve if ok, else -ve error code */
372
xps_impl_process_end(pl_interp_implementation_t * impl)
373
0
{
374
0
    return 0;
375
0
}
376
377
/* Skip to end of job.
378
 * Return 1 if done, 0 ok but EOJ not found, else negative error code.
379
 */
380
static int
381
xps_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *pcursor)
382
0
{
383
    /* assume XPS cannot be pjl embedded */
384
0
    pcursor->ptr = pcursor->limit;
385
0
    return 0;
386
0
}
387
388
/* Parser action for end-of-file */
389
static int
390
xps_impl_process_eof(pl_interp_implementation_t *impl)
391
0
{
392
0
    xps_interp_instance_t *instance = impl->interp_client_data;
393
0
    xps_context_t *ctx = instance->ctx;
394
0
    int code;
395
396
0
    if (instance->scratch_file)
397
0
    {
398
0
        if_debug0m('|', ctx->memory, "xps: executing scratch file\n");
399
0
        gp_fclose(instance->scratch_file);
400
0
        instance->scratch_file = NULL;
401
0
        code = xps_process_file(ctx, instance->scratch_name);
402
0
        gp_unlink(ctx->memory, instance->scratch_name);
403
0
        if (code < 0)
404
0
        {
405
0
            gs_catch(code, "cannot process XPS file");
406
0
            return e_ExitLanguage;
407
0
        }
408
0
    }
409
410
0
    return 0;
411
0
}
412
413
/* Report any errors after running a job */
414
static int
415
xps_impl_report_errors(pl_interp_implementation_t *impl,
416
        int code,           /* prev termination status */
417
        long file_position, /* file position of error, -1 if unknown */
418
        bool force_to_cout  /* force errors to cout */
419
        )
420
0
{
421
0
    return 0;
422
0
}
423
424
static void xps_free_key_func(xps_context_t *ctx, void *ptr)
425
7
{
426
7
    xps_free(ctx, ptr);
427
7
}
428
429
static void xps_free_font_func(xps_context_t *ctx, void *ptr)
430
7
{
431
7
    xps_free_font(ctx, ptr);
432
7
}
433
434
static void xps_free_hashed_colorspace(xps_context_t *ctx, void *ptr)
435
0
{
436
0
    gs_color_space *cs = (gs_color_space *)ptr;
437
0
    rc_decrement(cs, "xps_free_hashed_colorspace");
438
0
}
439
440
/* Wrap up interp instance after a "job" */
441
static int
442
xps_impl_dnit_job(pl_interp_implementation_t *impl)
443
149
{
444
149
    xps_interp_instance_t *instance = impl->interp_client_data;
445
149
    xps_context_t *ctx = instance->ctx;
446
149
    int i, code;
447
448
    /* return to original gstate */
449
149
    code = gs_grestore_only(ctx->pgs); /* destroys gs_save stack */
450
451
149
    if (gs_debug_c('|'))
452
0
        xps_debug_fixdocseq(ctx);
453
454
79.3k
    for (i = 0; i < ctx->zip_count; i++)
455
79.1k
        xps_free(ctx, ctx->zip_table[i].name);
456
149
    xps_free(ctx, ctx->zip_table);
457
458
    /* TODO: free resources too */
459
149
    xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func);
460
149
    xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, xps_free_hashed_colorspace);
461
462
149
    if (ctx->page_range)
463
0
    {
464
0
        if (ctx->page_range->page_list)
465
0
            xps_free(ctx, ctx->page_range->page_list);
466
0
        xps_free(ctx, ctx->page_range);
467
0
        ctx->page_range = NULL;
468
0
    }
469
470
149
    xps_free_fixed_pages(ctx);
471
149
    xps_free_fixed_documents(ctx);
472
473
149
    return code;
474
149
}
475
476
/* Deallocate a interpreter instance */
477
static int
478
xps_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
479
16.1k
{
480
16.1k
    xps_interp_instance_t *instance = impl->interp_client_data;
481
16.1k
    xps_context_t *ctx = instance->ctx;
482
16.1k
    gs_memory_t *mem = ctx->memory;
483
484
    /* language clients don't free the font cache machinery */
485
16.1k
    rc_decrement_cs(ctx->gray_lin, "xps_impl_deallocate_interp_instance");
486
16.1k
    rc_decrement_cs(ctx->gray, "xps_impl_deallocate_interp_instance");
487
16.1k
    rc_decrement_cs(ctx->cmyk, "xps_impl_deallocate_interp_instance");
488
16.1k
    rc_decrement_cs(ctx->srgb, "xps_impl_deallocate_interp_instance");
489
16.1k
    rc_decrement_cs(ctx->scrgb, "xps_impl_deallocate_interp_instance");
490
491
16.1k
    gx_pattern_cache_free(ctx->pgs->pattern_cache);
492
16.1k
    gs_gstate_free(ctx->pgs);
493
494
16.1k
    gs_free_object(mem, ctx->start_part, "xps_impl_deallocate_interp_instance");
495
16.1k
    gs_free_object(mem, ctx->fontdir, "xps_impl_deallocate_interp_instance");
496
16.1k
    gs_free_object(mem, ctx, "xps_impl_deallocate_interp_instance");
497
16.1k
    gs_free_object(mem, instance, "xps_impl_deallocate_interp_instance");
498
499
16.1k
    return 0;
500
16.1k
}
501
502
/* Parser implementation descriptor */
503
pl_interp_implementation_t xps_implementation =
504
{
505
    xps_impl_characteristics,
506
    xps_impl_allocate_interp_instance,
507
    NULL,                       /* get_device_memory */
508
    NULL,                       /* set_param */
509
    NULL,                       /* add_path */
510
    NULL,                       /* post_args_init */
511
    xps_impl_init_job,
512
    NULL,                       /* run_prefix_commands */
513
    xps_impl_process_file,
514
    xps_impl_process_begin,
515
    xps_impl_process,
516
    xps_impl_process_end,
517
    xps_impl_flush_to_eoj,
518
    xps_impl_process_eof,
519
    xps_impl_report_errors,
520
    xps_impl_dnit_job,
521
    xps_impl_deallocate_interp_instance,
522
    NULL,
523
};
524
525
/*
526
 * End-of-page function called by XPS parser.
527
 */
528
int
529
xps_show_page(xps_context_t *ctx, int num_copies, int flush)
530
23
{
531
23
    return pl_finish_page(ctx->memory->gs_lib_ctx->top_of_system,
532
23
                          ctx->pgs, num_copies, flush);
533
23
}
534
535
/*
536
 * We need to install a halftone ourselves, this is not
537
 * done automatically.
538
 */
539
540
static float
541
identity_transfer(double tint, const gx_transfer_map *ignore_map)
542
38.1k
{
543
38.1k
    return tint;
544
38.1k
}
545
546
/* The following is a 45 degree spot screen with the spots enumerated
547
 * in a defined order. */
548
static byte order16x16[256] = {
549
    38, 11, 14, 32, 165, 105, 90, 171, 38, 12, 14, 33, 161, 101, 88, 167,
550
    30, 6, 0, 16, 61, 225, 231, 125, 30, 6, 1, 17, 63, 222, 227, 122,
551
    27, 3, 8, 19, 71, 242, 205, 110, 28, 4, 9, 20, 74, 246, 208, 106,
552
    35, 24, 22, 40, 182, 46, 56, 144, 36, 25, 22, 41, 186, 48, 58, 148,
553
    152, 91, 81, 174, 39, 12, 15, 34, 156, 95, 84, 178, 40, 13, 16, 34,
554
    69, 212, 235, 129, 31, 7, 2, 18, 66, 216, 239, 133, 32, 8, 2, 18,
555
    79, 254, 203, 114, 28, 4, 10, 20, 76, 250, 199, 118, 29, 5, 10, 21,
556
    193, 44, 54, 142, 36, 26, 23, 42, 189, 43, 52, 139, 37, 26, 24, 42,
557
    39, 12, 15, 33, 159, 99, 87, 169, 38, 11, 14, 33, 163, 103, 89, 172,
558
    31, 7, 1, 17, 65, 220, 229, 123, 30, 6, 1, 17, 62, 223, 233, 127,
559
    28, 4, 9, 20, 75, 248, 210, 108, 27, 3, 9, 19, 72, 244, 206, 112,
560
    36, 25, 23, 41, 188, 49, 60, 150, 35, 25, 22, 41, 184, 47, 57, 146,
561
    157, 97, 85, 180, 40, 13, 16, 35, 154, 93, 83, 176, 39, 13, 15, 34,
562
    67, 218, 240, 135, 32, 8, 3, 19, 70, 214, 237, 131, 31, 7, 2, 18,
563
    78, 252, 197, 120, 29, 5, 11, 21, 80, 255, 201, 116, 29, 5, 10, 21,
564
    191, 43, 51, 137, 37, 27, 24, 43, 195, 44, 53, 140, 37, 26, 23, 42
565
};
566
567
#define source_phase_x 4
568
#define source_phase_y 0
569
570
static int
571
xps_install_halftone(xps_context_t *ctx, gx_device *pdevice)
572
149
{
573
149
    gs_halftone ht;
574
149
    gs_string thresh;
575
149
    int code;
576
577
149
    int width = 16;
578
149
    int height = 16;
579
149
    thresh.data = order16x16;
580
149
    thresh.size = width * height;
581
582
149
    if (gx_device_must_halftone(pdevice))
583
149
    {
584
149
        memset(&ht.rc, 0x00, sizeof(ht.rc));
585
149
        ht.type = ht_type_threshold;
586
149
        ht.objtype = HT_OBJTYPE_DEFAULT;
587
149
        ht.params.threshold.width = width;
588
149
        ht.params.threshold.height = height;
589
149
        ht.params.threshold.thresholds.data = thresh.data;
590
149
        ht.params.threshold.thresholds.size = thresh.size;
591
149
        ht.params.threshold.transfer = 0;
592
149
        ht.params.threshold.transfer_closure.proc = 0;
593
594
149
        gs_settransfer(ctx->pgs, identity_transfer);
595
596
149
        code = gs_sethalftone(ctx->pgs, &ht);
597
149
        if (code < 0)
598
0
            return gs_throw(code, "could not install halftone");
599
600
149
        code = gs_sethalftonephase(ctx->pgs, 0, 0);
601
149
        if (code < 0)
602
0
            return gs_throw(code, "could not set halftone phase");
603
149
    }
604
605
149
    return 0;
606
149
}