From 779ee853ec98ef8c887aa19bf6e7db24d74e3781 Mon Sep 17 00:00:00 2001 From: Rod S Date: Thu, 25 Jul 2024 10:28:39 -0300 Subject: [PATCH 1/2] First swing at a readme update --- Cargo.toml | 10 ++++----- README.md | 64 +++++++++++++++++++++++++----------------------------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 76a03cc..2759513 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,11 @@ [package] -name = "rustybuzz" -version = "0.17.0" -authors = ["Yevhenii Reizner "] +name = "harfruzz" +version = "0.0.1" edition = "2021" description = "A complete harfbuzz shaping algorithm port to Rust." -documentation = "https://docs.rs/rustybuzz/" +documentation = "https://docs.rs/harfruzz/" readme = "README.md" -repository = "https://github.com/RazrFalcon/rustybuzz" +repository = "https://github.com/harfbuzz/harfruzz" license = "MIT" keywords = ["text", "shaping", "opentype", "truetype"] categories = ["text-processing"] @@ -27,6 +26,7 @@ skrifa = { git = "https://github.com/googlefonts/fontations", rev = "2eb4b1c" } wasmi = { version = "0.34.0", optional = true } log = "0.4.22" +# TODO: remove entirely [dependencies.ttf-parser] git = "https://github.com/RazrFalcon/ttf-parser" rev = "85c1ff2" diff --git a/README.md b/README.md index b50f5a7..6e8512d 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,41 @@ -# rustybuzz -![Build Status](https://github.com/RazrFalcon/rustybuzz/workflows/Rust/badge.svg) -[![Crates.io](https://img.shields.io/crates/v/rustybuzz.svg)](https://crates.io/crates/rustybuzz) -[![Documentation](https://docs.rs/rustybuzz/badge.svg)](https://docs.rs/rustybuzz) +# harfruzz +![Build Status](https://github.com/harfbuzz/harfruzz/workflows/Rust/badge.svg) +[![Crates.io](https://img.shields.io/crates/v/harfruzz.svg)](https://crates.io/crates/harfruzz) +[![Documentation](https://docs.rs/harfruzz/badge.svg)](https://docs.rs/harfruzz) + +`harfruzz` is a fork of [`rustybuzz`](https://docs.rs/rustybuzz) to explore porting from `ttf-parser` to +[`read-fonts`](https://docs.rs/read-fonts) to avoid shipping (and maintaining) +multiple implementations of core font parsing for [`skrifa`](https://docs.rs/skrifa) consumers. +Further context in https://github.com/googlefonts/fontations/issues/956. `rustybuzz` is a complete [harfbuzz](https://github.com/harfbuzz/harfbuzz)'s shaping algorithm port to Rust. -Matches `harfbuzz` v9.0.0 +Matches `harfbuzz` [v9.0.0](https://github.com/harfbuzz/harfbuzz/releases/tag/9.0.0). ## Why? -Because you can add `rustybuzz = "*"` to your project and it just works. -No need for a C++ compiler. No need to configure anything. No need to link to system libraries. +https://github.com/googlefonts/oxidize outlines Google Fonts motivations to try to migrate font +production and consumption to Rust. ## Conformance -rustybuzz passes nearly all of harfbuzz shaping tests (2221 out of 2252 to be more precise). -So it's mostly identical, but there are still some tiny edge-cases which -are not implemented yet or cannot be implemented at all. +The following conformance issues need to be fixed: + +* harfruzz does not yet fully pass the harfbuzz shaping or fuzzing tests +* Malformed fonts will cause an error + * HarfBuzz uses fallback/dummy shaper in this case +* No Arabic fallback shaper (requires subsetting) +* `avar2` as well as other parts of the boring-expansion-spec are not supported yet. ## Major changes -- Subsetting removed. -- TrueType parsing is completely handled by the - [ttf-parser](https://github.com/RazrFalcon/ttf-parser). - And while the parsing algorithm is very different, it's not better or worse, just different. -- Malformed fonts will cause an error. HarfBuzz uses fallback/dummy shaper in this case. - No font size property. Shaping is always using UnitsPerEm. You should scale the result manually. - Most of the TrueType and Unicode handling code was moved into separate crates. -- rustybuzz doesn't interact with any system libraries and must produce exactly the same +- harfruzz doesn't interact with any system libraries and must produce exactly the same results on all OS'es and targets. - `mort` table is not supported, since it's deprecated by Apple. -- No Arabic fallback shaper, since it requires subsetting. - No `graphite` library support. -- `avar2` as well as other parts of the boring-expansion-spec are not supported yet. ## Performance @@ -43,32 +45,26 @@ See [benches/README.md](./benches/README.md) for details. ## Notes about the port -rustybuzz is not a faithful port. +harfruzz is not a faithful port. -harfbuzz can roughly be split into 6 parts: shaping, subsetting, TrueType parsing, -Unicode routines, custom containers and utilities (harfbuzz doesn't use C++ std) -and glue for system/3rd party libraries. In the mean time, rustybuzz contains only shaping. -All of the TrueType parsing was moved to the [ttf-parser](https://github.com/RazrFalcon/ttf-parser). -Subsetting was removed. Unicode code was mostly moved to external crates. -We don't need custom containers because Rust's std is good enough. -And we do not use any non Rust libraries, so no glue code either. +harfbuzz (C++ edition) can roughly be split into 6 parts: -In the end, we still have around 23 KLOC. While harfbuzz is around 80 KLOC. +1. shaping, handled by harfruzz +1. subsetting, (`hb-subset`) moves to a standalone crate, [klippa](https://github.com/googlefonts/fontations/tree/main/klippa) +1. TrueType parsing, handled by [`read-fonts`](https://docs.rs/read-fonts) +1. Unicode routines, migrated to external crates +1. custom containers and utilities (harfbuzz doesn't use C++ std), reimplemented in [`fontations`](https://github.com/googlefonts/fontations) where appropriate (e.g. int set) +1. glue for system/3rd party libraries, just gone ## Lines of code -As mentioned above, rustybuzz has around 23 KLOC. But this is not strictly true, -because there are a lot of auto-generated data tables. - -You can find the "real" code size using: +You can find the "real" code size (eliminating generated code) using: ```sh tokei --exclude hb/unicode_norm.rs --exclude hb/ot_shaper_vowel_constraints.rs \ --exclude '*_machine.rs' --exclude '*_table.rs' src ``` -Which gives us around 17 KLOC, which is still a lot. - ## Future work Since the port is finished, there is not much to do other than syncing it with @@ -121,6 +117,6 @@ But except that, there are no `unsafe` in this library and in most of its depend ## License -`rustybuzz` is licensed under the **MIT**. +`harfruzz` is licensed under the **MIT** license. `harfbuzz` is [licensed](https://github.com/harfbuzz/harfbuzz/blob/master/COPYING) under the **Old MIT** From 31d96392d6e90478abc1c25b8d3856db71bf9ba2 Mon Sep 17 00:00:00 2001 From: Rod S Date: Thu, 25 Jul 2024 10:37:39 -0300 Subject: [PATCH 2/2] s/rustybuzz/harfruzz/g --- examples/shape.rs | 52 +++++++++++++++++++++---------------------- src/hb/face.rs | 2 +- src/hb/shape_wasm.rs | 6 ++--- tests/shaping/main.rs | 42 +++++++++++++++++----------------- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/examples/shape.rs b/examples/shape.rs index 1faf57c..d968cd9 100644 --- a/examples/shape.rs +++ b/examples/shape.rs @@ -44,16 +44,16 @@ struct Args { font_file: Option, face_index: u32, font_ptem: Option, - variations: Vec, + variations: Vec, text: Option, text_file: Option, unicodes: Option, - direction: Option, - language: rustybuzz::Language, - script: Option, + direction: Option, + language: harfruzz::Language, + script: Option, utf8_clusters: bool, - cluster_level: rustybuzz::BufferClusterLevel, - features: Vec, + cluster_level: harfruzz::BufferClusterLevel, + features: Vec, no_glyph_names: bool, no_positions: bool, no_advances: bool, @@ -145,7 +145,7 @@ fn main() { } let font_data = std::fs::read(font_path).unwrap(); - let mut face = rustybuzz::Face::from_slice(&font_data, args.face_index).unwrap(); + let mut face = harfruzz::Face::from_slice(&font_data, args.face_index).unwrap(); face.set_points_per_em(args.font_ptem); @@ -175,7 +175,7 @@ fn main() { }; for text in lines { - let mut buffer = rustybuzz::UnicodeBuffer::new(); + let mut buffer = harfruzz::UnicodeBuffer::new(); buffer.push_str(&text); if let Some(d) = args.direction { @@ -194,31 +194,31 @@ fn main() { buffer.reset_clusters(); } - let glyph_buffer = rustybuzz::shape(&face, &args.features, buffer); + let glyph_buffer = harfruzz::shape(&face, &args.features, buffer); - let mut format_flags = rustybuzz::SerializeFlags::default(); + let mut format_flags = harfruzz::SerializeFlags::default(); if args.no_glyph_names { - format_flags |= rustybuzz::SerializeFlags::NO_GLYPH_NAMES; + format_flags |= harfruzz::SerializeFlags::NO_GLYPH_NAMES; } if args.no_clusters || args.ned { - format_flags |= rustybuzz::SerializeFlags::NO_CLUSTERS; + format_flags |= harfruzz::SerializeFlags::NO_CLUSTERS; } if args.no_positions { - format_flags |= rustybuzz::SerializeFlags::NO_POSITIONS; + format_flags |= harfruzz::SerializeFlags::NO_POSITIONS; } if args.no_advances || args.ned { - format_flags |= rustybuzz::SerializeFlags::NO_ADVANCES; + format_flags |= harfruzz::SerializeFlags::NO_ADVANCES; } if args.show_extents { - format_flags |= rustybuzz::SerializeFlags::GLYPH_EXTENTS; + format_flags |= harfruzz::SerializeFlags::GLYPH_EXTENTS; } if args.show_flags { - format_flags |= rustybuzz::SerializeFlags::GLYPH_FLAGS; + format_flags |= harfruzz::SerializeFlags::GLYPH_FLAGS; } println!("{}", glyph_buffer.serialize(&face, format_flags)); @@ -239,39 +239,39 @@ fn parse_unicodes(s: &str) -> Result { Ok(text) } -fn parse_features(s: &str) -> Result, String> { +fn parse_features(s: &str) -> Result, String> { let mut features = Vec::new(); for f in s.split(',') { - features.push(rustybuzz::Feature::from_str(&f)?); + features.push(harfruzz::Feature::from_str(&f)?); } Ok(features) } -fn parse_variations(s: &str) -> Result, String> { +fn parse_variations(s: &str) -> Result, String> { let mut variations = Vec::new(); for v in s.split(',') { - variations.push(rustybuzz::Variation::from_str(&v)?); + variations.push(harfruzz::Variation::from_str(&v)?); } Ok(variations) } -fn parse_cluster(s: &str) -> Result { +fn parse_cluster(s: &str) -> Result { match s { - "0" => Ok(rustybuzz::BufferClusterLevel::MonotoneGraphemes), - "1" => Ok(rustybuzz::BufferClusterLevel::MonotoneCharacters), - "2" => Ok(rustybuzz::BufferClusterLevel::Characters), + "0" => Ok(harfruzz::BufferClusterLevel::MonotoneGraphemes), + "1" => Ok(harfruzz::BufferClusterLevel::MonotoneCharacters), + "2" => Ok(harfruzz::BufferClusterLevel::Characters), _ => Err(format!("invalid cluster level")), } } -fn system_language() -> rustybuzz::Language { +fn system_language() -> harfruzz::Language { unsafe { libc::setlocale(libc::LC_ALL, b"\0" as *const _ as *const i8); let s = libc::setlocale(libc::LC_CTYPE, std::ptr::null()); let s = std::ffi::CStr::from_ptr(s); let s = s.to_str().expect("locale must be ASCII"); - rustybuzz::Language::from_str(s).unwrap() + harfruzz::Language::from_str(s).unwrap() } } diff --git a/src/hb/face.rs b/src/hb/face.rs index 08bd26e..3be499b 100644 --- a/src/hb/face.rs +++ b/src/hb/face.rs @@ -296,7 +296,7 @@ impl<'a> hb_font_t<'a> { bbox = glyf.bbox(glyph); } - // See https://github.com/RazrFalcon/rustybuzz/pull/98#issuecomment-1948430785 + // See https://github.com/RazrFalcon/harfruzz/pull/98#issuecomment-1948430785 if self.ttfp_face.tables().glyf.is_some() && bbox.is_none() { // Empty glyph; zero extents. return true; diff --git a/src/hb/shape_wasm.rs b/src/hb/shape_wasm.rs index 249553a..9363a2f 100644 --- a/src/hb/shape_wasm.rs +++ b/src/hb/shape_wasm.rs @@ -134,7 +134,7 @@ fn font_get_glyph(caller: Caller<'_, ShapingData>, _font: u32, codepoint: u32, u // fn font_get_scale(font: u32, x_scale: *mut i32, y_scale: *mut i32); // Returns the scale of the current font. fn font_get_scale(mut caller: Caller<'_, ShapingData>, _font: u32, x_scale: u32, y_scale: u32) { - // Return upem as rustybuzz has no scale. + // Return upem as harfruzz has no scale. let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); let upem = caller.data().font.units_per_em(); @@ -550,8 +550,8 @@ fn shape_with( .unwrap_or_default() .to_string_lossy(); - if !(shaper.eq_ignore_ascii_case("ot") || shaper.eq_ignore_ascii_case("rustybuzz")) { - log::warn!("Only ot shaper is available in rustybuzz."); + if !(shaper.eq_ignore_ascii_case("ot") || shaper.eq_ignore_ascii_case("harfruzz")) { + log::warn!("Only ot shaper is available in harfruzz."); return 0; } diff --git a/tests/shaping/main.rs b/tests/shaping/main.rs index 8918e6f..4ee124b 100644 --- a/tests/shaping/main.rs +++ b/tests/shaping/main.rs @@ -8,19 +8,19 @@ mod wasm; use std::str::FromStr; -use rustybuzz::BufferFlags; +use harfruzz::BufferFlags; struct Args { face_index: u32, font_ptem: Option, variations: Vec, - direction: Option, - language: Option, - script: Option, + direction: Option, + language: Option, + script: Option, #[allow(dead_code)] remove_default_ignorables: bool, unsafe_to_concat: bool, - cluster_level: rustybuzz::BufferClusterLevel, + cluster_level: harfruzz::BufferClusterLevel, features: Vec, pre_context: Option, post_context: Option, @@ -78,11 +78,11 @@ fn parse_string_list(s: &str) -> Result, String> { Ok(s.split(',').map(|s| s.to_string()).collect()) } -fn parse_cluster(s: &str) -> Result { +fn parse_cluster(s: &str) -> Result { match s { - "0" => Ok(rustybuzz::BufferClusterLevel::MonotoneGraphemes), - "1" => Ok(rustybuzz::BufferClusterLevel::MonotoneCharacters), - "2" => Ok(rustybuzz::BufferClusterLevel::Characters), + "0" => Ok(harfruzz::BufferClusterLevel::MonotoneGraphemes), + "1" => Ok(harfruzz::BufferClusterLevel::MonotoneCharacters), + "2" => Ok(harfruzz::BufferClusterLevel::Characters), _ => Err(format!("invalid cluster level")), } } @@ -107,7 +107,7 @@ pub fn shape(font_path: &str, text: &str, options: &str) -> String { let font_data = std::fs::read(font_path).unwrap_or_else(|e| panic!("Could not read {}: {}", font_path, e)); - let mut face = rustybuzz::Face::from_slice(&font_data, args.face_index).unwrap(); + let mut face = harfruzz::Face::from_slice(&font_data, args.face_index).unwrap(); face.set_points_per_em(args.font_ptem); @@ -115,12 +115,12 @@ pub fn shape(font_path: &str, text: &str, options: &str) -> String { let variations: Vec<_> = args .variations .iter() - .map(|s| rustybuzz::Variation::from_str(s).unwrap()) + .map(|s| harfruzz::Variation::from_str(s).unwrap()) .collect(); face.set_variations(&variations); } - let mut buffer = rustybuzz::UnicodeBuffer::new(); + let mut buffer = harfruzz::UnicodeBuffer::new(); if let Some(pre_context) = args.pre_context { buffer.set_pre_context(&pre_context); } @@ -156,35 +156,35 @@ pub fn shape(font_path: &str, text: &str, options: &str) -> String { let mut features = Vec::new(); for feature_str in args.features { - let feature = rustybuzz::Feature::from_str(&feature_str).unwrap(); + let feature = harfruzz::Feature::from_str(&feature_str).unwrap(); features.push(feature); } - let glyph_buffer = rustybuzz::shape(&face, &features, buffer); + let glyph_buffer = harfruzz::shape(&face, &features, buffer); - let mut format_flags = rustybuzz::SerializeFlags::default(); + let mut format_flags = harfruzz::SerializeFlags::default(); if args.no_glyph_names { - format_flags |= rustybuzz::SerializeFlags::NO_GLYPH_NAMES; + format_flags |= harfruzz::SerializeFlags::NO_GLYPH_NAMES; } if args.no_clusters || args.ned { - format_flags |= rustybuzz::SerializeFlags::NO_CLUSTERS; + format_flags |= harfruzz::SerializeFlags::NO_CLUSTERS; } if args.no_positions { - format_flags |= rustybuzz::SerializeFlags::NO_POSITIONS; + format_flags |= harfruzz::SerializeFlags::NO_POSITIONS; } if args.no_advances || args.ned { - format_flags |= rustybuzz::SerializeFlags::NO_ADVANCES; + format_flags |= harfruzz::SerializeFlags::NO_ADVANCES; } if args.show_extents { - format_flags |= rustybuzz::SerializeFlags::GLYPH_EXTENTS; + format_flags |= harfruzz::SerializeFlags::GLYPH_EXTENTS; } if args.show_flags { - format_flags |= rustybuzz::SerializeFlags::GLYPH_FLAGS; + format_flags |= harfruzz::SerializeFlags::GLYPH_FLAGS; } glyph_buffer.serialize(&face, format_flags)