write_fonts/tables/
loca.rs1use read_fonts::TopLevelTable;
6use types::Tag;
7
8use crate::{
9 validate::{Validate, ValidationCtx},
10 FontWrite,
11};
12
13#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct Loca {
19 pub(crate) offsets: Vec<u32>,
21 loca_format: LocaFormat,
22}
23
24#[repr(u8)]
32#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub enum LocaFormat {
35 #[default]
36 Short = 0,
37 Long = 1,
38}
39
40impl TopLevelTable for Loca {
41 const TAG: Tag = Tag::new(b"loca");
42}
43
44impl Loca {
45 pub fn new(offsets: Vec<u32>) -> Self {
53 let loca_format = LocaFormat::new(&offsets);
54
55 Loca {
56 offsets,
57 loca_format,
58 }
59 }
60
61 pub fn format(&self) -> LocaFormat {
62 self.loca_format
63 }
64}
65
66impl LocaFormat {
67 fn new(loca: &[u32]) -> LocaFormat {
68 const MAX_SHORT_LOCA_VALUE: u32 = 0x20000;
70 if loca.last().copied().unwrap_or_default() < MAX_SHORT_LOCA_VALUE
71 && loca.iter().all(|offset| offset % 2 == 0)
72 {
73 LocaFormat::Short
74 } else {
75 LocaFormat::Long
76 }
77 }
78}
79
80impl FontWrite for Loca {
81 fn write_into(&self, writer: &mut crate::TableWriter) {
82 match self.loca_format {
83 LocaFormat::Long => self.offsets.write_into(writer),
84 LocaFormat::Short => self
85 .offsets
86 .iter()
87 .for_each(|off| ((off >> 1) as u16).write_into(writer)),
88 }
89 }
90}
91
92impl Validate for Loca {
93 fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn no_glyphs_is_short() {
102 assert_eq!(LocaFormat::Short, LocaFormat::new(&Vec::new()));
103 }
104
105 #[test]
106 fn some_glyphs_is_short() {
107 assert_eq!(LocaFormat::Short, LocaFormat::new(&[24, 48, 112]));
108 }
109
110 #[test]
111 fn unpadded_glyphs_is_long() {
112 assert_eq!(LocaFormat::Long, LocaFormat::new(&[24, 7, 112]));
113 }
114
115 #[test]
116 fn big_glyphs_is_long() {
117 assert_eq!(
118 LocaFormat::Long,
119 LocaFormat::new(&(0..=32).map(|i| i * 0x1000).collect::<Vec<_>>())
120 );
121 }
122}