1#![deny(missing_docs)]
11
12use std::collections::{HashMap, HashSet};
13use std::io::Read;
14use std::sync::Arc;
15
16use fontdb::Database;
17use krilla::color::rgb;
18use krilla::geom::{Rect, Size, Transform};
19use krilla::paint::FillRule;
20use krilla::surface::Surface;
21use krilla::text::Font;
22use krilla::text::GlyphId;
23use usvg::{fontdb, roxmltree, Group, ImageKind, Node, Tree};
24
25use crate::util::RectExt;
26
27mod clip_path;
28mod filter;
29mod group;
30mod image;
31mod mask;
32mod path;
33mod text;
34mod util;
35
36#[derive(Copy, Clone, Debug)]
38pub struct SvgSettings {
39    pub embed_text: bool,
42    pub filter_scale: f32,
45}
46
47impl Default for SvgSettings {
48    fn default() -> Self {
49        Self {
50            embed_text: true,
51            filter_scale: 4.0,
52        }
53    }
54}
55
56pub trait SurfaceExt {
58    fn draw_svg(&mut self, tree: &Tree, size: Size, svg_settings: SvgSettings) -> Option<()>;
60}
61
62impl SurfaceExt for Surface<'_> {
63    fn draw_svg(&mut self, tree: &Tree, size: Size, svg_settings: SvgSettings) -> Option<()> {
64        let old_fill = self.get_fill().cloned();
65        let old_stroke = self.get_stroke().cloned();
66
67        let transform = Transform::from_scale(
68            size.width() / tree.size().width(),
69            size.height() / tree.size().height(),
70        );
71        self.push_transform(&transform);
72        self.push_clip_path(
73            &Rect::from_xywh(0.0, 0.0, tree.size().width(), tree.size().height())
74                .unwrap()
75                .to_clip_path(),
76            &FillRule::NonZero,
77        );
78        render_tree(tree, svg_settings, self);
79        self.pop();
80        self.pop();
81
82        self.set_fill(old_fill);
83        self.set_stroke(old_stroke);
84
85        Some(())
86    }
87}
88
89struct ProcessContext {
90    fonts: HashMap<fontdb::ID, Font>,
91    svg_settings: SvgSettings,
92}
93
94impl ProcessContext {
95    fn new(fonts: HashMap<fontdb::ID, Font>, svg_settings: SvgSettings) -> Self {
96        Self {
97            fonts,
98            svg_settings,
99        }
100    }
101}
102
103pub(crate) fn render_tree(tree: &Tree, svg_settings: SvgSettings, surface: &mut Surface) {
104    let mut db = tree.fontdb().clone();
105    let mut fc = get_context_from_group(Arc::make_mut(&mut db), svg_settings, tree.root());
106    group::render(tree.root(), surface, &mut fc);
107}
108
109pub(crate) fn render_node(
110    node: &Node,
111    mut tree_fontdb: Arc<Database>,
112    svg_settings: SvgSettings,
113    surface: &mut Surface,
114) {
115    let mut fc = get_context_from_node(Arc::make_mut(&mut tree_fontdb), svg_settings, node);
116    group::render_node(node, surface, &mut fc);
117}
118
119pub fn render_svg_glyph(
122    data: &[u8],
123    context_color: rgb::Color,
124    glyph: GlyphId,
125    surface: &mut Surface,
126) -> Option<()> {
127    let mut data = data;
128    let settings = SvgSettings::default();
129
130    let mut decoded = vec![];
131    if data.starts_with(&[0x1f, 0x8b]) {
132        let mut decoder = flate2::read::GzDecoder::new(data);
133        decoder.read_to_end(&mut decoded).ok()?;
134        data = &decoded;
135    }
136
137    let xml = std::str::from_utf8(data).ok()?;
138    let document = roxmltree::Document::parse(xml).ok()?;
139
140    let opts = usvg::Options {
146        style_sheet: Some(format!(
147            "svg {{ color: rgb({}, {}, {}) }}",
148            context_color.red(),
149            context_color.green(),
150            context_color.blue()
151        )),
152        ..Default::default()
153    };
154    let tree = Tree::from_xmltree(&document, &opts).ok()?;
155
156    if let Some(node) = tree.node_by_id(&format!("glyph{}", glyph.to_u32())) {
157        render_node(node, tree.fontdb().clone(), settings, surface)
158    } else {
159        render_tree(&tree, settings, surface)
162    };
163
164    Some(())
165}
166
167fn get_context_from_group(
168    tree_fontdb: &mut Database,
169    svg_settings: SvgSettings,
170    group: &Group,
171) -> ProcessContext {
172    let mut ids = HashSet::new();
173    get_ids_from_group_impl(group, &mut ids);
174    let ids = ids.into_iter().collect::<Vec<_>>();
175    let db = convert_fontdb(tree_fontdb, Some(ids));
176
177    ProcessContext::new(db, svg_settings)
178}
179
180fn get_context_from_node(
181    tree_fontdb: &mut Database,
182    svg_settings: SvgSettings,
183    node: &Node,
184) -> ProcessContext {
185    let mut ids = HashSet::new();
186    get_ids_impl(node, &mut ids);
187    let ids = ids.into_iter().collect::<Vec<_>>();
188    let db = convert_fontdb(tree_fontdb, Some(ids));
189
190    ProcessContext::new(db, svg_settings)
191}
192
193fn get_ids_from_group_impl(group: &Group, ids: &mut HashSet<fontdb::ID>) {
194    for child in group.children() {
195        get_ids_impl(child, ids);
196    }
197}
198
199fn get_ids_impl(node: &Node, ids: &mut HashSet<fontdb::ID>) {
201    match node {
202        Node::Text(t) => {
203            for span in t.layouted() {
204                for g in &span.positioned_glyphs {
205                    ids.insert(g.font);
206                }
207            }
208        }
209        Node::Group(group) => {
210            get_ids_from_group_impl(group, ids);
211        }
212        Node::Image(image) => {
213            if let ImageKind::SVG(svg) = image.kind() {
214                get_ids_from_group_impl(svg.root(), ids);
215            }
216        }
217        _ => {}
218    }
219
220    node.subroots(|subroot| get_ids_from_group_impl(subroot, ids));
221}
222
223fn convert_fontdb(db: &mut Database, ids: Option<Vec<fontdb::ID>>) -> HashMap<fontdb::ID, Font> {
224    let mut map = HashMap::new();
225
226    let ids = ids.unwrap_or(db.faces().map(|f| f.id).collect::<Vec<_>>());
227
228    for id in ids {
229        if let Some((font_data, index)) = unsafe { db.make_shared_face_data(id) } {
235            if let Some(font) = Font::new(font_data.into(), index) {
236                map.insert(id, font);
237            }
238        }
239    }
240
241    map
242}