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
// THIS FILE IS AUTOGENERATED.
// Any changes to this file will be overwritten.
// For more information about how codegen works, see font-codegen/README.md

#[allow(unused_imports)]
use crate::codegen_prelude::*;

/// [post (PostScript)](https://docs.microsoft.com/en-us/typography/opentype/spec/post#header) table
#[derive(Clone, Debug)]
pub struct Post {
    /// 0x00010000 for version 1.0 0x00020000 for version 2.0
    /// 0x00025000 for version 2.5 (deprecated) 0x00030000 for version
    /// 3.0
    pub version: Version16Dot16,
    /// Italic angle in counter-clockwise degrees from the vertical.
    /// Zero for upright text, negative for text that leans to the
    /// right (forward).
    pub italic_angle: Fixed,
    /// This is the suggested distance of the top of the underline from
    /// the baseline (negative values indicate below baseline). The
    /// PostScript definition of this FontInfo dictionary key (the y
    /// coordinate of the center of the stroke) is not used for
    /// historical reasons. The value of the PostScript key may be
    /// calculated by subtracting half the underlineThickness from the
    /// value of this field.
    pub underline_position: FWord,
    /// Suggested values for the underline thickness. In general, the
    /// underline thickness should match the thickness of the
    /// underscore character (U+005F LOW LINE), and should also match
    /// the strikeout thickness, which is specified in the OS/2 table.
    pub underline_thickness: FWord,
    /// Set to 0 if the font is proportionally spaced, non-zero if the
    /// font is not proportionally spaced (i.e. monospaced).
    pub is_fixed_pitch: u32,
    /// Minimum memory usage when an OpenType font is downloaded.
    pub min_mem_type42: u32,
    /// Maximum memory usage when an OpenType font is downloaded.
    pub max_mem_type42: u32,
    /// Minimum memory usage when an OpenType font is downloaded as a
    /// Type 1 font.
    pub min_mem_type1: u32,
    /// Maximum memory usage when an OpenType font is downloaded as a
    /// Type 1 font.
    pub max_mem_type1: u32,
    /// Number of glyphs (this should be the same as numGlyphs in
    /// 'maxp' table).
    pub num_glyphs: Option<u16>,
    /// Array of indices into the string data. See below for details.
    pub glyph_name_index: Option<Vec<u16>>,
    /// Storage for the string data.
    pub string_data: Option<Vec<PString>>,
}

impl FontWrite for Post {
    fn write_into(&self, writer: &mut TableWriter) {
        let version = self.version;
        version.write_into(writer);
        self.italic_angle.write_into(writer);
        self.underline_position.write_into(writer);
        self.underline_thickness.write_into(writer);
        self.is_fixed_pitch.write_into(writer);
        self.min_mem_type42.write_into(writer);
        self.max_mem_type42.write_into(writer);
        self.min_mem_type1.write_into(writer);
        self.max_mem_type1.write_into(writer);
        version.compatible(Version16Dot16::VERSION_2_0).then(|| {
            self.num_glyphs
                .as_ref()
                .expect("missing versioned field should have failed validation")
                .write_into(writer)
        });
        version.compatible(Version16Dot16::VERSION_2_0).then(|| {
            self.glyph_name_index
                .as_ref()
                .expect("missing versioned field should have failed validation")
                .write_into(writer)
        });
        version.compatible(Version16Dot16::VERSION_2_0).then(|| {
            self.string_data
                .as_ref()
                .expect("missing versioned field should have failed validation")
                .write_into(writer)
        });
    }
}

impl Validate for Post {
    fn validate_impl(&self, ctx: &mut ValidationCtx) {
        ctx.in_table("Post", |ctx| {
            let version = self.version;
            ctx.in_field("num_glyphs", |ctx| {
                if version.compatible(Version16Dot16::VERSION_2_0) && self.num_glyphs.is_none() {
                    ctx.report(format!("field must be present for version {version}"));
                }
            });
            ctx.in_field("glyph_name_index", |ctx| {
                if version.compatible(Version16Dot16::VERSION_2_0)
                    && self.glyph_name_index.is_none()
                {
                    ctx.report(format!("field must be present for version {version}"));
                }
                if self.glyph_name_index.is_some()
                    && self.glyph_name_index.as_ref().unwrap().len() > (u16::MAX as usize)
                {
                    ctx.report("array excedes max length");
                }
            });
        })
    }
}

#[cfg(feature = "parsing")]
impl<'a> FromObjRef<read_fonts::tables::post::Post<'a>> for Post {
    fn from_obj_ref(obj: &read_fonts::tables::post::Post<'a>, _: FontData) -> Self {
        let offset_data = obj.offset_data();
        Post {
            version: obj.version(),
            italic_angle: obj.italic_angle(),
            underline_position: obj.underline_position(),
            underline_thickness: obj.underline_thickness(),
            is_fixed_pitch: obj.is_fixed_pitch(),
            min_mem_type42: obj.min_mem_type42(),
            max_mem_type42: obj.max_mem_type42(),
            min_mem_type1: obj.min_mem_type1(),
            max_mem_type1: obj.max_mem_type1(),
            num_glyphs: obj.num_glyphs(),
            glyph_name_index: obj
                .glyph_name_index()
                .map(|obj| obj.iter().map(|x| x.get()).collect()),
            string_data: obj.string_data().map(|obj| {
                obj.iter()
                    .filter_map(|x| x.map(|x| FromObjRef::from_obj_ref(&x, offset_data)).ok())
                    .collect()
            }),
        }
    }
}

#[cfg(feature = "parsing")]
impl<'a> FromTableRef<read_fonts::tables::post::Post<'a>> for Post {}

#[cfg(feature = "parsing")]
impl<'a> FontRead<'a> for Post {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        <read_fonts::tables::post::Post as FontRead>::read(data).map(|x| x.to_owned_table())
    }
}