Coverage Report

Created: 2025-06-10 07:17

/src/ghostpdl/base/gdevnup.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
/* Device to implement N-up printing */
17
#include "math_.h"
18
#include "memory_.h"
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gpmisc.h"
22
#include "gsparam.h"
23
#include "gxdevice.h"
24
#include "gsdevice.h"   /* requires gsmatrix.h */
25
#include "gxiparam.h"   /* for image source size */
26
#include "gxgstate.h"
27
#include "gxpaint.h"
28
#include "gxpath.h"
29
#include "gxcpath.h"
30
#include "gsstype.h"
31
#include "gdevprn.h"
32
#include "gdevp14.h"        /* Needed to patch up the procs after compositor creation */
33
#include "gdevsclass.h"
34
#include "gxdevsop.h"
35
#include "gdevnup.h"
36
37
/* GC descriptor */
38
#define public_st_nup_device()  /* in gsdevice.c */\
39
  gs_public_st_complex_only(st_nup_device, gx_device, "Nup Device",\
40
    0, nup_device_enum_ptrs, nup_device_reloc_ptrs, default_subclass_finalize)
41
42
static
43
0
ENUM_PTRS_WITH(nup_device_enum_ptrs, gx_device *dev);
44
0
return 0; /* default case */
45
0
case 0:ENUM_RETURN(gx_device_enum_ptr(dev->parent));
46
0
case 1:ENUM_RETURN(gx_device_enum_ptr(dev->child));
47
0
ENUM_PTRS_END
48
0
static RELOC_PTRS_WITH(nup_device_reloc_ptrs, gx_device *dev)
49
0
{
50
0
    dev->parent = gx_device_reloc_ptr(dev->parent, gcst);
51
0
    dev->child = gx_device_reloc_ptr(dev->child, gcst);
52
0
}
53
0
RELOC_PTRS_END
54
55
public_st_nup_device();
56
57
58
/**************************************************************************************/
59
/* Externals not in headers                                                           */
60
/* Imported from gsdparam.c                                                           */
61
extern void rc_free_NupControl(gs_memory_t * mem, void *ptr_in, client_name_t cname);
62
/**************************************************************************************/
63
64
/* This device is one of the 'subclassing' devices, part of a chain or pipeline
65
 * of devices, each of which can process some aspect of the graphics methods
66
 * before passing them on to the next device in the chain.
67
 *
68
 * This operates by hooking the device procs:
69
 *    get_initial_matrix  To modify the scale and origin of the sub-page
70
 *    fillpage      To prevent erasing previously imaged sub-pages
71
 *    output_page   To ignore all output_page calls until all sub-pages
72
 *        have been imaged
73
 *    close_device    To output the final (partial) collection of sub-pages
74
 */
75
76
/* Device procedures */
77
static dev_proc_get_initial_matrix(nup_get_initial_matrix);
78
static dev_proc_close_device(nup_close_device);
79
static dev_proc_put_params(nup_put_params);
80
static dev_proc_output_page(nup_output_page);
81
static dev_proc_fillpage(nup_fillpage);
82
static dev_proc_dev_spec_op(nup_dev_spec_op);
83
84
/* The device prototype */
85
86
#define MAX_COORD (max_int_in_fixed - 1000)
87
#define MAX_RESOLUTION 4000
88
89
static void
90
nup_initialize_device_procs(gx_device *dev)
91
0
{
92
0
    default_subclass_initialize_device_procs(dev);
93
94
0
    set_dev_proc(dev, get_initial_matrix, nup_get_initial_matrix);
95
0
    set_dev_proc(dev, output_page, nup_output_page);
96
0
    set_dev_proc(dev, close_device, nup_close_device);
97
0
    set_dev_proc(dev, put_params, nup_put_params); /* to catch PageSize changes */
98
0
    set_dev_proc(dev, fillpage, nup_fillpage);
99
0
    set_dev_proc(dev, dev_spec_op, nup_dev_spec_op);
100
0
}
101
102
const
103
gx_device_nup gs_nup_device =
104
{
105
    /*
106
     * Define the device as 8-bit gray scale to avoid computing halftones.
107
     */
108
    std_device_dci_type_body_sc(gx_device_nup, nup_initialize_device_procs,
109
                        "N-up", &st_nup_device,
110
                        MAX_COORD, MAX_COORD,
111
                        MAX_RESOLUTION, MAX_RESOLUTION,
112
                        1, 8, 255, 0, 256, 1, NULL, NULL, NULL)
113
};
114
115
#undef MAX_COORD
116
#undef MAX_RESOLUTION
117
118
static void
119
nup_disable_nesting(Nup_device_subclass_data *pNup_data)
120
0
{
121
    /* set safe non-nesting defaults if we don't know the size of the Nested Page */
122
0
    pNup_data->PagesPerNest = 1;
123
0
    pNup_data->NupH = 1;
124
0
    pNup_data->NupV = 1;
125
0
    pNup_data->Scale = 1.0;
126
0
    pNup_data->PageCount = 0;
127
0
}
128
129
static int
130
ParseNupControl(gx_device *dev, Nup_device_subclass_data *pNup_data)
131
0
{
132
0
    int code = 0;
133
0
    float HScale, VScale;
134
135
    /* Make sure PageW and PageH are set -- from dev->width, dev->height */
136
0
    pNup_data->PageW = dev->width * 72.0 / dev->HWResolution[0];
137
0
    pNup_data->PageH = dev->height * 72.0 / dev->HWResolution[1];
138
139
    /* pNup_data->NestedPage[WH] size is set by nup_put_params by PageSize or .MediaSize */
140
0
    if (dev->NupControl== NULL) {
141
0
        nup_disable_nesting(pNup_data);
142
0
        return 0;
143
0
    }
144
    /* First parse the NupControl string for our parameters */
145
0
    if (sscanf(dev->NupControl->nupcontrol_str, "%dx%d", &(pNup_data->NupH), &(pNup_data->NupV)) != 2) {
146
0
        emprintf1(dev->memory, "*** Invalid NupControl format '%s'\n", dev->NupControl->nupcontrol_str);
147
0
        nup_disable_nesting(pNup_data);
148
0
        return_error(gs_error_unknownerror);
149
0
    }
150
0
    pNup_data->PagesPerNest = pNup_data->NupH * pNup_data->NupV;
151
152
    /* -dNupControl=1x1 effectively turns off nesting */
153
0
    if (pNup_data->PagesPerNest == 1) {
154
0
        nup_disable_nesting(pNup_data);
155
0
        return 0;
156
0
    }
157
0
    if (pNup_data->NestedPageW == 0.0 || pNup_data->NestedPageH == 0.0) {
158
0
        pNup_data->NestedPageW = pNup_data->PageW;
159
0
        pNup_data->NestedPageH = pNup_data->PageH;
160
0
    }
161
    /* Calculate based on the PageW and PageH and NestedPage size */
162
    /* Set HSize, VSize, Scale, HMargin, and VMargin      */
163
164
0
    HScale = pNup_data->PageW / (pNup_data->NestedPageW * pNup_data->NupH);
165
0
    VScale = pNup_data->PageH / (pNup_data->NestedPageH * pNup_data->NupV);
166
0
    if (HScale < VScale) {
167
0
        pNup_data->Scale = HScale;
168
0
        pNup_data->HMargin = 0.0;
169
0
        pNup_data->VMargin = (pNup_data->PageH - (HScale * pNup_data->NestedPageH * pNup_data->NupV))/2.0;
170
0
    } else {
171
0
        pNup_data->Scale = VScale;
172
0
        pNup_data->VMargin = 0.0;
173
0
        pNup_data->HMargin = (pNup_data->PageW - (VScale * pNup_data->NestedPageW * pNup_data->NupH))/2.0;
174
0
    }
175
0
    pNup_data->HSize = pNup_data->NestedPageW * pNup_data->Scale;
176
0
    pNup_data->VSize = pNup_data->NestedPageH * pNup_data->Scale;
177
178
0
    return code;
179
0
}
180
181
static void
182
nup_get_initial_matrix(gx_device *dev, gs_matrix *pmat)
183
0
{
184
0
    int code = 0, Hindex, Vindex;
185
0
    Nup_device_subclass_data *pNup_data = dev->subclass_data;
186
187
0
    if (pNup_data->PagesPerNest == 0)   /* not yet initialized */
188
0
        code = ParseNupControl(dev, pNup_data);
189
190
0
    default_subclass_get_initial_matrix(dev, pmat); /* get the matrix from the device */
191
0
    if (code < 0)
192
0
        return;
193
194
0
    if (pNup_data->PagesPerNest == 1)
195
0
        return;           /* nesting disabled */
196
197
    /* Modify the matrix according to N-up nesting paramters */
198
0
    pmat->tx += pNup_data->HMargin * pmat->xx;
199
0
    pmat->ty += pNup_data->VMargin * pmat->yy;    /* ty is the bottom */
200
201
0
    Hindex = imod(pNup_data->PageCount, pNup_data->NupH);
202
0
    Vindex = pNup_data->PageCount/pNup_data->NupH;
203
0
    Vindex = pNup_data->NupV - (imod(Vindex, pNup_data->NupV) + 1); /* rows from top down */
204
205
0
    pmat->tx += pNup_data->HSize * Hindex * pmat->xx;
206
0
    pmat->tx += pNup_data->VSize * Vindex * pmat->xy;
207
208
0
    pmat->ty += pNup_data->HSize * Hindex * pmat->yx;
209
0
    pmat->ty += pNup_data->VSize * Vindex * pmat->yy;
210
211
0
    pmat->xx *= pNup_data->Scale;
212
0
    pmat->xy *= pNup_data->Scale;
213
0
    pmat->yx *= pNup_data->Scale;
214
0
    pmat->yy *= pNup_data->Scale;
215
216
0
    return;
217
0
}
218
219
/* Used to set/resest child device's MediaSize which is needed around output_page */
220
static void
221
nup_set_children_MediaSize(gx_device *dev, float PageW, float PageH)
222
0
{
223
0
    do {
224
0
        dev = dev->child;
225
0
        dev->MediaSize[0] = PageW;
226
0
        dev->MediaSize[1] = PageH;
227
0
    } while (dev->child != NULL);
228
0
    return;
229
0
}
230
231
static int
232
nup_flush_nest_to_output(gx_device *dev, Nup_device_subclass_data *pNup_data)
233
0
{
234
0
    int code = 0;
235
236
0
    nup_set_children_MediaSize(dev, pNup_data->PageW, pNup_data->PageH);
237
0
    code = default_subclass_output_page(dev, 1, true);
238
0
    nup_set_children_MediaSize(dev, pNup_data->NestedPageW, pNup_data->NestedPageH);
239
240
0
    pNup_data->PageCount = 0;
241
0
    return code;
242
0
}
243
244
static int
245
nup_close_device(gx_device *dev)
246
0
{
247
0
    int code = 0, acode = 0;
248
0
    Nup_device_subclass_data *pNup_data = dev->subclass_data;
249
250
0
    if (pNup_data->PagesPerNest == 0)
251
0
        code = ParseNupControl(dev, pNup_data);
252
0
    if (code < 0)
253
0
        return code;
254
255
0
    if (pNup_data->PageCount > 0)
256
0
        acode = nup_flush_nest_to_output(dev, pNup_data);
257
258
    /* Reset the Nup control data */
259
    /* NB: the data will be freed from non_gc_memory by the finalize function */
260
0
    memset(pNup_data, 0, sizeof(Nup_device_subclass_data));
261
262
    /* close children devices, even if there was an error from flush (acode < 0) */
263
0
    code = default_subclass_close_device(dev);
264
265
0
    return min(code, acode);
266
0
}
267
268
/* Read .MediaSize or, if supported as a synonym, PageSize. */
269
static int
270
param_MediaSize(gs_param_list * plist, gs_param_name pname,
271
                const float *res, gs_param_float_array * pa)
272
0
{
273
0
    gs_param_name param_name;
274
0
    int ecode = 0;
275
0
    int code;
276
277
0
    switch (code = param_read_float_array(plist, (param_name = pname), pa)) {
278
0
    case 0:
279
0
        if (pa->size != 2) {
280
0
          ecode = gs_note_error(gs_error_rangecheck);
281
0
          pa->data = 0; /* mark as not filled */
282
0
        } else {
283
0
            float width_new = pa->data[0] * res[0] / 72;
284
0
            float height_new = pa->data[1] * res[1] / 72;
285
286
0
            if (width_new < 0 || height_new < 0)
287
0
                ecode = gs_note_error(gs_error_rangecheck);
288
0
#define max_coord (max_fixed / fixed_1)
289
0
#if max_coord < max_int
290
0
            else if (width_new > (long)max_coord || height_new > (long)max_coord)
291
0
                ecode = gs_note_error(gs_error_limitcheck);
292
0
#endif
293
0
#undef max_coord
294
0
            else
295
0
                break;
296
0
        }
297
0
        goto err;
298
0
    default:
299
0
        ecode = code;
300
0
err:  param_signal_error(plist, param_name, ecode);
301
        /* fall through */
302
0
    case 1:
303
0
        pa->data = 0;   /* mark as not filled */
304
0
    }
305
0
    return ecode;
306
0
}
307
308
/* Horrible hacked version of param_list_copy from gsparamx.c.
309
 * Copy one parameter list to another, recursively if necessary,
310
 * rewriting PageUsesTransparency to be true if it occurs. */
311
static int
312
copy_and_modify_sub(gs_param_list *plto, gs_param_list *plfrom, int *present)
313
0
{
314
0
    gs_param_enumerator_t key_enum;
315
0
    gs_param_key_t key;
316
0
    bool copy_persists;
317
0
    int code;
318
319
0
    if (present)
320
0
        *present = 0;
321
0
    if (plfrom == NULL)
322
0
        return 0;
323
324
    /* If plfrom and plto use different allocators, we must copy
325
     * aggregate values even if they are "persistent". */
326
0
    copy_persists = plto->memory == plfrom->memory;
327
328
0
    param_init_enumerator(&key_enum);
329
0
    while ((code = param_get_next_key(plfrom, &key_enum, &key)) == 0) {
330
0
        char string_key[256]; /* big enough for any reasonable key */
331
0
        gs_param_typed_value value;
332
0
        gs_param_collection_type_t coll_type;
333
0
        gs_param_typed_value copy;
334
335
0
        if (key.size > sizeof(string_key) - 1) {
336
0
            code = gs_note_error(gs_error_rangecheck);
337
0
            break;
338
0
        }
339
0
        memcpy(string_key, key.data, key.size);
340
0
        string_key[key.size] = 0;
341
0
        if ((code = param_read_typed(plfrom, string_key, &value)) != 0) {
342
0
            code = (code > 0 ? gs_note_error(gs_error_unknownerror) : code);
343
0
            break;
344
0
        }
345
        /* We used to use 'key.persistent' to determine whether we needed to copy the
346
         * key (by setting persistent_keys in the param list to false), but that isn't
347
         * correct! We are going to use the heap buffer 'string_key', not the original
348
         * key, and since that's on the heap it is NOT persistent....
349
         */
350
0
        gs_param_list_set_persistent_keys(plto, false);
351
0
        switch (value.type) {
352
0
        case gs_param_type_dict:
353
0
            coll_type = gs_param_collection_dict_any;
354
0
            goto cc;
355
0
        case gs_param_type_dict_int_keys:
356
0
            coll_type = gs_param_collection_dict_int_keys;
357
0
            goto cc;
358
0
        case gs_param_type_array:
359
0
            coll_type = gs_param_collection_array;
360
0
        cc:
361
0
            copy.value.d.size = value.value.d.size;
362
0
            if (copy.value.d.size == 0)
363
0
                break;
364
0
            if ((code = param_begin_write_collection(plto, string_key,
365
0
                                                     &copy.value.d,
366
0
                                                     coll_type)) < 0 ||
367
0
                (code = copy_and_modify_sub(copy.value.d.list,
368
0
                                            value.value.d.list,
369
0
                                            NULL)) < 0 ||
370
0
                (code = param_end_write_collection(plto, string_key,
371
0
                                                   &copy.value.d)) < 0)
372
0
                break;
373
0
            code = param_end_read_collection(plfrom, string_key,
374
0
                                             &value.value.d);
375
0
            break;
376
0
        case gs_param_type_bool:
377
0
            if (strcmp(string_key, "PageUsesTransparency") == 0 && present != NULL)
378
0
            {
379
0
                value.value.b = 1;
380
0
                *present = 1;
381
0
            }
382
0
            goto ca;
383
0
        case gs_param_type_string:
384
0
            value.value.s.persistent &= copy_persists; goto ca;
385
0
        case gs_param_type_name:
386
0
            value.value.n.persistent &= copy_persists; goto ca;
387
0
        case gs_param_type_int_array:
388
0
            value.value.ia.persistent &= copy_persists; goto ca;
389
0
        case gs_param_type_float_array:
390
0
            value.value.fa.persistent &= copy_persists; goto ca;
391
0
        case gs_param_type_string_array:
392
0
            value.value.sa.persistent &= copy_persists;
393
            /* fall through */
394
0
        ca:
395
0
        default:
396
0
            code = param_write_typed(plto, string_key, &value);
397
0
        }
398
0
        if (code < 0)
399
0
            break;
400
0
    }
401
0
    return code;
402
0
}
403
404
static int
405
param_list_copy_and_modify(gs_param_list *plto, gs_param_list *plfrom)
406
0
{
407
0
    int found_put;
408
0
    int code = copy_and_modify_sub(plto, plfrom, &found_put);
409
410
0
    if (code >= 0 && !found_put) {
411
0
        gs_param_typed_value value;
412
0
        value.type = gs_param_type_bool;
413
0
        value.value.b = 1;
414
0
        code = param_write_typed(plto, "PageUsesTransparency", &value);
415
0
    }
416
417
0
    return code;
418
0
}
419
420
static int
421
promote_errors(gs_param_list * plist_orig, gs_param_list * plist)
422
0
{
423
0
    gs_param_enumerator_t key_enum;
424
0
    gs_param_key_t key;
425
0
    int code;
426
0
    int error;
427
428
0
    param_init_enumerator(&key_enum);
429
0
    while ((code = param_get_next_key(plist_orig, &key_enum, &key)) == 0) {
430
0
        char string_key[256]; /* big enough for any reasonable key */
431
432
0
        if (key.size > sizeof(string_key) - 1) {
433
0
            code = gs_note_error(gs_error_rangecheck);
434
0
            break;
435
0
        }
436
0
        memcpy(string_key, key.data, key.size);
437
0
        string_key[key.size] = 0;
438
0
        error = param_read_signalled_error(plist, string_key);
439
0
        param_signal_error(plist_orig, string_key, error);
440
0
    }
441
442
0
    return code;
443
0
}
444
445
static int
446
nup_put_params(gx_device *dev, gs_param_list * plist_orig)
447
0
{
448
0
    int code, ecode = 0;
449
0
    gs_param_float_array msa;
450
0
    const float *data;
451
0
    const float *res = dev->HWResolution;
452
0
    gs_param_string nuplist;
453
0
    Nup_device_subclass_data* pNup_data = dev->subclass_data;
454
0
    gx_device *next_dev;
455
0
    gs_c_param_list *plist_c;
456
0
    gs_param_list *plist;
457
458
#if 0000
459
gs_param_list_dump(plist_orig);
460
#endif
461
462
0
    plist_c = gs_c_param_list_alloc(dev->memory->non_gc_memory, "nup_put_params");
463
0
    plist = (gs_param_list *)plist_c;
464
0
    if (plist == NULL)
465
0
        return_error(gs_error_VMerror);
466
0
    gs_c_param_list_write(plist_c, dev->memory->non_gc_memory);
467
0
    gs_param_list_set_persistent_keys((gs_param_list *)plist_c, false);
468
469
    /* Bulk copy the whole list. Can't enumerate and copy without it
470
     * becoming an absolute nightmare due to the stupid way we handle
471
     * 'collection' objects on writing. */
472
0
    code = param_list_copy_and_modify((gs_param_list *)plist_c, plist_orig);
473
0
    if (code < 0)
474
0
        goto fail;
475
476
0
    gs_c_param_list_read(plist_c);
477
478
0
    code = param_read_string(plist, "NupControl", &nuplist);
479
0
    if (code < 0)
480
0
        ecode = code;
481
482
0
    if (code == 0) {
483
0
        if (dev->NupControl && (nuplist.size == 0 ||
484
0
            (strncmp(dev->NupControl->nupcontrol_str, (const char *)nuplist.data, nuplist.size) != 0))) {
485
            /* If we have accumulated a nest when the NupControl changes, flush the nest */
486
0
            if (pNup_data->PagesPerNest > 1 && pNup_data->PageCount > 0)
487
0
                code = nup_flush_nest_to_output(dev, pNup_data);
488
0
            if (code < 0)
489
0
                ecode = code;
490
            /* There was a NupControl, but this one is different -- no longer use the old one */
491
0
            rc_decrement(dev->NupControl, "default put_params NupControl");
492
0
            dev->NupControl = 0;
493
0
        }
494
0
        if (dev->NupControl == NULL && nuplist.size > 0) {
495
0
            dev->NupControl = (gdev_nupcontrol *)gs_alloc_bytes(dev->memory->non_gc_memory,
496
0
                                                              sizeof(gdev_nupcontrol), "structure to hold nupcontrol_str");
497
0
            if (dev->NupControl == NULL) {
498
0
                code = gs_note_error(gs_error_VMerror);
499
0
                goto fail;
500
0
            }
501
0
            dev->NupControl->nupcontrol_str = (void *)gs_alloc_bytes(dev->memory->non_gc_memory,
502
0
                                                                     nuplist.size + 1, "nupcontrol string");
503
0
            if (dev->NupControl->nupcontrol_str == NULL){
504
0
                gs_free(dev->memory->non_gc_memory, dev->NupControl, 1, sizeof(gdev_nupcontrol),
505
0
                        "free structure to hold nupcontrol string");
506
0
                dev->NupControl = 0;
507
0
                code = gs_note_error(gs_error_VMerror);
508
0
                goto fail;
509
0
            }
510
0
            memset(dev->NupControl->nupcontrol_str, 0x00, nuplist.size + 1);
511
0
            memcpy(dev->NupControl->nupcontrol_str, nuplist.data, nuplist.size);
512
0
            rc_init_free(dev->NupControl, dev->memory->non_gc_memory, 1, rc_free_NupControl);
513
0
        }
514
        /* Propagate the new NupControl struct to children so get_params has a valid param */
515
0
        next_dev = dev->child;
516
0
        while (next_dev != NULL) {
517
0
            rc_decrement(next_dev->NupControl, "nup_put_params");
518
0
            next_dev->NupControl = dev->NupControl;
519
0
            rc_increment(next_dev->NupControl);
520
0
            next_dev = next_dev->child;
521
0
        }
522
        /* Propagate the new NupControl struct to parents so get_params has a valid param */
523
0
        next_dev = dev->parent;
524
0
        while (next_dev != NULL) {
525
0
            rc_decrement(next_dev->NupControl, "nup_put_params");
526
0
            next_dev->NupControl = dev->NupControl;
527
0
            rc_increment(next_dev->NupControl);
528
0
            next_dev = next_dev->parent;
529
0
        }
530
0
        if (ecode < 0) {
531
0
            code = ecode;
532
0
            goto fail;
533
0
        }
534
0
    }
535
536
0
    code = ParseNupControl(dev, pNup_data);   /* update the nesting params */
537
0
    if (code < 0)
538
0
        goto fail;
539
540
    /* If nesting is now off, just pass params on to children devices */
541
0
    if (pNup_data->PagesPerNest == 1) {
542
0
        code = default_subclass_put_params(dev, plist);
543
0
        goto fail; /* Not actually failing! */
544
0
    }
545
546
    /* .MediaSize takes precedence over PageSize, so we read PageSize first. */
547
0
    code = param_MediaSize(plist, "PageSize", res, &msa);
548
0
    if (code < 0)
549
0
        ecode = code;
550
    /* Prevent data from being set to 0 if PageSize is specified */
551
    /* but .MediaSize is not. */
552
0
    data = msa.data;
553
0
    code = param_MediaSize(plist, ".MediaSize", res, &msa);
554
0
    if (code < 0)
555
0
        ecode = code;
556
0
    else if (msa.data == NULL)
557
0
        msa.data = data;
558
0
    if (ecode < 0) {
559
0
        code = ecode;
560
0
        goto fail;
561
0
    }
562
563
    /* If there was PageSize or .MediaSize, update the NestedPage size */
564
0
    if (msa.data != NULL) {
565
0
        Nup_device_subclass_data *pNup_data = dev->subclass_data;
566
        /* Use the PostScript tolerance for PageSize "match" of 5 points */
567
0
        if (abs(pNup_data->NestedPageW - msa.data[0]) > 5 ||
568
0
            abs(pNup_data->NestedPageH - msa.data[1]) > 5) {
569
            /* If needed, flush previous nest before changing */
570
0
            if (pNup_data->PageCount > 0 && pNup_data->PagesPerNest > 1) {
571
0
                code = nup_flush_nest_to_output(dev, pNup_data);
572
0
                if (code < 0)
573
0
                    goto fail;
574
0
            }
575
0
            pNup_data->NestedPageW = msa.data[0];
576
0
            pNup_data->NestedPageH = msa.data[1];
577
            /* And update the Nup parameters based on the updated PageSize */
578
0
            code = ParseNupControl(dev, pNup_data);
579
0
            if (code < 0)
580
0
                goto fail;
581
0
        }
582
0
    }
583
584
    /* now that we've intercepted PageSize and/or MediaSize, pass the rest along */
585
0
    code = default_subclass_put_params(dev, plist);
586
587
    /* Now promote errors from the copied list to the original list. */
588
0
    {
589
0
        int ecode = promote_errors(plist_orig, plist);
590
0
        if (code == 0)
591
0
            code = ecode;
592
0
    }
593
594
0
fail:
595
0
    gs_c_param_list_release(plist_c);
596
0
    gs_free_object(dev->memory->non_gc_memory, plist_c, "nup_put_params");
597
598
0
    return code;
599
0
}
600
601
static int
602
nup_output_page(gx_device *dev, int num_copies, int flush)
603
0
{
604
0
    int code = 0;
605
0
    Nup_device_subclass_data *pNup_data = dev->subclass_data;
606
607
0
    if (pNup_data->PagesPerNest == 0)
608
0
        code = ParseNupControl(dev, pNup_data); /* ensure reasonable values */
609
0
    if (code < 0)
610
0
        return code;
611
612
    /* If nesting is off, pass output_page to children */
613
0
    if (pNup_data->PagesPerNest == 1) {
614
0
        code = default_subclass_output_page(dev, num_copies, flush);
615
0
        dev->PageCount = dev->child->PageCount;
616
0
        dev->ShowpageCount = dev->child->ShowpageCount;
617
0
        return code;
618
0
    }
619
620
    /* FIXME: Handle num_copies > 1 */
621
622
    /* pNup_data holds the number of 'sub pages' we have produced,
623
     * so update that. dev->PageCount holds the number of 'actual'
624
     * pages we've output, so only increment that if we really
625
     * do an output. */
626
0
    pNup_data->PageCount++;
627
0
    dev->ShowpageCount = dev->child->ShowpageCount;
628
0
    if (pNup_data->PageCount >= pNup_data->PagesPerNest) {
629
0
        code = nup_flush_nest_to_output(dev, pNup_data);
630
        /* Increment this afterwards, in case the child accesses
631
         * the value to fill in a %d in the filename. */
632
0
        dev->PageCount++;
633
0
    }
634
635
0
    return code;
636
0
}
637
638
static int
639
nup_fillpage(gx_device *dev, gs_gstate * pgs, gx_device_color *pdevc)
640
0
{
641
0
    int code = 0;
642
0
    Nup_device_subclass_data *pNup_data = dev->subclass_data;
643
644
0
    if (pNup_data->PagesPerNest == 0)
645
0
        code = ParseNupControl(dev, pNup_data);
646
0
    if (code < 0)
647
0
        return code;
648
649
    /* Only fill the first page of a nest */
650
0
    if (pNup_data->PageCount == 0)
651
0
        code = default_subclass_fillpage(dev, pgs, pdevc);
652
653
0
    return code;
654
0
}
655
656
static int
657
nup_dev_spec_op(gx_device *dev, int dev_spec_op, void *data, int size)
658
0
{
659
0
    Nup_device_subclass_data *pNup_data = dev->subclass_data;
660
0
    int code = 0;
661
662
0
    if (pNup_data->PagesPerNest == 0)   /* not yet initialized */
663
0
        code = ParseNupControl(dev, pNup_data);
664
0
    if (code < 0)
665
0
        return code;
666
667
    /* If nesting is now off, just pass spec_op on to children devices */
668
0
    if (pNup_data->PagesPerNest == 1)
669
0
        return default_subclass_dev_spec_op(dev, dev_spec_op, data, size);
670
671
0
    switch (dev_spec_op) {
672
0
        case gxdso_set_HWSize:
673
            /* Call ParseNupControl since that will set the PageW and PageH from the  */
674
            /* dev->width, dev->height as the size for the page on which we are placing */
675
            /* the nested pages. If we get here we know PagesPerNest > 0, so don't set  */
676
            /* the dev->width and dev->height             */
677
0
            code = ParseNupControl(dev, pNup_data);
678
0
            if (code < 0)
679
0
                return code;
680
0
            return 1;
681
0
            break;
682
0
        case gxdso_get_dev_param:
683
0
            {
684
0
                dev_param_req_t *request = (dev_param_req_t *)data;
685
0
                bool code = false;
686
687
                /* We need to disable pdfmark writing, primarily for CropBox, but also  */
688
                /* they are probably not relevant for multiple input files to a single  */
689
                /* output "page" (nest of several pages).       */
690
                /* Write a 'false' (0) into the param list provided by the caller.  */
691
0
                if (strcmp(request->Param, "PdfmarkCapable") == 0) {
692
0
                    return(param_write_bool(request->list, "PdfmarkCapable", &code));
693
0
                }
694
695
                /* The parameter above is a legacy special op used only by the old PostScript-based
696
                 * interpreter. By claiming that the device is not capable of pdfmarks this disables
697
                 * ALL pdfmarks with the pdfwrite device which means that many features go missing
698
                 * including all annotations (eg Link, Stamp, Text, FreeText etc). which is not
699
                 * ideal. The new PDF interpreter written in C has finer grained control and uses
700
                 * these two parameters to disable CropBox (and other Boxes) being written with a
701
                 * pdfmark, and disables Outlines and Dests if the page order is not preserved.
702
                 * We may need more of these later.
703
                 */
704
0
                code = true;
705
0
                if (strcmp(request->Param, "ModifiesPageSize") == 0) {
706
0
                    return(param_write_bool(request->list, "ModifiesPageSize", &code));
707
0
                }
708
0
                if (strcmp(request->Param, "ModifiesPageOrder") == 0) {
709
0
                    return(param_write_bool(request->list, "ModifiesPageOrder", &code));
710
0
                }
711
0
            }
712
            /* Fall through */
713
0
        default:
714
0
            break;
715
0
    }
716
0
    return default_subclass_dev_spec_op(dev, dev_spec_op, data, size);
717
0
}
718
719
int gx_device_nup_device_install(gx_device *dev)
720
0
{
721
0
    gs_param_typed_value value;
722
0
    gs_c_param_list *plist_c;
723
0
    int code;
724
725
0
    code = gx_device_subclass(dev, (gx_device *)&gs_nup_device, sizeof(Nup_device_subclass_data));
726
0
    if (code < 0)
727
0
        return code;
728
729
    /* Ensure that PageUsesTransparency is set. */
730
0
    plist_c = gs_c_param_list_alloc(dev->memory->non_gc_memory, "nup_open_device");
731
0
    if (plist_c == NULL)
732
0
        return_error(gs_error_VMerror);
733
0
    gs_c_param_list_write(plist_c, dev->memory->non_gc_memory);
734
0
    gs_param_list_set_persistent_keys((gs_param_list *)plist_c, false);
735
736
0
    value.type = gs_param_type_bool;
737
0
    value.value.b = 1;
738
0
    code = param_write_typed((gs_param_list *)plist_c, "PageUsesTransparency", &value);
739
0
    if (code >= 0) {
740
0
        gs_c_param_list_read(plist_c);
741
742
0
        code = default_subclass_put_params(dev, (gs_param_list *)plist_c);
743
0
        if (code >= 0)
744
0
            code = default_subclass_open_device(dev->child);
745
0
    }
746
0
    gs_c_param_list_release(plist_c);
747
0
    gs_free_object(dev->memory->non_gc_memory, plist_c, "nup_open_device");
748
749
0
    return code;
750
0
}