diff options
| -rw-r--r-- | _doc/SPECS.adoc | 12 | ||||
| -rw-r--r-- | element.go | 94 | ||||
| -rw-r--r-- | element_image.go | 163 | ||||
| -rw-r--r-- | element_image_test.go | 38 | ||||
| -rw-r--r-- | html_backend.go | 41 | ||||
| -rw-r--r-- | inline_parser.go | 30 | ||||
| -rw-r--r-- | testdata/element_image_test.txt | 35 |
7 files changed, 246 insertions, 167 deletions
diff --git a/_doc/SPECS.adoc b/_doc/SPECS.adoc index 20567ee..55e2615 100644 --- a/_doc/SPECS.adoc +++ b/_doc/SPECS.adoc @@ -346,12 +346,13 @@ ABSOLUTE_PATH = "/" WORD *( "/" WORD ) RELATIVE_PATH = ( "." / ".." ) "/" WORD * ( "/" WORD ) ---- + == Images -=== Inline image +=== Block image ---- -IMAGE_INLINE = "image:" URL "[" (IMAGE_ATTRS) "]" +BLOCK_IMAGE = "image::" URL "[" IMAGE_ATTRS "]" IMAGE_ATTRS = TEXT ("," IMAGE_WIDTH ("," IMAGE_HEIGHT)) *("," IMAGE_OPTS) @@ -360,6 +361,13 @@ IMAGE_OPTS = IMAGE_OPT_KEY "=" 1*VCHAR IMAGE_OPT_KEY = "title" / "float" / "align" / "role" / "link" ---- +=== Inline image + +---- +IMAGE_INLINE = "image:" URL "[" (IMAGE_ATTRS) "]" +---- + + == Video ---- @@ -301,100 +301,6 @@ func (el *element) parseBlockAudio(doc *Document, line []byte) bool { return true } -// parseBlockImage parse the image block or line. -// The line parameter must not have "image::" block or "image:" macro prefix. -func (el *element) parseBlockImage(doc *Document, line []byte) bool { - var ( - attrBegin = bytes.IndexByte(line, '[') - - attr string - key string - val string - kv []string - - attrs [][]byte - src []byte - battr []byte - alt []byte - - attrEnd int - dot int - x int - ) - - if attrBegin < 0 { - return false - } - attrEnd = bytes.IndexByte(line, ']') - if attrEnd < 0 { - return false - } - - src = bytes.TrimRight(line[:attrBegin], " \t") - - if el.Attrs == nil { - el.Attrs = make(map[string]string) - } - src = applySubstitutions(doc, src) - el.Attrs[attrNameSrc] = string(src) - - attrs = bytes.Split(line[attrBegin+1:attrEnd], []byte(`,`)) - if el.Attrs == nil { - el.Attrs = make(map[string]string) - } - var hasWidth bool - for x, battr = range attrs { - attr = string(battr) - if x == 0 { - alt = bytes.TrimSpace(attrs[0]) - if len(alt) == 0 { - dot = bytes.IndexByte(src, '.') - if dot > 0 { - alt = src[:dot] - } - } - el.Attrs[attrNameAlt] = string(alt) - continue - } - if x == 1 { - if ascii.IsDigits(attrs[1]) { - el.Attrs[attrNameWidth] = string(attrs[1]) - hasWidth = true - continue - } - } - if hasWidth && x == 2 { - if ascii.IsDigits(attrs[2]) { - el.Attrs[attrNameHeight] = string(attrs[2]) - } - } - kv = strings.SplitN(attr, `=`, 2) - if len(kv) != 2 { - continue - } - key = strings.TrimSpace(kv[0]) - val = strings.Trim(kv[1], `"`) - switch key { - case attrNameFloat, attrNameAlign, attrNameRole: - if val == `center` { - val = `text-center` - } - el.addRole(val) - default: - el.Attrs[key] = val - } - } - - for key, val = range el.Attrs { - if key == attrNameLink { - val = string(applySubstitutions(doc, []byte(val))) - el.Attrs[key] = val - } - } - - return true -} - func (el *element) parseInlineMarkup(doc *Document, kind int) { if len(el.raw) == 0 { return diff --git a/element_image.go b/element_image.go new file mode 100644 index 0000000..d98bb61 --- /dev/null +++ b/element_image.go @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package asciidoctor + +import ( + "bytes" + "fmt" + "io" + "strings" + + "git.sr.ht/~shulhan/pakakeh.go/lib/ascii" +) + +// parseBlockImage parse the image block or line. +// The line parameter must not contains the "image::" block or "image:" +// macro prefix. +func (el *element) parseBlockImage(doc *Document, line []byte) bool { + var attrBegin = bytes.IndexByte(line, '[') + if attrBegin < 0 { + return false + } + var attrEnd = bytes.IndexByte(line, ']') + if attrEnd < 0 { + return false + } + + var link = bytes.TrimRight(line[:attrBegin], " \t") + link = applySubstitutions(doc, link) + + if el.Attrs == nil { + el.Attrs = make(map[string]string) + } + el.Attrs[attrNameSrc] = string(link) + + var listAttribute = bytes.Split(line[attrBegin+1:attrEnd], []byte(`,`)) + if el.Attrs == nil { + el.Attrs = make(map[string]string) + } + + var ( + x int + battr []byte + hasWidth bool + ) + for x, battr = range listAttribute { + if x == 0 { + var alt = bytes.TrimSpace(listAttribute[0]) + if len(alt) == 0 { + var dot = bytes.IndexByte(link, '.') + if dot > 0 { + alt = link[:dot] + } + } + el.Attrs[attrNameAlt] = string(alt) + continue + } + if x == 1 { + if ascii.IsDigits(listAttribute[1]) { + el.Attrs[attrNameWidth] = string(listAttribute[1]) + hasWidth = true + continue + } + } + if hasWidth && x == 2 { + if ascii.IsDigits(listAttribute[2]) { + el.Attrs[attrNameHeight] = string(listAttribute[2]) + } + } + + var attr = string(battr) + var kv = strings.SplitN(attr, `=`, 2) + if len(kv) != 2 { + continue + } + var key = strings.TrimSpace(kv[0]) + var val = strings.Trim(kv[1], `"`) + + switch key { + case attrNameFloat, attrNameAlign, attrNameRole: + if val == `center` { + val = `text-center` + } + el.addRole(val) + + case attrNameLink: + val = string(applySubstitutions(doc, []byte(val))) + el.Attrs[key] = val + + default: + el.Attrs[key] = val + } + } + return true +} + +func parseInlineImage(doc *Document, content []byte) (el *element, n int) { + // If the next character is ':' (as in block "image::") mark it as + // invalid inline image, since this is block image that has been + // parsed but invalid (probably missing '[]'). + if content[0] == ':' { + return nil, 0 + } + + _, n = indexByteUnescape(content, ']') + if n < 0 { + return nil, 0 + } + + var lineImage = content[:n+1] + el = &element{ + elementAttribute: elementAttribute{ + Attrs: make(map[string]string), + }, + kind: elKindInlineImage, + } + if el.parseBlockImage(doc, lineImage) { + return el, n + 2 + } + return nil, 0 +} + +func htmlWriteInlineImage(el *element, out io.Writer) { + var ( + classes = strings.TrimSpace(`image ` + el.htmlClasses()) + + link string + withLink bool + ) + + fmt.Fprintf(out, `<span class=%q>`, classes) + link, withLink = el.Attrs[attrNameLink] + if withLink { + fmt.Fprintf(out, `<a class=%q href=%q>`, attrValueImage, link) + } + + var ( + src = el.Attrs[attrNameSrc] + alt = el.Attrs[attrNameAlt] + + width string + height string + ok bool + ) + + width, ok = el.Attrs[attrNameWidth] + if ok { + width = fmt.Sprintf(` width="%s"`, width) + } + height, ok = el.Attrs[attrNameHeight] + if ok { + height = fmt.Sprintf(` height="%s"`, height) + } + + fmt.Fprintf(out, `<img src=%q alt=%q%s%s>`, src, alt, width, height) + + if withLink { + fmt.Fprint(out, `</a>`) + } + + fmt.Fprint(out, `</span>`) +} diff --git a/element_image_test.go b/element_image_test.go new file mode 100644 index 0000000..a548501 --- /dev/null +++ b/element_image_test.go @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package asciidoctor + +import ( + "bytes" + "testing" + + "git.sr.ht/~shulhan/pakakeh.go/lib/test" +) + +func TestElementParseBlockImage(t *testing.T) { + var ( + tdata *test.Data + err error + ) + tdata, err = test.LoadData(`testdata/element_image_test.txt`) + if err != nil { + t.Fatal(err) + } + + var ( + caseName string + input []byte + got bytes.Buffer + ) + for caseName, input = range tdata.Input { + var doc = Parse(input) + + got.Reset() + doc.ToHTMLEmbedded(&got) + + var exp = string(tdata.Output[caseName]) + test.Assert(t, caseName, exp, got.String()) + } +} diff --git a/html_backend.go b/html_backend.go index b404ba7..14137e5 100644 --- a/html_backend.go +++ b/html_backend.go @@ -1063,47 +1063,6 @@ func htmlWriteHeader(doc *Document, out io.Writer) { fmt.Fprint(out, "\n</div>") } -func htmlWriteInlineImage(el *element, out io.Writer) { - var ( - classes = strings.TrimSpace(`image ` + el.htmlClasses()) - - link string - withLink bool - ) - - fmt.Fprintf(out, `<span class=%q>`, classes) - link, withLink = el.Attrs[attrNameLink] - if withLink { - fmt.Fprintf(out, `<a class=%q href=%q>`, attrValueImage, link) - } - - var ( - src = el.Attrs[attrNameSrc] - alt = el.Attrs[attrNameAlt] - - width string - height string - ok bool - ) - - width, ok = el.Attrs[attrNameWidth] - if ok { - width = fmt.Sprintf(` width="%s"`, width) - } - height, ok = el.Attrs[attrNameHeight] - if ok { - height = fmt.Sprintf(` height="%s"`, height) - } - - fmt.Fprintf(out, `<img src=%q alt=%q%s%s>`, src, alt, width, height) - - if withLink { - fmt.Fprint(out, `</a>`) - } - - fmt.Fprint(out, `</span>`) -} - func htmlWriteInlinePass(doc *Document, el *element, out io.Writer) { var text = htmlSubs(doc, el) diff --git a/inline_parser.go b/inline_parser.go index 2f18aaf..561cab7 100644 --- a/inline_parser.go +++ b/inline_parser.go @@ -694,36 +694,6 @@ func (pi *inlineParser) parseFormatUnconstrained( return false } -func parseInlineImage(doc *Document, content []byte) (elImage *element, n int) { - var ( - lineImage []byte - ) - - // If the next character is ':' (as in block "image::") mark it as - // invalid inline image, since this is block image that has been - // parsed but invalid (probably missing '[]'). - if content[0] == ':' { - return nil, 0 - } - - _, n = indexByteUnescape(content, ']') - if n < 0 { - return nil, 0 - } - - lineImage = content[:n+1] - elImage = &element{ - elementAttribute: elementAttribute{ - Attrs: make(map[string]string), - }, - kind: elKindInlineImage, - } - if elImage.parseBlockImage(doc, lineImage) { - return elImage, n + 2 - } - return nil, 0 -} - func (pi *inlineParser) parseMacro() bool { var ( el *element diff --git a/testdata/element_image_test.txt b/testdata/element_image_test.txt new file mode 100644 index 0000000..ae15cbb --- /dev/null +++ b/testdata/element_image_test.txt @@ -0,0 +1,35 @@ +>>> empty_block_image + +image::[] + +<<< empty_block_image + +<div class="imageblock"> +<div class="content"> +<img src="" alt=""> +</div> +</div> + +>>> with_absolute_path + +image::/absolute/path.png[Image alt text] + +<<< with_absolute_path + +<div class="imageblock"> +<div class="content"> +<img src="/absolute/path.png" alt="Image alt text"> +</div> +</div> + +>>> with_attr_link + +image::/image/path.png[Image alt text,link="{dummy}/link"] + +<<< with_attr_link + +<div class="imageblock"> +<div class="content"> +<img src="/image/path.png" alt="Image alt text"> +</div> +</div> |
