From 2fb6abf02861f714c829119058ebfac926141831 Mon Sep 17 00:00:00 2001 From: m15o Date: Sat, 25 Dec 2021 18:19:18 +0100 Subject: [PATCH] Adding badge --- assets/badge.png | Bin 0 -> 730 bytes config/cfg.go | 2 + web/handler/handler.go | 3 +- web/handler/html.go | 3 +- web/handler/html/index.html | 3 +- web/handler/login_check.go | 4 +- web/handler/user_show.go | 297 ++++++------------------------------ 7 files changed, 60 insertions(+), 252 deletions(-) create mode 100644 assets/badge.png diff --git a/assets/badge.png b/assets/badge.png new file mode 100644 index 0000000000000000000000000000000000000000..86c8497b5edf954b8829ec011900e3196e156c22 GIT binary patch literal 730 zcmV<00ww*4P)EX>4Tx04R}tkv&MmKpe$i(@Kj}9PFUtkfAzR5S8MnRVYG*P%E_RU~>J0CJjl7 zi=*ILaPVWX>fqw6tAnc`2!4RLxj8AiNQwVT3N2zhIPS;0dyl(!fY7Wm)eKAks%9DK zWJ1j5R>j~e!WcpbaRg*$>T{Bmg6H_UhmWs!F`ngp?$6PeFPIGQiNv!^H!R`};`pYe zbKWP8u(F&dJ|`YG=z_$LTvuFv<6LrB;F%F4lb$Dz5R0WQR=Sv#4V8GBIGR^A$``UO z=Q(e2R;zW^z9)ZSxS*{pbDic0l32tNB#2N@M+H?_h|#K%Vj@lZ2@n6Ut;i~_-3pw+PL?_=9;odEu4;7aTGYfWJ0lk`SM ziyi^}+rY(jM^pBI%N=0wNtX@Tk^D4;Vi9;hqi@Oq1Ghl$n%i4@AEysMhPq1K00)P_ zSc$UNJ>DJa?(N?*?f!lMZgX;_e~I0=00006VoOIv02u%o0C3kqr%wO?010qNS#tmY zE+YT{E+YYWr9XB6000McNlirueSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{006Q{L_t(o!|j$q4udcZ1c$13?)fDzNc;kR$-OUV4^^cU zStyt`!pM69C$QSp+VW6Jc@i+=4v=#WCkzmgw}AkUKN5{VO4FsxTb3DsTjb~%!oXk5 zz_YdoRqJcDx~|pM-)qlX|8{BMT3jtm6Rw$}-QCOQ*1(VW)-=@V>A~k>2BtK932V9A zi_b+1Tj!A_X-D}^*}|=7_50S@-de0Nu?ITb1ILcD?Q#Z+NJPnq7qU@SM`Zc~VgLXD M07*qoM6N<$g2Apt#{d8T literal 0 HcmV?d00001 diff --git a/config/cfg.go b/config/cfg.go index 7048cc4..76c2d98 100644 --- a/config/cfg.go +++ b/config/cfg.go @@ -15,6 +15,7 @@ type ( EmailHost string EmailHostAddr string ManualRegistration bool + EmojiFolder string } ) @@ -27,5 +28,6 @@ func New() *Config { KeyFile: os.Getenv("CERT_KEY_FILE"), AssetsDir: os.Getenv("ASSETS_DIR"), ManualRegistration: len(os.Getenv("MANUAL_REGISTRATION")) > 0, + EmojiFolder: os.Getenv("EMOJI_FOLDER"), } } diff --git a/web/handler/handler.go b/web/handler/handler.go index 5d7d042..e2372e7 100644 --- a/web/handler/handler.go +++ b/web/handler/handler.go @@ -82,8 +82,7 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http router.HandleFunc("/users/{user}/status", h.showUserStatusView).Methods(http.MethodGet) router.HandleFunc("/users/{user}/status.json", h.showUserStatusJSONView).Methods(http.MethodGet) - //router.HandleFunc("/users/{user}/status.png", h.showUserStatusImageViewEmoji).Methods(http.MethodGet) - //router.HandleFunc("/users/{user}/badge.png", h.showUserStatusBadgeView).Methods(http.MethodGet) + router.HandleFunc("/users/{user}/badge.png", h.showUserStatusImageViewEmoji).Methods(http.MethodGet) router.PathPrefix("/assets/").Handler( http.StripPrefix("/assets/", http.FileServer( diff --git a/web/handler/html.go b/web/handler/html.go index 68719c1..6444e95 100644 --- a/web/handler/html.go +++ b/web/handler/html.go @@ -166,12 +166,13 @@ var TplMap = map[string]string{ status widget for your homepage

Subscribe via Atom

+


{{ else }}

Welcome!

status.cafe is a place to share your current status.

Register now!

{{ end }} -

+


Status stream

diff --git a/web/handler/html/index.html b/web/handler/html/index.html index 23ca61e..1d71a72 100644 --- a/web/handler/html/index.html +++ b/web/handler/html/index.html @@ -15,12 +15,13 @@ status widget for your homepage

Subscribe via Atom

+


{{ else }}

Welcome!

status.cafe is a place to share your current status.

Register now!

{{ end }} -

+


Status stream

diff --git a/web/handler/login_check.go b/web/handler/login_check.go index 9019ccd..29318d7 100644 --- a/web/handler/login_check.go +++ b/web/handler/login_check.go @@ -1,6 +1,7 @@ package handler import ( + "github.com/gorilla/csrf" "net/http" "status/model" "status/web/handler/form" @@ -15,7 +16,8 @@ func (h *Handler) checkLogin(w http.ResponseWriter, r *http.Request) { if err != nil { f.Error = err.Error() h.renderLayout(w, "login", map[string]interface{}{ - "form": f, + "form": f, + csrf.TemplateTag: csrf.TemplateField(r), }, "") return } diff --git a/web/handler/user_show.go b/web/handler/user_show.go index 2a6a819..79de4b3 100644 --- a/web/handler/user_show.go +++ b/web/handler/user_show.go @@ -6,22 +6,20 @@ import ( "encoding/xml" "errors" "fmt" - "github.com/fogleman/gg" - "github.com/golang/freetype" "github.com/golang/freetype/truetype" "github.com/gorilla/mux" "golang.org/x/image/draw" "golang.org/x/image/font" + "golang.org/x/image/font/gofont/gobold" _ "golang.org/x/image/font/gofont/gobold" - "golang.org/x/image/font/gofont/goregular" "golang.org/x/image/math/fixed" "html/template" "image" - "image/color" - "image/jpeg" + "image/png" "io" "os" + "path/filepath" "status/model" "strconv" "strings" @@ -31,85 +29,19 @@ import ( "net/http" ) -const ( - exitSuccess = 0 - exitFailure = 1 -) - -const ( - imagePath = "/home/m15o/Downloads/noto-emoji-main/png/128" - aliasFile = "/home/m15o/Downloads/noto-emoji-main/emoji_aliases.txt" - fontSize = 64 // point - imageWidth = 640 // pixel - imageHeight = 120 // pixel - textTopMargin = 80 // fixed.I - lineHeight = 70 // fixed.I -) - -// emoji alias -var alias = map[string]string{} - -// -//func init() { -// fp, err := os.Open(aliasFile) -// if err != nil { -// panic(err) -// } -// -// s := bufio.NewScanner(fp) -// for s.Scan() { -// line := s.Text() -// -// /* -// alias file format: -// # 'fe0f' is not in these sequences -// 1f3c3;1f3c3_200d_2642 # RUNNER -> man running -// */ -// -// if strings.HasPrefix(line, "#") { -// continue -// } -// -// s := strings.Split(line, ";") -// if len(s) != 2 { -// continue -// } -// -// i := strings.Index(s[1], " ") -// if i < 0 { -// continue -// } -// -// from := s[0] -// to := s[1][:i] -// -// alias[from] = to -// } -// -// if err := s.Err(); err != nil { -// fmt.Fprintln(os.Stderr, "reading alias file:", err) -// } -// -//} - func exist(path string) bool { _, err := os.Stat(path) return err == nil } -// GetPath if unicode codepoint is included in list and file exists return file path -func getPath(r rune) (string, error) { +func getPath(r rune, emojipath string) (string, error) { if r > 47 && r < 58 { return "", errors.New("numeric char") } name := fmt.Sprintf("%.4x", r) var path string - if v, ok := alias[name]; ok { - path = fmt.Sprintf("%s/emoji_u%s.png", imagePath, v) - } else { - path = fmt.Sprintf("%s/emoji_u%s.png", imagePath, name) - } + path = fmt.Sprintf("%s/emoji_u%s.png", emojipath, name) if !exist(path) { return "", fmt.Errorf("%s does NOT exist", path) @@ -118,10 +50,10 @@ func getPath(r rune) (string, error) { return path, nil } -func loadEmoji(r rune, size int) (image.Image, bool) { +func loadEmoji(r rune, size int, emojipath string) (image.Image, bool) { var img image.Image - path, err := getPath(r) + path, err := getPath(r, emojipath) if err != nil { //fmt.Fprintln(os.Stderr, err) return img, false @@ -147,11 +79,10 @@ func loadEmoji(r rune, size int) (image.Image, bool) { return dst, true } -func renderLine(img draw.Image, dr *font.Drawer, s string) { +func renderLine(img image.Image, dr *font.Drawer, s, emojipath string) { size := dr.Face.Metrics().Ascent.Floor() + dr.Face.Metrics().Descent.Floor() - for _, r := range s { - emoji, ok := loadEmoji(r, size) + emoji, ok := loadEmoji(r, size, emojipath) if ok { // Drawer.Dot is glyph baseline of next glyph @@ -160,36 +91,33 @@ func renderLine(img draw.Image, dr *font.Drawer, s string) { rect := image.Rect(0, 0, size, size).Add(p) // draw emoji and ascend baseline - draw.Draw(img, rect, emoji, image.ZP, draw.Over) + draw.Draw(img.(draw.Image), rect, emoji, image.ZP, draw.Over) dr.Dot.X += fixed.I(size) } else { // fallback: use normal glyph - dr.DrawString(string(r)) } } } -func renderText(img draw.Image, face font.Face, text string) error { +func renderText(img image.Image, face font.Face, text, emojipath string) error { dr := &font.Drawer{ - Dst: img, + Dst: img.(draw.Image), Src: image.White, Face: face, Dot: fixed.Point26_6{}, } + for _, s := range strings.Split(text, "\n") { + dr.Dot.X = fixed.I(6) + dr.Dot.Y = fixed.I(13) - for i, s := range strings.Split(text, "\n") { - dr.Dot.X = (fixed.I(imageWidth) - dr.MeasureString(s)) / 2 - dr.Dot.Y = fixed.I(textTopMargin + i*lineHeight) - - renderLine(img, dr, s) + renderLine(img, dr, s, emojipath) } - return nil } func outputJPEG(img image.Image, w io.Writer) error { buf := new(bytes.Buffer) - err := jpeg.Encode(buf, img, nil) + err := jpeg.Encode(buf, img, &jpeg.Options{Quality: 100}) if err != nil { return err } @@ -316,50 +244,7 @@ func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) { h.view("status").Execute(w, map[string]interface{}{"status": status}) } -func drawText(canvas *image.RGBA, text string) error { - var ( - fgColor image.Image - fontFace *truetype.Font - err error - fontSize = 12.0 - ) - fgColor = image.White - fontFace, err = freetype.ParseFont(goregular.TTF) - fontDrawer := &font.Drawer{ - Dst: canvas, - Src: fgColor, - Face: truetype.NewFace(fontFace, &truetype.Options{ - Size: fontSize, - Hinting: font.HintingFull, - }), - } - //textBounds, _ := fontDrawer.BoundString(text) - xPosition := fixed.I(20) - //textHeight := textBounds.Max.Y - textBounds.Min.Y - yPosition := fixed.I(20) - fontDrawer.Dot = fixed.Point26_6{ - X: xPosition, - Y: yPosition, - } - fontDrawer.DrawString(text) - return err -} - -func createAvatar(text string) *image.RGBA { - bgColor := color.RGBA{ - R: 255, - G: 20, - B: 100, - A: 255, - } - background := image.NewRGBA(image.Rect(0, 0, 200, 100)) - draw.Draw(background, background.Bounds(), &image.Uniform{C: bgColor}, - image.Point{}, draw.Src) - drawText(background, text) - return background -} - -func (h *Handler) showUserStatusBadgeView(w http.ResponseWriter, r *http.Request) { +func (h *Handler) showUserStatusImageViewEmoji(w http.ResponseWriter, r *http.Request) { user := mux.Vars(r)["user"] if !h.storage.UserExists(user) { notFound(w) @@ -371,131 +256,49 @@ func (h *Handler) showUserStatusBadgeView(w http.ResponseWriter, r *http.Request return } - var username string + face := "🙂" if len(statuses) > 0 { - username = statuses[0].User + face = statuses[0].Face } - background := "#f0ffff" - - dc := gg.NewContext(88, 31) - dc.SetHexColor(background) - dc.Clear() - dc.SetHexColor("#191970") - font, err := truetype.Parse(goregular.TTF) - if err != nil { - panic("") - } - face := truetype.NewFace(font, &truetype.Options{ - Size: 12, - }) - dc.SetFontFace(face) - if err != nil { - fmt.Println(err) - } - //dc.DrawString(username, 10, 14) - dc.DrawStringAnchored(username, 88/2, 8, 0.5, 0.5) - dc.DrawStringAnchored("status.cafe", 88/2, 20, 0.5, 0.5) - dc.EncodePNG(w) -} - -// -//func (h *Handler) showUserStatusImageViewEmoji(w http.ResponseWriter, r *http.Request) { -// user := mux.Vars(r)["user"] -// if !h.storage.UserExists(user) { -// notFound(w) -// return -// } -// statuses, _, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0) -// if err != nil { -// serverError(w, err) -// return -// } -// -// var username string -// var face string -// if len(statuses) > 0 { -// username = statuses[0].User -// face = statuses[0].Face -// } -// -// img := image.NewRGBA(image.Rect(0, 0, imageWidth, imageHeight)) -// -// ft, err := truetype.Parse(gobold.TTF) -// if err != nil { -// fmt.Fprintln(os.Stderr, err) -// os.Exit(exitFailure) -// } -// -// opt := truetype.Options{ -// Size: fontSize, -// DPI: 0, -// Hinting: 0, -// GlyphCacheEntries: 0, -// SubPixelsX: 0, -// SubPixelsY: 0, -// } -// -// renderText(img, truetype.NewFace(ft, &opt), fmt.Sprintf("%s %s", username, face)) -// -// err = outputJPEG(img, w) -// if err != nil { -// fmt.Fprintln(os.Stderr, err) -// os.Exit(exitFailure) -// } -//} - -func (h *Handler) showUserStatusImageView(w http.ResponseWriter, r *http.Request) { - user := mux.Vars(r)["user"] - if !h.storage.UserExists(user) { - notFound(w) - return - } - statuses, _, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0) + f, err := os.Open(filepath.Join(h.cfg.AssetsDir, "badge.png")) if err != nil { serverError(w, err) return } - //w.Header().Set("Access-Control-Allow-Origin", "*") - //w.Header().Set("Content-Type", "application/json") - var text string - if len(statuses) > 0 { - text = statuses[0].Content - } - background := "#ffffff" - foreground := "#000000" - - if c := r.URL.Query().Get("background"); c != "" { - background = "#" + c - } - - if c := r.URL.Query().Get("foreground"); c != "" { - foreground = "#" + c - } - - width := 125 - dc := gg.NewContext(width, 125) - dc.SetHexColor(background) - dc.Clear() - dc.SetHexColor(foreground) - font, err := truetype.Parse(goregular.TTF) + img, err := png.Decode(f) if err != nil { - panic("") + serverError(w, err) + return } - face := truetype.NewFace(font, &truetype.Options{ - Size: 9, - }) - dc.SetFontFace(face) - if err != nil { - fmt.Println(err) - } - dc.DrawStringWrapped(text, 8, 4, 0, 0, float64(width-(2*8)), 1.5, gg.AlignLeft) - dc.EncodePNG(w) - //dc.SavePNG("out.png") - //avatar := createAvatar(text) - //png.Encode(w, avatar) + ft, err := truetype.Parse(gobold.TTF) + if err != nil { + serverError(w, err) + return + } + + opt := truetype.Options{ + Size: 14, + DPI: 0, + Hinting: 0, + GlyphCacheEntries: 0, + SubPixelsX: 0, + SubPixelsY: 0, + } + + err = renderText(img, truetype.NewFace(ft, &opt), fmt.Sprintf("%s", face), h.cfg.EmojiFolder) + if err != nil { + serverError(w, err) + return + } + + err = outputJPEG(img, w) + if err != nil { + serverError(w, err) + return + } } func truncate(s string, max int) string {