Skip to content

Commit

Permalink
Add Face::glyph_index_by_name
Browse files Browse the repository at this point in the history
Many fixes and changes to the `post` table.
  • Loading branch information
RazrFalcon committed Sep 18, 2022
1 parent e655839 commit 5f6b89e
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 53 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `cff::Table::glyph_width`
- `cff::Table::number_of_glyphs`
- `cff::Table::matrix`
- `post::Table::glyph_name`
- `post::Table::glyph_index_by_name`
- `post::Table::names`
- `Face::glyph_index_by_name`

### Changed
- `post::Table::names` is a method and not a field now.
- Use `post::Table::glyph_name` instead of `post::Table::names.get()`.

### Fixed
- (hmtx/vmtx) Allow missing additional side bearings.
- (loca) Allow incomplete table.
- Reduce strictness of some table length checks.
- (post) `post::Names::len` was returning a wrong value. Now this method is gone completely.
You can use `post::Table::names().count()` instead.

## [0.15.2] - 2022-06-17
### Fixed
Expand Down
21 changes: 20 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,25 @@ impl<'a> Face<'a> {
None
}

/// Resolves a Glyph ID for a glyph name.
///
/// Uses the `post` and `CFF` tables as sources.
///
/// Returns `None` when no name is associated with a `glyph`.
#[cfg(feature = "glyph-names")]
#[inline]
pub fn glyph_index_by_name(&self, name: &str) -> Option<GlyphId> {
if let Some(name) = self.tables.post.and_then(|post| post.glyph_index_by_name(name)) {
return Some(name);
}

if let Some(name) = self.tables.cff.as_ref().and_then(|cff| cff.glyph_index_by_name(name)) {
return Some(name);
}

None
}

/// Resolves a variation of a Glyph ID from two code points.
///
/// Implemented according to
Expand Down Expand Up @@ -1510,7 +1529,7 @@ impl<'a> Face<'a> {
#[cfg(feature = "glyph-names")]
#[inline]
pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&str> {
if let Some(name) = self.tables.post.and_then(|post| post.names.get(glyph_id)) {
if let Some(name) = self.tables.post.and_then(|post| post.glyph_name(glyph_id)) {
return Some(name);
}

Expand Down
126 changes: 74 additions & 52 deletions src/tables/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,63 +275,44 @@ const MACINTOSH_NAMES: &[&str] = &[
];


/// A list of glyph names.
/// An iterator over glyph names.
///
/// The `post` table doesn't provide the glyph names count,
/// so we have to simply iterate over all of them to find it out.
#[derive(Clone, Copy, Default)]
pub struct Names<'a> {
indexes: LazyArray16<'a, u16>,
data: &'a [u8],
offset: usize,
}

// TODO: add low-level iterator
impl<'a> Names<'a> {
/// Returns a glyph name by ID.
#[cfg(feature = "glyph-names")]
pub fn get(&self, glyph_id: GlyphId) -> Option<&'a str> {
let mut index = self.indexes.get(glyph_id.0)?;
impl core::fmt::Debug for Names<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "Names {{ ... }}")
}
}

// 'If the name index is between 0 and 257, treat the name index
// as a glyph index in the Macintosh standard order.'
if usize::from(index) < MACINTOSH_NAMES.len() {
Some(MACINTOSH_NAMES[usize::from(index)])
} else {
// 'If the name index is between 258 and 65535, then subtract 258 and use that
// to index into the list of Pascal strings at the end of the table.'
index -= MACINTOSH_NAMES.len() as u16;
impl<'a> Iterator for Names<'a> {
type Item = &'a str;

let mut s = Stream::new(self.data);
let mut i = 0;
while !s.at_end() && i < core::u16::MAX {
let len = s.read::<u8>()?;
fn next(&mut self) -> Option<Self::Item> {
// Glyph names are stored as Pascal Strings.
// Meaning u8 (len) + [u8] (data).

if i == index {
if len == 0 {
// Empty name is an error.
break;
} else {
let name = s.read_bytes(usize::from(len))?;
return core::str::from_utf8(name).ok();
}
} else {
s.advance(usize::from(len));
}
if self.offset >= self.data.len() {
return None;
}

i += 1;
}
let len = self.data[self.offset];
self.offset += 1;

None
// An empty name is an error.
if len == 0 {
return None;
}
}

/// Returns names count.
#[inline]
pub fn len(&self) -> u16 {
self.indexes.len()
}
}

impl core::fmt::Debug for Names<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "Names {{ ... }}")
let name = self.data.get(self.offset .. self.offset + usize::from(len))?;
self.offset += usize::from(len);
core::str::from_utf8(name).ok()
}
}

Expand All @@ -345,8 +326,9 @@ pub struct Table<'a> {
pub underline_metrics: LineMetrics,
/// Flag that indicates that the font is monospaced.
pub is_monospaced: bool,
/// A list of glyph names.
pub names: Names<'a>,

glyph_indexes: LazyArray16<'a, u16>,
names_data: &'a [u8],
}


Expand Down Expand Up @@ -376,20 +358,60 @@ impl<'a> Table<'a> {

let is_monospaced = Stream::read_at::<u32>(data, IS_FIXED_PITCH_OFFSET)? != 0;

let mut names = Names::default();
let mut names_data: &[u8] = &[];
let mut glyph_indexes = LazyArray16::default();
// Only version 2.0 of the table has data at the end.
if version == 0x00020000 {
let mut s = Stream::new_at(data, 32)?;
let count = s.read::<u16>()?;
names.indexes = s.read_array16::<u16>(count)?;
names.data = s.tail()?;
let indexes_count = s.read::<u16>()?;
glyph_indexes = s.read_array16::<u16>(indexes_count)?;
names_data = s.tail()?;
}

Some(Table {
italic_angle,
underline_metrics,
is_monospaced,
names,
names_data,
glyph_indexes,
})
}

/// Returns a glyph name by ID.
#[cfg(feature = "glyph-names")]
pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&'a str> {
let mut index = self.glyph_indexes.get(glyph_id.0)?;

// 'If the name index is between 0 and 257, treat the name index
// as a glyph index in the Macintosh standard order.'
if usize::from(index) < MACINTOSH_NAMES.len() {
Some(MACINTOSH_NAMES[usize::from(index)])
} else {
// 'If the name index is between 258 and 65535, then subtract 258 and use that
// to index into the list of Pascal strings at the end of the table.'
index -= MACINTOSH_NAMES.len() as u16;
self.names().nth(usize::from(index))
}
}

/// Returns a glyph ID by a name.
#[cfg(feature = "glyph-names")]
pub fn glyph_index_by_name(&self, name: &str) -> Option<GlyphId> {
let id = if let Some(index) = MACINTOSH_NAMES.iter().position(|n| *n == name) {
self.glyph_indexes.into_iter().position(|i| usize::from(i) == index)?
} else {
let mut index = self.names().position(|n| n == name)?;
index += MACINTOSH_NAMES.len();
self.glyph_indexes.into_iter().position(|i| usize::from(i) == index)?
};

Some(GlyphId(id as u16))
}

/// Returns an iterator over glyph names.
///
/// Default/predefined names are not included. Just the one in the font file.
pub fn names(&self) -> Names<'a> {
Names { data: self.names_data, offset: 0 }
}
}

0 comments on commit 5f6b89e

Please sign in to comment.