Files
go-jdenticon/jdenticon/icon.go
Kevin McIntyre f1544ef49c
Some checks failed
CI / Test (Go 1.24.x, ubuntu-latest) (push) Successful in 1m53s
CI / Code Quality (push) Failing after 26s
CI / Security Scan (push) Failing after 11s
CI / Test Coverage (push) Successful in 1m13s
CI / Benchmarks (push) Failing after 10m22s
CI / Build CLI (push) Failing after 8s
Benchmarks / Run Benchmarks (push) Failing after 10m13s
Release / Test (push) Successful in 55s
Release / Build (amd64, darwin, ) (push) Failing after 12s
Release / Build (amd64, linux, ) (push) Failing after 6s
Release / Build (amd64, windows, .exe) (push) Failing after 12s
Release / Build (arm64, darwin, ) (push) Failing after 12s
Release / Build (arm64, linux, ) (push) Failing after 12s
Release / Release (push) Has been skipped
CI / Test (Go 1.24.x, macos-latest) (push) Has been cancelled
CI / Test (Go 1.24.x, windows-latest) (push) Has been cancelled
chore: update module path to gitea.dockr.co/kev/go-jdenticon
Move hosting from GitHub to private Gitea instance.
2026-02-10 10:07:57 -05:00

111 lines
2.7 KiB
Go

package jdenticon
import (
"fmt"
"gitea.dockr.co/kev/go-jdenticon/internal/engine"
"gitea.dockr.co/kev/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
}