mod xml;
use crate::error::GlifParserError;
#[cfg(feature = "mfek")]
use crate::glif::mfek::MFEKGlif;
use crate::glif::{self, Glif};
use crate::matrix::{ApplyMatrix as _, GlifMatrix};
use crate::outline::Outline;
use crate::point::PointData;
use integer_or_float::IntegerOrFloat;
use kurbo::Affine;
pub use trees::{Forest, Node, Tree};
use IntegerOrFloat::Float;
#[cfg(feature = "glifserde")]
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::path::{Path, PathBuf};
#[allow(non_snake_case)] #[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Default, Hash, PartialEq)]
pub struct GlifComponent {
pub base: String,
pub filename: Option<PathBuf>,
pub xScale: IntegerOrFloat,
pub xyScale: IntegerOrFloat,
pub yxScale: IntegerOrFloat,
pub yScale: IntegerOrFloat,
pub xOffset: IntegerOrFloat,
pub yOffset: IntegerOrFloat,
pub identifier: Option<String>,
}
#[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct GlifComponents {
pub root: String,
pub vec: Vec<GlifComponent>,
pub uniques: HashSet<String>
}
impl GlifComponents {
pub fn new() -> Self {
Default::default()
}
}
impl GlifComponent {
pub fn new() -> Self {
Self {
xScale: Float(1.0),
yScale: Float(1.0),
..Default::default()
}
}
}
impl GlifComponent {
pub fn matrix(&self) -> GlifMatrix {
GlifMatrix(
self.xScale,
self.xyScale,
self.yxScale,
self.yScale,
self.xOffset,
self.yOffset,
)
}
}
#[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Component<PD: PointData> {
pub glif: Glif<PD>,
pub matrix: Affine,
}
impl<PD: PointData> Component<PD> {
pub fn new() -> Self {
Self::default()
}
}
#[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ComponentRect {
pub minx: f32,
pub miny: f32,
pub maxx: f32,
pub maxy: f32,
pub name: String,
}
impl ComponentRect {
pub fn from_rect_and_name(minx: f32, miny: f32, maxx: f32, maxy: f32, name: String) -> Self {
Self {
minx,
miny,
maxx,
maxy,
name,
}
}
}
use std::fs;
impl GlifComponent {
pub fn set_file_name<F: AsRef<Path>>(&mut self, gliffn: F) {
let mut retglifname = gliffn.as_ref().to_path_buf();
retglifname.set_file_name(glif::name_to_filename(&self.base, true));
self.filename = Some(retglifname);
}
pub fn to_component<PD: PointData>(&self) -> Result<Component<PD>, GlifParserError> {
let gliffn = &self
.filename
.as_ref()
.ok_or(GlifParserError::GlifFilenameNotSet(self.base.clone()))?;
let mut ret = Component::new();
ret.matrix = self.matrix().into();
ret.glif.name = self.base.clone();
ret.glif.filename = self.filename.clone();
let component_xml = fs::read_to_string(&gliffn).or(Err(GlifParserError::GlifFilenameNotSet(
"Glif filename leads to unreadable file".to_string(),
)))?;
let mut newglif: Glif<PD> = glif::read(&component_xml)?;
for component in newglif.components.vec.iter_mut() {
component.set_file_name(&gliffn);
}
ret.glif.components = newglif.components;
ret.glif.anchors = newglif.anchors;
ret.glif.outline = newglif.outline;
Ok(ret)
}
pub fn refers_to<PD: PointData>(&self, glif: &Glif<PD>) -> bool {
self.base == glif.name
}
}
pub trait FlattenedGlif
where
Self: Clone,
{
fn flattened(&self, rects: &mut Option<Vec<ComponentRect>>) -> Result<Self, GlifParserError>;
}
fn apply_component_rect<PD: PointData>(
last: &Node<Component<PD>>,
minx: &mut f32,
miny: &mut f32,
maxx: &mut f32,
maxy: &mut f32,
final_outline: &mut Outline<PD>,
) {
let mut matrices = vec![];
matrices.push((*last).data().matrix);
let mut pt = last.parent();
while let Some(parent) = pt {
matrices.push(parent.data().matrix);
pt = parent.parent();
}
match (*last).data().glif.outline {
Some(ref o) => {
let mut to_transform = o.clone();
for i in 0..to_transform.len() {
for j in 0..to_transform[i].len() {
let is_first = i == 0 && j == 0;
let mut p = to_transform[i][j].clone();
if p.x < *minx || is_first {
*minx = p.x;
}
if p.y < *miny || is_first {
*miny = p.y;
}
if p.x > *maxx || is_first {
*maxx = p.x;
}
if p.y > *maxy || is_first {
*maxy = p.y;
}
for m in &matrices {
p.apply_matrix(*m);
}
to_transform[i][j] = p;
}
}
final_outline.extend(to_transform);
}
None => {}
}
}
#[rustfmt::skip]
macro_rules! impl_flattened_glif {
($glifstruct:ident, $outline:ident) => {
impl<PD: PointData> FlattenedGlif for $glifstruct<PD> {
fn flattened(&self, rects: &mut Option<Vec<ComponentRect>>) -> Result<Self, GlifParserError> {
let mut ret = self.clone();
let components_r: Result<Forest<Component<PD>>, _> = (ret.components).into();
let components = components_r?;
let mut final_outline: Outline<PD> = Outline::new();
let mut component_rects = vec![];
for mut component in components {
let (mut minx, mut miny, mut maxx, mut maxy) = (0., 0., 0., 0.);
let component_name = component.data().glif.name.clone();
apply_component_rect(&component, &mut minx, &mut miny, &mut maxx, &mut maxy, &mut final_outline);
while let Some(last) = component.back() {
apply_component_rect(&last, &mut minx, &mut miny, &mut maxx, &mut maxy, &mut final_outline);
component.pop_back();
}
component_rects.push(ComponentRect { minx, maxx, miny, maxy, name: component_name });
}
ret.$outline = Some(final_outline);
ret.components = GlifComponents::new();
if let Some(ptr) = rects {
*ptr = component_rects;
}
Ok(ret)
}
}
}
}
impl_flattened_glif!(Glif, outline);
#[cfg(feature = "mfek")]
impl_flattened_glif!(MFEKGlif, flattened);
impl<PD: PointData> From<GlifComponents> for Result<Forest<Component<PD>>, GlifParserError> {
fn from(mut glifcs: GlifComponents) -> Self {
let mut forest = Forest::new();
let components: Vec<_> = glifcs.vec.drain(..).collect();
let cs = components.into_iter().map(|c| {
glifcs.build_component_tree(c.to_component()?)
}).collect::<Result<Vec<_>, GlifParserError>>()?;
for c in cs {
forest.push_back(c);
}
Ok(forest)
}
}
impl GlifComponents {
pub fn build_component_tree<PD: PointData>(
&mut self,
component: Component<PD>,
) -> Result<Tree<Component<PD>>, GlifParserError> {
let mut tree = Tree::new(component.clone());
for gc in component.glif.components.vec.into_iter() {
if !self.uniques.insert(gc.base.clone()) {
return Err(GlifParserError::GlifComponentsCyclical(format!(
"in glif {}, {} refers to {} (trying to flatten {})",
gc.base, component.glif.name, gc.base, self.root
)));
}
tree.push_back(self.build_component_tree(gc.to_component()?)?);
self.uniques.remove(&gc.base);
}
Ok(tree)
}
}