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