// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package opentype implements a glyph rasterizer for TTF (TrueType Fonts) and
// OTF (OpenType Fonts).
//
// This package provides a high-level API, centered on the NewFace function,
// implementing the golang.org/x/image/font.Face interface.
//
// The sibling golang.org/x/image/font/sfnt package provides a low-level API.
//
// # Security Considerations
//
// This package is not hardened against malicious inputs.
// Crashes, excessive CPU or memory consumption, or similar misbehavior
// caused by malicious or corrupt inputs will be handled as ordinary bugs,
// not vulnerabilities.
package opentype // import "golang.org/x/image/font/opentype"

import (
	"image"
	"image/draw"
	"io"

	"golang.org/x/image/font"
	"golang.org/x/image/font/sfnt"
	"golang.org/x/image/math/fixed"
	"golang.org/x/image/vector"
)

// ParseCollection parses an OpenType font collection, such as TTC or OTC data,
// from a []byte data source.
//
// If passed data for a single font, a TTF or OTF instead of a TTC or OTC, it
// will return a collection containing 1 font.
func ParseCollection(src []byte) (*Collection, error) {
	return sfnt.ParseCollection(src)
}

// ParseCollectionReaderAt parses an OpenType collection, such as TTC or OTC
// data, from an io.ReaderAt data source.
//
// If passed data for a single font, a TTF or OTF instead of a TTC or OTC, it
// will return a collection containing 1 font.
func ParseCollectionReaderAt(src io.ReaderAt) (*Collection, error) {
	return sfnt.ParseCollectionReaderAt(src)
}

// Collection is a collection of one or more fonts.
//
// All of the Collection methods are safe to call concurrently.
type Collection = sfnt.Collection

// Parse parses an OpenType font, such as TTF or OTF data, from a []byte data
// source.
func Parse(src []byte) (*Font, error) {
	return sfnt.Parse(src)
}

// ParseReaderAt parses an OpenType font, such as TTF or OTF data, from an
// io.ReaderAt data source.
func ParseReaderAt(src io.ReaderAt) (*Font, error) {
	return sfnt.ParseReaderAt(src)
}

// Font is an OpenType font, also known as an SFNT font.
//
// All of the Font methods are safe to call concurrently, as long as each call
// has a different *sfnt.Buffer (or nil).
//
// The Font methods that don't take a *sfnt.Buffer argument are always safe to
// call concurrently.
type Font = sfnt.Font

// FaceOptions describes the possible options given to NewFace when
// creating a new font.Face from a Font.
type FaceOptions struct {
	Size    float64      // Size is the font size in points
	DPI     float64      // DPI is the dots per inch resolution
	Hinting font.Hinting // Hinting selects how to quantize a vector font's glyph nodes
}

func defaultFaceOptions() *FaceOptions {
	return &FaceOptions{
		Size:    12,
		DPI:     72,
		Hinting: font.HintingNone,
	}
}

// Face implements the font.Face interface for Font values.
//
// A Face is not safe to use concurrently.
type Face struct {
	f       *Font
	hinting font.Hinting
	scale   fixed.Int26_6

	metrics    font.Metrics
	metricsSet bool

	buf  sfnt.Buffer
	rast vector.Rasterizer
	mask image.Alpha
}

// NewFace returns a new font.Face for the given Font.
//
// If opts is nil, sensible defaults will be used.
func NewFace(f *Font, opts *FaceOptions) (font.Face, error) {
	if opts == nil {
		opts = defaultFaceOptions()
	}
	face := &Face{
		f:       f,
		hinting: opts.Hinting,
		scale:   fixed.Int26_6(0.5 + (opts.Size * opts.DPI * 64 / 72)),
	}
	return face, nil
}

// Close satisfies the font.Face interface.
func (f *Face) Close() error {
	return nil
}

// Metrics satisfies the font.Face interface.
func (f *Face) Metrics() font.Metrics {
	if !f.metricsSet {
		var err error
		f.metrics, err = f.f.Metrics(&f.buf, f.scale, f.hinting)
		if err != nil {
			f.metrics = font.Metrics{}
		}
		f.metricsSet = true
	}
	return f.metrics
}

// Kern satisfies the font.Face interface.
func (f *Face) Kern(r0, r1 rune) fixed.Int26_6 {
	x0, _ := f.f.GlyphIndex(&f.buf, r0)
	x1, _ := f.f.GlyphIndex(&f.buf, r1)
	k, err := f.f.Kern(&f.buf, x0, x1, fixed.Int26_6(f.f.UnitsPerEm()), f.hinting)
	if err != nil {
		return 0
	}
	return k
}

// Glyph satisfies the font.Face interface.
func (f *Face) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
	x, err := f.f.GlyphIndex(&f.buf, r)
	if err != nil {
		return image.Rectangle{}, nil, image.Point{}, 0, false
	}

	// Call f.f.GlyphAdvance before f.f.LoadGlyph because the LoadGlyph docs
	// say this about the &f.buf argument: the segments become invalid to use
	// once [the buffer] is re-used.

	advance, err = f.f.GlyphAdvance(&f.buf, x, f.scale, f.hinting)
	if err != nil {
		return image.Rectangle{}, nil, image.Point{}, 0, false
	}

	segments, err := f.f.LoadGlyph(&f.buf, x, f.scale, nil)
	if err != nil {
		return image.Rectangle{}, nil, image.Point{}, 0, false
	}

	// Numerical notation used below:
	//  - 2    is an integer, "two"
	//  - 2:16 is a 26.6 fixed point number, "two and a quarter"
	//  - 2.5  is a float32 number, "two and a half"
	// Using 26.6 fixed point numbers means that there are 64 sub-pixel units
	// in 1 integer pixel unit.

	// Translate the sub-pixel bounding box from glyph space (where the glyph
	// origin is at (0:00, 0:00)) to dst space (where the glyph origin is at
	// the dot). dst space is the coordinate space that contains both the dot
	// (a sub-pixel position) and dr (an integer-pixel rectangle).
	dBounds := segments.Bounds().Add(dot)

	// Quantize the sub-pixel bounds (dBounds) to integer-pixel bounds (dr).
	dr.Min.X = dBounds.Min.X.Floor()
	dr.Min.Y = dBounds.Min.Y.Floor()
	dr.Max.X = dBounds.Max.X.Ceil()
	dr.Max.Y = dBounds.Max.Y.Ceil()
	width := dr.Dx()
	height := dr.Dy()
	if width < 0 || height < 0 {
		return image.Rectangle{}, nil, image.Point{}, 0, false
	}

	// Calculate the sub-pixel bias to convert from glyph space to rasterizer
	// space. In glyph space, the segments may be to the left or right and
	// above or below the glyph origin. In rasterizer space, the segments
	// should only be right and below (or equal to) the top-left corner (0.0,
	// 0.0). They should also be left and above (or equal to) the bottom-right
	// corner (width, height), as the rasterizer should enclose the glyph
	// bounding box.
	//
	// For example, suppose that dot.X was at the sub-pixel position 25:48,
	// three quarters of the way into the 26th pixel, and that bounds.Min.X was
	// 1:20. We then have dBounds.Min.X = 1:20 + 25:48 = 27:04, dr.Min.X = 27
	// and biasX = 25:48 - 27:00 = -1:16. A vertical stroke at 1:20 in glyph
	// space becomes (1:20 + -1:16) = 0:04 in rasterizer space. 0:04 as a
	// fixed.Int26_6 value is float32(4)/64.0 = 0.0625 as a float32 value.
	biasX := dot.X - fixed.Int26_6(dr.Min.X<<6)
	biasY := dot.Y - fixed.Int26_6(dr.Min.Y<<6)

	// Configure the mask image, re-allocating its buffer if necessary.
	nPixels := width * height
	if cap(f.mask.Pix) < nPixels {
		f.mask.Pix = make([]uint8, 2*nPixels)
	}
	f.mask.Pix = f.mask.Pix[:nPixels]
	f.mask.Stride = width
	f.mask.Rect.Min.X = 0
	f.mask.Rect.Min.Y = 0
	f.mask.Rect.Max.X = width
	f.mask.Rect.Max.Y = height

	// Rasterize the biased segments, converting from fixed.Int26_6 to float32.
	f.rast.Reset(width, height)
	f.rast.DrawOp = draw.Src
	for _, seg := range segments {
		switch seg.Op {
		case sfnt.SegmentOpMoveTo:
			f.rast.MoveTo(
				float32(seg.Args[0].X+biasX)/64,
				float32(seg.Args[0].Y+biasY)/64,
			)
		case sfnt.SegmentOpLineTo:
			f.rast.LineTo(
				float32(seg.Args[0].X+biasX)/64,
				float32(seg.Args[0].Y+biasY)/64,
			)
		case sfnt.SegmentOpQuadTo:
			f.rast.QuadTo(
				float32(seg.Args[0].X+biasX)/64,
				float32(seg.Args[0].Y+biasY)/64,
				float32(seg.Args[1].X+biasX)/64,
				float32(seg.Args[1].Y+biasY)/64,
			)
		case sfnt.SegmentOpCubeTo:
			f.rast.CubeTo(
				float32(seg.Args[0].X+biasX)/64,
				float32(seg.Args[0].Y+biasY)/64,
				float32(seg.Args[1].X+biasX)/64,
				float32(seg.Args[1].Y+biasY)/64,
				float32(seg.Args[2].X+biasX)/64,
				float32(seg.Args[2].Y+biasY)/64,
			)
		}
	}
	f.rast.Draw(&f.mask, f.mask.Bounds(), image.Opaque, image.Point{})

	return dr, &f.mask, f.mask.Rect.Min, advance, x != 0
}

// GlyphBounds satisfies the font.Face interface.
func (f *Face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
	x, _ := f.f.GlyphIndex(&f.buf, r)
	bounds, advance, err := f.f.GlyphBounds(&f.buf, x, f.scale, f.hinting)
	return bounds, advance, (err == nil) && (x != 0)
}

// GlyphAdvance satisfies the font.Face interface.
func (f *Face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
	x, _ := f.f.GlyphIndex(&f.buf, r)
	advance, err := f.f.GlyphAdvance(&f.buf, x, f.scale, f.hinting)
	return advance, (err == nil) && (x != 0)
}
