Support decoding of mac roman names

This allows discovery of Apple fonts without unicode name entries.
This commit is contained in:
Laurenz 2021-08-25 13:24:30 +02:00
parent 821536b253
commit f2882bf854
4 changed files with 51 additions and 16 deletions

View File

@ -15,7 +15,7 @@ use ttf_parser::{name_id, GlyphId, Tag};
use super::subset; use super::subset;
use crate::color::Color; use crate::color::Color;
use crate::font::{FaceId, FontStore}; use crate::font::{find_name, FaceId, FontStore};
use crate::geom::{self, Em, Length, Size}; use crate::geom::{self, Em, Length, Size};
use crate::image::{Image, ImageId, ImageStore}; use crate::image::{Image, ImageId, ImageStore};
use crate::layout::{Element, Frame, Geometry, Paint}; use crate::layout::{Element, Frame, Geometry, Paint};
@ -289,12 +289,7 @@ impl<'a> PdfExporter<'a> {
let face = self.fonts.get(face_id); let face = self.fonts.get(face_id);
let ttf = face.ttf(); let ttf = face.ttf();
let name = ttf let name = find_name(ttf.names(), name_id::POST_SCRIPT_NAME)
.names()
.find(|entry| {
entry.name_id() == name_id::POST_SCRIPT_NAME && entry.is_unicode()
})
.and_then(|entry| entry.to_string())
.unwrap_or_else(|| "unknown".to_string()); .unwrap_or_else(|| "unknown".to_string());
let base_font = format!("ABCDEF+{}", name); let base_font = format!("ABCDEF+{}", name);

View File

@ -6,10 +6,11 @@ use std::path::{Path, PathBuf};
use std::rc::Rc; use std::rc::Rc;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ttf_parser::{name_id, GlyphId}; use ttf_parser::{name_id, GlyphId, PlatformId};
use crate::geom::Em; use crate::geom::Em;
use crate::loading::{FileHash, Loader}; use crate::loading::{FileHash, Loader};
use crate::util::decode_mac_roman;
/// A unique identifier for a loaded font face. /// A unique identifier for a loaded font face.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
@ -352,16 +353,11 @@ impl FaceInfo {
data: &'a [u8], data: &'a [u8],
) -> impl Iterator<Item = FaceInfo> + 'a { ) -> impl Iterator<Item = FaceInfo> + 'a {
let count = ttf_parser::fonts_in_collection(data).unwrap_or(1); let count = ttf_parser::fonts_in_collection(data).unwrap_or(1);
(0 .. count).filter_map(move |index| {
fn find_name(face: &ttf_parser::Face, name_id: u16) -> Option<String> {
face.names().find_map(|entry| {
(entry.name_id() == name_id).then(|| entry.to_string()).flatten()
})
}
(0 .. count).filter_map(move |index| {
let face = ttf_parser::Face::from_slice(data, index).ok()?; let face = ttf_parser::Face::from_slice(data, index).ok()?;
let family = find_name(&face, name_id::TYPOGRAPHIC_FAMILY) let family = find_name(face.names(), name_id::TYPOGRAPHIC_FAMILY)
.or_else(|| find_name(&face, name_id::FAMILY))?; .or_else(|| find_name(face.names(), name_id::FAMILY))?;
let variant = FontVariant { let variant = FontVariant {
style: match (face.is_italic(), face.is_oblique()) { style: match (face.is_italic(), face.is_oblique()) {
@ -383,6 +379,23 @@ impl FaceInfo {
} }
} }
/// Find a decodable entry in a name table iterator.
pub fn find_name(mut names: ttf_parser::Names<'_>, name_id: u16) -> Option<String> {
names.find_map(|entry| {
if entry.name_id() == name_id {
if let Some(string) = entry.to_string() {
return Some(string);
}
if entry.platform_id() == PlatformId::Macintosh && entry.encoding_id() == 0 {
return Some(decode_mac_roman(entry.name()));
}
}
None
})
}
/// Properties that distinguish a face from other faces in the same family. /// Properties that distinguish a face from other faces in the same family.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]

25
src/util/mac.rs Normal file
View File

@ -0,0 +1,25 @@
/// Decode mac roman encoded bytes into a string.
pub fn decode_mac_roman(coded: &[u8]) -> String {
coded.iter().copied().map(char_from_mac_roman).collect()
}
/// Convert a mac roman coded character to a unicode char.
fn char_from_mac_roman(code: u8) -> char {
#[rustfmt::skip]
const TABLE: [char; 128] = [
'Ä', 'Å', 'Ç', 'É', 'Ñ', 'Ö', 'Ü', 'á', 'à', 'â', 'ä', 'ã', 'å', 'ç', 'é', 'è',
'ê', 'ë', 'í', 'ì', 'î', 'ï', 'ñ', 'ó', 'ò', 'ô', 'ö', 'õ', 'ú', 'ù', 'û', 'ü',
'†', '°', '¢', '£', '§', '•', '¶', 'ß', '®', '©', '™', '´', '¨', '≠', 'Æ', 'Ø',
'∞', '±', '≤', '≥', '¥', 'µ', '∂', '∑', '∏', 'π', '∫', 'ª', 'º', 'Ω', 'æ', 'ø',
'¿', '¡', '¬', '√', 'ƒ', '≈', '∆', '«', '»', '…', '\u{a0}', 'À', 'Ã', 'Õ', 'Œ', 'œ',
'', '—', '“', '”', '', '', '÷', '◊', 'ÿ', 'Ÿ', '', '€', '', '', 'fi', 'fl',
'‡', '·', '', '„', '‰', 'Â', 'Ê', 'Á', 'Ë', 'È', 'Í', 'Î', 'Ï', 'Ì', 'Ó', 'Ô',
'\u{f8ff}', 'Ò', 'Ú', 'Û', 'Ù', 'ı', 'ˆ', '˜', '¯', '˘', '˙', '˚', '¸', '˝', '˛', 'ˇ',
];
if code < 128 {
code as char
} else {
TABLE[(code - 128) as usize]
}
}

View File

@ -1,8 +1,10 @@
//! Utilities. //! Utilities.
mod eco; mod eco;
mod mac;
pub use eco::EcoString; pub use eco::EcoString;
pub use mac::decode_mac_roman;
use std::cell::RefMut; use std::cell::RefMut;
use std::cmp::Ordering; use std::cmp::Ordering;