/src/mupdf/source/xps/xps-link.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (C) 2004-2022 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 <string.h> |
27 | | #include <stdlib.h> |
28 | | |
29 | | /* Quick parsing of document to find links. */ |
30 | | |
31 | | static void |
32 | | xps_load_links_in_element(fz_context *ctx, xps_document *doc, fz_matrix ctm, |
33 | | char *base_uri, xps_resource *dict, fz_xml *node, fz_link **link); |
34 | | |
35 | | static void |
36 | | xps_add_link(fz_context *ctx, xps_document *doc, fz_rect area, char *base_uri, char *target_uri, fz_link **head) |
37 | 0 | { |
38 | 0 | fz_link *link = fz_new_derived_link(ctx, fz_link, area, target_uri); |
39 | 0 | link->next = *head; |
40 | 0 | *head = link; |
41 | 0 | } |
42 | | |
43 | | static void |
44 | | xps_load_links_in_path(fz_context *ctx, xps_document *doc, fz_matrix ctm, |
45 | | char *base_uri, xps_resource *dict, fz_xml *root, fz_link **link) |
46 | 0 | { |
47 | 0 | char *navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri"); |
48 | 0 | if (navigate_uri_att) |
49 | 0 | { |
50 | 0 | char *transform_att = fz_xml_att(root, "RenderTransform"); |
51 | 0 | fz_xml *transform_tag = fz_xml_down(fz_xml_find_down(root, "Path.RenderTransform")); |
52 | |
|
53 | 0 | char *data_att = fz_xml_att(root, "Data"); |
54 | 0 | fz_xml *data_tag = fz_xml_down(fz_xml_find_down(root, "Path.Data")); |
55 | |
|
56 | 0 | fz_path *path = NULL; |
57 | 0 | int fill_rule; |
58 | 0 | fz_rect area; |
59 | |
|
60 | 0 | xps_resolve_resource_reference(ctx, doc, dict, &data_att, &data_tag, NULL); |
61 | 0 | xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); |
62 | |
|
63 | 0 | ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm); |
64 | |
|
65 | 0 | if (data_att) |
66 | 0 | path = xps_parse_abbreviated_geometry(ctx, doc, data_att, &fill_rule); |
67 | 0 | else if (data_tag) |
68 | 0 | path = xps_parse_path_geometry(ctx, doc, dict, data_tag, 0, &fill_rule); |
69 | 0 | if (path) |
70 | 0 | { |
71 | 0 | fz_try(ctx) |
72 | 0 | area = fz_bound_path(ctx, path, NULL, ctm); |
73 | 0 | fz_always(ctx) |
74 | 0 | fz_drop_path(ctx, path); |
75 | 0 | fz_catch(ctx) |
76 | 0 | fz_rethrow(ctx); |
77 | 0 | xps_add_link(ctx, doc, area, base_uri, navigate_uri_att, link); |
78 | 0 | } |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | | static void |
83 | | xps_load_links_in_glyphs(fz_context *ctx, xps_document *doc, fz_matrix ctm, |
84 | | char *base_uri, xps_resource *dict, fz_xml *root, fz_link **link) |
85 | 0 | { |
86 | 0 | char *navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri"); |
87 | 0 | if (navigate_uri_att) |
88 | 0 | { |
89 | 0 | char *transform_att = fz_xml_att(root, "RenderTransform"); |
90 | 0 | fz_xml *transform_tag = fz_xml_down(fz_xml_find_down(root, "Path.RenderTransform")); |
91 | |
|
92 | 0 | char *bidi_level_att = fz_xml_att(root, "BidiLevel"); |
93 | 0 | char *font_size_att = fz_xml_att(root, "FontRenderingEmSize"); |
94 | 0 | char *font_uri_att = fz_xml_att(root, "FontUri"); |
95 | 0 | char *origin_x_att = fz_xml_att(root, "OriginX"); |
96 | 0 | char *origin_y_att = fz_xml_att(root, "OriginY"); |
97 | 0 | char *is_sideways_att = fz_xml_att(root, "IsSideways"); |
98 | 0 | char *indices_att = fz_xml_att(root, "Indices"); |
99 | 0 | char *unicode_att = fz_xml_att(root, "UnicodeString"); |
100 | 0 | char *style_att = fz_xml_att(root, "StyleSimulations"); |
101 | |
|
102 | 0 | int is_sideways = 0; |
103 | 0 | int bidi_level = 0; |
104 | 0 | fz_font *font; |
105 | 0 | fz_text *text = NULL; |
106 | 0 | fz_rect area; |
107 | |
|
108 | 0 | xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); |
109 | |
|
110 | 0 | ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm); |
111 | |
|
112 | 0 | if (is_sideways_att) |
113 | 0 | is_sideways = !strcmp(is_sideways_att, "true"); |
114 | 0 | if (bidi_level_att) |
115 | 0 | bidi_level = atoi(bidi_level_att); |
116 | |
|
117 | 0 | font = xps_lookup_font(ctx, doc, base_uri, font_uri_att, style_att); |
118 | 0 | if (!font) |
119 | 0 | return; |
120 | | |
121 | 0 | fz_var(text); |
122 | 0 | fz_try(ctx) |
123 | 0 | { |
124 | 0 | text = xps_parse_glyphs_imp(ctx, doc, ctm, font, fz_atof(font_size_att), |
125 | 0 | fz_atof(origin_x_att), fz_atof(origin_y_att), |
126 | 0 | is_sideways, bidi_level, indices_att, unicode_att); |
127 | 0 | area = fz_bound_text(ctx, text, NULL, ctm); |
128 | 0 | } |
129 | 0 | fz_always(ctx) |
130 | 0 | { |
131 | 0 | fz_drop_text(ctx, text); |
132 | 0 | fz_drop_font(ctx, font); |
133 | 0 | } |
134 | 0 | fz_catch(ctx) |
135 | 0 | fz_rethrow(ctx); |
136 | | |
137 | 0 | xps_add_link(ctx, doc, area, base_uri, navigate_uri_att, link); |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | static void |
142 | | xps_load_links_in_canvas(fz_context *ctx, xps_document *doc, fz_matrix ctm, |
143 | | char *base_uri, xps_resource *dict, fz_xml *root, fz_link **link) |
144 | 0 | { |
145 | 0 | xps_resource *new_dict = NULL; |
146 | 0 | fz_xml *node; |
147 | |
|
148 | 0 | char *navigate_uri_att = fz_xml_att(root, "FixedPage.NavigateUri"); |
149 | 0 | char *transform_att = fz_xml_att(root, "RenderTransform"); |
150 | 0 | fz_xml *transform_tag = fz_xml_down(fz_xml_find_down(root, "Canvas.RenderTransform")); |
151 | 0 | fz_xml *resource_tag = fz_xml_down(fz_xml_find_down(root, "Canvas.Resources")); |
152 | |
|
153 | 0 | if (resource_tag) |
154 | 0 | { |
155 | 0 | new_dict = xps_parse_resource_dictionary(ctx, doc, base_uri, resource_tag); |
156 | 0 | if (new_dict) |
157 | 0 | { |
158 | 0 | new_dict->parent = dict; |
159 | 0 | dict = new_dict; |
160 | 0 | } |
161 | 0 | } |
162 | |
|
163 | 0 | xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); |
164 | |
|
165 | 0 | ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm); |
166 | |
|
167 | 0 | if (navigate_uri_att) |
168 | 0 | fz_warn(ctx, "FixedPage.NavigateUri attribute on Canvas element"); |
169 | |
|
170 | 0 | for (node = fz_xml_down(root); node; node = fz_xml_next(node)) |
171 | 0 | xps_load_links_in_element(ctx, doc, ctm, base_uri, dict, node, link); |
172 | |
|
173 | 0 | if (new_dict) |
174 | 0 | xps_drop_resource_dictionary(ctx, doc, new_dict); |
175 | 0 | } |
176 | | |
177 | | static void |
178 | | xps_load_links_in_element(fz_context *ctx, xps_document *doc, fz_matrix ctm, char *base_uri, xps_resource *dict, fz_xml *node, fz_link **link) |
179 | 0 | { |
180 | 0 | if (fz_xml_is_tag(node, "Path")) |
181 | 0 | xps_load_links_in_path(ctx, doc, ctm, base_uri, dict, node, link); |
182 | 0 | else if (fz_xml_is_tag(node, "Glyphs")) |
183 | 0 | xps_load_links_in_glyphs(ctx, doc, ctm, base_uri, dict, node, link); |
184 | 0 | else if (fz_xml_is_tag(node, "Canvas")) |
185 | 0 | xps_load_links_in_canvas(ctx, doc, ctm, base_uri, dict, node, link); |
186 | 0 | else if (fz_xml_is_tag(node, "AlternateContent")) |
187 | 0 | { |
188 | 0 | node = xps_lookup_alternate_content(ctx, doc, node); |
189 | 0 | if (node) |
190 | 0 | xps_load_links_in_element(ctx, doc, ctm, base_uri, dict, node, link); |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | | static void |
195 | | xps_load_links_in_fixed_page(fz_context *ctx, xps_document *doc, fz_matrix ctm, xps_page *page, fz_link **link) |
196 | 0 | { |
197 | 0 | fz_xml *root, *node, *resource_tag; |
198 | 0 | xps_resource *dict = NULL; |
199 | 0 | char base_uri[1024]; |
200 | 0 | char *s; |
201 | |
|
202 | 0 | root = fz_xml_root(page->xml); |
203 | |
|
204 | 0 | if (!root) |
205 | 0 | return; |
206 | | |
207 | 0 | fz_strlcpy(base_uri, page->fix->name, sizeof base_uri); |
208 | 0 | s = strrchr(base_uri, '/'); |
209 | 0 | if (s) |
210 | 0 | s[1] = 0; |
211 | |
|
212 | 0 | resource_tag = fz_xml_down(fz_xml_find_down(root, "FixedPage.Resources")); |
213 | 0 | if (resource_tag) |
214 | 0 | dict = xps_parse_resource_dictionary(ctx, doc, base_uri, resource_tag); |
215 | |
|
216 | 0 | for (node = fz_xml_down(root); node; node = fz_xml_next(node)) |
217 | 0 | xps_load_links_in_element(ctx, doc, ctm, base_uri, dict, node, link); |
218 | |
|
219 | 0 | if (dict) |
220 | 0 | xps_drop_resource_dictionary(ctx, doc, dict); |
221 | 0 | } |
222 | | |
223 | | fz_link * |
224 | | xps_load_links(fz_context *ctx, fz_page *page_) |
225 | 0 | { |
226 | 0 | xps_page *page = (xps_page*)page_; |
227 | 0 | fz_matrix ctm; |
228 | 0 | fz_link *link = NULL; |
229 | 0 | ctm = fz_scale(72.0f / 96.0f, 72.0f / 96.0f); |
230 | 0 | xps_load_links_in_fixed_page(ctx, (xps_document*)page->super.doc, ctm, page, &link); |
231 | 0 | return link; |
232 | 0 | } |