mirror of
https://code.rocketnine.space/tslocum/gmitohtml.git
synced 2024-11-15 10:27:52 +01:00
Aaron Fischer
586b293bef
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.
184 lines
4.5 KiB
Go
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)
|
|
}
|