use core::ptr;
use crate::aat::map::*;
use crate::ot::layout::*;
use crate::ot::map::*;
use crate::ot::shaper::*;
use crate::shaper_font::LayoutData;
use crate::{Direction, Feature, Language, Script, Tag};
use super::plan::ShapePlan;
pub struct ShapePlanner<'a> {
pub layout: LayoutData<'a>,
pub direction: Direction,
pub script: Option<Script>,
pub language: Option<Language>,
pub ot_map: OtMapBuilder<'a>,
pub aat_map: AatMapBuilder,
pub apply_morx: bool,
pub script_zero_marks: bool,
pub script_fallback_position: bool,
pub shaper: &'static OtShaper,
}
impl<'a> ShapePlanner<'a> {
pub fn new(
layout: LayoutData<'a>,
direction: Direction,
script: Option<Script>,
language: Option<&Language>,
) -> Self {
let ot_map = OtMapBuilder::new(layout, script, language);
let aat_map = AatMapBuilder::new(language);
let mut shaper = match script {
Some(script) => categorize(
script,
direction,
ot_map.chosen_script(LayoutTableKind::Gsub),
),
None => &DEFAULT_SHAPER,
};
let script_zero_marks = shaper.zero_width_marks != ZeroWidthMarks::None;
let script_fallback_position = shaper.fallback_position;
let apply_morx = (layout.aat.morx.is_some() || layout.aat.mort.is_some())
&& (direction.is_horizontal() || layout.ot.gsub.is_none());
if apply_morx && !ptr::eq(ptr::from_ref(shaper), ptr::from_ref(&DEFAULT_SHAPER)) {
shaper = &DUMBER_SHAPER;
}
ShapePlanner {
layout,
direction,
script,
language: language.cloned(),
ot_map,
aat_map,
apply_morx,
script_zero_marks,
script_fallback_position,
shaper,
}
}
pub fn collect_features(&mut self, user_features: &[Feature]) {
static COMMON_FEATURES: &[(Tag, MapFeatureFlags)] = &[
(Tag::new(b"abvm"), F_GLOBAL),
(Tag::new(b"blwm"), F_GLOBAL),
(Tag::new(b"ccmp"), F_GLOBAL),
(Tag::new(b"locl"), F_GLOBAL),
(Tag::new(b"mark"), F_GLOBAL_MANUAL_JOINERS),
(Tag::new(b"mkmk"), F_GLOBAL_MANUAL_JOINERS),
(Tag::new(b"rlig"), F_GLOBAL),
];
static HORIZONTAL_FEATURES: &[(Tag, MapFeatureFlags)] = &[
(Tag::new(b"calt"), F_GLOBAL),
(Tag::new(b"clig"), F_GLOBAL),
(Tag::new(b"curs"), F_GLOBAL),
(Tag::new(b"dist"), F_GLOBAL),
(Tag::new(b"kern"), F_GLOBAL_HAS_FALLBACK),
(Tag::new(b"liga"), F_GLOBAL),
(Tag::new(b"rclt"), F_GLOBAL),
];
let empty = F_NONE;
self.ot_map.is_simple = true;
self.ot_map.enable_feature(Tag::new(b"rvrn"), empty, 1);
self.ot_map.add_gsub_pause(None);
match self.direction {
Direction::LeftToRight => {
self.ot_map.enable_feature(Tag::new(b"ltra"), empty, 1);
self.ot_map.enable_feature(Tag::new(b"ltrm"), empty, 1);
}
Direction::RightToLeft => {
self.ot_map.enable_feature(Tag::new(b"rtla"), empty, 1);
self.ot_map.add_feature(Tag::new(b"rtlm"), empty, 1);
}
_ => {}
}
self.ot_map.add_feature(Tag::new(b"frac"), empty, 1);
self.ot_map.add_feature(Tag::new(b"numr"), empty, 1);
self.ot_map.add_feature(Tag::new(b"dnom"), empty, 1);
self.ot_map
.enable_feature(Tag::new(b"rand"), F_RANDOM, OtMap::MAX_VALUE);
self.ot_map
.enable_feature(Tag::new(b"trak"), F_HAS_FALLBACK, 1);
self.ot_map.enable_feature(Tag::new(b"Harf"), empty, 1); self.ot_map.enable_feature(Tag::new(b"HARF"), empty, 1);
if let Some(func) = self.shaper.collect_features {
self.ot_map.is_simple = false;
func(self);
}
self.ot_map.enable_feature(Tag::new(b"Buzz"), empty, 1); self.ot_map.enable_feature(Tag::new(b"BUZZ"), empty, 1);
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 {
self.ot_map
.enable_feature(Tag::new(b"vert"), F_GLOBAL_SEARCH, 1);
}
if !user_features.is_empty() {
self.ot_map.is_simple = false;
}
for feature in user_features {
let flags = if feature.is_global() { F_GLOBAL } else { empty };
self.ot_map.add_feature(feature.tag, flags, feature.value);
}
if let Some(func) = self.shaper.override_features {
func(self);
}
}
pub fn compile(mut self, features: &[Feature]) -> ShapePlan {
let ot_map = self.ot_map.compile();
let mut aat_map = AatMap::default();
if self.apply_morx {
self.aat_map.compile(self.layout.aat, &mut aat_map);
}
let frac_mask = ot_map.get_1_mask(Tag::new(b"frac"));
let numr_mask = ot_map.get_1_mask(Tag::new(b"numr"));
let dnom_mask = ot_map.get_1_mask(Tag::new(b"dnom"));
let has_frac = frac_mask != 0 || (numr_mask != 0 && dnom_mask != 0);
let rtlm_mask = ot_map.get_1_mask(Tag::new(b"rtlm"));
let has_vert = ot_map.get_1_mask(Tag::new(b"vert")) != 0;
let horizontal = self.direction.is_horizontal();
let kern_tag = if horizontal {
Tag::new(b"kern")
} else {
Tag::new(b"vkrn")
};
let kern_mask = ot_map.get_mask(kern_tag).0;
let requested_kerning = kern_mask != 0;
let has_gpos_kern = ot_map
.get_feature_index(LayoutTableKind::Gpos, kern_tag)
.is_some();
let disable_gpos = self.shaper.gpos_tag.is_some()
&& self.shaper.gpos_tag != ot_map.chosen_script(LayoutTableKind::Gpos);
let fallback_glyph_classes = !has_glyph_classes(self.layout.ot);
let apply_morx = self.apply_morx;
let mut apply_gpos = false;
let mut apply_kerx = false;
let mut apply_kern = false;
let has_kerx = self.layout.aat.kerx.is_some();
let has_gsub = !apply_morx && self.layout.ot.gsub.is_some();
let has_gpos = !disable_gpos && self.layout.ot.gpos.is_some();
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 has_kerning(self.layout.aat) {
apply_kern = self.script_fallback_position;
}
}
let apply_fallback_kern = !(apply_gpos || apply_kerx || apply_kern);
let zero_marks = self.script_zero_marks
&& !apply_kerx
&& (!apply_kern || !has_machine_kerning(self.layout.aat));
let has_gpos_mark = ot_map.get_1_mask(Tag::new(b"mark")) != 0;
let mut adjust_mark_positioning_when_zeroing =
!apply_gpos && !apply_kerx && (!apply_kern || !has_cross_kerning(self.layout.aat));
let fallback_mark_positioning =
adjust_mark_positioning_when_zeroing && self.script_fallback_position;
if apply_morx {
adjust_mark_positioning_when_zeroing = false;
}
let apply_trak = self.layout.apply_trak;
let mut plan = ShapePlan {
direction: self.direction,
script: self.script,
language: self.language,
shaper: self.shaper,
ot_map,
aat_map,
data: None,
frac_mask,
numr_mask,
dnom_mask,
rtlm_mask,
kern_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: features.into(),
};
if let Some(func) = self.shaper.create_data {
plan.data = Some(func(&plan));
}
plan
}
}