1use crate::{
10 model::{
11 glyph::{GlyphSource, GlyphSourceOrder},
12 metrics::GlyphExtents,
13 Font,
14 },
15 tables::{
16 bitmap::{self as bdt, BitmapSize},
17 cbdt::Cbdt,
18 cblc::Cblc,
19 ebdt::Ebdt,
20 eblc::Eblc,
21 sbix::{self, Sbix},
22 },
23 types::{GlyphId, Tag},
24 FontData,
25};
26
27#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
29pub enum BitmapSource {
30 Sbix,
32 Cbdt,
34 Ebdt,
36}
37
38impl From<BitmapSource> for GlyphSource {
39 fn from(source: BitmapSource) -> Self {
40 match source {
41 BitmapSource::Sbix => Self::Sbix,
42 BitmapSource::Cbdt => Self::Cbdt,
43 BitmapSource::Ebdt => Self::Ebdt,
44 }
45 }
46}
47
48impl TryFrom<GlyphSource> for BitmapSource {
49 type Error = GlyphSource;
51
52 fn try_from(source: GlyphSource) -> Result<Self, GlyphSource> {
53 Ok(match source {
54 GlyphSource::Sbix => Self::Sbix,
55 GlyphSource::Cbdt => Self::Cbdt,
56 GlyphSource::Ebdt => Self::Ebdt,
57 _ => return Err(source),
58 })
59 }
60}
61
62#[cfg(feature = "libm")]
63#[allow(unused_imports)]
64use core_maths::CoreFloat;
65
66#[derive(Clone, Copy)]
71pub struct BitmapStrikes<'a> {
72 font: &'a Font,
73 kind: Kind<'a>,
74}
75
76#[derive(Clone, Copy)]
77enum Kind<'a> {
78 None,
79 Sbix(&'a Sbix<'a>),
80 Cbdt(&'a Cblc<'a>, &'a Cbdt<'a>),
81 Ebdt(&'a Eblc<'a>, &'a Ebdt<'a>),
82}
83
84impl<'a> BitmapStrikes<'a> {
85 pub(crate) fn new(font: &'a Font, order: GlyphSourceOrder) -> Self {
86 for source in order
87 .iter()
88 .filter_map(|source| BitmapSource::try_from(source).ok())
89 {
90 let kind = match source {
91 BitmapSource::Sbix => font.sbix().map(Kind::Sbix),
92 BitmapSource::Cbdt => font.cbdt().map(|(cblc, cbdt)| Kind::Cbdt(cblc, cbdt)),
93 BitmapSource::Ebdt => font.ebdt().map(|(eblc, ebdt)| Kind::Ebdt(eblc, ebdt)),
94 };
95 if let Some(kind) = kind {
96 return Self { font, kind };
97 }
98 }
99 Self {
100 font,
101 kind: Kind::None,
102 }
103 }
104
105 pub fn source(&self) -> Option<BitmapSource> {
108 match self.kind {
109 Kind::None => None,
110 Kind::Sbix(..) => Some(BitmapSource::Sbix),
111 Kind::Cbdt(..) => Some(BitmapSource::Cbdt),
112 Kind::Ebdt(..) => Some(BitmapSource::Ebdt),
113 }
114 }
115
116 pub fn len(&self) -> u32 {
118 let count = match self.kind {
119 Kind::None => 0,
120 Kind::Sbix(sbix) => sbix.strikes().len(),
121 Kind::Cbdt(cblc, _) => cblc.bitmap_sizes().len(),
122 Kind::Ebdt(eblc, _) => eblc.bitmap_sizes().len(),
123 };
124 count as u32
125 }
126
127 pub fn is_empty(&self) -> bool {
129 self.len() == 0
130 }
131
132 pub fn get(&self, index: u32) -> Option<Strike<'a>> {
134 let index = index as usize;
135 let kind = match self.kind {
136 Kind::None => return None,
137 Kind::Sbix(sbix) => {
138 let ppem = sbix.strikes().get(index).ok()?.ppem();
139 StrikeKind::Sbix(sbix, index as u32, ppem)
140 }
141 Kind::Cbdt(cblc, cbdt) => StrikeKind::Cbdt(cblc.bitmap_sizes().get(index)?, cblc, cbdt),
142 Kind::Ebdt(eblc, ebdt) => StrikeKind::Ebdt(eblc.bitmap_sizes().get(index)?, eblc, ebdt),
143 };
144 Some(Strike {
145 font: self.font,
146 kind,
147 })
148 }
149
150 pub fn iter(&self) -> impl Iterator<Item = Strike<'a>> + 'a {
152 let this = *self;
153 (0..this.len()).filter_map(move |index| this.get(index))
154 }
155
156 pub fn select(&self, ppem: Option<f32>) -> Option<Strike<'a>> {
162 let mut best: Option<Strike<'a>> = None;
163 for strike in self.iter() {
164 if best
165 .as_ref()
166 .is_none_or(|best| closer_to(ppem, strike.ppem(), best.ppem()))
167 {
168 best = Some(strike);
169 }
170 }
171 best
172 }
173
174 pub fn select_bitmap(&self, glyph: GlyphId, ppem: Option<f32>) -> Option<BitmapGlyph<'a>> {
181 let mut best: Option<BitmapGlyph<'a>> = None;
182 for strike in self.iter() {
183 if best
184 .as_ref()
185 .is_none_or(|best| closer_to(ppem, strike.ppem(), best.ppem()))
186 {
187 if let Some(bitmap) = strike.get(glyph) {
188 best = Some(bitmap);
189 }
190 }
191 }
192 best
193 }
194}
195
196fn closer_to(ppem: Option<f32>, candidate: u16, best: u16) -> bool {
203 let requested = ppem.filter(|ppem| *ppem > 0.0).unwrap_or(f32::INFINITY);
204 let (candidate, best) = (candidate as f32, best as f32);
205 (requested <= candidate && candidate < best) || (requested > best && candidate > best)
206}
207
208#[derive(Clone, Copy)]
210pub struct Strike<'a> {
211 font: &'a Font,
214 kind: StrikeKind<'a>,
215}
216
217#[derive(Clone, Copy)]
218enum StrikeKind<'a> {
219 Sbix(&'a Sbix<'a>, u32, u16),
221 Cbdt(&'a BitmapSize, &'a Cblc<'a>, &'a Cbdt<'a>),
222 Ebdt(&'a BitmapSize, &'a Eblc<'a>, &'a Ebdt<'a>),
223}
224
225impl<'a> Strike<'a> {
226 pub fn ppem_x(&self) -> u16 {
228 match &self.kind {
229 StrikeKind::Sbix(_, _, ppem) => *ppem,
230 StrikeKind::Cbdt(size, ..) | StrikeKind::Ebdt(size, ..) => size.ppem_x() as u16,
231 }
232 }
233
234 pub fn ppem_y(&self) -> u16 {
236 match &self.kind {
237 StrikeKind::Sbix(_, _, ppem) => *ppem,
238 StrikeKind::Cbdt(size, ..) | StrikeKind::Ebdt(size, ..) => size.ppem_y() as u16,
239 }
240 }
241
242 pub fn ppem(&self) -> u16 {
246 self.ppem_x().max(self.ppem_y())
247 }
248
249 pub fn source(&self) -> BitmapSource {
251 match &self.kind {
252 StrikeKind::Sbix(..) => BitmapSource::Sbix,
253 StrikeKind::Cbdt(..) => BitmapSource::Cbdt,
254 StrikeKind::Ebdt(..) => BitmapSource::Ebdt,
255 }
256 }
257
258 pub fn get(&self, glyph: GlyphId) -> Option<BitmapGlyph<'a>> {
264 if self.ppem_x() == 0 || self.ppem_y() == 0 {
265 return None;
266 }
267 let units_per_em = self.font.units_per_em();
268 match &self.kind {
269 StrikeKind::Sbix(sbix, index, _) => {
270 let strike = sbix.strikes().get(*index as usize).ok()?;
271 sbix_glyph(&strike, self.font, glyph)
272 }
273 StrikeKind::Cbdt(size, cblc, cbdt) => {
274 let location = size.location(cblc.offset_data(), glyph).ok()?;
275 let data = cbdt.data(&location).ok()?;
276 BitmapGlyph::from_bdt(glyph, BitmapSource::Cbdt, size, &data, units_per_em)
277 }
278 StrikeKind::Ebdt(size, eblc, ebdt) => {
279 let location = size.location(eblc.offset_data(), glyph).ok()?;
280 let data = ebdt.data(&location).ok()?;
281 BitmapGlyph::from_bdt(glyph, BitmapSource::Ebdt, size, &data, units_per_em)
282 }
283 }
284 }
285}
286
287const MAX_SBIX_DUPES: usize = 8;
291
292const PNG_SIGNATURE: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A];
294
295fn sbix_glyph<'a>(
299 strike: &sbix::Strike<'a>,
300 font: &Font,
301 mut glyph: GlyphId,
302) -> Option<BitmapGlyph<'a>> {
303 let requested = glyph;
306 for _ in 0..=MAX_SBIX_DUPES {
307 let entry = strike.glyph_data(glyph).ok()??;
308 match entry.graphic_type() {
309 tag if tag == Tag::new(b"dupe") => {
310 glyph = GlyphId::from(FontData::new(entry.data()).read_at::<u16>(0).ok()?);
311 }
312 tag if tag == Tag::new(b"png ") => {
313 let data = entry.data();
314 if data.get(..8)? != PNG_SIGNATURE {
315 return None;
316 }
317 let reader = FontData::new(data);
320 let width = reader.read_at::<u32>(16).ok()?;
321 let height = reader.read_at::<u32>(20).ok()?;
322 if width >= 1 << 16 || height >= 1 << 16 {
325 return None;
326 }
327 let ppem = strike.ppem();
328 let (x_bearing, y_bearing) = sbix_bearings(font, requested);
329 return Some(BitmapGlyph {
330 glyph: requested,
331 source: BitmapSource::Sbix,
332 pixels: Pixels::Png(data),
333 width,
334 height,
335 x_bearing,
336 y_bearing,
337 inner_x_bearing: entry.origin_offset_x() as i32,
338 inner_y_bearing: entry.origin_offset_y() as i32,
339 h_advance: None,
340 ppem_x: ppem,
341 ppem_y: ppem,
342 origin: Origin::BottomLeft,
343 units_per_em: font.units_per_em(),
344 });
345 }
346 _ => return None,
347 }
348 }
349 None
350}
351
352fn sbix_bearings(font: &Font, glyph: GlyphId) -> (i32, i32) {
359 let Some((glyf, loca)) = font.selected_glyf_loca() else {
360 return (0, 0);
361 };
362 let Ok(Some(outline)) = loca.get_glyf(glyph, glyf) else {
363 return (0, 0);
364 };
365 if outline.number_of_contours() == 0 {
366 return (0, 0);
367 }
368 let lsb = font
369 .h_metric_records()
370 .stated_side_bearing(glyph)
371 .map(|lsb| lsb.to_i32())
372 .unwrap_or(0);
373 (lsb, outline.y_min() as i32)
374}
375
376#[derive(Copy, Clone, PartialEq, Eq, Debug)]
378pub enum Origin {
379 TopLeft,
381 BottomLeft,
383}
384
385#[derive(Clone, Copy)]
392pub struct BitmapGlyph<'a> {
393 glyph: GlyphId,
394 source: BitmapSource,
395 pixels: Pixels<'a>,
396 width: u32,
397 height: u32,
398 x_bearing: i32,
399 y_bearing: i32,
400 inner_x_bearing: i32,
401 inner_y_bearing: i32,
402 h_advance: Option<u32>,
403 ppem_x: u16,
404 ppem_y: u16,
405 origin: Origin,
406 units_per_em: u16,
408}
409
410impl<'a> BitmapGlyph<'a> {
411 fn from_bdt(
412 glyph: GlyphId,
413 source: BitmapSource,
414 size: &BitmapSize,
415 data: &bdt::BitmapData<'a>,
416 units_per_em: u16,
417 ) -> Option<Self> {
418 let (inner_x_bearing, inner_y_bearing, advance, width, height) = match data.metrics {
419 bdt::BitmapMetrics::Small(metrics) => (
420 metrics.bearing_x() as i32,
421 metrics.bearing_y() as i32,
422 metrics.advance() as u32,
423 metrics.width() as u32,
424 metrics.height() as u32,
425 ),
426 bdt::BitmapMetrics::Big(metrics) => (
427 metrics.hori_bearing_x() as i32,
428 metrics.hori_bearing_y() as i32,
429 metrics.hori_advance() as u32,
430 metrics.width() as u32,
431 metrics.height() as u32,
432 ),
433 };
434 let bdt::BitmapContent::Data(format, bytes) = &data.content else {
435 return None;
438 };
439 let bits_per_pixel = size.bit_depth();
440 let pixels = match (bits_per_pixel, format) {
441 (32, bdt::BitmapDataFormat::Png) => Pixels::Png(bytes),
442 (32, bdt::BitmapDataFormat::ByteAligned) => {
444 Pixels::Bgra(Bgra::new(bytes, width, height)?)
445 }
446 (1 | 2 | 4 | 8, bdt::BitmapDataFormat::ByteAligned) => {
447 Pixels::Mask(Mask::new(bytes, width, height, bits_per_pixel, false)?)
448 }
449 (1 | 2 | 4 | 8, bdt::BitmapDataFormat::BitAligned) => {
450 Pixels::Mask(Mask::new(bytes, width, height, bits_per_pixel, true)?)
451 }
452 _ => return None,
453 };
454 Some(Self {
455 glyph,
456 source,
457 pixels,
458 width,
459 height,
460 x_bearing: 0,
461 y_bearing: 0,
462 inner_x_bearing,
463 inner_y_bearing,
464 h_advance: Some(advance),
465 ppem_x: size.ppem_x() as u16,
466 ppem_y: size.ppem_y() as u16,
467 origin: Origin::TopLeft,
468 units_per_em,
469 })
470 }
471
472 pub fn glyph(&self) -> GlyphId {
477 self.glyph
478 }
479
480 pub fn source(&self) -> BitmapSource {
482 self.source
483 }
484
485 pub fn pixels(&self) -> Pixels<'a> {
487 self.pixels
488 }
489
490 pub fn width(&self) -> u32 {
492 self.width
493 }
494
495 pub fn height(&self) -> u32 {
497 self.height
498 }
499
500 pub fn x_bearing(&self) -> i32 {
506 self.x_bearing
507 }
508
509 pub fn y_bearing(&self) -> i32 {
515 self.y_bearing
516 }
517
518 pub fn inner_x_bearing(&self) -> i32 {
521 self.inner_x_bearing
522 }
523
524 pub fn inner_y_bearing(&self) -> i32 {
527 self.inner_y_bearing
528 }
529
530 pub fn h_advance(&self) -> Option<u32> {
534 self.h_advance
535 }
536
537 pub fn ppem_x(&self) -> u16 {
540 self.ppem_x
541 }
542
543 pub fn ppem_y(&self) -> u16 {
546 self.ppem_y
547 }
548
549 pub fn origin(&self) -> Origin {
551 self.origin
552 }
553
554 pub fn ppem(&self) -> u16 {
558 self.ppem_x.max(self.ppem_y)
559 }
560
561 pub fn extents(&self) -> GlyphExtents<f32> {
567 let pixels = self.pixel_extents();
568 let units_per_em = self.units_per_em as f32;
569 let scale = |value: i32, ppem: u16| (value as f32 * units_per_em / ppem as f32).round();
571 GlyphExtents {
572 x_bearing: scale(pixels.x_bearing, self.ppem_x),
573 y_bearing: scale(pixels.y_bearing, self.ppem_y),
574 width: scale(pixels.width, self.ppem_x),
575 height: scale(pixels.height, self.ppem_y),
576 }
577 }
578
579 fn pixel_extents(&self) -> GlyphExtents<i32> {
582 let y_bearing = match self.origin {
583 Origin::TopLeft => self.inner_y_bearing,
584 Origin::BottomLeft => self.inner_y_bearing + self.height as i32,
585 };
586 GlyphExtents {
587 x_bearing: self.inner_x_bearing,
588 y_bearing,
589 width: self.width as i32,
590 height: self.height as i32,
591 }
592 }
593}
594
595#[derive(Clone, Copy)]
597pub enum Pixels<'a> {
598 Png(&'a [u8]),
600 Bgra(Bgra<'a>),
602 Mask(Mask<'a>),
604}
605
606#[derive(Clone, Copy)]
610pub struct Bgra<'a> {
611 data: &'a [u8],
612 width: u32,
613 height: u32,
614}
615
616impl<'a> Bgra<'a> {
617 pub fn new(data: &'a [u8], width: u32, height: u32) -> Option<Self> {
622 let len = (width as usize)
623 .checked_mul(height as usize)?
624 .checked_mul(4)?;
625 (data.len() >= len).then(|| Self {
626 data: &data[..len],
627 width,
628 height,
629 })
630 }
631
632 pub fn width(&self) -> u32 {
634 self.width
635 }
636
637 pub fn height(&self) -> u32 {
639 self.height
640 }
641
642 pub fn data(&self) -> &'a [u8] {
644 self.data
645 }
646
647 pub fn rows(&self) -> impl Iterator<Item = &'a [u8]> + 'a {
651 let row_len = (self.width as usize * 4).max(1);
652 self.data.chunks_exact(row_len)
653 }
654
655 pub fn pixel(&self, x: u32, y: u32) -> Option<[u8; 4]> {
658 if x >= self.width || y >= self.height {
659 return None;
660 }
661 let start = (y as usize * self.width as usize + x as usize) * 4;
662 self.data.get(start..start + 4)?.try_into().ok()
663 }
664}
665
666#[derive(Clone, Copy)]
671pub struct Mask<'a> {
672 data: &'a [u8],
673 width: u32,
674 height: u32,
675 bits_per_pixel: u8,
676 packed: bool,
677}
678
679impl<'a> Mask<'a> {
680 pub fn new(
689 data: &'a [u8],
690 width: u32,
691 height: u32,
692 bits_per_pixel: u8,
693 packed: bool,
694 ) -> Option<Self> {
695 if !matches!(bits_per_pixel, 1 | 2 | 4 | 8) {
696 return None;
697 }
698 let bits = bits_per_pixel as usize;
699 let len = if packed {
700 (width as usize)
701 .checked_mul(height as usize)?
702 .checked_mul(bits)?
703 .div_ceil(8)
704 } else {
705 (width as usize)
706 .checked_mul(bits)?
707 .div_ceil(8)
708 .checked_mul(height as usize)?
709 };
710 (data.len() >= len).then(|| Self {
711 data: &data[..len],
712 width,
713 height,
714 bits_per_pixel,
715 packed,
716 })
717 }
718
719 pub fn width(&self) -> u32 {
721 self.width
722 }
723
724 pub fn height(&self) -> u32 {
726 self.height
727 }
728
729 pub fn bits_per_pixel(&self) -> u8 {
731 self.bits_per_pixel
732 }
733
734 pub fn is_packed(&self) -> bool {
736 self.packed
737 }
738
739 pub fn data(&self) -> &'a [u8] {
741 self.data
742 }
743
744 pub fn decoded_len(&self) -> usize {
748 self.width as usize * self.height as usize
749 }
750
751 pub fn is_empty(&self) -> bool {
753 self.decoded_len() == 0
754 }
755
756 pub fn pixel(&self, x: u32, y: u32) -> Option<u8> {
760 if x >= self.width || y >= self.height {
761 return None;
762 }
763 Some(self.pixel_at(self.bit_offset(x, y)))
764 }
765
766 pub fn pixels(&self) -> impl Iterator<Item = u8> + 'a {
768 let mask = *self;
769 (0..mask.height).flat_map(move |y| mask.row(y).pixels())
770 }
771
772 pub fn rows(&self) -> impl Iterator<Item = MaskRow<'a>> + 'a {
774 let mask = *self;
775 (0..mask.height).map(move |y| mask.row(y))
776 }
777
778 pub fn decode_into(&self, dst: &mut [u8]) -> bool {
785 let len = self.decoded_len();
786 let Some(dst) = dst.get_mut(..len) else {
787 return false;
788 };
789 if self.bits_per_pixel == 8 {
790 dst.copy_from_slice(&self.data[..len]);
792 return true;
793 }
794 for (out, pixel) in dst.iter_mut().zip(self.pixels()) {
795 *out = pixel;
796 }
797 true
798 }
799
800 fn row(&self, y: u32) -> MaskRow<'a> {
801 MaskRow { mask: *self, y }
802 }
803
804 fn bit_offset(&self, x: u32, y: u32) -> usize {
806 let bits = self.bits_per_pixel as usize;
807 if self.packed {
808 (y as usize * self.width as usize + x as usize) * bits
809 } else {
810 let row_bits = (self.width as usize * bits).div_ceil(8) * 8;
811 y as usize * row_bits + x as usize * bits
812 }
813 }
814
815 fn pixel_at(&self, bit: usize) -> u8 {
817 let byte = self.data.get(bit >> 3).copied().unwrap_or(0);
818 match self.bits_per_pixel {
819 1 => ((byte >> (7 - (bit & 7))) & 1) * 255,
820 2 => ((byte >> (6 - (bit & 7))) & 3) * 85,
821 4 => ((byte >> (4 - (bit & 7))) & 15) * 17,
822 _ => byte,
823 }
824 }
825}
826
827#[derive(Clone, Copy)]
829pub struct MaskRow<'a> {
830 mask: Mask<'a>,
831 y: u32,
832}
833
834impl<'a> MaskRow<'a> {
835 pub fn y(&self) -> u32 {
837 self.y
838 }
839
840 pub fn pixel(&self, x: u32) -> Option<u8> {
842 self.mask.pixel(x, self.y)
843 }
844
845 pub fn pixels(&self) -> impl Iterator<Item = u8> + 'a {
847 let mask = self.mask;
848 let start = mask.bit_offset(0, self.y);
849 let bits = mask.bits_per_pixel as usize;
850 (0..mask.width as usize).map(move |x| mask.pixel_at(start + x * bits))
851 }
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857 use crate::model::glyph::GlyphSource;
858 use alloc::vec::Vec;
859
860 fn font(data: &[u8]) -> Font {
861 Font::new(data.to_vec(), 0).unwrap()
862 }
863
864 #[test]
865 fn cbdt_strikes() {
866 let font = font(font_test_data::CBDT);
867 let bitmaps = font.bitmap_strikes();
868 assert_eq!(bitmaps.source(), Some(BitmapSource::Cbdt));
869 assert_eq!(bitmaps.len(), 3);
870 let ppems: Vec<_> = bitmaps.iter().map(|strike| strike.ppem()).collect();
871 assert_eq!(ppems, [16, 64, 128]);
872 }
873
874 #[test]
875 fn cbdt_glyph_metrics() {
876 let font = font(font_test_data::CBDT);
877 let bitmaps = font.bitmap_strikes();
878 let zero = bitmaps.get(0).unwrap().get(GlyphId::new(0)).unwrap();
879 assert_eq!(zero.glyph(), GlyphId::new(0));
880 assert_eq!(zero.source(), BitmapSource::Cbdt);
881 assert_eq!((zero.width(), zero.height()), (11, 13));
882 assert_eq!((zero.x_bearing(), zero.y_bearing()), (0, 0));
883 assert_eq!((zero.inner_x_bearing(), zero.inner_y_bearing()), (1, 13));
884 assert_eq!(zero.h_advance(), Some(12));
885 assert_eq!(zero.origin(), Origin::TopLeft);
886 assert_eq!(
887 zero.pixel_extents(),
888 GlyphExtents {
889 x_bearing: 1,
890 y_bearing: 13,
891 width: 11,
892 height: 13,
893 }
894 );
895 let two = bitmaps.get(1).unwrap().get(GlyphId::new(2)).unwrap();
896 assert_eq!((two.width(), two.height()), (39, 52));
897 assert_eq!((two.x_bearing(), two.y_bearing()), (0, 0));
898 assert_eq!((two.inner_x_bearing(), two.inner_y_bearing()), (6, 52));
899 assert_eq!(two.h_advance(), Some(51));
900 assert!(matches!(two.pixels(), Pixels::Png(_)));
901 }
902
903 #[test]
904 fn strike_selection_follows_harfbuzz() {
905 let font = font(font_test_data::CBDT);
906 let bitmaps = font.bitmap_strikes();
907 let strike_for = |ppem| bitmaps.select(ppem).unwrap().ppem();
908 assert_eq!(strike_for(Some(12.0)), 16);
910 assert_eq!(strike_for(Some(16.0)), 16);
911 assert_eq!(strike_for(Some(17.0)), 64);
912 assert_eq!(strike_for(Some(60.0)), 64);
913 assert_eq!(strike_for(Some(200.0)), 128);
915 assert_eq!(strike_for(Some(16.5)), 64);
917 assert_eq!(strike_for(Some(15.5)), 16);
918 assert_eq!(strike_for(None), 128);
920 for ppem in [0.0, -0.0, -12.0, f32::NAN] {
921 assert_eq!(strike_for(Some(ppem)), 128, "{ppem}");
922 }
923 assert_eq!(
924 bitmaps
925 .select_bitmap(GlyphId::new(2), Some(12.0))
926 .unwrap()
927 .ppem_x(),
928 16
929 );
930 assert_eq!(
931 bitmaps
932 .select_bitmap(GlyphId::new(2), Some(0.0))
933 .unwrap()
934 .ppem_x(),
935 128
936 );
937 }
938
939 #[test]
940 fn a_glyph_comes_from_the_best_strike_that_has_it() {
941 let font = font(font_test_data::CBDT);
942 let bitmaps = font.bitmap_strikes();
943 let gid = GlyphId::new(2);
944 for (ppem, expected) in [
945 (Some(12.0), 16),
946 (Some(17.0), 64),
947 (Some(60.0), 64),
948 (None, 128),
949 ] {
950 assert_eq!(
951 bitmaps.select_bitmap(gid, ppem).unwrap().ppem(),
952 expected,
953 "{ppem:?}"
954 );
955 }
956 assert!(closer_to(Some(12.0), 16, 64));
958 assert!(!closer_to(Some(12.0), 64, 16));
959 assert!(closer_to(Some(200.0), 128, 64));
960 assert!(closer_to(None, 128, 16));
961 assert!(!closer_to(None, 16, 128));
962 }
963
964 #[test]
965 fn extents_scale_to_font_units_as_harfbuzz_does() {
966 let font = font(font_test_data::CBDT);
967 let glyph = font
968 .bitmap_strikes()
969 .select_bitmap(GlyphId::new(0), Some(16.0))
970 .unwrap();
971 let extents = glyph.extents();
972 let scale = font.units_per_em() as f32 / 16.0;
973 assert_eq!(extents.x_bearing, (1.0 * scale).round());
974 assert_eq!(extents.y_bearing, (13.0 * scale).round());
975 assert_eq!(extents.width, (11.0 * scale).round());
976 assert_eq!(extents.height, (13.0 * scale).round());
977 }
978
979 #[test]
982 fn a_strike_with_no_size_has_no_glyphs() {
983 use crate::{model::Blob, types::Tag, FontRef};
984 use alloc::sync::Arc;
985 let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(|tag| {
986 let font = FontRef::new(font_test_data::CBDT).ok()?;
987 let mut data = font.table_data(tag)?.as_bytes().to_vec();
988 if tag == Tag::new(b"CBLC") {
989 data[52] = 0;
992 }
993 Some(Blob::from(data))
994 });
995 let font = Font::new(source, 0).unwrap();
996 let bitmaps = font.bitmap_strikes();
997 let strike = bitmaps.get(0).unwrap();
998 assert_eq!(strike.ppem_x(), 0);
999 assert!(strike.get(GlyphId::new(0)).is_none());
1000 let glyph = bitmaps.select_bitmap(GlyphId::new(0), Some(1.0)).unwrap();
1001 assert!(glyph.ppem_x() > 0);
1002 let extents = glyph.extents();
1003 assert!(extents.width.is_finite() && extents.width > 0.0);
1004 }
1005
1006 #[test]
1007 fn sbix_glyphs() {
1008 let font = font(font_test_data::NOTO_HANDWRITING_SBIX);
1009 let bitmaps = font.bitmap_strikes();
1010 assert_eq!(bitmaps.source(), Some(BitmapSource::Sbix));
1011 assert_eq!(bitmaps.len(), 1);
1012 let strike = bitmaps.get(0).unwrap();
1013 assert_eq!(strike.ppem(), 109);
1014 let glyph = strike.get(GlyphId::new(7)).unwrap();
1015 assert_eq!(glyph.glyph(), GlyphId::new(7));
1016 assert_eq!(glyph.source(), BitmapSource::Sbix);
1017 assert_eq!((glyph.x_bearing(), glyph.y_bearing()), (0, 0));
1019 assert_eq!((glyph.inner_x_bearing(), glyph.inner_y_bearing()), (4, -27));
1020 assert_eq!(glyph.origin(), Origin::BottomLeft);
1021 assert!(matches!(glyph.pixels(), Pixels::Png(_)));
1022 assert!(glyph.width() > 0 && glyph.height() > 0);
1023 assert_eq!(glyph.pixel_extents().y_bearing, glyph.height() as i32 - 27);
1025 assert_eq!(glyph.h_advance(), None);
1026 }
1027
1028 #[test]
1029 fn sbix_bearings_come_from_the_outline_the_image_stands_in_for() {
1030 let font = font(font_test_data::NOTO_HANDWRITING_SBIX);
1031 assert_eq!(sbix_bearings(&font, GlyphId::new(0)), (51, -250));
1034 assert_eq!(sbix_bearings(&font, GlyphId::new(7)), (0, 0));
1036 }
1037
1038 #[test]
1039 fn ebdt_masks() {
1040 let font = font(font_test_data::EMBEDDED_BITMAPS);
1041 let bitmaps = font.bitmap_strikes_with(GlyphSourceOrder::new(&[GlyphSource::Ebdt]));
1042 assert_eq!(bitmaps.source(), Some(BitmapSource::Ebdt));
1043 let strike = bitmaps.get(0).unwrap();
1044 assert_eq!((strike.ppem_x(), strike.ppem_y()), (7, 7));
1045 let glyph = strike.get(GlyphId::new(1)).unwrap();
1046 assert_eq!(glyph.glyph(), GlyphId::new(1));
1047 assert_eq!(glyph.source(), BitmapSource::Ebdt);
1048 assert_eq!((glyph.width(), glyph.height()), (3, 8));
1049 let Pixels::Mask(mask) = &glyph.pixels() else {
1050 panic!("expected a mask");
1051 };
1052 assert_eq!(mask.bits_per_pixel(), 1);
1053 assert!(mask.is_packed());
1054 assert_eq!(mask.decoded_len(), 24);
1055 let pixels: Vec<_> = mask.pixels().map(|p| (p == 255) as u8).collect();
1057 assert_eq!(
1058 pixels,
1059 [1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0]
1060 );
1061 assert_eq!(mask.rows().count(), 8);
1062 assert_eq!(
1063 mask.rows().nth(1).unwrap().pixels().collect::<Vec<_>>(),
1064 [0, 255, 255]
1065 );
1066 let mut decoded = [0u8; 24];
1067 assert!(mask.decode_into(&mut decoded));
1068 assert_eq!(decoded[..3], [255, 255, 255]);
1069 assert!(!mask.decode_into(&mut [0u8; 23]));
1070 }
1071
1072 #[test]
1073 fn default_order_prefers_cbdt_over_ebdt() {
1074 let font = font(font_test_data::EMBEDDED_BITMAPS);
1075 assert_eq!(font.bitmap_strikes().source(), Some(BitmapSource::Cbdt));
1076 assert_eq!(
1077 font.bitmap_strikes_with(GlyphSourceOrder::OUTLINES)
1078 .source(),
1079 None
1080 );
1081 }
1082
1083 #[test]
1086 fn a_font_with_only_ebdt_has_bitmaps() {
1087 use crate::{model::Blob, types::Tag, FontRef};
1088 use alloc::sync::Arc;
1089 let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(|tag| {
1090 if tag == Tag::new(b"CBDT") || tag == Tag::new(b"CBLC") {
1091 return None;
1092 }
1093 let font = FontRef::new(font_test_data::EMBEDDED_BITMAPS).ok()?;
1094 Some(Blob::from(font.table_data(tag)?.as_bytes().to_vec()))
1095 });
1096 let font = Font::new(source, 0).unwrap();
1097 let bitmaps = font.bitmap_strikes();
1098 assert_eq!(bitmaps.source(), Some(BitmapSource::Ebdt));
1099 assert!(!bitmaps.is_empty());
1100 assert!(!GlyphSourceOrder::DEFAULT.contains(GlyphSource::Ebdt));
1101 }
1102
1103 #[test]
1104 fn unpacked_mask_pixels() {
1105 let mask = Mask::new(&[0xA0, 0x50], 4, 2, 1, false).unwrap();
1107 let pixels: Vec<_> = mask.pixels().collect();
1108 assert_eq!(pixels, [255, 0, 255, 0, 0, 255, 0, 255]);
1109 assert_eq!(mask.pixel(3, 1), Some(255));
1110 assert_eq!(mask.pixel(4, 1), None);
1111 let mask = Mask::new(&[0xF8, 0x40, 0x05, 0xA0], 3, 2, 4, false).unwrap();
1113 let pixels: Vec<_> = mask.pixels().collect();
1114 assert_eq!(pixels, [255, 136, 68, 0, 85, 170]);
1115 let mask = Mask::new(&[0xE4], 4, 1, 2, false).unwrap();
1117 assert_eq!(mask.pixels().collect::<Vec<_>>(), [255, 170, 85, 0]);
1118 }
1119
1120 #[test]
1121 fn packed_mask_pixels() {
1122 let mask = Mask::new(&[0xAB, 0x00], 3, 3, 1, true).unwrap();
1124 let pixels: Vec<_> = mask.pixels().collect();
1125 assert_eq!(pixels, [255, 0, 255, 0, 255, 0, 255, 255, 0]);
1126 let mask = Mask::new(&[0xE4, 0xC6, 0xC0], 5, 2, 2, true).unwrap();
1128 assert_eq!(
1129 mask.pixels().collect::<Vec<_>>(),
1130 [255, 170, 85, 0, 255, 0, 85, 170, 255, 0]
1131 );
1132 let mask = Mask::new(&[0xF0, 0x84, 0xA5], 3, 2, 4, true).unwrap();
1134 assert_eq!(
1135 mask.pixels().collect::<Vec<_>>(),
1136 [255, 0, 136, 68, 170, 85]
1137 );
1138 let mask = Mask::new(&[100, 200, 50, 0, 128, 255], 3, 2, 8, true).unwrap();
1140 let mut out = [0u8; 6];
1141 assert!(mask.decode_into(&mut out));
1142 assert_eq!(out, [100, 200, 50, 0, 128, 255]);
1143 }
1144
1145 #[test]
1146 fn short_or_odd_masks_are_refused() {
1147 assert!(Mask::new(&[0xFF], 3, 3, 1, true).is_none());
1148 assert!(Mask::new(&[1, 2, 3], 4, 2, 8, false).is_none());
1149 assert!(Mask::new(&[0; 16], 2, 2, 3, false).is_none());
1150 let empty = Mask::new(&[], 0, 5, 8, false).unwrap();
1151 assert!(empty.is_empty());
1152 assert_eq!(empty.pixels().count(), 0);
1153 }
1154
1155 #[test]
1156 fn bgra_rows_and_pixels() {
1157 let data: Vec<u8> = (0..16).collect();
1158 let bgra = Bgra::new(&data, 2, 2).unwrap();
1159 assert_eq!(bgra.rows().count(), 2);
1160 assert_eq!(bgra.pixel(1, 1), Some([12, 13, 14, 15]));
1161 assert_eq!(bgra.pixel(2, 0), None);
1162 assert!(Bgra::new(&data, 3, 2).is_none());
1163 }
1164}