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/gdevpdfm.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/gdevpdfm.c')
-rw-r--r--devices/vector/gdevpdfm.c2900
1 files changed, 2900 insertions, 0 deletions
diff --git a/devices/vector/gdevpdfm.c b/devices/vector/gdevpdfm.c
new file mode 100644
index 00000000..6e018aa4
--- /dev/null
+++ b/devices/vector/gdevpdfm.c
@@ -0,0 +1,2900 @@
+/* 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.
+*/
+
+
+/* pdfmark processing for PDF-writing driver */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gdevpdfx.h"
+#include "gdevpdfo.h"
+#include "szlibx.h"
+#include "slzwx.h"
+
+/* GC descriptors */
+private_st_pdf_article();
+
+/*
+ * The pdfmark pseudo-parameter indicates the occurrence of a pdfmark
+ * operator in the input file. Its "value" is the arguments of the operator,
+ * passed through essentially unchanged:
+ * (key, value)*, CTM, type
+ */
+
+/*
+ * Define an entry in a table of pdfmark-processing procedures.
+ * (The actual table is at the end of this file, to avoid the need for
+ * forward declarations for the procedures.)
+ */
+#define PDFMARK_NAMEABLE 1 /* allows _objdef */
+#define PDFMARK_ODD_OK 2 /* OK if odd # of parameters */
+#define PDFMARK_KEEP_NAME 4 /* don't substitute reference for name */
+ /* in 1st argument */
+#define PDFMARK_NO_REFS 8 /* don't substitute references for names */
+ /* anywhere */
+#define PDFMARK_TRUECTM 16 /* pass the true CTM to the procedure, */
+ /* not the one transformed to reflect the default user space */
+typedef struct pdfmark_name_s {
+ const char *mname;
+ pdfmark_proc((*proc));
+ byte options;
+} pdfmark_name;
+
+/* ---------------- Public utilities ---------------- */
+
+/* Compare a C string and a gs_param_string. */
+bool
+pdf_key_eq(const gs_param_string * pcs, const char *str)
+{
+ return (strlen(str) == pcs->size && pcs->data &&
+ !strncmp(str, (const char *)pcs->data, pcs->size));
+}
+
+/* Scan an integer out of a parameter string. */
+int
+pdfmark_scan_int(const gs_param_string * pstr, int *pvalue)
+{
+#define MAX_INT_STR 20
+ uint size = pstr->size;
+ char str[MAX_INT_STR + 1];
+
+ if (size > MAX_INT_STR)
+ return_error(gs_error_limitcheck);
+ memcpy(str, pstr->data, size);
+ str[size] = 0;
+ return (sscanf(str, "%d", pvalue) == 1 ? 0 :
+ gs_note_error(gs_error_rangecheck));
+#undef MAX_INT_STR
+}
+
+/* ---------------- Private utilities ---------------- */
+
+/* Find a key in a dictionary. */
+static bool
+pdfmark_find_key(const char *key, const gs_param_string * pairs, uint count,
+ gs_param_string * pstr)
+{
+ uint i;
+
+ for (i = 0; i < count; i += 2)
+ if (pdf_key_eq(&pairs[i], key)) {
+ *pstr = pairs[i + 1];
+ return true;
+ }
+ pstr->data = 0;
+ pstr->size = 0;
+ return false;
+}
+
+/*
+ * Get the page number for a page referenced by number or as /Next or /Prev.
+ * The result may be 0 if the page number is 0 or invalid.
+ */
+static int
+pdfmark_page_number(gx_device_pdf * pdev, const gs_param_string * pnstr)
+{
+ int page = pdev->next_page + 1;
+
+ if (pnstr->data == 0);
+ else if (pdf_key_eq(pnstr, "/Next"))
+ ++page;
+ else if (pdf_key_eq(pnstr, "/Prev"))
+ --page;
+ else if (pdfmark_scan_int(pnstr, &page) < 0)
+ page = 0;
+ return page;
+}
+
+/*
+ * This routine checks the page number is inside the valid range FirstPage->LastPage
+ * and if it is, and FirstPage is not 0, it updates the page number by rebasing it to
+ * the new FirstPage. If all conditions are met we also update the 'max_referred_page'
+ * which we check during closedown, and emit a warning if a reference should somehow
+ * point outside the valid range of pages in the document. This can happen if we reference
+ * a destination page that we haven't created yet, and when we get to the end of the
+ * job we discover we never did create it.
+ */
+static int
+update_max_page_reference(gx_device_pdf * pdev, int *page)
+{
+ if (*page < pdev->FirstPage || (pdev->LastPage != 0 && *page > pdev->LastPage)) {
+ emprintf1(pdev->memory, "Destination page %d lies outside the valid page range.\n", *page);
+ return -1;
+ }
+ else {
+ if (pdev->FirstPage != 0)
+ *page = (*page - pdev->FirstPage) + 1;
+
+ if (pdev->max_referred_page < *page)
+ pdev->max_referred_page = *page;
+ }
+ return 0;
+}
+
+/* Construct a destination string specified by /Page and/or /View. */
+/* Return 0 if none (but still fill in a default), 1 or 2 if present */
+/* (1 if only one of /Page or /View, 2 if both), <0 if error. */
+static int
+pdfmark_make_dest(char dstr[MAX_DEST_STRING], gx_device_pdf * pdev,
+ const char *Page_key, const char *View_key,
+ const gs_param_string * pairs, uint count, uint RequirePage)
+{
+ gs_param_string page_string, view_string;
+ int present =
+ pdfmark_find_key(Page_key, pairs, count, &page_string) +
+ pdfmark_find_key(View_key, pairs, count, &view_string);
+ int page=0;
+ gs_param_string action;
+ int len, code = 0;
+
+ if (present || RequirePage)
+ page = pdfmark_page_number(pdev, &page_string);
+
+ if (view_string.size == 0)
+ param_string_from_string(view_string, "[/XYZ null null null]");
+ if (page == 0)
+ strcpy(dstr, "[null ");
+ else if (pdfmark_find_key("/Action", pairs, count, &action) &&
+ pdf_key_eq(&action, "/GoToR")
+ )
+ gs_sprintf(dstr, "[%d ", page - 1);
+ else {
+ code = update_max_page_reference(pdev, &page);
+ if (code < 0)
+ return code;
+ gs_sprintf(dstr, "[%ld 0 R ", pdf_page_id(pdev, page));
+ }
+ len = strlen(dstr);
+ if (len + view_string.size > MAX_DEST_STRING)
+ return_error(gs_error_limitcheck);
+ if (view_string.data[0] != '[' ||
+ view_string.data[view_string.size - 1] != ']'
+ )
+ return_error(gs_error_rangecheck);
+ memcpy(dstr + len, view_string.data + 1, view_string.size - 1);
+ dstr[len + view_string.size - 1] = 0;
+ return present;
+}
+
+/*
+ * If a named destination is specified by a string, convert it to a name,
+ * update *dstr, and return 1; otherwise return 0.
+ */
+static int
+pdfmark_coerce_dest(gs_param_string *dstr, char dest[MAX_DEST_STRING])
+{
+ const byte *data = dstr->data;
+ uint size = dstr->size;
+ if (size == 0 || data[0] != '(')
+ return 0;
+ /****** HANDLE ESCAPES ******/
+ memcpy(dest, data, size - 1);
+ dest[0] = '/';
+ dest[size - 1] = 0;
+ dstr->data = (byte *)dest;
+ dstr->size = size - 1;
+ return 1;
+}
+
+/* Put pairs in a dictionary. */
+static int
+pdfmark_put_c_pair(cos_dict_t *pcd, const char *key,
+ const gs_param_string * pvalue)
+{
+ return cos_dict_put_c_key_string(pcd, key, pvalue->data, pvalue->size);
+}
+static int
+pdfmark_put_pair(cos_dict_t *pcd, const gs_param_string * pair)
+{
+ return cos_dict_put_string(pcd, pair->data, pair->size,
+ pair[1].data, pair[1].size);
+}
+
+/* Scan a Rect value. */
+static int
+pdfmark_scan_rect(gs_rect * prect, const gs_param_string * str,
+ const gs_matrix * pctm)
+{
+ uint size = str->size;
+ double v[4];
+#define MAX_RECT_STRING 100
+ char chars[MAX_RECT_STRING + 3];
+ int end_check;
+
+ if (str->size > MAX_RECT_STRING)
+ return_error(gs_error_limitcheck);
+ memcpy(chars, str->data, size);
+ strcpy(chars + size, " 0");
+ if (sscanf(chars, "[%lg %lg %lg %lg]%d",
+ &v[0], &v[1], &v[2], &v[3], &end_check) != 5
+ )
+ return_error(gs_error_rangecheck);
+ gs_point_transform(v[0], v[1], pctm, &prect->p);
+ gs_point_transform(v[2], v[3], pctm, &prect->q);
+ return 0;
+}
+
+/* Make a Rect value. */
+static void
+pdfmark_make_rect(char str[MAX_RECT_STRING], const gs_rect * prect)
+{
+ /*
+ * We have to use a stream and pprintf, rather than sprintf,
+ * because printf formats can't express the PDF restrictions on
+ * the form of the output.
+ */
+ stream s;
+
+ s_init(&s, NULL);
+ swrite_string(&s, (byte *)str, MAX_RECT_STRING - 1);
+ pprintg4(&s, "[%g %g %g %g]",
+ prect->p.x, prect->p.y, prect->q.x, prect->q.y);
+ str[stell(&s)] = 0;
+}
+
+#define MAX_BORDER_STRING 100
+/* Write a transformed Border value on a stream. */
+static int
+pdfmark_write_border(stream *s, const gs_param_string *str,
+ const gs_matrix *pctm)
+{
+ int i;
+
+ for (i = 0; i < str->size; i++)
+ stream_putc(s, str->data[i]);
+ return 0;
+}
+
+/* Put an element in a stream's dictionary. */
+static int
+cos_stream_put_c_strings(cos_stream_t *pcs, const char *key, const char *value)
+{
+ return cos_dict_put_c_strings(cos_stream_dict(pcs), key, value);
+}
+
+static int
+setup_pdfmark_stream_no_compression(gx_device_psdf *pdev0,
+ cos_stream_t *pco)
+{
+ /* This function is for pdfwrite only. */
+ gx_device_pdf *pdev = (gx_device_pdf *)pdev0;
+ gs_memory_t *mem = pdev->pdf_memory;
+
+ pco->input_strm = cos_write_stream_alloc(pco, pdev,
+ "setup_pdfmark_stream_compression");
+ if (pco->input_strm == 0)
+ return_error(gs_error_VMerror);
+ if (!pdev->binary_ok) {
+ stream_state *ss = s_alloc_state(mem, s_A85E_template.stype,
+ "setup_pdfmark_stream_compression");
+ if (ss == 0)
+ return_error(gs_error_VMerror);
+ if (s_add_filter(&pco->input_strm, &s_A85E_template, ss, mem) == 0) {
+ gs_free_object(mem, ss, "setup_image_compression");
+ return_error(gs_error_VMerror);
+ }
+ }
+ return 0;
+}
+
+/* Setup pdfmak stream compression. */
+static int
+setup_pdfmark_stream_compression(gx_device_psdf *pdev0,
+ cos_stream_t *pco)
+{
+ /* This function is for pdfwrite only. */
+ gx_device_pdf *pdev = (gx_device_pdf *)pdev0;
+ gs_memory_t *mem = pdev->pdf_memory;
+ static const pdf_filter_names_t fnames = {
+ PDF_FILTER_NAMES
+ };
+ const stream_template *templat =
+ (pdev->params.UseFlateCompression &&
+ pdev->version >= psdf_version_ll3 ?
+ &s_zlibE_template : &s_LZWE_template);
+ stream_state *st;
+
+ pco->input_strm = cos_write_stream_alloc(pco, pdev,
+ "setup_pdfmark_stream_compression");
+ if (pco->input_strm == 0)
+ return_error(gs_error_VMerror);
+ if (!pdev->binary_ok) {
+ stream_state *ss = s_alloc_state(mem, s_A85E_template.stype,
+ "setup_pdfmark_stream_compression");
+ if (ss == 0)
+ return_error(gs_error_VMerror);
+ if (s_add_filter(&pco->input_strm, &s_A85E_template, ss, mem) == 0) {
+ gs_free_object(mem, ss, "setup_image_compression");
+ return_error(gs_error_VMerror);
+ }
+ }
+ st = s_alloc_state(mem, templat->stype,
+ "setup_pdfmark_stream_compression");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ if (templat->set_defaults)
+ (*templat->set_defaults) (st);
+ if (s_add_filter(&pco->input_strm, templat, st, mem) == 0) {
+ gs_free_object(mem, st, "setup_image_compression");
+ return_error(gs_error_VMerror);
+ }
+ return pdf_put_filters(cos_stream_dict(pco), pdev, pco->input_strm, &fnames);
+}
+
+static int
+pdfmark_bind_named_object(gx_device_pdf *pdev, const gs_const_string *objname,
+ pdf_resource_t **pres)
+{
+ int code;
+
+ if (objname != NULL && objname->size) {
+ const cos_value_t *v = cos_dict_find(pdev->local_named_objects, objname->data, objname->size);
+
+ if (v != NULL) {
+ if (v->value_type == COS_VALUE_OBJECT) {
+ if (cos_type(v->contents.object) == &cos_generic_procs) {
+ /* The object was referred but not defined.
+ Use the old object id.
+ The old object stub to be dropped. */
+ pdf_reserve_object_id(pdev, *pres, v->contents.object->id);
+ } else if (!v->contents.object->written) {
+ /* We can't know whether the old object was referred or not.
+ Write it out for a consistent result in any case. */
+ code = cos_write_object(v->contents.object, pdev, resourceOther);
+
+ if (code < 0)
+ return code;
+ v->contents.object->written = true;
+ }
+ } else
+ return_error(gs_error_rangecheck); /* Must not happen. */
+ }
+ }
+ if ((*pres)->object->id == -1) {
+ if(objname != NULL && objname->size)
+ code = pdf_substitute_resource(pdev, pres, resourceXObject, NULL, false);
+ else
+ code = pdf_substitute_resource(pdev, pres, resourceXObject, NULL, true);
+ (*pres)->where_used |= pdev->used_mask;
+ if (code < 0)
+ return code;
+ } else {
+ /* Unfortunately we can't apply pdf_substitute_resource,
+ because the object may already be referred by its id.
+ Redundant objects may happen in this case.
+ For better results users should define objects before usage.
+ */
+ }
+ if (objname != NULL && objname->size) {
+ cos_value_t value;
+
+ code = cos_dict_put(pdev->local_named_objects, objname->data,
+ objname->size, cos_object_value(&value, (cos_object_t *)(*pres)->object));
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* ---------------- Miscellaneous pdfmarks ---------------- */
+
+/*
+ * Create the dictionary for an annotation or outline. For some
+ * unfathomable reason, PDF requires the following key substitutions
+ * relative to pdfmarks:
+ * In annotation and link dictionaries:
+ * /Action => /A, /Color => /C, /Title => /T
+ * In outline directionaries:
+ * /Action => /A, but *not* /Color or /Title
+ * In Action subdictionaries:
+ * /Dest => /D, /File => /F, /Subtype => /S
+ * and also the following substitutions:
+ * /Action /Launch /File xxx =>
+ * /A << /S /Launch /F xxx >>
+ * /Action /GoToR /File xxx /Dest yyy =>
+ * /A << /S /GoToR /F xxx /D yyy' >>
+ * /Action /Article /Dest yyy =>
+ * /A << /S /Thread /D yyy' >>
+ * /Action /GoTo => drop the Action key
+ * Also, \n in Contents strings must be replaced with \r.
+ * Also, an outline dictionary with no action, Dest, Page, or View has an
+ * implied GoTo action with Dest = [{ThisPage} /XYZ null null null].
+ * Note that for Thread actions, the Dest is not a real destination,
+ * and must not be processed as one.
+ *
+ * We always treat /A and /F as equivalent to /Action and /File
+ * respectively. The pdfmark and PDF documentation is so confused on the
+ * issue of when the long and short names should be used that we only give
+ * this a 50-50 chance of being right.
+ *
+ * Note that we must transform Rect and Border coordinates.
+ */
+
+typedef struct ao_params_s {
+ gx_device_pdf *pdev; /* for pdfmark_make_dest */
+ const char *subtype; /* default Subtype in top-level dictionary */
+ long src_pg; /* set to SrcPg - 1 if any */
+} ao_params_t;
+static int
+pdfmark_put_ao_pairs(gx_device_pdf * pdev, cos_dict_t *pcd,
+ const gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, ao_params_t * params,
+ bool for_outline)
+{
+ const gs_param_string *Action = 0;
+ const gs_param_string *File = 0;
+ const gs_param_string *URI = 0;
+ gs_param_string Dest;
+ gs_param_string Subtype;
+ uint i;
+ int code;
+ char dest[MAX_DEST_STRING];
+ bool coerce_dest = false;
+
+ Subtype.data = 0;
+ Subtype.size = 0;
+ Dest.data = 0;
+ Dest.size = 0;
+ if (params->subtype)
+ param_string_from_string(Subtype, params->subtype);
+ else
+ Subtype.data = 0;
+ for (i = 0; i < count; i += 2) {
+ const gs_param_string *pair = &pairs[i];
+ long src_pg;
+
+ if (pdf_key_eq(pair, "/SrcPg")){
+ unsigned char *buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, (pair[1].size + 1) * sizeof(unsigned char),
+ "pdf_xmp_write_translated");
+ memcpy(buf0, pair[1].data, pair[1].size);
+ buf0[pair[1].size] = 0x00;
+ if (sscanf((char *)buf0, "%ld", &src_pg) == 1)
+ params->src_pg = src_pg - 1;
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ }
+ else if (!for_outline && pdf_key_eq(pair, "/Color"))
+ pdfmark_put_c_pair(pcd, "/C", pair + 1);
+ else if (!for_outline && pdf_key_eq(pair, "/Title"))
+ pdfmark_put_c_pair(pcd, "/T", pair + 1);
+ else if (pdf_key_eq(pair, "/Action") || pdf_key_eq(pair, "/A"))
+ Action = pair;
+ /* Previously also catered for '/F', but at the top level (outside an
+ * Action dict which is handled below), a /F can only be the Flags for
+ * the annotation, not a File or JavaScript action.
+ */
+ else if (pdf_key_eq(pair, "/File") /* || pdf_key_eq(pair, "/F")*/)
+ File = pair;
+ else if (pdf_key_eq(pair, "/Dest")) {
+ Dest = pair[1];
+ coerce_dest = true;
+ }
+ else if (pdf_key_eq(pair, "/URI")) {
+ URI = pair; /* save it for placing into the Action dict */
+ }
+ else if (pdf_key_eq(pair, "/Page") || pdf_key_eq(pair, "/View")) {
+ /* Make a destination even if this is for an outline. */
+ if (Dest.data == 0) {
+ code = pdfmark_make_dest(dest, params->pdev, "/Page", "/View",
+ pairs, count, 0);
+ if (code >= 0) {
+ param_string_from_string(Dest, dest);
+ if (for_outline)
+ coerce_dest = false;
+ } else {
+ emprintf(pdev->memory, " **** Warning: Outline has invalid link that was discarded.\n");
+ return gs_note_error(gs_error_rangecheck);
+ }
+ }
+ } else if (pdf_key_eq(pair, "/Subtype"))
+ Subtype = pair[1];
+ /*
+ * We also have to replace all occurrences of \n in Contents
+ * strings with \r. Unfortunately, they probably have already
+ * been converted to \012....
+ */
+ else if (pdf_key_eq(pair, "/Contents")) {
+ byte *cstr;
+ uint csize = pair[1].size;
+ cos_value_t *pcv;
+ uint i, j;
+
+ /*
+ * Copy the string into value storage, then update it in place.
+ */
+ pdfmark_put_pair(pcd, pair);
+ /* Break const so we can update the (copied) string. */
+ pcv = (cos_value_t *)cos_dict_find_c_key(pcd, "/Contents");
+ if (pcv == NULL)
+ return_error(gs_error_ioerror); /* shouldn't be possible */
+ cstr = pcv->contents.chars.data;
+ /* Loop invariant: j <= i < csize. */
+ for (i = j = 0; i < csize;)
+ if (csize - i >= 2 && !memcmp(cstr + i, "\\n", 2) &&
+ (i == 0 || cstr[i - 1] != '\\')
+ ) {
+ cstr[j] = '\\', cstr[j + 1] = 'r';
+ i += 2, j += 2;
+ } else if (csize - i >= 4 && !memcmp(cstr + i, "\\012", 4) &&
+ (i == 0 || cstr[i - 1] != '\\')
+ ) {
+ cstr[j] = '\\', cstr[j + 1] = 'r';
+ i += 4, j += 2;
+ } else
+ cstr[j++] = cstr[i++];
+ if (j != i)
+ pcv->contents.chars.data =
+ gs_resize_string(pdev->pdf_memory, cstr, csize, j,
+ "pdfmark_put_ao_pairs");
+ } else if (pdf_key_eq(pair, "/Rect")) {
+ gs_rect rect;
+ char rstr[MAX_RECT_STRING];
+ int code = pdfmark_scan_rect(&rect, pair + 1, pctm);
+
+ if (code < 0)
+ return code;
+ pdfmark_make_rect(rstr, &rect);
+ cos_dict_put_c_key_string(pcd, "/Rect", (byte *)rstr,
+ strlen(rstr));
+ } else if (pdf_key_eq(pair, "/Border")) {
+ stream s;
+ char bstr[MAX_BORDER_STRING + 1];
+ int code;
+
+ s_init(&s, NULL);
+ swrite_string(&s, (byte *)bstr, MAX_BORDER_STRING + 1);
+ code = pdfmark_write_border(&s, pair + 1, pctm);
+ if (code < 0)
+ return code;
+ if (stell(&s) > MAX_BORDER_STRING)
+ return_error(gs_error_limitcheck);
+ bstr[stell(&s)] = 0;
+ cos_dict_put_c_key_string(pcd, "/Border", (byte *)bstr,
+ strlen(bstr));
+ } else if (for_outline && pdf_key_eq(pair, "/Count"))
+ DO_NOTHING;
+ else {
+ int i, j=0;
+ unsigned char *temp, *buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, pair[1].size * 2 * sizeof(unsigned char),
+ "pdf_xmp_write_translated");
+ if (buf0 == NULL)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pair[1].size; i++) {
+ byte c = pair[1].data[i];
+
+ buf0[j++] = c;
+ /* Acrobat doesn't like the 'short' escapes. and wants them as octal....
+ * I beliwvw this should be considered an Acrtobat bug, either escapes
+ * can be used, or not, we shouldn't have to force them to octal.
+ */
+ if (c == '\\') {
+ switch(pair[1].data[i + 1]) {
+ case 'b':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '0';
+ i++;
+ break;
+ case 'f':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '4';
+ i++;
+ break;
+ case 'n':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '2';
+ i++;
+ break;
+ case 'r':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '5';
+ i++;
+ break;
+ case 't':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '1';
+ i++;
+ break;
+ case '\\':
+ buf0[j++] = '1';
+ buf0[j++] = '3';
+ buf0[j++] = '4';
+ i++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ i = pair[1].size;
+ temp = (unsigned char *)pair[1].data;
+ ((gs_param_string *)pair)[1].data = buf0;
+ ((gs_param_string *)pair)[1].size = j;
+
+ pdfmark_put_pair(pcd, pair);
+
+ ((gs_param_string *)pair)[1].data = temp;
+ ((gs_param_string *)pair)[1].size = i;
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ }
+ }
+ if (!for_outline && pdf_key_eq(&Subtype, "/Link")) {
+ if (Action) {
+ /* Don't delete the Dest for GoTo or file-GoToR. */
+ if (pdf_key_eq(Action + 1, "/GoTo") ||
+ (File && pdf_key_eq(Action + 1, "/GoToR"))
+ )
+ DO_NOTHING;
+ else
+ Dest.data = 0;
+ }
+ }
+
+ /* Now handle the deferred keys. */
+ if (Action) {
+ const byte *astr = Action[1].data;
+ const uint asize = Action[1].size;
+
+ if ((File != 0 || Dest.data != 0 || URI != 0) &&
+ (pdf_key_eq(Action + 1, "/Launch") ||
+ (pdf_key_eq(Action + 1, "/GoToR") && File) ||
+ pdf_key_eq(Action + 1, "/Article"))
+ ) {
+ cos_dict_t *adict;
+ cos_value_t avalue;
+
+ if (pdf_key_eq(Action + 1, "/Launch") && pdev->PDFA != 0) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ /* Default behaviour matches Adobe Acrobat, warn and continue,
+ * output file will not be PDF/A compliant
+ */
+ case 0:
+ emprintf(pdev->memory,
+ "Launch annotations not permitted in PDF/A, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ /* Since the annotation would break PDF/A compatibility, do not
+ * include it, but warn the user that it has been dropped.
+ */
+ case 1:
+ emprintf(pdev->memory,
+ "Launch annotations not permitted in PDF/A, cannot drop annotation, aborting conversion\n");
+ return_error (gs_error_typecheck);
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Launch annotations not permitted in PDF/A, aborting conversion\n");
+ return_error (gs_error_typecheck);
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Launch annotations not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ }
+ }
+
+ adict = cos_dict_alloc(pdev, "action dict");
+ if (adict == 0)
+ return_error(gs_error_VMerror);
+ if (!for_outline) {
+ /* We aren't sure whether this is really needed.... */
+ code = cos_dict_put_c_strings(adict, "/Type", "/Action");
+ if (code < 0)
+ return code;
+ }
+ if (pdf_key_eq(Action + 1, "/Article")) {
+ code = cos_dict_put_c_strings(adict, "/S", "/Thread");
+ if (code < 0)
+ return code;
+ coerce_dest = false; /* Dest is not a real destination */
+ }
+ else
+ pdfmark_put_c_pair(adict, "/S", Action + 1);
+ if (Dest.data) {
+ if (coerce_dest)
+ pdfmark_coerce_dest(&Dest, dest);
+ pdfmark_put_c_pair(adict, "/D", &Dest);
+ Dest.data = 0; /* so we don't write it again */
+ }
+ if (File) {
+ pdfmark_put_c_pair(adict, "/F", File + 1);
+ File = 0; /* so we don't write it again */
+ }
+ if (URI) {
+ /* Adobe Distiller puts a /URI key from pdfmark into the */
+ /* Action dict with /S /URI as Subtype */
+ pdfmark_put_pair(adict, URI);
+ code = cos_dict_put_c_strings(adict, "/S", "/URI");
+ if (code < 0)
+ return code;
+ }
+ cos_dict_put(pcd, (const byte *)"/A", 2,
+ COS_OBJECT_VALUE(&avalue, adict));
+ } else if (asize >= 4 && !memcmp(astr, "<<", 2)) {
+ /* Replace occurrences of /Dest, /File, and /Subtype. */
+ const byte *scan = astr + 2;
+ const byte *end = astr + asize;
+ gs_param_string key, value;
+ cos_dict_t *adict = cos_dict_alloc(pdev, "action dict");
+ cos_value_t avalue;
+ int code = 0;
+
+ if (adict == 0)
+ return_error(gs_error_VMerror);
+ if (URI) {
+ /* Adobe Distiller puts a /URI key from pdfmark into the */
+ /* Action dict with /S /URI as Subtype */
+ pdfmark_put_pair(adict, URI);
+ if(cos_dict_put_c_strings(adict, "/S", "/URI") < 0)
+ return code;
+ }
+ while ((code = pdf_scan_token(&scan, end, &key.data)) > 0) {
+ key.size = scan - key.data;
+ if (key.data[0] != '/' ||
+ (code = pdf_scan_token_composite(&scan, end, &value.data)) != 1)
+ break;
+ value.size = scan - value.data;
+ if (pdev->PDFA != 0 && (pdf_key_eq(&key, "/Subtype") || pdf_key_eq(&key, "/S"))) {
+ if (pdf_key_eq(&value, "/Launch")) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ /* Default behaviour matches Adobe Acrobat, warn and continue,
+ * output file will not be PDF/A compliant
+ */
+ case 0:
+ emprintf(pdev->memory,
+ "Launch annotations not permitted in PDF/A, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ /* Since the annotation would break PDF/A compatibility, do not
+ * include it, but warn the user that it has been dropped.
+ */
+ case 1:
+ emprintf(pdev->memory,
+ "Launch annotations not permitted in PDF/A, cannot drop annotation, aborting conversion\n");
+ gs_free_object(pdev->memory->stable_memory, adict, "action dict");
+ return_error (gs_error_typecheck);
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Launch annotations not permitted in PDF/A, aborting conversion\n");
+ gs_free_object(pdev->memory->stable_memory, adict, "action dict");
+ return_error (gs_error_typecheck);
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Launch annotations not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ }
+ }
+ }
+ if (pdf_key_eq(&key, "/Dest") || pdf_key_eq(&key, "/D")) {
+ param_string_from_string(key, "/D");
+ if (value.data[0] == '(' && pdev->CompatibilityLevel < 1.2) {
+ int i;
+
+ for (i = 0;i < value.size; i++) {
+ if (value.data[i] == '\\') {
+ emprintf(pdev->memory,
+ "Link Destination contains characters which cannot be represented in a name.\nDestinations cannot be strings in versions prior to PDF 1.2. Annotation removed in the output.\n");
+ return gs_error_typecheck;
+ }
+ }
+ /****** FIXME: DETECT ESCAPES ******/
+ pdfmark_coerce_dest(&value, dest);
+ }
+ } else if (pdf_key_eq(&key, "/File"))
+ param_string_from_string(key, "/F");
+ else if (pdf_key_eq(&key, "/Subtype"))
+ param_string_from_string(key, "/S");
+ cos_dict_put_string(adict, key.data, key.size,
+ value.data, value.size);
+ }
+ if (code <= 0 || !pdf_key_eq(&key, ">>"))
+ return_error(gs_error_rangecheck);
+ cos_dict_put(pcd, (const byte *)"/A", 2,
+ COS_OBJECT_VALUE(&avalue, adict));
+ } else if (pdf_key_eq(Action + 1, "/GoTo"))
+ pdfmark_put_pair(pcd, Action);
+ else if (Action[1].size < 30) {
+ /* Hack: we could substitute names in pdfmark_process,
+ now should recognize whether it was done.
+ Not a perfect method though.
+ Go with it for a while. */
+ char buf[30];
+ int d0, d1;
+
+ memcpy(buf, Action[1].data, Action[1].size);
+ buf[Action[1].size] = 0;
+ if (sscanf(buf, "%d %d R", &d0, &d1) == 2)
+ pdfmark_put_pair(pcd, Action);
+ }
+ }
+ /*
+ * If we have /Dest or /File without the right kind of action,
+ * simply write it at the top level. This doesn't seem right,
+ * but I'm not sure what else to do.
+ */
+ if (Dest.data) {
+ if (coerce_dest)
+ pdfmark_coerce_dest(&Dest, dest);
+ /*
+ * For PDF 1.2 or better we write a Names tree, but in this case the names
+ * are required to be (counter-intuitively) strings, NOT name objects....
+ */
+ if (pdev->CompatibilityLevel > 1.1) {
+ gs_param_string DestString;
+ int i = 0, j;
+ char *D;
+
+ /*
+ * If the name has any 'unusual' characters, it is 'escaped' by starting with NULLs
+ * I suspect we no longer need that, but here we remove the escaping NULLs
+ */
+ if (Dest.size > 3 && Dest.data[0] == 0x00 && Dest.data[1] == 0x00 && Dest.data[Dest.size - 1] == 0x00) {
+ i = 2;
+ if (Dest.size > 5 && Dest.data[2] == 0x00 && Dest.data[3] == 0x00)
+ i += 2;
+ }
+
+ if (Dest.data[i] == '/') {
+ i++;
+
+ DestString.data = gs_alloc_bytes(pdev->memory->stable_memory, (Dest.size * 2) + 1, "DEST pdfmark temp string");
+ if (DestString.data == 0)
+ return_error(gs_error_VMerror);
+ DestString.size = (Dest.size * 2) + 1;
+ DestString.persistent = 0;
+ D = (char *)DestString.data;
+ D[0] = '(';
+ for (j=1;i<Dest.size;i++, j++) {
+ if (Dest.data[i] == '(' || Dest.data[i] == ')') {
+ D[j++] = '\\';
+ }
+ D[j] = Dest.data[i];
+ }
+ D[j++] = ')';
+ DestString.size = j;
+ pdfmark_put_c_pair(pcd, "/Dest", &DestString);
+ gs_free_object(pdev->memory->stable_memory, D, "DEST pdfmark temp string");
+ } else
+ pdfmark_put_c_pair(pcd, "/Dest", &Dest);
+ } else
+ pdfmark_put_c_pair(pcd, "/Dest", &Dest);
+ } else if (for_outline && !Action) {
+ /* Make an implicit destination. */
+ char dstr[1 + (sizeof(long) * 8 / 3 + 1) + 25 + 1];
+ long page_id = pdf_page_id(pdev, pdev->next_page + 1);
+
+ gs_sprintf(dstr, "[%ld 0 R /XYZ null null null]", page_id);
+ cos_dict_put_c_key_string(pcd, "/Dest", (const unsigned char*) dstr,
+ strlen(dstr));
+ }
+ if (File)
+ pdfmark_put_pair(pcd, File);
+ if (Subtype.data)
+ pdfmark_put_c_pair(pcd, "/Subtype", &Subtype);
+ return 0;
+}
+
+/* Copy an annotation dictionary. */
+static int
+pdfmark_annot(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string *objname,
+ const char *subtype)
+{
+ ao_params_t params;
+ cos_dict_t *pcd;
+ int page_index = pdev->next_page;
+ cos_array_t *annots;
+ cos_value_t value;
+ int code;
+
+ /* Annotations are only permitted in PDF/A if they have the
+ * Print flag enabled, so we need to prescan for that here.
+ */
+ if(pdev->PDFA != 0) {
+ int i, Flags = 0;
+ /* Check all the keys to see if we have a /F (Flags) key/value pair defined */
+ for (i = 0; i < count; i += 2) {
+ const gs_param_string *pair = &pairs[i];
+
+ if (pdf_key_eq(pair, "/F")) {
+ char Buffer[32];
+
+ pair = &pairs[i+1];
+ memcpy(Buffer, pair->data, pair->size);
+ Buffer[pair->size] = 0x00;
+ code = sscanf(Buffer, "%ld", (long *)&Flags);
+ if (code != 1)
+ emprintf(pdev->memory,
+ "Annotation has an invalid /Flags attribute\n");
+ break;
+ }
+ }
+ /* Check the Print flag, PDF/A annotations *must* be set to print */
+ if ((Flags & 4) == 0){
+ switch (pdev->PDFACompatibilityPolicy) {
+ /* Default behaviour matches Adobe Acrobat, warn and continue,
+ * output file will not be PDF/A compliant
+ */
+ case 0:
+ emprintf(pdev->memory,
+ "Annotation set to non-printing,\n not permitted in PDF/A, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ /* Since the annotation would break PDF/A compatibility, do not
+ * include it, but warn the user that it has been dropped.
+ */
+ case 1:
+ emprintf(pdev->memory,
+ "Annotation set to non-printing,\n not permitted in PDF/A, annotation will not be present in output file\n");
+ return 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Annotation set to non-printing,\n not permitted in PDF/A, aborting conversion\n");
+ return_error(gs_error_invalidfont);
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Annotation set to non-printing,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ }
+ }
+ }
+ if (pdev->PDFX != 0) {
+ gs_param_string Subtype;
+ bool discard = true;
+ pdf_page_t *page = 0L;
+
+ page = &pdev->pages[pdev->next_page];
+
+ if (subtype) {
+ param_string_from_string(Subtype, subtype);
+ if (pdf_key_eq(&Subtype, "/TrapNet") ||
+ pdf_key_eq(&Subtype, "/PrinterMark"))
+ discard = false;
+ }
+ if (discard) {
+ int i;
+ for (i = 0; i < count; i += 2) {
+ const gs_param_string *pair = &pairs[i];
+ if (pdf_key_eq(pair, "/Rect")) {
+ gs_rect rect;
+ const cos_value_t *v_trimbox, *v_bleedbox, *v_artbox, *v_cropbox;
+ const byte *p;
+ char buf[100];
+ int size, code;
+ float temp[4]; /* the type is float for sscanf. */
+ double pagebox[4] = {0, 0};
+
+ pagebox[2] = pdev->MediaSize[0];
+ pagebox[3] = pdev->MediaSize[1];
+
+ v_cropbox = v_bleedbox = v_trimbox = v_artbox = 0x0L;
+
+ code = pdfmark_scan_rect(&rect, pair + 1, pctm);
+
+ if (code < 0)
+ return code;
+
+ if (page && page->Page) {
+ v_trimbox = cos_dict_find_c_key(page->Page, "/TrimBox");
+ v_bleedbox = cos_dict_find_c_key(page->Page, "/BleedBox");
+ v_artbox = cos_dict_find_c_key(page->Page, "/ArtBox");
+ v_cropbox = cos_dict_find_c_key(page->Page, "/CropBox");
+ }
+
+ if (v_cropbox != NULL && v_cropbox->value_type == COS_VALUE_SCALAR) {
+ p = v_cropbox->contents.chars.data;
+ size = min (v_cropbox->contents.chars.size, sizeof(buf) - 1);
+ memcpy(buf, p, size);
+ buf[size] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
+ if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
+ }
+ }
+ if (v_bleedbox != NULL && v_bleedbox->value_type == COS_VALUE_SCALAR) {
+ p = v_bleedbox->contents.chars.data;
+ size = min (v_bleedbox->contents.chars.size, sizeof(buf) - 1);
+ memcpy(buf, p, size);
+ buf[size] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
+ if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
+ }
+ }
+ if (v_trimbox != NULL && v_trimbox->value_type == COS_VALUE_SCALAR) {
+ p = v_trimbox->contents.chars.data;
+ size = min (v_trimbox->contents.chars.size, sizeof(buf) - 1);
+ memcpy(buf, p, size);
+ buf[size] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
+ if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
+ }
+ }
+ if (v_artbox != NULL && v_artbox->value_type == COS_VALUE_SCALAR) {
+ p = v_artbox->contents.chars.data;
+ size = min (v_artbox->contents.chars.size, sizeof(buf) - 1);
+ memcpy(buf, p, size);
+ buf[size] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
+ if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
+ }
+ }
+ if (v_cropbox == 0 && v_trimbox == 0 && v_artbox == 0 && v_bleedbox == 0) {
+ if (pdev->PDFXTrimBoxToMediaBoxOffset.size >= 4 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[0] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[1] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[2] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[3] >= 0) {
+ pagebox[0] += pdev->PDFXTrimBoxToMediaBoxOffset.data[0];
+ pagebox[1] += pdev->PDFXTrimBoxToMediaBoxOffset.data[3];
+ pagebox[2] -= pdev->PDFXTrimBoxToMediaBoxOffset.data[1];
+ pagebox[3] -= pdev->PDFXTrimBoxToMediaBoxOffset.data[2];
+ } else if (pdev->PDFXBleedBoxToTrimBoxOffset.size >= 4 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[0] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[1] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[2] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[3] >= 0) {
+ pagebox[0] -= pdev->PDFXBleedBoxToTrimBoxOffset.data[0];
+ pagebox[1] -= pdev->PDFXBleedBoxToTrimBoxOffset.data[3];
+ pagebox[2] += pdev->PDFXBleedBoxToTrimBoxOffset.data[1];
+ pagebox[3] += pdev->PDFXBleedBoxToTrimBoxOffset.data[2];
+ }
+ }
+
+ if (rect.p.x > pagebox[2] || rect.q.x < pagebox[0] ||
+ rect.p.y > pagebox[3] || rect.q.y < pagebox[1])
+ break;
+ switch (pdev->PDFACompatibilityPolicy) {
+ /* Default behaviour matches Adobe Acrobat, warn and continue,
+ * output file will not be PDF/A compliant
+ */
+ case 0:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ /* Since the annotation would break PDF/A compatibility, do not
+ * include it, but warn the user that it has been dropped.
+ */
+ case 1:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, annotation will not be present in output file\n");
+ return 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, aborting conversion\n");
+ return_error(gs_error_invalidfont);
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Annotation s(not TrapNet or PrinterMark) on page,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ }
+ break;
+ }
+ }
+ if (i > count) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ /* Default behaviour matches Adobe Acrobat, warn and continue,
+ * output file will not be PDF/A compliant
+ */
+ case 0:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ /* Since the annotation would break PDF/A compatibility, do not
+ * include it, but warn the user that it has been dropped.
+ */
+ case 1:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, annotation will not be present in output file\n");
+ return 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, aborting conversion\n");
+ return_error(gs_error_invalidfont);
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Annotation s(not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ }
+ }
+ }
+ }
+ params.pdev = pdev;
+ params.subtype = subtype;
+ params.src_pg = -1;
+ code = pdf_make_named_dict(pdev, objname, &pcd, true);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_strings(pcd, "/Type", "/Annot");
+ if (code < 0)
+ return code;
+ code = pdfmark_put_ao_pairs(pdev, pcd, pairs, count, pctm, &params, false);
+ if (code < 0)
+ return code;
+ if (params.src_pg >= 0)
+ page_index = params.src_pg;
+ if (pdf_page_id(pdev, page_index + 1) <= 0)
+ return_error(gs_error_rangecheck);
+ annots = pdev->pages[page_index].Annots;
+ if (annots == 0) {
+ annots = cos_array_alloc(pdev, "pdfmark_annot");
+ if (annots == 0)
+ return_error(gs_error_VMerror);
+ pdev->pages[page_index].Annots = annots;
+ }
+ if (!objname) {
+ /* Write the annotation now. */
+ COS_WRITE_OBJECT(pcd, pdev, resourceAnnotation);
+ COS_RELEASE(pcd, "pdfmark_annot");
+ }
+ return cos_array_add(annots,
+ cos_object_value(&value, COS_OBJECT(pcd)));
+}
+
+/* ANN pdfmark */
+static int
+pdfmark_ANN(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Text");
+}
+
+/* LNK pdfmark (obsolescent) */
+static int
+pdfmark_LNK(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Link");
+}
+
+/* Write and release one node of the outline tree. */
+static int
+pdfmark_write_outline(gx_device_pdf * pdev, pdf_outline_node_t * pnode,
+ long next_id)
+{
+ stream *s;
+ int code = 0;
+
+ pdf_open_separate(pdev, pnode->id, resourceOutline);
+ if (pnode->action != NULL)
+ pnode->action->id = pnode->id;
+ else {
+ emprintf1(pdev->memory,
+ "pdfmark error: Outline node %ld has no action or destination.\n",
+ pnode->id);
+ code = gs_note_error(gs_error_undefined);
+ }
+ s = pdev->strm;
+ stream_puts(s, "<< ");
+ if (pnode->action != NULL)
+ cos_dict_elements_write(pnode->action, pdev);
+ if (pnode->count)
+ pprintd1(s, "/Count %d ", pnode->count);
+ pprintld1(s, "/Parent %ld 0 R\n", pnode->parent_id);
+ if (pnode->prev_id)
+ pprintld1(s, "/Prev %ld 0 R\n", pnode->prev_id);
+ if (next_id)
+ pprintld1(s, "/Next %ld 0 R\n", next_id);
+ if (pnode->first_id)
+ pprintld2(s, "/First %ld 0 R /Last %ld 0 R\n",
+ pnode->first_id, pnode->last_id);
+ stream_puts(s, ">>\n");
+ pdf_end_separate(pdev, resourceOutline);
+ if (pnode->action != NULL)
+ COS_FREE(pnode->action, "pdfmark_write_outline");
+ pnode->action = 0;
+ return code;
+}
+
+/* Adjust the parent's count when writing an outline node. */
+static void
+pdfmark_adjust_parent_count(pdf_outline_level_t * plevel)
+{
+ pdf_outline_level_t *parent = plevel - 1;
+ int count = plevel->last.count;
+
+ if (count > 0) {
+ if (parent->last.count < 0)
+ parent->last.count -= count;
+ else
+ parent->last.count += count;
+ }
+}
+
+/*
+ * Close the current level of the outline tree. Note that if we are at
+ * the end of the document, some of the levels may be incomplete if the
+ * Count values were incorrect.
+ */
+int
+pdfmark_close_outline(gx_device_pdf * pdev)
+{
+ int depth = pdev->outline_depth;
+ pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
+ int code = 0;
+
+ if (plevel->last.id) { /* check for incomplete tree */
+ code = pdfmark_write_outline(pdev, &plevel->last, 0);
+ }
+ if (depth > 0) {
+ plevel[-1].last.last_id = plevel->last.id;
+ pdfmark_adjust_parent_count(plevel);
+ --plevel;
+ if (plevel->last.count < 0)
+ pdev->closed_outline_depth--;
+ pdev->outline_depth--;
+ }
+ return code;
+}
+
+/* OUT pdfmark */
+static int
+pdfmark_OUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ int depth = pdev->outline_depth;
+ pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
+ int sub_count = 0;
+ uint i;
+ pdf_outline_node_t node;
+ ao_params_t ao;
+ int code;
+
+ for (i = 0; i < count; i += 2) {
+ const gs_param_string *pair = &pairs[i];
+
+ if (pdf_key_eq(pair, "/Count"))
+ pdfmark_scan_int(pair + 1, &sub_count);
+ }
+ if (sub_count != 0 && depth == pdev->max_outline_depth - 1) {
+ pdf_outline_level_t *new_ptr;
+
+ new_ptr = (pdf_outline_level_t *)gs_alloc_bytes(pdev->pdf_memory, (pdev->max_outline_depth + INITIAL_MAX_OUTLINE_DEPTH) * sizeof(pdf_outline_level_t) * sizeof(pdf_outline_level_t), "outline_levels array");
+ if (!new_ptr)
+ return_error(gs_error_VMerror);
+ memcpy (new_ptr, pdev->outline_levels, pdev->max_outline_depth * sizeof(pdf_outline_level_t));
+ gs_free_object(pdev->pdf_memory, pdev->outline_levels, "outline_levels array");
+ pdev->outline_levels = new_ptr;
+ pdev->max_outline_depth += INITIAL_MAX_OUTLINE_DEPTH;
+ plevel = &pdev->outline_levels[depth];
+ }
+ node.action = cos_dict_alloc(pdev, "pdfmark_OUT");
+ if (node.action == 0)
+ return_error(gs_error_VMerror);
+ ao.pdev = pdev;
+ ao.subtype = 0;
+ ao.src_pg = -1;
+ code = pdfmark_put_ao_pairs(pdev, node.action, pairs, count, pctm, &ao,
+ true);
+ if (code < 0)
+ return code;
+ if (pdev->outlines_id == 0)
+ pdev->outlines_id = pdf_obj_ref(pdev);
+ node.id = pdf_obj_ref(pdev);
+ node.parent_id =
+ (depth == 0 ? pdev->outlines_id : plevel[-1].last.id);
+ node.prev_id = plevel->last.id;
+ node.first_id = node.last_id = 0;
+ node.count = sub_count;
+ /* Add this node to the outline at the current level. */
+ if (plevel->first.id == 0) { /* First node at this level. */
+ if (depth > 0)
+ plevel[-1].last.first_id = node.id;
+ node.prev_id = 0;
+ plevel->first = node;
+ plevel->first.action = 0; /* never used */
+ } else { /* Write the previous node. */
+ if (depth > 0)
+ pdfmark_adjust_parent_count(plevel);
+ pdfmark_write_outline(pdev, &plevel->last, node.id);
+ }
+ plevel->last = node;
+ plevel->left--;
+ if (!pdev->closed_outline_depth)
+ pdev->outlines_open++;
+ /* If this node has sub-nodes, descend one level. */
+ if (sub_count != 0) {
+ pdev->outline_depth++;
+ ++plevel;
+ plevel->left = (sub_count > 0 ? sub_count : -sub_count);
+ plevel->first.id = 0;
+ plevel->last.count = plevel->last.id = 0;
+ plevel->first.action = plevel->last.action = 0; /* for GC */
+ if (sub_count < 0)
+ pdev->closed_outline_depth++;
+ } else {
+ while ((depth = pdev->outline_depth) > 0 &&
+ pdev->outline_levels[depth].left == 0
+ )
+ pdfmark_close_outline(pdev);
+ }
+ return 0;
+}
+
+/* Write an article bead. */
+static int
+pdfmark_write_bead(gx_device_pdf * pdev, const pdf_bead_t * pbead)
+{
+ stream *s;
+ char rstr[MAX_RECT_STRING];
+
+ pdf_open_separate(pdev, pbead->id, resourceArticle);
+ s = pdev->strm;
+ pprintld3(s, "<</T %ld 0 R/V %ld 0 R/N %ld 0 R",
+ pbead->article_id, pbead->prev_id, pbead->next_id);
+ if (pbead->page_id != 0)
+ pprintld1(s, "/P %ld 0 R", pbead->page_id);
+ pdfmark_make_rect(rstr, &pbead->rect);
+ pprints1(s, "/R%s>>\n", rstr);
+ return pdf_end_separate(pdev, resourceArticle);
+}
+
+/* Finish writing an article, and release its data. */
+int
+pdfmark_write_article(gx_device_pdf * pdev, const pdf_article_t * part)
+{
+ pdf_article_t art;
+ stream *s;
+
+ art = *part;
+ if (art.last.id == 0) {
+ /* Only one bead in the article. */
+ art.first.prev_id = art.first.next_id = art.first.id;
+ } else {
+ /* More than one bead in the article. */
+ art.first.prev_id = art.last.id;
+ art.last.next_id = art.first.id;
+ pdfmark_write_bead(pdev, &art.last);
+ }
+ pdfmark_write_bead(pdev, &art.first);
+ pdf_open_separate(pdev, art.contents->id, resourceArticle);
+ s = pdev->strm;
+ pprintld1(s, "<</F %ld 0 R/I<<", art.first.id);
+ cos_dict_elements_write(art.contents, pdev);
+ stream_puts(s, ">> >>\n");
+ return pdf_end_separate(pdev, resourceArticle);
+}
+
+/* ARTICLE pdfmark */
+static int
+pdfmark_ARTICLE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ gs_param_string title;
+ gs_param_string rectstr;
+ gs_rect rect;
+ long bead_id;
+ pdf_article_t *part;
+ int code;
+
+ if (!pdfmark_find_key("/Title", pairs, count, &title) ||
+ !pdfmark_find_key("/Rect", pairs, count, &rectstr)
+ )
+ return_error(gs_error_rangecheck);
+ if ((code = pdfmark_scan_rect(&rect, &rectstr, pctm)) < 0)
+ return code;
+ bead_id = pdf_obj_ref(pdev);
+
+ /* Find the article with this title, or create one. */
+ for (part = pdev->articles; part != 0; part = part->next) {
+ const cos_value_t *a_title =
+ cos_dict_find_c_key(part->contents, "/Title");
+
+ if (a_title != 0 && !COS_VALUE_IS_OBJECT(a_title) &&
+ !bytes_compare(a_title->contents.chars.data,
+ a_title->contents.chars.size,
+ title.data, title.size))
+ break;
+ }
+ if (part == 0) { /* Create the article. */
+ cos_dict_t *contents =
+ cos_dict_alloc(pdev, "pdfmark_ARTICLE(contents)");
+
+ if (contents == 0)
+ return_error(gs_error_VMerror);
+ part = gs_alloc_struct(mem, pdf_article_t, &st_pdf_article,
+ "pdfmark_ARTICLE(article)");
+ if (part == 0 || contents == 0) {
+ gs_free_object(mem, part, "pdfmark_ARTICLE(article)");
+ if (contents)
+ COS_FREE(contents, "pdfmark_ARTICLE(contents)");
+ return_error(gs_error_VMerror);
+ }
+ contents->id = pdf_obj_ref(pdev);
+ part->next = pdev->articles;
+ pdev->articles = part;
+ cos_dict_put_string(contents, (const byte *)"/Title", 6,
+ title.data, title.size);
+ part->first.id = part->last.id = 0;
+ part->contents = contents;
+ }
+ /*
+ * Add the bead to the article. This is similar to what we do for
+ * outline nodes, except that articles have only a page number and
+ * not View/Dest.
+ */
+ if (part->last.id == 0) {
+ part->first.next_id = bead_id;
+ part->last.id = part->first.id;
+ } else {
+ part->last.next_id = bead_id;
+ pdfmark_write_bead(pdev, &part->last);
+ }
+ part->last.prev_id = part->last.id;
+ part->last.id = bead_id;
+ part->last.article_id = part->contents->id;
+ part->last.next_id = 0;
+ part->last.rect = rect;
+ {
+ gs_param_string page_string;
+ int page = 0;
+ uint i;
+
+ pdfmark_find_key("/Page", pairs, count, &page_string);
+ page = pdfmark_page_number(pdev, &page_string);
+ code = update_max_page_reference(pdev, &page);
+ if (code < 0)
+ return code;
+ part->last.page_id = pdf_page_id(pdev, page);
+ for (i = 0; i < count; i += 2) {
+ if (pdf_key_eq(&pairs[i], "/Rect") || pdf_key_eq(&pairs[i], "/Page"))
+ continue;
+ pdfmark_put_pair(part->contents, &pairs[i]);
+ }
+ }
+ if (part->first.id == 0) { /* This is the first bead of the article. */
+ part->first = part->last;
+ part->last.id = 0;
+ }
+ return 0;
+}
+
+/* EMBED pdfmark */
+static int
+pdfmark_EMBED(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ gs_param_string key;
+ int i;
+
+ if (pdev->CompatibilityLevel < 1.4)
+ return_error(gs_error_undefined);
+ if (pdev->PDFA > 0 && pdev->PDFA < 2) {
+ switch(pdev->PDFACompatibilityPolicy) {
+ default:
+ case 0:
+ emprintf(pdev->memory,
+ "The PDF/A-1 specifcation prohibits the embedding of files, reverting to normal PDF output.\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ return 0;
+ case 1:
+ emprintf(pdev->memory,
+ "The PDF/A-1 specifcation prohibits the embedding of files, pdfamrk operatoin ignored.\n");
+ break;
+ case 2:
+ return_error(gs_error_undefined);
+ }
+ }
+ if (pdev->PDFA > 0 && pdev->PDFA < 3) {
+ emprintf(pdev->memory,
+ "The PDF/A-2 specifcation only permits the embedding of PDF/A-1 or PDF/A-2 files.\n");
+ emprintf(pdev->memory,
+ "The pdfwrite device has not validated this embedded file, output may not conform to PDF/A-2.\n");
+ }
+ if (!pdfmark_find_key("/FS", pairs, count, &key))
+ return_error(gs_error_rangecheck);
+ if (!pdfmark_find_key("/Name", pairs, count, &key))
+ return_error(gs_error_rangecheck);
+ if (!pdev->EmbeddedFiles) {
+ pdev->EmbeddedFiles = cos_dict_alloc(pdev, "pdfmark_EMBED(EmbeddedFiles)");
+ if (pdev->EmbeddedFiles == 0)
+ return_error(gs_error_VMerror);
+ pdev->EmbeddedFiles->id = pdf_obj_ref(pdev);
+ }
+
+ for (i = 0; i < count; i += 2) {
+ if (pdf_key_eq(&pairs[i], "/FS")) {
+ return cos_dict_put_string(pdev->EmbeddedFiles, key.data, key.size,
+ pairs[i+1].data, pairs[i+1].size);
+ }
+ }
+ return 0;
+}
+
+/* DEST pdfmark */
+static int
+pdfmark_DEST(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ int present;
+ char dest[MAX_DEST_STRING];
+ gs_param_string key;
+ cos_value_t value;
+ cos_dict_t *ddict;
+ int i, code;
+
+ if (!pdfmark_find_key("/Dest", pairs, count, &key) ||
+ (present =
+ pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count, 1)) < 0
+ )
+ return_error(gs_error_rangecheck);
+ cos_string_value(&value, (byte *)dest, strlen(dest));
+ if (!pdev->Dests) {
+ pdev->Dests = cos_dict_alloc(pdev, "pdfmark_DEST(Dests)");
+ if (pdev->Dests == 0)
+ return_error(gs_error_VMerror);
+ pdev->Dests->id = pdf_obj_ref(pdev);
+ }
+
+ /*
+ * Create the destination as a dictionary with a D key.
+ */
+ code = pdf_make_named_dict(pdev, objname, &ddict, false);
+ ddict->id = pdf_obj_ref(pdev);
+
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string(ddict, "/D", (byte *)dest,
+ strlen(dest));
+ for (i = 0; code >= 0 && i < count; i += 2)
+ if (!pdf_key_eq(&pairs[i], "/Dest") &&
+ !pdf_key_eq(&pairs[i], "/Page") &&
+ !pdf_key_eq(&pairs[i], "/View")
+ )
+ code = pdfmark_put_pair(ddict, &pairs[i]);
+ if (code < 0)
+ return code;
+ COS_WRITE_OBJECT(ddict, pdev, resourceOther);
+ COS_OBJECT_VALUE(&value, ddict);
+ COS_RELEASE(ddict, "pdfmark_DEST(Dests dict)");
+
+ return cos_dict_put(pdev->Dests, key.data, key.size, &value);
+}
+
+/* Check that pass-through PostScript code is a string. */
+static bool
+ps_source_ok(const gs_memory_t *mem, const gs_param_string * psource)
+{
+ if (psource->size >= 2 && psource->data[0] == '(' &&
+ psource->data[psource->size - 1] == ')'
+ )
+ return true;
+ else {
+ int i;
+ lprintf("bad PS passthrough: ");
+ for (i=0; i<psource->size; i++)
+ errprintf(mem, "%c", psource->data[i]);
+ errprintf(mem, "\n");
+ return false;
+ }
+}
+
+/* Write the contents of pass-through PostScript code. */
+/* Return the size written on the file. */
+static uint
+pdfmark_write_ps(stream *s, const gs_param_string * psource)
+{
+ /****** REMOVE ESCAPES WITH PSSDecode, SEE gdevpdfr p. 2 ******/
+ uint size = psource->size - 2;
+
+ stream_write(s, psource->data + 1, size);
+ stream_putc(s, '\n');
+ return size + 1;
+}
+
+/* Start a XObject. */
+static int
+start_XObject(gx_device_pdf * pdev, bool compress, cos_stream_t **ppcs)
+{ pdf_resource_t *pres;
+ cos_stream_t *pcs;
+ int code;
+
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, false,
+ pdev->CompressStreams);
+ if (code < 0)
+ return code;
+ pdev->accumulating_a_global_object = true;
+ pcs = (cos_stream_t *)pres->object;
+ pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject");
+ if (!pdev->substream_Resources)
+ return_error(gs_error_VMerror);
+ if (pdev->ForOPDFRead) {
+ code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
+ if (code < 0)
+ return code;
+ }
+ pres->named = true;
+ pres->where_used = 0; /* initially not used */
+ pcs->pres = pres;
+ *ppcs = pcs;
+ return 0;
+}
+
+/* PS pdfmark */
+#define MAX_PS_INLINE 100
+static int
+pdfmark_PS(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ gs_param_string source;
+ gs_param_string level1;
+
+ if (!pdfmark_find_key("/DataSource", pairs, count, &source) ||
+ !ps_source_ok(pdev->memory, &source) ||
+ (pdfmark_find_key("/Level1", pairs, count, &level1) &&
+ !ps_source_ok(pdev->memory, &level1))
+ )
+ return_error(gs_error_rangecheck);
+ if (level1.data == 0 && source.size <= MAX_PS_INLINE && objname == 0) {
+ /* Insert the PostScript code in-line */
+ int code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ stream *s;
+
+ if (code < 0)
+ return code;
+ s = pdev->strm;
+ stream_write(s, source.data, source.size);
+ stream_puts(s, " PS\n");
+ } else {
+ /* Put the PostScript code in a resource. */
+ cos_stream_t *pcs;
+ int code;
+ gs_id level1_id = gs_no_id;
+ pdf_resource_t *pres;
+
+ if (level1.data != 0) {
+ pdf_resource_t *pres;
+
+ code = pdf_enter_substream(pdev,
+ resourceXObject,
+ gs_no_id, &pres, true,
+ pdev->CompressStreams);
+ if (code < 0)
+ return code;
+ pcs = (cos_stream_t *)pres->object;
+ if (pdev->ForOPDFRead && objname != 0) {
+ code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
+ if (code < 0)
+ return code;
+ }
+ pres->named = (objname != 0);
+ pres->where_used = 0;
+ pcs->pres = pres;
+ DISCARD(pdfmark_write_ps(pdev->strm, &level1));
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ code = cos_write_object(pres->object, pdev, resourceOther);
+ if (code < 0)
+ return code;
+ level1_id = pres->object->id;
+ }
+ code = start_XObject(pdev, pdev->params.CompressPages, &pcs);
+ if (code < 0)
+ return code;
+ pres = pdev->accumulating_substream_resource;
+ code = cos_stream_put_c_strings(pcs, "/Type", "/XObject");
+ if (code < 0)
+ return code;
+ code = cos_stream_put_c_strings(pcs, "/Subtype", "/PS");
+ if (code < 0)
+ return code;
+ if (level1_id != gs_no_id) {
+ char r[MAX_DEST_STRING];
+
+ gs_sprintf(r, "%ld 0 R", level1_id);
+ code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Level1",
+ (byte *)r, strlen(r));
+ if (code < 0)
+ return code;
+ }
+ DISCARD(pdfmark_write_ps(pdev->strm, &source));
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ { gs_const_string objname1, *pon = NULL;
+
+ if (objname != NULL) {
+ objname1.data = objname->data;
+ objname1.size = objname->size;
+ pon = &objname1;
+ }
+ code = pdfmark_bind_named_object(pdev, pon, &pres);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ pcs->pres->where_used |= pdev->used_mask;
+ pprintld1(pdev->strm, "/R%ld Do\n", pcs->id);
+ }
+ return 0;
+}
+
+/* Common code for pdfmarks that do PUT into a specific object. */
+static int
+pdfmark_put_pairs(cos_dict_t *pcd, gs_param_string * pairs, uint count)
+{
+ int code = 0, i;
+
+ if (count & 1)
+ return_error(gs_error_rangecheck);
+ for (i = 0; code >= 0 && i < count; i += 2)
+ code = pdfmark_put_pair(pcd, pairs + i);
+ return code;
+}
+
+/* PAGES pdfmark */
+static int
+pdfmark_PAGES(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ return pdfmark_put_pairs(pdev->Pages, pairs, count);
+}
+
+/* PAGE pdfmark */
+static int
+pdfmark_PAGE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ return pdfmark_put_pairs(pdf_current_page_dict(pdev), pairs, count);
+}
+
+/* Add a page label for the current page. The last label on a page
+ * overrides all previous labels for this page. Unlabeled pages will get
+ * empty page labels. label == NULL flushes the last label */
+static int
+pdfmark_add_pagelabel(gx_device_pdf * pdev, const gs_param_string *label)
+{
+ cos_value_t value;
+ cos_dict_t *dict = 0;
+ int code = 0;
+
+ /* create label dict (and page label array if not present yet) */
+ if (label != 0) {
+ if (!pdev->PageLabels) {
+ pdev->PageLabels = cos_array_alloc(pdev,
+ "pdfmark_add_pagelabel(PageLabels)");
+ if (pdev->PageLabels == 0)
+ return_error(gs_error_VMerror);
+ pdev->PageLabels->id = pdf_obj_ref(pdev);
+
+ /* empty label for unlabled pages before first labled page */
+ pdev->PageLabels_current_page = 0;
+ pdev->PageLabels_current_label = cos_dict_alloc(pdev,
+ "pdfmark_add_pagelabel(first)");
+ if (pdev->PageLabels_current_label == 0)
+ return_error(gs_error_VMerror);
+ }
+
+ dict = cos_dict_alloc(pdev, "pdfmark_add_pagelabel(dict)");
+ if (dict == 0)
+ return_error(gs_error_VMerror);
+
+ code = cos_dict_put_c_key(dict, "/P", cos_string_value(&value,
+ label->data, label->size));
+ if (code < 0) {
+ COS_FREE(dict, "pdfmark_add_pagelabel(dict)");
+ return code;
+ }
+ }
+
+ /* flush current label */
+ if (label == 0 || pdev->next_page != pdev->PageLabels_current_page) {
+ /* handle current label */
+ if (pdev->PageLabels_current_label) {
+ if (code >= 0) {
+ code = cos_array_add_int(pdev->PageLabels,
+ pdev->PageLabels_current_page);
+ if (code >= 0)
+ code = cos_array_add(pdev->PageLabels,
+ COS_OBJECT_VALUE(&value,
+ pdev->PageLabels_current_label));
+ }
+ pdev->PageLabels_current_label = 0;
+ }
+
+ /* handle unlabled pages between current labeled page and
+ * next labeled page */
+ if (pdev->PageLabels) {
+ if (pdev->next_page - pdev->PageLabels_current_page > 1) {
+ cos_dict_t *tmp = cos_dict_alloc(pdev,
+ "pdfmark_add_pagelabel(tmp)");
+ if (tmp == 0)
+ return_error(gs_error_VMerror);
+
+ code = cos_array_add_int(pdev->PageLabels,
+ pdev->PageLabels_current_page + 1);
+ if (code >= 0)
+ code = cos_array_add(pdev->PageLabels,
+ COS_OBJECT_VALUE(&value, tmp));
+ }
+ }
+ }
+
+ /* new current label */
+ if (pdev->PageLabels_current_label)
+ COS_FREE(pdev->PageLabels_current_label,
+ "pdfmark_add_pagelabel(current_label)");
+ pdev->PageLabels_current_label = dict;
+ pdev->PageLabels_current_page = pdev->next_page;
+
+ return code;
+}
+
+/* Close the pagelabel numtree.*/
+int
+pdfmark_end_pagelabels(gx_device_pdf * pdev)
+{
+ return pdfmark_add_pagelabel(pdev, 0);
+}
+
+/* [ /Label string /PlateColor string pdfmark */
+/* FIXME: /PlateColor is ignored */
+static int
+pdfmark_PAGELABEL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ gs_param_string key;
+
+ if (pdev->CompatibilityLevel >= 1.3) {
+ if (pdfmark_find_key("/Label", pairs, count, &key)) {
+ return pdfmark_add_pagelabel(pdev, &key);
+ }
+ }
+ return 0;
+}
+
+/* DOCINFO pdfmark */
+static int
+pdfmark_DOCINFO(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ /*
+ * We could use pdfmark_put_pairs(pdev->Info, pairs, count), except
+ * that we want to replace "Distiller" with our own name as the
+ * Producer.
+ */
+ cos_dict_t *const pcd = pdev->Info;
+ int code = 0, i;
+
+ if (count & 1)
+ return_error(gs_error_rangecheck);
+ for (i = 0; code >= 0 && i < count; i += 2) {
+ const gs_param_string *pair = pairs + i;
+
+ if (pdev->CompatibilityLevel >= 2.0) {
+ if (!pdf_key_eq(pairs + i, "/ModDate") && !pdf_key_eq(pairs + i, "/CreationDate"))
+ continue;
+ }
+
+ if (pdev->PDFA !=0) {
+ const gs_param_string *p = pairs + i + 1;
+ if (p->size > 9 && memcmp(p->data, "(\\376\\377", 9) == 0) {
+ /* Can't handle UTF16BE in PDF/A1, so abort this pair or abort PDF/A or just abort,
+ * depending on PDFACompatibilityPolicy
+ */
+ switch (pdev->PDFACompatibilityPolicy) {
+ case 0:
+ emprintf(pdev->memory,
+ "UTF16BE text string detected in DOCINFO cannot be represented in XMP for PDF/A1, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ case 1:
+ emprintf(pdev->memory,
+ "UTF16BE text string detected in DOCINFO cannot be represented in XMP for PDF/A1, discarding DOCINFO\n");
+ continue;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "UTF16BE text string detected in DOCINFO cannot be represented in XMP for PDF/A1, aborting conversion.\n");
+ /* If we don't return a fatal error then zputdeviceparams simply ignores it (!) */
+ return_error(gs_error_Fatal);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (pdf_key_eq(pairs + i, "/Producer")) {
+ string_match_params params;
+ params = string_match_params_default;
+ params.ignore_case = true;
+
+ if (!string_match((const byte *)GS_PRODUCTFAMILY, strlen(GS_PRODUCTFAMILY), (const byte *)"GPL Ghostscript", 15, &params))
+ code = pdfmark_put_pair(pcd, pair);
+ } else
+ code = pdfmark_put_pair(pcd, pair);
+ if (code < 0)
+ break;
+ }
+ return code;
+}
+
+/* DOCVIEW pdfmark */
+static int
+pdfmark_DOCVIEW(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ char dest[MAX_DEST_STRING];
+ int code = 0;
+
+ if (count & 1)
+ return_error(gs_error_rangecheck);
+ code = pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count, 0);
+ if (code < 0)
+ return gs_note_error(gs_error_rangecheck);
+
+ if (code > 0) {
+ int i;
+
+ code = cos_dict_put_c_key_string(pdev->Catalog, "/OpenAction",
+ (byte *)dest, strlen(dest));
+ for (i = 0; code >= 0 && i < count; i += 2)
+ if (!(pdf_key_eq(&pairs[i], "/Page") ||
+ pdf_key_eq(&pairs[i], "/View"))
+ )
+ code = pdfmark_put_pair(pdev->Catalog, pairs + i);
+ return code;
+ } else
+ return pdfmark_put_pairs(pdev->Catalog, pairs, count);
+}
+
+/* ---------------- Named object pdfmarks ---------------- */
+
+/* [ /BBox [llx lly urx ury] /_objdef {obj} /BP pdfmark */
+static int
+pdfmark_BP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ gs_rect bbox;
+ cos_stream_t *pcs;
+ int code;
+ gs_matrix ictm;
+ byte bbox_str[6 + 6 * 15], matrix_str[6 + 6 * 15];
+ char chars[MAX_RECT_STRING + 1];
+ int bbox_str_len, matrix_str_len;
+ stream s;
+
+ if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/BBox"))
+ return_error(gs_error_rangecheck);
+ code = gs_matrix_invert(pctm, &ictm);
+ if (code < 0)
+ return code;
+ if (pairs[1].size > MAX_RECT_STRING)
+ return_error(gs_error_limitcheck);
+ memcpy(chars, pairs[1].data, pairs[1].size);
+ chars[pairs[1].size] = 0;
+ if (sscanf(chars, "[%lg %lg %lg %lg]",
+ &bbox.p.x, &bbox.p.y, &bbox.q.x, &bbox.q.y) != 4)
+ return_error(gs_error_rangecheck);
+ if ((pdev->used_mask << 1) == 0)
+ return_error(gs_error_limitcheck);
+ code = start_XObject(pdev, pdev->params.CompressPages, &pcs);
+ if (code < 0)
+ return code;
+ { byte *s = gs_alloc_string(pdev->memory, objname->size, "pdfmark_PS");
+
+ if (s == NULL)
+ return_error(gs_error_VMerror);
+ memcpy(s, objname->data, objname->size);
+ pdev->objname.data = s;
+ pdev->objname.size = objname->size;
+ }
+ pcs->is_graphics = true;
+ gs_bbox_transform(&bbox, pctm, &bbox);
+ s_init(&s, NULL);
+ swrite_string(&s, bbox_str, sizeof(bbox_str));
+ pprintg4(&s, "[%g %g %g %g]",
+ bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
+ bbox_str_len = stell(&s);
+ swrite_string(&s, matrix_str, sizeof(bbox_str));
+ pprintg6(&s, "[%g %g %g %g %g %g]",
+ ictm.xx, ictm.xy, ictm.yx, ictm.yy, ictm.tx, ictm.ty);
+ matrix_str_len = stell(&s);
+ if ((code = cos_stream_put_c_strings(pcs, "/Type", "/XObject")) < 0 ||
+ (code = cos_stream_put_c_strings(pcs, "/Subtype", "/Form")) < 0 ||
+ (code = cos_stream_put_c_strings(pcs, "/FormType", "1")) < 0 ||
+ (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/BBox",
+ bbox_str, bbox_str_len)) < 0 ||
+ (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Matrix",
+ matrix_str, matrix_str_len)) < 0 ||
+ (code = cos_dict_put_c_key_object(cos_stream_dict(pcs), "/Resources",
+ COS_OBJECT(pdev->substream_Resources))) < 0
+ )
+ return code;
+ /* Don't add to local_named_objects untill the object is created
+ to prevent pending references, which may appear
+ if /PUT pdfmark executes before pdf_substitute_resource in pdfmark_EP
+ drops this object.
+ */
+ pdev->FormDepth++;
+ return 0;
+}
+
+/* [ /EP pdfmark */
+static int
+pdfmark_EP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ int code;
+ pdf_resource_t *pres = pdev->accumulating_substream_resource;
+ gs_const_string objname = pdev->objname;
+
+ /* We are not currently accumulating a resource, this suggests an /EP
+ * without an opening /BP. This is (obviously) an error, Distiller throws
+ * an 'undefined' error, so we will too.
+ */
+ if (pres == NULL)
+ return_error(gs_error_undefined);
+
+ if (pdev->CompatibilityLevel <= 1.7) {
+ code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ code = pdfmark_bind_named_object(pdev, &objname, &pres);
+ if (code < 0)
+ return 0;
+ gs_free_const_string(pdev->memory, objname.data, objname.size, "pdfmark_EP");
+ pdev->FormDepth--;
+ return 0;
+}
+
+/* [ {obj} /SP pdfmark */
+static int
+pdfmark_SP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco; /* stream */
+ int code;
+
+ if (count != 1)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
+ return code;
+ if (pco->is_open || !pco->is_graphics)
+ return_error(gs_error_rangecheck);
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ pdf_put_matrix(pdev, "q ", pctm, "cm");
+ pprintld1(pdev->strm, "/R%ld Do Q\n", pco->id);
+ pco->pres->where_used |= pdev->used_mask;
+
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pco->pres);
+ if (code < 0)
+ return code;
+
+ return 0;
+}
+
+/* [ /_objdef {array} /type /array /OBJ pdfmark */
+/* [ /_objdef {dict} /type /dict /OBJ pdfmark */
+/* [ /_objdef {stream} /type /stream /OBJ pdfmark */
+static int
+pdfmark_OBJ(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ cos_type_t cotype;
+ cos_object_t *pco;
+ bool stream = false;
+ int code;
+
+ if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/type"))
+ return_error(gs_error_rangecheck);
+ if (pdf_key_eq(&pairs[1], "/array"))
+ cotype = cos_type_array;
+ else if (pdf_key_eq(&pairs[1], "/dict"))
+ cotype = cos_type_dict;
+ else if ((stream = pdf_key_eq(&pairs[1], "/stream")))
+ cotype = cos_type_stream;
+ else
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_make_named(pdev, objname, cotype, &pco, true)) < 0) {
+ /*
+ * For Distiller compatibility, allows multiple /OBJ pdfmarks with
+ * the same name and type, even though the pdfmark specification
+ * doesn't say anything about this being legal.
+ */
+ if (code == gs_error_rangecheck &&
+ pdf_refer_named(pdev, objname, &pco) >= 0 &&
+ cos_type(pco) == cotype
+ )
+ return 0; /* already exists, but OK */
+ return code;
+ }
+ if (stream) {
+ if (pdev->CompressStreams)
+ return setup_pdfmark_stream_compression((gx_device_psdf *)pdev,
+ (cos_stream_t *)pco);
+ else
+ return setup_pdfmark_stream_no_compression((gx_device_psdf *)pdev,
+ (cos_stream_t *)pco);
+ }
+ return 0;
+}
+
+/* [ {array} index value /PUT pdfmark */
+/* Dictionaries are converted to .PUTDICT */
+/* Streams are converted to .PUTSTREAM */
+static int
+pdfmark_PUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ cos_value_t value;
+ int code, index;
+
+ if (count != 3)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
+ return code;
+ if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
+ return code;
+ if (index < 0)
+ return_error(gs_error_rangecheck);
+ if (pco->written)
+ return_error(gs_error_rangecheck);
+ return cos_array_put((cos_array_t *)pco, index,
+ cos_string_value(&value, pairs[2].data, pairs[2].size));
+}
+
+/* [ {dict} key value ... /.PUTDICT pdfmark */
+/* [ {stream} key value ... /.PUTDICT pdfmark */
+/*
+ * Adobe's pdfmark documentation doesn't allow PUTDICT with a stream,
+ * but it's reasonable and unambiguous, and Acrobat Distiller accepts it,
+ * so we do too.
+ */
+static int
+pdfmark_PUTDICT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ int code, i;
+
+ if ((code = pdf_refer_named(pdev, &pairs[0], &pco)) < 0)
+ return code;
+ if (cos_type(pco) != cos_type_dict && cos_type(pco) != cos_type_stream)
+ return_error(gs_error_typecheck);
+ if (pco->written)
+ return_error(gs_error_rangecheck);
+
+ /* If this is a stream, and we are doing PDF/A output, and the stream
+ * is a Metadata stream, then we must not write it compressed. Bizarrely PDF/A
+ * excludes this.
+ */
+ if (cos_type(pco) == cos_type_stream && pdev->PDFA) {
+ for (i=0;i<count;i++) {
+ if (pairs[i].size == 9 && strncmp((const char *)pairs[i].data, "/Metadata", 9) == 0) {
+ cos_dict_t *pcd = (cos_dict_t *)pco;
+
+ /* Close the compressed stream */
+ gs_free_object(pdev->pdf_memory, pco->input_strm, "free old stream, replacing with new stream");
+ /* And create a new uncompressed stream */
+ code = setup_pdfmark_stream_no_compression((gx_device_psdf *)pdev,
+ (cos_stream_t *)pco);
+ if (code < 0)
+ return code;
+
+ /* We also need to remove any compression filters from the stream dictionary
+ * The only possible error here is that the key isn't found, which is possible
+ * and we don't care, so ignore the return code.
+ */
+ cos_dict_delete_c_key(pcd, "/Filter");
+ cos_dict_delete_c_key(pcd, "/DecodeParams");
+ }
+ }
+ }
+
+ return pdfmark_put_pairs((cos_dict_t *)pco, pairs + 1, count - 1);
+}
+
+/* [ {stream} string ... /.PUTSTREAM pdfmark */
+static int
+pdfmark_PUTSTREAM(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ int code, i;
+ uint l;
+
+ if (count < 2)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
+ return code;
+ if (!pco->is_open)
+ return_error(gs_error_rangecheck);
+ for (i = 1; i < count; ++i)
+ if (sputs(pco->input_strm, pairs[i].data, pairs[i].size, &l) != 0)
+ return_error(gs_error_ioerror);
+ if (pco->written)
+ return_error(gs_error_rangecheck);
+ return code;
+}
+
+/* [ {array} value /APPEND pdfmark */
+static int
+pdfmark_APPEND(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ cos_object_t *pco;
+ cos_value_t value;
+ int code;
+
+ if (count != 2)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
+ return code;
+ return cos_array_add((cos_array_t *)pco,
+ cos_string_value(&value, pairs[1].data, pairs[1].size));
+}
+
+/* [ {array} index value ... /.PUTINTERVAL pdfmark */
+static int
+pdfmark_PUTINTERVAL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ cos_value_t value;
+ int code, index, i;
+
+ if (count < 2)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
+ return code;
+ if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
+ return code;
+ if (index < 0)
+ return_error(gs_error_rangecheck);
+ for (i = 2; code >= 0 && i < count; ++i)
+ code = cos_array_put((cos_array_t *)pco, index + i - 2,
+ cos_string_value(&value, pairs[i].data, pairs[i].size));
+ return code;
+}
+
+/* [ {stream} /CLOSE pdfmark */
+static int
+pdfmark_CLOSE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ int code;
+
+ if (count != 1)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
+ return code;
+ if (!pco->is_open)
+ return_error(gs_error_rangecheck);
+ /* Currently we don't do anything special when closing a stream. */
+ pco->is_open = false;
+ return 0;
+}
+
+/* [ /NamespacePush pdfmark */
+static int
+pdfmark_NamespacePush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ if (count != 0)
+ return_error(gs_error_rangecheck);
+ return pdf_push_namespace(pdev);
+}
+
+/* [ /NamespacePop pdfmark */
+static int
+pdfmark_NamespacePop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ if (count != 0)
+ return_error(gs_error_rangecheck);
+ cos_dict_objects_write(pdev->local_named_objects, pdev);
+ return pdf_pop_namespace(pdev);
+}
+
+/* [ /_objdef {image} /NI pdfmark */
+static int
+pdfmark_NI(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ cos_object_t *pco;
+ int code;
+
+ if (objname == 0 || count != 0)
+ return_error(gs_error_rangecheck);
+ code = pdf_make_named(pdev, objname, cos_type_dict, &pco, true);
+ if (code < 0)
+ return code;
+ return cos_array_add_object(pdev->NI_stack, pco);
+}
+
+/* ---------------- Named content pdfmarks ---------------- */
+
+/* [ tag /MP pdfmark */
+static int
+pdfmark_MP(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ tag propdict /DP pdfmark */
+static int
+pdfmark_DP(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ tag /BMC pdfmark */
+static int
+pdfmark_BMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ int code;
+ char *tag;
+
+ if (count != 1) return_error(gs_error_rangecheck);
+
+ tag = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char),
+ "pdfmark_BMC");
+ memcpy(tag, pairs[0].data, pairs[0].size);
+ tag[pairs[0].size] = 0x00;
+
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0) return code;
+
+ pprints1(pdev->strm, "%s BMC\n", tag);
+
+ gs_free_object(pdev->memory, tag, "pdfmark_BMC");
+ return 0;
+}
+
+/* [ tag propdict /BDC pdfmark */
+static int
+pdfmark_BDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ int code;
+ cos_object_t *pco;
+ char *cstring;
+ pdf_resource_t *pres;
+
+ if (count != 2) return_error(gs_error_rangecheck);
+
+ /* check tag for /Name object syntax */
+ if ((pairs[0].data)[0] != '/') return_error(gs_error_rangecheck);
+
+ /* check propdict for {object name} syntax */
+ if (pdf_objname_is_valid(pairs[1].data, pairs[1].size))
+ {
+ code = pdf_refer_named(pdev, &pairs[1], &pco);
+ if(code < 0) return code;
+ }
+ else /* << inline prop dict >> */
+ {
+ /* strip << and >> */
+ if ((pairs[1].data)[0]=='<'&&(pairs[1].data)[1]=='<')
+ {
+ pairs[1].data=&(pairs[1].data[2]);
+ pairs[1].size-=2;
+ }
+ else
+ return_error(gs_error_rangecheck);
+
+ if ((pairs[1].data)[pairs[1].size-1]=='>'&&(pairs[1].data)[pairs[1].size-2]=='>')
+ pairs[1].size-=2;
+
+ /* convert inline propdict to C string with object names replaced by refs */
+ code = pdf_replace_names(pdev, &pairs[1], &pairs[1]);
+ if (code<0) return code;
+ cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[1].size + 1) * sizeof(unsigned char),
+ "pdfmark_BDC");
+ memcpy(cstring, pairs[1].data, pairs[1].size);
+ cstring[pairs[1].size] = 0x00;
+
+ code = pdf_make_named_dict(pdev, NULL, (cos_dict_t**) &pco, true);
+ if (code<0) return code;
+
+ /* copy inline propdict to new object */
+ code = cos_dict_put_c_strings((cos_dict_t*) pco, cstring, "");
+ if(code < 0) return code;
+ COS_WRITE_OBJECT(pco, pdev, resourceProperties);
+ COS_RELEASE(pco, "pdfmark_BDC");
+ gs_free_object(pdev->memory, cstring, "pdfmark_BDC");
+ }
+
+ pres = pdf_find_resource_by_resource_id(pdev, resourceProperties, pco->id);
+ if (pres==0){
+ if ((code = pdf_alloc_resource(pdev, resourceProperties, pco->id, &(pco->pres), pco->id))<0)
+ return code;
+ }
+
+ cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char),
+ "pdfmark_BDC");
+ memcpy(cstring, pairs[0].data, pairs[0].size);
+ cstring[pairs[0].size] = 0x00;
+
+ /* make sure we write to the correct stream */
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0) return code;
+
+ pprints1(pdev->strm, "%s", cstring); /* write tag */
+ pprintld1(pdev->strm, "/R%ld BDC\n", pco->id);
+ pco->pres->where_used |= pdev->used_mask;
+ if ((code = pdf_add_resource(pdev, pdev->substream_Resources, "/Properties", pco->pres))<0)
+ return code;
+
+ gs_free_object(pdev->memory, cstring, "pdfmark_BDC");
+ return 0;
+}
+
+/* [ /EMC pdfmark */
+static int
+pdfmark_EMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ int code;
+
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0) return code;
+ stream_puts(pdev->strm, "EMC\n");
+
+ return 0;
+}
+
+/* ---------------- Document structure pdfmarks ---------------- */
+
+/* [ newsubtype1 stdsubtype1 ... /StRoleMap pdfmark */
+static int
+pdfmark_StRoleMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ class1 {attrobj1} ... /StClassMap pdfmark */
+static int
+pdfmark_StClassMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/*
+ * [ [/_objdef {objname}] /Subtype name [/Title string] [/Alt string]
+ * [/ID string] [/Class name] [/At index] [/Bookmark dict] [action_pairs...]
+ * /StPNE pdfmark
+ */
+static int
+pdfmark_StPNE(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ [/Title string] [/Open bool] [action_pairs...] /StBookmarkRoot pdfmark */
+static int
+pdfmark_StBookmarkRoot(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ [/E {elt}] /StPush pdfmark */
+static int
+pdfmark_StPush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /StPop pdfmark */
+static int
+pdfmark_StPop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /StPopAll pdfmark */
+static int
+pdfmark_StPopAll(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ [/T tagname] [/At index] /StBMC pdfmark */
+static int
+pdfmark_StBMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ [/P propdict] [/T tagname] [/At index] /StBDC pdfmark */
+static int
+pdfmark_StBDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /Obj {obj} [/At index] /StOBJ pdfmark */
+static int
+pdfmark_StOBJ(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /Obj {obj} /StAttr pdfmark */
+static int
+pdfmark_StAttr(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /StoreName name /StStore pdfmark */
+static int
+pdfmark_StStore(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /StoreName name /StRetrieve pdfmark */
+static int
+pdfmark_StRetrieve(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+static int
+pdfmark_Metadata(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ int i;
+ gs_param_string key;
+ char data[10] = "/Metadata";
+
+ if (pdev->CompatibilityLevel < 1.4) {
+ dmprintf(pdev->pdf_memory, "Cannot add Metadata to PDF files with version earlier than 1.4.\n");
+ return 0;
+ }
+ if (pdev->PDFA != 0)
+ dmprintf(pdev->pdf_memory, "Warning: PDF/A output requires specific metadata, this pdfmark has overridden that,\n output conformance cannot be guaranteed\n");
+ if (pdev->PDFX != 0)
+ dmprintf(pdev->pdf_memory, "Warning: PDF/X output requires specific metadata, this pdfmark has overridden that,\n output conformance cannot be guaranteed\n");
+
+ if(pdev->ExtensionMetadata) {
+ dmprintf(pdev->pdf_memory, "Extension metadata exists when /Metadata pdfmark executed, discarding extension metadata.\n");
+ gs_free_object(pdev->pdf_memory->stable_memory, pdev->ExtensionMetadata, "Extension metadata discarded on /Metadata pdfmark");
+ }
+
+ if (!pdev->Catalog) {
+ gs_param_string nstr;
+
+ param_string_from_string(nstr, "{Catalog}");
+ pdf_create_named_dict(pdev, &nstr, &pdev->Catalog, 0L);
+ }
+
+ key.data = (const byte *)&data;
+ key.size = 9;
+
+ for (i = 0; i < count; i += 2) {
+ if (pdf_key_eq(&pairs[i], "{Catalog}")) {
+ return cos_dict_put_string(pdev->Catalog, key.data, key.size,
+ pairs[i+1].data, pairs[i+1].size);
+ }
+ }
+ return 0;
+}
+
+static int
+pdfmark_Ext_Metadata(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ int i, j=0;
+
+ if (pdev->CompatibilityLevel < 1.4) {
+ dmprintf(pdev->pdf_memory, "Cannot add Metadata to PDF files with version earlier than 1.4.\n");
+ return 0;
+ }
+
+ if (cos_dict_find_c_key(pdev->Catalog, "/Metadata")) {
+ dmprintf(pdev->pdf_memory, "Cannot add extension to Metadata specified with the /Metadata pdfmark\n");
+ return 0;
+ }
+
+ if(pdev->ExtensionMetadata) {
+ dmprintf(pdev->pdf_memory, "Extension metadata already defined, discarding old data.\n");
+ gs_free_object(pdev->pdf_memory->stable_memory, pdev->ExtensionMetadata, "Extension metadata");
+ }
+ pdev->ExtensionMetadata = (char *)gs_alloc_bytes(pdev->pdf_memory->stable_memory, pairs[1].size - 1, "Extension metadata");
+ memset(pdev->ExtensionMetadata, 0x00, pairs[1].size - 1);
+ for (i=1;i<pairs[1].size - 1;i++) {
+ if (pairs[1].data[i] == '\\') {
+ switch(pairs[1].data[i+1]) {
+ case '(':
+ case ')':
+ case '\\':
+ pdev->ExtensionMetadata[j++] = pairs[1].data[i+1];
+ i++;
+ break;
+ case 'r':
+ pdev->ExtensionMetadata[j++] = 0x0D;
+ i++;
+ break;
+ case 'n':
+ pdev->ExtensionMetadata[j++] = 0x0A;
+ i++;
+ break;
+ case 't':
+ pdev->ExtensionMetadata[j++] = 0x09;
+ i++;
+ break;
+ case 'b':
+ pdev->ExtensionMetadata[j++] = 0x08;
+ i++;
+ break;
+ case 'f':
+ pdev->ExtensionMetadata[j++] = 0x0C;
+ i++;
+ break;
+ default:
+ if((pairs[1].data[i+1]) >= 0x30 && (pairs[1].data[i+1]) <= 0x39) {
+ pdev->ExtensionMetadata[j++] = (pairs[1].data[i+1] - 0x30) * 64 + (pairs[1].data[i+2] - 0x30) * 8 + (pairs[1].data[i+3] - 0x30);
+ i += 3;
+ } else
+ pdev->ExtensionMetadata[j++] = pairs[1].data[i];
+ break;
+ }
+ } else
+ pdev->ExtensionMetadata[j++] = pairs[1].data[i];
+ }
+ return 0;
+}
+/* ---------------- Dispatch ---------------- */
+
+/*
+ * Define the pdfmark types we know about.
+ */
+static const pdfmark_name mark_names[] =
+{
+ /* Miscellaneous. */
+ {"ANN", pdfmark_ANN, PDFMARK_NAMEABLE},
+ {"LNK", pdfmark_LNK, PDFMARK_NAMEABLE},
+ {"OUT", pdfmark_OUT, 0},
+ {"ARTICLE", pdfmark_ARTICLE, 0},
+ {"DEST", pdfmark_DEST, PDFMARK_NAMEABLE},
+ {"EMBED", pdfmark_EMBED, PDFMARK_NAMEABLE},
+ {"PS", pdfmark_PS, PDFMARK_NAMEABLE},
+ {"PAGES", pdfmark_PAGES, 0},
+ {"PAGE", pdfmark_PAGE, 0},
+ {"PAGELABEL", pdfmark_PAGELABEL, 0},
+ {"DOCINFO", pdfmark_DOCINFO, 0},
+ {"DOCVIEW", pdfmark_DOCVIEW, 0},
+ /* Named objects. */
+ {"BP", pdfmark_BP, PDFMARK_NAMEABLE | PDFMARK_TRUECTM},
+ {"EP", pdfmark_EP, 0},
+ {"SP", pdfmark_SP, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME | PDFMARK_TRUECTM},
+ {"OBJ", pdfmark_OBJ, PDFMARK_NAMEABLE},
+ {"PUT", pdfmark_PUT, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
+ {".PUTDICT", pdfmark_PUTDICT, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
+ {".PUTINTERVAL", pdfmark_PUTINTERVAL, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
+ {".PUTSTREAM", pdfmark_PUTSTREAM, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME |
+ PDFMARK_NO_REFS},
+ {"APPEND", pdfmark_APPEND, PDFMARK_KEEP_NAME},
+ {"CLOSE", pdfmark_CLOSE, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
+ {"NamespacePush", pdfmark_NamespacePush, 0},
+ {"NamespacePop", pdfmark_NamespacePop, 0},
+ {"NI", pdfmark_NI, PDFMARK_NAMEABLE},
+ /* Marked content. */
+ {"MP", pdfmark_MP, PDFMARK_ODD_OK},
+ {"DP", pdfmark_DP, 0},
+ {"BMC", pdfmark_BMC, PDFMARK_ODD_OK},
+ {"BDC", pdfmark_BDC, PDFMARK_NO_REFS},
+ {"EMC", pdfmark_EMC, 0},
+ /* Document structure. */
+ {"StRoleMap", pdfmark_StRoleMap, 0},
+ {"StClassMap", pdfmark_StClassMap, 0},
+ {"StPNE", pdfmark_StPNE, PDFMARK_NAMEABLE},
+ {"StBookmarkRoot", pdfmark_StBookmarkRoot, 0},
+ {"StPush", pdfmark_StPush, 0},
+ {"StPop", pdfmark_StPop, 0},
+ {"StPopAll", pdfmark_StPopAll, 0},
+ {"StBMC", pdfmark_StBMC, 0},
+ {"StBDC", pdfmark_StBDC, 0},
+ /* EMC is listed under "Marked content" above. */
+ {"StOBJ", pdfmark_StOBJ, 0},
+ {"StAttr", pdfmark_StAttr, 0},
+ {"StStore", pdfmark_StStore, 0},
+ {"StRetrieve", pdfmark_StRetrieve, 0},
+ /* Metadata and extension */
+ {"Metadata", pdfmark_Metadata, 0},
+ {"Ext_Metadata", pdfmark_Ext_Metadata, 0},
+ /* End of list. */
+ {0, 0}
+};
+
+/* Process a pdfmark. */
+int
+pdfmark_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
+{
+ const gs_param_string *data = pma->data;
+ uint size = pma->size;
+ const gs_param_string *pts = &data[size - 1];
+ const gs_param_string *objname = 0;
+ gs_matrix ctm;
+ const pdfmark_name *pmn;
+ int code = 0;
+
+ { int cnt, len = pts[-1].size;
+ char buf[200]; /* 6 doubles should fit (%g == -0.14285714285714285e-101 = 25 chars) */
+
+ if (len > sizeof(buf) - 1)
+ return_error(gs_error_rangecheck);
+ memcpy(buf, pts[-1].data, len);
+ buf[len] = 0;
+ cnt = sscanf(buf, "[%g %g %g %g %g %g]",
+ &ctm.xx, &ctm.xy, &ctm.yx, &ctm.yy, &ctm.tx, &ctm.ty);
+ if (cnt != 6)
+ return_error(gs_error_rangecheck);
+ }
+ size -= 2; /* remove CTM & pdfmark name */
+ for (pmn = mark_names; pmn->mname != 0; ++pmn)
+ if (pdf_key_eq(pts, pmn->mname)) {
+ gs_memory_t *mem = pdev->pdf_memory;
+ int odd_ok = (pmn->options & PDFMARK_ODD_OK) != 0;
+ gs_param_string *pairs;
+ int j;
+
+ /*
+ * Our coordinate system is scaled so that user space is always
+ * default user space. Adjust the CTM to match this, except if this
+ * particular pdfmark requires the "true" CTM.
+ */
+ if (pmn->options & PDFMARK_TRUECTM)
+ DO_NOTHING;
+ else {
+ double xscale = 72.0 / pdev->HWResolution[0],
+ yscale = 72.0 / pdev->HWResolution[1];
+ ctm.xx *= xscale, ctm.xy *= yscale;
+ ctm.yx *= xscale, ctm.yy *= yscale;
+ ctm.tx *= xscale, ctm.ty *= yscale;
+ }
+ if (size & !odd_ok)
+ return_error(gs_error_rangecheck);
+ if (pmn->options & PDFMARK_NAMEABLE) {
+ /* Look for an object name. */
+ for (j = 0; j < size; j += 2) {
+ if (pdf_key_eq(&data[j], "/_objdef")) {
+ objname = &data[j + 1];
+ if (!pdf_objname_is_valid(objname->data,
+ objname->size)
+ )
+ return_error(gs_error_rangecheck);
+ /* Save the pairs without the name. */
+ size -= 2;
+ pairs = (gs_param_string *)
+ gs_alloc_byte_array(mem, size,
+ sizeof(gs_param_string),
+ "pdfmark_process(pairs)");
+ if (!pairs)
+ return_error(gs_error_VMerror);
+ memcpy(pairs, data, j * sizeof(*data));
+ memcpy(pairs + j, data + j + 2,
+ (size - j) * sizeof(*data));
+ goto copied;
+ }
+ }
+ }
+ /* Save all the pairs. */
+ pairs = (gs_param_string *)
+ gs_alloc_byte_array(mem, size, sizeof(gs_param_string),
+ "pdfmark_process(pairs)");
+ if (!pairs)
+ return_error(gs_error_VMerror);
+ memcpy(pairs, data, size * sizeof(*data));
+ copied: /* Substitute object references for names. */
+ if (!(pmn->options & PDFMARK_NO_REFS)) {
+ for (j = (pmn->options & PDFMARK_KEEP_NAME ? 1 : 1 - odd_ok);
+ j < size; j += 2 - odd_ok
+ ) {
+ code = pdf_replace_names(pdev, &pairs[j], &pairs[j]);
+ if (code < 0) {
+ gs_free_object(mem, pairs, "pdfmark_process(pairs)");
+ return code;
+ }
+ }
+ }
+ code = (*pmn->proc) (pdev, pairs, size, &ctm, objname);
+ gs_free_object(mem, pairs, "pdfmark_process(pairs)");
+ break;
+ }
+ return code;
+}