comparison mupdf-source/source/xps/xps-tile.c @ 3:2c135c81b16c

MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:44:09 +0200
parents b50eed0cc0ef
children
comparison
equal deleted inserted replaced
0:6015a75abc2d 3:2c135c81b16c
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24 #include "xps-imp.h"
25
26 #include <math.h>
27 #include <string.h>
28
29 #define TILE
30
31 /*
32 * Parse a tiling brush (visual and image brushes at this time) common
33 * properties. Use the callback to draw the individual tiles.
34 */
35
36 enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y };
37
38 struct closure
39 {
40 char *base_uri;
41 xps_resource *dict;
42 fz_xml *root;
43 void *user;
44 void (*func)(fz_context *ctx, xps_document*, fz_matrix, fz_rect, char*, xps_resource*, fz_xml*, void*);
45 };
46
47 static void
48 xps_paint_tiling_brush_clipped(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect viewbox, struct closure *c)
49 {
50 fz_device *dev = doc->dev;
51 fz_path *path;
52
53 path = fz_new_path(ctx);
54 fz_try(ctx)
55 {
56 fz_moveto(ctx, path, viewbox.x0, viewbox.y0);
57 fz_lineto(ctx, path, viewbox.x0, viewbox.y1);
58 fz_lineto(ctx, path, viewbox.x1, viewbox.y1);
59 fz_lineto(ctx, path, viewbox.x1, viewbox.y0);
60 fz_closepath(ctx, path);
61 fz_clip_path(ctx, dev, path, 0, ctm, fz_infinite_rect);
62 }
63 fz_always(ctx)
64 fz_drop_path(ctx, path);
65 fz_catch(ctx)
66 fz_rethrow(ctx);
67
68 c->func(ctx, doc, ctm, viewbox, c->base_uri, c->dict, c->root, c->user);
69 fz_pop_clip(ctx, dev);
70 }
71
72 static void
73 xps_paint_tiling_brush(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect viewbox, int tile_mode, struct closure *c)
74 {
75 fz_matrix ttm;
76
77 xps_paint_tiling_brush_clipped(ctx, doc, ctm, viewbox, c);
78
79 if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y)
80 {
81 ttm = fz_pre_scale(fz_pre_translate(ctm, viewbox.x1 * 2, 0), -1, 1);
82 xps_paint_tiling_brush_clipped(ctx, doc, ttm, viewbox, c);
83 }
84
85 if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y)
86 {
87 ttm = fz_pre_scale(fz_pre_translate(ctm, 0, viewbox.y1 * 2), 1, -1);
88 xps_paint_tiling_brush_clipped(ctx, doc, ttm, viewbox, c);
89 }
90
91 if (tile_mode == TILE_FLIP_X_Y)
92 {
93 ttm = fz_pre_scale(fz_pre_translate(ctm, viewbox.x1 * 2, viewbox.y1 * 2), -1, -1);
94 xps_paint_tiling_brush_clipped(ctx, doc, ttm, viewbox, c);
95 }
96 }
97
98 void
99 xps_parse_tiling_brush(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area,
100 char *base_uri, xps_resource *dict, fz_xml *root,
101 void (*func)(fz_context *ctx, xps_document*, fz_matrix, fz_rect, char*, xps_resource*, fz_xml*, void*), void *user)
102 {
103 fz_device *dev = doc->dev;
104 fz_xml *node;
105 struct closure c;
106
107 char *opacity_att;
108 char *transform_att;
109 char *viewbox_att;
110 char *viewport_att;
111 char *tile_mode_att;
112
113 fz_xml *transform_tag = NULL;
114
115 fz_rect viewbox;
116 fz_rect viewport;
117 float xstep, ystep;
118 float xscale, yscale;
119 int tile_mode;
120
121 opacity_att = fz_xml_att(root, "Opacity");
122 transform_att = fz_xml_att(root, "Transform");
123 viewbox_att = fz_xml_att(root, "Viewbox");
124 viewport_att = fz_xml_att(root, "Viewport");
125 tile_mode_att = fz_xml_att(root, "TileMode");
126
127 c.base_uri = base_uri;
128 c.dict = dict;
129 c.root = root;
130 c.user = user;
131 c.func = func;
132
133 for (node = fz_xml_down(root); node; node = fz_xml_next(node))
134 {
135 if (fz_xml_is_tag(node, "ImageBrush.Transform"))
136 transform_tag = fz_xml_down(node);
137 if (fz_xml_is_tag(node, "VisualBrush.Transform"))
138 transform_tag = fz_xml_down(node);
139 }
140
141 xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
142
143 ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm);
144
145 viewbox = fz_unit_rect;
146 if (viewbox_att)
147 viewbox = xps_parse_rectangle(ctx, doc, viewbox_att);
148
149 viewport = fz_unit_rect;
150 if (viewport_att)
151 viewport = xps_parse_rectangle(ctx, doc, viewport_att);
152
153 if (fabsf(viewport.x1 - viewport.x0) < 0.01f || fabsf(viewport.y1 - viewport.y0) < 0.01f)
154 fz_warn(ctx, "not drawing tile for viewport size %.4f x %.4f", viewport.x1 - viewport.x0, viewport.y1 - viewport.y0);
155 else if (fabsf(viewbox.x1 - viewbox.x0) < 0.01f || fabsf(viewbox.y1 - viewbox.y0) < 0.01f)
156 fz_warn(ctx, "not drawing tile for viewbox size %.4f x %.4f", viewbox.x1 - viewbox.x0, viewbox.y1 - viewbox.y0);
157
158 /* some sanity checks on the viewport/viewbox size */
159 if (fabsf(viewport.x1 - viewport.x0) < 0.01f) return;
160 if (fabsf(viewport.y1 - viewport.y0) < 0.01f) return;
161 if (fabsf(viewbox.x1 - viewbox.x0) < 0.01f) return;
162 if (fabsf(viewbox.y1 - viewbox.y0) < 0.01f) return;
163
164 xstep = viewbox.x1 - viewbox.x0;
165 ystep = viewbox.y1 - viewbox.y0;
166
167 xscale = (viewport.x1 - viewport.x0) / xstep;
168 yscale = (viewport.y1 - viewport.y0) / ystep;
169
170 tile_mode = TILE_NONE;
171 if (tile_mode_att)
172 {
173 if (!strcmp(tile_mode_att, "None"))
174 tile_mode = TILE_NONE;
175 if (!strcmp(tile_mode_att, "Tile"))
176 tile_mode = TILE_TILE;
177 if (!strcmp(tile_mode_att, "FlipX"))
178 tile_mode = TILE_FLIP_X;
179 if (!strcmp(tile_mode_att, "FlipY"))
180 tile_mode = TILE_FLIP_Y;
181 if (!strcmp(tile_mode_att, "FlipXY"))
182 tile_mode = TILE_FLIP_X_Y;
183 }
184
185 if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y)
186 xstep *= 2;
187 if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y)
188 ystep *= 2;
189
190 xps_begin_opacity(ctx, doc, ctm, area, base_uri, dict, opacity_att, NULL);
191
192 ctm = fz_pre_translate(ctm, viewport.x0, viewport.y0);
193 ctm = fz_pre_scale(ctm, xscale, yscale);
194 ctm = fz_pre_translate(ctm, -viewbox.x0, -viewbox.y0);
195
196 if (tile_mode != TILE_NONE)
197 {
198 int x0, y0, x1, y1;
199 fz_matrix invctm;
200 invctm = fz_invert_matrix(ctm);
201 area = fz_transform_rect(area, invctm);
202 x0 = floorf(area.x0 / xstep);
203 y0 = floorf(area.y0 / ystep);
204 x1 = ceilf(area.x1 / xstep);
205 y1 = ceilf(area.y1 / ystep);
206
207 #ifdef TILE
208 if ((x1 - x0) * (y1 - y0) > 1)
209 #else
210 if (0)
211 #endif
212 {
213 fz_rect bigview = viewbox;
214 bigview.x1 = bigview.x0 + xstep;
215 bigview.y1 = bigview.y0 + ystep;
216 fz_begin_tile(ctx, dev, area, bigview, xstep, ystep, ctm);
217 xps_paint_tiling_brush(ctx, doc, ctm, viewbox, tile_mode, &c);
218 fz_end_tile(ctx, dev);
219 }
220 else
221 {
222 int x, y;
223 for (y = y0; y < y1; y++)
224 {
225 for (x = x0; x < x1; x++)
226 {
227 fz_matrix ttm = fz_pre_translate(ctm, xstep * x, ystep * y);
228 xps_paint_tiling_brush(ctx, doc, ttm, viewbox, tile_mode, &c);
229 }
230 }
231 }
232 }
233 else
234 {
235 xps_paint_tiling_brush(ctx, doc, ctm, viewbox, tile_mode, &c);
236 }
237
238 xps_end_opacity(ctx, doc, base_uri, dict, opacity_att, NULL);
239 }
240
241 static void
242 xps_paint_visual_brush(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area,
243 char *base_uri, xps_resource *dict, fz_xml *root, void *visual_tag)
244 {
245 xps_parse_element(ctx, doc, ctm, area, base_uri, dict, (fz_xml *)visual_tag);
246 }
247
248 void
249 xps_parse_visual_brush(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area,
250 char *base_uri, xps_resource *dict, fz_xml *root)
251 {
252 fz_xml *node;
253
254 char *visual_uri;
255 char *visual_att;
256 fz_xml *visual_tag = NULL;
257
258 visual_att = fz_xml_att(root, "Visual");
259
260 for (node = fz_xml_down(root); node; node = fz_xml_next(node))
261 {
262 if (fz_xml_is_tag(node, "VisualBrush.Visual"))
263 visual_tag = fz_xml_down(node);
264 }
265
266 visual_uri = base_uri;
267 xps_resolve_resource_reference(ctx, doc, dict, &visual_att, &visual_tag, &visual_uri);
268
269 if (visual_tag)
270 {
271 xps_parse_tiling_brush(ctx, doc, ctm, area,
272 visual_uri, dict, root, xps_paint_visual_brush, visual_tag);
273 }
274 }
275
276 void
277 xps_parse_canvas(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, fz_xml *root)
278 {
279 fz_device *dev = doc->dev;
280 xps_resource *new_dict = NULL;
281 fz_xml *node;
282 char *opacity_mask_uri;
283
284 char *transform_att;
285 char *clip_att;
286 char *opacity_att;
287 char *opacity_mask_att;
288
289 fz_xml *transform_tag = NULL;
290 fz_xml *clip_tag = NULL;
291 fz_xml *opacity_mask_tag = NULL;
292
293 transform_att = fz_xml_att(root, "RenderTransform");
294 clip_att = fz_xml_att(root, "Clip");
295 opacity_att = fz_xml_att(root, "Opacity");
296 opacity_mask_att = fz_xml_att(root, "OpacityMask");
297
298 for (node = fz_xml_down(root); node; node = fz_xml_next(node))
299 {
300 if (fz_xml_is_tag(node, "Canvas.Resources") && fz_xml_down(node))
301 {
302 if (new_dict)
303 {
304 fz_warn(ctx, "ignoring follow-up resource dictionaries");
305 }
306 else
307 {
308 new_dict = xps_parse_resource_dictionary(ctx, doc, base_uri, fz_xml_down(node));
309 if (new_dict)
310 {
311 new_dict->parent = dict;
312 dict = new_dict;
313 }
314 }
315 }
316
317 if (fz_xml_is_tag(node, "Canvas.RenderTransform"))
318 transform_tag = fz_xml_down(node);
319 if (fz_xml_is_tag(node, "Canvas.Clip"))
320 clip_tag = fz_xml_down(node);
321 if (fz_xml_is_tag(node, "Canvas.OpacityMask"))
322 opacity_mask_tag = fz_xml_down(node);
323 }
324
325 fz_try(ctx)
326 {
327 opacity_mask_uri = base_uri;
328 xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
329 xps_resolve_resource_reference(ctx, doc, dict, &clip_att, &clip_tag, NULL);
330 xps_resolve_resource_reference(ctx, doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
331
332 ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm);
333
334 if (clip_att || clip_tag)
335 xps_clip(ctx, doc, ctm, dict, clip_att, clip_tag);
336
337 xps_begin_opacity(ctx, doc, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
338
339 for (node = fz_xml_down(root); node; node = fz_xml_next(node))
340 xps_parse_element(ctx, doc, ctm, area, base_uri, dict, node);
341
342 xps_end_opacity(ctx, doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
343
344 if (clip_att || clip_tag)
345 fz_pop_clip(ctx, dev);
346 }
347 fz_always(ctx)
348 xps_drop_resource_dictionary(ctx, doc, new_dict);
349 fz_catch(ctx)
350 fz_rethrow(ctx);
351 }
352
353 void
354 xps_parse_fixed_page(fz_context *ctx, xps_document *doc, fz_matrix ctm, xps_page *page)
355 {
356 fz_xml *root, *node;
357 xps_resource *dict;
358 char base_uri[1024];
359 fz_rect area;
360 char *s;
361
362 fz_strlcpy(base_uri, page->fix->name, sizeof base_uri);
363 s = strrchr(base_uri, '/');
364 if (s)
365 s[1] = 0;
366
367 dict = NULL;
368
369 doc->opacity_top = 0;
370 doc->opacity[0] = 1;
371
372 root = fz_xml_root(page->xml);
373 if (!root)
374 return;
375
376 area = fz_transform_rect(fz_unit_rect, fz_scale(page->fix->width, page->fix->height));
377
378 fz_try(ctx)
379 {
380 for (node = fz_xml_down(root); node; node = fz_xml_next(node))
381 {
382 if (fz_xml_is_tag(node, "FixedPage.Resources") && fz_xml_down(node))
383 {
384 if (dict)
385 fz_warn(ctx, "ignoring follow-up resource dictionaries");
386 else
387 dict = xps_parse_resource_dictionary(ctx, doc, base_uri, fz_xml_down(node));
388 }
389 xps_parse_element(ctx, doc, ctm, area, base_uri, dict, node);
390 }
391 }
392 fz_always(ctx)
393 xps_drop_resource_dictionary(ctx, doc, dict);
394 fz_catch(ctx)
395 fz_rethrow(ctx);
396 }
397
398 void
399 xps_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
400 {
401 xps_page *page = (xps_page*)page_;
402 xps_document *doc = (xps_document*)page->super.doc;
403 fz_matrix page_ctm;
404
405 page_ctm = fz_pre_scale(ctm, 72.0f / 96.0f, 72.0f / 96.0f);
406
407 doc->cookie = cookie;
408 doc->dev = dev;
409 xps_parse_fixed_page(ctx, doc, page_ctm, page);
410 doc->cookie = NULL;
411 doc->dev = NULL;
412 }