font/sfnt: support TrueType glyph transformations.

Change-Id: Iea2387b5e30dd0fff53e2808b25599c3be5b1cdb
Reviewed-on: https://go-review.googlesource.com/38210
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Nigel Tao 2017-03-16 12:01:23 +11:00
parent 792d36e11d
commit 1995ed1a25
4 changed files with 129 additions and 17 deletions

View File

@ -88,7 +88,7 @@ func TestProprietaryAdobeSourceSansProTTF(t *testing.T) {
}
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 TestProprietaryMicrosoftComicSansMS(t *testing.T) {
}
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 @@ var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
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)),
},
},
}

View File

@ -1025,8 +1025,13 @@ type Buffer struct {
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 @@ const (
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
}

View File

@ -43,6 +43,16 @@ func cubeTo(xa, ya, xb, yb, xc, yc fixed.Int26_6) Segment {
}
}
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",

View File

@ -255,10 +255,42 @@ func loadCompoundGlyf(f *Font, b *Buffer, data []byte, stackBottom, recursionDep
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 @@ func loadCompoundGlyf(f *Font, b *Buffer, data []byte, stackBottom, recursionDep
}
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)
}
}
}