From 55479e3523ed033d01ef0b21da0a211e8f514890 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Tue, 19 Sep 2023 01:35:32 -0300 Subject: [PATCH] replacing npub/nprofile references with username on rendered images, line length fixes for quotes and preceding quotes with a space -- along with some small refactors. --- image.go | 8 ++- render.go | 4 +- text.go | 60 +++++++++++--------- utils.go | 160 +++++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 160 insertions(+), 72 deletions(-) diff --git a/image.go b/image.go index 560f66c..7b354a1 100644 --- a/image.go +++ b/image.go @@ -21,7 +21,13 @@ func generate(w http.ResponseWriter, r *http.Request) { return } - lines := normalizeText(renderInlineMentions(event.Content)) + lines := normalizeText( + replaceUserReferencesWithNames(r.Context(), + renderQuotesAsArrowPrefixedText(r.Context(), + event.Content, + ), + ), + ) img, err := drawImage(lines, getPreviewStyle(r)) if err != nil { diff --git a/render.go b/render.go index 523583b..0e66c8e 100644 --- a/render.go +++ b/render.go @@ -24,7 +24,7 @@ type Event struct { } func render(w http.ResponseWriter, r *http.Request) { - fmt.Println(r.URL.Path, ":~", r.Header.Get("user-agent")) + fmt.Println(r.URL.Path, "#/", r.Header.Get("user-agent")) w.Header().Set("Content-Type", "text/html") typ := "" @@ -306,7 +306,7 @@ func render(w http.ResponseWriter, r *http.Request) { if event.Kind == 30023 || event.Kind == 30024 { content = mdToHTML(content, typ == "telegram_instant_view") } else { - content = basicFormatting(renderInlineMentions(html.EscapeString(content))) + content = basicFormatting(renderQuotesAsHTML(r.Context(), html.EscapeString(content))) } // pretty JSON diff --git a/text.go b/text.go index f5e59e1..d115fd2 100644 --- a/text.go +++ b/text.go @@ -3,7 +3,6 @@ package main import ( "image" "image/draw" - "regexp" "strings" "github.com/apatters/go-wordwrap" @@ -12,34 +11,45 @@ import ( ) const ( - MAX_LINES = 20 - MAX_CHARS_PER_LINE = 51 - FONT_SIZE = 7 + MAX_LINES = 20 + MAX_CHARS_PER_LINE = 52 + MAX_CHARS_PER_QUOTE_LINE = 48 + FONT_SIZE = 7 ) -func normalizeText(t string) []string { - re := regexp.MustCompile(`{div}.*?{/div}`) - t = re.ReplaceAllString(t, "") +func normalizeText(input []string) []string { lines := make([]string, 0, MAX_LINES) - mention := false - maxChars := MAX_CHARS_PER_LINE - for _, line := range strings.Split(t, "\n") { - line = wordwrap.Wrap(maxChars, line) - for _, subline := range strings.Split(line, "\n") { - if strings.HasPrefix(subline, "{blockquote}") { - mention = true - subline = strings.ReplaceAll(subline, "{blockquote}", "") - subline = strings.ReplaceAll(subline, "{/blockquote}", "") - maxChars = MAX_CHARS_PER_LINE - 1 - } else if strings.HasSuffix(subline, "{/blockquote}") { - mention = false - subline = strings.ReplaceAll(subline, "{/blockquote}", "") - maxChars = MAX_CHARS_PER_LINE + l := 0 // global line counter + + for _, block := range input { + quoting := false + maxChars := MAX_CHARS_PER_LINE + if strings.HasPrefix(block, "> ") { + quoting = true + maxChars = MAX_CHARS_PER_QUOTE_LINE // on quote lines we tolerate less characters + block = block[2:] + lines = append(lines, "") // add an empty line before each quote + l++ + } + for _, line := range strings.Split(block, "\n") { + if l == MAX_LINES { + // escape and return here if we're over max lines + return lines } - if mention { - subline = "> " + subline + + line = wordwrap.Wrap(maxChars, strings.TrimSpace(line)) + for _, subline := range strings.Split(line, "\n") { + // if a line has a word so big that it would overflow (like a nevent), hide it with an ellipsis + if len(subline) > maxChars { + subline = subline[0:maxChars-1] + "…" + } + if quoting { + subline = "> " + subline + } + + lines = append(lines, subline) + l++ } - lines = append(lines, subline) } } return lines @@ -59,7 +69,7 @@ func drawImage(lines []string, style string) (image.Image, error) { rgba := image.NewRGBA(image.Rect(0, 0, width, height)) // draw the empty image - draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) + draw.Draw(rgba, rgba.Bounds(), bg, image.Point{}, draw.Src) // create new freetype context to get ready for // adding text. diff --git a/utils.go b/utils.go index a3a80b0..617a4b4 100644 --- a/utils.go +++ b/utils.go @@ -23,6 +23,16 @@ import ( "github.com/pelletier/go-toml" ) +var ( + urlSuffixMatcher = regexp.MustCompile(`[\w-_.]+\.[\w-_.]+(\/[\/\w]*)?$`) + nostrEveryMatcher = regexp.MustCompile(`\S*(nostr:)?((npub|note|nevent|nprofile|naddr)1[a-z0-9]+)\b`) + nostrNoteNeventMatcher = regexp.MustCompile(`\S*(nostr:)?((note|nevent)1[a-z0-9]+)\b`) + nostrNpubNprofileMatcher = regexp.MustCompile(`\S*(nostr:)?((npub|nprofile)1[a-z0-9]+)\b`) + hrefMatcher = regexp.MustCompile(`\S*(https?://\S+)\S*`) + imgsMatcher = regexp.MustCompile(`\S*(\()?(https?://\S+(\.jpg|\.jpeg|\.png|\.webp|\.gif))\S*`) + videoMatcher = regexp.MustCompile(`\S*(https?://\S+(\.mp4|\.ogg|\.webm|.mov))\S*`) +) + var kindNames = map[int]string{ 0: "Metadata", 1: "Short Text Note", @@ -97,8 +107,6 @@ var kindNIPS = map[int]string{ 30078: "78", } -var urlSuffixMatcher = regexp.MustCompile(`[\w-_.]+\.[\w-_.]+(\/[\/\w]*)?$`) - type ClientReference struct { Name string URL string @@ -208,11 +216,8 @@ func replaceImageURLsWithTags(input string, replacement string) string { // Match and replace image URLs with a custom replacement // Usually is html => ` ` // or markdown !()[...] tags for further processing => `![](%s)` - var regex *regexp.Regexp - imgsPattern := `\S*(\()?(https?://\S+(\.jpg|\.jpeg|\.png|\.webp|\.gif))\S*` - regex = regexp.MustCompile(imgsPattern) - input = regex.ReplaceAllStringFunc(input, func(match string) string { - submatch := regex.FindStringSubmatch(match) + input = imgsMatcher.ReplaceAllStringFunc(input, func(match string) string { + submatch := imgsMatcher.FindStringSubmatch(match) if len(submatch) < 2 || strings.Contains(submatch[0], "](") { // Markdown ![](...) image return match @@ -228,11 +233,8 @@ func replaceVideoURLsWithTags(input string, replacement string) string { // Match and replace video URLs with a custom replacement // Usually is html