summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipdfm-x/spc_pdfm.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
committerKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
commitad547a6b5986815fda458221149728d9d9ab1d87 (patch)
tree16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/dvipdfm-x/spc_pdfm.c
parent947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff)
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/dvipdfm-x/spc_pdfm.c')
-rw-r--r--Build/source/texk/dvipdfm-x/spc_pdfm.c2435
1 files changed, 2435 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdfm-x/spc_pdfm.c b/Build/source/texk/dvipdfm-x/spc_pdfm.c
new file mode 100644
index 00000000000..d45e09330f2
--- /dev/null
+++ b/Build/source/texk/dvipdfm-x/spc_pdfm.c
@@ -0,0 +1,2435 @@
+/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
+
+ Copyright (C) 2002-2020 by Jin-Hwan Cho and Shunsaku Hirata,
+ the dvipdfmx project team.
+
+ Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+
+#include "system.h"
+#include "mem.h"
+#include "error.h"
+#include "mfileio.h"
+
+#include "numbers.h"
+
+#include "fontmap.h"
+#include "dpxfile.h"
+#include "dpxutil.h"
+#include "dpxconf.h"
+
+#include "unicode.h"
+
+#include "pdfobj.h"
+#include "pdfparse.h"
+
+#include "pdfdoc.h"
+
+#include "pdfximage.h"
+#include "pdfdraw.h"
+#include "pdfcolor.h"
+#include "pdfdev.h"
+
+#include "specials.h"
+
+#include "spc_util.h"
+#include "spc_pdfm.h"
+
+#include "dvipdfmx.h"
+
+#define SPC_PDFM_SUPPORT_ANNOT_TRANS 1
+
+struct tounicode {
+ int cmap_id;
+ int unescape_backslash;
+ pdf_obj *taintkeys; /* An array of PDF names. */
+};
+
+struct spc_pdf_
+{
+ pdf_obj *annot_dict; /* pending annotation dict */
+ int lowest_level; /* current min level of outlines */
+ struct tounicode cd; /* For to-UTF16-BE conversion :( */
+ pdf_obj *pageresources; /* Add to all page resource dict */
+};
+
+static struct spc_pdf_ _pdf_stat = {
+ NULL,
+ 255,
+ { -1, 0, NULL },
+ NULL
+};
+
+static pdf_obj *
+parse_pdf_reference (const char **start, const char *end, void *user_data)
+{
+ pdf_obj *result = NULL;
+ char *name;
+
+ skip_white(start, end);
+ name = parse_opt_ident(start, end);
+ if (name) {
+ result = spc_lookup_reference(name);
+ if (!result) {
+ WARN("Could not find the named reference (@%s).", name);
+ }
+ RELEASE(name);
+ } else {
+ WARN("Could not find a reference name.");
+ result = NULL;
+ }
+
+ return result;
+}
+
+static int
+spc_handler_pdfm__init (void *dp)
+{
+ struct spc_pdf_ *sd = dp;
+ /* The folllowing dictionary entry keys are considered as keys for
+ * text strings. Be sure that string object is NOT always a text string.
+ */
+ static const char *default_taintkeys[] = {
+ "Title", "Author", "Subject", "Keywords",
+ "Creator", "Producer", "Contents", "Subj",
+ "TU", "T", "TM", NULL /* EOD */
+ };
+ int i;
+
+ sd->annot_dict = NULL;
+ sd->lowest_level = 255;
+ sd->cd.taintkeys = pdf_new_array();
+ for (i = 0; default_taintkeys[i] != NULL; i++) {
+ pdf_add_array(sd->cd.taintkeys,
+ pdf_new_name(default_taintkeys[i]));
+ }
+ sd->pageresources = NULL;
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm__clean (void *dp)
+{
+ struct spc_pdf_ *sd = dp;
+
+ if (sd->annot_dict) {
+ WARN("Unbalanced bann and eann found.");
+ pdf_release_obj(sd->annot_dict);
+ }
+ sd->lowest_level = 255;
+ sd->annot_dict = NULL;
+ if (sd->cd.taintkeys)
+ pdf_release_obj(sd->cd.taintkeys);
+ sd->cd.taintkeys = NULL;
+ if (sd->pageresources)
+ pdf_release_obj(sd->pageresources);
+ sd->pageresources = NULL;
+
+ return 0;
+}
+
+
+int
+spc_pdfm_at_begin_document (void)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ return spc_handler_pdfm__init(sd);
+}
+
+int
+spc_pdfm_at_end_document (void)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ return spc_handler_pdfm__clean(sd);
+}
+
+/* Why should we have this kind of things? */
+static int
+safeputresdent (pdf_obj *kp, pdf_obj *vp, void *dp)
+{
+ char *key;
+
+ ASSERT(kp && vp && dp);
+
+ key = pdf_name_value(kp);
+ if (pdf_lookup_dict(dp, key))
+ WARN("Object \"%s\" already defined in dict! (ignored)", key);
+ else {
+ pdf_add_dict(dp,
+ pdf_link_obj(kp), pdf_link_obj(vp));
+ }
+ return 0;
+}
+
+static int
+safeputresdict (pdf_obj *kp, pdf_obj *vp, void *dp)
+{
+ char *key;
+ pdf_obj *dict;
+
+ ASSERT(kp && vp && dp);
+
+ key = pdf_name_value(kp);
+ dict = pdf_lookup_dict(dp, key);
+
+ /* Not sure what is the best way to handle this situation */
+ if (PDF_OBJ_INDIRECTTYPE(dict)) {
+ if (PDF_OBJ_INDIRECTTYPE(vp)) {
+ /* If two indirect objects are pointing the same object, do nothing. */
+ if (!pdf_compare_reference(dict, vp)) {
+ return 0;
+ } else {
+ /* otherwise merge the content of old one (see below) */
+ dict = pdf_deref_obj(dict);
+ pdf_release_obj(dict); /* decrement link count */
+ }
+ }
+ }
+
+ if (pdf_obj_typeof(vp) == PDF_INDIRECT) {
+ /* Copy the content of old resource category dict (if exists) */
+ if (dict) {
+ pdf_obj *dst = pdf_deref_obj(vp);
+ if (dst) {
+ if (pdf_obj_typeof(dst) == PDF_DICT) {
+ pdf_foreach_dict(dict, safeputresdent, dst);
+ pdf_release_obj(dst);
+ } else {
+ WARN("Invalid type (not DICT) for page/form resource dict entry: key=\"%s\"", key);
+ pdf_release_obj(dst);
+ return -1;
+ }
+ }
+ }
+ pdf_add_dict(dp, pdf_new_name(key), pdf_link_obj(vp));
+ } else if (pdf_obj_typeof(vp) == PDF_DICT) {
+ if (dict)
+ pdf_foreach_dict(vp, safeputresdent, dict);
+ else {
+ pdf_add_dict(dp, pdf_new_name(key), pdf_link_obj(vp));
+ }
+ } else {
+ WARN("Invalid type (not DICT) for page/form resource dict entry: key=\"%s\"", key);
+ return -1;
+ }
+
+ return 0;
+}
+
+static
+int putpageresources (pdf_obj *kp, pdf_obj *vp, void *dp)
+{
+ char *resource_name;
+
+ ASSERT(kp && vp);
+
+ resource_name = pdf_name_value(kp);
+ pdf_doc_add_page_resource(dp, resource_name, pdf_link_obj(vp));
+
+ return 0;
+}
+
+static
+int forallresourcecategory (pdf_obj *kp, pdf_obj *vp, void *dp)
+{
+ int r = -1;
+ char *category;
+
+ ASSERT(kp && vp);
+
+ category = pdf_name_value(kp);
+ switch (pdf_obj_typeof(vp)) {
+ case PDF_DICT:
+ r = pdf_foreach_dict(vp, putpageresources, category);
+ break;
+ case PDF_INDIRECT:
+ {
+ /* In case pdf:pageresouces << /Category @res >> */
+ pdf_obj *obj;
+ obj = pdf_deref_obj(vp);
+ if (!obj) {
+ WARN("Can't deref object for page resource: %s", category);
+ r = -1;
+ } else if (pdf_obj_typeof(obj) != PDF_DICT) {
+ WARN("Invalid object type for page resource: %s", category);
+ r = -1;
+ } else {
+ pdf_obj *res_dict, *dict;
+
+ res_dict = pdf_doc_current_page_resources();
+ dict = pdf_lookup_dict(res_dict, category);
+ if (!dict) {
+ pdf_add_dict(res_dict, pdf_new_name(category), pdf_link_obj(vp));
+ } else {
+ if (pdf_obj_typeof(dict) == PDF_INDIRECT) {
+ dict = pdf_deref_obj(dict);
+ pdf_release_obj(dict); /* FIXME: jus to decrement link counter */
+ }
+#if 0
+ /* This will leave garbage (object "res") since object "res"
+ * supplied as resource dictionary will have label but we copy the
+ * content of res here and never use reference to it.
+ */
+ pdf_foreach_dict(obj, safeputresdent, dict);
+#else
+ /* With the below code resource dictionary is replaced by user
+ * supplied one, @res. However, there is a problem that all
+ * resources including internally generated one may go into single
+ * dictionary referenced by @res, and will be visible from any
+ * subsequent pages.
+ */
+ pdf_foreach_dict(dict, safeputresdent, obj);
+ pdf_add_dict(res_dict, pdf_new_name(category), pdf_link_obj(vp));
+#endif
+ }
+ pdf_release_obj(obj);
+ }
+ }
+ break;
+ default:
+ WARN("Invalid object type for page resource specified for \"%s\"", category);
+ }
+
+ return r;
+}
+
+int
+spc_pdfm_at_end_page (void)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+
+ if (sd->pageresources) {
+ pdf_foreach_dict(sd->pageresources, forallresourcecategory, NULL);
+ }
+
+ return 0;
+}
+
+/* Dvipdfm specials */
+static int
+spc_handler_pdfm_bop (struct spc_env *spe, struct spc_arg *args)
+{
+ if (args->curptr < args->endptr) {
+ pdf_doc_set_bop_content(args->curptr,
+ (int) (args->endptr - args->curptr));
+ }
+
+ args->curptr = args->endptr;
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_eop (struct spc_env *spe, struct spc_arg *args)
+{
+ if (args->curptr < args->endptr) {
+ pdf_doc_set_eop_content(args->curptr,
+ (int) (args->endptr - args->curptr));
+ }
+
+ args->curptr = args->endptr;
+
+ return 0;
+}
+
+#define streamfiltered(o) \
+ (pdf_lookup_dict(pdf_stream_dict((o)), "Filter") ? 1 : 0)
+
+/* Think what happens if you do
+ *
+ * pdf:put @resources << /Font << >> >>
+ *
+ */
+static int
+spc_handler_pdfm_put (struct spc_env *spe, struct spc_arg *ap)
+{
+ pdf_obj *obj1, *obj2; /* put obj2 into obj1 */
+ char *ident;
+ int error = 0;
+
+ skip_white(&ap->curptr, ap->endptr);
+
+ ident = parse_opt_ident(&ap->curptr, ap->endptr);
+ if (!ident) {
+ spc_warn(spe, "Missing object identifier.");
+ return -1;
+ }
+ obj1 = spc_lookup_object(ident);
+ if (!obj1) {
+ spc_warn(spe, "Specified object not exist: %s", ident);
+ RELEASE(ident);
+ return -1;
+ }
+ skip_white(&ap->curptr, ap->endptr);
+
+ obj2 = parse_pdf_object_extended(&ap->curptr, ap->endptr, NULL, parse_pdf_reference, spe);
+ if (!obj2) {
+ spc_warn(spe, "Missing (an) object(s) to put into \"%s\"!", ident);
+ RELEASE(ident);
+ return -1;
+ }
+
+ switch (pdf_obj_typeof(obj1)) {
+ case PDF_DICT:
+ if (pdf_obj_typeof(obj2) != PDF_DICT) {
+ spc_warn(spe, "Inconsistent object type for \"put\" (expecting DICT): %s", ident);
+ error = -1;
+ } else {
+ if (!strcmp(ident, "resources"))
+ error = pdf_foreach_dict(obj2, safeputresdict, obj1);
+ else {
+ pdf_merge_dict(obj1, obj2);
+ }
+ }
+ break;
+
+ case PDF_STREAM:
+ if (pdf_obj_typeof(obj2) == PDF_DICT)
+ pdf_merge_dict(pdf_stream_dict(obj1), obj2);
+ else if (pdf_obj_typeof(obj2) == PDF_STREAM)
+#if 0
+ {
+ pdf_merge_dict(pdf_stream_dict(obj1), pdf_stream_dict(obj2));
+ pdf_add_stream(obj1, pdf_stream_dataptr(obj2), pdf_stream_length(obj2));
+ }
+#else
+ {
+ spc_warn(spe, "\"put\" operation not supported for STREAM <- STREAM: %s", ident);
+ error = -1;
+ }
+#endif
+ else {
+ spc_warn(spe, "Invalid type: expecting a DICT or STREAM: %s", ident);
+ error = -1;
+ }
+ break;
+
+ case PDF_ARRAY:
+ /* dvipdfm */
+ pdf_add_array(obj1, pdf_link_obj(obj2));
+ while (ap->curptr < ap->endptr) {
+ pdf_obj *obj3 = parse_pdf_object_extended(&ap->curptr, ap->endptr, NULL, parse_pdf_reference, spe);
+ if (!obj3)
+ break;
+ pdf_add_array(obj1, obj3);
+ skip_white(&ap->curptr, ap->endptr);
+ }
+ break;
+
+ default:
+ spc_warn(spe, "Can't \"put\" object into non-DICT/STREAM/ARRAY type object: %s", ident);
+ error = -1;
+ break;
+ }
+ pdf_release_obj(obj2);
+ RELEASE(ident);
+
+ return error;
+}
+
+
+/* For pdf:tounicode support
+ * This feature is provided for convenience. TeX can't do
+ * input encoding conversion.
+ */
+#include "cmap.h"
+
+static size_t
+calculate_size_utf16 (const unsigned char *p, const unsigned char *endptr)
+{
+ size_t len = 0;
+
+ while (p < endptr) {
+ unsigned char c = *p;
+ if (c < 0x80) {
+ len += 2;
+ p += 1;
+ } else if (c < 0xE0) {
+ len += 2;
+ p += 2;
+ } else if (c < 0xF0) {
+ len += 2;
+ p += 3;
+ } else if (c < 0xF8) {
+ len += 4; /* Surrogate */
+ p += 4;
+ } else if (c < 0xFC) {
+ len += 4; /* Surrogate */
+ p += 5;
+ } else if (c < 0xFE) {
+ len += 4; /* Surrogate */
+ p += 6;
+ }
+ }
+
+ return len;
+}
+
+static int
+reencode_string_from_utf8_to_utf16be (pdf_obj *instring)
+{
+ int error = 0;
+ unsigned char *strptr;
+ size_t length;
+ int non_ascii;
+ const unsigned char *p, *endptr;
+
+ ASSERT(instring);
+ ASSERT(PDF_OBJ_STRINGTYPE(instring));
+
+ strptr = pdf_string_value(instring);
+ length = pdf_string_length(instring);
+
+ /* check if the input string is strictly ASCII */
+ p = strptr;
+ endptr = strptr + length;
+ non_ascii = 0;
+ for ( ; p < endptr; p++) {
+ if (*p > 127)
+ non_ascii++;
+ }
+ if (non_ascii == 0)
+ return 0; /* no need to reencode ASCII strings */
+
+ if (!UC_UTF8_is_valid_string(strptr, endptr)) {
+ error = -1;
+ } else {
+ unsigned char *q, *buf, *limptr;
+ size_t len;
+
+ p = strptr;
+ /* Rough estimate of output length. */
+ len = calculate_size_utf16(p, endptr) + 2;
+ buf = NEW(len, unsigned char);
+ q = buf;
+ limptr = buf + len;
+ q[0] = 0xfe; q[1] = 0xff;
+ q += 2;
+ while (p < endptr && q < limptr && !error) {
+ int32_t ucv;
+ size_t count;
+
+ ucv = UC_UTF8_decode_char(&p, endptr);
+ if (!UC_is_valid(ucv)) {
+ error = -1;
+ } else {
+ count = UC_UTF16BE_encode_char(ucv, &q, limptr);
+ if (count == 0) {
+ error = -1;
+ }
+ }
+ }
+ if (!error)
+ pdf_set_string(instring, buf, q - buf);
+ RELEASE(buf);
+ }
+
+ return error;
+}
+
+static int
+reencode_string (CMap *cmap, pdf_obj *instring)
+{
+ int error = 0;
+
+ if (!instring || !PDF_OBJ_STRINGTYPE(instring))
+ return -1;
+
+ if (cmap) {
+ unsigned char *obuf;
+ unsigned char *obufcur;
+ const unsigned char *inbufcur;
+ int inbufleft, obufleft, obufsize;
+
+ inbufleft = pdf_string_length(instring);
+ inbufcur = pdf_string_value (instring);
+
+ obufsize = inbufleft * 4 + 2;
+ obuf = NEW(obufsize, unsigned char);
+ obuf[0] = 0xfe;
+ obuf[1] = 0xff;
+ obufcur = obuf + 2;
+ obufleft = obufsize - 2;
+
+ CMap_decode(cmap, &inbufcur, &inbufleft, &obufcur, &obufleft);
+
+ if (inbufleft > 0)
+ error = -1;
+ if (!error)
+ pdf_set_string(instring, obuf, obufsize - obufleft);
+ RELEASE(obuf);
+ }
+
+ return error;
+}
+
+/* The purpose of this routine is to check if given string object is
+ * surely an object for *text* strings. It does not do a complete check
+ * but does a quick check. Please add entries for taintkeys if you have found
+ * additional dictionary entries which is considered as a text string.
+ */
+static int
+need_reencode (pdf_obj *kp, pdf_obj *vp, struct tounicode *cd)
+{
+ int r = 0, i;
+ pdf_obj *tk;
+
+ ASSERT( cd && cd->taintkeys );
+ ASSERT( pdf_obj_typeof(kp) == PDF_NAME );
+ ASSERT( pdf_obj_typeof(vp) == PDF_STRING );
+
+ for (i = 0; i < pdf_array_length(cd->taintkeys); i++) {
+ tk = pdf_get_array(cd->taintkeys, i);
+ ASSERT( tk && pdf_obj_typeof(tk) == PDF_NAME );
+ if (!strcmp(pdf_name_value(kp), pdf_name_value(tk))) {
+ r = 1;
+ break;
+ }
+ }
+ if (r) {
+ /* Check UTF-16BE BOM. */
+ if (pdf_string_length(vp) >= 2 &&
+ !memcmp(pdf_string_value(vp), "\xfe\xff", 2))
+ r = 0;
+ }
+
+ return r;
+}
+
+static int
+modify_strings (pdf_obj *kp, pdf_obj *vp, void *dp)
+{
+ int r = 0; /* continue */
+ struct tounicode *cd = dp;
+
+ ASSERT( pdf_obj_typeof(kp) == PDF_NAME );
+
+ switch (pdf_obj_typeof(vp)) {
+ case PDF_STRING:
+ if (cd && cd->cmap_id >= 0 && cd->taintkeys) {
+ CMap *cmap = CMap_cache_get(cd->cmap_id);
+ if (need_reencode(kp, vp, cd))
+ r = reencode_string(cmap, vp);
+ } else if ((dpx_conf.compat_mode == dpx_mode_xdv_mode) && cd && cd->taintkeys) {
+ if (need_reencode(kp, vp, cd))
+ r = reencode_string_from_utf8_to_utf16be(vp);
+ }
+ if (r < 0) /* error occured... */
+ WARN("Input string conversion (to UTF16BE) failed for %s...", pdf_name_value(kp));
+ break;
+ /* Array elements are also checked. */
+ case PDF_ARRAY:
+ {
+ int i;
+ for (i = 0; i < pdf_array_length(vp); i++) {
+ pdf_obj *obj;
+ obj = pdf_get_array(vp, i);
+ r = modify_strings(kp, obj, dp);
+ if (r < 0)
+ break;
+ }
+ }
+ break;
+ case PDF_DICT:
+ r = pdf_foreach_dict(vp, modify_strings, dp);
+ break;
+ case PDF_STREAM:
+ r = pdf_foreach_dict(pdf_stream_dict(vp), modify_strings, dp);
+ break;
+ }
+
+ return r;
+}
+
+static pdf_obj *
+parse_pdf_dict_with_tounicode (const char **pp, const char *endptr, struct tounicode *cd)
+{
+ pdf_obj *dict;
+
+ /* disable this test for XDV files, as we do UTF8 reencoding with no cmap */
+ if ((dpx_conf.compat_mode != dpx_mode_xdv_mode) && cd->cmap_id < 0) {
+ dict = parse_pdf_object_extended(pp, endptr, NULL, parse_pdf_reference, NULL);
+ if (dict && !PDF_OBJ_DICTTYPE(dict)) {
+ WARN("Dictionary type object expected but non-dictionary type found.");
+ pdf_release_obj(dict);
+ dict = NULL;
+ }
+ } else {
+ /* :( */
+ if (cd && cd->unescape_backslash) {
+ dict = parse_pdf_tainted_dict(pp, endptr, parse_pdf_reference, NULL);
+ } else {
+ dict = parse_pdf_object_extended(pp, endptr, NULL, parse_pdf_reference, NULL);
+ }
+ if (dict) {
+ if (!PDF_OBJ_DICTTYPE(dict)) {
+ WARN("Dictionary type object expected but non-dictionary type found.");
+ pdf_release_obj(dict);
+ dict = NULL;
+ } else {
+ pdf_foreach_dict(dict, modify_strings, cd);
+ }
+ }
+ }
+
+ return dict;
+}
+
+static void
+set_rect_for_annot (struct spc_env *spe, pdf_rect *rect, transform_info ti)
+{
+ pdf_coord cp, cp1, cp2, cp3, cp4;
+
+ spc_get_current_point(spe, &cp);
+
+ if (ti.flags & INFO_HAS_USER_BBOX) {
+ cp1.x = cp.x + ti.bbox.llx;
+ cp1.y = cp.y + ti.bbox.lly;
+ cp2.x = cp.x + ti.bbox.urx;
+ cp2.y = cp.y + ti.bbox.lly;
+ cp3.x = cp.x + ti.bbox.urx;
+ cp3.y = cp.y + ti.bbox.ury;
+ cp4.x = cp.x + ti.bbox.llx;
+ cp4.y = cp.y + ti.bbox.ury;
+ } else {
+ cp1.x = cp.x;
+ cp1.y = cp.y - spe->mag * ti.depth;
+ cp2.x = cp.x + spe->mag * ti.width;
+ cp2.y = cp.y - spe->mag * ti.depth;
+ cp3.x = cp.x + spe->mag * ti.width;
+ cp3.y = cp.y + spe->mag * ti.height;
+ cp4.x = cp.x;
+ cp4.y = cp.y + spe->mag * ti.height;
+ }
+ pdf_dev_transform(&cp1, NULL);
+ pdf_dev_transform(&cp2, NULL);
+ pdf_dev_transform(&cp3, NULL);
+ pdf_dev_transform(&cp4, NULL);
+ rect->llx = min4(cp1.x, cp2.x, cp3.x, cp4.x);
+ rect->lly = min4(cp1.y, cp2.y, cp3.y, cp4.y);
+ rect->urx = max4(cp1.x, cp2.x, cp3.x, cp4.x);
+ rect->ury = max4(cp1.y, cp2.y, cp3.y, cp4.y);
+}
+
+static int
+spc_handler_pdfm_annot (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ pdf_obj *annot_dict;
+ pdf_rect rect;
+ char *ident = NULL;
+ transform_info ti;
+
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr[0] == '@') {
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ skip_white(&args->curptr, args->endptr);
+ }
+
+ transform_info_clear(&ti);
+ if (spc_util_read_dimtrns(spe, &ti, args, 0) < 0) {
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ }
+
+ if ((ti.flags & INFO_HAS_USER_BBOX) &&
+ ((ti.flags & INFO_HAS_WIDTH) || (ti.flags & INFO_HAS_HEIGHT))) {
+ spc_warn(spe, "You can't specify both bbox and width/height.");
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ }
+
+ annot_dict = parse_pdf_dict_with_tounicode(&args->curptr, args->endptr, &sd->cd);
+ if (!annot_dict) {
+ spc_warn(spe, "Could not find dictionary object.");
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ } else if (!PDF_OBJ_DICTTYPE(annot_dict)) {
+ spc_warn(spe, "Invalid type: not dictionary object.");
+ if (ident)
+ RELEASE(ident);
+ pdf_release_obj(annot_dict);
+ return -1;
+ }
+
+ set_rect_for_annot(spe, &rect, ti);
+
+ /* Order is important... */
+ if (ident)
+ spc_push_object(spe, ident, pdf_link_obj(annot_dict));
+ /* Add this reference. */
+ pdf_doc_add_annot(pdf_doc_current_page_number(), &rect, annot_dict, 1);
+
+ if (ident) {
+ RELEASE(ident);
+ }
+ pdf_release_obj(annot_dict);
+
+ return 0;
+}
+
+/* NOTE: This can't have ident. See "Dvipdfm User's Manual".
+ * 1 Jul. 2020: ident allowed (upon request)
+ * Only first annotation can be accessed in line break cases.
+ */
+static int
+spc_handler_pdfm_bann (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ char *ident = NULL;
+ int error = 0;
+
+ if (sd->annot_dict) {
+ spc_warn(spe, "Can't begin an annotation when one is pending.");
+ return -1;
+ }
+
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr[0] == '@') {
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ skip_white(&args->curptr, args->endptr);
+ }
+
+ sd->annot_dict = parse_pdf_dict_with_tounicode(&args->curptr, args->endptr, &sd->cd);
+ if (!sd->annot_dict) {
+ spc_warn(spe, "Ignoring annotation with invalid dictionary.");
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ } else if (!PDF_OBJ_DICTTYPE(sd->annot_dict)) {
+ spc_warn(spe, "Invalid type: not a dictionary object.");
+ pdf_release_obj(sd->annot_dict);
+ sd->annot_dict = NULL;
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ }
+
+ error = spc_begin_annot(spe, pdf_link_obj(sd->annot_dict));
+ if (ident) {
+ spc_push_object(spe, ident, pdf_link_obj(sd->annot_dict));
+ RELEASE(ident);
+ }
+
+ return error;
+}
+
+static int
+spc_handler_pdfm_eann (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ int error = 0;
+
+ if (!sd->annot_dict) {
+ spc_warn(spe, "Tried to end an annotation without starting one!");
+ return -1;
+ }
+
+ error = spc_end_annot(spe);
+
+ pdf_release_obj(sd->annot_dict);
+ sd->annot_dict = NULL;
+
+ return error;
+}
+
+/* For supporting \phantom within bann-eann.
+ *
+ * \special{pdf:xann width 50pt height 8pt depth 1pt}
+ *
+ * tells dvipdfmx to extend the current annotation rectangle
+ * by the amount specified (witdh 50pt, height 8pt, depth 1pt)
+ * This was introduced since in the following situation
+ *
+ * \special{pdf:bann ...}\phantom{Some texts}\special{pdf:eann}
+ *
+ * annotation is not created since there is no annotation
+ * rectangle calculated due to no object being put.
+ */
+static int
+spc_handler_pdfm_xann (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_rect rect;
+ transform_info ti;
+
+ if (!spc_is_tracking_boxes(spe)) {
+ /* Silently ignore */
+ args->curptr = args->endptr;
+ return 0;
+ }
+
+ skip_white(&args->curptr, args->endptr);
+
+ transform_info_clear(&ti);
+ if (spc_util_read_dimtrns(spe, &ti, args, 0) < 0) {
+ return -1;
+ }
+
+ if ((ti.flags & INFO_HAS_USER_BBOX) &&
+ ((ti.flags & INFO_HAS_WIDTH) || (ti.flags & INFO_HAS_HEIGHT))) {
+ spc_warn(spe, "You can't specify both bbox and width/height.");
+ return -1;
+ }
+
+ set_rect_for_annot(spe, &rect, ti);
+ pdf_doc_expand_box(&rect);
+
+ return 0;
+}
+
+/* Color:.... */
+static int
+spc_handler_pdfm_bcolor (struct spc_env *spe, struct spc_arg *ap)
+{
+ int error = 0;
+ pdf_color fc, sc;
+ pdf_color *pfc, *psc;
+
+ skip_white(&ap->curptr, ap->endptr);
+
+ pdf_color_get_current(&psc, &pfc);
+ if (ap->curptr < ap->endptr &&
+ (ap->curptr[0] == 'f' || ap->curptr[0] == 's')) {
+ pdf_color_copycolor(&sc, psc);
+ pdf_color_copycolor(&fc, pfc);
+ while (!error && ap->curptr < ap->endptr) {
+ if (ap->curptr <= ap->endptr + strlen("fill") &&
+ !memcmp(ap->curptr, "fill", strlen("fill"))) {
+ ap->curptr += strlen("fill");
+ skip_white(&ap->curptr, ap->endptr);
+ error = spc_util_read_pdfcolor(spe, &fc, ap, pfc);
+ } else if (ap->curptr <= ap->endptr + strlen("stroke") &&
+ !memcmp(ap->curptr, "stroke", strlen("stroke"))) {
+ ap->curptr += strlen("stroke");
+ skip_white(&ap->curptr, ap->endptr);
+ error = spc_util_read_pdfcolor(spe, &sc, ap, psc);
+ }
+ skip_white(&ap->curptr, ap->endptr);
+ }
+ } else {
+ error = spc_util_read_pdfcolor(spe, &fc, ap, pfc);
+ if (!error) {
+ if (ap->curptr < ap->endptr) {
+ error = spc_util_read_pdfcolor(spe, &sc, ap, psc);
+ } else {
+ pdf_color_copycolor(&sc, &fc);
+ }
+ }
+ }
+
+ if (error) {
+ spc_warn(spe, "Invalid color specification?");
+ } else {
+ skip_white(&ap->curptr, ap->endptr);
+ pdf_color_push(&sc, &fc); /* save currentcolor */
+ }
+
+ return error;
+}
+
+/*
+ * This special changes the current color without clearing the color stack.
+ * It therefore differs from "color rgb 1 0 0".
+ */
+static int
+spc_handler_pdfm_scolor (struct spc_env *spe, struct spc_arg *ap)
+{
+ int error = 0;
+ pdf_color fc, sc;
+ pdf_color *pfc, *psc;
+
+ skip_white(&ap->curptr, ap->endptr);
+
+ pdf_color_get_current(&psc, &pfc);
+ if (ap->curptr < ap->endptr &&
+ (ap->curptr[0] == 'f' || ap->curptr[0] == 's')) {
+ pdf_color_copycolor(&sc, psc);
+ pdf_color_copycolor(&fc, pfc);
+ while (!error && ap->curptr < ap->endptr) {
+ if (ap->curptr <= ap->endptr + strlen("fill") &&
+ !memcmp(ap->curptr, "fill", strlen("fill"))) {
+ ap->curptr += strlen("fill");
+ skip_white(&ap->curptr, ap->endptr);
+ error = spc_util_read_pdfcolor(spe, &fc, ap, pfc);
+ } else if (ap->curptr <= ap->endptr + strlen("stroke") &&
+ !memcmp(ap->curptr, "stroke", strlen("stroke"))) {
+ ap->curptr += strlen("stroke");
+ skip_white(&ap->curptr, ap->endptr);
+ error = spc_util_read_pdfcolor(spe, &sc, ap, psc);
+ }
+ skip_white(&ap->curptr, ap->endptr);
+ }
+ } else {
+ error = spc_util_read_pdfcolor(spe, &fc, ap, pfc);
+ if (!error) {
+ if (ap->curptr < ap->endptr) {
+ error = spc_util_read_pdfcolor(spe, &sc, ap, psc);
+ } else {
+ pdf_color_copycolor(&sc, &fc);
+ }
+ }
+ }
+
+ if (error) {
+ spc_warn(spe, "Invalid color specification?");
+ } else {
+ pdf_color_set(&sc, &fc);
+ }
+
+ return error;
+}
+
+static int
+spc_handler_pdfm_ecolor (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_color_pop();
+ return 0;
+}
+
+
+static int
+spc_handler_pdfm_btrans (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_tmatrix M;
+ pdf_coord cp;
+ transform_info ti;
+
+ transform_info_clear(&ti);
+ if (spc_util_read_dimtrns(spe, &ti, args, 0) < 0) {
+ return -1;
+ }
+
+ /* btrans inside bcontent-econtent bug fix.
+ * I don't know if this is the only place needs to be fixed...
+ */
+ spc_get_current_point(spe, &cp);
+ /* Create transformation matrix */
+ pdf_copymatrix(&M, &(ti.matrix));
+ M.e += ((1.0 - M.a) * cp.x - M.c * cp.y);
+ M.f += ((1.0 - M.d) * cp.y - M.b * cp.x);
+
+ pdf_dev_gsave();
+ pdf_dev_concat(&M);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_etrans (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_dev_grestore();
+
+ /*
+ * Unfortunately, the following line is necessary in case
+ * of a color change inside of the save/restore pair.
+ * (Font changes are automatically corrected by pdf_dev_grestore().)
+ * Anything that was done there must be redone, so in effect,
+ * we make no assumptions about what fonts. We act like we are
+ * starting a new page.
+ */
+ pdf_dev_reset_color(0);
+ pdf_dev_reset_xgstate(0);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_outline (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ pdf_obj *item_dict, *tmp;
+ int level, is_open = -1;
+ int current_depth;
+
+ skip_white(&args->curptr, args->endptr);
+
+ /*
+ * pdf:outline is extended to support open/close feature
+ *
+ * pdf:outline 1 ... (as DVIPDFM)
+ * pdf:outline [] 1 ... (open bookmark)
+ * pdf:outline [-] 1 ... (closed bookmark)
+ */
+ if (args->curptr+3 < args->endptr && *args->curptr == '[') {
+ args->curptr++;
+ if (*args->curptr == '-') {
+ args->curptr++;
+ } else {
+ is_open = 1;
+ }
+ args->curptr++;
+ }
+ skip_white(&args->curptr, args->endptr);
+
+ tmp = parse_pdf_object(&args->curptr, args->endptr, NULL);
+ if (!tmp) {
+ spc_warn(spe, "Missing number for outline item depth.");
+ return -1;
+ } else if (!PDF_OBJ_NUMBERTYPE(tmp)) {
+ pdf_release_obj(tmp);
+ spc_warn(spe, "Expecting number for outline item depth.");
+ return -1;
+ }
+
+ item_dict = NULL;
+
+ level = (int) pdf_number_value(tmp);
+ pdf_release_obj(tmp);
+
+ /* What is this? Starting at level 3 and can go down to level 1?
+ *
+ * Here is the original comment:
+ * Make sure we know where the starting level is
+ *
+ * NOTE: added
+ * We need this for converting pages from 3rd to... :(
+ */
+ sd->lowest_level = MIN(sd->lowest_level, level);
+
+ level += 1 - sd->lowest_level;
+
+ item_dict = parse_pdf_dict_with_tounicode(&args->curptr, args->endptr, &sd->cd);
+ if (!item_dict) {
+ spc_warn(spe, "Ignoring invalid dictionary.");
+ return -1;
+ }
+ current_depth = pdf_doc_bookmarks_depth();
+ if (current_depth > level) {
+ while (current_depth-- > level)
+ pdf_doc_bookmarks_up();
+ } else if (current_depth < level) {
+ while (current_depth++ < level)
+ pdf_doc_bookmarks_down();
+ }
+
+ pdf_doc_bookmarks_add(item_dict, is_open);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_article (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ char *ident;
+ pdf_obj *info_dict;
+
+ skip_white (&args->curptr, args->endptr);
+
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ if (!ident) {
+ spc_warn(spe, "Article name expected but not found.");
+ return -1;
+ }
+
+ info_dict = parse_pdf_dict_with_tounicode(&args->curptr, args->endptr, &sd->cd);
+ if (!info_dict) {
+ spc_warn(spe, "Ignoring article with invalid info dictionary.");
+ RELEASE(ident);
+ return -1;
+ }
+
+ pdf_doc_begin_article(ident, pdf_link_obj(info_dict));
+ spc_push_object(spe, ident, info_dict);
+ RELEASE(ident);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_bead (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ pdf_obj *article;
+ pdf_obj *article_info;
+ char *article_name;
+ pdf_rect rect;
+ int page_no;
+ transform_info ti;
+
+ skip_white(&args->curptr, args->endptr);
+
+ if (args->curptr[0] != '@') {
+ spc_warn(spe, "Article identifier expected but not found.");
+ return -1;
+ }
+
+ article_name = parse_opt_ident(&args->curptr, args->endptr);
+ if (!article_name) {
+ spc_warn(spe, "Article reference expected but not found.");
+ return -1;
+ }
+
+ /* If okay so far, try to get a bounding box */
+ transform_info_clear(&ti);
+ if (spc_util_read_dimtrns(spe, &ti, args, 0) < 0) {
+ RELEASE(article_name);
+ return -1;
+ }
+
+ if ((ti.flags & INFO_HAS_USER_BBOX) &&
+ ((ti.flags & INFO_HAS_WIDTH) || (ti.flags & INFO_HAS_HEIGHT))) {
+ spc_warn(spe, "You can't specify both bbox and width/height.");
+ RELEASE(article_name);
+ return -1;
+ }
+
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr[0] != '<') {
+ article_info = pdf_new_dict();
+ } else {
+ article_info = parse_pdf_dict_with_tounicode(&args->curptr, args->endptr, &sd->cd);
+ if (!article_info) {
+ spc_warn(spe, "Error in reading dictionary.");
+ RELEASE(article_name);
+ return -1;
+ }
+ }
+
+ /* Does this article exist yet */
+ article = spc_lookup_object(article_name);
+ if (article) {
+ pdf_merge_dict (article, article_info);
+ pdf_release_obj(article_info);
+ } else {
+ pdf_doc_begin_article(article_name, pdf_link_obj(article_info));
+ spc_push_object(spe, article_name, article_info);
+ }
+ page_no = pdf_doc_current_page_number();
+ set_rect_for_annot(spe, &rect, ti);
+ pdf_doc_add_bead(article_name, NULL, page_no, &rect);
+
+ RELEASE(article_name);
+ return 0;
+}
+
+static int
+spc_handler_pdfm_image (struct spc_env *spe, struct spc_arg *args)
+{
+ int xobj_id;
+ char *ident = NULL;
+ pdf_obj *fspec;
+ transform_info ti;
+ load_options options = {1, 0, NULL};
+
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr[0] == '@') {
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ skip_white(&args->curptr, args->endptr);
+ }
+
+ /* 2015/12/29
+ * There should not be "page" and "pagebox" in read_dimtrns().
+ * It is for reading "dimensions" and "transformations" and "page" is
+ * completely unrelated.
+ */
+ transform_info_clear(&ti);
+ if (spc_util_read_blahblah(spe, &ti,
+ &options.page_no, &options.bbox_type, args) < 0) {
+ spc_warn(spe, "Reading option field in pdf:image failed.");
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ }
+
+ skip_white(&args->curptr, args->endptr);
+ fspec = parse_pdf_object(&args->curptr, args->endptr, NULL);
+ if (!fspec) {
+ spc_warn(spe, "Missing filename string for pdf:image.");
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ } else if (!PDF_OBJ_STRINGTYPE(fspec)) {
+ spc_warn(spe, "Missing filename string for pdf:image.");
+ pdf_release_obj(fspec);
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ }
+
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr < args->endptr) {
+ options.dict = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ }
+
+ xobj_id = pdf_ximage_load_image(ident, pdf_string_value(fspec), options);
+
+ if (xobj_id < 0) {
+ spc_warn(spe, "Could not find image resource...");
+ pdf_release_obj(fspec);
+ if (ident)
+ RELEASE(ident);
+ return -1;
+ }
+
+ if (!(ti.flags & INFO_DO_HIDE)) {
+ spc_put_image(spe, xobj_id, &ti, spe->x_user, spe->y_user);
+ }
+
+ if (ident) {
+ if ((dpx_conf.compat_mode == dpx_mode_compat_mode) &&
+ pdf_ximage_get_subtype(xobj_id) == PDF_XOBJECT_TYPE_IMAGE)
+ pdf_ximage_set_attr(xobj_id, 1, 1, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0);
+ RELEASE(ident);
+ }
+
+ pdf_release_obj(fspec);
+
+ return 0;
+}
+
+/* Use do_names instead. */
+static int
+spc_handler_pdfm_dest (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_obj *name, *array;
+
+ skip_white(&args->curptr, args->endptr);
+
+ name = parse_pdf_object(&args->curptr, args->endptr, NULL);
+ if (!name) {
+ spc_warn(spe, "PDF string expected for destination name but not found.");
+ return -1;
+ } else if (!PDF_OBJ_STRINGTYPE(name)) {
+ spc_warn(spe, "PDF string expected for destination name but invalid type.");
+ pdf_release_obj(name);
+ return -1;
+ }
+
+#if 0
+ if ((dpx_conf.compat_mode == dpx_mode_xdv_mode) && maybe_reencode_utf8(name) < 0)
+ WARN("Failed to convert input string to UTF16...");
+#endif
+
+ array = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ if (!array) {
+ spc_warn(spe, "No destination specified for pdf:dest.");
+ pdf_release_obj(name);
+ return -1;
+ } else if (!PDF_OBJ_ARRAYTYPE(array)) {
+ spc_warn(spe, "Destination not specified as an array object!");
+ pdf_release_obj(name);
+ pdf_release_obj(array);
+ return -1;
+ }
+
+ pdf_doc_add_names("Dests",
+ pdf_string_value (name),
+ pdf_string_length(name),
+ array);
+ pdf_release_obj(name);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_names (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_obj *category, *key, *value, *tmp;
+ int i, size;
+
+ category = parse_pdf_object(&args->curptr, args->endptr, NULL);
+ if (!category) {
+ spc_warn(spe, "PDF name expected but not found.");
+ return -1;
+ } else if (!PDF_OBJ_NAMETYPE(category)) {
+ spc_warn(spe, "PDF name expected but not found.");
+ pdf_release_obj(category);
+ return -1;
+ }
+
+ tmp = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ if (!tmp) {
+ spc_warn(spe, "PDF object expected but not found.");
+ pdf_release_obj(category);
+ return -1;
+ } else if (PDF_OBJ_ARRAYTYPE(tmp)) {
+ size = pdf_array_length(tmp);
+ if (size % 2 != 0) {
+ spc_warn(spe, "Array size not multiple of 2 for pdf:names.");
+ pdf_release_obj(category);
+ pdf_release_obj(tmp);
+ return -1;
+ }
+
+ for (i = 0; i < size / 2; i++) {
+ key = pdf_get_array(tmp, 2 * i);
+ value = pdf_get_array(tmp, 2 * i + 1);
+ if (!PDF_OBJ_STRINGTYPE(key)) {
+ spc_warn(spe, "Name tree key must be string.");
+ pdf_release_obj(category);
+ pdf_release_obj(tmp);
+ return -1;
+ } else if (pdf_doc_add_names(pdf_name_value(category),
+ pdf_string_value (key),
+ pdf_string_length(key),
+ pdf_link_obj(value)) < 0) {
+ spc_warn(spe, "Failed to add Name tree entry...");
+ pdf_release_obj(category);
+ pdf_release_obj(tmp);
+ return -1;
+ }
+ }
+ pdf_release_obj(tmp);
+ } else if (PDF_OBJ_STRINGTYPE(tmp)) {
+ key = tmp;
+ value = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ if (!value) {
+ pdf_release_obj(category);
+ pdf_release_obj(key);
+ spc_warn(spe, "PDF object expected but not found.");
+ return -1;
+ }
+ if (pdf_doc_add_names(pdf_name_value(category),
+ pdf_string_value (key),
+ pdf_string_length(key),
+ value) < 0) {
+ spc_warn(spe, "Failed to add Name tree entry...");
+ pdf_release_obj(category);
+ pdf_release_obj(key);
+ return -1;
+ }
+ pdf_release_obj(key);
+ } else {
+ pdf_release_obj(tmp);
+ pdf_release_obj(category);
+ spc_warn(spe, "Invalid object type for pdf:names.");
+ return -1;
+ }
+ pdf_release_obj(category);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_docinfo (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ pdf_obj *docinfo, *dict;
+
+ dict = parse_pdf_dict_with_tounicode(&args->curptr, args->endptr, &sd->cd);
+ if (!dict) {
+ spc_warn(spe, "Dictionary object expected but not found.");
+ return -1;
+ }
+
+ docinfo = pdf_doc_docinfo();
+ pdf_merge_dict(docinfo, dict);
+ pdf_release_obj(dict);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_docview (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ pdf_obj *catalog, *dict;
+ pdf_obj *pref_old, *pref_add;
+
+ dict = parse_pdf_dict_with_tounicode(&args->curptr, args->endptr, &sd->cd);
+ if (!dict) {
+ spc_warn(spe, "Dictionary object expected but not found.");
+ return -1;
+ }
+
+ catalog = pdf_doc_catalog();
+ /* Avoid overriding whole ViewerPreferences */
+ pref_old = pdf_lookup_dict(catalog, "ViewerPreferences");
+ pref_add = pdf_lookup_dict(dict, "ViewerPreferences");
+ if (pref_old && pref_add) {
+ pdf_merge_dict (pref_old, pref_add);
+ pdf_remove_dict(dict, "ViewerPreferences");
+ }
+ pdf_merge_dict (catalog, dict);
+ pdf_release_obj(dict);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_close (struct spc_env *spe, struct spc_arg *args)
+{
+ char *ident;
+
+ skip_white(&args->curptr, args->endptr);
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ if (ident) {
+ spc_flush_object(spe, ident);
+ RELEASE(ident);
+ } else { /* Close all? */
+ spc_warn(spe, "pdf:close without an argument no longer supported!");
+ spc_clear_objects(spe);
+ }
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_object (struct spc_env *spe, struct spc_arg *args)
+{
+ char *ident;
+ pdf_obj *object;
+
+ skip_white(&args->curptr, args->endptr);
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ if (!ident) {
+ spc_warn(spe, "Could not find a object identifier.");
+ return -1;
+ }
+
+ object = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ if (!object) {
+ spc_warn(spe, "Could not find an object definition for \"%s\".", ident);
+ RELEASE(ident);
+ return -1;
+ } else {
+ spc_push_object(spe, ident, object);
+ }
+ RELEASE(ident);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_content (struct spc_env *spe, struct spc_arg *args)
+{
+ int len = 0;
+
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr < args->endptr) {
+ pdf_tmatrix M;
+ pdf_coord cp;
+
+ spc_get_current_point(spe, &cp);
+ pdf_setmatrix(&M, 1.0, 0.0, 0.0, 1.0, cp.x, cp.y);
+ work_buffer[len++] = ' ';
+ work_buffer[len++] = 'q';
+ work_buffer[len++] = ' ';
+ len += pdf_sprint_matrix(work_buffer + len, &M);
+ work_buffer[len++] = ' ';
+ work_buffer[len++] = 'c';
+ work_buffer[len++] = 'm';
+ work_buffer[len++] = ' ';
+
+ pdf_doc_add_page_content(work_buffer, len); /* op: q cm */
+ len = (int) (args->endptr - args->curptr);
+ pdf_doc_add_page_content(args->curptr, len); /* op: ANY */
+ pdf_doc_add_page_content(" Q", 2); /* op: Q */
+ }
+ args->curptr = args->endptr;
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_literal (struct spc_env *spe, struct spc_arg *args)
+{
+ int direct = 0;
+
+ skip_white(&args->curptr, args->endptr);
+ while (args->curptr < args->endptr) {
+ if (args->curptr + 7 <= args->endptr &&
+ !strncmp(args->curptr, "reverse", 7)) {
+ args->curptr += 7;
+ WARN("The special \"pdf:literal reverse ...\" is no longer supported.\nIgnore the \"reverse\" option.");
+ } else if (args->curptr + 6 <= args->endptr &&
+ !strncmp(args->curptr, "direct", 6)) {
+ direct = 1;
+ args->curptr += 6;
+ } else {
+ break;
+ }
+ skip_white(&args->curptr, args->endptr);
+ }
+
+ if (args->curptr < args->endptr) {
+ pdf_tmatrix M;
+ pdf_coord cp;
+
+ spc_get_current_point(spe, &cp);
+ if (!direct) {
+ M.a = M.d = 1.0; M.b = M.c = 0.0;
+ M.e = cp.x; M.f = cp.y;
+ pdf_dev_concat(&M);
+ }
+ pdf_doc_add_page_content(" ", 1); /* op: */
+ pdf_doc_add_page_content(args->curptr, (int) (args->endptr - args->curptr)); /* op: ANY */
+ if (!direct) {
+ M.e = -cp.x; M.f = -cp.y;
+ pdf_dev_concat(&M);
+ }
+ }
+
+ args->curptr = args->endptr;
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_bcontent (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_tmatrix M;
+ double xpos, ypos;
+
+ pdf_dev_gsave();
+ spc_get_coord(spe, &xpos, &ypos);
+ pdf_setmatrix(&M, 1.0, 0.0, 0.0, 1.0, spe->x_user - xpos, spe->y_user - ypos);
+ pdf_dev_concat(&M);
+ spc_push_coord(spe, spe->x_user, spe->y_user);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_econtent (struct spc_env *spe, struct spc_arg *args)
+{
+ spc_pop_coord(spe);
+ pdf_dev_grestore();
+ pdf_dev_reset_color(0);
+ pdf_dev_reset_xgstate(0);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_code (struct spc_env *spe, struct spc_arg *args)
+{
+ skip_white(&args->curptr, args->endptr);
+
+ if (args->curptr < args->endptr) {
+ pdf_doc_add_page_content(" ", 1); /* op: */
+ pdf_doc_add_page_content(args->curptr, (int) (args->endptr - args->curptr)); /* op: ANY */
+ args->curptr = args->endptr;
+ }
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_do_nothing (struct spc_env *spe, struct spc_arg *args)
+{
+ args->curptr = args->endptr;
+ return 0;
+}
+
+#define STRING_STREAM 0
+#define FILE_STREAM 1
+
+static int
+spc_handler_pdfm_stream_with_type (struct spc_env *spe, struct spc_arg *args, int type)
+{
+ pdf_obj *fstream;
+ int nb_read;
+ char *ident, *instring, *fullname;
+ pdf_obj *tmp;
+ FILE *fp;
+
+ skip_white(&args->curptr, args->endptr);
+
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ if (!ident) {
+ spc_warn(spe, "Missing objname for pdf:(f)stream.");
+ return -1;
+ }
+
+ skip_white(&args->curptr, args->endptr);
+
+ tmp = parse_pdf_object(&args->curptr, args->endptr, NULL);
+ if (!tmp) {
+ spc_warn(spe, "Missing input string for pdf:(f)stream.");
+ RELEASE(ident);
+ return -1;
+ } else if (!PDF_OBJ_STRINGTYPE(tmp)) {
+ spc_warn(spe, "Invalid type of input string for pdf:(f)stream.");
+ pdf_release_obj(tmp);
+ RELEASE(ident);
+ return -1;
+ }
+
+ instring = pdf_string_value(tmp);
+
+ switch (type) {
+ case FILE_STREAM:
+ if (!instring) {
+ spc_warn(spe, "Missing filename for pdf:fstream.");
+ pdf_release_obj(tmp);
+ RELEASE(ident);
+ return -1;
+ }
+ fullname = kpse_find_pict(instring);
+ if (!fullname) {
+ spc_warn(spe, "File \"%s\" not found.", instring);
+ pdf_release_obj(tmp);
+ RELEASE(ident);
+ return -1;
+ }
+ fp = DPXFOPEN(fullname, DPX_RES_TYPE_BINARY);
+ if (!fp) {
+ spc_warn(spe, "Could not open file: %s", instring);
+ pdf_release_obj(tmp);
+ RELEASE(ident);
+ RELEASE(fullname);
+ return -1;
+ }
+ fstream = pdf_new_stream(STREAM_COMPRESS);
+ while ((nb_read =
+ fread(work_buffer, sizeof(char), WORK_BUFFER_SIZE, fp)) > 0)
+ pdf_add_stream(fstream, work_buffer, nb_read);
+ MFCLOSE(fp);
+ RELEASE(fullname);
+ break;
+ case STRING_STREAM:
+ fstream = pdf_new_stream(STREAM_COMPRESS);
+ pdf_add_stream(fstream, pdf_string_value(tmp), pdf_string_length(tmp));
+ break;
+ default:
+ pdf_release_obj(tmp);
+ RELEASE(ident);
+ return -1;
+ }
+ pdf_release_obj(tmp);
+
+ /*
+ * Optional dict.
+ *
+ * TODO: check Length, Filter...
+ */
+ skip_white(&args->curptr, args->endptr);
+
+ if (args->curptr[0] == '<') {
+ pdf_obj *stream_dict;
+
+ stream_dict = pdf_stream_dict(fstream);
+
+ tmp = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ if (!tmp) {
+ spc_warn(spe, "Parsing dictionary failed.");
+ pdf_release_obj(fstream);
+ RELEASE(ident);
+ return -1;
+ } else if (!PDF_OBJ_DICTTYPE(tmp)) {
+ spc_warn(spe, "Expecting dictionary type object but non-dictionary type found.");
+ pdf_release_obj(fstream);
+ pdf_release_obj(tmp);
+ RELEASE(ident);
+ return -1;
+ }
+ if (pdf_lookup_dict(tmp, "Length")) {
+ pdf_remove_dict(tmp, "Length");
+ } else if (pdf_lookup_dict(tmp, "Filter")) {
+ pdf_remove_dict(tmp, "Filter");
+ }
+ pdf_merge_dict(stream_dict, tmp);
+ pdf_release_obj(tmp);
+ }
+
+ /* Users should explicitly close this. */
+ spc_push_object(spe, ident, fstream);
+ RELEASE(ident);
+
+ return 0;
+}
+
+/*
+ * STREAM: Create a PDF stream object from an input string.
+ *
+ * pdf: stream @objname (input_string) [PDF_DICT]
+ */
+static int
+spc_handler_pdfm_stream (struct spc_env *spe, struct spc_arg *args)
+{
+ return spc_handler_pdfm_stream_with_type (spe, args, STRING_STREAM);
+}
+
+/*
+ * FSTREAM: Create a PDF stream object from an existing file.
+ *
+ * pdf: fstream @objname (filename) [PDF_DICT]
+ */
+static int
+spc_handler_pdfm_fstream (struct spc_env *spe, struct spc_arg *args)
+{
+ return spc_handler_pdfm_stream_with_type (spe, args, FILE_STREAM);
+}
+
+/* Grab page content as follows:
+ *
+ * Reference point = (x_user, y_user)
+ *
+ * Case 1. \special{pdf:bxobj @obj width WD height HT depth DP}
+ *
+ * Grab the box with the lower-left corner (x_user, y_user-DP)
+ * and the upper right corner (x_user+WD, y_user+HT).
+ *
+ * Case 2. \special{pdf:bxobj @obj bbox LLX LLY URX, URY}
+ *
+ * Grab the box with the lower-left corner (x_user+LLX, y_user+LLY)
+ * and the upper right corner (x_user+URX, y_user+URY).
+ *
+ * Note that scale, xscale, yscale, xoffset, yoffset options are ignored.
+ */
+static int
+spc_handler_pdfm_bform (struct spc_env *spe, struct spc_arg *args)
+{
+ int error;
+ char *ident;
+ pdf_rect cropbox;
+ pdf_coord cp;
+ transform_info ti;
+
+ skip_white(&args->curptr, args->endptr);
+
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ if (!ident) {
+ spc_warn(spe, "A form XObject must have name.");
+ return -1;
+ }
+
+ transform_info_clear(&ti);
+ if (spc_util_read_dimtrns(spe, &ti, args, 0) < 0) {
+ RELEASE(ident);
+ return -1;
+ }
+
+ /* A XForm with zero dimension results in a non-invertible transformation
+ * matrix. And it may result in unpredictable behaviour. It might be an
+ * error in Acrobat. Bounding box with zero dimension may cause division
+ * by zero.
+ */
+ if (ti.flags & INFO_HAS_USER_BBOX) {
+ if (ti.bbox.urx - ti.bbox.llx == 0.0 ||
+ ti.bbox.ury - ti.bbox.lly == 0.0) {
+ spc_warn(spe, "Bounding box has a zero dimension.");
+ RELEASE(ident);
+ return -1;
+ }
+ cropbox.llx = ti.bbox.llx;
+ cropbox.lly = ti.bbox.lly;
+ cropbox.urx = ti.bbox.urx;
+ cropbox.ury = ti.bbox.ury;
+ } else {
+ if (ti.width == 0.0 ||
+ ti.depth + ti.height == 0.0) {
+ spc_warn(spe, "Bounding box has a zero dimension.");
+ RELEASE(ident);
+ return -1;
+ }
+ cropbox.llx = 0.0;
+ cropbox.lly = -ti.depth;
+ cropbox.urx = ti.width;
+ cropbox.ury = ti.height;
+ }
+
+ spc_get_current_point(spe, &cp);
+ error = spc_begin_form(spe, ident, cp, &cropbox);
+
+ if (error)
+ spc_warn(spe, "Couldn't start form object.");
+
+ RELEASE(ident);
+
+ return error;
+}
+
+/* An extra dictionary after exobj must be merged to the form dictionary,
+ * not resource dictionary.
+ * Please use pdf:put @resources (before pdf:exobj) instead.
+ */
+static int
+spc_handler_pdfm_eform (struct spc_env *spe, struct spc_arg *args)
+{
+ int error;
+ pdf_obj *attrib = NULL;
+ struct spc_pdf_ *sd = &_pdf_stat;
+
+ skip_white(&args->curptr, args->endptr);
+
+ if (args->curptr < args->endptr) {
+ attrib = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ if (attrib && !PDF_OBJ_DICTTYPE(attrib)) {
+ pdf_release_obj(attrib);
+ attrib = NULL;
+ }
+ }
+ /* pageresources here too */
+ if (sd->pageresources) {
+ pdf_foreach_dict(sd->pageresources, forallresourcecategory, NULL);
+ }
+ error = spc_end_form(spe, attrib);
+
+ return error;
+}
+
+/* Saved XObjects can be used as follows:
+ *
+ * Reference point = (x_user, y_user)
+ *
+ * Case 1. \special{pdf:uxobj @obj width WD height HT depth DP}
+ *
+ * Scale the XObject to fit in the box
+ * [x_user, y_user-DP, x_user+WD, y_user+HT].
+ *
+ * Case 2. \special{pdf:uxobj @obj xscale XS yscale YS}
+ *
+ * Scale the XObject with XS and YS. Note that width and xscale
+ * or height and yscale cannot be used together.
+ *
+ * Case 3. \special{pdf:bxobj @obj bbox LLX LLY URX, URY}
+ *
+ * Scale the XObject to fit in the box
+ * [x_user+LLX, y_user+LLY, x_user+URX, y_user+URY].
+ *
+ * Note that xoffset and yoffset moves the reference point where the
+ * lower-left corner of the XObject will be put.
+ */
+static int
+spc_handler_pdfm_uxobj (struct spc_env *spe, struct spc_arg *args)
+{
+ int xobj_id;
+ char *ident;
+ transform_info ti;
+
+ skip_white(&args->curptr, args->endptr);
+
+ ident = parse_opt_ident(&args->curptr, args->endptr);
+ if (!ident) {
+ spc_warn(spe, "No object identifier given.");
+ return -1;
+ }
+
+ transform_info_clear(&ti);
+ if (args->curptr < args->endptr) {
+ if (spc_util_read_dimtrns(spe, &ti, args, 0) < 0) {
+ RELEASE(ident);
+ return -1;
+ }
+ }
+
+ xobj_id = pdf_ximage_findresource(ident);
+ if (xobj_id < 0) {
+ xobj_id = pdf_ximage_reserve(ident);
+ }
+
+ spc_put_image(spe, xobj_id, &ti, spe->x_user, spe->y_user);
+ RELEASE(ident);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_link (struct spc_env *spe, struct spc_arg *args)
+{
+ return spc_resume_annot(spe);
+}
+
+static int
+spc_handler_pdfm_nolink (struct spc_env *spe, struct spc_arg *args)
+{
+ return spc_suspend_annot(spe);
+}
+
+
+
+/* Handled at BOP */
+static int
+spc_handler_pdfm_pagesize (struct spc_env *spe, struct spc_arg *args)
+{
+ args->curptr = args->endptr;
+
+ return 0;
+}
+
+/* Please remove this.
+ * This should be handled before processing pages!
+ */
+static int
+spc_handler_pdfm_bgcolor (struct spc_env *spe, struct spc_arg *args)
+{
+ int error;
+ pdf_color colorspec;
+
+ error = spc_util_read_pdfcolor(spe, &colorspec, args, NULL);
+ if (error)
+ spc_warn(spe, "No valid color specified?");
+ else {
+ pdf_doc_set_bgcolor(&colorspec);
+ }
+
+ return error;
+}
+
+#define THEBUFFLENGTH 1024
+static int
+spc_handler_pdfm_mapline (struct spc_env *spe, struct spc_arg *ap)
+{
+ fontmap_rec *mrec;
+ char *map_name, opchr;
+ int error = 0;
+ static char buffer[THEBUFFLENGTH];
+ const char *p;
+ char *q;
+ int count;
+
+ skip_white(&ap->curptr, ap->endptr);
+ if (ap->curptr >= ap->endptr) {
+ spc_warn(spe, "Empty mapline special?");
+ return -1;
+ }
+
+ opchr = ap->curptr[0];
+ if (opchr == '-' || opchr == '+')
+ ap->curptr++;
+
+ skip_white(&ap->curptr, ap->endptr);
+
+ switch (opchr) {
+ case '-':
+ map_name = parse_ident(&ap->curptr, ap->endptr);
+ if (map_name) {
+ pdf_remove_fontmap_record(map_name);
+ RELEASE(map_name);
+ } else {
+ spc_warn(spe, "Invalid fontmap line: Missing TFM name.");
+ error = -1;
+ }
+ break;
+ default:
+ p = ap->curptr;
+ q = buffer;
+ count = 0;
+ while (p < ap->endptr && count < THEBUFFLENGTH - 1) {
+ *q++ = *p++;
+ count++;
+ }
+ if (count == THEBUFFLENGTH - 1) {
+ spc_warn(spe, "Invalid fontmap line: Too long a line.");
+ *q = 0;
+ return -1;
+ }
+ *q = '\0';
+ mrec = NEW(1, fontmap_rec);
+ pdf_init_fontmap_record(mrec);
+ error = pdf_read_fontmap_line(mrec, buffer, (int) (ap->endptr - ap->curptr), is_pdfm_mapline(buffer));
+ if (error)
+ spc_warn(spe, "Invalid fontmap line.");
+ else if (opchr == '+')
+ pdf_append_fontmap_record(mrec->map_name, mrec);
+ else
+ pdf_insert_fontmap_record(mrec->map_name, mrec);
+ pdf_clear_fontmap_record(mrec);
+ RELEASE(mrec);
+ break;
+ }
+ if (!error)
+ ap->curptr = ap->endptr;
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_mapfile (struct spc_env *spe, struct spc_arg *args)
+{
+ char *mapfile;
+ int mode, error = 0;
+
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr >= args->endptr)
+ return 0;
+
+ switch (args->curptr[0]) {
+ case '-':
+ mode = FONTMAP_RMODE_REMOVE;
+ args->curptr++;
+ break;
+ case '+':
+ mode = FONTMAP_RMODE_APPEND;
+ args->curptr++;
+ break;
+ default:
+ mode = FONTMAP_RMODE_REPLACE;
+ break;
+ }
+
+ mapfile = parse_val_ident(&args->curptr, args->endptr);
+ if (!mapfile) {
+ spc_warn(spe, "No fontmap file specified.");
+ return -1;
+ } else {
+ error = pdf_load_fontmap_file(mapfile, mode);
+ }
+ RELEASE(mapfile);
+
+ return error;
+}
+
+
+static int
+spc_handler_pdfm_tounicode (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ char *cmap_name;
+ pdf_obj *taint_keys;
+
+ /* First clear */
+ sd->cd.cmap_id = -1;
+ sd->cd.unescape_backslash = 0;
+
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr >= args->endptr) {
+ spc_warn(spe, "Missing CMap name for pdf:tounicode.");
+ return -1;
+ }
+
+ /* _FIXME_
+ * Any valid char allowed for PDF name object should be allowed here.
+ * The argument to this special should be a PDF name obejct.
+ * But it's too late to change this special.
+ */
+ cmap_name = parse_ident(&args->curptr, args->endptr);
+ if (!cmap_name) {
+ spc_warn(spe, "Missing ToUnicode mapping name...");
+ return -1;
+ }
+
+ sd->cd.cmap_id = CMap_cache_find(cmap_name);
+ if (sd->cd.cmap_id < 0) {
+ spc_warn(spe, "Failed to load ToUnicode mapping: %s", cmap_name);
+ RELEASE(cmap_name);
+ return -1;
+ }
+
+ /* Shift-JIS like encoding may contain backslash in 2nd byte.
+ * WARNING: This will add nasty extension to PDF parser.
+ */
+ if (sd->cd.cmap_id >= 0) {
+ if (strstr(cmap_name, "RKSJ") ||
+ strstr(cmap_name, "B5") ||
+ strstr(cmap_name, "GBK") ||
+ strstr(cmap_name, "KSC"))
+ sd->cd.unescape_backslash = 1;
+ }
+ RELEASE(cmap_name);
+
+ /* Additional "taint key"
+ * An array of PDF name objects can be supplied optionally.
+ * Dictionary entries specified by this option will be added to the list
+ * of dictionary keys to be treated as the target of "ToUnicode" conversion.
+ */
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr < args->endptr) {
+ taint_keys = parse_pdf_object(&args->curptr, args->endptr, NULL);
+ if (taint_keys) {
+ if (PDF_OBJ_ARRAYTYPE(taint_keys)) {
+ int i;
+ for (i = 0; i < pdf_array_length(taint_keys); i++) {
+ pdf_obj *key;
+
+ key = pdf_get_array(taint_keys, i);
+ if (PDF_OBJ_NAMETYPE(key))
+ pdf_add_array(sd->cd.taintkeys, pdf_link_obj(key));
+ else {
+ spc_warn(spe, "Invalid argument specified in pdf:tounicode special.");
+ }
+ }
+ } else {
+ spc_warn(spe, "Invalid argument specified in pdf:unicode special.");
+ }
+ pdf_release_obj(taint_keys);
+ }
+ }
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_pageresources (struct spc_env *spe, struct spc_arg *args)
+{
+ struct spc_pdf_ *sd = &_pdf_stat;
+ pdf_obj *dict;
+
+ dict = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ if (!dict) {
+ spc_warn(spe, "Dictionary object expected but not found.");
+ return -1;
+ }
+
+ if (sd->pageresources)
+ pdf_release_obj(sd->pageresources);
+ sd->pageresources = dict;
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_bxgstate (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_obj *obj;
+
+ skip_white(&args->curptr, args->endptr);
+ obj = parse_pdf_object_extended(&args->curptr, args->endptr, NULL, parse_pdf_reference, spe);
+ if (!obj) {
+ spc_warn(spe, "Could not find an object definition.");
+ return -1;
+ } else if (!PDF_OBJ_DICTTYPE(obj)) {
+ spc_warn(spe, "Parsed object for ExtGState not a dictionary object!");
+ pdf_release_obj(obj);
+ return -1;
+ }
+ pdf_dev_xgstate_push(obj);
+
+ skip_white(&args->curptr, args->endptr);
+
+ return 0;
+}
+
+static int
+spc_handler_pdfm_exgstate (struct spc_env *spe, struct spc_arg *args)
+{
+ pdf_dev_xgstate_pop();
+ skip_white(&args->curptr, args->endptr);
+ return 0;
+}
+
+static int
+spc_handler_pdft_compat_page (struct spc_env *spe, struct spc_arg *args)
+{
+ skip_white(&args->curptr, args->endptr);
+ if (args->curptr < args->endptr) {
+ pdf_doc_add_page_content(" ", 1); /* op: */
+ pdf_doc_add_page_content(args->curptr, (int) (args->endptr - args->curptr)); /* op: ANY */
+ }
+
+ args->curptr = args->endptr;
+
+ return 0;
+}
+
+static struct spc_handler pdfm_handlers[] = {
+ {"annotation", spc_handler_pdfm_annot},
+ {"annotate", spc_handler_pdfm_annot},
+ {"annot", spc_handler_pdfm_annot},
+ {"ann", spc_handler_pdfm_annot},
+
+ {"outline", spc_handler_pdfm_outline},
+ {"out", spc_handler_pdfm_outline},
+
+ {"article", spc_handler_pdfm_article},
+ {"art", spc_handler_pdfm_article},
+
+ {"bead", spc_handler_pdfm_bead},
+ {"thread", spc_handler_pdfm_bead},
+
+ {"destination", spc_handler_pdfm_dest},
+ {"dest", spc_handler_pdfm_dest},
+
+
+ {"object", spc_handler_pdfm_object},
+ {"obj", spc_handler_pdfm_object},
+
+
+ {"docinfo", spc_handler_pdfm_docinfo},
+ {"docview", spc_handler_pdfm_docview},
+
+ {"content", spc_handler_pdfm_content},
+ {"put", spc_handler_pdfm_put},
+ {"close", spc_handler_pdfm_close},
+ {"bop", spc_handler_pdfm_bop},
+ {"eop", spc_handler_pdfm_eop},
+
+ {"image", spc_handler_pdfm_image},
+ {"img", spc_handler_pdfm_image},
+ {"epdf", spc_handler_pdfm_image},
+
+ {"link", spc_handler_pdfm_link},
+ {"nolink", spc_handler_pdfm_nolink},
+
+ {"begincolor", spc_handler_pdfm_bcolor},
+ {"bcolor", spc_handler_pdfm_bcolor},
+ {"bc", spc_handler_pdfm_bcolor},
+
+ {"setcolor", spc_handler_pdfm_scolor},
+ {"scolor", spc_handler_pdfm_scolor},
+ {"sc", spc_handler_pdfm_scolor},
+
+ {"endcolor", spc_handler_pdfm_ecolor},
+ {"ecolor", spc_handler_pdfm_ecolor},
+ {"ec", spc_handler_pdfm_ecolor},
+
+ {"begingray", spc_handler_pdfm_bcolor},
+ {"bgray", spc_handler_pdfm_bcolor},
+ {"bg", spc_handler_pdfm_bcolor},
+
+ {"endgray", spc_handler_pdfm_ecolor},
+ {"egray", spc_handler_pdfm_ecolor},
+ {"eg", spc_handler_pdfm_ecolor},
+
+ {"bgcolor", spc_handler_pdfm_bgcolor},
+ {"bgc", spc_handler_pdfm_bgcolor},
+ {"bbc", spc_handler_pdfm_bgcolor},
+ {"bbg", spc_handler_pdfm_bgcolor},
+
+ {"pagesize", spc_handler_pdfm_pagesize},
+
+ {"bannot", spc_handler_pdfm_bann},
+ {"beginann", spc_handler_pdfm_bann},
+ {"bann", spc_handler_pdfm_bann},
+
+ {"eannot", spc_handler_pdfm_eann},
+ {"endann", spc_handler_pdfm_eann},
+ {"eann", spc_handler_pdfm_eann},
+
+ {"btrans", spc_handler_pdfm_btrans},
+ {"begintransform", spc_handler_pdfm_btrans},
+ {"begintrans", spc_handler_pdfm_btrans},
+ {"bt", spc_handler_pdfm_btrans},
+
+ {"etrans", spc_handler_pdfm_etrans},
+ {"endtransform", spc_handler_pdfm_etrans},
+ {"endtrans", spc_handler_pdfm_etrans},
+ {"et", spc_handler_pdfm_etrans},
+
+ {"bform", spc_handler_pdfm_bform},
+ {"beginxobj", spc_handler_pdfm_bform},
+ {"bxobj", spc_handler_pdfm_bform},
+
+ {"eform", spc_handler_pdfm_eform},
+ {"endxobj", spc_handler_pdfm_eform},
+ {"exobj", spc_handler_pdfm_eform},
+
+ {"usexobj", spc_handler_pdfm_uxobj},
+ {"uxobj", spc_handler_pdfm_uxobj},
+
+ {"tounicode", spc_handler_pdfm_tounicode},
+ {"literal", spc_handler_pdfm_literal},
+ {"stream", spc_handler_pdfm_stream},
+ {"fstream", spc_handler_pdfm_fstream},
+ {"names", spc_handler_pdfm_names},
+ {"mapline", spc_handler_pdfm_mapline},
+ {"mapfile", spc_handler_pdfm_mapfile},
+
+ {"bcontent", spc_handler_pdfm_bcontent},
+ {"econtent", spc_handler_pdfm_econtent},
+ {"code", spc_handler_pdfm_code},
+
+ {"minorversion", spc_handler_pdfm_do_nothing},
+ {"majorversion", spc_handler_pdfm_do_nothing},
+ {"encrypt", spc_handler_pdfm_do_nothing},
+
+ {"pageresources", spc_handler_pdfm_pageresources},
+ {"trailerid", spc_handler_pdfm_do_nothing},
+
+ {"xannot", spc_handler_pdfm_xann},
+ {"extendann", spc_handler_pdfm_xann},
+ {"xann", spc_handler_pdfm_xann},
+
+ {"bxgstate", spc_handler_pdfm_bxgstate},
+ {"exgstate", spc_handler_pdfm_exgstate},
+};
+
+static struct spc_handler pdft_compat_handlers[] = {
+ /* Text supplied to "direct" command should go inside of BT/ET block
+ * but dvipdfmx currently can't be implemented so.
+ * Here, "direct" is for the moment just an alias of "page".
+ */
+ {"direct", spc_handler_pdft_compat_page},
+ {"page", spc_handler_pdft_compat_page},
+};
+
+int
+spc_pdfm_check_special (const char *buf, int len)
+{
+ int r = 0;
+ const char *p, *endptr;
+
+ p = buf;
+ endptr = p + len;
+
+ skip_white(&p, endptr);
+ if (p + strlen("pdf:") <= endptr &&
+ !memcmp(p, "pdf:", strlen("pdf:"))) {
+ r = 1;
+ }
+
+ return r;
+}
+
+int
+spc_pdfm_setup_handler (struct spc_handler *sph,
+ struct spc_env *spe, struct spc_arg *ap)
+{
+ int error = -1, i;
+ char *q;
+
+ ASSERT(sph && spe && ap);
+
+ skip_white(&ap->curptr, ap->endptr);
+ if (ap->curptr + strlen("pdf:") >= ap->endptr ||
+ memcmp(ap->curptr, "pdf:", strlen("pdf:"))) {
+ spc_warn(spe, "Not pdf: special???");
+ return -1;
+ }
+ ap->curptr += strlen("pdf:");
+
+ skip_white(&ap->curptr, ap->endptr);
+ q = parse_c_ident(&ap->curptr, ap->endptr);
+ if (q) {
+ int is_pdft_compat = 0;
+ if (ap->curptr < ap->endptr) {
+ if (ap->curptr[0] == ':') {
+ is_pdft_compat = 1;
+ ap->curptr++;
+ }
+ }
+ if (is_pdft_compat) {
+ for (i = 0; i < sizeof(pdft_compat_handlers) / sizeof(struct spc_handler); i++) {
+ if (!strcmp(q, pdft_compat_handlers[i].key)) {
+ ap->command = pdft_compat_handlers[i].key;
+ sph->key = "pdf:";
+ sph->exec = pdft_compat_handlers[i].exec;
+ skip_white(&ap->curptr, ap->endptr);
+ error = 0;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < sizeof(pdfm_handlers) / sizeof(struct spc_handler); i++) {
+ if (!strcmp(q, pdfm_handlers[i].key)) {
+ ap->command = pdfm_handlers[i].key;
+ sph->key = "pdf:";
+ sph->exec = pdfm_handlers[i].exec;
+ skip_white(&ap->curptr, ap->endptr);
+ error = 0;
+ break;
+ }
+ }
+ }
+ RELEASE(q);
+ }
+
+ return error;
+}