Mercurial > hgrepos > Python2 > PyMuPDF
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/svg/svg-doc.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,343 @@ +// Copyright (C) 2004-2024 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" +#include "svg-imp.h" + +typedef struct +{ + fz_page super; + svg_document *doc; +} svg_page; + +static void +svg_drop_document(fz_context *ctx, fz_document *doc_) +{ + svg_document *doc = (svg_document*)doc_; + fz_drop_tree(ctx, doc->idmap, NULL); + fz_drop_xml(ctx, doc->xml); +} + +static int +svg_count_pages(fz_context *ctx, fz_document *doc_, int chapter) +{ + return 1; +} + +static fz_rect +svg_bound_page(fz_context *ctx, fz_page *page_, fz_box_type box) +{ + svg_page *page = (svg_page*)page_; + svg_document *doc = page->doc; + + svg_parse_document_bounds(ctx, doc, doc->root); + + return fz_make_rect(0, 0, doc->width, doc->height); +} + +static void +svg_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) +{ + svg_page *page = (svg_page*)page_; + svg_document *doc = page->doc; + svg_run_document(ctx, doc, doc->root, dev, ctm); +} + +static void +svg_drop_page(fz_context *ctx, fz_page *page_) +{ + /* nothing */ +} + +static fz_page * +svg_load_page(fz_context *ctx, fz_document *doc_, int chapter, int number) +{ + svg_document *doc = (svg_document*)doc_; + svg_page *page; + + if (number != 0) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot find page %d", number); + + page = fz_new_derived_page(ctx, svg_page, doc_); + page->super.bound_page = svg_bound_page; + page->super.run_page_contents = svg_run_page; + page->super.drop_page = svg_drop_page; + page->doc = doc; + + return (fz_page*)page; +} + +static void +svg_build_id_map(fz_context *ctx, svg_document *doc, fz_xml *root) +{ + fz_xml *node; + + char *id_att = fz_xml_att(root, "id"); + if (id_att) + doc->idmap = fz_tree_insert(ctx, doc->idmap, id_att, root); + + for (node = fz_xml_down(root); node; node = fz_xml_next(node)) + svg_build_id_map(ctx, doc, node); +} + +static int +svg_lookup_metadata(fz_context *ctx, fz_document *doc_, const char *key, char *buf, size_t size) +{ + if (!strcmp(key, FZ_META_FORMAT)) + return 1 + (int)fz_strlcpy(buf, "SVG", size); + return -1; +} + +static fz_document * +svg_open_document_with_xml(fz_context *ctx, fz_xml_doc *xmldoc, fz_xml *xml, const char *base_uri, fz_archive *zip) +{ + svg_document *doc; + + doc = fz_new_derived_document(ctx, svg_document); + doc->super.drop_document = svg_drop_document; + doc->super.count_pages = svg_count_pages; + doc->super.load_page = svg_load_page; + doc->super.lookup_metadata = svg_lookup_metadata; + + doc->idmap = NULL; + if (base_uri) + fz_strlcpy(doc->base_uri, base_uri, sizeof doc->base_uri); + doc->xml = NULL; + doc->root = xml; + doc->zip = zip; + + fz_try(ctx) + { + if (xmldoc) + svg_build_id_map(ctx, doc, fz_xml_root(xmldoc)); + else + svg_build_id_map(ctx, doc, doc->root); + } + fz_catch(ctx) + { + fz_drop_document(ctx, &doc->super); + fz_rethrow(ctx); + } + + return (fz_document*)doc; +} + +static fz_document * +svg_open_document_with_buffer(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip) +{ + svg_document *doc; + + doc = fz_new_derived_document(ctx, svg_document); + doc->super.drop_document = svg_drop_document; + doc->super.count_pages = svg_count_pages; + doc->super.load_page = svg_load_page; + doc->super.lookup_metadata = svg_lookup_metadata; + + doc->idmap = NULL; + if (base_uri) + fz_strlcpy(doc->base_uri, base_uri, sizeof doc->base_uri); + doc->zip = zip; + + fz_try(ctx) + { + doc->xml = fz_parse_xml(ctx, buf, 0); + doc->root = fz_xml_root(doc->xml); + svg_build_id_map(ctx, doc, doc->root); + } + fz_catch(ctx) + { + fz_drop_document(ctx, &doc->super); + fz_rethrow(ctx); + } + + return (fz_document*)doc; +} + +static fz_document * +svg_open_document(fz_context *ctx, const fz_document_handler *handler, fz_stream *file, fz_stream *accel, fz_archive *zip, void *state) +{ + fz_buffer *buf = fz_read_all(ctx, file, 0); + fz_document *doc = NULL; + + fz_try(ctx) + doc = svg_open_document_with_buffer(ctx, buf, NULL, NULL); + fz_always(ctx) + fz_drop_buffer(ctx, buf); + fz_catch(ctx) + fz_rethrow(ctx); + + return doc; +} + +fz_display_list * +fz_new_display_list_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip, float *w, float *h) +{ + fz_document *doc; + fz_display_list *list = NULL; + + doc = svg_open_document_with_buffer(ctx, buf, base_uri, zip); + fz_try(ctx) + { + list = fz_new_display_list_from_page_number(ctx, doc, 0); + *w = ((svg_document*)doc)->width; + *h = ((svg_document*)doc)->height; + } + fz_always(ctx) + fz_drop_document(ctx, doc); + fz_catch(ctx) + fz_rethrow(ctx); + + return list; +} + +fz_display_list * +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) +{ + fz_document *doc; + fz_display_list *list = NULL; + + doc = svg_open_document_with_xml(ctx, xmldoc, xml, base_uri, zip); + fz_try(ctx) + { + list = fz_new_display_list_from_page_number(ctx, doc, 0); + *w = ((svg_document*)doc)->width; + *h = ((svg_document*)doc)->height; + } + fz_always(ctx) + fz_drop_document(ctx, doc); + fz_catch(ctx) + fz_rethrow(ctx); + + return list; +} + +fz_image * +fz_new_image_from_svg(fz_context *ctx, fz_buffer *buf, const char *base_uri, fz_archive *zip) +{ + fz_display_list *list; + fz_image *image = NULL; + float w, h; + + list = fz_new_display_list_from_svg(ctx, buf, base_uri, zip, &w, &h); + fz_try(ctx) + image = fz_new_image_from_display_list(ctx, w, h, list); + fz_always(ctx) + fz_drop_display_list(ctx, list); + fz_catch(ctx) + fz_rethrow(ctx); + return image; +} + +fz_image * +fz_new_image_from_svg_xml(fz_context *ctx, fz_xml_doc *xmldoc, fz_xml *xml, const char *base_uri, fz_archive *zip) +{ + fz_display_list *list; + fz_image *image = NULL; + float w, h; + + list = fz_new_display_list_from_svg_xml(ctx, xmldoc, xml, base_uri, zip, &w, &h); + fz_try(ctx) + image = fz_new_image_from_display_list(ctx, w, h, list); + fz_always(ctx) + fz_drop_display_list(ctx, list); + fz_catch(ctx) + fz_rethrow(ctx); + return image; +} + +static const char *svg_extensions[] = +{ + "svg", + NULL +}; + +static const char *svg_mimetypes[] = +{ + "image/svg+xml", + NULL +}; + +static int +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) +{ + // A standalone SVG document is an XML document with an <svg> root element. + // + // Assume the document is ASCII or UTF-8. + // + // Parse the start of the file using a simplified XML parser, skipping + // processing instructions and comments, and stopping at the first + // element. + // + // Return failure on anything unexpected, or if the first element is not SVG. + + int c; + + if (state) + *state = NULL; + if (free_state) + *free_state = NULL; + + if (stm == NULL) + return 0; + +parse_text: + // Skip whitespace until "<" + c = fz_read_byte(ctx, stm); + while (c == ' ' || c == '\r' || c == '\n' || c == '\t') + c = fz_read_byte(ctx, stm); + if (c == '<') + goto parse_element; + return 0; + +parse_element: + // Either "<?...>" or "<!...>" or "<svg" or not an SVG document. + c = fz_read_byte(ctx, stm); + if (c == '!' || c == '?') + goto parse_comment; + if (c != 's') + return 0; + c = fz_read_byte(ctx, stm); + if (c != 'v') + return 0; + c = fz_read_byte(ctx, stm); + if (c != 'g') + return 0; + return 100; + +parse_comment: + // Skip everything after "<?" or "<!" until ">" + c = fz_read_byte(ctx, stm); + while (c != EOF && c != '>') + c = fz_read_byte(ctx, stm); + if (c == '>') + goto parse_text; + return 0; +} + +fz_document_handler svg_document_handler = +{ + NULL, + svg_open_document, + svg_extensions, + svg_mimetypes, + svg_recognize_doc_content +};
