diff --git a/pkg/renderer/context.go b/pkg/renderer/context.go index 4b95334e..10fc5d17 100644 --- a/pkg/renderer/context.go +++ b/pkg/renderer/context.go @@ -8,7 +8,7 @@ import ( // Context is a custom implementation of the standard golang context.Context interface, // which carries the types.Document which is being processed type Context struct { - Config *configuration.Configuration + Config *configuration.Configuration // TODO: use composition (remove the `Config` field) // TableOfContents exists even if the document did not specify the `:toc:` attribute. // It will take into account the configured `:toclevels:` attribute value. TableOfContents types.TableOfContents diff --git a/pkg/renderer/sgml/html5/icon.go b/pkg/renderer/sgml/html5/icon.go index 9f6557e8..d9a35a5b 100644 --- a/pkg/renderer/sgml/html5/icon.go +++ b/pkg/renderer/sgml/html5/icon.go @@ -11,7 +11,7 @@ const ( `{{ if .Link }}{{ end }}` + `` - iconImageTmpl = `{{ .Alt }}{{ end }}
-{{ if ne .Href "" }}{{ end }}{{ .Alt }}{{ if ne .Href "" }}{{ end }} +{{ if ne .Href "" }}{{ end }}{{ .Alt }}{{ if ne .Href "" }}{{ end }}
{{ if .Title }}
{{ .Caption }}{{ .Title }}
{{ else }} {{ end }} ` - inlineImageTmpl = `{{ if ne .Href "" }}{{ end }}{{ .Alt }}{{ if ne .Href "" }}{{ end }}` + inlineImageTmpl = `{{ if ne .Href "" }}{{ end }}{{ .Alt }}{{ if ne .Href "" }}{{ end }}` ) diff --git a/pkg/renderer/sgml/html5/image_test.go b/pkg/renderer/sgml/html5/image_test.go index 84babb82..81f0547f 100644 --- a/pkg/renderer/sgml/html5/image_test.go +++ b/pkg/renderer/sgml/html5/image_test.go @@ -353,4 +353,70 @@ image::file:///bar/foo.png[]` Expect(RenderHTML(source)).To(MatchHTML(expected)) }) }) + + Context("data-uri", func() { + // see https://docs.asciidoctor.org/asciidoctor/latest/html-backend/manage-images/#allow-uri-read-attribute + + It("inline image with imagesdir", func() { + source := ` +:imagesdir: ../../../../test/images +:data-uri: + +image:favicon-glasses-16x16.png[Glasses]` + + expected := `
+

Glasses

+
+` + Expect(RenderHTML(source)).To(MatchHTML(expected)) + }) + + It("inline image not found", func() { + source := ` +:imagesdir: ./path/to/somewhere/else +:data-uri: + +image:favicon-glasses-16x16.png[Glasses]` + + expected := `
+

Glasses

+
+` + Expect(RenderHTML(source)).To(MatchHTML(expected)) + // TODO: check that the log/output contains a WARNING message (`image to embed not found or not readable`) + }) + + It("block image with imagesdir", func() { + source := ` +:imagesdir: ../../../../test/images +:data-uri: + +image::favicon-glasses-16x16.png[Glasses]` + + expected := `
+
+Glasses +
+
+` + Expect(RenderHTML(source)).To(MatchHTML(expected)) + }) + + It("block image not found", func() { + source := ` +:imagesdir: ./path/to/somewhere/else +:data-uri: + +image::favicon-glasses-16x16.png[Glasses]` + + expected := `
+
+Glasses +
+
+` + Expect(RenderHTML(source)).To(MatchHTML(expected)) + // TODO: check that the log/output contains a WARNING message (`image to embed not found or not readable`) + }) + }) }) diff --git a/pkg/renderer/sgml/icon.go b/pkg/renderer/sgml/icon.go index 93298d1a..706bb5ec 100644 --- a/pkg/renderer/sgml/icon.go +++ b/pkg/renderer/sgml/icon.go @@ -98,7 +98,7 @@ func (r *sgmlRenderer) renderIcon(ctx *renderer.Context, icon types.Icon, admoni Flip string Width string Height string - Path string + Src string Admonition bool }{ Class: icon.Class, @@ -111,7 +111,7 @@ func (r *sgmlRenderer) renderIcon(ctx *renderer.Context, icon types.Icon, admoni Flip: icon.Attributes.GetAsStringWithDefault(types.AttrIconFlip, ""), Link: icon.Attributes.GetAsStringWithDefault(types.AttrInlineLink, ""), Window: icon.Attributes.GetAsStringWithDefault(types.AttrImageWindow, ""), - Path: renderIconPath(ctx, icon.Class), + Src: renderIconPath(ctx, icon.Class), Admonition: admonition, }) return string(s.String()), err diff --git a/pkg/renderer/sgml/image.go b/pkg/renderer/sgml/image.go index ecdafd96..438dc251 100644 --- a/pkg/renderer/sgml/image.go +++ b/pkg/renderer/sgml/image.go @@ -1,6 +1,8 @@ package sgml import ( + "encoding/base64" + "io/ioutil" "net/url" "path/filepath" "strconv" @@ -50,13 +52,14 @@ func (r *sgmlRenderer) renderImageBlock(ctx *renderer.Context, img *types.ImageB if err != nil { return "", errors.Wrap(err, "unable to render image") } - path := img.Location.Stringify() - alt, err := r.renderImageAlt(img.Attributes, path) + src := r.getImageSrc(ctx, img.Location) + alt, err := r.renderImageAlt(img.Attributes, src) if err != nil { return "", errors.Wrap(err, "unable to render image") } err = r.blockImage.Execute(result, struct { ID string + Src string Title string ImageNumber int Caption string @@ -65,9 +68,9 @@ func (r *sgmlRenderer) renderImageBlock(ctx *renderer.Context, img *types.ImageB Alt string Width string Height string - Path string }{ ID: r.renderElementID(img.Attributes), + Src: src, Title: title, ImageNumber: number, Caption: caption.String(), @@ -76,7 +79,6 @@ func (r *sgmlRenderer) renderImageBlock(ctx *renderer.Context, img *types.ImageB Alt: alt, Width: img.Attributes.GetAsStringWithDefault(types.AttrWidth, ""), Height: img.Attributes.GetAsStringWithDefault(types.AttrHeight, ""), - Path: path, }) if err != nil { @@ -92,8 +94,8 @@ func (r *sgmlRenderer) renderInlineImage(ctx *Context, img *types.InlineImage) ( return "", errors.Wrap(err, "unable to render image") } href := img.Attributes.GetAsStringWithDefault(types.AttrInlineLink, "") - path := img.Location.Stringify() - alt, err := r.renderImageAlt(img.Attributes, path) + src := r.getImageSrc(ctx, img.Location) + alt, err := r.renderImageAlt(img.Attributes, src) if err != nil { return "", errors.Wrap(err, "unable to render image") } @@ -103,21 +105,21 @@ func (r *sgmlRenderer) renderInlineImage(ctx *Context, img *types.InlineImage) ( } err = r.inlineImage.Execute(result, struct { + Src string Roles string Title string Href string Alt string Width string Height string - Path string }{ + Src: src, Title: title, Roles: roles, Href: href, Alt: alt, Width: img.Attributes.GetAsStringWithDefault(types.AttrWidth, ""), Height: img.Attributes.GetAsStringWithDefault(types.AttrHeight, ""), - Path: path, }) if err != nil { @@ -129,6 +131,25 @@ func (r *sgmlRenderer) renderInlineImage(ctx *Context, img *types.InlineImage) ( return result.String(), nil } +func (r *sgmlRenderer) getImageSrc(ctx *Context, location *types.Location) string { + src := location.Stringify() + + // if Data URI is enables, then include the content of the file in the `src` attribute of the `` tag + if !ctx.Attributes.Has("data-uri") { + return src + } + dir := filepath.Dir(ctx.Config.Filename) + src = filepath.Join(dir, src) + result := "data:image/" + strings.TrimPrefix(filepath.Ext(src), ".") + ";base64," + data, err := ioutil.ReadFile(src) + if err != nil { + log.Warnf("image to embed not found or not readable: %s", src) + return result + } + result += base64.StdEncoding.EncodeToString(data) + return result +} + func (r *sgmlRenderer) renderImageAlt(attrs types.Attributes, path string) (string, error) { if alt, found, err := attrs.GetAsString(types.AttrImageAlt); err != nil { return "", errors.Wrap(err, "unable to render image") diff --git a/pkg/renderer/sgml/xhtml5/icon.go b/pkg/renderer/sgml/xhtml5/icon.go index efab1a0c..d4315547 100644 --- a/pkg/renderer/sgml/xhtml5/icon.go +++ b/pkg/renderer/sgml/xhtml5/icon.go @@ -8,7 +8,7 @@ const ( // // Only the img tag needs to be made XHTML safe. - iconImageTmpl = `{{ .Alt }}{{ end }}\n" + "
\n" + `{{ if .Href }}{{ end }}` + - `{{ .Alt }}{{ if .Href }}{{ end }}\n" + @@ -16,7 +16,7 @@ const ( inlineImageTmpl = `` + `{{ if .Href }}{{ end }}` + - `