font/sfnt: support TrueType glyph transformations.
Change-Id: Iea2387b5e30dd0fff53e2808b25599c3be5b1cdb
Reviewed-on: https://go-review.googlesource.com/38210
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/font/sfnt/proprietary_test.go b/font/sfnt/proprietary_test.go
index 4c6a633..9137fea 100644
--- a/font/sfnt/proprietary_test.go
+++ b/font/sfnt/proprietary_test.go
@@ -88,7 +88,7 @@
}
func TestProprietaryMicrosoftArial(t *testing.T) {
- testProprietary(t, "microsoft", "Arial.ttf", 1200, 599)
+ testProprietary(t, "microsoft", "Arial.ttf", 1200, -1)
}
func TestProprietaryMicrosoftComicSansMS(t *testing.T) {
@@ -96,7 +96,7 @@
}
func TestProprietaryMicrosoftTimesNewRoman(t *testing.T) {
- testProprietary(t, "microsoft", "Times_New_Roman.ttf", 1200, 423)
+ testProprietary(t, "microsoft", "Times_New_Roman.ttf", 1200, -1)
}
func TestProprietaryMicrosoftWebdings(t *testing.T) {
@@ -534,6 +534,42 @@
translate(339, 650, lineTo(371, 1194)),
translate(339, 650, lineTo(222, 1194)),
},
+
+ '﴾': { // U+FD3E ORNATE LEFT PARENTHESIS.
+ // - contour #0
+ moveTo(560, -384),
+ lineTo(516, -429),
+ quadTo(412, -304, 361, -226),
+ quadTo(258, -68, 201, 106),
+ quadTo(127, 334, 127, 595),
+ quadTo(127, 845, 201, 1069),
+ quadTo(259, 1246, 361, 1404),
+ quadTo(414, 1487, 514, 1608),
+ lineTo(560, 1566),
+ quadTo(452, 1328, 396, 1094),
+ quadTo(336, 845, 336, 603),
+ quadTo(336, 359, 370, 165),
+ quadTo(398, 8, 454, -142),
+ quadTo(482, -217, 560, -384),
+ },
+
+ '﴿': { // U+FD3F ORNATE RIGHT PARENTHESIS
+ // - contour #0
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, moveTo(560, -384)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, lineTo(516, -429)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(412, -304, 361, -226)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(258, -68, 201, 106)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(127, 334, 127, 595)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(127, 845, 201, 1069)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(259, 1246, 361, 1404)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(414, 1487, 514, 1608)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, lineTo(560, 1566)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(452, 1328, 396, 1094)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(336, 845, 336, 603)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(336, 359, 370, 165)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(398, 8, 454, -142)),
+ transform(-1<<14, 0, 0, +1<<14, 653, 0, quadTo(482, -217, 560, -384)),
+ },
},
}
diff --git a/font/sfnt/sfnt.go b/font/sfnt/sfnt.go
index 71aae52..e7c6670 100644
--- a/font/sfnt/sfnt.go
+++ b/font/sfnt/sfnt.go
@@ -1025,8 +1025,13 @@
segments []Segment
// compoundStack holds the components of a TrueType compound glyph.
compoundStack [maxCompoundStackSize]struct {
- glyphIndex GlyphIndex
- dx, dy int16
+ glyphIndex GlyphIndex
+ dx, dy int16
+ hasTransform bool
+ transformXX int16
+ transformXY int16
+ transformYX int16
+ transformYY int16
}
// psi is a PostScript interpreter for when the Font is an OpenType/CFF
// font.
@@ -1061,12 +1066,31 @@
SegmentOpCubeTo
)
-func translate(dx, dy fixed.Int26_6, s Segment) Segment {
- s.Args[0] += dx
- s.Args[1] += dy
- s.Args[2] += dx
- s.Args[3] += dy
- s.Args[4] += dx
- s.Args[5] += dy
- return s
+// translateArgs applies a translation to args.
+func translateArgs(args *[6]fixed.Int26_6, dx, dy fixed.Int26_6) {
+ args[0] += dx
+ args[1] += dy
+ args[2] += dx
+ args[3] += dy
+ args[4] += dx
+ args[5] += dy
+}
+
+// transformArgs applies an affine transformation to args. The t?? arguments
+// are 2.14 fixed point values.
+func transformArgs(args *[6]fixed.Int26_6, txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6) {
+ args[0], args[1] = tform(txx, txy, tyx, tyy, dx, dy, args[0], args[1])
+ args[2], args[3] = tform(txx, txy, tyx, tyy, dx, dy, args[2], args[3])
+ args[4], args[5] = tform(txx, txy, tyx, tyy, dx, dy, args[4], args[5])
+}
+
+func tform(txx, txy, tyx, tyy int16, dx, dy, x, y fixed.Int26_6) (newX, newY fixed.Int26_6) {
+ const half = 1 << 13
+ newX = dx +
+ fixed.Int26_6((int64(x)*int64(txx)+half)>>14) +
+ fixed.Int26_6((int64(y)*int64(txy)+half)>>14)
+ newY = dy +
+ fixed.Int26_6((int64(x)*int64(tyx)+half)>>14) +
+ fixed.Int26_6((int64(y)*int64(tyy)+half)>>14)
+ return newX, newY
}
diff --git a/font/sfnt/sfnt_test.go b/font/sfnt/sfnt_test.go
index 85d96a9..4f10e84 100644
--- a/font/sfnt/sfnt_test.go
+++ b/font/sfnt/sfnt_test.go
@@ -43,6 +43,16 @@
}
}
+func translate(dx, dy fixed.Int26_6, s Segment) Segment {
+ translateArgs(&s.Args, dx, dy)
+ return s
+}
+
+func transform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, s Segment) Segment {
+ transformArgs(&s.Args, txx, txy, tyx, tyy, dx, dy)
+ return s
+}
+
func checkSegmentsEqual(got, want []Segment) error {
if len(got) != len(want) {
return fmt.Errorf("got %d elements, want %d\noverall:\ngot %v\nwant %v",
diff --git a/font/sfnt/truetype.go b/font/sfnt/truetype.go
index 3c0f880..553302f 100644
--- a/font/sfnt/truetype.go
+++ b/font/sfnt/truetype.go
@@ -255,10 +255,42 @@
if flags&flagArgsAreXYValues == 0 {
return errUnsupportedCompoundGlyph
}
- // TODO: read the other elem.transform elements.
- if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
- return errUnsupportedCompoundGlyph
+ elem.hasTransform = flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0
+ if elem.hasTransform {
+ switch {
+ case flags&flagWeHaveAScale != 0:
+ if len(data) < 2 {
+ return errInvalidGlyphData
+ }
+ elem.transformXX = int16(u16(data))
+ elem.transformXY = 0
+ elem.transformYX = 0
+ elem.transformYY = elem.transformXX
+ data = data[2:]
+ case flags&flagWeHaveAnXAndYScale != 0:
+ if len(data) < 4 {
+ return errInvalidGlyphData
+ }
+ elem.transformXX = int16(u16(data[0:]))
+ elem.transformXY = 0
+ elem.transformYX = 0
+ elem.transformYY = int16(u16(data[2:]))
+ data = data[4:]
+ case flags&flagWeHaveATwoByTwo != 0:
+ if len(data) < 8 {
+ return errInvalidGlyphData
+ }
+ elem.transformXX = int16(u16(data[0:]))
+ elem.transformXY = int16(u16(data[2:]))
+ elem.transformYX = int16(u16(data[4:]))
+ elem.transformYY = int16(u16(data[6:]))
+ data = data[8:]
+ // TODO: find a font that does this, so we can verify that
+ // we've got the xy vs yx ordering right.
+ return errUnsupportedCompoundGlyph
+ }
}
+
if flags&flagMoreComponents == 0 {
break
}
@@ -276,8 +308,18 @@
}
dx, dy := fixed.Int26_6(elem.dx), fixed.Int26_6(elem.dy)
segs := b.segments[base:]
- for j := range segs {
- segs[j] = translate(dx, dy, segs[j])
+ if elem.hasTransform {
+ txx := elem.transformXX
+ txy := elem.transformXY
+ tyx := elem.transformYX
+ tyy := elem.transformYY
+ for j := range segs {
+ transformArgs(&segs[j].Args, txx, txy, tyx, tyy, dx, dy)
+ }
+ } else {
+ for j := range segs {
+ translateArgs(&segs[j].Args, dx, dy)
+ }
}
}