/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 | } |