Coverage Report

Created: 2022-04-16 11:23

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