diff --git a/Cargo.toml b/Cargo.toml index 863c398..af3527d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,8 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -resvg = { version = "0.23.0", default-features = false, features = ["filter"] } -usvg = { version = "0.23.0", default-features = false, features = [ "filter", "text"] } +resvg = { version = "0.33.0", default-features = false, features = ["text", "raster-images"] } +usvg = { version = "0.33.0", default-features = false, features = ["text"] } svgtypes = "0.8.0" tiny-skia = "0.6.1" diff --git a/package.json b/package.json index ecf2685..44371ae 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "tslib": "^2.3.1", "typescript": "^4.5.4", "vitest": "^0.9.4", - "wasm-pack": "^0.10.2" + "wasm-pack": "^0.11.1" }, "type": "module", "packageManager": "pnpm@8.5.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6958bde..b4ff63a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,8 +71,8 @@ importers: specifier: ^0.9.4 version: 0.9.4 wasm-pack: - specifier: ^0.10.2 - version: 0.10.2 + specifier: ^0.11.1 + version: 0.11.1 benchmark: dependencies: @@ -134,10 +134,10 @@ importers: version: 1.16.6 '@typescript-eslint/eslint-plugin': specifier: ^5.8.1 - version: 5.8.1(@typescript-eslint/parser@5.9.0)(eslint@8.6.0)(typescript@4.5.4) + version: 5.8.1(@typescript-eslint/parser@5.9.0)(typescript@4.5.4) '@typescript-eslint/parser': specifier: ^5.9.0 - version: 5.9.0(eslint@8.6.0)(typescript@4.5.4) + version: 5.9.0(typescript@4.5.4) autoprefixer: specifier: ^10.4.1 version: 10.4.1(postcss@8.4.5) @@ -325,7 +325,7 @@ packages: '@types/node': 17.0.6 chalk: 4.1.2 cosmiconfig: 7.0.1 - cosmiconfig-typescript-loader: 1.0.9(@types/node@17.0.6)(cosmiconfig@7.0.1)(typescript@4.5.4) + cosmiconfig-typescript-loader: 1.0.9(@types/node@17.0.6)(typescript@4.5.4) lodash: 4.17.21 resolve-from: 5.0.0 typescript: 4.5.4 @@ -1475,7 +1475,7 @@ packages: '@types/node': 17.0.6 dev: true - /@typescript-eslint/eslint-plugin@5.8.1(@typescript-eslint/parser@5.9.0)(eslint@8.6.0)(typescript@4.5.4): + /@typescript-eslint/eslint-plugin@5.8.1(@typescript-eslint/parser@5.9.0)(typescript@4.5.4): resolution: {integrity: sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1486,11 +1486,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/experimental-utils': 5.8.1(eslint@8.6.0)(typescript@4.5.4) - '@typescript-eslint/parser': 5.9.0(eslint@8.6.0)(typescript@4.5.4) + '@typescript-eslint/experimental-utils': 5.8.1(typescript@4.5.4) + '@typescript-eslint/parser': 5.9.0(typescript@4.5.4) '@typescript-eslint/scope-manager': 5.8.1 debug: 4.3.3 - eslint: 8.6.0 functional-red-black-tree: 1.0.1 ignore: 5.2.0 regexpp: 3.2.0 @@ -1501,7 +1500,7 @@ packages: - supports-color dev: true - /@typescript-eslint/experimental-utils@5.8.1(eslint@8.6.0)(typescript@4.5.4): + /@typescript-eslint/experimental-utils@5.8.1(typescript@4.5.4): resolution: {integrity: sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1511,15 +1510,14 @@ packages: '@typescript-eslint/scope-manager': 5.8.1 '@typescript-eslint/types': 5.8.1 '@typescript-eslint/typescript-estree': 5.8.1(typescript@4.5.4) - eslint: 8.6.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@8.6.0) + eslint-utils: 3.0.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/parser@5.9.0(eslint@8.6.0)(typescript@4.5.4): + /@typescript-eslint/parser@5.9.0(typescript@4.5.4): resolution: {integrity: sha512-/6pOPz8yAxEt4PLzgbFRDpZmHnXCeZgPDrh/1DaVKOjvn/UPMlWhbx/gA96xRi2JxY1kBl2AmwVbyROUqys5xQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1533,7 +1531,6 @@ packages: '@typescript-eslint/types': 5.9.0 '@typescript-eslint/typescript-estree': 5.9.0(typescript@4.5.4) debug: 4.3.3 - eslint: 8.6.0 typescript: 4.5.4 transitivePeerDependencies: - supports-color @@ -1826,10 +1823,10 @@ packages: postcss-value-parser: 4.2.0 dev: true - /axios@0.21.4: - resolution: {integrity: sha1-xnuQ3AVo5cHPKwuFjEO6KOLtpXU=} + /axios@0.26.1: + resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} dependencies: - follow-redirects: 1.14.6 + follow-redirects: 1.15.2 transitivePeerDependencies: - debug dev: true @@ -1873,11 +1870,11 @@ packages: engines: {node: '>=8'} dev: true - /binary-install@0.1.1: - resolution: {integrity: sha1-wbIvF0WBdk5cUs0WZkzx0ofji9Q=} + /binary-install@1.1.0: + resolution: {integrity: sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==} engines: {node: '>=10'} dependencies: - axios: 0.21.4 + axios: 0.26.1 rimraf: 3.0.2 tar: 6.1.11 transitivePeerDependencies: @@ -2143,7 +2140,7 @@ packages: dev: false /chownr@2.0.0: - resolution: {integrity: sha1-Fb++U9LqtM9w8YqM1o6+Wzyx3s4=} + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} dev: true @@ -2362,12 +2359,11 @@ packages: resolution: {integrity: sha1-pgQtNjTCsn6TKPg3uWX6yDgI24U=} dev: true - /cosmiconfig-typescript-loader@1.0.9(@types/node@17.0.6)(cosmiconfig@7.0.1)(typescript@4.5.4): + /cosmiconfig-typescript-loader@1.0.9(@types/node@17.0.6)(typescript@4.5.4): resolution: {integrity: sha512-tRuMRhxN4m1Y8hP9SNYfz7jRwt8lZdWxdjg/ohg5esKmsndJIn4yT96oJVcf5x0eA11taXl+sIp+ielu529k6g==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@types/node': '*' - cosmiconfig: '>=7' typescript: '>=3' dependencies: '@types/node': 17.0.6 @@ -3385,6 +3381,15 @@ packages: estraverse: 5.3.0 dev: true + /eslint-utils@3.0.0: + resolution: {integrity: sha1-iuuvrOc0W7M1WdsKHxOh0tSMNnI=} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint-visitor-keys: 2.1.0 + dev: true + /eslint-utils@3.0.0(eslint@8.6.0): resolution: {integrity: sha1-iuuvrOc0W7M1WdsKHxOh0tSMNnI=} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} @@ -3668,8 +3673,8 @@ packages: resolution: {integrity: sha1-KNmWnqkGYbUTQlnzEqtqp5KaxeI=} dev: true - /follow-redirects@1.14.6: - resolution: {integrity: sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==} + /follow-redirects@1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -3719,7 +3724,7 @@ packages: universalify: 2.0.0 /fs-minipass@2.1.0: - resolution: {integrity: sha1-f1A2/b8SxjwWkZDL5BmchSJx+fs=} + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} dependencies: minipass: 3.1.6 @@ -4723,7 +4728,7 @@ packages: dev: true /minizlib@2.1.2: - resolution: {integrity: sha1-6Q00Zrogm5MkUVCKEc49NjIUWTE=} + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} dependencies: minipass: 3.1.6 @@ -4735,7 +4740,7 @@ packages: dev: false /mkdirp@0.5.5: - resolution: {integrity: sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=} + resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==} hasBin: true dependencies: minimist: 1.2.5 @@ -6042,7 +6047,7 @@ packages: dev: true /rimraf@2.7.1: - resolution: {integrity: sha1-NXl/E6f9rcVmFCwp1PB8ytSD4+w=} + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} hasBin: true dependencies: glob: 7.2.0 @@ -6683,7 +6688,7 @@ packages: dev: false /tar@6.1.11: - resolution: {integrity: sha1-Z2CjjwA6+hsv/Q/+npq70Oqz1iE=} + resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==} engines: {node: '>= 10'} dependencies: chownr: 2.0.0 @@ -7062,12 +7067,12 @@ packages: - stylus dev: true - /wasm-pack@0.10.2: - resolution: {integrity: sha512-Kuh8XAArQNVoikMUenjDs4+g6LpktgfXgqlp03G2dJKDBokA7Y8Cx6MDCnvREzNnmprk9IohHytwycVSemwe/w==} + /wasm-pack@0.11.1: + resolution: {integrity: sha512-0BKEioKJY/SMqahDEoaUUR8jrRkHO0cdYhRqqHKQMY3Bac6Eep3ZRsTlpFSSwS4LYPxd+Tb5KFFNhUikCkq8Yg==} hasBin: true requiresBuild: true dependencies: - binary-install: 0.1.1 + binary-install: 1.1.0 transitivePeerDependencies: - debug dev: true @@ -7190,7 +7195,7 @@ packages: dev: true /yallist@4.0.0: - resolution: {integrity: sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=} + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} diff --git a/src/converter.rs b/src/converter.rs index 010b092..93d5063 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -1,11 +1,10 @@ use std::collections::HashSet; use std::str::FromStr; -use tiny_skia::Color; -use tiny_skia::Pixmap; -use tiny_skia::Transform; +use resvg::tiny_skia::{Color, Pixmap, Transform}; use usvg::fontdb::Database; -use usvg::{FitTo, OptionsRef, Size, Tree}; +use usvg::{Options, Size, Tree}; +use usvg::{TreeParsing, TreeTextToPath}; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -43,34 +42,35 @@ impl Converter { self.fantasy_family.as_deref(), self.monospace_family.as_deref(), ); - let faces = fontdb.faces(); - let default_font_family = if faces.is_empty() { - "sans-serif" + let faces = &mut fontdb.faces(); + let default_font_family = if fontdb.is_empty() { + "sans-serif".to_string() } else { - &faces[0].family + faces.next().clone().unwrap().families[0].0.to_string() }; - let svg_options = OptionsRef { + let svg_options = Options { resources_dir: None, dpi: 96.0, - font_family: default_font_family, + font_family: default_font_family.clone(), font_size: 12.0, - languages: &["en".to_string()], + languages: vec!["en".to_string()], shape_rendering: usvg::ShapeRendering::GeometricPrecision, text_rendering: usvg::TextRendering::OptimizeLegibility, image_rendering: usvg::ImageRendering::OptimizeQuality, - keep_named_groups: false, default_size: Size::new( width.unwrap_or(100.0).into(), height.unwrap_or(100.0).into(), ) .ok_or_else(|| JsValue::from_str("Invalid width or height"))?, - fontdb: &fontdb, - image_href_resolver: &usvg::ImageHrefResolver::default(), + image_href_resolver: usvg::ImageHrefResolver::default(), }; + let scale = scale.unwrap_or(1.0); - let tree = + let mut tree = Tree::from_str(svg, &svg_options).map_err(|e| JsValue::from_str(&e.to_string()))?; - let svg_size = tree.svg_node().size; + tree.convert_text(&fontdb); + + let svg_size = tree.size; let (width, height) = match (width, height) { (Some(w), Some(h)) => (w.round() as u32, h.round() as u32), (Some(w), _) => ( @@ -94,12 +94,23 @@ impl Converter { pixmap.fill(parse_color_string(&color)); } - resvg::render( - &tree, - FitTo::Size(width, height), - Transform::default(), - pixmap.as_mut(), + let rtree = resvg::Tree::from_usvg(&tree); + + let size = resvg::IntSize::from_usvg(tree.size); + let size1 = size.to_size(); + let fit_to_size = resvg::IntSize::new(width, height).map(|s| size.scale_to(s)); + let size2 = match fit_to_size { + Some(v) => v.to_size(), + None => size.to_size(), + }; + + let tx = Transform::from_scale( + (size2.width() / size1.width()) as f32, + (size2.height() / size1.height()) as f32, ); + + rtree.render(tx, &mut pixmap.as_mut()); + pixmap .encode_png() .map_err(|e| JsValue::from_str(&e.to_string())) @@ -109,8 +120,8 @@ impl Converter { pub fn list_fonts(&self) -> Box<[JsValue]> { load_fonts(&self.fonts, None, None, None, None, None) .faces() - .iter() - .map(|f| &f.family) + .into_iter() + .map(|f| &f.families[0].0) .collect::>() .iter() .map(|s| JsValue::from_str(s)) diff --git a/test/vrt/expected/edge-circle.50w.png b/test/vrt/expected/edge-circle.50w.png index cb2bd76..4cfbced 100644 Binary files a/test/vrt/expected/edge-circle.50w.png and b/test/vrt/expected/edge-circle.50w.png differ diff --git a/test/vrt/expected/fe-turblence.1x.png b/test/vrt/expected/fe-turblence.1x.png index 88af382..00444a6 100644 Binary files a/test/vrt/expected/fe-turblence.1x.png and b/test/vrt/expected/fe-turblence.1x.png differ diff --git a/test/vrt/expected/fe-turblence.2x.png b/test/vrt/expected/fe-turblence.2x.png index 0f4af4d..fa55007 100644 Binary files a/test/vrt/expected/fe-turblence.2x.png and b/test/vrt/expected/fe-turblence.2x.png differ diff --git a/test/vrt/expected/fe-turblence.50w.png b/test/vrt/expected/fe-turblence.50w.png index b1a6e74..6a7962d 100644 Binary files a/test/vrt/expected/fe-turblence.50w.png and b/test/vrt/expected/fe-turblence.50w.png differ diff --git a/test/vrt/expected/fe-turblence.50w30h.png b/test/vrt/expected/fe-turblence.50w30h.png index 9bb8d16..7fb9af0 100644 Binary files a/test/vrt/expected/fe-turblence.50w30h.png and b/test/vrt/expected/fe-turblence.50w30h.png differ diff --git a/test/vrt/expected/fe-turblence.blue.png b/test/vrt/expected/fe-turblence.blue.png index 08d9895..cf6e316 100644 Binary files a/test/vrt/expected/fe-turblence.blue.png and b/test/vrt/expected/fe-turblence.blue.png differ diff --git a/test/vrt/expected/fe-turblence.green.png b/test/vrt/expected/fe-turblence.green.png index 156effd..c2cdd2f 100644 Binary files a/test/vrt/expected/fe-turblence.green.png and b/test/vrt/expected/fe-turblence.green.png differ diff --git a/test/vrt/expected/fe-turblence.red.png b/test/vrt/expected/fe-turblence.red.png index 04e87ef..106baa2 100644 Binary files a/test/vrt/expected/fe-turblence.red.png and b/test/vrt/expected/fe-turblence.red.png differ