Skip to content

Commit

Permalink
fix: use indexmap to keep order of fvar data
Browse files Browse the repository at this point in the history
  • Loading branch information
zimond committed Nov 14, 2024
1 parent 60cb517 commit 380f300
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 26 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ unicode-script = { version = "0.5.4", optional = true }
woff2-patched = { version = "0.3.0", optional = true }
png = { version = "0.17.13", optional = true }
inflections = "1.1.1"
indexmap = "2.6.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
wit-bindgen-rt = { version = "0.24.0", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fontkit-rs",
"version": "0.0.12",
"version": "0.0.13-beta.1",
"description": "Toolkit used to load, match, measure, and render texts",
"main": "index.js",
"directories": {
Expand Down
48 changes: 30 additions & 18 deletions src/font.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use arc_swap::ArcSwap;
#[cfg(feature = "parse")]
use byteorder::{BigEndian, ReadBytesExt};
#[cfg(feature = "parse")]
use indexmap::IndexMap;
use ordered_float::OrderedFloat;
use ouroboros::self_referencing;
#[cfg(feature = "parse")]
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
#[cfg(feature = "parse")]
Expand Down Expand Up @@ -114,16 +114,6 @@ pub(super) struct FvarInstance {
pub(super) postscript: Name,
}

#[derive(Clone)]
enum Variant {
Index(u32),
Instance {
coords: Vec<Fixed>,
names: Vec<FvarInstance>,
axes: Vec<VariationAxis>,
},
}

/// Returns whether a buffer is WOFF font data.
pub fn is_woff(buf: &[u8]) -> bool {
buf.len() > 4 && buf[0] == 0x77 && buf[1] == 0x4F && buf[2] == 0x46 && buf[3] == 0x46
Expand Down Expand Up @@ -154,6 +144,7 @@ pub fn is_otf(buf: &[u8]) -> bool {
&& buf[4] == 0x00
}

#[derive(Debug)]
pub(crate) struct VariationData {
pub key: FontKey,
pub names: Vec<Name>,
Expand All @@ -177,7 +168,7 @@ impl VariationData {
.collect::<Vec<_>>();

// get fvar if any
let mut instances: HashMap<Vec<OrderedFloat<f32>>, Vec<FvarInstance>> = HashMap::new();
let mut instances: IndexMap<Vec<OrderedFloat<f32>>, Vec<FvarInstance>> = IndexMap::new();
if let (Some(_), Some(name_table)) = (face.tables().fvar, face.tables().name) {
// currently ttf-parser is missing `fvar`'s instance records, we parse them
// directly from `RawFace`
Expand Down Expand Up @@ -334,12 +325,12 @@ impl VariationData {
.iter()
.position(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wght"));
if let Some(value) = width_axis_index.and_then(|i| coords.get(i)) {
key.stretch = Some(value.0 as u16);
// mapping wdth to usWidthClass, ref: https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_wdth
key.stretch = Some(((value.0 / 100.0) * 5.0).round().min(1.0).max(9.0) as u16);
}
if let Some(value) = weight_axis_index.and_then(|i| coords.get(i)) {
key.weight = Some(value.0 as u16);
}
key.family = variation_names[0].postscript.name.clone();
for (coord, axis) in coords.iter().zip(axes.iter()) {
key.variations
.push((String::from_utf8(axis.tag.to_bytes().to_vec())?, coord.0));
Expand Down Expand Up @@ -473,6 +464,20 @@ impl Font {
let mut buffer = Vec::new();
let mut file = std::fs::File::open(path)?;
file.read_to_end(&mut buffer).unwrap();

#[cfg(feature = "woff2-patched")]
if is_woff2(&buffer) {
buffer = woff2_patched::convert_woff2_to_ttf(&mut buffer.as_slice())?;
}
#[cfg(feature = "parse")]
if is_woff(&buffer) {
use std::io::Cursor;

let reader = Cursor::new(buffer);
let mut otf_buf = Cursor::new(Vec::new());
crate::conv::woff::convert_woff_to_otf(reader, &mut otf_buf)?;
buffer = otf_buf.into_inner();
}
self.buffer.swap(Arc::new(buffer));
}
Ok(())
Expand All @@ -488,9 +493,15 @@ impl Font {
let filters = Filter::from_key(key);
let mut queue = self.variants.iter().collect::<Vec<_>>();
for filter in filters {
queue.retain(|v| v.fulfils(&filter));
if queue.len() == 1 {
let mut q = queue.clone();
q.retain(|v| v.fulfils(&filter));
if q.len() == 1 {
queue = q;
break;
} else if q.is_empty() {
break;
} else {
queue = q;
}
}
let variant = queue[0];
Expand All @@ -500,7 +511,8 @@ impl Font {
buffer,
face_builder: |buf| Face::parse(buf, variant.index),
}
.try_build()?;
.try_build()
.unwrap();
face.with_face_mut(|face| {
for (coord, axis) in &variant.key.variations {
face.set_variation(Tag::from_bytes_lossy(coord.as_bytes()), *axis);
Expand Down
35 changes: 30 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,25 @@ impl FontKit {
}

pub fn exact_match(&self, key: &font::FontKey) -> Option<StaticFace> {
return self.fonts.get(key)?.face(key).ok();
let face = self.query(key)?;
let mut patched_key = key.clone();
if patched_key.weight.is_none() {
patched_key.weight = Some(400);
}
if patched_key.stretch.is_none() {
patched_key.stretch = Some(5);
}
if patched_key.italic.is_none() {
patched_key.italic = Some(false);
}
if face.key() == patched_key {
return Some(face);
} else {
return None;
}
}

pub fn query(&self, key: &font::FontKey) -> Option<StaticFace> {
if let Some(result) = self.exact_match(key) {
return Some(result as _);
}
let mut search_results = self
.fonts
.iter()
Expand All @@ -163,6 +175,19 @@ impl FontKit {
}
None
}

pub fn keys(&self) -> Vec<FontKey> {
self.fonts
.iter()
.flat_map(|i| {
i.value()
.variants()
.iter()
.map(|i| i.key.clone())
.collect::<Vec<_>>()
})
.collect()
}
}

#[cfg(feature = "parse")]
Expand Down Expand Up @@ -206,7 +231,7 @@ fn load_font_from_path(path: impl AsRef<std::path::Path>) -> Option<Font> {
}
};
font.set_path(path.to_path_buf());
// println!("{:?}", font.names);
// println!("{:?}", font.variants());
font.unload();
Some(font)
}
Expand Down
2 changes: 1 addition & 1 deletion src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ impl TextMetrics {
}
}

pub(crate) fn has_missing(&self) -> bool {
pub fn has_missing(&self) -> bool {
self.positions
.read()
.map(|p| p.iter().any(|c| c.metrics.missing))
Expand Down
2 changes: 1 addition & 1 deletion tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,6 @@ pub fn test_complex_text_wrap() -> Result<(), Error> {
});
area.unwrap_text();
area.wrap_text(64.4)?;
assert_eq!(area.value_string(), "商家\n热卖\n1234\n567\n8");
assert_eq!(area.value_string(), "商家\n热卖\n123\n456\n78");
Ok(())
}

0 comments on commit 380f300

Please sign in to comment.