Skip to content

Commit

Permalink
[opentype] fix variation coordinate format; load cff2 table
Browse files Browse the repository at this point in the history
  • Loading branch information
benoitkugler committed Dec 3, 2023
1 parent f4aac9f commit b77a9b9
Show file tree
Hide file tree
Showing 25 changed files with 883 additions and 176 deletions.
2 changes: 1 addition & 1 deletion harfbuzz/fonts.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func (f *Font) ExtentsForDirection(direction Direction) api.FontExtents {
return extents
}

func (font *Font) varCoords() []float32 { return font.face.Coords }
func (font *Font) varCoords() []tables.Coord { return font.face.Coords }

func (font *Font) getXDelta(varStore tables.ItemVarStore, device tables.DeviceTable) Position {
switch device := device.(type) {
Expand Down
2 changes: 1 addition & 1 deletion harfbuzz/fonts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func TestAdvanceTtVarCompV(t *testing.T) {

func TestAdvanceTtVarGvarInfer(t *testing.T) {
ft := openFontFile(t, "fonts/TestGVAREight.ttf")
coords := []float32{float32(100) / (1 << 14)}
coords := []font.VarCoord{100}

face := &font.Face{Font: ft, Coords: coords}
font := NewFont(face)
Expand Down
2 changes: 1 addition & 1 deletion harfbuzz/ot_shaper.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ type shaperOpentype struct {

type otShapePlanKey = [2]int // -1 for not found

func newShaperOpentype(tables *font.Font, coords []float32) *shaperOpentype {
func newShaperOpentype(tables *font.Font, coords []tables.Coord) *shaperOpentype {
var out shaperOpentype
out.key = otShapePlanKey{
0: tables.GSUB.FindVariationIndex(coords),
Expand Down
8 changes: 5 additions & 3 deletions harfbuzz/shape.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package harfbuzz

import (
"fmt"

"github.com/go-text/typesetting/opentype/tables"
)

// ported from harfbuzz/src/hb-shape.cc, harfbuzz/src/hb-shape-plan.cc Copyright © 2009, 2012 Behdad Esfahbod
Expand Down Expand Up @@ -47,7 +49,7 @@ type shapePlan struct {
}

func (plan *shapePlan) init(copy bool, font *Font, props SegmentProperties,
userFeatures []Feature, coords []float32,
userFeatures []Feature, coords []tables.Coord,
) {
plan.props = props
if !copy {
Expand Down Expand Up @@ -91,7 +93,7 @@ func (plan shapePlan) equal(other shapePlan) bool {
// plus the variation-space coordinates @coords.
// See newShapePlanCached for caching support.
func newShapePlan(font *Font, props SegmentProperties,
userFeatures []Feature, coords []float32,
userFeatures []Feature, coords []tables.Coord,
) *shapePlan {
if debugMode {
fmt.Printf("NEW SHAPE PLAN: face:%p features:%v coords:%v\n", &font.face, userFeatures, coords)
Expand Down Expand Up @@ -126,7 +128,7 @@ func (sp *shapePlan) execute(font *Font, buffer *Buffer, features []Feature) {
// creates (or returns) a cached shaping plan suitable for reuse, for a combination
// of `face`, `userFeatures`, `props`, plus the variation-space coordinates `coords`.
func (b *Buffer) newShapePlanCached(font *Font, props SegmentProperties,
userFeatures []Feature, coords []float32,
userFeatures []Feature, coords []tables.Coord,
) *shapePlan {
var key shapePlan
key.init(false, font, props, userFeatures, coords)
Expand Down
26 changes: 13 additions & 13 deletions opentype/api/font/cff/cff2.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type CFF2 struct {
// otherwise, it can be safely indexed by `fdSelect` output
fonts []privateFonts

varStore tables.ItemVarStore // optional
VarStore tables.ItemVarStore // optional
}

type privateFonts struct {
Expand Down Expand Up @@ -54,7 +54,7 @@ func ParseCFF2(src []byte) (*CFF2, error) {
psi ps.Machine
)
if err := psi.Run(topDictSrc, nil, nil, &tp); err != nil {
return nil, err
return nil, fmt.Errorf("reading top dict: %s", err)
}

var (
Expand Down Expand Up @@ -84,7 +84,7 @@ func ParseCFF2(src []byte) (*CFF2, error) {
var fd fontDict2
err = psi.Run(font, nil, nil, &fd)
if err != nil {
return nil, err
return nil, fmt.Errorf("reading font dict: %s", err)
}
end := int(fd.privateDictOffset + fd.privateDictSize)
if L := len(src); L < end {
Expand All @@ -94,7 +94,7 @@ func ParseCFF2(src []byte) (*CFF2, error) {
var pd privateDict2
err = psi.Run(src[fd.privateDictOffset:end], nil, nil, &pd)
if err != nil {
return nil, err
return nil, fmt.Errorf("reading private dict: %s", err)
}

out.fonts[i].defaultVSIndex = pd.vsindex
Expand Down Expand Up @@ -136,7 +136,7 @@ func ParseCFF2(src []byte) (*CFF2, error) {
return nil, fmt.Errorf("reading variation store: EOF: expected length: %d, got %d", end, L)
}
vstore := src[tp.vstore+2 : end]
out.varStore, _, err = tables.ParseItemVarStore(vstore)
out.VarStore, _, err = tables.ParseItemVarStore(vstore)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -173,22 +173,22 @@ func (tp *topDict2) Apply(state *ps.Machine, op ps.Operator) error {
if state.ArgStack.Top < 1 {
return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", op)
}
tp.charStrings = state.ArgStack.Pop()
tp.charStrings = int32(state.ArgStack.Pop())
case ps.Operator{Operator: 36, IsEscaped: true}: // FDArray
if state.ArgStack.Top < 1 {
return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", op)
}
tp.fdArray = state.ArgStack.Pop()
tp.fdArray = int32(state.ArgStack.Pop())
case ps.Operator{Operator: 37, IsEscaped: true}: // FDSelect
if state.ArgStack.Top < 1 {
return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", op)
}
tp.fdSelect = state.ArgStack.Pop()
tp.fdSelect = int32(state.ArgStack.Pop())
case ps.Operator{Operator: 24, IsEscaped: false}: // vstore
if state.ArgStack.Top < 1 {
return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", op)
}
tp.vstore = state.ArgStack.Pop()
tp.vstore = int32(state.ArgStack.Pop())
default:
return fmt.Errorf("invalid operator %s in Top Dict", op)
}
Expand All @@ -208,8 +208,8 @@ func (fd *fontDict2) Apply(state *ps.Machine, op ps.Operator) error {
if state.ArgStack.Top < 2 {
return fmt.Errorf("invalid number of arguments for operator %s in Font Dict", op)
}
fd.privateDictOffset = state.ArgStack.Pop()
fd.privateDictSize = state.ArgStack.Pop()
fd.privateDictOffset = int32(state.ArgStack.Pop())
fd.privateDictSize = int32(state.ArgStack.Pop())
return nil
default:
return fmt.Errorf("invalid operator %s in Font Dict", op)
Expand Down Expand Up @@ -237,13 +237,13 @@ func (priv *privateDict2) Apply(state *ps.Machine, op ps.Operator) error {
if state.ArgStack.Top < 1 {
return errors.New("invalid stack size for 'subrs' in private Dict charstring")
}
priv.subrsOffset = state.ArgStack.Pop()
priv.subrsOffset = int32(state.ArgStack.Pop())
return nil
case 22: // "vsindex"
if state.ArgStack.Top < 1 {
return fmt.Errorf("invalid stack size for %s in private Dict", op)
}
priv.vsindex = state.ArgStack.Pop()
priv.vsindex = int32(state.ArgStack.Pop())
return nil
case 23: // "blend"
return nil
Expand Down
24 changes: 13 additions & 11 deletions opentype/api/font/cff/charstring.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package cff
import (
"errors"
"fmt"
"math"

"github.com/go-text/typesetting/opentype/api"
ps "github.com/go-text/typesetting/opentype/api/font/cff/interpreter"
Expand Down Expand Up @@ -44,8 +43,8 @@ type type2CharstringHandler struct {
// found in private DICT, needed since we can't differenciate
// no width set from 0 width
// `width` must be initialized to default width
nominalWidthX int32
width int32
nominalWidthX float64
width float64
}

func (type2CharstringHandler) Context() ps.Context { return ps.Type2Charstring }
Expand Down Expand Up @@ -143,8 +142,9 @@ func (met *type2CharstringHandler) Apply(state *ps.Machine, op ps.Operator) erro
// LoadGlyph parses the glyph charstring to compute segments and path bounds.
// It returns an error if the glyph is invalid or if decoding the charstring fails.
//
// [coords] must either have the same length as the variations axis, or be empty
func (f *CFF2) LoadGlyph(glyph tables.GlyphID, coords []float32) ([]api.Segment, ps.PathBounds, error) {
// [coords] must either have the same length as the variations axis, or be empty,
// and be normalized
func (f *CFF2) LoadGlyph(glyph tables.GlyphID, coords []tables.Coord) ([]api.Segment, ps.PathBounds, error) {
if int(glyph) >= len(f.Charstrings) {
return nil, ps.PathBounds{}, errGlyph
}
Expand All @@ -165,18 +165,19 @@ func (f *CFF2) LoadGlyph(glyph tables.GlyphID, coords []float32) ([]api.Segment,
font := f.fonts[index]

loader.coords = coords
loader.vars = f.varStore
loader.vars = f.VarStore
loader.setVSIndex(int(font.defaultVSIndex))

err = psi.Run(f.Charstrings[glyph], font.localSubrs, f.globalSubrs, &loader)

return loader.cs.Segments, loader.cs.Bounds, err
}

// cff2CharstringHandler implements operators needed to fetch CFF2 charstring metrics
type cff2CharstringHandler struct {
cs ps.CharstringReader

coords []float32 // variation coordinates
coords []tables.Coord // normalized variation coordinates
vars tables.ItemVarStore

// the currently active ItemVariationData subtable (default to 0)
Expand Down Expand Up @@ -214,7 +215,7 @@ func (met *cff2CharstringHandler) blend(state *ps.Machine) error {
if state.ArgStack.Top < 1 {
return errors.New("missing n argument for blend operator")
}
n := state.ArgStack.Pop()
n := int32(state.ArgStack.Pop())
k := int32(len(met.scalars))
if state.ArgStack.Top < n*(k+1) {
return errors.New("missing arguments for blend operator")
Expand All @@ -224,12 +225,13 @@ func (met *cff2CharstringHandler) blend(state *ps.Machine) error {
args := state.ArgStack.Vals[state.ArgStack.Top-n*(k+1) : state.ArgStack.Top]
// the first n values are the 'default' arguments
for i := int32(0); i < n; i++ {
baseValue := math.Float32frombits(uint32(args[i]))
baseValue := args[i]
deltas := args[n+i*k : n+(i+1)*k] // all the regions, for one operand
v := 0.
for ik, delta := range deltas {
baseValue += met.scalars[ik] * math.Float32frombits(uint32(delta))
v += float64(met.scalars[ik]) * delta
}
args[i] = int32(math.Round(float64(baseValue))) // upadte the stack with the blended value
args[i] = baseValue + v // update the stack with the blended value
}
}

Expand Down
27 changes: 11 additions & 16 deletions opentype/api/font/cff/interpreter/charstrings.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package psinterpreter
import (
"errors"
"fmt"
"math"

"github.com/go-text/typesetting/opentype/api"
)
Expand Down Expand Up @@ -33,19 +34,20 @@ func (b *PathBounds) Enlarge(pt Point) {

// ToExtents converts a path bounds to the corresponding glyph extents.
func (b *PathBounds) ToExtents() api.GlyphExtents {
xBearing, yBearing := math.Round(b.Min.X), math.Round(b.Max.Y)
return api.GlyphExtents{
XBearing: float32(b.Min.X),
YBearing: float32(b.Max.Y),
Width: float32(b.Max.X - b.Min.X),
Height: float32(b.Min.Y - b.Max.Y),
XBearing: float32(xBearing),
YBearing: float32(yBearing),
Width: float32(math.Round(b.Max.X - xBearing)),
Height: float32(math.Round(b.Min.Y - yBearing)),
}
}

// Point is a 2D Point in font units.
type Point struct{ X, Y int32 }
type Point struct{ X, Y float64 }

// Move translates the Point.
func (p *Point) Move(dx, dy int32) {
func (p *Point) Move(dx, dy float64) {
p.X += dx
p.Y += dy
}
Expand Down Expand Up @@ -165,21 +167,14 @@ func (out *CharstringReader) ensureClosePath() {
}
}

func abs(x int32) int32 {
if x < 0 {
return -x
}
return x
}

// ------------------------------------------------------------

// LocalSubr pops the subroutine index and call it
func LocalSubr(state *Machine) error {
if state.ArgStack.Top < 1 {
return errors.New("invalid callsubr operator (empty stack)")
}
index := state.ArgStack.Pop()
index := int32(state.ArgStack.Pop())
return state.CallSubroutine(index, true)
}

Expand All @@ -188,7 +183,7 @@ func GlobalSubr(state *Machine) error {
if state.ArgStack.Top < 1 {
return errors.New("invalid callgsubr operator (empty stack)")
}
index := state.ArgStack.Pop()
index := int32(state.ArgStack.Pop())
return state.CallSubroutine(index, false)
}

Expand Down Expand Up @@ -578,7 +573,7 @@ func (out *CharstringReader) Flex1(state *Machine) error {
pt5.Move(state.ArgStack.Vals[8], state.ArgStack.Vals[9])
pt6 := pt5

if abs(d.X) > abs(d.Y) {
if math.Abs(d.X) > math.Abs(d.Y) {
pt6.X += state.ArgStack.Vals[10]
pt6.Y = out.CurrentPoint.Y
} else {
Expand Down
Loading

0 comments on commit b77a9b9

Please sign in to comment.