···11+Gwenn Le Bihan <gwenn.lebihan7@gmail.com> Ewen Le Bihan
22+Gwenn Le Bihan <gwenn.lebihan7@gmail.com> Gwen Le Bihan
33+Gwenn Le Bihan <gwenn.lebihan7@gmail.com> Gwenn Le Bihan
+45-7
src/canvas.rs
···11use core::panic;
22use rayon::prelude::*;
33-use std::{collections::HashMap, fs::OpenOptions, io::Write, ops::Range};
33+use resvg::usvg;
44+use std::{collections::HashMap, fs::OpenOptions, io::Write, ops::Range, sync::Arc};
4556use itertools::Itertools as _;
67use measure_time::info_time;
78use rand::Rng;
89910use crate::{
1010- layer::Layer, objects::Object, random_color, Angle, Color, ColorMapping, ColoredObject,
1111- Containable, Fill, Filter, LineSegment, ObjectSizes, Point, Region,
1111+ fonts::{load_fonts, FontOptions},
1212+ layer::Layer,
1313+ objects::Object,
1414+ random_color, Angle, Color, ColorMapping, ColoredObject, Containable, Fill, Filter,
1515+ LineSegment, ObjectSizes, Point, Region,
1216};
13171418#[derive(Debug, Clone)]
···1923 pub polygon_vertices_range: Range<usize>,
2024 pub canvas_outter_padding: usize,
2125 pub object_sizes: ObjectSizes,
2626+ pub font_options: FontOptions,
2227 pub colormap: ColorMapping,
2828+2329 /// The layers are in order of top to bottom: the first layer will be rendered on top of the second, etc.
2430 pub layers: Vec<Layer>,
2531 pub background: Option<Color>,
···28342935 /// Render cache for the SVG string. Prevents having to re-calculate a pixmap when the SVG hasn't changed.
3036 png_render_cache: Option<String>,
3737+ fontdb: Option<Arc<usvg::fontdb::Database>>,
3138}
32393340impl Canvas {
···187194 polygon_vertices_range: 2..7,
188195 canvas_outter_padding: 10,
189196 object_sizes: ObjectSizes::default(),
197197+ font_options: FontOptions::default(),
190198 colormap: ColorMapping::default(),
191199 layers: vec![],
192200 world_region: Region::new(0, 0, 3, 3).unwrap(),
193201 background: None,
194202 png_render_cache: None,
203203+ fontdb: None,
195204 }
205205+ }
206206+207207+ pub fn fonts_loaded(&self) -> bool {
208208+ self.fontdb.is_some()
209209+ }
210210+211211+ fn load_fonts(&mut self) -> anyhow::Result<()> {
212212+ if self.fonts_loaded() {
213213+ return Ok(());
214214+ }
215215+216216+ info_time!("load_fonts");
217217+ let usvg = load_fonts(&self.font_options)?;
218218+ self.fontdb = Some(usvg.fontdb);
219219+ return Ok(());
196220 }
197221198222 pub fn random_layer(&self, name: &str) -> Layer {
···586610 height: u32,
587611 ) -> anyhow::Result<tiny_skia::Pixmap> {
588612 info_time!("render_to_pixmap_no_cache");
613613+614614+ self.load_fonts()?;
615615+589616 let mut pixmap = self.create_pixmap(width, height);
590617591591- let parsed_svg = &svg_to_usvg_tree(&self.render_to_svg()?)?;
618618+ let parsed_svg = &svg_to_usvg_tree(&self.render_to_svg()?, &self.fontdb)?;
592619593620 self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg);
594621···603630 ) -> anyhow::Result<Option<tiny_skia::Pixmap>> {
604631 info_time!("render_to_pixmap");
605632633633+ self.load_fonts()?;
634634+606635 let new_svg_contents = self.render_to_svg()?;
607636 if let Some(cached_svg) = &self.png_render_cache {
608637 if *cached_svg == new_svg_contents {
···613642614643 let mut pixmap = self.create_pixmap(width, height);
615644616616- let parsed_svg = &svg_to_usvg_tree(&new_svg_contents)?;
645645+ let parsed_svg = &svg_to_usvg_tree(&new_svg_contents, &self.fontdb)?;
617646618647 self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg);
619648···672701 }
673702}
674703675675-fn svg_to_usvg_tree(svg: &str) -> anyhow::Result<resvg::usvg::Tree> {
704704+fn svg_to_usvg_tree(
705705+ svg: &str,
706706+ fontdb: &Option<Arc<usvg::fontdb::Database>>,
707707+) -> anyhow::Result<resvg::usvg::Tree> {
676708 info_time!("svg_to_usvg_tree");
677709 Ok(resvg::usvg::Tree::from_str(
678710 svg,
679679- &resvg::usvg::Options::default(),
711711+ &match fontdb {
712712+ Some(fontdb) => resvg::usvg::Options {
713713+ fontdb: fontdb.clone(),
714714+ ..Default::default()
715715+ },
716716+ None => resvg::usvg::Options::default(),
717717+ },
680718 )?)
681719}
+45
src/fonts.rs
···11+use std::path::PathBuf;
22+33+use resvg::usvg;
44+55+#[derive(Default, Debug, Clone)]
66+pub struct FontOptions {
77+ skip_system_fonts: bool,
88+ font_files: Vec<PathBuf>,
99+ font_dirs: Vec<PathBuf>,
1010+ serif_family: Option<String>,
1111+ sans_serif_family: Option<String>,
1212+ cursive_family: Option<String>,
1313+ fantasy_family: Option<String>,
1414+ monospace_family: Option<String>,
1515+}
1616+1717+pub fn load_fonts(args: &FontOptions) -> anyhow::Result<usvg::Options> {
1818+ let mut usvg = usvg::Options {
1919+ font_family: args.sans_serif_family.clone().unwrap_or("Arial".into()),
2020+ ..Default::default()
2121+ };
2222+ let fontdb = usvg.fontdb_mut();
2323+2424+ if !args.skip_system_fonts {
2525+ fontdb.load_system_fonts();
2626+ }
2727+2828+ for path in &args.font_files {
2929+ if let Err(e) = fontdb.load_font_file(path) {
3030+ log::warn!("Failed to load '{}' cause {}.", path.display(), e);
3131+ }
3232+ }
3333+3434+ for path in &args.font_dirs {
3535+ fontdb.load_fonts_dir(path);
3636+ }
3737+3838+ fontdb.set_serif_family(args.serif_family.as_deref().unwrap_or("Times New Roman"));
3939+ fontdb.set_sans_serif_family(args.sans_serif_family.as_deref().unwrap_or("Arial"));
4040+ fontdb.set_cursive_family(args.cursive_family.as_deref().unwrap_or("Comic Sans MS"));
4141+ fontdb.set_fantasy_family(args.fantasy_family.as_deref().unwrap_or("Impact"));
4242+ fontdb.set_monospace_family(args.monospace_family.as_deref().unwrap_or("Courier New"));
4343+4444+ Ok(usvg)
4545+}