summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Deutschmann <whissi@gentoo.org>2019-10-15 12:24:12 +0200
committerThomas Deutschmann <whissi@gentoo.org>2020-08-13 11:26:55 +0200
commite088156d5b620e5e639580dacf85c6dc13823c74 (patch)
tree57f5c025e203279944da512166c20bc0521d8ccd /devices/vector/gdevpdfr.c
downloadghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.gz
ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.bz2
ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.zip
Import Ghostscript 9.50ghostscript-9.50
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
Diffstat (limited to 'devices/vector/gdevpdfr.c')
-rw-r--r--devices/vector/gdevpdfr.c512
1 files changed, 512 insertions, 0 deletions
diff --git a/devices/vector/gdevpdfr.c b/devices/vector/gdevpdfr.c
new file mode 100644
index 00000000..6d9f7f6f
--- /dev/null
+++ b/devices/vector/gdevpdfr.c
@@ -0,0 +1,512 @@
+/* Copyright (C) 2001-2019 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.
+*/
+
+
+/* Named object pdfmark processing */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gdevpdfx.h"
+#include "gdevpdfo.h"
+#include "scanchar.h"
+#include "strimpl.h"
+#include "sstring.h"
+
+#ifndef gs_error_syntaxerror
+# define gs_error_syntaxerror gs_error_rangecheck
+#endif
+
+/* Test whether an object name has valid syntax, {name}. */
+bool
+pdf_objname_is_valid(const byte *data, uint size)
+{
+ return (size >= 2 && data[0] == '{' &&
+ (const byte *)memchr(data, '}', size) == data + size - 1);
+}
+
+/*
+ * Look up a named object. Return_error(gs_error_rangecheck if the syntax is invalid.
+ * If the object is missing, return gs_error_undefined.
+ */
+int
+pdf_find_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_object_t **ppco)
+{
+ const cos_value_t *pvalue;
+
+ if (!pdf_objname_is_valid(pname->data, pname->size))
+ return_error(gs_error_rangecheck);
+ if ((pvalue = cos_dict_find(pdev->local_named_objects, pname->data,
+ pname->size)) != 0 ||
+ (pvalue = cos_dict_find(pdev->global_named_objects, pname->data,
+ pname->size)) != 0
+ ) {
+ *ppco = pvalue->contents.object;
+ return 0;
+ }
+ return_error(gs_error_undefined);
+}
+
+/*
+ * Create a (local) named object. id = -1L means do not assign an id.
+ * pname = 0 means just create the object, do not name it. Note that
+ * during initialization, local_named_objects == global_named_objects.
+ */
+int
+pdf_create_named(gx_device_pdf *pdev, const gs_param_string *pname,
+ cos_type_t cotype, cos_object_t **ppco, long id)
+{
+ cos_object_t *pco;
+ cos_value_t value;
+
+ *ppco = pco = cos_object_alloc(pdev, "pdf_create_named");
+ if (pco == 0)
+ return_error(gs_error_VMerror);
+ pco->id =
+ (id == -1 ? 0L : id == 0 ? pdf_obj_ref(pdev) : id);
+ if (pname) {
+ int code = cos_dict_put(pdev->local_named_objects, pname->data,
+ pname->size, cos_object_value(&value, pco));
+
+ if (code < 0)
+ return code;
+ }
+ if (cotype != cos_type_generic)
+ cos_become(pco, cotype);
+ *ppco = pco;
+ return 0;
+}
+int
+pdf_create_named_dict(gx_device_pdf *pdev, const gs_param_string *pname,
+ cos_dict_t **ppcd, long id)
+{
+ cos_object_t *pco;
+ int code = pdf_create_named(pdev, pname, cos_type_dict, &pco, id);
+
+ *ppcd = (cos_dict_t *)pco;
+ return code;
+}
+
+/*
+ * Look up a named object as for pdf_find_named. If the object does not
+ * exist, create it (as a dictionary if it is one of the predefined names
+ * {ThisPage}, {NextPage}, {PrevPage}, or {Page<#>}, otherwise as a
+ * generic object) and return 1.
+ */
+int
+pdf_refer_named(gx_device_pdf * pdev, const gs_param_string * pname_orig,
+ cos_object_t **ppco)
+{
+ const gs_param_string *pname = pname_orig;
+ int code = pdf_find_named(pdev, pname, ppco);
+ char page_name_chars[6 + 10 + 2]; /* {Page<n>}, enough for an int */
+ gs_param_string pnstr;
+ int page_number;
+
+ if (code != gs_error_undefined)
+ return code;
+ /*
+ * Check for a predefined name. Map ThisPage, PrevPage, and NextPage
+ * to the appropriate Page<#> name.
+ */
+ if (pname->size >= 7 && pname->size < sizeof(page_name_chars)) {
+ memcpy(page_name_chars, pname->data, pname->size);
+ page_name_chars[pname->size] = 0;
+ if (sscanf(page_name_chars, "{Page%d}", &page_number) == 1)
+ goto cpage;
+ }
+ if (pdf_key_eq(pname, "{ThisPage}"))
+ page_number = pdev->next_page + 1;
+ else if (pdf_key_eq(pname, "{NextPage}"))
+ page_number = pdev->next_page + 2;
+ else if (pdf_key_eq(pname, "{PrevPage}"))
+ page_number = pdev->next_page;
+ else {
+ code = pdf_create_named(pdev, pname, cos_type_generic, ppco, 0L);
+ return (code < 0 ? code : 1);
+ }
+ if (page_number <= 0)
+ return code;
+ gs_sprintf(page_name_chars, "{Page%d}", page_number);
+ param_string_from_string(pnstr, page_name_chars);
+ pname = &pnstr;
+ code = pdf_find_named(pdev, pname, ppco);
+ if (code != gs_error_undefined)
+ return code;
+ cpage:
+ if (pdf_page_id(pdev, page_number) <= 0)
+ return_error(gs_error_rangecheck);
+ *ppco = COS_OBJECT(pdev->pages[page_number - 1].Page);
+ return 0;
+}
+
+/*
+ * Look up a named object as for pdf_refer_named. If the object already
+ * exists and is not simply a forward reference, return gs_error_rangecheck;
+ * if it exists as a forward reference, set its type and return 0;
+ * otherwise, create the object with the given type and return 1.
+ */
+int
+pdf_make_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_type_t cotype, cos_object_t **ppco, bool assign_id)
+{
+ if (pname) {
+ int code = pdf_refer_named(pdev, pname, ppco);
+ cos_object_t *pco = *ppco;
+
+ if (code < 0)
+ return code;
+ if (cos_type(pco) != cos_type_generic)
+ return_error(gs_error_rangecheck);
+ if (assign_id && pco->id == 0)
+ pco->id = pdf_obj_ref(pdev);
+ cos_become(pco, cotype);
+ return code;
+ } else {
+ int code = pdf_create_named(pdev, pname, cotype, ppco,
+ (assign_id ? 0L : -1L));
+
+ return (code < 0 ? code : 1);
+ }
+}
+int
+pdf_make_named_dict(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_dict_t **ppcd, bool assign_id)
+{
+ cos_object_t *pco;
+ int code = pdf_make_named(pdev, pname, cos_type_dict, &pco, assign_id);
+
+ *ppcd = (cos_dict_t *)pco;
+ return code;
+}
+
+/*
+ * Look up a named object as for pdf_refer_named. If the object does not
+ * exist, return gs_error_undefined; if the object exists but has the wrong type,
+ * return gs_error_typecheck.
+ */
+int
+pdf_get_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_type_t cotype, cos_object_t **ppco)
+{
+ int code = pdf_refer_named(pdev, pname, ppco);
+
+ if (code < 0)
+ return code;
+ if (cos_type(*ppco) != cotype)
+ return_error(gs_error_typecheck);
+ return code;
+}
+
+/*
+ * Push the current local namespace onto the namespace stack, and reset it
+ * to an empty namespace.
+ */
+int
+pdf_push_namespace(gx_device_pdf *pdev)
+{
+ int code = cos_array_add_object(pdev->Namespace_stack,
+ COS_OBJECT(pdev->local_named_objects));
+ cos_dict_t *pcd =
+ cos_dict_alloc(pdev, "pdf_push_namespace(local_named_objects)");
+ cos_array_t *pca =
+ cos_array_alloc(pdev, "pdf_push_namespace(NI_stack)");
+
+ if (code < 0 ||
+ (code = cos_array_add_object(pdev->Namespace_stack,
+ COS_OBJECT(pdev->NI_stack))) < 0
+ )
+ return code;
+ if (pcd == 0 || pca == 0)
+ return_error(gs_error_VMerror);
+ pdev->local_named_objects = pcd;
+ pdev->NI_stack = pca;
+ return 0;
+}
+
+/*
+ * Pop the top local namespace from the namespace stack. Return an error if
+ * the stack is empty.
+ */
+int
+pdf_pop_namespace(gx_device_pdf *pdev)
+{
+ cos_value_t nis_value, lno_value;
+ int code = cos_array_unadd(pdev->Namespace_stack, &nis_value);
+
+ if (code < 0 ||
+ (code = cos_array_unadd(pdev->Namespace_stack, &lno_value)) < 0
+ )
+ return code;
+ COS_FREE(pdev->local_named_objects,
+ "pdf_pop_namespace(local_named_objects)");
+ pdev->local_named_objects = (cos_dict_t *)lno_value.contents.object;
+ COS_FREE(pdev->NI_stack, "pdf_pop_namespace(NI_stack)");
+ pdev->NI_stack = (cos_array_t *)nis_value.contents.object;
+ return 0;
+}
+
+/*
+ * Scan a token from a string. <<, >>, [, and ] are treated as tokens.
+ * Return 1 if a token was scanned, 0 if we reached the end of the string,
+ * or an error. On a successful return, the token extends from *ptoken up
+ * to but not including *pscan.
+ *
+ * Note that this scanner expects a subset of PostScript syntax, not PDF
+ * syntax. In particular, it doesn't understand ASCII85 strings,
+ * doesn't process the PDF #-escape syntax within names, and does only
+ * minimal syntax checking. It also recognizes one extension to PostScript
+ * syntax, to allow gs_pdfwr.ps to pass names that include non-regular
+ * characters: If a name is immediately preceded by two null characters,
+ * the name includes everything up to a following null character. The only
+ * place that currently generates this convention is the PostScript code
+ * that pre-processes the arguments for pdfmarks, in lib/gs_pdfwr.ps.
+ */
+int
+pdf_scan_token(const byte **pscan, const byte * end, const byte **ptoken)
+{
+ const byte *p = *pscan;
+
+ while (p < end && scan_char_decoder[*p] == ctype_space) {
+ ++p;
+ if (p[-1] == 0 && p + 1 < end && p + 2 < end && *p == 0 && p[1] == 0 && p[2] == '/') {
+ /* Special handling for names delimited by a triple start and double end null character. */
+ *ptoken = p + 2;
+ while (*p != 0 || p[1] != 0)
+ if (++p >= end || p + 1 >= end)
+ return_error(gs_error_syntaxerror); /* no terminator */
+ *pscan = p + 1;
+ return 1;
+ } else {
+ if (p[-1] == 0 && p + 1 < end && *p == 0 && p[2] == '/') {
+ /* Special handling for names delimited by a double start and single end null character. */
+ *ptoken = ++p;
+ while (*p != 0)
+ if (++p >= end)
+ return_error(gs_error_syntaxerror); /* no terminator */
+ *pscan = p;
+ return 1;
+ }
+ }
+ }
+ *ptoken = p;
+ if (p >= end) {
+ *pscan = p;
+ return 0;
+ }
+ switch (*p) {
+ case '%':
+ case ')':
+ return_error(gs_error_syntaxerror);
+ case '(': {
+ /* Skip over the string. */
+ byte buf[50]; /* size is arbitrary */
+ stream_cursor_read r;
+ stream_cursor_write w;
+ stream_PSSD_state ss;
+ int status;
+
+ s_PSSD_init((stream_state *)&ss);
+ r.ptr = p; /* skip the '(' */
+ r.limit = end - 1;
+ w.limit = buf + sizeof(buf) - 1;
+ do {
+ /* One picky compiler complains if we initialize to buf - 1. */
+ w.ptr = buf; w.ptr--;
+ status = (*s_PSSD_template.process)
+ ((stream_state *) & ss, &r, &w, true);
+ }
+ while (status == 1);
+ *pscan = r.ptr + 1;
+ return 1;
+ }
+ case '<':
+ if (end - p < 2)
+ return_error(gs_error_syntaxerror);
+ if (p[1] != '<') {
+ /*
+ * We need the cast because some compilers declare memchar as
+ * returning a char * rather than a void *.
+ */
+ p = (const byte *)memchr(p + 1, '>', end - p - 1);
+ if (p == 0)
+ return_error(gs_error_syntaxerror);
+ }
+ goto m2;
+ case '>':
+ if (end - p < 2 || p[1] != '>')
+ return_error(gs_error_syntaxerror);
+m2: *pscan = p + 2;
+ return 1;
+ case '[': case ']': case '{': case '}':
+ *pscan = p + 1;
+ return 1;
+ case '/':
+ ++p;
+ default:
+ break;
+ }
+ while (p < end && scan_char_decoder[*p] <= ctype_name)
+ ++p;
+ *pscan = p;
+ if (p == *ptoken) /* no chars scanned, i.e., not ctype_name */
+ return_error(gs_error_syntaxerror);
+ return 1;
+}
+/*
+ * Scan a possibly composite token: arrays and dictionaries are treated as
+ * single tokens.
+ */
+int
+pdf_scan_token_composite(const byte **pscan, const byte * end,
+ const byte **ptoken_orig)
+{
+ int level = 0;
+ const byte *ignore_token;
+ const byte **ptoken = ptoken_orig;
+ int code;
+
+ do {
+ code = pdf_scan_token(pscan, end, ptoken);
+ if (code <= 0)
+ return (code < 0 || level == 0 ? code :
+ gs_note_error(gs_error_syntaxerror));
+ switch (**ptoken) {
+ case '<': case '[': case '{':
+ ++level; break;
+ case '>': case ']': case '}':
+ if (level == 0)
+ return_error(gs_error_syntaxerror);
+ --level; break;
+ }
+ ptoken = &ignore_token;
+ } while (level);
+ return code;
+}
+
+/* Replace object names with object references in a (parameter) string. */
+static const byte *
+pdfmark_next_object(const byte * scan, const byte * end, const byte **pname,
+ cos_object_t **ppco, gx_device_pdf * pdev)
+{
+ /*
+ * Starting at scan, find the next object reference, set *pname
+ * to point to it in the string, store the object at *ppco,
+ * and return a pointer to the first character beyond the
+ * reference. If there are no more object references, set
+ * *pname = end, *ppco = 0, and return end.
+ */
+ int code;
+
+ while ((code = pdf_scan_token(&scan, end, pname)) != 0) {
+ gs_param_string sname;
+
+ if (code < 0) {
+ ++scan;
+ continue;
+ }
+ if (**pname != '{')
+ continue;
+ /* Back up over the { and rescan as a single token. */
+ scan = *pname;
+ code = pdf_scan_token_composite(&scan, end, pname);
+ if (code < 0) {
+ ++scan;
+ continue;
+ }
+ sname.data = *pname;
+ sname.size = scan - sname.data;
+ /*
+ * Forward references are allowed. If there is an error,
+ * simply retain the name as a literal string.
+ */
+ code = pdf_refer_named(pdev, &sname, ppco);
+ if (code < 0)
+ continue;
+ return scan;
+ }
+ *ppco = 0;
+ return end;
+}
+int
+pdf_replace_names(gx_device_pdf * pdev, const gs_param_string * from,
+ gs_param_string * to)
+{
+ const byte *start = from->data;
+ const byte *end = start + from->size;
+ const byte *scan;
+ uint size = 0;
+ cos_object_t *pco;
+ bool any = false;
+ byte *sto;
+ char ref[1 + 10 + 5 + 1]; /* max obj number is 10 digits */
+
+ /* Do a first pass to compute the length of the result. */
+ for (scan = start; scan < end;) {
+ const byte *sname;
+ const byte *next =
+ pdfmark_next_object(scan, end, &sname, &pco, pdev);
+
+ size += sname - scan;
+ if (pco) {
+ gs_sprintf(ref, " %ld 0 R ", pco->id);
+ size += strlen(ref);
+ /* Special 'name' escaping convention (see gs_pdfwr.ps, /.pdf===dict
+ * the /nametype procedure). We do not want to write out the NULL
+ * characters, we'll remove them in pass 2, for now don't count
+ * them into the string size.
+ */
+ if (sname >= (start + 2) && sname[-1] == 0x00 && sname[-2] == 0x00 && next[0] == 0x00)
+ size -= 3;
+ }
+ scan = next;
+ any |= next != sname;
+ }
+ to->persistent = true; /* ??? */
+ if (!any) {
+ to->data = start;
+ to->size = size;
+ return 0;
+ }
+ sto = gs_alloc_bytes(pdev->pdf_memory, size, "pdf_replace_names");
+ if (sto == 0)
+ return_error(gs_error_VMerror);
+ to->data = sto;
+ to->size = size;
+ /* Do a second pass to do the actual substitutions. */
+ for (scan = start; scan < end;) {
+ const byte *sname;
+ const byte *next =
+ pdfmark_next_object(scan, end, &sname, &pco, pdev);
+ uint copy = sname - scan;
+ int rlen;
+
+ memcpy(sto, scan, copy);
+ sto += copy;
+ if (pco) {
+ gs_sprintf(ref, " %ld 0 R ", pco->id);
+ rlen = strlen(ref);
+ if (sname >= (start + 2) && sname[-1] == 0x00 && sname[-2] == 0x00 && next[0] == 0x00) {
+ sto -= 2;
+ next++;
+ }
+ memcpy(sto, ref, rlen);
+ sto += rlen;
+ }
+ scan = next;
+ }
+ return 0;
+}