gmitohtml-upstream/pkg/gmitohtml/convert.go
Aaron Fischer 586b293bef Convert images to images instead of links
Image links will result in a `img` tag instead of a `a` tag. This
behaviour is disabled by default and can be enabled by adding the
following line to to config file:

convertimages: true

To get access to the config in the convert file, I had to move the
config file inside the gmitohtml namespace. This is specially handy
later on if the config file contains other settings which are useful for
the rest of the codebase.
2021-06-09 01:19:26 +02:00

184 lines
4.5 KiB
Go

package gmitohtml
import (
"bufio"
"bytes"
"errors"
"fmt"
"html"
"net/url"
"path"
"strings"
"sync"
)
// ErrInvalidURL is the error returned when the URL is invalid.
var ErrInvalidURL = errors.New("invalid URL")
var daemonAddress string
var assetLock sync.Mutex
var imageExtensions = []string{"png", "jpg", "jpeg", "gif", "svg", "webp"}
func rewriteURL(u string, loc *url.URL) string {
if daemonAddress != "" {
scheme := "gemini"
if strings.HasPrefix(loc.Path, "/file/") {
scheme = "file"
}
if strings.HasPrefix(u, "file://") {
if !allowFileAccess {
return "http://" + daemonAddress + "/?FileAccessNotAllowed"
}
return "http://" + daemonAddress + "/file/" + u[7:]
}
offset := 0
if strings.HasPrefix(u, "gemini://") {
offset = 9
}
firstSlash := strings.IndexRune(u[offset:], '/')
if firstSlash != -1 {
u = strings.ToLower(u[:firstSlash+offset]) + u[firstSlash+offset:]
}
if strings.HasPrefix(u, "gemini://") {
return "http://" + daemonAddress + "/gemini/" + u[9:]
} else if strings.Contains(u, "://") {
return u
} else if loc != nil && len(u) > 0 && !strings.HasPrefix(u, "//") {
if u[0] != '/' {
if loc.Path[len(loc.Path)-1] == '/' {
u = path.Join("/", loc.Path, u)
} else {
u = path.Join("/", path.Dir(loc.Path), u)
}
}
return "http://" + daemonAddress + "/" + scheme + "/" + strings.ToLower(loc.Host) + u
}
return "http://" + daemonAddress + "/" + scheme + "/" + u
}
return u
}
func newPage() []byte {
data := []byte(pageHeader)
if daemonAddress != "" {
data = append(data, navHeader...)
}
return append(data, contentHeader...)
}
// Convert converts text/gemini to text/html.
func Convert(page []byte, u string) []byte {
var result []byte
var preformatted bool
parsedURL, err := url.Parse(u)
if err != nil {
parsedURL = nil
err = nil
}
scanner := bufio.NewScanner(bytes.NewReader(page))
for scanner.Scan() {
line := scanner.Bytes()
l := len(line)
if l >= 3 && string(line[0:3]) == "```" {
preformatted = !preformatted
if preformatted {
result = append(result, []byte("<pre>\n")...)
} else {
result = append(result, []byte("</pre>\n")...)
}
continue
}
if preformatted {
result = append(result, html.EscapeString(string(line))...)
result = append(result, []byte("\n")...)
continue
}
if l >= 6 && bytes.HasPrefix(line, []byte("=>")) {
splitStart := 2
if line[splitStart] == ' ' || line[splitStart] == '\t' {
splitStart++
}
var split [][]byte
firstSpace := bytes.IndexRune(line[splitStart:], ' ')
firstTab := bytes.IndexRune(line[splitStart:], '\t')
if firstSpace != -1 && (firstTab == -1 || firstSpace < firstTab) {
split = bytes.SplitN(line[splitStart:], []byte(" "), 2)
} else if firstTab != -1 {
split = bytes.SplitN(line[splitStart:], []byte("\t"), 2)
}
var linkURL []byte
var linkLabel []byte
if len(split) == 2 {
linkURL = split[0]
linkLabel = split[1]
} else {
linkURL = line[splitStart:]
linkLabel = line[splitStart:]
}
// If link ends with gif/png/jpg, add a image instead of a link
parts := strings.Split(string(linkURL), ".")
extension := parts[len(parts)-1]
isImage := false
for _, ext := range imageExtensions {
if extension == ext {
isImage = true
}
}
if isImage && Config.ConvertImages {
img := append([]byte(`<img src="`), html.EscapeString(rewriteURL(string(linkURL), parsedURL))...)
img = append(img, []byte(`" alt="`)...)
img = append(img, html.EscapeString(string(linkLabel))...)
img = append(img, []byte(`"/>`)...)
result = append(result, img...)
} else {
link := append([]byte(`<a href="`), html.EscapeString(rewriteURL(string(linkURL), parsedURL))...)
link = append(link, []byte(`">`)...)
link = append(link, html.EscapeString(string(linkLabel))...)
link = append(link, []byte(`</a>`)...)
result = append(result, link...)
result = append(result, []byte("<br>")...)
}
continue
}
heading := 0
for i := 0; i < l; i++ {
if line[i] == '#' {
heading++
} else {
break
}
}
if heading > 0 {
result = append(result, []byte(fmt.Sprintf("<h%d>%s</h%d>", heading, html.EscapeString(string(line[heading:])), heading))...)
continue
}
result = append(result, html.EscapeString(string(line))...)
result = append(result, []byte("<br>")...)
}
if preformatted {
result = append(result, []byte("</pre>\n")...)
}
data := newPage()
data = append(data, result...)
data = append(data, []byte(pageFooter)...)
return fillTemplateVariables(data, u, false)
}