Coverage Report

Created: 2026-04-01 07:17

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
3
{
24
3
    xps_resource_t *new_dict = NULL;
25
3
    xps_item_t *node;
26
3
    char *opacity_mask_uri;
27
3
    int code;
28
29
3
    char *transform_att;
30
3
    char *clip_att;
31
3
    char *opacity_att;
32
3
    char *opacity_mask_att;
33
34
3
    xps_item_t *transform_tag = NULL;
35
3
    xps_item_t *clip_tag = NULL;
36
3
    xps_item_t *opacity_mask_tag = NULL;
37
38
3
    gs_matrix transform;
39
40
3
    transform_att = xps_att(root, "RenderTransform");
41
3
    clip_att = xps_att(root, "Clip");
42
3
    opacity_att = xps_att(root, "Opacity");
43
3
    opacity_mask_att = xps_att(root, "OpacityMask");
44
45
2.85k
    for (node = xps_down(root); node; node = xps_next(node))
46
2.85k
    {
47
2.85k
        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
2.85k
        if (!strcmp(xps_tag(node), "Canvas.RenderTransform"))
60
0
            transform_tag = xps_down(node);
61
2.85k
        if (!strcmp(xps_tag(node), "Canvas.Clip"))
62
0
            clip_tag = xps_down(node);
63
2.85k
        if (!strcmp(xps_tag(node), "Canvas.OpacityMask"))
64
0
            opacity_mask_tag = xps_down(node);
65
2.85k
    }
66
67
3
    opacity_mask_uri = base_uri;
68
3
    xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
69
3
    xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
70
3
    xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
71
72
3
    gs_gsave(ctx->pgs);
73
74
3
    gs_make_identity(&transform);
75
3
    if (transform_att)
76
0
        xps_parse_render_transform(ctx, transform_att, &transform);
77
3
    if (transform_tag)
78
0
        xps_parse_matrix_transform(ctx, transform_tag, &transform);
79
3
    gs_concat(ctx->pgs, &transform);
80
81
3
    if (clip_att || clip_tag)
82
3
    {
83
3
        if (clip_att)
84
3
            xps_parse_abbreviated_geometry(ctx, clip_att);
85
3
        if (clip_tag)
86
0
            xps_parse_path_geometry(ctx, dict, clip_tag, 0);
87
3
        xps_clip(ctx);
88
3
    }
89
90
3
    code = xps_begin_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag, false, false);
91
3
    if (code)
92
0
    {
93
0
        gs_grestore(ctx->pgs);
94
0
        return gs_rethrow(code, "cannot create transparency group");
95
0
    }
96
97
2.85k
    for (node = xps_down(root); node; node = xps_next(node))
98
2.85k
    {
99
2.85k
        code = xps_parse_element(ctx, base_uri, dict, node);
100
2.85k
        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
2.85k
    }
107
108
3
    xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
109
110
3
    gs_grestore(ctx->pgs);
111
112
3
    if (new_dict)
113
0
        xps_free_resource_dictionary(ctx, new_dict);
114
115
3
    return 0;
116
3
}
117
118
int
119
xps_parse_fixed_page(xps_context_t *ctx, xps_part_t *part)
120
20
{
121
20
    xps_item_t *root, *node;
122
20
    xps_resource_t *dict;
123
20
    char *width_att;
124
20
    char *height_att;
125
20
    char base_uri[1024];
126
20
    char *s;
127
20
    int code, code1, code2;
128
20
    int page_spot_colors = 0;
129
130
20
    if_debug1m('|', ctx->memory, "doc: parsing page %s\n", part->name);
131
132
20
    gs_strlcpy(base_uri, part->name, sizeof base_uri);
133
20
    s = strrchr(base_uri, '/');
134
20
    if (s)
135
20
        s[1] = 0;
136
137
20
    root = xps_parse_xml(ctx, part->data, part->size);
138
20
    if (!root)
139
17
        return gs_rethrow(-1, "cannot parse xml");
140
141
3
    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
3
    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
3
    width_att = xps_att(root, "Width");
160
3
    height_att = xps_att(root, "Height");
161
162
3
    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
3
    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
3
    dict = NULL;
174
175
    /* Setup new page */
176
3
    {
177
3
        gs_memory_t *mem = ctx->memory;
178
3
        gs_gstate *pgs = ctx->pgs;
179
3
        gx_device *dev = gs_currentdevice(pgs);
180
3
        gs_param_float_array fa;
181
3
        float fv[2];
182
3
        gs_c_param_list list;
183
184
3
        gs_c_param_list_write(&list, mem);
185
186
3
        fv[0] = atoi(width_att) / 96.0 * 72.0;
187
3
        fv[1] = atoi(height_att) / 96.0 * 72.0;
188
3
        fa.persistent = false;
189
3
        fa.data = fv;
190
3
        fa.size = 2;
191
192
        /* Pre-parse looking for transparency */
193
3
        ctx->has_transparency = false;
194
20
        for (node = xps_down(root); node; node = xps_next(node))
195
17
        {
196
17
            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
17
            if (xps_element_has_transparency(ctx, base_uri, node))
203
0
            {
204
0
                ctx->has_transparency = true;
205
0
                break;
206
0
            }
207
17
        }
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
3
        code  = param_write_int((gs_param_list *)&list, "PageSpotColors", &(page_spot_colors));
219
3
        code1 = param_write_bool((gs_param_list *)&list, "PageUsesTransparency", &(ctx->has_transparency));
220
3
        code2 = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa);
221
3
        if ( code >= 0 || code1 >= 0 || code2 >= 0)
222
3
        {
223
3
            gs_c_param_list_read(&list);
224
3
            code = gs_putdeviceparams(dev, (gs_param_list *)&list);
225
3
            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
3
        }
231
3
        gs_c_param_list_release(&list);
232
233
        /* nb this is for the demo it is wrong and should be removed */
234
3
        gs_initgraphics(pgs);
235
236
        /* 96 dpi default - and put the origin at the top of the page */
237
238
3
        gs_initmatrix(pgs);
239
240
3
        code = gs_scale(pgs, 72.0/96.0, -72.0/96.0);
241
3
        if (code < 0) {
242
0
            xps_free_item(ctx, root);
243
0
            return gs_rethrow(code, "cannot set page transform");
244
0
        }
245
246
3
        code = gs_translate(pgs, 0.0, -atoi(height_att));
247
3
        if (code < 0) {
248
0
            xps_free_item(ctx, root);
249
0
            return gs_rethrow(code, "cannot set page transform");
250
0
        }
251
252
3
        code = gs_erasepage(pgs);
253
3
        if (code < 0) {
254
0
            xps_free_item(ctx, root);
255
0
            return gs_rethrow(code, "cannot clear page");
256
0
        }
257
3
    }
258
259
    /* save the state with the original device before we push */
260
3
    gs_gsave(ctx->pgs);
261
262
3
    if (ctx->use_transparency && ctx->has_transparency)
263
0
    {
264
0
        code = gs_push_pdf14trans_device(ctx->pgs, false, false, 0, 0);
265
0
        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
0
    }
272
273
    /* Draw contents */
274
275
20
    for (node = xps_down(root); node; node = xps_next(node))
276
17
    {
277
17
        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
17
        code = xps_parse_element(ctx, base_uri, dict, node);
291
17
        if (code)
292
0
        {
293
0
            gs_pop_pdf14trans_device(ctx->pgs, false);
294
0
            gs_grestore(ctx->pgs);
295
0
            if (dict)
296
0
                xps_free_resource_dictionary(ctx, dict);
297
0
            xps_free_item(ctx, root);
298
0
            return gs_rethrow(code, "cannot parse child of FixedPage");
299
0
        }
300
17
    }
301
302
3
    if (ctx->use_transparency && ctx->has_transparency)
303
0
    {
304
0
        code = gs_pop_pdf14trans_device(ctx->pgs, false);
305
0
        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
0
    }
314
315
    /* Flush page */
316
3
    {
317
3
        code = xps_show_page(ctx, 1, true); /* copies, flush */
318
3
        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
3
    }
327
328
    /* restore the original device, discarding the pdf14 compositor */
329
3
    gs_grestore(ctx->pgs);
330
331
3
    if (dict)
332
0
    {
333
0
        xps_free_resource_dictionary(ctx, dict);
334
0
    }
335
336
3
    xps_free_item(ctx, root);
337
338
3
    return 0;
339
3
}