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 font_test_data::bebuffer::BeBuffer;
558
559 use super::*;
560 use crate::{
561 tables::variations::ItemVariationStore, types::F2Dot14, FontData, FontRead, FontRef,
562 TableProvider,
563 };
564
565 #[test]
566 fn int_operands() {
567 let empty = FontData::new(&[]);
569 let min_byte = FontData::new(&[0]);
570 let max_byte = FontData::new(&[255]);
571 assert_eq!(parse_int(&mut empty.cursor(), 32).unwrap(), -107);
573 assert_eq!(parse_int(&mut empty.cursor(), 246).unwrap(), 107);
574 assert_eq!(parse_int(&mut min_byte.cursor(), 247).unwrap(), 108);
576 assert_eq!(parse_int(&mut max_byte.cursor(), 250).unwrap(), 1131);
577 assert_eq!(parse_int(&mut min_byte.cursor(), 251).unwrap(), -108);
579 assert_eq!(parse_int(&mut max_byte.cursor(), 254).unwrap(), -1131);
580 }
581
582 #[test]
583 fn binary_coded_decimal_operands() {
584 let bytes = FontData::new(&[0xe2, 0xa2, 0x5f]);
593 assert_eq!(
594 parse_bcd(&mut bytes.cursor()).unwrap(),
595 Fixed::from_f64(-2.25)
596 );
597 let bytes = FontData::new(&[0x0a, 0x14, 0x05, 0x41, 0xc3, 0xff]);
598 assert_eq!(
599 parse_bcd(&mut bytes.cursor()).unwrap(),
600 Fixed::from_f64(0.140541E-3)
601 );
602 }
603
604 #[test]
605 fn example_top_dict_tokens() {
606 use Operator::*;
607 let top_dict_data = &font_test_data::cff2::EXAMPLE[5..12];
608 let tokens: Vec<_> = tokens(top_dict_data).map(|entry| entry.unwrap()).collect();
609 let expected: &[Token] = &[
610 68.into(),
611 FdArrayOffset.into(),
612 56.into(),
613 CharstringsOffset.into(),
614 16.into(),
615 VariationStoreOffset.into(),
616 ];
617 assert_eq!(&tokens, expected);
618 }
619
620 #[test]
621 fn example_top_dict_entries() {
622 use Entry::*;
623 let top_dict_data = &font_test_data::cff2::EXAMPLE[0x5..=0xB];
624 let entries: Vec<_> = entries(top_dict_data, None)
625 .map(|entry| entry.unwrap())
626 .collect();
627 let expected: &[Entry] = &[
628 FdArrayOffset(68),
629 CharstringsOffset(56),
630 VariationStoreOffset(16),
631 ];
632 assert_eq!(&entries, expected);
633 }
634
635 #[test]
636 fn example_private_dict_entries() {
637 use Entry::*;
638 let private_dict_data = &font_test_data::cff2::EXAMPLE[0x4f..=0xc0];
639 let store =
640 ItemVariationStore::read(FontData::new(&font_test_data::cff2::EXAMPLE[18..])).unwrap();
641 let coords = &[F2Dot14::from_f32(0.0)];
642 let blend_state = BlendState::new(store, coords, 0).unwrap();
643 let entries: Vec<_> = entries(private_dict_data, Some(blend_state))
644 .map(|entry| entry.unwrap())
645 .collect();
646 fn make_blues(values: &[f64]) -> Blues {
647 Blues::new(values.iter().copied().map(Fixed::from_f64))
648 }
649 fn make_stem_snaps(values: &[f64]) -> StemSnaps {
650 StemSnaps::new(values.iter().copied().map(Fixed::from_f64))
651 }
652 let expected: &[Entry] = &[
653 BlueValues(make_blues(&[
654 -20.0, 0.0, 472.0, 490.0, 525.0, 540.0, 645.0, 660.0, 670.0, 690.0, 730.0, 750.0,
655 ])),
656 OtherBlues(make_blues(&[-250.0, -240.0])),
657 FamilyBlues(make_blues(&[
658 -20.0, 0.0, 473.0, 491.0, 525.0, 540.0, 644.0, 659.0, 669.0, 689.0, 729.0, 749.0,
659 ])),
660 FamilyOtherBlues(make_blues(&[-249.0, -239.0])),
661 BlueScale(Fixed::from_f64(0.037506103515625)),
662 BlueFuzz(Fixed::ZERO),
663 StdHw(Fixed::from_f64(55.0)),
664 StdVw(Fixed::from_f64(80.0)),
665 StemSnapH(make_stem_snaps(&[40.0, 55.0])),
666 StemSnapV(make_stem_snaps(&[80.0, 90.0])),
667 SubrsOffset(114),
668 ];
669 assert_eq!(&entries, expected);
670 }
671
672 #[test]
673 fn noto_serif_display_top_dict_entries() {
674 use Entry::*;
675 let top_dict_data = FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED)
676 .unwrap()
677 .cff()
678 .unwrap()
679 .top_dicts()
680 .get(0)
681 .unwrap();
682 let entries: Vec<_> = entries(top_dict_data, None)
683 .map(|entry| entry.unwrap())
684 .collect();
685 let expected = &[
686 Version(StringId::new(391)),
687 Notice(StringId::new(392)),
688 Copyright(StringId::new(393)),
689 FullName(StringId::new(394)),
690 FamilyName(StringId::new(395)),
691 FontBbox([-693.0, -470.0, 2797.0, 1048.0].map(Fixed::from_f64)),
692 Charset(517),
693 PrivateDictRange(549..587),
694 CharstringsOffset(521),
695 ];
696 assert_eq!(&entries, expected);
697 }
698
699 #[test]
704 fn private_dict_range_avoid_overflow() {
705 let private_dict = BeBuffer::new()
708 .push(29u8) .push(-1i32) .push(29u8) .push(-1i32) .push(18u8) .to_vec();
714 let _ = entries(&private_dict, None).count();
716 }
717}