package og import ( "bytes" "image" "image/color" "image/draw" "image/png" "github.com/golang/freetype" "github.com/golang/freetype/truetype" "golang.org/x/image/font" "golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/gofont/gobold" ) const ( imgWidth = 1200 imgHeight = 630 ) var ( regularFont *truetype.Font boldFont *truetype.Font ) func init() { var err error regularFont, err = freetype.ParseFont(goregular.TTF) if err != nil { panic(err) } boldFont, err = freetype.ParseFont(gobold.TTF) if err != nil { panic(err) } } func parseHexColor(hex string) color.RGBA { if len(hex) == 0 { return color.RGBA{16, 185, 129, 255} // #10b981 emerald } if hex[0] == '#' { hex = hex[1:] } if len(hex) != 6 { return color.RGBA{16, 185, 129, 255} // #10b981 emerald } var r, g, b uint8 for i, c := range hex { var v uint8 switch { case c >= '0' && c <= '9': v = uint8(c - '0') case c >= 'a' && c <= 'f': v = uint8(c - 'a' + 10) case c >= 'A' && c <= 'F': v = uint8(c - 'A' + 10) } switch i { case 0: r = v << 4 case 1: r |= v case 2: g = v << 4 case 3: g |= v case 4: b = v << 4 case 5: b |= v } } return color.RGBA{r, g, b, 255} } func Generate(title, siteName, accentColor string) ([]byte, error) { accent := parseHexColor(accentColor) img := image.NewRGBA(image.Rect(0, 0, imgWidth, imgHeight)) for y := 0; y < imgHeight; y++ { ratio := float64(y) / float64(imgHeight) r := uint8(float64(accent.R) * (1 - ratio*0.3)) g := uint8(float64(accent.G) * (1 - ratio*0.3)) b := uint8(float64(accent.B) * (1 - ratio*0.3)) for x := 0; x < imgWidth; x++ { img.Set(x, y, color.RGBA{r, g, b, 255}) } } c := freetype.NewContext() c.SetDPI(72) c.SetClip(img.Bounds()) c.SetDst(img) c.SetSrc(image.White) c.SetHinting(font.HintingFull) titleSize := 64.0 if len(title) > 50 { titleSize = 48.0 } if len(title) > 80 { titleSize = 40.0 } c.SetFont(boldFont) c.SetFontSize(titleSize) wrapped := wrapText(title, 28) y := 200 for _, line := range wrapped { pt := freetype.Pt(80, y) c.DrawString(line, pt) y += int(titleSize * 1.4) } c.SetFont(regularFont) c.SetFontSize(28) pt := freetype.Pt(80, imgHeight-80) c.DrawString(siteName, pt) var buf bytes.Buffer if err := png.Encode(&buf, img); err != nil { return nil, err } return buf.Bytes(), nil } func wrapText(text string, maxChars int) []string { if len(text) <= maxChars { return []string{text} } var lines []string words := splitWords(text) var current string for _, word := range words { if current == "" { current = word } else if len(current)+1+len(word) <= maxChars { current += " " + word } else { lines = append(lines, current) current = word } } if current != "" { lines = append(lines, current) } if len(lines) > 4 { lines = lines[:4] if len(lines[3]) > 3 { lines[3] = lines[3][:len(lines[3])-3] + "..." } } return lines } func splitWords(s string) []string { var words []string var current string for _, r := range s { if r == ' ' || r == '\t' || r == '\n' { if current != "" { words = append(words, current) current = "" } } else { current += string(r) } } if current != "" { words = append(words, current) } return words } var _ = draw.Draw