Files
go-jdenticon/jdenticon/icon.go
Kevin McIntyre d9e84812ff Initial release: Go Jdenticon library v0.1.0
- Core library with SVG and PNG generation
- CLI tool with generate and batch commands
- Cross-platform path handling for Windows compatibility
- Comprehensive test suite with integration tests
2026-01-03 23:41:48 -05:00

111 lines
2.7 KiB
Go

package jdenticon
import (
"fmt"
"github.com/ungluedlabs/go-jdenticon/internal/engine"
"github.com/ungluedlabs/go-jdenticon/internal/renderer"
)
// Icon represents a generated identicon that can be rendered as SVG or PNG.
// It wraps the internal engine.Icon to provide a clean public API.
type Icon struct {
engineIcon *engine.Icon
}
// newIcon creates a new public Icon from an internal engine.Icon.
func newIcon(engineIcon *engine.Icon) *Icon {
return &Icon{
engineIcon: engineIcon,
}
}
// ToSVG renders the icon as an SVG string.
//
// Returns the SVG markup as a string, or an error if rendering fails.
func (i *Icon) ToSVG() (string, error) {
if i.engineIcon == nil {
return "", ErrInvalidIcon("icon data is nil")
}
size := int(i.engineIcon.Size)
svgRenderer := renderer.NewSVGRenderer(size)
if err := i.renderToRenderer(svgRenderer); err != nil {
return "", NewErrRenderFailed("SVG", err)
}
return svgRenderer.ToSVG(), nil
}
// ToPNG renders the icon as PNG bytes.
//
// Returns the PNG data as a byte slice, or an error if rendering fails.
func (i *Icon) ToPNG() ([]byte, error) {
if i.engineIcon == nil {
return nil, ErrInvalidIcon("icon data is nil")
}
size := int(i.engineIcon.Size)
pngRenderer := renderer.NewPNGRenderer(size)
if err := i.renderToRenderer(pngRenderer); err != nil {
return nil, NewErrRenderFailed("PNG", err)
}
png, err := pngRenderer.ToPNG()
if err != nil {
return nil, NewErrRenderFailed("PNG", err)
}
return png, nil
}
// renderToRenderer renders the icon to any renderer that implements the Renderer interface.
func (i *Icon) renderToRenderer(r renderer.Renderer) error {
// Set background if specified
if i.engineIcon.Config.BackColor != nil {
color := i.engineIcon.Config.BackColor
colorStr := color.String()
opacity := float64(color.A) / 255.0
r.SetBackground(colorStr, opacity)
}
// Render each shape group
for _, shapeGroup := range i.engineIcon.Shapes {
colorStr := shapeGroup.Color.String()
r.BeginShape(colorStr)
for _, shape := range shapeGroup.Shapes {
if err := i.renderShape(r, shape); err != nil {
return fmt.Errorf("shape rendering failed: %w", err)
}
}
r.EndShape()
}
return nil
}
// renderShape renders a single shape to the renderer.
func (i *Icon) renderShape(r renderer.Renderer, shape engine.Shape) error {
switch shape.Type {
case "polygon":
if len(shape.Points) < 3 {
return fmt.Errorf("polygon must have at least 3 points, got %d", len(shape.Points))
}
r.AddPolygon(shape.Points)
case "circle":
topLeft := engine.Point{X: shape.CircleX, Y: shape.CircleY}
r.AddCircle(topLeft, shape.CircleSize, shape.Invert)
default:
return fmt.Errorf("unsupported shape type: %s", shape.Type)
}
return nil
}