use std::io::{Cursor, Read, Seek, SeekFrom};
use byteorder::{LE, ReadBytesExt};
use crate::core::*;
#[derive(Clone)]
#[repr(C)]
pub struct Metadata {
pub total_size: u32,
pub version: EOTVersion,
pub flags: u32,
pub panose: [u8; 10],
pub charset: EOTCharset,
pub italic: bool,
pub weight: u32,
pub permissions: u16,
pub unicode_range: [u32; 4],
pub code_page_range: [u32; 2],
pub check_sum_adjustment: u32,
pub family_name: Vec<u16>,
pub style_name: Vec<u16>,
pub version_name: Vec<u16>,
pub full_name: Vec<u16>,
pub num_root_strings: ::core::ffi::c_uint,
pub font_data_size: u32,
pub font_data_offset: u32,
pub eudc_info: EUDCInfo,
pub do_not_use: Vec<u16>,
}
impl Metadata {
pub const ZERO: Metadata = Metadata {
total_size: 0,
version: 0 as EOTVersion,
flags: 0,
panose: [0; 10],
charset: ANSI_CHARSET,
italic: false,
weight: 0,
permissions: 0,
unicode_range: [0; 4],
code_page_range: [0; 2],
check_sum_adjustment: 0,
family_name: Vec::new(),
style_name: Vec::new(),
version_name: Vec::new(),
full_name: Vec::new(),
do_not_use: Vec::new(),
num_root_strings: 0,
font_data_size: 0,
font_data_offset: 0,
eudc_info: EUDCInfo {
exists: false,
code_page: 0,
flags: 0,
font_data: Vec::new(),
},
};
}
fn skip(c: &mut Cursor<&[u8]>, amount: u16) -> Result<(), Error> {
c.seek(SeekFrom::Current(amount as i64)).map_err(|_| Error::INSUFFICIENT_BYTES)?;
Ok(())
}
fn skip_padding(c: &mut Cursor<&[u8]>, amount: u16) -> Result<(), Error> {
for _ in 0..(amount as usize) {
if c.read_u8().map_err(|_| Error::INSUFFICIENT_BYTES)? != 0 {
return Err(Error::CORRUPT_FILE_PADDING_NOT_ZERO);
}
}
Ok(())
}
fn read_u32_le2(c: &mut Cursor<&[u8]>) -> Result<u32, Error> {
c.read_u32::<LE>().map_err(|_| Error::INSUFFICIENT_BYTES)
}
fn read_u16_le2(c: &mut Cursor<&[u8]>) -> Result<u16, Error> {
c.read_u16::<LE>().map_err(|_| Error::INSUFFICIENT_BYTES)
}
fn read_metadata_length(c: &mut Cursor<&[u8]>) -> Result<(u32, u32, u32), Error> {
let total_length = read_u32_le2(c)?;
let font_length = read_u32_le2(c)?;
if let Some(diff) = total_length.checked_sub(font_length) {
Ok((total_length, diff, font_length))
} else {
Err(Error::CORRUPT_FILE)
}
}
fn read_u16_array(c: &mut Cursor<&[u8]>) -> Result<Vec<u16>, Error> {
let size = read_u16_le2(c)? as usize;
if !size.is_multiple_of(2) {
return Err(Error::BOGUS_STRING_SIZE);
}
let mut buf = Vec::with_capacity(size / 2);
for _ in 0..size / 2 {
buf.push(read_u16_le2(c)?);
}
Ok(buf)
}
fn read_byte_array(c: &mut Cursor<&[u8]>) -> Result<Vec<u8>, Error> {
let size = read_u32_le2(c)? as usize;
let mut buf = Vec::with_capacity(size);
for _ in 0..size {
buf.push(c.read_u8().map_err(|_| Error::INSUFFICIENT_BYTES)?);
}
Ok(buf)
}
fn read_metadata_with_version(
c: &mut Cursor<&[u8]>, meta: &mut Metadata, version: EOTVersion,
) -> Result<(), Error> {
meta.version = version;
meta.flags = read_u32_le2(c)?;
c.read_exact(&mut meta.panose).map_err(|_| Error::INSUFFICIENT_BYTES)?;
meta.charset = c.read_u8().map_err(|_| Error::INSUFFICIENT_BYTES)? as u32;
meta.italic = c.read_u8().map_err(|_| Error::INSUFFICIENT_BYTES)? != 0;
meta.weight = read_u32_le2(c)?;
meta.permissions = read_u16_le2(c)?;
if read_u16_le2(c)? != 0x504c {
return Err(Error::CORRUPT_FILE);
}
for i in 0..4 {
meta.unicode_range[i] = read_u32_le2(c)?;
}
for i in 0..2 {
meta.code_page_range[i] = read_u32_le2(c)?;
}
meta.check_sum_adjustment = read_u32_le2(c)?;
skip(c, 16)?;
skip_padding(c, 2)?;
meta.family_name = read_u16_array(c)?;
skip_padding(c, 2)?;
meta.style_name = read_u16_array(c)?;
skip_padding(c, 2)?;
meta.version_name = read_u16_array(c)?;
skip_padding(c, 2)?;
meta.full_name = read_u16_array(c)?;
if meta.version > VERSION_1 {
skip_padding(c, 2)?;
meta.do_not_use = read_u16_array(c)?;
if meta.version == VERSION_3 {
skip(c, 4)?; meta.eudc_info.code_page = read_u32_le2(c)?;
skip_padding(c, 2)?;
let signature_size = read_u16_le2(c)?;
skip_padding(c, signature_size)?;
meta.eudc_info.flags = read_u32_le2(c)?;
meta.eudc_info.font_data = read_byte_array(c)?;
meta.eudc_info.exists = !meta.eudc_info.font_data.is_empty();
}
}
meta.font_data_offset = c.position() as u32;
let expected_header_size = meta.total_size.wrapping_sub(meta.font_data_size);
if meta.font_data_offset < expected_header_size {
return Err(Error::HEADER_TOO_BIG);
}
Ok(())
}
pub fn read_metadata(bytes: &[u8]) -> Result<Metadata, Error> {
let mut c = Cursor::new(bytes);
let (total_size, metadata_size, font_data_size) = read_metadata_length(&mut c)?;
if bytes.len() < metadata_size as usize {
return Err(Error::INSUFFICIENT_BYTES);
}
let coded_version = match read_u32_le2(&mut c)? {
0x00010000 => VERSION_1,
0x00020001 => VERSION_2,
0x00020002 => VERSION_3,
_ => return Err(Error::CORRUPT_FILE),
};
let mut try_version = coded_version;
let mut bumped_up = false;
let mut knocked_down = false;
loop {
let mut met = Metadata::ZERO;
met.total_size = total_size;
met.font_data_size = font_data_size;
let pos = c.position() as usize;
if bytes.len() < met.font_data_size as usize + pos {
return Err(Error::CORRUPT_FILE);
}
match read_metadata_with_version(&mut c, &mut met, try_version) {
Ok(()) =>
if try_version == coded_version {
return Ok(met);
} else {
return Err(Error::WARN_BAD_VERSION);
},
Err(Error::HEADER_TOO_BIG) => {
if knocked_down || try_version == VERSION_3 {
return Err(Error::CORRUPT_FILE);
}
knocked_down = false;
bumped_up = true;
try_version += 1;
}
Err(Error::INSUFFICIENT_BYTES) => {
if bumped_up || try_version == VERSION_1 {
return Err(Error::CORRUPT_FILE);
}
knocked_down = true;
bumped_up = false;
try_version -= 1;
}
Err(e) => return Err(e),
}
}
}
pub fn can_legally_edit(metadata: &Metadata) -> bool {
const EDITING_MASK: u16 = 0x8;
metadata.permissions == 0 || ((metadata.permissions & EDITING_MASK) != 0)
}