Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update readme and name #2

Merged
merged 2 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
[package]
name = "rustybuzz"
version = "0.17.0"
authors = ["Yevhenii Reizner <[email protected]>"]
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"]
Expand All @@ -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"
Expand Down
64 changes: 30 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has always been confusingly worded. The fallback shaper uses some of the font serialization code in HarfBuzz (which exists mainly to help with subsetting code) to build lookups on the fly, but it does not require subsetting the fonts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ty, will clarify in a followon


## 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

Expand All @@ -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
Expand Down Expand Up @@ -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**
52 changes: 26 additions & 26 deletions examples/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ struct Args {
font_file: Option<PathBuf>,
face_index: u32,
font_ptem: Option<f32>,
variations: Vec<rustybuzz::Variation>,
variations: Vec<harfruzz::Variation>,
text: Option<String>,
text_file: Option<PathBuf>,
unicodes: Option<String>,
direction: Option<rustybuzz::Direction>,
language: rustybuzz::Language,
script: Option<rustybuzz::Script>,
direction: Option<harfruzz::Direction>,
language: harfruzz::Language,
script: Option<harfruzz::Script>,
utf8_clusters: bool,
cluster_level: rustybuzz::BufferClusterLevel,
features: Vec<rustybuzz::Feature>,
cluster_level: harfruzz::BufferClusterLevel,
features: Vec<harfruzz::Feature>,
no_glyph_names: bool,
no_positions: bool,
no_advances: bool,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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 {
Expand All @@ -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));
Expand All @@ -239,39 +239,39 @@ fn parse_unicodes(s: &str) -> Result<String, String> {
Ok(text)
}

fn parse_features(s: &str) -> Result<Vec<rustybuzz::Feature>, String> {
fn parse_features(s: &str) -> Result<Vec<harfruzz::Feature>, 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<Vec<rustybuzz::Variation>, String> {
fn parse_variations(s: &str) -> Result<Vec<harfruzz::Variation>, 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<rustybuzz::BufferClusterLevel, String> {
fn parse_cluster(s: &str) -> Result<harfruzz::BufferClusterLevel, String> {
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()
}
}
2 changes: 1 addition & 1 deletion src/hb/face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/hb/shape_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;
}

Expand Down
Loading
Loading