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:
parent
792d36e11d
commit
1995ed1a25
|
@ -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)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1027,6 +1027,11 @@ type Buffer struct {
|
|||
compoundStack [maxCompoundStackSize]struct {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
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:]
|
||||
if elem.hasTransform {
|
||||
txx := elem.transformXX
|
||||
txy := elem.transformXY
|
||||
tyx := elem.transformYX
|
||||
tyy := elem.transformYY
|
||||
for j := range segs {
|
||||
segs[j] = translate(dx, dy, segs[j])
|
||||
transformArgs(&segs[j].Args, txx, txy, tyx, tyy, dx, dy)
|
||||
}
|
||||
} else {
|
||||
for j := range segs {
|
||||
translateArgs(&segs[j].Args, dx, dy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user