diff options
Diffstat (limited to 'xps/xpsdoc.c')
-rw-r--r-- | xps/xpsdoc.c | 277 |
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; +} |