Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpsanalyze.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 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 - analyze page checking for transparency.
18
 * This is a stripped down parser that looks for alpha values < 1.0 in
19
 * any part of the page.
20
 */
21
22
#include "ghostxps.h"
23
24
static int xps_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root);
25
static int xps_glyphs_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root);
26
27
static int
28
xps_remote_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, char *source_att)
29
0
{
30
0
    char part_name[1024];
31
0
    char part_uri[1024];
32
0
    xps_part_t *part;
33
0
    xps_item_t *xml;
34
0
    char *s;
35
0
    int has_transparency;
36
37
0
    xps_absolute_path(part_name, base_uri, source_att, sizeof part_name);
38
0
    part = xps_read_part(ctx, part_name);
39
0
    if (!part)
40
0
    {
41
0
        return gs_throw1(-1, "cannot find remote resource part '%s'", part_name);
42
0
    }
43
44
0
    xml = xps_parse_xml(ctx, part->data, part->size);
45
0
    if (!xml)
46
0
    {
47
0
        xps_free_part(ctx, part);
48
0
        return gs_rethrow(-1, "cannot parse xml");
49
0
    }
50
51
0
    if (strcmp(xps_tag(xml), "ResourceDictionary"))
52
0
    {
53
0
        xps_free_item(ctx, xml);
54
0
        xps_free_part(ctx, part);
55
0
        return gs_throw1(-1, "expected ResourceDictionary element (found %s)", xps_tag(xml));
56
0
    }
57
58
0
    gs_strlcpy(part_uri, part_name, sizeof part_uri);
59
0
    s = strrchr(part_uri, '/');
60
0
    if (s)
61
0
        s[1] = 0;
62
63
0
    has_transparency = xps_resource_dictionary_has_transparency(ctx, part_uri, xml);
64
0
    xps_free_item(ctx, xml);
65
0
    xps_free_part(ctx, part);
66
0
    return has_transparency;
67
0
}
68
69
int
70
xps_resource_dictionary_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root)
71
0
{
72
0
    char *source;
73
0
    xps_item_t *node;
74
75
0
    source = xps_att(root, "Source");
76
0
    if (source)
77
0
        return xps_remote_resource_dictionary_has_transparency(ctx, base_uri, source);
78
79
0
    for (node = xps_down(root); node; node = xps_next(node))
80
0
    {
81
0
        if (!strcmp(xps_tag(node), "RadialGradientBrush") ||
82
0
                !strcmp(xps_tag(node), "LinearGradientBrush") ||
83
0
                !strcmp(xps_tag(node), "SolidColorBrush") ||
84
0
                !strcmp(xps_tag(node), "VisualBrush") ||
85
0
                !strcmp(xps_tag(node), "ImageBrush"))
86
0
            if (xps_brush_has_transparency(ctx, base_uri, node))
87
0
                return 1;
88
0
        if (!strcmp(xps_tag(node), "Glyphs"))
89
0
            if (xps_glyphs_has_transparency(ctx, base_uri, node))
90
0
                return 1;
91
0
    }
92
93
0
    return 0;
94
0
}
95
96
static int
97
xps_gradient_stops_have_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root)
98
0
{
99
0
    xps_item_t *node;
100
0
    char *color_att;
101
0
    float samples[XPS_MAX_COLORS];
102
103
0
    for (node = xps_down(root); node; node = xps_next(node))
104
0
    {
105
0
        if (!strcmp(xps_tag(node), "GradientStop"))
106
0
        {
107
0
            color_att = xps_att(node, "Color");
108
0
            if (color_att)
109
0
            {
110
0
                xps_parse_color(ctx, base_uri, color_att, NULL, samples);
111
0
                if (samples[0] < 1.0)
112
0
                {
113
                    /*dmputs(ctx->memory, "page has transparency: GradientStop has alpha\n"); */
114
0
                    return 1;
115
0
                }
116
0
            }
117
0
        }
118
0
    }
119
120
0
    return 0;
121
0
}
122
123
static int
124
xps_gradient_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root)
125
0
{
126
0
    xps_item_t *node;
127
0
    char *opacity_att;
128
129
0
    opacity_att = xps_att(root, "Opacity");
130
0
    if (opacity_att)
131
0
    {
132
0
        if (atof(opacity_att) < 1.0)
133
0
        {
134
            /*dmputs(ctx->memory, "page has transparency: GradientBrush Opacity\n"); */
135
0
            return 1;
136
0
        }
137
0
    }
138
139
0
    for (node = xps_down(root); node; node = xps_next(node))
140
0
    {
141
0
        if (!strcmp(xps_tag(node), "RadialGradientBrush.GradientStops"))
142
0
        {
143
0
            if (xps_gradient_stops_have_transparency(ctx, base_uri, node))
144
0
                return 1;
145
0
        }
146
0
        if (!strcmp(xps_tag(node), "LinearGradientBrush.GradientStops"))
147
0
        {
148
0
            if (xps_gradient_stops_have_transparency(ctx, base_uri, node))
149
0
                return 1;
150
0
        }
151
0
    }
152
153
0
    return 0;
154
0
}
155
156
static int
157
xps_brush_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root)
158
0
{
159
0
    char *opacity_att;
160
0
    char *color_att;
161
0
    xps_item_t *node;
162
163
0
    float samples[XPS_MAX_COLORS];
164
165
0
    if (!strcmp(xps_tag(root), "SolidColorBrush"))
166
0
    {
167
0
        opacity_att = xps_att(root, "Opacity");
168
0
        if (opacity_att)
169
0
        {
170
0
            float opacity = atof(opacity_att);
171
0
            if (opacity < 1.0 && opacity != 0.0)
172
0
            {
173
                /*dmputs(ctx->memory, "page has transparency: SolidColorBrush Opacity\n"); */
174
0
                return 1;
175
0
            }
176
0
        }
177
178
0
        color_att = xps_att(root, "Color");
179
0
        if (color_att)
180
0
        {
181
0
            xps_parse_color(ctx, base_uri, color_att, NULL, samples);
182
0
            if (samples[0] < 1.0 && samples[0] != 0.0)
183
0
            {
184
                /*dmputs(ctx->memory, "page has transparency: SolidColorBrush Color has alpha\n"); */
185
0
                return 1;
186
0
            }
187
0
        }
188
0
    }
189
190
0
    if (!strcmp(xps_tag(root), "VisualBrush"))
191
0
    {
192
0
        char *opacity_att = xps_att(root, "Opacity");
193
0
        if (opacity_att)
194
0
        {
195
0
            if (atof(opacity_att) < 1.0)
196
0
            {
197
                /*dmputs(ctx->memory, "page has transparency: VisualBrush Opacity\n"); */
198
0
                return 1;
199
0
            }
200
0
        }
201
202
0
        for (node = xps_down(root); node; node = xps_next(node))
203
0
        {
204
0
            if (!strcmp(xps_tag(node), "VisualBrush.Visual"))
205
0
            {
206
0
                if (xps_element_has_transparency(ctx, base_uri, xps_down(node)))
207
0
                    return 1;
208
0
            }
209
0
        }
210
0
    }
211
212
0
    if (!strcmp(xps_tag(root), "ImageBrush"))
213
0
    {
214
0
        if (xps_image_brush_has_transparency(ctx, base_uri, root))
215
0
            return 1;
216
0
    }
217
218
0
    if (!strcmp(xps_tag(root), "LinearGradientBrush"))
219
0
    {
220
0
        if (xps_gradient_brush_has_transparency(ctx, base_uri, root))
221
0
            return 1;
222
0
    }
223
224
0
    if (!strcmp(xps_tag(root), "RadialGradientBrush"))
225
0
    {
226
0
        if (xps_gradient_brush_has_transparency(ctx, base_uri, root))
227
0
            return 1;
228
0
    }
229
230
0
    return 0;
231
0
}
232
233
static int
234
xps_path_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root)
235
2.85k
{
236
2.85k
    xps_item_t *node;
237
238
2.85k
    for (node = xps_down(root); node; node = xps_next(node))
239
0
    {
240
0
        if (!strcmp(xps_tag(node), "Path.OpacityMask"))
241
0
        {
242
            /*dmputs(ctx->memory, "page has transparency: Path.OpacityMask\n"); */
243
0
            return 1;
244
0
        }
245
246
0
        if (!strcmp(xps_tag(node), "Path.Stroke"))
247
0
        {
248
0
            if (xps_brush_has_transparency(ctx, base_uri, xps_down(node)))
249
0
                return 1;
250
0
        }
251
252
0
        if (!strcmp(xps_tag(node), "Path.Fill"))
253
0
        {
254
0
            if (xps_brush_has_transparency(ctx, base_uri, xps_down(node)))
255
0
                return 1;
256
0
        }
257
0
    }
258
259
2.85k
    return 0;
260
2.85k
}
261
262
static int
263
xps_glyphs_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root)
264
14
{
265
14
    xps_item_t *node;
266
267
14
    for (node = xps_down(root); node; node = xps_next(node))
268
0
    {
269
0
        if (!strcmp(xps_tag(node), "Glyphs.OpacityMask"))
270
0
        {
271
            /*dmputs(ctx->memory, "page has transparency: Glyphs.OpacityMask\n"); */
272
0
            return 1;
273
0
        }
274
275
0
        if (!strcmp(xps_tag(node), "Glyphs.Fill"))
276
0
        {
277
0
            if (xps_brush_has_transparency(ctx, base_uri, xps_down(node)))
278
0
                return 1;
279
0
        }
280
0
    }
281
282
14
    return 0;
283
14
}
284
285
static int
286
xps_canvas_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *root)
287
3
{
288
3
    xps_item_t *node;
289
290
2.85k
    for (node = xps_down(root); node; node = xps_next(node))
291
2.85k
    {
292
2.85k
        if (!strcmp(xps_tag(node), "Canvas.Resources"))
293
0
        {
294
0
            if (xps_down(node) && xps_resource_dictionary_has_transparency(ctx, base_uri, xps_down(node)))
295
0
                return 1;
296
0
        }
297
298
2.85k
        if (!strcmp(xps_tag(node), "Canvas.OpacityMask"))
299
0
        {
300
            /*dmputs(ctx->memory, "page has transparency: Canvas.OpacityMask\n"); */
301
0
            return 1;
302
0
        }
303
304
2.85k
        if (xps_element_has_transparency(ctx, base_uri, node))
305
0
            return 1;
306
2.85k
    }
307
308
3
    return 0;
309
3
}
310
311
int
312
xps_element_has_transparency(xps_context_t *ctx, char *base_uri, xps_item_t *node)
313
2.87k
{
314
2.87k
    char *opacity_att;
315
2.87k
    char *stroke_att;
316
2.87k
    char *fill_att;
317
318
2.87k
    float samples[XPS_MAX_COLORS];
319
320
2.87k
    stroke_att = xps_att(node, "Stroke");
321
2.87k
    if (stroke_att)
322
62
    {
323
62
        xps_parse_color(ctx, base_uri, stroke_att, NULL, samples);
324
62
        if (samples[0] < 1.0 && samples[0] != 0.0)
325
0
        {
326
            /*dmprintf1(ctx->memory, "page has transparency: Stroke alpha=%g\n", samples[0]); */
327
0
            return 1;
328
0
        }
329
62
    }
330
331
2.87k
    fill_att = xps_att(node, "Fill");
332
2.87k
    if (fill_att)
333
2.80k
    {
334
2.80k
        xps_parse_color(ctx, base_uri, fill_att, NULL, samples);
335
2.80k
        if (samples[0] < 1.0 && samples[0] != 0.0)
336
0
        {
337
            /*dmprintf1(ctx->memory, "page has transparency: Fill alpha=%g\n", samples[0]); */
338
0
            return 1;
339
0
        }
340
2.80k
    }
341
342
2.87k
    opacity_att = xps_att(node, "Opacity");
343
2.87k
    if (opacity_att)
344
0
    {
345
0
        float opacity = atof(opacity_att);
346
0
        if (opacity < 1.0 && opacity != 0.0)
347
0
        {
348
            /*dmprintf1(ctx->memory, "page has transparency: Opacity=%g\n", atof(opacity_att)); */
349
0
            return 1;
350
0
        }
351
0
    }
352
353
2.87k
    if (xps_att(node, "OpacityMask"))
354
0
    {
355
        /*dmputs(ctx->memory, "page has transparency: OpacityMask\n"); */
356
0
        return 1;
357
0
    }
358
359
2.87k
    if (!strcmp(xps_tag(node), "Path"))
360
2.85k
        if (xps_path_has_transparency(ctx, base_uri, node))
361
0
            return 1;
362
2.87k
    if (!strcmp(xps_tag(node), "Glyphs"))
363
14
        if (xps_glyphs_has_transparency(ctx, base_uri, node))
364
0
            return 1;
365
2.87k
    if (!strcmp(xps_tag(node), "Canvas"))
366
3
        if (xps_canvas_has_transparency(ctx, base_uri, node))
367
0
            return 1;
368
369
2.87k
    return 0;
370
2.87k
}