summaryrefslogtreecommitdiff
path: root/src/font.rs
blob: 78fc0d441f5b2a71e42a9c899146b3e836a149cd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! Font handling.

use std::fmt::{self, Display, Formatter};

use fontdock::FaceFromVec;

/// An owned font face.
pub struct FaceBuf {
    data: Box<[u8]>,
    ttf: ttf_parser::Face<'static>,
    buzz: rustybuzz::Face<'static>,
}

impl FaceBuf {
    /// The raw face data.
    pub fn data(&self) -> &[u8] {
        &self.data
    }

    /// Get a reference to the underlying ttf-parser face.
    pub fn ttf(&self) -> &ttf_parser::Face<'_> {
        // We can't implement Deref because that would leak the internal 'static
        // lifetime.
        &self.ttf
    }

    /// Get a reference to the underlying rustybuzz face.
    pub fn buzz(&self) -> &rustybuzz::Face<'_> {
        // We can't implement Deref because that would leak the internal 'static
        // lifetime.
        &self.buzz
    }
}

impl FaceFromVec for FaceBuf {
    fn from_vec(vec: Vec<u8>, i: u32) -> Option<Self> {
        let data = vec.into_boxed_slice();

        // SAFETY: The slices's location is stable in memory since we don't
        //         touch it and it can't be touched from outside this type.
        let slice: &'static [u8] =
            unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) };

        Some(Self {
            data,
            ttf: ttf_parser::Face::from_slice(slice, i).ok()?,
            buzz: rustybuzz::Face::from_slice(slice, i)?,
        })
    }
}

/// Identifies a vertical metric of a font.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum VerticalFontMetric {
    /// The distance from the baseline to the typographic ascender.
    ///
    /// Corresponds to the typographic ascender from the `OS/2` table if present
    /// and falls back to the ascender from the `hhea` table otherwise.
    Ascender,
    /// The approximate height of uppercase letters.
    CapHeight,
    /// The approximate height of non-ascending lowercase letters.
    XHeight,
    /// The baseline on which the letters rest.
    Baseline,
    /// The distance from the baseline to the typographic descender.
    ///
    /// Corresponds to the typographic descender from the `OS/2` table if
    /// present and falls back to the descender from the `hhea` table otherwise.
    Descender,
}

impl VerticalFontMetric {
    /// Look up the metric in the given font face.
    pub fn lookup(self, face: &ttf_parser::Face) -> i16 {
        match self {
            VerticalFontMetric::Ascender => lookup_ascender(face),
            VerticalFontMetric::CapHeight => face
                .capital_height()
                .filter(|&h| h > 0)
                .unwrap_or_else(|| lookup_ascender(face)),
            VerticalFontMetric::XHeight => face
                .x_height()
                .filter(|&h| h > 0)
                .unwrap_or_else(|| lookup_ascender(face)),
            VerticalFontMetric::Baseline => 0,
            VerticalFontMetric::Descender => lookup_descender(face),
        }
    }
}

/// The ascender of the face.
fn lookup_ascender(face: &ttf_parser::Face) -> i16 {
    // We prefer the typographic ascender over the Windows ascender because
    // it can be overly large if the font has large glyphs.
    face.typographic_ascender().unwrap_or_else(|| face.ascender())
}

/// The descender of the face.
fn lookup_descender(face: &ttf_parser::Face) -> i16 {
    // See `lookup_ascender` for reason.
    face.typographic_descender().unwrap_or_else(|| face.descender())
}

impl Display for VerticalFontMetric {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.pad(match self {
            Self::Ascender => "ascender",
            Self::CapHeight => "cap-height",
            Self::XHeight => "x-height",
            Self::Baseline => "baseline",
            Self::Descender => "descender",
        })
    }
}