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}