use std::ops::Index;
use std::fmt::{ Debug, Formatter, Result as FmtResult };
use super::ReadError;
use super::core::*;
const STANDARD_MACINTOSH_GLYPH_NAMES: [&str; 258] = [
".notdef",
".null",
"nonmarkingreturn",
"space",
"exclam",
"quotedbl",
"numbersign",
"dollar",
"percent",
"ampersand",
"quotesingle",
"parenleft",
"parenright",
"asterisk",
"plus",
"comma",
"hyphen",
"period",
"slash",
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"colon",
"semicolon",
"less",
"equal",
"greater",
"question",
"at",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"bracketleft",
"backslash",
"bracketright",
"asciicircum",
"underscore",
"grave",
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"braceleft",
"bar",
"braceright",
"asciitilde",
"Adieresis",
"Aring",
"Ccedilla",
"Eacute",
"Ntilde",
"Odieresis",
"Udieresis",
"aacute",
"agrave",
"acircumflex",
"adieresis",
"atilde",
"aring",
"ccedilla",
"eacute",
"egrave",
"ecircumflex",
"edieresis",
"iacute",
"igrave",
"icircumflex",
"idieresis",
"ntilde",
"oacute",
"ograve",
"ocircumflex",
"odieresis",
"otilde",
"uacute",
"ugrave",
"ucircumflex",
"udieresis",
"dagger",
"degree",
"cent",
"sterling",
"section",
"bullet",
"paragraph",
"germandbls",
"registered",
"copyright",
"trademark",
"acute",
"dieresis",
"notequal",
"AE",
"Oslash",
"infinity",
"plusminus",
"lessequal",
"greaterequal",
"yen",
"mu",
"partialdiff",
"summation",
"product",
"pi",
"integral",
"ordfeminine",
"ordmasculine",
"Omega",
"ae",
"oslash",
"questiondown",
"exclamdown",
"logicalnot",
"radical",
"florin",
"approxequal",
"Delta",
"guillemotleft",
"guillemotright",
"ellipsis",
"nonbreakingspace",
"Agrave",
"Atilde",
"Otilde",
"OE",
"oe",
"endash",
"emdash",
"quotedblleft",
"quotedblright",
"quoteleft",
"quoteright",
"divide",
"lozenge",
"ydieresis",
"Ydieresis",
"fraction",
"currency",
"guilsinglleft",
"guilsinglright",
"fi",
"fl",
"daggerdbl",
"periodcentered",
"quotesinglbase",
"quotedblbase",
"perthousand",
"Acircumflex",
"Ecircumflex",
"Aacute",
"Edieresis",
"Egrave",
"Iacute",
"Icircumflex",
"Idieresis",
"Igrave",
"Oacute",
"Ocircumflex",
"apple",
"Ograve",
"Uacute",
"Ucircumflex",
"Ugrave",
"dotlessi",
"circumflex",
"tilde",
"macron",
"breve",
"dotaccent",
"ring",
"cedilla",
"hungarumlaut",
"ogonek",
"caron",
"Lslash",
"lslash",
"Scaron",
"scaron",
"Zcaron",
"zcaron",
"brokenbar",
"Eth",
"eth",
"Yacute",
"yacute",
"Thorn",
"thorn",
"minus",
"multiply",
"onesuperior",
"twosuperior",
"threesuperior",
"onehalf",
"onequarter",
"threequarters",
"franc",
"Gbreve",
"gbreve",
"Idotaccent",
"Scedilla",
"scedilla",
"Cacute",
"cacute",
"Ccaron",
"ccaron",
"dcroat",
];
#[derive(Clone, Copy)]
pub enum GlyphNames<'a> {
Standard,
Subset(&'a [u8]),
Superset(&'a [u8]),
}
impl<'a> GlyphNames<'a> {
pub fn len(&self) -> u16 {
match self {
Self::Standard => 258,
Self::Subset(x) => x.uint16(0),
Self::Superset(x) => x.uint16(0),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, index: u16) -> &'a str {
match self {
Self::Standard => STANDARD_MACINTOSH_GLYPH_NAMES[index as usize],
Self::Subset(x) => {
let offset = x.int8(2 + index as usize);
let index = index as i32 + offset as i32;
STANDARD_MACINTOSH_GLYPH_NAMES[index as usize]
},
Self::Superset(x) => {
let index = x.uint16(2 + index as usize * 2);
if index < 258 {
return STANDARD_MACINTOSH_GLYPH_NAMES[index as usize];
}
let index = index - 258;
let mut data = &x[2 + x.uint16(0) as usize * 2..];
for _ in 0..index {
let end = 1 + data[0] as usize;
data = &data[end..];
}
let end = 1 + data[0] as usize;
let data = &data[1..end];
std::str::from_utf8(data).unwrap() },
}
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = &str> {
(0..self.len()).map(|x| self.get(x))
}
}
impl<'a> Index<u16> for GlyphNames<'a> {
type Output = str;
fn index(&self, index: u16) -> &Self::Output { self.get(index) }
}
impl Debug for GlyphNames<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_list().entries(self.iter()).finish()
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct PostScriptTable<'a>(&'a [u8]);
impl<'a> RandomAccess<'a> for PostScriptTable<'a> {
fn bytes(&self) -> &'a [u8] { self.0 }
}
impl<'a> PostScriptTable<'a> {
pub fn version(&self) -> Version16Dot16 { self.version16dot16(0) }
pub fn italic_angle(&self) -> Fixed { self.fixed(4) }
pub fn underline_position(&self) -> i16 { self.int16(8) }
pub fn underline_thickness(&self) -> i16 { self.int16(10) }
pub fn is_fixed_pitch(&self) -> u32 { self.uint32(12) }
pub fn min_mem_type42(&self) -> u32 { self.uint32(16) }
pub fn max_mem_type42(&self) -> u32 { self.uint32(20) }
pub fn min_mem_type1(&self) -> u32 { self.uint32(24) }
pub fn max_mem_type1(&self) -> u32 { self.uint32(28) }
pub fn glyph_names(&self) -> Option<GlyphNames<'a>> {
match self.version() {
Version16Dot16(0x0001, 0x0000) => Some(GlyphNames::Standard),
Version16Dot16(0x0002, 0x0000) => Some(GlyphNames::Superset(&self.0[32..])),
Version16Dot16(0x0002, 0x5000) => Some(GlyphNames::Subset(&self.0[32..])),
Version16Dot16(0x0003, 0x0000) => None,
_ => unreachable!(),
}
}
}
impl Debug for PostScriptTable<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("PostScriptTable")
.field("version", &self.version())
.field("italic_angle", &self.italic_angle())
.field("underline_position", &self.underline_position())
.field("underline_thickness", &self.underline_thickness())
.field("is_fixed_pitch", &self.is_fixed_pitch())
.field("min_mem_type42", &self.min_mem_type42())
.field("max_mem_type42", &self.max_mem_type42())
.field("min_mem_type1", &self.min_mem_type1())
.field("max_mem_type1", &self.max_mem_type1())
.field("glyph_names", &self.glyph_names())
.finish()
}
}
impl<'a> TryFrom<&'a [u8]> for PostScriptTable<'a> {
type Error = ReadError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() < 32 { return Err(ReadError::UnexpectedEof); }
match value.version16dot16(0) {
Version16Dot16(0x0001, 0x0000) => Ok(PostScriptTable(value)),
Version16Dot16(0x0002, 0x0000) => {
Ok(PostScriptTable(value))
},
Version16Dot16(0x0002, 0x5000) => {
Ok(PostScriptTable(value))
},
Version16Dot16(0x0003, 0x0000) => Ok(PostScriptTable(value)),
x => Err(ReadError::UnsupportedTableVersion16Dot16(x))
}
}
}