// XML2RFC v2 rendering backend
package mmark
import (
"bytes"
"fmt"
"strconv"
"strings"
)
// References code in Xml2rfcv3.go
// XML renderer configuration options.
const (
XML2_STANDALONE = 1 << iota // create standalone document
)
// Xml2 is a type that implements the Renderer interface for XML2RFV3 output.
//
// Do not create this directly, instead use the Xml2Renderer function.
type xml2 struct {
flags int // XML2_* options
sectionLevel int // current section level
docLevel int // frontmatter/mainmatter or backmatter
part bool // parts cannot nest, if true a part has been opened
specialSection int // are we in a special section
paraInList bool // subsequent paras in lists are faked with vspace
// store the IAL we see for this block element
ial *inlineAttr
// titleBlock in TOML
titleBlock *title
// (@good) example list group counter
group map[string]int
}
// Xml2Renderer creates and configures a Xml2 object, which
// satisfies the Renderer interface.
//
// flags is a set of XML2_* options ORed together
func Xml2Renderer(flags int) Renderer {
return &xml2{flags: flags, group: make(map[string]int)}
}
func (options *xml2) Flags() int { return options.flags }
func (options *xml2) State() int { return 0 }
func (options *xml2) SetAttr(i *inlineAttr) {
options.ial = i
}
func (options *xml2) Attr() *inlineAttr {
if options.ial == nil {
return newInlineAttr()
}
return options.ial
}
func (options *xml2) AttrString(i *inlineAttr) string {
if i == nil {
return ""
}
s := ""
if i.id != "" {
s = " anchor=\"" + i.id + "\""
}
keys := i.SortClasses()
if len(keys) > 0 {
s += " class=\"" + strings.Join(keys, " ") + "\""
}
keys = i.SortAttributes()
attr := make([]string, len(keys))
for j, k := range keys {
v := i.attr[k]
attr[j] = k + "=\"" + v + "\""
}
if len(keys) > 0 {
s += " " + strings.Join(attr, " ")
}
return s
}
// render code chunks using verbatim, or listings if we have a language
func (options *xml2) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure, callout bool) {
ial := options.Attr()
ial.GetOrDefaultAttr("align", "center")
ialArtwork := newInlineAttr()
prefix := ial.Value("prefix")
ial.DropAttr("prefix") // it's a fake attribute, so drop it
if lang == "" {
lang = ial.Value("type")
}
if lang != "" {
ialArtwork.SetAttr("type", lang)
}
ial.DropAttr("type")
// subfigure stuff. TODO(miek): check
if len(caption) > 0 {
ial.GetOrDefaultAttr("title", string(sanitizeXML(caption)))
}
s := options.AttrString(ial)
out.WriteString("\n\n")
text = blockCodePrefix(prefix, text)
if callout {
attrEscapeInCode(options, out, text)
} else {
writeEntity(out, text)
}
out.WriteString("\n")
}
func (options *xml2) CalloutCode(out *bytes.Buffer, index, id string) {
// Should link to id
attrEscape(out, []byte("<"))
out.WriteString(index)
attrEscape(out, []byte(">"))
return
}
func (options *xml2) CalloutText(out *bytes.Buffer, index string, id []string) {
out.WriteByte('(')
for i, k := range id {
out.WriteString(k)
if i < len(id)-1 {
out.WriteString(", ")
}
}
out.WriteByte(')')
}
func (options *xml2) TitleBlockTOML(out *bytes.Buffer, block *title) {
if options.flags&XML2_STANDALONE == 0 {
return
}
options.titleBlock = block
out.WriteString(" 0 {
updates := make([]string, len(options.titleBlock.Updates))
for i := range updates {
updates[i] = strconv.Itoa(options.titleBlock.Updates[i])
}
out.WriteString(" updates=\"" + strings.Join(updates, ", ") + "\"")
}
if len(options.titleBlock.Obsoletes) > 0 {
obsoletes := make([]string, len(options.titleBlock.Obsoletes))
for i := range obsoletes {
obsoletes[i] = strconv.Itoa(options.titleBlock.Obsoletes[i])
}
out.WriteString(" obsoletes=\"" + strings.Join(obsoletes, ", ") + "\"")
}
if options.titleBlock.Number > 0 {
out.WriteString(fmt.Sprintf(" number=\"%d\"", options.titleBlock.Number))
}
out.WriteString(">\n")
// Default processing instructions
for _, p := range PIs {
out.WriteString(titleBlockTOMLPI(options.titleBlock.PI, p, 2))
}
out.WriteString("\n")
options.docLevel = _DOC_FRONT_MATTER
out.WriteString("")
out.WriteString(options.titleBlock.Title + "\n\n")
for _, a := range options.titleBlock.Author {
titleBlockTOMLAuthor(out, a)
}
titleBlockTOMLDate(out, options.titleBlock.Date)
out.WriteString("" + options.titleBlock.Area + "\n")
out.WriteString("" + options.titleBlock.Workgroup + "\n")
titleBlockTOMLKeyword(out, options.titleBlock.Keyword)
out.WriteString("\n")
}
func (options *xml2) BlockQuote(out *bytes.Buffer, text []byte, attribution []byte) {
// Fake a list paragraph
// TODO(miek): IAL, clear them for now
options.Attr()
out.WriteString("\n")
out.Write(text)
// check for "person -- URI" syntax use those if found
// need to strip tags because these are attributes
if len(attribution) != 0 {
parts := bytes.Split(attribution, []byte("-- "))
if len(parts) > 0 {
cite := bytes.TrimSpace(parts[0])
var quotedFrom []byte
if len(parts) == 2 {
quotedFrom = bytes.TrimSpace(parts[1])
}
out.WriteString("-- ")
out.Write(cite)
if len(parts) == 2 {
if len(cite) > 0 {
out.WriteString(", ")
}
// len(quotedFrom) == 0 check as well?
out.Write(quotedFrom)
}
out.WriteString("\n")
}
}
out.WriteString("\n")
}
func (options *xml2) Aside(out *bytes.Buffer, text []byte) {
options.BlockQuote(out, text, nil)
}
func (options *xml2) CommentHtml(out *bytes.Buffer, text []byte) {
i := bytes.Index(text, []byte("-->"))
if i > 0 {
text = text[:i]
}
// strip,