fn parse_buffer_with_index()

in src/font.rs [159:358]


    fn parse_buffer_with_index(buffer: &[u8], index: u32) -> Result<Vec<VariationData>, Error> {
        use ttf_parser::{name_id, Fixed, VariationAxis};

        let face = Face::parse(buffer, index)?;
        let axes: Vec<VariationAxis> = face
            .tables()
            .fvar
            .map(|v| v.axes.into_iter())
            .into_iter()
            .flatten()
            .collect::<Vec<_>>();

        // get fvar if any
        let mut instances: IndexMap<Vec<OrderedFloat<f32>>, Vec<FvarInstance>> = IndexMap::new();
        if let (Some(_), Some(name_table)) = (face.tables().fvar, face.tables().name) {
            // currently ttf-parser is missing `fvar`'s instance records, we parse them
            // directly from `RawFace`
            let data: &[u8] = face
                .raw_face()
                .table(ttf_parser::Tag::from_bytes(b"fvar"))
                .unwrap();
            let mut raw = &*data;
            let _version = raw.read_u32::<BigEndian>()?;
            let axis_offset = raw.read_u16::<BigEndian>()?;
            let _ = raw.read_u16::<BigEndian>()?;
            let axis_count = raw.read_u16::<BigEndian>()?;
            let axis_size = raw.read_u16::<BigEndian>()?;
            let instance_count = raw.read_u16::<BigEndian>()?;
            let instance_size = raw.read_u16::<BigEndian>()?;

            let data = &data[(axis_offset as usize + (axis_count as usize * axis_size as usize))..];
            for i in 0..instance_count {
                let mut raw = &data[(i as usize * instance_size as usize)..];
                let sub_family_name_id = raw.read_u16::<BigEndian>()?;
                let _ = raw.read_u16::<BigEndian>()?;
                let coords = (0..axis_count)
                    .map(|_| {
                        use ttf_parser::FromData;
                        let mut v = [0_u8; 4];
                        raw.read_exact(&mut v)
                            .map(|_| OrderedFloat(Fixed::parse(&v).unwrap().0))
                    })
                    .collect::<Result<Vec<_>, _>>()?;
                let postscript_name_id = if raw.is_empty() {
                    None
                } else {
                    Some(raw.read_u16::<BigEndian>()?)
                };
                let sub_family = name_table
                    .names
                    .into_iter()
                    .find(|name| name.name_id == sub_family_name_id)
                    .and_then(|name| {
                        Some(Name {
                            id: name.name_id,
                            name: name.to_string().or_else(|| {
                                // try to force unicode encoding
                                Some(std::str::from_utf8(name.name).ok()?.to_string())
                            })?,
                            language_id: name.language_id,
                        })
                    });
                let postscript = name_table
                    .names
                    .into_iter()
                    .find(|name| Some(name.name_id) == postscript_name_id)
                    .and_then(|name| {
                        Some(Name {
                            id: name.name_id,
                            name: name.to_string().or_else(|| {
                                // try to force unicode encoding
                                Some(std::str::from_utf8(name.name).ok()?.to_string())
                            })?,
                            language_id: name.language_id,
                        })
                    });
                if let (Some(sub_family), Some(postscript)) = (sub_family, postscript) {
                    instances.entry(coords).or_default().push(FvarInstance {
                        sub_family,
                        postscript,
                    })
                }
            }
        }
        let instances = instances
            .into_iter()
            .map(|(coords, names)| {
                return (
                    coords.into_iter().map(|v| Fixed(v.0)).collect::<Vec<_>>(),
                    names,
                );
            })
            .collect::<Vec<_>>();
        let mut style_names = vec![];
        let names = face
            .names()
            .into_iter()
            .filter_map(|name| {
                let id = name.name_id;
                let mut name_str = name.to_string().or_else(|| {
                    // try to force unicode encoding
                    Some(std::str::from_utf8(name.name).ok()?.to_string())
                })?;
                if id == name_id::TYPOGRAPHIC_SUBFAMILY {
                    style_names.push(Name {
                        id,
                        name: name_str.clone(),
                        language_id: name.language_id,
                    });
                }
                if id == name_id::FAMILY
                    || id == name_id::FULL_NAME
                    || id == name_id::POST_SCRIPT_NAME
                    || id == name_id::TYPOGRAPHIC_FAMILY
                {
                    if id == name_id::POST_SCRIPT_NAME {
                        name_str = name_str.replace(" ", "-");
                    }
                    Some(Name {
                        id,
                        name: name_str,
                        language_id: name.language_id,
                    })
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();

        if names.is_empty() {
            return Err(Error::EmptyName);
        }
        // Select a good name
        let ascii_name = names
            .iter()
            .map(|item| &item.name)
            .filter(|name| name.is_ascii() && name.len() > 3)
            .min_by(|n1, n2| match n1.len().cmp(&n2.len()) {
                std::cmp::Ordering::Equal => n1
                    .chars()
                    .filter(|c| *c == '-')
                    .count()
                    .cmp(&n2.chars().filter(|c| *c == '-').count()),
                ordering @ _ => ordering,
            })
            .cloned()
            .map(|name| {
                if name.starts_with(".") {
                    (&name[1..]).to_string()
                } else {
                    name
                }
            });
        let mut results = vec![];
        let key = FontKey {
            weight: Some(face.weight().to_number()),
            italic: Some(face.is_italic()),
            stretch: Some(face.width().to_number()),
            family: ascii_name.clone().unwrap_or_else(|| names[0].name.clone()),
            variations: vec![],
        };
        for (coords, variation_names) in instances {
            let mut key = key.clone();
            let width_axis_index = axes
                .iter()
                .position(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wdth"));
            let weight_axis_index = axes
                .iter()
                .position(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wght"));
            if let Some(value) = width_axis_index.and_then(|i| coords.get(i)) {
                // mapping wdth to usWidthClass, ref: https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxistag_wdth
                key.stretch = Some(((value.0 / 100.0) * 5.0).round().min(1.0).max(9.0) as u16);
            }
            if let Some(value) = weight_axis_index.and_then(|i| coords.get(i)) {
                key.weight = Some(value.0 as u16);
            }
            for (coord, axis) in coords.iter().zip(axes.iter()) {
                key.variations
                    .push((String::from_utf8(axis.tag.to_bytes().to_vec())?, coord.0));
            }
            results.push(VariationData {
                key,
                names: names.clone(),
                style_names: style_names.clone(),
                variation_names,
                index,
            });
        }
        if results.is_empty() {
            // this is not a variable font, add normal font data
            results.push(VariationData {
                names,
                key,
                variation_names: vec![],
                style_names,
                index,
            })
        }
        Ok(results)
    }