package textmeasure

import (
	
	
	

	
	
	
	
)

// parsedFont keeps the metrics that the legacy freetype/truetype package
// exposed. opentype uses the richer OpenType line and GPOS metrics by default,
// which would otherwise change D2's text measurements and diagram geometry.
type parsedFont struct {
	font         *sfnt.Font
	unitsPerEm   fixed.Int26_6
	ascentUnits  fixed.Int26_6
	descentUnits fixed.Int26_6
	kern         []kernPair
}

type kernPair struct {
	key   uint32
	value int16
}

func parseFont( []byte) (*parsedFont, error) {
	,  := opentype.ParseCollection()
	if  != nil {
		return nil, 
	}
	,  := .Font(0)
	if  != nil {
		return nil, 
	}

	 := fixed.I(int(.UnitsPerEm()))
	,  := .Metrics(nil, , font.HintingNone)
	if  != nil {
		return nil, 
	}
	,  := parseLegacyKern()
	if  != nil {
		return nil, 
	}

	return &parsedFont{
		font:         ,
		unitsPerEm:   ,
		ascentUnits:  .Ascent,
		descentUnits: .Descent,
		kern:         ,
	}, nil
}

func ( *parsedFont) ( float64) font.Face {
	,  := opentype.NewFace(.font, &opentype.FaceOptions{
		Size:    ,
		DPI:     72,
		Hinting: font.HintingNone,
	})
	if  != nil {
		// parseFont has already validated the font. NewFace currently cannot
		// fail for a parsed font, so an error here indicates an invariant break.
		panic(fmt.Sprintf("create OpenType face: %v", ))
	}

	return &metricPreservingFace{
		Face:         ,
		font:         .font,
		scale:        fixed.Int26_6(0.5 + *64),
		unitsPerEm:   .unitsPerEm,
		ascentUnits:  .ascentUnits,
		descentUnits: .descentUnits,
		kern:         .kern,
	}
}

type metricPreservingFace struct {
	font.Face
	font         *sfnt.Font
	buffer       sfnt.Buffer
	scale        fixed.Int26_6
	unitsPerEm   fixed.Int26_6
	ascentUnits  fixed.Int26_6
	descentUnits fixed.Int26_6
	kern         []kernPair
}

func ( *metricPreservingFace) () font.Metrics {
	return font.Metrics{
		Height:  .scale,
		Ascent:  ceilScale(.scale, .ascentUnits, .unitsPerEm),
		Descent: ceilScale(.scale, .descentUnits, .unitsPerEm),
	}
}

func ( *metricPreservingFace) (,  rune) fixed.Int26_6 {
	,  := .font.GlyphIndex(&.buffer, )
	if  != nil {
		return 0
	}
	,  := .font.GlyphIndex(&.buffer, )
	if  != nil {
		return 0
	}
	 := uint32()<<16 | uint32()
	 := sort.Search(len(.kern), func( int) bool {
		return .kern[].key >= 
	})
	if  == len(.kern) || .kern[].key !=  {
		return 0
	}
	return roundScale(.scale, fixed.I(int(.kern[].value)), .unitsPerEm)
}

func ( *metricPreservingFace) ( rune) (fixed.Rectangle26_6, fixed.Int26_6, bool) {
	,  := .font.GlyphIndex(&.buffer, )
	if  != nil {
		return fixed.Rectangle26_6{}, 0, false
	}
	, ,  := .font.GlyphBounds(&.buffer, , .scale, font.HintingNone)
	return , ,  == nil
}

func ( *metricPreservingFace) ( rune) (fixed.Int26_6, bool) {
	,  := .font.GlyphIndex(&.buffer, )
	if  != nil {
		return 0, false
	}
	,  := .font.GlyphAdvance(&.buffer, , .scale, font.HintingNone)
	return ,  == nil
}

func ceilScale(, ,  fixed.Int26_6) fixed.Int26_6 {
	 := int64() * int64()
	return fixed.Int26_6(( + int64() - 1) / int64())
}

func roundScale(, ,  fixed.Int26_6) fixed.Int26_6 {
	 := int64() * int64()
	if  >= 0 {
		 += int64() / 2
	} else {
		 -= int64() / 2
	}
	return fixed.Int26_6( / int64())
}

func parseLegacyKern( []byte) ([]kernPair, error) {
	,  := sfntTable(, "kern")
	if  != nil ||  == nil {
		return nil, 
	}
	if len() < 18 {
		return nil, fmt.Errorf("invalid kern table: too short")
	}
	if binary.BigEndian.Uint16([0:2]) != 0 {
		return nil, fmt.Errorf("unsupported kern table version")
	}
	if binary.BigEndian.Uint16([2:4]) == 0 {
		return nil, fmt.Errorf("invalid kern table: no subtables")
	}

	 := int(binary.BigEndian.Uint16([6:8]))
	 := binary.BigEndian.Uint16([8:10])
	if  != 0x0001 {
		return nil, fmt.Errorf("unsupported kern table coverage 0x%04x", )
	}
	 := int(binary.BigEndian.Uint16([10:12]))
	if  < 14 || 6* != -14 || 4+ > len() {
		return nil, fmt.Errorf("invalid kern table length")
	}

	 := make([]kernPair, )
	for  := range  {
		 := 18 + 6*
		[] = kernPair{
			key:   binary.BigEndian.Uint32([ : +4]),
			value: int16(binary.BigEndian.Uint16([+4 : +6])),
		}
	}
	return , nil
}

func sfntTable( []byte,  string) ([]byte, error) {
	if len() < 12 {
		return nil, fmt.Errorf("invalid sfnt: too short")
	}

	 := 0
	if string([:4]) == "ttcf" {
		if len() < 16 || binary.BigEndian.Uint32([8:12]) == 0 {
			return nil, fmt.Errorf("invalid font collection")
		}
		 = int(binary.BigEndian.Uint32([12:16]))
		if  < 0 || +12 > len() {
			return nil, fmt.Errorf("invalid font collection offset")
		}
	}

	 := int(binary.BigEndian.Uint16([+4 : +6]))
	 :=  + 12
	if  > (len()-)/16 {
		return nil, fmt.Errorf("invalid sfnt table directory")
	}
	for  := 0;  < ; ++ {
		 := [+16* : +16*(+1)]
		if string([:4]) !=  {
			continue
		}
		 := int(binary.BigEndian.Uint32([8:12]))
		 := int(binary.BigEndian.Uint32([12:16]))
		if  < 0 ||  < 0 ||  > len()- {
			return nil, fmt.Errorf("invalid %s table bounds", )
		}
		return [ : +], nil
	}
	return nil, nil
}