1 | //! A [Compact Font Format Table]( |
2 | //! https://docs.microsoft.com/en-us/typography/opentype/spec/cff) implementation. |
3 | |
4 | // Useful links: |
5 | // http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf |
6 | // http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf |
7 | // https://github.com/opentypejs/opentype.js/blob/master/src/tables/cff.js |
8 | |
9 | use core::convert::TryFrom; |
10 | use core::num::NonZeroU16; |
11 | use core::ops::Range; |
12 | |
13 | use super::argstack::ArgumentsStack; |
14 | use super::charset::{parse_charset, Charset}; |
15 | use super::charstring::CharStringParser; |
16 | use super::dict::DictionaryParser; |
17 | use super::encoding::{parse_encoding, Encoding, STANDARD_ENCODING}; |
18 | use super::index::{parse_index, skip_index, Index}; |
19 | #[cfg (feature = "glyph-names" )] |
20 | use super::std_names::STANDARD_NAMES; |
21 | use super::{calc_subroutine_bias, conv_subroutine_index, Builder, CFFError, IsEven, StringId}; |
22 | use crate::parser::{LazyArray16, NumFrom, Stream, TryNumFrom}; |
23 | use crate::{DummyOutline, GlyphId, OutlineBuilder, Rect, RectF}; |
24 | |
25 | // Limits according to the Adobe Technical Note #5176, chapter 4 DICT Data. |
26 | const MAX_OPERANDS_LEN: usize = 48; |
27 | |
28 | // Limits according to the Adobe Technical Note #5177 Appendix B. |
29 | const STACK_LIMIT: u8 = 10; |
30 | const MAX_ARGUMENTS_STACK_LEN: usize = 48; |
31 | |
32 | const TWO_BYTE_OPERATOR_MARK: u8 = 12; |
33 | |
34 | /// Enumerates some operators defined in the Adobe Technical Note #5177. |
35 | mod operator { |
36 | pub const HORIZONTAL_STEM: u8 = 1; |
37 | pub const VERTICAL_STEM: u8 = 3; |
38 | pub const VERTICAL_MOVE_TO: u8 = 4; |
39 | pub const LINE_TO: u8 = 5; |
40 | pub const HORIZONTAL_LINE_TO: u8 = 6; |
41 | pub const VERTICAL_LINE_TO: u8 = 7; |
42 | pub const CURVE_TO: u8 = 8; |
43 | pub const CALL_LOCAL_SUBROUTINE: u8 = 10; |
44 | pub const RETURN: u8 = 11; |
45 | pub const ENDCHAR: u8 = 14; |
46 | pub const HORIZONTAL_STEM_HINT_MASK: u8 = 18; |
47 | pub const HINT_MASK: u8 = 19; |
48 | pub const COUNTER_MASK: u8 = 20; |
49 | pub const MOVE_TO: u8 = 21; |
50 | pub const HORIZONTAL_MOVE_TO: u8 = 22; |
51 | pub const VERTICAL_STEM_HINT_MASK: u8 = 23; |
52 | pub const CURVE_LINE: u8 = 24; |
53 | pub const LINE_CURVE: u8 = 25; |
54 | pub const VV_CURVE_TO: u8 = 26; |
55 | pub const HH_CURVE_TO: u8 = 27; |
56 | pub const SHORT_INT: u8 = 28; |
57 | pub const CALL_GLOBAL_SUBROUTINE: u8 = 29; |
58 | pub const VH_CURVE_TO: u8 = 30; |
59 | pub const HV_CURVE_TO: u8 = 31; |
60 | pub const HFLEX: u8 = 34; |
61 | pub const FLEX: u8 = 35; |
62 | pub const HFLEX1: u8 = 36; |
63 | pub const FLEX1: u8 = 37; |
64 | pub const FIXED_16_16: u8 = 255; |
65 | } |
66 | |
67 | /// Enumerates some operators defined in the Adobe Technical Note #5176, |
68 | /// Table 9 Top DICT Operator Entries |
69 | mod top_dict_operator { |
70 | pub const CHARSET_OFFSET: u16 = 15; |
71 | pub const ENCODING_OFFSET: u16 = 16; |
72 | pub const CHAR_STRINGS_OFFSET: u16 = 17; |
73 | pub const PRIVATE_DICT_SIZE_AND_OFFSET: u16 = 18; |
74 | pub const FONT_MATRIX: u16 = 1207; |
75 | pub const ROS: u16 = 1230; |
76 | pub const FD_ARRAY: u16 = 1236; |
77 | pub const FD_SELECT: u16 = 1237; |
78 | } |
79 | |
80 | /// Enumerates some operators defined in the Adobe Technical Note #5176, |
81 | /// Table 23 Private DICT Operators |
82 | mod private_dict_operator { |
83 | pub const LOCAL_SUBROUTINES_OFFSET: u16 = 19; |
84 | pub const DEFAULT_WIDTH: u16 = 20; |
85 | pub const NOMINAL_WIDTH: u16 = 21; |
86 | } |
87 | |
88 | /// Enumerates Charset IDs defined in the Adobe Technical Note #5176, Table 22 |
89 | mod charset_id { |
90 | pub const ISO_ADOBE: usize = 0; |
91 | pub const EXPERT: usize = 1; |
92 | pub const EXPERT_SUBSET: usize = 2; |
93 | } |
94 | |
95 | /// Enumerates Charset IDs defined in the Adobe Technical Note #5176, Table 16 |
96 | mod encoding_id { |
97 | pub const STANDARD: usize = 0; |
98 | pub const EXPERT: usize = 1; |
99 | } |
100 | |
101 | #[derive (Clone, Copy, Debug)] |
102 | pub(crate) enum FontKind<'a> { |
103 | SID(SIDMetadata<'a>), |
104 | CID(CIDMetadata<'a>), |
105 | } |
106 | |
107 | #[derive (Clone, Copy, Default, Debug)] |
108 | pub(crate) struct SIDMetadata<'a> { |
109 | local_subrs: Index<'a>, |
110 | /// Can be zero. |
111 | default_width: f32, |
112 | /// Can be zero. |
113 | nominal_width: f32, |
114 | encoding: Encoding<'a>, |
115 | } |
116 | |
117 | #[derive (Clone, Copy, Default, Debug)] |
118 | pub(crate) struct CIDMetadata<'a> { |
119 | fd_array: Index<'a>, |
120 | fd_select: FDSelect<'a>, |
121 | } |
122 | |
123 | /// An affine transformation matrix. |
124 | #[allow (missing_docs)] |
125 | #[derive (Clone, Copy, Debug)] |
126 | pub struct Matrix { |
127 | pub sx: f32, |
128 | pub ky: f32, |
129 | pub kx: f32, |
130 | pub sy: f32, |
131 | pub tx: f32, |
132 | pub ty: f32, |
133 | } |
134 | |
135 | impl Default for Matrix { |
136 | fn default() -> Self { |
137 | Self { |
138 | sx: 0.001, |
139 | ky: 0.0, |
140 | kx: 0.0, |
141 | sy: 0.001, |
142 | tx: 0.0, |
143 | ty: 0.0, |
144 | } |
145 | } |
146 | } |
147 | |
148 | #[derive (Default)] |
149 | struct TopDict { |
150 | charset_offset: Option<usize>, |
151 | encoding_offset: Option<usize>, |
152 | char_strings_offset: usize, |
153 | private_dict_range: Option<Range<usize>>, |
154 | matrix: Matrix, |
155 | has_ros: bool, |
156 | fd_array_offset: Option<usize>, |
157 | fd_select_offset: Option<usize>, |
158 | } |
159 | |
160 | fn parse_top_dict(s: &mut Stream) -> Option<TopDict> { |
161 | let mut top_dict = TopDict::default(); |
162 | |
163 | let index = parse_index::<u16>(s)?; |
164 | |
165 | // The Top DICT INDEX should have only one dictionary. |
166 | let data = index.get(0)?; |
167 | |
168 | let mut operands_buffer = [0.0; MAX_OPERANDS_LEN]; |
169 | let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer); |
170 | while let Some(operator) = dict_parser.parse_next() { |
171 | match operator.get() { |
172 | top_dict_operator::CHARSET_OFFSET => { |
173 | top_dict.charset_offset = dict_parser.parse_offset(); |
174 | } |
175 | top_dict_operator::ENCODING_OFFSET => { |
176 | top_dict.encoding_offset = dict_parser.parse_offset(); |
177 | } |
178 | top_dict_operator::CHAR_STRINGS_OFFSET => { |
179 | top_dict.char_strings_offset = dict_parser.parse_offset()?; |
180 | } |
181 | top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET => { |
182 | top_dict.private_dict_range = dict_parser.parse_range(); |
183 | } |
184 | top_dict_operator::FONT_MATRIX => { |
185 | dict_parser.parse_operands()?; |
186 | let operands = dict_parser.operands(); |
187 | if operands.len() == 6 { |
188 | top_dict.matrix = Matrix { |
189 | sx: operands[0] as f32, |
190 | ky: operands[1] as f32, |
191 | kx: operands[2] as f32, |
192 | sy: operands[3] as f32, |
193 | tx: operands[4] as f32, |
194 | ty: operands[5] as f32, |
195 | }; |
196 | } |
197 | } |
198 | top_dict_operator::ROS => { |
199 | top_dict.has_ros = true; |
200 | } |
201 | top_dict_operator::FD_ARRAY => { |
202 | top_dict.fd_array_offset = dict_parser.parse_offset(); |
203 | } |
204 | top_dict_operator::FD_SELECT => { |
205 | top_dict.fd_select_offset = dict_parser.parse_offset(); |
206 | } |
207 | _ => {} |
208 | } |
209 | } |
210 | |
211 | Some(top_dict) |
212 | } |
213 | |
214 | // TODO: move to integration |
215 | #[cfg (test)] |
216 | mod tests { |
217 | use super::*; |
218 | |
219 | #[test ] |
220 | fn private_dict_size_overflow() { |
221 | let data = &[ |
222 | 0x00, 0x01, // count: 1 |
223 | 0x01, // offset size: 1 |
224 | 0x01, // index [0]: 1 |
225 | 0x0C, // index [1]: 14 |
226 | 0x1D, 0x7F, 0xFF, 0xFF, 0xFF, // length: i32::MAX |
227 | 0x1D, 0x7F, 0xFF, 0xFF, 0xFF, // offset: i32::MAX |
228 | 0x12, // operator: 18 (private) |
229 | ]; |
230 | |
231 | let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap(); |
232 | assert_eq!(top_dict.private_dict_range, Some(2147483647..4294967294)); |
233 | } |
234 | |
235 | #[test ] |
236 | fn private_dict_negative_char_strings_offset() { |
237 | let data = &[ |
238 | 0x00, 0x01, // count: 1 |
239 | 0x01, // offset size: 1 |
240 | 0x01, // index [0]: 1 |
241 | 0x03, // index [1]: 3 |
242 | // Item 0 |
243 | 0x8A, // offset: -1 |
244 | 0x11, // operator: 17 (char_string) |
245 | ]; |
246 | |
247 | assert!(parse_top_dict(&mut Stream::new(data)).is_none()); |
248 | } |
249 | |
250 | #[test ] |
251 | fn private_dict_no_char_strings_offset_operand() { |
252 | let data = &[ |
253 | 0x00, 0x01, // count: 1 |
254 | 0x01, // offset size: 1 |
255 | 0x01, // index [0]: 1 |
256 | 0x02, // index [1]: 2 |
257 | // Item 0 |
258 | // <-- No number here. |
259 | 0x11, // operator: 17 (char_string) |
260 | ]; |
261 | |
262 | assert!(parse_top_dict(&mut Stream::new(data)).is_none()); |
263 | } |
264 | |
265 | #[test ] |
266 | fn negative_private_dict_offset_and_size() { |
267 | let data = &[ |
268 | 0x00, 0x01, // count: 1 |
269 | 0x01, // offset size: 1 |
270 | 0x01, // index [0]: 1 |
271 | 0x04, // index [1]: 4 |
272 | // Item 0 |
273 | 0x8A, // length: -1 |
274 | 0x8A, // offset: -1 |
275 | 0x12, // operator: 18 (private) |
276 | ]; |
277 | |
278 | let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap(); |
279 | assert!(top_dict.private_dict_range.is_none()); |
280 | } |
281 | } |
282 | |
283 | #[derive (Default, Debug)] |
284 | struct PrivateDict { |
285 | local_subroutines_offset: Option<usize>, |
286 | default_width: Option<f32>, |
287 | nominal_width: Option<f32>, |
288 | } |
289 | |
290 | fn parse_private_dict(data: &[u8]) -> PrivateDict { |
291 | let mut dict: PrivateDict = PrivateDict::default(); |
292 | let mut operands_buffer: [f64; 48] = [0.0; MAX_OPERANDS_LEN]; |
293 | let mut dict_parser: DictionaryParser<'_> = DictionaryParser::new(data, &mut operands_buffer); |
294 | while let Some(operator: Operator) = dict_parser.parse_next() { |
295 | if operator.get() == private_dict_operator::LOCAL_SUBROUTINES_OFFSET { |
296 | dict.local_subroutines_offset = dict_parser.parse_offset(); |
297 | } else if operator.get() == private_dict_operator::DEFAULT_WIDTH { |
298 | dict.default_width = dict_parser.parse_number().map(|n: f64| n as f32); |
299 | } else if operator.get() == private_dict_operator::NOMINAL_WIDTH { |
300 | dict.nominal_width = dict_parser.parse_number().map(|n: f64| n as f32); |
301 | } |
302 | } |
303 | |
304 | dict |
305 | } |
306 | |
307 | fn parse_font_dict(data: &[u8]) -> Option<Range<usize>> { |
308 | let mut operands_buffer: [f64; 48] = [0.0; MAX_OPERANDS_LEN]; |
309 | let mut dict_parser: DictionaryParser<'_> = DictionaryParser::new(data, &mut operands_buffer); |
310 | while let Some(operator: Operator) = dict_parser.parse_next() { |
311 | if operator.get() == top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET { |
312 | return dict_parser.parse_range(); |
313 | } |
314 | } |
315 | |
316 | None |
317 | } |
318 | |
319 | /// In CID fonts, to get local subroutines we have to: |
320 | /// 1. Find Font DICT index via FDSelect by GID. |
321 | /// 2. Get Font DICT data from FDArray using this index. |
322 | /// 3. Get a Private DICT offset from a Font DICT. |
323 | /// 4. Get a local subroutine offset from Private DICT. |
324 | /// 5. Parse a local subroutine at offset. |
325 | fn parse_cid_local_subrs<'a>( |
326 | data: &'a [u8], |
327 | glyph_id: GlyphId, |
328 | cid: &CIDMetadata, |
329 | ) -> Option<Index<'a>> { |
330 | let font_dict_index: u8 = cid.fd_select.font_dict_index(glyph_id)?; |
331 | let font_dict_data: &[u8] = cid.fd_array.get(index:u32::from(font_dict_index))?; |
332 | let private_dict_range: Range = parse_font_dict(font_dict_data)?; |
333 | let private_dict_data: &[u8] = data.get(index:private_dict_range.clone())?; |
334 | let private_dict: PrivateDict = parse_private_dict(private_dict_data); |
335 | let subroutines_offset: usize = private_dict.local_subroutines_offset?; |
336 | |
337 | // 'The local subroutines offset is relative to the beginning |
338 | // of the Private DICT data.' |
339 | let start: usize = private_dict_range.start.checked_add(subroutines_offset)?; |
340 | let subrs_data: &[u8] = data.get(index:start..)?; |
341 | let mut s: Stream<'_> = Stream::new(subrs_data); |
342 | parse_index::<u16>(&mut s) |
343 | } |
344 | |
345 | struct CharStringParserContext<'a> { |
346 | metadata: &'a Table<'a>, |
347 | width: Option<f32>, |
348 | stems_len: u32, |
349 | has_endchar: bool, |
350 | has_seac: bool, |
351 | glyph_id: GlyphId, // Required to parse local subroutine in CID fonts. |
352 | local_subrs: Option<Index<'a>>, |
353 | } |
354 | |
355 | fn parse_char_string( |
356 | data: &[u8], |
357 | metadata: &Table, |
358 | glyph_id: GlyphId, |
359 | width_only: bool, |
360 | builder: &mut dyn OutlineBuilder, |
361 | ) -> Result<(Rect, Option<f32>), CFFError> { |
362 | let local_subrs = match metadata.kind { |
363 | FontKind::SID(ref sid) => Some(sid.local_subrs), |
364 | FontKind::CID(_) => None, // Will be resolved on request. |
365 | }; |
366 | |
367 | let mut ctx = CharStringParserContext { |
368 | metadata, |
369 | width: None, |
370 | stems_len: 0, |
371 | has_endchar: false, |
372 | has_seac: false, |
373 | glyph_id, |
374 | local_subrs, |
375 | }; |
376 | |
377 | let mut inner_builder = Builder { |
378 | builder, |
379 | bbox: RectF::new(), |
380 | }; |
381 | |
382 | let stack = ArgumentsStack { |
383 | data: &mut [0.0; MAX_ARGUMENTS_STACK_LEN], // 192B |
384 | len: 0, |
385 | max_len: MAX_ARGUMENTS_STACK_LEN, |
386 | }; |
387 | let mut parser = CharStringParser { |
388 | stack, |
389 | builder: &mut inner_builder, |
390 | x: 0.0, |
391 | y: 0.0, |
392 | has_move_to: false, |
393 | is_first_move_to: true, |
394 | width_only, |
395 | }; |
396 | _parse_char_string(&mut ctx, data, 0, &mut parser)?; |
397 | |
398 | if width_only { |
399 | return Ok((Rect::zero(), ctx.width)); |
400 | } |
401 | |
402 | if !ctx.has_endchar { |
403 | return Err(CFFError::MissingEndChar); |
404 | } |
405 | |
406 | let bbox = parser.builder.bbox; |
407 | |
408 | // Check that bbox was changed. |
409 | if bbox.is_default() { |
410 | return Err(CFFError::ZeroBBox); |
411 | } |
412 | |
413 | let rect = bbox.to_rect().ok_or(CFFError::BboxOverflow)?; |
414 | Ok((rect, ctx.width)) |
415 | } |
416 | |
417 | fn _parse_char_string( |
418 | ctx: &mut CharStringParserContext, |
419 | char_string: &[u8], |
420 | depth: u8, |
421 | p: &mut CharStringParser, |
422 | ) -> Result<(), CFFError> { |
423 | let mut s = Stream::new(char_string); |
424 | while !s.at_end() { |
425 | let op = s.read::<u8>().ok_or(CFFError::ReadOutOfBounds)?; |
426 | match op { |
427 | 0 | 2 | 9 | 13 | 15 | 16 | 17 => { |
428 | // Reserved. |
429 | return Err(CFFError::InvalidOperator); |
430 | } |
431 | operator::HORIZONTAL_STEM |
432 | | operator::VERTICAL_STEM |
433 | | operator::HORIZONTAL_STEM_HINT_MASK |
434 | | operator::VERTICAL_STEM_HINT_MASK => { |
435 | // y dy {dya dyb}* hstem |
436 | // x dx {dxa dxb}* vstem |
437 | // y dy {dya dyb}* hstemhm |
438 | // x dx {dxa dxb}* vstemhm |
439 | |
440 | // If the stack length is uneven, than the first value is a `width`. |
441 | let len = if p.stack.len().is_odd() && ctx.width.is_none() { |
442 | ctx.width = Some(p.stack.at(0)); |
443 | p.stack.len() - 1 |
444 | } else { |
445 | p.stack.len() |
446 | }; |
447 | |
448 | ctx.stems_len += len as u32 >> 1; |
449 | |
450 | // We are ignoring the hint operators. |
451 | p.stack.clear(); |
452 | } |
453 | operator::VERTICAL_MOVE_TO => { |
454 | let mut i = 0; |
455 | if p.stack.len() == 2 { |
456 | i += 1; |
457 | if ctx.width.is_none() { |
458 | ctx.width = Some(p.stack.at(0)); |
459 | } |
460 | } |
461 | |
462 | p.parse_vertical_move_to(i)?; |
463 | } |
464 | operator::LINE_TO => { |
465 | p.parse_line_to()?; |
466 | } |
467 | operator::HORIZONTAL_LINE_TO => { |
468 | p.parse_horizontal_line_to()?; |
469 | } |
470 | operator::VERTICAL_LINE_TO => { |
471 | p.parse_vertical_line_to()?; |
472 | } |
473 | operator::CURVE_TO => { |
474 | p.parse_curve_to()?; |
475 | } |
476 | operator::CALL_LOCAL_SUBROUTINE => { |
477 | if p.stack.is_empty() { |
478 | return Err(CFFError::InvalidArgumentsStackLength); |
479 | } |
480 | |
481 | if depth == STACK_LIMIT { |
482 | return Err(CFFError::NestingLimitReached); |
483 | } |
484 | |
485 | // Parse and remember the local subroutine for the current glyph. |
486 | // Since it's a pretty complex task, we're doing it only when |
487 | // a local subroutine is actually requested by the glyphs charstring. |
488 | if ctx.local_subrs.is_none() { |
489 | if let FontKind::CID(ref cid) = ctx.metadata.kind { |
490 | ctx.local_subrs = |
491 | parse_cid_local_subrs(ctx.metadata.table_data, ctx.glyph_id, cid); |
492 | } |
493 | } |
494 | |
495 | if let Some(local_subrs) = ctx.local_subrs { |
496 | let subroutine_bias = calc_subroutine_bias(local_subrs.len()); |
497 | let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?; |
498 | let char_string = local_subrs |
499 | .get(index) |
500 | .ok_or(CFFError::InvalidSubroutineIndex)?; |
501 | _parse_char_string(ctx, char_string, depth + 1, p)?; |
502 | } else { |
503 | return Err(CFFError::NoLocalSubroutines); |
504 | } |
505 | |
506 | if ctx.has_endchar && !ctx.has_seac { |
507 | if !s.at_end() { |
508 | return Err(CFFError::DataAfterEndChar); |
509 | } |
510 | |
511 | break; |
512 | } |
513 | } |
514 | operator::RETURN => { |
515 | break; |
516 | } |
517 | TWO_BYTE_OPERATOR_MARK => { |
518 | // flex |
519 | let op2 = s.read::<u8>().ok_or(CFFError::ReadOutOfBounds)?; |
520 | match op2 { |
521 | operator::HFLEX => p.parse_hflex()?, |
522 | operator::FLEX => p.parse_flex()?, |
523 | operator::HFLEX1 => p.parse_hflex1()?, |
524 | operator::FLEX1 => p.parse_flex1()?, |
525 | _ => return Err(CFFError::UnsupportedOperator), |
526 | } |
527 | } |
528 | operator::ENDCHAR => { |
529 | if p.stack.len() == 4 || (ctx.width.is_none() && p.stack.len() == 5) { |
530 | // Process 'seac'. |
531 | let accent_char = seac_code_to_glyph_id(&ctx.metadata.charset, p.stack.pop()) |
532 | .ok_or(CFFError::InvalidSeacCode)?; |
533 | let base_char = seac_code_to_glyph_id(&ctx.metadata.charset, p.stack.pop()) |
534 | .ok_or(CFFError::InvalidSeacCode)?; |
535 | let dy = p.stack.pop(); |
536 | let dx = p.stack.pop(); |
537 | |
538 | if ctx.width.is_none() && !p.stack.is_empty() { |
539 | ctx.width = Some(p.stack.pop()) |
540 | } |
541 | |
542 | ctx.has_seac = true; |
543 | |
544 | if depth == STACK_LIMIT { |
545 | return Err(CFFError::NestingLimitReached); |
546 | } |
547 | |
548 | let base_char_string = ctx |
549 | .metadata |
550 | .char_strings |
551 | .get(u32::from(base_char.0)) |
552 | .ok_or(CFFError::InvalidSeacCode)?; |
553 | _parse_char_string(ctx, base_char_string, depth + 1, p)?; |
554 | p.x = dx; |
555 | p.y = dy; |
556 | |
557 | let accent_char_string = ctx |
558 | .metadata |
559 | .char_strings |
560 | .get(u32::from(accent_char.0)) |
561 | .ok_or(CFFError::InvalidSeacCode)?; |
562 | _parse_char_string(ctx, accent_char_string, depth + 1, p)?; |
563 | } else if p.stack.len() == 1 && ctx.width.is_none() { |
564 | ctx.width = Some(p.stack.pop()); |
565 | } |
566 | |
567 | if !p.is_first_move_to { |
568 | p.is_first_move_to = true; |
569 | p.builder.close(); |
570 | } |
571 | |
572 | if !s.at_end() { |
573 | return Err(CFFError::DataAfterEndChar); |
574 | } |
575 | |
576 | ctx.has_endchar = true; |
577 | |
578 | break; |
579 | } |
580 | operator::HINT_MASK | operator::COUNTER_MASK => { |
581 | let mut len = p.stack.len(); |
582 | |
583 | // We are ignoring the hint operators. |
584 | p.stack.clear(); |
585 | |
586 | // If the stack length is uneven, than the first value is a `width`. |
587 | if len.is_odd() { |
588 | len -= 1; |
589 | if ctx.width.is_none() { |
590 | ctx.width = Some(p.stack.at(0)); |
591 | } |
592 | } |
593 | |
594 | ctx.stems_len += len as u32 >> 1; |
595 | |
596 | s.advance(usize::num_from((ctx.stems_len + 7) >> 3)); |
597 | } |
598 | operator::MOVE_TO => { |
599 | let mut i = 0; |
600 | if p.stack.len() == 3 { |
601 | i += 1; |
602 | if ctx.width.is_none() { |
603 | ctx.width = Some(p.stack.at(0)); |
604 | } |
605 | } |
606 | |
607 | p.parse_move_to(i)?; |
608 | } |
609 | operator::HORIZONTAL_MOVE_TO => { |
610 | let mut i = 0; |
611 | if p.stack.len() == 2 { |
612 | i += 1; |
613 | if ctx.width.is_none() { |
614 | ctx.width = Some(p.stack.at(0)); |
615 | } |
616 | } |
617 | |
618 | p.parse_horizontal_move_to(i)?; |
619 | } |
620 | operator::CURVE_LINE => { |
621 | p.parse_curve_line()?; |
622 | } |
623 | operator::LINE_CURVE => { |
624 | p.parse_line_curve()?; |
625 | } |
626 | operator::VV_CURVE_TO => { |
627 | p.parse_vv_curve_to()?; |
628 | } |
629 | operator::HH_CURVE_TO => { |
630 | p.parse_hh_curve_to()?; |
631 | } |
632 | operator::SHORT_INT => { |
633 | let n = s.read::<i16>().ok_or(CFFError::ReadOutOfBounds)?; |
634 | p.stack.push(f32::from(n))?; |
635 | } |
636 | operator::CALL_GLOBAL_SUBROUTINE => { |
637 | if p.stack.is_empty() { |
638 | return Err(CFFError::InvalidArgumentsStackLength); |
639 | } |
640 | |
641 | if depth == STACK_LIMIT { |
642 | return Err(CFFError::NestingLimitReached); |
643 | } |
644 | |
645 | let subroutine_bias = calc_subroutine_bias(ctx.metadata.global_subrs.len()); |
646 | let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?; |
647 | let char_string = ctx |
648 | .metadata |
649 | .global_subrs |
650 | .get(index) |
651 | .ok_or(CFFError::InvalidSubroutineIndex)?; |
652 | _parse_char_string(ctx, char_string, depth + 1, p)?; |
653 | |
654 | if ctx.has_endchar && !ctx.has_seac { |
655 | if !s.at_end() { |
656 | return Err(CFFError::DataAfterEndChar); |
657 | } |
658 | |
659 | break; |
660 | } |
661 | } |
662 | operator::VH_CURVE_TO => { |
663 | p.parse_vh_curve_to()?; |
664 | } |
665 | operator::HV_CURVE_TO => { |
666 | p.parse_hv_curve_to()?; |
667 | } |
668 | 32..=246 => { |
669 | p.parse_int1(op)?; |
670 | } |
671 | 247..=250 => { |
672 | p.parse_int2(op, &mut s)?; |
673 | } |
674 | 251..=254 => { |
675 | p.parse_int3(op, &mut s)?; |
676 | } |
677 | operator::FIXED_16_16 => { |
678 | p.parse_fixed(&mut s)?; |
679 | } |
680 | } |
681 | |
682 | if p.width_only && ctx.width.is_some() { |
683 | break; |
684 | } |
685 | } |
686 | |
687 | // TODO: 'A charstring subroutine must end with either an endchar or a return operator.' |
688 | |
689 | Ok(()) |
690 | } |
691 | |
692 | fn seac_code_to_glyph_id(charset: &Charset, n: f32) -> Option<GlyphId> { |
693 | let code: u8 = u8::try_num_from(n)?; |
694 | |
695 | let sid: u8 = STANDARD_ENCODING[usize::from(code)]; |
696 | let sid: StringId = StringId(u16::from(sid)); |
697 | |
698 | match charset { |
699 | Charset::ISOAdobe => { |
700 | // ISO Adobe charset only defines string ids up to 228 (zcaron) |
701 | if code <= 228 { |
702 | Some(GlyphId(sid.0)) |
703 | } else { |
704 | None |
705 | } |
706 | } |
707 | Charset::Expert | Charset::ExpertSubset => None, |
708 | _ => charset.sid_to_gid(sid), |
709 | } |
710 | } |
711 | |
712 | #[derive (Clone, Copy, Debug)] |
713 | enum FDSelect<'a> { |
714 | Format0(LazyArray16<'a, u8>), |
715 | Format3(&'a [u8]), // It's easier to parse it in-place. |
716 | } |
717 | |
718 | impl Default for FDSelect<'_> { |
719 | fn default() -> Self { |
720 | FDSelect::Format0(LazyArray16::default()) |
721 | } |
722 | } |
723 | |
724 | impl FDSelect<'_> { |
725 | fn font_dict_index(&self, glyph_id: GlyphId) -> Option<u8> { |
726 | match self { |
727 | FDSelect::Format0(ref array) => array.get(glyph_id.0), |
728 | FDSelect::Format3(data) => { |
729 | let mut s = Stream::new(data); |
730 | let number_of_ranges = s.read::<u16>()?; |
731 | if number_of_ranges == 0 { |
732 | return None; |
733 | } |
734 | |
735 | // 'A sentinel GID follows the last range element and serves |
736 | // to delimit the last range in the array.' |
737 | // So we can simply increase the number of ranges by one. |
738 | let number_of_ranges = number_of_ranges.checked_add(1)?; |
739 | |
740 | // Range is: GlyphId + u8 |
741 | let mut prev_first_glyph = s.read::<GlyphId>()?; |
742 | let mut prev_index = s.read::<u8>()?; |
743 | for _ in 1..number_of_ranges { |
744 | let curr_first_glyph = s.read::<GlyphId>()?; |
745 | if (prev_first_glyph..curr_first_glyph).contains(&glyph_id) { |
746 | return Some(prev_index); |
747 | } else { |
748 | prev_index = s.read::<u8>()?; |
749 | } |
750 | |
751 | prev_first_glyph = curr_first_glyph; |
752 | } |
753 | |
754 | None |
755 | } |
756 | } |
757 | } |
758 | } |
759 | |
760 | fn parse_fd_select<'a>(number_of_glyphs: u16, s: &mut Stream<'a>) -> Option<FDSelect<'a>> { |
761 | let format: u8 = s.read::<u8>()?; |
762 | match format { |
763 | 0 => Some(FDSelect::Format0(s.read_array16::<u8>(count:number_of_glyphs)?)), |
764 | 3 => Some(FDSelect::Format3(s.tail()?)), |
765 | _ => None, |
766 | } |
767 | } |
768 | |
769 | fn parse_sid_metadata<'a>( |
770 | data: &'a [u8], |
771 | top_dict: TopDict, |
772 | encoding: Encoding<'a>, |
773 | ) -> Option<FontKind<'a>> { |
774 | let mut metadata = SIDMetadata::default(); |
775 | metadata.encoding = encoding; |
776 | |
777 | let private_dict = if let Some(range) = top_dict.private_dict_range.clone() { |
778 | parse_private_dict(data.get(range)?) |
779 | } else { |
780 | return Some(FontKind::SID(metadata)); |
781 | }; |
782 | |
783 | metadata.default_width = private_dict.default_width.unwrap_or(0.0); |
784 | metadata.nominal_width = private_dict.nominal_width.unwrap_or(0.0); |
785 | |
786 | if let (Some(private_dict_range), Some(subroutines_offset)) = ( |
787 | top_dict.private_dict_range, |
788 | private_dict.local_subroutines_offset, |
789 | ) { |
790 | // 'The local subroutines offset is relative to the beginning |
791 | // of the Private DICT data.' |
792 | if let Some(start) = private_dict_range.start.checked_add(subroutines_offset) { |
793 | let data = data.get(start..data.len())?; |
794 | let mut s = Stream::new(data); |
795 | metadata.local_subrs = parse_index::<u16>(&mut s)?; |
796 | } |
797 | } |
798 | |
799 | Some(FontKind::SID(metadata)) |
800 | } |
801 | |
802 | fn parse_cid_metadata(data: &[u8], top_dict: TopDict, number_of_glyphs: u16) -> Option<FontKind> { |
803 | let (charset_offset, fd_array_offset, fd_select_offset) = match ( |
804 | top_dict.charset_offset, |
805 | top_dict.fd_array_offset, |
806 | top_dict.fd_select_offset, |
807 | ) { |
808 | (Some(a), Some(b), Some(c)) => (a, b, c), |
809 | _ => return None, // charset, FDArray and FDSelect must be set. |
810 | }; |
811 | |
812 | if charset_offset <= charset_id::EXPERT_SUBSET { |
813 | // 'There are no predefined charsets for CID fonts.' |
814 | // Adobe Technical Note #5176, chapter 18 CID-keyed Fonts |
815 | return None; |
816 | } |
817 | |
818 | let mut metadata = CIDMetadata::default(); |
819 | |
820 | metadata.fd_array = { |
821 | let mut s = Stream::new_at(data, fd_array_offset)?; |
822 | parse_index::<u16>(&mut s)? |
823 | }; |
824 | |
825 | metadata.fd_select = { |
826 | let mut s = Stream::new_at(data, fd_select_offset)?; |
827 | parse_fd_select(number_of_glyphs, &mut s)? |
828 | }; |
829 | |
830 | Some(FontKind::CID(metadata)) |
831 | } |
832 | |
833 | /// A [Compact Font Format Table]( |
834 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/cff). |
835 | #[derive (Clone, Copy)] |
836 | pub struct Table<'a> { |
837 | // The whole CFF table. |
838 | // Used to resolve a local subroutine in a CID font. |
839 | table_data: &'a [u8], |
840 | |
841 | #[allow (dead_code)] |
842 | strings: Index<'a>, |
843 | global_subrs: Index<'a>, |
844 | charset: Charset<'a>, |
845 | number_of_glyphs: NonZeroU16, |
846 | matrix: Matrix, |
847 | char_strings: Index<'a>, |
848 | kind: FontKind<'a>, |
849 | } |
850 | |
851 | impl<'a> Table<'a> { |
852 | /// Parses a table from raw data. |
853 | pub fn parse(data: &'a [u8]) -> Option<Self> { |
854 | let mut s = Stream::new(data); |
855 | |
856 | // Parse Header. |
857 | let major = s.read::<u8>()?; |
858 | s.skip::<u8>(); // minor |
859 | let header_size = s.read::<u8>()?; |
860 | s.skip::<u8>(); // Absolute offset |
861 | |
862 | if major != 1 { |
863 | return None; |
864 | } |
865 | |
866 | // Jump to Name INDEX. It's not necessarily right after the header. |
867 | if header_size > 4 { |
868 | s.advance(usize::from(header_size) - 4); |
869 | } |
870 | |
871 | // Skip Name INDEX. |
872 | skip_index::<u16>(&mut s)?; |
873 | |
874 | let top_dict = parse_top_dict(&mut s)?; |
875 | |
876 | // Must be set, otherwise there are nothing to parse. |
877 | if top_dict.char_strings_offset == 0 { |
878 | return None; |
879 | } |
880 | |
881 | // String INDEX. |
882 | let strings = parse_index::<u16>(&mut s)?; |
883 | |
884 | // Parse Global Subroutines INDEX. |
885 | let global_subrs = parse_index::<u16>(&mut s)?; |
886 | |
887 | let char_strings = { |
888 | let mut s = Stream::new_at(data, top_dict.char_strings_offset)?; |
889 | parse_index::<u16>(&mut s)? |
890 | }; |
891 | |
892 | // 'The number of glyphs is the value of the count field in the CharStrings INDEX.' |
893 | let number_of_glyphs = u16::try_from(char_strings.len()) |
894 | .ok() |
895 | .and_then(NonZeroU16::new)?; |
896 | |
897 | let charset = match top_dict.charset_offset { |
898 | Some(charset_id::ISO_ADOBE) => Charset::ISOAdobe, |
899 | Some(charset_id::EXPERT) => Charset::Expert, |
900 | Some(charset_id::EXPERT_SUBSET) => Charset::ExpertSubset, |
901 | Some(offset) => { |
902 | let mut s = Stream::new_at(data, offset)?; |
903 | parse_charset(number_of_glyphs, &mut s)? |
904 | } |
905 | None => Charset::ISOAdobe, // default |
906 | }; |
907 | |
908 | let matrix = top_dict.matrix; |
909 | |
910 | let kind = if top_dict.has_ros { |
911 | parse_cid_metadata(data, top_dict, number_of_glyphs.get())? |
912 | } else { |
913 | // Only SID fonts are allowed to have an Encoding. |
914 | let encoding = match top_dict.encoding_offset { |
915 | Some(encoding_id::STANDARD) => Encoding::new_standard(), |
916 | Some(encoding_id::EXPERT) => Encoding::new_expert(), |
917 | Some(offset) => parse_encoding(&mut Stream::new_at(data, offset)?)?, |
918 | None => Encoding::new_standard(), // default |
919 | }; |
920 | |
921 | parse_sid_metadata(data, top_dict, encoding)? |
922 | }; |
923 | |
924 | Some(Self { |
925 | table_data: data, |
926 | strings, |
927 | global_subrs, |
928 | charset, |
929 | number_of_glyphs, |
930 | matrix, |
931 | char_strings, |
932 | kind, |
933 | }) |
934 | } |
935 | |
936 | /// Returns a total number of glyphs in the font. |
937 | /// |
938 | /// Never zero. |
939 | #[inline ] |
940 | pub fn number_of_glyphs(&self) -> u16 { |
941 | self.number_of_glyphs.get() |
942 | } |
943 | |
944 | /// Returns a font transformation matrix. |
945 | #[inline ] |
946 | pub fn matrix(&self) -> Matrix { |
947 | self.matrix |
948 | } |
949 | |
950 | /// Outlines a glyph. |
951 | pub fn outline( |
952 | &self, |
953 | glyph_id: GlyphId, |
954 | builder: &mut dyn OutlineBuilder, |
955 | ) -> Result<Rect, CFFError> { |
956 | let data = self |
957 | .char_strings |
958 | .get(u32::from(glyph_id.0)) |
959 | .ok_or(CFFError::NoGlyph)?; |
960 | parse_char_string(data, self, glyph_id, false, builder).map(|v| v.0) |
961 | } |
962 | |
963 | /// Resolves a Glyph ID for a code point. |
964 | /// |
965 | /// Similar to [`Face::glyph_index`](crate::Face::glyph_index) but 8bit |
966 | /// and uses CFF encoding and charset tables instead of TrueType `cmap`. |
967 | pub fn glyph_index(&self, code_point: u8) -> Option<GlyphId> { |
968 | match self.kind { |
969 | FontKind::SID(ref sid_meta) => { |
970 | match sid_meta.encoding.code_to_gid(&self.charset, code_point) { |
971 | Some(id) => Some(id), |
972 | None => { |
973 | // Try using the Standard encoding otherwise. |
974 | // Custom Encodings does not guarantee to include all glyphs. |
975 | Encoding::new_standard().code_to_gid(&self.charset, code_point) |
976 | } |
977 | } |
978 | } |
979 | FontKind::CID(_) => None, |
980 | } |
981 | } |
982 | |
983 | /// Returns a glyph width. |
984 | /// |
985 | /// This value is different from outline bbox width and is stored separately. |
986 | /// |
987 | /// Technically similar to [`Face::glyph_hor_advance`](crate::Face::glyph_hor_advance). |
988 | pub fn glyph_width(&self, glyph_id: GlyphId) -> Option<u16> { |
989 | match self.kind { |
990 | FontKind::SID(ref sid) => { |
991 | let data = self.char_strings.get(u32::from(glyph_id.0))?; |
992 | let (_, width) = |
993 | parse_char_string(data, self, glyph_id, true, &mut DummyOutline).ok()?; |
994 | let width = width |
995 | .map(|w| sid.nominal_width + w) |
996 | .unwrap_or(sid.default_width); |
997 | u16::try_from(width as i32).ok() |
998 | } |
999 | FontKind::CID(_) => None, |
1000 | } |
1001 | } |
1002 | |
1003 | /// Returns a glyph ID by a name. |
1004 | #[cfg (feature = "glyph-names" )] |
1005 | pub fn glyph_index_by_name(&self, name: &str) -> Option<GlyphId> { |
1006 | match self.kind { |
1007 | FontKind::SID(_) => { |
1008 | let sid = if let Some(index) = STANDARD_NAMES.iter().position(|n| *n == name) { |
1009 | StringId(index as u16) |
1010 | } else { |
1011 | let index = self |
1012 | .strings |
1013 | .into_iter() |
1014 | .position(|n| n == name.as_bytes())?; |
1015 | StringId((STANDARD_NAMES.len() + index) as u16) |
1016 | }; |
1017 | |
1018 | self.charset.sid_to_gid(sid) |
1019 | } |
1020 | FontKind::CID(_) => None, |
1021 | } |
1022 | } |
1023 | |
1024 | /// Returns a glyph name. |
1025 | #[cfg (feature = "glyph-names" )] |
1026 | pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&'a str> { |
1027 | match self.kind { |
1028 | FontKind::SID(_) => { |
1029 | let sid = self.charset.gid_to_sid(glyph_id)?; |
1030 | let sid = usize::from(sid.0); |
1031 | match STANDARD_NAMES.get(sid) { |
1032 | Some(name) => Some(name), |
1033 | None => { |
1034 | let idx = u32::try_from(sid - STANDARD_NAMES.len()).ok()?; |
1035 | let name = self.strings.get(idx)?; |
1036 | core::str::from_utf8(name).ok() |
1037 | } |
1038 | } |
1039 | } |
1040 | FontKind::CID(_) => None, |
1041 | } |
1042 | } |
1043 | |
1044 | /// Returns the CID corresponding to a glyph ID. |
1045 | /// |
1046 | /// Returns `None` if this is not a CIDFont. |
1047 | #[cfg (feature = "glyph-names" )] |
1048 | pub fn glyph_cid(&self, glyph_id: GlyphId) -> Option<u16> { |
1049 | match self.kind { |
1050 | FontKind::SID(_) => None, |
1051 | FontKind::CID(_) => self.charset.gid_to_sid(glyph_id).map(|id| id.0), |
1052 | } |
1053 | } |
1054 | } |
1055 | |
1056 | impl core::fmt::Debug for Table<'_> { |
1057 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1058 | write!(f, "Table {{ ... }}" ) |
1059 | } |
1060 | } |
1061 | |