#[cfg(feature = "std")]
use std::vec::Vec;
#[cfg(feature = "std")]
use std::string::String;
#[cfg(feature = "std")]
use crate::parser::LazyArray;
use crate::parser::Stream;
use crate::{Font, TableName, Result, Error};
use crate::raw::name as raw;
pub mod name_id {
#![allow(missing_docs)]
pub const COPYRIGHT_NOTICE: u16 = 0;
pub const FAMILY: u16 = 1;
pub const SUBFAMILY: u16 = 2;
pub const UNIQUE_ID: u16 = 3;
pub const FULL_NAME: u16 = 4;
pub const VERSION: u16 = 5;
pub const POST_SCRIPT_NAME: u16 = 6;
pub const TRADEMARK: u16 = 7;
pub const MANUFACTURER: u16 = 8;
pub const DESIGNER: u16 = 9;
pub const DESCRIPTION: u16 = 10;
pub const VENDOR_URL: u16 = 11;
pub const DESIGNER_URL: u16 = 12;
pub const LICENSE: u16 = 13;
pub const LICENSE_URL: u16 = 14;
pub const TYPOGRAPHIC_FAMILY: u16 = 16;
pub const TYPOGRAPHIC_SUBFAMILY: u16 = 17;
pub const COMPATIBLE_FULL: u16 = 18;
pub const SAMPLE_TEXT: u16 = 19;
pub const POST_SCRIPT_CID: u16 = 20;
pub const WWS_FAMILY: u16 = 21;
pub const WWS_SUBFAMILY: u16 = 22;
pub const LIGHT_BACKGROUND_PALETTE: u16 = 23;
pub const DARK_BACKGROUND_PALETTE: u16 = 24;
pub const VARIATIONS_POST_SCRIPT_NAME_PREFIX: u16 = 25;
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum PlatformId {
Unicode,
Macintosh,
Iso,
Windows,
Custom,
}
impl PlatformId {
pub(crate) fn from_u16(n: u16) -> Option<Self> {
match n {
0 => Some(PlatformId::Unicode),
1 => Some(PlatformId::Macintosh),
2 => Some(PlatformId::Iso),
3 => Some(PlatformId::Windows),
4 => Some(PlatformId::Custom),
_ => None,
}
}
}
#[cfg(feature = "std")]
#[inline]
fn is_unicode_encoding(platform_id: PlatformId, encoding_id: u16) -> bool {
const WINDOWS_UNICODE_BMP_ENCODING_ID: u16 = 1;
match platform_id {
PlatformId::Unicode => true,
PlatformId::Windows if encoding_id == WINDOWS_UNICODE_BMP_ENCODING_ID => true,
_ => false,
}
}
#[derive(Clone, Copy)]
pub struct Name<'a> {
data: raw::NameRecord,
strings: &'a [u8],
}
impl<'a> Name<'a> {
pub fn platform_id(&self) -> Option<PlatformId> {
PlatformId::from_u16(self.data.platform_id())
}
pub fn encoding_id(&self) -> u16 {
self.data.encoding_id()
}
pub fn language_id(&self) -> u16 {
self.data.language_id()
}
pub fn name_id(&self) -> u16 {
self.data.name_id()
}
pub fn name(&self) -> &'a [u8] {
let start = self.data.offset() as usize;
let end = start + self.data.length() as usize;
self.strings.get(start..end).unwrap_or(&[])
}
#[cfg(feature = "std")]
#[inline(never)]
pub fn name_utf8(&self) -> Option<String> {
if self.is_unicode() {
self.name_from_utf16_be()
} else {
None
}
}
#[cfg(feature = "std")]
#[inline]
fn is_unicode(&self) -> bool {
is_unicode_encoding(self.platform_id().unwrap(), self.encoding_id())
}
#[cfg(feature = "std")]
#[inline(never)]
fn name_from_utf16_be(&self) -> Option<String> {
let mut name: Vec<u16> = Vec::new();
for c in LazyArray::new(self.name()) {
name.push(c);
}
String::from_utf16(&name).ok()
}
}
#[cfg(feature = "std")]
impl<'a> core::fmt::Debug for Name<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let name = self.name_utf8();
f.debug_struct("Name")
.field("name", &name.as_ref().map(core::ops::Deref::deref)
.unwrap_or("unsupported encoding"))
.field("platform_id", &self.platform_id())
.field("encoding_id", &self.encoding_id())
.field("language_id", &self.language_id())
.field("name_id", &self.name_id())
.finish()
}
}
#[cfg(not(feature = "std"))]
impl<'a> core::fmt::Debug for Name<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Name")
.field("name", &self.name())
.field("platform_id", &self.platform_id())
.field("encoding_id", &self.encoding_id())
.field("language_id", &self.language_id())
.field("name_id", &self.name_id())
.finish()
}
}
#[derive(Clone, Copy)]
#[allow(missing_debug_implementations)]
pub struct Names<'a> {
names: &'a [u8],
storage: &'a [u8],
index: u16,
total: u16,
}
impl<'a> Names<'a> {
fn new(names: &'a [u8], storage: &'a [u8]) -> Self {
Names {
names,
storage,
index: 0,
total: (names.len() / raw::NameRecord::SIZE) as u16,
}
}
}
impl<'a> Iterator for Names<'a> {
type Item = Name<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.total {
self.index += 1;
self.nth(self.index as usize - 1)
} else {
None
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let start = raw::NameRecord::SIZE * n;
let end = start + raw::NameRecord::SIZE;
let data = self.names.get(start..end)?;
Some(Name {
data: raw::NameRecord::new(data),
strings: self.storage,
})
}
}
impl<'a> Font<'a> {
pub fn names(&self) -> Names {
match self._names() {
Ok(v) => v,
Err(_) => Names { names: &[], storage: &[], index: 0, total: 0 },
}
}
#[inline(never)]
fn _names(&self) -> Result<Names> {
const LANG_TAG_RECORD_SIZE: u16 = 4;
let data = self.name.ok_or_else(|| Error::TableMissing(TableName::Naming))?;
let mut s = Stream::new(data);
let format: u16 = s.read()?;
let count: u16 = s.read()?;
s.skip::<u16>();
if format == 0 {
Ok(Names::new(s.read_bytes(raw::NameRecord::SIZE as u32 * count as u32)?, s.tail()?))
} else if format == 1 {
let lang_tag_count: u16 = s.read()?;
let lang_tag_len = lang_tag_count
.checked_mul(LANG_TAG_RECORD_SIZE)
.ok_or_else(|| Error::NotATrueType)?;
s.skip_len(lang_tag_len); Ok(Names::new(s.read_bytes(raw::NameRecord::SIZE as u32 * count as u32)?, s.tail()?))
} else {
Err(Error::NotATrueType)
}
}
#[cfg(feature = "std")]
pub fn family_name(&self) -> Option<String> {
let mut idx = None;
let mut iter = self.names();
for (i, name) in iter.enumerate() {
if name.name_id() == name_id::TYPOGRAPHIC_FAMILY && name.is_unicode() {
idx = Some(i);
break;
} else if name.name_id() == name_id::FAMILY && name.is_unicode() {
idx = Some(i);
}
}
iter.nth(idx?).and_then(|name| name.name_from_utf16_be())
}
#[cfg(feature = "std")]
pub fn post_script_name(&self) -> Option<String> {
self.names()
.find(|name| name.name_id() == name_id::POST_SCRIPT_NAME && name.is_unicode())
.and_then(|name| name.name_from_utf16_be())
}
}