1use core::{fmt, ops};
4
5use super::types::Cursor;
6use crate::{
7 alloc::Vec,
8 write::{VecExt, WriteTable},
9 ParseError, ParseErrorKind, TableTag,
10};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum FontCategory {
15 Regular,
17 Bold,
19 Italic,
21 BoldAndItalic,
23}
24
25impl fmt::Display for FontCategory {
26 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
27 formatter.write_str(self.as_str())
28 }
29}
30
31impl FontCategory {
32 pub const fn as_str(self) -> &'static str {
34 match self {
35 Self::Regular => "regular",
36 Self::Bold => "bold",
37 Self::Italic => "italic",
38 Self::BoldAndItalic => "bold italic",
39 }
40 }
41}
42
43#[derive(Debug, Clone, Copy)]
48pub enum EmbeddingPermissions {
49 Installable,
51 RestrictedLicense,
53 PreviewAndPrint,
55 Editable,
57}
58
59impl EmbeddingPermissions {
60 pub fn is_lenient(self) -> bool {
64 matches!(self, Self::Installable | Self::Editable)
65 }
66}
67
68#[derive(Debug, Clone, Copy)]
70pub struct UsagePermissions {
71 pub(crate) raw: u16,
72 pub embedding: EmbeddingPermissions,
74 pub embed_only_bitmaps: bool,
77 pub allow_subsetting: bool,
79}
80
81impl UsagePermissions {
82 fn parse(cursor: &mut Cursor<'_>) -> Result<Self, ParseError> {
83 const EMBEDDING_MASK: u16 = 0x0f;
84 const SUBSETTING_MASK: u16 = 0x0100;
85 const EMBED_BITMAPS_MASK: u16 = 0x0200;
86
87 cursor.read_u16_checked(|raw| {
88 let raw_embedding = raw & EMBEDDING_MASK;
89 let embedding = match raw_embedding {
90 0 => EmbeddingPermissions::Installable,
91 2 => EmbeddingPermissions::RestrictedLicense,
92 4 => EmbeddingPermissions::PreviewAndPrint,
93 8 => EmbeddingPermissions::Editable,
94 _ => {
95 return Err(ParseErrorKind::UnexpectedValue {
96 name: "usage_permissions",
97 expected: "one of 0, 2, 4, or 8".into(),
98 actual: raw_embedding.into(),
99 })
100 }
101 };
102
103 let can_subset = raw & SUBSETTING_MASK == 0;
104 let embed_only_bitmaps = raw & EMBED_BITMAPS_MASK != 0;
105
106 Ok(Self {
107 raw,
108 embedding,
109 embed_only_bitmaps,
110 allow_subsetting: can_subset,
111 })
112 })
113 }
114}
115
116#[derive(Debug, Clone, Copy)]
117pub(crate) struct Os2Table<'a> {
118 version: u16,
119 not_parsed_after_version: [u8; 6],
121 pub(super) usage_permissions: UsagePermissions,
122 not_parsed_after_permissions: [u8; 32],
124 unicode_ranges: u128,
125 vendor_id: [u8; 4],
126 selection: u16,
127 pub(super) first_char_index: u16,
128 pub(super) last_char_index: u16,
129 not_parsed_after_char_index: [u8; 10],
131 code_page_ranges: u64,
132 not_parsed_tail: &'a [u8],
133}
134
135impl<'a> Os2Table<'a> {
136 #[cfg_attr(
137 feature = "tracing",
138 tracing::instrument(level = "debug", err, skip(cursor), fields(range = ?cursor.range()))
139 )]
140 pub(super) fn parse(mut cursor: Cursor<'a>) -> Result<Self, ParseError> {
141 let version = cursor.read_u16_checked(|version| {
142 if !(2..=5).contains(&version) {
143 return Err(ParseErrorKind::UnexpectedValue {
144 name: "version",
145 expected: "value between 2 and 5".into(),
146 actual: version.into(),
147 });
148 }
149 Ok(version)
150 })?;
151 #[cfg(feature = "tracing")]
152 tracing::debug!(version, "parsed table version");
153
154 let not_parsed_after_version = cursor.read_byte_array::<6>()?;
155 let usage_permissions = UsagePermissions::parse(&mut cursor)?;
156 let not_parsed_after_permissions = cursor.read_byte_array::<32>()?;
157 let unicode_ranges = cursor.read_u128()?;
158 let vendor_id = cursor.read_byte_array::<4>()?;
159 let selection = cursor.read_u16()?;
160 let first_char_index = cursor.read_u16()?;
161 let last_char_index = cursor.read_u16()?;
162 let not_parsed_after_char_index = cursor.read_byte_array::<10>()?;
163 let code_page_ranges = cursor.read_u64()?;
164
165 #[cfg(feature = "tracing")]
166 tracing::debug!(
167 ?usage_permissions,
168 unicode_ranges,
169 selection,
170 first_char_index,
171 last_char_index,
172 code_page_ranges,
173 "parsed basic info"
174 );
175
176 Ok(Self {
177 version,
178 not_parsed_after_version,
179 usage_permissions,
180 not_parsed_after_permissions,
181 unicode_ranges,
182 vendor_id,
183 selection,
184 first_char_index,
185 last_char_index,
186 not_parsed_after_char_index,
187 code_page_ranges,
188 not_parsed_tail: cursor.bytes(),
189 })
190 }
191
192 pub(super) fn category(&self) -> FontCategory {
193 const ITALIC_MASK: u16 = 1;
194 const BOLD_MASK: u16 = 32;
195
196 let is_italic = self.selection & ITALIC_MASK != 0;
197 let is_bold = self.selection & BOLD_MASK != 0;
198 match (is_bold, is_italic) {
199 (false, false) => FontCategory::Regular,
200 (true, false) => FontCategory::Bold,
201 (false, true) => FontCategory::Italic,
202 (true, true) => FontCategory::BoldAndItalic,
203 }
204 }
205
206 pub(crate) fn subset(&mut self, char_range: ops::RangeInclusive<char>) {
207 self.unicode_ranges = 0;
209 self.code_page_ranges = 0;
210
211 self.first_char_index = u16::try_from(*char_range.start()).unwrap_or(u16::MAX);
212 self.last_char_index = u16::try_from(*char_range.end()).unwrap_or(u16::MAX);
213 }
214}
215
216impl WriteTable for Os2Table<'_> {
217 fn tag(&self) -> TableTag {
218 TableTag::OS2
219 }
220
221 fn write_to_vec(&self, buffer: &mut Vec<u8>) {
222 buffer.write_u16(self.version);
223 buffer.extend_from_slice(&self.not_parsed_after_version);
224 buffer.write_u16(self.usage_permissions.raw);
225 buffer.extend_from_slice(&self.not_parsed_after_permissions);
226 buffer.extend_from_slice(&self.unicode_ranges.to_be_bytes());
227 buffer.extend_from_slice(&self.vendor_id);
228 buffer.write_u16(self.selection);
229 buffer.write_u16(self.first_char_index);
230 buffer.write_u16(self.last_char_index);
231 buffer.extend_from_slice(&self.not_parsed_after_char_index);
232 buffer.write_u64(self.code_page_ranges);
233 buffer.extend_from_slice(self.not_parsed_tail);
234 }
235}