1use std::marker::PhantomData;
2
3use crate::{sys, AllocationError, CharSet, FontFace, Map, Set, SubsettingError, TagSet, U32Set};
4
5mod flags;
6
7pub use flags::*;
8
9pub struct SubsetInput(*mut sys::hb_subset_input_t);
23
24impl SubsetInput {
25 #[doc(alias = "hb_subset_input_create_or_fail")]
27 pub fn new() -> Result<Self, AllocationError> {
28 let input = unsafe { sys::hb_subset_input_create_or_fail() };
29 if input.is_null() {
30 return Err(AllocationError);
31 }
32 Ok(Self(input))
33 }
34
35 #[doc(alias = "hb_subset_input_keep_everything")]
40 pub fn keep_everything(&mut self) {
41 unsafe { sys::hb_subset_input_keep_everything(self.as_raw()) }
42 }
43
44 #[doc(alias = "hb_subset_input_set_flags")]
60 #[doc(alias = "hb_subset_input_get_flags")]
61 pub fn flags(&mut self) -> FlagRef<'_> {
62 FlagRef(
63 self,
64 Flags(unsafe { sys::hb_subset_input_get_flags(self.as_raw()) }),
65 )
66 }
67
68 #[doc(alias = "hb_subset_input_glyph_set")]
72 #[doc(alias = "hb_subset_input_set")]
73 #[doc(alias = "HB_SUBSET_SETS_GLYPH_INDEX")]
74 pub fn glyph_set(&mut self) -> U32Set<'_> {
75 unsafe {
76 Set::from_raw(sys::hb_set_reference(sys::hb_subset_input_glyph_set(
77 self.as_raw(),
78 )))
79 }
80 }
81
82 #[doc(alias = "hb_subset_input_unicode_set")]
86 #[doc(alias = "hb_subset_input_set")]
87 #[doc(alias = "HB_SUBSET_SETS_UNICODE")]
88 pub fn unicode_set(&mut self) -> CharSet<'_> {
89 unsafe {
90 Set::from_raw(sys::hb_set_reference(sys::hb_subset_input_unicode_set(
91 self.as_raw(),
92 )))
93 }
94 }
95
96 #[doc(alias = "hb_subset_input_set")]
100 #[doc(alias = "HB_SUBSET_SETS_NO_SUBSET_TABLE_TAG")]
101 pub fn no_subset_table_tag_set(&mut self) -> TagSet<'_> {
102 unsafe {
103 Set::from_raw(sys::hb_set_reference(sys::hb_subset_input_set(
104 self.as_raw(),
105 sys::hb_subset_sets_t::NO_SUBSET_TABLE_TAG,
106 )))
107 }
108 }
109
110 #[doc(alias = "hb_subset_input_set")]
114 #[doc(alias = "HB_SUBSET_SETS_DROP_TABLE_TAG")]
115 pub fn drop_table_tag_set(&mut self) -> TagSet<'_> {
116 unsafe {
117 Set::from_raw(sys::hb_set_reference(sys::hb_subset_input_set(
118 self.as_raw(),
119 sys::hb_subset_sets_t::DROP_TABLE_TAG,
120 )))
121 }
122 }
123
124 #[doc(alias = "hb_subset_input_set")]
128 #[doc(alias = "HB_SUBSET_SETS_NAME_ID")]
129 pub fn name_id_set(&mut self) -> U32Set<'_> {
130 unsafe {
131 Set::from_raw(sys::hb_set_reference(sys::hb_subset_input_set(
132 self.as_raw(),
133 sys::hb_subset_sets_t::NAME_ID,
134 )))
135 }
136 }
137
138 #[doc(alias = "hb_subset_input_set")]
142 #[doc(alias = "HB_SUBSET_SETS_NAME_LANG_ID")]
143 pub fn name_lang_id_set(&mut self) -> U32Set<'_> {
144 unsafe {
145 Set::from_raw(sys::hb_set_reference(sys::hb_subset_input_set(
146 self.as_raw(),
147 sys::hb_subset_sets_t::NAME_LANG_ID,
148 )))
149 }
150 }
151
152 #[doc(alias = "hb_subset_input_set")]
156 #[doc(alias = "HB_SUBSET_SETS_LAYOUT_FEATURE_TAG")]
157 pub fn layout_feature_tag_set(&mut self) -> TagSet<'_> {
158 unsafe {
159 Set::from_raw(sys::hb_set_reference(sys::hb_subset_input_set(
160 self.as_raw(),
161 sys::hb_subset_sets_t::LAYOUT_FEATURE_TAG,
162 )))
163 }
164 }
165
166 #[doc(alias = "hb_subset_input_set")]
170 #[doc(alias = "HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG")]
171 pub fn layout_script_tag_set(&mut self) -> TagSet<'_> {
172 unsafe {
173 Set::from_raw(sys::hb_set_reference(sys::hb_subset_input_set(
174 self.as_raw(),
175 sys::hb_subset_sets_t::LAYOUT_SCRIPT_TAG,
176 )))
177 }
178 }
179
180 #[doc(alias = "hb_subset_input_old_to_new_glyph_mapping")]
193 pub fn old_to_new_glyph_mapping(&mut self) -> Map<'_, u32, u32> {
194 unsafe {
195 Map::from_raw(sys::hb_map_reference(
196 sys::hb_subset_input_old_to_new_glyph_mapping(self.as_raw()),
197 ))
198 }
199 }
200
201 #[doc(alias = "hb_subset_or_fail")]
203 pub fn subset_font(&self, font: &FontFace<'_>) -> Result<FontFace<'static>, SubsettingError> {
204 let face = unsafe { sys::hb_subset_or_fail(font.as_raw(), self.as_raw()) };
205 if face.is_null() {
206 return Err(SubsettingError);
207 }
208 Ok(unsafe { FontFace::from_raw(face) })
209 }
210
211 #[doc(alias = "hb_subset_plan_create_or_fail")]
215 pub fn plan<'f>(&self, font: &'f FontFace<'_>) -> Result<SubsetPlan<'f, '_>, SubsettingError> {
216 let plan = unsafe { sys::hb_subset_plan_create_or_fail(font.as_raw(), self.as_raw()) };
217 if plan.is_null() {
218 return Err(SubsettingError);
219 }
220 Ok(unsafe { SubsetPlan::from_raw(plan) })
221 }
222}
223
224impl SubsetInput {
225 pub fn into_raw(self) -> *mut sys::hb_subset_input_t {
231 let ptr = self.0;
232 std::mem::forget(self);
233 ptr
234 }
235
236 pub fn as_raw(&self) -> *mut sys::hb_subset_input_t {
240 self.0
241 }
242
243 pub unsafe fn from_raw(subset: *mut sys::hb_subset_input_t) -> Self {
249 Self(subset)
250 }
251}
252
253impl Drop for SubsetInput {
254 #[doc(alias = "hb_subset_input_destroy")]
255 fn drop(&mut self) {
256 unsafe { sys::hb_subset_input_destroy(self.0) }
257 }
258}
259
260pub struct SubsetPlan<'f, 'b> {
264 plan: *mut sys::hb_subset_plan_t,
265 unicode_to_old_glyph_mapping: Map<'static, char, u32>,
267 new_to_old_glyph_mapping: Map<'static, u32, u32>,
268 old_to_new_glyph_mapping: Map<'static, u32, u32>,
269 _font: PhantomData<&'f FontFace<'b>>,
270}
271
272impl<'f, 'b> SubsetPlan<'f, 'b> {
273 #[doc(alias = "hb_subset_plan_execute_or_fail")]
275 pub fn subset(&self) -> Result<FontFace<'b>, SubsettingError> {
276 let font = unsafe { sys::hb_subset_plan_execute_or_fail(self.as_raw()) };
277 if font.is_null() {
278 return Err(SubsettingError);
279 }
280 Ok(unsafe { FontFace::from_raw(font) })
281 }
282
283 #[doc(alias = "hb_subset_plan_unicode_to_old_glyph_mapping")]
285 pub fn unicode_to_old_glyph_mapping(&self) -> &'_ Map<'_, char, u32> {
286 &self.unicode_to_old_glyph_mapping
287 }
288
289 #[doc(alias = "hb_subset_plan_new_to_old_glyph_mapping")]
291 pub fn new_to_old_glyph_mapping(&self) -> &'_ Map<'_, u32, u32> {
292 &self.new_to_old_glyph_mapping
293 }
294
295 #[doc(alias = "hb_subset_plan_old_to_new_glyph_mapping")]
297 pub fn old_to_new_glyph_mapping(&self) -> &'_ Map<'_, u32, u32> {
298 &self.old_to_new_glyph_mapping
299 }
300}
301
302impl<'f, 'b> SubsetPlan<'f, 'b> {
303 pub fn into_raw(self) -> *mut sys::hb_subset_plan_t {
309 let ptr = self.plan;
310 std::mem::forget(self);
311 ptr
312 }
313
314 pub fn as_raw(&self) -> *mut sys::hb_subset_plan_t {
318 self.plan
319 }
320
321 pub unsafe fn from_raw(plan: *mut sys::hb_subset_plan_t) -> Self {
327 let unicode_to_old_glyph_mapping = unsafe {
328 Map::from_raw(sys::hb_map_reference(
329 sys::hb_subset_plan_unicode_to_old_glyph_mapping(plan),
330 ))
331 };
332 let new_to_old_glyph_mapping = unsafe {
333 Map::from_raw(sys::hb_map_reference(
334 sys::hb_subset_plan_new_to_old_glyph_mapping(plan),
335 ))
336 };
337 let old_to_new_glyph_mapping = unsafe {
338 Map::from_raw(sys::hb_map_reference(
339 sys::hb_subset_plan_old_to_new_glyph_mapping(plan),
340 ))
341 };
342
343 Self {
344 plan,
345 unicode_to_old_glyph_mapping,
346 new_to_old_glyph_mapping,
347 old_to_new_glyph_mapping,
348 _font: PhantomData,
349 }
350 }
351}
352
353impl<'f, 'b> Drop for SubsetPlan<'f, 'b> {
354 #[doc(alias = "hb_subset_plan_destroy")]
355 fn drop(&mut self) {
356 unsafe { sys::hb_subset_plan_destroy(self.plan) }
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363 use crate::{tests::NOTO_SANS, Blob};
364
365 #[test]
366 fn keep_everything_should_keep_all_codepoints_and_glyphs() {
367 let mut subset = SubsetInput::new().unwrap();
368 subset.keep_everything();
369 assert_eq!(subset.unicode_set().len(), u32::MAX as usize);
370 assert_eq!(subset.glyph_set().len(), u32::MAX as usize);
371 let orig = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
372 let new = subset.subset_font(&orig).unwrap();
373 assert_eq!(
374 orig.covered_codepoints().unwrap().len(),
375 new.covered_codepoints().unwrap().len()
376 );
377 assert_eq!(orig.glyph_count(), new.glyph_count());
378 }
379
380 #[test]
381 fn keeping_codepoints_should_keep_ligatures() {
382 let font = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
383 let mut subset = SubsetInput::new().unwrap();
384 subset.unicode_set().insert('f');
385 subset.unicode_set().insert('i');
386 let font = subset.subset_font(&font).unwrap();
387 assert_eq!(font.covered_codepoints().unwrap().len(), 2);
388 assert_eq!(font.glyph_count(), 6); }
391
392 #[test]
393 fn old_to_new_glyph_mapping() {
394 let font = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
395 let char_to_glyph = font.nominal_glyph_mapping().unwrap();
396
397 let mut subset = SubsetInput::new().unwrap();
399 subset
400 .old_to_new_glyph_mapping()
401 .insert(char_to_glyph.get('a').unwrap(), 5);
402 subset
403 .old_to_new_glyph_mapping()
404 .insert(char_to_glyph.get('b').unwrap(), 709);
405 subset.unicode_set().insert('a');
406 subset.unicode_set().insert('b');
407
408 let font = subset.subset_font(&font).unwrap();
409 assert_eq!(font.glyph_count(), 710);
411
412 let char_to_glyph = font.nominal_glyph_mapping().unwrap();
413 assert_eq!(char_to_glyph.get('a').unwrap(), 5);
415 assert_eq!(char_to_glyph.get('b').unwrap(), 709);
416 }
417
418 #[test]
419 fn convert_subset_into_raw_and_back() {
420 let subset = SubsetInput::new().unwrap();
421 let subset_ptr = subset.into_raw();
422 let subset = unsafe { SubsetInput::from_raw(subset_ptr) };
423 drop(subset);
424 }
425
426 #[test]
427 fn convert_plan_into_raw_and_back() {
428 let font = FontFace::new(Blob::from_file(NOTO_SANS).unwrap()).unwrap();
429 let subset = SubsetInput::new().unwrap();
430 let plan = subset.plan(&font).unwrap();
431 let plan_ptr = plan.into_raw();
432 let plan = unsafe { SubsetPlan::from_raw(plan_ptr) };
433 drop(plan);
434 }
435}