Skip to main content

fontcore/
fontreader.rs

1use base64::{engine::general_purpose, Engine as _};
2use bin_rs::reader::{BinaryReader, BytesReader};
3use std::collections::HashMap;
4#[cfg(debug_assertions)]
5use std::fs::File;
6use std::io::{Error, ErrorKind, SeekFrom};
7use std::path::PathBuf;
8
9#[cfg(feature = "svg-fonts")]
10use crate::commands::SvgGlyphLayer;
11use crate::commands::{
12    Command as DrawCommand, FontMetrics as DrawFontMetrics, Glyph, GlyphBounds, GlyphFlow,
13    GlyphLayer, GlyphMetrics as DrawGlyphMetrics, GlyphPaint, GlyphRun, PathGlyphLayer,
14    PositionedGlyph, RasterGlyphLayer,
15};
16use crate::fontheader;
17use crate::opentype::color::sbix;
18use crate::opentype::color::svg;
19use crate::opentype::color::{colr, cpal};
20#[cfg(feature = "layout")]
21use crate::opentype::extentions::gdef;
22#[cfg(feature = "layout")]
23use crate::opentype::extentions::gpos;
24#[cfg(feature = "layout")]
25use crate::opentype::extentions::gsub;
26use crate::opentype::outline::glyf::ParsedGlyph;
27use crate::opentype::platforms::PlatformID;
28use crate::opentype::requires::cmap::CmapEncodings;
29use crate::opentype::requires::hhea::HHEA;
30use crate::opentype::requires::hmtx::LongHorMetric;
31use crate::opentype::requires::name::NameID;
32use crate::opentype::requires::vhea::VHEA;
33use crate::opentype::requires::vmtx::VerticalMetric;
34use crate::opentype::requires::*;
35use crate::opentype::{outline::*, OTFHeader};
36use crate::util::sniff_encoded_image_dimensions;
37
38#[cfg(debug_assertions)]
39use std::io::{BufWriter, Write};
40
41#[cfg(feature = "svg-fonts")]
42pub(crate) fn svg_document_to_glyph_layers(
43    document: &svg::SvgGlyphDocument,
44    scale_x: f32,
45    scale_y: f32,
46) -> Vec<GlyphLayer> {
47    let path_layers = crate::svgparse::svg_to_path_layers(&document.payload, scale_x, scale_y);
48    let keep_svg_fallback = crate::svgparse::svg_requires_svg_fallback(&document.payload);
49    if !path_layers.is_empty() {
50        let mut layers: Vec<GlyphLayer> = path_layers.into_iter().map(GlyphLayer::Path).collect();
51        if keep_svg_fallback {
52            layers.push(GlyphLayer::Svg(SvgGlyphLayer {
53                document: document.payload.clone(),
54                view_box_min_x: document.view_box_min_x * scale_x,
55                view_box_min_y: document.view_box_min_y * scale_y,
56                view_box_width: document.view_box_width * scale_x,
57                view_box_height: document.view_box_height * scale_y,
58                width: (document.view_box_width * scale_x).abs().max(1.0),
59                height: (document.view_box_height * scale_y).abs().max(1.0),
60                offset_x: 0.0,
61                offset_y: 0.0,
62            }));
63        }
64        return layers;
65    }
66
67    vec![GlyphLayer::Svg(SvgGlyphLayer {
68        document: document.payload.clone(),
69        view_box_min_x: document.view_box_min_x * scale_x,
70        view_box_min_y: document.view_box_min_y * scale_y,
71        view_box_width: document.view_box_width * scale_x,
72        view_box_height: document.view_box_height * scale_y,
73        width: (document.view_box_width * scale_x).abs().max(1.0),
74        height: (document.view_box_height * scale_y).abs().max(1.0),
75        offset_x: 0.0,
76        offset_y: 0.0,
77    })]
78}
79
80#[derive(Debug, Clone)]
81pub enum PathCommand {
82    MoveTo { x: f64, y: f64 },
83    LineTo { x: f64, y: f64 },
84    QuadTo { cx: f64, cy: f64, x: f64, y: f64 },
85    ClosePath,
86}
87
88#[derive(Debug, Clone)]
89pub enum BitmapGlyphFormat {
90    Png,
91    Jpeg,
92}
93
94#[derive(Debug, Clone)]
95pub struct BitmapGlyphCommands {
96    pub offset_x: f64,
97    pub offset_y: f64,
98    pub width: f64,
99    pub height: f64,
100    pub format: BitmapGlyphFormat,
101    pub data: Vec<u8>,
102}
103
104#[derive(Debug, Clone)]
105pub struct GlyphCommands {
106    pub ch: char,
107    pub glyph_id: usize,
108    pub origin_x: f64,
109    pub origin_y: f64,
110    pub advance_width: f64,
111    pub commands: Vec<PathCommand>,
112    pub bitmap: Option<BitmapGlyphCommands>,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum GlyphFormat {
117    OpenTypeGlyph,
118    CFF,
119    CFF2,
120    SVG,
121    Bitmap,
122    Unknown,
123}
124
125#[derive(Debug, Clone)]
126
127pub enum FontLayout {
128    Horizontal(HorizontalLayout),
129    Vertical(VerticalLayout),
130    Unknown,
131}
132
133#[derive(Debug, Clone)]
134pub struct GriphData {
135    glyph_id: usize,
136    pub(crate) open_type_glyf: Option<OpenTypeGlyph>,
137}
138
139#[derive(Debug, Clone)]
140pub struct OpenTypeGlyph {
141    layout: FontLayout,
142    glyph: FontData,
143    variation_coords: Vec<f32>,
144}
145
146#[derive(Debug, Clone)]
147pub enum FontData {
148    Glyph(glyf::Glyph),
149    ParsedGlyph(glyf::ParsedGlyph),
150    CFF(Vec<u8>),
151    CFF2(Vec<u8>),
152    SVG(String),
153    Bitmap(String, Vec<u8>),
154}
155
156#[derive(Debug, Clone)]
157pub struct Font {
158    pub font_type: fontheader::FontHeaders,
159    pub(crate) outline_format: GlyphFormat,
160    pub(crate) cmap: Option<CmapEncodings>, // must
161    pub(crate) head: Option<head::HEAD>,    // must
162    pub(crate) hhea: Option<hhea::HHEA>,    // must
163    pub(crate) hmtx: Option<hmtx::HMTX>,    // must
164    pub(crate) maxp: Option<maxp::MAXP>,    // must
165    pub(crate) name: Option<name::NAME>,    // must
166    pub(crate) name_table: Option<name::NameTable>,
167    pub(crate) os2: Option<os2::OS2>,    // must
168    pub(crate) post: Option<post::POST>, // must
169    pub(crate) fvar: Option<fvar::FVAR>,
170    pub(crate) avar: Option<avar::AVAR>,
171    pub(crate) gvar: Option<gvar::GVAR>,
172    pub(crate) loca: Option<loca::LOCA>, // openType font, CFF/CFF2 none
173    pub(crate) glyf: Option<glyf::GLYF>, // openType font, CFF/CFF2 none
174    #[cfg(feature = "cff")]
175    pub(crate) cff: Option<cff::CFF>, // CFF font, openType none
176    pub(crate) hvar: Option<hvar::HVAR>,
177    pub(crate) mvar: Option<mvar::MVAR>,
178    pub(crate) colr: Option<colr::COLR>,
179    pub(crate) cpal: Option<cpal::CPAL>,
180    #[cfg(feature = "layout")]
181    pub(crate) gdef: Option<gdef::GDEF>,
182    #[cfg(feature = "layout")]
183    pub(crate) gpos: Option<gpos::GPOS>,
184    #[cfg(feature = "layout")]
185    pub(crate) gsub: Option<gsub::GSUB>,
186    pub(crate) svg: Option<svg::SVG>,
187    pub(crate) sbix: Option<sbix::SBIX>,
188    pub(crate) vhea: Option<vhea::VHEA>,
189    pub(crate) vvar: Option<vvar::VVAR>,
190    pub(crate) vmtx: Option<vmtx::VMTX>,
191    hmtx_pos: Option<Pointer>,
192    vmtx_pos: Option<Pointer>,
193    loca_pos: Option<Pointer>, // OpenType font, CFF/CFF2 none
194    glyf_pos: Option<Pointer>, // OpenType font, CFF/CFF2 none
195    sbix_pos: Option<Pointer>,
196    pub(crate) more_fonts: Box<Vec<Font>>,
197    current_font: usize,
198}
199
200#[derive(Debug, Clone, Copy)]
201enum ResolvedTextUnit {
202    Glyph(ResolvedGlyph),
203    Newline,
204    Tab,
205}
206
207#[derive(Debug, Clone)]
208pub(crate) enum ParsedTextUnit {
209    Glyph {
210        text: String,
211        ch: char,
212        variation_selector: char,
213    },
214    Newline,
215    Tab,
216}
217
218#[derive(Debug, Clone, Copy)]
219struct ResolvedGlyph {
220    ch: char,
221    glyph_id: usize,
222    prefer_color: bool,
223    ligature_components: u16,
224}
225
226#[derive(Debug, Clone, Copy, Default)]
227struct GlyphPositionAdjustment {
228    placement_x: f32,
229    placement_y: f32,
230    advance_x: f32,
231    advance_y: f32,
232}
233
234#[derive(Debug, Clone, Copy)]
235struct GlyphAttachmentPlacement {
236    glyph_index: usize,
237    adjustment: GlyphPositionAdjustment,
238}
239
240#[derive(Debug, Clone, Copy, Default)]
241pub(crate) struct TextUnitSupport {
242    pub(crate) has_glyph: bool,
243    pub(crate) has_outline: bool,
244    pub(crate) has_color: bool,
245}
246
247impl TextUnitSupport {
248    pub(crate) fn is_supported(self) -> bool {
249        self.has_glyph && (self.has_outline || self.has_color)
250    }
251}
252
253impl Font {
254    fn empty() -> Self {
255        Self {
256            font_type: fontheader::FontHeaders::Unknown,
257            outline_format: GlyphFormat::Unknown,
258            cmap: None,
259            head: None,
260            hhea: None,
261            hmtx: None,
262            maxp: None,
263            name: None,
264            name_table: None,
265            os2: None,
266            post: None,
267            fvar: None,
268            avar: None,
269            gvar: None,
270            loca: None,
271            glyf: None,
272            #[cfg(feature = "cff")]
273            cff: None,
274            hvar: None,
275            mvar: None,
276            colr: None,
277            cpal: None,
278            #[cfg(feature = "layout")]
279            gdef: None,
280            #[cfg(feature = "layout")]
281            gpos: None,
282            #[cfg(feature = "layout")]
283            gsub: None,
284            sbix: None,
285            svg: None,
286            vhea: None,
287            vvar: None,
288            vmtx: None,
289            hmtx_pos: None,
290            vmtx_pos: None,
291            loca_pos: None,
292            glyf_pos: None,
293            sbix_pos: None,
294            more_fonts: Box::<Vec<Font>>::default(),
295            current_font: 0,
296        }
297    }
298
299    pub fn get_name_list(&self, locale: &String) -> HashMap<u16, String> {
300        let Some(name_table) = self.current_name_table() else {
301            return HashMap::new();
302        };
303        let platform_id = PlatformID::Windows;
304        let name = name_table.get_name_list(locale, platform_id);
305        if name.is_empty() {
306            let platform_id = PlatformID::Macintosh;
307            name_table.get_name_list(locale, platform_id)
308        } else {
309            name
310        }
311    }
312
313    pub fn get_font_from_file(filename: &PathBuf) -> Result<Self, Error> {
314        font_load_from_file(filename)
315    }
316
317    pub fn get_font_from_buffer(fontdata: &[u8]) -> Result<Self, Error> {
318        let mut reader = BytesReader::new(fontdata);
319        let font_type = fontheader::get_font_type(&mut reader)?;
320        if let fontheader::FontHeaders::WOFF2(header) = font_type {
321            let declared_length = header.length as usize;
322            if declared_length > fontdata.len() {
323                return Err(Error::new(
324                    ErrorKind::UnexpectedEof,
325                    format!(
326                        "WOFF2 buffer is shorter than declared length: {} < {}",
327                        fontdata.len(),
328                        declared_length
329                    ),
330                ));
331            }
332            let mut input = &fontdata[..declared_length];
333            let ttf = woff2::decode::convert_woff2_to_ttf(&mut input).map_err(|err| {
334                Error::new(
335                    ErrorKind::InvalidData,
336                    format!("Failed to decode WOFF2 font: {err}"),
337                )
338            })?;
339            return Self::get_font_from_buffer(&ttf);
340        }
341
342        reader.seek(SeekFrom::Start(0))?;
343        font_load(&mut reader)
344    }
345
346    pub(crate) fn get_h_metrix_with_coords(&self, id: usize, coordinates: &[f32]) -> LongHorMetric {
347        if self.current_font == 0 {
348            let mut metric = self
349                .hmtx
350                .as_ref()
351                .map(|hmtx| hmtx.get_metrix(id))
352                .unwrap_or(LongHorMetric {
353                    advance_width: 0,
354                    left_side_bearing: 0,
355                });
356            if let Some(hvar) = self.hvar.as_ref() {
357                if let Some(delta) = hvar.advance_offset(id, coordinates) {
358                    metric.advance_width = apply_u16_delta(metric.advance_width, delta);
359                }
360                if let Some(delta) = hvar.left_side_bearing_offset(id, coordinates) {
361                    metric.left_side_bearing = apply_i16_delta(metric.left_side_bearing, delta);
362                }
363            }
364            metric
365        } else {
366            let font = &self.more_fonts[self.current_font - 1];
367            let mut metric = font
368                .hmtx
369                .as_ref()
370                .map(|hmtx| hmtx.get_metrix(id))
371                .unwrap_or(LongHorMetric {
372                    advance_width: 0,
373                    left_side_bearing: 0,
374                });
375            if let Some(hvar) = font.hvar.as_ref() {
376                if let Some(delta) = hvar.advance_offset(id, coordinates) {
377                    metric.advance_width = apply_u16_delta(metric.advance_width, delta);
378                }
379                if let Some(delta) = hvar.left_side_bearing_offset(id, coordinates) {
380                    metric.left_side_bearing = apply_i16_delta(metric.left_side_bearing, delta);
381                }
382            }
383            metric
384        }
385    }
386
387    pub(crate) fn get_v_metrix_with_coords(
388        &self,
389        id: usize,
390        coordinates: &[f32],
391    ) -> VerticalMetric {
392        if self.current_font == 0 {
393            let mut metric = self
394                .vmtx
395                .as_ref()
396                .map(|vmtx| vmtx.get_metrix(id))
397                .unwrap_or(VerticalMetric {
398                    advance_height: 0,
399                    top_side_bearing: 0,
400                });
401            if let Some(vvar) = self.vvar.as_ref() {
402                if let Some(delta) = vvar.advance_offset(id, coordinates) {
403                    metric.advance_height = apply_u16_delta(metric.advance_height, delta);
404                }
405                if let Some(delta) = vvar.top_side_bearing_offset(id, coordinates) {
406                    metric.top_side_bearing = apply_i16_delta(metric.top_side_bearing, delta);
407                }
408            }
409            metric
410        } else {
411            let font = &self.more_fonts[self.current_font - 1];
412            let mut metric = font
413                .vmtx
414                .as_ref()
415                .map(|vmtx| vmtx.get_metrix(id))
416                .unwrap_or(VerticalMetric {
417                    advance_height: 0,
418                    top_side_bearing: 0,
419                });
420            if let Some(vvar) = font.vvar.as_ref() {
421                if let Some(delta) = vvar.advance_offset(id, coordinates) {
422                    metric.advance_height = apply_u16_delta(metric.advance_height, delta);
423                }
424                if let Some(delta) = vvar.top_side_bearing_offset(id, coordinates) {
425                    metric.top_side_bearing = apply_i16_delta(metric.top_side_bearing, delta);
426                }
427            }
428            metric
429        }
430    }
431
432    pub fn get_vertical_layout(&self, id: usize) -> Option<VerticalLayout> {
433        self.get_vertical_layout_with_coords(id, &[])
434    }
435
436    pub fn get_vertical_layout_with_coords(
437        &self,
438        id: usize,
439        coordinates: &[f32],
440    ) -> Option<VerticalLayout> {
441        let vhea = self.current_vhea();
442        if let Some(vhea) = vhea {
443            let mut v_metrix = self.get_v_metrix_with_coords(id, coordinates);
444            if let Some(variation) = self.current_gvar_variation(id, coordinates) {
445                if let Some(metric) = variation.vertical_metric {
446                    v_metrix = metric;
447                }
448            }
449            return Some(VerticalLayout {
450                tsb: v_metrix.top_side_bearing as isize,
451                advance_height: v_metrix.advance_height as isize,
452                accender: self.metric_value_i16(tag4("vasc"), vhea.get_accender(), coordinates)
453                    as isize,
454                descender: self.metric_value_i16(tag4("vdsc"), vhea.get_descender(), coordinates)
455                    as isize,
456                line_gap: self.metric_value_i16(tag4("vlgp"), vhea.get_line_gap(), coordinates)
457                    as isize,
458                vhea: vhea.clone(),
459            });
460        } else {
461            return None;
462        }
463    }
464
465    pub fn get_horizontal_layout(&self, id: usize) -> HorizontalLayout {
466        self.get_horizontal_layout_with_coords(id, &[])
467    }
468
469    pub fn get_horizontal_layout_with_coords(
470        &self,
471        id: usize,
472        coordinates: &[f32],
473    ) -> HorizontalLayout {
474        let mut h_metrix = self.get_h_metrix_with_coords(id, coordinates);
475        if let Some(variation) = self.current_gvar_variation(id, coordinates) {
476            if let Some(metric) = variation.horizontal_metric {
477                h_metrix = metric;
478            }
479        }
480        let hhea = self.current_hhea().cloned().unwrap_or(HHEA {
481            major_version: 0,
482            minor_version: 0,
483            ascender: 0,
484            descender: 0,
485            line_gap: 0,
486            advance_width_max: 0,
487            min_left_side_bearing: 0,
488            min_right_side_bearing: 0,
489            x_max_extent: 0,
490            caret_slope_rise: 0,
491            caret_slope_run: 0,
492            caret_offset: 0,
493            reserved1: 0,
494            reserved2: 0,
495            reserved3: 0,
496            reserved4: 0,
497            metric_data_format: 0,
498            number_of_hmetrics: 0,
499        });
500        let lsb = h_metrix.left_side_bearing as isize;
501        let advance_width = h_metrix.advance_width as isize;
502
503        let accender =
504            self.metric_value_i16(tag4("hasc"), hhea.get_accender(), coordinates) as isize;
505        let descender =
506            self.metric_value_i16(tag4("hdsc"), hhea.get_descender(), coordinates) as isize;
507        let line_gap =
508            self.metric_value_i16(tag4("hlgp"), hhea.get_line_gap(), coordinates) as isize;
509
510        HorizontalLayout {
511            lsb,
512            advance_width,
513            accender,
514            descender,
515            line_gap,
516            hhea,
517        }
518    }
519
520    pub fn get_glyph_from_id(&self, glyph_id: usize) -> GriphData {
521        self.get_glyph_from_id_axis(glyph_id, false)
522    }
523
524    pub fn get_layout(&self, glyph_id: usize, is_vert: bool) -> FontLayout {
525        self.get_layout_with_coords(glyph_id, is_vert, &[])
526    }
527
528    pub fn get_layout_with_coords(
529        &self,
530        glyph_id: usize,
531        is_vert: bool,
532        coordinates: &[f32],
533    ) -> FontLayout {
534        if is_vert {
535            if let Some(result) = self.get_vertical_layout_with_coords(glyph_id, coordinates) {
536                FontLayout::Vertical(result)
537            } else {
538                FontLayout::Horizontal(
539                    self.get_horizontal_layout_with_coords(glyph_id, coordinates),
540                )
541            }
542        } else {
543            FontLayout::Horizontal(self.get_horizontal_layout_with_coords(glyph_id, coordinates))
544        }
545    }
546
547    pub fn get_layout_with_options(
548        &self,
549        glyph_id: usize,
550        is_vert: bool,
551        options: &crate::commands::FontOptions<'_>,
552    ) -> FontLayout {
553        let coordinates = self.normalized_variation_coords(options);
554        self.get_layout_with_coords(glyph_id, is_vert, &coordinates)
555    }
556
557    pub fn get_glyph_with_uvs_axis(&self, ch: char, vs: char, is_vert: bool) -> GriphData {
558        let glyph_id = self.resolve_glyph_id_with_uvs(ch, vs, is_vert).unwrap_or(0);
559        self.get_glyph_from_id_axis(glyph_id, is_vert)
560    }
561
562    pub fn get_glyph_with_uvs(&self, ch: char, vs: char) -> GriphData {
563        self.get_glyph_with_uvs_axis(ch, vs, false)
564    }
565
566    pub fn get_glyph(&self, ch: char) -> GriphData {
567        self.get_glyph_with_uvs(ch, '\u{0}')
568    }
569
570    pub fn get_svg_from_id(
571        &self,
572        glyph_id: usize,
573        fontsize: f64,
574        fontunit: &str,
575    ) -> Result<String, Error> {
576        let layout = self.get_layout(glyph_id, false);
577        #[cfg(feature = "cff")]
578        if let Some(cff) = self.cff.as_ref() {
579            let string = cff.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0)?;
580            return Ok(string);
581        }
582
583        if self.current_outline_format() == GlyphFormat::CFF2 {
584            return Err(Error::new(
585                ErrorKind::Unsupported,
586                "CFF2 outlines are not supported yet",
587            ));
588        }
589
590        // utf-32
591        let pos = glyph_id as u32;
592        if let Some(glyf) = self.current_glyf() {
593            let glyph = glyf.get_glyph(pos as usize).ok_or_else(|| {
594                Error::new(
595                    std::io::ErrorKind::Other,
596                    "glyph is none,also you need --features cff".to_string(),
597                )
598            })?;
599            if let Some(sbix) = self.current_sbix() {
600                let result = sbix.get_svg(pos as u32, fontsize, fontunit, &layout, 0.0, 0.0);
601                if let Some(svg) = result {
602                    let mut string = "".to_string();
603                    #[cfg(debug_assertions)]
604                    {
605                        string += &format!("<!-- glyf id: {} -->", pos);
606                    }
607                    string += &svg;
608                    return Ok(string);
609                }
610            } else if let Some(svg) = self.current_svg_table() {
611                let result = svg.get_svg(pos as u32, fontsize, fontunit, &layout, 0.0, 0.0);
612                if let Some(svg) = result {
613                    let mut string = "".to_string();
614                    #[cfg(debug_assertions)]
615                    {
616                        string += &format!("<!-- glyf id: {} -->", pos);
617                        if let FontLayout::Horizontal(layout) = &layout {
618                            string += &format!(
619                                "<!-- layout {} {} {} {} {} -->\n",
620                                layout.lsb,
621                                layout.advance_width,
622                                layout.accender,
623                                layout.descender,
624                                layout.line_gap
625                            );
626                        }
627                    }
628                    string += &svg;
629                    return Ok(string);
630                }
631            }
632
633            let cpal = self.current_cpal();
634            let colr = self.current_colr();
635
636            if let Some(colr) = colr.as_ref() {
637                let layers = colr.get_layer_record(pos as u16);
638                if layers.is_empty() {
639                    return Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0));
640                }
641                let mut string = glyph.get_svg_heder(fontsize, fontunit, &layout);
642                #[cfg(debug_assertions)]
643                {
644                    string += &format!("\n<!-- glyf id: {} -->", pos);
645                }
646
647                for layer in layers {
648                    let glyf_id = layer.glyph_id as u32;
649                    let Some(cpal) = cpal.as_ref() else {
650                        return Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0));
651                    };
652                    let pallet = cpal.get_pallet(layer.palette_index as usize);
653                    #[cfg(debug_assertions)]
654                    {
655                        string += &format!("<!-- pallet index {} -->\n", layer.palette_index);
656                        string += &format!(
657                            "<!-- Red {} Green {} Blue {} Alpha {} -->\n",
658                            pallet.red, pallet.green, pallet.blue, pallet.alpha
659                        );
660                    }
661                    string += &format!(
662                        "<g fill=\"rgba({}, {}, {}, {})\">\n",
663                        pallet.red, pallet.green, pallet.blue, pallet.alpha
664                    );
665                    string += &glyf.get_svg_path(glyf_id as usize, &layout, 0.0, 0.0);
666                    string += "</g>\n";
667                }
668                string += "</svg>";
669                Ok(string)
670            } else {
671                #[cfg(debug_assertions)]
672                {
673                    let string = glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0);
674                    return Ok(format!("<!-- glyf id: {} -->{}", pos, string));
675                }
676                #[cfg(not(debug_assertions))]
677                Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0))
678            }
679        } else {
680            return Err(Error::new(
681                std::io::ErrorKind::Other,
682                "glyf is none".to_string(),
683            ));
684        }
685    }
686
687    pub fn get_svg_with_uvs_axis(
688        &self,
689        ch: char,
690        vs: char,
691        fontsize: f64,
692        fontunit: &str,
693        is_vert: bool,
694    ) -> Result<String, Error> {
695        // svg ?
696        // sbix ?
697        // cff ?
698
699        #[cfg(feature = "cff")]
700        if let Some(cff) = self.cff.as_ref() {
701            let glyf_data = self.get_glyph_with_uvs_axis(ch, vs, is_vert);
702            let glyph_id = glyf_data.glyph_id;
703            let layout = self.get_layout(glyph_id as usize, is_vert);
704            let string = cff.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0);
705            return string;
706        }
707
708        if self.current_outline_format() == GlyphFormat::CFF2 {
709            return Err(Error::new(
710                ErrorKind::Unsupported,
711                "CFF2 outlines are not supported yet",
712            ));
713        }
714
715        // utf-32
716        let glyf_data = self.get_glyph_with_uvs_axis(ch, vs, is_vert);
717        let glyph_id = glyf_data.glyph_id;
718
719        let open_type_glyph = glyf_data
720            .open_type_glyf
721            .as_ref()
722            .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyph is none"))?;
723        let layout = &open_type_glyph.layout;
724        match &open_type_glyph.glyph {
725            FontData::Glyph(glyph) => {
726                if let Some(sbix) = self.current_sbix() {
727                    let result =
728                        sbix.get_svg(glyph_id as u32, fontsize, fontunit, &layout, 0.0, 0.0);
729                    if let Some(svg) = result {
730                        let mut string = "".to_string();
731                        #[cfg(debug_assertions)]
732                        {
733                            string += &format!("<!-- {} glyf id: {} -->", ch, glyph_id);
734                        }
735                        string += &svg;
736                        return Ok(string);
737                    }
738                } else if let Some(svg) = self.current_svg_table() {
739                    let result =
740                        svg.get_svg(glyph_id as u32, fontsize, fontunit, &layout, 0.0, 0.0);
741                    if let Some(svg) = result {
742                        let mut string = "".to_string();
743                        #[cfg(debug_assertions)]
744                        {
745                            string += &format!("<!-- {} glyf id: {} -->", ch, glyph_id);
746                            if let FontLayout::Horizontal(layout) = layout {
747                                string += &format!(
748                                    "<!-- layout {} {} {} {} {} -->\n",
749                                    layout.lsb,
750                                    layout.advance_width,
751                                    layout.accender,
752                                    layout.descender,
753                                    layout.line_gap
754                                );
755                            } else if let FontLayout::Vertical(layout) = layout {
756                                string += &format!(
757                                    "<!-- layout vert {} {} {} {} {} -->\n",
758                                    layout.tsb,
759                                    layout.advance_height,
760                                    layout.accender,
761                                    layout.descender,
762                                    layout.line_gap
763                                );
764                            }
765                        }
766                        string += &svg;
767                        return Ok(string);
768                    }
769                }
770                let Some(glyf) = self.current_glyf() else {
771                    return Err(Error::new(std::io::ErrorKind::Other, "glyf is none"));
772                };
773
774                let cpal = self.current_cpal();
775                let colr = self.current_colr();
776
777                if let Some(colr) = colr.as_ref() {
778                    let layers = colr.get_layer_record(glyph_id as u16);
779                    if layers.is_empty() {
780                        return Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0));
781                    }
782                    let mut string = glyph.get_svg_heder(fontsize, fontunit, &layout);
783                    #[cfg(debug_assertions)]
784                    {
785                        string += &format!("\n<!-- {} glyf id: {} -->", ch, glyph_id);
786                    }
787
788                    for layer in layers {
789                        let glyf_id = layer.glyph_id as u32;
790                        let Some(cpal) = cpal.as_ref() else {
791                            return Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0));
792                        };
793                        let pallet = cpal.get_pallet(layer.palette_index as usize);
794                        #[cfg(debug_assertions)]
795                        {
796                            string += &format!("<!-- pallet index {} -->\n", layer.palette_index);
797                            string += &format!(
798                                "<!-- Red {} Green {} Blue {} Alpha {} -->\n",
799                                pallet.red, pallet.green, pallet.blue, pallet.alpha
800                            );
801                        }
802                        string += &format!(
803                            "<g fill=\"rgba({}, {}, {}, {})\">\n",
804                            pallet.red, pallet.green, pallet.blue, pallet.alpha
805                        );
806                        string += &glyf.get_svg_path(glyf_id as usize, &layout, 0.0, 0.0);
807                        string += "</g>\n";
808                    }
809                    string += "</svg>";
810                    Ok(string)
811                } else {
812                    #[cfg(debug_assertions)]
813                    {
814                        let string = glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0);
815                        return Ok(format!("<!-- {} glyf id: {} -->{}", ch, glyph_id, string));
816                    }
817                    #[cfg(not(debug_assertions))]
818                    Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0))
819                }
820            }
821            FontData::ParsedGlyph(parsed) => {
822                let mut string =
823                    glyf::Glyph::get_svg_header_from_parsed(parsed, fontsize, fontunit, layout);
824                string += &glyf::Glyph::get_svg_path_parsed(parsed, layout, 0.0, 0.0);
825                string += "\n</svg>";
826                Ok(string)
827            }
828            _ => Err(Error::new(
829                std::io::ErrorKind::Other,
830                "glyf is none".to_string(),
831            )),
832        }
833    }
834
835    pub fn get_svg_with_uvs(
836        &self,
837        ch: char,
838        vs: char,
839        fontsize: f64,
840        fontunit: &str,
841    ) -> Result<String, Error> {
842        self.get_svg_with_uvs_axis(ch, vs, fontsize, fontunit, false)
843    }
844
845    pub fn get_svg(&self, ch: char, fontsize: f64, fontunit: &str) -> Result<String, Error> {
846        self.get_svg_with_uvs(ch, '\u{0}', fontsize, fontunit)
847    }
848
849    fn current_hhea(&self) -> Result<&HHEA, Error> {
850        if self.current_font == 0 {
851            self.hhea
852                .as_ref()
853                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "hhea is none"))
854        } else {
855            self.more_fonts[self.current_font - 1]
856                .hhea
857                .as_ref()
858                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "hhea is none"))
859        }
860    }
861
862    fn current_head(&self) -> Result<&head::HEAD, Error> {
863        if self.current_font == 0 {
864            self.head
865                .as_ref()
866                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "head is none"))
867        } else {
868            self.more_fonts[self.current_font - 1]
869                .head
870                .as_ref()
871                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "head is none"))
872        }
873    }
874
875    fn current_vhea(&self) -> Option<&VHEA> {
876        if self.current_font == 0 {
877            self.vhea.as_ref()
878        } else {
879            self.more_fonts[self.current_font - 1].vhea.as_ref()
880        }
881    }
882
883    fn current_name_table(&self) -> Option<&name::NameTable> {
884        if self.current_font == 0 {
885            self.name_table.as_ref()
886        } else {
887            self.more_fonts[self.current_font - 1].name_table.as_ref()
888        }
889    }
890
891    fn current_cmap(&self) -> Result<&CmapEncodings, Error> {
892        if self.current_font == 0 {
893            self.cmap
894                .as_ref()
895                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "cmap is none"))
896        } else {
897            self.more_fonts[self.current_font - 1]
898                .cmap
899                .as_ref()
900                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "cmap is none"))
901        }
902    }
903
904    fn current_fvar(&self) -> Option<&fvar::FVAR> {
905        if self.current_font == 0 {
906            self.fvar.as_ref()
907        } else {
908            self.more_fonts[self.current_font - 1].fvar.as_ref()
909        }
910    }
911
912    fn current_avar(&self) -> Option<&avar::AVAR> {
913        if self.current_font == 0 {
914            self.avar.as_ref()
915        } else {
916            self.more_fonts[self.current_font - 1].avar.as_ref()
917        }
918    }
919
920    fn current_mvar(&self) -> Option<&mvar::MVAR> {
921        if self.current_font == 0 {
922            self.mvar.as_ref()
923        } else {
924            self.more_fonts[self.current_font - 1].mvar.as_ref()
925        }
926    }
927
928    fn current_gvar(&self) -> Option<&gvar::GVAR> {
929        if self.current_font == 0 {
930            self.gvar.as_ref()
931        } else {
932            self.more_fonts[self.current_font - 1].gvar.as_ref()
933        }
934    }
935
936    fn current_glyf(&self) -> Option<&glyf::GLYF> {
937        if self.current_font == 0 {
938            self.glyf.as_ref()
939        } else {
940            self.more_fonts[self.current_font - 1].glyf.as_ref()
941        }
942    }
943
944    fn current_outline_format(&self) -> GlyphFormat {
945        if self.current_font == 0 {
946            self.outline_format
947        } else {
948            self.more_fonts[self.current_font - 1].outline_format
949        }
950    }
951
952    fn current_colr(&self) -> Option<&colr::COLR> {
953        if self.current_font == 0 {
954            self.colr.as_ref()
955        } else {
956            self.more_fonts[self.current_font - 1].colr.as_ref()
957        }
958    }
959
960    fn current_cpal(&self) -> Option<&cpal::CPAL> {
961        if self.current_font == 0 {
962            self.cpal.as_ref()
963        } else {
964            self.more_fonts[self.current_font - 1].cpal.as_ref()
965        }
966    }
967
968    fn current_sbix(&self) -> Option<&sbix::SBIX> {
969        if self.current_font == 0 {
970            self.sbix.as_ref()
971        } else {
972            self.more_fonts[self.current_font - 1].sbix.as_ref()
973        }
974    }
975
976    fn current_svg_table(&self) -> Option<&svg::SVG> {
977        if self.current_font == 0 {
978            self.svg.as_ref()
979        } else {
980            self.more_fonts[self.current_font - 1].svg.as_ref()
981        }
982    }
983
984    #[cfg(feature = "layout")]
985    fn current_gpos(&self) -> Option<&gpos::GPOS> {
986        if self.current_font == 0 {
987            self.gpos.as_ref()
988        } else {
989            self.more_fonts[self.current_font - 1].gpos.as_ref()
990        }
991    }
992
993    #[cfg(feature = "layout")]
994    fn current_gsub(&self) -> Option<&gsub::GSUB> {
995        if self.current_font == 0 {
996            self.gsub.as_ref()
997        } else {
998            self.more_fonts[self.current_font - 1].gsub.as_ref()
999        }
1000    }
1001
1002    #[cfg(feature = "layout")]
1003    fn current_gdef(&self) -> Option<&gdef::GDEF> {
1004        if self.current_font == 0 {
1005            self.gdef.as_ref()
1006        } else {
1007            self.more_fonts[self.current_font - 1].gdef.as_ref()
1008        }
1009    }
1010
1011    #[cfg(feature = "cff")]
1012    fn current_cff(&self) -> Option<&cff::CFF> {
1013        if self.current_font == 0 {
1014            self.cff.as_ref()
1015        } else {
1016            self.more_fonts[self.current_font - 1].cff.as_ref()
1017        }
1018    }
1019
1020    fn normalized_variation_coords(&self, options: &crate::commands::FontOptions<'_>) -> Vec<f32> {
1021        let Some(fvar) = self.current_fvar() else {
1022            return Vec::new();
1023        };
1024
1025        let mut coordinates = fvar
1026            .axes
1027            .iter()
1028            .map(|axis| {
1029                let value = options
1030                    .variations
1031                    .iter()
1032                    .find(|setting| u32::from_be_bytes(setting.tag) == axis.tag)
1033                    .map(|setting| setting.value)
1034                    .unwrap_or(axis.default_value);
1035                axis.normalized_value(value)
1036            })
1037            .collect::<Vec<_>>();
1038
1039        if let Some(avar) = self.current_avar() {
1040            for index in 0..coordinates.len() {
1041                avar.map_coordinate(&mut coordinates, index);
1042            }
1043        }
1044
1045        coordinates
1046    }
1047
1048    fn metric_value_i16(&self, tag: u32, base: i16, coordinates: &[f32]) -> i16 {
1049        if coordinates.is_empty() {
1050            return base;
1051        }
1052
1053        if let Some(mvar) = self.current_mvar() {
1054            if let Some(delta) = mvar.metric_offset(tag, coordinates) {
1055                return apply_i16_delta(base, delta);
1056            }
1057        }
1058
1059        base
1060    }
1061
1062    fn get_glyph_from_id_with_options(
1063        &self,
1064        glyph_id: usize,
1065        is_vert: bool,
1066        options: &crate::commands::FontOptions<'_>,
1067    ) -> GriphData {
1068        let coordinates = self.normalized_variation_coords(options);
1069        let mut layout = self.get_layout_with_coords(glyph_id, is_vert, &coordinates);
1070
1071        #[cfg(feature = "cff")]
1072        if let Some(cff) = self.current_cff() {
1073            let string = cff.to_code_with_coords(glyph_id, &layout, &coordinates);
1074            let open_type_glyf = Some(OpenTypeGlyph {
1075                layout,
1076                glyph: FontData::CFF(string.as_bytes().to_vec()),
1077                variation_coords: coordinates.clone(),
1078            });
1079
1080            return GriphData {
1081                glyph_id,
1082                open_type_glyf,
1083            };
1084        }
1085
1086        let open_type_glyph = match self.current_outline_format() {
1087            GlyphFormat::OpenTypeGlyph => {
1088                let glyf = self
1089                    .current_glyf()
1090                    .expect("glyf outline format should expose glyf table");
1091                let glyph = glyf
1092                    .get_glyph(glyph_id)
1093                    .expect("glyph id should resolve inside glyf table");
1094                let glyph =
1095                    if let Some(variation) = self.current_gvar_variation(glyph_id, &coordinates) {
1096                        Self::apply_varied_metrics_to_layout(
1097                            &mut layout,
1098                            variation.horizontal_metric.as_ref(),
1099                            variation.vertical_metric.as_ref(),
1100                        );
1101                        FontData::ParsedGlyph(variation.parsed)
1102                    } else if self.current_gvar().is_some() {
1103                        if let Some(parsed) =
1104                            self.current_gvar_component_varied_parsed(glyph_id, &coordinates)
1105                        {
1106                            FontData::ParsedGlyph(parsed)
1107                        } else {
1108                            FontData::Glyph(glyph.clone())
1109                        }
1110                    } else {
1111                        FontData::Glyph(glyph.clone())
1112                    };
1113                OpenTypeGlyph {
1114                    layout,
1115                    glyph,
1116                    variation_coords: coordinates.clone(),
1117                }
1118            }
1119            GlyphFormat::CFF2 => OpenTypeGlyph {
1120                layout,
1121                glyph: FontData::CFF2(Vec::new()),
1122                variation_coords: coordinates.clone(),
1123            },
1124            _ => OpenTypeGlyph {
1125                layout,
1126                glyph: FontData::CFF2(Vec::new()),
1127                variation_coords: coordinates.clone(),
1128            },
1129        };
1130
1131        GriphData {
1132            glyph_id,
1133            open_type_glyf: Some(open_type_glyph),
1134        }
1135    }
1136
1137    fn current_gvar_variation(
1138        &self,
1139        glyph_id: usize,
1140        coordinates: &[f32],
1141    ) -> Option<gvar::GlyphVariationResult> {
1142        let gvar = self.current_gvar()?;
1143        let component_varied = self.current_gvar_component_varied_parsed(glyph_id, coordinates)?;
1144
1145        let horizontal_metric = Some(self.get_h_metrix_with_coords(glyph_id, coordinates));
1146        let vertical_metric = self
1147            .current_vhea()
1148            .map(|_| self.get_v_metrix_with_coords(glyph_id, coordinates));
1149
1150        gvar.apply_to_parsed_glyph_with_metrics(
1151            glyph_id,
1152            coordinates,
1153            &component_varied,
1154            horizontal_metric,
1155            vertical_metric,
1156        )
1157    }
1158
1159    fn current_gvar_component_varied_parsed(
1160        &self,
1161        glyph_id: usize,
1162        coordinates: &[f32],
1163    ) -> Option<ParsedGlyph> {
1164        let gvar = self.current_gvar()?;
1165        let glyf = self.current_glyf()?;
1166        let glyph = glyf.get_glyph(glyph_id)?;
1167        let raw_parsed = glyph.parse();
1168        if raw_parsed.number_of_contours < 0 {
1169            glyf.parse_glyph_with_variation(glyph_id, &|component_glyph_id, parsed| {
1170                if component_glyph_id == glyph_id {
1171                    None
1172                } else {
1173                    gvar.apply_to_parsed_glyph(component_glyph_id, coordinates, parsed)
1174                }
1175            })
1176            .or(Some(raw_parsed))
1177        } else {
1178            Some(raw_parsed)
1179        }
1180    }
1181
1182    fn apply_varied_metrics_to_layout(
1183        layout: &mut FontLayout,
1184        horizontal_metric: Option<&LongHorMetric>,
1185        vertical_metric: Option<&VerticalMetric>,
1186    ) {
1187        match layout {
1188            FontLayout::Horizontal(current) => {
1189                if let Some(metric) = horizontal_metric {
1190                    current.lsb = metric.left_side_bearing as isize;
1191                    current.advance_width = metric.advance_width as isize;
1192                }
1193            }
1194            FontLayout::Vertical(current) => {
1195                if let Some(metric) = vertical_metric {
1196                    current.tsb = metric.top_side_bearing as isize;
1197                    current.advance_height = metric.advance_height as isize;
1198                }
1199            }
1200            FontLayout::Unknown => {}
1201        }
1202    }
1203
1204    fn get_glyph_from_id_axis(&self, glyph_id: usize, is_vert: bool) -> GriphData {
1205        self.get_glyph_from_id_with_options(
1206            glyph_id,
1207            is_vert,
1208            &crate::commands::FontOptions::from_parsed(self),
1209        )
1210    }
1211
1212    fn resolve_glyph_id_with_uvs(&self, ch: char, vs: char, is_vert: bool) -> Result<usize, Error> {
1213        let glyph_id = self
1214            .current_cmap()?
1215            .get_glyph_position_from_uvs(ch as u32, vs as u32) as usize;
1216
1217        #[cfg(feature = "layout")]
1218        {
1219            if is_vert {
1220                if let Some(gsub) = self.current_gsub() {
1221                    return Ok(gsub
1222                        .lookup_vertical(glyph_id as u16)
1223                        .unwrap_or(glyph_id as u16) as usize);
1224                }
1225            }
1226        }
1227
1228        #[cfg(not(feature = "layout"))]
1229        let _ = is_vert;
1230
1231        Ok(glyph_id)
1232    }
1233
1234    fn is_variation_selector(ch: char) -> bool {
1235        (0xfe00..=0xfe0f).contains(&(ch as u32)) || (0xE0100..=0xE01EF).contains(&(ch as u32))
1236    }
1237
1238    fn is_emoji_modifier(ch: char) -> bool {
1239        (0x1F3FB..=0x1F3FF).contains(&(ch as u32))
1240    }
1241
1242    fn is_zero_width_joiner(ch: char) -> bool {
1243        ch == '\u{200D}'
1244    }
1245
1246    fn is_keycap_mark(ch: char) -> bool {
1247        ch == '\u{20E3}'
1248    }
1249
1250    fn is_combining_mark(ch: char) -> bool {
1251        matches!(
1252            ch as u32,
1253            0x0300..=0x036F
1254                | 0x0483..=0x0489
1255                | 0x0591..=0x05BD
1256                | 0x05BF
1257                | 0x05C1..=0x05C2
1258                | 0x05C4..=0x05C5
1259                | 0x05C7
1260                | 0x0610..=0x061A
1261                | 0x064B..=0x065F
1262                | 0x0670
1263                | 0x06D6..=0x06DC
1264                | 0x06DF..=0x06E4
1265                | 0x06E7..=0x06E8
1266                | 0x06EA..=0x06ED
1267                | 0x0711
1268                | 0x0730..=0x074A
1269                | 0x07A6..=0x07B0
1270                | 0x07EB..=0x07F3
1271                | 0x0816..=0x0819
1272                | 0x081B..=0x0823
1273                | 0x0825..=0x0827
1274                | 0x0829..=0x082D
1275                | 0x0859..=0x085B
1276                | 0x08D3..=0x08E1
1277                | 0x08E3..=0x08FF
1278                | 0x0F18..=0x0F19
1279                | 0x0F35
1280                | 0x0F37
1281                | 0x0F39
1282                | 0x0F71..=0x0F7E
1283                | 0x0F80..=0x0F84
1284                | 0x0F86..=0x0F87
1285                | 0x0F8D..=0x0FBC
1286                | 0x1AB0..=0x1AFF
1287                | 0x1DC0..=0x1DFF
1288                | 0x20D0..=0x20FF
1289                | 0x3099..=0x309A
1290                | 0xFE20..=0xFE2F
1291        )
1292    }
1293
1294    fn is_regional_indicator(ch: char) -> bool {
1295        (0x1F1E6..=0x1F1FF).contains(&(ch as u32))
1296    }
1297
1298    fn is_tag_character(ch: char) -> bool {
1299        (0xE0020..=0xE007E).contains(&(ch as u32)) || ch == '\u{E007F}'
1300    }
1301
1302    fn is_default_emoji_scalar(ch: char) -> bool {
1303        matches!(ch as u32, 0x1F000..=0x1FAFF | 0x2600..=0x27BF)
1304    }
1305
1306    fn text_prefers_color_glyph(text: &str) -> bool {
1307        let mut saw_emoji_scalar = false;
1308        for ch in text.chars() {
1309            if Self::is_variation_selector(ch)
1310                || Self::is_emoji_modifier(ch)
1311                || Self::is_zero_width_joiner(ch)
1312                || Self::is_keycap_mark(ch)
1313                || Self::is_regional_indicator(ch)
1314                || Self::is_tag_character(ch)
1315            {
1316                return true;
1317            }
1318            if Self::is_default_emoji_scalar(ch) {
1319                saw_emoji_scalar = true;
1320            }
1321        }
1322        saw_emoji_scalar
1323    }
1324
1325    fn extend_cluster_suffix(chars: &[char], index: &mut usize, text: &mut String) {
1326        while *index < chars.len() {
1327            let ch = chars[*index];
1328            if Self::is_variation_selector(ch)
1329                || Self::is_emoji_modifier(ch)
1330                || Self::is_keycap_mark(ch)
1331                || Self::is_tag_character(ch)
1332                || Self::is_combining_mark(ch)
1333            {
1334                text.push(ch);
1335                *index += 1;
1336            } else {
1337                break;
1338            }
1339        }
1340    }
1341
1342    fn cluster_glyph_scalars(text: &str) -> Vec<(char, char)> {
1343        let chars: Vec<char> = text.chars().collect();
1344        let mut glyphs = Vec::new();
1345        let mut index = 0usize;
1346
1347        while index < chars.len() {
1348            let ch = chars[index];
1349            if Self::is_variation_selector(ch) {
1350                index += 1;
1351                continue;
1352            }
1353
1354            let mut variation_selector = '\0';
1355            if index + 1 < chars.len() && Self::is_variation_selector(chars[index + 1]) {
1356                variation_selector = chars[index + 1];
1357                index += 1;
1358            }
1359
1360            glyphs.push((ch, variation_selector));
1361            index += 1;
1362        }
1363
1364        glyphs
1365    }
1366
1367    fn parse_text_units(text: &str) -> Vec<ParsedTextUnit> {
1368        let chars: Vec<char> = text.chars().collect();
1369        let mut units = Vec::new();
1370        let mut index = 0;
1371
1372        while index < chars.len() {
1373            let ch = chars[index];
1374            match ch {
1375                '\r' => {
1376                    index += 1;
1377                }
1378                '\n' => {
1379                    units.push(ParsedTextUnit::Newline);
1380                    index += 1;
1381                }
1382                '\t' => {
1383                    units.push(ParsedTextUnit::Tab);
1384                    index += 1;
1385                }
1386                _ if Self::is_variation_selector(ch) => {
1387                    index += 1;
1388                }
1389                _ => {
1390                    let mut text = String::new();
1391                    text.push(ch);
1392                    let mut variation_selector = '\0';
1393                    if index + 1 < chars.len() && Self::is_variation_selector(chars[index + 1]) {
1394                        variation_selector = chars[index + 1];
1395                        text.push(variation_selector);
1396                        index += 1;
1397                    }
1398
1399                    index += 1;
1400                    Self::extend_cluster_suffix(&chars, &mut index, &mut text);
1401
1402                    if Self::is_regional_indicator(ch)
1403                        && index < chars.len()
1404                        && Self::is_regional_indicator(chars[index])
1405                    {
1406                        text.push(chars[index]);
1407                        index += 1;
1408                        Self::extend_cluster_suffix(&chars, &mut index, &mut text);
1409                    }
1410
1411                    while index < chars.len() && Self::is_zero_width_joiner(chars[index]) {
1412                        text.push(chars[index]);
1413                        index += 1;
1414                        if index >= chars.len() {
1415                            break;
1416                        }
1417                        text.push(chars[index]);
1418                        index += 1;
1419                        Self::extend_cluster_suffix(&chars, &mut index, &mut text);
1420                    }
1421
1422                    units.push(ParsedTextUnit::Glyph {
1423                        text,
1424                        ch,
1425                        variation_selector,
1426                    });
1427                }
1428            }
1429        }
1430
1431        units
1432    }
1433
1434    pub(crate) fn parse_text_units_for_fallback(text: &str) -> Vec<ParsedTextUnit> {
1435        Self::parse_text_units(text)
1436    }
1437
1438    #[cfg(feature = "layout")]
1439    fn apply_gsub_sequence_stages(
1440        &self,
1441        glyphs: &mut Vec<(usize, usize)>,
1442        locale: Option<&str>,
1443        is_right_to_left: bool,
1444        font_variant: crate::commands::FontVariant,
1445    ) {
1446        let Some(gsub) = self.current_gsub() else {
1447            return;
1448        };
1449
1450        // Keep the shaping order explicit:
1451        // 1. canonical composition / decomposition
1452        // 2. locale / variant specific substitutions
1453        // 3. RTL joining and contextual forms
1454        gsub.apply_ccmp_sequence(glyphs);
1455        gsub.apply_variant_sequence(glyphs, locale, font_variant);
1456        if is_right_to_left {
1457            gsub.apply_joining_sequence(glyphs, locale);
1458            gsub.apply_rtl_contextual_sequence(glyphs, locale);
1459        }
1460    }
1461
1462    #[cfg(feature = "layout")]
1463    fn apply_gsub_ligature_stage(
1464        &self,
1465        output: &mut Vec<ResolvedTextUnit>,
1466        expanded_glyphs: &[ResolvedGlyph],
1467        locale: Option<&str>,
1468        is_right_to_left: bool,
1469    ) {
1470        let Some(gsub) = self.current_gsub() else {
1471            output.extend(expanded_glyphs.iter().copied().map(ResolvedTextUnit::Glyph));
1472            return;
1473        };
1474
1475        const MAX_LIGATURE_COMPONENTS: usize = 8;
1476        let glyph_ids: Vec<usize> = expanded_glyphs.iter().map(|glyph| glyph.glyph_id).collect();
1477        let mut index = 0;
1478        while index < expanded_glyphs.len() {
1479            let max_len = (expanded_glyphs.len() - index).min(MAX_LIGATURE_COMPONENTS);
1480            let mut matched = None;
1481            for len in (2..=max_len).rev() {
1482                if is_right_to_left {
1483                    if let Some(glyph_id) =
1484                        gsub.lookup_rlig_sequence(&glyph_ids[index..index + len], locale)
1485                    {
1486                        matched = Some((glyph_id, len));
1487                        break;
1488                    }
1489                }
1490                if let Some(glyph_id) = gsub.lookup_liga_sequence(&glyph_ids[index..index + len]) {
1491                    matched = Some((glyph_id, len));
1492                    break;
1493                }
1494            }
1495
1496            if let Some((glyph_id, len)) = matched {
1497                output.push(ResolvedTextUnit::Glyph(ResolvedGlyph {
1498                    ch: expanded_glyphs[index].ch,
1499                    glyph_id,
1500                    prefer_color: expanded_glyphs[index].prefer_color,
1501                    ligature_components: len as u16,
1502                }));
1503                index += len;
1504            } else {
1505                output.push(ResolvedTextUnit::Glyph(expanded_glyphs[index]));
1506                index += 1;
1507            }
1508        }
1509    }
1510
1511    fn flush_shaped_glyphs(
1512        &self,
1513        output: &mut Vec<ResolvedTextUnit>,
1514        glyphs: &mut Vec<ResolvedGlyph>,
1515        locale: Option<&str>,
1516        is_right_to_left: bool,
1517        font_variant: crate::commands::FontVariant,
1518    ) {
1519        #[cfg(not(feature = "layout"))]
1520        let _ = (locale, is_right_to_left, font_variant);
1521
1522        if glyphs.is_empty() {
1523            return;
1524        }
1525
1526        #[cfg(feature = "layout")]
1527        if self.current_gsub().is_some() {
1528            let mut ccmp_glyphs = glyphs
1529                .iter()
1530                .enumerate()
1531                .map(|(source_index, glyph)| (glyph.glyph_id, source_index))
1532                .collect::<Vec<_>>();
1533            self.apply_gsub_sequence_stages(
1534                &mut ccmp_glyphs,
1535                locale,
1536                is_right_to_left,
1537                font_variant,
1538            );
1539            let expanded_glyphs = ccmp_glyphs
1540                .into_iter()
1541                .map(|(glyph_id, source_index)| ResolvedGlyph {
1542                    ch: glyphs[source_index].ch,
1543                    glyph_id,
1544                    prefer_color: glyphs[source_index].prefer_color,
1545                    ligature_components: glyphs[source_index].ligature_components,
1546                })
1547                .collect::<Vec<_>>();
1548            self.apply_gsub_ligature_stage(output, &expanded_glyphs, locale, is_right_to_left);
1549            glyphs.clear();
1550            return;
1551        }
1552
1553        output.extend(glyphs.iter().copied().map(ResolvedTextUnit::Glyph));
1554        glyphs.clear();
1555    }
1556
1557    fn shape_text_units(
1558        &self,
1559        text: &str,
1560        is_vert: bool,
1561        is_right_to_left: bool,
1562        locale: Option<&str>,
1563        font_variant: crate::commands::FontVariant,
1564    ) -> Result<Vec<ResolvedTextUnit>, Error> {
1565        #[cfg(not(feature = "layout"))]
1566        let _ = (locale, is_right_to_left, font_variant);
1567
1568        let mut output = Vec::new();
1569        let mut pending_glyphs = Vec::new();
1570
1571        for unit in Self::parse_text_units(text) {
1572            match unit {
1573                ParsedTextUnit::Newline => {
1574                    self.flush_shaped_glyphs(
1575                        &mut output,
1576                        &mut pending_glyphs,
1577                        locale,
1578                        is_right_to_left,
1579                        font_variant,
1580                    );
1581                    output.push(ResolvedTextUnit::Newline);
1582                }
1583                ParsedTextUnit::Tab => {
1584                    self.flush_shaped_glyphs(
1585                        &mut output,
1586                        &mut pending_glyphs,
1587                        locale,
1588                        is_right_to_left,
1589                        font_variant,
1590                    );
1591                    output.push(ResolvedTextUnit::Tab);
1592                }
1593                ParsedTextUnit::Glyph { text, .. } => {
1594                    let prefer_color = Self::text_prefers_color_glyph(&text);
1595                    for (ch, variation_selector) in Self::cluster_glyph_scalars(&text) {
1596                        let glyph_id =
1597                            self.resolve_glyph_id_with_uvs(ch, variation_selector, is_vert)?;
1598                        #[cfg(feature = "layout")]
1599                        let glyph_id = if let Some(locale) = locale {
1600                            if let Some(gsub) = self.current_gsub() {
1601                                gsub.lookup_locale(glyph_id, locale)
1602                            } else {
1603                                glyph_id
1604                            }
1605                        } else {
1606                            glyph_id
1607                        };
1608                        pending_glyphs.push(ResolvedGlyph {
1609                            ch,
1610                            glyph_id,
1611                            prefer_color,
1612                            ligature_components: 1,
1613                        });
1614                    }
1615                }
1616            }
1617        }
1618
1619        self.flush_shaped_glyphs(
1620            &mut output,
1621            &mut pending_glyphs,
1622            locale,
1623            is_right_to_left,
1624            font_variant,
1625        );
1626        Ok(output)
1627    }
1628
1629    pub(crate) fn supports_text_unit(
1630        &self,
1631        unit: &ParsedTextUnit,
1632        text_direction: crate::commands::TextDirection,
1633        locale: Option<&str>,
1634        font_variant: crate::commands::FontVariant,
1635    ) -> bool {
1636        self.text_unit_support(unit, text_direction, locale, font_variant)
1637            .is_supported()
1638    }
1639
1640    pub(crate) fn text_unit_support(
1641        &self,
1642        unit: &ParsedTextUnit,
1643        text_direction: crate::commands::TextDirection,
1644        locale: Option<&str>,
1645        font_variant: crate::commands::FontVariant,
1646    ) -> TextUnitSupport {
1647        #[cfg(not(feature = "layout"))]
1648        let _ = (locale, font_variant);
1649
1650        match unit {
1651            ParsedTextUnit::Newline | ParsedTextUnit::Tab => TextUnitSupport {
1652                has_glyph: true,
1653                has_outline: true,
1654                has_color: false,
1655            },
1656            ParsedTextUnit::Glyph { text, .. } => {
1657                let is_vert = text_direction.is_vertical();
1658                let is_right_to_left = text_direction.is_right_to_left();
1659                let Ok(shaped_units) =
1660                    self.shape_text_units(text, is_vert, is_right_to_left, locale, font_variant)
1661                else {
1662                    return TextUnitSupport::default();
1663                };
1664
1665                if !shaped_units
1666                    .iter()
1667                    .any(|unit| matches!(unit, ResolvedTextUnit::Glyph(_)))
1668                {
1669                    return TextUnitSupport::default();
1670                }
1671
1672                let mut support = TextUnitSupport::default();
1673                for unit in shaped_units {
1674                    let ResolvedTextUnit::Glyph(glyph) = unit else {
1675                        continue;
1676                    };
1677                    if glyph.glyph_id == 0 {
1678                        return TextUnitSupport::default();
1679                    }
1680
1681                    support.has_glyph = true;
1682
1683                    #[cfg(feature = "cff")]
1684                    if self.current_cff().is_some() {
1685                        support.has_outline = true;
1686                    }
1687
1688                    if self
1689                        .current_glyf()
1690                        .and_then(|glyf| glyf.get_glyph(glyph.glyph_id))
1691                        .is_some()
1692                    {
1693                        support.has_outline = true;
1694                    }
1695
1696                    if self
1697                        .current_colr()
1698                        .map(|colr| !colr.get_layer_record(glyph.glyph_id as u16).is_empty())
1699                        .unwrap_or(false)
1700                    {
1701                        support.has_color = true;
1702                    }
1703
1704                    if self
1705                        .current_sbix()
1706                        .and_then(|sbix| sbix.get_raster_glyph(glyph.glyph_id as u32, 16.0, "px"))
1707                        .is_some()
1708                    {
1709                        support.has_color = true;
1710                    }
1711
1712                    if self
1713                        .current_svg_table()
1714                        .map(|svg| svg.has_glyph(glyph.glyph_id as u32))
1715                        .unwrap_or(false)
1716                    {
1717                        support.has_color = true;
1718                    }
1719
1720                    if !support.has_outline && !support.has_color {
1721                        return TextUnitSupport::default();
1722                    }
1723                }
1724
1725                support
1726            }
1727        }
1728    }
1729
1730    fn push_svg_html_unit(
1731        &self,
1732        svgs: &mut Vec<String>,
1733        unit: ParsedTextUnit,
1734        fontsize: f64,
1735        fontunit: &str,
1736        is_vert: bool,
1737    ) -> Result<(), Error> {
1738        match unit {
1739            ParsedTextUnit::Newline => {
1740                svgs.push("<br>".to_string());
1741            }
1742            ParsedTextUnit::Tab => {
1743                svgs.push(
1744                    "<span style=\"width: 4em; display: inline-block;\"></span>\n".to_string(),
1745                );
1746            }
1747            ParsedTextUnit::Glyph {
1748                text,
1749                ch,
1750                variation_selector,
1751            } => {
1752                let svg = if text.chars().count() > 2
1753                    || text.contains('\u{200D}')
1754                    || text
1755                        .chars()
1756                        .filter(|ch| Self::is_regional_indicator(*ch))
1757                        .count()
1758                        > 1
1759                {
1760                    self.text2svg(&text, fontsize, fontunit)?
1761                } else {
1762                    self.get_svg_with_uvs_axis(ch, variation_selector, fontsize, fontunit, is_vert)?
1763                };
1764                svgs.push(svg);
1765            }
1766        }
1767        Ok(())
1768    }
1769
1770    #[cfg(test)]
1771    pub(crate) fn debug_shape_glyph_ids(
1772        &self,
1773        text: &str,
1774        locale: Option<&str>,
1775    ) -> Result<Vec<usize>, Error> {
1776        let mut glyph_ids = Vec::new();
1777        for unit in self.shape_text_units(
1778            text,
1779            false,
1780            false,
1781            locale,
1782            crate::commands::FontVariant::Normal,
1783        )? {
1784            if let ResolvedTextUnit::Glyph(glyph) = unit {
1785                glyph_ids.push(glyph.glyph_id);
1786            }
1787        }
1788        Ok(glyph_ids)
1789    }
1790
1791    #[cfg(test)]
1792    #[allow(dead_code)]
1793    pub(crate) fn debug_shape_glyph_ids_with_direction(
1794        &self,
1795        text: &str,
1796        locale: Option<&str>,
1797        is_right_to_left: bool,
1798    ) -> Result<Vec<usize>, Error> {
1799        let mut glyph_ids = Vec::new();
1800        for unit in self.shape_text_units(
1801            text,
1802            false,
1803            is_right_to_left,
1804            locale,
1805            crate::commands::FontVariant::Normal,
1806        )? {
1807            if let ResolvedTextUnit::Glyph(glyph) = unit {
1808                glyph_ids.push(glyph.glyph_id);
1809            }
1810        }
1811        Ok(glyph_ids)
1812    }
1813
1814    #[cfg(test)]
1815    #[allow(dead_code)]
1816    pub(crate) fn debug_shape_glyph_ids_with_variant(
1817        &self,
1818        text: &str,
1819        locale: Option<&str>,
1820        font_variant: crate::commands::FontVariant,
1821    ) -> Result<Vec<usize>, Error> {
1822        let mut glyph_ids = Vec::new();
1823        for unit in self.shape_text_units(text, false, false, locale, font_variant)? {
1824            if let ResolvedTextUnit::Glyph(glyph) = unit {
1825                glyph_ids.push(glyph.glyph_id);
1826            }
1827        }
1828        Ok(glyph_ids)
1829    }
1830
1831    fn default_line_height(&self) -> Result<f64, Error> {
1832        self.default_line_height_with_options(&crate::commands::FontOptions::from_parsed(self))
1833    }
1834
1835    fn default_line_height_with_options(
1836        &self,
1837        options: &crate::commands::FontOptions<'_>,
1838    ) -> Result<f64, Error> {
1839        let hhea = self.current_hhea()?;
1840        let coordinates = self.normalized_variation_coords(options);
1841        let ascender = self.metric_value_i16(tag4("hasc"), hhea.get_accender(), &coordinates);
1842        let descender = self.metric_value_i16(tag4("hdsc"), hhea.get_descender(), &coordinates);
1843        let line_gap = self.metric_value_i16(tag4("hlgp"), hhea.get_line_gap(), &coordinates);
1844        Ok((ascender - descender + line_gap) as f64)
1845    }
1846
1847    #[allow(dead_code)]
1848    fn glyph_unit_at(units: &[ResolvedTextUnit], index: usize) -> Option<ResolvedGlyph> {
1849        match units.get(index) {
1850            Some(ResolvedTextUnit::Glyph(glyph)) => Some(*glyph),
1851            _ => None,
1852        }
1853    }
1854
1855    fn resolved_glyph_can_use_outline(
1856        &self,
1857        open_type_glyph: &OpenTypeGlyph,
1858        glyph_id: usize,
1859    ) -> bool {
1860        if self
1861            .current_colr()
1862            .map(|colr| !colr.get_layer_record(glyph_id as u16).is_empty())
1863            .unwrap_or(false)
1864        {
1865            return true;
1866        }
1867
1868        #[cfg(feature = "cff")]
1869        if self.current_cff().is_some() {
1870            return true;
1871        }
1872
1873        if self.current_outline_format() == GlyphFormat::CFF2 {
1874            return false;
1875        }
1876
1877        matches!(
1878            open_type_glyph.glyph,
1879            FontData::Glyph(_) | FontData::ParsedGlyph(_)
1880        )
1881    }
1882
1883    #[cfg(feature = "svg-fonts")]
1884    fn build_svg_layers(
1885        &self,
1886        glyph_id: usize,
1887        layout: &FontLayout,
1888        scale_x: f32,
1889        scale_y: f32,
1890    ) -> Option<Vec<GlyphLayer>> {
1891        let document = self
1892            .current_svg_table()?
1893            .get_glyph_document(glyph_id as u32, layout)?;
1894        Some(svg_document_to_glyph_layers(&document, scale_x, scale_y))
1895    }
1896
1897    fn pair_adjustment_for_index(
1898        &self,
1899        units: &[ResolvedTextUnit],
1900        index: usize,
1901        locale: Option<&str>,
1902        is_vertical: bool,
1903        scale_x: f32,
1904        scale_y: f32,
1905    ) -> GlyphPositionAdjustment {
1906        #[cfg(not(feature = "layout"))]
1907        {
1908            let _ = (units, index, locale, is_vertical, scale_x, scale_y);
1909            GlyphPositionAdjustment::default()
1910        }
1911
1912        #[cfg(feature = "layout")]
1913        {
1914            let Some(gpos) = self.current_gpos() else {
1915                return GlyphPositionAdjustment::default();
1916            };
1917            let Some(current) = Self::glyph_unit_at(units, index) else {
1918                return GlyphPositionAdjustment::default();
1919            };
1920
1921            let mut adjustment = GlyphPositionAdjustment::default();
1922            let previous_index = self.find_previous_spacing_glyph_index(units, index);
1923            let next_index = self.find_next_spacing_glyph_index(units, index);
1924
1925            if let Some(previous_index) = previous_index {
1926                if let Some(previous) = Self::glyph_unit_at(units, previous_index) {
1927                    if let Some(pair) = gpos.lookup_pair_adjustment(
1928                        previous.glyph_id as u16,
1929                        current.glyph_id as u16,
1930                        is_vertical,
1931                        locale,
1932                    ) {
1933                        adjustment.placement_x += pair.second.x_placement as f32 * scale_x;
1934                        adjustment.placement_y += pair.second.y_placement as f32 * scale_y;
1935                        adjustment.advance_x += pair.second.x_advance as f32 * scale_x;
1936                        adjustment.advance_y += pair.second.y_advance as f32 * scale_y;
1937                    }
1938                }
1939            }
1940
1941            if let Some(next_index) = next_index {
1942                if let Some(next) = Self::glyph_unit_at(units, next_index) {
1943                    if let Some(pair) = gpos.lookup_pair_adjustment(
1944                        current.glyph_id as u16,
1945                        next.glyph_id as u16,
1946                        is_vertical,
1947                        locale,
1948                    ) {
1949                        adjustment.placement_x += pair.first.x_placement as f32 * scale_x;
1950                        adjustment.placement_y += pair.first.y_placement as f32 * scale_y;
1951                        adjustment.advance_x += pair.first.x_advance as f32 * scale_x;
1952                        adjustment.advance_y += pair.first.y_advance as f32 * scale_y;
1953                    }
1954                }
1955            }
1956
1957            adjustment
1958        }
1959    }
1960
1961    #[cfg(feature = "layout")]
1962    fn mark_attachment_for_index(
1963        &self,
1964        units: &[ResolvedTextUnit],
1965        unit_glyph_indices: &[Option<usize>],
1966        index: usize,
1967        locale: Option<&str>,
1968        scale_x: f32,
1969        scale_y: f32,
1970    ) -> Option<GlyphAttachmentPlacement> {
1971        let gpos = self.current_gpos()?;
1972        let current = Self::glyph_unit_at(units, index)?;
1973        if let Some(previous_mark_unit_index) = self.find_previous_mark_glyph_index(units, index) {
1974            let previous_mark = Self::glyph_unit_at(units, previous_mark_unit_index)?;
1975            let glyph_index = unit_glyph_indices
1976                .get(previous_mark_unit_index)
1977                .and_then(|glyph_index| *glyph_index)?;
1978            if let Some(adjustment) = gpos.lookup_mark_to_mark_adjustment(
1979                previous_mark.glyph_id as u16,
1980                current.glyph_id as u16,
1981                locale,
1982            ) {
1983                return Some(GlyphAttachmentPlacement {
1984                    glyph_index,
1985                    adjustment: GlyphPositionAdjustment {
1986                        placement_x: adjustment.x_placement as f32 * scale_x,
1987                        placement_y: adjustment.y_placement as f32 * scale_y,
1988                        advance_x: 0.0,
1989                        advance_y: 0.0,
1990                    },
1991                });
1992            }
1993        }
1994
1995        let base_index = self.find_previous_spacing_glyph_index(units, index)?;
1996        let base = Self::glyph_unit_at(units, base_index)?;
1997        let glyph_index = unit_glyph_indices
1998            .get(base_index)
1999            .and_then(|glyph_index| *glyph_index)?;
2000        let ligature_component_index = base.ligature_components.saturating_sub(1) as usize;
2001        let adjustment = if base.ligature_components > 1 {
2002            gpos.lookup_mark_to_ligature_adjustment(
2003                base.glyph_id as u16,
2004                current.glyph_id as u16,
2005                ligature_component_index,
2006                locale,
2007            )
2008            .or_else(|| {
2009                gpos.lookup_mark_to_base_adjustment(
2010                    base.glyph_id as u16,
2011                    current.glyph_id as u16,
2012                    locale,
2013                )
2014            })?
2015        } else {
2016            gpos.lookup_mark_to_base_adjustment(
2017                base.glyph_id as u16,
2018                current.glyph_id as u16,
2019                locale,
2020            )?
2021        };
2022
2023        Some(GlyphAttachmentPlacement {
2024            glyph_index,
2025            adjustment: GlyphPositionAdjustment {
2026                placement_x: adjustment.x_placement as f32 * scale_x,
2027                placement_y: adjustment.y_placement as f32 * scale_y,
2028                advance_x: 0.0,
2029                advance_y: 0.0,
2030            },
2031        })
2032    }
2033
2034    #[cfg(not(feature = "layout"))]
2035    fn mark_attachment_for_index(
2036        &self,
2037        units: &[ResolvedTextUnit],
2038        unit_glyph_indices: &[Option<usize>],
2039        index: usize,
2040        locale: Option<&str>,
2041        scale_x: f32,
2042        scale_y: f32,
2043    ) -> Option<GlyphAttachmentPlacement> {
2044        let _ = (units, unit_glyph_indices, index, locale, scale_x, scale_y);
2045        None
2046    }
2047
2048    #[cfg(feature = "layout")]
2049    fn find_previous_mark_glyph_index(
2050        &self,
2051        units: &[ResolvedTextUnit],
2052        index: usize,
2053    ) -> Option<usize> {
2054        let cursor = index.checked_sub(1)?;
2055        Self::glyph_unit_at(units, cursor).map(|_| cursor)
2056    }
2057
2058    #[cfg(feature = "layout")]
2059    fn find_previous_spacing_glyph_index(
2060        &self,
2061        units: &[ResolvedTextUnit],
2062        index: usize,
2063    ) -> Option<usize> {
2064        let mut cursor = index.checked_sub(1)?;
2065        loop {
2066            match Self::glyph_unit_at(units, cursor) {
2067                Some(glyph)
2068                    if !self.gdef_is_ignored_for_pair_positioning(glyph.glyph_id as u16) =>
2069                {
2070                    return Some(cursor);
2071                }
2072                Some(_) => {
2073                    cursor = cursor.checked_sub(1)?;
2074                }
2075                None => return None,
2076            }
2077        }
2078    }
2079
2080    #[cfg(feature = "layout")]
2081    fn find_next_spacing_glyph_index(
2082        &self,
2083        units: &[ResolvedTextUnit],
2084        index: usize,
2085    ) -> Option<usize> {
2086        let mut cursor = index + 1;
2087        while cursor < units.len() {
2088            match Self::glyph_unit_at(units, cursor) {
2089                Some(glyph)
2090                    if !self.gdef_is_ignored_for_pair_positioning(glyph.glyph_id as u16) =>
2091                {
2092                    return Some(cursor);
2093                }
2094                Some(_) => {
2095                    cursor += 1;
2096                }
2097                None => return None,
2098            }
2099        }
2100        None
2101    }
2102
2103    #[cfg(feature = "layout")]
2104    fn gdef_is_ignored_for_pair_positioning(&self, glyph_id: u16) -> bool {
2105        self.current_gdef()
2106            .map(|gdef| gdef.is_mark_glyph(glyph_id))
2107            .unwrap_or(false)
2108    }
2109
2110    #[cfg(not(feature = "layout"))]
2111    fn gdef_is_ignored_for_pair_positioning(&self, glyph_id: u16) -> bool {
2112        let _ = glyph_id;
2113        false
2114    }
2115
2116    #[cfg(feature = "layout")]
2117    fn gdef_supports_mark_attachment(&self, glyph_id: u16) -> bool {
2118        self.current_gdef()
2119            .map(|gdef| {
2120                gdef.is_mark_glyph(glyph_id)
2121                    && (gdef.mark_attachment_class(glyph_id).is_some()
2122                        || gdef.has_attach_points(glyph_id))
2123            })
2124            .unwrap_or(false)
2125    }
2126
2127    #[cfg(not(feature = "layout"))]
2128    fn gdef_supports_mark_attachment(&self, glyph_id: u16) -> bool {
2129        let _ = glyph_id;
2130        false
2131    }
2132
2133    pub fn text2glyph_run(
2134        &self,
2135        text: &str,
2136        options: &crate::commands::FontOptions<'_>,
2137    ) -> Result<GlyphRun, Error> {
2138        let _ = self.current_head()?;
2139
2140        if !options.font_size.is_finite() || options.font_size <= 0.0 {
2141            return Err(Error::new(
2142                ErrorKind::InvalidInput,
2143                "font_size must be a positive finite value",
2144            ));
2145        }
2146
2147        let default_line_height = self.default_line_height_with_options(options)? as f32;
2148        let scale_y = options.font_size / default_line_height.max(1.0);
2149        let scale_x = scale_y * options.font_stretch.0.max(0.0);
2150        let line_height = options.line_height.unwrap_or(options.font_size);
2151        let is_vertical = options.text_direction.is_vertical();
2152        let is_right_to_left = options.text_direction.is_right_to_left();
2153        if !line_height.is_finite() || line_height <= 0.0 {
2154            return Err(Error::new(
2155                ErrorKind::InvalidInput,
2156                "line_height must be a positive finite value",
2157            ));
2158        }
2159
2160        let mut glyphs: Vec<PositionedGlyph> = Vec::new();
2161        let mut cursor_x = 0.0f32;
2162        let mut cursor_y = 0.0f32;
2163        let tab_advance = line_height;
2164        let shaped_units = self.shape_text_units(
2165            text,
2166            is_vertical,
2167            is_right_to_left,
2168            options.locale,
2169            options.font_variant,
2170        )?;
2171        let mut unit_glyph_indices = vec![None; shaped_units.len()];
2172
2173        for (index, unit) in shaped_units.iter().enumerate() {
2174            match *unit {
2175                ResolvedTextUnit::Newline => {
2176                    if is_vertical {
2177                        cursor_x -= line_height;
2178                        cursor_y = 0.0;
2179                    } else {
2180                        cursor_x = 0.0;
2181                        cursor_y += line_height;
2182                    }
2183                }
2184                ResolvedTextUnit::Tab => {
2185                    if is_vertical {
2186                        cursor_y += tab_advance * 4.0;
2187                    } else if is_right_to_left {
2188                        cursor_x -= tab_advance * 4.0;
2189                    } else {
2190                        cursor_x += tab_advance * 4.0;
2191                    }
2192                }
2193                ResolvedTextUnit::Glyph(resolved) => {
2194                    let glyph_data = self.get_glyph_from_id_with_options(
2195                        resolved.glyph_id,
2196                        is_vertical,
2197                        options,
2198                    );
2199                    let open_type_glyph = glyph_data
2200                        .open_type_glyf
2201                        .as_ref()
2202                        .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyph is none"))?;
2203                    let glyph_id = glyph_data.glyph_id;
2204                    let can_use_outline =
2205                        self.resolved_glyph_can_use_outline(open_type_glyph, glyph_id);
2206                    #[cfg(feature = "svg-fonts")]
2207                    let can_use_svg = self
2208                        .current_svg_table()
2209                        .map(|svg| svg.has_glyph(glyph_id as u32))
2210                        .unwrap_or(false);
2211
2212                    let layers = if let Some(sbix) = self.current_sbix() {
2213                        if let Some(bitmap) =
2214                            sbix.get_raster_glyph(glyph_id as u32, options.font_size, "px")
2215                        {
2216                            if resolved.prefer_color || !can_use_outline {
2217                                let mut raster = RasterGlyphLayer::from_encoded(bitmap.glyph_data);
2218                                raster.offset_x = bitmap.offset_x * options.font_stretch.0.max(0.0);
2219                                raster.offset_y = bitmap.offset_y;
2220                                raster.width = bitmap.width;
2221                                raster.height = bitmap.height;
2222                                vec![GlyphLayer::Raster(raster)]
2223                            } else {
2224                                self.build_outline_layers(
2225                                    glyph_id,
2226                                    open_type_glyph,
2227                                    scale_x,
2228                                    scale_y,
2229                                    resolved.ch,
2230                                )?
2231                            }
2232                        } else {
2233                            #[cfg(feature = "svg-fonts")]
2234                            {
2235                                if can_use_svg && (resolved.prefer_color || !can_use_outline) {
2236                                    self.build_svg_layers(
2237                                        glyph_id,
2238                                        &open_type_glyph.layout,
2239                                        scale_x,
2240                                        scale_y,
2241                                    )
2242                                    .ok_or_else(|| {
2243                                        Error::new(
2244                                            ErrorKind::Unsupported,
2245                                            format!(
2246                                                "failed to extract SVG glyph layer for {:?}",
2247                                                resolved.ch
2248                                            ),
2249                                        )
2250                                    })?
2251                                } else {
2252                                    self.build_outline_layers(
2253                                        glyph_id,
2254                                        open_type_glyph,
2255                                        scale_x,
2256                                        scale_y,
2257                                        resolved.ch,
2258                                    )?
2259                                }
2260                            }
2261                            #[cfg(not(feature = "svg-fonts"))]
2262                            {
2263                                self.build_outline_layers(
2264                                    glyph_id,
2265                                    open_type_glyph,
2266                                    scale_x,
2267                                    scale_y,
2268                                    resolved.ch,
2269                                )?
2270                            }
2271                        }
2272                    } else {
2273                        #[cfg(feature = "svg-fonts")]
2274                        {
2275                            if can_use_svg && (resolved.prefer_color || !can_use_outline) {
2276                                self.build_svg_layers(
2277                                    glyph_id,
2278                                    &open_type_glyph.layout,
2279                                    scale_x,
2280                                    scale_y,
2281                                )
2282                                .ok_or_else(|| {
2283                                    Error::new(
2284                                        ErrorKind::Unsupported,
2285                                        format!(
2286                                            "failed to extract SVG glyph layer for {:?}",
2287                                            resolved.ch
2288                                        ),
2289                                    )
2290                                })?
2291                            } else {
2292                                self.build_outline_layers(
2293                                    glyph_id,
2294                                    open_type_glyph,
2295                                    scale_x,
2296                                    scale_y,
2297                                    resolved.ch,
2298                                )?
2299                            }
2300                        }
2301                        #[cfg(not(feature = "svg-fonts"))]
2302                        {
2303                            self.build_outline_layers(
2304                                glyph_id,
2305                                open_type_glyph,
2306                                scale_x,
2307                                scale_y,
2308                                resolved.ch,
2309                            )?
2310                        }
2311                    };
2312
2313                    let mut metrics =
2314                        glyph_metrics_from_layout(&open_type_glyph.layout, scale_x, scale_y);
2315                    let adjustment = self.pair_adjustment_for_index(
2316                        &shaped_units,
2317                        index,
2318                        options.locale,
2319                        is_vertical,
2320                        scale_x,
2321                        scale_y,
2322                    );
2323                    let mark_attachment = self.mark_attachment_for_index(
2324                        &shaped_units,
2325                        &unit_glyph_indices,
2326                        index,
2327                        options.locale,
2328                        scale_x,
2329                        scale_y,
2330                    );
2331                    metrics.advance_x += adjustment.advance_x;
2332                    metrics.advance_y += adjustment.advance_y;
2333                    metrics.bounds = glyph_layers_bounds(&layers);
2334                    let uses_gpos_mark_attachment = mark_attachment.is_some();
2335                    let gdef_attach_glyph_index = if !uses_gpos_mark_attachment
2336                        && self.gdef_supports_mark_attachment(glyph_id as u16)
2337                    {
2338                        self.find_previous_spacing_glyph_index(&shaped_units, index)
2339                            .and_then(|unit_index| unit_glyph_indices.get(unit_index))
2340                            .and_then(|glyph_index| *glyph_index)
2341                    } else {
2342                        None
2343                    };
2344                    let uses_gdef_mark_attachment =
2345                        !uses_gpos_mark_attachment && gdef_attach_glyph_index.is_some();
2346                    let uses_mark_attachment =
2347                        uses_gpos_mark_attachment || uses_gdef_mark_attachment;
2348                    let attach_glyph_index = mark_attachment
2349                        .map(|attachment| attachment.glyph_index)
2350                        .or(gdef_attach_glyph_index);
2351                    let origin_x = if uses_mark_attachment {
2352                        let base_x = glyphs[attach_glyph_index.expect("checked some")].x;
2353                        let placement_x = mark_attachment
2354                            .map(|mark| mark.adjustment.placement_x)
2355                            .unwrap_or(0.0)
2356                            + adjustment.placement_x;
2357                        base_x + placement_x
2358                    } else if is_right_to_left && !is_vertical {
2359                        cursor_x - metrics.advance_x + adjustment.placement_x
2360                    } else {
2361                        cursor_x + adjustment.placement_x
2362                    };
2363                    let origin_y = if uses_mark_attachment {
2364                        let base_y = glyphs[attach_glyph_index.expect("checked some")].y;
2365                        let placement_y = mark_attachment
2366                            .map(|mark| mark.adjustment.placement_y)
2367                            .unwrap_or(0.0)
2368                            + adjustment.placement_y;
2369                        base_y + placement_y
2370                    } else {
2371                        cursor_y + adjustment.placement_y
2372                    };
2373                    if uses_mark_attachment {
2374                        metrics.advance_x = 0.0;
2375                        metrics.advance_y = 0.0;
2376                    }
2377                    let glyph = Glyph {
2378                        font: Some(font_metrics_from_layout(&open_type_glyph.layout, scale_y)),
2379                        metrics,
2380                        layers,
2381                    };
2382                    glyphs.push(PositionedGlyph::new(glyph, origin_x, origin_y));
2383                    unit_glyph_indices[index] = Some(glyphs.len() - 1);
2384                    if !uses_mark_attachment {
2385                        if is_right_to_left && !is_vertical {
2386                            cursor_x -= metrics.advance_x;
2387                        } else {
2388                            cursor_x += metrics.advance_x;
2389                        }
2390                        cursor_y += metrics.advance_y;
2391                    }
2392                }
2393            }
2394        }
2395
2396        Ok(GlyphRun::new(glyphs))
2397    }
2398
2399    fn build_outline_layers(
2400        &self,
2401        glyph_id: usize,
2402        open_type_glyph: &OpenTypeGlyph,
2403        scale_x: f32,
2404        scale_y: f32,
2405        _ch: char,
2406    ) -> Result<Vec<GlyphLayer>, Error> {
2407        let color_layers =
2408            self.build_colr_layers(glyph_id, &open_type_glyph.layout, scale_x, scale_y);
2409        if !color_layers.is_empty() {
2410            return Ok(color_layers);
2411        }
2412
2413        #[cfg(feature = "cff")]
2414        if let Some(cff) = self.current_cff() {
2415            let commands =
2416                cff.to_path_commands_with_coords(glyph_id, 1.0, &open_type_glyph.variation_coords)?;
2417            let commands = transform_cff_commands(&commands, scale_x, scale_y);
2418            return Ok(vec![GlyphLayer::Path(PathGlyphLayer::new(
2419                commands,
2420                GlyphPaint::CurrentColor,
2421            ))]);
2422        }
2423
2424        if self.current_outline_format() == GlyphFormat::CFF2 {
2425            return Err(Error::new(
2426                ErrorKind::Unsupported,
2427                "CFF2 outlines are not supported yet",
2428            ));
2429        }
2430
2431        match &open_type_glyph.glyph {
2432            FontData::Glyph(_) => {
2433                let glyf = self
2434                    .current_glyf()
2435                    .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyf is none"))?;
2436                let commands = glyf.to_path_commands(glyph_id, &open_type_glyph.layout, 0.0, 0.0);
2437                let commands =
2438                    transform_glyf_commands(&commands, &open_type_glyph.layout, scale_x, scale_y);
2439                Ok(vec![GlyphLayer::Path(PathGlyphLayer::new(
2440                    commands,
2441                    GlyphPaint::CurrentColor,
2442                ))])
2443            }
2444            FontData::ParsedGlyph(parsed) => {
2445                let commands =
2446                    glyf::Glyph::to_path_commands_parsed(parsed, &open_type_glyph.layout, 0.0, 0.0);
2447                let commands =
2448                    transform_glyf_commands(&commands, &open_type_glyph.layout, scale_x, scale_y);
2449                Ok(vec![GlyphLayer::Path(PathGlyphLayer::new(
2450                    commands,
2451                    GlyphPaint::CurrentColor,
2452                ))])
2453            }
2454            FontData::Bitmap(_, _) => Err(Error::new(
2455                ErrorKind::Unsupported,
2456                "bitmap glyphs are only supported through sbix raster layers",
2457            )),
2458            FontData::SVG(_) => Err(Error::new(
2459                ErrorKind::Unsupported,
2460                "SVG glyph layers are not supported yet",
2461            )),
2462            _ => Err(Error::new(
2463                ErrorKind::Unsupported,
2464                "glyph outlines are not available for this font",
2465            )),
2466        }
2467    }
2468
2469    fn build_colr_layers(
2470        &self,
2471        glyph_id: usize,
2472        layout: &FontLayout,
2473        scale_x: f32,
2474        scale_y: f32,
2475    ) -> Vec<GlyphLayer> {
2476        let (Some(colr), Some(cpal), Some(glyf)) = (
2477            self.current_colr(),
2478            self.current_cpal(),
2479            self.current_glyf(),
2480        ) else {
2481            return Vec::new();
2482        };
2483
2484        let mut layers = Vec::new();
2485        for layer in colr.get_layer_record(glyph_id as u16) {
2486            if glyf.get_glyph(layer.glyph_id as usize).is_none() {
2487                continue;
2488            }
2489            let commands = glyf.to_path_commands(layer.glyph_id as usize, layout, 0.0, 0.0);
2490            let commands = transform_glyf_commands(&commands, layout, scale_x, scale_y);
2491            let color = cpal.get_pallet(layer.palette_index as usize);
2492            let argb = ((color.alpha as u32) << 24)
2493                | ((color.red as u32) << 16)
2494                | ((color.green as u32) << 8)
2495                | color.blue as u32;
2496            layers.push(GlyphLayer::Path(PathGlyphLayer::new(
2497                commands,
2498                GlyphPaint::Solid(argb),
2499            )));
2500        }
2501
2502        layers
2503    }
2504
2505    fn legacy_colr_commands(
2506        &self,
2507        glyph_id: usize,
2508        layout: &FontLayout,
2509        origin_x: f64,
2510        origin_y: f64,
2511    ) -> Vec<PathCommand> {
2512        let (Some(colr), Some(glyf)) = (self.current_colr(), self.current_glyf()) else {
2513            return Vec::new();
2514        };
2515
2516        let mut commands = Vec::new();
2517        for layer in colr.get_layer_record(glyph_id as u16) {
2518            if glyf.get_glyph(layer.glyph_id as usize).is_none() {
2519                continue;
2520            }
2521            commands.extend(glyf.to_path_commands(
2522                layer.glyph_id as usize,
2523                layout,
2524                origin_x,
2525                origin_y,
2526            ));
2527        }
2528        commands
2529    }
2530
2531    pub(crate) fn text2commands(&self, text: &str) -> Result<Vec<GlyphCommands>, Error> {
2532        let mut result = Vec::new();
2533        let mut cursor_x = 0.0;
2534        let mut line_index = 0usize;
2535        let line_height = self
2536            .default_line_height_with_options(&crate::commands::FontOptions::from_parsed(self))?;
2537        let tab_advance = line_height;
2538        let shaped_units = self.shape_text_units(
2539            text,
2540            false,
2541            false,
2542            None,
2543            crate::commands::FontVariant::Normal,
2544        )?;
2545
2546        for (index, unit) in shaped_units.iter().enumerate() {
2547            match *unit {
2548                ResolvedTextUnit::Newline => {
2549                    cursor_x = 0.0;
2550                    line_index += 1;
2551                }
2552                ResolvedTextUnit::Tab => {
2553                    cursor_x += tab_advance * 4.0;
2554                }
2555                ResolvedTextUnit::Glyph(resolved) => {
2556                    let glyph_data = self.get_glyph_from_id_with_options(
2557                        resolved.glyph_id,
2558                        false,
2559                        &crate::commands::FontOptions::from_parsed(self),
2560                    );
2561                    let open_type_glyph = glyph_data
2562                        .open_type_glyf
2563                        .as_ref()
2564                        .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyph is none"))?;
2565                    let adjustment =
2566                        self.pair_adjustment_for_index(&shaped_units, index, None, false, 1.0, 1.0);
2567                    let origin_y =
2568                        -(line_index as f64 * line_height) + adjustment.placement_y as f64;
2569                    let advance_width = match &open_type_glyph.layout {
2570                        FontLayout::Horizontal(layout) => layout.advance_width as f64,
2571                        FontLayout::Vertical(layout) => layout.advance_height as f64,
2572                        FontLayout::Unknown => 0.0,
2573                    } + adjustment.advance_x as f64;
2574                    let origin_x = cursor_x + adjustment.placement_x as f64;
2575                    let can_use_outline =
2576                        self.resolved_glyph_can_use_outline(open_type_glyph, glyph_data.glyph_id);
2577
2578                    if let Some(sbix) = self.current_sbix() {
2579                        if let Some(bitmap) = sbix.get_raster_glyph(
2580                            glyph_data.glyph_id as u32,
2581                            line_height as f32,
2582                            "px",
2583                        ) {
2584                            if resolved.prefer_color || !can_use_outline {
2585                                let format = if bitmap.graphic_type == u32::from_be_bytes(*b"png ")
2586                                {
2587                                    BitmapGlyphFormat::Png
2588                                } else if bitmap.graphic_type == u32::from_be_bytes(*b"jpg ") {
2589                                    BitmapGlyphFormat::Jpeg
2590                                } else {
2591                                    return Err(Error::new(
2592                                        std::io::ErrorKind::Unsupported,
2593                                        "unsupported sbix image format",
2594                                    ));
2595                                };
2596                                let sniffed_dimensions =
2597                                    sniff_encoded_image_dimensions(&bitmap.glyph_data);
2598                                result.push(GlyphCommands {
2599                                    ch: resolved.ch,
2600                                    glyph_id: glyph_data.glyph_id,
2601                                    origin_x,
2602                                    origin_y,
2603                                    advance_width,
2604                                    commands: Vec::new(),
2605                                    bitmap: Some(BitmapGlyphCommands {
2606                                        offset_x: bitmap.offset_x as f64,
2607                                        offset_y: bitmap.offset_y as f64,
2608                                        width: bitmap
2609                                            .width
2610                                            .map(|width| width as f64)
2611                                            .or_else(|| {
2612                                                sniffed_dimensions.map(|(_, width, _)| width as f64)
2613                                            })
2614                                            .unwrap_or(line_height),
2615                                        height: bitmap
2616                                            .height
2617                                            .map(|height| height as f64)
2618                                            .or_else(|| {
2619                                                sniffed_dimensions
2620                                                    .map(|(_, _, height)| height as f64)
2621                                            })
2622                                            .unwrap_or(line_height),
2623                                        format,
2624                                        data: bitmap.glyph_data,
2625                                    }),
2626                                });
2627                                cursor_x += advance_width;
2628                                continue;
2629                            }
2630                        }
2631                    }
2632
2633                    match &open_type_glyph.glyph {
2634                        FontData::Glyph(_) => {
2635                            let mut commands = self.legacy_colr_commands(
2636                                glyph_data.glyph_id,
2637                                &open_type_glyph.layout,
2638                                origin_x,
2639                                origin_y,
2640                            );
2641                            if commands.is_empty() {
2642                                let glyf = self.current_glyf().ok_or_else(|| {
2643                                    Error::new(std::io::ErrorKind::Other, "glyf is none")
2644                                })?;
2645                                commands = glyf.to_path_commands(
2646                                    glyph_data.glyph_id,
2647                                    &open_type_glyph.layout,
2648                                    origin_x,
2649                                    origin_y,
2650                                );
2651                            }
2652                            result.push(GlyphCommands {
2653                                ch: resolved.ch,
2654                                glyph_id: glyph_data.glyph_id,
2655                                origin_x,
2656                                origin_y,
2657                                advance_width,
2658                                commands,
2659                                bitmap: None,
2660                            });
2661                            cursor_x += advance_width;
2662                        }
2663                        FontData::ParsedGlyph(parsed) => {
2664                            let commands = glyf::Glyph::to_path_commands_parsed(
2665                                parsed,
2666                                &open_type_glyph.layout,
2667                                origin_x,
2668                                origin_y,
2669                            );
2670                            result.push(GlyphCommands {
2671                                ch: resolved.ch,
2672                                glyph_id: glyph_data.glyph_id,
2673                                origin_x,
2674                                origin_y,
2675                                advance_width,
2676                                commands,
2677                                bitmap: None,
2678                            });
2679                            cursor_x += advance_width;
2680                        }
2681                        FontData::CFF(_) | FontData::CFF2(_) => {
2682                            return Err(Error::new(
2683                                ErrorKind::Unsupported,
2684                                "legacy text2commands does not support CFF/CFF2 outlines",
2685                            ));
2686                        }
2687                        _ => {
2688                            return Err(Error::new(
2689                                std::io::ErrorKind::Other,
2690                                "text2commands supports glyf outlines and sbix bitmap glyphs only",
2691                            ));
2692                        }
2693                    }
2694                }
2695            }
2696        }
2697
2698        Ok(result)
2699    }
2700
2701    #[cfg(test)]
2702    pub(crate) fn text2command(&self, text: &str) -> Result<Vec<GlyphCommands>, Error> {
2703        self.text2commands(text)
2704    }
2705
2706    pub fn measure(&self, text: &str) -> Result<f64, Error> {
2707        self.measure_with_options(text, &crate::commands::FontOptions::from_parsed(self))
2708    }
2709
2710    pub fn measure_with_options(
2711        &self,
2712        text: &str,
2713        options: &crate::commands::FontOptions<'_>,
2714    ) -> Result<f64, Error> {
2715        let mut cursor_x = 0.0;
2716        let mut cursor_y = 0.0;
2717        let mut max_line_width: f64 = 0.0;
2718        let line_height = self.default_line_height_with_options(options)?;
2719        let tab_advance = line_height;
2720        let is_vertical = options.text_direction.is_vertical();
2721        let is_right_to_left = options.text_direction.is_right_to_left();
2722        let shaped_units = self.shape_text_units(
2723            text,
2724            is_vertical,
2725            is_right_to_left,
2726            options.locale,
2727            options.font_variant,
2728        )?;
2729
2730        for (index, unit) in shaped_units.iter().enumerate() {
2731            match *unit {
2732                ResolvedTextUnit::Newline => {
2733                    max_line_width = if is_vertical {
2734                        max_line_width.max(cursor_y)
2735                    } else if is_right_to_left {
2736                        max_line_width.max(-cursor_x)
2737                    } else {
2738                        max_line_width.max(cursor_x)
2739                    };
2740                    if is_vertical {
2741                        cursor_x -= line_height;
2742                        cursor_y = 0.0;
2743                    } else {
2744                        cursor_x = 0.0;
2745                    }
2746                }
2747                ResolvedTextUnit::Tab => {
2748                    if is_vertical {
2749                        cursor_y += tab_advance * 4.0;
2750                    } else if is_right_to_left {
2751                        cursor_x -= tab_advance * 4.0;
2752                    } else {
2753                        cursor_x += tab_advance * 4.0;
2754                    }
2755                }
2756                ResolvedTextUnit::Glyph(resolved) => {
2757                    let glyph_data = self.get_glyph_from_id_with_options(
2758                        resolved.glyph_id,
2759                        is_vertical,
2760                        options,
2761                    );
2762                    let open_type_glyph = glyph_data
2763                        .open_type_glyf
2764                        .as_ref()
2765                        .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyph is none"))?;
2766
2767                    let adjustment = self.pair_adjustment_for_index(
2768                        &shaped_units,
2769                        index,
2770                        options.locale,
2771                        is_vertical,
2772                        1.0,
2773                        1.0,
2774                    );
2775                    let (advance_x, advance_y) = match &open_type_glyph.layout {
2776                        FontLayout::Horizontal(layout) => (layout.advance_width as f64, 0.0),
2777                        FontLayout::Vertical(layout) => (0.0, layout.advance_height as f64),
2778                        FontLayout::Unknown => (0.0, 0.0),
2779                    };
2780                    if is_right_to_left && !is_vertical {
2781                        cursor_x -= advance_x + adjustment.advance_x as f64;
2782                    } else {
2783                        cursor_x += advance_x + adjustment.advance_x as f64;
2784                    }
2785                    cursor_y += advance_y + adjustment.advance_y as f64;
2786                }
2787            }
2788        }
2789
2790        Ok(if is_vertical {
2791            max_line_width.max(cursor_y)
2792        } else if is_right_to_left {
2793            max_line_width.max(-cursor_x)
2794        } else {
2795            max_line_width.max(cursor_x)
2796        })
2797    }
2798
2799    pub(crate) fn text2svg(
2800        &self,
2801        text: &str,
2802        fontsize: f64,
2803        fontunit: &str,
2804    ) -> Result<String, Error> {
2805        let glyphs = self.text2commands(text)?;
2806        let line_height = self.default_line_height()?;
2807        let mut svg_elements = Vec::new();
2808        let mut min_x = 0.0;
2809        let mut min_y = 0.0;
2810        let mut max_x = 0.0;
2811        let mut max_y = 0.0;
2812        let mut has_point = false;
2813
2814        for glyph in glyphs.iter() {
2815            let d = path_commands_to_svg_path(&glyph.commands);
2816            if !d.is_empty() {
2817                let (glyph_min_x, glyph_min_y, glyph_max_x, glyph_max_y) =
2818                    path_command_bounds(&glyph.commands);
2819                if !has_point {
2820                    min_x = glyph_min_x;
2821                    min_y = glyph_min_y;
2822                    max_x = glyph_max_x;
2823                    max_y = glyph_max_y;
2824                    has_point = true;
2825                } else {
2826                    min_x = min_x.min(glyph_min_x);
2827                    min_y = min_y.min(glyph_min_y);
2828                    max_x = max_x.max(glyph_max_x);
2829                    max_y = max_y.max(glyph_max_y);
2830                }
2831                svg_elements.push(format!("<path d=\"{}\" fill=\"currentColor\"/>", d));
2832            }
2833
2834            if let Some(bitmap) = glyph.bitmap.as_ref() {
2835                let glyph_min_x = glyph.origin_x + bitmap.offset_x;
2836                let glyph_min_y = glyph.origin_y + bitmap.offset_y;
2837                let glyph_max_x = glyph_min_x + bitmap.width;
2838                let glyph_max_y = glyph_min_y + bitmap.height;
2839                if !has_point {
2840                    min_x = glyph_min_x;
2841                    min_y = glyph_min_y;
2842                    max_x = glyph_max_x;
2843                    max_y = glyph_max_y;
2844                    has_point = true;
2845                } else {
2846                    min_x = min_x.min(glyph_min_x);
2847                    min_y = min_y.min(glyph_min_y);
2848                    max_x = max_x.max(glyph_max_x);
2849                    max_y = max_y.max(glyph_max_y);
2850                }
2851                svg_elements.push(bitmap_glyph_to_svg_image(glyph, bitmap));
2852            }
2853        }
2854
2855        if !has_point {
2856            let size = format!("0{}", fontunit);
2857            return Ok(format!(
2858                "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{}\" height=\"{}\" viewBox=\"0 0 0 0\"></svg>",
2859                size, size
2860            ));
2861        }
2862
2863        const SVG_EXPORT_PADDING: f64 = 4.0;
2864        min_x -= SVG_EXPORT_PADDING;
2865        min_y -= SVG_EXPORT_PADDING;
2866        let view_width = (max_x - min_x + SVG_EXPORT_PADDING).max(1.0);
2867        let view_height = (max_y - min_y + SVG_EXPORT_PADDING).max(1.0);
2868        let scale = fontsize / line_height.max(1.0);
2869        let width = (view_width * scale).ceil();
2870        let height = (view_height * scale).ceil();
2871
2872        let mut svg = format!(
2873            "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{}{}\" height=\"{}{}\" viewBox=\"{} {} {} {}\" overflow=\"visible\">",
2874            width, fontunit, height, fontunit, min_x, min_y, view_width, view_height
2875        );
2876        for element in svg_elements {
2877            svg += &element;
2878        }
2879        svg += "</svg>";
2880        Ok(svg)
2881    }
2882
2883    pub fn get_name(&self, name_id: NameID, locale: &String) -> Result<String, Error> {
2884        let name_table = self
2885            .current_name_table()
2886            .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "name table is none"))?;
2887        let platform_id = PlatformID::Windows;
2888        name_table.get_name(name_id, locale, platform_id)
2889    }
2890
2891    pub(crate) fn face_name_by_id(&self, name_id: u16) -> Option<String> {
2892        let locale = "en-US".to_string();
2893        let name_table = if self.current_font == 0 {
2894            self.name_table.as_ref()?
2895        } else {
2896            self.more_fonts[self.current_font - 1].name_table.as_ref()?
2897        };
2898
2899        let names = name_table.get_name_list(&locale, PlatformID::Windows);
2900        names
2901            .get(&name_id)
2902            .cloned()
2903            .or_else(|| name_table.default_namelist.get(&name_id).cloned())
2904            .filter(|name| !name.trim().is_empty())
2905    }
2906
2907    pub(crate) fn face_variation_axes(&self) -> Vec<fvar::VariationAxisRecord> {
2908        self.current_fvar()
2909            .map(|fvar| fvar.axes.clone())
2910            .unwrap_or_default()
2911    }
2912
2913    pub(crate) fn face_family_name(&self) -> String {
2914        let locale = "en-US".to_string();
2915        self.get_name(NameID::TypographicFamilyName, &locale)
2916            .ok()
2917            .filter(|name| !name.trim().is_empty())
2918            .or_else(|| {
2919                self.get_name(NameID::FontFamilyName, &locale)
2920                    .ok()
2921                    .filter(|name| !name.trim().is_empty())
2922            })
2923            .unwrap_or_else(|| "Unknown Family".to_string())
2924    }
2925
2926    pub(crate) fn face_full_name(&self) -> Option<String> {
2927        let locale = "en-US".to_string();
2928        self.get_name(NameID::FullFontName, &locale)
2929            .ok()
2930            .filter(|name| !name.trim().is_empty())
2931            .or_else(|| {
2932                self.get_name(NameID::PostScriptName, &locale)
2933                    .ok()
2934                    .filter(|name| !name.trim().is_empty())
2935            })
2936    }
2937
2938    pub(crate) fn face_weight_class(&self) -> u16 {
2939        let os2 = if self.current_font == 0 {
2940            self.os2.as_ref()
2941        } else {
2942            self.more_fonts[self.current_font - 1].os2.as_ref()
2943        };
2944        os2.map(|os2| os2.weight_class()).unwrap_or(400)
2945    }
2946
2947    pub(crate) fn face_width_class(&self) -> u16 {
2948        let os2 = if self.current_font == 0 {
2949            self.os2.as_ref()
2950        } else {
2951            self.more_fonts[self.current_font - 1].os2.as_ref()
2952        };
2953        os2.map(|os2| os2.width_class()).unwrap_or(5)
2954    }
2955
2956    pub(crate) fn face_is_italic(&self) -> bool {
2957        let head_mac_style = if self.current_font == 0 {
2958            self.head.as_ref().map(|head| head.mac_style)
2959        } else {
2960            self.more_fonts[self.current_font - 1]
2961                .head
2962                .as_ref()
2963                .map(|head| head.mac_style)
2964        }
2965        .unwrap_or(0);
2966        if head_mac_style & 0x0002 == 0x0002 {
2967            return true;
2968        }
2969
2970        let post_italic_angle = if self.current_font == 0 {
2971            self.post.as_ref().map(|post| post.italic_angle)
2972        } else {
2973            self.more_fonts[self.current_font - 1]
2974                .post
2975                .as_ref()
2976                .map(|post| post.italic_angle)
2977        }
2978        .unwrap_or(0);
2979        if post_italic_angle != 0 {
2980            return true;
2981        }
2982
2983        let os2_selection = if self.current_font == 0 {
2984            self.os2.as_ref().map(|os2| os2.selection_flags())
2985        } else {
2986            self.more_fonts[self.current_font - 1]
2987                .os2
2988                .as_ref()
2989                .map(|os2| os2.selection_flags())
2990        }
2991        .unwrap_or(0);
2992        os2_selection & 0x0001 == 0x0001
2993    }
2994
2995    #[cfg(debug_assertions)]
2996    pub fn get_name_raw(&self) -> String {
2997        let name = if self.current_font == 0 {
2998            self.name.as_ref()
2999        } else {
3000            self.more_fonts[self.current_font - 1].name.as_ref()
3001        };
3002        Self::debug_optional_table_string(name, "name", |name| name.to_string())
3003    }
3004
3005    #[cfg(debug_assertions)]
3006    pub fn get_maxp_raw(&self) -> String {
3007        let maxp = if self.current_font == 0 {
3008            self.maxp.as_ref()
3009        } else {
3010            self.more_fonts[self.current_font - 1].maxp.as_ref()
3011        };
3012        Self::debug_optional_table_string(maxp, "maxp", |maxp| maxp.to_string())
3013    }
3014
3015    #[cfg(debug_assertions)]
3016    pub fn get_header_raw(&self) -> String {
3017        let head = if self.current_font == 0 {
3018            self.head.as_ref()
3019        } else {
3020            self.more_fonts[self.current_font - 1].head.as_ref()
3021        };
3022        Self::debug_optional_table_string(head, "head", |head| head.to_string())
3023    }
3024
3025    #[cfg(debug_assertions)]
3026    pub fn get_os2_raw(&self) -> String {
3027        let os2 = if self.current_font == 0 {
3028            self.os2.as_ref()
3029        } else {
3030            self.more_fonts[self.current_font - 1].os2.as_ref()
3031        };
3032        Self::debug_optional_table_string(os2, "os2", |os2| os2.to_string())
3033    }
3034
3035    #[cfg(debug_assertions)]
3036    pub fn get_hhea_raw(&self) -> String {
3037        let hhea = if self.current_font == 0 {
3038            self.hhea.as_ref()
3039        } else {
3040            self.more_fonts[self.current_font - 1].hhea.as_ref()
3041        };
3042        Self::debug_optional_table_string(hhea, "hhea", |hhea| hhea.to_string())
3043    }
3044
3045    #[cfg(debug_assertions)]
3046    pub fn get_cmap_raw(&self) -> String {
3047        let cmap = if self.current_font == 0 {
3048            self.cmap.as_ref()
3049        } else {
3050            self.more_fonts[self.current_font - 1].cmap.as_ref()
3051        };
3052        let Some(cmap) = cmap else {
3053            return "cmap is none".to_string();
3054        };
3055        let encodings = &cmap.cmap_encodings;
3056        let mut string = String::new();
3057        for encoding in encodings.as_ref().iter() {
3058            string += &format!(
3059                "Encording Record\n{}\n",
3060                encoding.encoding_record.to_string()
3061            );
3062            string += &format!(
3063                "Subtable\n{}\n",
3064                encoding.cmap_subtable.get_part_of_string(10)
3065            );
3066        }
3067        string
3068    }
3069
3070    #[cfg(debug_assertions)]
3071    pub fn get_sbix_raw(&self) -> String {
3072        let sbix = if self.current_font == 0 {
3073            if let Some(sbix) = &self.sbix {
3074                sbix
3075            } else {
3076                return "sbix is none".to_string();
3077            }
3078        } else {
3079            let sbix = self.more_fonts[self.current_font - 1].sbix.as_ref();
3080            if let Some(sbix) = sbix {
3081                sbix
3082            } else {
3083                return "sbix is none".to_string();
3084            }
3085        };
3086        sbix.to_string()
3087    }
3088
3089    #[cfg(debug_assertions)]
3090    pub fn get_svg_raw(&self) -> String {
3091        let svg = if self.current_font == 0 {
3092            if let Some(svg) = &self.svg {
3093                svg
3094            } else {
3095                return "svg is none".to_string();
3096            }
3097        } else {
3098            let svg = self.more_fonts[self.current_font - 1].svg.as_ref();
3099            if let Some(svg) = svg {
3100                svg
3101            } else {
3102                return "svg is none".to_string();
3103            }
3104        };
3105        svg.to_string()
3106    }
3107
3108    #[cfg(debug_assertions)]
3109    pub fn get_post_raw(&self) -> String {
3110        let post = if self.current_font == 0 {
3111            self.post.as_ref()
3112        } else {
3113            self.more_fonts[self.current_font - 1].post.as_ref()
3114        };
3115        Self::debug_optional_table_string(post, "post", |post| post.to_string())
3116    }
3117
3118    #[cfg(debug_assertions)]
3119    fn debug_optional_table_string<T, F>(table: Option<&T>, name: &str, to_string: F) -> String
3120    where
3121        F: FnOnce(&T) -> String,
3122    {
3123        if let Some(table) = table {
3124            to_string(table)
3125        } else {
3126            format!("{} is none", name)
3127        }
3128    }
3129
3130    #[cfg(debug_assertions)]
3131    pub fn get_cpal_raw(&self) -> String {
3132        let cpal = if self.current_font == 0 {
3133            self.cpal.as_ref()
3134        } else {
3135            self.more_fonts[self.current_font - 1].cpal.as_ref()
3136        };
3137        Self::debug_optional_table_string(cpal, "cpal", |cpal| cpal.to_string())
3138    }
3139
3140    #[cfg(debug_assertions)]
3141    pub fn get_colr_raw(&self) -> String {
3142        let colr = if self.current_font == 0 {
3143            self.colr.as_ref()
3144        } else {
3145            self.more_fonts[self.current_font - 1].colr.as_ref()
3146        };
3147        Self::debug_optional_table_string(colr, "colr", |colr| colr.to_string())
3148    }
3149    #[cfg(debug_assertions)]
3150    #[cfg(feature = "layout")]
3151    pub fn get_vhea_raw(&self) -> String {
3152        let vhea = if self.current_font == 0 {
3153            self.vhea.as_ref()
3154        } else {
3155            self.more_fonts[self.current_font - 1].vhea.as_ref()
3156        };
3157        Self::debug_optional_table_string(vhea, "vhea", |vhea| vhea.to_string())
3158    }
3159
3160    #[cfg(debug_assertions)]
3161    #[cfg(feature = "layout")]
3162    pub fn get_gdef_raw(&self) -> String {
3163        let gdef = if self.current_font == 0 {
3164            self.gdef.as_ref()
3165        } else {
3166            self.more_fonts[self.current_font - 1].gdef.as_ref()
3167        };
3168        Self::debug_optional_table_string(gdef, "gdef", |gdef| gdef.to_string())
3169    }
3170
3171    #[cfg(debug_assertions)]
3172    #[cfg(feature = "layout")]
3173    pub fn get_gsub_raw(&self) -> String {
3174        let gsub = if self.current_font == 0 {
3175            self.gsub.as_ref()
3176        } else {
3177            self.more_fonts[self.current_font - 1].gsub.as_ref()
3178        };
3179        Self::debug_optional_table_string(gsub, "gsub", |gsub| gsub.to_string())
3180    }
3181
3182    pub fn get_html_vert(
3183        &self,
3184        string: &str,
3185        fontsize: f64,
3186        fontunit: &str,
3187    ) -> Result<String, Error> {
3188        let mut html = String::new();
3189        html += "<html>\n";
3190        html += "<head>\n";
3191        html += "<meta charset=\"UTF-8\">\n";
3192        html += "<title>fontreader</title>\n";
3193        html += "<style>body {writing-mode: vertical-rl; }</style>\n";
3194        html += "</head>\n";
3195        html += "<body>\n";
3196        let mut svgs = Vec::new();
3197        for unit in Self::parse_text_units(string) {
3198            self.push_svg_html_unit(&mut svgs, unit, fontsize, fontunit, true)?;
3199        }
3200
3201        for svg in svgs {
3202            html += &svg;
3203        }
3204        html += "</body>\n";
3205        html += "</html>\n";
3206        Ok(html)
3207    }
3208
3209    pub fn get_html(&self, string: &str, fontsize: f64, fontunit: &str) -> Result<String, Error> {
3210        let mut html = String::new();
3211        html += "<html>\n";
3212        html += "<head>\n";
3213        html += "<meta charset=\"UTF-8\">\n";
3214        html += "<title>fontreader</title>\n";
3215        html += "</head>\n";
3216        html += "<body>\n";
3217        let mut svgs = Vec::new();
3218        for unit in Self::parse_text_units(string) {
3219            self.push_svg_html_unit(&mut svgs, unit, fontsize, fontunit, false)?;
3220        }
3221        for svg in svgs {
3222            html += &svg;
3223        }
3224        html += "</body>\n";
3225        html += "</html>\n";
3226        Ok(html)
3227    }
3228
3229    pub fn get_info(&self) -> Result<String, Error> {
3230        let mut string = String::new();
3231        let name = self
3232            .name
3233            .as_ref()
3234            .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "name table is none"))?;
3235        let font_famiry = name.get_family_name();
3236        let subfamily_name = name.get_subfamily_name();
3237        string += &format!("Font famiry: {} {}\n", font_famiry, subfamily_name);
3238        for more_font in self.more_fonts.iter() {
3239            let Some(name) = more_font.name.as_ref() else {
3240                continue;
3241            };
3242            let font_famiry = name.get_family_name();
3243            let subfamily_name = name.get_subfamily_name();
3244            string += &format!("Font famiry: {} {}\n", font_famiry, subfamily_name);
3245        }
3246        Ok(string)
3247    }
3248
3249    pub fn get_font_count(&self) -> usize {
3250        self.more_fonts.len() + 1
3251    }
3252
3253    pub fn get_font_number(&self) -> usize {
3254        self.current_font
3255    }
3256
3257    pub fn set_font(&mut self, number: usize) -> Result<(), String> {
3258        if number <= self.more_fonts.len() {
3259            self.current_font = number;
3260            Ok(())
3261        } else {
3262            Err("font number is out of range".to_owned())
3263        }
3264    }
3265}
3266
3267#[derive(Debug, Clone)]
3268pub struct HorizontalLayout {
3269    pub lsb: isize,
3270    pub advance_width: isize,
3271    pub accender: isize,
3272    pub descender: isize,
3273    pub line_gap: isize,
3274    #[allow(dead_code)]
3275    pub(crate) hhea: HHEA,
3276}
3277
3278#[derive(Debug, Clone)]
3279pub struct VerticalLayout {
3280    pub tsb: isize,
3281    pub advance_height: isize,
3282    pub accender: isize,
3283    pub descender: isize,
3284    pub line_gap: isize,
3285    #[allow(dead_code)]
3286    pub(crate) vhea: VHEA,
3287}
3288
3289#[derive(Debug, Clone)]
3290struct Pointer {
3291    pub(crate) offset: u32,
3292    pub(crate) length: u32,
3293}
3294
3295fn font_load_from_file(filename: &PathBuf) -> Result<Font, Error> {
3296    #[cfg(target_arch = "wasm32")]
3297    {
3298        let _ = filename;
3299        return Err(Error::new(
3300            ErrorKind::Unsupported,
3301            "file font loading is not supported on wasm32",
3302        ));
3303    }
3304
3305    #[cfg(not(target_arch = "wasm32"))]
3306    {
3307        let fontdata = std::fs::read(filename)?;
3308        Font::get_font_from_buffer(&fontdata)
3309    }
3310}
3311
3312fn path_commands_to_svg_path(commands: &[PathCommand]) -> String {
3313    let mut d = String::new();
3314    for command in commands {
3315        match command {
3316            PathCommand::MoveTo { x, y } => d += &format!("M{} {} ", x, y),
3317            PathCommand::LineTo { x, y } => d += &format!("L{} {} ", x, y),
3318            PathCommand::QuadTo { cx, cy, x, y } => d += &format!("Q{} {} {} {} ", cx, cy, x, y),
3319            PathCommand::ClosePath => d += "Z ",
3320        }
3321    }
3322    d.trim_end().to_string()
3323}
3324
3325fn path_command_bounds(commands: &[PathCommand]) -> (f64, f64, f64, f64) {
3326    let mut min_x = 0.0;
3327    let mut min_y = 0.0;
3328    let mut max_x = 0.0;
3329    let mut max_y = 0.0;
3330    let mut has_point = false;
3331
3332    let mut add_point = |x: f64, y: f64| {
3333        if !has_point {
3334            min_x = x;
3335            min_y = y;
3336            max_x = x;
3337            max_y = y;
3338            has_point = true;
3339        } else {
3340            min_x = min_x.min(x);
3341            min_y = min_y.min(y);
3342            max_x = max_x.max(x);
3343            max_y = max_y.max(y);
3344        }
3345    };
3346
3347    for command in commands {
3348        match command {
3349            PathCommand::MoveTo { x, y } | PathCommand::LineTo { x, y } => add_point(*x, *y),
3350            PathCommand::QuadTo { cx, cy, x, y } => {
3351                add_point(*cx, *cy);
3352                add_point(*x, *y);
3353            }
3354            PathCommand::ClosePath => {}
3355        }
3356    }
3357
3358    (min_x, min_y, max_x, max_y)
3359}
3360
3361fn bitmap_glyph_to_svg_image(glyph: &GlyphCommands, bitmap: &BitmapGlyphCommands) -> String {
3362    let mime = match bitmap.format {
3363        BitmapGlyphFormat::Png => "image/png",
3364        BitmapGlyphFormat::Jpeg => "image/jpeg",
3365    };
3366    let encoded = general_purpose::STANDARD.encode(&bitmap.data);
3367    format!(
3368        "<image x=\"{}\" y=\"{}\" width=\"{}\" height=\"{}\" href=\"data:{};base64,{}\"/>",
3369        glyph.origin_x + bitmap.offset_x,
3370        glyph.origin_y + bitmap.offset_y,
3371        bitmap.width,
3372        bitmap.height,
3373        mime,
3374        encoded
3375    )
3376}
3377
3378fn glyph_baseline_shift(layout: &FontLayout) -> f32 {
3379    match layout {
3380        FontLayout::Horizontal(layout) => (layout.accender + layout.line_gap) as f32,
3381        FontLayout::Vertical(layout) => (layout.accender - layout.descender) as f32,
3382        FontLayout::Unknown => 0.0,
3383    }
3384}
3385
3386fn transform_glyf_commands(
3387    commands: &[PathCommand],
3388    layout: &FontLayout,
3389    scale_x: f32,
3390    scale_y: f32,
3391) -> Vec<DrawCommand> {
3392    let baseline_shift = glyph_baseline_shift(layout) as f64;
3393    commands
3394        .iter()
3395        .map(|command| match command {
3396            PathCommand::MoveTo { x, y } => {
3397                DrawCommand::MoveTo(*x as f32 * scale_x, (*y - baseline_shift) as f32 * scale_y)
3398            }
3399            PathCommand::LineTo { x, y } => {
3400                DrawCommand::Line(*x as f32 * scale_x, (*y - baseline_shift) as f32 * scale_y)
3401            }
3402            PathCommand::QuadTo { cx, cy, x, y } => DrawCommand::Bezier(
3403                (
3404                    *cx as f32 * scale_x,
3405                    (*cy - baseline_shift) as f32 * scale_y,
3406                ),
3407                (*x as f32 * scale_x, (*y - baseline_shift) as f32 * scale_y),
3408            ),
3409            PathCommand::ClosePath => DrawCommand::Close,
3410        })
3411        .collect()
3412}
3413
3414fn transform_cff_commands(
3415    commands: &[DrawCommand],
3416    scale_x: f32,
3417    scale_y: f32,
3418) -> Vec<DrawCommand> {
3419    commands
3420        .iter()
3421        .map(|command| match command {
3422            DrawCommand::MoveTo(x, y) => DrawCommand::MoveTo(*x * scale_x, *y * scale_y),
3423            DrawCommand::Line(x, y) => DrawCommand::Line(*x * scale_x, *y * scale_y),
3424            DrawCommand::Bezier((cx, cy), (x, y)) => {
3425                DrawCommand::Bezier((*cx * scale_x, *cy * scale_y), (*x * scale_x, *y * scale_y))
3426            }
3427            DrawCommand::CubicBezier((xa, ya), (xb, yb), (xc, yc)) => DrawCommand::CubicBezier(
3428                (*xa * scale_x, *ya * scale_y),
3429                (*xb * scale_x, *yb * scale_y),
3430                (*xc * scale_x, *yc * scale_y),
3431            ),
3432            DrawCommand::Close => DrawCommand::Close,
3433        })
3434        .collect()
3435}
3436
3437fn font_metrics_from_layout(layout: &FontLayout, scale_y: f32) -> DrawFontMetrics {
3438    match layout {
3439        FontLayout::Horizontal(layout) => DrawFontMetrics {
3440            ascent: layout.accender as f32 * scale_y,
3441            descent: (-layout.descender) as f32 * scale_y,
3442            line_gap: layout.line_gap as f32 * scale_y,
3443            flow: GlyphFlow::Horizontal,
3444        },
3445        FontLayout::Vertical(layout) => DrawFontMetrics {
3446            ascent: layout.accender as f32 * scale_y,
3447            descent: (-layout.descender) as f32 * scale_y,
3448            line_gap: layout.line_gap as f32 * scale_y,
3449            flow: GlyphFlow::Vertical,
3450        },
3451        FontLayout::Unknown => DrawFontMetrics {
3452            ascent: 0.0,
3453            descent: 0.0,
3454            line_gap: 0.0,
3455            flow: GlyphFlow::Horizontal,
3456        },
3457    }
3458}
3459
3460fn glyph_metrics_from_layout(layout: &FontLayout, scale_x: f32, scale_y: f32) -> DrawGlyphMetrics {
3461    match layout {
3462        FontLayout::Horizontal(layout) => DrawGlyphMetrics {
3463            advance_x: layout.advance_width as f32 * scale_x,
3464            advance_y: 0.0,
3465            bearing_x: layout.lsb as f32 * scale_x,
3466            bearing_y: layout.accender as f32 * scale_y,
3467            bounds: None,
3468        },
3469        FontLayout::Vertical(layout) => DrawGlyphMetrics {
3470            advance_x: 0.0,
3471            advance_y: layout.advance_height as f32 * scale_y,
3472            bearing_x: 0.0,
3473            bearing_y: layout.accender as f32 * scale_y,
3474            bounds: None,
3475        },
3476        FontLayout::Unknown => DrawGlyphMetrics::default(),
3477    }
3478}
3479
3480fn glyph_layers_bounds(layers: &[GlyphLayer]) -> Option<GlyphBounds> {
3481    let mut bounds = None;
3482
3483    for layer in layers {
3484        match layer {
3485            GlyphLayer::Path(path) => {
3486                for command in path.commands.iter() {
3487                    match command {
3488                        DrawCommand::MoveTo(x, y) | DrawCommand::Line(x, y) => {
3489                            extend_bounds(&mut bounds, *x + path.offset_x, *y + path.offset_y);
3490                        }
3491                        DrawCommand::Bezier((cx, cy), (x, y)) => {
3492                            extend_bounds(&mut bounds, *cx + path.offset_x, *cy + path.offset_y);
3493                            extend_bounds(&mut bounds, *x + path.offset_x, *y + path.offset_y);
3494                        }
3495                        DrawCommand::CubicBezier((xa, ya), (xb, yb), (xc, yc)) => {
3496                            extend_bounds(&mut bounds, *xa + path.offset_x, *ya + path.offset_y);
3497                            extend_bounds(&mut bounds, *xb + path.offset_x, *yb + path.offset_y);
3498                            extend_bounds(&mut bounds, *xc + path.offset_x, *yc + path.offset_y);
3499                        }
3500                        DrawCommand::Close => {}
3501                    }
3502                }
3503            }
3504            GlyphLayer::Raster(raster) => {
3505                if let (Some(width), Some(height)) = (raster.width, raster.height) {
3506                    extend_bounds(&mut bounds, raster.offset_x, raster.offset_y);
3507                    extend_bounds(
3508                        &mut bounds,
3509                        raster.offset_x + width as f32,
3510                        raster.offset_y + height as f32,
3511                    );
3512                }
3513            }
3514            #[cfg(feature = "svg-fonts")]
3515            GlyphLayer::Svg(svg) => {
3516                extend_bounds(
3517                    &mut bounds,
3518                    svg.offset_x + svg.view_box_min_x,
3519                    svg.offset_y + svg.view_box_min_y,
3520                );
3521                extend_bounds(
3522                    &mut bounds,
3523                    svg.offset_x + svg.view_box_min_x + svg.view_box_width,
3524                    svg.offset_y + svg.view_box_min_y + svg.view_box_height,
3525                );
3526            }
3527        }
3528    }
3529
3530    bounds
3531}
3532
3533fn extend_bounds(bounds: &mut Option<GlyphBounds>, x: f32, y: f32) {
3534    if let Some(bounds) = bounds.as_mut() {
3535        bounds.min_x = bounds.min_x.min(x);
3536        bounds.min_y = bounds.min_y.min(y);
3537        bounds.max_x = bounds.max_x.max(x);
3538        bounds.max_y = bounds.max_y.max(y);
3539    } else {
3540        *bounds = Some(GlyphBounds {
3541            min_x: x,
3542            min_y: y,
3543            max_x: x,
3544            max_y: y,
3545        });
3546    }
3547}
3548
3549#[cfg(debug_assertions)]
3550#[allow(dead_code)]
3551fn font_debug(_font: &Font) {
3552    // create or open file
3553    let filename = "test/font.txt";
3554    let file = match File::create(filename) {
3555        Ok(it) => it,
3556        Err(_) => match File::open("test/font.txt") {
3557            Ok(it) => it,
3558            Err(_) => return,
3559        },
3560    };
3561    let mut writer = BufWriter::new(file);
3562
3563    let Some(cmap) = _font.cmap.as_ref() else {
3564        return;
3565    };
3566    let Some(head) = _font.head.as_ref() else {
3567        return;
3568    };
3569    let Some(hhea) = _font.hhea.as_ref() else {
3570        return;
3571    };
3572    let Some(maxp) = _font.maxp.as_ref() else {
3573        return;
3574    };
3575    let Some(hmtx) = _font.hmtx.as_ref() else {
3576        return;
3577    };
3578    let Some(loca) = _font.loca.as_ref() else {
3579        let _ = writeln!(&mut writer, "loca is none. it is not glyf font.");
3580        return;
3581    };
3582    let Some(glyf) = _font.glyf.as_ref() else {
3583        return;
3584    };
3585
3586    let encoding_records = &cmap.get_encoding_engine();
3587    let _ = writeln!(&mut writer, "{}", cmap.cmap);
3588    for i in 0..encoding_records.len() {
3589        let _ = writeln!(&mut writer, "{} {}", i, encoding_records[i].to_string());
3590    }
3591    let _ = writeln!(&mut writer, "{}", head.to_string());
3592    let _ = writeln!(&mut writer, "{}", hhea.to_string());
3593    let _ = writeln!(&mut writer, "{}", maxp.to_string());
3594    let _ = writeln!(&mut writer, "{}", hmtx.to_string());
3595    if let Some(os2) = _font.os2.as_ref() {
3596        let _ = writeln!(&mut writer, "{}", os2.to_string());
3597    }
3598    if let Some(post) = _font.post.as_ref() {
3599        let _ = writeln!(&mut writer, "{}", post.to_string());
3600    }
3601    if let Some(name) = _font.name.as_ref() {
3602        let _ = writeln!(&mut writer, "{}", name.to_string());
3603    }
3604    let _ = writeln!(&mut writer, "{}", loca.to_string());
3605    if let Some(cpal) = _font.cpal.as_ref() {
3606        let _ = writeln!(&mut writer, "{}", cpal.to_string());
3607    }
3608    if let Some(colr) = _font.colr.as_ref() {
3609        let _ = writeln!(&mut writer, "{}", colr.to_string());
3610    }
3611
3612    let _ = writeln!(&mut writer, "long cmap -> griph");
3613    let cmap_encodings = cmap.clone();
3614    for i in 0x0020..0x0ff {
3615        let pos = cmap_encodings.get_glyph_position(i);
3616        let Some(glyph) = glyf.get_glyph(pos as usize) else {
3617            continue;
3618        };
3619        let layout = _font.get_layout(pos as usize, false);
3620        let svg = glyph.to_svg(32.0, "pt", &layout, 0.0, 0.0);
3621        let Some(ch) = char::from_u32(i) else {
3622            continue;
3623        };
3624        let _ = writeln!(&mut writer, "{}:{:04} ", ch, pos);
3625        let _ = writeln!(&mut writer, "{}", glyph.to_string());
3626        let _ = writeln!(&mut writer, "{}:{:?}", i, layout);
3627        let _ = writeln!(&mut writer, "{}", svg);
3628    }
3629    let _ = writeln!(&mut writer);
3630    for i in 0x4e00..0x4eff {
3631        if i as u32 % 16 == 0 {
3632            let _ = writeln!(&mut writer);
3633        }
3634        let pos = cmap_encodings.get_glyph_position(i as u32);
3635        let Some(glyph) = glyf.get_glyph(pos as usize) else {
3636            continue;
3637        };
3638        let layout = _font.get_layout(pos as usize, false);
3639        let svg = glyph.to_svg(100.0, "px", &layout, 0.0, 0.0);
3640        let Some(ch) = char::from_u32(i as u32) else {
3641            continue;
3642        };
3643        let _ = write!(&mut writer, "{}:{:04} ", ch, pos);
3644        let _ = writeln!(&mut writer, "{}", svg);
3645    }
3646    let _ = writeln!(&mut writer);
3647    let i = 0x2a6b2;
3648    let pos = cmap_encodings.get_glyph_position(i as u32);
3649    if let Some(ch) = char::from_u32(i as u32) {
3650        let _ = writeln!(&mut writer, "{}:{:04} ", ch, pos);
3651    }
3652}
3653
3654fn apply_i16_delta(base: i16, delta: f32) -> i16 {
3655    let value = base as f32 + delta.round();
3656    value.clamp(i16::MIN as f32, i16::MAX as f32) as i16
3657}
3658
3659fn apply_u16_delta(base: u16, delta: f32) -> u16 {
3660    let value = base as f32 + delta.round();
3661    value.clamp(0.0, u16::MAX as f32) as u16
3662}
3663
3664fn tag4(tag: &str) -> u32 {
3665    let mut bytes = [0u8; 4];
3666    bytes.copy_from_slice(tag.as_bytes());
3667    u32::from_be_bytes(bytes)
3668}
3669
3670fn font_load<R: BinaryReader>(file: &mut R) -> Result<Font, Error> {
3671    match fontheader::get_font_type(file)? {
3672        fontheader::FontHeaders::OTF(header) => {
3673            let font = from_opentype(file, &header);
3674            #[cfg(debug_assertions)]
3675            {
3676                // font_debug(font.as_ref().unwrap());
3677            }
3678            font
3679        }
3680        fontheader::FontHeaders::TTC(header) => {
3681            let num_fonts = header.num_fonts;
3682            let font_collection = header.font_collection.as_ref();
3683            let table = &font_collection[0];
3684            let mut font = from_opentype(file, table);
3685            #[cfg(debug_assertions)]
3686            {
3687                // font_debug(font.as_ref().unwrap());
3688            }
3689
3690            let mut fonts = Vec::new();
3691            for i in 1..num_fonts {
3692                let table = &font_collection[i as usize];
3693                if let Ok(font) = from_opentype(file, table) {
3694                    fonts.push(font);
3695                }
3696            }
3697            if let Ok(font) = font.as_mut() {
3698                font.more_fonts = Box::new(fonts);
3699                #[cfg(debug_assertions)]
3700                {
3701                    //    font_debug(font.as_ref().unwrap());
3702                }
3703            }
3704            font
3705        }
3706        fontheader::FontHeaders::WOFF(header) => {
3707            let mut font = Font::empty();
3708            font.font_type = fontheader::FontHeaders::WOFF(header.clone());
3709            let woff = crate::woff::WOFF::from(file, header)?;
3710
3711            let mut hmtx_table = None;
3712            let mut loca_table = None;
3713            let mut glyf_table = None;
3714            let mut sbix_table = None;
3715            let mut vmtx_table = None;
3716            for table in woff.tables {
3717                let tag: [u8; 4] = [
3718                    (table.tag >> 24) as u8,
3719                    (table.tag >> 16) as u8,
3720                    (table.tag >> 8) as u8,
3721                    table.tag as u8,
3722                ];
3723                // println!("tag: {}", crate::util::u32_to_string(table.tag));
3724                match &tag {
3725                    b"cmap" => {
3726                        let mut reader = BytesReader::new(&table.data);
3727                        let cmap_encodings =
3728                            CmapEncodings::new(&mut reader, 0, table.data.len() as u32)?;
3729                        font.cmap = Some(cmap_encodings);
3730                    }
3731                    b"head" => {
3732                        let mut reader = BytesReader::new(&table.data);
3733                        let head = head::HEAD::new(&mut reader, 0, table.data.len() as u32)?;
3734                        font.head = Some(head);
3735                    }
3736                    b"OS/2" => {
3737                        let mut reader = BytesReader::new(&table.data);
3738                        let os2 = os2::OS2::new(&mut reader, 0, table.data.len() as u32)?;
3739                        font.os2 = Some(os2);
3740                    }
3741                    b"fvar" => {
3742                        let mut reader = BytesReader::new(&table.data);
3743                        let fvar = fvar::FVAR::new(&mut reader, 0, table.data.len() as u32)?;
3744                        font.fvar = Some(fvar);
3745                    }
3746                    b"gvar" => {
3747                        let mut reader = BytesReader::new(&table.data);
3748                        let gvar = gvar::GVAR::new(&mut reader, 0, table.data.len() as u32)?;
3749                        font.gvar = Some(gvar);
3750                    }
3751                    b"avar" => {
3752                        let mut reader = BytesReader::new(&table.data);
3753                        let avar = avar::AVAR::new(&mut reader, 0, table.data.len() as u32)?;
3754                        font.avar = Some(avar);
3755                    }
3756                    b"hhea" => {
3757                        let mut reader = BytesReader::new(&table.data);
3758                        let hhea = hhea::HHEA::new(&mut reader, 0, table.data.len() as u32)?;
3759                        font.hhea = Some(hhea);
3760                    }
3761                    b"maxp" => {
3762                        let mut reader = BytesReader::new(&table.data);
3763                        let maxp = maxp::MAXP::new(&mut reader, 0, table.data.len() as u32)?;
3764                        font.maxp = Some(maxp);
3765                    }
3766                    b"hmtx" => {
3767                        hmtx_table = Some(table);
3768                    }
3769                    b"name" => {
3770                        let mut reader = BytesReader::new(&table.data);
3771                        let name = name::NAME::new(&mut reader, 0, table.data.len() as u32)?;
3772                        let name_table = name::NameTable::new(&name);
3773                        font.name = Some(name);
3774                        font.name_table = Some(name_table);
3775                    }
3776                    b"post" => {
3777                        let mut reader = BytesReader::new(&table.data);
3778                        let post = post::POST::new(&mut reader, 0, table.data.len() as u32)?;
3779                        font.post = Some(post);
3780                    }
3781                    b"loca" => {
3782                        loca_table = Some(table);
3783                    }
3784                    b"glyf" => {
3785                        glyf_table = Some(table);
3786                    }
3787                    b"COLR" => {
3788                        let mut reader = BytesReader::new(&table.data);
3789                        let colr = colr::COLR::new(&mut reader, 0, table.data.len() as u32)?;
3790                        font.colr = Some(colr);
3791                    }
3792                    b"HVAR" => {
3793                        let mut reader = BytesReader::new(&table.data);
3794                        let hvar = hvar::HVAR::new(&mut reader, 0, table.data.len() as u32)?;
3795                        font.hvar = Some(hvar);
3796                    }
3797                    b"MVAR" => {
3798                        let mut reader = BytesReader::new(&table.data);
3799                        let mvar = mvar::MVAR::new(&mut reader, 0, table.data.len() as u32)?;
3800                        font.mvar = Some(mvar);
3801                    }
3802                    b"CPAL" => {
3803                        let mut reader = BytesReader::new(&table.data);
3804                        let cpal = cpal::CPAL::new(&mut reader, 0, table.data.len() as u32)?;
3805                        font.cpal = Some(cpal);
3806                    }
3807                    b"sbix" => {
3808                        sbix_table = Some(table);
3809                    }
3810                    b"SVG " => {
3811                        let mut reader = BytesReader::new(&table.data);
3812                        let svg = svg::SVG::new(&mut reader, 0, table.data.len() as u32)?;
3813                        font.svg = Some(svg);
3814                    }
3815                    #[cfg(feature = "cff")]
3816                    b"CFF " => {
3817                        let mut reader = BytesReader::new(&table.data);
3818                        let cff = cff::CFF::new(&mut reader, 0, table.data.len() as u32).map_err(
3819                            |err| Error::new(std::io::ErrorKind::InvalidData, err.to_string()),
3820                        )?;
3821                        font.cff = Some(cff);
3822                        font.outline_format = GlyphFormat::CFF;
3823                    }
3824                    #[cfg(feature = "cff")]
3825                    b"CFF2" => {
3826                        let mut reader = BytesReader::new(&table.data);
3827                        let cff = cff::CFF::new(&mut reader, 0, table.data.len() as u32).map_err(
3828                            |err| Error::new(std::io::ErrorKind::InvalidData, err.to_string()),
3829                        )?;
3830                        font.cff = Some(cff);
3831                        font.outline_format = GlyphFormat::CFF2;
3832                    }
3833                    #[cfg(feature = "layout")]
3834                    b"GPOS" => {
3835                        let mut reader = BytesReader::new(&table.data);
3836                        let gpos = gpos::GPOS::new(&mut reader, 0, table.data.len() as u32)?;
3837                        font.gpos = Some(gpos);
3838                    }
3839                    #[cfg(feature = "layout")]
3840                    b"GSUB" => {
3841                        let mut reader = BytesReader::new(&table.data);
3842                        let gsub = gsub::GSUB::new(&mut reader, 0, table.data.len() as u32)?;
3843                        font.gsub = Some(gsub);
3844                    }
3845                    #[cfg(feature = "layout")]
3846                    b"GDEF" => {
3847                        let mut reader = BytesReader::new(&table.data);
3848                        let gdef = gdef::GDEF::new(&mut reader, 0, table.data.len() as usize)?;
3849                        font.gdef = Some(gdef);
3850                    }
3851                    b"vhea" => {
3852                        let mut reader = BytesReader::new(&table.data);
3853                        let vhea = vhea::VHEA::new(&mut reader, 0, table.data.len() as u32)?;
3854                        font.vhea = Some(vhea);
3855                    }
3856                    b"vmtx" => {
3857                        vmtx_table = Some(table);
3858                    }
3859                    b"VVAR" => {
3860                        let mut reader = BytesReader::new(&table.data);
3861                        let vvar = vvar::VVAR::new(&mut reader, 0, table.data.len() as u32)?;
3862                        font.vvar = Some(vvar);
3863                    }
3864                    _ => {}
3865                }
3866            }
3867            let hmtx_table = hmtx_table
3868                .as_ref()
3869                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No hmtx table"))?;
3870            let hhea = font
3871                .hhea
3872                .as_ref()
3873                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No hhea table"))?;
3874            let maxp = font
3875                .maxp
3876                .as_ref()
3877                .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No maxp table"))?;
3878            let mut reader = BytesReader::new(&hmtx_table.data);
3879            let hmtx = hmtx::HMTX::new(
3880                &mut reader,
3881                0,
3882                hmtx_table.data.len() as u32,
3883                hhea.number_of_hmetrics,
3884                maxp.num_glyphs,
3885            )?;
3886            font.hmtx = Some(hmtx);
3887            if let Some(vmtx_table) = vmtx_table {
3888                let mut reader = BytesReader::new(&vmtx_table.data);
3889                let vhea = font
3890                    .vhea
3891                    .as_ref()
3892                    .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No vhea table"))?;
3893                let vmtx = vmtx::VMTX::new(
3894                    &mut reader,
3895                    0,
3896                    vmtx_table.data.len() as u32,
3897                    vhea.number_of_vmetrics,
3898                    maxp.num_glyphs,
3899                )?;
3900                font.vmtx = Some(vmtx);
3901            }
3902            if let (Some(loca_table), Some(glyf_table)) = (loca_table.as_ref(), glyf_table.as_ref())
3903            {
3904                let mut reader = BytesReader::new(&loca_table.data);
3905                let head = font
3906                    .head
3907                    .as_ref()
3908                    .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No head table"))?;
3909                let index_to_loc_format = head.index_to_loc_format as usize;
3910                let loca = loca::LOCA::new_by_size(
3911                    &mut reader,
3912                    0,
3913                    loca_table.data.len() as u32,
3914                    index_to_loc_format,
3915                )?;
3916                font.loca = Some(loca);
3917                let mut reader = BytesReader::new(&glyf_table.data);
3918                let loca = font
3919                    .loca
3920                    .as_ref()
3921                    .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No loca table"))?;
3922                let glyf = glyf::GLYF::new(&mut reader, 0, glyf_table.data.len() as u32, loca);
3923                font.glyf = Some(glyf);
3924                font.outline_format = GlyphFormat::OpenTypeGlyph;
3925            }
3926
3927            if let Some(sbix_table) = sbix_table {
3928                let mut reader = BytesReader::new(&sbix_table.data);
3929                let num_glyphs = maxp.num_glyphs as u32;
3930                let sbix =
3931                    sbix::SBIX::new(&mut reader, 0, sbix_table.data.len() as u32, num_glyphs)?;
3932                font.sbix = Some(sbix);
3933            }
3934            #[cfg(debug_assertions)]
3935            {
3936                // font_debug(&font);
3937            }
3938            Ok(font)
3939        }
3940        fontheader::FontHeaders::WOFF2(_) => todo!(),
3941        fontheader::FontHeaders::Unknown => {
3942            //todo!(),
3943            Err(Error::new(
3944                std::io::ErrorKind::Other,
3945                "Unknown font type".to_string(),
3946            ))
3947        }
3948    }
3949}
3950
3951fn from_opentype<R: BinaryReader>(file: &mut R, header: &OTFHeader) -> Result<Font, Error> {
3952    let mut font = Font::empty();
3953    font.font_type = fontheader::FontHeaders::OTF(header.clone());
3954
3955    let records = header.table_records.as_ref();
3956
3957    for record in records.iter() {
3958        let tag: [u8; 4] = record.table_tag.to_be_bytes();
3959        match &tag {
3960            b"cmap" => {
3961                let cmap_encodings = CmapEncodings::new(file, record.offset, record.length)?;
3962                font.cmap = Some(cmap_encodings);
3963            }
3964            b"head" => {
3965                let head = head::HEAD::new(file, record.offset, record.length)?;
3966                font.head = Some(head);
3967            }
3968            b"fvar" => {
3969                let fvar = fvar::FVAR::new(file, record.offset, record.length)?;
3970                font.fvar = Some(fvar);
3971            }
3972            b"gvar" => {
3973                let gvar = gvar::GVAR::new(file, record.offset, record.length)?;
3974                font.gvar = Some(gvar);
3975            }
3976            b"avar" => {
3977                let avar = avar::AVAR::new(file, record.offset, record.length)?;
3978                font.avar = Some(avar);
3979            }
3980            b"hhea" => {
3981                let hhea = hhea::HHEA::new(file, record.offset, record.length)?;
3982                font.hhea = Some(hhea);
3983            }
3984            b"hmtx" => {
3985                let htmx_pos = Pointer {
3986                    offset: record.offset,
3987                    length: record.length,
3988                };
3989                font.hmtx_pos = Some(htmx_pos);
3990            }
3991            b"maxp" => {
3992                let maxp = maxp::MAXP::new(file, record.offset, record.length)?;
3993                font.maxp = Some(maxp);
3994            }
3995            b"name" => {
3996                let name = name::NAME::new(file, record.offset, record.length)?;
3997                let name_table = name::NameTable::new(&name);
3998                font.name = Some(name);
3999                font.name_table = Some(name_table);
4000            }
4001            b"OS/2" => {
4002                let os2 = os2::OS2::new(file, record.offset, record.length)?;
4003                font.os2 = Some(os2);
4004            }
4005            b"post" => {
4006                let post = post::POST::new(file, record.offset, record.length)?;
4007                font.post = Some(post);
4008            }
4009            b"loca" => {
4010                let loca_pos = Pointer {
4011                    offset: record.offset,
4012                    length: record.length,
4013                };
4014                font.loca_pos = Some(loca_pos);
4015            }
4016            b"glyf" => {
4017                let glyf_pos = Pointer {
4018                    offset: record.offset,
4019                    length: record.length,
4020                };
4021                font.glyf_pos = Some(glyf_pos);
4022            }
4023            b"COLR" => {
4024                let colr = colr::COLR::new(file, record.offset, record.length)?;
4025                font.colr = Some(colr);
4026            }
4027            b"HVAR" => {
4028                let hvar = hvar::HVAR::new(file, record.offset, record.length)?;
4029                font.hvar = Some(hvar);
4030            }
4031            b"MVAR" => {
4032                let mvar = mvar::MVAR::new(file, record.offset, record.length)?;
4033                font.mvar = Some(mvar);
4034            }
4035            b"CPAL" => {
4036                let cpal = cpal::CPAL::new(file, record.offset, record.length)?;
4037                font.cpal = Some(cpal);
4038            }
4039            b"sbix" => {
4040                let sbix_pos = Pointer {
4041                    offset: record.offset,
4042                    length: record.length,
4043                };
4044                font.sbix_pos = Some(sbix_pos);
4045            }
4046            b"SVG " => {
4047                let svg = svg::SVG::new(file, record.offset, record.length)?;
4048                font.svg = Some(svg);
4049            }
4050            #[cfg(feature = "cff")]
4051            b"CFF " => {
4052                let cff = cff::CFF::new(file, record.offset, record.length)
4053                    .map_err(|err| Error::new(std::io::ErrorKind::InvalidData, err.to_string()))?;
4054                font.cff = Some(cff);
4055                font.outline_format = GlyphFormat::CFF;
4056            }
4057            #[cfg(feature = "cff")]
4058            b"CFF2" => {
4059                let cff = cff::CFF::new(file, record.offset, record.length)
4060                    .map_err(|err| Error::new(std::io::ErrorKind::InvalidData, err.to_string()))?;
4061                font.cff = Some(cff);
4062                font.outline_format = GlyphFormat::CFF2;
4063            }
4064            #[cfg(feature = "layout")]
4065            b"GPOS" => {
4066                let gpos = gpos::GPOS::new(file, record.offset, record.length)?;
4067                font.gpos = Some(gpos);
4068            }
4069            #[cfg(feature = "layout")]
4070            b"GSUB" => {
4071                let gsub = gsub::GSUB::new(file, record.offset, record.length)?;
4072                font.gsub = Some(gsub);
4073                #[cfg(debug_assertions)]
4074                {
4075                    // println!("{}", &font.gsub.as_ref().unwrap().to_string());
4076                }
4077            }
4078            #[cfg(feature = "layout")]
4079            b"GDEF" => {
4080                let gdef = gdef::GDEF::new(file, record.offset as u64, record.length as usize)?;
4081                font.gdef = Some(gdef);
4082                #[cfg(debug_assertions)]
4083                {
4084                    // println!("{}", &font.gdef.as_ref().unwrap().to_string());
4085                }
4086            }
4087            #[cfg(feature = "layout")]
4088            b"vhea" => {
4089                let vhea = vhea::VHEA::new(file, record.offset, record.length)?;
4090                font.vhea = Some(vhea);
4091            }
4092            #[cfg(feature = "layout")]
4093            b"vmtx" => {
4094                let vmtx_pos = Pointer {
4095                    offset: record.offset,
4096                    length: record.length,
4097                };
4098                font.vmtx_pos = Some(vmtx_pos);
4099            }
4100            b"VVAR" => {
4101                let vvar = vvar::VVAR::new(file, record.offset, record.length)?;
4102                font.vvar = Some(vvar);
4103            }
4104            _ => {
4105                debug_assert!(true, "Unknown table tag")
4106            }
4107        }
4108    }
4109
4110    let num_glyphs = font
4111        .maxp
4112        .as_ref()
4113        .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No maxp table"))?
4114        .num_glyphs;
4115    let number_of_hmetrics = font
4116        .hhea
4117        .as_ref()
4118        .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No hhea table"))?
4119        .number_of_hmetrics;
4120    let hmtx_pointer = font
4121        .hmtx_pos
4122        .as_ref()
4123        .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No hmtx table pointer"))?;
4124    let offset = hmtx_pointer.offset;
4125    let length = hmtx_pointer.length;
4126
4127    let hmtx = hmtx::HMTX::new(file, offset, length, number_of_hmetrics, num_glyphs)?;
4128    font.hmtx = Some(hmtx);
4129
4130    if font.vmtx_pos.is_some() {
4131        let number_of_vmetrics = font
4132            .vhea
4133            .as_ref()
4134            .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No vhea table"))?
4135            .number_of_vmetrics;
4136        let vmtx_pointer = font
4137            .vmtx_pos
4138            .as_ref()
4139            .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No vmtx table pointer"))?;
4140        let offset = vmtx_pointer.offset;
4141        let length = vmtx_pointer.length;
4142        let vmtx = vmtx::VMTX::new(file, offset, length, number_of_vmetrics, num_glyphs)?;
4143        font.vmtx = Some(vmtx);
4144    }
4145
4146    if let Some(offset) = font.loca_pos.as_ref() {
4147        let length = offset.length;
4148        let index_to_loc_format = font
4149            .head
4150            .as_ref()
4151            .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No head table"))?
4152            .index_to_loc_format as usize;
4153        let loca = loca::LOCA::new_by_size(file, offset.offset, length, index_to_loc_format)?;
4154        font.loca = Some(loca);
4155        let glyf_pointer = font
4156            .glyf_pos
4157            .as_ref()
4158            .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No glyf table pointer"))?;
4159        let offset = glyf_pointer.offset;
4160        let length = glyf_pointer.length;
4161        let loca = font
4162            .loca
4163            .as_ref()
4164            .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No loca table"))?;
4165        let glyf = glyf::GLYF::new(file, offset, length, loca);
4166        font.glyf = Some(glyf);
4167        font.outline_format = GlyphFormat::OpenTypeGlyph;
4168    }
4169    if let Some(offset) = font.sbix_pos.as_ref() {
4170        let sbix = sbix::SBIX::new(file, offset.offset, offset.length, num_glyphs as u32)?;
4171        font.sbix = Some(sbix);
4172    }
4173
4174    if font.cmap.is_none() {
4175        debug_assert!(true, "No cmap table");
4176        return Err(Error::new(
4177            std::io::ErrorKind::Other,
4178            "No cmap table".to_string(),
4179        ));
4180    }
4181    if font.head.is_none() {
4182        debug_assert!(true, "No head table");
4183        return Err(Error::new(
4184            std::io::ErrorKind::Other,
4185            "No head table".to_string(),
4186        ));
4187    }
4188    if font.hhea.is_none() {
4189        debug_assert!(true, "No hhea table");
4190        return Err(Error::new(
4191            std::io::ErrorKind::Other,
4192            "No hhea table".to_string(),
4193        ));
4194    }
4195    if font.hmtx.is_none() {
4196        debug_assert!(true, "No hmtx table");
4197        return Err(Error::new(
4198            std::io::ErrorKind::Other,
4199            "No hmtx table".to_string(),
4200        ));
4201    }
4202    if font.maxp.is_none() {
4203        debug_assert!(true, "No maxp table");
4204        return Err(Error::new(
4205            std::io::ErrorKind::Other,
4206            "No maxp table".to_string(),
4207        ));
4208    }
4209    if font.name.is_none() {
4210        debug_assert!(true, "No name table");
4211        return Err(Error::new(
4212            std::io::ErrorKind::Other,
4213            "No name table".to_string(),
4214        ));
4215    }
4216
4217    Ok(font)
4218}