Skip to content

Commit b2f667e

Browse files
committed
feat: add font resolve support and builder api
1 parent 7e40b02 commit b2f667e

File tree

3 files changed

+106
-49
lines changed

3 files changed

+106
-49
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pathfinder_content = { version = "0.5.0", default-features = false }
2828
pathfinder_simd = { version = "0.5.1", features = ["pf-no-simd"] }
2929
futures = "0.3.21"
3030
infer = "0.8.0"
31+
ouroboros = "0.15.0"
32+
roxmltree = "0.14.1"
33+
fontkit = "0.1.0"
3134

3235
[target.'cfg(all(not(all(target_os = "linux", target_arch = "aarch64", target_env = "musl")), not(all(target_os = "windows", target_arch = "aarch64")), not(target_arch = "wasm32")))'.dependencies]
3336
mimalloc-rust = { version = "0.1" }

src/builder.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use fontdb::Database;
2+
use fontkit::FontKey;
3+
#[cfg(not(target_arch = "wasm32"))]
4+
use napi::bindgen_prelude::{Buffer, Either, Error as NapiError};
5+
#[cfg(not(target_arch = "wasm32"))]
6+
use napi_derive::napi;
7+
use ouroboros::self_referencing;
8+
use roxmltree::Document;
9+
10+
use crate::options::JsOptions;
11+
12+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
13+
#[cfg_attr(not(target_arch = "wasm32"), napi)]
14+
#[ouroboros::self_referencing]
15+
pub struct ResvgBuilder {
16+
font_db: fontdb::Database,
17+
js_options: JsOptions,
18+
data: String,
19+
#[borrows(data)]
20+
#[covariant]
21+
doc: Document<'this>,
22+
}
23+
24+
#[napi(js_name = "FontKey")]
25+
pub struct FontKeyWrapper(FontKey);
26+
27+
#[cfg(not(target_arch = "wasm32"))]
28+
#[napi]
29+
impl ResvgBuilder {
30+
#[napi(constructor)]
31+
pub fn new_napi(
32+
svg: Either<String, Buffer>,
33+
options: Option<String>,
34+
) -> Result<ResvgBuilder, NapiError> {
35+
let js_options: JsOptions = options
36+
.and_then(|o| serde_json::from_str(o.as_str()).ok())
37+
.unwrap_or_default();
38+
let _ = env_logger::builder()
39+
.filter_level(js_options.log_level)
40+
.try_init();
41+
let mut opts = js_options.to_usvg_options();
42+
crate::options::tweak_usvg_options(&mut opts);
43+
let data = match svg {
44+
Either::A(a) => a,
45+
Either::B(b) => std::str::from_utf8(b.as_ref())
46+
.map_err(|e| napi::Error::from_reason(format!("{}", e)))?
47+
.to_string(),
48+
};
49+
ResvgBuilderTryBuilder {
50+
font_db: Database::new(),
51+
js_options,
52+
data,
53+
doc_builder: |input| Document::parse(input),
54+
}
55+
.try_build()
56+
.map_err(|e| napi::Error::from_reason(format!("{}", e)))
57+
}
58+
59+
#[napi]
60+
pub fn texts_to_resolve(&self) -> Vec<FontKeyWrapper> {
61+
vec![]
62+
}
63+
64+
// fn new_inner(
65+
// svg: &Either<String, Buffer>,
66+
// options: Option<String>,
67+
// ) -> Result<Resvg, NapiError> {
68+
// let opts_ref = opts.to_ref();
69+
// // Parse the SVG string into a tree.
70+
// let tree = match svg {
71+
// Either::A(a) => usvg::Tree::from_str(a.as_str(), &opts_ref),
72+
// Either::B(b) => usvg::Tree::from_data(b.as_ref(), &opts_ref),
73+
// }
74+
// .map_err(|e| napi::Error::from_reason(format!("{}", e)))?;
75+
// Ok(Resvg { tree, js_options })
76+
// }
77+
}
78+
79+
#[cfg(target_arch = "wasm32")]
80+
#[wasm_bindgen]
81+
impl ResvgBuilder {
82+
#[wasm_bindgen(constructor)]
83+
pub fn new(svg: IStringOrBuffer, options: Option<String>) -> Result<Resvg, js_sys::Error> {
84+
let js_options: JsOptions = options
85+
.and_then(|o| serde_json::from_str(o.as_str()).ok())
86+
.unwrap_or_default();
87+
88+
let mut opts = js_options.to_usvg_options();
89+
options::tweak_usvg_options(&mut opts);
90+
let opts_ref = opts.to_ref();
91+
let tree = if js_sys::Uint8Array::instanceof(&svg) {
92+
let uintarray = js_sys::Uint8Array::unchecked_from_js_ref(&svg);
93+
let svg_buffer = uintarray.to_vec();
94+
usvg::Tree::from_data(&svg_buffer, &opts_ref).map_err(Error::from)
95+
} else if let Some(s) = svg.as_string() {
96+
usvg::Tree::from_str(s.as_str(), &opts_ref).map_err(Error::from)
97+
} else {
98+
Err(Error::InvalidInput)
99+
}?;
100+
Ok(Resvg { tree, js_options })
101+
}
102+
}

src/lib.rs

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use wasm_bindgen::{
2626
JsCast,
2727
};
2828

29+
mod builder;
2930
mod error;
3031
mod fonts;
3132
mod options;
@@ -125,34 +126,6 @@ impl RenderedImage {
125126
#[cfg(not(target_arch = "wasm32"))]
126127
#[napi]
127128
impl Resvg {
128-
#[napi(constructor)]
129-
pub fn new(svg: Either<String, Buffer>, options: Option<String>) -> Result<Resvg, NapiError> {
130-
Resvg::new_inner(&svg, options)
131-
}
132-
133-
fn new_inner(
134-
svg: &Either<String, Buffer>,
135-
options: Option<String>,
136-
) -> Result<Resvg, NapiError> {
137-
let js_options: JsOptions = options
138-
.and_then(|o| serde_json::from_str(o.as_str()).ok())
139-
.unwrap_or_default();
140-
let _ = env_logger::builder()
141-
.filter_level(js_options.log_level)
142-
.try_init();
143-
144-
let mut opts = js_options.to_usvg_options();
145-
options::tweak_usvg_options(&mut opts);
146-
let opts_ref = opts.to_ref();
147-
// Parse the SVG string into a tree.
148-
let tree = match svg {
149-
Either::A(a) => usvg::Tree::from_str(a.as_str(), &opts_ref),
150-
Either::B(b) => usvg::Tree::from_data(b.as_ref(), &opts_ref),
151-
}
152-
.map_err(|e| napi::Error::from_reason(format!("{}", e)))?;
153-
Ok(Resvg { tree, js_options })
154-
}
155-
156129
#[napi]
157130
/// Renders an SVG in Node.js
158131
pub fn render(&self) -> Result<RenderedImage, NapiError> {
@@ -265,27 +238,6 @@ impl Resvg {
265238
#[cfg(target_arch = "wasm32")]
266239
#[wasm_bindgen]
267240
impl Resvg {
268-
#[wasm_bindgen(constructor)]
269-
pub fn new(svg: IStringOrBuffer, options: Option<String>) -> Result<Resvg, js_sys::Error> {
270-
let js_options: JsOptions = options
271-
.and_then(|o| serde_json::from_str(o.as_str()).ok())
272-
.unwrap_or_default();
273-
274-
let mut opts = js_options.to_usvg_options();
275-
options::tweak_usvg_options(&mut opts);
276-
let opts_ref = opts.to_ref();
277-
let tree = if js_sys::Uint8Array::instanceof(&svg) {
278-
let uintarray = js_sys::Uint8Array::unchecked_from_js_ref(&svg);
279-
let svg_buffer = uintarray.to_vec();
280-
usvg::Tree::from_data(&svg_buffer, &opts_ref).map_err(Error::from)
281-
} else if let Some(s) = svg.as_string() {
282-
usvg::Tree::from_str(s.as_str(), &opts_ref).map_err(Error::from)
283-
} else {
284-
Err(Error::InvalidInput)
285-
}?;
286-
Ok(Resvg { tree, js_options })
287-
}
288-
289241
/// Get the SVG width
290242
#[wasm_bindgen(getter)]
291243
pub fn width(&self) -> f64 {

0 commit comments

Comments
 (0)