Bump SVG & PDF cinematic universe (#4316)

This commit is contained in:
Laurenz 2024-06-06 17:30:49 +02:00 committed by GitHub
parent 681badf76a
commit 8f7ba8d495
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 404 additions and 408 deletions

188
Cargo.lock generated
View File

@ -392,7 +392,7 @@ dependencies = [
"comemo-macros", "comemo-macros",
"once_cell", "once_cell",
"parking_lot", "parking_lot",
"siphasher 1.0.0", "siphasher 1.0.1",
] ]
[[package]] [[package]]
@ -701,14 +701,14 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d"
dependencies = [ dependencies = [
"roxmltree", "roxmltree 0.19.0",
] ]
[[package]] [[package]]
name = "fontdb" name = "fontdb"
version = "0.16.2" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770"
dependencies = [ dependencies = [
"fontconfig-parser", "fontconfig-parser",
"log", "log",
@ -783,16 +783,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "gif"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
dependencies = [
"color_quant",
"weezl",
]
[[package]] [[package]]
name = "gif" name = "gif"
version = "0.13.1" version = "0.13.1"
@ -1054,12 +1044,28 @@ dependencies = [
"bytemuck", "bytemuck",
"byteorder", "byteorder",
"color_quant", "color_quant",
"gif 0.13.1", "gif",
"jpeg-decoder", "jpeg-decoder",
"num-traits", "num-traits",
"png", "png",
] ]
[[package]]
name = "image"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"gif",
"num-traits",
"png",
"zune-core",
"zune-jpeg",
]
[[package]] [[package]]
name = "imagesize" name = "imagesize"
version = "0.12.0" version = "0.12.0"
@ -1203,11 +1209,12 @@ dependencies = [
[[package]] [[package]]
name = "kurbo" name = "kurbo"
version = "0.9.5" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"smallvec",
] ]
[[package]] [[package]]
@ -1612,11 +1619,11 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]] [[package]]
name = "pdf-writer" name = "pdf-writer"
version = "0.9.3" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24e9127455063c816e661caac9ecd9043ad2871f55be93014e6838a8ced2332b" checksum = "af6a7882fda7808481d43c51cadfc3ec934c6af72612a1fe6985ce329a2f0469"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 2.4.2",
"itoa", "itoa",
"memchr", "memchr",
"ryu", "ryu",
@ -1678,9 +1685,9 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "pixglyph" name = "pixglyph"
version = "0.3.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e0f8ad4c197db38125b880c3c44544788665c7d5f4c42f5a35da44bca1a712" checksum = "4a64dec9fae2e0f75bb2a7d910b4e8b0d15ecd706fb0d61394774b3223c50a97"
dependencies = [ dependencies = [
"ttf-parser", "ttf-parser",
] ]
@ -1904,15 +1911,14 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]] [[package]]
name = "resvg" name = "resvg"
version = "0.38.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c34501046959e06470ba62a2dc7f31c15f94ac250d842a45f9e012f4ee40c1e" checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051"
dependencies = [ dependencies = [
"gif 0.12.0", "gif",
"jpeg-decoder", "jpeg-decoder",
"log", "log",
"pico-args", "pico-args",
"png",
"rgb", "rgb",
"svgtypes", "svgtypes",
"tiny-skia", "tiny-skia",
@ -1940,6 +1946,12 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
[[package]]
name = "roxmltree"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@ -1976,9 +1988,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]] [[package]]
name = "rustybuzz" name = "rustybuzz"
version = "0.12.1" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0ae5692c5beaad6a9e22830deeed7874eae8a4e3ba4076fb48e12c56856222c" checksum = "7730060ad401b0d1807c904ea56735288af101430aa0d2ab8358b789f5f37002"
dependencies = [ dependencies = [
"bitflags 2.4.2", "bitflags 2.4.2",
"bytemuck", "bytemuck",
@ -2160,9 +2172,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54ac45299ccbd390721be55b412d41931911f654fa99e2cb8bfb57184b2061fe" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]] [[package]]
name = "slotmap" name = "slotmap"
@ -2249,27 +2261,33 @@ checksum = "09eab8a83bff89ba2200bd4c59be45c7c787f988431b936099a5a266c957f2f9"
[[package]] [[package]]
name = "svg2pdf" name = "svg2pdf"
version = "0.10.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba36b330062be8497fd96597227a757b621b86c4d24d164b06e4522b52b3693e" checksum = "e31565956eb1dc398c0d9776ee1d1bac4e34759af63dcbe0520df32313a5b53b"
dependencies = [ dependencies = [
"image", "fontdb",
"image 0.25.1",
"log",
"miniz_oxide", "miniz_oxide",
"once_cell", "once_cell",
"pdf-writer", "pdf-writer",
"resvg", "resvg",
"siphasher 1.0.1",
"subsetter",
"tiny-skia", "tiny-skia",
"ttf-parser",
"unicode-properties",
"usvg", "usvg",
] ]
[[package]] [[package]]
name = "svgtypes" name = "svgtypes"
version = "0.13.0" version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c"
dependencies = [ dependencies = [
"kurbo", "kurbo",
"siphasher 0.3.11", "siphasher 1.0.1",
] ]
[[package]] [[package]]
@ -2499,9 +2517,9 @@ dependencies = [
[[package]] [[package]]
name = "ttf-parser" name = "ttf-parser"
version = "0.20.0" version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
[[package]] [[package]]
name = "two-face" name = "two-face"
@ -2541,7 +2559,7 @@ dependencies = [
"icu_provider_blob", "icu_provider_blob",
"icu_segmenter", "icu_segmenter",
"if_chain", "if_chain",
"image", "image 0.24.9",
"indexmap 2.2.5", "indexmap 2.2.5",
"kamadak-exif", "kamadak-exif",
"kurbo", "kurbo",
@ -2555,12 +2573,12 @@ dependencies = [
"qcms", "qcms",
"rayon", "rayon",
"regex", "regex",
"roxmltree", "roxmltree 0.20.0",
"rustybuzz", "rustybuzz",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml 0.9.32", "serde_yaml 0.9.32",
"siphasher 1.0.0", "siphasher 1.0.1",
"smallvec", "smallvec",
"stacker", "stacker",
"syntect", "syntect",
@ -2710,7 +2728,7 @@ dependencies = [
"bytemuck", "bytemuck",
"comemo", "comemo",
"ecow", "ecow",
"image", "image 0.24.9",
"indexmap 2.2.5", "indexmap 2.2.5",
"miniz_oxide", "miniz_oxide",
"once_cell", "once_cell",
@ -2733,10 +2751,10 @@ version = "0.11.0"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"comemo", "comemo",
"image", "image 0.24.9",
"pixglyph", "pixglyph",
"resvg", "resvg",
"roxmltree", "roxmltree 0.20.0",
"tiny-skia", "tiny-skia",
"ttf-parser", "ttf-parser",
"typst", "typst",
@ -2817,7 +2835,7 @@ dependencies = [
"once_cell", "once_cell",
"portable-atomic", "portable-atomic",
"rayon", "rayon",
"siphasher 1.0.0", "siphasher 1.0.1",
"thin-vec", "thin-vec",
] ]
@ -2857,15 +2875,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]] [[package]]
name = "unicode-bidi-mirroring" name = "unicode-bidi-mirroring"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
[[package]] [[package]]
name = "unicode-ccc" name = "unicode-ccc"
version = "0.1.2" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@ -2960,62 +2978,29 @@ dependencies = [
[[package]] [[package]]
name = "usvg" name = "usvg"
version = "0.38.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377f62b4a3c173de8654c1aa80ab1dac1154e6f13a779a9943e53780120d1625" checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032"
dependencies = [
"base64 0.21.7",
"log",
"pico-args",
"usvg-parser",
"usvg-text-layout",
"usvg-tree",
"xmlwriter",
]
[[package]]
name = "usvg-parser"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "351a05e6f2023d6b4e946f734240a3927aefdcf930d7d42587a2c8a8869814b0"
dependencies = [ dependencies = [
"base64 0.22.0",
"data-url", "data-url",
"flate2", "flate2",
"fontdb",
"imagesize", "imagesize",
"kurbo", "kurbo",
"log", "log",
"roxmltree", "pico-args",
"simplecss", "roxmltree 0.20.0",
"siphasher 0.3.11",
"svgtypes",
"usvg-tree",
]
[[package]]
name = "usvg-text-layout"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c41888b9d5cf431fe852eaf9d047bbde83251b98f1749c2f08b1071e6db46e2"
dependencies = [
"fontdb",
"kurbo",
"log",
"rustybuzz", "rustybuzz",
"unicode-bidi", "simplecss",
"unicode-script", "siphasher 1.0.1",
"unicode-vo",
"usvg-tree",
]
[[package]]
name = "usvg-tree"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18863e0404ed153d6e56362c5b1146db9f4f262a3244e3cf2dbe7d8a85909f05"
dependencies = [
"strict-num", "strict-num",
"svgtypes", "svgtypes",
"tiny-skia-path", "tiny-skia-path",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"xmlwriter",
] ]
[[package]] [[package]]
@ -3514,3 +3499,18 @@ dependencies = [
"simd-adler32", "simd-adler32",
"typed-arena", "typed-arena",
] ]
[[package]]
name = "zune-core"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-jpeg"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448"
dependencies = [
"zune-core",
]

View File

@ -46,7 +46,7 @@ dirs = "5"
ecow = { version = "0.2", features = ["serde"] } ecow = { version = "0.2", features = ["serde"] }
env_proxy = "0.4" env_proxy = "0.4"
flate2 = "1" flate2 = "1"
fontdb = { version = "0.16", default-features = false } fontdb = { version = "0.18", default-features = false }
fs_extra = "1.3" fs_extra = "1.3"
hayagriva = "0.5.3" hayagriva = "0.5.3"
heck = "0.4" heck = "0.4"
@ -60,7 +60,7 @@ if_chain = "1"
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] } image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
indexmap = { version = "2", features = ["serde"] } indexmap = { version = "2", features = ["serde"] }
kamadak-exif = "0.5" kamadak-exif = "0.5"
kurbo = "0.9" # in sync with usvg kurbo = "0.11"
libfuzzer-sys = "0.4" libfuzzer-sys = "0.4"
lipsum = "0.9" lipsum = "0.9"
log = "0.4" log = "0.4"
@ -74,9 +74,9 @@ oxipng = { version = "9.0", default-features = false, features = ["filetime", "p
palette = { version = "0.7.3", default-features = false, features = ["approx", "libm"] } palette = { version = "0.7.3", default-features = false, features = ["approx", "libm"] }
parking_lot = "0.12.1" parking_lot = "0.12.1"
pathdiff = "0.2" pathdiff = "0.2"
pdf-writer = "0.9.3" pdf-writer = "0.10.0"
phf = { version = "0.11", features = ["macros"] } phf = { version = "0.11", features = ["macros"] }
pixglyph = "0.3" pixglyph = "0.4"
png = "0.17" png = "0.17"
portable-atomic = "1.6" portable-atomic = "1.6"
proc-macro2 = "1" proc-macro2 = "1"
@ -85,9 +85,9 @@ quote = "1"
qcms = "0.3.0" qcms = "0.3.0"
rayon = "1.7.0" rayon = "1.7.0"
regex = "1" regex = "1"
resvg = { version = "0.38.0", default-features = false, features = ["raster-images"] } resvg = { version = "0.42", default-features = false, features = ["raster-images"] }
roxmltree = "0.19" roxmltree = "0.20"
rustybuzz = "0.12.1" rustybuzz = "0.14"
same-file = "1" same-file = "1"
self-replace = "1.3.7" self-replace = "1.3.7"
semver = "1" semver = "1"
@ -99,7 +99,7 @@ siphasher = "1"
smallvec = { version = "1.11.1", features = ["union", "const_generics", "const_new"] } smallvec = { version = "1.11.1", features = ["union", "const_generics", "const_new"] }
stacker = "0.1.15" stacker = "0.1.15"
subsetter = "0.1.1" subsetter = "0.1.1"
svg2pdf = "0.10" svg2pdf = "0.11.0"
syn = { version = "2", features = ["full", "extra-traits"] } syn = { version = "2", features = ["full", "extra-traits"] }
syntect = { version = "5", default-features = false, features = ["parsing", "regex-fancy", "plist-load", "yaml-load"] } syntect = { version = "5", default-features = false, features = ["parsing", "regex-fancy", "plist-load", "yaml-load"] }
tar = "0.4" tar = "0.4"
@ -108,7 +108,7 @@ thin-vec = "0.2.13"
time = { version = "0.3.20", features = ["formatting", "macros", "parsing"] } time = { version = "0.3.20", features = ["formatting", "macros", "parsing"] }
tiny-skia = "0.11" tiny-skia = "0.11"
toml = { version = "0.8", default-features = false, features = ["parse", "display"] } toml = { version = "0.8", default-features = false, features = ["parse", "display"] }
ttf-parser = "0.20.0" ttf-parser = "0.21.0"
two-face = { version = "0.3.0", default-features = false, features = ["syntect-fancy"] } two-face = { version = "0.3.0", default-features = false, features = ["syntect-fancy"] }
typed-arena = "2" typed-arena = "2"
unicode-bidi = "0.3.13" unicode-bidi = "0.3.13"
@ -119,7 +119,7 @@ unicode-script = "0.5"
unicode-segmentation = "1" unicode-segmentation = "1"
unscanny = "0.1" unscanny = "0.1"
ureq = { version = "2", default-features = false, features = ["native-tls", "gzip", "json"] } ureq = { version = "2", default-features = false, features = ["native-tls", "gzip", "json"] }
usvg = { version = "0.38.0", default-features = false, features = ["text"] } usvg = { version = "0.42", default-features = false, features = ["text"] }
walkdir = "2" walkdir = "2"
wasmi = "0.31.0" wasmi = "0.31.0"
xmlparser = "0.13.5" xmlparser = "0.13.5"

View File

@ -90,12 +90,12 @@ pub fn write_images(context: &WithGlobalRefs) -> (PdfChunk, HashMap<Image, Ref>)
} }
} }
} }
EncodedImage::Svg(svg_chunk) => { EncodedImage::Svg(svg_chunk, id) => {
let mut map = HashMap::new(); let mut map = HashMap::new();
svg_chunk.renumber_into(&mut chunk.chunk, |old| { svg_chunk.renumber_into(&mut chunk.chunk, |old| {
*map.entry(old).or_insert_with(|| chunk.alloc.bump()) *map.entry(old).or_insert_with(|| chunk.alloc.bump())
}); });
out.insert(image.clone(), map[&Ref::new(1)]); out.insert(image.clone(), map[&id]);
} }
} }
} }
@ -132,7 +132,10 @@ pub fn deferred_image(image: Image) -> (Deferred<EncodedImage>, Option<ColorSpac
EncodedImage::Raster { data, filter, has_color, width, height, icc, alpha } EncodedImage::Raster { data, filter, has_color, width, height, icc, alpha }
} }
ImageKind::Svg(svg) => EncodedImage::Svg(encode_svg(svg)), ImageKind::Svg(svg) => {
let (chunk, id) = encode_svg(svg);
EncodedImage::Svg(chunk, id)
}
}); });
(deferred, color_space) (deferred, color_space)
@ -176,25 +179,8 @@ fn encode_alpha(raster: &RasterImage) -> (Vec<u8>, Filter) {
} }
/// Encode an SVG into a chunk of PDF objects. /// Encode an SVG into a chunk of PDF objects.
/// fn encode_svg(svg: &SvgImage) -> (Chunk, Ref) {
/// The main XObject will have ID 1. svg2pdf::to_chunk(svg.tree(), svg2pdf::ConversionOptions::default())
fn encode_svg(svg: &SvgImage) -> Chunk {
let mut chunk = Chunk::new();
// Safety: We do not keep any references to tree nodes beyond the
// scope of `with`.
unsafe {
svg.with(|tree| {
svg2pdf::convert_tree_into(
tree,
svg2pdf::Options::default(),
&mut chunk,
Ref::new(1),
);
});
}
chunk
} }
/// A pre-encoded image. /// A pre-encoded image.
@ -219,5 +205,5 @@ pub enum EncodedImage {
/// A vector graphic. /// A vector graphic.
/// ///
/// The chunk is the SVG converted to PDF objects. /// The chunk is the SVG converted to PDF objects.
Svg(Chunk), Svg(Chunk, Ref),
} }

View File

@ -115,29 +115,12 @@ fn write_page(
return; return;
}; };
let global_resources_ref = ctx.resources.reference; let mut annotations = Vec::with_capacity(page.content.links.len());
let mut page_writer = chunk.page(page_ref);
page_writer.parent(page_tree_ref);
let w = page.content.size.x.to_f32();
let h = page.content.size.y.to_f32();
page_writer.media_box(Rect::new(0.0, 0.0, w, h));
page_writer.contents(content_id);
page_writer.pair(Name(b"Resources"), global_resources_ref);
if page.content.uses_opacities {
page_writer
.group()
.transparency()
.isolated(false)
.knockout(false)
.color_space()
.srgb();
}
let mut annotations = page_writer.annotations();
for (dest, rect) in &page.content.links { for (dest, rect) in &page.content.links {
let mut annotation = annotations.push(); let id = chunk.alloc();
annotations.push(id);
let mut annotation = chunk.annotation(id);
annotation.subtype(AnnotationType::Link).rect(*rect); annotation.subtype(AnnotationType::Link).rect(*rect);
annotation.border(0.0, 0.0, 0.0, None).flags(AnnotationFlags::PRINT); annotation.border(0.0, 0.0, 0.0, None).flags(AnnotationFlags::PRINT);
@ -180,7 +163,27 @@ fn write_page(
} }
} }
annotations.finish(); let mut page_writer = chunk.page(page_ref);
page_writer.parent(page_tree_ref);
let w = page.content.size.x.to_f32();
let h = page.content.size.y.to_f32();
page_writer.media_box(Rect::new(0.0, 0.0, w, h));
page_writer.contents(content_id);
page_writer.pair(Name(b"Resources"), ctx.resources.reference);
if page.content.uses_opacities {
page_writer
.group()
.transparency()
.isolated(false)
.knockout(false)
.color_space()
.srgb();
}
page_writer.annotations(annotations);
page_writer.finish(); page_writer.finish();
chunk chunk

View File

@ -72,15 +72,14 @@ fn scaled_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
} }
// Safety: We do not keep any references to tree nodes beyond the scope // Safety: We do not keep any references to tree nodes beyond the scope
// of `with`. // of `with`.
ImageKind::Svg(svg) => unsafe { ImageKind::Svg(svg) => {
svg.with(|tree| { let tree = svg.tree();
let ts = tiny_skia::Transform::from_scale( let ts = tiny_skia::Transform::from_scale(
w as f32 / tree.size.width(), w as f32 / tree.size().width(),
h as f32 / tree.size.height(), h as f32 / tree.size().height(),
); );
resvg::render(tree, ts, &mut pixmap.as_mut()) resvg::render(tree, ts, &mut pixmap.as_mut())
}); }
},
} }
Some(Arc::new(pixmap)) Some(Arc::new(pixmap))
} }

View File

@ -6,7 +6,8 @@ use std::sync::Arc;
use az::SaturatingAs; use az::SaturatingAs;
use ecow::EcoString; use ecow::EcoString;
use rustybuzz::{ShapePlan, Tag, UnicodeBuffer}; use rustybuzz::{ShapePlan, UnicodeBuffer};
use ttf_parser::Tag;
use unicode_script::{Script, UnicodeScript}; use unicode_script::{Script, UnicodeScript};
use super::SpanMapper; use super::SpanMapper;

View File

@ -4,12 +4,11 @@ use std::io::Read;
use ecow::EcoString; use ecow::EcoString;
use ttf_parser::GlyphId; use ttf_parser::GlyphId;
use usvg::{TreeParsing, TreeWriting};
use crate::layout::{Abs, Axes, Em, Frame, FrameItem, Point, Size}; use crate::layout::{Abs, Axes, Em, Frame, FrameItem, Point, Size};
use crate::syntax::Span; use crate::syntax::Span;
use crate::text::{Font, Glyph, Lang, TextItem}; use crate::text::{Font, Glyph, Lang, TextItem};
use crate::visualize::{Color, Image, Paint, Rgb}; use crate::visualize::{Color, Image, Rgb};
/// Tells if a glyph is a color glyph or not in a given font. /// Tells if a glyph is a color glyph or not in a given font.
pub fn is_color_glyph(font: &Font, g: &Glyph) -> bool { pub fn is_color_glyph(font: &Font, g: &Glyph) -> bool {
@ -78,7 +77,8 @@ fn draw_raster_glyph(
/// Draws a COLR glyph in a frame. /// Draws a COLR glyph in a frame.
fn draw_colr_glyph(frame: &mut Frame, font: &Font, glyph_id: GlyphId) { fn draw_colr_glyph(frame: &mut Frame, font: &Font, glyph_id: GlyphId) {
let mut painter = ColrPainter { font, current_glyph: glyph_id, frame }; let mut painter = ColrPainter { font, current_glyph: glyph_id, frame };
font.ttf().paint_color_glyph(glyph_id, 0, &mut painter); let black = ttf_parser::RgbaColor::new(0, 0, 0, 255);
font.ttf().paint_color_glyph(glyph_id, 0, black, &mut painter);
} }
/// Draws COLR glyphs in a frame. /// Draws COLR glyphs in a frame.
@ -91,8 +91,20 @@ struct ColrPainter<'f, 't> {
current_glyph: GlyphId, current_glyph: GlyphId,
} }
impl<'f, 't> ColrPainter<'f, 't> { impl<'f, 't> ttf_parser::colr::Painter<'_> for ColrPainter<'f, 't> {
fn paint(&mut self, fill: Paint) { fn outline_glyph(&mut self, glyph_id: GlyphId) {
self.current_glyph = glyph_id;
}
fn paint(&mut self, paint: ttf_parser::colr::Paint) {
let ttf_parser::colr::Paint::Solid(color) = paint else { return };
let color = Color::Rgb(Rgb::new(
color.red as f32 / 255.0,
color.green as f32 / 255.0,
color.blue as f32 / 255.0,
color.alpha as f32 / 255.0,
));
self.frame.push( self.frame.push(
// With images, the position corresponds to the top-left corner, but // With images, the position corresponds to the top-left corner, but
// in the case of text it matches the baseline-left point. Here, we // in the case of text it matches the baseline-left point. Here, we
@ -101,7 +113,7 @@ impl<'f, 't> ColrPainter<'f, 't> {
FrameItem::Text(TextItem { FrameItem::Text(TextItem {
font: self.font.clone(), font: self.font.clone(),
size: Abs::pt(self.font.units_per_em()), size: Abs::pt(self.font.units_per_em()),
fill, fill: color.into(),
stroke: None, stroke: None,
lang: Lang::ENGLISH, lang: Lang::ENGLISH,
region: None, region: None,
@ -116,29 +128,21 @@ impl<'f, 't> ColrPainter<'f, 't> {
span: (Span::detached(), 0), span: (Span::detached(), 0),
}], }],
}), }),
) );
}
}
impl<'f, 't> ttf_parser::colr::Painter for ColrPainter<'f, 't> {
fn outline(&mut self, glyph_id: GlyphId) {
self.current_glyph = glyph_id;
} }
fn paint_foreground(&mut self) { // These are not implemented.
// Default to black if no color was specified fn push_clip(&mut self) {}
self.paint(Paint::Solid(Color::BLACK)) fn push_clip_box(&mut self, _: ttf_parser::colr::ClipBox) {}
} fn pop_clip(&mut self) {}
fn push_layer(&mut self, _: ttf_parser::colr::CompositeMode) {}
fn paint_color(&mut self, color: ttf_parser::RgbaColor) { fn pop_layer(&mut self) {}
let color = Color::Rgb(Rgb::new( fn push_translate(&mut self, _: f32, _: f32) {}
color.red as f32 / 255.0, fn push_scale(&mut self, _: f32, _: f32) {}
color.green as f32 / 255.0, fn push_rotate(&mut self, _: f32) {}
color.blue as f32 / 255.0, fn push_skew(&mut self, _: f32, _: f32) {}
color.alpha as f32 / 255.0, fn push_transform(&mut self, _: ttf_parser::Transform) {}
)); fn pop_transform(&mut self) {}
self.paint(Paint::Solid(color));
}
} }
/// Draws an SVG glyph in a frame. /// Draws an SVG glyph in a frame.
@ -164,25 +168,16 @@ fn draw_svg_glyph(
// Parse SVG. // Parse SVG.
let opts = usvg::Options::default(); let opts = usvg::Options::default();
let mut tree = usvg::Tree::from_xmltree(&document, &opts).ok()?; let tree = usvg::Tree::from_xmltree(&document, &opts).ok()?;
// Compute the space we need to draw our glyph.
// See https://github.com/RazrFalcon/resvg/issues/602 for why
// using the svg size is problematic here.
tree.calculate_bounding_boxes();
let mut bbox = usvg::BBox::default();
if let Some(tree_bbox) = tree.root.bounding_box {
bbox = bbox.expand(tree_bbox);
}
let bbox = bbox.to_rect()?;
let mut data = tree.to_string(&usvg::XmlOptions::default());
let bbox = tree.root().bounding_box();
let width = bbox.width() as f64; let width = bbox.width() as f64;
let height = bbox.height() as f64; let height = bbox.height() as f64;
let left = bbox.left() as f64; let left = bbox.left() as f64;
let top = bbox.top() as f64; let top = bbox.top() as f64;
let mut data = tree.to_string(&usvg::WriteOptions::default());
// The SVG coordinates and the font coordinates are not the same: the Y axis // The SVG coordinates and the font coordinates are not the same: the Y axis
// is mirrored. But the origin of the axes are the same (which means that // is mirrored. But the origin of the axes are the same (which means that
// the horizontal axis in the SVG document corresponds to the baseline). See // the horizontal axis in the SVG document corresponds to the baseline). See

View File

@ -141,6 +141,12 @@ impl Debug for FontWeight {
} }
} }
impl From<fontdb::Weight> for FontWeight {
fn from(weight: fontdb::Weight) -> Self {
Self::from_number(weight.0)
}
}
cast! { cast! {
FontWeight, FontWeight,
self => IntoValue::into_value(match self { self => IntoValue::into_value(match self {
@ -237,6 +243,21 @@ impl FontStretch {
Ratio::new(self.0 as f64 / 1000.0) Ratio::new(self.0 as f64 / 1000.0)
} }
/// Round to one of the pre-defined variants.
pub fn round(self) -> Self {
match self.0 {
..=562 => Self::ULTRA_CONDENSED,
563..=687 => Self::EXTRA_CONDENSED,
688..=812 => Self::CONDENSED,
813..=937 => Self::SEMI_CONDENSED,
938..=1062 => Self::NORMAL,
1063..=1187 => Self::SEMI_EXPANDED,
1188..=1374 => Self::EXPANDED,
1375..=1749 => Self::EXTRA_EXPANDED,
1750.. => Self::ULTRA_EXPANDED,
}
}
/// The absolute ratio distance between this and another font stretch. /// The absolute ratio distance between this and another font stretch.
pub fn distance(self, other: Self) -> Ratio { pub fn distance(self, other: Self) -> Ratio {
(self.to_ratio() - other.to_ratio()).abs() (self.to_ratio() - other.to_ratio()).abs()
@ -248,6 +269,7 @@ impl Default for FontStretch {
Self::NORMAL Self::NORMAL
} }
} }
impl Repr for FontStretch { impl Repr for FontStretch {
fn repr(&self) -> EcoString { fn repr(&self) -> EcoString {
self.to_ratio().repr() self.to_ratio().repr()

View File

@ -31,9 +31,9 @@ pub use self::space::*;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use ecow::{eco_format, EcoString}; use ecow::{eco_format, EcoString};
use rustybuzz::{Feature, Tag}; use rustybuzz::Feature;
use smallvec::SmallVec; use smallvec::SmallVec;
use ttf_parser::Rect; use ttf_parser::{Rect, Tag};
use crate::diag::{bail, warning, HintedStrResult, SourceResult}; use crate::diag::{bail, warning, HintedStrResult, SourceResult};
use crate::engine::Engine; use crate::engine::Engine;

View File

@ -1,17 +1,17 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::{Arc, Mutex};
use comemo::Tracked; use comemo::Tracked;
use ecow::EcoString; use ecow::EcoString;
use once_cell::sync::Lazy; use siphasher::sip128::{Hasher128, SipHasher13};
use siphasher::sip128::Hasher128;
use usvg::{ImageHrefResolver, Node, PostProcessingSteps, TreeParsing, TreePostProc};
use crate::diag::{format_xml_like_error, StrResult}; use crate::diag::{format_xml_like_error, StrResult};
use crate::foundations::Bytes; use crate::foundations::Bytes;
use crate::layout::Axes; use crate::layout::Axes;
use crate::text::{FontVariant, FontWeight}; use crate::text::{
Font, FontBook, FontFlags, FontStretch, FontStyle, FontVariant, FontWeight,
};
use crate::visualize::Image; use crate::visualize::Image;
use crate::World; use crate::World;
@ -24,23 +24,16 @@ struct Repr {
data: Bytes, data: Bytes,
size: Axes<f64>, size: Axes<f64>,
font_hash: u128, font_hash: u128,
tree: sync::SyncTree, tree: usvg::Tree,
} }
impl SvgImage { impl SvgImage {
/// Decode an SVG image without fonts. /// Decode an SVG image without fonts.
#[comemo::memoize] #[comemo::memoize]
pub fn new(data: Bytes) -> StrResult<SvgImage> { pub fn new(data: Bytes) -> StrResult<SvgImage> {
let mut tree = let tree =
usvg::Tree::from_data(&data, &OPTIONS).map_err(format_usvg_error)?; usvg::Tree::from_data(&data, &base_options()).map_err(format_usvg_error)?;
tree.calculate_bounding_boxes(); Ok(Self(Arc::new(Repr { data, size: tree_size(&tree), font_hash: 0, tree })))
Ok(Self(Arc::new(Repr {
data,
size: tree_size(&tree),
font_hash: 0,
// Safety: We just created the tree and hold the only reference.
tree: unsafe { sync::SyncTree::new(tree) },
})))
} }
/// Decode an SVG image with access to fonts. /// Decode an SVG image with access to fonts.
@ -50,22 +43,25 @@ impl SvgImage {
world: Tracked<dyn World + '_>, world: Tracked<dyn World + '_>,
families: &[String], families: &[String],
) -> StrResult<SvgImage> { ) -> StrResult<SvgImage> {
let mut tree = let book = world.book();
usvg::Tree::from_data(&data, &OPTIONS).map_err(format_usvg_error)?; let resolver = Mutex::new(FontResolver::new(world, book, families));
let mut font_hash = 0; let tree = usvg::Tree::from_data(
if tree.has_text_nodes() { &data,
let (fontdb, hash) = load_svg_fonts(world, &mut tree, families); &usvg::Options {
tree.postprocess(PostProcessingSteps::default(), &fontdb); font_resolver: usvg::FontResolver {
font_hash = hash; select_font: Box::new(|font, db| {
} resolver.lock().unwrap().select_font(font, db)
tree.calculate_bounding_boxes(); }),
Ok(Self(Arc::new(Repr { select_fallback: Box::new(|c, exclude_fonts, db| {
data, resolver.lock().unwrap().select_fallback(c, exclude_fonts, db)
size: tree_size(&tree), }),
font_hash, },
// Safety: We just created the tree and hold the only reference. ..base_options()
tree: unsafe { sync::SyncTree::new(tree) }, },
}))) )
.map_err(format_usvg_error)?;
let font_hash = resolver.into_inner().unwrap().finish();
Ok(Self(Arc::new(Repr { data, size: tree_size(&tree), font_hash, tree })))
} }
/// The raw image data. /// The raw image data.
@ -83,34 +79,9 @@ impl SvgImage {
self.0.size.y self.0.size.y
} }
/// Performs an operation with the usvg tree. /// Accesses the usvg tree.
/// pub fn tree(&self) -> &usvg::Tree {
/// This makes the tree uniquely available to the current thread and blocks &self.0.tree
/// other accesses to it.
///
/// # Safety
/// The caller may not hold any references to `Rc`s contained in the usvg
/// Tree after `f` returns.
///
/// # Why is it unsafe?
/// Sadly, usvg's Tree is neither `Sync` nor `Send` because it uses `Rc`
/// internally and sending a tree to another thread could result in data
/// races when an `Rc`'s ref-count is modified from two threads at the same
/// time.
///
/// However, access to the tree is actually safe if we don't clone `Rc`s /
/// only clone them while holding a mutex and drop all clones before the
/// mutex is released. Sadly, we can't enforce this variant at the type
/// system level. Therefore, access is guarded by this function (which makes
/// it reasonable hard to keep references around) and its usage still
/// remains `unsafe` (because it's still possible to have `Rc`s escape).
///
/// See also: <https://github.com/RazrFalcon/resvg/issues/544>
pub unsafe fn with<F>(&self, f: F)
where
F: FnOnce(&usvg::Tree),
{
self.0.tree.with(f)
} }
} }
@ -124,127 +95,31 @@ impl Hash for Repr {
} }
} }
/// The conversion options. /// The base conversion options, to be extended with font-related options
static OPTIONS: Lazy<usvg::Options> = Lazy::new(|| usvg::Options { /// because those can change across the document.
// Disable usvg's default to "Times New Roman". Instead, we default to fn base_options() -> usvg::Options<'static> {
// the empty family and later, when we traverse the SVG, we check for usvg::Options {
// empty and non-existing family names and replace them with the true // Disable usvg's default to "Times New Roman".
// fallback family. This way, we can memoize SVG decoding with and font_family: String::new(),
// without fonts if the SVG does not contain text.
font_family: String::new(),
// We override the DPI here so that we get the correct the size when // We override the DPI here so that we get the correct the size when
// scaling the image to its natural size. // scaling the image to its natural size.
dpi: Image::DEFAULT_DPI as f32, dpi: Image::DEFAULT_DPI as f32,
// Override usvg's resource loading defaults. // Override usvg's resource loading defaults.
resources_dir: None, resources_dir: None,
image_href_resolver: ImageHrefResolver { image_href_resolver: usvg::ImageHrefResolver {
resolve_data: ImageHrefResolver::default_data_resolver(), resolve_data: usvg::ImageHrefResolver::default_data_resolver(),
resolve_string: Box::new(|_, _| None), resolve_string: Box::new(|_, _| None),
}, },
..Default::default() ..Default::default()
});
/// Discover and load the fonts referenced by an SVG.
fn load_svg_fonts(
world: Tracked<dyn World + '_>,
tree: &mut usvg::Tree,
families: &[String],
) -> (fontdb::Database, u128) {
let book = world.book();
let mut fontdb = fontdb::Database::new();
let mut hasher = siphasher::sip128::SipHasher13::new();
let mut loaded = HashMap::<usize, Option<String>>::new();
// Loads a font into the database and return it's usvg-compatible name.
let mut load_into_db = |id: usize| -> Option<String> {
loaded
.entry(id)
.or_insert_with(|| {
let font = world.font(id)?;
fontdb.load_font_source(fontdb::Source::Binary(Arc::new(
font.data().clone(),
)));
font.data().hash(&mut hasher);
font.find_name(ttf_parser::name_id::TYPOGRAPHIC_FAMILY)
.or_else(|| font.find_name(ttf_parser::name_id::FAMILY))
})
.clone()
};
// Determine the best font for each text node.
for child in &mut tree.root.children {
traverse_svg(child, &mut |node| {
let usvg::Node::Text(ref mut text) = node else { return };
for chunk in &mut text.chunks {
'spans: for span in &mut chunk.spans {
let Some(text) = chunk.text.get(span.start..span.end) else {
continue;
};
let variant = FontVariant {
style: span.font.style.into(),
weight: FontWeight::from_number(span.font.weight),
stretch: span.font.stretch.into(),
};
// Find a font that covers the whole text among the span's fonts
// and the current document font families.
let mut like = None;
for family in span.font.families.iter().chain(families) {
let Some(id) = book.select(&family.to_lowercase(), variant)
else {
continue;
};
let Some(info) = book.info(id) else { continue };
like.get_or_insert(info);
if text.chars().all(|c| info.coverage.contains(c as u32)) {
if let Some(usvg_family) = load_into_db(id) {
span.font.families = vec![usvg_family];
continue 'spans;
}
}
}
// If we didn't find a match, select a fallback font.
if let Some(id) = book.select_fallback(like, variant, text) {
if let Some(usvg_family) = load_into_db(id) {
span.font.families = vec![usvg_family];
}
}
}
}
});
}
(fontdb, hasher.finish128().as_u128())
}
/// Search for all font families referenced by an SVG.
fn traverse_svg<F>(node: &mut usvg::Node, f: &mut F)
where
F: FnMut(&mut usvg::Node),
{
f(node);
node.subroots_mut(|subroot| {
for child in &mut subroot.children {
traverse_svg(child, f);
}
});
if let Node::Group(ref mut group) = node {
for child in &mut group.children {
traverse_svg(child, f);
}
} }
} }
/// The ceiled pixel size of an SVG. /// The pixel size of an SVG.
fn tree_size(tree: &usvg::Tree) -> Axes<f64> { fn tree_size(tree: &usvg::Tree) -> Axes<f64> {
Axes::new(tree.size.width() as f64, tree.size.height() as f64) Axes::new(tree.size().width() as f64, tree.size().height() as f64)
} }
/// Format the user-facing SVG decoding error message. /// Format the user-facing SVG decoding error message.
@ -260,40 +135,155 @@ fn format_usvg_error(error: usvg::Error) -> EcoString {
} }
} }
mod sync { /// Provides Typst's fonts to usvg.
use std::sync::Mutex; struct FontResolver<'a> {
/// Typst's font book.
book: &'a FontBook,
/// The world we use to load fonts.
world: Tracked<'a, dyn World + 'a>,
/// The active list of font families at the location of the SVG.
families: &'a [String],
/// A mapping from Typst font indices to fontdb IDs.
to_id: HashMap<usize, Option<fontdb::ID>>,
/// The reverse mapping.
from_id: HashMap<fontdb::ID, Font>,
/// Accumulates a hash of all used fonts.
hasher: SipHasher13,
}
/// A synchronized wrapper around a `usvg::Tree`. impl<'a> FontResolver<'a> {
pub struct SyncTree(Mutex<usvg::Tree>); /// Create a new font provider.
fn new(
impl SyncTree { world: Tracked<'a, dyn World + 'a>,
/// Create a new synchronized tree. book: &'a FontBook,
/// families: &'a [String],
/// # Safety ) -> Self {
/// The tree must be completely owned by `tree`, there may not be any Self {
/// other references to `Rc`s contained in it. book,
pub unsafe fn new(tree: usvg::Tree) -> Self { world,
Self(Mutex::new(tree)) families,
} to_id: HashMap::new(),
from_id: HashMap::new(),
/// Perform an operation with the usvg tree. hasher: SipHasher13::new(),
///
/// # Safety
/// The caller may not hold any references to `Rc`s contained in
/// the usvg Tree after returning.
pub unsafe fn with<F>(&self, f: F)
where
F: FnOnce(&usvg::Tree),
{
let tree = self.0.lock().unwrap();
f(&tree)
} }
} }
// Safety: usvg's Tree is only non-Sync and non-Send because it uses `Rc` /// Returns a hash of all used fonts.
// internally. By wrapping it in a mutex and forbidding outstanding fn finish(self) -> u128 {
// references to the tree to remain after a `with` call, we guarantee that self.hasher.finish128().as_u128()
// no two threads try to change a ref-count at the same time. }
unsafe impl Sync for SyncTree {} }
unsafe impl Send for SyncTree {}
impl FontResolver<'_> {
/// Select a font.
fn select_font(
&mut self,
font: &usvg::Font,
db: &mut Arc<fontdb::Database>,
) -> Option<fontdb::ID> {
let variant = FontVariant {
style: font.style().into(),
weight: FontWeight::from_number(font.weight()),
stretch: font.stretch().into(),
};
// Find a family that is available.
font.families()
.iter()
.filter_map(|family| match family {
usvg::FontFamily::Named(named) => Some(named),
// We don't support generic families at the moment.
_ => None,
})
.chain(self.families)
.filter_map(|named| self.book.select(&named.to_lowercase(), variant))
.find_map(|index| self.get_or_load(index, db))
}
/// Select a fallback font.
fn select_fallback(
&mut self,
c: char,
exclude_fonts: &[fontdb::ID],
db: &mut Arc<fontdb::Database>,
) -> Option<fontdb::ID> {
// Get the font info of the originally selected font.
let like = exclude_fonts
.first()
.and_then(|first| self.from_id.get(first))
.map(|font| font.info());
// usvg doesn't provide a variant in the fallback handler, but
// `exclude_fonts` is actually never empty in practice. Still, we
// prefer to fall back to the default variant rather than panicking
// in case that changes in the future.
let variant = like.map(|info| info.variant).unwrap_or_default();
// Select the font.
let index =
self.book.select_fallback(like, variant, c.encode_utf8(&mut [0; 4]))?;
self.get_or_load(index, db)
}
/// Tries to retrieve the ID for the index or loads the font, allocating
/// a new ID.
fn get_or_load(
&mut self,
index: usize,
db: &mut Arc<fontdb::Database>,
) -> Option<fontdb::ID> {
self.to_id
.get(&index)
.copied()
.unwrap_or_else(|| self.load(index, db))
}
/// Tries to load the font with the given index in the font book into the
/// database and returns its ID.
fn load(
&mut self,
index: usize,
db: &mut Arc<fontdb::Database>,
) -> Option<fontdb::ID> {
let font = self.world.font(index)?;
let info = font.info();
let variant = info.variant;
let id = Arc::make_mut(db).push_face_info(fontdb::FaceInfo {
id: fontdb::ID::dummy(),
source: fontdb::Source::Binary(Arc::new(font.data().clone())),
index: font.index(),
families: vec![(
info.family.clone(),
ttf_parser::Language::English_UnitedStates,
)],
post_script_name: String::new(),
style: match variant.style {
FontStyle::Normal => fontdb::Style::Normal,
FontStyle::Italic => fontdb::Style::Italic,
FontStyle::Oblique => fontdb::Style::Oblique,
},
weight: fontdb::Weight(variant.weight.to_number()),
stretch: match variant.stretch.round() {
FontStretch::ULTRA_CONDENSED => ttf_parser::Width::UltraCondensed,
FontStretch::EXTRA_CONDENSED => ttf_parser::Width::ExtraCondensed,
FontStretch::CONDENSED => ttf_parser::Width::Condensed,
FontStretch::SEMI_CONDENSED => ttf_parser::Width::SemiCondensed,
FontStretch::NORMAL => ttf_parser::Width::Normal,
FontStretch::SEMI_EXPANDED => ttf_parser::Width::SemiExpanded,
FontStretch::EXPANDED => ttf_parser::Width::Expanded,
FontStretch::EXTRA_EXPANDED => ttf_parser::Width::ExtraExpanded,
FontStretch::ULTRA_EXPANDED => ttf_parser::Width::UltraExpanded,
_ => unreachable!(),
},
monospaced: info.flags.contains(FontFlags::MONOSPACE),
});
font.hash(&mut self.hasher);
self.to_id.insert(index, Some(id));
self.from_id.insert(id, font);
Some(id)
}
} }