mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
Compare commits
2 Commits
f9cfd05af8
...
harfrust-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
775a292b79 | ||
|
|
5e3358b85a |
15
Cargo.lock
generated
15
Cargo.lock
generated
@@ -2821,6 +2821,7 @@ dependencies = [
|
||||
"fontsan",
|
||||
"freetype-sys",
|
||||
"harfbuzz-sys",
|
||||
"harfrust",
|
||||
"ipc-channel",
|
||||
"itertools 0.14.0",
|
||||
"libc",
|
||||
@@ -3785,6 +3786,19 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "harfrust"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92c020db12c71d8a12a3fe7607873cade3a01a6287e29d540c8723276221b9d8"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bytemuck",
|
||||
"core_maths",
|
||||
"read-fonts",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
@@ -7129,6 +7143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"core_maths",
|
||||
"font-types",
|
||||
]
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ gstreamer-gl-sys = "0.24"
|
||||
gstreamer-sys = "0.24"
|
||||
gstreamer-video = "0.24"
|
||||
harfbuzz-sys = "0.6.1"
|
||||
harfrust = "0.3.2"
|
||||
headers = "0.4"
|
||||
hitrace = "0.1.5"
|
||||
html5ever = "0.36.1"
|
||||
|
||||
@@ -14,6 +14,9 @@ test = true
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
default = ["harfrust", "harfbuzz"]
|
||||
harfbuzz = ["dep:harfbuzz-sys"]
|
||||
harfrust = ["dep:harfrust"]
|
||||
tracing = ["dep:tracing"]
|
||||
|
||||
[dependencies]
|
||||
@@ -25,7 +28,8 @@ euclid = { workspace = true }
|
||||
fonts_traits = { workspace = true }
|
||||
fontsan = { git = "https://github.com/servo/fontsan" }
|
||||
# FIXME (#34517): macOS only needs this when building libservo without `--features media-gstreamer`
|
||||
harfbuzz-sys = { workspace = true, features = ["bundled"] }
|
||||
harfbuzz-sys = { workspace = true, optional = true, features = ["bundled"] }
|
||||
harfrust = { workspace = true, optional = true }
|
||||
ipc-channel = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
|
||||
@@ -881,12 +881,23 @@ impl FontFamilyDescriptor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct FontBaseline {
|
||||
pub ideographic_baseline: f32,
|
||||
pub alphabetic_baseline: f32,
|
||||
pub hanging_baseline: f32,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for FontBaseline {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} {} {}",
|
||||
self.ideographic_baseline, self.alphabetic_baseline, self.hanging_baseline
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a mapping array `mapping` and a value, map that value onto
|
||||
/// the value specified by the array. For instance, for FontConfig
|
||||
/// values of weights, we would map these onto the CSS [0..1000] range
|
||||
|
||||
@@ -91,10 +91,12 @@ impl FallbackFontSelectionOptions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "harfbuzz")]
|
||||
pub(crate) fn float_to_fixed(before: usize, f: f64) -> i32 {
|
||||
((1i32 << before) as f64 * f) as i32
|
||||
}
|
||||
|
||||
#[cfg(feature = "harfbuzz")]
|
||||
pub(crate) fn fixed_to_float(before: usize, f: i32) -> f64 {
|
||||
f as f64 * 1.0f64 / ((1i32 << before) as f64)
|
||||
}
|
||||
|
||||
113
components/fonts/shapers/both.rs
Normal file
113
components/fonts/shapers/both.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use std::io::Write as _;
|
||||
|
||||
use app_units::Au;
|
||||
|
||||
use super::harfbuzz::ShapedGlyphData as HarfBuzzShapedGlyphData;
|
||||
use super::harfrust::ShapedGlyphData as HarfRustShapedGlyphData;
|
||||
use super::{HarfBuzzShapedGlyphData as THarfBuzzShapedGlyphData, HarfBuzzShaper, HarfRustShaper};
|
||||
use crate::{Font, FontBaseline, GlyphStore, ShapingOptions};
|
||||
|
||||
pub(crate) struct ShapedGlyphData {
|
||||
buzz: HarfBuzzShapedGlyphData,
|
||||
rust: HarfRustShapedGlyphData,
|
||||
}
|
||||
|
||||
pub(crate) struct Shaper {
|
||||
buzz: HarfBuzzShaper,
|
||||
rust: HarfRustShaper,
|
||||
}
|
||||
|
||||
impl Shaper {
|
||||
pub(crate) fn new(font: &Font) -> Self {
|
||||
Self {
|
||||
buzz: HarfBuzzShaper::new(font),
|
||||
rust: HarfRustShaper::new(font),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn shaped_glyph_data(
|
||||
&self,
|
||||
text: &str,
|
||||
options: &ShapingOptions,
|
||||
) -> ShapedGlyphData {
|
||||
ShapedGlyphData {
|
||||
buzz: self.buzz.shaped_glyph_data(text, options),
|
||||
rust: self.rust.shaped_glyph_data(text, options),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
|
||||
let glyph_data = self.shaped_glyph_data(text, options);
|
||||
let font = self.buzz.font();
|
||||
|
||||
let equal = shape_data_eq(&glyph_data.buzz, &glyph_data.rust);
|
||||
if !equal {
|
||||
println!("SHAPING DATA DIFFERENT:");
|
||||
println!("Input text:");
|
||||
println!("{text}");
|
||||
println!("Buzz:");
|
||||
log_shape_data(&glyph_data.buzz);
|
||||
println!("Rust:");
|
||||
log_shape_data(&glyph_data.rust);
|
||||
println!("========================");
|
||||
}
|
||||
|
||||
super::shape_text_harfbuzz(&glyph_data.rust, font, text, options, glyphs);
|
||||
}
|
||||
|
||||
pub fn baseline(&self) -> Option<FontBaseline> {
|
||||
let buzz_baseline = self.buzz.baseline();
|
||||
let rust_baseline = self.rust.baseline();
|
||||
|
||||
// Debug log
|
||||
let equal = buzz_baseline == rust_baseline;
|
||||
let eq_word = if equal { "same" } else { "diff" };
|
||||
println!(
|
||||
"BL ({eq_word}) C: {:?} | R: {:?}",
|
||||
buzz_baseline, rust_baseline
|
||||
);
|
||||
|
||||
rust_baseline
|
||||
}
|
||||
}
|
||||
|
||||
fn shape_data_eq(a: &impl THarfBuzzShapedGlyphData, b: &impl THarfBuzzShapedGlyphData) -> bool {
|
||||
if a.len() != b.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut a_y_pos = Au::new(0);
|
||||
let mut b_y_pos = Au::new(0);
|
||||
for i in 0..a.len() {
|
||||
if a.byte_offset_of_glyph(i) != b.byte_offset_of_glyph(i) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if a.entry_for_glyph(i, &mut a_y_pos) != b.entry_for_glyph(i, &mut b_y_pos) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn log_shape_data(data: &impl THarfBuzzShapedGlyphData) {
|
||||
let mut out = std::io::stdout().lock();
|
||||
writeln!(&mut out, "len: {}", data.len()).unwrap();
|
||||
writeln!(&mut out, "offsets:").unwrap();
|
||||
for i in 0..data.len() {
|
||||
write!(&mut out, "{} ", data.byte_offset_of_glyph(i)).unwrap();
|
||||
}
|
||||
writeln!(&mut out).unwrap();
|
||||
writeln!(&mut out, "entries:").unwrap();
|
||||
let mut y_pos = Au::new(0);
|
||||
for i in 0..data.len() {
|
||||
let entry = data.entry_for_glyph(i, &mut y_pos);
|
||||
write!(&mut out, "cp: {} ad: {} ", entry.codepoint, entry.advance.0).unwrap();
|
||||
match entry.offset {
|
||||
Some(offset) => write!(&mut out, "Some(x:{}, y:{})", offset.x.0, offset.y.0).unwrap(),
|
||||
None => write!(&mut out, "None").unwrap(),
|
||||
};
|
||||
writeln!(&mut out).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,11 @@ impl Shaper {
|
||||
}
|
||||
|
||||
/// Calculate the layout metrics associated with the given text with the [`Shaper`]s font.
|
||||
fn shaped_glyph_data(&self, text: &str, options: &ShapingOptions) -> ShapedGlyphData {
|
||||
pub(crate) fn shaped_glyph_data(
|
||||
&self,
|
||||
text: &str,
|
||||
options: &ShapingOptions,
|
||||
) -> ShapedGlyphData {
|
||||
unsafe {
|
||||
let hb_buffer: *mut hb_buffer_t = hb_buffer_create();
|
||||
hb_buffer_set_direction(
|
||||
@@ -268,7 +272,7 @@ impl Shaper {
|
||||
}
|
||||
}
|
||||
|
||||
fn font(&self) -> &Font {
|
||||
pub(crate) fn font(&self) -> &Font {
|
||||
unsafe { &(*self.font) }
|
||||
}
|
||||
|
||||
|
||||
254
components/fonts/shapers/harfrust.rs
Normal file
254
components/fonts/shapers/harfrust.rs
Normal file
@@ -0,0 +1,254 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use app_units::Au;
|
||||
use euclid::default::Point2D;
|
||||
use harfrust::{
|
||||
Feature, FontRef as HarfRustFontRef, GlyphBuffer, Script, ShaperData, ShaperInstance, Tag,
|
||||
UnicodeBuffer, Variation,
|
||||
};
|
||||
use num_traits::Zero as _;
|
||||
use read_fonts::TableProvider;
|
||||
use read_fonts::types::BigEndian;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use super::{HarfBuzzShapedGlyphData, ShapedGlyphEntry, unicode_script_to_iso15924_tag};
|
||||
use crate::{Font, FontBaseline, FontData, GlyphStore, ShapingFlags, ShapingOptions};
|
||||
|
||||
/// Convert a `webrender_api::FontVariation` to a `harfrust::Variation`
|
||||
fn wr_variation_to_hr_varation(wr_variation: webrender_api::FontVariation) -> harfrust::Variation {
|
||||
Variation {
|
||||
tag: Tag::from_u32(wr_variation.tag),
|
||||
value: wr_variation.value,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ShapedGlyphData {
|
||||
data: GlyphBuffer,
|
||||
scale: f64,
|
||||
}
|
||||
|
||||
impl HarfBuzzShapedGlyphData for ShapedGlyphData {
|
||||
fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
fn byte_offset_of_glyph(&self, i: usize) -> u32 {
|
||||
self.data.glyph_infos()[i].cluster
|
||||
}
|
||||
|
||||
fn entry_for_glyph(&self, i: usize, y_pos: &mut app_units::Au) -> super::ShapedGlyphEntry {
|
||||
let glyph_info_i = self.data.glyph_infos()[i];
|
||||
let pos_info_i = self.data.glyph_positions()[i];
|
||||
let x_offset = Au::from_f64_px(pos_info_i.x_offset as f64 * self.scale);
|
||||
let y_offset = Au::from_f64_px(pos_info_i.y_offset as f64 * self.scale);
|
||||
let x_advance = Au::from_f64_px(pos_info_i.x_advance as f64 * self.scale);
|
||||
let y_advance = Au::from_f64_px(pos_info_i.y_advance as f64 * self.scale);
|
||||
|
||||
let offset = if x_offset.is_zero() && y_offset.is_zero() && y_advance.is_zero() {
|
||||
None
|
||||
} else {
|
||||
// adjust the pen..
|
||||
if y_advance > Au::zero() {
|
||||
*y_pos -= y_advance;
|
||||
}
|
||||
|
||||
Some(Point2D::new(x_offset, *y_pos - y_offset))
|
||||
};
|
||||
|
||||
ShapedGlyphEntry {
|
||||
codepoint: glyph_info_i.glyph_id,
|
||||
advance: x_advance,
|
||||
offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Shaper {
|
||||
font: *const Font,
|
||||
/// The raw byte data of the font
|
||||
font_data: FontData,
|
||||
/// The index of a font in it's collection (.ttc)
|
||||
/// If the font file is not a collection then this is 0
|
||||
font_index: u32,
|
||||
// Used for scaling HarfRust's output
|
||||
scale: f64,
|
||||
|
||||
/// Pre-computed data for shaping a font
|
||||
shaper_data: ShaperData,
|
||||
|
||||
/// Pre-computed data for shaping a variable font with a particular set of variations.
|
||||
/// If there are no variations then we don't create a ShaperInstance.
|
||||
shaper_instance: Option<ShaperInstance>,
|
||||
}
|
||||
|
||||
// `Font` and `FontData` are both threadsafe, so we can make the data structures here as thread-safe as well.
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl Sync for Shaper {}
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl Send for Shaper {}
|
||||
|
||||
impl Shaper {
|
||||
pub(crate) fn new(font: &Font) -> Self {
|
||||
let raw_font = font
|
||||
.font_data_and_index()
|
||||
.expect("Error creating shaper for font");
|
||||
let font_data = raw_font.data.clone();
|
||||
let font_index = raw_font.index;
|
||||
|
||||
// Set points-per-em. if zero, performs no hinting in that direction
|
||||
let ppem = font.descriptor.pt_size.to_f64_px();
|
||||
let font_ref = read_fonts::FontRef::from_index(font_data.as_ref(), font_index).unwrap();
|
||||
let units_per_em = font_ref.head().unwrap().units_per_em();
|
||||
let scale = ppem / (units_per_em as f64);
|
||||
|
||||
// Create cached shaping data for the font
|
||||
let hr_font = HarfRustFontRef::from_index(font_data.as_ref(), font_index).unwrap();
|
||||
let shaper_data = ShaperData::new(&hr_font);
|
||||
|
||||
// If variable fonts are enabled and the font has variations, create a ShaperInstance to set on the shaper.
|
||||
let variations = font.variations();
|
||||
let shaper_instance =
|
||||
if servo_config::pref!(layout_variable_fonts_enabled) && !variations.is_empty() {
|
||||
let variations_iter = variations.iter().copied().map(wr_variation_to_hr_varation);
|
||||
Some(ShaperInstance::from_variations(&hr_font, variations_iter))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self {
|
||||
font: font as *const Font,
|
||||
font_data,
|
||||
font_index,
|
||||
scale,
|
||||
shaper_data,
|
||||
shaper_instance,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shaper {
|
||||
pub(crate) fn shaped_glyph_data(
|
||||
&self,
|
||||
text: &str,
|
||||
options: &crate::ShapingOptions,
|
||||
) -> ShapedGlyphData {
|
||||
let mut buffer = UnicodeBuffer::new();
|
||||
|
||||
// Set direction
|
||||
buffer.set_direction(if options.flags.contains(ShapingFlags::RTL_FLAG) {
|
||||
harfrust::Direction::RightToLeft
|
||||
} else {
|
||||
harfrust::Direction::LeftToRight
|
||||
});
|
||||
|
||||
// Set script
|
||||
let script_tag = Tag::from_u32(unicode_script_to_iso15924_tag(options.script));
|
||||
let script = Script::from_iso15924_tag(script_tag).unwrap();
|
||||
buffer.set_script(script);
|
||||
|
||||
// Push text
|
||||
buffer.push_str(text);
|
||||
|
||||
// Features
|
||||
let mut features = SmallVec::<[Feature; 2]>::new();
|
||||
if options
|
||||
.flags
|
||||
.contains(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG)
|
||||
{
|
||||
features.push(Feature::new(Tag::new(b"liga"), 0, ..));
|
||||
}
|
||||
if options
|
||||
.flags
|
||||
.contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
|
||||
{
|
||||
features.push(Feature::new(Tag::new(b"kern"), 0, ..));
|
||||
}
|
||||
|
||||
let hr_font =
|
||||
HarfRustFontRef::from_index(self.font_data.as_ref(), self.font_index).unwrap();
|
||||
let shaper = self
|
||||
.shaper_data
|
||||
.shaper(&hr_font)
|
||||
.instance(self.shaper_instance.as_ref())
|
||||
.build();
|
||||
|
||||
let glyph_buffer = shaper.shape(buffer, &features);
|
||||
|
||||
ShapedGlyphData {
|
||||
data: glyph_buffer,
|
||||
scale: self.scale,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) fn font(&self) -> &Font {
|
||||
// SAFETY: the font actually owns this shaper so it cannot have been dropped
|
||||
unsafe { &(*self.font) }
|
||||
}
|
||||
|
||||
pub(crate) fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
|
||||
let glyph_data = self.shaped_glyph_data(text, options);
|
||||
let font = self.font();
|
||||
super::shape_text_harfbuzz(&glyph_data, font, text, options, glyphs);
|
||||
}
|
||||
|
||||
pub(crate) fn baseline(&self) -> Option<crate::FontBaseline> {
|
||||
let font_ref =
|
||||
read_fonts::FontRef::from_index(self.font_data.as_ref(), self.font_index).unwrap();
|
||||
|
||||
// Load the horizontal axis of the BASE table
|
||||
let base_table = font_ref.base().ok()?;
|
||||
let horiz_axis = base_table.horiz_axis()?.ok()?;
|
||||
|
||||
// Get the index of each baseline tag in the tag list
|
||||
let tag_list = horiz_axis.base_tag_list()?.ok()?;
|
||||
let baseline_tags = tag_list.baseline_tags();
|
||||
let romn_tag_idx = baseline_tags
|
||||
.binary_search(&BigEndian::from(Tag::new(b"romn")))
|
||||
.ok();
|
||||
let hang_tag_idx = baseline_tags
|
||||
.binary_search(&BigEndian::from(Tag::new(b"hang")))
|
||||
.ok();
|
||||
let ideo_tag_idx = baseline_tags
|
||||
.binary_search(&BigEndian::from(Tag::new(b"ideo")))
|
||||
.ok();
|
||||
|
||||
// Bail early if none of the baseline tags exist in the tag list
|
||||
if romn_tag_idx.is_none() && hang_tag_idx.is_none() && ideo_tag_idx.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Find the DFLT (default) script record
|
||||
let script_list = horiz_axis.base_script_list().ok()?;
|
||||
let script_records = script_list.base_script_records();
|
||||
let default_script_record_idx = script_records
|
||||
.binary_search_by_key(&Tag::from_be_bytes(*b"DFLT"), |record| {
|
||||
record.base_script_tag()
|
||||
})
|
||||
.ok()?;
|
||||
let default_script_record = &script_records[default_script_record_idx];
|
||||
|
||||
// Lookup the baseline coordinates DFLT script record
|
||||
let base_script = default_script_record
|
||||
.base_script(script_list.offset_data())
|
||||
.ok()?;
|
||||
let base_script_values = base_script.base_values()?.ok()?;
|
||||
let base_coords = base_script_values.base_coords();
|
||||
|
||||
// Search for the baseline corresponding to a given baseline index
|
||||
let get_coord = |idx: usize| -> Option<f32> {
|
||||
base_coords
|
||||
.get(idx)
|
||||
.ok()
|
||||
.map(|coord| coord.coordinate() as f32 * self.scale as f32)
|
||||
};
|
||||
|
||||
Some(FontBaseline {
|
||||
ideographic_baseline: ideo_tag_idx.and_then(get_coord).unwrap_or(0.0),
|
||||
alphabetic_baseline: romn_tag_idx.and_then(get_coord).unwrap_or(0.0),
|
||||
hanging_baseline: hang_tag_idx.and_then(get_coord).unwrap_or(0.0),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,41 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
mod harfbuzz;
|
||||
use std::cmp;
|
||||
|
||||
use app_units::Au;
|
||||
use base::text::is_bidi_control;
|
||||
use euclid::default::Point2D;
|
||||
use fonts_traits::ByteIndex;
|
||||
pub(crate) use harfbuzz::Shaper;
|
||||
use log::debug;
|
||||
use num_traits::Zero as _;
|
||||
|
||||
const NO_GLYPH: i32 = -1;
|
||||
|
||||
use crate::{Font, GlyphData, GlyphId, GlyphStore, ShapingOptions, advance_for_shaped_glyph};
|
||||
|
||||
#[cfg(feature = "harfbuzz")]
|
||||
mod harfbuzz;
|
||||
#[cfg(feature = "harfbuzz")]
|
||||
pub(crate) use harfbuzz::Shaper as HarfBuzzShaper;
|
||||
|
||||
#[cfg(feature = "harfrust")]
|
||||
mod harfrust;
|
||||
#[cfg(feature = "harfrust")]
|
||||
pub(crate) use harfrust::Shaper as HarfRustShaper;
|
||||
|
||||
#[cfg(all(feature = "harfbuzz", feature = "harfrust"))]
|
||||
mod both;
|
||||
#[cfg(all(feature = "harfbuzz", feature = "harfrust"))]
|
||||
pub(crate) use BothShaper as Shaper;
|
||||
// Configure default shaper (actually used)
|
||||
#[cfg(all(feature = "harfbuzz", not(feature = "harfrust")))]
|
||||
pub(crate) use HarfBuzzShaper as Shaper;
|
||||
#[cfg(all(not(feature = "harfbuzz"), feature = "harfrust"))]
|
||||
pub(crate) use HarfRustShaper as Shaper;
|
||||
#[cfg(all(feature = "harfbuzz", feature = "harfrust"))]
|
||||
pub(crate) use both::Shaper as BothShaper;
|
||||
|
||||
const NO_GLYPH: i32 = -1;
|
||||
|
||||
/// Utility function to convert a `unicode_script::Script` enum into the corresponding `c_uint` tag that
|
||||
/// harfbuzz uses to represent unicode scipts.
|
||||
fn unicode_script_to_iso15924_tag(script: unicode_script::Script) -> u32 {
|
||||
@@ -31,6 +51,7 @@ fn unicode_script_to_iso15924_tag(script: unicode_script::Script) -> u32 {
|
||||
u32::from_be_bytes(bytes)
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct ShapedGlyphEntry {
|
||||
codepoint: GlyphId,
|
||||
advance: Au,
|
||||
|
||||
@@ -63,7 +63,7 @@ pub struct FontDataAndIndex {
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum FontDataError {
|
||||
FailedToLoad,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user