Skip to content

Commit

Permalink
feat: refactoring and adding test cases
Browse files Browse the repository at this point in the history
When `loadSystemFonts: true`, the `defaultFontFamily` option can also be omitted.
  • Loading branch information
yisibl committed Aug 6, 2023
1 parent 7e66224 commit 381e44f
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 83 deletions.
26 changes: 26 additions & 0 deletions __test__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,32 @@ test('should return undefined if bbox is invalid', (t) => {
t.is(resvg.innerBBox(), undefined)
})

test('should render using font buffer provided by options', async (t) => {
const svg = `<svg width='480' height='150' viewBox='-20 -80 550 100' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
<text x='0' y='0' font-size='100' fill='#000'>Font Buffer</text>
</svg>`

const expectedResultBuffer = await fs.readFile(join(__dirname, './options_font_buffer_expected_result.png'))

const resvg = new Resvg(svg, {
font: {
fontFiles: ['./__test__/Pacifico-Regular.ttf'],
// fontDirs: ['./__test__/',],
// loadSystemFonts: false,
// defaultFontFamily: ' ',
},
logLevel: 'debug',
})
const renderedResult = resvg.render().asPng()

const expectedResult = await jimp.read(Buffer.from(expectedResultBuffer.buffer))
const actualPng = await jimp.read(Buffer.from(renderedResult))

await fs.writeFile(join(__dirname, './options_font_buffer_expected_extend.png'), renderedResult)

t.is(jimp.diff(expectedResult, actualPng, 0.01).percent, 0) // 0 means similar, 1 means not similar
})

test('should be load custom fonts', (t) => {
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
Expand Down
4 changes: 3 additions & 1 deletion __test__/wasm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ test('should return undefined if bbox is invalid', (t) => {

test('should render using font buffer provided by options', async (t) => {
const svg = `<svg width='480' height='150' viewBox='-20 -80 550 100' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
<text x='0' y='0' font-size='100' font-family='Pacifico' fill='#000000'>Font Buffer</text>
<text x='0' y='0' font-size='100' fill='#000000'>Font Buffer</text>
</svg>`

const pacificoBuffer = await fs.readFile(join(__dirname, './Pacifico-Regular.ttf'))
Expand All @@ -339,6 +339,8 @@ test('should render using font buffer provided by options', async (t) => {
const options = {
font: {
fontsBuffers: [pacificoBuffer],
loadSystemFonts: false,
defaultFontFamily: 'Pacifico',
},
}

Expand Down
182 changes: 102 additions & 80 deletions src/fonts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use crate::options::*;
use resvg::usvg_text_layout::fontdb::Database;
use resvg::usvg_text_layout::fontdb::{Database, Language};


#[cfg(not(target_arch = "wasm32"))]
use log::{debug, warn};
Expand All @@ -13,7 +14,6 @@ use resvg::usvg_text_layout::fontdb::{Family, Query, Source};

#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsCast;
use resvg::usvg_text_layout::fontdb::{Database, Family, Language, Query, Source};

/// Loads fonts.
#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -22,12 +22,6 @@ pub fn load_fonts(font_options: &JsFontOptions) -> Database {
let mut fontdb = Database::new();
let now = std::time::Instant::now();

// 加载系统字体
// https://github.com/RazrFalcon/fontdb/blob/052d74b9eb45f2c4f446846a53f33bd965e2662d/src/lib.rs#L261
if font_options.load_system_fonts {
fontdb.load_system_fonts();
}

// 加载指定路径的字体
for path in &font_options.font_files {
if let Err(e) = fontdb.load_font_file(path) {
Expand All @@ -40,50 +34,124 @@ pub fn load_fonts(font_options: &JsFontOptions) -> Database {
fontdb.load_fonts_dir(path);
}

// 加载系统字体,Wasm 不支持
// https://github.com/RazrFalcon/fontdb/blob/052d74b9eb45f2c4f446846a53f33bd965e2662d/src/lib.rs#L261
if font_options.load_system_fonts {
fontdb.load_system_fonts();
}

set_font_families(font_options, &mut fontdb);

debug!(
"Loaded {} font faces in {}ms.",
fontdb.len(),
now.elapsed().as_micros() as f64 / 1000.0
);

fontdb
}

/// Loads fonts.
#[cfg(target_arch = "wasm32")]
pub fn load_wasm_fonts(
font_options: &JsFontOptions,
fonts_buffers: Option<js_sys::Array>,
fontdb: &mut Database,
) -> Result<(), js_sys::Error> {
if let Some(fonts_buffers) = fonts_buffers {
for font in fonts_buffers.values().into_iter() {
let raw_font = font?;
let font_data = raw_font.dyn_into::<js_sys::Uint8Array>()?.to_vec();
fontdb.load_font_data(font_data);
}
}

set_font_families(font_options, fontdb);

Ok(())
}

fn set_font_families(font_options: &JsFontOptions, fontdb: &mut Database) {
let mut default_font_family = font_options.default_font_family.clone();

// 当默认字体为空时,尝试直接从 font_files 中加载读取字体名称,然后设置到默认的 font-family 中
// TODO: 判断只有 font_files 没有 default_font_family 选项的情况
if !font_options.default_font_family.is_empty() && font_options.font_files.len() > 0 {
fontdb.faces().into_iter().for_each(|face| {
let new_family = face
.families
.iter()
.find(|f| f.1 == Language::English_UnitedStates)
.unwrap();
default_font_family = new_family.0.clone();
});

// If a default font family exists, set all other families to that family.
// This prevents fonts from not being rendered in SVG.
if font_options.default_font_family.to_string().trim().is_empty() {
if font_options.font_files.len() > 0 || font_options.font_dirs.len() > 0 {
for face in fontdb.faces() {
// debug!("font_id = {}, post_script_name = {} ", face.id, face.post_script_name);

let new_family = face
.families
.iter()
.find(|f| f.1 == Language::English_UnitedStates)
.unwrap_or(&face.families[0]);

default_font_family = new_family.0.clone();
// debug!("默认字体匹配到了 = {} ", default_font_family);
break;
}


// 遍历所有加载的字体
// for face in fontdb.faces() {
// if let Source::File(ref path) = &face.source {
// // 如果 path.display() 中包含了 font_options.font_files 中的字体路径,则设置为默认字体,并打印出 font_files 中的路径
// // debug!("font_id = {}, post_script_name = {} ", face.id, face.post_script_name);

// // 匹配到 font_files 中的字体后,设置默认字体
// for font_file in &font_options.font_files {
// if path.display().to_string().contains(font_file) {
// let new_family = face
// .families
// .iter()
// .find(|f| f.1 == Language::English_UnitedStates)
// .unwrap_or(&face.families[0]);

// default_font_family = new_family.0.clone();
// break;
// }
// }
// }
// }

// If a default font family exists, set all other families to that family.
// This prevents fonts from not being rendered in SVG.
// fontdb.faces().into_iter().for_each(|face| {
// if let Some(new_family) = face
// .families
// .iter()
// .find(|f| f.1 == Language::English_UnitedStates)
// {
// default_font_family = new_family.0.clone();
// debug!("默认字体匹配到了 = {} ", default_font_family);
// }
// });

} else {
default_font_family = "Arial".to_string();
}

fontdb.set_serif_family(&default_font_family);
fontdb.set_sans_serif_family(&default_font_family);
fontdb.set_cursive_family(&default_font_family);
fontdb.set_fantasy_family(&default_font_family);
fontdb.set_monospace_family(&default_font_family);
} else {
// Set generic font families
// - `serif` - Times New Roman
// - `sans-serif` - Arial
// - `cursive` - Comic Sans MS
// - `fantasy` - Impact (Papyrus on macOS)
// - `monospace` - Courier New
fontdb.set_serif_family(&font_options.serif_family);
fontdb.set_sans_serif_family(&font_options.sans_serif_family);
fontdb.set_cursive_family(&font_options.cursive_family);
fontdb.set_fantasy_family(&font_options.fantasy_family);
fontdb.set_monospace_family(&font_options.monospace_family);
fontdb.set_serif_family(&font_options.default_font_family);
fontdb.set_sans_serif_family(&font_options.default_font_family);
fontdb.set_cursive_family(&font_options.default_font_family);
fontdb.set_fantasy_family(&font_options.default_font_family);
fontdb.set_monospace_family(&font_options.default_font_family);
}
debug!("默认字体 = {} ", font_options.default_font_family,);

#[cfg(not(target_arch = "wasm32"))]
find_and_debug_font_path(fontdb, default_font_family.as_str())
}

#[cfg(not(target_arch = "wasm32"))]
fn find_and_debug_font_path(fontdb: &mut Database, font_family: &str) {
// 查找指定字体的路径
let font_family: &str = &font_options.default_font_family;
// let font_family: &str = default_font_family.as_str();
let query = Query {
families: &[Family::Name(font_family)],
..Query::default()
Expand All @@ -107,50 +175,4 @@ pub fn load_fonts(font_options: &JsFontOptions) -> Database {
warn!("Warning: The default font '{}' not found.", font_family);
}
}

fontdb
}

/// Loads fonts.
#[cfg(target_arch = "wasm32")]
pub fn load_fonts(
font_options: &JsFontOptions,
fonts_buffers: Option<js_sys::Array>,
fontdb: &mut Database,
) -> Result<(), js_sys::Error> {
if let Some(fonts_buffers) = fonts_buffers {
for font in fonts_buffers.values().into_iter() {
let raw_font = font?;
let font_data = raw_font.dyn_into::<js_sys::Uint8Array>()?.to_vec();
fontdb.load_font_data(font_data);
}
}

set_font_families(font_options, fontdb);

Ok(())
}

fn set_font_families(font_options: &JsFontOptions, fontdb: &mut Database) {
// Set generic font families
// - `serif` - Times New Roman
// - `sans-serif` - Arial
// - `cursive` - Comic Sans MS
// - `fantasy` - Impact (Papyrus on macOS)
// - `monospace` - Courier New
if !font_options.default_font_family.is_empty() {
// If a default font family exists, set all other families to that family.
// This prevents fonts from not being rendered in SVG.
fontdb.set_serif_family(&font_options.default_font_family);
fontdb.set_sans_serif_family(&font_options.default_font_family);
fontdb.set_cursive_family(&font_options.default_font_family);
fontdb.set_fantasy_family(&font_options.default_font_family);
fontdb.set_monospace_family(&font_options.default_font_family);
} else {
fontdb.set_serif_family(&font_options.serif_family);
fontdb.set_sans_serif_family(&font_options.sans_serif_family);
fontdb.set_cursive_family(&font_options.cursive_family);
fontdb.set_fantasy_family(&font_options.fantasy_family);
fontdb.set_monospace_family(&font_options.monospace_family);
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ impl Resvg {

let (mut opts, mut fontdb) = js_options.to_usvg_options();

crate::fonts::load_fonts(&js_options.font, custom_font_buffers, &mut fontdb)?;
crate::fonts::load_wasm_fonts(&js_options.font, custom_font_buffers, &mut fontdb)?;

options::tweak_usvg_options(&mut opts);
let mut tree = if js_sys::Uint8Array::instanceof(&svg) {
Expand Down
2 changes: 1 addition & 1 deletion src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ impl Default for JsFontOptions {
load_system_fonts: true,
font_files: vec![],
font_dirs: vec![],
default_font_family: "Arial".to_string(),
default_font_family: "".to_string(),
default_font_size: 12.0,
serif_family: "Times New Roman".to_string(),
sans_serif_family: "Arial".to_string(),
Expand Down
Binary file modified wasm/index_bg.wasm
Binary file not shown.

0 comments on commit 381e44f

Please sign in to comment.