summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Vrhel <michael.vrhel@artifex.com>2020-08-26 16:53:36 -0700
committerMichael Vrhel <michael.vrhel@artifex.com>2020-09-22 10:13:31 -0700
commit9bfaeeace268f6a59f325e12efab9fb8273f64e0 (patch)
tree4cb7bdb6ee283bcef465e2918ff00d68db8d9021
parentc4e79952b42ebccb669063e053e3d7ce0f88cc22 (diff)
downloadghostpdl-9bfaeeace268f6a59f325e12efab9fb8273f64e0.tar.gz
XPS interpreter: Have interpreter handle page range processing
This has the obvious benefit that the interpreter will skip processing pages except those that lie in the FirstPage LastPage range. If the PageList is used, the XPS interpreter will create a new list of linked pages to process. The XPS interpreter handles all the PageList cases currently implemented by the PDF interpreter including even and odd. In addition, the XPS interpreter will handle backward indexing (e.g. 100-1), as well as starting from last page to another page (e.g. -2 which means from last page to second page), and page repeats (e.g. 1,2,1,2,1,2 or 1-3,3-1 or 1-,-1 which is do page 1 to end and end to page 1) Also, setting -dFirstPage=4 -dLastPage=1 will create pages 4,3,2,1
-rw-r--r--doc/Use.htm24
-rw-r--r--xps/ghostxps.h12
-rw-r--r--xps/xps.mak2
-rw-r--r--xps/xpsdoc.c31
-rw-r--r--xps/xpstop.c67
-rw-r--r--xps/xpszip.c205
6 files changed, 338 insertions, 3 deletions
diff --git a/doc/Use.htm b/doc/Use.htm
index b2f01e3b4..8349d38c1 100644
--- a/doc/Use.htm
+++ b/doc/Use.htm
@@ -794,6 +794,18 @@ is applied to each PDF file separately. So if you were to set <code>-sPageList=1
exercise caution when using this switch, and probably should not use it at all when processing a mixture of PostScript
and PDF files on the same command line.
</p>
+<p>
+The XPS language like the PDF language allows random access to pages. The XPS interpreter handles all the PageList cases
+discussed above. It also handles cases such as:
+<blockquote><pre>
+-sPageList=1,2,1,2 indicates repeated pages. Pages processed in order 1, 2, 1, 2.
+-sPageList=10-5 indicates pages will be processed in the order 10, 9, 8, 7, 6, 5.
+-sPageList=1-,-1 indicates first processing from page 1 to end and then from end to page 1.
+</pre></blockquote>
+In addition, the XPS interpreter allows the use of a -dLastPage < -dFirstPage. In this
+case the pages will be processed backwards from LastPage to FirstPage.
+</p>
+
</dd>
</dl>
@@ -2666,6 +2678,18 @@ is applied to each PDF file separately. So if you were to set <code>-sPageList=1
exercise caution when using this switch, and probably should not use it at all when processing a mixture of PostScript
and PDF files on the same command line.
</p>
+<p>
+The XPS language like the PDF language allows random access to pages. The XPS interpreter handles all the PageList cases
+discussed above. It also handles cases such as:
+<blockquote><pre>
+-sPageList=1,2,1,2 indicates repeated pages. Pages processed in order 1, 2, 1, 2.
+-sPageList=10-5 indicates pages will be processed in the order 10, 9, 8, 7, 6, 5.
+-sPageList=1-,-1 indicates first processing from page 1 to end and then from end to page 1.
+</pre></blockquote>
+In addition, the XPS interpreter allows the use of a -dLastPage < -dFirstPage. In this
+case the pages will be processed backwards from LastPage to FirstPage.
+</p>
+
</dd>
</dl>
diff --git a/xps/ghostxps.h b/xps/ghostxps.h
index 19ffb236f..cf029db4d 100644
--- a/xps/ghostxps.h
+++ b/xps/ghostxps.h
@@ -177,6 +177,7 @@ void xps_free_part(xps_context_t *ctx, xps_part_t *part);
typedef struct xps_document_s xps_document_t;
typedef struct xps_page_s xps_page_t;
+typedef struct xps_page_range_s xps_page_range_t;
struct xps_document_s
{
@@ -192,6 +193,15 @@ struct xps_page_s
xps_page_t *next;
};
+struct xps_page_range_s
+{
+ int first;
+ int last;
+ int reverse;
+ int current;
+ char *page_list;
+};
+
int xps_parse_metadata(xps_context_t *ctx, xps_part_t *part);
void xps_free_fixed_pages(xps_context_t *ctx);
void xps_free_fixed_documents(xps_context_t *ctx);
@@ -410,6 +420,8 @@ struct xps_context_s
xps_page_t *first_page; /* first page of document */
xps_page_t *last_page; /* last page of document */
+ xps_page_range_t *page_range; /* interpreter-based page range handling */
+
char *base_uri; /* base uri for parsing XML and resolving relative paths */
char *part_uri; /* part uri for parsing metadata relations */
diff --git a/xps/xps.mak b/xps/xps.mak
index 105e2d640..c1b4d0a89 100644
--- a/xps/xps.mak
+++ b/xps/xps.mak
@@ -137,7 +137,7 @@ $(PLOBJ)xpsimpl.$(OBJ): $(XPSGEN)xpsimpl.c \
$(MAKEDIRS)
$(XPSCCC) $(XPSGEN)xpsimpl.c $(XPSO_)xpsimpl.$(OBJ)
-$(XPS_TOP_OBJ): $(XPSSRC)xpstop.c $(plmain_h) $(pltop_h) $(XPSINCLUDES) $(GLOBJ)gconfig.$(OBJ) \
+$(XPS_TOP_OBJ): $(XPSSRC)xpstop.c $(plmain_h) $(pltop_h) $(gsparam_h) $(XPSINCLUDES) $(GLOBJ)gconfig.$(OBJ) \
$(pconfig_h) $(XPS_MAK) $(MAKEDIRS)
$(XPSCCC) $(XPSSRC)xpstop.c $(XPSO_)xpstop.$(OBJ)
diff --git a/xps/xpsdoc.c b/xps/xpsdoc.c
index 17fce9fc1..97445ec2f 100644
--- a/xps/xpsdoc.c
+++ b/xps/xpsdoc.c
@@ -134,6 +134,24 @@ xps_add_fixed_page(xps_context_t *ctx, char *name, int width, int height)
if (!strcmp(page->name, name))
return;
+ if (ctx->page_range && !ctx->page_range->page_list)
+ {
+ ctx->page_range->current++;
+
+ if (ctx->page_range->reverse)
+ {
+ if (ctx->page_range->current < ctx->page_range->last ||
+ ctx->page_range->current > ctx->page_range->first)
+ return;
+ }
+ else
+ {
+ if ((ctx->page_range->first != 0 && ctx->page_range->current < ctx->page_range->first) ||
+ (ctx->page_range->last != 0 && ctx->page_range->current > ctx->page_range->last))
+ return;
+ }
+ }
+
if_debug1m('|', ctx->memory, "doc: adding page %s\n", name);
page = xps_alloc(ctx, sizeof(xps_page_t));
@@ -153,8 +171,17 @@ xps_add_fixed_page(xps_context_t *ctx, char *name, int width, int height)
}
else
{
- ctx->last_page->next = page;
- ctx->last_page = page;
+ /* FirstPage < LastPage? */
+ if (ctx->page_range && ctx->page_range->reverse)
+ {
+ page->next = ctx->first_page;
+ ctx->first_page = page;
+ }
+ else
+ {
+ ctx->last_page->next = page;
+ ctx->last_page = page;
+ }
}
}
diff --git a/xps/xpstop.c b/xps/xpstop.c
index 6717da985..424ca12fb 100644
--- a/xps/xpstop.c
+++ b/xps/xpstop.c
@@ -27,6 +27,7 @@
#include "gxdevice.h" /* so we can include gxht.h below */
#include "gxht.h" /* gsht1.h is incomplete, we need storage size of gs_halftone */
#include "gsht1.h"
+#include "gsparam.h"
#include <assert.h>
@@ -197,6 +198,9 @@ xps_impl_init_job(pl_interp_implementation_t *impl,
xps_context_t *ctx = instance->ctx;
gs_c_param_list list;
int code;
+ bool disable_page_handler = false;
+ int true_val = 1;
+ gs_memory_t* mem = ctx->memory;
if (gs_debug_c('|'))
xps_zip_trace = 1;
@@ -237,6 +241,61 @@ xps_impl_init_job(pl_interp_implementation_t *impl,
gs_setscanconverter(ctx->pgs, pl_main_get_scanconverter(ctx->memory));
+ /* Disable the page handler as the XPS interpreter will handle page range.
+ List takes precedent over firstpage lastpage */
+ if (pdevice->PageList != NULL && pdevice->PageHandlerPushed)
+ {
+ ctx->page_range = xps_alloc(ctx, sizeof(xps_page_range_t));
+ if (!ctx->page_range)
+ {
+ return gs_rethrow(gs_error_VMerror, "out of memory: page_range struct");
+ }
+
+ ctx->page_range->page_list = xps_strdup(ctx, pdevice->PageList->Pages);
+ if (!ctx->page_range->page_list)
+ {
+ return gs_rethrow(gs_error_VMerror, "out of memory: page_list");
+ }
+ disable_page_handler = true;
+ }
+ else if ((pdevice->FirstPage > 0 || pdevice->LastPage > 0) &&
+ pdevice->PageHandlerPushed)
+ {
+ disable_page_handler = true;
+
+ ctx->page_range = xps_alloc(ctx, sizeof(xps_page_range_t));
+ if (!ctx->page_range)
+ {
+ return gs_throw(gs_error_VMerror, "out of memory: page_range struct");
+ }
+ ctx->page_range->first = pdevice->FirstPage;
+ ctx->page_range->last = pdevice->LastPage;
+ ctx->page_range->current = 0;
+ ctx->page_range->page_list = NULL;
+
+ if (ctx->page_range->first != 0 &&
+ ctx->page_range->last != 0 &&
+ ctx->page_range->first > ctx->page_range->last)
+ ctx->page_range->reverse = true;
+ else
+ ctx->page_range->reverse = false;
+ }
+
+ if (disable_page_handler)
+ {
+ gs_c_param_list_write(&list, mem);
+ code = param_write_bool((gs_param_list*)&list, "DisablePageHandler", &(true_val));
+ if (code >= 0)
+ {
+ gs_c_param_list_read(&list);
+ code = gs_putdeviceparams(pdevice, (gs_param_list*)&list);
+ gs_c_param_list_release(&list);
+ if (code < 0) {
+ return gs_rethrow(code, "cannot set device parameters");
+ }
+ }
+ }
+
/* gsave and grestore (among other places) assume that */
/* there are at least 2 gstates on the graphics stack. */
/* Ensure that now. */
@@ -413,6 +472,14 @@ xps_impl_dnit_job(pl_interp_implementation_t *impl)
xps_hash_free(ctx, ctx->font_table, xps_free_key_func, xps_free_font_func);
xps_hash_free(ctx, ctx->colorspace_table, xps_free_key_func, xps_free_hashed_colorspace);
+ if (ctx->page_range)
+ {
+ if (ctx->page_range->page_list)
+ xps_free(ctx, ctx->page_range->page_list);
+ xps_free(ctx, ctx->page_range);
+ ctx->page_range = NULL;
+ }
+
xps_free_fixed_pages(ctx);
xps_free_fixed_documents(ctx);
diff --git a/xps/xpszip.c b/xps/xpszip.c
index 1c3843567..d00678794 100644
--- a/xps/xpszip.c
+++ b/xps/xpszip.c
@@ -565,6 +565,200 @@ xps_read_and_process_page_part(xps_context_t *ctx, char *name)
return gs_okay;
}
+/* XPS page reordering based upon Device PageList setting */
+static int
+xps_reorder_add_page(xps_context_t* ctx, xps_page_t ***page_ptr, xps_page_t* page_to_add)
+{
+ xps_page_t* new_page;
+
+ new_page = xps_alloc(ctx, sizeof(xps_page_t));
+ if (!new_page)
+ {
+ return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_add_page\n");
+ }
+
+ new_page->name = xps_strdup(ctx, page_to_add->name);
+ if (!new_page->name)
+ {
+ return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_add_page\n");
+ }
+ new_page->height = page_to_add->height;
+ new_page->width = page_to_add->width;
+ new_page->next = NULL;
+
+ **page_ptr = new_page;
+ *page_ptr = &(new_page->next);
+
+ return 0;
+}
+
+static char*
+xps_reorder_get_range(xps_context_t *ctx, char *page_list, int *start, int *end, int num_pages)
+{
+ int comma, dash, len;
+
+ len = strlen(page_list);
+ comma = strcspn(page_list, ",");
+ dash = strcspn(page_list, "-");
+
+ if (dash < comma)
+ {
+ /* Dash at start */
+ if (dash == 0)
+ {
+ *start = num_pages;
+ *end = atoi(&(page_list[dash + 1]));
+ }
+ else
+ {
+ *start = atoi(page_list);
+
+ /* Dash at end */
+ if (page_list[dash + 1] == 0 || page_list[dash + 1] == ',')
+ {
+ *end = num_pages;
+ }
+ else
+ {
+ *end = atoi(&(page_list[dash + 1]));
+ }
+ }
+ }
+ else
+ {
+ *start = atoi(page_list);
+ *end = *start;
+ }
+ return comma == len ? page_list + comma : page_list + comma + 1;
+}
+
+static int
+xps_reorder_pages(xps_context_t *ctx)
+{
+ char *page_list = ctx->page_range->page_list;
+ char *str;
+ xps_page_t **page_ptr_array, *page = ctx->first_page;
+ int count = 0, k;
+ int code;
+ int start;
+ int end;
+ xps_page_t* first_page = NULL;
+ xps_page_t* last_page;
+ xps_page_t** page_tail = &first_page;
+
+ if (page == NULL)
+ return 0;
+
+ while (page != NULL)
+ {
+ count++;
+ page = page->next;
+ }
+
+ /* Create an array of pointers to the current pages */
+ page_ptr_array = xps_alloc(ctx, sizeof(xps_page_t*) * count);
+ if (page_ptr_array == NULL)
+ return gs_throw(gs_error_VMerror, "out of memory: xps_reorder_pages\n");
+
+ page = ctx->first_page;
+ for (k = 0; k < count; k++)
+ {
+ page_ptr_array[k] = page;
+ page = page->next;
+ }
+
+ if (strcmp(page_list, "even") == 0)
+ {
+ for (k = 1; k < count; k += 2)
+ {
+ code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[k]);
+ if (code < 0)
+ return code;
+ }
+ }
+ else if (strcmp(page_list, "odd") == 0)
+ {
+ for (k = 0; k < count; k += 2)
+ {
+ code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[k]);
+ if (code < 0)
+ return code;
+ }
+ }
+ else
+ {
+ /* Requirements. All characters must be 0 to 9 or - and ,
+ No ,, or -- */
+ str = page_list;
+ do
+ {
+ if (*str != ',' && *str != '-' && (*str < 0x30 || *str > 0x39))
+ return gs_throw(gs_error_typecheck, "Bad page list: xps_reorder_pages\n");
+
+ if ((*str == ',' && *(str + 1) == ',') || (*str == '-' && *(str + 1) == '-'))
+ return gs_throw(gs_error_typecheck, "Bad page list: xps_reorder_pages\n");
+ }
+ while (*(++str));
+
+ str = page_list;
+ do
+ {
+ /* Process each comma separated item. */
+ str = xps_reorder_get_range(ctx, str, &start, &end, count);
+
+ /* Threshold page range */
+ if (start > count)
+ start = count;
+
+ if (end > count)
+ end = count;
+
+ /* Add page(s) */
+ if (start == end)
+ {
+ code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[start - 1]);
+ if (code < 0)
+ return code;
+ }
+ else if (start < end)
+ {
+ for (k = start - 1; k < end; k++)
+ {
+ code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[k]);
+ if (code < 0)
+ return code;
+ }
+ }
+ else
+ {
+ for (k = start; k >= end; k--)
+ {
+ code = xps_reorder_add_page(ctx, &page_tail, page_ptr_array[k - 1]);
+ if (code < 0)
+ return code;
+ }
+ }
+ }
+ while (*str);
+ }
+
+ /* Replace the pages. */
+ if (first_page != NULL)
+ {
+ /* Set to the last page not its next pointer (Thanks to Robin Watts) */
+ last_page = (xps_page_t*)(((char*)page_tail) - offsetof(xps_page_t, next));
+
+ xps_free_fixed_pages(ctx);
+ xps_free(ctx, page_ptr_array);
+ ctx->first_page = first_page;
+ ctx->last_page = last_page;
+ }
+ else
+ return gs_throw(gs_error_rangecheck, "Bad page list: xps_reorder_pages\n");
+
+ return 0;
+}
+
/*
* Called by xpstop.c
*/
@@ -700,6 +894,17 @@ xps_process_file(xps_context_t *ctx, const char *filename)
}
}
+ /* If we have a page list, adjust pages now */
+ if (ctx->page_range && ctx->page_range->page_list)
+ {
+ code = xps_reorder_pages(ctx);
+ if (code)
+ {
+ code = gs_rethrow(code, "invalid page range setting");
+ goto cleanup;
+ }
+ }
+
for (page = ctx->first_page; page; page = page->next)
{
code = xps_read_and_process_page_part(ctx, page->name);