svg-doc.c 8.2 KB

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