simple_pcf/lib.rs
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
#![no_std]
#![doc = include_str!("../README.md")]
/**
A PCF font
Use [parse](Pcf::parse) to create one, [get_glyph_pixels](Pcf::get_glyph_pixels) to get a glyph by index,
and [iter_unicode_entries](Pcf::iter_unicode_entries) to get a glyph index by unicode character.
PCF stores ASCII glyphs at the corresponding index, so you can retrieve ASCII characters without calling
[iter_unicode_entries](Pcf::iter_unicode_entries) by giving the ASCII number to [get_glyph_pixels](Pcf::get_glyph_pixels) directly.
*/
pub struct Pcf<'a> {
pub glyph_byte_length: usize,
pub glyph_width: usize,
pub glyph_height: usize,
pub glyphs: &'a [u8],
pub unicode_table: Option<&'a [u8]>,
}
impl<'a> Pcf<'a> {
/**
Parse a PCF font from some bytes
See [ParseError] for possible errors.
PCF fonts may optionally have a unicode table indicating which glyphs correspond to which
unicode characters. This function does not validate the structure of the unicode table.
Errors in the encoding of the unicode table, if they can be detected, are reported by the
iterator returned from the [iter_unicode_entries](Pcf::iter_unicode_entries) function.
*/
pub fn parse(data: &[u8]) -> Result<Pcf, ParseError> {
use ParseError::*;
if data.len() < 32 {
return Err(HeaderMissing);
}
if &data[0..4] != &[0x72, 0xb5, 0x4a, 0x86] {
return Err(InvalidMagicBytes);
}
let version = u32::from_le_bytes(data[4..8].try_into().unwrap());
if 0 != version {
return Err(UnknownVersion(version));
}
let header_size = u32::from_le_bytes(data[8..12].try_into().unwrap()) as usize;
let flags = u32::from_le_bytes(data[12..16].try_into().unwrap());
let has_unicode_table = 1 == (flags & 0x00000001);
let glyph_count = u32::from_le_bytes(data[16..20].try_into().unwrap()) as usize;
let glyph_byte_length = u32::from_le_bytes(data[20..24].try_into().unwrap()) as usize;
let glyph_height = u32::from_le_bytes(data[24..28].try_into().unwrap()) as usize;
let glyph_width = u32::from_le_bytes(data[28..32].try_into().unwrap()) as usize;
let expected_byte_count = header_size + (glyph_count * glyph_byte_length);
if data.len() < expected_byte_count {
return Err(GlyphTableTruncated { expected_byte_count: expected_byte_count });
}
let glyphs = &data[header_size..][..glyph_byte_length * glyph_count];
let unicode_table =
if has_unicode_table {
Some(&data[expected_byte_count..])
} else {
None
};
let pcf = Pcf {
glyph_byte_length: glyph_byte_length,
glyph_width: glyph_width,
glyph_height: glyph_height,
glyphs,
unicode_table,
};
Ok(pcf)
}
/// Get the bits corresponding to the glyph's bitmap
///
/// PCF stores the bitmap as packed bits. Each byte in the slice contains
/// eight pixels, with the last byte of each row containing padding bits
/// so that the next row starts on a byte boundary.
///
/// You might be looking for [get_glyph_pixels](Pcf::get_glyph_pixels) instead, which unpacks
/// the bits for you.
pub fn get_glyph_bits(&self, glyph_index: usize) -> Option<&[u8]> {
let start = self.glyph_byte_length * glyph_index;
let end = start + self.glyph_byte_length;
self.glyphs.get(start..end)
}
/**
Get the pixels that correspond to the given glyph's bitmap
PCF stores bitmaps in the packed bits of each byte.
This iterator unpacks the bits, returning a boolean for each pixel in the bitmap indicating
whether that pixel is lit or not.
PCF stores ASCII glyphs in the corresponding index, so you can retrieve ASCII glyphs directly
by calling `get_glyph_pixels(b'a' as usize)`. Unicode characters must be looked up with
(iter_unicode_entries)[Pcf::iter_unicode_entries].
*/
pub fn get_glyph_pixels<'b>(&'b self, glyph_index: usize) -> Option<impl Iterator<Item=bool> + 'b> {
let glyph_bits = self.get_glyph_bits(glyph_index)?;
let bytes_per_row = (self.glyph_width / 8) + 1;
let iterator =
(0..self.glyph_height).flat_map(move |y| {
(0..self.glyph_width).map(move |x| {
let byte_index = (x / 8) + (y * bytes_per_row);
let byte = glyph_bits[byte_index];
let bit_offset = 7 - (x % 8);
let bit = (byte >> bit_offset) & 1;
bit == 1
})
});
Some(iterator)
}
/**
Iterate over the entries in the unicode table
PCF fonts may optionally include a unicode table indicating which unicode character each glyph corresponds to.
If such a table exists, this function returns an iterator that will yield each unicode entry along with the
index of the glyph that represents to it.
In the case of a malformed unicode table, the behavior of this iterator is unpredictable
because errors can not always be unambiguously detected. The iterator may return a unicode entry with
a string containing multiple graphemes that ought to each have their own glyph, it may return None,
or it may return a [core::str::Utf8Error].
If you are using the standard library, this iterator can be used to construct a HashMap lookup table of unicode
character to glyph index pairs.
```rust(ignore)
let glyph_lookup_table = pcf.iter_unicode_entries().unwrap()
.filter_map(|(index, result)| result.ok().map(|character| (character, index)))
.collect::<std::collections::HashMap<&str, usize>>();
```
*/
pub fn iter_unicode_entries<'b>(&'b self) -> Option<impl Iterator<Item=(usize, Result<&'b str, core::str::Utf8Error>)> + 'b> {
let table = self.unicode_table?;
let iterator = table.split(|&x| x == 0xff).enumerate().flat_map(move |(glyph_index, unicode_entries)| {
unicode_entries.split(|&x| x == 0xfe).map(move |unicode_string| {
(glyph_index, core::str::from_utf8(unicode_string))
})
});
Some(iterator)
}
}
/// Possible errors returned by the [Pcf::parse] function.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ParseError {
/// The provided buffer is not large enough to contain the PCF header.
HeaderMissing,
/// The PCF header does not contain 72 b5 4a 86 as its first four bytes.
InvalidMagicBytes,
/// The PCF header contains a version other than 0.
/// (0 is the only version that exists as of writing)
UnknownVersion(u32),
/// The provided buffer is not large enough to contain all of the glyphs
/// that the PCF header indicated that it should.
GlyphTableTruncated { expected_byte_count: usize, },
}