/
rendering.go
89 lines (81 loc) · 2.4 KB
/
rendering.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package jupyter
import (
"bytes"
"encoding/xml"
"fmt"
"image"
"image/png"
"net/http"
"strings"
)
const svgNamespace = "http://www.w3.org/2000/svg"
// renderDisplayData takes a value and renders it in one or more display-data
// representations.
func renderDisplayData(value interface{}) (data, metadata map[string]interface{}, err error) {
// TODO(axw) sniff/provide a way to specify types for things that net/http does not sniff:
// - SVG
// - JSON
// - LaTeX
data = make(map[string]interface{})
metadata = make(map[string]interface{})
var stringValue, contentType string
switch typedValue := value.(type) {
case image.Image:
// TODO(axw) provide a way for the user to use alternative encodings?
//
// The current presumption is that images will be mostly used
// for displaying graphs or illustrations where PNG produces
// better looking images.
var buf bytes.Buffer
if err := png.Encode(&buf, typedValue); err != nil {
return nil, nil, fmt.Errorf("encoding image: %v", err)
}
bounds := typedValue.Bounds()
data["image/png"] = buf.Bytes()
data["text/plain"] = fmt.Sprintf("%dx%d image", bounds.Dx(), bounds.Dy())
metadata["image/png"] = map[string]interface{}{
"width": bounds.Dx(),
"height": bounds.Dy(),
}
return data, metadata, nil
case []byte:
contentType = detectContentType(typedValue)
stringValue = string(typedValue)
case string:
contentType = detectContentType([]byte(typedValue))
stringValue = typedValue
default:
stringValue = fmt.Sprint(typedValue)
value = stringValue
contentType = detectContentType([]byte(stringValue))
}
data["text/plain"] = stringValue
if contentType != "text/plain" {
// "A plain text representation should always be
// provided in the text/plain mime-type."
data[contentType] = value
}
return data, metadata, nil
}
func detectContentType(data []byte) string {
contentType := http.DetectContentType(data)
// Strip off the parameters ("; ...") from content-type;
// Jupyter does not expect them.
pos := strings.IndexRune(contentType, ';')
if pos != -1 {
contentType = contentType[:pos]
}
if contentType == "text/xml" {
var document struct {
XMLName xml.Name
}
// If we fail to unmarshal the XML, just return the
// content-type as it is.
if err := xml.Unmarshal(data, &document); err == nil {
if document.XMLName.Space == svgNamespace {
contentType = "image/svg+xml"
}
}
}
return contentType
}