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) {
|
func TestProprietaryMicrosoftArial(t *testing.T) {
|
||||||
testProprietary(t, "microsoft", "Arial.ttf", 1200, 599)
|
testProprietary(t, "microsoft", "Arial.ttf", 1200, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProprietaryMicrosoftComicSansMS(t *testing.T) {
|
func TestProprietaryMicrosoftComicSansMS(t *testing.T) {
|
||||||
|
@ -96,7 +96,7 @@ func TestProprietaryMicrosoftComicSansMS(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProprietaryMicrosoftTimesNewRoman(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) {
|
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(371, 1194)),
|
||||||
translate(339, 650, lineTo(222, 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)),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1025,8 +1025,13 @@ type Buffer struct {
|
||||||
segments []Segment
|
segments []Segment
|
||||||
// compoundStack holds the components of a TrueType compound glyph.
|
// compoundStack holds the components of a TrueType compound glyph.
|
||||||
compoundStack [maxCompoundStackSize]struct {
|
compoundStack [maxCompoundStackSize]struct {
|
||||||
glyphIndex GlyphIndex
|
glyphIndex GlyphIndex
|
||||||
dx, dy int16
|
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
|
// psi is a PostScript interpreter for when the Font is an OpenType/CFF
|
||||||
// font.
|
// font.
|
||||||
|
@ -1061,12 +1066,31 @@ const (
|
||||||
SegmentOpCubeTo
|
SegmentOpCubeTo
|
||||||
)
|
)
|
||||||
|
|
||||||
func translate(dx, dy fixed.Int26_6, s Segment) Segment {
|
// translateArgs applies a translation to args.
|
||||||
s.Args[0] += dx
|
func translateArgs(args *[6]fixed.Int26_6, dx, dy fixed.Int26_6) {
|
||||||
s.Args[1] += dy
|
args[0] += dx
|
||||||
s.Args[2] += dx
|
args[1] += dy
|
||||||
s.Args[3] += dy
|
args[2] += dx
|
||||||
s.Args[4] += dx
|
args[3] += dy
|
||||||
s.Args[5] += dy
|
args[4] += dx
|
||||||
return s
|
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 {
|
func checkSegmentsEqual(got, want []Segment) error {
|
||||||
if len(got) != len(want) {
|
if len(got) != len(want) {
|
||||||
return fmt.Errorf("got %d elements, want %d\noverall:\ngot %v\nwant %v",
|
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 {
|
if flags&flagArgsAreXYValues == 0 {
|
||||||
return errUnsupportedCompoundGlyph
|
return errUnsupportedCompoundGlyph
|
||||||
}
|
}
|
||||||
// TODO: read the other elem.transform elements.
|
elem.hasTransform = flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0
|
||||||
if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
|
if elem.hasTransform {
|
||||||
return errUnsupportedCompoundGlyph
|
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 {
|
if flags&flagMoreComponents == 0 {
|
||||||
break
|
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)
|
dx, dy := fixed.Int26_6(elem.dx), fixed.Int26_6(elem.dy)
|
||||||
segs := b.segments[base:]
|
segs := b.segments[base:]
|
||||||
for j := range segs {
|
if elem.hasTransform {
|
||||||
segs[j] = translate(dx, dy, segs[j])
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user