package gnft import ( b64 "encoding/base64" "math/rand" "strconv" "strings" "gno.land/p/nt/ufmt" ) // SVG template structure: // The template is split at variable insertion points for efficient string concatenation. // Full template with placeholders (for reference): // // // // // ... (path elements) ... // // // // <-- variable // <-- variable // // ... // // // svgTemplate holds pre-split template parts for efficient concatenation. // Usage: svgTemplate[0] + x1 + svgTemplate[1] + y1 + ... + color2 + svgTemplate[6] var svgTemplate = [7]string{ // [0] SVG header and body (before x1) ` `, } // charset contains valid hex digits for color generation. const charset = "0123456789ABCDEF" // Parameter range constants for gradient coordinates. const ( x1Min = 7 x1Max = 13 y1Min = 7 y1Max = 13 x2Min = 121 x2Max = 126 y2Min = 121 y2Max = 126 x1Range = x1Max - x1Min + 1 y1Range = y1Max - y1Min + 1 x2Range = x2Max - x2Min + 1 y2Range = y2Max - y2Min + 1 ) // genImageParamsString generates random gradient parameters and returns them as a compact string. // Format: "x1,y1,x2,y2,color1,color2" (e.g., "10,12,125,123,#AABBCC,#DDEEFF") func genImageParamsString(r *rand.Rand) string { x1 := x1Min + r.Uint64N(x1Range) y1 := y1Min + r.Uint64N(y1Range) x2 := x2Min + r.Uint64N(x2Range) y2 := y2Min + r.Uint64N(y2Range) var buf1 [7]byte var buf2 [7]byte buf1[0] = '#' buf2[0] = '#' for i := 1; i < 7; i++ { buf1[i] = charset[r.IntN(16)] buf2[i] = charset[r.IntN(16)] } color1 := string(buf1[:]) color2 := string(buf2[:]) return strconv.Itoa(int(x1)) + "," + strconv.Itoa(int(y1)) + "," + strconv.Itoa(int(x2)) + "," + strconv.Itoa(int(y2)) + "," + color1 + "," + color2 } // ImageParams holds parsed and validated image parameters. type ImageParams struct { x1, y1, x2, y2 int color1, color2 string } // validateCoordinate checks if a coordinate value is within valid range. // Returns error with details about which coordinate failed if out of range. func validateCoordinate(value int, min int, max int, name string) error { if value < min || value > max { details := ufmt.Sprintf("%s=%d (expected range: [%d, %d])", name, value, min, max) return makeErrorWithDetails(errInvalidTokenParamsRange, details) } return nil } // parseImageParams parses and validates parameters in one step. // Returns parsed ImageParams pointer or error. // Returns nil on error to avoid allocating zero-value struct. // Expected format: "x1,y1,x2,y2,color1,color2" func parseImageParams(s string) (*ImageParams, error) { parts := strings.Split(s, ",") if len(parts) != 6 { return nil, errInvalidTokenParams } x1, err := strconv.Atoi(parts[0]) if err != nil { return nil, errInvalidTokenParams } y1, err := strconv.Atoi(parts[1]) if err != nil { return nil, errInvalidTokenParams } x2, err := strconv.Atoi(parts[2]) if err != nil { return nil, errInvalidTokenParams } y2, err := strconv.Atoi(parts[3]) if err != nil { return nil, errInvalidTokenParams } // Validate coordinate ranges with detailed error messages if err := validateCoordinate(x1, x1Min, x1Max, "x1"); err != nil { return nil, err } if err := validateCoordinate(y1, y1Min, y1Max, "y1"); err != nil { return nil, err } if err := validateCoordinate(x2, x2Min, x2Max, "x2"); err != nil { return nil, err } if err := validateCoordinate(y2, y2Min, y2Max, "y2"); err != nil { return nil, err } color1 := parts[4] color2 := parts[5] // Validate color format (#XXXXXX) if !isValidHexColor(color1) || !isValidHexColor(color2) { return nil, errInvalidColorFormat } return &ImageParams{ x1: x1, y1: y1, x2: x2, y2: y2, color1: color1, color2: color2, }, nil } // generateImageURI converts parsed image parameters to a base64-encoded SVG image URI. func (params ImageParams) generateImageURI() string { svg := params.generateSVG() sEnc := b64.StdEncoding.EncodeToString([]byte(svg)) return "data:image/svg+xml;base64," + sEnc } // generateSVG generates SVG image from parsed and validated parameters. func (params ImageParams) generateSVG() string { return svgTemplate[0] + strconv.Itoa(params.x1) + svgTemplate[1] + strconv.Itoa(params.y1) + svgTemplate[2] + strconv.Itoa(params.x2) + svgTemplate[3] + strconv.Itoa(params.y2) + svgTemplate[4] + params.color1 + svgTemplate[5] + params.color2 + svgTemplate[6] } // isValidHexColor checks if a string is a valid hex color in #XXXXXX format. func isValidHexColor(color string) bool { if len(color) != 7 || color[0] != '#' { return false } for i := 1; i < 7; i++ { c := color[i] isHex := (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') if !isHex { return false } } return true }