1use std::ops::Range;
4
5use super::{BlendState, Error, Number, Stack, StringId};
6use crate::{types::Fixed, Cursor, ReadError};
7
8#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
13pub enum Operator {
14 Version,
15 Notice,
16 FullName,
17 FamilyName,
18 Weight,
19 FontBbox,
20 CharstringsOffset,
21 PrivateDictRange,
22 VariationStoreOffset,
23 Copyright,
24 IsFixedPitch,
25 ItalicAngle,
26 UnderlinePosition,
27 UnderlineThickness,
28 PaintType,
29 CharstringType,
30 FontMatrix,
31 StrokeWidth,
32 FdArrayOffset,
33 FdSelectOffset,
34 BlueValues,
35 OtherBlues,
36 FamilyBlues,
37 FamilyOtherBlues,
38 SubrsOffset,
39 VariationStoreIndex,
40 BlueScale,
41 BlueShift,
42 BlueFuzz,
43 LanguageGroup,
44 ExpansionFactor,
45 Encoding,
46 Charset,
47 UniqueId,
48 Xuid,
49 SyntheticBase,
50 PostScript,
51 BaseFontName,
52 BaseFontBlend,
53 Ros,
54 CidFontVersion,
55 CidFontRevision,
56 CidFontType,
57 CidCount,
58 UidBase,
59 FontName,
60 StdHw,
61 StdVw,
62 DefaultWidthX,
63 NominalWidthX,
64 Blend,
65 StemSnapH,
66 StemSnapV,
67 ForceBold,
68 InitialRandomSeed,
69}
70
71impl Operator {
72 fn from_opcode(opcode: u8) -> Option<Self> {
73 use Operator::*;
74 Some(match opcode {
75 0 => Version,
77 1 => Notice,
78 2 => FullName,
79 3 => FamilyName,
80 4 => Weight,
81 5 => FontBbox,
82 13 => UniqueId,
83 14 => Xuid,
84 15 => Charset,
85 16 => Encoding,
86 17 => CharstringsOffset,
87 18 => PrivateDictRange,
88 24 => VariationStoreOffset,
89 6 => BlueValues,
91 7 => OtherBlues,
92 8 => FamilyBlues,
93 9 => FamilyOtherBlues,
94 10 => StdHw,
95 11 => StdVw,
96 19 => SubrsOffset,
97 20 => DefaultWidthX,
98 21 => NominalWidthX,
99 22 => VariationStoreIndex,
100 23 => Blend,
101 _ => return None,
103 })
104 }
105
106 fn from_extended_opcode(opcode: u8) -> Option<Self> {
107 use Operator::*;
108 Some(match opcode {
109 0 => Copyright,
111 1 => IsFixedPitch,
112 2 => ItalicAngle,
113 3 => UnderlinePosition,
114 4 => UnderlineThickness,
115 5 => PaintType,
116 6 => CharstringType,
117 7 => FontMatrix,
118 8 => StrokeWidth,
119 20 => SyntheticBase,
120 21 => PostScript,
121 22 => BaseFontName,
122 23 => BaseFontBlend,
123 30 => Ros,
124 31 => CidFontVersion,
125 32 => CidFontRevision,
126 33 => CidFontType,
127 34 => CidCount,
128 35 => UidBase,
129 36 => FdArrayOffset,
130 37 => FdSelectOffset,
131 38 => FontName,
132 9 => BlueScale,
134 10 => BlueShift,
135 11 => BlueFuzz,
136 12 => StemSnapH,
137 13 => StemSnapV,
138 14 => ForceBold,
139 17 => LanguageGroup,
140 18 => ExpansionFactor,
141 19 => InitialRandomSeed,
142 _ => return None,
143 })
144 }
145}
146
147#[derive(Copy, Clone, PartialEq, Eq, Debug)]
149pub enum Token {
150 Operator(Operator),
151 Operand(Number),
152}
153
154impl From<Operator> for Token {
155 fn from(value: Operator) -> Self {
156 Self::Operator(value)
157 }
158}
159
160impl<T> From<T> for Token
161where
162 T: Into<Number>,
163{
164 fn from(value: T) -> Self {
165 Self::Operand(value.into())
166 }
167}
168
169pub fn tokens(dict_data: &[u8]) -> impl Iterator<Item = Result<Token, Error>> + '_ + Clone {
175 let mut cursor = crate::FontData::new(dict_data).cursor();
176 std::iter::from_fn(move || {
177 if cursor.remaining_bytes() == 0 {
178 None
179 } else {
180 Some(parse_token(&mut cursor))
181 }
182 })
183}
184
185fn parse_token(cursor: &mut Cursor) -> Result<Token, Error> {
186 const ESCAPE: u8 = 12;
188 let b0 = cursor.read::<u8>()?;
189 Ok(if b0 == ESCAPE {
190 let b1 = cursor.read::<u8>()?;
191 Token::Operator(Operator::from_extended_opcode(b1).ok_or(Error::InvalidDictOperator(b1))?)
192 } else {
193 match b0 {
195 28 | 29 | 32..=254 => Token::Operand(parse_int(cursor, b0)?.into()),
196 30 => Token::Operand(parse_bcd(cursor)?.into()),
197 _ => Token::Operator(Operator::from_opcode(b0).ok_or(Error::InvalidDictOperator(b0))?),
198 }
199 })
200}
201
202#[derive(Clone, PartialEq, Eq, Debug)]
204pub enum Entry {
205 Version(StringId),
206 Notice(StringId),
207 FullName(StringId),
208 FamilyName(StringId),
209 Weight(StringId),
210 FontBbox([Fixed; 4]),
211 CharstringsOffset(usize),
212 PrivateDictRange(Range<usize>),
213 VariationStoreOffset(usize),
214 Copyright(StringId),
215 IsFixedPitch(bool),
216 ItalicAngle(Fixed),
217 UnderlinePosition(Fixed),
218 UnderlineThickness(Fixed),
219 PaintType(i32),
220 CharstringType(i32),
221 FontMatrix([Fixed; 6]),
222 StrokeWidth(Fixed),
223 FdArrayOffset(usize),
224 FdSelectOffset(usize),
225 BlueValues(Blues),
226 OtherBlues(Blues),
227 FamilyBlues(Blues),
228 FamilyOtherBlues(Blues),
229 SubrsOffset(usize),
230 VariationStoreIndex(u16),
231 BlueScale(Fixed),
232 BlueShift(Fixed),
233 BlueFuzz(Fixed),
234 LanguageGroup(i32),
235 ExpansionFactor(Fixed),
236 Encoding(usize),
237 Charset(usize),
238 UniqueId(i32),
239 Xuid,
240 SyntheticBase(i32),
241 PostScript(StringId),
242 BaseFontName(StringId),
243 BaseFontBlend,
244 Ros {
245 registry: StringId,
246 ordering: StringId,
247 supplement: Fixed,
248 },
249 CidFontVersion(Fixed),
250 CidFontRevision(Fixed),
251 CidFontType(i32),
252 CidCount(u32),
253 UidBase(i32),
254 FontName(StringId),
255 StdHw(Fixed),
256 StdVw(Fixed),
257 DefaultWidthX(Fixed),
258 NominalWidthX(Fixed),
259 StemSnapH(StemSnaps),
260 StemSnapV(StemSnaps),
261 ForceBold(bool),
262 InitialRandomSeed(i32),
263}
264
265pub fn entries<'a>(
274 dict_data: &'a [u8],
275 mut blend_state: Option<BlendState<'a>>,
276) -> impl Iterator<Item = Result<Entry, Error>> + 'a {
277 let mut stack = Stack::new();
278 let mut token_iter = tokens(dict_data);
279 std::iter::from_fn(move || loop {
280 let token = match token_iter.next()? {
281 Ok(token) => token,
282 Err(e) => return Some(Err(e)),
283 };
284 match token {
285 Token::Operand(number) => match stack.push(number) {
286 Ok(_) => continue,
287 Err(e) => return Some(Err(e)),
288 },
289 Token::Operator(op) => {
290 if op == Operator::Blend || op == Operator::VariationStoreIndex {
291 let state = match blend_state.as_mut() {
292 Some(state) => state,
293 None => return Some(Err(Error::MissingBlendState)),
294 };
295 if op == Operator::VariationStoreIndex {
296 match stack
297 .get_i32(0)
298 .and_then(|ix| state.set_store_index(ix as u16))
299 {
300 Ok(_) => {}
301 Err(e) => return Some(Err(e)),
302 }
303 }
304 if op == Operator::Blend {
305 match stack.apply_blend(state) {
306 Ok(_) => continue,
307 Err(e) => return Some(Err(e)),
308 }
309 }
310 }
311 let entry = parse_entry(op, &mut stack);
312 stack.clear();
313 return Some(entry);
314 }
315 }
316 })
317}
318
319fn parse_entry(op: Operator, stack: &mut Stack) -> Result<Entry, Error> {
320 use Operator::*;
321 Ok(match op {
322 Version => Entry::Version(stack.pop_i32()?.into()),
323 Notice => Entry::Notice(stack.pop_i32()?.into()),
324 FullName => Entry::FullName(stack.pop_i32()?.into()),
325 FamilyName => Entry::FamilyName(stack.pop_i32()?.into()),
326 Weight => Entry::Weight(stack.pop_i32()?.into()),
327 FontBbox => Entry::FontBbox([
328 stack.get_fixed(0)?,
329 stack.get_fixed(1)?,
330 stack.get_fixed(2)?,
331 stack.get_fixed(3)?,
332 ]),
333 CharstringsOffset => Entry::CharstringsOffset(stack.pop_i32()? as usize),
334 PrivateDictRange => {
335 let len = stack.get_i32(0)? as usize;
336 let start = stack.get_i32(1)? as usize;
337 let end = start.checked_add(len).ok_or(ReadError::OutOfBounds)?;
338 Entry::PrivateDictRange(start..end)
339 }
340 VariationStoreOffset => Entry::VariationStoreOffset(stack.pop_i32()? as usize),
341 Copyright => Entry::Copyright(stack.pop_i32()?.into()),
342 IsFixedPitch => Entry::IsFixedPitch(stack.pop_i32()? != 0),
343 ItalicAngle => Entry::ItalicAngle(stack.pop_fixed()?),
344 UnderlinePosition => Entry::UnderlinePosition(stack.pop_fixed()?),
345 UnderlineThickness => Entry::UnderlineThickness(stack.pop_fixed()?),
346 PaintType => Entry::PaintType(stack.pop_i32()?),
347 CharstringType => Entry::CharstringType(stack.pop_i32()?),
348 FontMatrix => Entry::FontMatrix([
349 stack.get_fixed(0)?,
350 stack.get_fixed(1)?,
351 stack.get_fixed(2)?,
352 stack.get_fixed(3)?,
353 stack.get_fixed(4)?,
354 stack.get_fixed(5)?,
355 ]),
356 StrokeWidth => Entry::StrokeWidth(stack.pop_fixed()?),
357 FdArrayOffset => Entry::FdArrayOffset(stack.pop_i32()? as usize),
358 FdSelectOffset => Entry::FdSelectOffset(stack.pop_i32()? as usize),
359 BlueValues => {
360 stack.apply_delta_prefix_sum();
361 Entry::BlueValues(Blues::new(stack.fixed_values()))
362 }
363 OtherBlues => {
364 stack.apply_delta_prefix_sum();
365 Entry::OtherBlues(Blues::new(stack.fixed_values()))
366 }
367 FamilyBlues => {
368 stack.apply_delta_prefix_sum();
369 Entry::FamilyBlues(Blues::new(stack.fixed_values()))
370 }
371 FamilyOtherBlues => {
372 stack.apply_delta_prefix_sum();
373 Entry::FamilyOtherBlues(Blues::new(stack.fixed_values()))
374 }
375 SubrsOffset => Entry::SubrsOffset(stack.pop_i32()? as usize),
376 VariationStoreIndex => Entry::VariationStoreIndex(stack.pop_i32()? as u16),
377 BlueScale => Entry::BlueScale(stack.pop_fixed()?),
378 BlueShift => Entry::BlueShift(stack.pop_fixed()?),
379 BlueFuzz => Entry::BlueFuzz(stack.pop_fixed()?),
380 LanguageGroup => Entry::LanguageGroup(stack.pop_i32()?),
381 ExpansionFactor => Entry::ExpansionFactor(stack.pop_fixed()?),
382 Encoding => Entry::Encoding(stack.pop_i32()? as usize),
383 Charset => Entry::Charset(stack.pop_i32()? as usize),
384 UniqueId => Entry::UniqueId(stack.pop_i32()?),
385 Xuid => Entry::Xuid,
386 SyntheticBase => Entry::SyntheticBase(stack.pop_i32()?),
387 PostScript => Entry::PostScript(stack.pop_i32()?.into()),
388 BaseFontName => Entry::BaseFontName(stack.pop_i32()?.into()),
389 BaseFontBlend => Entry::BaseFontBlend,
390 Ros => Entry::Ros {
391 registry: stack.get_i32(0)?.into(),
392 ordering: stack.get_i32(1)?.into(),
393 supplement: stack.get_fixed(2)?,
394 },
395 CidFontVersion => Entry::CidFontVersion(stack.pop_fixed()?),
396 CidFontRevision => Entry::CidFontRevision(stack.pop_fixed()?),
397 CidFontType => Entry::CidFontType(stack.pop_i32()?),
398 CidCount => Entry::CidCount(stack.pop_i32()? as u32),
399 UidBase => Entry::UidBase(stack.pop_i32()?),
400 FontName => Entry::FontName(stack.pop_i32()?.into()),
401 StdHw => Entry::StdHw(stack.pop_fixed()?),
402 StdVw => Entry::StdVw(stack.pop_fixed()?),
403 DefaultWidthX => Entry::DefaultWidthX(stack.pop_fixed()?),
404 NominalWidthX => Entry::NominalWidthX(stack.pop_fixed()?),
405 StemSnapH => {
406 stack.apply_delta_prefix_sum();
407 Entry::StemSnapH(StemSnaps::new(stack.fixed_values()))
408 }
409 StemSnapV => {
410 stack.apply_delta_prefix_sum();
411 Entry::StemSnapV(StemSnaps::new(stack.fixed_values()))
412 }
413 ForceBold => Entry::ForceBold(stack.pop_i32()? != 0),
414 InitialRandomSeed => Entry::InitialRandomSeed(stack.pop_i32()?),
415 Blend => unreachable!(),
417 })
418}
419
420const MAX_BLUE_VALUES: usize = 7;
422
423#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
428pub struct Blues {
429 values: [(Fixed, Fixed); MAX_BLUE_VALUES],
430 len: u32,
431}
432
433impl Blues {
434 pub fn new(values: impl Iterator<Item = Fixed>) -> Self {
435 let mut blues = Self::default();
436 let mut stash = Fixed::ZERO;
437 for (i, value) in values.take(MAX_BLUE_VALUES * 2).enumerate() {
438 if (i & 1) == 0 {
439 stash = value;
440 } else {
441 blues.values[i / 2] = (stash, value);
442 blues.len += 1;
443 }
444 }
445 blues
446 }
447
448 pub fn values(&self) -> &[(Fixed, Fixed)] {
449 &self.values[..self.len as usize]
450 }
451}
452
453const MAX_STEM_SNAPS: usize = 12;
457
458#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
462pub struct StemSnaps {
463 values: [Fixed; MAX_STEM_SNAPS],
464 len: u32,
465}
466
467impl StemSnaps {
468 fn new(values: impl Iterator<Item = Fixed>) -> Self {
469 let mut snaps = Self::default();
470 for (value, target_value) in values.take(MAX_STEM_SNAPS).zip(&mut snaps.values) {
471 *target_value = value;
472 snaps.len += 1;
473 }
474 snaps
475 }
476
477 pub fn values(&self) -> &[Fixed] {
478 &self.values[..self.len as usize]
479 }
480}
481
482pub(crate) fn parse_int(cursor: &mut Cursor, b0: u8) -> Result<i32, Error> {
483 Ok(match b0 {
492 32..=246 => b0 as i32 - 139,
493 247..=250 => (b0 as i32 - 247) * 256 + cursor.read::<u8>()? as i32 + 108,
494 251..=254 => -(b0 as i32 - 251) * 256 - cursor.read::<u8>()? as i32 - 108,
495 28 => cursor.read::<i16>()? as i32,
496 29 => cursor.read::<i32>()?,
497 _ => {
498 return Err(Error::InvalidNumber);
499 }
500 })
501}
502
503fn parse_bcd(cursor: &mut Cursor) -> Result<Fixed, Error> {
505 const MAX_LEN: usize = 32;
511 let mut buf = [0u8; MAX_LEN];
512 let mut n = 0;
513 let mut push = |byte| {
514 if n < MAX_LEN {
515 buf[n] = byte;
516 n += 1;
517 Ok(())
518 } else {
519 Err(Error::InvalidNumber)
520 }
521 };
522 'outer: loop {
533 let b = cursor.read::<u8>()?;
534 for nibble in [(b >> 4) & 0xF, b & 0xF] {
535 match nibble {
536 0x0..=0x9 => push(b'0' + nibble)?,
537 0xA => push(b'.')?,
538 0xB => push(b'E')?,
539 0xC => {
540 push(b'E')?;
541 push(b'-')?;
542 }
543 0xE => push(b'-')?,
544 0xF => break 'outer,
545 _ => return Err(Error::InvalidNumber),
546 }
547 }
548 }
549 std::str::from_utf8(&buf[..n])
550 .map_or(None, |buf| buf.parse::<f64>().ok())
551 .map(Fixed::from_f64)
552 .ok_or(Error::InvalidNumber)
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558 use crate::{
559 tables::variations::ItemVariationStore, test_helpers::BeBuffer, types::F2Dot14, FontData,
560 FontRead, FontRef, TableProvider,
561 };
562
563 #[test]
564 fn int_operands() {
565 let empty = FontData::new(&[]);
567 let min_byte = FontData::new(&[0]);
568 let max_byte = FontData::new(&[255]);
569 assert_eq!(parse_int(&mut empty.cursor(), 32).unwrap(), -107);
571 assert_eq!(parse_int(&mut empty.cursor(), 246).unwrap(), 107);
572 assert_eq!(parse_int(&mut min_byte.cursor(), 247).unwrap(), 108);
574 assert_eq!(parse_int(&mut max_byte.cursor(), 250).unwrap(), 1131);
575 assert_eq!(parse_int(&mut min_byte.cursor(), 251).unwrap(), -108);
577 assert_eq!(parse_int(&mut max_byte.cursor(), 254).unwrap(), -1131);
578 }
579
580 #[test]
581 fn binary_coded_decimal_operands() {
582 let bytes = FontData::new(&[0xe2, 0xa2, 0x5f]);
591 assert_eq!(
592 parse_bcd(&mut bytes.cursor()).unwrap(),
593 Fixed::from_f64(-2.25)
594 );
595 let bytes = FontData::new(&[0x0a, 0x14, 0x05, 0x41, 0xc3, 0xff]);
596 assert_eq!(
597 parse_bcd(&mut bytes.cursor()).unwrap(),
598 Fixed::from_f64(0.140541E-3)
599 );
600 }
601
602 #[test]
603 fn example_top_dict_tokens() {
604 use Operator::*;
605 let top_dict_data = &font_test_data::cff2::EXAMPLE[5..12];
606 let tokens: Vec<_> = tokens(top_dict_data).map(|entry| entry.unwrap()).collect();
607 let expected: &[Token] = &[
608 68.into(),
609 FdArrayOffset.into(),
610 56.into(),
611 CharstringsOffset.into(),
612 16.into(),
613 VariationStoreOffset.into(),
614 ];
615 assert_eq!(&tokens, expected);
616 }
617
618 #[test]
619 fn example_top_dict_entries() {
620 use Entry::*;
621 let top_dict_data = &font_test_data::cff2::EXAMPLE[0x5..=0xB];
622 let entries: Vec<_> = entries(top_dict_data, None)
623 .map(|entry| entry.unwrap())
624 .collect();
625 let expected: &[Entry] = &[
626 FdArrayOffset(68),
627 CharstringsOffset(56),
628 VariationStoreOffset(16),
629 ];
630 assert_eq!(&entries, expected);
631 }
632
633 #[test]
634 fn example_private_dict_entries() {
635 use Entry::*;
636 let private_dict_data = &font_test_data::cff2::EXAMPLE[0x4f..=0xc0];
637 let store =
638 ItemVariationStore::read(FontData::new(&font_test_data::cff2::EXAMPLE[18..])).unwrap();
639 let coords = &[F2Dot14::from_f32(0.0)];
640 let blend_state = BlendState::new(store, coords, 0).unwrap();
641 let entries: Vec<_> = entries(private_dict_data, Some(blend_state))
642 .map(|entry| entry.unwrap())
643 .collect();
644 fn make_blues(values: &[f64]) -> Blues {
645 Blues::new(values.iter().copied().map(Fixed::from_f64))
646 }
647 fn make_stem_snaps(values: &[f64]) -> StemSnaps {
648 StemSnaps::new(values.iter().copied().map(Fixed::from_f64))
649 }
650 let expected: &[Entry] = &[
651 BlueValues(make_blues(&[
652 -20.0, 0.0, 472.0, 490.0, 525.0, 540.0, 645.0, 660.0, 670.0, 690.0, 730.0, 750.0,
653 ])),
654 OtherBlues(make_blues(&[-250.0, -240.0])),
655 FamilyBlues(make_blues(&[
656 -20.0, 0.0, 473.0, 491.0, 525.0, 540.0, 644.0, 659.0, 669.0, 689.0, 729.0, 749.0,
657 ])),
658 FamilyOtherBlues(make_blues(&[-249.0, -239.0])),
659 BlueScale(Fixed::from_f64(0.037506103515625)),
660 BlueFuzz(Fixed::ZERO),
661 StdHw(Fixed::from_f64(55.0)),
662 StdVw(Fixed::from_f64(80.0)),
663 StemSnapH(make_stem_snaps(&[40.0, 55.0])),
664 StemSnapV(make_stem_snaps(&[80.0, 90.0])),
665 SubrsOffset(114),
666 ];
667 assert_eq!(&entries, expected);
668 }
669
670 #[test]
671 fn noto_serif_display_top_dict_entries() {
672 use Entry::*;
673 let top_dict_data = FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED)
674 .unwrap()
675 .cff()
676 .unwrap()
677 .top_dicts()
678 .get(0)
679 .unwrap();
680 let entries: Vec<_> = entries(top_dict_data, None)
681 .map(|entry| entry.unwrap())
682 .collect();
683 let expected = &[
684 Version(StringId::new(391)),
685 Notice(StringId::new(392)),
686 Copyright(StringId::new(393)),
687 FullName(StringId::new(394)),
688 FamilyName(StringId::new(395)),
689 FontBbox([-693.0, -470.0, 2797.0, 1048.0].map(Fixed::from_f64)),
690 Charset(517),
691 PrivateDictRange(549..587),
692 CharstringsOffset(521),
693 ];
694 assert_eq!(&entries, expected);
695 }
696
697 #[test]
702 fn private_dict_range_avoid_overflow() {
703 let private_dict = BeBuffer::new()
706 .push(29u8) .push(-1i32) .push(29u8) .push(-1i32) .push(18u8) .to_vec();
712 let _ = entries(&private_dict, None).count();
714 }
715}