, make that the title
- title := ""
- if bytes.HasPrefix(output, []byte("
")) {
- end := 0
- // we know the buffer ends with a newline, so no need to check bounds
- for output[end] != '\n' {
- end++
- }
- if bytes.HasSuffix(output[:end], []byte("
")) {
- title = string(output[len("
") : end-len("
")])
- }
- }
-
- ending := ""
- if xhtml {
- fmt.Fprint(out, "")
- fmt.Fprintln(out, "")
- ending = " /"
- } else {
- fmt.Fprint(out, "")
- fmt.Fprintln(out, "")
- }
- fmt.Fprintln(out, "")
- fmt.Fprintf(out, " %s\n", title)
- fmt.Fprintf(out, " \n",
- blackfriday.VERSION, ending)
- fmt.Fprintf(out, " \n",
- ending)
- if css != "" {
- fmt.Fprintf(out, " \n", css)
- }
- fmt.Fprintln(out, "")
- fmt.Fprintln(out, "")
- }
if _, err = out.Write(output); err != nil {
fmt.Fprintln(os.Stderr, "Error writing output:", err)
os.Exit(-1)
}
- if page {
- fmt.Fprintln(out, "")
- fmt.Fprintln(out, "")
- }
+}
+
+// try to guess the title from the input buffer
+// just check if it starts with an
element and use that
+func getTitle(input []byte) string {
+ i := 0
+
+ // skip blank lines
+ for i < len(input) && (input[i] == '\n' || input[i] == '\r') {
+ i++
+ }
+ if i >= len(input) {
+ return DEFAULT_TITLE
+ }
+ if input[i] == '\r' && i+1 < len(input) && input[i+1] == '\n' {
+ i++
+ }
+
+ // find the first line
+ start := i
+ for i < len(input) && input[i] != '\n' && input[i] != '\r' {
+ i++
+ }
+ line1 := input[start:i]
+ if input[i] == '\r' && i+1 < len(input) && input[i+1] == '\n' {
+ i++
+ }
+ i++
+
+ // check for a prefix header
+ if len(line1) >= 3 && line1[0] == '#' && (line1[1] == ' ' || line1[1] == '\t') {
+ return strings.TrimSpace(string(line1[2:]))
+ }
+
+ // check for an underlined header
+ if i >= len(input) || input[i] != '=' {
+ return DEFAULT_TITLE
+ }
+ for i < len(input) && input[i] == '=' {
+ i++
+ }
+ for i < len(input) && (input[i] == ' ' || input[i] == '\t') {
+ i++
+ }
+ if i >= len(input) || (input[i] != '\n' && input[i] != '\r') {
+ return DEFAULT_TITLE
+ }
+
+ return strings.TrimSpace(string(line1))
}
diff --git a/html.go b/html.go
index 83fb5ea..85ffee1 100644
--- a/html.go
+++ b/html.go
@@ -28,6 +28,7 @@ const (
HTML_SKIP_LINKS
HTML_SAFELINK
HTML_TOC
+ HTML_COMPLETE_PAGE
HTML_GITHUB_BLOCKCODE
HTML_USE_XHTML
HTML_USE_SMARTYPANTS
@@ -36,19 +37,23 @@ const (
)
type htmlOptions struct {
- flags int
+ flags int // HTML_* options
closeTag string // how to end singleton tags: either " />\n" or ">\n"
- tocData struct {
- headerCount int
- currentLevel int
- }
+ title string // document title
+ css string // optional css file url (used with HTML_COMPLETE_PAGE)
+
+ // table of contents data
+ headerCount int
+ currentLevel int
+ toc *bytes.Buffer
+
smartypants *SmartypantsRenderer
}
var xhtmlClose = " />\n"
var htmlClose = ">\n"
-func HtmlRenderer(flags int) *Renderer {
+func HtmlRenderer(flags int, title string, css string) *Renderer {
// configure the rendering engine
r := new(Renderer)
r.BlockCode = htmlBlockCode
@@ -73,34 +78,34 @@ func HtmlRenderer(flags int) *Renderer {
r.RawHtmlTag = htmlRawTag
r.TripleEmphasis = htmlTripleEmphasis
r.StrikeThrough = htmlStrikeThrough
+
+ r.Entity = htmlEntity
r.NormalText = htmlNormalText
+ r.DocumentHeader = htmlDocumentHeader
+ r.DocumentFooter = htmlDocumentFooter
+
closeTag := htmlClose
if flags&HTML_USE_XHTML != 0 {
closeTag = xhtmlClose
}
- r.Opaque = &htmlOptions{flags: flags, closeTag: closeTag, smartypants: Smartypants(flags)}
- return r
-}
-
-func HtmlTocRenderer(flags int) *Renderer {
- // configure the rendering engine
- r := new(Renderer)
- r.Header = htmlTocHeader
-
- r.CodeSpan = htmlCodeSpan
- r.DoubleEmphasis = htmlDoubleEmphasis
- r.Emphasis = htmlEmphasis
- r.TripleEmphasis = htmlTripleEmphasis
- r.StrikeThrough = htmlStrikeThrough
-
- r.DocumentFooter = htmlTocFinalize
-
- closeTag := ">\n"
- if flags&HTML_USE_XHTML != 0 {
- closeTag = " />\n"
+ var toc *bytes.Buffer
+ if flags&HTML_TOC != 0 {
+ toc = new(bytes.Buffer)
+ }
+
+ r.Opaque = &htmlOptions{
+ flags: flags,
+ closeTag: closeTag,
+ title: title,
+ css: css,
+
+ headerCount: 0,
+ currentLevel: 0,
+ toc: toc,
+
+ smartypants: Smartypants(flags),
}
- r.Opaque = &htmlOptions{flags: flags | HTML_TOC, closeTag: closeTag}
return r
}
@@ -159,8 +164,8 @@ func htmlHeader(out *bytes.Buffer, text func() bool, level int, opaque interface
}
if options.flags&HTML_TOC != 0 {
- out.WriteString(fmt.Sprintf("", level, options.tocData.headerCount))
- options.tocData.headerCount++
+ out.WriteString(fmt.Sprintf("", level, options.headerCount))
+ options.headerCount++
} else {
out.WriteString(fmt.Sprintf("", level))
}
@@ -169,6 +174,12 @@ func htmlHeader(out *bytes.Buffer, text func() bool, level int, opaque interface
out.Truncate(marker)
return
}
+
+ // are we building a table of contents?
+ if options.flags&HTML_TOC != 0 {
+ htmlTocHeader(out.Bytes()[marker:], level, opaque)
+ }
+
out.WriteString(fmt.Sprintf("\n", level))
}
@@ -553,6 +564,10 @@ func htmlStrikeThrough(out *bytes.Buffer, text []byte, opaque interface{}) bool
return true
}
+func htmlEntity(out *bytes.Buffer, entity []byte, opaque interface{}) {
+ out.Write(entity)
+}
+
func htmlNormalText(out *bytes.Buffer, text []byte, opaque interface{}) {
options := opaque.(*htmlOptions)
if options.flags&HTML_USE_SMARTYPANTS != 0 {
@@ -562,46 +577,93 @@ func htmlNormalText(out *bytes.Buffer, text []byte, opaque interface{}) {
}
}
-func htmlTocHeader(out *bytes.Buffer, text func() bool, level int, opaque interface{}) {
+func htmlTocHeader(text []byte, level int, opaque interface{}) {
options := opaque.(*htmlOptions)
- marker := out.Len()
- for level > options.tocData.currentLevel {
- if options.tocData.currentLevel > 0 {
- out.WriteString("
")
+ for level > options.currentLevel {
+ if options.currentLevel > 0 {
+ options.toc.WriteString("