Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpsdoc.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* XPS interpreter - document parsing */
18
19
#include "ghostxps.h"
20
21
#include <expat.h>
22
23
xps_part_t *
24
xps_new_part(xps_context_t *ctx, const char *name, int size)
25
96
{
26
96
    xps_part_t *part;
27
28
96
    part = xps_alloc(ctx, sizeof(xps_part_t));
29
96
    if (!part) {
30
0
        gs_throw(gs_error_VMerror, "out of memory: xps_new_part\n");
31
0
        return NULL;
32
0
    }
33
96
    part->name = xps_strdup(ctx, name);
34
96
    part->size = size;
35
96
    part->data = xps_alloc(ctx, size);
36
96
    if (!part->data) {
37
0
        xps_free(ctx, part);
38
0
        gs_throw(gs_error_VMerror, "out of memory: xps_new_part\n");
39
0
        return NULL;
40
0
    }
41
42
96
    return part;
43
96
}
44
45
void
46
xps_free_part(xps_context_t *ctx, xps_part_t *part)
47
94
{
48
94
    if (part == NULL)
49
0
        return;
50
94
    xps_free(ctx, part->name);
51
94
    xps_free(ctx, part->data);
52
94
    xps_free(ctx, part);
53
94
}
54
55
/*
56
 * The FixedDocumentSequence and FixedDocument parts determine
57
 * which parts correspond to actual pages, and the page order.
58
 */
59
60
void
61
xps_debug_fixdocseq(xps_context_t *ctx)
62
0
{
63
0
    xps_document_t *fixdoc = ctx->first_fixdoc;
64
0
    xps_page_t *page = ctx->first_page;
65
66
0
    if (ctx->start_part)
67
0
        dmprintf1(ctx->memory, "start part %s\n", ctx->start_part);
68
69
0
    while (fixdoc)
70
0
    {
71
0
        dmprintf1(ctx->memory, "fixdoc %s\n", fixdoc->name);
72
0
        fixdoc = fixdoc->next;
73
0
    }
74
75
0
    while (page)
76
0
    {
77
0
        dmprintf3(ctx->memory, "page %s w=%d h=%d\n", page->name, page->width, page->height);
78
0
        page = page->next;
79
0
    }
80
0
}
81
82
static void
83
xps_add_fixed_document(xps_context_t *ctx, char *name)
84
23
{
85
23
    xps_document_t *fixdoc;
86
87
    /* Check for duplicates first */
88
23
    for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
89
0
        if (!strcmp(fixdoc->name, name))
90
0
            return;
91
92
23
    if_debug1m('|', ctx->memory, "doc: adding fixdoc %s\n", name);
93
94
23
    fixdoc = xps_alloc(ctx, sizeof(xps_document_t));
95
23
    if (!fixdoc) {
96
0
        gs_throw(gs_error_VMerror, "out of memory: xps_add_fixed_document\n");
97
0
        return;
98
0
    }
99
23
    fixdoc->name = xps_strdup(ctx, name);
100
23
    fixdoc->next = NULL;
101
102
23
    if (!ctx->first_fixdoc)
103
23
    {
104
23
        ctx->first_fixdoc = fixdoc;
105
23
        ctx->last_fixdoc = fixdoc;
106
23
    }
107
0
    else
108
0
    {
109
0
        ctx->last_fixdoc->next = fixdoc;
110
0
        ctx->last_fixdoc = fixdoc;
111
0
    }
112
23
}
113
114
void
115
xps_free_fixed_documents(xps_context_t *ctx)
116
90
{
117
90
    xps_document_t *node = ctx->first_fixdoc;
118
113
    while (node)
119
23
    {
120
23
        xps_document_t *next = node->next;
121
23
        xps_free(ctx, node->name);
122
23
        xps_free(ctx, node);
123
23
        node = next;
124
23
    }
125
90
    ctx->first_fixdoc = NULL;
126
90
    ctx->last_fixdoc = NULL;
127
90
}
128
129
static void
130
xps_add_fixed_page(xps_context_t *ctx, char *name, int width, int height)
131
22
{
132
22
    xps_page_t *page;
133
134
    /* Check for duplicates first */
135
22
    for (page = ctx->first_page; page; page = page->next)
136
0
        if (!strcmp(page->name, name))
137
0
            return;
138
139
22
    if (ctx->page_range && !ctx->page_range->page_list)
140
0
    {
141
0
        ctx->page_range->current++;
142
143
0
        if (ctx->page_range->reverse)
144
0
        {
145
0
            if (ctx->page_range->current < ctx->page_range->last ||
146
0
                ctx->page_range->current > ctx->page_range->first)
147
0
                return;
148
0
        }
149
0
        else
150
0
        {
151
0
            if ((ctx->page_range->first != 0 && ctx->page_range->current < ctx->page_range->first) ||
152
0
                (ctx->page_range->last != 0 && ctx->page_range->current > ctx->page_range->last))
153
0
                return;
154
0
        }
155
0
    }
156
157
22
    if_debug1m('|', ctx->memory, "doc: adding page %s\n", name);
158
159
22
    page = xps_alloc(ctx, sizeof(xps_page_t));
160
22
    if (!page) {
161
0
        gs_throw(gs_error_VMerror, "out of memory: xps_add_fixed_page\n");
162
0
        return;
163
0
    }
164
22
    page->name = xps_strdup(ctx, name);
165
22
    page->width = width;
166
22
    page->height = height;
167
22
    page->next = NULL;
168
169
22
    if (!ctx->first_page)
170
22
    {
171
22
        ctx->first_page = page;
172
22
        ctx->last_page = page;
173
22
    }
174
0
    else
175
0
    {
176
        /* FirstPage < LastPage? */
177
0
        if (ctx->page_range && ctx->page_range->reverse)
178
0
        {
179
0
            page->next = ctx->first_page;
180
0
            ctx->first_page = page;
181
0
        }
182
0
        else
183
0
        {
184
0
            ctx->last_page->next = page;
185
0
            ctx->last_page = page;
186
0
        }
187
0
    }
188
22
}
189
190
void
191
xps_free_fixed_pages(xps_context_t *ctx)
192
90
{
193
90
    xps_page_t *node = ctx->first_page;
194
112
    while (node)
195
22
    {
196
22
        xps_page_t *next = node->next;
197
22
        xps_free(ctx, node->name);
198
22
        xps_free(ctx, node);
199
22
        node = next;
200
22
    }
201
90
    ctx->first_page = NULL;
202
90
    ctx->last_page = NULL;
203
90
}
204
205
/*
206
 * Parse the fixed document sequence structure and _rels/.rels to find the
207
 * start part. We hook up unique expat handlers for this, since we don't need
208
 * the full document model.
209
 */
210
211
static void
212
xps_parse_metadata_imp(void *zp, char *name, char **atts)
213
162
{
214
162
    xps_context_t *ctx = zp;
215
162
    int i;
216
217
162
    if (!strcmp(name, "Relationship"))
218
48
    {
219
48
        char tgtbuf[1024];
220
48
        char *target = NULL;
221
48
        char *type = NULL;
222
48
        char *id = NULL;
223
224
192
        for (i = 0; atts[i]; i += 2)
225
144
        {
226
144
            if (!strcmp(atts[i], "Target"))
227
48
                target = atts[i + 1];
228
144
            if (!strcmp(atts[i], "Type"))
229
48
                type = atts[i + 1];
230
144
            if (!strcmp(atts[i], "Id"))
231
48
                id = atts[i + 1];
232
144
        }
233
234
48
        if (target && type)
235
48
        {
236
48
            xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf);
237
48
            if (!strcmp(type, REL_START_PART) ||
238
24
                !strcmp(type, REL_START_PART_OXPS))
239
24
                ctx->start_part = xps_strdup(ctx, tgtbuf);
240
48
            if (!id)
241
0
                gs_warn1("missing relationship Id for %s", target);
242
48
        }
243
48
    }
244
245
162
    if (!strcmp(name, "DocumentReference"))
246
23
    {
247
23
        char *source = NULL;
248
23
        char srcbuf[1024];
249
250
46
        for (i = 0; atts[i]; i += 2)
251
23
        {
252
23
            if (!strcmp(atts[i], "Source"))
253
23
                source = atts[i + 1];
254
23
        }
255
256
23
        if (source)
257
23
        {
258
23
            xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf);
259
23
            xps_add_fixed_document(ctx, srcbuf);
260
23
        }
261
23
    }
262
263
162
    if (!strcmp(name, "PageContent"))
264
22
    {
265
22
        char *source = NULL;
266
22
        char srcbuf[1024];
267
22
        int width = 0;
268
22
        int height = 0;
269
270
44
        for (i = 0; atts[i]; i += 2)
271
22
        {
272
22
            if (!strcmp(atts[i], "Source"))
273
22
                source = atts[i + 1];
274
22
            if (!strcmp(atts[i], "Width"))
275
0
                width = atoi(atts[i + 1]);
276
22
            if (!strcmp(atts[i], "Height"))
277
0
                height = atoi(atts[i + 1]);
278
22
        }
279
280
22
        if (source)
281
22
        {
282
22
            xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf);
283
22
            xps_add_fixed_page(ctx, srcbuf, width, height);
284
22
        }
285
22
    }
286
162
}
287
288
int
289
xps_parse_metadata(xps_context_t *ctx, xps_part_t *part)
290
69
{
291
69
    XML_Parser xp;
292
69
    int code;
293
69
    char buf[1024];
294
69
    char *s;
295
296
    /* Save directory name part */
297
69
    gs_strlcpy(buf, part->name, sizeof buf);
298
69
    s = strrchr(buf, '/');
299
69
    if (s)
300
69
        s[0] = 0;
301
302
    /* _rels parts are voodoo: their URI references are from
303
     * the part they are associated with, not the actual _rels
304
     * part being parsed.
305
     */
306
69
    s = strstr(buf, "/_rels");
307
69
    if (s)
308
24
        *s = 0;
309
310
69
    ctx->base_uri = buf;
311
69
    ctx->part_uri = part->name;
312
313
69
    xp = XML_ParserCreate(NULL);
314
69
    if (!xp)
315
0
        return gs_throw(-1, "cannot create XML parser");
316
317
69
    XML_SetUserData(xp, ctx);
318
69
    XML_SetParamEntityParsing(xp, XML_PARAM_ENTITY_PARSING_NEVER);
319
69
    XML_SetStartElementHandler(xp, (XML_StartElementHandler)xps_parse_metadata_imp);
320
321
69
    code = XML_Parse(xp, (char*)part->data, part->size, 1);
322
323
69
    XML_ParserFree(xp);
324
325
69
    ctx->base_uri = NULL;
326
69
    ctx->part_uri = NULL;
327
328
69
    if (code == 0)
329
0
        return gs_throw1(-1, "cannot parse XML in part: %s", part->name);
330
331
69
    return 0;
332
69
}