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 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)), + }, }, } 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 @@ 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 } 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 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", 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 @@ 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) + } } }