this repo has no description
1package main
2
3import (
4 "bytes"
5 "flag"
6 "fmt"
7 "image"
8 "image/color"
9 "image/png"
10 "os"
11 "path/filepath"
12 "strconv"
13 "strings"
14 "text/template"
15
16 "github.com/srwiley/oksvg"
17 "github.com/srwiley/rasterx"
18 "golang.org/x/image/draw"
19 "tangled.org/core/appview/pages"
20 "tangled.org/core/ico"
21)
22
23func main() {
24 var (
25 size string
26 fillColor string
27 output string
28 )
29
30 flag.StringVar(&size, "size", "512x512", "Output size in format WIDTHxHEIGHT (e.g., 512x512)")
31 flag.StringVar(&fillColor, "color", "#000000", "Fill color in hex format (e.g., #FF5733)")
32 flag.StringVar(&output, "output", "dolly.svg", "Output file path (format detected from extension: .svg, .png, or .ico)")
33 flag.Parse()
34
35 width, height, err := parseSize(size)
36 if err != nil {
37 fmt.Fprintf(os.Stderr, "Error parsing size: %v\n", err)
38 os.Exit(1)
39 }
40
41 // Detect format from file extension
42 ext := strings.ToLower(filepath.Ext(output))
43 format := strings.TrimPrefix(ext, ".")
44
45 if format != "svg" && format != "png" && format != "ico" {
46 fmt.Fprintf(os.Stderr, "Invalid file extension: %s. Must be .svg, .png, or .ico\n", ext)
47 os.Exit(1)
48 }
49
50 if !isValidHexColor(fillColor) {
51 fmt.Fprintf(os.Stderr, "Invalid color format: %s. Use hex format like #FF5733\n", fillColor)
52 os.Exit(1)
53 }
54
55 svgData, err := dolly(fillColor)
56 if err != nil {
57 fmt.Fprintf(os.Stderr, "Error generating SVG: %v\n", err)
58 os.Exit(1)
59 }
60
61 // Create output directory if it doesn't exist
62 dir := filepath.Dir(output)
63 if dir != "" && dir != "." {
64 if err := os.MkdirAll(dir, 0755); err != nil {
65 fmt.Fprintf(os.Stderr, "Error creating output directory: %v\n", err)
66 os.Exit(1)
67 }
68 }
69
70 switch format {
71 case "svg":
72 err = saveSVG(svgData, output, width, height)
73 case "png":
74 err = savePNG(svgData, output, width, height)
75 case "ico":
76 err = saveICO(svgData, output, width, height)
77 }
78
79 if err != nil {
80 fmt.Fprintf(os.Stderr, "Error saving file: %v\n", err)
81 os.Exit(1)
82 }
83
84 fmt.Printf("Successfully generated %s (%dx%d)\n", output, width, height)
85}
86
87func dolly(hexColor string) ([]byte, error) {
88 tpl, err := template.New("dolly").
89 ParseFS(pages.Files, "templates/fragments/dolly/logo.html")
90 if err != nil {
91 return nil, err
92 }
93
94 var svgData bytes.Buffer
95 if err := tpl.ExecuteTemplate(&svgData, "fragments/dolly/logo", pages.DollyParams{
96 FillColor: hexColor,
97 }); err != nil {
98 return nil, err
99 }
100
101 return svgData.Bytes(), nil
102}
103
104func svgToImage(svgData []byte, w, h int) (image.Image, error) {
105 icon, err := oksvg.ReadIconStream(bytes.NewReader(svgData))
106 if err != nil {
107 return nil, fmt.Errorf("error parsing SVG: %v", err)
108 }
109
110 icon.SetTarget(0, 0, float64(w), float64(h))
111 rgba := image.NewRGBA(image.Rect(0, 0, w, h))
112 draw.Draw(rgba, rgba.Bounds(), &image.Uniform{color.Transparent}, image.Point{}, draw.Src)
113 scanner := rasterx.NewScannerGV(w, h, rgba, rgba.Bounds())
114 raster := rasterx.NewDasher(w, h, scanner)
115 icon.Draw(raster, 1.0)
116
117 return rgba, nil
118}
119
120func parseSize(size string) (int, int, error) {
121 parts := strings.Split(size, "x")
122 if len(parts) != 2 {
123 return 0, 0, fmt.Errorf("invalid size format, use WIDTHxHEIGHT")
124 }
125
126 width, err := strconv.Atoi(parts[0])
127 if err != nil {
128 return 0, 0, fmt.Errorf("invalid width: %v", err)
129 }
130
131 height, err := strconv.Atoi(parts[1])
132 if err != nil {
133 return 0, 0, fmt.Errorf("invalid height: %v", err)
134 }
135
136 if width <= 0 || height <= 0 {
137 return 0, 0, fmt.Errorf("width and height must be positive")
138 }
139
140 return width, height, nil
141}
142
143func isValidHexColor(hex string) bool {
144 if len(hex) != 7 || hex[0] != '#' {
145 return false
146 }
147 _, err := strconv.ParseUint(hex[1:], 16, 32)
148 return err == nil
149}
150
151func saveSVG(svgData []byte, filepath string, _, _ int) error {
152 return os.WriteFile(filepath, svgData, 0644)
153}
154
155func savePNG(svgData []byte, filepath string, width, height int) error {
156 img, err := svgToImage(svgData, width, height)
157 if err != nil {
158 return err
159 }
160
161 f, err := os.Create(filepath)
162 if err != nil {
163 return err
164 }
165 defer f.Close()
166
167 return png.Encode(f, img)
168}
169
170func saveICO(svgData []byte, filepath string, width, height int) error {
171 img, err := svgToImage(svgData, width, height)
172 if err != nil {
173 return err
174 }
175
176 icoData, err := ico.ImageToIco(img)
177 if err != nil {
178 return err
179 }
180
181 return os.WriteFile(filepath, icoData, 0644)
182}