summaryrefslogtreecommitdiff
path: root/xps/xpsdoc.c
diff options
context:
space:
mode:
Diffstat (limited to 'xps/xpsdoc.c')
-rw-r--r--xps/xpsdoc.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/xps/xpsdoc.c b/xps/xpsdoc.c
new file mode 100644
index 00000000..3e9f7c96
--- /dev/null
+++ b/xps/xpsdoc.c
@@ -0,0 +1,277 @@
+/* Copyright (C) 2006-2010 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 that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* XPS interpreter - document parsing */
+
+#include "ghostxps.h"
+
+#include <expat.h>
+
+xps_part_t *
+xps_new_part(xps_context_t *ctx, char *name, int size)
+{
+ xps_part_t *part;
+
+ part = xps_alloc(ctx, sizeof(xps_part_t));
+ part->name = xps_strdup(ctx, name);
+ part->size = size;
+ part->data = xps_alloc(ctx, size);
+
+ return part;
+}
+
+void
+xps_free_part(xps_context_t *ctx, xps_part_t *part)
+{
+ xps_free(ctx, part->name);
+ xps_free(ctx, part->data);
+ xps_free(ctx, part);
+}
+
+/*
+ * The FixedDocumentSequence and FixedDocument parts determine
+ * which parts correspond to actual pages, and the page order.
+ */
+
+void
+xps_debug_fixdocseq(xps_context_t *ctx)
+{
+ xps_document_t *fixdoc = ctx->first_fixdoc;
+ xps_page_t *page = ctx->first_page;
+
+ if (ctx->start_part)
+ dprintf1("start part %s\n", ctx->start_part);
+
+ while (fixdoc)
+ {
+ dprintf1("fixdoc %s\n", fixdoc->name);
+ fixdoc = fixdoc->next;
+ }
+
+ while (page)
+ {
+ dprintf3("page %s w=%d h=%d\n", page->name, page->width, page->height);
+ page = page->next;
+ }
+}
+
+static void
+xps_add_fixed_document(xps_context_t *ctx, char *name)
+{
+ xps_document_t *fixdoc;
+
+ /* Check for duplicates first */
+ for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
+ if (!strcmp(fixdoc->name, name))
+ return;
+
+ if_debug1('|', "doc: adding fixdoc %s\n", name);
+
+ fixdoc = xps_alloc(ctx, sizeof(xps_document_t));
+ fixdoc->name = xps_strdup(ctx, name);
+ fixdoc->next = NULL;
+
+ if (!ctx->first_fixdoc)
+ {
+ ctx->first_fixdoc = fixdoc;
+ ctx->last_fixdoc = fixdoc;
+ }
+ else
+ {
+ ctx->last_fixdoc->next = fixdoc;
+ ctx->last_fixdoc = fixdoc;
+ }
+}
+
+void
+xps_free_fixed_documents(xps_context_t *ctx)
+{
+ xps_document_t *node = ctx->first_fixdoc;
+ while (node)
+ {
+ xps_document_t *next = node->next;
+ xps_free(ctx, node->name);
+ xps_free(ctx, node);
+ node = next;
+ }
+ ctx->first_fixdoc = NULL;
+ ctx->last_fixdoc = NULL;
+}
+
+static void
+xps_add_fixed_page(xps_context_t *ctx, char *name, int width, int height)
+{
+ xps_page_t *page;
+
+ /* Check for duplicates first */
+ for (page = ctx->first_page; page; page = page->next)
+ if (!strcmp(page->name, name))
+ return;
+
+ if_debug1('|', "doc: adding page %s\n", name);
+
+ page = xps_alloc(ctx, sizeof(xps_page_t));
+ page->name = xps_strdup(ctx, name);
+ page->width = width;
+ page->height = height;
+ page->next = NULL;
+
+ if (!ctx->first_page)
+ {
+ ctx->first_page = page;
+ ctx->last_page = page;
+ }
+ else
+ {
+ ctx->last_page->next = page;
+ ctx->last_page = page;
+ }
+}
+
+void
+xps_free_fixed_pages(xps_context_t *ctx)
+{
+ xps_page_t *node = ctx->first_page;
+ while (node)
+ {
+ xps_page_t *next = node->next;
+ xps_free(ctx, node->name);
+ xps_free(ctx, node);
+ node = next;
+ }
+ ctx->first_page = NULL;
+ ctx->last_page = NULL;
+}
+
+/*
+ * Parse the fixed document sequence structure and _rels/.rels to find the
+ * start part. We hook up unique expat handlers for this, since we don't need
+ * the full document model.
+ */
+
+static void
+xps_parse_metadata_imp(void *zp, char *name, char **atts)
+{
+ xps_context_t *ctx = zp;
+ int i;
+
+ if (!strcmp(name, "Relationship"))
+ {
+ char tgtbuf[1024];
+ char *target = NULL;
+ char *type = NULL;
+
+ for (i = 0; atts[i]; i += 2)
+ {
+ if (!strcmp(atts[i], "Target"))
+ target = atts[i + 1];
+ if (!strcmp(atts[i], "Type"))
+ type = atts[i + 1];
+ }
+
+ if (target && type)
+ {
+ xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf);
+ if (!strcmp(type, REL_START_PART))
+ ctx->start_part = xps_strdup(ctx, tgtbuf);
+ }
+ }
+
+ if (!strcmp(name, "DocumentReference"))
+ {
+ char *source = NULL;
+ char srcbuf[1024];
+
+ for (i = 0; atts[i]; i += 2)
+ {
+ if (!strcmp(atts[i], "Source"))
+ source = atts[i + 1];
+ }
+
+ if (source)
+ {
+ xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf);
+ xps_add_fixed_document(ctx, srcbuf);
+ }
+ }
+
+ if (!strcmp(name, "PageContent"))
+ {
+ char *source = NULL;
+ char srcbuf[1024];
+ int width = 0;
+ int height = 0;
+
+ for (i = 0; atts[i]; i += 2)
+ {
+ if (!strcmp(atts[i], "Source"))
+ source = atts[i + 1];
+ if (!strcmp(atts[i], "Width"))
+ width = atoi(atts[i + 1]);
+ if (!strcmp(atts[i], "Height"))
+ height = atoi(atts[i + 1]);
+ }
+
+ if (source)
+ {
+ xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf);
+ xps_add_fixed_page(ctx, srcbuf, width, height);
+ }
+ }
+}
+
+int
+xps_parse_metadata(xps_context_t *ctx, xps_part_t *part)
+{
+ XML_Parser xp;
+ int code;
+ char buf[1024];
+ char *s;
+
+ /* Save directory name part */
+ xps_strlcpy(buf, part->name, sizeof buf);
+ s = strrchr(buf, '/');
+ if (s)
+ s[0] = 0;
+
+ /* _rels parts are voodoo: their URI references are from
+ * the part they are associated with, not the actual _rels
+ * part being parsed.
+ */
+ s = strstr(buf, "/_rels");
+ if (s)
+ *s = 0;
+
+ ctx->base_uri = buf;
+ ctx->part_uri = part->name;
+
+ xp = XML_ParserCreate(NULL);
+ if (!xp)
+ return gs_throw(-1, "cannot create XML parser");
+
+ XML_SetUserData(xp, ctx);
+ XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER);
+ XML_SetStartElementHandler(xp, (XML_StartElementHandler)xps_parse_metadata_imp);
+
+ code = XML_Parse(xp, (char*)part->data, part->size, 1);
+
+ XML_ParserFree(xp);
+
+ ctx->base_uri = NULL;
+ ctx->part_uri = NULL;
+
+ if (code == 0)
+ return gs_throw1(-1, "cannot parse XML in part: %s", part->name);
+
+ return 0;
+}