// XML2RFC v3 rendering backend package mmark import ( "bytes" "fmt" "strconv" "strings" ) // XML renderer configuration options. const ( XML_STANDALONE = 1 << iota // create standalone document ) var words2119 = map[string]bool{ "MUST": true, "MUST NOT": true, "REQUIRED": true, "SHALL": true, "SHALL NOT": true, "SHOULD": true, "SHOULD NOT": true, "RECOMMENDED": true, "MAY": true, "OPTIONAL": true, } // Xml is a type that implements the Renderer interface for XML2RFV3 output. // // Do not create this directly, instead use the XmlRenderer function. type xml struct { flags int // XML_* 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 para bool // when true we're in a para, artworks need to close it first then. // Store the IAL we see for this block element ial *inlineAttr // TitleBlock in TOML titleBlock *title } // XmlRenderer creates and configures a Xml object, which // satisfies the Renderer interface. // // flags is a set of XML_* options ORed together func XmlRenderer(flags int) Renderer { return &xml{flags: flags} } func (options *xml) Flags() int { return options.flags } func (options *xml) State() int { return 0 } func (options *xml) SetAttr(i *inlineAttr) { options.ial = i } func (options *xml) Attr() *inlineAttr { if options.ial == nil { return newInlineAttr() } return options.ial } func (options *xml) 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 *xml) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure, callout bool) { if options.para { // close it out.WriteString("") defer out.WriteString("") } // Tick of language for sourcecode... ial := options.Attr() if lang != "" { ial.GetOrDefaultAttr("type", lang) } prefix := ial.Value("prefix") ial.DropAttr("prefix") // it's a fake attribute, so drop it s := options.AttrString(ial) text = blockCodePrefix(prefix, text) // if in a figure quote suppress
and caption use if !subfigure && len(caption) > 0 { out.WriteString("\n") s = "" out.WriteString("") out.Write(caption) out.WriteString("\n") } if lang != "" { out.WriteString("\n\n") } else { out.WriteString("\n") } writeEntity(out, text) if lang != "" { out.WriteString("\n") } else { out.WriteString("\n") } if !subfigure && len(caption) > 0 { out.WriteString("
\n") } } func (options *xml) CalloutCode(out *bytes.Buffer, index, id string) { printf(nil, "TODO implement: CalloutCode") } func (options *xml) CalloutText(out *bytes.Buffer, index string, id []string) { printf(nil, "TODO implement: CalloutText") } func (options *xml) TitleBlockTOML(out *bytes.Buffer, block *title) { if options.flags&XML_STANDALONE == 0 { return } // Processing Instructions are attribute of know. options.titleBlock = block out.WriteString(" 0 { out.WriteString(fmt.Sprintf(" number=\"%d\"", options.titleBlock.Number)) } out.WriteString(" docName=\"" + options.titleBlock.DocName + "\">") if len(options.titleBlock.Updates) > 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, ", ") + "\"") } out.WriteString("\n") out.WriteString("\n") 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 *xml) BlockQuote(out *bytes.Buffer, text []byte, attribution []byte) { // check for "person -- URI" syntax use those if found // need to strip tags because these are attributes ial := options.Attr() if len(attribution) != 0 { parts := bytes.Split(attribution, []byte(" -- ")) if len(parts) == 2 { cite := string(bytes.TrimSpace(parts[0])) quotedFrom := sanitizeXML(bytes.TrimSpace(parts[1])) ial.GetOrDefaultAttr("cite", cite) ial.GetOrDefaultAttr("quotedFrom", string(quotedFrom)) } } out.WriteString("\n") out.Write(text) out.WriteString("\n") } func (options *xml) Aside(out *bytes.Buffer, text []byte) { ial := options.Attr() s := options.AttrString(ial) out.WriteString("\n") out.Write(text) out.WriteString("\n") } func (options *xml) CommentHtml(out *bytes.Buffer, text []byte) { // nothing fancy any left of the first `:` will be used as the source="..." // if the syntax is different, don't output anything. i := bytes.Index(text, []byte("-->")) if i > 0 { text = text[:i] } // strip,