1 | #include "mupdf/fitz.h" |
2 | #include "svg-imp.h" |
3 | |
4 | typedef struct svg_page_s svg_page; |
5 | |
6 | struct svg_page_s |
7 | { |
8 | fz_page super; |
9 | svg_document *doc; |
10 | }; |
11 | |
12 | static void |
13 | svg_drop_document(fz_context *ctx, fz_document *doc_) |
14 | { |
15 | svg_document *doc = (svg_document*)doc_; |
16 | fz_drop_tree(ctx, doc->idmap, NULL); |
17 | fz_drop_xml(ctx, doc->xml); |
18 | } |
19 | |
20 | static int |
21 | svg_count_pages(fz_context *ctx, fz_document *doc_) |
22 | { |
23 | return 1; |
24 | } |
25 | |
26 | static fz_rect |
27 | svg_bound_page(fz_context *ctx, fz_page *page_) |
28 | { |
29 | svg_page *page = (svg_page*)page_; |
30 | svg_document *doc = page->doc; |
31 | |
32 | svg_parse_document_bounds(ctx, doc, doc->root); |
33 | |
34 | return fz_make_rect(0, 0, doc->width, doc->height); |
35 | } |
36 | |
37 | static void |
38 | svg_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) |
39 | { |
40 | svg_page *page = (svg_page*)page_; |
41 | svg_document *doc = page->doc; |
42 | svg_run_document(ctx, doc, doc->root, dev, ctm); |
43 | } |
44 | |
45 | static void |
46 | svg_drop_page(fz_context *ctx, fz_page *page_) |
47 | { |
48 | /* nothing */ |
49 | } |
50 | |
51 | static fz_page * |
52 | svg_load_page(fz_context *ctx, fz_document *doc_, int number) |
53 | { |
54 | svg_document *doc = (svg_document*)doc_; |
55 | svg_page *page; |
56 | |
57 | if (number != 0) |
58 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page %d" , number); |
59 | |
60 | page = fz_new_derived_page(ctx, svg_page); |
61 | page->super.bound_page = svg_bound_page; |
62 | page->super.run_page_contents = svg_run_page; |
63 | page->super.drop_page = svg_drop_page; |
64 | page->doc = doc; |
65 | |
66 | return (fz_page*)page; |
67 | } |
68 | |
69 | static void |
70 | svg_build_id_map(fz_context *ctx, svg_document *doc, fz_xml *root) |
71 | { |
72 | fz_xml *node; |
73 | |
74 | char *id_att = fz_xml_att(root, "id" ); |
75 | if (id_att) |
76 | doc->idmap = fz_tree_insert(ctx, doc->idmap, id_att, root); |
77 | |
78 | for (node = fz_xml_down(root); node; node = fz_xml_next(node)) |
79 | svg_build_id_map(ctx, doc, node); |
80 | } |
81 | |
82 | static fz_document * |
83 | svg_open_document_with_xml(fz_context *ctx, fz_xml *xml, const char *base_uri, fz_archive *zip) |
84 | { |
85 | svg_document *doc; |
86 | |
87 | doc = fz_new_derived_document(ctx, svg_document); |
88 | doc->super.drop_document = svg_drop_document; |
89 | doc->super.count_pages = svg_count_pages; |
90 | doc->super.load_page = svg_load_page; |
91 | |
92 | doc->idmap = NULL; |
93 | doc->xml = NULL; |
94 | doc->root = xml; |
95 | doc->zip = zip; |
96 | |
97 | fz_try(ctx) |
98 | { |
99 | svg_build_id_map(ctx, doc, doc->root); |
100 | } |
101 | fz_catch(ctx) |
102 | { |
103 | fz_drop_document(ctx, &doc->super); |
104 | fz_rethrow(ctx); |
105 | } |
106 | |
107 | return (fz_document*)doc; |
108 | } |
109 | |
110 | static fz_document * |
111 | svg_open_document_with_buffer(fz_context *ctx, fz_buffer *buf, 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 | |
120 | doc->idmap = NULL; |
121 | if (base_uri) |
122 | fz_strlcpy(doc->base_uri, base_uri, sizeof doc->base_uri); |
123 | doc->zip = zip; |
124 | |
125 | fz_try(ctx) |
126 | { |
127 | doc->xml = fz_parse_xml(ctx, buf, 0); |
128 | doc->root = fz_xml_root(doc->xml); |
129 | svg_build_id_map(ctx, doc, doc->root); |
130 | } |
131 | fz_catch(ctx) |
132 | { |
133 | fz_drop_document(ctx, &doc->super); |
134 | fz_rethrow(ctx); |
135 | } |
136 | |
137 | return (fz_document*)doc; |
138 | } |
139 | |
140 | static fz_document * |
141 | svg_open_document_with_stream(fz_context *ctx, fz_stream *file) |
142 | { |
143 | fz_buffer *buf; |
144 | fz_document *doc = NULL; |
145 | |
146 | buf = fz_read_all(ctx, file, 0); |
147 | fz_try(ctx) |
148 | doc = svg_open_document_with_buffer(ctx, buf, NULL, NULL); |
149 | fz_always(ctx) |
150 | fz_drop_buffer(ctx, buf); |
151 | fz_catch(ctx) |
152 | fz_rethrow(ctx); |
153 | |
154 | return doc; |
155 | } |
156 | |
157 | /* |
158 | Parse an SVG document into a display-list. |
159 | */ |
160 | fz_display_list * |
161 | fz_new_display_list_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip, float *w, float *h) |
162 | { |
163 | fz_document *doc; |
164 | fz_display_list *list = NULL; |
165 | |
166 | doc = svg_open_document_with_buffer(ctx, buf, base_uri, zip); |
167 | fz_try(ctx) |
168 | { |
169 | list = fz_new_display_list_from_page_number(ctx, doc, 0); |
170 | *w = ((svg_document*)doc)->width; |
171 | *h = ((svg_document*)doc)->height; |
172 | } |
173 | fz_always(ctx) |
174 | fz_drop_document(ctx, doc); |
175 | fz_catch(ctx) |
176 | fz_rethrow(ctx); |
177 | |
178 | return list; |
179 | } |
180 | |
181 | /* |
182 | Parse an SVG document into a display-list. |
183 | */ |
184 | fz_display_list * |
185 | fz_new_display_list_from_svg_xml(fz_context *ctx, fz_xml *xml, const char *base_uri, fz_archive *zip, float *w, float *h) |
186 | { |
187 | fz_document *doc; |
188 | fz_display_list *list = NULL; |
189 | |
190 | doc = svg_open_document_with_xml(ctx, xml, base_uri, zip); |
191 | fz_try(ctx) |
192 | { |
193 | list = fz_new_display_list_from_page_number(ctx, doc, 0); |
194 | *w = ((svg_document*)doc)->width; |
195 | *h = ((svg_document*)doc)->height; |
196 | } |
197 | fz_always(ctx) |
198 | fz_drop_document(ctx, doc); |
199 | fz_catch(ctx) |
200 | fz_rethrow(ctx); |
201 | |
202 | return list; |
203 | } |
204 | |
205 | /* |
206 | Create a scalable image from an SVG document. |
207 | */ |
208 | fz_image * |
209 | fz_new_image_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip) |
210 | { |
211 | fz_display_list *list; |
212 | fz_image *image = NULL; |
213 | float w, h; |
214 | |
215 | list = fz_new_display_list_from_svg(ctx, buf, base_uri, zip, &w, &h); |
216 | fz_try(ctx) |
217 | image = fz_new_image_from_display_list(ctx, w, h, list); |
218 | fz_always(ctx) |
219 | fz_drop_display_list(ctx, list); |
220 | fz_catch(ctx) |
221 | fz_rethrow(ctx); |
222 | return image; |
223 | } |
224 | |
225 | /* |
226 | Create a scalable image from an SVG document. |
227 | */ |
228 | fz_image * |
229 | fz_new_image_from_svg_xml(fz_context *ctx, fz_xml *xml, const char *base_uri, fz_archive *zip) |
230 | { |
231 | fz_display_list *list; |
232 | fz_image *image = NULL; |
233 | float w, h; |
234 | |
235 | list = fz_new_display_list_from_svg_xml(ctx, xml, base_uri, zip, &w, &h); |
236 | fz_try(ctx) |
237 | image = fz_new_image_from_display_list(ctx, w, h, list); |
238 | fz_always(ctx) |
239 | fz_drop_display_list(ctx, list); |
240 | fz_catch(ctx) |
241 | fz_rethrow(ctx); |
242 | return image; |
243 | } |
244 | |
245 | static const char *svg_extensions[] = |
246 | { |
247 | "svg" , |
248 | NULL |
249 | }; |
250 | |
251 | static const char *svg_mimetypes[] = |
252 | { |
253 | "image/svg+xml" , |
254 | NULL |
255 | }; |
256 | |
257 | fz_document_handler svg_document_handler = |
258 | { |
259 | NULL, |
260 | NULL, |
261 | svg_open_document_with_stream, |
262 | svg_extensions, |
263 | svg_mimetypes |
264 | }; |
265 | |