1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! This crate exposes a [HarfBuzz](https://github.com/harfbuzz/harfbuzz) API for subsetting a font.
//!
//! From HarfBuzz documentation:
//! > Subsetting reduces the codepoint coverage of font files and removes all data that is no longer needed. A subset
//! > input describes the desired subset. The input is provided along with a font to the subsetting operation. Output is
//! > a new font file containing only the data specified in the input.
//! >
//! > Currently most outline and bitmap tables are supported: glyf, CFF, CFF2, sbix, COLR, and CBDT/CBLC. This also
//! > includes fonts with variable outlines via OpenType variations. Notably EBDT/EBLC and SVG are not supported. Layout
//! > subsetting is supported only for OpenType Layout tables (GSUB, GPOS, GDEF). Notably subsetting of graphite or AAT
//! > tables is not yet supported.
//! >
//! > Fonts with graphite or AAT tables may still be subsetted but will likely need to use the retain glyph ids option
//! > and configure the subset to pass through the layout tables untouched.
//!
//! # Usage
//! The simplest way to construct a subset of a font is to use [`subset`] function:
//! ```no_run
//! # use std::fs;
//! let font = fs::read("tests/fonts/NotoSans.ttf").unwrap();
//! let subset_font = hb_subset::subset(&font, "abc".chars()).unwrap();
//! fs::write("fonts/subset.ttf", subset_font).unwrap();
//! ```
//!
//! To get more control over how the font is subset and what gets included, you can use the lower level API directly:
//! ```rust
//! # use hb_subset::bindings::*;
//! // Load font directly from a file
//! let font = Blob::from_file("tests/fonts/NotoSans.ttf").unwrap();
//! let font = FontFace::new(font).unwrap();
//!
//! // Construct a subset manually and include only some of the letters
//! let mut subset = SubsetInput::new().unwrap();
//! subset.unicode_set().insert('f');
//! subset.unicode_set().insert('i');
//!
//! // Subset the font using just-constructed subset input
//! let new_font = subset.subset_font(&font).unwrap();
//!
//! // Extract the raw font and write to an output file
//! std::fs::write("out.ttf", &*new_font.underlying_blob()).unwrap();
//! ```

#![warn(missing_docs)]

use bindings::{Blob, FontFace, SubsetInput};
use thiserror::Error;

pub mod sys;

/// An enumeration over possible errors.
#[derive(Debug, Error)]
pub enum Error {
    /// An error returned when an allocation fails.
    #[error("Failed to allocate object")]
    AllocationError,
    #[error("Failed to subset font face")]
    /// An error returned when font face could not be subset.
    SubsetError,
    /// An error returned when a font face could not be extracted from blob.
    #[error("Failed to extract font face from blob")]
    FontFaceExtractionError,
}

pub mod bindings;

/// A convenient method to create a subset of a font over given characters.
///
/// The returned font can be used everywhere where the original font was used, as long as the string contains only
/// characters from the given set. In particular, the font includes all relevant ligatures.
pub fn subset(font: &[u8], characters: impl IntoIterator<Item = char>) -> Result<Vec<u8>, Error> {
    // Add all characters to subset, and nothing more.
    let mut subset = SubsetInput::new()?;
    let mut unicode_set = subset.unicode_set();
    for char in characters {
        unicode_set.insert(char);
    }

    // Load the original font, and then construct a subset from it
    let font = FontFace::new(Blob::from_bytes(font)?)?;
    let new_font = subset.subset_font(&font)?;
    let new_font = new_font.underlying_blob().to_vec();
    Ok(new_font)
}