comparison mupdf-source/source/svg/svg-doc.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2024 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 "svg-imp.h"
25
26 typedef struct
27 {
28 fz_page super;
29 svg_document *doc;
30 } svg_page;
31
32 static void
33 svg_drop_document(fz_context *ctx, fz_document *doc_)
34 {
35 svg_document *doc = (svg_document*)doc_;
36 fz_drop_tree(ctx, doc->idmap, NULL);
37 fz_drop_xml(ctx, doc->xml);
38 }
39
40 static int
41 svg_count_pages(fz_context *ctx, fz_document *doc_, int chapter)
42 {
43 return 1;
44 }
45
46 static fz_rect
47 svg_bound_page(fz_context *ctx, fz_page *page_, fz_box_type box)
48 {
49 svg_page *page = (svg_page*)page_;
50 svg_document *doc = page->doc;
51
52 svg_parse_document_bounds(ctx, doc, doc->root);
53
54 return fz_make_rect(0, 0, doc->width, doc->height);
55 }
56
57 static void
58 svg_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
59 {
60 svg_page *page = (svg_page*)page_;
61 svg_document *doc = page->doc;
62 svg_run_document(ctx, doc, doc->root, dev, ctm);
63 }
64
65 static void
66 svg_drop_page(fz_context *ctx, fz_page *page_)
67 {
68 /* nothing */
69 }
70
71 static fz_page *
72 svg_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number)
73 {
74 svg_document *doc = (svg_document*)doc_;
75 svg_page *page;
76
77 if (number != 0)
78 fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot find page %d", number);
79
80 page = fz_new_derived_page(ctx, svg_page, doc_);
81 page->super.bound_page = svg_bound_page;
82 page->super.run_page_contents = svg_run_page;
83 page->super.drop_page = svg_drop_page;
84 page->doc = doc;
85
86 return (fz_page*)page;
87 }
88
89 static void
90 svg_build_id_map(fz_context *ctx, svg_document *doc, fz_xml *root)
91 {
92 fz_xml *node;
93
94 char *id_att = fz_xml_att(root, "id");
95 if (id_att)
96 doc->idmap = fz_tree_insert(ctx, doc->idmap, id_att, root);
97
98 for (node = fz_xml_down(root); node; node = fz_xml_next(node))
99 svg_build_id_map(ctx, doc, node);
100 }
101
102 static int
103 svg_lookup_metadata(fz_context *ctx, fz_document *doc_, const char *key, char *buf, size_t size)
104 {
105 if (!strcmp(key, FZ_META_FORMAT))
106 return 1 + (int)fz_strlcpy(buf, "SVG", size);
107 return -1;
108 }
109
110 static fz_document *
111 svg_open_document_with_xml(fz_context *ctx, fz_xml_doc *xmldoc, fz_xml *xml, const char *base_uri, fz_archive *zip)
112 {
113 svg_document *doc;
114
115 doc = fz_new_derived_document(ctx, svg_document);
116 doc->super.drop_document = svg_drop_document;
117 doc->super.count_pages = svg_count_pages;
118 doc->super.load_page = svg_load_page;
119 doc->super.lookup_metadata = svg_lookup_metadata;
120
121 doc->idmap = NULL;
122 if (base_uri)
123 fz_strlcpy(doc->base_uri, base_uri, sizeof doc->base_uri);
124 doc->xml = NULL;
125 doc->root = xml;
126 doc->zip = zip;
127
128 fz_try(ctx)
129 {
130 if (xmldoc)
131 svg_build_id_map(ctx, doc, fz_xml_root(xmldoc));
132 else
133 svg_build_id_map(ctx, doc, doc->root);
134 }
135 fz_catch(ctx)
136 {
137 fz_drop_document(ctx, &doc->super);
138 fz_rethrow(ctx);
139 }
140
141 return (fz_document*)doc;
142 }
143
144 static fz_document *
145 svg_open_document_with_buffer(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip)
146 {
147 svg_document *doc;
148
149 doc = fz_new_derived_document(ctx, svg_document);
150 doc->super.drop_document = svg_drop_document;
151 doc->super.count_pages = svg_count_pages;
152 doc->super.load_page = svg_load_page;
153 doc->super.lookup_metadata = svg_lookup_metadata;
154
155 doc->idmap = NULL;
156 if (base_uri)
157 fz_strlcpy(doc->base_uri, base_uri, sizeof doc->base_uri);
158 doc->zip = zip;
159
160 fz_try(ctx)
161 {
162 doc->xml = fz_parse_xml(ctx, buf, 0);
163 doc->root = fz_xml_root(doc->xml);
164 svg_build_id_map(ctx, doc, doc->root);
165 }
166 fz_catch(ctx)
167 {
168 fz_drop_document(ctx, &doc->super);
169 fz_rethrow(ctx);
170 }
171
172 return (fz_document*)doc;
173 }
174
175 static fz_document *
176 svg_open_document(fz_context *ctx, const fz_document_handler *handler, fz_stream *file, fz_stream *accel, fz_archive *zip, void *state)
177 {
178 fz_buffer *buf = fz_read_all(ctx, file, 0);
179 fz_document *doc = NULL;
180
181 fz_try(ctx)
182 doc = svg_open_document_with_buffer(ctx, buf, NULL, NULL);
183 fz_always(ctx)
184 fz_drop_buffer(ctx, buf);
185 fz_catch(ctx)
186 fz_rethrow(ctx);
187
188 return doc;
189 }
190
191 fz_display_list *
192 fz_new_display_list_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip, float *w, float *h)
193 {
194 fz_document *doc;
195 fz_display_list *list = NULL;
196
197 doc = svg_open_document_with_buffer(ctx, buf, base_uri, zip);
198 fz_try(ctx)
199 {
200 list = fz_new_display_list_from_page_number(ctx, doc, 0);
201 *w = ((svg_document*)doc)->width;
202 *h = ((svg_document*)doc)->height;
203 }
204 fz_always(ctx)
205 fz_drop_document(ctx, doc);
206 fz_catch(ctx)
207 fz_rethrow(ctx);
208
209 return list;
210 }
211
212 fz_display_list *
213 fz_new_display_list_from_svg_xml(fz_context *ctx, fz_xml_doc *xmldoc, fz_xml *xml, const char *base_uri, fz_archive *zip, float *w, float *h)
214 {
215 fz_document *doc;
216 fz_display_list *list = NULL;
217
218 doc = svg_open_document_with_xml(ctx, xmldoc, xml, base_uri, zip);
219 fz_try(ctx)
220 {
221 list = fz_new_display_list_from_page_number(ctx, doc, 0);
222 *w = ((svg_document*)doc)->width;
223 *h = ((svg_document*)doc)->height;
224 }
225 fz_always(ctx)
226 fz_drop_document(ctx, doc);
227 fz_catch(ctx)
228 fz_rethrow(ctx);
229
230 return list;
231 }
232
233 fz_image *
234 fz_new_image_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip)
235 {
236 fz_display_list *list;
237 fz_image *image = NULL;
238 float w, h;
239
240 list = fz_new_display_list_from_svg(ctx, buf, base_uri, zip, &w, &h);
241 fz_try(ctx)
242 image = fz_new_image_from_display_list(ctx, w, h, list);
243 fz_always(ctx)
244 fz_drop_display_list(ctx, list);
245 fz_catch(ctx)
246 fz_rethrow(ctx);
247 return image;
248 }
249
250 fz_image *
251 fz_new_image_from_svg_xml(fz_context *ctx, fz_xml_doc *xmldoc, fz_xml *xml, const char *base_uri, fz_archive *zip)
252 {
253 fz_display_list *list;
254 fz_image *image = NULL;
255 float w, h;
256
257 list = fz_new_display_list_from_svg_xml(ctx, xmldoc, xml, base_uri, zip, &w, &h);
258 fz_try(ctx)
259 image = fz_new_image_from_display_list(ctx, w, h, list);
260 fz_always(ctx)
261 fz_drop_display_list(ctx, list);
262 fz_catch(ctx)
263 fz_rethrow(ctx);
264 return image;
265 }
266
267 static const char *svg_extensions[] =
268 {
269 "svg",
270 NULL
271 };
272
273 static const char *svg_mimetypes[] =
274 {
275 "image/svg+xml",
276 NULL
277 };
278
279 static int
280 svg_recognize_doc_content(fz_context *ctx, const fz_document_handler *handler, fz_stream *stm, fz_archive *dir, void **state, fz_document_recognize_state_free_fn **free_state)
281 {
282 // A standalone SVG document is an XML document with an <svg> root element.
283 //
284 // Assume the document is ASCII or UTF-8.
285 //
286 // Parse the start of the file using a simplified XML parser, skipping
287 // processing instructions and comments, and stopping at the first
288 // element.
289 //
290 // Return failure on anything unexpected, or if the first element is not SVG.
291
292 int c;
293
294 if (state)
295 *state = NULL;
296 if (free_state)
297 *free_state = NULL;
298
299 if (stm == NULL)
300 return 0;
301
302 parse_text:
303 // Skip whitespace until "<"
304 c = fz_read_byte(ctx, stm);
305 while (c == ' ' || c == '\r' || c == '\n' || c == '\t')
306 c = fz_read_byte(ctx, stm);
307 if (c == '<')
308 goto parse_element;
309 return 0;
310
311 parse_element:
312 // Either "<?...>" or "<!...>" or "<svg" or not an SVG document.
313 c = fz_read_byte(ctx, stm);
314 if (c == '!' || c == '?')
315 goto parse_comment;
316 if (c != 's')
317 return 0;
318 c = fz_read_byte(ctx, stm);
319 if (c != 'v')
320 return 0;
321 c = fz_read_byte(ctx, stm);
322 if (c != 'g')
323 return 0;
324 return 100;
325
326 parse_comment:
327 // Skip everything after "<?" or "<!" until ">"
328 c = fz_read_byte(ctx, stm);
329 while (c != EOF && c != '>')
330 c = fz_read_byte(ctx, stm);
331 if (c == '>')
332 goto parse_text;
333 return 0;
334 }
335
336 fz_document_handler svg_document_handler =
337 {
338 NULL,
339 svg_open_document,
340 svg_extensions,
341 svg_mimetypes,
342 svg_recognize_doc_content
343 };