use alloc::{boxed::Box, collections::btree_map::BTreeMap, rc::Rc, vec::Vec};
use allsorts_subset_browser::{
binary::read::ReadScope,
font_data::FontData,
gsub::RawGlyphFlags,
layout::{GDEFTable, LayoutCache, GPOS, GSUB},
tables::{
cmap::{owned::CmapSubtable as OwnedCmapSubtable, CmapSubtable},
glyf::{BoundingBox, GlyfRecord, GlyfTable, Glyph},
loca::{LocaOffsets, LocaTable},
FontTableProvider, HeadTable, HheaTable, MaxpTable,
},
};
use azul_core::app_resources::{
Advance, Anchor, FontMetrics, GlyphInfo, GlyphOrigin, Placement, RawGlyph, VariationSelector,
};
use tinyvec::tiny_vec;
pub fn get_font_metrics(font_bytes: &[u8], font_index: usize) -> FontMetrics {
#[derive(Default)]
struct Os2Info {
x_avg_char_width: i16,
us_weight_class: u16,
us_width_class: u16,
fs_type: u16,
y_subscript_x_size: i16,
y_subscript_y_size: i16,
y_subscript_x_offset: i16,
y_subscript_y_offset: i16,
y_superscript_x_size: i16,
y_superscript_y_size: i16,
y_superscript_x_offset: i16,
y_superscript_y_offset: i16,
y_strikeout_size: i16,
y_strikeout_position: i16,
s_family_class: i16,
panose: [u8; 10],
ul_unicode_range1: u32,
ul_unicode_range2: u32,
ul_unicode_range3: u32,
ul_unicode_range4: u32,
ach_vend_id: u32,
fs_selection: u16,
us_first_char_index: u16,
us_last_char_index: u16,
s_typo_ascender: Option<i16>,
s_typo_descender: Option<i16>,
s_typo_line_gap: Option<i16>,
us_win_ascent: Option<u16>,
us_win_descent: Option<u16>,
ul_code_page_range1: Option<u32>,
ul_code_page_range2: Option<u32>,
sx_height: Option<i16>,
s_cap_height: Option<i16>,
us_default_char: Option<u16>,
us_break_char: Option<u16>,
us_max_context: Option<u16>,
us_lower_optical_point_size: Option<u16>,
us_upper_optical_point_size: Option<u16>,
}
let scope = ReadScope::new(font_bytes);
let font_file = match scope.read::<FontData<'_>>() {
Ok(o) => o,
Err(_) => return FontMetrics::default(),
};
let provider = match font_file.table_provider(font_index) {
Ok(o) => o,
Err(_) => return FontMetrics::default(),
};
let font = match allsorts_subset_browser::font::Font::new(provider).ok() {
Some(s) => s,
_ => return FontMetrics::default(),
};
let hhea_table = &font.hhea_table;
let head_table = match font.head_table().ok() {
Some(Some(s)) => s,
_ => return FontMetrics::default(),
};
let os2_table = match font.os2_table().ok() {
Some(Some(s)) => Os2Info {
x_avg_char_width: s.x_avg_char_width,
us_weight_class: s.us_weight_class,
us_width_class: s.us_width_class,
fs_type: s.fs_type,
y_subscript_x_size: s.y_subscript_x_size,
y_subscript_y_size: s.y_subscript_y_size,
y_subscript_x_offset: s.y_subscript_x_offset,
y_subscript_y_offset: s.y_subscript_y_offset,
y_superscript_x_size: s.y_superscript_x_size,
y_superscript_y_size: s.y_superscript_y_size,
y_superscript_x_offset: s.y_superscript_x_offset,
y_superscript_y_offset: s.y_superscript_y_offset,
y_strikeout_size: s.y_strikeout_size,
y_strikeout_position: s.y_strikeout_position,
s_family_class: s.s_family_class,
panose: s.panose,
ul_unicode_range1: s.ul_unicode_range1,
ul_unicode_range2: s.ul_unicode_range2,
ul_unicode_range3: s.ul_unicode_range3,
ul_unicode_range4: s.ul_unicode_range4,
ach_vend_id: s.ach_vend_id,
fs_selection: s.fs_selection.bits(),
us_first_char_index: s.us_first_char_index,
us_last_char_index: s.us_last_char_index,
s_typo_ascender: s.version0.as_ref().map(|q| q.s_typo_ascender),
s_typo_descender: s.version0.as_ref().map(|q| q.s_typo_descender),
s_typo_line_gap: s.version0.as_ref().map(|q| q.s_typo_line_gap),
us_win_ascent: s.version0.as_ref().map(|q| q.us_win_ascent),
us_win_descent: s.version0.as_ref().map(|q| q.us_win_descent),
ul_code_page_range1: s.version1.as_ref().map(|q| q.ul_code_page_range1),
ul_code_page_range2: s.version1.as_ref().map(|q| q.ul_code_page_range2),
sx_height: s.version2to4.as_ref().map(|q| q.sx_height),
s_cap_height: s.version2to4.as_ref().map(|q| q.s_cap_height),
us_default_char: s.version2to4.as_ref().map(|q| q.us_default_char),
us_break_char: s.version2to4.as_ref().map(|q| q.us_break_char),
us_max_context: s.version2to4.as_ref().map(|q| q.us_max_context),
us_lower_optical_point_size: s.version5.as_ref().map(|q| q.us_lower_optical_point_size),
us_upper_optical_point_size: s.version5.as_ref().map(|q| q.us_upper_optical_point_size),
},
_ => Os2Info::default(),
};
FontMetrics {
units_per_em: if head_table.units_per_em == 0 {
1000_u16
} else {
head_table.units_per_em
},
font_flags: head_table.flags,
x_min: head_table.x_min,
y_min: head_table.y_min,
x_max: head_table.x_max,
y_max: head_table.y_max,
ascender: hhea_table.ascender,
descender: hhea_table.descender,
line_gap: hhea_table.line_gap,
advance_width_max: hhea_table.advance_width_max,
min_left_side_bearing: hhea_table.min_left_side_bearing,
min_right_side_bearing: hhea_table.min_right_side_bearing,
x_max_extent: hhea_table.x_max_extent,
caret_slope_rise: hhea_table.caret_slope_rise,
caret_slope_run: hhea_table.caret_slope_run,
caret_offset: hhea_table.caret_offset,
num_h_metrics: hhea_table.num_h_metrics,
x_avg_char_width: os2_table.x_avg_char_width,
us_weight_class: os2_table.us_weight_class,
us_width_class: os2_table.us_width_class,
fs_type: os2_table.fs_type,
y_subscript_x_size: os2_table.y_subscript_x_size,
y_subscript_y_size: os2_table.y_subscript_y_size,
y_subscript_x_offset: os2_table.y_subscript_x_offset,
y_subscript_y_offset: os2_table.y_subscript_y_offset,
y_superscript_x_size: os2_table.y_superscript_x_size,
y_superscript_y_size: os2_table.y_superscript_y_size,
y_superscript_x_offset: os2_table.y_superscript_x_offset,
y_superscript_y_offset: os2_table.y_superscript_y_offset,
y_strikeout_size: os2_table.y_strikeout_size,
y_strikeout_position: os2_table.y_strikeout_position,
s_family_class: os2_table.s_family_class,
panose: os2_table.panose,
ul_unicode_range1: os2_table.ul_unicode_range1,
ul_unicode_range2: os2_table.ul_unicode_range2,
ul_unicode_range3: os2_table.ul_unicode_range3,
ul_unicode_range4: os2_table.ul_unicode_range4,
ach_vend_id: os2_table.ach_vend_id,
fs_selection: os2_table.fs_selection,
us_first_char_index: os2_table.us_first_char_index,
us_last_char_index: os2_table.us_last_char_index,
s_typo_ascender: os2_table.s_typo_ascender.into(),
s_typo_descender: os2_table.s_typo_descender.into(),
s_typo_line_gap: os2_table.s_typo_line_gap.into(),
us_win_ascent: os2_table.us_win_ascent.into(),
us_win_descent: os2_table.us_win_descent.into(),
ul_code_page_range1: os2_table.ul_code_page_range1.into(),
ul_code_page_range2: os2_table.ul_code_page_range2.into(),
sx_height: os2_table.sx_height.into(),
s_cap_height: os2_table.s_cap_height.into(),
us_default_char: os2_table.us_default_char.into(),
us_break_char: os2_table.us_break_char.into(),
us_max_context: os2_table.us_max_context.into(),
us_lower_optical_point_size: os2_table.us_lower_optical_point_size.into(),
us_upper_optical_point_size: os2_table.us_upper_optical_point_size.into(),
}
}
#[derive(Clone)]
pub struct ParsedFont {
pub font_metrics: FontMetrics,
pub num_glyphs: u16,
pub hhea_table: HheaTable,
pub hmtx_data: Vec<u8>,
pub maxp_table: MaxpTable,
pub gsub_cache: Option<LayoutCache<GSUB>>,
pub gpos_cache: Option<LayoutCache<GPOS>>,
pub opt_gdef_table: Option<Rc<GDEFTable>>,
pub glyph_records_decoded: BTreeMap<u16, OwnedGlyph>,
pub space_width: Option<usize>,
pub cmap_subtable: Option<OwnedCmapSubtable>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum GlyphOutlineOperation {
MoveTo(OutlineMoveTo),
LineTo(OutlineLineTo),
QuadraticCurveTo(OutlineQuadTo),
CubicCurveTo(OutlineCubicTo),
ClosePath,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineMoveTo {
pub x: f32,
pub y: f32,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineLineTo {
pub x: f32,
pub y: f32,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineQuadTo {
pub ctrl_1_x: f32,
pub ctrl_1_y: f32,
pub end_x: f32,
pub end_y: f32,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineCubicTo {
pub ctrl_1_x: f32,
pub ctrl_1_y: f32,
pub ctrl_2_x: f32,
pub ctrl_2_y: f32,
pub end_x: f32,
pub end_y: f32,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct GlyphOutline {
pub operations: GlyphOutlineOperationVec,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct GlyphOutlineBuilder {
operations: Vec<GlyphOutlineOperation>,
}
impl Default for GlyphOutlineBuilder {
fn default() -> Self {
GlyphOutlineBuilder {
operations: Vec::new(),
}
}
}
impl ttf_parser::OutlineBuilder for GlyphOutlineBuilder {
fn move_to(&mut self, x: f32, y: f32) {
self.operations
.push(GlyphOutlineOperation::MoveTo(OutlineMoveTo { x, y }));
}
fn line_to(&mut self, x: f32, y: f32) {
self.operations
.push(GlyphOutlineOperation::LineTo(OutlineLineTo { x, y }));
}
fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
self.operations
.push(GlyphOutlineOperation::QuadraticCurveTo(OutlineQuadTo {
ctrl_1_x: x1,
ctrl_1_y: y1,
end_x: x,
end_y: y,
}));
}
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
self.operations
.push(GlyphOutlineOperation::CubicCurveTo(OutlineCubicTo {
ctrl_1_x: x1,
ctrl_1_y: y1,
ctrl_2_x: x2,
ctrl_2_y: y2,
end_x: x,
end_y: y,
}));
}
fn close(&mut self) {
self.operations.push(GlyphOutlineOperation::ClosePath);
}
}
azul_css::impl_vec!(
GlyphOutlineOperation,
GlyphOutlineOperationVec,
GlyphOutlineOperationVecDestructor
);
azul_css::impl_vec_clone!(
GlyphOutlineOperation,
GlyphOutlineOperationVec,
GlyphOutlineOperationVecDestructor
);
azul_css::impl_vec_debug!(GlyphOutlineOperation, GlyphOutlineOperationVec);
azul_css::impl_vec_partialord!(GlyphOutlineOperation, GlyphOutlineOperationVec);
azul_css::impl_vec_partialeq!(GlyphOutlineOperation, GlyphOutlineOperationVec);
#[derive(Debug, Clone)]
#[repr(C)]
pub struct OwnedGlyphBoundingBox {
pub max_x: i16,
pub max_y: i16,
pub min_x: i16,
pub min_y: i16,
}
#[derive(Debug, Clone)]
pub struct OwnedGlyph {
pub bounding_box: OwnedGlyphBoundingBox,
pub horz_advance: u16,
pub outline: Option<GlyphOutline>,
}
impl OwnedGlyph {
fn from_glyph_data<'a>(glyph: Glyph<'a>, horz_advance: u16) -> Option<Self> {
let bbox = glyph.bounding_box()?;
Some(Self {
bounding_box: OwnedGlyphBoundingBox {
max_x: bbox.x_max,
max_y: bbox.y_max,
min_x: bbox.x_min,
min_y: bbox.y_min,
},
horz_advance,
outline: None,
})
}
}
impl ParsedFont {
pub fn from_bytes(
font_bytes: &[u8],
font_index: usize,
parse_glyph_outlines: bool,
) -> Option<Self> {
use allsorts_subset_browser::tag;
let scope = ReadScope::new(font_bytes);
let font_file = scope.read::<FontData<'_>>().ok()?;
let provider = font_file.table_provider(font_index).ok()?;
let head_table = provider
.table_data(tag::HEAD)
.ok()
.and_then(|head_data| ReadScope::new(&head_data?).read::<HeadTable>().ok());
let maxp_table = provider
.table_data(tag::MAXP)
.ok()
.and_then(|maxp_data| ReadScope::new(&maxp_data?).read::<MaxpTable>().ok())
.unwrap_or(MaxpTable {
num_glyphs: 0,
version1_sub_table: None,
});
let index_to_loc = head_table
.map(|s| s.index_to_loc_format)
.unwrap_or(allsorts_subset_browser::tables::IndexToLocFormat::Long);
let num_glyphs = maxp_table.num_glyphs as usize;
let loca_table = provider.table_data(tag::LOCA).ok();
let loca_table = loca_table
.as_ref()
.and_then(|loca_data| {
ReadScope::new(&loca_data.as_ref()?)
.read_dep::<LocaTable<'_>>((num_glyphs, index_to_loc))
.ok()
})
.unwrap_or(LocaTable {
offsets: LocaOffsets::Long(
allsorts_subset_browser::binary::read::ReadArray::empty(),
),
});
let glyf_table = provider.table_data(tag::GLYF).ok();
let mut glyf_table = glyf_table
.as_ref()
.and_then(|glyf_data| {
ReadScope::new(&glyf_data.as_ref()?)
.read_dep::<GlyfTable<'_>>(&loca_table)
.ok()
})
.unwrap_or(GlyfTable::new(Vec::new()).unwrap());
let hmtx_data = provider
.table_data(tag::HMTX)
.ok()
.and_then(|s| Some(s?.to_vec()))
.unwrap_or_default();
let hhea_table = provider
.table_data(tag::HHEA)
.ok()
.and_then(|hhea_data| ReadScope::new(&hhea_data?).read::<HheaTable>().ok())
.unwrap_or(unsafe { std::mem::zeroed() });
let font_metrics = get_font_metrics(font_bytes, font_index);
let glyph_records_decoded = glyf_table
.records_mut()
.into_iter()
.enumerate()
.filter_map(|(glyph_index, glyph_record)| {
if glyph_index > (u16::MAX as usize) {
return None;
}
glyph_record.parse().ok()?;
let glyph_index = glyph_index as u16;
let horz_advance = allsorts_subset_browser::glyph_info::advance(
&maxp_table,
&hhea_table,
&hmtx_data,
glyph_index,
)
.unwrap_or_default();
match glyph_record {
GlyfRecord::Present { .. } => None,
GlyfRecord::Parsed(g) => OwnedGlyph::from_glyph_data(g.clone(), horz_advance)
.map(|g| (glyph_index, g)),
}
})
.collect::<Vec<_>>();
let glyph_records_decoded = glyph_records_decoded.into_iter().collect();
let mut font_data_impl = allsorts_subset_browser::font::Font::new(provider).ok()?;
let gsub_cache = font_data_impl.gsub_cache().ok().and_then(|s| s);
let gpos_cache = font_data_impl.gpos_cache().ok().and_then(|s| s);
let opt_gdef_table = font_data_impl.gdef_table().ok().and_then(|o| o);
let num_glyphs = font_data_impl.num_glyphs();
let cmap_subtable = ReadScope::new(font_data_impl.cmap_subtable_data());
let cmap_subtable = cmap_subtable
.read::<CmapSubtable<'_>>()
.ok()
.and_then(|s| s.to_owned());
let mut font = ParsedFont {
font_metrics,
num_glyphs,
hhea_table,
hmtx_data,
maxp_table,
gsub_cache,
gpos_cache,
opt_gdef_table,
cmap_subtable,
glyph_records_decoded,
space_width: None,
};
let space_width = font.get_space_width_internal();
font.space_width = space_width;
Some(font)
}
fn get_space_width_internal(&mut self) -> Option<usize> {
let glyph_index = self.lookup_glyph_index(' ' as u32)?;
allsorts_subset_browser::glyph_info::advance(
&self.maxp_table,
&self.hhea_table,
&self.hmtx_data,
glyph_index,
)
.ok()
.map(|s| s as usize)
}
#[inline]
pub const fn get_space_width(&self) -> Option<usize> {
self.space_width
}
pub fn get_horizontal_advance(&self, glyph_index: u16) -> u16 {
self.glyph_records_decoded
.get(&glyph_index)
.map(|gi| gi.horz_advance)
.unwrap_or_default()
}
pub fn get_glyph_size(&self, glyph_index: u16) -> Option<(i32, i32)> {
let g = self.glyph_records_decoded.get(&glyph_index)?;
let glyph_width = g.bounding_box.max_x as i32 - g.bounding_box.min_x as i32; let glyph_height = g.bounding_box.max_y as i32 - g.bounding_box.min_y as i32; Some((glyph_width, glyph_height))
}
pub fn shape(&self, text: &[u32], script: u32, lang: Option<u32>) -> ShapedTextBufferUnsized {
shape(self, text, script, lang).unwrap_or_default()
}
pub fn lookup_glyph_index(&self, c: u32) -> Option<u16> {
match self
.cmap_subtable
.as_ref()
.and_then(|s| s.map_glyph(c).ok())
{
Some(Some(c)) => Some(c),
_ => None,
}
}
}
#[derive(Debug, PartialEq, Default)]
pub struct ShapedTextBufferUnsized {
pub infos: Vec<GlyphInfo>,
}
impl ShapedTextBufferUnsized {
pub fn get_word_visual_width_unscaled(&self) -> usize {
self.infos
.iter()
.map(|s| s.size.get_x_advance_total_unscaled() as usize)
.sum()
}
}
macro_rules! tag {
($w:expr) => {
tag(*$w)
};
}
const fn tag(chars: [u8; 4]) -> u32 {
((chars[3] as u32) << 0)
| ((chars[2] as u32) << 8)
| ((chars[1] as u32) << 16)
| ((chars[0] as u32) << 24)
}
#[allow(dead_code)]
pub fn estimate_script_and_language(text: &str) -> (u32, Option<u32>) {
use crate::text::script::Script;
const TAG_ADLM: u32 = tag!(b"adlm"); const TAG_AHOM: u32 = tag!(b"ahom"); const TAG_HLUW: u32 = tag!(b"hluw"); const TAG_ARAB: u32 = tag!(b"arab"); const TAG_ARMN: u32 = tag!(b"armn"); const TAG_AVST: u32 = tag!(b"avst"); const TAG_BALI: u32 = tag!(b"bali"); const TAG_BAMU: u32 = tag!(b"bamu"); const TAG_BASS: u32 = tag!(b"bass"); const TAG_BATK: u32 = tag!(b"batk"); const TAG_BENG: u32 = tag!(b"beng"); const TAG_BNG2: u32 = tag!(b"bng2"); const TAG_BHKS: u32 = tag!(b"bhks"); const TAG_BOPO: u32 = tag!(b"bopo"); const TAG_BRAH: u32 = tag!(b"brah"); const TAG_BRAI: u32 = tag!(b"brai"); const TAG_BUGI: u32 = tag!(b"bugi"); const TAG_BUHD: u32 = tag!(b"buhd"); const TAG_BYZM: u32 = tag!(b"byzm"); const TAG_CANS: u32 = tag!(b"cans"); const TAG_CARI: u32 = tag!(b"cari"); const TAG_AGHB: u32 = tag!(b"aghb"); const TAG_CAKM: u32 = tag!(b"cakm"); const TAG_CHAM: u32 = tag!(b"cham"); const TAG_CHER: u32 = tag!(b"cher"); const TAG_CHRS: u32 = tag!(b"chrs"); const TAG_HANI: u32 = tag!(b"hani"); const TAG_COPT: u32 = tag!(b"copt"); const TAG_CPRT: u32 = tag!(b"cprt"); const TAG_CYRL: u32 = tag!(b"cyrl"); const TAG_DFLT: u32 = tag!(b"DFLT"); const TAG_DSRT: u32 = tag!(b"dsrt"); const TAG_DEVA: u32 = tag!(b"deva"); const TAG_DEV2: u32 = tag!(b"dev2"); const TAG_DIAK: u32 = tag!(b"diak"); const TAG_DOGR: u32 = tag!(b"dogr"); const TAG_DUPL: u32 = tag!(b"dupl"); const TAG_EGYP: u32 = tag!(b"egyp"); const TAG_ELBA: u32 = tag!(b"elba"); const TAG_ELYM: u32 = tag!(b"elym"); const TAG_ETHI: u32 = tag!(b"ethi"); const TAG_GEOR: u32 = tag!(b"geor"); const TAG_GLAG: u32 = tag!(b"glag"); const TAG_GOTH: u32 = tag!(b"goth"); const TAG_GRAN: u32 = tag!(b"gran"); const TAG_GREK: u32 = tag!(b"grek"); const TAG_GUJR: u32 = tag!(b"gujr"); const TAG_GJR2: u32 = tag!(b"gjr2"); const TAG_GONG: u32 = tag!(b"gong"); const TAG_GURU: u32 = tag!(b"guru"); const TAG_GUR2: u32 = tag!(b"gur2"); const TAG_HANG: u32 = tag!(b"hang"); const TAG_JAMO: u32 = tag!(b"jamo"); const TAG_ROHG: u32 = tag!(b"rohg"); const TAG_HANO: u32 = tag!(b"hano"); const TAG_HATR: u32 = tag!(b"hatr"); const TAG_HEBR: u32 = tag!(b"hebr"); const TAG_HIRG: u32 = tag!(b"kana"); const TAG_ARMI: u32 = tag!(b"armi"); const TAG_PHLI: u32 = tag!(b"phli"); const TAG_PRTI: u32 = tag!(b"prti"); const TAG_JAVA: u32 = tag!(b"java"); const TAG_KTHI: u32 = tag!(b"kthi"); const TAG_KNDA: u32 = tag!(b"knda"); const TAG_KND2: u32 = tag!(b"knd2"); const TAG_KANA: u32 = tag!(b"kana"); const TAG_KALI: u32 = tag!(b"kali"); const TAG_KHAR: u32 = tag!(b"khar"); const TAG_KITS: u32 = tag!(b"kits"); const TAG_KHMR: u32 = tag!(b"khmr"); const TAG_KHOJ: u32 = tag!(b"khoj"); const TAG_SIND: u32 = tag!(b"sind"); const TAG_LAO: u32 = tag!(b"lao "); const TAG_LATN: u32 = tag!(b"latn"); const TAG_LEPC: u32 = tag!(b"lepc"); const TAG_LIMB: u32 = tag!(b"limb"); const TAG_LINA: u32 = tag!(b"lina"); const TAG_LINB: u32 = tag!(b"linb"); const TAG_LISU: u32 = tag!(b"lisu"); const TAG_LYCI: u32 = tag!(b"lyci"); const TAG_LYDI: u32 = tag!(b"lydi"); const TAG_MAHJ: u32 = tag!(b"mahj"); const TAG_MAKA: u32 = tag!(b"maka"); const TAG_MLYM: u32 = tag!(b"mlym"); const TAG_MLM2: u32 = tag!(b"mlm2"); const TAG_MAND: u32 = tag!(b"mand"); const TAG_MANI: u32 = tag!(b"mani"); const TAG_MARC: u32 = tag!(b"marc"); const TAG_GONM: u32 = tag!(b"gonm"); const TAG_MATH: u32 = tag!(b"math"); const TAG_MEDF: u32 = tag!(b"medf"); const TAG_MTEI: u32 = tag!(b"mtei"); const TAG_MEND: u32 = tag!(b"mend"); const TAG_MERC: u32 = tag!(b"merc"); const TAG_MERO: u32 = tag!(b"mero"); const TAG_PLRD: u32 = tag!(b"plrd"); const TAG_MODI: u32 = tag!(b"modi"); const TAG_MONG: u32 = tag!(b"mong"); const TAG_MROO: u32 = tag!(b"mroo"); const TAG_MULT: u32 = tag!(b"mult"); const TAG_MUSC: u32 = tag!(b"musc"); const TAG_MYMR: u32 = tag!(b"mymr"); const TAG_MYM2: u32 = tag!(b"mym2"); const TAG_NBAT: u32 = tag!(b"nbat"); const TAG_NAND: u32 = tag!(b"nand"); const TAG_NEWA: u32 = tag!(b"newa"); const TAG_TALU: u32 = tag!(b"talu"); const TAG_NKO: u32 = tag!(b"nko "); const TAG_NSHU: u32 = tag!(b"nshu"); const TAG_HMNP: u32 = tag!(b"hmnp"); const TAG_ORYA: u32 = tag!(b"orya"); const TAG_ORY2: u32 = tag!(b"ory2"); const TAG_OGAM: u32 = tag!(b"ogam"); const TAG_OLCK: u32 = tag!(b"olck"); const TAG_ITAL: u32 = tag!(b"ital"); const TAG_HUNG: u32 = tag!(b"hung"); const TAG_NARB: u32 = tag!(b"narb"); const TAG_PERM: u32 = tag!(b"perm"); const TAG_XPEO: u32 = tag!(b"xpeo"); const TAG_SOGO: u32 = tag!(b"sogo"); const TAG_SARB: u32 = tag!(b"sarb"); const TAG_ORKH: u32 = tag!(b"orkh"); const TAG_OSGE: u32 = tag!(b"osge"); const TAG_OSMA: u32 = tag!(b"osma"); const TAG_HMNG: u32 = tag!(b"hmng"); const TAG_PALM: u32 = tag!(b"palm"); const TAG_PAUC: u32 = tag!(b"pauc"); const TAG_PHAG: u32 = tag!(b"phag"); const TAG_PHNX: u32 = tag!(b"phnx"); const TAG_PHLP: u32 = tag!(b"phlp"); const TAG_RJNG: u32 = tag!(b"rjng"); const TAG_RUNR: u32 = tag!(b"runr"); const TAG_SAMR: u32 = tag!(b"samr"); const TAG_SAUR: u32 = tag!(b"saur"); const TAG_SHRD: u32 = tag!(b"shrd"); const TAG_SHAW: u32 = tag!(b"shaw"); const TAG_SIDD: u32 = tag!(b"sidd"); const TAG_SGNW: u32 = tag!(b"sgnw"); const TAG_SINH: u32 = tag!(b"sinh"); const TAG_SOGD: u32 = tag!(b"sogd"); const TAG_SORA: u32 = tag!(b"sora"); const TAG_SOYO: u32 = tag!(b"soyo"); const TAG_XSUX: u32 = tag!(b"xsux"); const TAG_SUND: u32 = tag!(b"sund"); const TAG_SYLO: u32 = tag!(b"sylo"); const TAG_SYRC: u32 = tag!(b"syrc"); const TAG_TGLG: u32 = tag!(b"tglg"); const TAG_TAGB: u32 = tag!(b"tagb"); const TAG_TALE: u32 = tag!(b"tale"); const TAG_LANA: u32 = tag!(b"lana"); const TAG_TAVT: u32 = tag!(b"tavt"); const TAG_TAKR: u32 = tag!(b"takr"); const TAG_TAML: u32 = tag!(b"taml"); const TAG_TML2: u32 = tag!(b"tml2"); const TAG_TANG: u32 = tag!(b"tang"); const TAG_TELU: u32 = tag!(b"telu"); const TAG_TEL2: u32 = tag!(b"tel2"); const TAG_THAA: u32 = tag!(b"thaa"); const TAG_THAI: u32 = tag!(b"thai"); const TAG_TIBT: u32 = tag!(b"tibt"); const TAG_TFNG: u32 = tag!(b"tfng"); const TAG_TIRH: u32 = tag!(b"tirh"); const TAG_UGAR: u32 = tag!(b"ugar"); const TAG_VAI: u32 = tag!(b"vai "); const TAG_WCHO: u32 = tag!(b"wcho"); const TAG_WARA: u32 = tag!(b"wara"); const TAG_YEZI: u32 = tag!(b"yezi"); const TAG_ZANB: u32 = tag!(b"zanb");
let lang = None;
let script = match crate::text::script::detect_script(text).unwrap_or(Script::Latin) {
Script::Arabic => TAG_ARAB,
Script::Bengali => TAG_BENG,
Script::Cyrillic => TAG_CYRL,
Script::Devanagari => TAG_DEVA,
Script::Ethiopic => TAG_ETHI,
Script::Georgian => TAG_GEOR,
Script::Greek => TAG_GREK,
Script::Gujarati => TAG_GUJR,
Script::Gurmukhi => TAG_GUR2,
Script::Hangul => TAG_HANG,
Script::Hebrew => TAG_HEBR,
Script::Hiragana => TAG_HIRG, Script::Kannada => TAG_KND2,
Script::Katakana => TAG_KANA,
Script::Khmer => TAG_KHMR,
Script::Latin => TAG_LATN,
Script::Malayalam => TAG_MLYM,
Script::Mandarin => TAG_MAND,
Script::Myanmar => TAG_MYM2,
Script::Oriya => TAG_ORYA,
Script::Sinhala => TAG_SINH,
Script::Tamil => TAG_TAML,
Script::Telugu => TAG_TELU,
Script::Thai => TAG_THAI,
};
(script, lang)
}
fn shape<'a>(
font: &ParsedFont,
text: &[u32],
script: u32,
lang: Option<u32>,
) -> Option<ShapedTextBufferUnsized> {
use core::convert::TryFrom;
use allsorts_subset_browser::{
gpos::apply as gpos_apply,
gsub::{apply as gsub_apply, FeatureMask, Features},
};
let mut chars_iter = text.iter().peekable();
let mut glyphs = Vec::with_capacity(text.len());
while let Some((ch, ch_as_char)) = chars_iter
.next()
.and_then(|c| Some((c, core::char::from_u32(*c)?)))
{
match allsorts_subset_browser::unicode::VariationSelector::try_from(ch_as_char) {
Ok(_) => {} Err(()) => {
let vs = chars_iter.peek().and_then(|&next| {
allsorts_subset_browser::unicode::VariationSelector::try_from(
core::char::from_u32(*next)?,
)
.ok()
});
let glyph_index = font.lookup_glyph_index(*ch).unwrap_or(0);
glyphs.push(make_raw_glyph(ch_as_char, glyph_index, vs));
}
}
}
const DOTTED_CIRCLE: u32 = '\u{25cc}' as u32;
let dotted_circle_index = font.lookup_glyph_index(DOTTED_CIRCLE).unwrap_or(0);
if let Some(gsub) = &font.gsub_cache {
gsub_apply(
dotted_circle_index,
gsub,
font.opt_gdef_table.as_ref().map(|f| Rc::as_ref(f)),
script,
lang,
&Features::Mask(FeatureMask::empty()),
None, font.num_glyphs,
&mut glyphs,
)
.ok()?;
}
let kerning = true;
let mut infos = allsorts_subset_browser::gpos::Info::init_from_glyphs(
font.opt_gdef_table.as_ref().map(|f| Rc::as_ref(f)),
glyphs,
);
if let Some(gpos) = &font.gpos_cache {
gpos_apply(
gpos,
font.opt_gdef_table.as_ref().map(|f| Rc::as_ref(f)),
kerning,
&Features::Mask(FeatureMask::all()),
None, script,
lang,
&mut infos,
)
.ok()?;
}
let infos = infos
.iter()
.filter_map(|info| {
let glyph_index = info.glyph.glyph_index;
let adv_x = font.get_horizontal_advance(glyph_index);
let (size_x, size_y) = font.get_glyph_size(glyph_index)?;
let advance = Advance {
advance_x: adv_x,
size_x,
size_y,
kerning: info.kerning,
};
let info = translate_info(&info, advance);
Some(info)
})
.collect();
Some(ShapedTextBufferUnsized { infos })
}
#[inline]
fn translate_info(i: &allsorts_subset_browser::gpos::Info, size: Advance) -> GlyphInfo {
GlyphInfo {
glyph: translate_raw_glyph(&i.glyph),
size,
kerning: i.kerning,
placement: translate_placement(&i.placement),
}
}
fn make_raw_glyph(
ch: char,
glyph_index: u16,
variation: Option<allsorts_subset_browser::unicode::VariationSelector>,
) -> allsorts_subset_browser::gsub::RawGlyph<()> {
allsorts_subset_browser::gsub::RawGlyph {
unicodes: tiny_vec![[char; 1] => ch],
glyph_index,
liga_component_pos: 0,
glyph_origin: allsorts_subset_browser::gsub::GlyphOrigin::Char(ch),
flags: RawGlyphFlags::empty(),
extra_data: (),
variation,
}
}
#[inline]
fn translate_raw_glyph(rg: &allsorts_subset_browser::gsub::RawGlyph<()>) -> RawGlyph {
RawGlyph {
unicode_codepoint: rg.unicodes.get(0).map(|s| (*s) as u32).into(),
glyph_index: rg.glyph_index,
liga_component_pos: rg.liga_component_pos,
glyph_origin: translate_glyph_origin(&rg.glyph_origin),
small_caps: rg.small_caps(),
multi_subst_dup: rg.multi_subst_dup(),
is_vert_alt: rg.is_vert_alt(),
fake_bold: rg.fake_bold(),
fake_italic: rg.fake_italic(),
variation: rg
.variation
.as_ref()
.map(translate_variation_selector)
.into(),
}
}
#[inline]
const fn translate_glyph_origin(g: &allsorts_subset_browser::gsub::GlyphOrigin) -> GlyphOrigin {
use allsorts_subset_browser::gsub::GlyphOrigin::*;
match g {
Char(c) => GlyphOrigin::Char(*c),
Direct => GlyphOrigin::Direct,
}
}
#[inline]
const fn translate_placement(p: &allsorts_subset_browser::gpos::Placement) -> Placement {
use allsorts_subset_browser::gpos::Placement::*;
use azul_core::app_resources::{
CursiveAnchorPlacement, MarkAnchorPlacement, PlacementDistance,
};
match p {
None => Placement::None,
Distance(x, y) => Placement::Distance(PlacementDistance { x: *x, y: *y }),
MarkAnchor(i, a1, a2) => Placement::MarkAnchor(MarkAnchorPlacement {
base_glyph_index: *i,
base_glyph_anchor: translate_anchor(a1),
mark_anchor: translate_anchor(a2),
}),
MarkOverprint(i) => Placement::MarkOverprint(*i),
CursiveAnchor(i, b, a1, a2) => Placement::CursiveAnchor(CursiveAnchorPlacement {
exit_glyph_index: *i,
right_to_left: *b,
exit_glyph_anchor: translate_anchor(a1),
entry_glyph_anchor: translate_anchor(a2),
}),
}
}
const fn translate_variation_selector(
v: &allsorts_subset_browser::unicode::VariationSelector,
) -> VariationSelector {
use allsorts_subset_browser::unicode::VariationSelector::*;
match v {
VS01 => VariationSelector::VS01,
VS02 => VariationSelector::VS02,
VS03 => VariationSelector::VS03,
VS15 => VariationSelector::VS15,
VS16 => VariationSelector::VS16,
}
}
#[inline]
const fn translate_anchor(anchor: &allsorts_subset_browser::layout::Anchor) -> Anchor {
Anchor {
x: anchor.x,
y: anchor.y,
}
}