Skip to content

Commit 8389962

Browse files
committed
Transform CFF glyphs by their matrix
Some fonts use the shear part of this matrix to do an oblique version. See discussion at typst/pixglyph#3
1 parent a6813b4 commit 8389962

File tree

5 files changed

+44
-16
lines changed

5 files changed

+44
-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: 19 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,19 @@ 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 Some((units_per_em, matrix)) = self.transform else {
85+
return (x, y);
86+
};
87+
let (mut tx, mut ty) = (x, y);
88+
tx = tx * matrix.sx + ty * matrix.kx + matrix.tx;
89+
ty = tx * matrix.ky + ty * matrix.sy + matrix.ty;
90+
tx *= units_per_em as f32;
91+
ty *= units_per_em as f32;
92+
(tx, ty)
93+
}
7594
}
7695

7796
/// 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)