1use crate::outline::Point;
6use crate::{
7 EmbeddingFormat, Font, MISSING_GLYPH_REMAP, Subset, SubsetError, SubsetErrorKind as Kind,
8 be_u16, be_u32, find_table_full, table_checksum,
9};
10use std::collections::{BTreeMap, BTreeSet};
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum Command {
15 Move(Point),
16 Line(Point),
17 Curve(Point, Point, Point),
18 Close,
19}
20#[derive(Debug, Clone, PartialEq)]
21pub struct CffOutline {
22 pub commands: Vec<Command>,
23 pub advance: u16,
24 pub lsb: i16,
25}
26fn err(kind: Kind, offset: usize) -> SubsetError {
27 SubsetError::new(kind, *b"CFF ", None, Some(offset))
28}
29fn malformed(offset: usize) -> SubsetError {
30 err(Kind::Malformed, offset)
31}
32fn slice(d: &[u8], start: usize, len: usize) -> Result<&[u8], SubsetError> {
33 d.get(start..start.checked_add(len).ok_or(malformed(start))?)
34 .ok_or(malformed(start))
35}
36fn index(d: &[u8], start: usize) -> Result<(Vec<&[u8]>, usize), SubsetError> {
37 let count = usize::from(be_u16(d, start).ok_or(malformed(start))?);
38 if count == 0 {
39 return Ok((Vec::new(), start + 2));
40 }
41 let size = usize::from(*d.get(start + 2).ok_or(malformed(start + 2))?);
42 if !(1..=4).contains(&size) {
43 return Err(malformed(start + 2));
44 }
45 let base = start
46 .checked_add(3 + (count + 1) * size)
47 .ok_or(malformed(start))?;
48 let offset = |i: usize| -> Result<usize, SubsetError> {
49 let bytes = slice(d, start + 3 + i * size, size)?;
50 let v = bytes.iter().fold(0usize, |a, &b| (a << 8) | usize::from(b));
51 base.checked_add(v.checked_sub(1).ok_or(malformed(start))?)
52 .ok_or(malformed(start))
53 };
54 if offset(0)? != base {
55 return Err(malformed(start));
56 }
57 let mut objects = Vec::with_capacity(count);
58 for i in 0..count {
59 let a = offset(i)?;
60 let b = offset(i + 1)?;
61 objects.push(slice(d, a, b.checked_sub(a).ok_or(malformed(a))?)?);
62 }
63 Ok((objects, offset(count)?))
64}
65fn number(d: &[u8], p: &mut usize, dict: bool) -> Result<Option<f64>, SubsetError> {
66 let at = *p;
67 let b = *d.get(at).ok_or(malformed(at))?;
68 let (v, n) = match b {
69 32..=246 => (f64::from(b) - 139.0, 1),
70 247..=250 => (
71 f64::from(b - 247) * 256.0 + f64::from(*d.get(at + 1).ok_or(malformed(at))?) + 108.0,
72 2,
73 ),
74 251..=254 => (
75 -f64::from(b - 251) * 256.0 - f64::from(*d.get(at + 1).ok_or(malformed(at))?) - 108.0,
76 2,
77 ),
78 28 => (f64::from(be_u16(d, at + 1).ok_or(malformed(at))? as i16), 3),
79 29 if dict => (f64::from(be_u32(d, at + 1).ok_or(malformed(at))? as i32), 5),
80 255 if !dict => (
81 f64::from(be_u32(d, at + 1).ok_or(malformed(at))? as i32) / 65536.0,
82 5,
83 ),
84 30 if dict => {
85 let mut text = String::new();
86 let mut pos = at + 1;
87 'real: loop {
88 let v = *d.get(pos).ok_or(malformed(pos))?;
89 pos += 1;
90 for digit in [v >> 4, v & 15] {
91 match digit {
92 0..=9 => text.push(char::from(b'0' + digit)),
93 10 => text.push('.'),
94 11 => text.push('E'),
95 12 => text.push_str("E-"),
96 14 => text.push('-'),
97 15 => break 'real,
98 _ => return Err(malformed(pos - 1)),
99 }
100 if text.len() > 64 {
101 return Err(err(Kind::BudgetExceeded, at));
102 }
103 }
104 }
105 let v = text.parse::<f64>().map_err(|_| malformed(at))?;
106 if !v.is_finite() {
107 return Err(malformed(at));
108 }
109 (v, pos - at)
110 }
111 _ => return Ok(None),
112 };
113 *p += n;
114 Ok(Some(v))
115}
116fn dictionary(d: &[u8]) -> Result<BTreeMap<u16, Vec<f64>>, SubsetError> {
117 let mut out = BTreeMap::new();
118 let mut stack = Vec::new();
119 let mut p = 0;
120 while p < d.len() {
121 if let Some(v) = number(d, &mut p, true)? {
122 stack.push(v);
123 if stack.len() > 48 {
124 return Err(err(Kind::BudgetExceeded, p));
125 }
126 continue;
127 }
128 let b = d[p];
129 p += 1;
130 let op = if b == 12 {
131 let b = *d.get(p).ok_or(malformed(p))?;
132 p += 1;
133 0x0c00 + u16::from(b)
134 } else {
135 u16::from(b)
136 };
137 if out.insert(op, std::mem::take(&mut stack)).is_some() {
138 return Err(malformed(p));
139 }
140 }
141 if !stack.is_empty() {
142 return Err(malformed(p));
143 }
144 Ok(out)
145}
146fn integer(v: f64, p: usize) -> Result<usize, SubsetError> {
147 if v < 0.0 || v > u32::MAX as f64 || v.fract() != 0.0 {
148 Err(malformed(p))
149 } else {
150 Ok(v as usize)
151 }
152}
153struct Cff<'a> {
154 chars: Vec<&'a [u8]>,
155 local: Vec<&'a [u8]>,
156 global: Vec<&'a [u8]>,
157}
158impl<'a> Cff<'a> {
159 fn parse(font: &'a Font) -> Result<Self, SubsetError> {
160 font.validate_font_metrics()?;
161 let (o, n) = find_table_full(&font.data, b"CFF ").ok_or(SubsetError::new(
162 Kind::UnsupportedFormat,
163 *b"sfnt",
164 None,
165 None,
166 ))?;
167 let d = slice(&font.data, o, n)?;
168 if d.first() != Some(&1) {
169 return Err(err(Kind::UnsupportedFormat, 0));
170 }
171 let header = usize::from(*d.get(2).ok_or(malformed(2))?);
172 if header < 4 || !matches!(d.get(3), Some(1..=4)) {
173 return Err(malformed(2));
174 }
175 let (names, p) = index(d, header)?;
176 if names.len() != 1 {
177 return Err(err(Kind::UnsupportedFormat, header));
178 }
179 let (top, p) = index(d, p)?;
180 if top.len() != 1 {
181 return Err(malformed(p));
182 }
183 let (_, p) = index(d, p)?;
184 let (global, _) = index(d, p)?;
185 let dict = dictionary(top[0])?;
186 if dict.get(&0x0c05).is_some_and(|v| v.as_slice() != [0.0]) {
187 return Err(err(Kind::UnsupportedFormat, header));
188 }
189 if dict.contains_key(&0x0c1e) {
190 return Err(err(Kind::UnsupportedFormat, header));
191 }
192 if dict.get(&0x0c06).is_some_and(|v| v.as_slice() != [2.0]) {
193 return Err(err(Kind::UnsupportedFormat, header));
194 }
195 if let Some(matrix) = dict.get(&0x0c07) {
196 let unit = 1.0 / f64::from(font.units_per_em);
197 if matrix.len() != 6
198 || matrix
199 .iter()
200 .zip([unit, 0.0, 0.0, unit, 0.0, 0.0])
201 .any(|(a, b)| (a - b).abs() > 1e-12)
202 {
203 return Err(err(Kind::UnsupportedFormat, header));
204 }
205 } else if font.units_per_em != 1000 {
206 return Err(err(Kind::UnsupportedFormat, header));
207 }
208 let cs = dict
209 .get(&17)
210 .filter(|v| v.len() == 1)
211 .ok_or(malformed(header))?;
212 let (chars, _) = index(d, integer(cs[0], header)?)?;
213 if chars.len() != usize::from(font.num_glyphs) {
214 return Err(malformed(header));
215 }
216 let mut local = Vec::new();
217 if let Some(private) = dict.get(&18) {
218 if private.len() != 2 {
219 return Err(malformed(header));
220 }
221 let size = integer(private[0], header)?;
222 let base = integer(private[1], header)?;
223 let pd = dictionary(slice(d, base, size)?)?;
224 if let Some(subrs) = pd.get(&19) {
225 if subrs.len() != 1 {
226 return Err(malformed(base));
227 }
228 let sub = base
229 .checked_add(integer(subrs[0], base)?)
230 .ok_or(malformed(base))?;
231 local = index(d, sub)?.0;
232 }
233 }
234 Ok(Self {
235 chars,
236 local,
237 global,
238 })
239 }
240 fn outline(&self, font: &Font, gid: u16) -> Result<CffOutline, SubsetError> {
241 self.outline_budget(font, gid, &mut 100_000)
242 }
243 fn outline_budget(
244 &self,
245 font: &Font,
246 gid: u16,
247 budget: &mut usize,
248 ) -> Result<CffOutline, SubsetError> {
249 let initial = (*budget).min(100_000);
250 let program = self.chars.get(usize::from(gid)).ok_or(SubsetError::new(
251 Kind::InvalidGlyph,
252 *b"CFF ",
253 Some(gid),
254 None,
255 ))?;
256 let mut vm = Vm {
257 cff: self,
258 stack: Vec::new(),
259 out: Vec::new(),
260 x: 0.0,
261 y: 0.0,
262 open: false,
263 width: false,
264 stems: 0,
265 fuel: initial,
266 };
267 let ended = vm.run(program, 0).map_err(|mut e| {
268 e.glyph = Some(gid);
269 e
270 })?;
271 *budget -= initial - vm.fuel;
272 if !ended {
273 return Err(SubsetError::new(
274 Kind::Malformed,
275 *b"CFF ",
276 Some(gid),
277 Some(program.len()),
278 ));
279 }
280 Ok(CffOutline {
281 commands: vm.out,
282 advance: font.advance_width(gid),
283 lsb: font.left_side_bearing(gid),
284 })
285 }
286}
287struct Vm<'a, 'b> {
288 cff: &'a Cff<'b>,
289 stack: Vec<f64>,
290 out: Vec<Command>,
291 x: f64,
292 y: f64,
293 open: bool,
294 width: bool,
295 stems: usize,
296 fuel: usize,
297}
298impl Vm<'_, '_> {
299 fn point(&mut self, dx: f64, dy: f64) -> Result<Point, SubsetError> {
300 self.x += dx;
301 self.y += dy;
302 if !self.x.is_finite() || !self.y.is_finite() || self.x.abs() > 1e9 || self.y.abs() > 1e9 {
303 return Err(malformed(0));
304 }
305 Ok(Point {
306 x: self.x,
307 y: self.y,
308 })
309 }
310 fn close(&mut self) {
311 if self.open {
312 self.out.push(Command::Close);
313 self.open = false;
314 }
315 }
316 fn line(&mut self, dx: f64, dy: f64) -> Result<(), SubsetError> {
317 if !self.open {
318 return Err(malformed(0));
319 }
320 let p = self.point(dx, dy)?;
321 self.out.push(Command::Line(p));
322 Ok(())
323 }
324 fn curve(&mut self, v: &[f64]) -> Result<(), SubsetError> {
325 if !self.open || v.len() != 6 {
326 return Err(malformed(0));
327 }
328 let a = self.point(v[0], v[1])?;
329 let b = self.point(v[2], v[3])?;
330 let c = self.point(v[4], v[5])?;
331 self.out.push(Command::Curve(a, b, c));
332 Ok(())
333 }
334 fn run(&mut self, d: &[u8], depth: usize) -> Result<bool, SubsetError> {
335 if depth > 10 {
336 return Err(err(Kind::BudgetExceeded, 0));
337 }
338 let mut p = 0;
339 while p < d.len() {
340 if self.fuel == 0 || self.out.len() > 65_536 {
341 return Err(err(Kind::BudgetExceeded, p));
342 }
343 self.fuel -= 1;
344 if let Some(v) = number(d, &mut p, false)? {
345 self.stack.push(v);
346 if self.stack.len() > 48 {
347 return Err(err(Kind::BudgetExceeded, p));
348 }
349 continue;
350 }
351 let at = p;
352 let op = d[p];
353 p += 1;
354 if op == 10 || op == 29 {
355 let v = self.stack.pop().ok_or(malformed(at))?;
356 let subrs = if op == 10 {
357 &self.cff.local
358 } else {
359 &self.cff.global
360 };
361 let bias = if subrs.len() < 1240 {
362 107.0
363 } else if subrs.len() < 33900 {
364 1131.0
365 } else {
366 32768.0
367 };
368 let i = integer(v + bias, at)?;
369 let sub = subrs.get(i).ok_or(malformed(at))?;
370 if self.run(sub, depth + 1)? {
371 return Ok(true);
372 }
373 continue;
374 }
375 if op == 11 {
376 if depth == 0 {
377 return Err(malformed(at));
378 }
379 return Ok(false);
380 }
381 let mut s = std::mem::take(&mut self.stack);
382 match op {
383 1 | 3 | 18 | 23 | 19 | 20 => {
384 if !self.width && s.len() % 2 == 1 {
385 s.remove(0);
386 }
387 self.width = true;
388 if s.len() % 2 != 0 {
389 return Err(malformed(at));
390 }
391 self.stems += s.len() / 2;
392 if self.stems > 96 {
393 return Err(err(Kind::BudgetExceeded, at));
394 }
395 if op == 19 || op == 20 {
396 let n = self.stems.div_ceil(8);
397 slice(d, p, n)?;
398 p += n;
399 }
400 }
401 4 | 21 | 22 => {
402 let n = if op == 21 { 2 } else { 1 };
403 if !self.width && s.len() == n + 1 {
404 s.remove(0);
405 }
406 self.width = true;
407 if s.len() != n {
408 return Err(malformed(at));
409 }
410 self.close();
411 let (dx, dy) = match op {
412 4 => (0.0, s[0]),
413 22 => (s[0], 0.0),
414 _ => (s[0], s[1]),
415 };
416 let pt = self.point(dx, dy)?;
417 self.out.push(Command::Move(pt));
418 self.open = true;
419 }
420 5 => {
421 if s.is_empty() || s.len() % 2 != 0 {
422 return Err(malformed(at));
423 }
424 for v in s.chunks_exact(2) {
425 self.line(v[0], v[1])?;
426 }
427 }
428 6 | 7 => {
429 if s.is_empty() {
430 return Err(malformed(at));
431 }
432 let mut horizontal = op == 6;
433 for v in s {
434 self.line(
435 if horizontal { v } else { 0.0 },
436 if horizontal { 0.0 } else { v },
437 )?;
438 horizontal = !horizontal;
439 }
440 }
441 8 => {
442 if s.is_empty() || s.len() % 6 != 0 {
443 return Err(malformed(at));
444 }
445 for v in s.chunks_exact(6) {
446 self.curve(v)?;
447 }
448 }
449 24 => {
450 if s.len() < 8 || (s.len() - 2) % 6 != 0 {
451 return Err(malformed(at));
452 }
453 let n = s.len() - 2;
454 for v in s[..n].chunks_exact(6) {
455 self.curve(v)?;
456 }
457 self.line(s[n], s[n + 1])?;
458 }
459 25 => {
460 if s.len() < 8 || (s.len() - 6) % 2 != 0 {
461 return Err(malformed(at));
462 }
463 let n = s.len() - 6;
464 for v in s[..n].chunks_exact(2) {
465 self.line(v[0], v[1])?;
466 }
467 self.curve(&s[n..])?;
468 }
469 26 | 27 => {
470 if s.len() < 4 || !matches!(s.len() % 4, 0 | 1) {
471 return Err(malformed(at));
472 }
473 let mut extra = if s.len() % 4 == 1 { s.remove(0) } else { 0.0 };
474 for v in s.chunks_exact(4) {
475 if op == 26 {
476 self.curve(&[extra, v[0], v[1], v[2], 0.0, v[3]])?;
477 } else {
478 self.curve(&[v[0], extra, v[1], v[2], v[3], 0.0])?;
479 }
480 extra = 0.0;
481 }
482 }
483 30 | 31 => {
484 if s.len() < 4 || !matches!(s.len() % 4, 0 | 1) {
485 return Err(malformed(at));
486 }
487 let count = s.len() / 4;
488 let mut horizontal = op == 31;
489 for (i, v) in s[..count * 4].chunks_exact(4).enumerate() {
490 let extra = if i + 1 == count && s.len() % 4 == 1 {
491 s[s.len() - 1]
492 } else {
493 0.0
494 };
495 if horizontal {
496 self.curve(&[v[0], 0.0, v[1], v[2], extra, v[3]])?;
497 } else {
498 self.curve(&[0.0, v[0], v[1], v[2], v[3], extra])?;
499 }
500 horizontal = !horizontal;
501 }
502 }
503 12 => {
504 let escape = *d.get(p).ok_or(malformed(p))?;
505 p += 1;
506 match escape {
507 34 if s.len() == 7 => {
508 self.curve(&[s[0], 0.0, s[1], s[2], s[3], 0.0])?;
509 self.curve(&[s[4], 0.0, s[5], -s[2], s[6], 0.0])?;
510 }
511 35 if s.len() == 13 => {
512 self.curve(&s[..6])?;
513 self.curve(&s[6..12])?;
514 }
515 36 if s.len() == 9 => {
516 self.curve(&[s[0], s[1], s[2], s[3], s[4], 0.0])?;
517 self.curve(&[s[5], 0.0, s[6], s[7], s[8], -s[1] - s[3] - s[7]])?;
518 }
519 37 if s.len() == 11 => {
520 let dx = s[0] + s[2] + s[4] + s[6] + s[8];
521 let dy = s[1] + s[3] + s[5] + s[7] + s[9];
522 self.curve(&s[..6])?;
523 let (x, y) = if dx.abs() > dy.abs() {
524 (s[10], -dy)
525 } else {
526 (-dx, s[10])
527 };
528 self.curve(&[s[6], s[7], s[8], s[9], x, y])?;
529 }
530 34..=37 => return Err(malformed(at)),
531 _ => return Err(err(Kind::UnsupportedOperator, at)),
532 }
533 }
534 14 => {
535 if !self.width && s.len() % 2 == 1 {
536 s.remove(0);
537 }
538 if s.len() == 4 {
539 return Err(err(Kind::UnsupportedOperator, at));
540 }
541 if !s.is_empty() {
542 return Err(malformed(at));
543 }
544 self.close();
545 return Ok(true);
546 }
547 _ => return Err(err(Kind::UnsupportedOperator, at)),
548 }
549 }
550 Err(malformed(p))
551 }
552}
553impl Font {
554 pub fn cff_outline(&self, gid: u16) -> Result<CffOutline, SubsetError> {
556 Cff::parse(self)?.outline(self, gid)
557 }
558}
559fn dict_int(out: &mut Vec<u8>, v: i32) {
560 out.push(29);
561 out.extend_from_slice(&v.to_be_bytes());
562}
563fn encode_index(objects: &[Vec<u8>]) -> Result<Vec<u8>, SubsetError> {
564 let count = u16::try_from(objects.len()).map_err(|_| err(Kind::Capacity, 0))?;
565 let mut out = count.to_be_bytes().to_vec();
566 if count == 0 {
567 return Ok(out);
568 }
569 out.push(4);
570 let mut offset = 1u32;
571 out.extend_from_slice(&offset.to_be_bytes());
572 for v in objects {
573 offset = offset
574 .checked_add(u32::try_from(v.len()).map_err(|_| err(Kind::Capacity, 0))?)
575 .ok_or(err(Kind::Capacity, 0))?;
576 out.extend_from_slice(&offset.to_be_bytes());
577 }
578 for v in objects {
579 out.extend_from_slice(v);
580 }
581 Ok(out)
582}
583fn cs_num(out: &mut Vec<u8>, v: f64) -> Result<(), SubsetError> {
584 if v.fract() == 0.0 && (-32768.0..=32767.0).contains(&v) {
585 out.push(28);
586 out.extend_from_slice(&(v as i16).to_be_bytes());
587 } else {
588 let fixed = (v * 65536.0).round();
589 if fixed < i32::MIN as f64 || fixed > i32::MAX as f64 {
590 return Err(err(Kind::Capacity, 0));
591 }
592 out.push(255);
593 out.extend_from_slice(&(fixed as i32).to_be_bytes());
594 }
595 Ok(())
596}
597fn encode_outline(o: &CffOutline) -> Result<Vec<u8>, SubsetError> {
598 let mut out = Vec::new();
599 cs_num(&mut out, f64::from(o.advance))?;
600 let mut current = Point { x: 0.0, y: 0.0 };
601 for c in &o.commands {
602 match c {
603 Command::Move(p) | Command::Line(p) => {
604 cs_num(&mut out, p.x - current.x)?;
605 cs_num(&mut out, p.y - current.y)?;
606 out.push(if matches!(c, Command::Move(_)) { 21 } else { 5 });
607 current = *p;
608 }
609 Command::Curve(a, b, c) => {
610 for p in [a, b, c] {
611 cs_num(&mut out, p.x - current.x)?;
612 cs_num(&mut out, p.y - current.y)?;
613 current = *p;
614 }
615 out.push(8);
616 }
617 Command::Close => {}
618 }
619 }
620 out.push(14);
621 Ok(out)
622}
623fn cff_program(outlines: &[CffOutline], upem: u16) -> Result<Vec<u8>, SubsetError> {
624 let names = encode_index(&[b"FmdSubset".to_vec()])?;
625 let strings: Vec<_> = (1..outlines.len())
626 .map(|i| format!("g{i}").into_bytes())
627 .collect();
628 let strings = encode_index(&strings)?;
629 let chars = encode_index(
630 &outlines
631 .iter()
632 .map(encode_outline)
633 .collect::<Result<Vec<_>, _>>()?,
634 )?;
635 let mut charset = vec![0];
636 for i in 1..outlines.len() {
637 let sid = u16::try_from(390 + i).map_err(|_| err(Kind::Capacity, 0))?;
638 charset.extend_from_slice(&sid.to_be_bytes());
639 }
640 let mut bounds: Option<[f64; 4]> = None;
641 for outline in outlines {
642 for command in &outline.commands {
643 let points: &[Point] = match command {
644 Command::Move(p) | Command::Line(p) => std::slice::from_ref(p),
645 Command::Curve(a, b, c) => &[*a, *b, *c],
646 Command::Close => &[],
647 };
648 for p in points {
649 bounds = Some(match bounds {
650 None => [p.x, p.y, p.x, p.y],
651 Some([x0, y0, x1, y1]) => [x0.min(p.x), y0.min(p.y), x1.max(p.x), y1.max(p.y)],
652 });
653 }
654 }
655 }
656 let bounds = bounds.unwrap_or([0.0; 4]);
657 let make_top = |charset_off: i32, chars_off: i32| -> Vec<u8> {
659 let mut d = Vec::new();
660 for (i, v) in bounds.iter().enumerate() {
661 dict_int(
662 &mut d,
663 if i < 2 {
664 v.floor() as i32
665 } else {
666 v.ceil() as i32
667 },
668 );
669 }
670 d.push(5);
671 dict_int(&mut d, 0);
672 dict_int(&mut d, chars_off + chars.len() as i32);
673 d.push(18);
674 dict_int(&mut d, charset_off);
675 d.push(15);
676 dict_int(&mut d, chars_off);
677 d.push(17);
678 let value = format!("{:.16}", 1.0 / f64::from(upem));
680 for val in [value.as_str(), "0", "0", value.as_str(), "0", "0"] {
681 d.push(30);
682 let mut digits: Vec<u8> = val
683 .bytes()
684 .map(|b| if b == b'.' { 10 } else { b - b'0' })
685 .collect();
686 digits.push(15);
687 if digits.len() % 2 != 0 {
688 digits.push(15);
689 }
690 for pair in digits.chunks_exact(2) {
691 d.push(pair[0] * 16 + pair[1]);
692 }
693 }
694 d.extend_from_slice(&[12, 7]);
695 d
696 };
697 let top_size = encode_index(&[make_top(0, 0)])?.len();
698 let charset_off = 4 + names.len() + top_size + strings.len() + 2;
699 let mut out = vec![1, 0, 4, 4];
700 out.extend(names);
701 out.extend(encode_index(&[make_top(
702 charset_off as i32,
703 (charset_off + charset.len()) as i32,
704 )])?);
705 out.extend(strings);
706 out.extend([0, 0]);
707 out.extend(charset);
708 out.extend(chars);
709 Ok(out)
710}
711pub(crate) fn subset(font: &Font, glyphs: &[u16], chars: &[char]) -> Result<Subset, SubsetError> {
712 let cff = Cff::parse(font)?;
713 let mut keep = BTreeSet::from([0]);
714 for &g in glyphs {
715 if g >= font.num_glyphs {
716 return Err(SubsetError::new(
717 Kind::InvalidGlyph,
718 *b"maxp",
719 Some(g),
720 None,
721 ));
722 }
723 keep.insert(g);
724 }
725 let mut map = vec![MISSING_GLYPH_REMAP; usize::from(font.num_glyphs)];
726 let mut outlines = Vec::new();
727 let mut command_count = 0usize;
728 let mut instruction_budget = 5_000_000usize;
729 for (i, &g) in keep.iter().enumerate() {
730 map[usize::from(g)] = i as u16;
731 let outline = cff.outline_budget(font, g, &mut instruction_budget)?;
732 command_count += outline.commands.len();
733 if command_count > 1_000_000 {
734 return Err(err(Kind::BudgetExceeded, 0));
735 }
736 outlines.push(outline);
737 }
738 let cff = cff_program(&outlines, font.units_per_em)?;
739 let mut tables = BTreeMap::new();
740 for tag in [*b"head", *b"hhea", *b"OS/2", *b"name"] {
741 if let Some((o, n)) = find_table_full(&font.data, &tag) {
742 tables.insert(tag, slice(&font.data, o, n)?.to_vec());
743 }
744 }
745 tables.insert(*b"name", vec![0, 0, 0, 0, 0, 6]);
747 let n = keep.len() as u16;
748 let head = tables.get_mut(b"head").ok_or(malformed(0))?;
749 crate::write_u32(head, 8, 0).ok_or(malformed(8))?;
750 crate::write_u16(head, 50, 0).ok_or(malformed(50))?;
751 crate::write_u16(tables.get_mut(b"hhea").ok_or(malformed(0))?, 34, n).ok_or(malformed(34))?;
752 let mut maxp = 0x0000_5000u32.to_be_bytes().to_vec();
753 maxp.extend_from_slice(&n.to_be_bytes());
754 tables.insert(*b"maxp", maxp);
755 let mut metrics = Vec::new();
756 for o in &outlines {
757 metrics.extend_from_slice(&o.advance.to_be_bytes());
758 metrics.extend_from_slice(&o.lsb.to_be_bytes());
759 }
760 tables.insert(*b"hmtx", metrics);
761 let cmap = font.build_cmap12(chars, &map).ok_or(SubsetError::new(
762 Kind::Capacity,
763 *b"cmap",
764 None,
765 None,
766 ))?;
767 tables.insert(*b"cmap", cmap);
768 let mut post = vec![0; 32];
769 post[..4].copy_from_slice(&0x0003_0000u32.to_be_bytes());
770 tables.insert(*b"post", post);
771 tables.insert(*b"CFF ", cff);
772 Ok(Subset {
773 bytes: assemble(&tables)?,
774 glyph_map: map,
775 format: EmbeddingFormat::OpenTypeCff,
776 })
777}
778fn assemble(tables: &BTreeMap<[u8; 4], Vec<u8>>) -> Result<Vec<u8>, SubsetError> {
779 let n = tables.len();
780 let pow = 1usize << n.ilog2();
781 let mut out = vec![0; 12 + n * 16];
782 out[..4].copy_from_slice(b"OTTO");
783 for (off, v) in [
784 (4, n as u16),
785 (6, (pow * 16) as u16),
786 (8, n.ilog2() as u16),
787 (10, ((n - pow) * 16) as u16),
788 ] {
789 out[off..off + 2].copy_from_slice(&v.to_be_bytes());
790 }
791 let mut head = 0;
792 for (i, (tag, data)) in tables.iter().enumerate() {
793 while out.len() % 4 != 0 {
794 out.push(0);
795 }
796 let offset = out.len();
797 let p = 12 + i * 16;
798 out[p..p + 4].copy_from_slice(tag);
799 for (p, v) in [
800 (p + 4, table_checksum(data)),
801 (
802 p + 8,
803 u32::try_from(offset).map_err(|_| err(Kind::Capacity, 0))?,
804 ),
805 (p + 12, data.len() as u32),
806 ] {
807 out[p..p + 4].copy_from_slice(&v.to_be_bytes());
808 }
809 if tag == b"head" {
810 head = offset;
811 }
812 out.extend_from_slice(data);
813 }
814 while out.len() % 4 != 0 {
815 out.push(0);
816 }
817 let sum = 0xB1B0_AFBAu32.wrapping_sub(table_checksum(&out));
818 crate::write_u32(&mut out, head + 8, sum).ok_or(malformed(head))?;
819 Ok(out)
820}
821
822#[cfg(test)]
823#[allow(clippy::unwrap_used, clippy::expect_used)]
824mod tests {
825 use super::*;
826 const FONT: &[u8] = include_bytes!("../fonts/test-cff/Bravura.otf");
827 #[test]
828 fn charstring_failures_keep_distinct_typed_causes() {
829 let f = Font::parse(FONT.to_vec()).unwrap();
830 for (program, kind) in [
831 (vec![12, 23, 14], Kind::UnsupportedOperator),
832 (vec![28, 0], Kind::Malformed),
833 (vec![5, 14], Kind::Malformed),
834 (vec![11], Kind::Malformed),
835 ] {
836 let c = Cff {
837 chars: vec![&program],
838 local: vec![],
839 global: vec![],
840 };
841 let e = c.outline(&f, 0).unwrap_err();
842 assert_eq!(e.kind, kind);
843 assert_eq!(e.glyph, Some(0));
844 assert!(e.offset.is_some());
845 }
846 let recursive = [32, 10, 11];
847 let main = [32, 10, 14];
848 let c = Cff {
849 chars: vec![&main],
850 local: vec![&recursive],
851 global: vec![],
852 };
853 assert_eq!(c.outline(&f, 0).unwrap_err().kind, Kind::BudgetExceeded);
854 }
855 #[test]
856 fn zero_glyph_font_and_cff2_refuse_without_panics() {
857 let mut d = FONT.to_vec();
858 let (o, _) = find_table_full(&d, b"maxp").unwrap();
859 crate::write_u16(&mut d, o + 4, 0).unwrap();
860 let f = Font::parse(d).unwrap();
861 assert_eq!(
862 f.try_subset_glyphs(&[], &[]).unwrap_err().kind,
863 Kind::Malformed
864 );
865 let mut d = FONT.to_vec();
866 let (o, _) = find_table_full(&d, b"CFF ").unwrap();
867 d[o] = 2;
868 let f = Font::parse(d).unwrap();
869 assert_eq!(f.cff_outline(0).unwrap_err().kind, Kind::UnsupportedFormat);
870 }
871 #[test]
872 fn cff_subset_has_valid_sfnt_checksum() {
873 let f = Font::parse(FONT.to_vec()).unwrap();
874 let s = f.try_subset_glyphs(&[5, 1000], &[]).unwrap();
875 assert_eq!(table_checksum(&s.bytes), 0xB1B0_AFBA);
876 }
877}