Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpspage.c
Line
Count
Source
1
/* Copyright (C) 2001-2023 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 - page parsing */
18
19
#include "ghostxps.h"
20
21
int
22
xps_parse_canvas(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root)
23
20
{
24
20
    xps_resource_t *new_dict = NULL;
25
20
    xps_item_t *node;
26
20
    char *opacity_mask_uri;
27
20
    int code;
28
29
20
    char *transform_att;
30
20
    char *clip_att;
31
20
    char *opacity_att;
32
20
    char *opacity_mask_att;
33
34
20
    xps_item_t *transform_tag = NULL;
35
20
    xps_item_t *clip_tag = NULL;
36
20
    xps_item_t *opacity_mask_tag = NULL;
37
38
20
    gs_matrix transform;
39
40
20
    transform_att = xps_att(root, "RenderTransform");
41
20
    clip_att = xps_att(root, "Clip");
42
20
    opacity_att = xps_att(root, "Opacity");
43
20
    opacity_mask_att = xps_att(root, "OpacityMask");
44
45
9.84k
    for (node = xps_down(root); node; node = xps_next(node))
46
9.82k
    {
47
9.82k
        if (!strcmp(xps_tag(node), "Canvas.Resources") && xps_down(node))
48
0
        {
49
0
            code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xps_down(node));
50
0
            if (code)
51
0
                return gs_rethrow(code, "cannot load Canvas.Resources");
52
0
            if (new_dict && new_dict != dict)
53
0
            {
54
0
                new_dict->parent = dict;
55
0
                dict = new_dict;
56
0
            }
57
0
        }
58
59
9.82k
        if (!strcmp(xps_tag(node), "Canvas.RenderTransform"))
60
0
            transform_tag = xps_down(node);
61
9.82k
        if (!strcmp(xps_tag(node), "Canvas.Clip"))
62
7
            clip_tag = xps_down(node);
63
9.82k
        if (!strcmp(xps_tag(node), "Canvas.OpacityMask"))
64
0
            opacity_mask_tag = xps_down(node);
65
9.82k
    }
66
67
20
    opacity_mask_uri = base_uri;
68
20
    xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
69
20
    xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
70
20
    xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
71
72
20
    gs_gsave(ctx->pgs);
73
74
20
    gs_make_identity(&transform);
75
20
    if (transform_att)
76
7
        xps_parse_render_transform(ctx, transform_att, &transform);
77
20
    if (transform_tag)
78
0
        xps_parse_matrix_transform(ctx, transform_tag, &transform);
79
20
    gs_concat(ctx->pgs, &transform);
80
81
20
    if (clip_att || clip_tag)
82
20
    {
83
20
        if (clip_att)
84
13
            xps_parse_abbreviated_geometry(ctx, clip_att);
85
20
        if (clip_tag)
86
7
            xps_parse_path_geometry(ctx, dict, clip_tag, 0);
87
20
        xps_clip(ctx);
88
20
    }
89
90
20
    code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag, false, false);
91
20
    if (code)
92
0
    {
93
0
        gs_grestore(ctx->pgs);
94
0
        return gs_rethrow(code, "cannot create transparency group");
95
0
    }
96
97
9.84k
    for (node = xps_down(root); node; node = xps_next(node))
98
9.82k
    {
99
9.82k
        code = xps_parse_element(ctx, base_uri, dict, node);
100
9.82k
        if (code)
101
0
        {
102
0
            xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
103
0
            gs_grestore(ctx->pgs);
104
0
            return gs_rethrow(code, "cannot parse child of Canvas");
105
0
        }
106
9.82k
    }
107
108
20
    xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
109
110
20
    gs_grestore(ctx->pgs);
111
112
20
    if (new_dict)
113
0
        xps_free_resource_dictionary(ctx, new_dict);
114
115
20
    return 0;
116
20
}
117
118
int
119
xps_parse_fixed_page(xps_context_t *ctx, xps_part_t *part)
120
108
{
121
108
    xps_item_t *root, *node;
122
108
    xps_resource_t *dict;
123
108
    char *width_att;
124
108
    char *height_att;
125
108
    char base_uri[1024];
126
108
    char *s;
127
108
    int code, code1, code2;
128
108
    int page_spot_colors = 0;
129
130
108
    if_debug1m('|', ctx->memory, "doc: parsing page %s\n", part->name);
131
132
108
    gs_strlcpy(base_uri, part->name, sizeof base_uri);
133
108
    s = strrchr(base_uri, '/');
134
108
    if (s)
135
108
        s[1] = 0;
136
137
108
    root = xps_parse_xml(ctx, part->data, part->size);
138
108
    if (!root)
139
79
        return gs_rethrow(-1, "cannot parse xml");
140
141
29
    if (!strcmp(xps_tag(root), "AlternateContent"))
142
0
    {
143
0
        xps_item_t *node = xps_lookup_alternate_content(root);
144
0
        if (!node)
145
0
        {
146
0
            xps_free_item(ctx, root);
147
0
            return gs_throw(-1, "expected FixedPage alternate content element");
148
0
        }
149
0
        xps_detach_and_free_remainder(ctx, root, node);
150
0
        root = node;
151
0
    }
152
153
29
    if (strcmp(xps_tag(root), "FixedPage"))
154
0
    {
155
0
        xps_free_item(ctx, root);
156
0
        return gs_throw(-1, "expected FixedPage element");
157
0
    }
158
159
29
    width_att = xps_att(root, "Width");
160
29
    height_att = xps_att(root, "Height");
161
162
29
    if (!width_att)
163
0
    {
164
0
        xps_free_item(ctx, root);
165
0
        return gs_throw(-1, "FixedPage missing required attribute: Width");
166
0
    }
167
29
    if (!height_att)
168
0
    {
169
0
        xps_free_item(ctx, root);
170
0
        return gs_throw(-1, "FixedPage missing required attribute: Height");
171
0
    }
172
173
29
    dict = NULL;
174
175
    /* Setup new page */
176
29
    {
177
29
        gs_memory_t *mem = ctx->memory;
178
29
        gs_gstate *pgs = ctx->pgs;
179
29
        gx_device *dev = gs_currentdevice(pgs);
180
29
        gs_param_float_array fa;
181
29
        float fv[2];
182
29
        gs_c_param_list list;
183
184
29
        gs_c_param_list_write(&list, mem);
185
186
29
        fv[0] = atoi(width_att) / 96.0 * 72.0;
187
29
        fv[1] = atoi(height_att) / 96.0 * 72.0;
188
29
        fa.persistent = false;
189
29
        fa.data = fv;
190
29
        fa.size = 2;
191
192
        /* Pre-parse looking for transparency */
193
29
        ctx->has_transparency = false;
194
100
        for (node = xps_down(root); node; node = xps_next(node))
195
84
        {
196
84
            if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node))
197
0
                if (xps_resource_dictionary_has_transparency(ctx, base_uri, xps_down(node)))
198
0
                {
199
0
                    ctx->has_transparency = true;
200
0
                    break;
201
0
                }
202
84
            if (xps_element_has_transparency(ctx, base_uri, node))
203
13
            {
204
13
                ctx->has_transparency = true;
205
13
                break;
206
13
            }
207
84
        }
208
209
        /* At some point we may want to add the pre-parse for named colors and n-channel
210
           colors here. The XPS spec makes it optional to put the colorant names in the
211
           ICC profile. So we would need some sort of fall back and we would need to know
212
           if a name color that we encounter is one that we already encountered, which would get
213
           very messy in terms of comparing ICC profiles. Especially for example, if
214
           the same spot color was used individually AND in an n-channel color profile.
215
           Since XPS usage is rare, and the demand for support of real spot color separation
216
           non-existent, we will set the PageSpotColors to 0 at this point. */
217
218
29
        code  = param_write_int((gs_param_list *)&list, "PageSpotColors", &(page_spot_colors));
219
29
        code1 = param_write_bool((gs_param_list *)&list, "PageUsesTransparency", &(ctx->has_transparency));
220
29
        code2 = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa);
221
29
        if ( code >= 0 || code1 >= 0 || code2 >= 0)
222
29
        {
223
29
            gs_c_param_list_read(&list);
224
29
            code = gs_putdeviceparams(dev, (gs_param_list *)&list);
225
29
            if (code < 0) {
226
0
                gs_c_param_list_release(&list);
227
0
                xps_free_item(ctx, root);
228
0
                return gs_rethrow(code, "cannot set device parameters");
229
0
            }
230
29
        }
231
29
        gs_c_param_list_release(&list);
232
233
        /* nb this is for the demo it is wrong and should be removed */
234
29
        gs_initgraphics(pgs);
235
236
        /* 96 dpi default - and put the origin at the top of the page */
237
238
29
        gs_initmatrix(pgs);
239
240
29
        code = gs_scale(pgs, 72.0/96.0, -72.0/96.0);
241
29
        if (code < 0) {
242
0
            xps_free_item(ctx, root);
243
0
            return gs_rethrow(code, "cannot set page transform");
244
0
        }
245
246
29
        code = gs_translate(pgs, 0.0, -atoi(height_att));
247
29
        if (code < 0) {
248
0
            xps_free_item(ctx, root);
249
0
            return gs_rethrow(code, "cannot set page transform");
250
0
        }
251
252
29
        code = gs_erasepage(pgs);
253
29
        if (code < 0) {
254
0
            xps_free_item(ctx, root);
255
0
            return gs_rethrow(code, "cannot clear page");
256
0
        }
257
29
    }
258
259
    /* save the state with the original device before we push */
260
29
    gs_gsave(ctx->pgs);
261
262
29
    if (ctx->use_transparency && ctx->has_transparency)
263
13
    {
264
13
        code = gs_push_pdf14trans_device(ctx->pgs, false, false, 0, 0);
265
13
        if (code < 0)
266
0
        {
267
0
            gs_grestore(ctx->pgs);
268
0
            xps_free_item(ctx, root);
269
0
            return gs_rethrow(code, "cannot install transparency device");
270
0
        }
271
13
    }
272
273
    /* Draw contents */
274
275
93
    for (node = xps_down(root); node; node = xps_next(node))
276
71
    {
277
71
        if (!strcmp(xps_tag(node), "FixedPage.Resources") && xps_down(node))
278
0
        {
279
0
            code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xps_down(node));
280
0
            if (code)
281
0
            {
282
0
                gs_pop_pdf14trans_device(ctx->pgs, false);
283
0
                gs_grestore(ctx->pgs);
284
0
                if (dict)
285
0
                    xps_free_resource_dictionary(ctx, dict);
286
0
                xps_free_item(ctx, root);
287
0
                return gs_rethrow(code, "cannot load FixedPage.Resources");
288
0
            }
289
0
        }
290
71
        code = xps_parse_element(ctx, base_uri, dict, node);
291
71
        if (code)
292
7
        {
293
7
            gs_pop_pdf14trans_device(ctx->pgs, false);
294
7
            gs_grestore(ctx->pgs);
295
7
            if (dict)
296
0
                xps_free_resource_dictionary(ctx, dict);
297
7
            xps_free_item(ctx, root);
298
7
            return gs_rethrow(code, "cannot parse child of FixedPage");
299
7
        }
300
71
    }
301
302
22
    if (ctx->use_transparency && ctx->has_transparency)
303
7
    {
304
7
        code = gs_pop_pdf14trans_device(ctx->pgs, false);
305
7
        if (code < 0)
306
0
        {
307
0
            gs_grestore(ctx->pgs);
308
0
            if (dict)
309
0
                xps_free_resource_dictionary(ctx, dict);
310
0
            xps_free_item(ctx, root);
311
0
            return gs_rethrow(code, "cannot uninstall transparency device");
312
0
        }
313
7
    }
314
315
    /* Flush page */
316
22
    {
317
22
        code = xps_show_page(ctx, 1, true); /* copies, flush */
318
22
        if (code < 0)
319
0
        {
320
0
            gs_grestore(ctx->pgs);
321
0
            if (dict)
322
0
                xps_free_resource_dictionary(ctx, dict);
323
0
            xps_free_item(ctx, root);
324
0
            return gs_rethrow(code, "cannot flush page");
325
0
        }
326
22
    }
327
328
    /* restore the original device, discarding the pdf14 compositor */
329
22
    gs_grestore(ctx->pgs);
330
331
22
    if (dict)
332
0
    {
333
0
        xps_free_resource_dictionary(ctx, dict);
334
0
    }
335
336
22
    xps_free_item(ctx, root);
337
338
22
    return 0;
339
22
}