Skip to content

Commit 8417ce3

Browse files
authored
Transform CFF glyphs by their matrix (#198)
1 parent eab7348 commit 8417ce3

File tree

5 files changed

+46
-16
lines changed

5 files changed

+46
-16
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,7 @@ impl<'a> Face<'a> {
13371337

13381338
bdat,
13391339
cbdt,
1340-
cff: raw_tables.cff.and_then(cff::Table::parse),
1340+
cff: raw_tables.cff.and_then(|data| cff::Table::parse(data, head.units_per_em)),
13411341
cmap: raw_tables.cmap.and_then(cmap::Table::parse),
13421342
colr,
13431343
ebdt,

src/tables/cff/cff1.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ pub(crate) struct CIDMetadata<'a> {
122122

123123
/// An affine transformation matrix.
124124
#[allow(missing_docs)]
125-
#[derive(Clone, Copy, Debug)]
125+
#[derive(Clone, Copy, Debug, PartialEq)]
126126
pub struct Matrix {
127127
pub sx: f32,
128128
pub ky: f32,
@@ -374,9 +374,12 @@ fn parse_char_string(
374374
local_subrs,
375375
};
376376

377+
let transform = (metadata.units_per_em, metadata.matrix());
378+
let transform = (transform != (1000, Matrix::default())).then_some(transform);
377379
let mut inner_builder = Builder {
378380
builder,
379381
bbox: RectF::new(),
382+
transform,
380383
};
381384

382385
let stack = ArgumentsStack {
@@ -846,11 +849,15 @@ pub struct Table<'a> {
846849
matrix: Matrix,
847850
char_strings: Index<'a>,
848851
kind: FontKind<'a>,
852+
853+
// Copy of Face::units_per_em().
854+
// Required to do glyph outlining, since coordinates must be scaled up by this before applying the `matrix`.
855+
units_per_em: u16,
849856
}
850857

851858
impl<'a> Table<'a> {
852859
/// Parses a table from raw data.
853-
pub fn parse(data: &'a [u8]) -> Option<Self> {
860+
pub fn parse(data: &'a [u8], units_per_em: u16) -> Option<Self> {
854861
let mut s = Stream::new(data);
855862

856863
// Parse Header.
@@ -930,6 +937,7 @@ impl<'a> Table<'a> {
930937
matrix,
931938
char_strings,
932939
kind,
940+
units_per_em,
933941
})
934942
}
935943

src/tables/cff/cff2.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ fn parse_char_string(
250250
let mut inner_builder = Builder {
251251
builder,
252252
bbox: RectF::new(),
253+
transform: None,
253254
};
254255

255256
let stack = ArgumentsStack {

src/tables/cff/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,29 @@ pub enum CFFError {
4545
pub(crate) struct Builder<'a> {
4646
builder: &'a mut dyn OutlineBuilder,
4747
bbox: RectF,
48+
transform: Option<(u16, cff1::Matrix)>,
4849
}
4950

5051
impl<'a> Builder<'a> {
5152
#[inline]
5253
fn move_to(&mut self, x: f32, y: f32) {
54+
let (x, y) = self.transform(x, y);
5355
self.bbox.extend_by(x, y);
5456
self.builder.move_to(x, y);
5557
}
5658

5759
#[inline]
5860
fn line_to(&mut self, x: f32, y: f32) {
61+
let (x, y) = self.transform(x, y);
5962
self.bbox.extend_by(x, y);
6063
self.builder.line_to(x, y);
6164
}
6265

6366
#[inline]
6467
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
68+
let (x1, y1) = self.transform(x1, y1);
69+
let (x2, y2) = self.transform(x2, y2);
70+
let (x, y) = self.transform(x, y);
6571
self.bbox.extend_by(x1, y1);
6672
self.bbox.extend_by(x2, y2);
6773
self.bbox.extend_by(x, y);
@@ -72,6 +78,21 @@ impl<'a> Builder<'a> {
7278
fn close(&mut self) {
7379
self.builder.close();
7480
}
81+
82+
#[inline]
83+
fn transform(&self, x: f32, y: f32) -> (f32, f32) {
84+
let (units_per_em, matrix) = if let Some(transform) = self.transform {
85+
transform
86+
} else {
87+
return (x, y);
88+
};
89+
let (mut tx, mut ty) = (x, y);
90+
tx = tx * matrix.sx + ty * matrix.kx + matrix.tx;
91+
ty = tx * matrix.ky + ty * matrix.sy + matrix.ty;
92+
tx *= units_per_em as f32;
93+
ty *= units_per_em as f32;
94+
(tx, ty)
95+
}
7596
}
7697

7798
/// A type-safe wrapper for string ID.

tests/tables/cff1.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ fn unsupported_version() {
312312
UInt8(0), // absolute offset
313313
]);
314314

315-
assert!(cff::Table::parse(&data).is_none());
315+
assert!(cff::Table::parse(&data, 1000).is_none());
316316
}
317317

318318
#[test]
@@ -360,7 +360,7 @@ fn non_default_header_size() {
360360
UInt8(operator::ENDCHAR),
361361
]);
362362

363-
let table = cff::Table::parse(&data).unwrap();
363+
let table = cff::Table::parse(&data, 1000).unwrap();
364364
let mut builder = Builder(String::new());
365365
let rect = table.outline(GlyphId(0), &mut builder).unwrap();
366366

@@ -377,7 +377,7 @@ macro_rules! test_cs_with_subrs {
377377
#[test]
378378
fn $name() {
379379
let data = gen_cff($glob, $loc, $values);
380-
let table = cff::Table::parse(&data).unwrap();
380+
let table = cff::Table::parse(&data, 1000).unwrap();
381381
let mut builder = Builder(String::new());
382382
let rect = table.outline(GlyphId(0), &mut builder).unwrap();
383383

@@ -398,7 +398,7 @@ macro_rules! test_cs_err {
398398
#[test]
399399
fn $name() {
400400
let data = gen_cff(&[], &[], $values);
401-
let table = cff::Table::parse(&data).unwrap();
401+
let table = cff::Table::parse(&data, 1000).unwrap();
402402
let mut builder = Builder(String::new());
403403
let res = table.outline(GlyphId(0), &mut builder);
404404
assert_eq!(res.unwrap_err(), $err);
@@ -575,7 +575,7 @@ test_cs!(vv_curve_to_with_x, &[
575575
#[test]
576576
fn only_endchar() {
577577
let data = gen_cff(&[], &[], &[UInt8(operator::ENDCHAR)]);
578-
let table = cff::Table::parse(&data).unwrap();
578+
let table = cff::Table::parse(&data, 1000).unwrap();
579579
let mut builder = Builder(String::new());
580580
assert!(table.outline(GlyphId(0), &mut builder).is_err());
581581
}
@@ -800,7 +800,7 @@ fn endchar_in_subr_with_extra_data_1() {
800800
]
801801
);
802802

803-
let table = cff::Table::parse(&data).unwrap();
803+
let table = cff::Table::parse(&data, 1000).unwrap();
804804
let mut builder = Builder(String::new());
805805
let res = table.outline(GlyphId(0), &mut builder);
806806
assert_eq!(res.unwrap_err(), CFFError::DataAfterEndChar);
@@ -827,7 +827,7 @@ fn endchar_in_subr_with_extra_data_2() {
827827
]
828828
);
829829

830-
let table = cff::Table::parse(&data).unwrap();
830+
let table = cff::Table::parse(&data, 1000).unwrap();
831831
let mut builder = Builder(String::new());
832832
let res = table.outline(GlyphId(0), &mut builder);
833833
assert_eq!(res.unwrap_err(), CFFError::DataAfterEndChar);
@@ -854,7 +854,7 @@ fn subr_without_return() {
854854
]
855855
);
856856

857-
let table = cff::Table::parse(&data).unwrap();
857+
let table = cff::Table::parse(&data, 1000).unwrap();
858858
let mut builder = Builder(String::new());
859859
let res = table.outline(GlyphId(0), &mut builder);
860860
assert_eq!(res.unwrap_err(), CFFError::DataAfterEndChar);
@@ -876,7 +876,7 @@ fn recursive_local_subr() {
876876
]
877877
);
878878

879-
let table = cff::Table::parse(&data).unwrap();
879+
let table = cff::Table::parse(&data, 1000).unwrap();
880880
let mut builder = Builder(String::new());
881881
let res = table.outline(GlyphId(0), &mut builder);
882882
assert_eq!(res.unwrap_err(), CFFError::NestingLimitReached);
@@ -898,7 +898,7 @@ fn recursive_global_subr() {
898898
]
899899
);
900900

901-
let table = cff::Table::parse(&data).unwrap();
901+
let table = cff::Table::parse(&data, 1000).unwrap();
902902
let mut builder = Builder(String::new());
903903
let res = table.outline(GlyphId(0), &mut builder);
904904
assert_eq!(res.unwrap_err(), CFFError::NestingLimitReached);
@@ -923,7 +923,7 @@ fn recursive_mixed_subr() {
923923
]
924924
);
925925

926-
let table = cff::Table::parse(&data).unwrap();
926+
let table = cff::Table::parse(&data, 1000).unwrap();
927927
let mut builder = Builder(String::new());
928928
let res = table.outline(GlyphId(0), &mut builder);
929929
assert_eq!(res.unwrap_err(), CFFError::NestingLimitReached);
@@ -952,7 +952,7 @@ fn zero_char_string_offset() {
952952
UInt8(top_dict_operator::CHAR_STRINGS_OFFSET as u8),
953953
]);
954954

955-
assert!(cff::Table::parse(&data).is_none());
955+
assert!(cff::Table::parse(&data, 1000).is_none());
956956
}
957957

958958
#[test]
@@ -978,7 +978,7 @@ fn invalid_char_string_offset() {
978978
UInt8(top_dict_operator::CHAR_STRINGS_OFFSET as u8),
979979
]);
980980

981-
assert!(cff::Table::parse(&data).is_none());
981+
assert!(cff::Table::parse(&data, 1000).is_none());
982982
}
983983

984984
// TODO: return from main

0 commit comments

Comments
 (0)