1use std::collections::HashMap;
4
5use write_fonts::{
6 BuilderError, FontBuilder,
7 tables::{
8 self as wtables, gdef::GlyphClassDef, layout::FeatureParams, maxp::Maxp, stat::AxisValue,
9 },
10 types::{GlyphId16, NameId},
11};
12
13use super::Opts;
14
15use crate::GlyphMap;
16
17pub struct Compilation {
29 pub(crate) opts: Opts,
31 pub head: Option<wtables::head::Head>,
33 pub hhea: Option<wtables::hhea::Hhea>,
35 pub vhea: Option<wtables::vhea::Vhea>,
37 pub os2: Option<wtables::os2::Os2>,
39 pub gdef: Option<wtables::gdef::Gdef>,
41 pub base: Option<wtables::base::Base>,
43 pub name: Option<wtables::name::Name>,
45 pub stat: Option<wtables::stat::Stat>,
47 pub gsub: Option<wtables::gsub::Gsub>,
49 pub gpos: Option<wtables::gpos::Gpos>,
51 pub gdef_classes: Option<HashMap<GlyphId16, GlyphClassDef>>,
56}
57
58impl Compilation {
59 pub fn has_non_layout_tables(&self) -> bool {
61 self.head.is_some()
62 || self.hhea.is_some()
63 || self.vhea.is_some()
64 || self.os2.is_some()
65 || self.base.is_some()
66 || self.name.is_some()
67 || self.stat.is_some()
68 }
69
70 pub fn remap_name_ids(&mut self, first_avail_id: u16) {
79 let id_offset = first_avail_id.saturating_sub(NameId::LAST_RESERVED_NAME_ID.to_u16() + 1);
80 log::info!("remapping FEA name ideas with delta {id_offset}");
81 if id_offset == 0 {
82 return;
83 }
84
85 let adjust_id = |id: NameId| {
86 if !id.is_reserved() {
87 id.checked_add(id_offset)
90 .unwrap_or(NameId::LAST_ALLOWED_NAME_ID)
91 } else {
92 id
93 }
94 };
95
96 if let Some(name) = self.name.as_mut() {
97 let records = std::mem::take(&mut name.name_record);
98 name.name_record = records
99 .into_iter()
100 .map(|mut rec| {
101 rec.name_id = adjust_id(rec.name_id);
102 rec
103 })
104 .collect();
105 }
106
107 if let Some(gsub) = self.gsub.as_mut() {
108 gsub.feature_list
109 .as_mut()
110 .feature_records
111 .iter_mut()
112 .for_each(|rec| match rec.feature.as_mut().feature_params.as_mut() {
113 Some(FeatureParams::StylisticSet(params)) => {
114 params.ui_name_id = adjust_id(params.ui_name_id);
115 }
116 Some(FeatureParams::CharacterVariant(params)) => {
117 params.feat_ui_label_name_id = adjust_id(params.feat_ui_label_name_id);
118 params.feat_ui_tooltip_text_name_id =
119 adjust_id(params.feat_ui_tooltip_text_name_id);
120 params.sample_text_name_id = adjust_id(params.sample_text_name_id);
121 params.first_param_ui_label_name_id =
122 adjust_id(params.first_param_ui_label_name_id);
123 }
124 _ => (),
125 });
126 }
127 if let Some(stat) = self.stat.as_mut() {
128 stat.elided_fallback_name_id = stat
129 .elided_fallback_name_id
130 .map(|id| id.to_u16().saturating_add(id_offset).into());
131 stat.design_axes.iter_mut().for_each(|axe| {
132 axe.axis_name_id = adjust_id(axe.axis_name_id);
133 });
134 if let Some(blah) = stat.offset_to_axis_values.as_mut() {
135 blah.iter_mut().for_each(|val| match val.as_mut() {
136 AxisValue::Format1(val) => val.value_name_id = adjust_id(val.value_name_id),
137 AxisValue::Format2(val) => val.value_name_id = adjust_id(val.value_name_id),
138 AxisValue::Format3(val) => val.value_name_id = adjust_id(val.value_name_id),
139 AxisValue::Format4(val) => val.value_name_id = adjust_id(val.value_name_id),
140 });
141 }
142 }
143 }
144
145 pub fn to_font_builder(&self) -> Result<FontBuilder<'_>, BuilderError> {
153 let mut builder = FontBuilder::default();
154 macro_rules! add_if_some {
155 ($table:expr_2021) => {
156 if let Some(table) = $table.as_ref() {
157 builder.add_table(table)?;
158 }
159 };
160 }
161 add_if_some!(self.head);
162 add_if_some!(self.hhea);
163 add_if_some!(self.vhea);
164 add_if_some!(self.os2);
165 add_if_some!(self.gdef);
166 add_if_some!(self.base);
167 add_if_some!(self.name);
168 add_if_some!(self.stat);
169 add_if_some!(self.gsub);
170 add_if_some!(self.gpos);
171 Ok(builder)
172 }
173
174 pub fn to_binary(&self, glyph_map: &GlyphMap) -> Result<Vec<u8>, BuilderError> {
180 let mut builder = self.to_font_builder()?;
183 let maxp = Maxp::new(glyph_map.len().try_into().unwrap());
184 builder.add_table(&maxp)?;
185 if self.opts.make_post_table {
186 let post = glyph_map.make_post_table();
187 builder.add_table(&post)?;
188 }
189
190 Ok(builder.build())
191 }
192}