use crate::cff::dict::{Dict, Operand, Operator};
use crate::cff::index::Index;
use crate::Error;
#[derive(Debug, Clone, PartialEq)]
pub struct PrivateHints {
pub blue_values: Vec<f64>,
pub other_blues: Vec<f64>,
pub family_blues: Vec<f64>,
pub family_other_blues: Vec<f64>,
pub blue_scale: f64,
pub blue_shift: f64,
pub blue_fuzz: f64,
pub std_hw: Option<f64>,
pub std_vw: Option<f64>,
pub stem_snap_h: Vec<f64>,
pub stem_snap_v: Vec<f64>,
pub force_bold: bool,
pub language_group: i32,
pub expansion_factor: f64,
pub initial_random_seed: i32,
}
impl Default for PrivateHints {
fn default() -> Self {
Self {
blue_values: Vec::new(),
other_blues: Vec::new(),
family_blues: Vec::new(),
family_other_blues: Vec::new(),
blue_scale: 0.039625,
blue_shift: 7.0,
blue_fuzz: 1.0,
std_hw: None,
std_vw: None,
stem_snap_h: Vec::new(),
stem_snap_v: Vec::new(),
force_bold: false,
language_group: 0,
expansion_factor: 0.06,
initial_random_seed: 0,
}
}
}
fn undeltify(operands: &[Operand]) -> Vec<f64> {
let mut acc = 0.0_f64;
let mut out = Vec::with_capacity(operands.len());
for op in operands {
acc += op.as_f64();
out.push(acc);
}
out
}
fn number_or(dict: &Dict, op: Operator, default: f64) -> f64 {
dict.get_array(op)
.and_then(|v| v.last().copied())
.map(|o| o.as_f64())
.unwrap_or(default)
}
fn number_opt(dict: &Dict, op: Operator) -> Option<f64> {
dict.get_array(op)
.and_then(|v| v.last().copied())
.map(|o| o.as_f64())
}
fn int_or(dict: &Dict, op: Operator, default: i32) -> i32 {
dict.get_array(op)
.and_then(|v| v.last().copied())
.map(|o| match o {
Operand::Int(n) => n,
Operand::Real(r) => r as i32,
})
.unwrap_or(default)
}
#[derive(Debug, Clone)]
pub(crate) struct PrivateDict<'a> {
pub(crate) default_width_x: f32,
pub(crate) nominal_width_x: f32,
pub(crate) local_subrs: Option<Index<'a>>,
pub(crate) hints: PrivateHints,
}
impl<'a> PrivateDict<'a> {
pub(crate) fn parse(bytes: &'a [u8], private_off: usize, size: usize) -> Result<Self, Error> {
let end = private_off
.checked_add(size)
.ok_or(Error::Cff("Private DICT size overflow"))?;
if end > bytes.len() {
return Err(Error::UnexpectedEof);
}
let dict = Dict::parse(&bytes[private_off..end])?;
let default_width_x = dict
.get_array(Operator::DefaultWidthX)
.and_then(|v| v.last().copied())
.map(|o| o.as_f64() as f32)
.unwrap_or(0.0);
let nominal_width_x = dict
.get_array(Operator::NominalWidthX)
.and_then(|v| v.last().copied())
.map(|o| o.as_f64() as f32)
.unwrap_or(0.0);
let hints = PrivateHints {
blue_values: dict
.get_array(Operator::BlueValues)
.map(undeltify)
.unwrap_or_default(),
other_blues: dict
.get_array(Operator::OtherBlues)
.map(undeltify)
.unwrap_or_default(),
family_blues: dict
.get_array(Operator::FamilyBlues)
.map(undeltify)
.unwrap_or_default(),
family_other_blues: dict
.get_array(Operator::FamilyOtherBlues)
.map(undeltify)
.unwrap_or_default(),
blue_scale: number_or(&dict, Operator::BlueScale, 0.039625),
blue_shift: number_or(&dict, Operator::BlueShift, 7.0),
blue_fuzz: number_or(&dict, Operator::BlueFuzz, 1.0),
std_hw: number_opt(&dict, Operator::StdHW),
std_vw: number_opt(&dict, Operator::StdVW),
stem_snap_h: dict
.get_array(Operator::StemSnapH)
.map(undeltify)
.unwrap_or_default(),
stem_snap_v: dict
.get_array(Operator::StemSnapV)
.map(undeltify)
.unwrap_or_default(),
force_bold: dict
.get_array(Operator::ForceBold)
.and_then(|v| v.last().copied())
.and_then(|o| o.as_int())
.map(|n| n != 0)
.unwrap_or(false),
language_group: int_or(&dict, Operator::LanguageGroup, 0),
expansion_factor: number_or(&dict, Operator::ExpansionFactor, 0.06),
initial_random_seed: int_or(&dict, Operator::InitialRandomSeed, 0),
};
let local_subrs = if let Some(off) = dict.get_int(Operator::Subrs) {
if off < 0 {
return Err(Error::Cff("negative local subrs offset"));
}
let abs = private_off
.checked_add(off as usize)
.ok_or(Error::Cff("local subrs overflow"))?;
Some(Index::parse(bytes, abs)?)
} else {
None
};
Ok(Self {
default_width_x,
nominal_width_x,
local_subrs,
hints,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_when_absent() {
let bytes = vec![];
let p = PrivateDict::parse(&bytes, 0, 0).unwrap();
assert_eq!(p.default_width_x, 0.0);
assert_eq!(p.nominal_width_x, 0.0);
assert!(p.local_subrs.is_none());
assert_eq!(p.hints, PrivateHints::default());
assert_eq!(p.hints.blue_scale, 0.039625);
assert_eq!(p.hints.blue_shift, 7.0);
assert_eq!(p.hints.blue_fuzz, 1.0);
assert!(p.hints.std_hw.is_none());
assert!(p.hints.std_vw.is_none());
assert!(!p.hints.force_bold);
assert_eq!(p.hints.language_group, 0);
assert_eq!(p.hints.expansion_factor, 0.06);
assert_eq!(p.hints.initial_random_seed, 0);
assert!(p.hints.blue_values.is_empty());
}
#[test]
fn picks_up_widths() {
let dict_bytes = vec![248, 136, 20, 248, 36, 21];
let mut whole = vec![0u8, 0, 0, 0];
whole.extend_from_slice(&dict_bytes);
let p = PrivateDict::parse(&whole, 4, dict_bytes.len()).unwrap();
assert_eq!(p.default_width_x, 500.0);
assert_eq!(p.nominal_width_x, 400.0);
}
fn enc_int_1(n: i32) -> Vec<u8> {
assert!((-107..=107).contains(&n));
vec![(n + 139) as u8]
}
fn enc_int_short(n: i32) -> Vec<u8> {
let n = n as i16;
let b = n.to_be_bytes();
vec![28, b[0], b[1]]
}
#[test]
fn blue_values_undeltified() {
let mut dict_bytes = Vec::new();
for n in [-14, 14, 662, 14, -226, 10, 223, 0] {
if (-107..=107).contains(&n) {
dict_bytes.extend(enc_int_1(n));
} else {
dict_bytes.extend(enc_int_short(n));
}
}
dict_bytes.push(6); let p = PrivateDict::parse(&dict_bytes, 0, dict_bytes.len()).unwrap();
assert_eq!(
p.hints.blue_values,
vec![-14.0, 0.0, 662.0, 676.0, 450.0, 460.0, 683.0, 683.0]
);
}
#[test]
fn other_and_family_blues() {
let mut dict_bytes = Vec::new();
dict_bytes.extend(enc_int_short(-250));
dict_bytes.extend(enc_int_1(8));
dict_bytes.push(7);
dict_bytes.extend(enc_int_1(0));
dict_bytes.extend(enc_int_1(10));
dict_bytes.push(8);
let p = PrivateDict::parse(&dict_bytes, 0, dict_bytes.len()).unwrap();
assert_eq!(p.hints.other_blues, vec![-250.0, -242.0]);
assert_eq!(p.hints.family_blues, vec![0.0, 10.0]);
assert!(p.hints.family_other_blues.is_empty());
}
#[test]
fn std_widths_and_stem_snaps() {
let mut dict_bytes = Vec::new();
dict_bytes.extend(enc_int_1(28));
dict_bytes.push(10);
dict_bytes.extend(enc_int_1(84));
dict_bytes.push(11);
dict_bytes.extend(enc_int_1(28));
dict_bytes.extend(enc_int_1(8));
dict_bytes.extend_from_slice(&[12, 12]);
dict_bytes.extend(enc_int_1(84));
dict_bytes.extend(enc_int_1(-10));
dict_bytes.extend_from_slice(&[12, 13]);
let p = PrivateDict::parse(&dict_bytes, 0, dict_bytes.len()).unwrap();
assert_eq!(p.hints.std_hw, Some(28.0));
assert_eq!(p.hints.std_vw, Some(84.0));
assert_eq!(p.hints.stem_snap_h, vec![28.0, 36.0]);
assert_eq!(p.hints.stem_snap_v, vec![84.0, 74.0]);
}
#[test]
fn force_bold_and_language_group() {
let mut dict_bytes = Vec::new();
dict_bytes.extend(enc_int_1(1));
dict_bytes.extend_from_slice(&[12, 14]);
dict_bytes.extend(enc_int_1(1));
dict_bytes.extend_from_slice(&[12, 17]);
let p = PrivateDict::parse(&dict_bytes, 0, dict_bytes.len()).unwrap();
assert!(p.hints.force_bold);
assert_eq!(p.hints.language_group, 1);
assert_eq!(p.hints.blue_scale, 0.039625);
}
#[test]
fn blue_scale_shift_fuzz_overrides() {
let mut dict_bytes = Vec::new();
dict_bytes.extend(enc_int_1(50));
dict_bytes.extend_from_slice(&[12, 9]);
dict_bytes.extend(enc_int_1(5));
dict_bytes.extend_from_slice(&[12, 10]);
dict_bytes.extend(enc_int_1(2));
dict_bytes.extend_from_slice(&[12, 11]);
let p = PrivateDict::parse(&dict_bytes, 0, dict_bytes.len()).unwrap();
assert_eq!(p.hints.blue_scale, 50.0);
assert_eq!(p.hints.blue_shift, 5.0);
assert_eq!(p.hints.blue_fuzz, 2.0);
}
#[test]
fn expansion_factor_and_random_seed() {
let mut dict_bytes = Vec::new();
dict_bytes.extend(enc_int_1(0));
dict_bytes.extend_from_slice(&[12, 18]);
dict_bytes.extend(enc_int_1(42));
dict_bytes.extend_from_slice(&[12, 19]);
let p = PrivateDict::parse(&dict_bytes, 0, dict_bytes.len()).unwrap();
assert_eq!(p.hints.expansion_factor, 0.0);
assert_eq!(p.hints.initial_random_seed, 42);
}
#[test]
fn tn5176_appendix_d_private_dict_example() {
let mut d = Vec::new();
for n in [-14, 14, 662, 14, -226, 10, 223, 0] {
if (-107..=107).contains(&n) {
d.extend(enc_int_1(n));
} else {
d.extend(enc_int_short(n));
}
}
d.push(6);
for n in [262, 8, -488, 1] {
if (-107..=107).contains(&n) {
d.extend(enc_int_1(n));
} else {
d.extend(enc_int_short(n));
}
}
d.push(7);
for n in [-14, 14, 450, 10, 202, 14] {
if (-107..=107).contains(&n) {
d.extend(enc_int_1(n));
} else {
d.extend(enc_int_short(n));
}
}
d.push(8);
for n in [-218, 1, 479, 8, 124, 0] {
if (-107..=107).contains(&n) {
d.extend(enc_int_1(n));
} else {
d.extend(enc_int_short(n));
}
}
d.push(9);
d.extend(enc_int_1(28));
d.push(10);
d.extend(enc_int_1(84));
d.push(11);
d.extend(enc_int_short(250));
d.push(20);
let p = PrivateDict::parse(&d, 0, d.len()).unwrap();
assert_eq!(
p.hints.blue_values,
vec![-14.0, 0.0, 662.0, 676.0, 450.0, 460.0, 683.0, 683.0]
);
assert_eq!(p.hints.other_blues, vec![262.0, 270.0, -218.0, -217.0]);
assert_eq!(
p.hints.family_blues,
vec![-14.0, 0.0, 450.0, 460.0, 662.0, 676.0]
);
assert_eq!(
p.hints.family_other_blues,
vec![-218.0, -217.0, 262.0, 270.0, 394.0, 394.0]
);
assert_eq!(p.hints.std_hw, Some(28.0));
assert_eq!(p.hints.std_vw, Some(84.0));
assert_eq!(p.default_width_x, 250.0);
}
}