1mod deltas;
4mod hint;
5mod memory;
6mod outline;
7
8#[cfg(feature = "libm")]
9#[allow(unused_imports)]
10use core_maths::CoreFloat;
11
12pub use hint::{HintError, HintInstance, HintOutline};
13pub use outline::{Outline, ScaledOutline};
14
15use super::{DrawError, GlyphHMetrics, Hinting};
16use crate::{GLYF_COMPOSITE_RECURSION_LIMIT, MAX_GLYF_POINTS, MAX_GRAPH_EDGES};
17use memory::{FreeTypeOutlineMemory, HarfBuzzOutlineMemory};
18use raw::{FontRef, ReadError};
19use read_fonts::{
20 tables::{
21 glyf::{
22 Anchor, CompositeGlyph, CompositeGlyphFlags, Glyf, Glyph, PointMarker, SimpleGlyph,
23 },
24 gvar::Gvar,
25 hdmx::Hdmx,
26 loca::Loca,
27 },
28 types::{F26Dot6, F2Dot14, Fixed, GlyphId, Point, Tag},
29 TableProvider,
30};
31
32pub const PHANTOM_POINT_COUNT: usize = 4;
34
35#[derive(Clone)]
37pub struct Outlines<'a> {
38 pub(crate) font: FontRef<'a>,
39 pub(crate) glyph_metrics: GlyphHMetrics<'a>,
40 loca: Loca<'a>,
41 glyf: Glyf<'a>,
42 gvar: Option<Gvar<'a>>,
43 hdmx: Option<Hdmx<'a>>,
44 fpgm: &'a [u8],
45 prep: &'a [u8],
46 cvt_len: u32,
47 max_function_defs: u16,
48 max_instruction_defs: u16,
49 max_twilight_points: u16,
50 max_stack_elements: u16,
51 max_storage: u16,
52 glyph_count: u16,
53 units_per_em: u16,
54 os2_vmetrics: [i16; 2],
55 prefer_interpreter: bool,
56 pub(crate) fractional_size_hinting: bool,
57}
58
59impl<'a> Outlines<'a> {
60 pub fn new(font: &FontRef<'a>) -> Option<Self> {
61 let head = font.head().ok()?;
62 let fractional_size_hinting = !head
65 .flags()
66 .contains(read_fonts::tables::head::Flags::FORCE_INTEGER_PPEM);
67 let loca = font.loca(Some(head.index_to_loc_format() == 1)).ok()?;
68 let glyf = font.glyf().ok()?;
69 let glyph_metrics = GlyphHMetrics::new(font)?;
70 let (
71 glyph_count,
72 max_function_defs,
73 max_instruction_defs,
74 max_twilight_points,
75 max_stack_elements,
76 max_storage,
77 max_instructions,
78 ) = font
79 .maxp()
80 .map(|maxp| {
81 (
82 maxp.num_glyphs(),
83 maxp.max_function_defs().unwrap_or_default(),
84 maxp.max_instruction_defs().unwrap_or_default(),
85 maxp.max_twilight_points()
88 .unwrap_or_default()
89 .saturating_add(4),
90 maxp.max_stack_elements()
93 .unwrap_or_default()
94 .saturating_add(32),
95 maxp.max_storage().unwrap_or_default(),
96 maxp.max_size_of_instructions().unwrap_or_default(),
97 )
98 })
99 .unwrap_or_default();
100 let os2_vmetrics = font
101 .os2()
102 .map(|os2| [os2.s_typo_ascender(), os2.s_typo_descender()])
103 .unwrap_or_default();
104 let fpgm = font
105 .data_for_tag(Tag::new(b"fpgm"))
106 .unwrap_or_default()
107 .as_bytes();
108 let prep = font
109 .data_for_tag(Tag::new(b"prep"))
110 .unwrap_or_default()
111 .as_bytes();
112 let prefer_interpreter = !(max_instructions == 0 && fpgm.is_empty() && prep.is_empty());
115 let cvt_len = font.cvt().map(|cvt| cvt.len() as u32).unwrap_or_default();
116 Some(Self {
117 font: font.clone(),
118 glyph_metrics,
119 loca,
120 glyf,
121 gvar: font.gvar().ok(),
122 hdmx: font.hdmx().ok(),
123 fpgm,
124 prep,
125 cvt_len,
126 max_function_defs,
127 max_instruction_defs,
128 max_twilight_points,
129 max_stack_elements,
130 max_storage,
131 glyph_count,
132 units_per_em: font.head().ok()?.units_per_em(),
133 os2_vmetrics,
134 prefer_interpreter,
135 fractional_size_hinting,
136 })
137 }
138
139 pub fn units_per_em(&self) -> u16 {
140 self.units_per_em
141 }
142
143 pub fn glyph_count(&self) -> usize {
144 self.glyph_count as usize
145 }
146
147 pub fn prefer_interpreter(&self) -> bool {
148 self.prefer_interpreter
149 }
150
151 pub fn outline(&self, glyph_id: GlyphId) -> Result<Outline<'a>, DrawError> {
152 let mut outline = Outline {
153 glyph_id,
154 has_variations: self.gvar.is_some(),
155 ..Default::default()
156 };
157 let glyph = self.loca.get_glyf(glyph_id, &self.glyf)?;
158 if let Some(glyph) = glyph.as_ref() {
159 self.outline_rec(glyph, &mut outline, 0, 0, &mut 0)?;
160 }
161 outline.points += PHANTOM_POINT_COUNT;
162 outline.max_stack = self.max_stack_elements as usize;
163 outline.cvt_count = self.cvt_len as usize;
164 outline.storage_count = self.max_storage as usize;
165 outline.max_twilight_points = self.max_twilight_points as usize;
166 outline.glyph = glyph;
167 Ok(outline)
168 }
169
170 pub fn compute_scale(&self, ppem: Option<f32>) -> Scale26Dot6 {
171 Scale26Dot6::new(ppem, self.units_per_em)
172 }
173
174 pub fn compute_hinted_scale(&self, ppem: Option<f32>) -> Scale26Dot6 {
175 if let Some(ppem) = ppem {
176 if !self.fractional_size_hinting {
177 return self.compute_scale(Some(F26Dot6::from_f64(ppem as f64).round().to_f32()));
182 }
183 }
184 self.compute_scale(ppem)
185 }
186}
187
188impl Outlines<'_> {
189 fn outline_rec(
190 &self,
191 glyph: &Glyph,
192 outline: &mut Outline,
193 component_depth: usize,
194 recurse_depth: usize,
195 total_components: &mut usize,
196 ) -> Result<(), DrawError> {
197 if recurse_depth > GLYF_COMPOSITE_RECURSION_LIMIT {
198 return Err(DrawError::RecursionLimitExceeded(outline.glyph_id));
199 }
200 match glyph {
201 Glyph::Simple(simple) => {
202 let num_points = simple.num_points();
203 let num_points_with_phantom = num_points + PHANTOM_POINT_COUNT;
204 outline.max_simple_points = outline.max_simple_points.max(num_points_with_phantom);
205 outline.points += num_points;
206 if outline.points > MAX_GLYF_POINTS {
207 return Err(DrawError::TooManyPoints(outline.glyph_id));
208 }
209 outline.contours += simple.end_pts_of_contours().len();
210 outline.has_hinting = outline.has_hinting || simple.instruction_length() != 0;
211 outline.max_other_points = outline.max_other_points.max(num_points_with_phantom);
212 outline.has_overlaps |= simple.has_overlapping_contours();
213 }
214 Glyph::Composite(composite) => {
215 let (mut count, instructions) = composite.count_and_instructions();
216 count += PHANTOM_POINT_COUNT;
217 let point_base = outline.points;
218 for (component, flags) in composite.component_glyphs_and_flags() {
219 outline.has_overlaps |= flags.contains(CompositeGlyphFlags::OVERLAP_COMPOUND);
220 let component_glyph = self.loca.get_glyf(component.into(), &self.glyf)?;
221 let Some(component_glyph) = component_glyph else {
222 continue;
223 };
224 *total_components += 1;
225 if *total_components > MAX_GRAPH_EDGES {
226 return Err(DrawError::RecursionLimitExceeded(outline.glyph_id));
227 }
228 self.outline_rec(
229 &component_glyph,
230 outline,
231 component_depth + count,
232 recurse_depth + 1,
233 total_components,
234 )?;
235 }
236 let has_hinting = !instructions.unwrap_or_default().is_empty();
237 if has_hinting {
238 let num_points_in_composite = outline.points - point_base + PHANTOM_POINT_COUNT;
241 outline.max_other_points =
242 outline.max_other_points.max(num_points_in_composite);
243 }
244 outline.max_component_delta_stack = outline
245 .max_component_delta_stack
246 .max(component_depth + count);
247 outline.has_hinting = outline.has_hinting || has_hinting;
248 }
249 }
250 Ok(())
251 }
252
253 fn hdmx_width(&self, ppem: f32, glyph_id: GlyphId) -> Option<u8> {
254 let hdmx = self.hdmx.as_ref()?;
255 let ppem_u8 = ppem as u8;
256 if ppem_u8 as f32 == ppem {
258 hdmx.record_for_size(ppem_u8)?
260 .widths
261 .get(glyph_id.to_u32() as usize)
262 .copied()
263 } else {
264 None
265 }
266 }
267}
268
269trait Scaler {
270 fn outlines(&self) -> &Outlines<'_>;
271 fn setup_phantom_points(
272 &mut self,
273 bounds: [i16; 4],
274 lsb: i32,
275 advance: i32,
276 tsb: i32,
277 vadvance: i32,
278 );
279 fn load_empty(&mut self, glyph_id: GlyphId) -> Result<(), DrawError>;
280 fn load_simple(&mut self, glyph: &SimpleGlyph, glyph_id: GlyphId) -> Result<(), DrawError>;
281 fn load_composite(
282 &mut self,
283 glyph: &CompositeGlyph,
284 glyph_id: GlyphId,
285 recurse_depth: usize,
286 ) -> Result<(), DrawError>;
287
288 fn load(
289 &mut self,
290 glyph: &Option<Glyph>,
291 glyph_id: GlyphId,
292 recurse_depth: usize,
293 ) -> Result<(), DrawError> {
294 if recurse_depth > GLYF_COMPOSITE_RECURSION_LIMIT {
295 return Err(DrawError::RecursionLimitExceeded(glyph_id));
296 }
297 let bounds = match &glyph {
298 Some(glyph) => [glyph.x_min(), glyph.x_max(), glyph.y_min(), glyph.y_max()],
299 _ => [0; 4],
300 };
301 let outlines = self.outlines();
302 let lsb = outlines.glyph_metrics.lsb(glyph_id, &[]);
303 let advance = outlines.glyph_metrics.advance_width(glyph_id, &[]);
304 let [ascent, descent] = outlines.os2_vmetrics.map(|x| x as i32);
305 let tsb = ascent - bounds[3] as i32;
306 let vadvance = ascent - descent;
307 self.setup_phantom_points(bounds, lsb, advance, tsb, vadvance);
308 match glyph {
309 Some(Glyph::Simple(simple)) => self.load_simple(simple, glyph_id),
310 Some(Glyph::Composite(composite)) => {
311 self.load_composite(composite, glyph_id, recurse_depth)
312 }
313 None => self.load_empty(glyph_id),
314 }
315 }
316}
317
318pub(crate) struct HarfBuzzScaler<'a> {
320 outlines: &'a Outlines<'a>,
321 memory: HarfBuzzOutlineMemory<'a>,
322 coords: &'a [F2Dot14],
323 point_count: usize,
324 contour_count: usize,
325 component_delta_count: usize,
326 ppem: f32,
327 scale: f32,
328 phantom: [Point<f32>; PHANTOM_POINT_COUNT],
334}
335
336impl<'a> HarfBuzzScaler<'a> {
337 pub(crate) fn unhinted(
338 outlines: &'a Outlines<'a>,
339 outline: &'a Outline,
340 buf: &'a mut [u8],
341 ppem: Option<f32>,
342 coords: &'a [F2Dot14],
343 ) -> Result<Self, DrawError> {
344 outline.ensure_point_count_limit()?;
345 let scale = if outlines.units_per_em == 0 {
346 1.0
347 } else {
348 ppem.map(|ppem| ppem / outlines.units_per_em as f32)
349 .unwrap_or(1.0)
350 };
351 let memory =
352 HarfBuzzOutlineMemory::new(outline, buf).ok_or(DrawError::InsufficientMemory)?;
353 Ok(Self {
354 outlines,
355 memory,
356 coords,
357 point_count: 0,
358 contour_count: 0,
359 component_delta_count: 0,
360 ppem: ppem.unwrap_or_default(),
361 scale,
362 phantom: Default::default(),
363 })
364 }
365
366 pub(crate) fn scale(
367 mut self,
368 glyph: &Option<Glyph>,
369 glyph_id: GlyphId,
370 ) -> Result<ScaledOutline<'a, f32>, DrawError> {
371 self.load(glyph, glyph_id, 0)?;
372 Ok(ScaledOutline::new(
373 &mut self.memory.points[..self.point_count],
374 self.phantom,
375 &mut self.memory.flags[..self.point_count],
376 &mut self.memory.contours[..self.contour_count],
377 self.outlines.hdmx_width(self.ppem, glyph_id),
378 ))
379 }
380}
381
382#[derive(Copy, Clone)]
384pub(crate) struct Scale26Dot6 {
385 scale: Fixed,
386 is_scaled: bool,
388}
389
390impl Scale26Dot6 {
391 fn new(ppem: Option<f32>, units_per_em: u16) -> Self {
392 if let Some(ppem) = ppem {
393 if units_per_em > 0 {
394 return Self {
395 scale: Fixed::from_bits((ppem * 64.) as i32)
396 / Fixed::from_bits(units_per_em as i32),
397 is_scaled: true,
398 };
399 }
400 }
401 Self {
402 scale: Fixed::from_bits(0x10000),
403 is_scaled: false,
404 }
405 }
406
407 fn apply(&self, value: i32) -> F26Dot6 {
408 F26Dot6::from_bits((Fixed::from_bits(value) * self.scale).to_bits())
409 }
410
411 fn apply_point(&self, value: Point<i32>) -> Point<F26Dot6> {
412 Point::new(self.apply(value.x), self.apply(value.y))
413 }
414
415 fn mul(&self, value: F26Dot6) -> F26Dot6 {
416 F26Dot6::from_bits((Fixed::from_bits(value.to_bits()) * self.scale).to_bits())
417 }
418
419 fn mul_point(&self, value: Point<F26Dot6>) -> Point<F26Dot6> {
420 Point::new(self.mul(value.x), self.mul(value.y))
421 }
422
423 pub(crate) fn to_bits(self) -> i32 {
424 self.scale.to_bits()
425 }
426}
427
428pub(crate) struct FreeTypeScaler<'a> {
430 outlines: &'a Outlines<'a>,
431 memory: FreeTypeOutlineMemory<'a>,
432 coords: &'a [F2Dot14],
433 point_count: usize,
434 contour_count: usize,
435 component_delta_count: usize,
436 ppem: f32,
437 scale: Scale26Dot6,
438 is_hinted: bool,
439 pedantic_hinting: bool,
440 phantom: [Point<F26Dot6>; PHANTOM_POINT_COUNT],
446 hinter: Option<&'a HintInstance>,
447}
448
449impl<'a> FreeTypeScaler<'a> {
450 pub(crate) fn unhinted(
451 outlines: &'a Outlines<'a>,
452 outline: &'a Outline,
453 buf: &'a mut [u8],
454 ppem: Option<f32>,
455 coords: &'a [F2Dot14],
456 ) -> Result<Self, DrawError> {
457 outline.ensure_point_count_limit()?;
458 let scale = outlines.compute_scale(ppem);
459 let memory = FreeTypeOutlineMemory::new(outline, buf, Hinting::None)
460 .ok_or(DrawError::InsufficientMemory)?;
461 Ok(Self {
462 outlines,
463 memory,
464 coords,
465 point_count: 0,
466 contour_count: 0,
467 component_delta_count: 0,
468 ppem: ppem.unwrap_or_default(),
469 scale,
470 is_hinted: false,
471 pedantic_hinting: false,
472 phantom: Default::default(),
473 hinter: None,
474 })
475 }
476
477 pub(crate) fn hinted(
478 outlines: &'a Outlines<'a>,
479 outline: &'a Outline,
480 buf: &'a mut [u8],
481 ppem: Option<f32>,
482 coords: &'a [F2Dot14],
483 hinter: &'a HintInstance,
484 pedantic_hinting: bool,
485 ) -> Result<Self, DrawError> {
486 outline.ensure_point_count_limit()?;
487 let scale = outlines.compute_hinted_scale(ppem);
488 let memory = FreeTypeOutlineMemory::new(outline, buf, Hinting::Embedded)
489 .ok_or(DrawError::InsufficientMemory)?;
490 Ok(Self {
491 outlines,
492 memory,
493 coords,
494 point_count: 0,
495 contour_count: 0,
496 component_delta_count: 0,
497 ppem: ppem.unwrap_or_default(),
498 scale,
499 is_hinted: scale.is_scaled,
501 pedantic_hinting,
502 phantom: Default::default(),
503 hinter: Some(hinter),
504 })
505 }
506
507 pub(crate) fn scale(
508 mut self,
509 glyph: &Option<Glyph>,
510 glyph_id: GlyphId,
511 ) -> Result<ScaledOutline<'a, F26Dot6>, DrawError> {
512 self.load(glyph, glyph_id, 0)?;
513 let hdmx_width = if self.is_hinted
517 && self
518 .hinter
519 .as_ref()
520 .map(|hinter| !hinter.backward_compatibility())
521 .unwrap_or(true)
522 {
523 self.outlines.hdmx_width(self.ppem, glyph_id)
524 } else {
525 None
526 };
527 Ok(ScaledOutline::new(
528 &mut self.memory.scaled[..self.point_count],
529 self.phantom,
530 &mut self.memory.flags[..self.point_count],
531 &mut self.memory.contours[..self.contour_count],
532 hdmx_width,
533 ))
534 }
535}
536
537impl Scaler for FreeTypeScaler<'_> {
538 fn setup_phantom_points(
539 &mut self,
540 bounds: [i16; 4],
541 lsb: i32,
542 advance: i32,
543 tsb: i32,
544 vadvance: i32,
545 ) {
546 self.phantom[0].x = F26Dot6::from_bits(bounds[0] as i32 - lsb);
550 self.phantom[0].y = F26Dot6::ZERO;
551 self.phantom[1].x = self.phantom[0].x + F26Dot6::from_bits(advance);
552 self.phantom[1].y = F26Dot6::ZERO;
553 self.phantom[2].x = F26Dot6::ZERO;
555 self.phantom[2].y = F26Dot6::from_bits(bounds[3] as i32 + tsb);
556 self.phantom[3].x = F26Dot6::ZERO;
557 self.phantom[3].y = self.phantom[2].y - F26Dot6::from_bits(vadvance);
558 }
559
560 fn outlines(&self) -> &Outlines<'_> {
561 self.outlines
562 }
563
564 fn load_empty(&mut self, glyph_id: GlyphId) -> Result<(), DrawError> {
565 let scale = self.scale;
568 let mut unscaled = self.phantom.map(|point| point.map(|x| x.to_bits()));
569 if self.outlines.gvar.is_some() && !self.coords.is_empty() {
570 if let Ok(Some(deltas)) = self.outlines.gvar.as_ref().unwrap().phantom_point_deltas(
571 &self.outlines.glyf,
572 &self.outlines.loca,
573 self.coords,
574 glyph_id,
575 ) {
576 unscaled[0] += deltas[0].map(Fixed::to_i32);
577 unscaled[1] += deltas[1].map(Fixed::to_i32);
578 }
579 }
580 if self.scale.is_scaled {
581 for (phantom, unscaled) in self.phantom.iter_mut().zip(&unscaled) {
582 *phantom = scale.apply_point(*unscaled);
583 }
584 } else {
585 for (phantom, unscaled) in self.phantom.iter_mut().zip(&unscaled) {
586 *phantom = unscaled.map(F26Dot6::from_i32);
587 }
588 }
589 Ok(())
590 }
591
592 fn load_simple(&mut self, glyph: &SimpleGlyph, glyph_id: GlyphId) -> Result<(), DrawError> {
593 use DrawError::InsufficientMemory;
594 let points_start = self.point_count;
596 let point_count = glyph.num_points();
597 let phantom_start = point_count;
598 let points_end = points_start + point_count + PHANTOM_POINT_COUNT;
599 let point_range = points_start..points_end;
600 let other_points_end = point_count + PHANTOM_POINT_COUNT;
601 let scaled = self
603 .memory
604 .scaled
605 .get_mut(point_range.clone())
606 .ok_or(InsufficientMemory)?;
607 let flags = self
608 .memory
609 .flags
610 .get_mut(point_range)
611 .ok_or(InsufficientMemory)?;
612 let unscaled = self
616 .memory
617 .unscaled
618 .get_mut(..other_points_end)
619 .ok_or(InsufficientMemory)?;
620 glyph.read_points_fast(&mut unscaled[..point_count], &mut flags[..point_count])?;
623 let contours_start = self.contour_count;
625 let contour_end_pts = glyph.end_pts_of_contours();
626 let contour_count = contour_end_pts.len();
627 let contours_end = contours_start + contour_count;
628 let contours = self
629 .memory
630 .contours
631 .get_mut(contours_start..contours_end)
632 .ok_or(InsufficientMemory)?;
633 let mut last_end_pt = 0;
636 for (end_pt, contour) in contour_end_pts.iter().zip(contours.iter_mut()) {
637 let end_pt = end_pt.get();
638 if end_pt < last_end_pt {
639 return Err(ReadError::MalformedData(
640 "unordered contour end points in TrueType glyph",
641 )
642 .into());
643 }
644 last_end_pt = end_pt;
645 *contour = end_pt;
646 }
647 self.point_count += point_count;
649 self.contour_count += contour_count;
650 for (i, phantom) in self.phantom.iter().enumerate() {
652 unscaled[phantom_start + i] = phantom.map(|x| x.to_bits());
653 flags[phantom_start + i] = Default::default();
654 }
655 let mut have_deltas = false;
656 if self.outlines.gvar.is_some() && !self.coords.is_empty() {
657 let gvar = self.outlines.gvar.as_ref().unwrap();
658 let glyph = deltas::SimpleGlyph {
659 points: &mut unscaled[..],
660 flags: &mut flags[..],
661 contours,
662 };
663 let deltas = self
664 .memory
665 .deltas
666 .get_mut(..point_count + PHANTOM_POINT_COUNT)
667 .ok_or(InsufficientMemory)?;
668 let iup_buffer = self
669 .memory
670 .iup_buffer
671 .get_mut(..point_count + PHANTOM_POINT_COUNT)
672 .ok_or(InsufficientMemory)?;
673 if deltas::simple_glyph(gvar, glyph_id, self.coords, glyph, iup_buffer, deltas).is_ok()
674 {
675 have_deltas = true;
676 }
677 }
678 let ins = glyph.instructions();
679 let is_hinted = self.is_hinted;
680 if self.scale.is_scaled {
681 let scale = self.scale;
682 if have_deltas {
683 for ((point, unscaled), delta) in scaled
684 .iter_mut()
685 .zip(unscaled.iter())
686 .zip(self.memory.deltas.iter())
687 {
688 let delta = delta.map(Fixed::to_f26dot6);
689 let scaled = scale.mul_point(unscaled.map(F26Dot6::from_i32) + delta);
690 *point = scaled.map(|v| F26Dot6::from_bits(v.to_i32()));
693 }
694 if self.outlines.glyph_metrics.hvar.is_some() {
698 for ((point, unscaled), delta) in scaled[phantom_start..]
699 .iter_mut()
700 .zip(&unscaled[phantom_start..])
701 .zip(&self.memory.deltas[phantom_start..])
702 {
703 let delta = delta.map(Fixed::to_i32).map(F26Dot6::from_i32);
704 let scaled = scale.mul_point(unscaled.map(F26Dot6::from_i32) + delta);
705 *point = scaled.map(|v| F26Dot6::from_bits(v.to_i32()));
706 }
707 }
708 if is_hinted {
709 for (unscaled, delta) in unscaled.iter_mut().zip(self.memory.deltas.iter()) {
712 *unscaled += delta.map(Fixed::to_i32);
713 }
714 }
715 } else {
716 for (point, unscaled) in scaled.iter_mut().zip(unscaled.iter_mut()) {
717 *point = scale.apply_point(*unscaled);
718 }
719 }
720 } else {
721 if have_deltas {
722 for (unscaled, delta) in unscaled.iter_mut().zip(self.memory.deltas.iter()) {
724 *unscaled += delta.map(Fixed::to_i32);
725 }
726 }
727 for (point, unscaled) in scaled.iter_mut().zip(unscaled.iter()) {
729 *point = unscaled.map(F26Dot6::from_i32);
730 }
731 }
732 self.phantom.copy_from_slice(&scaled[phantom_start..]);
734 if let (Some(hinter), true) = (self.hinter.as_ref(), is_hinted) {
735 if !ins.is_empty() {
736 let original_scaled = self
738 .memory
739 .original_scaled
740 .get_mut(..other_points_end)
741 .ok_or(InsufficientMemory)?;
742 original_scaled.copy_from_slice(scaled);
743 for point in &mut scaled[phantom_start..] {
745 point.x = point.x.round();
746 point.y = point.y.round();
747 }
748 let mut input = HintOutline {
749 glyph_id,
750 unscaled,
751 scaled,
752 original_scaled,
753 flags,
754 contours,
755 bytecode: ins,
756 phantom: &mut self.phantom,
757 stack: self.memory.stack,
758 cvt: self.memory.cvt,
759 storage: self.memory.storage,
760 twilight_scaled: self.memory.twilight_scaled,
761 twilight_original_scaled: self.memory.twilight_original_scaled,
762 twilight_flags: self.memory.twilight_flags,
763 is_composite: false,
764 coords: self.coords,
765 };
766 let hint_res = hinter.hint(self.outlines, &mut input, self.pedantic_hinting);
767 if let (Err(e), true) = (hint_res, self.pedantic_hinting) {
768 return Err(e)?;
769 }
770 } else if !hinter.backward_compatibility() {
771 for (scaled, phantom) in scaled[phantom_start..].iter().zip(&mut self.phantom) {
779 *phantom = scaled.map(|x| x.round());
780 }
781 }
782 }
783 if points_start != 0 {
784 for contour_end in contours.iter_mut() {
786 *contour_end += points_start as u16;
787 }
788 }
789 Ok(())
790 }
791
792 fn load_composite(
793 &mut self,
794 glyph: &CompositeGlyph,
795 glyph_id: GlyphId,
796 recurse_depth: usize,
797 ) -> Result<(), DrawError> {
798 use DrawError::InsufficientMemory;
799 let scale = self.scale;
800 let point_base = self.point_count;
802 let contour_base = self.contour_count;
803 let mut have_deltas = false;
806 let delta_base = self.component_delta_count;
807 if self.outlines.gvar.is_some() && !self.coords.is_empty() {
808 let gvar = self.outlines.gvar.as_ref().unwrap();
809 let count = glyph.components().count() + PHANTOM_POINT_COUNT;
810 let deltas = self
811 .memory
812 .composite_deltas
813 .get_mut(delta_base..delta_base + count)
814 .ok_or(InsufficientMemory)?;
815 if deltas::composite_glyph(gvar, glyph_id, self.coords, &mut deltas[..]).is_ok() {
816 for (phantom, delta) in self
818 .phantom
819 .iter_mut()
820 .zip(&deltas[deltas.len() - PHANTOM_POINT_COUNT..])
821 {
822 *phantom += delta.map(Fixed::to_i32).map(F26Dot6::from_bits);
823 }
824 have_deltas = true;
825 }
826 self.component_delta_count += count;
827 }
828 if self.scale.is_scaled {
829 for point in self.phantom.iter_mut() {
830 *point = scale.mul_point(*point);
831 }
832 } else {
833 for point in self.phantom.iter_mut() {
834 *point = point.map(|x| F26Dot6::from_i32(x.to_bits()));
835 }
836 }
837 for (i, component) in glyph.components().enumerate() {
838 let phantom = self.phantom;
841 let start_point = self.point_count;
843 let component_glyph = self
844 .outlines
845 .loca
846 .get_glyf(component.glyph.into(), &self.outlines.glyf)?;
847 self.load(&component_glyph, component.glyph.into(), recurse_depth + 1)?;
848 let end_point = self.point_count;
849 if !component
850 .flags
851 .contains(CompositeGlyphFlags::USE_MY_METRICS)
852 {
853 self.phantom = phantom;
856 }
857 fn scale_component(x: F2Dot14) -> Fixed {
859 Fixed::from_bits(x.to_bits() as i32 * 4)
860 }
861 let xform = &component.transform;
862 let xx = scale_component(xform.xx);
863 let yx = scale_component(xform.yx);
864 let xy = scale_component(xform.xy);
865 let yy = scale_component(xform.yy);
866 let have_xform = component.flags.intersects(
867 CompositeGlyphFlags::WE_HAVE_A_SCALE
868 | CompositeGlyphFlags::WE_HAVE_AN_X_AND_Y_SCALE
869 | CompositeGlyphFlags::WE_HAVE_A_TWO_BY_TWO,
870 );
871 if have_xform {
872 let scaled = &mut self.memory.scaled[start_point..end_point];
873 if self.scale.is_scaled {
874 for point in scaled {
875 let p = point.map(|c| Fixed::from_bits(c.to_bits()));
876 let x = p.x * xx + p.y * xy;
877 let y = p.x * yx + p.y * yy;
878 let [x, y] = [x, y].map(|c| F26Dot6::from_bits(c.to_bits()));
879 point.x = x;
880 point.y = y;
881 }
882 } else {
883 for point in scaled {
884 let unscaled = point.map(|c| Fixed::from_bits(c.to_i32()));
887 let x = unscaled.x * xx + unscaled.y * xy;
888 let y = unscaled.x * yx + unscaled.y * yy;
889 *point = Point::new(x, y).map(|c| F26Dot6::from_i32(c.to_bits()));
890 }
891 }
892 }
893 let anchor_offset = match component.anchor {
894 Anchor::Offset { x, y } => {
895 let (mut x, mut y) = (x as i32, y as i32);
896 if have_xform
897 && component.flags
898 & (CompositeGlyphFlags::SCALED_COMPONENT_OFFSET
899 | CompositeGlyphFlags::UNSCALED_COMPONENT_OFFSET)
900 == CompositeGlyphFlags::SCALED_COMPONENT_OFFSET
901 {
902 fn hypot(a: Fixed, b: Fixed) -> Fixed {
906 let a = a.to_bits().abs();
907 let b = b.to_bits().abs();
908 Fixed::from_bits(if a > b {
909 a + ((3 * b) >> 3)
910 } else {
911 b + ((3 * a) >> 3)
912 })
913 }
914 x = (Fixed::from_bits(x) * hypot(xx, xy)).to_bits();
916 y = (Fixed::from_bits(y) * hypot(yy, yx)).to_bits();
917 }
918 if have_deltas {
919 let delta = self
920 .memory
921 .composite_deltas
922 .get(delta_base + i)
923 .copied()
924 .unwrap_or_default();
925 x += delta.x.to_i32();
928 y += delta.y.to_i32();
929 }
930 if scale.is_scaled {
931 let mut offset = scale.apply_point(Point::new(x, y));
932 if self.is_hinted
933 && component
934 .flags
935 .contains(CompositeGlyphFlags::ROUND_XY_TO_GRID)
936 {
937 offset.y = offset.y.round();
939 }
940 offset
941 } else {
942 Point::new(x, y).map(F26Dot6::from_i32)
943 }
944 }
945 Anchor::Point { base, component } => {
946 let (base_offset, component_offset) = (base as usize, component as usize);
947 let base_point = self
948 .memory
949 .scaled
950 .get(point_base + base_offset)
951 .ok_or(DrawError::InvalidAnchorPoint(glyph_id, base))?;
952 let component_point = self
953 .memory
954 .scaled
955 .get(start_point + component_offset)
956 .ok_or(DrawError::InvalidAnchorPoint(glyph_id, component))?;
957 *base_point - *component_point
958 }
959 };
960 if anchor_offset.x != F26Dot6::ZERO || anchor_offset.y != F26Dot6::ZERO {
961 for point in &mut self.memory.scaled[start_point..end_point] {
962 *point += anchor_offset;
963 }
964 }
965 }
966 if have_deltas {
967 self.component_delta_count = delta_base;
968 }
969 if let (Some(hinter), true) = (self.hinter.as_ref(), self.is_hinted) {
970 let ins = glyph.instructions().unwrap_or_default();
971 if !ins.is_empty() {
972 let start_point = point_base;
975 let end_point = self.point_count + PHANTOM_POINT_COUNT;
976 let point_range = start_point..end_point;
977 let phantom_start = point_range.len() - PHANTOM_POINT_COUNT;
978 let scaled = &mut self.memory.scaled[point_range.clone()];
979 let flags = self
980 .memory
981 .flags
982 .get_mut(point_range.clone())
983 .ok_or(InsufficientMemory)?;
984 for (i, phantom) in self.phantom.iter().enumerate() {
986 scaled[phantom_start + i] = *phantom;
987 flags[phantom_start + i] = Default::default();
988 }
989 let other_points_end = point_range.len();
990 let unscaled = self
991 .memory
992 .unscaled
993 .get_mut(..other_points_end)
994 .ok_or(InsufficientMemory)?;
995 for (scaled, unscaled) in scaled.iter().zip(unscaled.iter_mut()) {
996 *unscaled = scaled.map(|x| x.to_bits());
997 }
998 let original_scaled = self
999 .memory
1000 .original_scaled
1001 .get_mut(..other_points_end)
1002 .ok_or(InsufficientMemory)?;
1003 original_scaled.copy_from_slice(scaled);
1004 let contours = self
1005 .memory
1006 .contours
1007 .get_mut(contour_base..self.contour_count)
1008 .ok_or(InsufficientMemory)?;
1009 for p in &mut scaled[phantom_start..] {
1011 p.x = p.x.round();
1012 p.y = p.y.round();
1013 }
1014 for flag in flags.iter_mut() {
1016 flag.clear_marker(PointMarker::TOUCHED);
1017 }
1018 if point_base != 0 {
1021 let delta = point_base as u16;
1022 for contour in contours.iter_mut() {
1023 *contour -= delta;
1024 }
1025 }
1026 let mut input = HintOutline {
1027 glyph_id,
1028 unscaled,
1029 scaled,
1030 original_scaled,
1031 flags,
1032 contours,
1033 bytecode: ins,
1034 phantom: &mut self.phantom,
1035 stack: self.memory.stack,
1036 cvt: self.memory.cvt,
1037 storage: self.memory.storage,
1038 twilight_scaled: self.memory.twilight_scaled,
1039 twilight_original_scaled: self.memory.twilight_original_scaled,
1040 twilight_flags: self.memory.twilight_flags,
1041 is_composite: true,
1042 coords: self.coords,
1043 };
1044 let hint_res = hinter.hint(self.outlines, &mut input, self.pedantic_hinting);
1045 if let (Err(e), true) = (hint_res, self.pedantic_hinting) {
1046 return Err(e)?;
1047 }
1048 if point_base != 0 {
1050 let delta = point_base as u16;
1051 for contour in contours.iter_mut() {
1052 *contour += delta;
1053 }
1054 }
1055 }
1056 }
1057 Ok(())
1058 }
1059}
1060
1061impl Scaler for HarfBuzzScaler<'_> {
1062 fn setup_phantom_points(
1063 &mut self,
1064 bounds: [i16; 4],
1065 lsb: i32,
1066 advance: i32,
1067 tsb: i32,
1068 vadvance: i32,
1069 ) {
1070 self.phantom[0].x = bounds[0] as f32 - lsb as f32;
1073 self.phantom[0].y = 0.0;
1074 self.phantom[1].x = self.phantom[0].x + advance as f32;
1075 self.phantom[1].y = 0.0;
1076 self.phantom[2].x = 0.0;
1078 self.phantom[2].y = bounds[3] as f32 + tsb as f32;
1079 self.phantom[3].x = 0.0;
1080 self.phantom[3].y = self.phantom[2].y - vadvance as f32;
1081 }
1082
1083 fn outlines(&self) -> &Outlines<'_> {
1084 self.outlines
1085 }
1086
1087 fn load_empty(&mut self, glyph_id: GlyphId) -> Result<(), DrawError> {
1088 let scale = self.scale;
1091 let mut unscaled = self.phantom;
1092 if self.outlines.glyph_metrics.hvar.is_none()
1093 && self.outlines.gvar.is_some()
1094 && !self.coords.is_empty()
1095 {
1096 if let Ok(Some(deltas)) = self.outlines.gvar.as_ref().unwrap().phantom_point_deltas(
1097 &self.outlines.glyf,
1098 &self.outlines.loca,
1099 self.coords,
1100 glyph_id,
1101 ) {
1102 unscaled[0] += deltas[0].map(Fixed::to_f32);
1103 unscaled[1] += deltas[1].map(Fixed::to_f32);
1104 }
1105 }
1106 for (phantom, unscaled) in self.phantom.iter_mut().zip(&unscaled) {
1107 *phantom = *unscaled * scale;
1108 }
1109 Ok(())
1110 }
1111
1112 fn load_simple(&mut self, glyph: &SimpleGlyph, glyph_id: GlyphId) -> Result<(), DrawError> {
1113 use DrawError::InsufficientMemory;
1114 let points_start = self.point_count;
1116 let point_count = glyph.num_points();
1117 let phantom_start = point_count;
1118 let points_end = points_start + point_count + PHANTOM_POINT_COUNT;
1119 let point_range = points_start..points_end;
1120 let points = self
1122 .memory
1123 .points
1124 .get_mut(point_range.clone())
1125 .ok_or(InsufficientMemory)?;
1126 let flags = self
1127 .memory
1128 .flags
1129 .get_mut(point_range)
1130 .ok_or(InsufficientMemory)?;
1131 glyph.read_points_fast(&mut points[..point_count], &mut flags[..point_count])?;
1132 let contours_start = self.contour_count;
1134 let contour_end_pts = glyph.end_pts_of_contours();
1135 let contour_count = contour_end_pts.len();
1136 let contours_end = contours_start + contour_count;
1137 let contours = self
1138 .memory
1139 .contours
1140 .get_mut(contours_start..contours_end)
1141 .ok_or(InsufficientMemory)?;
1142 for (end_pt, contour) in contour_end_pts.iter().zip(contours.iter_mut()) {
1144 *contour = end_pt.get();
1145 }
1146 self.point_count += point_count;
1148 self.contour_count += contour_count;
1149 for (i, phantom) in self.phantom.iter().enumerate() {
1151 points[phantom_start + i] = *phantom;
1152 flags[phantom_start + i] = Default::default();
1153 }
1154 if self.outlines.gvar.is_some() && !self.coords.is_empty() {
1156 let gvar = self.outlines.gvar.as_ref().unwrap();
1157 let glyph = deltas::SimpleGlyph {
1158 points: &mut points[..],
1159 flags: &mut flags[..],
1160 contours,
1161 };
1162 let deltas = self
1163 .memory
1164 .deltas
1165 .get_mut(..point_count + PHANTOM_POINT_COUNT)
1166 .ok_or(InsufficientMemory)?;
1167 let iup_buffer = self
1168 .memory
1169 .iup_buffer
1170 .get_mut(..point_count + PHANTOM_POINT_COUNT)
1171 .ok_or(InsufficientMemory)?;
1172 if deltas::simple_glyph(gvar, glyph_id, self.coords, glyph, iup_buffer, deltas).is_ok()
1173 {
1174 for (point, delta) in points.iter_mut().zip(deltas) {
1175 *point += *delta;
1176 }
1177 }
1178 }
1179 if self.scale != 1.0 {
1181 for point in points.iter_mut() {
1182 *point *= self.scale;
1183 }
1184 }
1185
1186 if points_start != 0 {
1187 for contour_end in contours.iter_mut() {
1189 *contour_end += points_start as u16;
1190 }
1191 }
1192 Ok(())
1193 }
1194
1195 fn load_composite(
1196 &mut self,
1197 glyph: &CompositeGlyph,
1198 glyph_id: GlyphId,
1199 recurse_depth: usize,
1200 ) -> Result<(), DrawError> {
1201 use DrawError::InsufficientMemory;
1202 let scale = self.scale;
1203 let point_base = self.point_count;
1205 let mut have_deltas = false;
1208 let delta_base = self.component_delta_count;
1209 if self.outlines.gvar.is_some() && !self.coords.is_empty() {
1210 let gvar = self.outlines.gvar.as_ref().unwrap();
1211 let count = glyph.components().count() + PHANTOM_POINT_COUNT;
1212 let deltas = self
1213 .memory
1214 .composite_deltas
1215 .get_mut(delta_base..delta_base + count)
1216 .ok_or(InsufficientMemory)?;
1217 if deltas::composite_glyph(gvar, glyph_id, self.coords, &mut deltas[..]).is_ok() {
1218 for (phantom, delta) in self
1220 .phantom
1221 .iter_mut()
1222 .zip(&deltas[deltas.len() - PHANTOM_POINT_COUNT..])
1223 {
1224 *phantom += *delta;
1225 }
1226 have_deltas = true;
1227 }
1228 self.component_delta_count += count;
1229 }
1230 if scale != 1.0 {
1231 for point in self.phantom.iter_mut() {
1232 *point *= scale;
1233 }
1234 }
1235 for (i, component) in glyph.components().enumerate() {
1236 let phantom = self.phantom;
1239 let start_point = self.point_count;
1241 let component_glyph = self
1242 .outlines
1243 .loca
1244 .get_glyf(component.glyph.into(), &self.outlines.glyf)?;
1245 self.load(&component_glyph, component.glyph.into(), recurse_depth + 1)?;
1246 let end_point = self.point_count;
1247 if !component
1248 .flags
1249 .contains(CompositeGlyphFlags::USE_MY_METRICS)
1250 {
1251 self.phantom = phantom;
1254 }
1255 let have_xform = component.flags.intersects(
1256 CompositeGlyphFlags::WE_HAVE_A_SCALE
1257 | CompositeGlyphFlags::WE_HAVE_AN_X_AND_Y_SCALE
1258 | CompositeGlyphFlags::WE_HAVE_A_TWO_BY_TWO,
1259 );
1260 let mut transform = if have_xform {
1261 let xform = &component.transform;
1262 [
1263 xform.xx,
1264 xform.yx,
1265 xform.xy,
1266 xform.yy,
1267 F2Dot14::ZERO,
1268 F2Dot14::ZERO,
1269 ]
1270 .map(|x| x.to_f32())
1271 } else {
1272 [1.0, 0.0, 0.0, 1.0, 0.0, 0.0] };
1274
1275 let anchor_offset = match component.anchor {
1276 Anchor::Offset { x, y } => {
1277 let (mut x, mut y) = (x as f32, y as f32);
1278 if have_xform
1279 && component.flags
1280 & (CompositeGlyphFlags::SCALED_COMPONENT_OFFSET
1281 | CompositeGlyphFlags::UNSCALED_COMPONENT_OFFSET)
1282 == CompositeGlyphFlags::SCALED_COMPONENT_OFFSET
1283 {
1284 x *= hypot(transform[0], transform[2]);
1287 y *= hypot(transform[1], transform[3]);
1288 }
1289 Point::new(x, y)
1290 + self
1291 .memory
1292 .composite_deltas
1293 .get(delta_base + i)
1294 .copied()
1295 .unwrap_or_default()
1296 }
1297 Anchor::Point { base, component } => {
1298 let (base_offset, component_offset) = (base as usize, component as usize);
1299 let base_point = self
1300 .memory
1301 .points
1302 .get(point_base + base_offset)
1303 .ok_or(DrawError::InvalidAnchorPoint(glyph_id, base))?;
1304 let component_point = self
1305 .memory
1306 .points
1307 .get(start_point + component_offset)
1308 .ok_or(DrawError::InvalidAnchorPoint(glyph_id, component))?;
1309 *base_point - *component_point
1310 }
1311 };
1312 transform[4] = anchor_offset.x;
1313 transform[5] = anchor_offset.y;
1314
1315 let points = &mut self.memory.points[start_point..end_point];
1316 for point in points.iter_mut() {
1317 *point = map_point(transform, *point);
1318 }
1319 }
1320 if have_deltas {
1321 self.component_delta_count = delta_base;
1322 }
1323 Ok(())
1324 }
1325}
1326
1327fn hypot(x: f32, y: f32) -> f32 {
1329 x.hypot(y)
1330}
1331
1332fn map_point(transform: [f32; 6], p: Point<f32>) -> Point<f32> {
1333 Point {
1334 x: transform[0] * p.x + transform[2] * p.y + transform[4],
1335 y: transform[1] * p.x + transform[3] * p.y + transform[5],
1336 }
1337}
1338
1339#[cfg(test)]
1340mod tests {
1341 use super::*;
1342 use crate::MetadataProvider;
1343 use raw::{
1344 tables::{
1345 glyf::{CompositeGlyphFlags, Glyf, SimpleGlyphFlags},
1346 loca::Loca,
1347 },
1348 FontRead, FontRef, TableProvider,
1349 };
1350
1351 #[test]
1352 fn overlap_flags() {
1353 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
1354 let scaler = Outlines::new(&font).unwrap();
1355 let glyph_count = font.maxp().unwrap().num_glyphs();
1356 let expected_gids_with_overlap = vec![2, 3];
1359 assert_eq!(
1360 expected_gids_with_overlap,
1361 (0..glyph_count)
1362 .filter(|gid| scaler.outline(GlyphId::from(*gid)).unwrap().has_overlaps)
1363 .collect::<Vec<_>>()
1364 );
1365 }
1366
1367 #[test]
1368 fn interpreter_preference() {
1369 let font = FontRef::new(font_test_data::COLRV0V1).unwrap();
1371 let outlines = Outlines::new(&font).unwrap();
1372 assert!(!outlines.prefer_interpreter());
1374 let font = FontRef::new(font_test_data::TTHINT_SUBSET).unwrap();
1376 let outlines = Outlines::new(&font).unwrap();
1377 assert!(outlines.prefer_interpreter());
1379 }
1380
1381 #[test]
1382 fn empty_glyph_advance() {
1383 let font = FontRef::new(font_test_data::HVAR_WITH_TRUNCATED_ADVANCE_INDEX_MAP).unwrap();
1384 let outlines = Outlines::new(&font).unwrap();
1385 let coords = [F2Dot14::from_f32(0.5)];
1386 let ppem = Some(24.0);
1387 let gid = font.charmap().map(' ').unwrap();
1388 let outline = outlines.outline(gid).unwrap();
1389 assert!(outline.glyph.is_none());
1391 let mut buf = [0u8; 128];
1392 let scaler =
1393 FreeTypeScaler::unhinted(&outlines, &outline, &mut buf, ppem, &coords).unwrap();
1394 let scaled = scaler.scale(&outline.glyph, gid).unwrap();
1395 let advance = scaled.adjusted_advance_width();
1396 assert!(advance != F26Dot6::ZERO);
1397 }
1398
1399 #[test]
1400 fn empty_glyphs_have_phantom_points_too() {
1401 let font = FontRef::new(font_test_data::HVAR_WITH_TRUNCATED_ADVANCE_INDEX_MAP).unwrap();
1402 let outlines = Outlines::new(&font).unwrap();
1403 let gid = font.charmap().map(' ').unwrap();
1404 let outline = outlines.outline(gid).unwrap();
1405 assert!(outline.glyph.is_none());
1406 assert_eq!(outline.points, PHANTOM_POINT_COUNT);
1407 }
1408
1409 #[test]
1412 fn composite_component_limit() {
1413 use font_test_data::bebuffer::BeBuffer;
1414 let font = FontRef::new(font_test_data::GLYF_COMPONENTS).unwrap();
1415 fn build_with_n_comps(n_comps: usize) -> [BeBuffer; 2] {
1416 let mut glyf_buf = BeBuffer::new();
1417 glyf_buf = glyf_buf.push(0i16); glyf_buf = glyf_buf.extend([0i16; 4]); glyf_buf = glyf_buf.push(0u16); let glyph0_end = glyf_buf.len();
1421 glyf_buf = glyf_buf.push(-1i16); glyf_buf = glyf_buf.extend([0i16; 4]); for i in 0..n_comps {
1425 let flags = if i == n_comps - 1 {
1426 CompositeGlyphFlags::ARGS_ARE_XY_VALUES
1427 } else {
1428 CompositeGlyphFlags::MORE_COMPONENTS | CompositeGlyphFlags::ARGS_ARE_XY_VALUES
1429 };
1430 glyf_buf = glyf_buf.push(flags); glyf_buf = glyf_buf.push(0u16); glyf_buf = glyf_buf.extend([0u8; 2]); }
1434 let glyph1_end = glyf_buf.len();
1435 let mut loca_buf = font_test_data::bebuffer::BeBuffer::new();
1436 loca_buf = loca_buf.extend([0u32, glyph0_end as u32, glyph1_end as u32]);
1437 [glyf_buf, loca_buf]
1438 }
1439 let gid = GlyphId::new(1);
1440 let [glyf_buf, loca_buf] = build_with_n_comps(MAX_GRAPH_EDGES + 1);
1443 let mut outlines = Outlines::new(&font).unwrap();
1444 outlines.glyf = Glyf::read(glyf_buf.data().into()).unwrap();
1445 outlines.loca = Loca::read(loca_buf.data().into(), true).unwrap();
1446 let result = outlines.outline(gid);
1447 assert!(matches!(result, Err(DrawError::RecursionLimitExceeded(_))));
1448 let [glyf_buf, loca_buf] = build_with_n_comps(MAX_GRAPH_EDGES);
1451 let mut outlines = Outlines::new(&font).unwrap();
1452 outlines.glyf = Glyf::read(glyf_buf.data().into()).unwrap();
1453 outlines.loca = Loca::read(loca_buf.data().into(), true).unwrap();
1454 let result = outlines.outline(gid);
1455 assert!(result.is_ok());
1456 }
1457
1458 #[test]
1461 fn composite_with_too_many_points() {
1462 let font = FontRef::new(font_test_data::GLYF_COMPONENTS).unwrap();
1463 let mut outlines = Outlines::new(&font).unwrap();
1464 let mut glyf_buf = font_test_data::bebuffer::BeBuffer::new();
1467 let simple_glyph_point_count = 40000;
1470 glyf_buf = glyf_buf.push(1u16); glyf_buf = glyf_buf.extend([0i16; 4]); glyf_buf = glyf_buf.push((simple_glyph_point_count - 1) as u16); glyf_buf = glyf_buf.push(0u16); for _ in 0..simple_glyph_point_count {
1475 glyf_buf =
1476 glyf_buf.push(SimpleGlyphFlags::X_SHORT_VECTOR | SimpleGlyphFlags::Y_SHORT_VECTOR);
1477 }
1478 for _ in 0..simple_glyph_point_count * 2 {
1480 glyf_buf = glyf_buf.push(0u8);
1481 }
1482 let glyph0_end = glyf_buf.len();
1483 glyf_buf = glyf_buf.push(-1i16); glyf_buf = glyf_buf.extend([0i16; 4]); for i in 0..2 {
1487 let flags = if i == 0 {
1488 CompositeGlyphFlags::MORE_COMPONENTS | CompositeGlyphFlags::ARGS_ARE_XY_VALUES
1489 } else {
1490 CompositeGlyphFlags::ARGS_ARE_XY_VALUES
1491 };
1492 glyf_buf = glyf_buf.push(flags); glyf_buf = glyf_buf.push(0u16); glyf_buf = glyf_buf.extend([0u8; 2]); }
1496 let glyph1_end = glyf_buf.len();
1497 outlines.glyf = Glyf::read(glyf_buf.data().into()).unwrap();
1498 let mut loca_buf = font_test_data::bebuffer::BeBuffer::new();
1500 loca_buf = loca_buf.extend([0u32, glyph0_end as u32, glyph1_end as u32]);
1501 outlines.loca = Loca::read(loca_buf.data().into(), true).unwrap();
1502 let gid = GlyphId::new(1);
1503 let result = outlines.outline(gid);
1504 assert!(matches!(result, Err(DrawError::TooManyPoints(_))));
1505 }
1506
1507 #[test]
1508 fn fractional_size_hinting() {
1509 let font = FontRef::from_index(font_test_data::TINOS_SUBSET, 0).unwrap();
1510 let outlines = Outlines::new(&font).unwrap();
1511 assert!(!outlines.fractional_size_hinting);
1513 for size in [10.0, 10.2, 10.5, 10.8, 11.0] {
1516 assert_eq!(
1517 outlines.compute_hinted_scale(Some(size)).scale,
1518 outlines.compute_hinted_scale(Some(size.round())).scale
1519 );
1520 }
1521 }
1522}