use std::collections::HashSet;
use std::convert::TryFrom;
use std::path::PathBuf;
use super::*;
use crate::error::{ErrorKind, GlifLoadError};
use crate::glyph::builder::OutlineBuilder;
use crate::names::NameList;
use quick_xml::{
events::{BytesStart, Event},
Reader,
};
#[cfg(test)]
pub(crate) fn parse_glyph(xml: &[u8]) -> Result<Glyph, GlifLoadError> {
GlifParser::from_xml(xml, None)
}
type Version = (u32, u32);
const VERSION_1: Version = (1, 0);
const VERSION_2: Version = (2, 0);
const UTF8_BOM: &[u8] = &[0xEF, 0xBB, 0xBF];
pub(crate) struct GlifParser<'names> {
glyph: Glyph,
version: Version,
seen_identifiers: HashSet<Identifier>,
has_warned_for_smooth_point: bool,
names: Option<&'names NameList>,
}
impl<'names> GlifParser<'names> {
pub(crate) fn from_xml(
xml: &[u8],
names: Option<&'names NameList>,
) -> Result<Glyph, GlifLoadError> {
let xml = xml.strip_prefix(UTF8_BOM).unwrap_or(xml);
let mut reader = Reader::from_reader(xml);
let mut buf = Vec::new();
reader.config_mut().trim_text(true);
let (name, version) = start(&mut reader, &mut buf, names)?;
let glyph = Glyph::new_impl(name);
let parser = GlifParser {
glyph,
seen_identifiers: Default::default(),
names,
version,
has_warned_for_smooth_point: false,
};
parser.parse_body(&mut reader, xml, &mut buf)
}
fn parse_body(
mut self,
reader: &mut Reader<&[u8]>,
raw_xml: &[u8],
buf: &mut Vec<u8>,
) -> Result<Glyph, GlifLoadError> {
let mut seen_advance = false;
let mut seen_lib = false;
let mut seen_outline = false;
loop {
match reader.read_event_into(buf)? {
Event::Start(start) => match start.name().as_ref() {
b"outline" if seen_outline => {
return Err(ErrorKind::DuplicateElement("outline").into());
}
b"outline" => {
seen_outline = true;
self.parse_outline(reader, buf)?;
}
b"lib" if seen_lib => {
return Err(ErrorKind::DuplicateElement("lib").into());
}
b"lib" => {
seen_lib = true;
self.parse_lib(reader, raw_xml, buf)?;
}
b"note" if self.version == VERSION_1 => {
log::warn!(
"v1 .glif '{}' contains unexpected 'note' field",
self.glyph.name
);
self.parse_note(reader, buf)?;
self.glyph.note = None;
}
b"note" if self.glyph.note.is_some() => {
return Err(ErrorKind::DuplicateElement("note").into());
}
b"note" => self.parse_note(reader, buf)?,
_other => return Err(ErrorKind::UnexpectedElement.into()),
},
Event::Empty(start) => match start.name().as_ref() {
b"outline" if seen_outline => {
return Err(ErrorKind::DuplicateElement("outline").into());
}
b"outline" => {
seen_outline = true;
}
b"advance" if seen_advance => {
return Err(ErrorKind::DuplicateElement("advance").into());
}
b"advance" => {
seen_advance = true;
self.parse_advance(start)?;
}
b"unicode" => self.parse_unicode(start)?,
b"anchor" if self.version == VERSION_1 => {
return Err(ErrorKind::UnexpectedV1Element("anchor").into());
}
b"anchor" => self.parse_anchor(start)?,
b"guideline" if self.version == VERSION_1 => {
return Err(ErrorKind::UnexpectedV1Element("guideline").into());
}
b"guideline" => self.parse_guideline(start)?,
b"image" if self.version == VERSION_1 => {
return Err(ErrorKind::UnexpectedV1Element("image").into());
}
b"image" if self.glyph.image.is_some() => {
return Err(ErrorKind::DuplicateElement("image").into());
}
b"image" => self.parse_image(start)?,
_other => return Err(ErrorKind::UnexpectedElement.into()),
},
Event::End(ref end) if end.name().as_ref() == b"glyph" => break,
_other => return Err(ErrorKind::MissingCloseTag.into()),
}
buf.clear();
}
self.glyph.load_object_libs()?;
Ok(self.glyph)
}
fn parse_outline(
&mut self,
reader: &mut Reader<&[u8]>,
buf: &mut Vec<u8>,
) -> Result<(), GlifLoadError> {
let mut outline_builder = OutlineBuilder::new();
loop {
match reader.read_event_into(buf)? {
Event::Start(start) => {
let mut new_buf = Vec::new(); match start.name().as_ref() {
b"contour" => {
self.parse_contour(start, reader, &mut new_buf, &mut outline_builder)?;
}
_other => return Err(ErrorKind::UnexpectedElement.into()),
}
}
Event::Empty(start) => {
match start.name().as_ref() {
b"contour" => (), b"component" => self.parse_component(start, &mut outline_builder)?,
_other => return Err(ErrorKind::UnexpectedElement.into()),
}
}
Event::End(ref end) if end.name().as_ref() == b"outline" => break,
Event::Eof => return Err(ErrorKind::UnexpectedEof.into()),
_other => return Err(ErrorKind::UnexpectedElement.into()),
}
buf.clear();
}
let (mut contours, components) = outline_builder.finish()?;
if self.version == VERSION_1 {
for c in &mut contours {
if c.points.len() == 1
&& c.points[0].typ == PointType::Move
&& c.points[0].name.is_some()
{
let anchor_point = c.points.remove(0);
let anchor =
Anchor::new(anchor_point.x, anchor_point.y, anchor_point.name, None, None);
self.glyph.anchors.push(anchor);
}
}
contours.retain(|c| !c.points.is_empty());
}
self.glyph.contours.extend(contours);
self.glyph.components.extend(components);
Ok(())
}
fn parse_identifier(&mut self, value: &str) -> Result<Identifier, GlifLoadError> {
if self.version == VERSION_1 {
return Err(ErrorKind::UnexpectedV1Attribute("identifier").into());
}
let id =
Identifier::new(value).map_err(|_| GlifLoadError::Parse(ErrorKind::BadIdentifier))?;
if !self.seen_identifiers.insert(id.clone()) {
return Err(ErrorKind::DuplicateIdentifier.into());
}
Ok(id)
}
fn parse_contour(
&mut self,
data: BytesStart,
reader: &mut Reader<&[u8]>,
buf: &mut Vec<u8>,
outline_builder: &mut OutlineBuilder,
) -> Result<(), GlifLoadError> {
let mut identifier = None;
for attr in data.attributes() {
if self.version == VERSION_1 {
return Err(ErrorKind::UnexpectedAttribute.into());
}
let attr = attr?;
let value = attr.unescape_value()?;
match attr.key.as_ref() {
b"identifier" => identifier = Some(self.parse_identifier(&value)?),
_other => return Err(ErrorKind::UnexpectedAttribute.into()),
}
}
outline_builder.begin_path(identifier)?;
loop {
match reader.read_event_into(buf)? {
Event::End(ref end) if end.name().as_ref() == b"contour" => break,
Event::Empty(ref start) if start.name().as_ref() == b"point" => {
self.parse_point(start, outline_builder)?;
}
Event::Eof => return Err(ErrorKind::UnexpectedEof.into()),
_other => return Err(ErrorKind::UnexpectedElement.into()),
}
buf.clear();
}
outline_builder.end_path()?;
Ok(())
}
fn parse_component(
&mut self,
start: BytesStart,
outline_builder: &mut OutlineBuilder,
) -> Result<(), GlifLoadError> {
let mut base: Option<Name> = None;
let mut identifier: Option<Identifier> = None;
let mut transform = AffineTransform::default();
for attr in start.attributes() {
let attr = attr?;
let value = attr.unescape_value()?;
let kind = ErrorKind::BadNumber;
match attr.key.as_ref() {
b"xScale" => transform.x_scale = value.parse().map_err(|_| kind)?,
b"xyScale" => transform.xy_scale = value.parse().map_err(|_| kind)?,
b"yxScale" => transform.yx_scale = value.parse().map_err(|_| kind)?,
b"yScale" => transform.y_scale = value.parse().map_err(|_| kind)?,
b"xOffset" => transform.x_offset = value.parse().map_err(|_| kind)?,
b"yOffset" => transform.y_offset = value.parse().map_err(|_| kind)?,
b"base" if value.is_empty() => {
return Err(ErrorKind::ComponentEmptyBase.into());
}
b"base" => {
let name = Name::new(&value).map_err(|_| ErrorKind::InvalidName)?;
let name = self.names.as_ref().map(|n| n.get(&name)).unwrap_or(name);
base = Some(name);
}
b"identifier" => {
identifier = Some(self.parse_identifier(&value)?);
}
_other => return Err(ErrorKind::UnexpectedComponentField.into()),
}
}
match base {
Some(base) => {
outline_builder.add_component(base, transform, identifier);
Ok(())
}
None => Err(ErrorKind::ComponentMissingBase.into()),
}
}
fn parse_lib(
&mut self,
reader: &mut Reader<&[u8]>,
raw_xml: &[u8],
buf: &mut Vec<u8>,
) -> Result<(), GlifLoadError> {
let start = reader.buffer_position() as usize;
let mut end = start;
loop {
match reader.read_event_into(buf)? {
Event::End(ref end) if end.name().as_ref() == b"lib" => break,
Event::Eof => return Err(ErrorKind::UnexpectedEof.into()),
_other => end = reader.buffer_position() as usize,
}
buf.clear();
}
let plist_slice = &raw_xml[start..end];
match plist::Value::from_reader_xml(plist_slice)
.map_err(|_| GlifLoadError::Parse(ErrorKind::BadLib))
.and_then(|x| {
x.into_dictionary().ok_or(GlifLoadError::Parse(ErrorKind::LibMustBeDictionary))
}) {
Ok(dict) => {
self.glyph.lib = dict;
}
Err(e) => log::info!("glyph '{}' {e}", self.glyph.name),
}
Ok(())
}
fn parse_note(
&mut self,
reader: &mut Reader<&[u8]>,
buf: &mut Vec<u8>,
) -> Result<(), GlifLoadError> {
loop {
match reader.read_event_into(buf)? {
Event::End(ref end) if end.name().as_ref() == b"note" => break,
Event::Text(text) => {
self.glyph.note = Some(text.decode()?.into_owned());
}
Event::Eof => return Err(ErrorKind::UnexpectedEof.into()),
_other => (),
}
buf.clear();
}
Ok(())
}
fn parse_point(
&mut self,
data: &BytesStart<'_>,
outline_builder: &mut OutlineBuilder,
) -> Result<(), GlifLoadError> {
let mut name: Option<Name> = None;
let mut x: Option<f64> = None;
let mut y: Option<f64> = None;
let mut typ = PointType::OffCurve;
let mut identifier: Option<Identifier> = None;
let mut smooth = false;
for attr in data.attributes() {
let attr = attr?;
let value = attr.unescape_value()?;
match attr.key.as_ref() {
b"x" => {
x = Some(value.parse().map_err(|_| ErrorKind::BadNumber)?);
}
b"y" => {
y = Some(value.parse().map_err(|_| ErrorKind::BadNumber)?);
}
b"name" => name = Some(Name::new(&value).map_err(|_| ErrorKind::InvalidName)?),
b"type" => {
typ = value.parse()?;
}
b"smooth" => smooth = value == "yes",
b"identifier" => {
identifier = Some(self.parse_identifier(&value)?);
}
_other => return Err(ErrorKind::UnexpectedPointField.into()),
}
}
match (x, y) {
(Some(x), Some(y)) => {
if typ == PointType::OffCurve && smooth {
if !self.has_warned_for_smooth_point {
log::info!(
"glyph '{}' has off-curve point with 'smooth' attribute set",
self.glyph.name
);
}
self.has_warned_for_smooth_point = true;
smooth = false;
}
outline_builder.add_point((x, y), typ, smooth, name, identifier)?;
Ok(())
}
_ => Err(ErrorKind::BadPoint.into()),
}
}
fn parse_advance(&mut self, data: BytesStart) -> Result<(), GlifLoadError> {
let mut width: f64 = 0.0;
let mut height: f64 = 0.0;
for attr in data.attributes() {
let attr = attr?;
match attr.key.as_ref() {
b"width" | b"height" => {
let value = attr.unescape_value()?;
let value: f64 = value.parse().map_err(|_| ErrorKind::BadNumber)?;
match attr.key.as_ref() {
b"width" => width = value,
b"height" => height = value,
_other => unreachable!(),
};
}
_other => return Err(ErrorKind::UnexpectedAttribute.into()),
}
}
self.glyph.width = width;
self.glyph.height = height;
Ok(())
}
fn parse_unicode(&mut self, data: BytesStart) -> Result<(), GlifLoadError> {
for attr in data.attributes() {
let attr = attr?;
match attr.key.as_ref() {
b"hex" => {
let value = attr.unescape_value()?;
let chr = u32::from_str_radix(&value, 16)
.map_err(|_| value.to_string())
.and_then(|n| char::try_from(n).map_err(|_| value.to_string()))
.map_err(|_| ErrorKind::BadHexValue)?;
self.glyph.codepoints.insert(chr);
}
_other => return Err(ErrorKind::UnexpectedAttribute.into()),
}
}
Ok(())
}
fn parse_anchor(&mut self, data: BytesStart) -> Result<(), GlifLoadError> {
let mut x: Option<f64> = None;
let mut y: Option<f64> = None;
let mut name: Option<Name> = None;
let mut color: Option<Color> = None;
let mut identifier: Option<Identifier> = None;
for attr in data.attributes() {
let attr = attr?;
let value = attr.unescape_value()?;
match attr.key.as_ref() {
b"x" => {
x = Some(value.parse().map_err(|_| ErrorKind::BadNumber)?);
}
b"y" => {
y = Some(value.parse().map_err(|_| ErrorKind::BadNumber)?);
}
b"name" => name = Some(Name::new(&value).map_err(|_| ErrorKind::InvalidName)?),
b"color" => color = Some(value.parse().map_err(|_| ErrorKind::BadColor)?),
b"identifier" => {
identifier = Some(self.parse_identifier(&value)?);
}
_other => return Err(ErrorKind::UnexpectedAnchorField.into()),
}
}
match (x, y) {
(Some(x), Some(y)) => {
self.glyph.anchors.push(Anchor::new(x, y, name, color, identifier));
Ok(())
}
_ => Err(ErrorKind::BadAnchor.into()),
}
}
fn parse_guideline(&mut self, data: BytesStart) -> Result<(), GlifLoadError> {
let mut x: Option<f64> = None;
let mut y: Option<f64> = None;
let mut angle: Option<f64> = None;
let mut name: Option<Name> = None;
let mut color: Option<Color> = None;
let mut identifier: Option<Identifier> = None;
for attr in data.attributes() {
let attr = attr?;
let value = attr.unescape_value()?;
match attr.key.as_ref() {
b"x" => {
x = Some(value.parse().map_err(|_| ErrorKind::BadNumber)?);
}
b"y" => {
y = Some(value.parse().map_err(|_| ErrorKind::BadNumber)?);
}
b"angle" => {
let angle_value = value.parse().map_err(|_| ErrorKind::BadNumber)?;
if !(0.0..=360.0).contains(&angle_value) {
return Err(ErrorKind::BadAngle.into());
}
angle = Some(angle_value);
}
b"name" => name = Some(Name::new(&value).map_err(|_| ErrorKind::InvalidName)?),
b"color" => color = Some(value.parse().map_err(|_| ErrorKind::BadColor)?),
b"identifier" => {
identifier = Some(self.parse_identifier(&value)?);
}
_other => return Err(ErrorKind::UnexpectedGuidelineField.into()),
}
}
let line = match (x, y, angle) {
(Some(x), None, None) => Line::Vertical(x),
(None, Some(y), None) => Line::Horizontal(y),
(Some(x), Some(y), Some(degrees)) => Line::Angle { x, y, degrees },
_ => return Err(ErrorKind::BadGuideline.into()),
};
self.glyph.guidelines.push(Guideline::new(line, name, color, identifier));
Ok(())
}
fn parse_image(&mut self, data: BytesStart) -> Result<(), GlifLoadError> {
let mut filename: Option<PathBuf> = None;
let mut color: Option<Color> = None;
let mut transform = AffineTransform::default();
for attr in data.attributes() {
let attr = attr?;
let value = attr.unescape_value()?;
let kind = ErrorKind::BadNumber;
match attr.key.as_ref() {
b"xScale" => transform.x_scale = value.parse().map_err(|_| kind)?,
b"xyScale" => transform.xy_scale = value.parse().map_err(|_| kind)?,
b"yxScale" => transform.yx_scale = value.parse().map_err(|_| kind)?,
b"yScale" => transform.y_scale = value.parse().map_err(|_| kind)?,
b"xOffset" => transform.x_offset = value.parse().map_err(|_| kind)?,
b"yOffset" => transform.y_offset = value.parse().map_err(|_| kind)?,
b"color" => color = Some(value.parse().map_err(|_| ErrorKind::BadColor)?),
b"fileName" => filename = Some(PathBuf::from(value.to_string())),
_other => return Err(ErrorKind::UnexpectedImageField.into()),
}
}
match filename {
Some(file_name) => {
self.glyph.image = Some(
Image::new(file_name, color, transform)
.map_err(|_| GlifLoadError::Parse(ErrorKind::BadImage))?,
);
Ok(())
}
None => Err(ErrorKind::BadImage.into()),
}
}
}
fn start(
reader: &mut Reader<&[u8]>,
buf: &mut Vec<u8>,
names: Option<&NameList>,
) -> Result<(Name, Version), GlifLoadError> {
loop {
match reader.read_event_into(buf)? {
Event::Comment(_) => (),
Event::Decl(_decl) => (),
Event::Start(ref start) if start.name().as_ref() == b"glyph" => {
let mut name: Option<Name> = None;
let mut format_major = 0;
let mut format_minor = 0;
for attr in start.attributes() {
let attr = attr?;
let value = attr.unescape_value()?;
match attr.key.as_ref() {
b"name" => {
let value = Name::new(&value).map_err(|_| ErrorKind::InvalidName)?;
name = Some(names.as_ref().map(|n| n.get(&value)).unwrap_or(value));
}
b"format" => {
format_major = value.parse().map_err(|_| ErrorKind::BadNumber)?;
}
b"formatMinor" => {
format_minor = value.parse().map_err(|_| ErrorKind::BadNumber)?;
}
_other => return Err(ErrorKind::UnexpectedAttribute.into()),
}
}
let name = name.ok_or(ErrorKind::WrongFirstElement)?;
let version = (format_major, format_minor);
if version != VERSION_1 && version != VERSION_2 {
return Err(ErrorKind::UnsupportedGlifVersion.into());
} else {
return Ok((name, version));
}
}
_other => return Err(ErrorKind::WrongFirstElement.into()),
}
buf.clear();
}
}