1use alloc::{boxed::Box, collections::btree_map::BTreeMap, rc::Rc, vec::Vec};
2
3use allsorts_subset_browser::{
4 binary::read::ReadScope,
5 font_data::FontData,
6 gsub::RawGlyphFlags,
7 layout::{GDEFTable, LayoutCache, GPOS, GSUB},
8 tables::{
9 cmap::{owned::CmapSubtable as OwnedCmapSubtable, CmapSubtable},
10 glyf::{BoundingBox, GlyfRecord, GlyfTable, Glyph},
11 loca::{LocaOffsets, LocaTable},
12 FontTableProvider, HeadTable, HheaTable, MaxpTable,
13 },
14};
15use azul_core::app_resources::{
16 Advance, Anchor, FontMetrics, GlyphInfo, GlyphOrigin, Placement, RawGlyph, VariationSelector,
17};
18use tinyvec::tiny_vec;
19
20pub fn get_font_metrics(font_bytes: &[u8], font_index: usize) -> FontMetrics {
21 #[derive(Default)]
22 struct Os2Info {
23 x_avg_char_width: i16,
24 us_weight_class: u16,
25 us_width_class: u16,
26 fs_type: u16,
27 y_subscript_x_size: i16,
28 y_subscript_y_size: i16,
29 y_subscript_x_offset: i16,
30 y_subscript_y_offset: i16,
31 y_superscript_x_size: i16,
32 y_superscript_y_size: i16,
33 y_superscript_x_offset: i16,
34 y_superscript_y_offset: i16,
35 y_strikeout_size: i16,
36 y_strikeout_position: i16,
37 s_family_class: i16,
38 panose: [u8; 10],
39 ul_unicode_range1: u32,
40 ul_unicode_range2: u32,
41 ul_unicode_range3: u32,
42 ul_unicode_range4: u32,
43 ach_vend_id: u32,
44 fs_selection: u16,
45 us_first_char_index: u16,
46 us_last_char_index: u16,
47 s_typo_ascender: Option<i16>,
48 s_typo_descender: Option<i16>,
49 s_typo_line_gap: Option<i16>,
50 us_win_ascent: Option<u16>,
51 us_win_descent: Option<u16>,
52 ul_code_page_range1: Option<u32>,
53 ul_code_page_range2: Option<u32>,
54 sx_height: Option<i16>,
55 s_cap_height: Option<i16>,
56 us_default_char: Option<u16>,
57 us_break_char: Option<u16>,
58 us_max_context: Option<u16>,
59 us_lower_optical_point_size: Option<u16>,
60 us_upper_optical_point_size: Option<u16>,
61 }
62
63 let scope = ReadScope::new(font_bytes);
64 let font_file = match scope.read::<FontData<'_>>() {
65 Ok(o) => o,
66 Err(_) => return FontMetrics::default(),
67 };
68 let provider = match font_file.table_provider(font_index) {
69 Ok(o) => o,
70 Err(_) => return FontMetrics::default(),
71 };
72 let font = match allsorts_subset_browser::font::Font::new(provider).ok() {
73 Some(s) => s,
74 _ => return FontMetrics::default(),
75 };
76
77 let hhea_table = &font.hhea_table;
79 let head_table = match font.head_table().ok() {
80 Some(Some(s)) => s,
81 _ => return FontMetrics::default(),
82 };
83
84 let os2_table = match font.os2_table().ok() {
85 Some(Some(s)) => Os2Info {
86 x_avg_char_width: s.x_avg_char_width,
87 us_weight_class: s.us_weight_class,
88 us_width_class: s.us_width_class,
89 fs_type: s.fs_type,
90 y_subscript_x_size: s.y_subscript_x_size,
91 y_subscript_y_size: s.y_subscript_y_size,
92 y_subscript_x_offset: s.y_subscript_x_offset,
93 y_subscript_y_offset: s.y_subscript_y_offset,
94 y_superscript_x_size: s.y_superscript_x_size,
95 y_superscript_y_size: s.y_superscript_y_size,
96 y_superscript_x_offset: s.y_superscript_x_offset,
97 y_superscript_y_offset: s.y_superscript_y_offset,
98 y_strikeout_size: s.y_strikeout_size,
99 y_strikeout_position: s.y_strikeout_position,
100 s_family_class: s.s_family_class,
101 panose: s.panose,
102 ul_unicode_range1: s.ul_unicode_range1,
103 ul_unicode_range2: s.ul_unicode_range2,
104 ul_unicode_range3: s.ul_unicode_range3,
105 ul_unicode_range4: s.ul_unicode_range4,
106 ach_vend_id: s.ach_vend_id,
107 fs_selection: s.fs_selection.bits(),
108 us_first_char_index: s.us_first_char_index,
109 us_last_char_index: s.us_last_char_index,
110
111 s_typo_ascender: s.version0.as_ref().map(|q| q.s_typo_ascender),
112 s_typo_descender: s.version0.as_ref().map(|q| q.s_typo_descender),
113 s_typo_line_gap: s.version0.as_ref().map(|q| q.s_typo_line_gap),
114 us_win_ascent: s.version0.as_ref().map(|q| q.us_win_ascent),
115 us_win_descent: s.version0.as_ref().map(|q| q.us_win_descent),
116
117 ul_code_page_range1: s.version1.as_ref().map(|q| q.ul_code_page_range1),
118 ul_code_page_range2: s.version1.as_ref().map(|q| q.ul_code_page_range2),
119
120 sx_height: s.version2to4.as_ref().map(|q| q.sx_height),
121 s_cap_height: s.version2to4.as_ref().map(|q| q.s_cap_height),
122 us_default_char: s.version2to4.as_ref().map(|q| q.us_default_char),
123 us_break_char: s.version2to4.as_ref().map(|q| q.us_break_char),
124 us_max_context: s.version2to4.as_ref().map(|q| q.us_max_context),
125
126 us_lower_optical_point_size: s.version5.as_ref().map(|q| q.us_lower_optical_point_size),
127 us_upper_optical_point_size: s.version5.as_ref().map(|q| q.us_upper_optical_point_size),
128 },
129 _ => Os2Info::default(),
130 };
131
132 FontMetrics {
133 units_per_em: if head_table.units_per_em == 0 {
135 1000_u16
136 } else {
137 head_table.units_per_em
138 },
139 font_flags: head_table.flags,
140 x_min: head_table.x_min,
141 y_min: head_table.y_min,
142 x_max: head_table.x_max,
143 y_max: head_table.y_max,
144
145 ascender: hhea_table.ascender,
147 descender: hhea_table.descender,
148 line_gap: hhea_table.line_gap,
149 advance_width_max: hhea_table.advance_width_max,
150 min_left_side_bearing: hhea_table.min_left_side_bearing,
151 min_right_side_bearing: hhea_table.min_right_side_bearing,
152 x_max_extent: hhea_table.x_max_extent,
153 caret_slope_rise: hhea_table.caret_slope_rise,
154 caret_slope_run: hhea_table.caret_slope_run,
155 caret_offset: hhea_table.caret_offset,
156 num_h_metrics: hhea_table.num_h_metrics,
157
158 x_avg_char_width: os2_table.x_avg_char_width,
160 us_weight_class: os2_table.us_weight_class,
161 us_width_class: os2_table.us_width_class,
162 fs_type: os2_table.fs_type,
163 y_subscript_x_size: os2_table.y_subscript_x_size,
164 y_subscript_y_size: os2_table.y_subscript_y_size,
165 y_subscript_x_offset: os2_table.y_subscript_x_offset,
166 y_subscript_y_offset: os2_table.y_subscript_y_offset,
167 y_superscript_x_size: os2_table.y_superscript_x_size,
168 y_superscript_y_size: os2_table.y_superscript_y_size,
169 y_superscript_x_offset: os2_table.y_superscript_x_offset,
170 y_superscript_y_offset: os2_table.y_superscript_y_offset,
171 y_strikeout_size: os2_table.y_strikeout_size,
172 y_strikeout_position: os2_table.y_strikeout_position,
173 s_family_class: os2_table.s_family_class,
174 panose: os2_table.panose,
175 ul_unicode_range1: os2_table.ul_unicode_range1,
176 ul_unicode_range2: os2_table.ul_unicode_range2,
177 ul_unicode_range3: os2_table.ul_unicode_range3,
178 ul_unicode_range4: os2_table.ul_unicode_range4,
179 ach_vend_id: os2_table.ach_vend_id,
180 fs_selection: os2_table.fs_selection,
181 us_first_char_index: os2_table.us_first_char_index,
182 us_last_char_index: os2_table.us_last_char_index,
183 s_typo_ascender: os2_table.s_typo_ascender.into(),
184 s_typo_descender: os2_table.s_typo_descender.into(),
185 s_typo_line_gap: os2_table.s_typo_line_gap.into(),
186 us_win_ascent: os2_table.us_win_ascent.into(),
187 us_win_descent: os2_table.us_win_descent.into(),
188 ul_code_page_range1: os2_table.ul_code_page_range1.into(),
189 ul_code_page_range2: os2_table.ul_code_page_range2.into(),
190 sx_height: os2_table.sx_height.into(),
191 s_cap_height: os2_table.s_cap_height.into(),
192 us_default_char: os2_table.us_default_char.into(),
193 us_break_char: os2_table.us_break_char.into(),
194 us_max_context: os2_table.us_max_context.into(),
195 us_lower_optical_point_size: os2_table.us_lower_optical_point_size.into(),
196 us_upper_optical_point_size: os2_table.us_upper_optical_point_size.into(),
197 }
198}
199
200#[derive(Clone)]
201pub struct ParsedFont {
202 pub font_metrics: FontMetrics,
203 pub num_glyphs: u16,
204 pub hhea_table: HheaTable,
205 pub hmtx_data: Vec<u8>,
206 pub maxp_table: MaxpTable,
207 pub gsub_cache: Option<LayoutCache<GSUB>>,
208 pub gpos_cache: Option<LayoutCache<GPOS>>,
209 pub opt_gdef_table: Option<Rc<GDEFTable>>,
210 pub glyph_records_decoded: BTreeMap<u16, OwnedGlyph>,
211 pub space_width: Option<usize>,
212 pub cmap_subtable: Option<OwnedCmapSubtable>,
213}
214
215#[derive(Debug, Clone, PartialEq, PartialOrd)]
216#[repr(C, u8)]
217pub enum GlyphOutlineOperation {
218 MoveTo(OutlineMoveTo),
219 LineTo(OutlineLineTo),
220 QuadraticCurveTo(OutlineQuadTo),
221 CubicCurveTo(OutlineCubicTo),
222 ClosePath,
223}
224
225#[derive(Debug, Clone, PartialEq, PartialOrd)]
226#[repr(C)]
227pub struct OutlineMoveTo {
228 pub x: f32,
229 pub y: f32,
230}
231
232#[derive(Debug, Clone, PartialEq, PartialOrd)]
233#[repr(C)]
234pub struct OutlineLineTo {
235 pub x: f32,
236 pub y: f32,
237}
238
239#[derive(Debug, Clone, PartialEq, PartialOrd)]
240#[repr(C)]
241pub struct OutlineQuadTo {
242 pub ctrl_1_x: f32,
243 pub ctrl_1_y: f32,
244 pub end_x: f32,
245 pub end_y: f32,
246}
247
248#[derive(Debug, Clone, PartialEq, PartialOrd)]
249#[repr(C)]
250pub struct OutlineCubicTo {
251 pub ctrl_1_x: f32,
252 pub ctrl_1_y: f32,
253 pub ctrl_2_x: f32,
254 pub ctrl_2_y: f32,
255 pub end_x: f32,
256 pub end_y: f32,
257}
258
259#[derive(Debug, Clone, PartialEq, PartialOrd)]
260#[repr(C)]
261pub struct GlyphOutline {
262 pub operations: GlyphOutlineOperationVec,
263}
264
265#[derive(Debug, Clone, PartialEq, PartialOrd)]
266struct GlyphOutlineBuilder {
267 operations: Vec<GlyphOutlineOperation>,
268}
269
270impl Default for GlyphOutlineBuilder {
271 fn default() -> Self {
272 GlyphOutlineBuilder {
273 operations: Vec::new(),
274 }
275 }
276}
277
278impl ttf_parser::OutlineBuilder for GlyphOutlineBuilder {
279 fn move_to(&mut self, x: f32, y: f32) {
280 self.operations
281 .push(GlyphOutlineOperation::MoveTo(OutlineMoveTo { x, y }));
282 }
283 fn line_to(&mut self, x: f32, y: f32) {
284 self.operations
285 .push(GlyphOutlineOperation::LineTo(OutlineLineTo { x, y }));
286 }
287 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
288 self.operations
289 .push(GlyphOutlineOperation::QuadraticCurveTo(OutlineQuadTo {
290 ctrl_1_x: x1,
291 ctrl_1_y: y1,
292 end_x: x,
293 end_y: y,
294 }));
295 }
296 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
297 self.operations
298 .push(GlyphOutlineOperation::CubicCurveTo(OutlineCubicTo {
299 ctrl_1_x: x1,
300 ctrl_1_y: y1,
301 ctrl_2_x: x2,
302 ctrl_2_y: y2,
303 end_x: x,
304 end_y: y,
305 }));
306 }
307 fn close(&mut self) {
308 self.operations.push(GlyphOutlineOperation::ClosePath);
309 }
310}
311
312azul_css::impl_vec!(
313 GlyphOutlineOperation,
314 GlyphOutlineOperationVec,
315 GlyphOutlineOperationVecDestructor
316);
317azul_css::impl_vec_clone!(
318 GlyphOutlineOperation,
319 GlyphOutlineOperationVec,
320 GlyphOutlineOperationVecDestructor
321);
322azul_css::impl_vec_debug!(GlyphOutlineOperation, GlyphOutlineOperationVec);
323azul_css::impl_vec_partialord!(GlyphOutlineOperation, GlyphOutlineOperationVec);
324azul_css::impl_vec_partialeq!(GlyphOutlineOperation, GlyphOutlineOperationVec);
325
326#[derive(Debug, Clone)]
327#[repr(C)]
328pub struct OwnedGlyphBoundingBox {
329 pub max_x: i16,
330 pub max_y: i16,
331 pub min_x: i16,
332 pub min_y: i16,
333}
334
335#[derive(Debug, Clone)]
336pub struct OwnedGlyph {
337 pub bounding_box: OwnedGlyphBoundingBox,
338 pub horz_advance: u16,
339 pub outline: Option<GlyphOutline>,
340}
341
342impl OwnedGlyph {
343 fn from_glyph_data<'a>(glyph: Glyph<'a>, horz_advance: u16) -> Option<Self> {
344 let bbox = glyph.bounding_box()?;
345 Some(Self {
346 bounding_box: OwnedGlyphBoundingBox {
347 max_x: bbox.x_max,
348 max_y: bbox.y_max,
349 min_x: bbox.x_min,
350 min_y: bbox.y_min,
351 },
352 horz_advance,
353 outline: None,
354 })
355 }
356}
357
358impl ParsedFont {
359 pub fn from_bytes(
360 font_bytes: &[u8],
361 font_index: usize,
362 parse_glyph_outlines: bool,
363 ) -> Option<Self> {
364 use allsorts_subset_browser::tag;
365
366 let scope = ReadScope::new(font_bytes);
367 let font_file = scope.read::<FontData<'_>>().ok()?;
368 let provider = font_file.table_provider(font_index).ok()?;
369
370 let head_table = provider
371 .table_data(tag::HEAD)
372 .ok()
373 .and_then(|head_data| ReadScope::new(&head_data?).read::<HeadTable>().ok());
374
375 let maxp_table = provider
376 .table_data(tag::MAXP)
377 .ok()
378 .and_then(|maxp_data| ReadScope::new(&maxp_data?).read::<MaxpTable>().ok())
379 .unwrap_or(MaxpTable {
380 num_glyphs: 0,
381 version1_sub_table: None,
382 });
383
384 let index_to_loc = head_table
385 .map(|s| s.index_to_loc_format)
386 .unwrap_or(allsorts_subset_browser::tables::IndexToLocFormat::Long);
387 let num_glyphs = maxp_table.num_glyphs as usize;
388
389 let loca_table = provider.table_data(tag::LOCA).ok();
390 let loca_table = loca_table
391 .as_ref()
392 .and_then(|loca_data| {
393 ReadScope::new(&loca_data.as_ref()?)
394 .read_dep::<LocaTable<'_>>((num_glyphs, index_to_loc))
395 .ok()
396 })
397 .unwrap_or(LocaTable {
398 offsets: LocaOffsets::Long(
399 allsorts_subset_browser::binary::read::ReadArray::empty(),
400 ),
401 });
402
403 let glyf_table = provider.table_data(tag::GLYF).ok();
404 let mut glyf_table = glyf_table
405 .as_ref()
406 .and_then(|glyf_data| {
407 ReadScope::new(&glyf_data.as_ref()?)
408 .read_dep::<GlyfTable<'_>>(&loca_table)
409 .ok()
410 })
411 .unwrap_or(GlyfTable::new(Vec::new()).unwrap());
412
413 let hmtx_data = provider
414 .table_data(tag::HMTX)
415 .ok()
416 .and_then(|s| Some(s?.to_vec()))
417 .unwrap_or_default();
418
419 let hhea_table = provider
420 .table_data(tag::HHEA)
421 .ok()
422 .and_then(|hhea_data| ReadScope::new(&hhea_data?).read::<HheaTable>().ok())
423 .unwrap_or(unsafe { std::mem::zeroed() });
424
425 let font_metrics = get_font_metrics(font_bytes, font_index);
426
427 let glyph_records_decoded = glyf_table
429 .records_mut()
430 .into_iter()
431 .enumerate()
432 .filter_map(|(glyph_index, glyph_record)| {
433 if glyph_index > (u16::MAX as usize) {
434 return None;
435 }
436 glyph_record.parse().ok()?;
437 let glyph_index = glyph_index as u16;
438 let horz_advance = allsorts_subset_browser::glyph_info::advance(
439 &maxp_table,
440 &hhea_table,
441 &hmtx_data,
442 glyph_index,
443 )
444 .unwrap_or_default();
445
446 match glyph_record {
447 GlyfRecord::Present { .. } => None,
448 GlyfRecord::Parsed(g) => OwnedGlyph::from_glyph_data(g.clone(), horz_advance)
449 .map(|g| (glyph_index, g)),
450 }
451 })
452 .collect::<Vec<_>>();
453
454 let glyph_records_decoded = glyph_records_decoded.into_iter().collect();
455
456 let mut font_data_impl = allsorts_subset_browser::font::Font::new(provider).ok()?;
457
458 let gsub_cache = font_data_impl.gsub_cache().ok().and_then(|s| s);
460 let gpos_cache = font_data_impl.gpos_cache().ok().and_then(|s| s);
461 let opt_gdef_table = font_data_impl.gdef_table().ok().and_then(|o| o);
462 let num_glyphs = font_data_impl.num_glyphs();
463
464 let cmap_subtable = ReadScope::new(font_data_impl.cmap_subtable_data());
465 let cmap_subtable = cmap_subtable
466 .read::<CmapSubtable<'_>>()
467 .ok()
468 .and_then(|s| s.to_owned());
469
470 let mut font = ParsedFont {
471 font_metrics,
472 num_glyphs,
473 hhea_table,
474 hmtx_data,
475 maxp_table,
476 gsub_cache,
477 gpos_cache,
478 opt_gdef_table,
479 cmap_subtable,
480 glyph_records_decoded,
481 space_width: None,
482 };
483
484 let space_width = font.get_space_width_internal();
485 font.space_width = space_width;
486
487 Some(font)
488 }
489
490 fn get_space_width_internal(&mut self) -> Option<usize> {
491 let glyph_index = self.lookup_glyph_index(' ' as u32)?;
492 allsorts_subset_browser::glyph_info::advance(
493 &self.maxp_table,
494 &self.hhea_table,
495 &self.hmtx_data,
496 glyph_index,
497 )
498 .ok()
499 .map(|s| s as usize)
500 }
501
502 #[inline]
504 pub const fn get_space_width(&self) -> Option<usize> {
505 self.space_width
506 }
507
508 pub fn get_horizontal_advance(&self, glyph_index: u16) -> u16 {
509 self.glyph_records_decoded
510 .get(&glyph_index)
511 .map(|gi| gi.horz_advance)
512 .unwrap_or_default()
513 }
514
515 pub fn get_glyph_size(&self, glyph_index: u16) -> Option<(i32, i32)> {
517 let g = self.glyph_records_decoded.get(&glyph_index)?;
518 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))
521 }
522
523 pub fn shape(&self, text: &[u32], script: u32, lang: Option<u32>) -> ShapedTextBufferUnsized {
524 shape(self, text, script, lang).unwrap_or_default()
525 }
526
527 pub fn lookup_glyph_index(&self, c: u32) -> Option<u16> {
528 match self
529 .cmap_subtable
530 .as_ref()
531 .and_then(|s| s.map_glyph(c).ok())
532 {
533 Some(Some(c)) => Some(c),
534 _ => None,
535 }
536 }
537}
538
539#[derive(Debug, PartialEq, Default)]
540pub struct ShapedTextBufferUnsized {
541 pub infos: Vec<GlyphInfo>,
542}
543
544impl ShapedTextBufferUnsized {
545 pub fn get_word_visual_width_unscaled(&self) -> usize {
547 self.infos
548 .iter()
549 .map(|s| s.size.get_x_advance_total_unscaled() as usize)
550 .sum()
551 }
552}
553
554macro_rules! tag {
562 ($w:expr) => {
563 tag(*$w)
564 };
565}
566
567const fn tag(chars: [u8; 4]) -> u32 {
568 ((chars[3] as u32) << 0)
569 | ((chars[2] as u32) << 8)
570 | ((chars[1] as u32) << 16)
571 | ((chars[0] as u32) << 24)
572}
573
574#[allow(dead_code)]
576pub fn estimate_script_and_language(text: &str) -> (u32, Option<u32>) {
577 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) {
762 Script::Arabic => TAG_ARAB,
763 Script::Bengali => TAG_BENG,
764 Script::Cyrillic => TAG_CYRL,
765 Script::Devanagari => TAG_DEVA,
766 Script::Ethiopic => TAG_ETHI,
767 Script::Georgian => TAG_GEOR,
768 Script::Greek => TAG_GREK,
769 Script::Gujarati => TAG_GUJR,
770 Script::Gurmukhi => TAG_GUR2,
771 Script::Hangul => TAG_HANG,
772 Script::Hebrew => TAG_HEBR,
773 Script::Hiragana => TAG_HIRG, Script::Kannada => TAG_KND2,
775 Script::Katakana => TAG_KANA,
776 Script::Khmer => TAG_KHMR,
777 Script::Latin => TAG_LATN,
778 Script::Malayalam => TAG_MLYM,
779 Script::Mandarin => TAG_MAND,
780 Script::Myanmar => TAG_MYM2,
781 Script::Oriya => TAG_ORYA,
782 Script::Sinhala => TAG_SINH,
783 Script::Tamil => TAG_TAML,
784 Script::Telugu => TAG_TELU,
785 Script::Thai => TAG_THAI,
786 };
787
788 (script, lang)
789}
790
791fn shape<'a>(
796 font: &ParsedFont,
797 text: &[u32],
798 script: u32,
799 lang: Option<u32>,
800) -> Option<ShapedTextBufferUnsized> {
801 use core::convert::TryFrom;
802
803 use allsorts_subset_browser::{
804 gpos::apply as gpos_apply,
805 gsub::{apply as gsub_apply, FeatureMask, Features},
806 };
807
808 let mut chars_iter = text.iter().peekable();
814 let mut glyphs = Vec::with_capacity(text.len());
815
816 while let Some((ch, ch_as_char)) = chars_iter
817 .next()
818 .and_then(|c| Some((c, core::char::from_u32(*c)?)))
819 {
820 match allsorts_subset_browser::unicode::VariationSelector::try_from(ch_as_char) {
821 Ok(_) => {} Err(()) => {
823 let vs = chars_iter.peek().and_then(|&next| {
824 allsorts_subset_browser::unicode::VariationSelector::try_from(
825 core::char::from_u32(*next)?,
826 )
827 .ok()
828 });
829
830 let glyph_index = font.lookup_glyph_index(*ch).unwrap_or(0);
831 glyphs.push(make_raw_glyph(ch_as_char, glyph_index, vs));
832 }
833 }
834 }
835
836 const DOTTED_CIRCLE: u32 = '\u{25cc}' as u32;
837 let dotted_circle_index = font.lookup_glyph_index(DOTTED_CIRCLE).unwrap_or(0);
838
839 if let Some(gsub) = &font.gsub_cache {
841 gsub_apply(
842 dotted_circle_index,
843 gsub,
844 font.opt_gdef_table.as_ref().map(|f| Rc::as_ref(f)),
845 script,
846 lang,
847 &Features::Mask(FeatureMask::empty()),
848 None, font.num_glyphs,
850 &mut glyphs,
851 )
852 .ok()?;
853 }
854
855 let kerning = true;
858 let mut infos = allsorts_subset_browser::gpos::Info::init_from_glyphs(
859 font.opt_gdef_table.as_ref().map(|f| Rc::as_ref(f)),
860 glyphs,
861 );
862
863 if let Some(gpos) = &font.gpos_cache {
864 gpos_apply(
865 gpos,
866 font.opt_gdef_table.as_ref().map(|f| Rc::as_ref(f)),
867 kerning,
868 &Features::Mask(FeatureMask::all()),
869 None, script,
871 lang,
872 &mut infos,
873 )
874 .ok()?;
875 }
876
877 let infos = infos
879 .iter()
880 .filter_map(|info| {
881 let glyph_index = info.glyph.glyph_index;
882 let adv_x = font.get_horizontal_advance(glyph_index);
883 let (size_x, size_y) = font.get_glyph_size(glyph_index)?;
884 let advance = Advance {
885 advance_x: adv_x,
886 size_x,
887 size_y,
888 kerning: info.kerning,
889 };
890 let info = translate_info(&info, advance);
891 Some(info)
892 })
893 .collect();
894
895 Some(ShapedTextBufferUnsized { infos })
896}
897
898#[inline]
899fn translate_info(i: &allsorts_subset_browser::gpos::Info, size: Advance) -> GlyphInfo {
900 GlyphInfo {
901 glyph: translate_raw_glyph(&i.glyph),
902 size,
903 kerning: i.kerning,
904 placement: translate_placement(&i.placement),
905 }
906}
907
908fn make_raw_glyph(
909 ch: char,
910 glyph_index: u16,
911 variation: Option<allsorts_subset_browser::unicode::VariationSelector>,
912) -> allsorts_subset_browser::gsub::RawGlyph<()> {
913 allsorts_subset_browser::gsub::RawGlyph {
914 unicodes: tiny_vec![[char; 1] => ch],
915 glyph_index,
916 liga_component_pos: 0,
917 glyph_origin: allsorts_subset_browser::gsub::GlyphOrigin::Char(ch),
918 flags: RawGlyphFlags::empty(),
919 extra_data: (),
920 variation,
921 }
922}
923
924#[inline]
925fn translate_raw_glyph(rg: &allsorts_subset_browser::gsub::RawGlyph<()>) -> RawGlyph {
926 RawGlyph {
927 unicode_codepoint: rg.unicodes.get(0).map(|s| (*s) as u32).into(),
928 glyph_index: rg.glyph_index,
929 liga_component_pos: rg.liga_component_pos,
930 glyph_origin: translate_glyph_origin(&rg.glyph_origin),
931 small_caps: rg.small_caps(),
932 multi_subst_dup: rg.multi_subst_dup(),
933 is_vert_alt: rg.is_vert_alt(),
934 fake_bold: rg.fake_bold(),
935 fake_italic: rg.fake_italic(),
936 variation: rg
937 .variation
938 .as_ref()
939 .map(translate_variation_selector)
940 .into(),
941 }
942}
943
944#[inline]
945const fn translate_glyph_origin(g: &allsorts_subset_browser::gsub::GlyphOrigin) -> GlyphOrigin {
946 use allsorts_subset_browser::gsub::GlyphOrigin::*;
947 match g {
948 Char(c) => GlyphOrigin::Char(*c),
949 Direct => GlyphOrigin::Direct,
950 }
951}
952
953#[inline]
954const fn translate_placement(p: &allsorts_subset_browser::gpos::Placement) -> Placement {
955 use allsorts_subset_browser::gpos::Placement::*;
956 use azul_core::app_resources::{
957 CursiveAnchorPlacement, MarkAnchorPlacement, PlacementDistance,
958 };
959
960 match p {
961 None => Placement::None,
962 Distance(x, y) => Placement::Distance(PlacementDistance { x: *x, y: *y }),
963 MarkAnchor(i, a1, a2) => Placement::MarkAnchor(MarkAnchorPlacement {
964 base_glyph_index: *i,
965 base_glyph_anchor: translate_anchor(a1),
966 mark_anchor: translate_anchor(a2),
967 }),
968 MarkOverprint(i) => Placement::MarkOverprint(*i),
969 CursiveAnchor(i, b, a1, a2) => Placement::CursiveAnchor(CursiveAnchorPlacement {
970 exit_glyph_index: *i,
971 right_to_left: *b,
972 exit_glyph_anchor: translate_anchor(a1),
973 entry_glyph_anchor: translate_anchor(a2),
974 }),
975 }
976}
977
978const fn translate_variation_selector(
979 v: &allsorts_subset_browser::unicode::VariationSelector,
980) -> VariationSelector {
981 use allsorts_subset_browser::unicode::VariationSelector::*;
982 match v {
983 VS01 => VariationSelector::VS01,
984 VS02 => VariationSelector::VS02,
985 VS03 => VariationSelector::VS03,
986 VS15 => VariationSelector::VS15,
987 VS16 => VariationSelector::VS16,
988 }
989}
990
991#[inline]
992const fn translate_anchor(anchor: &allsorts_subset_browser::layout::Anchor) -> Anchor {
993 Anchor {
994 x: anchor.x,
995 y: anchor.y,
996 }
997}