Coverage Report

Created: 2023-06-07 06:20

/src/mupdf/source/xps/xps-tile.c
Line
Count
Source (jump to first uncovered line)
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
0
{
50
0
  fz_device *dev = doc->dev;
51
0
  fz_path *path;
52
53
0
  path = fz_new_path(ctx);
54
0
  fz_try(ctx)
55
0
  {
56
0
    fz_moveto(ctx, path, viewbox.x0, viewbox.y0);
57
0
    fz_lineto(ctx, path, viewbox.x0, viewbox.y1);
58
0
    fz_lineto(ctx, path, viewbox.x1, viewbox.y1);
59
0
    fz_lineto(ctx, path, viewbox.x1, viewbox.y0);
60
0
    fz_closepath(ctx, path);
61
0
    fz_clip_path(ctx, dev, path, 0, ctm, fz_infinite_rect);
62
0
  }
63
0
  fz_always(ctx)
64
0
    fz_drop_path(ctx, path);
65
0
  fz_catch(ctx)
66
0
    fz_rethrow(ctx);
67
68
0
  c->func(ctx, doc, ctm, viewbox, c->base_uri, c->dict, c->root, c->user);
69
0
  fz_pop_clip(ctx, dev);
70
0
}
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
0
{
75
0
  fz_matrix ttm;
76
77
0
  xps_paint_tiling_brush_clipped(ctx, doc, ctm, viewbox, c);
78
79
0
  if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y)
80
0
  {
81
0
    ttm = fz_pre_scale(fz_pre_translate(ctm, viewbox.x1 * 2, 0), -1, 1);
82
0
    xps_paint_tiling_brush_clipped(ctx, doc, ttm, viewbox, c);
83
0
  }
84
85
0
  if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y)
86
0
  {
87
0
    ttm = fz_pre_scale(fz_pre_translate(ctm, 0, viewbox.y1 * 2), 1, -1);
88
0
    xps_paint_tiling_brush_clipped(ctx, doc, ttm, viewbox, c);
89
0
  }
90
91
0
  if (tile_mode == TILE_FLIP_X_Y)
92
0
  {
93
0
    ttm = fz_pre_scale(fz_pre_translate(ctm, viewbox.x1 * 2, viewbox.y1 * 2), -1, -1);
94
0
    xps_paint_tiling_brush_clipped(ctx, doc, ttm, viewbox, c);
95
0
  }
96
0
}
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
0
{
103
0
  fz_device *dev = doc->dev;
104
0
  fz_xml *node;
105
0
  struct closure c;
106
107
0
  char *opacity_att;
108
0
  char *transform_att;
109
0
  char *viewbox_att;
110
0
  char *viewport_att;
111
0
  char *tile_mode_att;
112
113
0
  fz_xml *transform_tag = NULL;
114
115
0
  fz_rect viewbox;
116
0
  fz_rect viewport;
117
0
  float xstep, ystep;
118
0
  float xscale, yscale;
119
0
  int tile_mode;
120
121
0
  opacity_att = fz_xml_att(root, "Opacity");
122
0
  transform_att = fz_xml_att(root, "Transform");
123
0
  viewbox_att = fz_xml_att(root, "Viewbox");
124
0
  viewport_att = fz_xml_att(root, "Viewport");
125
0
  tile_mode_att = fz_xml_att(root, "TileMode");
126
127
0
  c.base_uri = base_uri;
128
0
  c.dict = dict;
129
0
  c.root = root;
130
0
  c.user = user;
131
0
  c.func = func;
132
133
0
  for (node = fz_xml_down(root); node; node = fz_xml_next(node))
134
0
  {
135
0
    if (fz_xml_is_tag(node, "ImageBrush.Transform"))
136
0
      transform_tag = fz_xml_down(node);
137
0
    if (fz_xml_is_tag(node, "VisualBrush.Transform"))
138
0
      transform_tag = fz_xml_down(node);
139
0
  }
140
141
0
  xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
142
143
0
  ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm);
144
145
0
  viewbox = fz_unit_rect;
146
0
  if (viewbox_att)
147
0
    viewbox = xps_parse_rectangle(ctx, doc, viewbox_att);
148
149
0
  viewport = fz_unit_rect;
150
0
  if (viewport_att)
151
0
    viewport = xps_parse_rectangle(ctx, doc, viewport_att);
152
153
0
  if (fabsf(viewport.x1 - viewport.x0) < 0.01f || fabsf(viewport.y1 - viewport.y0) < 0.01f)
154
0
    fz_warn(ctx, "not drawing tile for viewport size %.4f x %.4f", viewport.x1 - viewport.x0, viewport.y1 - viewport.y0);
155
0
  else if (fabsf(viewbox.x1 - viewbox.x0) < 0.01f || fabsf(viewbox.y1 - viewbox.y0) < 0.01f)
156
0
    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
0
  if (fabsf(viewport.x1 - viewport.x0) < 0.01f) return;
160
0
  if (fabsf(viewport.y1 - viewport.y0) < 0.01f) return;
161
0
  if (fabsf(viewbox.x1 - viewbox.x0) < 0.01f) return;
162
0
  if (fabsf(viewbox.y1 - viewbox.y0) < 0.01f) return;
163
164
0
  xstep = viewbox.x1 - viewbox.x0;
165
0
  ystep = viewbox.y1 - viewbox.y0;
166
167
0
  xscale = (viewport.x1 - viewport.x0) / xstep;
168
0
  yscale = (viewport.y1 - viewport.y0) / ystep;
169
170
0
  tile_mode = TILE_NONE;
171
0
  if (tile_mode_att)
172
0
  {
173
0
    if (!strcmp(tile_mode_att, "None"))
174
0
      tile_mode = TILE_NONE;
175
0
    if (!strcmp(tile_mode_att, "Tile"))
176
0
      tile_mode = TILE_TILE;
177
0
    if (!strcmp(tile_mode_att, "FlipX"))
178
0
      tile_mode = TILE_FLIP_X;
179
0
    if (!strcmp(tile_mode_att, "FlipY"))
180
0
      tile_mode = TILE_FLIP_Y;
181
0
    if (!strcmp(tile_mode_att, "FlipXY"))
182
0
      tile_mode = TILE_FLIP_X_Y;
183
0
  }
184
185
0
  if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y)
186
0
    xstep *= 2;
187
0
  if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y)
188
0
    ystep *= 2;
189
190
0
  xps_begin_opacity(ctx, doc, ctm, area, base_uri, dict, opacity_att, NULL);
191
192
0
  ctm = fz_pre_translate(ctm, viewport.x0, viewport.y0);
193
0
  ctm = fz_pre_scale(ctm, xscale, yscale);
194
0
  ctm = fz_pre_translate(ctm, -viewbox.x0, -viewbox.y0);
195
196
0
  if (tile_mode != TILE_NONE)
197
0
  {
198
0
    int x0, y0, x1, y1;
199
0
    fz_matrix invctm;
200
0
    invctm = fz_invert_matrix(ctm);
201
0
    area = fz_transform_rect(area, invctm);
202
0
    x0 = floorf(area.x0 / xstep);
203
0
    y0 = floorf(area.y0 / ystep);
204
0
    x1 = ceilf(area.x1 / xstep);
205
0
    y1 = ceilf(area.y1 / ystep);
206
207
0
#ifdef TILE
208
0
    if ((x1 - x0) * (y1 - y0) > 1)
209
#else
210
    if (0)
211
#endif
212
0
    {
213
0
      fz_rect bigview = viewbox;
214
0
      bigview.x1 = bigview.x0 + xstep;
215
0
      bigview.y1 = bigview.y0 + ystep;
216
0
      fz_begin_tile(ctx, dev, area, bigview, xstep, ystep, ctm);
217
0
      xps_paint_tiling_brush(ctx, doc, ctm, viewbox, tile_mode, &c);
218
0
      fz_end_tile(ctx, dev);
219
0
    }
220
0
    else
221
0
    {
222
0
      int x, y;
223
0
      for (y = y0; y < y1; y++)
224
0
      {
225
0
        for (x = x0; x < x1; x++)
226
0
        {
227
0
          fz_matrix ttm = fz_pre_translate(ctm, xstep * x, ystep * y);
228
0
          xps_paint_tiling_brush(ctx, doc, ttm, viewbox, tile_mode, &c);
229
0
        }
230
0
      }
231
0
    }
232
0
  }
233
0
  else
234
0
  {
235
0
    xps_paint_tiling_brush(ctx, doc, ctm, viewbox, tile_mode, &c);
236
0
  }
237
238
0
  xps_end_opacity(ctx, doc, base_uri, dict, opacity_att, NULL);
239
0
}
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
0
{
245
0
  xps_parse_element(ctx, doc, ctm, area, base_uri, dict, (fz_xml *)visual_tag);
246
0
}
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
0
{
252
0
  fz_xml *node;
253
254
0
  char *visual_uri;
255
0
  char *visual_att;
256
0
  fz_xml *visual_tag = NULL;
257
258
0
  visual_att = fz_xml_att(root, "Visual");
259
260
0
  for (node = fz_xml_down(root); node; node = fz_xml_next(node))
261
0
  {
262
0
    if (fz_xml_is_tag(node, "VisualBrush.Visual"))
263
0
      visual_tag = fz_xml_down(node);
264
0
  }
265
266
0
  visual_uri = base_uri;
267
0
  xps_resolve_resource_reference(ctx, doc, dict, &visual_att, &visual_tag, &visual_uri);
268
269
0
  if (visual_tag)
270
0
  {
271
0
    xps_parse_tiling_brush(ctx, doc, ctm, area,
272
0
      visual_uri, dict, root, xps_paint_visual_brush, visual_tag);
273
0
  }
274
0
}
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
0
{
279
0
  fz_device *dev = doc->dev;
280
0
  xps_resource *new_dict = NULL;
281
0
  fz_xml *node;
282
0
  char *opacity_mask_uri;
283
284
0
  char *transform_att;
285
0
  char *clip_att;
286
0
  char *opacity_att;
287
0
  char *opacity_mask_att;
288
289
0
  fz_xml *transform_tag = NULL;
290
0
  fz_xml *clip_tag = NULL;
291
0
  fz_xml *opacity_mask_tag = NULL;
292
293
0
  transform_att = fz_xml_att(root, "RenderTransform");
294
0
  clip_att = fz_xml_att(root, "Clip");
295
0
  opacity_att = fz_xml_att(root, "Opacity");
296
0
  opacity_mask_att = fz_xml_att(root, "OpacityMask");
297
298
0
  for (node = fz_xml_down(root); node; node = fz_xml_next(node))
299
0
  {
300
0
    if (fz_xml_is_tag(node, "Canvas.Resources") && fz_xml_down(node))
301
0
    {
302
0
      if (new_dict)
303
0
      {
304
0
        fz_warn(ctx, "ignoring follow-up resource dictionaries");
305
0
      }
306
0
      else
307
0
      {
308
0
        new_dict = xps_parse_resource_dictionary(ctx, doc, base_uri, fz_xml_down(node));
309
0
        if (new_dict)
310
0
        {
311
0
          new_dict->parent = dict;
312
0
          dict = new_dict;
313
0
        }
314
0
      }
315
0
    }
316
317
0
    if (fz_xml_is_tag(node, "Canvas.RenderTransform"))
318
0
      transform_tag = fz_xml_down(node);
319
0
    if (fz_xml_is_tag(node, "Canvas.Clip"))
320
0
      clip_tag = fz_xml_down(node);
321
0
    if (fz_xml_is_tag(node, "Canvas.OpacityMask"))
322
0
      opacity_mask_tag = fz_xml_down(node);
323
0
  }
324
325
0
  fz_try(ctx)
326
0
  {
327
0
    opacity_mask_uri = base_uri;
328
0
    xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL);
329
0
    xps_resolve_resource_reference(ctx, doc, dict, &clip_att, &clip_tag, NULL);
330
0
    xps_resolve_resource_reference(ctx, doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
331
332
0
    ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm);
333
334
0
    if (clip_att || clip_tag)
335
0
      xps_clip(ctx, doc, ctm, dict, clip_att, clip_tag);
336
337
0
    xps_begin_opacity(ctx, doc, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
338
339
0
    for (node = fz_xml_down(root); node; node = fz_xml_next(node))
340
0
      xps_parse_element(ctx, doc, ctm, area, base_uri, dict, node);
341
342
0
    xps_end_opacity(ctx, doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
343
344
0
    if (clip_att || clip_tag)
345
0
      fz_pop_clip(ctx, dev);
346
0
  }
347
0
  fz_always(ctx)
348
0
    xps_drop_resource_dictionary(ctx, doc, new_dict);
349
0
  fz_catch(ctx)
350
0
    fz_rethrow(ctx);
351
0
}
352
353
void
354
xps_parse_fixed_page(fz_context *ctx, xps_document *doc, fz_matrix ctm, xps_page *page)
355
0
{
356
0
  fz_xml *root, *node;
357
0
  xps_resource *dict;
358
0
  char base_uri[1024];
359
0
  fz_rect area;
360
0
  char *s;
361
362
0
  fz_strlcpy(base_uri, page->fix->name, sizeof base_uri);
363
0
  s = strrchr(base_uri, '/');
364
0
  if (s)
365
0
    s[1] = 0;
366
367
0
  dict = NULL;
368
369
0
  doc->opacity_top = 0;
370
0
  doc->opacity[0] = 1;
371
372
0
  root = fz_xml_root(page->xml);
373
0
  if (!root)
374
0
    return;
375
376
0
  area = fz_transform_rect(fz_unit_rect, fz_scale(page->fix->width, page->fix->height));
377
378
0
  fz_try(ctx)
379
0
  {
380
0
    for (node = fz_xml_down(root); node; node = fz_xml_next(node))
381
0
    {
382
0
      if (fz_xml_is_tag(node, "FixedPage.Resources") && fz_xml_down(node))
383
0
      {
384
0
        if (dict)
385
0
          fz_warn(ctx, "ignoring follow-up resource dictionaries");
386
0
        else
387
0
          dict = xps_parse_resource_dictionary(ctx, doc, base_uri, fz_xml_down(node));
388
0
      }
389
0
      xps_parse_element(ctx, doc, ctm, area, base_uri, dict, node);
390
0
    }
391
0
  }
392
0
  fz_always(ctx)
393
0
    xps_drop_resource_dictionary(ctx, doc, dict);
394
0
  fz_catch(ctx)
395
0
    fz_rethrow(ctx);
396
0
}
397
398
void
399
xps_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
400
0
{
401
0
  xps_page *page = (xps_page*)page_;
402
0
  xps_document *doc = (xps_document*)page->super.doc;
403
0
  fz_matrix page_ctm;
404
405
0
  page_ctm = fz_pre_scale(ctm, 72.0f / 96.0f, 72.0f / 96.0f);
406
407
0
  doc->cookie = cookie;
408
0
  doc->dev = dev;
409
0
  xps_parse_fixed_page(ctx, doc, page_ctm, page);
410
0
  doc->cookie = NULL;
411
0
  doc->dev = NULL;
412
0
}