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., 39 Mesa Street, Suite 108A, San Francisco,
CA 94129, USA, for further information.
*/
#include "ghostxps.h"
#include <expat.h>
xps_part_t *
xps_new_part(xps_context_t *ctx, const char *name, int size)
{
xps_part_t *part;
part = xps_alloc(ctx, sizeof(xps_part_t));
if (!part) {
gs_throw(gs_error_VMerror, "out of memory: xps_new_part\n");
return NULL;
}
part->name = xps_strdup(ctx, name);
part->size = size;
part->data = xps_alloc(ctx, size);
if (!part->data) {
xps_free(ctx, part);
gs_throw(gs_error_VMerror, "out of memory: xps_new_part\n");
return NULL;
}
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)
dmprintf1(ctx->memory, "start part %s\n", ctx->start_part);
while (fixdoc)
{
dmprintf1(ctx->memory, "fixdoc %s\n", fixdoc->name);
fixdoc = fixdoc->next;
}
while (page)
{
dmprintf3(ctx->memory, "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;
for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
if (!strcmp(fixdoc->name, name))
return;
if_debug1m('|', ctx->memory, "doc: adding fixdoc %s\n", name);
fixdoc = xps_alloc(ctx, sizeof(xps_document_t));
if (!fixdoc) {
gs_throw(gs_error_VMerror, "out of memory: xps_add_fixed_document\n");
return;
}
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;
for (page = ctx->first_page; page; page = page->next)
if (!strcmp(page->name, name))
return;
if (ctx->page_range && !ctx->page_range->page_list)
{
ctx->page_range->current++;
if (ctx->page_range->reverse)
{
if (ctx->page_range->current < ctx->page_range->last ||
ctx->page_range->current > ctx->page_range->first)
return;
}
else
{
if ((ctx->page_range->first != 0 && ctx->page_range->current < ctx->page_range->first) ||
(ctx->page_range->last != 0 && ctx->page_range->current > ctx->page_range->last))
return;
}
}
if_debug1m('|', ctx->memory, "doc: adding page %s\n", name);
page = xps_alloc(ctx, sizeof(xps_page_t));
if (!page) {
gs_throw(gs_error_VMerror, "out of memory: xps_add_fixed_page\n");
return;
}
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
{
if (ctx->page_range && ctx->page_range->reverse)
{
page->next = ctx->first_page;
ctx->first_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;
char *id = 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 (!strcmp(atts[i], "Id"))
id = atts[i + 1];
}
if (target && type)
{
xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf);
if (!strcmp(type, REL_START_PART) ||
!strcmp(type, REL_START_PART_OXPS))
ctx->start_part = xps_strdup(ctx, tgtbuf);
if (!id)
gs_warn1("missing relationship Id for %s", target);
}
}
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;
gs_strlcpy(buf, part->name, sizeof buf);
s = strrchr(buf, '/');
if (s)
s[0] = 0;
* 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;
}