1#![cfg(feature = "font_loading")]
2
3use azul_css::{AzString, U8Vec};
4use rust_fontconfig::{FcFontCache, FontSource};
5
6pub mod loading {
7 #![cfg(feature = "std")]
8 #![cfg(feature = "font_loading")]
9 #![cfg_attr(not(feature = "std"), no_std)]
10
11 use std::io::Error as IoError;
12
13 use azul_css::{AzString, StringVec, U8Vec};
14 use rust_fontconfig::FcFontCache;
15
16 #[cfg(not(miri))]
17 pub fn build_font_cache() -> FcFontCache {
18 FcFontCache::build()
19 }
20
21 #[cfg(miri)]
22 pub fn build_font_cache() -> FcFontCache {
23 FcFontCache::default()
24 }
25
26 #[derive(Debug)]
27 pub enum FontReloadError {
28 Io(IoError, AzString),
29 FontNotFound(AzString),
30 FontLoadingNotActive(AzString),
31 }
32
33 impl Clone for FontReloadError {
34 fn clone(&self) -> Self {
35 use self::FontReloadError::*;
36 match self {
37 Io(err, path) => Io(IoError::new(err.kind(), "Io Error"), path.clone()),
38 FontNotFound(id) => FontNotFound(id.clone()),
39 FontLoadingNotActive(id) => FontLoadingNotActive(id.clone()),
40 }
41 }
42 }
43
44 azul_core::impl_display!(FontReloadError, {
45 Io(err, path_buf) => format!("Could not load \"{}\" - IO error: {}", path_buf.as_str(), err),
46 FontNotFound(id) => format!("Could not locate system font: \"{:?}\" found", id),
47 FontLoadingNotActive(id) => format!("Could not load system font: \"{:?}\": crate was not compiled with --features=\"font_loading\"", id)
48 });
49}
50pub mod mock {
51 use std::collections::BTreeMap;
57
58 use crate::text3::cache::LayoutFontMetrics;
59
60 #[derive(Debug, Clone)]
65 pub struct MockFont {
66 pub font_metrics: LayoutFontMetrics,
68 pub space_width: Option<usize>,
70 pub glyph_advances: BTreeMap<u16, u16>,
72 pub glyph_sizes: BTreeMap<u16, (i32, i32)>,
74 pub glyph_indices: BTreeMap<u32, u16>,
76 }
77
78 impl MockFont {
79 pub fn new(font_metrics: LayoutFontMetrics) -> Self {
81 MockFont {
82 font_metrics,
83 space_width: Some(10),
84 glyph_advances: BTreeMap::new(),
85 glyph_sizes: BTreeMap::new(),
86 glyph_indices: BTreeMap::new(),
87 }
88 }
89
90 pub fn with_space_width(mut self, width: usize) -> Self {
92 self.space_width = Some(width);
93 self
94 }
95
96 pub fn with_glyph_advance(mut self, glyph_index: u16, advance: u16) -> Self {
98 self.glyph_advances.insert(glyph_index, advance);
99 self
100 }
101
102 pub fn with_glyph_size(mut self, glyph_index: u16, size: (i32, i32)) -> Self {
104 self.glyph_sizes.insert(glyph_index, size);
105 self
106 }
107
108 pub fn with_glyph_index(mut self, unicode: u32, index: u16) -> Self {
110 self.glyph_indices.insert(unicode, index);
111 self
112 }
113 }
114}
115
116pub mod parsed {
117 use core::fmt;
118 use std::{collections::BTreeMap, sync::Arc};
119
120 use allsorts::{
121 binary::read::ReadScope,
122 font_data::FontData,
123 layout::{GDEFTable, LayoutCache, LayoutCacheData, GPOS, GSUB},
124 subset::{subset as allsorts_subset, whole_font, CmapTarget, SubsetProfile},
125 tables::{
126 cmap::owned::CmapSubtable as OwnedCmapSubtable,
127 glyf::{
128 ComponentOffsets, CompositeGlyph, CompositeGlyphArgument, CompositeGlyphComponent,
129 CompositeGlyphScale, EmptyGlyph, Glyph, Point, SimpleGlyph,
130 },
131 kern::owned::KernTable,
132 FontTableProvider, HheaTable, MaxpTable,
133 },
134 tag,
135 };
136 use azul_core::resources::{
137 GlyphOutline, GlyphOutlineOperation, OutlineCubicTo, OutlineLineTo, OutlineMoveTo,
138 OutlineQuadTo, OwnedGlyphBoundingBox,
139 };
140 use azul_css::props::basic::FontMetrics as CssFontMetrics;
141
142 pub use crate::font::mock::MockFont;
144 use crate::text3::cache::LayoutFontMetrics;
145
146 pub type GsubCache = Arc<LayoutCacheData<GSUB>>;
148 pub type GposCache = Arc<LayoutCacheData<GPOS>>;
150 pub type GlyphOutlineContours = Vec<Vec<GlyphOutlineOperation>>;
152
153 #[derive(Clone)]
161 pub struct ParsedFont {
162 pub hash: u64,
164 pub font_metrics: LayoutFontMetrics,
166 pub pdf_font_metrics: PdfFontMetrics,
168 pub num_glyphs: u16,
170 pub hhea_table: HheaTable,
172 pub hmtx_data: Vec<u8>,
174 pub vmtx_data: Vec<u8>,
176 pub maxp_table: MaxpTable,
178 pub gsub_cache: Option<GsubCache>,
180 pub gpos_cache: Option<GposCache>,
182 pub opt_gdef_table: Option<Arc<GDEFTable>>,
184 pub opt_kern_table: Option<Arc<KernTable>>,
186 pub glyph_records_decoded: BTreeMap<u16, OwnedGlyph>,
188 pub space_width: Option<usize>,
190 pub cmap_subtable: Option<OwnedCmapSubtable>,
192 pub mock: Option<Box<MockFont>>,
194 pub reverse_glyph_cache: std::collections::BTreeMap<u16, String>,
196 pub original_bytes: Vec<u8>,
198 pub original_index: usize,
200 pub index_to_cid: BTreeMap<u16, u16>,
202 pub font_type: FontType,
204 pub font_name: Option<String>,
206 }
207
208 #[derive(Debug, Clone, PartialEq)]
213 pub enum FontType {
214 TrueType,
216 OpenTypeCFF(Vec<u8>),
219 }
220
221 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
226 #[repr(C)]
227 pub struct PdfFontMetrics {
228 pub units_per_em: u16,
231 pub font_flags: u16,
233 pub x_min: i16,
235 pub y_min: i16,
237 pub x_max: i16,
239 pub y_max: i16,
241
242 pub ascender: i16,
245 pub descender: i16,
247 pub line_gap: i16,
249 pub advance_width_max: u16,
251 pub caret_slope_rise: i16,
253 pub caret_slope_run: i16,
255
256 pub x_avg_char_width: i16,
259 pub us_weight_class: u16,
261 pub us_width_class: u16,
263 pub y_strikeout_size: i16,
265 pub y_strikeout_position: i16,
267 }
268
269 impl Default for PdfFontMetrics {
270 fn default() -> Self {
271 PdfFontMetrics::zero()
272 }
273 }
274
275 impl PdfFontMetrics {
276 pub const fn zero() -> Self {
277 PdfFontMetrics {
278 units_per_em: 1000,
279 font_flags: 0,
280 x_min: 0,
281 y_min: 0,
282 x_max: 0,
283 y_max: 0,
284 ascender: 0,
285 descender: 0,
286 line_gap: 0,
287 advance_width_max: 0,
288 caret_slope_rise: 0,
289 caret_slope_run: 0,
290 x_avg_char_width: 0,
291 us_weight_class: 0,
292 us_width_class: 0,
293 y_strikeout_size: 0,
294 y_strikeout_position: 0,
295 }
296 }
297 }
298
299 #[derive(Debug, Clone)]
304 pub struct SubsetFont {
305 pub bytes: Vec<u8>,
307 pub glyph_mapping: BTreeMap<u16, (u16, char)>,
309 }
310
311 impl SubsetFont {
312 pub fn subset_text(&self, text: &str) -> String {
316 text.chars()
317 .filter_map(|c| {
318 self.glyph_mapping.values().find_map(|(ngid, ch)| {
319 if *ch == c {
320 char::from_u32(*ngid as u32)
321 } else {
322 None
323 }
324 })
325 })
326 .collect()
327 }
328 }
329
330 impl PartialEq for ParsedFont {
331 fn eq(&self, other: &Self) -> bool {
332 self.hash == other.hash
333 }
334 }
335
336 impl Eq for ParsedFont {}
337
338 const FONT_B64_START: &str = "data:font/ttf;base64,";
339
340 impl serde::Serialize for ParsedFont {
341 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
342 use base64::Engine;
343 let s = format!(
344 "{FONT_B64_START}{}",
345 base64::prelude::BASE64_STANDARD.encode(&self.to_bytes(None).unwrap_or_default())
346 );
347 s.serialize(serializer)
348 }
349 }
350
351 impl<'de> serde::Deserialize<'de> for ParsedFont {
352 fn deserialize<D: serde::Deserializer<'de>>(
353 deserializer: D,
354 ) -> Result<ParsedFont, D::Error> {
355 use base64::Engine;
356 let s = String::deserialize(deserializer)?;
357 let b64 = if s.starts_with(FONT_B64_START) {
358 let b = &s[FONT_B64_START.len()..];
359 base64::prelude::BASE64_STANDARD.decode(&b).ok()
360 } else {
361 None
362 };
363
364 let mut warnings = Vec::new();
365 ParsedFont::from_bytes(&b64.unwrap_or_default(), 0, &mut warnings).ok_or_else(|| {
366 serde::de::Error::custom(format!("Font deserialization error: {warnings:?}"))
367 })
368 }
369 }
370
371 impl fmt::Debug for ParsedFont {
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 f.debug_struct("ParsedFont")
374 .field("hash", &self.hash)
375 .field("font_metrics", &self.font_metrics)
376 .field("num_glyphs", &self.num_glyphs)
377 .field("hhea_table", &self.hhea_table)
378 .field(
379 "hmtx_data",
380 &format_args!("<{} bytes>", self.hmtx_data.len()),
381 )
382 .field("maxp_table", &self.maxp_table)
383 .field(
384 "glyph_records_decoded",
385 &format_args!("{} entries", self.glyph_records_decoded.len()),
386 )
387 .field("space_width", &self.space_width)
388 .field("cmap_subtable", &self.cmap_subtable)
389 .finish()
390 }
391 }
392
393 #[derive(Debug, Clone, PartialEq, Eq)]
395 pub struct FontParseWarning {
396 pub severity: FontParseWarningSeverity,
398 pub message: String,
400 }
401
402 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
404 pub enum FontParseWarningSeverity {
405 Info,
407 Warning,
409 Error,
411 }
412
413 impl FontParseWarning {
414 pub fn info(message: String) -> Self {
416 Self {
417 severity: FontParseWarningSeverity::Info,
418 message,
419 }
420 }
421
422 pub fn warning(message: String) -> Self {
424 Self {
425 severity: FontParseWarningSeverity::Warning,
426 message,
427 }
428 }
429
430 pub fn error(message: String) -> Self {
432 Self {
433 severity: FontParseWarningSeverity::Error,
434 message,
435 }
436 }
437 }
438
439 impl ParsedFont {
440 pub fn from_bytes(
452 font_bytes: &[u8],
453 font_index: usize,
454 warnings: &mut Vec<FontParseWarning>,
455 ) -> Option<Self> {
456 use std::{
457 collections::hash_map::DefaultHasher,
458 hash::{Hash, Hasher},
459 };
460
461 use allsorts::{
462 binary::read::ReadScope,
463 font_data::FontData,
464 tables::{
465 cmap::{owned::CmapSubtable as OwnedCmapSubtable, CmapSubtable},
466 glyf::{GlyfRecord, GlyfTable},
467 loca::{LocaOffsets, LocaTable},
468 FontTableProvider, HeadTable, HheaTable, MaxpTable,
469 },
470 tag,
471 };
472
473 let scope = ReadScope::new(font_bytes);
474 let font_file = match scope.read::<FontData<'_>>() {
475 Ok(ff) => {
476 warnings.push(FontParseWarning::info(
477 "Successfully read font data".to_string(),
478 ));
479 ff
480 }
481 Err(e) => {
482 warnings.push(FontParseWarning::error(format!(
483 "Failed to read font data: {}",
484 e
485 )));
486 return None;
487 }
488 };
489 let provider = match font_file.table_provider(font_index) {
490 Ok(p) => {
491 warnings.push(FontParseWarning::info(format!(
492 "Successfully loaded font at index {}",
493 font_index
494 )));
495 p
496 }
497 Err(e) => {
498 warnings.push(FontParseWarning::error(format!(
499 "Failed to get table provider for font index {}: {}",
500 font_index, e
501 )));
502 return None;
503 }
504 };
505
506 let font_name = provider.table_data(tag::NAME).ok().and_then(|name_data| {
508 ReadScope::new(&name_data?)
509 .read::<allsorts::tables::NameTable>()
510 .ok()
511 .and_then(|name_table| {
512 name_table.string_for_id(allsorts::tables::NameTable::POSTSCRIPT_NAME)
513 })
514 });
515
516 let head_table = provider
517 .table_data(tag::HEAD)
518 .ok()
519 .and_then(|head_data| ReadScope::new(&head_data?).read::<HeadTable>().ok())?;
520
521 let maxp_table = provider
522 .table_data(tag::MAXP)
523 .ok()
524 .and_then(|maxp_data| ReadScope::new(&maxp_data?).read::<MaxpTable>().ok())
525 .unwrap_or(MaxpTable {
526 num_glyphs: 0,
527 version1_sub_table: None,
528 });
529
530 let index_to_loc = head_table.index_to_loc_format;
531 let num_glyphs = maxp_table.num_glyphs as usize;
532
533 let loca_table = provider.table_data(tag::LOCA).ok();
534 let loca_table = loca_table
535 .as_ref()
536 .and_then(|loca_data| {
537 ReadScope::new(&loca_data.as_ref()?)
538 .read_dep::<LocaTable<'_>>((
539 num_glyphs.min(u16::MAX as usize) as u16,
540 index_to_loc,
541 ))
542 .ok()
543 })
544 .unwrap_or(LocaTable {
545 offsets: LocaOffsets::Long(allsorts::binary::read::ReadArray::empty()),
546 });
547
548 let glyf_table = provider.table_data(tag::GLYF).ok();
549 let mut glyf_table = glyf_table
550 .as_ref()
551 .and_then(|glyf_data| {
552 ReadScope::new(&glyf_data.as_ref()?)
553 .read_dep::<GlyfTable<'_>>(&loca_table)
554 .ok()
555 })
556 .unwrap_or(GlyfTable::new(Vec::new()).unwrap());
557
558 let hmtx_data = provider
559 .table_data(tag::HMTX)
560 .ok()
561 .and_then(|s| Some(s?.to_vec()))
562 .unwrap_or_default();
563
564 let vmtx_data = provider
565 .table_data(tag::VMTX)
566 .ok()
567 .and_then(|s| Some(s?.to_vec()))
568 .unwrap_or_default();
569
570 let hhea_table = provider
571 .table_data(tag::HHEA)
572 .ok()
573 .and_then(|hhea_data| ReadScope::new(&hhea_data?).read::<HheaTable>().ok())
574 .unwrap_or(unsafe { std::mem::zeroed() });
575
576 let font_metrics = LayoutFontMetrics {
578 units_per_em: if head_table.units_per_em == 0 {
579 1000
580 } else {
581 head_table.units_per_em
582 },
583 ascent: hhea_table.ascender as f32,
584 descent: hhea_table.descender as f32,
585 line_gap: hhea_table.line_gap as f32,
586 };
587
588 let pdf_font_metrics =
590 Self::parse_pdf_font_metrics(font_bytes, font_index, &head_table, &hhea_table);
591
592 let glyf_records_count = glyf_table.records().len();
595 let use_glyf_parsing = glyf_records_count > 0;
596
597 warnings.push(FontParseWarning::info(format!(
598 "Font has {} glyf records, {} total glyphs, use_glyf_parsing={}",
599 glyf_records_count, num_glyphs, use_glyf_parsing
600 )));
601
602 let glyph_records_decoded = if use_glyf_parsing {
603 warnings.push(FontParseWarning::info(
604 "Parsing glyph outlines from glyf table".to_string(),
605 ));
606 glyf_table
609 .records_mut()
610 .into_iter()
611 .enumerate()
612 .filter_map(|(glyph_index, glyph_record)| {
613 if glyph_index > (u16::MAX as usize) {
614 return None;
615 }
616
617 if let Err(_e) = glyph_record.parse() {
619 let glyph_index = glyph_index as u16;
621 let horz_advance = allsorts::glyph_info::advance(
622 &maxp_table,
623 &hhea_table,
624 &hmtx_data,
625 glyph_index,
626 )
627 .unwrap_or_default();
628
629 return Some((
631 glyph_index,
632 OwnedGlyph {
633 horz_advance,
634 bounding_box: OwnedGlyphBoundingBox {
635 min_x: 0,
636 min_y: 0,
637 max_x: horz_advance as i16,
638 max_y: 0,
639 },
640 outline: Vec::new(),
641 unresolved_composite: Vec::new(),
642 phantom_points: None,
643 },
644 ));
645 }
646
647 let glyph_index = glyph_index as u16;
648 let horz_advance = allsorts::glyph_info::advance(
649 &maxp_table,
650 &hhea_table,
651 &hmtx_data,
652 glyph_index,
653 )
654 .unwrap_or_default();
655
656 match glyph_record {
658 GlyfRecord::Present { .. } => {
659 Some((
661 glyph_index,
662 OwnedGlyph {
663 horz_advance,
664 bounding_box: OwnedGlyphBoundingBox {
665 min_x: 0,
666 min_y: 0,
667 max_x: horz_advance as i16,
668 max_y: 0,
669 },
670 outline: Vec::new(),
671 unresolved_composite: Vec::new(),
672 phantom_points: None,
673 },
674 ))
675 }
676 GlyfRecord::Parsed(g) => OwnedGlyph::from_glyph_data(g, horz_advance)
677 .map(|g| (glyph_index, g)),
678 }
679 })
680 .collect::<Vec<_>>()
681 .into_iter()
682 .collect::<BTreeMap<_, _>>()
683 } else {
684 warnings.push(FontParseWarning::info(format!(
687 "Using hmtx-only fallback for {} glyphs (CFF font or no glyf table)",
688 num_glyphs
689 )));
690 (0..num_glyphs as usize)
691 .filter_map(|glyph_index| {
692 if glyph_index > u16::MAX as usize {
693 return None;
694 }
695 let glyph_index_u16 = glyph_index as u16;
696 let horz_advance = allsorts::glyph_info::advance(
697 &maxp_table,
698 &hhea_table,
699 &hmtx_data,
700 glyph_index_u16,
701 )
702 .unwrap_or_default();
703
704 Some((
705 glyph_index_u16,
706 OwnedGlyph {
707 horz_advance,
708 bounding_box: OwnedGlyphBoundingBox {
709 min_x: 0,
710 min_y: 0,
711 max_x: horz_advance as i16,
712 max_y: 0,
713 },
714 outline: Vec::new(), unresolved_composite: Vec::new(),
716 phantom_points: None,
717 },
718 ))
719 })
720 .collect::<BTreeMap<_, _>>()
721 };
722
723 let mut glyph_records_decoded = glyph_records_decoded;
725 for _ in 0..6 {
726 let composite_glyphs_to_resolve = glyph_records_decoded
727 .iter()
728 .filter(|s| !s.1.unresolved_composite.is_empty())
729 .map(|(k, v)| (*k, v.clone()))
730 .collect::<Vec<_>>();
731
732 if composite_glyphs_to_resolve.is_empty() {
733 break;
734 }
735
736 for (k, mut v) in composite_glyphs_to_resolve {
737 resolved_glyph_components(&mut v, &glyph_records_decoded);
738 glyph_records_decoded.insert(k, v);
739 }
740 }
741
742 let mut font_data_impl = allsorts::font::Font::new(provider).ok()?;
743
744 let gsub_cache = font_data_impl.gsub_cache().ok().and_then(|s| s);
746 let gpos_cache = font_data_impl.gpos_cache().ok().and_then(|s| s);
747 let opt_gdef_table = font_data_impl.gdef_table().ok().and_then(|o| o);
748 let num_glyphs = font_data_impl.num_glyphs();
749
750 let opt_kern_table = font_data_impl
751 .kern_table()
752 .ok()
753 .and_then(|s| Some(s?.to_owned()));
754
755 let cmap_data = font_data_impl.cmap_subtable_data();
756 let cmap_subtable = ReadScope::new(cmap_data);
757 let cmap_subtable = cmap_subtable
758 .read::<CmapSubtable<'_>>()
759 .ok()
760 .and_then(|s| s.to_owned());
761
762 let mut hasher = DefaultHasher::new();
764 font_bytes.hash(&mut hasher);
765 font_index.hash(&mut hasher);
766 let hash = hasher.finish();
767
768 let mut font = ParsedFont {
769 hash,
770 font_metrics,
771 pdf_font_metrics,
772 num_glyphs,
773 hhea_table,
774 hmtx_data,
775 vmtx_data,
776 maxp_table,
777 gsub_cache,
778 gpos_cache,
779 opt_gdef_table,
780 opt_kern_table,
781 cmap_subtable,
782 glyph_records_decoded,
783 space_width: None,
784 mock: None,
785 reverse_glyph_cache: BTreeMap::new(),
786 original_bytes: font_bytes.to_vec(),
787 original_index: font_index,
788 index_to_cid: BTreeMap::new(), font_type: FontType::TrueType, font_name,
791 };
792
793 let space_width = font.get_space_width_internal();
795
796 let _ = (|| {
799 let space_gid = font.lookup_glyph_index(' ' as u32)?;
800 if font.glyph_records_decoded.contains_key(&space_gid) {
801 return None; }
803 let space_width_val = space_width?;
804 let space_record = OwnedGlyph {
805 bounding_box: OwnedGlyphBoundingBox {
806 max_x: 0,
807 max_y: 0,
808 min_x: 0,
809 min_y: 0,
810 },
811 horz_advance: space_width_val as u16,
812 outline: Vec::new(),
813 unresolved_composite: Vec::new(),
814 phantom_points: None,
815 };
816 font.glyph_records_decoded.insert(space_gid, space_record);
817 Some(())
818 })();
819
820 font.space_width = space_width;
821
822 Some(font)
823 }
824
825 fn parse_pdf_font_metrics(
827 font_bytes: &[u8],
828 font_index: usize,
829 head_table: &allsorts::tables::HeadTable,
830 hhea_table: &allsorts::tables::HheaTable,
831 ) -> PdfFontMetrics {
832 use allsorts::{
833 binary::read::ReadScope,
834 font_data::FontData,
835 tables::{os2::Os2, FontTableProvider},
836 tag,
837 };
838
839 let scope = ReadScope::new(font_bytes);
840 let font_file = scope.read::<FontData<'_>>().ok();
841 let provider = font_file
842 .as_ref()
843 .and_then(|ff| ff.table_provider(font_index).ok());
844
845 let os2_table = provider
846 .as_ref()
847 .and_then(|p| p.table_data(tag::OS_2).ok())
848 .and_then(|os2_data| {
849 let data = os2_data?;
850 let scope = ReadScope::new(&data);
851 scope.read_dep::<Os2>(data.len()).ok()
852 });
853
854 let base = PdfFontMetrics {
856 units_per_em: head_table.units_per_em,
857 font_flags: head_table.flags,
858 x_min: head_table.x_min,
859 y_min: head_table.y_min,
860 x_max: head_table.x_max,
861 y_max: head_table.y_max,
862 ascender: hhea_table.ascender,
863 descender: hhea_table.descender,
864 line_gap: hhea_table.line_gap,
865 advance_width_max: hhea_table.advance_width_max,
866 caret_slope_rise: hhea_table.caret_slope_rise,
867 caret_slope_run: hhea_table.caret_slope_run,
868 ..PdfFontMetrics::zero()
869 };
870
871 os2_table
873 .map(|os2| PdfFontMetrics {
874 x_avg_char_width: os2.x_avg_char_width,
875 us_weight_class: os2.us_weight_class,
876 us_width_class: os2.us_width_class,
877 y_strikeout_size: os2.y_strikeout_size,
878 y_strikeout_position: os2.y_strikeout_position,
879 ..base
880 })
881 .unwrap_or(base)
882 }
883
884 fn get_space_width_internal(&self) -> Option<usize> {
889 if let Some(mock) = self.mock.as_ref() {
890 return mock.space_width;
891 }
892 let glyph_index = self.lookup_glyph_index(' ' as u32)?;
893
894 allsorts::glyph_info::advance(
895 &self.maxp_table,
896 &self.hhea_table,
897 &self.hmtx_data,
898 glyph_index,
899 )
900 .ok()
901 .map(|s| s as usize)
902 }
903
904 pub fn lookup_glyph_index(&self, codepoint: u32) -> Option<u16> {
906 let cmap = self.cmap_subtable.as_ref()?;
907 cmap.map_glyph(codepoint).ok().flatten()
908 }
909
910 pub fn get_horizontal_advance(&self, glyph_index: u16) -> u16 {
912 if let Some(mock) = self.mock.as_ref() {
913 return mock.glyph_advances.get(&glyph_index).copied().unwrap_or(0);
914 }
915 self.glyph_records_decoded
916 .get(&glyph_index)
917 .map(|gi| gi.horz_advance)
918 .unwrap_or_default()
919 }
920
921 pub fn num_glyphs(&self) -> u16 {
923 self.num_glyphs
924 }
925
926 pub fn has_glyph(&self, codepoint: u32) -> bool {
928 self.lookup_glyph_index(codepoint).is_some()
929 }
930
931 pub fn get_vertical_metrics(
936 &self,
937 _glyph_id: u16,
938 ) -> Option<crate::text3::cache::VerticalMetrics> {
939 None
941 }
942
943 pub fn get_font_metrics(&self) -> crate::text3::cache::LayoutFontMetrics {
945 let descent = if self.font_metrics.descent > 0.0 {
947 self.font_metrics.descent
948 } else {
949 -self.font_metrics.descent
950 };
951
952 crate::text3::cache::LayoutFontMetrics {
953 ascent: self.font_metrics.ascent,
954 descent,
955 line_gap: self.font_metrics.line_gap,
956 units_per_em: self.font_metrics.units_per_em,
957 }
958 }
959
960 pub fn to_bytes(&self, tags: Option<&[u32]>) -> Result<Vec<u8>, String> {
966 let scope = ReadScope::new(&self.original_bytes);
967 let font_file = scope.read::<FontData<'_>>().map_err(|e| e.to_string())?;
968 let provider = font_file
969 .table_provider(self.original_index)
970 .map_err(|e| e.to_string())?;
971
972 let tags_to_use = tags.unwrap_or(&[
973 tag::CMAP,
974 tag::HEAD,
975 tag::HHEA,
976 tag::HMTX,
977 tag::MAXP,
978 tag::NAME,
979 tag::OS_2,
980 tag::POST,
981 tag::GLYF,
982 tag::LOCA,
983 ]);
984
985 whole_font(&provider, tags_to_use).map_err(|e| e.to_string())
986 }
987
988 pub fn subset(
1000 &self,
1001 glyph_ids: &[(u16, char)],
1002 cmap_target: CmapTarget,
1003 ) -> Result<(Vec<u8>, BTreeMap<u16, (u16, char)>), String> {
1004 let scope = ReadScope::new(&self.original_bytes);
1005 let font_file = scope.read::<FontData<'_>>().map_err(|e| e.to_string())?;
1006 let provider = font_file
1007 .table_provider(self.original_index)
1008 .map_err(|e| e.to_string())?;
1009
1010 let glyph_mapping: BTreeMap<u16, (u16, char)> = glyph_ids
1012 .iter()
1013 .enumerate()
1014 .map(|(new_id, &(original_id, ch))| (original_id, (new_id as u16, ch)))
1015 .collect();
1016
1017 let ids: Vec<u16> = glyph_ids.iter().map(|(id, _)| *id).collect();
1019
1020 let font_bytes = allsorts_subset(&provider, &ids, &SubsetProfile::Pdf, cmap_target)
1022 .map_err(|e| format!("Subset error: {:?}", e))?;
1023
1024 Ok((font_bytes, glyph_mapping))
1025 }
1026
1027 pub fn get_glyph_width_internal(&self, glyph_index: u16) -> Option<usize> {
1029 allsorts::glyph_info::advance(
1030 &self.maxp_table,
1031 &self.hhea_table,
1032 &self.hmtx_data,
1033 glyph_index,
1034 )
1035 .ok()
1036 .map(|s| s as usize)
1037 }
1038
1039 #[inline]
1041 pub const fn get_space_width(&self) -> Option<usize> {
1042 self.space_width
1043 }
1044
1045 pub fn cache_glyph_mapping(&mut self, glyph_id: u16, cluster_text: &str) {
1049 self.reverse_glyph_cache
1050 .insert(glyph_id, cluster_text.to_string());
1051 }
1052
1053 pub fn get_glyph_cluster_text(&self, glyph_id: u16) -> Option<&str> {
1056 self.reverse_glyph_cache.get(&glyph_id).map(|s| s.as_str())
1057 }
1058
1059 pub fn get_glyph_primary_char(&self, glyph_id: u16) -> Option<char> {
1063 self.reverse_glyph_cache
1064 .get(&glyph_id)
1065 .and_then(|text| text.chars().next())
1066 }
1067
1068 pub fn clear_glyph_cache(&mut self) {
1070 self.reverse_glyph_cache.clear();
1071 }
1072
1073 pub fn get_glyph_bbox_size(&self, glyph_index: u16) -> Option<(i32, i32)> {
1076 let g = self.glyph_records_decoded.get(&glyph_index)?;
1077 let glyph_width = g.horz_advance as i32;
1078 let glyph_height = g.bounding_box.max_y as i32 - g.bounding_box.min_y as i32;
1079 Some((glyph_width, glyph_height))
1080 }
1081 }
1082
1083 #[derive(Debug, Clone, PartialEq, PartialOrd)]
1084 struct GlyphOutlineBuilder {
1085 operations: Vec<GlyphOutlineOperation>,
1086 }
1087
1088 impl Default for GlyphOutlineBuilder {
1089 fn default() -> Self {
1090 GlyphOutlineBuilder {
1091 operations: Vec::new(),
1092 }
1093 }
1094 }
1095
1096 #[derive(Debug, Clone)]
1097 pub struct OwnedGlyph {
1098 pub bounding_box: OwnedGlyphBoundingBox,
1099 pub horz_advance: u16,
1100 pub outline: Vec<GlyphOutline>,
1101 pub unresolved_composite: Vec<CompositeGlyphComponent>,
1103 pub phantom_points: Option<[Point; 4]>,
1104 }
1105
1106 impl OwnedGlyph {
1107 pub fn from_glyph_data(glyph: &Glyph, horz_advance: u16) -> Option<Self> {
1108 let bbox = glyph.bounding_box()?;
1109 Some(Self {
1110 bounding_box: OwnedGlyphBoundingBox {
1111 max_x: bbox.x_max,
1112 max_y: bbox.y_max,
1113 min_x: bbox.x_min,
1114 min_y: bbox.y_min,
1115 },
1116 horz_advance,
1117 phantom_points: glyph.phantom_points(),
1118 unresolved_composite: match glyph {
1119 Glyph::Empty(_) => Vec::new(),
1120 Glyph::Composite(c) => c.glyphs.clone(),
1121 Glyph::Simple(s) => Vec::new(),
1122 },
1123 outline: translate_glyph_outline(glyph)
1124 .unwrap_or_default()
1125 .into_iter()
1126 .map(|ol| GlyphOutline {
1127 operations: ol.into(),
1128 })
1129 .collect(),
1130 })
1131 }
1132 }
1133
1134 fn translate_glyph_outline(glyph: &Glyph) -> Option<GlyphOutlineContours> {
1136 match glyph {
1137 Glyph::Empty(e) => translate_empty_glyph(e),
1138 Glyph::Simple(sg) => translate_simple_glyph(sg),
1139 Glyph::Composite(cg) => translate_composite_glyph(cg),
1140 }
1141 }
1142
1143 fn translate_empty_glyph(glyph: &EmptyGlyph) -> Option<GlyphOutlineContours> {
1145 let f = glyph.phantom_points?;
1146 Some(vec![vec![
1147 GlyphOutlineOperation::MoveTo(OutlineMoveTo {
1148 x: f[0].0,
1149 y: f[0].1,
1150 }),
1151 GlyphOutlineOperation::LineTo(OutlineLineTo {
1152 x: f[1].0,
1153 y: f[1].1,
1154 }),
1155 GlyphOutlineOperation::LineTo(OutlineLineTo {
1156 x: f[2].0,
1157 y: f[2].1,
1158 }),
1159 GlyphOutlineOperation::LineTo(OutlineLineTo {
1160 x: f[3].0,
1161 y: f[3].1,
1162 }),
1163 GlyphOutlineOperation::ClosePath,
1164 ]])
1165 }
1166
1167 fn translate_simple_glyph(glyph: &SimpleGlyph) -> Option<GlyphOutlineContours> {
1169 let mut outlines = Vec::new();
1170
1171 for contour in glyph.contours() {
1173 let mut operations = Vec::new();
1174 let contour_len = contour.len();
1175
1176 if contour_len == 0 {
1177 continue;
1178 }
1179
1180 let first_on_curve_idx = contour
1182 .iter()
1183 .position(|(flag, _)| flag.is_on_curve())
1184 .unwrap_or(0);
1185
1186 let (first_flag, first_point) = contour[first_on_curve_idx];
1187
1188 if !first_flag.is_on_curve() {
1190 let last_idx = contour_len - 1;
1192 let (_, last_point) = contour[last_idx];
1193 let implicit_x = (last_point.0 + first_point.0) / 2;
1194 let implicit_y = (last_point.1 + first_point.1) / 2;
1195 operations.push(GlyphOutlineOperation::MoveTo(OutlineMoveTo {
1196 x: implicit_x,
1197 y: implicit_y,
1198 }));
1199 } else {
1200 operations.push(GlyphOutlineOperation::MoveTo(OutlineMoveTo {
1201 x: first_point.0,
1202 y: first_point.1,
1203 }));
1204 }
1205
1206 let mut i = 0;
1208 while i < contour_len {
1209 let curr_idx = (first_on_curve_idx + 1 + i) % contour_len;
1210 let (curr_flag, curr_point) = contour[curr_idx];
1211 let next_idx = (curr_idx + 1) % contour_len;
1212 let (next_flag, next_point) = contour[next_idx];
1213
1214 if curr_flag.is_on_curve() {
1215 operations.push(GlyphOutlineOperation::LineTo(OutlineLineTo {
1217 x: curr_point.0,
1218 y: curr_point.1,
1219 }));
1220 i += 1;
1221 } else if next_flag.is_on_curve() {
1222 operations.push(GlyphOutlineOperation::QuadraticCurveTo(OutlineQuadTo {
1224 ctrl_1_x: curr_point.0,
1225 ctrl_1_y: curr_point.1,
1226 end_x: next_point.0,
1227 end_y: next_point.1,
1228 }));
1229 i += 2; } else {
1231 let implicit_x = (curr_point.0 + next_point.0) / 2;
1233 let implicit_y = (curr_point.1 + next_point.1) / 2;
1234
1235 operations.push(GlyphOutlineOperation::QuadraticCurveTo(OutlineQuadTo {
1236 ctrl_1_x: curr_point.0,
1237 ctrl_1_y: curr_point.1,
1238 end_x: implicit_x,
1239 end_y: implicit_y,
1240 }));
1241 i += 1; }
1243 }
1244
1245 operations.push(GlyphOutlineOperation::ClosePath);
1247 outlines.push(operations);
1248 }
1249
1250 Some(outlines)
1251 }
1252
1253 fn translate_composite_glyph(glyph: &CompositeGlyph) -> Option<GlyphOutlineContours> {
1255 let bbox = glyph.bounding_box;
1258 Some(vec![vec![
1259 GlyphOutlineOperation::MoveTo(OutlineMoveTo {
1260 x: bbox.x_min,
1261 y: bbox.y_min,
1262 }),
1263 GlyphOutlineOperation::LineTo(OutlineLineTo {
1264 x: bbox.x_max,
1265 y: bbox.y_min,
1266 }),
1267 GlyphOutlineOperation::LineTo(OutlineLineTo {
1268 x: bbox.x_max,
1269 y: bbox.y_max,
1270 }),
1271 GlyphOutlineOperation::LineTo(OutlineLineTo {
1272 x: bbox.x_min,
1273 y: bbox.y_max,
1274 }),
1275 GlyphOutlineOperation::ClosePath,
1276 ]])
1277 }
1278
1279 pub fn resolved_glyph_components(og: &mut OwnedGlyph, all_glyphs: &BTreeMap<u16, OwnedGlyph>) {
1281 let mut unresolved_composites = Vec::new();
1284 for i in og.unresolved_composite.iter() {
1285 let owned_glyph = match all_glyphs.get(&i.glyph_index) {
1286 Some(s) => s,
1287 None => {
1288 unresolved_composites.push(i.clone());
1289 continue;
1290 }
1291 };
1292 og.outline.extend_from_slice(&owned_glyph.outline);
1293 }
1294
1295 og.unresolved_composite = unresolved_composites;
1296 }
1297
1298 fn transform_component_outlines(
1299 outlines: &mut Vec<Vec<GlyphOutlineOperation>>,
1300 scale: Option<CompositeGlyphScale>,
1301 arg1: CompositeGlyphArgument,
1302 arg2: CompositeGlyphArgument,
1303 offset_type: ComponentOffsets,
1304 ) {
1305 let (offset_x, offset_y) = match (arg1, arg2) {
1307 (CompositeGlyphArgument::I16(x), CompositeGlyphArgument::I16(y)) => (x, y),
1308 (CompositeGlyphArgument::U16(x), CompositeGlyphArgument::U16(y)) => {
1309 (x as i16, y as i16)
1310 }
1311 (CompositeGlyphArgument::I8(x), CompositeGlyphArgument::I8(y)) => {
1312 (i16::from(x), i16::from(y))
1313 }
1314 (CompositeGlyphArgument::U8(x), CompositeGlyphArgument::U8(y)) => {
1315 (i16::from(x), i16::from(y))
1316 }
1317 _ => (0, 0), };
1319
1320 for outline in outlines {
1322 for op in outline.as_mut_slice() {
1323 match op {
1324 GlyphOutlineOperation::MoveTo(point) => {
1325 transform_point(point, offset_x, offset_y, scale, offset_type);
1326 }
1327 GlyphOutlineOperation::LineTo(point) => {
1328 transform_point_lineto(point, offset_x, offset_y, scale, offset_type);
1329 }
1330 GlyphOutlineOperation::QuadraticCurveTo(curve) => {
1331 transform_quad_point(curve, offset_x, offset_y, scale, offset_type);
1332 }
1333 GlyphOutlineOperation::CubicCurveTo(curve) => {
1334 transform_cubic_point(curve, offset_x, offset_y, scale, offset_type);
1335 }
1336 GlyphOutlineOperation::ClosePath => {}
1337 }
1338 }
1339 }
1340 }
1341
1342 fn transform_point(
1343 point: &mut OutlineMoveTo,
1344 offset_x: i16,
1345 offset_y: i16,
1346 scale: Option<CompositeGlyphScale>,
1347 offset_type: ComponentOffsets,
1348 ) {
1349 if let Some(scale_factor) = scale {
1351 match scale_factor {
1352 CompositeGlyphScale::Scale(s) => {
1353 let scale = f32::from(s);
1354 point.x = (point.x as f32 * scale) as i16;
1355 point.y = (point.y as f32 * scale) as i16;
1356 }
1357 CompositeGlyphScale::XY { x_scale, y_scale } => {
1358 point.x = (point.x as f32 * f32::from(x_scale)) as i16;
1359 point.y = (point.y as f32 * f32::from(y_scale)) as i16;
1360 }
1361 CompositeGlyphScale::Matrix(matrix) => {
1362 let new_x = (point.x as f32 * f32::from(matrix[0][0])
1363 + point.y as f32 * f32::from(matrix[0][1]))
1364 as i16;
1365 let new_y = (point.x as f32 * f32::from(matrix[1][0])
1366 + point.y as f32 * f32::from(matrix[1][1]))
1367 as i16;
1368 point.x = new_x;
1369 point.y = new_y;
1370 }
1371 }
1372 }
1373
1374 match offset_type {
1376 ComponentOffsets::Scaled => {
1377 point.x += offset_x;
1379 point.y += offset_y;
1380 }
1381 ComponentOffsets::Unscaled => {
1382 point.x += offset_x;
1384 point.y += offset_y;
1385 }
1386 }
1387 }
1388
1389 fn transform_point_lineto(
1391 point: &mut OutlineLineTo,
1392 offset_x: i16,
1393 offset_y: i16,
1394 scale: Option<CompositeGlyphScale>,
1395 offset_type: ComponentOffsets,
1396 ) {
1397 if let Some(scale_factor) = scale {
1400 match scale_factor {
1401 CompositeGlyphScale::Scale(s) => {
1402 let scale = f32::from(s);
1403 point.x = (point.x as f32 * scale) as i16;
1404 point.y = (point.y as f32 * scale) as i16;
1405 }
1406 CompositeGlyphScale::XY { x_scale, y_scale } => {
1407 point.x = (point.x as f32 * f32::from(x_scale)) as i16;
1408 point.y = (point.y as f32 * f32::from(y_scale)) as i16;
1409 }
1410 CompositeGlyphScale::Matrix(matrix) => {
1411 let new_x = (point.x as f32 * f32::from(matrix[0][0])
1412 + point.y as f32 * f32::from(matrix[0][1]))
1413 as i16;
1414 let new_y = (point.x as f32 * f32::from(matrix[1][0])
1415 + point.y as f32 * f32::from(matrix[1][1]))
1416 as i16;
1417 point.x = new_x;
1418 point.y = new_y;
1419 }
1420 }
1421 }
1422
1423 match offset_type {
1425 ComponentOffsets::Scaled => {
1426 point.x += offset_x;
1428 point.y += offset_y;
1429 }
1430 ComponentOffsets::Unscaled => {
1431 point.x += offset_x;
1433 point.y += offset_y;
1434 }
1435 }
1436 }
1437
1438 fn transform_quad_point(
1439 point: &mut OutlineQuadTo,
1440 offset_x: i16,
1441 offset_y: i16,
1442 scale: Option<CompositeGlyphScale>,
1443 offset_type: ComponentOffsets,
1444 ) {
1445 if let Some(scale_factor) = scale {
1447 match scale_factor {
1448 CompositeGlyphScale::Scale(s) => {
1449 let scale = f32::from(s);
1450 point.ctrl_1_x = (point.ctrl_1_x as f32 * scale) as i16;
1451 point.ctrl_1_y = (point.ctrl_1_y as f32 * scale) as i16;
1452 point.end_x = (point.end_x as f32 * scale) as i16;
1453 point.end_y = (point.end_y as f32 * scale) as i16;
1454 }
1455 CompositeGlyphScale::XY { x_scale, y_scale } => {
1456 point.ctrl_1_x = (point.ctrl_1_x as f32 * f32::from(x_scale)) as i16;
1457 point.ctrl_1_y = (point.ctrl_1_y as f32 * f32::from(y_scale)) as i16;
1458 point.end_x = (point.end_x as f32 * f32::from(x_scale)) as i16;
1459 point.end_y = (point.end_y as f32 * f32::from(y_scale)) as i16;
1460 }
1461 CompositeGlyphScale::Matrix(matrix) => {
1462 let new_ctrl_x = (point.ctrl_1_x as f32 * f32::from(matrix[0][0])
1464 + point.ctrl_1_y as f32 * f32::from(matrix[0][1]))
1465 as i16;
1466 let new_ctrl_y = (point.ctrl_1_x as f32 * f32::from(matrix[1][0])
1467 + point.ctrl_1_y as f32 * f32::from(matrix[1][1]))
1468 as i16;
1469
1470 let new_end_x = (point.end_x as f32 * f32::from(matrix[0][0])
1472 + point.end_y as f32 * f32::from(matrix[0][1]))
1473 as i16;
1474 let new_end_y = (point.end_x as f32 * f32::from(matrix[1][0])
1475 + point.end_y as f32 * f32::from(matrix[1][1]))
1476 as i16;
1477
1478 point.ctrl_1_x = new_ctrl_x;
1479 point.ctrl_1_y = new_ctrl_y;
1480 point.end_x = new_end_x;
1481 point.end_y = new_end_y;
1482 }
1483 }
1484 }
1485
1486 match offset_type {
1488 ComponentOffsets::Scaled => {
1489 point.ctrl_1_x += offset_x;
1490 point.ctrl_1_y += offset_y;
1491 point.end_x += offset_x;
1492 point.end_y += offset_y;
1493 }
1494 ComponentOffsets::Unscaled => {
1495 point.ctrl_1_x += offset_x;
1496 point.ctrl_1_y += offset_y;
1497 point.end_x += offset_x;
1498 point.end_y += offset_y;
1499 }
1500 }
1501 }
1502
1503 fn transform_cubic_point(
1504 point: &mut OutlineCubicTo,
1505 offset_x: i16,
1506 offset_y: i16,
1507 scale: Option<CompositeGlyphScale>,
1508 offset_type: ComponentOffsets,
1509 ) {
1510 if let Some(scale_factor) = scale {
1512 match scale_factor {
1513 CompositeGlyphScale::Scale(s) => {
1514 let scale = f32::from(s);
1515 point.ctrl_1_x = (point.ctrl_1_x as f32 * scale) as i16;
1516 point.ctrl_1_y = (point.ctrl_1_y as f32 * scale) as i16;
1517 point.ctrl_2_x = (point.ctrl_2_x as f32 * scale) as i16;
1518 point.ctrl_2_y = (point.ctrl_2_y as f32 * scale) as i16;
1519 point.end_x = (point.end_x as f32 * scale) as i16;
1520 point.end_y = (point.end_y as f32 * scale) as i16;
1521 }
1522 CompositeGlyphScale::XY { x_scale, y_scale } => {
1523 point.ctrl_1_x = (point.ctrl_1_x as f32 * f32::from(x_scale)) as i16;
1524 point.ctrl_1_y = (point.ctrl_1_y as f32 * f32::from(y_scale)) as i16;
1525 point.ctrl_2_x = (point.ctrl_2_x as f32 * f32::from(x_scale)) as i16;
1526 point.ctrl_2_y = (point.ctrl_2_y as f32 * f32::from(y_scale)) as i16;
1527 point.end_x = (point.end_x as f32 * f32::from(x_scale)) as i16;
1528 point.end_y = (point.end_y as f32 * f32::from(y_scale)) as i16;
1529 }
1530 CompositeGlyphScale::Matrix(matrix) => {
1531 let new_ctrl1_x = (point.ctrl_1_x as f32 * f32::from(matrix[0][0])
1533 + point.ctrl_1_y as f32 * f32::from(matrix[0][1]))
1534 as i16;
1535 let new_ctrl1_y = (point.ctrl_1_x as f32 * f32::from(matrix[1][0])
1536 + point.ctrl_1_y as f32 * f32::from(matrix[1][1]))
1537 as i16;
1538
1539 let new_ctrl2_x = (point.ctrl_2_x as f32 * f32::from(matrix[0][0])
1541 + point.ctrl_2_y as f32 * f32::from(matrix[0][1]))
1542 as i16;
1543 let new_ctrl2_y = (point.ctrl_2_x as f32 * f32::from(matrix[1][0])
1544 + point.ctrl_2_y as f32 * f32::from(matrix[1][1]))
1545 as i16;
1546
1547 let new_end_x = (point.end_x as f32 * f32::from(matrix[0][0])
1549 + point.end_y as f32 * f32::from(matrix[0][1]))
1550 as i16;
1551 let new_end_y = (point.end_x as f32 * f32::from(matrix[1][0])
1552 + point.end_y as f32 * f32::from(matrix[1][1]))
1553 as i16;
1554
1555 point.ctrl_1_x = new_ctrl1_x;
1556 point.ctrl_1_y = new_ctrl1_y;
1557 point.ctrl_2_x = new_ctrl2_x;
1558 point.ctrl_2_y = new_ctrl2_y;
1559 point.end_x = new_end_x;
1560 point.end_y = new_end_y;
1561 }
1562 }
1563 }
1564
1565 match offset_type {
1567 ComponentOffsets::Scaled => {
1568 point.ctrl_1_x += offset_x;
1569 point.ctrl_1_y += offset_y;
1570 point.ctrl_2_x += offset_x;
1571 point.ctrl_2_y += offset_y;
1572 point.end_x += offset_x;
1573 point.end_y += offset_y;
1574 }
1575 ComponentOffsets::Unscaled => {
1576 point.ctrl_1_x += offset_x;
1577 point.ctrl_1_y += offset_y;
1578 point.ctrl_2_x += offset_x;
1579 point.ctrl_2_y += offset_y;
1580 point.end_x += offset_x;
1581 point.end_y += offset_y;
1582 }
1583 }
1584 }
1585
1586 impl crate::text3::cache::ShallowClone for ParsedFont {
1589 fn shallow_clone(&self) -> Self {
1590 self.clone() }
1592 }
1593
1594 impl crate::text3::cache::ParsedFontTrait for ParsedFont {
1595 fn shape_text(
1596 &self,
1597 text: &str,
1598 script: crate::font_traits::Script,
1599 language: crate::font_traits::Language,
1600 direction: crate::font_traits::BidiDirection,
1601 style: &crate::font_traits::StyleProperties,
1602 ) -> Result<Vec<crate::font_traits::Glyph>, crate::font_traits::LayoutError> {
1603 crate::text3::default::shape_text_for_parsed_font(
1605 self, text, script, language, direction, style,
1606 )
1607 }
1608
1609 fn get_hash(&self) -> u64 {
1610 self.hash
1611 }
1612
1613 fn get_glyph_size(
1614 &self,
1615 glyph_id: u16,
1616 font_size_px: f32,
1617 ) -> Option<azul_core::geom::LogicalSize> {
1618 self.glyph_records_decoded.get(&glyph_id).map(|record| {
1619 let units_per_em = self.font_metrics.units_per_em as f32;
1620 let scale_factor = if units_per_em > 0.0 {
1621 font_size_px / units_per_em
1622 } else {
1623 0.01
1624 };
1625 let bbox = &record.bounding_box;
1626 azul_core::geom::LogicalSize {
1627 width: (bbox.max_x - bbox.min_x) as f32 * scale_factor,
1628 height: (bbox.max_y - bbox.min_y) as f32 * scale_factor,
1629 }
1630 })
1631 }
1632
1633 fn get_hyphen_glyph_and_advance(&self, font_size: f32) -> Option<(u16, f32)> {
1634 let glyph_id = self.lookup_glyph_index('-' as u32)?;
1635 let advance_units = self.get_horizontal_advance(glyph_id);
1636 let scale_factor = if self.font_metrics.units_per_em > 0 {
1637 font_size / (self.font_metrics.units_per_em as f32)
1638 } else {
1639 return None;
1640 };
1641 let scaled_advance = advance_units as f32 * scale_factor;
1642 Some((glyph_id, scaled_advance))
1643 }
1644
1645 fn get_kashida_glyph_and_advance(&self, font_size: f32) -> Option<(u16, f32)> {
1646 let glyph_id = self.lookup_glyph_index('\u{0640}' as u32)?;
1647 let advance_units = self.get_horizontal_advance(glyph_id);
1648 let scale_factor = if self.font_metrics.units_per_em > 0 {
1649 font_size / (self.font_metrics.units_per_em as f32)
1650 } else {
1651 return None;
1652 };
1653 let scaled_advance = advance_units as f32 * scale_factor;
1654 Some((glyph_id, scaled_advance))
1655 }
1656
1657 fn has_glyph(&self, codepoint: u32) -> bool {
1658 self.lookup_glyph_index(codepoint).is_some()
1659 }
1660
1661 fn get_vertical_metrics(
1662 &self,
1663 glyph_id: u16,
1664 ) -> Option<crate::text3::cache::VerticalMetrics> {
1665 None
1667 }
1668
1669 fn get_font_metrics(&self) -> crate::text3::cache::LayoutFontMetrics {
1670 self.font_metrics.clone()
1671 }
1672
1673 fn num_glyphs(&self) -> u16 {
1674 self.num_glyphs
1675 }
1676 }
1677}