221 lines
6.3 KiB
Go
221 lines
6.3 KiB
Go
|
/*
|
||
|
* Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung)
|
||
|
*
|
||
|
* Permission to use, copy, modify, and distribute this software for any
|
||
|
* purpose with or without fee is hereby granted, provided that the above
|
||
|
* copyright notice and this permission notice appear in all copies.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
package gofpdf
|
||
|
|
||
|
import (
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// HTMLBasicSegmentType defines a segment of literal text in which the current
|
||
|
// attributes do not vary, or an open tag or a close tag.
|
||
|
type HTMLBasicSegmentType struct {
|
||
|
Cat byte // 'O' open tag, 'C' close tag, 'T' text
|
||
|
Str string // Literal text unchanged, tags are lower case
|
||
|
Attr map[string]string // Attribute keys are lower case
|
||
|
}
|
||
|
|
||
|
// HTMLBasicTokenize returns a list of HTML tags and literal elements. This is
|
||
|
// done with regular expressions, so the result is only marginally better than
|
||
|
// useless.
|
||
|
func HTMLBasicTokenize(htmlStr string) (list []HTMLBasicSegmentType) {
|
||
|
// This routine is adapted from http://www.fpdf.org/
|
||
|
list = make([]HTMLBasicSegmentType, 0, 16)
|
||
|
htmlStr = strings.Replace(htmlStr, "\n", " ", -1)
|
||
|
htmlStr = strings.Replace(htmlStr, "\r", "", -1)
|
||
|
tagRe, _ := regexp.Compile(`(?U)<.*>`)
|
||
|
attrRe, _ := regexp.Compile(`([^=]+)=["']?([^"']+)`)
|
||
|
capList := tagRe.FindAllStringIndex(htmlStr, -1)
|
||
|
if capList != nil {
|
||
|
var seg HTMLBasicSegmentType
|
||
|
var parts []string
|
||
|
pos := 0
|
||
|
for _, cap := range capList {
|
||
|
if pos < cap[0] {
|
||
|
seg.Cat = 'T'
|
||
|
seg.Str = htmlStr[pos:cap[0]]
|
||
|
seg.Attr = nil
|
||
|
list = append(list, seg)
|
||
|
}
|
||
|
if htmlStr[cap[0]+1] == '/' {
|
||
|
seg.Cat = 'C'
|
||
|
seg.Str = strings.ToLower(htmlStr[cap[0]+2 : cap[1]-1])
|
||
|
seg.Attr = nil
|
||
|
list = append(list, seg)
|
||
|
} else {
|
||
|
// Extract attributes
|
||
|
parts = strings.Split(htmlStr[cap[0]+1:cap[1]-1], " ")
|
||
|
if len(parts) > 0 {
|
||
|
for j, part := range parts {
|
||
|
if j == 0 {
|
||
|
seg.Cat = 'O'
|
||
|
seg.Str = strings.ToLower(parts[0])
|
||
|
seg.Attr = make(map[string]string)
|
||
|
} else {
|
||
|
attrList := attrRe.FindAllStringSubmatch(part, -1)
|
||
|
if attrList != nil {
|
||
|
for _, attr := range attrList {
|
||
|
seg.Attr[strings.ToLower(attr[1])] = attr[2]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
list = append(list, seg)
|
||
|
}
|
||
|
}
|
||
|
pos = cap[1]
|
||
|
}
|
||
|
if len(htmlStr) > pos {
|
||
|
seg.Cat = 'T'
|
||
|
seg.Str = htmlStr[pos:]
|
||
|
seg.Attr = nil
|
||
|
list = append(list, seg)
|
||
|
}
|
||
|
} else {
|
||
|
list = append(list, HTMLBasicSegmentType{Cat: 'T', Str: htmlStr, Attr: nil})
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// HTMLBasicType is used for rendering a very basic subset of HTML. It supports
|
||
|
// only hyperlinks and bold, italic and underscore attributes. In the Link
|
||
|
// structure, the ClrR, ClrG and ClrB fields (0 through 255) define the color
|
||
|
// of hyperlinks. The Bold, Italic and Underscore values define the hyperlink
|
||
|
// style.
|
||
|
type HTMLBasicType struct {
|
||
|
pdf *Fpdf
|
||
|
Link struct {
|
||
|
ClrR, ClrG, ClrB int
|
||
|
Bold, Italic, Underscore bool
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// HTMLBasicNew returns an instance that facilitates writing basic HTML in the
|
||
|
// specified PDF file.
|
||
|
func (f *Fpdf) HTMLBasicNew() (html HTMLBasicType) {
|
||
|
html.pdf = f
|
||
|
html.Link.ClrR, html.Link.ClrG, html.Link.ClrB = 0, 0, 128
|
||
|
html.Link.Bold, html.Link.Italic, html.Link.Underscore = false, false, true
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Write prints text from the current position using the currently selected
|
||
|
// font. See HTMLBasicNew() to create a receiver that is associated with the
|
||
|
// PDF document instance. The text can be encoded with a basic subset of HTML
|
||
|
// that includes hyperlinks and tags for italic (I), bold (B), underscore
|
||
|
// (U) and center (CENTER) attributes. When the right margin is reached a line
|
||
|
// break occurs and text continues from the left margin. Upon method exit, the
|
||
|
// current position is left at the end of the text.
|
||
|
//
|
||
|
// lineHt indicates the line height in the unit of measure specified in New().
|
||
|
func (html *HTMLBasicType) Write(lineHt float64, htmlStr string) {
|
||
|
var boldLvl, italicLvl, underscoreLvl, linkBold, linkItalic, linkUnderscore int
|
||
|
var textR, textG, textB = html.pdf.GetTextColor()
|
||
|
var hrefStr string
|
||
|
if html.Link.Bold {
|
||
|
linkBold = 1
|
||
|
}
|
||
|
if html.Link.Italic {
|
||
|
linkItalic = 1
|
||
|
}
|
||
|
if html.Link.Underscore {
|
||
|
linkUnderscore = 1
|
||
|
}
|
||
|
setStyle := func(boldAdj, italicAdj, underscoreAdj int) {
|
||
|
styleStr := ""
|
||
|
boldLvl += boldAdj
|
||
|
if boldLvl > 0 {
|
||
|
styleStr += "B"
|
||
|
}
|
||
|
italicLvl += italicAdj
|
||
|
if italicLvl > 0 {
|
||
|
styleStr += "I"
|
||
|
}
|
||
|
underscoreLvl += underscoreAdj
|
||
|
if underscoreLvl > 0 {
|
||
|
styleStr += "U"
|
||
|
}
|
||
|
html.pdf.SetFont("", styleStr, 0)
|
||
|
}
|
||
|
putLink := func(urlStr, txtStr string) {
|
||
|
// Put a hyperlink
|
||
|
html.pdf.SetTextColor(html.Link.ClrR, html.Link.ClrG, html.Link.ClrB)
|
||
|
setStyle(linkBold, linkItalic, linkUnderscore)
|
||
|
html.pdf.WriteLinkString(lineHt, txtStr, urlStr)
|
||
|
setStyle(-linkBold, -linkItalic, -linkUnderscore)
|
||
|
html.pdf.SetTextColor(textR, textG, textB)
|
||
|
}
|
||
|
list := HTMLBasicTokenize(htmlStr)
|
||
|
var ok bool
|
||
|
alignStr := "L"
|
||
|
for _, el := range list {
|
||
|
switch el.Cat {
|
||
|
case 'T':
|
||
|
if len(hrefStr) > 0 {
|
||
|
putLink(hrefStr, el.Str)
|
||
|
hrefStr = ""
|
||
|
} else {
|
||
|
if alignStr == "C" || alignStr == "R" {
|
||
|
html.pdf.WriteAligned(0, lineHt, el.Str, alignStr)
|
||
|
} else {
|
||
|
html.pdf.Write(lineHt, el.Str)
|
||
|
}
|
||
|
}
|
||
|
case 'O':
|
||
|
switch el.Str {
|
||
|
case "b":
|
||
|
setStyle(1, 0, 0)
|
||
|
case "i":
|
||
|
setStyle(0, 1, 0)
|
||
|
case "u":
|
||
|
setStyle(0, 0, 1)
|
||
|
case "br":
|
||
|
html.pdf.Ln(lineHt)
|
||
|
case "center":
|
||
|
html.pdf.Ln(lineHt)
|
||
|
alignStr = "C"
|
||
|
case "right":
|
||
|
html.pdf.Ln(lineHt)
|
||
|
alignStr = "R"
|
||
|
case "left":
|
||
|
html.pdf.Ln(lineHt)
|
||
|
alignStr = "L"
|
||
|
case "a":
|
||
|
hrefStr, ok = el.Attr["href"]
|
||
|
if !ok {
|
||
|
hrefStr = ""
|
||
|
}
|
||
|
}
|
||
|
case 'C':
|
||
|
switch el.Str {
|
||
|
case "b":
|
||
|
setStyle(-1, 0, 0)
|
||
|
case "i":
|
||
|
setStyle(0, -1, 0)
|
||
|
case "u":
|
||
|
setStyle(0, 0, -1)
|
||
|
case "center":
|
||
|
html.pdf.Ln(lineHt)
|
||
|
alignStr = "L"
|
||
|
case "right":
|
||
|
html.pdf.Ln(lineHt)
|
||
|
alignStr = "L"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|