use thiserror::Error;
use num_enum::{ FromPrimitive, IntoPrimitive };
use std::ops::Range;
use std::fmt::{ Debug, Formatter, Result as FmtResult };
use bytemuck::{ Zeroable, Pod };
use mchr::Pullable;
use mchr::str::StdDecoder;
pub type BytesIter<'a> = std::iter::Copied<std::slice::Iter<'a, u8>>;
use super::ReadError;
use super::core::*;
use super::common::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[derive(FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum NameKind {
CopyrightNotice = 0,
FamilyName = 1,
FontSubfamily = 2,
UniqueSubfamilyId = 3,
FullName = 4,
Version = 5,
PostScriptName = 6,
TrademarkNotice = 7,
Manufacturer = 8,
Designer = 9,
Description = 10,
VendorUrl = 11,
DesignerUrl = 12,
LicenseDescription = 13,
LicenseUrl = 14,
PreferredFamily = 16,
PreferredSubfamily = 17,
CompatibleFull = 18,
SampleText = 19,
VariationsPostScriptNamePrefix = 25,
#[num_enum(catch_all)]
Other(u16),
}
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(transparent)]
pub struct NameRecord([u8; 12]);
impl<'a> RandomAccess<'a> for &'a NameRecord {
fn bytes(&self) -> &'a [u8] { &self.0 }
}
impl NameRecord {
pub fn encoding_and_language(&self) -> PlatformEncodingAndLanguage { (self.uint16(0), self.uint16(2), self.uint16(4)).into() }
pub fn kind(&self) -> NameKind { NameKind::from(self.uint16(6)) }
pub fn text(&self) -> Range<u16> {
let start = self.uint16(10);
let stop = start + self.uint16(8);
start..stop
}
}
impl Debug for NameRecord {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("NameRecord")
.field("encoding_and_language", &self.encoding_and_language())
.field("kind", &self.kind())
.field("text", &self.text())
.finish()
}
}
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(transparent)]
pub struct LangTagRecord([u8; 4]);
impl<'a> RandomAccess<'a> for &'a LangTagRecord {
fn bytes(&self) -> &'a [u8] { &self.0 }
}
impl LangTagRecord {
pub fn text(&self) -> Range<u16> {
let start = self.uint16(2);
let stop = start + self.uint16(0);
start..stop
}
}
impl Debug for LangTagRecord {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("LangTagRecord")
.field("text", &self.text())
.finish()
}
}
#[derive(Error, Debug)]
pub enum StringError {
#[error("invalid string range: {0:?}")]
InvalidStringRange(Range<u16>),
#[error("unsupported encoding: {0:?}")]
UnsupportedPlatformEncoding(PlatformEncodingAndLanguage),
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct NamingTable<'a>(&'a [u8]);
impl<'a> RandomAccess<'a> for NamingTable<'a> {
fn bytes(&self) -> &'a [u8] { self.0 }
}
impl<'a> NamingTable<'a> {
pub fn version(&self) -> u16 { self.uint16(0) }
pub fn name_count(&self) -> u16 { self.uint16(2) }
pub fn string_offset(&self) -> u16 { self.uint16(4) }
pub fn name_records(&self) -> &'a [NameRecord] { self.array(6, self.name_count() as usize) }
pub fn string_data(&self, range: Range<u16>) -> Result<&'a [u8], Range<u16>> {
let offset = self.string_offset() as usize;
let start = offset + range.start as usize;
let stop = offset + range.end as usize;
let data = self.bytes();
if stop <= data.len() {
Ok(&data[start..stop])
} else {
Err(range)
}
}
pub fn string(&self, record: &NameRecord) -> Result<StdDecoder<BytesIter<'a>>, StringError> {
let data = self.string_data(record.text()).map_err(StringError::InvalidStringRange)?;
let encoding = record.encoding_and_language();
let encoding = encoding.character_encoding().ok_or(StringError::UnsupportedPlatformEncoding(encoding))?;
Ok(data.iter().copied().transcode(encoding))
}
}
impl<'a> Debug for NamingTable<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_list()
.entries(self.iter())
.finish()
}
}
impl<'a> TryFrom<&'a [u8]> for NamingTable<'a> {
type Error = ReadError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() < 6 {
return Err(ReadError::UnexpectedEof);
}
let value = NamingTable(value);
let version = value.version();
if !matches!(version, 0 | 1) {
return Err(ReadError::UnsupportedTableVersionSingle(version));
}
if value.bytes().len() < 6 + value.name_count() as usize * 12 {
return Err(ReadError::UnexpectedEof);
}
Ok(value)
}
}
#[derive(Clone, Copy)]
pub struct Name<'a> {
table: NamingTable<'a>,
record: &'a NameRecord,
}
impl<'a> Name<'a> {
pub fn encoding_and_language(&self) -> PlatformEncodingAndLanguage { self.record.encoding_and_language() }
pub fn kind(&self) -> NameKind { self.record.kind() }
pub fn text(&self) -> Result<StdDecoder<BytesIter<'a>>, StringError> { self.table.string(self.record) }
}
impl<'a> NamingTable<'a> {
pub fn iter(&self) -> impl ExactSizeIterator<Item = Name<'a>> + '_ {
self.name_records().iter().map(|record| Name { table: *self, record })
}
}
impl<'a> Debug for Name<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("Name")
.field("encoding_and_language", &self.encoding_and_language())
.field("kind", &self.kind())
.field("text", &self.text().map(|x| x.buffered().collect::<String>()))
.finish()
}
}