text/emoji/bitmap.go
Functions
func BestStrikeForPPEM
func (p *SBIXParser) BestStrikeForPPEM(ppem uint16) int {
if len(p.strikes) == 0 {
return -1
}
bestIdx := 0
bestDiff := absDiff(p.strikes[0].ppem, ppem)
for i := 1; i < len(p.strikes); i++ {
diff := absDiff(p.strikes[i].ppem, ppem)
// Prefer larger or equal size
if diff < bestDiff || (diff == bestDiff && p.strikes[i].ppem > p.strikes[bestIdx].ppem) {
bestIdx = i
bestDiff = diff
}
}
return bestIdx
}
func Decode
Decode decodes the bitmap data to an image.Image.
Currently only supports PNG format.
Returns nil if the format is not supported or data is invalid.
func (b *BitmapGlyph) Decode() (image.Image, error) {
switch b.Format {
case FormatPNG:
return png.Decode(bytes.NewReader(b.Data))
case FormatJPEG:
// JPEG support would require image/jpeg import
// For now, return an error indicating it's not yet implemented
return nil, errors.New("emoji: JPEG bitmap decoding not yet implemented")
default:
return nil, ErrUnsupportedBitmapFormat
}
}
func GetGlyph
GetGlyph extracts bitmap data for a glyph at the given strike.
func (p *SBIXParser) GetGlyph(glyphID, strikeIndex int) (*BitmapGlyph, error) {
if strikeIndex < 0 || strikeIndex >= len(p.strikes) {
return nil, ErrGlyphNotInBitmap
}
if glyphID < 0 || glyphID >= int(p.numGlyphs) {
return nil, ErrGlyphNotInBitmap
}
strike := &p.strikes[strikeIndex]
glyphStart := strike.glyphData[glyphID]
glyphEnd := strike.glyphData[glyphID+1]
if glyphEnd <= glyphStart {
return nil, ErrGlyphNotInBitmap
}
// Glyph data format:
// originOffsetX: int16
// originOffsetY: int16
// graphicType: 4 bytes (tag)
// data: remaining bytes
dataOffset := strike.offset + glyphStart
if int(dataOffset)+8 > len(p.data) {
return nil, ErrInvalidSBIXData
}
// #nosec G115 -- uint16 to int16 reinterpretation is intentional for font data
originX := int16(binary.BigEndian.Uint16(p.data[dataOffset : dataOffset+2]))
// #nosec G115 -- uint16 to int16 reinterpretation is intentional for font data
originY := int16(binary.BigEndian.Uint16(p.data[dataOffset+2 : dataOffset+4]))
graphicType := string(p.data[dataOffset+4 : dataOffset+8])
// Determine format from graphic type tag
format, err := parseGraphicType(graphicType)
if err != nil {
return nil, err
}
// Extract image data
imageDataOffset := dataOffset + 8
imageDataEnd := strike.offset + glyphEnd
if int(imageDataEnd) > len(p.data) {
return nil, ErrInvalidSBIXData
}
imageData := p.data[imageDataOffset:imageDataEnd]
bitmap := &BitmapGlyph{
GlyphID: uint16(glyphID), //#nosec G115 -- bounds checked above
Data: imageData,
Format: format,
OriginX: float32(originX),
OriginY: float32(originY),
PPEM: strike.ppem,
}
// Try to decode PNG to get dimensions
if format == FormatPNG && len(imageData) > 0 {
if img, decodeErr := png.Decode(bytes.NewReader(imageData)); decodeErr == nil {
bounds := img.Bounds()
bitmap.Width = bounds.Dx()
bitmap.Height = bounds.Dy()
}
}
return bitmap, nil
}
func HasGlyph
HasGlyph returns true if the glyph has bitmap data at the given strike.
func (p *SBIXParser) HasGlyph(glyphID, strikeIndex int) bool {
if strikeIndex < 0 || strikeIndex >= len(p.strikes) {
return false
}
if glyphID < 0 || glyphID >= int(p.numGlyphs) {
return false
}
strike := &p.strikes[strikeIndex]
glyphStart := strike.glyphData[glyphID]
glyphEnd := strike.glyphData[glyphID+1]
return glyphEnd > glyphStart
}
func HasTable
HasTable returns true if CBDT/CBLC tables are present.
func (p *CBDTParser) HasTable() bool {
return len(p.cbdtData) > 0 && len(p.cblcData) > 0
}
func NewCBDTParser
NewCBDTParser creates a new CBDT parser.
func NewCBDTParser(cbdtData, cblcData []byte) (*CBDTParser, error) {
if len(cbdtData) == 0 {
return nil, ErrNoCBDTTable
}
if len(cblcData) == 0 {
return nil, errors.New("emoji: font has no CBLC table")
}
return &CBDTParser{
cbdtData: cbdtData,
cblcData: cblcData,
}, nil
}
func NewSBIXParser
NewSBIXParser creates a new sbix parser.
numGlyphs should be from the maxp table.
func NewSBIXParser(data []byte, numGlyphs uint16) (*SBIXParser, error) {
if len(data) == 0 {
return nil, ErrNoSBIXTable
}
if len(data) < 8 {
return nil, ErrInvalidSBIXData
}
p := &SBIXParser{
data: data,
numGlyphs: numGlyphs,
}
if err := p.parse(); err != nil {
return nil, err
}
return p, nil
}
func NumStrikes
NumStrikes returns the number of bitmap strikes (sizes).
func (p *SBIXParser) NumStrikes() int {
return len(p.strikes)
}
func StrikePPEM
StrikePPEM returns the ppem for a strike index.
func (p *SBIXParser) StrikePPEM(strikeIndex int) uint16 {
if strikeIndex < 0 || strikeIndex >= len(p.strikes) {
return 0
}
return p.strikes[strikeIndex].ppem
}
func String
String returns the string name of the bitmap format.
func (f BitmapFormat) String() string {
if int(f) < len(bitmapFormatNames) {
return bitmapFormatNames[f]
}
return unknownStr
}
Structs
type BitmapGlyph struct
BitmapGlyph represents a bitmap emoji from sbix/CBDT tables.
type BitmapGlyph struct {
// GlyphID is the glyph ID this bitmap represents.
GlyphID uint16
// Data contains the raw bitmap data (PNG, JPEG, etc.).
Data []byte
// Format indicates how Data is encoded.
Format BitmapFormat
// Width is the bitmap width in pixels.
Width int
// Height is the bitmap height in pixels.
Height int
// OriginX is the horizontal offset from glyph origin.
OriginX float32
// OriginY is the vertical offset from glyph origin.
OriginY float32
// PPEM is the pixels-per-em for this bitmap size.
PPEM uint16
}
type SBIXParser struct
SBIXParser parses the sbix (Standard Bitmap Graphics) table.
sbix is Apple's format for embedded bitmap graphics (emoji).
type SBIXParser struct {
data []byte
numGlyphs uint16
// Parsed strike information
strikes []sbixStrike
}
type CBDTParser struct
CBDTParser parses the CBDT (Color Bitmap Data) table.
CBDT is Google's format for embedded bitmap graphics (emoji).
It works together with CBLC (Color Bitmap Location) table.
type CBDTParser struct {
cbdtData []byte
cblcData []byte
}
BestStrikeForPPEM returns the strike index best matching the requested ppem.