| 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 |  | 
|---|