Skip to content

Commit

Permalink
More.
Browse files Browse the repository at this point in the history
  • Loading branch information
RazrFalcon committed Feb 22, 2024
1 parent ced063b commit 4e9471c
Show file tree
Hide file tree
Showing 13 changed files with 358 additions and 340 deletions.
4 changes: 2 additions & 2 deletions src/hb/aat_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ pub const HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED: u8 = 0;
pub const HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE: u8 = 1;

/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN */
pub const HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN: u8 = 0; /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */
pub const HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN: u8 = 1; /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */
pub const HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN: u8 = 0; /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */
pub const HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN: u8 = 1; /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */
pub const HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON: u8 = 2;
pub const HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF: u8 = 3;

Expand Down
3 changes: 2 additions & 1 deletion src/hb/aat_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ impl hb_aat_map_builder_t {
// small-caps if necessary, so we need to check for that possibility.
// https://github.com/harfbuzz/harfbuzz/issues/2307
if mapping.aat_feature_type == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE
&& mapping.selector_to_enable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS
&& mapping.selector_to_enable
== HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS
{
feature = feat
.names
Expand Down
1 change: 1 addition & 0 deletions src/hb/machine_cursor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::ops::{Add, AddAssign, Sub, SubAssign};

// Similar to machine_index_t in harfbuzz, but Rust specific.
#[derive(Debug)]
pub struct MachineCursor<'a, T, F> {
data: &'a [T],
Expand Down
1 change: 1 addition & 0 deletions src/hb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod ot_layout_gpos_table;
mod ot_layout_gsub_table;
mod ot_layout_gsubgpos;
mod ot_map;
mod ot_shape;
mod ot_shape_complex;
mod ot_shape_complex_arabic;
mod ot_shape_complex_arabic_table;
Expand Down
323 changes: 323 additions & 0 deletions src/hb/ot_shape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
use super::aat_map;
use super::feature;
use super::ot_layout::TableIndex;
use super::ot_map::hb_ot_map_builder_t;
use super::ot_map::{hb_ot_map_t, FeatureFlags};
use super::ot_shape_complex::*;
use super::shape_plan::hb_ot_shape_plan_t;
use super::{hb_font_t, hb_tag_t};
use crate::{Direction, Feature, Language, Script};

pub struct hb_ot_shape_planner_t<'a> {
pub face: &'a hb_font_t<'a>,
pub direction: Direction,
pub script: Option<Script>,
pub ot_map: hb_ot_map_builder_t<'a>,
pub aat_map: aat_map::hb_aat_map_builder_t,
pub apply_morx: bool,
pub script_zero_marks: bool,
pub script_fallback_mark_positioning: bool,
pub shaper: &'static ComplexShaper,
}

impl<'a> hb_ot_shape_planner_t<'a> {
pub fn new(
face: &'a hb_font_t<'a>,
direction: Direction,
script: Option<Script>,
language: Option<&Language>,
) -> Self {
let ot_map = hb_ot_map_builder_t::new(face, script, language);
let aat_map = aat_map::hb_aat_map_builder_t::default();

let mut shaper = match script {
Some(script) => {
complex_categorize(script, direction, ot_map.chosen_script(TableIndex::GSUB))
}
None => &DEFAULT_SHAPER,
};

let script_zero_marks = shaper.zero_width_marks.is_some();
let script_fallback_mark_positioning = shaper.fallback_position;

// https://github.com/harfbuzz/harfbuzz/issues/2124
let apply_morx =
face.tables().morx.is_some() && (direction.is_horizontal() || face.gsub.is_none());

// https://github.com/harfbuzz/harfbuzz/issues/1528
if apply_morx && shaper as *const _ != &DEFAULT_SHAPER as *const _ {
shaper = &DUMBER_SHAPER;
}

hb_ot_shape_planner_t {
face,
direction,
script,
ot_map,
aat_map,
apply_morx,
script_zero_marks,
script_fallback_mark_positioning,
shaper,
}
}

pub fn collect_features(&mut self, user_features: &[Feature]) {
const COMMON_FEATURES: &[(hb_tag_t, FeatureFlags)] = &[
(feature::ABOVE_BASE_MARK_POSITIONING, FeatureFlags::GLOBAL),
(feature::BELOW_BASE_MARK_POSITIONING, FeatureFlags::GLOBAL),
(
feature::GLYPH_COMPOSITION_DECOMPOSITION,
FeatureFlags::GLOBAL,
),
(feature::LOCALIZED_FORMS, FeatureFlags::GLOBAL),
(
feature::MARK_POSITIONING,
FeatureFlags::GLOBAL_MANUAL_JOINERS,
),
(
feature::MARK_TO_MARK_POSITIONING,
FeatureFlags::GLOBAL_MANUAL_JOINERS,
),
(feature::REQUIRED_LIGATURES, FeatureFlags::GLOBAL),
];

const HORIZONTAL_FEATURES: &[(hb_tag_t, FeatureFlags)] = &[
(feature::CONTEXTUAL_ALTERNATES, FeatureFlags::GLOBAL),
(feature::CONTEXTUAL_LIGATURES, FeatureFlags::GLOBAL),
(feature::CURSIVE_POSITIONING, FeatureFlags::GLOBAL),
(feature::DISTANCES, FeatureFlags::GLOBAL),
(feature::KERNING, FeatureFlags::GLOBAL_HAS_FALLBACK),
(feature::STANDARD_LIGATURES, FeatureFlags::GLOBAL),
(
feature::REQUIRED_CONTEXTUAL_ALTERNATES,
FeatureFlags::GLOBAL,
),
];

let empty = FeatureFlags::empty();

self.ot_map
.enable_feature(feature::REQUIRED_VARIATION_ALTERNATES, empty, 1);
self.ot_map.add_gsub_pause(None);

match self.direction {
Direction::LeftToRight => {
self.ot_map
.enable_feature(feature::LEFT_TO_RIGHT_ALTERNATES, empty, 1);
self.ot_map
.enable_feature(feature::LEFT_TO_RIGHT_MIRRORED_FORMS, empty, 1);
}
Direction::RightToLeft => {
self.ot_map
.enable_feature(feature::RIGHT_TO_LEFT_ALTERNATES, empty, 1);
self.ot_map
.add_feature(feature::RIGHT_TO_LEFT_MIRRORED_FORMS, empty, 1);
}
_ => {}
}

// Automatic fractions.
self.ot_map.add_feature(feature::FRACTIONS, empty, 1);
self.ot_map.add_feature(feature::NUMERATORS, empty, 1);
self.ot_map.add_feature(feature::DENOMINATORS, empty, 1);

// Random!
self.ot_map.enable_feature(
feature::RANDOMIZE,
FeatureFlags::RANDOM,
hb_ot_map_t::MAX_VALUE,
);

// Tracking. We enable dummy feature here just to allow disabling
// AAT 'trak' table using features.
// https://github.com/harfbuzz/harfbuzz/issues/1303
self.ot_map
.enable_feature(hb_tag_t::from_bytes(b"trak"), FeatureFlags::HAS_FALLBACK, 1);

self.ot_map
.enable_feature(hb_tag_t::from_bytes(b"Harf"), empty, 1); // Considered required.
self.ot_map
.enable_feature(hb_tag_t::from_bytes(b"HARF"), empty, 1); // Considered discretionary.

if let Some(func) = self.shaper.collect_features {
func(self);
}

self.ot_map
.enable_feature(hb_tag_t::from_bytes(b"Buzz"), empty, 1); // Considered required.
self.ot_map
.enable_feature(hb_tag_t::from_bytes(b"BUZZ"), empty, 1); // Considered discretionary.

for &(tag, flags) in COMMON_FEATURES {
self.ot_map.add_feature(tag, flags, 1);
}

if self.direction.is_horizontal() {
for &(tag, flags) in HORIZONTAL_FEATURES {
self.ot_map.add_feature(tag, flags, 1);
}
} else {
// We only apply `vert` feature. See:
// https://github.com/harfbuzz/harfbuzz/commit/d71c0df2d17f4590d5611239577a6cb532c26528
// https://lists.freedesktop.org/archives/harfbuzz/2013-August/003490.html

// We really want to find a 'vert' feature if there's any in the font, no
// matter which script/langsys it is listed (or not) under.
// See various bugs referenced from:
// https://github.com/harfbuzz/harfbuzz/issues/63
self.ot_map
.enable_feature(feature::VERTICAL_WRITING, FeatureFlags::GLOBAL_SEARCH, 1);
}

for feature in user_features {
let flags = if feature.is_global() {
FeatureFlags::GLOBAL
} else {
empty
};
self.ot_map.add_feature(feature.tag, flags, feature.value);
}

if self.apply_morx {
for feature in user_features {
self.aat_map
.add_feature(self.face, feature.tag, feature.value);
}
}

if let Some(func) = self.shaper.override_features {
func(self);
}
}

pub fn compile(mut self, user_features: &[Feature]) -> hb_ot_shape_plan_t {
let ot_map = self.ot_map.compile();

let aat_map = if self.apply_morx {
self.aat_map.compile(self.face)
} else {
aat_map::hb_aat_map_t::default()
};

let frac_mask = ot_map.get_1_mask(feature::FRACTIONS);
let numr_mask = ot_map.get_1_mask(feature::NUMERATORS);
let dnom_mask = ot_map.get_1_mask(feature::DENOMINATORS);
let has_frac = frac_mask != 0 || (numr_mask != 0 && dnom_mask != 0);

let rtlm_mask = ot_map.get_1_mask(feature::RIGHT_TO_LEFT_MIRRORED_FORMS);
let has_vert = ot_map.get_1_mask(feature::VERTICAL_WRITING) != 0;

let horizontal = self.direction.is_horizontal();
let kern_tag = if horizontal {
feature::KERNING
} else {
feature::VERTICAL_KERNING
};
let kern_mask = ot_map.get_mask(kern_tag).0;
let requested_kerning = kern_mask != 0;
let trak_mask = ot_map.get_mask(hb_tag_t::from_bytes(b"trak")).0;
let requested_tracking = trak_mask != 0;

let has_gpos_kern = ot_map
.get_feature_index(TableIndex::GPOS, kern_tag)
.is_some();
let disable_gpos = self.shaper.gpos_tag.is_some()
&& self.shaper.gpos_tag != ot_map.chosen_script(TableIndex::GPOS);

// Decide who provides glyph classes. GDEF or Unicode.
let fallback_glyph_classes = !self
.face
.tables()
.gdef
.map_or(false, |table| table.has_glyph_classes());

// Decide who does substitutions. GSUB, morx, or fallback.
let apply_morx = self.apply_morx;

let mut apply_gpos = false;
let mut apply_kerx = false;
let mut apply_kern = false;

// Decide who does positioning. GPOS, kerx, kern, or fallback.
let has_kerx = self.face.tables().kerx.is_some();
let has_gsub = self.face.tables().gsub.is_some();
let has_gpos = !disable_gpos && self.face.tables().gpos.is_some();

// Prefer GPOS over kerx if GSUB is present;
// https://github.com/harfbuzz/harfbuzz/issues/3008
if has_kerx && !(has_gsub && has_gpos) {
apply_kerx = true;
} else if has_gpos {
apply_gpos = true;
}

if !apply_kerx && (!has_gpos_kern || !apply_gpos) {
if has_kerx {
apply_kerx = true;
} else if super::kerning::has_kerning(self.face) {
apply_kern = true;
}
}

let apply_fallback_kern = !(apply_gpos || apply_kerx || apply_kern);
let zero_marks = self.script_zero_marks
&& !apply_kerx
&& (!apply_kern || !super::kerning::has_machine_kerning(self.face));

let has_gpos_mark = ot_map.get_1_mask(feature::MARK_POSITIONING) != 0;

let mut adjust_mark_positioning_when_zeroing = !apply_gpos
&& !apply_kerx
&& (!apply_kern || !super::kerning::has_cross_kerning(self.face));

let fallback_mark_positioning =
adjust_mark_positioning_when_zeroing && self.script_fallback_mark_positioning;

// If we're using morx shaping, we cancel mark position adjustment because
// Apple Color Emoji assumes this will NOT be done when forming emoji sequences;
// https://github.com/harfbuzz/harfbuzz/issues/2967.
if apply_morx {
adjust_mark_positioning_when_zeroing = false;
}

// Currently we always apply trak.
let apply_trak = requested_tracking && self.face.tables().trak.is_some();

let mut plan = hb_ot_shape_plan_t {
direction: self.direction,
script: self.script,
shaper: self.shaper,
ot_map,
aat_map,
data: None,
frac_mask,
numr_mask,
dnom_mask,
rtlm_mask,
kern_mask,
trak_mask,
requested_kerning,
has_frac,
has_vert,
has_gpos_mark,
zero_marks,
fallback_glyph_classes,
fallback_mark_positioning,
adjust_mark_positioning_when_zeroing,
apply_gpos,
apply_kern,
apply_fallback_kern,
apply_kerx,
apply_morx,
apply_trak,
user_features: user_features.to_vec(),
};

if let Some(func) = self.shaper.create_data {
plan.data = Some(func(&plan));
}

plan
}
}
7 changes: 4 additions & 3 deletions src/hb/ot_shape_complex.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use alloc::boxed::Box;
use core::any::Any;

use super::ot_shape::*;
use crate::hb::buffer::hb_buffer_t;
use crate::hb::common::TagExt;
use crate::hb::ot_shape_normalize::{
hb_ot_shape_normalization_mode_t, hb_ot_shape_normalize_context_t,
HB_OT_SHAPE_NORMALIZATION_MODE_AUTO,
};
use crate::hb::shape_plan::{hb_ot_shape_plan_t, ShapePlanner};
use crate::hb::shape_plan::hb_ot_shape_plan_t;
use crate::hb::{hb_font_t, hb_tag_t, script, Direction, Script};

pub const MAX_COMBINING_MARKS: usize = 32;
Expand Down Expand Up @@ -49,12 +50,12 @@ pub const DUMBER_SHAPER: ComplexShaper = ComplexShaper {
pub struct ComplexShaper {
/// Called during `shape_plan()`.
/// Shapers should use plan.map to add their features and callbacks.
pub collect_features: Option<fn(&mut ShapePlanner)>,
pub collect_features: Option<fn(&mut hb_ot_shape_planner_t)>,

/// Called during `shape_plan()`.
/// Shapers should use plan.map to override features and add callbacks after
/// common features are added.
pub override_features: Option<fn(&mut ShapePlanner)>,
pub override_features: Option<fn(&mut hb_ot_shape_planner_t)>,

/// Called at the end of `shape_plan()`.
/// Whatever shapers return will be accessible through `plan.data()` later.
Expand Down
Loading

0 comments on commit 4e9471c

Please sign in to comment.