// 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,