xps-tile.c 11 KB

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