summaryrefslogtreecommitdiff
path: root/base/pagelist.c
diff options
context:
space:
mode:
Diffstat (limited to 'base/pagelist.c')
-rw-r--r--base/pagelist.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/base/pagelist.c b/base/pagelist.c
new file mode 100644
index 000000000..c687b7b98
--- /dev/null
+++ b/base/pagelist.c
@@ -0,0 +1,246 @@
+/* Copyright (C) 2022-2022 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
+ CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Functions to parse a PageList string and processing utilitiess */
+
+
+#include "memory_.h"
+#include "string_.h" /* for strcspn */
+#include <stdlib.h> /* for atoi */
+#include "gsmemory.h"
+#include "gserrors.h"
+#include "pagelist.h"
+
+/* array contains an "ordered" flag, then even/odd, start, end triples */
+
+/* Allocate an array of ranges. A range of 0-0 terminates the list */
+/* Note the first int 0 is order unknown, < 0 is unordered, > 0 is */
+/* ordered and thus is suitable for sequential parsers like PS or PCL. */
+/* This first int is also used by test_printed as the offset into the */
+/* array for fast processing by SkipPage. */
+/* returns error code < 0 or number of ranges if success. */
+int
+pagelist_parse_to_array(char *page_list, gs_memory_t *mem, int num_pages, int **parray)
+{
+ int i = 0, range_count = 0;
+ int *pagelist_array = NULL;
+ char *p = page_list;
+ int prev_end = 0; /* used to track if the ranges are ordered */
+ int ordered = 1; /* default is ordered, until it's not */
+ int comma, len;
+
+ *parray = NULL; /* in case PageList is empty */
+ if (page_list[0] == 0)
+ return gs_error_rangecheck; /* empty list not valid */
+
+ do {
+ len = strlen(p);
+ comma = strcspn(p, ",");
+ p += comma + 1;
+ if (comma > 0)
+ range_count++;
+ } while (comma < len);
+ if (range_count == 0)
+ return gs_error_rangecheck; /* not valid */
+
+ range_count++; /* room for the end of list marker: 0, 0, 0 */
+ pagelist_array = (int *)gs_alloc_byte_array(mem, 1 + (range_count * 3), sizeof(int), "pagelist_parse_to_array");
+ *parray = pagelist_array; /* return the array (NULL is OK) */
+ if (pagelist_array == NULL)
+ return gs_error_VMerror;
+
+ memset(pagelist_array, 0, (1 + range_count * 3) * sizeof(int));
+ /* Fill the array */
+
+ p = page_list; /* go back to start of string */
+ for (i=1; i < (range_count - 1) * 3; ) {
+ /* Parsing method from Michael Vrhel's code in xps */
+ int dash, start, end, even_odd;
+
+ even_odd = 0;
+ start = 1;
+ end = -1; /* internal flag for last page */
+ len = strlen(p);
+ comma = strcspn(p, ",");
+ dash = strcspn(p, "-");
+
+ if (comma == 0) {
+ p++;
+ continue; /* skip leading or adjacent commas */
+ }
+ if (strncmp(p, "even", 4) == 0) {
+ even_odd = 2;
+ p += 4;
+ } else if (strncmp(p, "odd", 3) == 0) {
+ even_odd = 1;
+ p += 3;
+ }
+ /* check if the next after "even" or "odd" is ':' */
+ if (even_odd != 0) {
+ start = even_odd; /* default is entire range */
+ end = -1;
+ if (*p == ':')
+ p++; /* skip to the range */
+ len = strlen(p);
+ comma = strcspn(p, ",");
+ dash = strcspn(p, "-");
+ }
+ if (comma > 0 && *p != 0) {
+ /* We have a range to process */
+ if (dash < comma) {
+ /* Dash at start */
+ if (dash == 0) {
+ start = -1;
+ end = atoi(p + dash + 1);
+ } else {
+ start = atoi(p);
+ /* Dash at end */
+ if (p[dash + 1] == 0 || p[dash + 1] == ',') {
+ end = -1;
+ } else {
+ end = atoi(p + dash + 1);
+ }
+ }
+ } else {
+ start = atoi(p);
+ end = start;
+ }
+ }
+ if (comma == len)
+ p += comma;
+ else
+ p += comma + 1;
+
+ /* If even or odd, and we have a "num_pages" flag == -1, adjust to proper value */
+ if (even_odd == 2) {
+ if (start == -1)
+ start = num_pages & -2;
+ if (end == -1)
+ end = num_pages & -2;
+ } else if (even_odd == 1) {
+ if (start == -1)
+ start = num_pages - (1 & (num_pages ^ 1));
+ if (end == -1)
+ end = num_pages - (1 & (num_pages ^ 1));
+ } else {
+ if (start == -1)
+ start = num_pages;
+ if (end == -1)
+ end = num_pages;
+ }
+ /* Store this range, bump the index as we do */
+ pagelist_array[i++] = even_odd;
+ pagelist_array[i++] = start;
+ pagelist_array[i++] = end;
+
+ if (start <= prev_end || start > end)
+ ordered = -1;
+ prev_end = end;
+ }
+ /* Note final array entries are 0, 0, 0 from memset above */
+ pagelist_array[0] = ordered;
+ return range_count;
+}
+
+/* function to calculate the total number of pages to be output */
+int
+pagelist_number_of_pages(const int *parray)
+{
+ int count = 0, i;
+
+ /* loop over ranges until start == 0 */
+ for (i=1; parray[i+1] != 0; i += 3) {
+ int start = parray[i+1];
+ int end = parray[i+2];
+
+ if (end >= start)
+ count += 1 + end - start;
+ else
+ count += 1 + start - end;
+ }
+ return count;
+}
+
+/* Return true if this pagelist is in strict increasing order */
+bool
+pagelist_test_ordered(int *parray)
+{
+ int prev;
+ int i;
+
+ /* check flag (fast for use by the flp SkipPage function */
+ if (parray[0] > 0)
+ return true;
+ if (parray[0] < 0)
+ return false;
+
+ /* parray[0] == 0, scan until start of range is 0 */
+ prev = 0; /* page 1 or more is legal */
+ for (i=1; parray[i+1] != 0; i += 3) {
+ if (parray[i+1] <= prev || parray[i+1] < parray[i+2])
+ break; /* NOT ordered */
+ prev = parray[i+2]; /* advance to end of range */
+ }
+ /* if we made it to the marker at the end, then the list is ordered */
+ parray[0] = (parray[i+1] == 0) ? 1 : -1;
+ return parray[0] > 0;
+}
+
+/* Return true if this page is to be printed. (For sequential processing) */
+/* This assumes/requires that the PageList is strictly sequential. */
+bool
+pagelist_test_printed(int *parray, int pagenum)
+{
+ int i = parray[0];
+
+ if (i <= 0) { /* shouldn't happen */
+ pagelist_test_ordered(parray); /* in case caller didn't do this */
+ i = parray[0];
+ if (i < 0)
+ return false; /* list is NOT ordered, punt */
+ }
+
+ /* advance to next range if pagenum past the end of this range */
+ /* stopping at the 0, 0, 0 marker (start == 0) */
+ while (pagenum > parray[i+2] && parray[i+1] != 0) {
+ i += 3; /* bump to next range for next call */
+ parray[0] = i; /* update for next entry */
+ }
+ if (parray[i+1] == 0)
+ return false; /* reached end of ranges */
+
+ /* First Test even, then odd */
+ if (parray[i] == 2 && (pagenum & 1) == 1)
+ return false;
+ if (parray[i] == 1 && (pagenum & 1) == 0)
+ return false;
+
+ /* Fast case for ordered list and sequential page numbers (FLP device) */
+ if (i > 0) {
+ if (pagenum >= parray[i+1] && pagenum <= parray[i+2]) {
+ return true; /* page was in this range */
+ } else {
+ if (pagenum < parray[i+1])
+ return false; /* pagennum in between ranges that skip */
+ }
+ }
+ return false;
+}
+
+void
+pagelist_free_range_array(gs_memory_t *mem, int *parray)
+{
+ gs_free_object(mem, parray, "flp_close_device");
+}