mod deque;
mod number;
mod stack;
use tarrasque::{ExtractError, ExtractResult, Stream};
use crate::error::{CffError, UNDEFINED, UNSUPPORTED};
use crate::index::{Index};
use self::deque::{NumberDeque};
use self::number::*;
use self::stack::{SubroutineStack};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Charstring<'s> {
global: Index<'s, &'s [u8]>,
local: Option<Index<'s, &'s [u8]>>,
bytes: &'s [u8],
}
impl<'s> Charstring<'s> {
#[inline]
pub(crate) fn new(
global: Index<'s, &'s [u8]>,
local: Option<Index<'s, &'s [u8]>>,
bytes: &'s [u8],
) -> Self {
Charstring { global, local, bytes }
}
#[inline]
pub fn operations(&self) -> OperationIter<'s> {
OperationIter {
global: self.global,
local: self.local,
stream: Stream(self.bytes),
numbers: NumberDeque::new(),
subroutines: SubroutineStack::new(),
current: None,
length: 0,
num_stems: 0,
hstem: 0,
vstem: 0,
point: Point::new(0, 0),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Point {
pub x: i16,
pub y: i16,
}
impl Point {
#[inline]
pub fn new(x: i16, y: i16) -> Point {
Point { x, y }
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Operation {
CounterMask([u8; 12]),
CurveTo(Point, Point, Point),
HintMask([u8; 12]),
HStem(i16, i16),
LineTo(Point),
MoveTo(Point),
VStem(i16, i16),
Width(i16),
}
#[derive(Copy, Clone, Debug)]
pub struct OperationIter<'s> {
global: Index<'s, &'s [u8]>,
local: Option<Index<'s, &'s [u8]>>,
stream: Stream<'s>,
numbers: NumberDeque,
subroutines: SubroutineStack<'s>,
current: Option<u16>,
length: u8,
num_stems: u8,
hstem: i16,
vstem: i16,
point: Point,
}
impl<'s> OperationIter<'s> {
#[inline]
fn call(
&mut self, subroutines: Index<'s, &'s [u8]>, index: i32
) -> ExtractResult<'s, ()> {
let index = if subroutines.len() < 1240 {
index + 107
} else if subroutines.len() < 33900 {
index + 1131
} else {
index + 32768
};
if let Some(subroutine) = subroutines.get(index as usize) {
self.subroutines.push(self.stream)?;
self.stream = Stream(&subroutine);
Ok(())
} else {
Err(ExtractError::Code(UNDEFINED))
}
}
#[inline]
fn mask(
&mut self, operator: u16, f: impl Fn([u8; 12]) -> Operation
) -> ExtractResult<'s, Option<Operation>> {
if !self.numbers.is_empty() {
self.num_stems += 1;
let x = self.vstem + self.numbers.pop_bottom::<i16>()?;
self.vstem = x + self.numbers.pop_bottom::<i16>()?;
self.current = Some(operator);
return Ok(Some(Operation::VStem(x, self.vstem)));
}
let num_bytes = (self.num_stems + 7) / 8;
let bytes: &[u8] = self.stream.extract(num_bytes as usize)?;
let mut mask = [0; 12];
for (index, byte) in bytes.iter().enumerate() {
mask[index] = *byte;
}
self.current = None;
Ok(Some(f(mask)))
}
#[inline]
fn hstem(&mut self) -> ExtractResult<'s, Option<Operation>> {
if self.numbers.len() % 2 != 0 {
self.current = Some(1);
Ok(Some(Operation::Width(self.numbers.pop_bottom()?)))
} else {
self.num_stems += 1;
let y = self.hstem + self.numbers.pop_bottom::<i16>()?;
self.hstem = y + self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(1) } else { None };
Ok(Some(Operation::HStem(y, self.hstem)))
}
}
#[inline]
fn vstem(&mut self) -> ExtractResult<'s, Option<Operation>> {
if self.numbers.len() % 2 != 0 {
self.current = Some(3);
Ok(Some(Operation::Width(self.numbers.pop_bottom()?)))
} else {
self.num_stems += 1;
let x = self.vstem + self.numbers.pop_bottom::<i16>()?;
self.vstem = x + self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(3) } else { None };
Ok(Some(Operation::VStem(x, self.vstem)))
}
}
#[inline]
fn vmoveto(&mut self) -> ExtractResult<'s, Option<Operation>> {
if self.numbers.len() == 2 {
self.current = Some(4);
Ok(Some(Operation::Width(self.numbers.pop_bottom()?)))
} else {
self.point.y += self.numbers.pop_bottom::<i16>()?;
self.current = None;
Ok(Some(Operation::MoveTo(self.point)))
}
}
#[inline]
fn rlineto(
&mut self, operator: u16
) -> ExtractResult<'s, Option<Operation>> {
self.point.x += self.numbers.pop_bottom::<i16>()?;
self.point.y += self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(operator) } else { None };
Ok(Some(Operation::LineTo(self.point)))
}
#[inline]
fn hlineto(&mut self) -> ExtractResult<'s, Option<Operation>> {
if (self.length % 2 != 0) == (self.numbers.len() % 2 != 0) {
self.point.x += self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(6) } else { None };
Ok(Some(Operation::LineTo(self.point)))
} else {
self.point.y += self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(6) } else { None };
Ok(Some(Operation::LineTo(self.point)))
}
}
#[inline]
fn vlineto(&mut self) -> ExtractResult<'s, Option<Operation>> {
if (self.length % 2 != 0) == (self.numbers.len() % 2 != 0) {
self.point.y += self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(7) } else { None };
Ok(Some(Operation::LineTo(self.point)))
} else {
self.point.x += self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(7) } else { None };
Ok(Some(Operation::LineTo(self.point)))
}
}
#[inline]
fn rrcurveto(
&mut self, operator: u16
) -> ExtractResult<'s, Option<Operation>> {
let c1x = self.point.x + self.numbers.pop_bottom::<i16>()?;
let c1y = self.point.y + self.numbers.pop_bottom::<i16>()?;
let c2x = c1x + self.numbers.pop_bottom::<i16>()?;
let c2y = c1y + self.numbers.pop_bottom::<i16>()?;
self.point.x = c2x + self.numbers.pop_bottom::<i16>()?;
self.point.y = c2y + self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(operator) } else { None };
let c1 = Point::new(c1x, c1y);
let c2 = Point::new(c2x, c2y);
Ok(Some(Operation::CurveTo(c1, c2, self.point)))
}
#[inline]
fn callsubr(&mut self) -> ExtractResult<'s, Option<Operation>> {
if let Some(local) = self.local {
let index = i32::from(self.numbers.pop_top::<i16>()?);
self.call(local, index)?;
Ok(None)
} else {
Err(ExtractError::Code(UNDEFINED))
}
}
#[inline]
fn rmoveto(&mut self) -> ExtractResult<'s, Option<Operation>> {
if self.numbers.len() == 3 {
self.current = Some(21);
Ok(Some(Operation::Width(self.numbers.pop_bottom()?)))
} else {
self.point.x += self.numbers.pop_bottom::<i16>()?;
self.point.y += self.numbers.pop_bottom::<i16>()?;
self.current = None;
Ok(Some(Operation::MoveTo(self.point)))
}
}
#[inline]
fn hmoveto(&mut self) -> ExtractResult<'s, Option<Operation>> {
if self.numbers.len() == 2 {
self.current = Some(22);
Ok(Some(Operation::Width(self.numbers.pop_bottom()?)))
} else {
self.point.x += self.numbers.pop_bottom::<i16>()?;
self.current = None;
Ok(Some(Operation::MoveTo(self.point)))
}
}
#[inline]
fn rcurveline(&mut self) -> ExtractResult<'s, Option<Operation>> {
if self.numbers.len() == 2 {
self.rlineto(24)
} else {
self.rrcurveto(24)
}
}
#[inline]
fn rlinecurve(&mut self) -> ExtractResult<'s, Option<Operation>> {
if self.numbers.len() == 6 {
self.rrcurveto(25)
} else {
self.rlineto(25)
}
}
#[inline]
fn vvcurveto(&mut self) -> ExtractResult<'s, Option<Operation>> {
let c1x = if self.numbers.len() % 2 != 0 {
self.point.x + self.numbers.pop_bottom::<i16>()?
} else {
self.point.x
};
let c1y = self.point.y + self.numbers.pop_bottom::<i16>()?;
let c2x = c1x + self.numbers.pop_bottom::<i16>()?;
let c2y = c1y + self.numbers.pop_bottom::<i16>()?;
self.point.x = c2x;
self.point.y = c2y + self.numbers.pop_bottom::<i16>()?;
self.current = if !self.numbers.is_empty() { Some(26) } else { None };
let c1 = Point::new(c1x, c1y);
let c2 = Point::new(c2x, c2y);
Ok(Some(Operation::CurveTo(c1, c2, self.point)))
}
#[inline]
fn hhcurveto(&mut self) -> ExtractResult<'s, Option<Operation>> {
let c1y = if self.numbers.len() % 2 != 0 {
self.point.y + self.numbers.pop_bottom::<i16>()?
} else {
self.point.y
};
let c1x = self.point.x + self.numbers.pop_bottom::<i16>()?;
let c2x = c1x + self.numbers.pop_bottom::<i16>()?;
let c2y = c1y + self.numbers.pop_bottom::<i16>()?;
self.point.x = c2x + self.numbers.pop_bottom::<i16>()?;
self.point.y = c2y;
self.current = if !self.numbers.is_empty() { Some(27) } else { None };
let c1 = Point::new(c1x, c1y);
let c2 = Point::new(c2x, c2y);
Ok(Some(Operation::CurveTo(c1, c2, self.point)))
}
#[inline]
fn callgsubr(&mut self) -> ExtractResult<'s, Option<Operation>> {
let global = self.global;
let index = i32::from(self.numbers.pop_top::<i16>()?);
self.call(global, index)?;
Ok(None)
}
#[inline]
fn vhcurveto(&mut self) -> ExtractResult<'s, Option<Operation>> {
let eight1 = ((self.length & 0xFE) % 8) == 0;
let eight2 = ((self.numbers.len() & 0xFE) % 8) == 0;
let vertical = eight1 == eight2;
let (c1x, c1y, c2x, c2y) = if vertical {
let c1x = self.point.x;
let c1y = self.point.y + self.numbers.pop_bottom::<i16>()?;
let c2x = c1x + self.numbers.pop_bottom::<i16>()?;
let c2y = c1y + self.numbers.pop_bottom::<i16>()?;
self.point.x = c2x + self.numbers.pop_bottom::<i16>()?;
self.point.y = if self.numbers.len() == 1 {
c2y + self.numbers.pop_bottom::<i16>()?
} else {
c2y
};
(c1x, c1y, c2x, c2y)
} else {
let c1x = self.point.x + self.numbers.pop_bottom::<i16>()?;
let c1y = self.point.y;
let c2x = c1x + self.numbers.pop_bottom::<i16>()?;
let c2y = c1y + self.numbers.pop_bottom::<i16>()?;
self.point.y = c2y + self.numbers.pop_bottom::<i16>()?;
self.point.x = if self.numbers.len() == 1 {
c2x + self.numbers.pop_bottom::<i16>()?
} else {
c2x
};
(c1x, c1y, c2x, c2y)
};
self.current = if !self.numbers.is_empty() { Some(30) } else { None };
let c1 = Point::new(c1x, c1y);
let c2 = Point::new(c2x, c2y);
Ok(Some(Operation::CurveTo(c1, c2, self.point)))
}
#[inline]
fn hvcurveto(&mut self) -> ExtractResult<'s, Option<Operation>> {
let eight1 = ((self.length & 0xFE) % 8) == 0;
let eight2 = ((self.numbers.len() & 0xFE) % 8) == 0;
let horizontal = eight1 == eight2;
let (c1x, c1y, c2x, c2y) = if horizontal {
let c1x = self.point.x + self.numbers.pop_bottom::<i16>()?;
let c1y = self.point.y;
let c2x = c1x + self.numbers.pop_bottom::<i16>()?;
let c2y = c1y + self.numbers.pop_bottom::<i16>()?;
self.point.y = c2y + self.numbers.pop_bottom::<i16>()?;
self.point.x = if self.numbers.len() == 1 {
c2x + self.numbers.pop_bottom::<i16>()?
} else {
c2x
};
(c1x, c1y, c2x, c2y)
} else {
let c1x = self.point.x;
let c1y = self.point.y + self.numbers.pop_bottom::<i16>()?;
let c2x = c1x + self.numbers.pop_bottom::<i16>()?;
let c2y = c1y + self.numbers.pop_bottom::<i16>()?;
self.point.x = c2x + self.numbers.pop_bottom::<i16>()?;
self.point.y = if self.numbers.len() == 1 {
c2y + self.numbers.pop_bottom::<i16>()?
} else {
c2y
};
(c1x, c1y, c2x, c2y)
};
self.current = if !self.numbers.is_empty() { Some(31) } else { None };
let c1 = Point::new(c1x, c1y);
let c2 = Point::new(c2x, c2y);
Ok(Some(Operation::CurveTo(c1, c2, self.point)))
}
#[inline]
fn execute(
&mut self, operator: u16
) -> ExtractResult<'s, Option<Operation>> {
match operator {
1 | 18 => self.hstem(),
3 | 23 => self.vstem(),
4 => self.vmoveto(),
5 => self.rlineto(5),
6 => self.hlineto(),
7 => self.vlineto(),
8 => self.rrcurveto(8),
10 => self.callsubr(),
11 => { self.stream = self.subroutines.pop()?; Ok(None) },
14 => { self.stream.skip(usize::max_value()); Ok(None) },
19 => self.mask(19, Operation::HintMask),
20 => self.mask(20, Operation::CounterMask),
21 => self.rmoveto(),
22 => self.hmoveto(),
24 => self.rcurveline(),
25 => self.rlinecurve(),
26 => self.vvcurveto(),
27 => self.hhcurveto(),
29 => self.callgsubr(),
30 => self.vhcurveto(),
31 => self.hvcurveto(),
_ => Err(ExtractError::Code(UNSUPPORTED)),
}
}
}
impl<'s> Iterator for OperationIter<'s> {
type Item = Result<Operation, CffError<'s>>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
macro_rules! attempt {
($expr:expr) => ({
match $expr {
Ok(ok) => ok,
Err(err) => return Some(Err(err.into())),
}
});
}
while !self.stream.is_empty() {
if let Some(operator) = self.current {
let operation = self.execute(operator).map(Option::unwrap);
return Some(Ok(attempt!(operation)));
}
let byte: u8 = attempt!(self.stream.extract(()));
if byte == 28 || byte >= 32 {
let number = attempt!(self.stream.extract(byte));
attempt!(self.numbers.push_top(number));
continue;
}
let mut operator = u16::from(byte);
if operator == 12 {
let low = attempt!(self.stream.extract::<u8, _>(()));
operator = 3072 + u16::from(low);
}
self.length = self.numbers.len() as u8;
if let Some(operation) = attempt!(self.execute(operator)) {
return Some(Ok(operation));
}
}
None
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! assert_charstring_eq {
([$(($($number:tt)+)), *], [$($operation:expr), *,]) => ({
let mut bytes = [0; 256];
let mut size = 0;
$(assert_charstring_eq!(NUMBER: bytes, size, $($number)+);)*;
let global = Stream(&[0, 0, 1, 1]).extract(()).unwrap();
let charstring = Charstring::new(global, None, &bytes[0..size]);
size = 0;
let mut operations = [Operation::Width(0); 32];
for operation in charstring.operations() {
operations[size] = operation.unwrap();
size += 1;
}
assert_eq!(&operations[..size], &[$($operation), *]);
});
(NUMBER: $bytes:expr, $size:expr, [$number:expr]) => ({
$bytes[$size] = $number;
$size += 1;
});
(NUMBER: $bytes:expr, $size:expr, $number:expr) => ({
let mut number: i16 = $number;
if number >= -107 && number <= 107 {
$bytes[$size] = (number + 139) as u8;
$size += 1;
} else if number >= 108 && number <= 1131 {
number -= 108;
$bytes[$size] = ((number / 256) + 247) as u8;
$bytes[$size + 1] = (number % 256) as u8;
$size += 2;
} else if number >= -1131 && number <= -108 {
number = (number + 108).abs();
$bytes[$size] = ((number / 256) + 251) as u8;
$bytes[$size + 1] = (number % 256) as u8;
$size += 2;
} else {
$bytes[$size] = 28;
$bytes[$size + 1] = (number >> 8) as u8;
$bytes[$size + 2] = (number & 0xFF) as u8;
$size += 3;
}
});
}
#[test]
fn test_charstring_hstem() {
assert_charstring_eq!([(121), (-21), ([1]), ([14])], [
Operation::HStem(121, 100),
]);
assert_charstring_eq!([(121), (-21), (400), (-20), ([1]), ([14])], [
Operation::HStem(121, 100),
Operation::HStem(500, 480),
]);
assert_charstring_eq!([(32), (121), (-21), ([1]), ([14])], [
Operation::Width(32),
Operation::HStem(121, 100),
]);
assert_charstring_eq!([(32), (121), (-21), (400), (-20), ([1]), ([14])], [
Operation::Width(32),
Operation::HStem(121, 100),
Operation::HStem(500, 480),
]);
}
#[test]
fn test_charstring_vstem() {
assert_charstring_eq!([(121), (-21), ([3]), ([14])], [
Operation::VStem(121, 100),
]);
assert_charstring_eq!([(121), (-21), (400), (-20), ([3]), ([14])], [
Operation::VStem(121, 100),
Operation::VStem(500, 480),
]);
assert_charstring_eq!([(32), (121), (-21), ([3]), ([14])], [
Operation::Width(32),
Operation::VStem(121, 100),
]);
assert_charstring_eq!([(32), (121), (-21), (400), (-20), ([3]), ([14])], [
Operation::Width(32),
Operation::VStem(121, 100),
Operation::VStem(500, 480),
]);
}
#[test]
fn test_charstring_vmoveto() {
assert_charstring_eq!([(32), ([4]), ([14])], [
Operation::MoveTo(Point::new(0, 32)),
]);
assert_charstring_eq!([(32), (64), ([4]), ([14])], [
Operation::Width(32),
Operation::MoveTo(Point::new(0, 64)),
]);
}
#[test]
fn test_charstring_rlineto() {
assert_charstring_eq!([(32), (64), ([5]), ([14])], [
Operation::LineTo(Point::new(32, 64)),
]);
assert_charstring_eq!([(32), (64), (96), (128), ([5]), ([14])], [
Operation::LineTo(Point::new(32, 64)),
Operation::LineTo(Point::new(128, 192)),
]);
}
#[test]
fn test_charstring_hlineto() {
assert_charstring_eq!([(32), ([6]), ([14])], [
Operation::LineTo(Point::new(32, 0)),
]);
assert_charstring_eq!([(32), (64), ([6]), ([14])], [
Operation::LineTo(Point::new(32, 0)),
Operation::LineTo(Point::new(32, 64)),
]);
assert_charstring_eq!([(32), (64), (96), ([6]), ([14])], [
Operation::LineTo(Point::new(32, 0)),
Operation::LineTo(Point::new(32, 64)),
Operation::LineTo(Point::new(128, 64)),
]);
assert_charstring_eq!([(32), (64), (96), (128), ([6]), ([14])], [
Operation::LineTo(Point::new(32, 0)),
Operation::LineTo(Point::new(32, 64)),
Operation::LineTo(Point::new(128, 64)),
Operation::LineTo(Point::new(128, 192)),
]);
}
#[test]
fn test_charstring_vlineto() {
assert_charstring_eq!([(32), ([7]), ([14])], [
Operation::LineTo(Point::new(0, 32)),
]);
assert_charstring_eq!([(32), (64), ([7]), ([14])], [
Operation::LineTo(Point::new(0, 32)),
Operation::LineTo(Point::new(64, 32)),
]);
assert_charstring_eq!([(32), (64), (96), ([7]), ([14])], [
Operation::LineTo(Point::new(0, 32)),
Operation::LineTo(Point::new(64, 32)),
Operation::LineTo(Point::new(64, 128)),
]);
assert_charstring_eq!([(32), (64), (96), (128), ([7]), ([14])], [
Operation::LineTo(Point::new(0, 32)),
Operation::LineTo(Point::new(64, 32)),
Operation::LineTo(Point::new(64, 128)),
Operation::LineTo(Point::new(192, 128)),
]);
}
#[test]
fn test_charstring_rrcurveto() {
assert_charstring_eq!([(32), (64), (96), (128), (160), (192), ([8]), ([14])], [
Operation::CurveTo(Point::new(32, 64), Point::new(128, 192), Point::new(288, 384)),
]);
assert_charstring_eq!([
(32), (64), (96), (128), (160), (192),
(-32), (-64), (-96), (-128), (-160), (-192), ([8]),
([14])
], [
Operation::CurveTo(Point::new(32, 64), Point::new(128, 192), Point::new(288, 384)),
Operation::CurveTo(Point::new(256, 320), Point::new(160, 192), Point::new(0, 0)),
]);
}
#[test]
fn test_charstring_hintmask() {
assert_charstring_eq!([
(121), (-21), (400), (-20), ([1]),
(121), (-21), (400), (-20), ([19]),
([0b0101_0000]),
([14])],
[
Operation::HStem(121, 100),
Operation::HStem(500, 480),
Operation::VStem(121, 100),
Operation::VStem(500, 480),
Operation::HintMask([0b0101_0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
]);
}
#[test]
fn test_charstring_cntrmask() {
assert_charstring_eq!([
(121), (-21), (400), (-20), ([1]),
(121), (-21), (400), (-20), ([20]),
([0b0101_0000]),
([14])],
[
Operation::HStem(121, 100),
Operation::HStem(500, 480),
Operation::VStem(121, 100),
Operation::VStem(500, 480),
Operation::CounterMask([0b0101_0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
]);
}
#[test]
fn test_charstring_rmoveto() {
assert_charstring_eq!([(32), (64), ([21]), ([14])], [
Operation::MoveTo(Point::new(32, 64)),
]);
assert_charstring_eq!([(32), (64), (96), ([21]), ([14])], [
Operation::Width(32),
Operation::MoveTo(Point::new(64, 96)),
]);
}
#[test]
fn test_charstring_hmoveto() {
assert_charstring_eq!([(32), ([22]), ([14])], [
Operation::MoveTo(Point::new(32, 0)),
]);
assert_charstring_eq!([(32), (64), ([22]), ([14])], [
Operation::Width(32),
Operation::MoveTo(Point::new(64, 0)),
]);
}
#[test]
fn test_charstring_rcurveline() {
assert_charstring_eq!([
(32), (64), (96), (128), (160), (192),
(-32), (-64), ([24]),
([14])
], [
Operation::CurveTo(Point::new(32, 64), Point::new(128, 192), Point::new(288, 384)),
Operation::LineTo(Point::new(256, 320)),
]);
assert_charstring_eq!([
(32), (64), (96), (128), (160), (192),
(-32), (-64), (-96), (-128), (-160), (-192),
(32), (64), ([24]),
([14])
], [
Operation::CurveTo(Point::new(32, 64), Point::new(128, 192), Point::new(288, 384)),
Operation::CurveTo(Point::new(256, 320), Point::new(160, 192), Point::new(0, 0)),
Operation::LineTo(Point::new(32, 64)),
]);
}
#[test]
fn test_charstring_rlinecurve() {
assert_charstring_eq!([
(32), (64),
(96), (128), (160), (192), (-32), (-64), ([25]),
([14])
], [
Operation::LineTo(Point::new(32, 64)),
Operation::CurveTo(Point::new(128, 192), Point::new(288, 384), Point::new(256, 320)),
]);
assert_charstring_eq!([
(32), (64),
(96), (128),
(160), (192), (-32), (-64), (-128), (-160), ([25]),
([14])
], [
Operation::LineTo(Point::new(32, 64)),
Operation::LineTo(Point::new(128, 192)),
Operation::CurveTo(Point::new(288, 384), Point::new(256, 320), Point::new(128, 160)),
]);
}
#[test]
fn test_charstring_vvcurveto() {
assert_charstring_eq!([(32), (64), (96), (128), ([26]), ([14])], [
Operation::CurveTo(Point::new(0, 32), Point::new(64, 128), Point::new(64, 256)),
]);
assert_charstring_eq!([(32), (64), (96), (128), (160), ([26]), ([14])], [
Operation::CurveTo(Point::new(32, 64), Point::new(128, 192), Point::new(128, 352)),
]);
assert_charstring_eq!([
(32), (64), (96), (128),
(-128), (-96), (-64), (-32), ([26]),
([14])
], [
Operation::CurveTo(Point::new(0, 32), Point::new(64, 128), Point::new(64, 256)),
Operation::CurveTo(Point::new(64, 128), Point::new(-32, 64), Point::new(-32, 32)),
]);
assert_charstring_eq!([
(32), (64), (96), (128), (160),
(-160), (-128), (-96), (-64), ([26]),
([14])
], [
Operation::CurveTo(Point::new(32, 64), Point::new(128, 192), Point::new(128, 352)),
Operation::CurveTo(Point::new(128, 192), Point::new(0, 96), Point::new(0, 32)),
]);
}
#[test]
fn test_charstring_hhcurveto() {
assert_charstring_eq!([(32), (64), (96), (128), ([27]), ([14])], [
Operation::CurveTo(Point::new(32, 0), Point::new(96, 96), Point::new(224, 96)),
]);
assert_charstring_eq!([(32), (64), (96), (128), (160), ([27]), ([14])], [
Operation::CurveTo(Point::new(64, 32), Point::new(160, 160), Point::new(320, 160)),
]);
assert_charstring_eq!([
(32), (64), (96), (128),
(-128), (-96), (-64), (-32), ([27]),
([14])
], [
Operation::CurveTo(Point::new(32, 0), Point::new(96, 96), Point::new(224, 96)),
Operation::CurveTo(Point::new(96, 96), Point::new(0, 32), Point::new(-32, 32)),
]);
assert_charstring_eq!([
(32), (64), (96), (128), (160),
(-160), (-128), (-96), (-64), ([27]),
([14])
], [
Operation::CurveTo(Point::new(64, 32), Point::new(160, 160), Point::new(320, 160)),
Operation::CurveTo(Point::new(160, 160), Point::new(32, 64), Point::new(-32, 64)),
]);
}
#[test]
fn test_charstring_vhcurveto() {
assert_charstring_eq!([(32), (64), (96), (128), ([30]), ([14])], [
Operation::CurveTo(Point::new(0, 32), Point::new(64, 128), Point::new(192, 128)),
]);
assert_charstring_eq!([(32), (64), (96), (128), (160), ([30]), ([14])], [
Operation::CurveTo(Point::new(0, 32), Point::new(64, 128), Point::new(192, 288)),
]);
assert_charstring_eq!([
(32), (64), (96), (128),
(160), (192), (-192), (-160),
(-128), (-96), (-64), (-32), ([30]),
([14])
], [
Operation::CurveTo(Point::new(0, 32), Point::new(64, 128), Point::new(192, 128)),
Operation::CurveTo(Point::new(352, 128), Point::new(544, -64), Point::new(544, -224)),
Operation::CurveTo(Point::new(544, -352), Point::new(448, -416), Point::new(416, -416)),
]);
assert_charstring_eq!([
(32), (64), (96), (128),
(160), (192), (-192), (-160),
(-128), (-96), (-64), (-32), (32), ([30]),
([14])
], [
Operation::CurveTo(Point::new(0, 32), Point::new(64, 128), Point::new(192, 128)),
Operation::CurveTo(Point::new(352, 128), Point::new(544, -64), Point::new(544, -224)),
Operation::CurveTo(Point::new(544, -352), Point::new(448, -416), Point::new(416, -384)),
]);
}
#[test]
fn test_charstring_hvcurveto() {
assert_charstring_eq!([(32), (64), (96), (128), ([31]), ([14])], [
Operation::CurveTo(Point::new(32, 0), Point::new(96, 96), Point::new(96, 224)),
]);
assert_charstring_eq!([(32), (64), (96), (128), (160), ([31]), ([14])], [
Operation::CurveTo(Point::new(32, 0), Point::new(96, 96), Point::new(256, 224)),
]);
assert_charstring_eq!([
(32), (64), (96), (128),
(160), (192), (-192), (-160),
(-128), (-96), (-64), (-32), ([31]),
([14])
], [
Operation::CurveTo(Point::new(32, 0), Point::new(96, 96), Point::new(96, 224)),
Operation::CurveTo(Point::new(96, 384), Point::new(288, 192), Point::new(128, 192)),
Operation::CurveTo(Point::new(0, 192), Point::new(-96, 128), Point::new(-96, 96)),
]);
assert_charstring_eq!([
(32), (64), (96), (128),
(160), (192), (-192), (-160),
(-128), (-96), (-64), (-32), (32), ([31]),
([14])
], [
Operation::CurveTo(Point::new(32, 0), Point::new(96, 96), Point::new(96, 224)),
Operation::CurveTo(Point::new(96, 384), Point::new(288, 192), Point::new(128, 192)),
Operation::CurveTo(Point::new(0, 192), Point::new(-96, 128), Point::new(-64, 96)),
]);
}
}