1use std::{path::Path, sync::Arc};
2
3use allsorts::{
4 gpos,
5 gsub::{self, FeatureInfo, FeatureMask, Features},
6};
7use azul_core::{geom::LogicalSize, glyph::Placement};
8use azul_css::props::basic::FontRef;
9use rust_fontconfig::FcFontCache;
10
11use crate::{
12 font::parsed::ParsedFont,
13 text3::{
14 cache::{
15 BidiDirection, BidiLevel, FontManager, FontSelector, FontVariantCaps,
16 FontVariantLigatures, FontVariantNumeric, Glyph, GlyphOrientation, GlyphSource,
17 LayoutError, LayoutFontMetrics, ParsedFontTrait, Point, ShallowClone, StyleProperties,
18 TextCombineUpright, TextDecoration, TextOrientation, VerticalMetrics, WritingMode,
19 },
20 script::Script,
21 },
22};
23
24pub fn font_ref_from_bytes(
37 font_bytes: &[u8],
38 font_index: usize,
39 parse_outlines: bool,
40) -> Option<FontRef> {
41 let mut warnings = Vec::new();
43 let parsed_font = ParsedFont::from_bytes(font_bytes, font_index, &mut warnings)?;
44
45 Some(crate::parsed_font_to_font_ref(parsed_font))
46}
47
48#[derive(Debug, Default, Clone)]
53pub struct PathLoader;
54
55impl PathLoader {
56 pub fn new() -> Self {
57 PathLoader
58 }
59
60 pub fn load_from_path(&self, path: &Path, font_index: usize) -> Result<FontRef, LayoutError> {
63 let font_bytes = std::fs::read(path).map_err(|e| {
64 LayoutError::FontNotFound(FontSelector {
65 family: path.to_string_lossy().into_owned(),
66 weight: rust_fontconfig::FcWeight::Normal,
67 style: crate::text3::cache::FontStyle::Normal,
68 unicode_ranges: Vec::new(),
69 })
70 })?;
71 self.load_font(&font_bytes, font_index)
72 }
73}
74
75impl FontManager<FontRef> {
76 pub fn new_with_fc_cache(fc_cache: FcFontCache) -> Result<Self, LayoutError> {
77 FontManager::new(fc_cache)
78 }
79}
80
81impl PathLoader {
82 pub fn load_font(&self, font_bytes: &[u8], font_index: usize) -> Result<FontRef, LayoutError> {
86 font_ref_from_bytes(font_bytes, font_index, true).ok_or_else(|| {
88 LayoutError::ShapingError("Failed to parse font with allsorts".to_string())
89 })
90 }
91}
92
93impl crate::text3::cache::ShallowClone for FontRef {
97 fn shallow_clone(&self) -> Self {
98 self.clone()
100 }
101}
102
103#[inline]
105fn get_parsed_font(font_ref: &FontRef) -> &ParsedFont {
106 unsafe { &*(font_ref.get_parsed() as *const ParsedFont) }
107}
108
109impl ParsedFontTrait for FontRef {
110 fn shape_text(
111 &self,
112 text: &str,
113 script: Script,
114 language: crate::text3::script::Language,
115 direction: BidiDirection,
116 style: &StyleProperties,
117 ) -> Result<Vec<Glyph>, LayoutError> {
118 let parsed = get_parsed_font(self);
120 parsed.shape_text_for_font_ref(self, text, script, language, direction, style)
121 }
122
123 fn get_hash(&self) -> u64 {
124 get_parsed_font(self).hash
125 }
126
127 fn get_glyph_size(&self, glyph_id: u16, font_size: f32) -> Option<LogicalSize> {
128 get_parsed_font(self).get_glyph_size(glyph_id, font_size)
129 }
130
131 fn get_hyphen_glyph_and_advance(&self, font_size: f32) -> Option<(u16, f32)> {
132 get_parsed_font(self).get_hyphen_glyph_and_advance(font_size)
133 }
134
135 fn get_kashida_glyph_and_advance(&self, font_size: f32) -> Option<(u16, f32)> {
136 get_parsed_font(self).get_kashida_glyph_and_advance(font_size)
137 }
138
139 fn has_glyph(&self, codepoint: u32) -> bool {
140 get_parsed_font(self).has_glyph(codepoint)
141 }
142
143 fn get_vertical_metrics(&self, glyph_id: u16) -> Option<VerticalMetrics> {
144 get_parsed_font(self).get_vertical_metrics(glyph_id)
145 }
146
147 fn get_font_metrics(&self) -> LayoutFontMetrics {
148 get_parsed_font(self).font_metrics.clone()
149 }
150
151 fn num_glyphs(&self) -> u16 {
152 get_parsed_font(self).num_glyphs
153 }
154}
155
156pub trait FontRefExt {
160 fn get_bytes(&self) -> &[u8];
162 fn get_full_font_metrics(&self) -> azul_css::props::basic::FontMetrics;
164}
165
166impl FontRefExt for FontRef {
167 fn get_bytes(&self) -> &[u8] {
168 &get_parsed_font(self).original_bytes
169 }
170
171 fn get_full_font_metrics(&self) -> azul_css::props::basic::FontMetrics {
172 use azul_css::{OptionI16, OptionU16, OptionU32};
173
174 let parsed = get_parsed_font(self);
175 let pdf = &parsed.pdf_font_metrics;
176
177 azul_css::props::basic::FontMetrics {
179 units_per_em: pdf.units_per_em,
181 font_flags: pdf.font_flags,
182 x_min: pdf.x_min,
183 y_min: pdf.y_min,
184 x_max: pdf.x_max,
185 y_max: pdf.y_max,
186
187 ascender: pdf.ascender,
189 descender: pdf.descender,
190 line_gap: pdf.line_gap,
191 advance_width_max: pdf.advance_width_max,
192 min_left_side_bearing: 0, min_right_side_bearing: 0, x_max_extent: 0, caret_slope_rise: pdf.caret_slope_rise,
196 caret_slope_run: pdf.caret_slope_run,
197 caret_offset: 0, num_h_metrics: 0, x_avg_char_width: pdf.x_avg_char_width,
202 us_weight_class: pdf.us_weight_class,
203 us_width_class: pdf.us_width_class,
204 fs_type: 0, y_subscript_x_size: 0, y_subscript_y_size: 0, y_subscript_x_offset: 0, y_subscript_y_offset: 0, y_superscript_x_size: 0, y_superscript_y_size: 0, y_superscript_x_offset: 0, y_superscript_y_offset: 0, y_strikeout_size: pdf.y_strikeout_size,
214 y_strikeout_position: pdf.y_strikeout_position,
215 s_family_class: 0, panose: azul_css::props::basic::Panose::zero(),
217 ul_unicode_range1: 0, ul_unicode_range2: 0, ul_unicode_range3: 0, ul_unicode_range4: 0, ach_vend_id: 0, fs_selection: 0, us_first_char_index: 0, us_last_char_index: 0, s_typo_ascender: OptionI16::None,
228 s_typo_descender: OptionI16::None,
229 s_typo_line_gap: OptionI16::None,
230 us_win_ascent: OptionU16::None,
231 us_win_descent: OptionU16::None,
232
233 ul_code_page_range1: OptionU32::None,
235 ul_code_page_range2: OptionU32::None,
236
237 sx_height: OptionI16::None,
239 s_cap_height: OptionI16::None,
240 us_default_char: OptionU16::None,
241 us_break_char: OptionU16::None,
242 us_max_context: OptionU16::None,
243
244 us_lower_optical_point_size: OptionU16::None,
246 us_upper_optical_point_size: OptionU16::None,
247 }
248 }
249}
250
251impl ParsedFont {
259 fn shape_text_for_font_ref(
262 &self,
263 font_ref: &FontRef,
264 text: &str,
265 script: Script,
266 language: crate::text3::script::Language,
267 direction: BidiDirection,
268 style: &StyleProperties,
269 ) -> Result<Vec<Glyph>, LayoutError> {
270 let shaped = shape_text_internal(self, text, script, language, direction, style)?;
272
273 let font_hash = font_ref.get_hash();
275 let font_metrics = LayoutFontMetrics {
276 ascent: self.font_metrics.ascent,
277 descent: self.font_metrics.descent,
278 line_gap: self.font_metrics.line_gap,
279 units_per_em: self.font_metrics.units_per_em,
280 };
281
282 Ok(shaped
283 .into_iter()
284 .map(|g| Glyph {
285 glyph_id: g.glyph_id,
286 codepoint: g.codepoint,
287 font_hash,
288 font_metrics: font_metrics.clone(),
289 style: g.style,
290 source: g.source,
291 logical_byte_index: g.logical_byte_index,
292 logical_byte_len: g.logical_byte_len,
293 content_index: g.content_index,
294 cluster: g.cluster,
295 advance: g.advance,
296 kerning: g.kerning,
297 offset: g.offset,
298 vertical_advance: g.vertical_advance,
299 vertical_origin_y: g.vertical_origin_y,
300 vertical_bearing: g.vertical_bearing,
301 orientation: g.orientation,
302 script: g.script,
303 bidi_level: g.bidi_level,
304 })
305 .collect())
306 }
307
308 fn get_hash(&self) -> u64 {
309 self.hash
310 }
311
312 fn get_glyph_size(&self, glyph_id: u16, font_size_px: f32) -> Option<LogicalSize> {
313 self.glyph_records_decoded.get(&glyph_id).map(|record| {
314 let units_per_em = self.font_metrics.units_per_em as f32;
315 let scale_factor = if units_per_em > 0.0 {
316 font_size_px / units_per_em
317 } else {
318 0.01 };
320
321 let bbox = &record.bounding_box;
323
324 LogicalSize {
325 width: (bbox.max_x - bbox.min_x) as f32 * scale_factor,
326 height: (bbox.max_y - bbox.min_y) as f32 * scale_factor,
327 }
328 })
329 }
330
331 fn get_hyphen_glyph_and_advance(&self, font_size: f32) -> Option<(u16, f32)> {
332 let glyph_id = self.lookup_glyph_index('-' as u32)?;
333 let advance_units = self.get_horizontal_advance(glyph_id);
334 let scale_factor = if self.font_metrics.units_per_em > 0 {
335 font_size / (self.font_metrics.units_per_em as f32)
336 } else {
337 return None;
338 };
339 let scaled_advance = advance_units as f32 * scale_factor;
340 Some((glyph_id, scaled_advance))
341 }
342
343 fn get_kashida_glyph_and_advance(&self, font_size: f32) -> Option<(u16, f32)> {
344 let glyph_id = self.lookup_glyph_index('\u{0640}' as u32)?;
346 let advance_units = self.get_horizontal_advance(glyph_id);
347 let scale_factor = if self.font_metrics.units_per_em > 0 {
348 font_size / (self.font_metrics.units_per_em as f32)
349 } else {
350 return None;
351 };
352 let scaled_advance = advance_units as f32 * scale_factor;
353 Some((glyph_id, scaled_advance))
354 }
355}
356
357fn build_feature_mask_for_script(script: Script) -> FeatureMask {
369 use Script::*;
370
371 let mut mask = FeatureMask::default(); match script {
376 Arabic => {
378 mask |= FeatureMask::INIT; mask |= FeatureMask::MEDI; mask |= FeatureMask::FINA; mask |= FeatureMask::ISOL; }
385
386 Devanagari | Bengali | Gujarati | Gurmukhi | Kannada | Malayalam | Oriya | Tamil
388 | Telugu => {
389 mask |= FeatureMask::NUKT; mask |= FeatureMask::AKHN; mask |= FeatureMask::RPHF; mask |= FeatureMask::RKRF; mask |= FeatureMask::PREF; mask |= FeatureMask::BLWF; mask |= FeatureMask::ABVF; mask |= FeatureMask::HALF; mask |= FeatureMask::PSTF; mask |= FeatureMask::VATU; mask |= FeatureMask::CJCT; }
401
402 Myanmar => {
404 mask |= FeatureMask::PREF; mask |= FeatureMask::BLWF; mask |= FeatureMask::PSTF; }
408
409 Khmer => {
411 mask |= FeatureMask::PREF; mask |= FeatureMask::BLWF; mask |= FeatureMask::ABVF; mask |= FeatureMask::PSTF; }
416
417 Thai => {
419 }
422
423 Hebrew => {
425 }
428
429 Hangul => {
431 }
435
436 Ethiopic => {
438 }
441
442 Latin | Greek | Cyrillic => {
444 }
450
451 Georgian => {
453 }
455
456 Hiragana | Katakana | Mandarin => {
458 }
462
463 Sinhala => {
465 mask |= FeatureMask::AKHN; mask |= FeatureMask::RPHF; mask |= FeatureMask::VATU; }
469 }
470
471 mask
472}
473
474fn to_opentype_script_tag(script: Script) -> u32 {
476 use Script::*;
477 match script {
479 Arabic => u32::from_be_bytes(*b"arab"),
480 Bengali => u32::from_be_bytes(*b"beng"),
481 Cyrillic => u32::from_be_bytes(*b"cyrl"),
482 Devanagari => u32::from_be_bytes(*b"deva"),
483 Ethiopic => u32::from_be_bytes(*b"ethi"),
484 Georgian => u32::from_be_bytes(*b"geor"),
485 Greek => u32::from_be_bytes(*b"grek"),
486 Gujarati => u32::from_be_bytes(*b"gujr"),
487 Gurmukhi => u32::from_be_bytes(*b"guru"),
488 Hangul => u32::from_be_bytes(*b"hang"),
489 Hebrew => u32::from_be_bytes(*b"hebr"),
490 Hiragana => u32::from_be_bytes(*b"kana"),
491 Kannada => u32::from_be_bytes(*b"knda"),
492 Katakana => u32::from_be_bytes(*b"kana"),
493 Khmer => u32::from_be_bytes(*b"khmr"),
494 Latin => u32::from_be_bytes(*b"latn"),
495 Malayalam => u32::from_be_bytes(*b"mlym"),
496 Mandarin => u32::from_be_bytes(*b"hani"),
497 Myanmar => u32::from_be_bytes(*b"mymr"),
498 Oriya => u32::from_be_bytes(*b"orya"),
499 Sinhala => u32::from_be_bytes(*b"sinh"),
500 Tamil => u32::from_be_bytes(*b"taml"),
501 Telugu => u32::from_be_bytes(*b"telu"),
502 Thai => u32::from_be_bytes(*b"thai"),
503 }
504}
505
506fn parse_font_feature(feature_str: &str) -> Option<(u32, u32)> {
509 let mut parts = feature_str.split('=');
510 let tag_str = parts.next()?.trim();
511 let value_str = parts.next().unwrap_or("1").trim(); if tag_str.len() > 4 {
515 return None;
516 }
517 let padded_tag_str = format!("{:<4}", tag_str);
519
520 let tag = u32::from_be_bytes(padded_tag_str.as_bytes().try_into().ok()?);
521 let value = value_str.parse::<u32>().ok()?;
522
523 Some((tag, value))
524}
525
526fn add_variant_features(style: &StyleProperties, features: &mut Vec<FeatureInfo>) {
528 let mut add_on = |tag_str: &[u8; 4]| {
530 features.push(FeatureInfo {
531 feature_tag: u32::from_be_bytes(*tag_str),
532 alternate: None,
533 });
534 };
535
536 match style.font_variant_ligatures {
544 FontVariantLigatures::Discretionary => add_on(b"dlig"),
545 FontVariantLigatures::Historical => add_on(b"hlig"),
546 FontVariantLigatures::Contextual => add_on(b"calt"),
547 _ => {} }
549
550 match style.font_variant_caps {
552 FontVariantCaps::SmallCaps => add_on(b"smcp"),
553 FontVariantCaps::AllSmallCaps => {
554 add_on(b"c2sc");
555 add_on(b"smcp");
556 }
557 FontVariantCaps::PetiteCaps => add_on(b"pcap"),
558 FontVariantCaps::AllPetiteCaps => {
559 add_on(b"c2pc");
560 add_on(b"pcap");
561 }
562 FontVariantCaps::Unicase => add_on(b"unic"),
563 FontVariantCaps::TitlingCaps => add_on(b"titl"),
564 FontVariantCaps::Normal => {}
565 }
566
567 match style.font_variant_numeric {
569 FontVariantNumeric::LiningNums => add_on(b"lnum"),
570 FontVariantNumeric::OldstyleNums => add_on(b"onum"),
571 FontVariantNumeric::ProportionalNums => add_on(b"pnum"),
572 FontVariantNumeric::TabularNums => add_on(b"tnum"),
573 FontVariantNumeric::DiagonalFractions => add_on(b"frac"),
574 FontVariantNumeric::StackedFractions => add_on(b"afrc"),
575 FontVariantNumeric::Ordinal => add_on(b"ordn"),
576 FontVariantNumeric::SlashedZero => add_on(b"zero"),
577 FontVariantNumeric::Normal => {}
578 }
579}
580
581#[cfg(feature = "text_layout_hyphenation")]
583fn to_opentype_lang_tag(lang: hyphenation::Language) -> u32 {
584 use hyphenation::Language::*;
585 let tag_bytes = match lang {
588 Afrikaans => *b"AFK ",
589 Albanian => *b"SQI ",
590 Armenian => *b"HYE ",
591 Assamese => *b"ASM ",
592 Basque => *b"EUQ ",
593 Belarusian => *b"BEL ",
594 Bengali => *b"BEN ",
595 Bulgarian => *b"BGR ",
596 Catalan => *b"CAT ",
597 Chinese => *b"ZHS ",
598 Coptic => *b"COP ",
599 Croatian => *b"HRV ",
600 Czech => *b"CSY ",
601 Danish => *b"DAN ",
602 Dutch => *b"NLD ",
603 EnglishGB => *b"ENG ",
604 EnglishUS => *b"ENU ",
605 Esperanto => *b"ESP ",
606 Estonian => *b"ETI ",
607 Ethiopic => *b"ETI ",
608 Finnish => *b"FIN ",
609 FinnishScholastic => *b"FIN ",
610 French => *b"FRA ",
611 Friulan => *b"FRL ",
612 Galician => *b"GLC ",
613 Georgian => *b"KAT ",
614 German1901 => *b"DEU ",
615 German1996 => *b"DEU ",
616 GermanSwiss => *b"DES ",
617 GreekAncient => *b"GRC ",
618 GreekMono => *b"ELL ",
619 GreekPoly => *b"ELL ",
620 Gujarati => *b"GUJ ",
621 Hindi => *b"HIN ",
622 Hungarian => *b"HUN ",
623 Icelandic => *b"ISL ",
624 Indonesian => *b"IND ",
625 Interlingua => *b"INA ",
626 Irish => *b"IRI ",
627 Italian => *b"ITA ",
628 Kannada => *b"KAN ",
629 Kurmanji => *b"KUR ",
630 Latin => *b"LAT ",
631 LatinClassic => *b"LAT ",
632 LatinLiturgical => *b"LAT ",
633 Latvian => *b"LVI ",
634 Lithuanian => *b"LTH ",
635 Macedonian => *b"MKD ",
636 Malayalam => *b"MAL ",
637 Marathi => *b"MAR ",
638 Mongolian => *b"MNG ",
639 NorwegianBokmal => *b"NOR ",
640 NorwegianNynorsk => *b"NYN ",
641 Occitan => *b"OCI ",
642 Oriya => *b"ORI ",
643 Pali => *b"PLI ",
644 Panjabi => *b"PAN ",
645 Piedmontese => *b"PMS ",
646 Polish => *b"PLK ",
647 Portuguese => *b"PTG ",
648 Romanian => *b"ROM ",
649 Romansh => *b"RMC ",
650 Russian => *b"RUS ",
651 Sanskrit => *b"SAN ",
652 SerbianCyrillic => *b"SRB ",
653 SerbocroatianCyrillic => *b"SHC ",
654 SerbocroatianLatin => *b"SHL ",
655 SlavonicChurch => *b"CSL ",
656 Slovak => *b"SKY ",
657 Slovenian => *b"SLV ",
658 Spanish => *b"ESP ",
659 Swedish => *b"SVE ",
660 Tamil => *b"TAM ",
661 Telugu => *b"TEL ",
662 Thai => *b"THA ",
663 Turkish => *b"TRK ",
664 Turkmen => *b"TUK ",
665 Ukrainian => *b"UKR ",
666 Uppersorbian => *b"HSB ",
667 Welsh => *b"CYM ",
668 };
669 u32::from_be_bytes(tag_bytes)
670}
671
672fn shape_text_internal(
675 parsed_font: &ParsedFont,
676 text: &str,
677 script: Script,
678 language: crate::text3::script::Language,
679 direction: BidiDirection,
680 style: &StyleProperties,
681) -> Result<Vec<Glyph>, LayoutError> {
682 let script_tag = to_opentype_script_tag(script);
683 #[cfg(feature = "text_layout_hyphenation")]
684 let lang_tag = to_opentype_lang_tag(language);
685 #[cfg(not(feature = "text_layout_hyphenation"))]
686 let lang_tag = 0u32;
687
688 let mut user_features: Vec<FeatureInfo> = style
689 .font_features
690 .iter()
691 .filter_map(|s| parse_font_feature(s))
692 .map(|(tag, value)| FeatureInfo {
693 feature_tag: tag,
694 alternate: if value > 1 {
695 Some(value as usize)
696 } else {
697 None
698 },
699 })
700 .collect();
701 add_variant_features(style, &mut user_features);
702
703 let opt_gdef = parsed_font.opt_gdef_table.as_ref().map(|v| &**v);
704
705 let mut raw_glyphs: Vec<allsorts::gsub::RawGlyph<()>> = text
706 .char_indices()
707 .filter_map(|(cluster, ch)| {
708 let glyph_index = parsed_font.lookup_glyph_index(ch as u32).unwrap_or(0);
709 if cluster > u16::MAX as usize {
710 None
711 } else {
712 Some(allsorts::gsub::RawGlyph {
713 unicodes: tinyvec::tiny_vec![[char; 1] => ch],
714 glyph_index,
715 liga_component_pos: cluster as u16,
716 glyph_origin: allsorts::gsub::GlyphOrigin::Char(ch),
717 flags: allsorts::gsub::RawGlyphFlags::empty(),
718 extra_data: (),
719 variation: None,
720 })
721 }
722 })
723 .collect();
724
725 if let Some(gsub) = parsed_font.gsub_cache.as_ref() {
726 let features = if user_features.is_empty() {
727 Features::Mask(build_feature_mask_for_script(script))
728 } else {
729 Features::Custom(user_features.clone())
730 };
731
732 let dotted_circle_index = parsed_font
733 .lookup_glyph_index(allsorts::DOTTED_CIRCLE as u32)
734 .unwrap_or(0);
735 gsub::apply(
736 dotted_circle_index,
737 gsub,
738 opt_gdef,
739 script_tag,
740 Some(lang_tag),
741 &features,
742 None,
743 parsed_font.num_glyphs(),
744 &mut raw_glyphs,
745 )
746 .map_err(|e| LayoutError::ShapingError(e.to_string()))?;
747 }
748
749 let mut infos = gpos::Info::init_from_glyphs(opt_gdef, raw_glyphs);
750
751 if let Some(gpos) = parsed_font.gpos_cache.as_ref() {
752 let kern_table = parsed_font
753 .opt_kern_table
754 .as_ref()
755 .map(|kt| kt.as_borrowed());
756 let apply_kerning = kern_table.is_some();
757 gpos::apply(
758 gpos,
759 opt_gdef,
760 kern_table,
761 apply_kerning,
762 &Features::Custom(user_features),
763 None,
764 script_tag,
765 Some(lang_tag),
766 &mut infos,
767 )
768 .map_err(|e| LayoutError::ShapingError(e.to_string()))?;
769 }
770
771 let font_size = style.font_size_px;
772 let scale_factor = if parsed_font.font_metrics.units_per_em > 0 {
773 font_size / (parsed_font.font_metrics.units_per_em as f32)
774 } else {
775 0.01
776 };
777
778 let mut shaped_glyphs = Vec::new();
779 for info in infos.iter() {
780 let cluster = info.glyph.liga_component_pos as u32;
781 let source_char = text
782 .get(cluster as usize..)
783 .and_then(|s| s.chars().next())
784 .unwrap_or('\u{FFFD}');
785
786 let base_advance = parsed_font.get_horizontal_advance(info.glyph.glyph_index);
787 let advance = base_advance as f32 * scale_factor;
788 let kerning = info.kerning as f32 * scale_factor;
789
790 let (offset_x_units, offset_y_units) =
791 if let allsorts::gpos::Placement::Distance(x, y) = info.placement {
792 (x, y)
793 } else {
794 (0, 0)
795 };
796 let offset_x = offset_x_units as f32 * scale_factor;
797 let offset_y = offset_y_units as f32 * scale_factor;
798
799 let glyph = Glyph {
800 glyph_id: info.glyph.glyph_index,
801 codepoint: source_char,
802 font_hash: parsed_font.get_hash(),
803 font_metrics: LayoutFontMetrics {
804 ascent: parsed_font.font_metrics.ascent,
805 descent: parsed_font.font_metrics.descent,
806 line_gap: parsed_font.font_metrics.line_gap,
807 units_per_em: parsed_font.font_metrics.units_per_em,
808 },
809 style: Arc::new(style.clone()),
810 source: GlyphSource::Char,
811 logical_byte_index: cluster as usize,
812 logical_byte_len: source_char.len_utf8(),
813 content_index: 0,
814 cluster,
815 advance,
816 kerning,
817 offset: Point {
818 x: offset_x,
819 y: offset_y,
820 },
821 vertical_advance: 0.0,
822 vertical_origin_y: 0.0,
823 vertical_bearing: Point { x: 0.0, y: 0.0 },
824 orientation: GlyphOrientation::Horizontal,
825 script,
826 bidi_level: BidiLevel::new(if direction.is_rtl() { 1 } else { 0 }),
827 };
828 shaped_glyphs.push(glyph);
829 }
830
831 Ok(shaped_glyphs)
832}
833
834pub fn shape_text_for_parsed_font(
837 parsed_font: &ParsedFont,
838 text: &str,
839 script: Script,
840 language: crate::text3::script::Language,
841 direction: BidiDirection,
842 style: &StyleProperties,
843) -> Result<Vec<Glyph>, LayoutError> {
844 shape_text_internal(parsed_font, text, script, language, direction, style)
846}