simple_pcf/
lib.rs

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