Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gxclpage.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2024 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
17
/* Page object management */
18
#include "gdevprn.h"
19
#include "gdevdevn.h"
20
#include "gxcldev.h"
21
#include "gxclpage.h"
22
#include "gsicc_cache.h"
23
#include "gsparams.h"
24
#include "string_.h"
25
#include <ctype.h>  /* for isalpha, etc. */
26
27
extern dev_proc_open_device(clist_open);
28
29
/* Save the current clist state into a saved page structure,
30
 * and optionally stashes the files into the given save_files
31
 * pointers.
32
 * Does NOT alter the clist files. */
33
/* RJW: Does too. The opendevice call at the end calls clist_open */
34
static int
35
do_page_save(gx_device_printer * pdev, gx_saved_page * page, clist_file_ptr *save_files)
36
0
{
37
0
    gx_device_clist *cdev = (gx_device_clist *) pdev;
38
0
    gx_device_clist_writer * const pcldev = (gx_device_clist_writer *)pdev;
39
0
    int code;
40
0
    gs_c_param_list paramlist;
41
0
    gs_devn_params *pdevn_params;
42
43
    /* Save the device information. */
44
0
    strncpy(page->dname, pdev->dname, sizeof(page->dname)-1);
45
0
    page->color_info = pdev->color_info;
46
0
    page->tag = pdev->graphics_type_tag;
47
0
    page->io_procs = cdev->common.page_info.io_procs;
48
    /* Save the page information. */
49
0
    memcpy(page->cfname, pcldev->page_info.cfname, sizeof(page->cfname));
50
0
    memcpy(page->bfname, pcldev->page_info.bfname, sizeof(page->bfname));
51
0
    page->bfile_end_pos = pcldev->page_info.bfile_end_pos;
52
0
    if (save_files != NULL) {
53
0
      save_files[0] =  pcldev->page_info.cfile;
54
0
      save_files[1] =  pcldev->page_info.bfile;
55
0
      pcldev->page_info.cfile = NULL;
56
0
      pcldev->page_info.bfile = NULL;
57
0
    }
58
0
    pcldev->page_info.cfname[0] = 0;
59
0
    pcldev->page_info.bfname[0] = 0;
60
0
    page->tile_cache_size = pcldev->page_info.tile_cache_size;
61
0
    page->line_ptrs_offset = pcldev->page_info.line_ptrs_offset;
62
0
    page->num_planar_planes = pcldev->num_planar_planes;
63
0
    page->band_params = pcldev->page_info.band_params;
64
    /* Now serialize and save the rest of the information from the device params */
65
    /* we count on this to correctly set the color_info, devn_params and icc_struct */
66
0
    page->mem = pdev->memory->non_gc_memory;
67
0
    gs_c_param_list_write(&paramlist, pdev->memory);
68
0
    if ((code = gs_getdeviceparams((gx_device *)pdev, (gs_param_list *)&paramlist)) < 0) {
69
0
        goto params_out;
70
0
    }
71
    /* fetch bytes needed for param list */
72
0
    gs_c_param_list_read(&paramlist);
73
0
    if ((code = gs_param_list_serialize((gs_param_list *)&paramlist, NULL, 0)) < 0) {
74
0
        goto params_out;
75
0
    }
76
0
    page->paramlist_len = code;
77
0
    if ((page->paramlist = gs_alloc_bytes(page->mem,
78
0
                                           page->paramlist_len,
79
0
                                           "saved_page paramlist")) == NULL) {
80
0
        code = gs_error_VMerror;
81
0
        goto params_out;
82
0
    }
83
0
    code = gs_param_list_serialize((gs_param_list *)&paramlist, page->paramlist,
84
0
                                   page->paramlist_len);
85
0
params_out:
86
0
    gs_c_param_list_release(&paramlist);
87
0
    if (code < 0)
88
0
        return code;     /* all device param errors collect here */
89
90
    /* Save other information. */
91
    /* If this device has spot colors that were added dynamically, we need to pass the names */
92
    /* through as well. These are from the devn_params->separations->names array.            */
93
0
    if ((pdevn_params = dev_proc(pdev, ret_devn_params)((gx_device *)pdev)) != NULL) {
94
0
        int i;
95
96
0
        page->num_separations = pdevn_params->separations.num_separations;
97
0
        for (i=0; i < page->num_separations; i++) {
98
0
            page->separation_name_sizes[i] = pdevn_params->separations.names[i].size;
99
0
            page->separation_names[i] = gs_alloc_bytes(page->mem, page->separation_name_sizes[i],
100
0
                                                       "saved_page separation_names");
101
0
            if (page->separation_names[i] == NULL) {
102
0
                gs_free_object(page->mem, page->paramlist, "saved_page paramlist");
103
0
                while (--i > 0)
104
0
                    gs_free_object(page->mem, page->separation_names[i],
105
0
                                   "saved_page separation_names");
106
0
                return gs_error_VMerror;
107
0
            }
108
0
            memcpy(page->separation_names[i], pdevn_params->separations.names[i].data,
109
0
                   page->separation_name_sizes[i]);
110
0
        }
111
0
    }
112
    /* Now re-open the clist device so that we get new files for the next page */
113
0
    return clist_open((gx_device *) pdev);
114
0
}
115
116
/* Save a page. The elements are allocated by this function in non_gc_memory */
117
int
118
gdev_prn_save_page(gx_device_printer * pdev, gx_saved_page * page)
119
0
{
120
0
    gx_device_clist *cdev = (gx_device_clist *) pdev;
121
0
    gx_device_clist_writer * const pcldev = (gx_device_clist_writer *)pdev;
122
0
    int code;
123
124
    /* Make sure we are banding. */
125
0
    if (!PRINTER_IS_CLIST(pdev))
126
0
        return_error(gs_error_rangecheck);
127
128
0
    if ((code = clist_end_page(pcldev)) < 0 ||
129
0
        (code = cdev->common.page_info.io_procs->fclose(pcldev->page_info.cfile, pcldev->page_info.cfname, false)) < 0 ||
130
0
        (code = cdev->common.page_info.io_procs->fclose(pcldev->page_info.bfile, pcldev->page_info.bfname, false)) < 0
131
0
        )
132
0
        return code;
133
0
    return do_page_save(pdev, page, NULL);
134
0
}
135
136
/* Render an array of saved pages. */
137
int
138
gdev_prn_render_pages(gx_device_printer * pdev,
139
                      const gx_placed_page * ppages, int count)
140
0
{
141
0
    gx_device_clist_reader * const pcldev =
142
0
        (gx_device_clist_reader *)pdev;
143
144
    /* Check to make sure the pages are compatible with the device. */
145
0
    {
146
0
        int i;
147
148
0
        for (i = 0; i < count; ++i) {
149
0
            const gx_saved_page *page = ppages[i].page;
150
151
            /* We would like to fully check the color representation, */
152
            /* but we don't have enough information to do that. */
153
0
            if (strcmp(page->dname, pdev->dname) != 0 ||
154
0
                !gx_color_info_equal(&page->color_info, &pdev->color_info)
155
0
                )
156
0
                return_error(gs_error_rangecheck);
157
            /* Currently we don't allow translation in Y. */
158
0
            if (ppages[i].offset.y != 0)
159
0
                return_error(gs_error_rangecheck);
160
            /* Make sure the band parameters are compatible. */
161
0
            if (page->band_params.BandBufferSpace !=
162
0
                pdev->buffer_space ||
163
0
                page->band_params.BandWidth !=
164
0
                pdev->width
165
0
                )
166
0
                return_error(gs_error_rangecheck);
167
            /* Currently we require all band heights to be the same. */
168
0
            if (i > 0 && page->band_params.BandHeight !=
169
0
                         ppages[0].page->band_params.BandHeight)
170
0
                return_error(gs_error_rangecheck);
171
0
        }
172
0
    }
173
    /* Set up the page list in the device. */
174
    /****** SHOULD FACTOR THIS OUT OF clist_render_init ******/
175
0
    pcldev->ymin = pcldev->ymax = 0;
176
0
    pcldev->pages = ppages;
177
0
    pcldev->num_pages = count;
178
0
    pcldev->offset_map = NULL;
179
0
    pcldev->icc_table = NULL;   /* FIXME: output_page doesn't load these */
180
0
    pcldev->icc_cache_cl = NULL;  /* FIXME: output_page doesn't load these */
181
    /* Render the pages. */
182
0
    {
183
0
        int code = (*dev_proc(pdev, output_page))
184
0
            ((gx_device *) pdev, (pdev->IgnoreNumCopies || pdev->NumCopies_set <= 0) ? 1 : pdev->NumCopies, true);
185
186
        /* Delete the temporary files and free the paramlist. */
187
0
        int i;
188
189
0
        for (i = 0; i < count; ++i) {
190
0
            gx_saved_page *page = ppages[i].page;
191
192
0
            pcldev->page_info.io_procs->unlink(page->cfname);
193
0
            pcldev->page_info.io_procs->unlink(page->bfname);
194
0
            gs_free_object(page->mem, page->paramlist, "gdev_prn_render_pages");
195
0
            page->paramlist = NULL;
196
0
        }
197
0
        return code;
198
0
    }
199
0
}
200
201
/*
202
 * Allocate and initialize the list structure
203
 */
204
gx_saved_pages_list *
205
gx_saved_pages_list_new(gx_device_printer *pdev)
206
0
{
207
0
    gx_saved_pages_list *newlist;
208
0
    gs_memory_t *non_gc_mem = pdev->memory->non_gc_memory;
209
210
0
    if ((newlist =
211
0
         (gx_saved_pages_list *)gs_alloc_bytes(non_gc_mem,
212
0
                                               sizeof(gx_saved_pages_list),
213
0
                                               "gx_saved_pages_list_new")) == NULL)
214
0
        return NULL;
215
216
0
    memset(newlist, 0, sizeof(gx_saved_pages_list));
217
0
    newlist->mem = non_gc_mem;
218
0
    newlist->PageCount = pdev->PageCount; /* PageCount when list created */
219
0
    newlist->collated_copies = 1;
220
0
    return newlist;
221
0
}
222
223
/*
224
 * Add a new saved page to the end of an in memory list. Refer to the
225
 * documentation for gx_saved_pages_list. This allocates the saved_
226
 * page as well as the saved_pages_list_element and relies on the
227
 * gdev_prn_save_page to use non_gc_memory since this list is never
228
 * in GC memory.
229
 */
230
int
231
gx_saved_pages_list_add(gx_device_printer * pdev)
232
0
{
233
0
    gx_saved_pages_list *list = pdev->saved_pages_list;
234
0
    gx_saved_pages_list_element *new_list_element;
235
0
    gx_saved_page *newpage;
236
0
    int code;
237
238
0
    if ((newpage =
239
0
         (gx_saved_page *)gs_alloc_bytes(list->mem,
240
0
                                         sizeof(gx_saved_page),
241
0
                                         "gx_saved_pages_list_add")) == NULL)
242
0
        return_error (gs_error_VMerror);
243
244
0
    if ((new_list_element =
245
0
         (gx_saved_pages_list_element *)gs_alloc_bytes(list->mem,
246
0
                                                      sizeof(gx_saved_pages_list_element),
247
0
                                                      "gx_saved_pages_list_add")) == NULL) {
248
0
        gs_free_object(list->mem, newpage, "gx_saved_pages_list_add");
249
0
        return_error (gs_error_VMerror);
250
0
    }
251
252
0
    if ((code = gdev_prn_save_page(pdev, newpage)) < 0) {
253
0
        gs_free_object(list->mem, new_list_element, "gx_saved_pages_list_add");
254
0
        gs_free_object(list->mem, newpage, "gx_saved_pages_list_add");
255
0
        return code;
256
0
    }
257
0
    new_list_element->sequence_number = ++list->count;
258
0
    new_list_element->page = newpage;
259
0
    new_list_element->next = NULL;
260
0
    if (list->tail == NULL) {
261
        /* list was empty, start it */
262
0
        new_list_element->prev = NULL;
263
0
        list->head = list->tail = new_list_element;
264
0
    } else {
265
        /* place as new tail */
266
0
        new_list_element->prev = list->tail;
267
0
        list->tail->next = new_list_element;
268
0
        list->tail = new_list_element;
269
0
    }
270
0
    return 0;     /* success */
271
0
}
272
273
/* Free the contents of all saved pages, unlink the files and free the
274
 * saved_page structures. Does not free the gx_saved_pages_list struct.
275
 */
276
void
277
gx_saved_pages_list_free(gx_saved_pages_list *list)
278
0
{
279
0
    gx_saved_pages_list_element *curr_elem = list->head;
280
0
    while (curr_elem != NULL) {
281
0
        gx_saved_page *curr_page;
282
0
        gx_saved_pages_list_element *next_elem;
283
284
0
        curr_page  = curr_elem->page;
285
0
        curr_page->io_procs->unlink(curr_page->cfname);
286
0
        curr_page->io_procs->unlink(curr_page->bfname);
287
0
        gs_free_object(curr_page->mem, curr_page->paramlist, "gx_saved_pages_list_free");
288
0
        gs_free_object(list->mem, curr_page, "gx_saved_pages_list_free");
289
290
0
        next_elem = curr_elem->next;
291
0
        gs_free_object(list->mem, curr_elem, "gx_saved_pages_list_free");
292
0
        curr_elem = next_elem;
293
0
    };
294
295
    /* finally free the list itself */
296
0
    gs_free_object(list->mem, list, "gx_saved_pages_list_free");
297
0
}
298
299
300
/* This enum has to be in the same order as saved_pages_keys */
301
typedef enum {
302
    PARAM_UNKNOWN = 0,
303
    PARAM_BEGIN,
304
    PARAM_END,
305
    PARAM_FLUSH,
306
    PARAM_PRINT,
307
    PARAM_COPIES,
308
    PARAM_NORMAL,
309
    PARAM_REVERSE,
310
    PARAM_EVEN,
311
    PARAM_EVEN0PAD,
312
    PARAM_ODD,
313
    /* any new keywords precede these */
314
    PARAM_NUMBER,
315
    PARAM_DASH,
316
    PARAM_STAR
317
} saved_pages_key_enum;
318
319
static saved_pages_key_enum
320
param_find_key(byte *token, int token_size)
321
0
{
322
0
    int i;
323
0
    static const char *saved_pages_keys[] = {
324
0
        "begin", "end", "flush", "print", "copies", "normal", "reverse", "even", "even0pad", "odd"
325
0
    };
326
0
    saved_pages_key_enum found = PARAM_UNKNOWN;
327
328
0
    if (*token >= '0' && *token <= '9')
329
0
        return PARAM_NUMBER;
330
0
    if (*token == '-')
331
0
        return PARAM_DASH;
332
0
    if (*token == (byte)'*')
333
0
        return PARAM_STAR;
334
335
0
    for (i=0; i < (sizeof(saved_pages_keys)/sizeof(saved_pages_keys[0])); i++) {
336
0
        if (strncasecmp((char *)token, saved_pages_keys[i], token_size) == 0) {
337
0
            found = (saved_pages_key_enum) (i + 1);
338
0
            break;
339
0
         }
340
0
    }
341
0
    return found;
342
0
}
343
344
/* Find next token, skipping any leading whitespace or non-alphanumeric */
345
/* Returns pointer to next token and updates 'token_size'. Caller can   */
346
/* use (token - param) + token_size  to update to next token in the     */
347
/* param string (param) and remaining char count (param_left)           */
348
/* Returns NULL and token_size =0 if no more alphanumeric tokens        */
349
static byte *
350
param_parse_token(byte *param, int param_left, int *token_size)
351
0
{
352
0
    int token_len = 0;
353
0
    byte *token = param;
354
0
    bool single_char_token = true;
355
356
    /* skip leading junk */
357
0
    while (param_left > 0) {
358
0
        if (isalnum(*token)) {
359
0
            single_char_token = false; /* we'll scan past this keyword */
360
0
            break;
361
0
        }
362
0
        if (*token == (byte)'-') /* valid in page range */
363
0
            break;
364
0
        if (*token == (byte)'*') /* valid in page range */
365
0
            break;
366
        /* skip any other junk */
367
0
        token++;
368
0
        param_left--;
369
0
    }
370
0
    if (param_left == 0) {
371
0
        *token_size = 0;  /* no token found */
372
0
        return NULL;    /* No more items */
373
0
    }
374
0
    if (single_char_token) {
375
0
        param_left--;   /* we've consumed one character */
376
0
        *token_size = 1;
377
0
        return token;
378
0
    }
379
380
    /* token points to start, skip valid alphanumeric characters after */
381
    /* the first. Single character tokens terminate this scan.         */
382
0
    while (param_left > 0) {
383
0
        if (!isalnum(token[token_len]))
384
0
            break;
385
0
        token_len++;
386
0
        param_left--;
387
0
    }
388
0
    *token_size = token_len;
389
0
    return token;
390
0
}
391
392
static int
393
do_page_load(gx_device_printer *pdev, gx_saved_page *page, clist_file_ptr *save_files)
394
0
{
395
0
    int code;
396
0
    gx_device_clist_reader *crdev = (gx_device_clist_reader *)pdev;
397
0
    gs_c_param_list paramlist;
398
0
    gs_devn_params *pdevn_params;
399
400
    /* if this is a DeviceN device (that supports spot colors), we need to load the */
401
    /* devn_params saved in the page (num_separations, separations[])               */
402
0
    if ((pdevn_params = dev_proc(pdev, ret_devn_params)((gx_device *)pdev)) != NULL) {
403
0
        int i;
404
405
0
        pdevn_params->separations.num_separations = page->num_separations;
406
0
        for (i=0; i < page->num_separations; i++) {
407
0
            pdevn_params->separations.names[i].size = page->separation_name_sizes[i];
408
0
            pdevn_params->separations.names[i].data = gs_alloc_bytes(pdev->memory->stable_memory,
409
0
                                                                     page->separation_name_sizes[i],
410
0
                                                                     "saved_page separation_names");
411
0
            if (pdevn_params->separations.names[i].data == NULL) {
412
0
                while (--i > 0)
413
0
                    gs_free_object(pdev->memory->stable_memory,
414
0
                                   pdevn_params->separations.names[i].data,
415
0
                                   "saved_page separation_names");
416
0
                code = gs_error_VMerror;
417
0
                goto out;
418
0
            }
419
0
            memcpy(pdevn_params->separations.names[i].data, page->separation_names[i],
420
0
                   page->separation_name_sizes[i]);
421
0
        }
422
0
    }
423
    /* fetch and put the params we saved with the page */
424
0
    gs_c_param_list_write(&paramlist, pdev->memory);
425
0
    if ((code = gs_param_list_unserialize((gs_param_list *)&paramlist, page->paramlist)) < 0)
426
0
        goto out;
427
0
    gs_c_param_list_read(&paramlist);
428
0
    code = gs_putdeviceparams((gx_device *)pdev, (gs_param_list *)&paramlist);
429
0
    gs_c_param_list_release(&paramlist);
430
0
    if (code < 0) {
431
0
        goto out;
432
0
    }
433
0
    if (code > 0)
434
0
        if ((code = gs_opendevice((gx_device *)pdev)) < 0)
435
0
            goto out;
436
437
    /* If the device is now a writer, that means putparams realloced the device */
438
    /* so we need to get back to reader mode and remove the extra clist files  */
439
0
    if (CLIST_IS_WRITER((gx_device_clist *)pdev)) {
440
0
        if ((code = clist_close_writer_and_init_reader((gx_device_clist *)crdev)) < 0)
441
0
            goto out;
442
        /* close and unlink the temp files just created */
443
0
        if (crdev->page_info.cfile != NULL)
444
0
            crdev->page_info.io_procs->fclose(crdev->page_info.cfile, crdev->page_info.cfname, true);
445
0
        if (crdev->page_info.bfile != NULL)
446
0
            crdev->page_info.io_procs->fclose(crdev->page_info.bfile, crdev->page_info.bfname, true);
447
0
        crdev->page_info.cfile = crdev->page_info.bfile = NULL;
448
0
    }
449
450
    /* set up the page_info, after putdeviceparams that may have changed things */
451
0
    crdev->page_info.io_procs = page->io_procs;
452
0
    crdev->page_info.tile_cache_size = page->tile_cache_size;
453
0
    crdev->page_info.bfile_end_pos = page->bfile_end_pos;
454
0
    crdev->page_info.band_params = page->band_params;
455
0
    crdev->graphics_type_tag = page->tag;
456
0
    crdev->page_info.line_ptrs_offset = page->line_ptrs_offset;
457
0
    crdev->num_planar_planes = page->num_planar_planes;
458
459
0
    crdev->yplane.index = -1;
460
0
    crdev->pages = NULL;
461
0
    crdev->num_pages = 1;   /* single page at a time */
462
0
    crdev->offset_map = NULL;
463
0
    crdev->render_threads = NULL;
464
0
    crdev->ymin = crdev->ymax = 0;      /* invalidate buffer contents to force rasterizing */
465
466
    /* We probably don't need to copy in the filenames, but do it in case something expects it */
467
0
    memcpy(crdev->page_info.cfname, page->cfname, sizeof(crdev->page_info.cfname));
468
0
    memcpy(crdev->page_info.bfname, page->bfname, sizeof(crdev->page_info.bfname));
469
0
    if (save_files != NULL)
470
0
    {
471
0
        crdev->page_info.cfile = save_files[0];
472
0
        crdev->page_info.bfile = save_files[1];
473
0
    }
474
0
out:
475
0
    return code;
476
0
}
477
478
static int
479
gx_saved_page_load(gx_device_printer *pdev, gx_saved_page *page)
480
0
{
481
0
    int code;
482
0
    gx_device_clist_reader *crdev = (gx_device_clist_reader *)pdev;
483
484
0
    code = do_page_load(pdev, page, NULL);
485
0
    if (code < 0)
486
0
        return code;
487
488
    /* Now open this page's files */
489
0
    code = crdev->page_info.io_procs->fopen(crdev->page_info.cfname,
490
0
               gp_fmode_rb, &(crdev->page_info.cfile), crdev->bandlist_memory,
491
0
               crdev->bandlist_memory, true);
492
0
    if (code >= 0) {
493
0
        code = crdev->page_info.io_procs->fopen(crdev->page_info.bfname,
494
0
                   gp_fmode_rb, &(crdev->page_info.bfile), crdev->bandlist_memory,
495
0
                   crdev->bandlist_memory, false);
496
0
    }
497
498
0
    return code;
499
0
}
500
501
static int
502
gx_output_saved_page(gx_device_printer *pdev, gx_saved_page *page)
503
0
{
504
0
    int code, ecode;
505
    /* Note that banding_type is NOT a device parameter handled in the paramlist */
506
0
    gdev_banding_type save_banding_type = pdev->space_params.banding_type;
507
0
    gx_device_clist_reader *crdev = (gx_device_clist_reader *)pdev;
508
509
0
    pdev->space_params.banding_type = BandingAlways;
510
511
0
    if ((code = gx_saved_page_load(pdev, page)) < 0)
512
0
        goto out;
513
514
    /* don't want the band files touched after printing */
515
0
    crdev->do_not_open_or_close_bandfiles = true;
516
517
    /* load the color_usage_array */
518
0
    if ((code = clist_read_color_usage_array(crdev)) < 0)
519
0
        goto out;
520
0
    if ((code = clist_read_icctable(crdev)) < 0)
521
0
        goto out;
522
0
    code = (crdev->icc_cache_cl = gsicc_cache_new(crdev->memory)) == NULL ?
523
0
           gs_error_VMerror : code;
524
0
    if (code < 0)
525
0
        goto out;
526
527
    /* After setting params, make sure bg_printing is off */
528
0
    pdev->bg_print_requested = false;
529
530
    /* Note: we never flush pages allowing for re-printing from the list */
531
    /* data (files) will be deleted when the list is flushed or freed.   */
532
0
    code = (*dev_proc(pdev, output_page)) ((gx_device *) pdev,
533
0
               (pdev->IgnoreNumCopies || pdev->NumCopies_set <= 0) ? 1 : pdev->NumCopies, false);
534
535
0
    clist_free_icc_table(crdev->icc_table, crdev->memory);
536
0
    crdev->icc_table = NULL;
537
0
    rc_decrement(crdev->icc_cache_cl,"clist_finish_page");
538
0
    crdev->icc_cache_cl = NULL;
539
540
    /* Close the clist files */
541
0
    ecode = crdev->page_info.io_procs->fclose(crdev->page_info.cfile, crdev->page_info.cfname, false);
542
0
    if (ecode >= 0) {
543
0
        crdev->page_info.cfile = NULL;
544
0
        ecode = crdev->page_info.io_procs->fclose(crdev->page_info.bfile, crdev->page_info.bfname, false);
545
0
    }
546
0
    if (ecode < 0) {
547
0
        code = ecode;
548
0
        goto out;
549
0
    }
550
0
    crdev->page_info.bfile = NULL;
551
552
0
out:
553
0
    pdev->space_params.banding_type = save_banding_type;
554
0
    return code;
555
0
}
556
557
/*
558
 * Print selected pages from the list to on the selected device. The
559
 * saved_pages_list is NOT modified, allowing for reprint / recovery
560
 * print. Each saved_page is printed on a separate page (unlike the
561
 * gdev_prn_render_pages above which prints several saved_pages on
562
 * a single "master" page, performing imposition).
563
 *
564
 * This is primarily intended to allow printing in non-standard order
565
 * (reverse, odd, even) or producing collated copies for a job.
566
 *
567
 * On success return the number of bytes consumed or error code < 0.
568
 * The printed_count will contain the number of pages printed.
569
 *
570
 * -------------------------------------------------------------------
571
 *
572
 * The param string may begin with whitespace. The string is parsed
573
 * and the selected pages are printed. There may be any number of ranges
574
 * and or keywords separated by whitespace and/or comma ','.
575
 *
576
 * NB: The pdev printer device's PageCount is updated to reflect the
577
 *     total number of pages produced (per the spec for this parameter)
578
 *     since we may be producing multiple collated copies.
579
 *     Also the list PageCount is updated after printing since
580
 *     there may be a subsequent 'print' action.
581
 * keywords:
582
 *  copies #  Set the number of copies for subsequent printing actions
583
 *                      "copies 1" resets to a single copy
584
 *  normal    All pages are printed in normal order
585
 *  reverse   All pages are printed in reverse order
586
 *         The following two options may be useful for duplexing.
587
 *  odd   All odd pages are printed, e.g. 1, 3, 5, ...
588
 *  even    All even pages are printed, e.g. 2, 4, 6, ...
589
 *                      NB: an extra blank page will be printed if the list
590
 *                      has an odd number of pages.
591
 *      even0pad        All even pages, but no extra blank page if there are
592
 *                      an odd number of pages on the list.
593
 * range syntax:
594
 *  range range multiple ranges are separated by commas ','
595
 *      and/or whitespace.
596
 *  #   a specific page number is a valid range
597
 *
598
 *  inclusive ranges below can have whitespace before
599
 *  or after the dash '-'.
600
 *  #-#   a range consisting of all pages from the first
601
 *      page # up to (and including) the second page #.
602
 *  #-*   all pages from # up through the last page
603
 *      "1-*" is equivalent to "normal"
604
 *  *-#   all pages from the last up through # page.
605
 *      "reverse" is equivalent to "*-1"
606
 */
607
int
608
gx_saved_pages_list_print(gx_device_printer *pdev, gx_saved_pages_list *list,
609
                          byte *param, int param_size, int *printed_count)
610
0
{
611
0
    byte *param_scan = NULL;
612
0
    int param_left;
613
0
    int start_page = 0;       /* 0 means start_page unknown */
614
0
    int end_page = 0;       /* < 0 means waiting for the end of a # - # range */
615
                                                /* i.e, a '-' was scanned. 0 means unknown */
616
0
    int tmp_num;        /* during token scanning loop */
617
0
    int code = 0, endcode = 0;
618
0
    byte *token;
619
0
    int copy, token_size;
620
0
    gx_device_clist_reader *crdev = (gx_device_clist_reader *)pdev;
621
    /* the following are used so we can go back to the original state */
622
0
    bool save_bg_print = false;                 /* arbitrary, silence warning */
623
0
    bool save_bandfile_open_close = false;      /* arbitrary, silence warning */
624
0
    gx_saved_page saved_page;
625
0
    clist_file_ptr saved_files[2];
626
627
    /* save the current (empty) page while we print  */
628
0
    if ((code = do_page_save(pdev, &saved_page, saved_files)) < 0) {
629
0
        emprintf(pdev->memory, "gx_saved_pages_list_print: Error getting device params\n");
630
0
        goto out;
631
0
    }
632
633
    /* save_page leaves the clist in writer mode, so prepare for reading clist */
634
    /* files. When we are done with printing, we'll go back to write mode.     */
635
0
    if ((code = clist_close_writer_and_init_reader((gx_device_clist *)pdev)) < 0)
636
0
        goto out;
637
638
    /* While printing, disable the saved_pages mode and bg_print */
639
0
    pdev->saved_pages_list = NULL;
640
0
    save_bg_print = pdev->bg_print_requested;
641
642
    /* Inhibits modifying the clist at end of output_page */
643
0
    save_bandfile_open_close = crdev->do_not_open_or_close_bandfiles;
644
0
    crdev->do_not_open_or_close_bandfiles = true;
645
646
0
    pdev->PageCount = list->PageCount;    /* adjust to value last printed */
647
648
    /* loop producing the number of copies */
649
    /* Note: list->collated_copies may change if 'copies' param follows the 'print' */
650
0
    for (copy = 1; copy <= list->collated_copies; copy++) {
651
0
        int page_skip = 0;
652
0
        bool do_blank_page_pad;
653
0
        int page;
654
655
        /* Set to start of 'print' params to do collated copies */
656
0
        param_scan = param;
657
0
        param_left = param_size;
658
0
        while ((token = param_parse_token(param_scan, param_left, &token_size)) != NULL) {
659
0
            saved_pages_key_enum key;
660
661
0
            page = 0;
662
0
            do_blank_page_pad = false;     /* only set for EVEN */
663
0
            key = param_find_key(token, token_size);
664
0
            switch (key) {
665
0
              case PARAM_DASH:
666
0
                if (start_page == 0) {
667
0
                    emprintf(pdev->memory, "gx_saved_pages_list_print: '-' unexpected\n");
668
0
                    code = gs_error_typecheck;
669
0
                    goto out;
670
0
                }
671
0
                end_page = -1;    /* the next number will end a range */
672
0
                break;
673
674
0
              case PARAM_STAR:
675
0
                page = list->count;   /* last page */
676
0
              case PARAM_NUMBER:
677
0
                if (page == 0) {
678
0
                     if (sscanf((const char *)token, "%d", &page) != 1) {
679
0
                         emprintf1(pdev->memory, "gx_saved_pages_list_print: Number format error '%s'\n", token);
680
0
                         code = gs_error_typecheck;
681
0
                         goto out;
682
0
                     }
683
0
                }
684
0
                if (start_page == 0) {
685
0
                    start_page = page;    /* first number seen */
686
0
                } else {
687
                    /* a second number was seen after the start_page was set */
688
0
                    if (end_page < 0) {
689
0
                        end_page = page;    /* end of a '# - #' range */
690
0
                        page_skip = (end_page >= start_page) ? 1 : -1;
691
0
                    } else {
692
                        /* 'page' was NOT part of a range after printing 'page' will start a new range */
693
0
                        end_page = start_page;  /* single page */
694
0
                        page_skip = 1;
695
0
                    }
696
0
                }
697
0
                break;
698
699
0
              case PARAM_COPIES:      /* copies requires a number next */
700
                /* Move to past 'copies' token */
701
0
                param_left -= token - param_scan + token_size;
702
0
                param_scan = token + token_size;
703
704
0
                if ((token = param_parse_token(param_scan, param_left, &token_size)) == NULL ||
705
0
                     param_find_key(token, token_size) != PARAM_NUMBER) {
706
0
                    emprintf(pdev->memory, "gx_saved_pages_list_print: copies not followed by number.\n");
707
0
                    code = gs_error_typecheck;
708
0
                    goto out;
709
0
                }
710
0
                if (sscanf((const char *)token, "%d", &tmp_num) != 1) {
711
0
                    emprintf1(pdev->memory, "gx_saved_pages_list_print: Number format error '%s'\n", token);
712
0
                    code = gs_error_typecheck;
713
0
                    goto out;
714
0
                }
715
0
                list->collated_copies = tmp_num;  /* save it for our loop */
716
0
                break;
717
718
0
              case PARAM_NORMAL:      /* sets both start and end */
719
0
                start_page = 1;
720
0
                end_page = list->count;
721
0
                page_skip = 1;
722
0
                break ;
723
724
0
              case PARAM_REVERSE:     /* sets both start and end */
725
0
                start_page = list->count;
726
0
                end_page = 1;
727
0
                page_skip = -1;
728
0
                break;
729
730
0
              case PARAM_EVEN:      /* sets both start and end */
731
0
                do_blank_page_pad = (list->count & 1) != 0; /* pad if odd */
732
0
              case PARAM_EVEN0PAD:    /* sets both start and end */
733
0
                start_page = 2;
734
0
                end_page = list->count;
735
0
                page_skip = 2;
736
0
                break ;
737
738
0
              case PARAM_ODD:     /* sets both start and end */
739
0
                start_page = 1;
740
0
                end_page = list->count;
741
0
                page_skip = 2;
742
0
                break ;
743
744
0
              case PARAM_UNKNOWN:
745
0
              case PARAM_BEGIN:
746
0
              case PARAM_END:
747
0
              case PARAM_FLUSH:
748
0
              case PARAM_PRINT:
749
0
                token_size = 0;     /* non-print range token seen */
750
0
            }
751
0
            if (end_page > 0) {
752
                /* Here we have a range to print since start and end are known */
753
0
                int curr_page = start_page;
754
0
                gx_saved_pages_list_element *curr_elem = NULL;
755
756
                /* get the start_page saved_page */
757
0
                if (start_page <= list->count) {
758
0
                    for (curr_elem = list->head; curr_elem->sequence_number != start_page;
759
0
                                curr_elem = curr_elem->next) {
760
0
                        if (curr_elem->next == NULL) {
761
0
                             emprintf1(pdev->memory, "gx_saved_pages_list_print: page %d not found.\n", start_page);
762
0
                             code = gs_error_rangecheck;;
763
0
                             goto out;
764
0
                        }
765
0
                    }
766
0
                }
767
768
0
                for ( ; curr_elem != NULL; ) {
769
770
                    /* print the saved page from the current curr_elem */
771
772
0
                    if (gs_debug_c(':'))
773
0
                        dmprintf1(pdev->memory, "Printing page %d\n", curr_page);
774
0
                    if ((code = gx_output_saved_page(pdev, curr_elem->page)) < 0)
775
0
                        goto out;
776
777
0
                    curr_page += page_skip;
778
0
                    if (page_skip >= 0) {
779
0
                        if (curr_page > end_page)
780
0
                            break;
781
0
                        curr_elem = curr_elem->next;
782
0
                        if (page_skip > 1)
783
0
                            curr_elem = curr_elem->next;
784
0
                    } else {
785
                        /* reverse print order */
786
0
                        if (curr_page < end_page)
787
0
                            break;
788
0
                        curr_elem = curr_elem->prev;
789
                        /* Below is not needed since we never print reverse even/odd */
790
0
                        if (page_skip < -1) /* lgtm [cpp/constant-comparison] */
791
0
                            curr_elem = curr_elem->prev;
792
0
                    }
793
0
                    if (curr_elem == NULL) {
794
0
                         emprintf1(pdev->memory, "gx_saved_pages_list_print: page %d not found.\n", curr_page);
795
0
                         code = gs_error_rangecheck;;
796
0
                         goto out;
797
0
                    }
798
0
                }
799
800
                /* If we were printing EVEN, we may need to spit out a blank 'pad' page */
801
0
                if (do_blank_page_pad) {
802
                    /* print the empty page we had upon entry */
803
                    /* FIXME: Note that the page size may not match the last odd page */
804
0
                    if ((code = gx_output_saved_page(pdev, &saved_page)) < 0)
805
0
                        goto out;
806
0
                }
807
808
                /* After printing, reset to handle next page range */
809
0
                start_page = (start_page == end_page) ? page : 0;   /* if single page, set start_page */
810
                                                                    /* from the number scanned above  */
811
0
                end_page = 0;
812
0
            }
813
0
            if (token_size == 0)
814
0
                break;       /* finished with print ranges */
815
816
            /* Move to next token */
817
0
            param_left -= token - param_scan + token_size;
818
0
            param_scan = token + token_size;
819
0
        }
820
0
    }
821
0
out:
822
    /* restore the device parameters saved upon entry */
823
0
    *printed_count = pdev->PageCount - list->PageCount;
824
0
    list->PageCount = pdev->PageCount;    /* retain for subsequent print action */
825
0
    pdev->saved_pages_list = list;
826
0
    pdev->bg_print_requested = save_bg_print;
827
0
    crdev->do_not_open_or_close_bandfiles = save_bandfile_open_close;
828
829
    /* load must be after we've set saved_pages_list which forces clist mode. */
830
0
    do_page_load(pdev, &saved_page, saved_files);
831
832
    /* Finally, do the finish page which will reset the clist to empty and write mode */
833
0
    endcode = clist_finish_page((gx_device *)pdev, true);
834
0
    return code < 0 ? code : endcode < 0 ? endcode : param_scan - param;
835
0
}
836
837
/*
838
 * Caller should make sure that this device supports saved_pages:
839
 * dev_proc(dev, dev_spec_op)(dev, gxdso_supports_saved_pages, NULL, 0) == 1
840
 *
841
 * Returns < 0 if error, 1 if erasepage needed, 0 if no action needed.
842
 */
843
int
844
gx_saved_pages_param_process(gx_device_printer *pdev, byte *param, int param_size)
845
0
{
846
0
    byte *param_scan = param;
847
0
    int param_left = param_size;
848
0
    byte *token;
849
0
    int token_size, code, printed_count, collated_copies = 1;
850
0
    int tmp_num;      /* during token scanning loop */
851
0
    int erasepage_needed = 0;
852
853
0
    while (pdev->child)
854
0
        pdev = (gx_device_printer *)pdev->child;
855
856
0
    while ((token = param_parse_token(param_scan, param_left, &token_size)) != NULL) {
857
858
0
        switch (param_find_key(token, token_size)) {
859
0
          case PARAM_BEGIN:
860
0
            if (pdev->saved_pages_list == NULL) {
861
0
                if ((pdev->saved_pages_list = gx_saved_pages_list_new(pdev)) == NULL)
862
0
                    return_error(gs_error_VMerror);
863
0
                pdev->finalize = gdev_prn_finalize; /* set to make sure the list gets freed */
864
865
                /* We need to change to clist mode. Always uses clist when saving pages */
866
0
                pdev->saved_pages_list->save_banding_type = pdev->space_params.banding_type;
867
0
                pdev->space_params.banding_type = BandingAlways;
868
0
                if ((code = gdev_prn_reallocate_memory((gx_device *)pdev, &pdev->space_params, pdev->width, pdev->height)) < 0)
869
0
                    return code;
870
0
                erasepage_needed |= 1;
871
0
            }
872
0
            break;
873
874
0
          case PARAM_END:
875
0
            if (pdev->saved_pages_list != NULL) {
876
                /* restore to what was set before the "begin" */
877
0
                pdev->space_params.banding_type = pdev->saved_pages_list->save_banding_type;
878
0
                gx_saved_pages_list_free(pdev->saved_pages_list);
879
0
                pdev->saved_pages_list = NULL;
880
                /* We may need to change from clist mode since we forced clist when saving pages */
881
0
                code = gdev_prn_reallocate_memory((gx_device *)pdev, &pdev->space_params, pdev->width, pdev->height);
882
0
                if (code < 0)
883
0
                    return code;
884
0
                erasepage_needed |= 1;       /* make sure next page is erased */
885
0
            }
886
0
            break;
887
888
0
          case PARAM_FLUSH:
889
0
            if (pdev->saved_pages_list != NULL) {
890
                /* Save the collated copy count so the list we return will have it */
891
0
                collated_copies = pdev->saved_pages_list->collated_copies;
892
0
                gx_saved_pages_list_free(pdev->saved_pages_list);
893
0
            }
894
            /* Always return with an empty list, even if we weren't saving previously */
895
0
            if ((pdev->saved_pages_list = gx_saved_pages_list_new(pdev)) == NULL)
896
0
                return_error(gs_error_VMerror);
897
0
            pdev->finalize = gdev_prn_finalize; /* set to make sure the list gets freed */
898
            /* restore the original count */
899
0
            pdev->saved_pages_list->collated_copies = collated_copies;
900
0
            break;
901
902
0
          case PARAM_COPIES:      /* copies requires a number next */
903
            /* make sure that we have a list */
904
0
            if (pdev->saved_pages_list == NULL) {
905
0
                return_error(gs_error_rangecheck);  /* copies not allowed before a 'begin' */
906
0
            }
907
            /* Move to past 'copies' token */
908
0
            param_left -= token - param_scan + token_size;
909
0
            param_scan = token + token_size;
910
911
0
            if ((token = param_parse_token(param_scan, param_left, &token_size)) == NULL ||
912
0
                 param_find_key(token, token_size) != PARAM_NUMBER) {
913
0
                emprintf(pdev->memory, "gx_saved_pages_param_process: copies not followed by number.\n");
914
0
                return_error(gs_error_typecheck);
915
0
            }
916
0
            if (sscanf((const char *)token, "%d", &tmp_num) != 1) {
917
0
                emprintf1(pdev->memory, "gx_saved_pages_list_print: Number format error '%s'\n", token);
918
0
                code = gs_error_typecheck;
919
0
                return code;
920
0
            }
921
0
            pdev->saved_pages_list->collated_copies = tmp_num;  /* save it for our loop */
922
0
            break;
923
924
0
          case PARAM_PRINT:
925
            /* Move to past 'print' token */
926
0
            param_left -= token - param_scan + token_size;
927
0
            param_scan = token + token_size;
928
929
0
            code = param_left;      /* in case list is NULL, skip rest of string */
930
0
            if (pdev->saved_pages_list != NULL) {
931
0
                if ((code = gx_saved_pages_list_print(pdev, pdev->saved_pages_list,
932
0
                                                 param_scan, param_left, &printed_count)) < 0)
933
0
                    return code;
934
0
                erasepage_needed |= 1;       /* make sure next page is erased */
935
0
            }
936
            /* adjust for all of the print parameters */
937
0
            token_size += code;
938
0
            break;
939
940
          /* We are expecting an action keyword, so other keywords and tokens */
941
          /* are not valid here (mostly the 'print' parameters).              */
942
0
          default:
943
0
            {
944
0
                byte *bad_token = gs_alloc_string(pdev->memory, token_size+1, "saved_pages_param_process");
945
0
                byte *param_string = gs_alloc_string(pdev->memory, param_size+1, "saved_pages_param_process");
946
0
                if (bad_token != NULL && param_string != NULL) {
947
0
                    memcpy(bad_token, token, token_size);
948
0
                    bad_token[token_size] = 0;          /* terminate string */
949
0
                    memcpy(param_string, param, param_size);
950
0
                    param_string[param_size] = 0;          /* terminate string */
951
0
                    emprintf2(pdev->memory, "*** Invalid saved-pages token '%s'\n *** in param string '%s'\n",
952
0
                              bad_token, param_string);
953
0
                    gs_free_string(pdev->memory, bad_token, token_size+1,  "saved_pages_param_process");
954
0
                    gs_free_string(pdev->memory, param_string, param_size+1,  "saved_pages_param_process");
955
0
                }
956
0
            }
957
0
        }
958
        /* Move to next token */
959
0
        param_left -= token - param_scan + token_size;
960
0
        param_scan = token + token_size;
961
962
0
    }
963
0
    return erasepage_needed;
964
0
}