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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#![deny(missing_docs)]

//! Bitmap font handling.

pub mod cbdt;
pub mod sbix;

use num_traits as num;

use crate::error::ParseError;

/// Bit depth of bitmap data.
#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd)]
pub enum BitDepth {
    /// 1-bit per pixel (black and white).
    One = 1,
    /// 2-bits per pixel (grey).
    Two = 2,
    /// 4-bits per pixel (grey).
    Four = 4,
    /// 8-bits per pixel (grey).
    Eight = 8,
    /// 32-bits per pixel (RGBA)
    ThirtyTwo = 32,
}

/// A bitmap glyph with metrics.
pub struct BitmapGlyph {
    /// Horizontal pixels per em.
    ///
    /// Will be `None` if image data is a vector image.
    pub ppem_x: Option<u16>,
    /// Vertical pixels per em.
    ///
    /// Will be `None` if image data is a vector image.
    pub ppem_y: Option<u16>,
    /// Glyph metrics in pixels.
    pub metrics: Metrics,
    /// Bitmap data.
    pub bitmap: Bitmap,
}

/// Bitmap data, either raw or encapsulated in a container format like PNG.
#[allow(missing_docs)]
pub enum Bitmap {
    Embedded(EmbeddedBitmap),
    Encapsulated(EncapsulatedBitmap),
}

/// Raw bitmap data.
pub struct EmbeddedBitmap {
    /// The width of the bitmap in pixels.
    pub width: u8,
    /// The height of the bitmap in pixels.
    pub height: u8,
    /// The format of the pixel data.
    pub format: BitDepth,
    /// Raw pixel data.
    pub data: Box<[u8]>,
}

/// Bitmap data encapsulated in a container format like PNG.
pub struct EncapsulatedBitmap {
    /// The container format used to hold the bitmap data.
    pub format: EncapsulatedFormat,
    /// Bitmap data.
    pub data: Box<[u8]>,
}

/// The container format of an `EncapsulatedBitmap`.
#[allow(missing_docs)]
pub enum EncapsulatedFormat {
    Jpeg,
    Png,
    Tiff,
    Svg,
    /// A format not part of the OpenType specification.
    Other(u32),
}

/// Bitmap glyph metrics either embedded or from `hmtx`/`vmtx`.
#[derive(Debug)]
pub enum Metrics {
    /// Metrics were embedded with the bitmap.
    Embedded(EmbeddedMetrics),
    /// Metrics are available in the `hmtx` and `vmtx` tables.
    HmtxVmtx(OriginOffset),
}

/// Bitmap offset from glyph origin in font units.
#[derive(Debug)]
pub struct OriginOffset {
    /// The horizontal (x-axis) offset from the left edge of the graphic to the glyph’s origin.
    pub x: i16,
    /// The vertical (y-axis) offset from the bottom edge of the graphic to the glyph’s origin.
    pub y: i16,
}

/// Metrics embedded alongside the bitmap.
///
/// One or both of the horizontal or vertical metrics with always be present.
#[derive(Debug)]
pub struct EmbeddedMetrics {
    /// Horizontal pixels per em.
    pub ppem_x: u8,
    /// Vertical pixels per em.
    pub ppem_y: u8,
    /// Horizontal metrics.
    hori: Option<BitmapMetrics>,
    /// Vertical metrics.
    vert: Option<BitmapMetrics>,
}

/// The actual embedded bitmap glyph metrics in pixels.
#[derive(Copy, Clone, Debug)]
pub struct BitmapMetrics {
    /// Distance in pixels from the horizontal origin to the left edge of the bitmap.
    pub origin_offset_x: i16,
    /// Distance in pixels from the horizontal origin to the bottom edge of the bitmap.
    pub origin_offset_y: i16,
    /// Advance width in pixels.
    pub advance: u8,
    /// The spacing of the line before the baseline in pixels.
    pub ascender: i8,
    /// The spacing of the line after the baseline in pixels.
    pub descender: i8,
}

impl EmbeddedMetrics {
    fn new(
        ppem_x: u8,
        ppem_y: u8,
        hori: Option<BitmapMetrics>,
        vert: Option<BitmapMetrics>,
    ) -> Result<Self, ParseError> {
        if hori.is_none() && vert.is_none() {
            return Err(ParseError::MissingValue);
        }

        Ok(EmbeddedMetrics {
            ppem_x,
            ppem_y,
            hori,
            vert,
        })
    }

    /// Metrics for horizontal layout.
    pub fn hori(&self) -> Option<&BitmapMetrics> {
        self.hori.as_ref()
    }

    /// Metrics for vertical layout.
    pub fn vert(&self) -> Option<&BitmapMetrics> {
        self.vert.as_ref()
    }
}

/// Returns true if `value` is closer to zero than `current_best`, favouring positive values even
/// if they're further away from zero.
fn bigger_or_closer_to_zero<V>(value: V, current_best: V) -> bool
where
    V: PartialOrd + num::Signed + num::Zero,
{
    if value == V::zero() {
        return true;
    } else if current_best == V::zero() {
        return false;
    }

    match (current_best.is_positive(), value.is_positive()) {
        (true, true) if value < current_best => true,
        (true, false) => false,
        (false, true) => true,
        (false, false) if value > current_best => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_bigger_or_closer_to_zero() {
        // zero always wins
        assert!(bigger_or_closer_to_zero(0, -1));
        assert!(bigger_or_closer_to_zero(0, 0));
        assert!(bigger_or_closer_to_zero(0, 1));
        assert!(!bigger_or_closer_to_zero(-1, 0));
        assert!(!bigger_or_closer_to_zero(1, 0));

        // current best is negative
        assert!(bigger_or_closer_to_zero(10, -5)); // positive wins, even if further from zero
        assert!(bigger_or_closer_to_zero(-2, -5)); // negative wins if closer to zero
        assert!(!bigger_or_closer_to_zero(-7, -5));

        // current best is positive
        assert!(bigger_or_closer_to_zero(2, 5)); // positive wins if smaller
        assert!(!bigger_or_closer_to_zero(-2, 5)); // positive wins, even if further from zero
        assert!(!bigger_or_closer_to_zero(7, 5));
    }
}