use getset::*;
use serde_derive::{Serialize, Deserialize};
use std::collections::BTreeMap;
use std::io::Write;
use crate::binary::{ReadBytes, WriteBytes};
use crate::error::{Result, RLibError};
use crate::files::{DecodeableExtraData, Decodeable, EncodeableExtraData, Encodeable};
use crate::utils::check_size_mismatch;
pub const EXTENSION: &str = ".cuf";
#[cfg(test)] mod font_test;
const SIGNATURE: &[u8; 4] = b"CUF0";
#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub struct Font {
properties: CUFProperties,
glyphs: BTreeMap<u16, Glyph>,
supports_kerning: bool,
kerning_skip: u16,
kerning_blocks: Vec<Vec<u8>>,
}
#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub struct CUFProperties {
first_prop: u16,
second_prop: u16,
line_height: u16,
fourth_prop: u16,
fifth_prop: u16,
baseline: u16,
layout_y_offset: u16,
space_justify: u16,
layout_x_offset: u16,
h_size: u16,
v_size: u16,
}
#[derive(PartialEq, Clone, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub struct Glyph {
code: u16,
character: u16,
alloc_height: i8,
alloc_width: u8,
width: u8,
height: u8,
kerning: u32,
data: Vec<u8>,
}
impl Decodeable for Font {
fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
let signature_bytes = data.read_slice(4, false)?;
if signature_bytes.as_slice() != SIGNATURE {
return Err(RLibError::DecodingFontUnsupportedSignature(signature_bytes));
}
let mut font = Self::default();
font.properties.first_prop = data.read_u16()?;
font.properties.second_prop = data.read_u16()?;
font.properties.line_height = data.read_u16()?;
font.properties.fourth_prop = data.read_u16()?;
font.properties.fifth_prop = data.read_u16()?;
font.properties.baseline = data.read_u16()?;
font.properties.layout_y_offset = data.read_u16()?;
font.properties.space_justify = data.read_u16()?;
font.properties.layout_x_offset = data.read_u16()?;
font.properties.h_size = data.read_u16()?;
font.properties.v_size = data.read_u16()?;
let _glyph_count = data.read_u16()?;
let _glyph_data_size = data.read_u32()?;
for index in 0..=u16::MAX {
let code = data.read_u16()?;
if code == 0xFFFF {
continue;
}
let mut glyph = Glyph::default();
glyph.code = code;
glyph.character = index;
font.glyphs.insert(index, glyph);
}
for index in 0..=u16::MAX {
if let Some(glyph) = font.glyphs_mut().get_mut(&index) {
glyph.alloc_height = data.read_i8()?;
glyph.alloc_width = data.read_u8()?;
glyph.width = data.read_u8()?;
glyph.height = data.read_u8()?;
}
}
for _ in 0..font.glyphs().len() {
let _offset = data.read_u32()?;
}
for glyph in font.glyphs_mut().values_mut() {
let size = glyph.height as usize * glyph.width as usize;
if size != 0 {
glyph.data = data.read_slice(size, false)?;
}
}
if let Ok(kerning_size) = data.read_u16() {
font.supports_kerning = true;
font.kerning_skip = data.read_u16()?;
for _ in 0..kerning_size {
let block = data.read_slice(kerning_size as usize, false)?;
font.kerning_blocks.push(block);
}
}
check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
Ok(font)
}
}
impl Encodeable for Font {
fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
buffer.write_all(SIGNATURE)?;
buffer.write_u16(*self.properties().first_prop())?;
buffer.write_u16(*self.properties().second_prop())?;
buffer.write_u16(*self.properties().line_height())?;
buffer.write_u16(*self.properties().fourth_prop())?;
buffer.write_u16(*self.properties().fifth_prop())?;
buffer.write_u16(*self.properties().baseline())?;
buffer.write_u16(*self.properties().layout_y_offset())?;
buffer.write_u16(*self.properties().space_justify())?;
buffer.write_u16(*self.properties().layout_x_offset())?;
buffer.write_u16(*self.properties().h_size())?;
buffer.write_u16(*self.properties().v_size())?;
buffer.write_u16(self.glyphs().len() as u16)?;
let mut glyphs = vec![];
let mut dimensions = vec![];
let mut offsets = vec![];
let mut data = vec![];
for index in 0..=u16::MAX {
match self.glyphs().get(&index) {
Some(glyph) => {
glyphs.write_u16(glyph.code)?;
dimensions.write_i8(glyph.alloc_height)?;
dimensions.write_u8(glyph.alloc_width)?;
dimensions.write_u8(glyph.width)?;
dimensions.write_u8(glyph.height)?;
if glyph.data().is_empty() &&
glyph.alloc_height == 0 &&
glyph.alloc_width == 0 &&
glyph.width == 0 &&
glyph.height == 0 {
offsets.write_u32(0)?;
} else {
offsets.write_u32(data.len() as u32)?;
data.write_all(&glyph.data)?;
}
},
None => {
glyphs.write_u16(0xFFFF)?;
},
}
}
buffer.write_u32(data.len() as u32)?;
buffer.write_all(&glyphs)?;
buffer.write_all(&dimensions)?;
buffer.write_all(&offsets)?;
buffer.write_all(&data)?;
if self.supports_kerning {
buffer.write_u16(self.kerning_blocks.len() as u16)?;
buffer.write_u16(self.kerning_skip)?;
for block in self.kerning_blocks() {
buffer.write_all(block)?;
}
}
Ok(())
}
}