diff options
| -rw-r--r-- | def.go | 18 | ||||
| -rw-r--r-- | fpdf.go | 134 | ||||
| -rw-r--r-- | fpdf_test.go | 222 | ||||
| -rw-r--r-- | htmlbasic.go | 196 | ||||
| -rw-r--r-- | svgwrite.go | 60 | 
5 files changed, 379 insertions, 251 deletions
| @@ -1,5 +1,5 @@  /* - * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung) + * Copyright (c) 2013-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 @@ -66,6 +66,14 @@ type ImageInfoType struct {  	scale float64 // document scaling factor  } +// PointConvert returns the value of pt, expressed in points (1/72 inch), as a +// value expressed in the unit of measure specified in New(). Since font +// management in Fpdf uses points, this method can help with line height +// calculations and other methods that require user units. +func (f *Fpdf) PointConvert(pt float64) float64 { +	return pt / f.k +} +  // Extent returns the width and height of the image in the units of the Fpdf  // object.  func (info *ImageInfoType) Extent() (wd, ht float64) { @@ -157,10 +165,6 @@ type Fpdf struct {  	currentFont      fontDefType               // current font info  	fontSizePt       float64                   // current font size in points  	fontSize         float64                   // current font size in user unit -	drawColor        string                    // commands for drawing color -	fillColor        string                    // commands for filling color -	textColor        string                    // commands for text color -	colorFlag        bool                      // indicates whether fill and text colors are different  	ws               float64                   // word spacing  	images           map[string]*ImageInfoType // array of used images  	pageLinks        [][]linkType              // pageLinks[page][link], both 1-based @@ -192,6 +196,10 @@ type Fpdf struct {  	clipNest         int                       // Number of active clipping contexts  	transformNest    int                       // Number of active transformation contexts  	err              error                     // Set if error occurs during life cycle of instance +	colorFlag        bool                      // indicates whether fill and text colors are different +	color            struct {                  // Composite values of colors +		draw, fill, text clrType +	}  }  type encType struct { @@ -80,11 +80,11 @@ func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType)  	f.lasth = 0  	f.fontFamily = ""  	f.fontStyle = "" -	f.fontSizePt = 12 +	f.SetFontSize(12)  	f.underline = false -	f.drawColor = "0 G" -	f.fillColor = "0 g" -	f.textColor = "0 g" +	f.SetDrawColor(0, 0, 0) // "0 G" +	f.SetFillColor(0, 0, 0) // "0 g" +	f.SetTextColor(0, 0, 0) // "0 g"  	f.colorFlag = false  	f.ws = 0  	f.fontpath = fontDirStr @@ -512,9 +512,9 @@ func (f *Fpdf) AddPageFormat(orientationStr string, size SizeType) {  	}  	fontsize := f.fontSizePt  	lw := f.lineWidth -	dc := f.drawColor -	fc := f.fillColor -	tc := f.textColor +	dc := f.color.draw +	fc := f.color.fill +	tc := f.color.text  	cf := f.colorFlag  	if f.page > 0 {  		// Page footer @@ -542,15 +542,15 @@ func (f *Fpdf) AddPageFormat(orientationStr string, size SizeType) {  		}  	}  	// 	Set colors -	f.drawColor = dc -	if dc != "0 G" { -		f.out(dc) +	f.color.draw = dc +	if dc.str != "0 G" { +		f.out(dc.str)  	} -	f.fillColor = fc -	if fc != "0 g" { -		f.out(fc) +	f.color.fill = fc +	if fc.str != "0 g" { +		f.out(fc.str)  	} -	f.textColor = tc +	f.color.text = tc  	f.colorFlag = cf  	// 	Page header  	if f.headerFnc != nil { @@ -571,15 +571,15 @@ func (f *Fpdf) AddPageFormat(orientationStr string, size SizeType) {  		}  	}  	// Restore colors -	if f.drawColor != dc { -		f.drawColor = dc -		f.out(dc) +	if f.color.draw.str != dc.str { +		f.color.draw = dc +		f.out(dc.str)  	} -	if f.fillColor != fc { -		f.fillColor = fc -		f.out(fc) +	if f.color.fill.str != fc.str { +		f.color.fill = fc +		f.out(fc.str)  	} -	f.textColor = tc +	f.color.text = tc  	f.colorFlag = cf  	return  } @@ -613,36 +613,34 @@ func (f *Fpdf) PageNo() int {  }  type clrType struct { -	r, g, b float64 +	r, g, b    float64 +	ir, ig, ib int +	gray       bool +	str        string  } -func colorComp(v int) float64 { +func colorComp(v int) (int, float64) {  	if v < 0 {  		v = 0  	} else if v > 255 {  		v = 255  	} -	return float64(v) / 255.0 +	return v, float64(v) / 255.0  } -func colorValueString(r, g, b int) string { -	clr := colorValue(r, g, b) -	return sprintf("%.3f %.3f %.3f", clr.r, clr.g, clr.b) -} - -func colorValue(r, g, b int) (clr clrType) { -	clr.r = colorComp(r) -	clr.g = colorComp(g) -	clr.b = colorComp(b) -	return -} - -func colorString(r, g, b int, grayStr, fullStr string) (str string) { -	clr := colorValue(r, g, b) -	if r == g && r == b { -		str = sprintf("%.3f %s", clr.r, grayStr) +func colorValue(r, g, b int, grayStr, fullStr string) (clr clrType) { +	clr.ir, clr.r = colorComp(r) +	clr.ig, clr.g = colorComp(g) +	clr.ib, clr.b = colorComp(b) +	clr.gray = clr.ir == clr.ig && clr.r == clr.b +	if len(grayStr) > 0 { +		if clr.gray { +			clr.str = sprintf("%.3f %s", clr.r, grayStr) +		} else { +			clr.str = sprintf("%.3f %.3f %.3f %s", clr.r, clr.g, clr.b, fullStr) +		}  	} else { -		str = sprintf("%.3f %.3f %.3f %s", clr.r, clr.g, clr.b, fullStr) +		clr.str = sprintf("%.3f %.3f %.3f", clr.r, clr.g, clr.b)  	}  	return  } @@ -652,30 +650,48 @@ func colorString(r, g, b int, grayStr, fullStr string) (str string) {  // The method can be called before the first page is created and the value is  // retained from page to page.  func (f *Fpdf) SetDrawColor(r, g, b int) { -	f.drawColor = colorString(r, g, b, "G", "RG") +	f.color.draw = colorValue(r, g, b, "G", "RG") +	// f.drawColor = colorString(f.color.draw, "G", "RG")  	if f.page > 0 { -		f.out(f.drawColor) +		f.out(f.color.draw.str)  	}  } +// GetDrawColor returns the current draw color as RGB components (0 - 255). +func (f *Fpdf) GetDrawColor() (int, int, int) { +	return f.color.draw.ir, f.color.draw.ig, f.color.draw.ib +} +  // SetFillColor defines the color used for all filling operations (filled  // rectangles and cell backgrounds). It is expressed in RGB components (0  // -255). The method can be called before the first page is created and the  // value is retained from page to page.  func (f *Fpdf) SetFillColor(r, g, b int) { -	f.fillColor = colorString(r, g, b, "g", "rg") -	f.colorFlag = f.fillColor != f.textColor +	f.color.fill = colorValue(r, g, b, "g", "rg") +	// f.fillColor = colorString(f.color.fill, "g", "rg") +	f.colorFlag = f.color.fill.str != f.color.text.str  	if f.page > 0 { -		f.out(f.fillColor) +		f.out(f.color.fill.str)  	}  } +// GetFillColor returns the current fill color as RGB components (0 - 255). +func (f *Fpdf) GetFillColor() (int, int, int) { +	return f.color.fill.ir, f.color.fill.ig, f.color.fill.ib +} +  // SetTextColor defines the color used for text. It is expressed in RGB  // components (0 - 255). The method can be called before the first page is  // created and the value is retained from page to page.  func (f *Fpdf) SetTextColor(r, g, b int) { -	f.textColor = colorString(r, g, b, "g", "rg") -	f.colorFlag = f.fillColor != f.textColor +	f.color.text = colorValue(r, g, b, "g", "rg") +	// f.textColor = colorString(f.color.text, "g", "rg") +	f.colorFlag = f.color.fill.str != f.color.text.str +} + +// GetTextColor returns the current text color as RGB components (0 - 255). +func (f *Fpdf) GetTextColor() (int, int, int) { +	return f.color.text.ir, f.color.text.ig, f.color.text.ib  }  // GetStringWidth returns the length of a string in user units. A font must be @@ -958,8 +974,10 @@ func (f *Fpdf) gradientClipEnd() {  func (f *Fpdf) gradient(tp int, r1, g1, b1 int, r2, g2, b2 int, x1, y1 float64, x2, y2 float64, r float64) {  	pos := len(f.gradientList) -	f.gradientList = append(f.gradientList, gradientType{tp, colorValueString(r1, g1, b1), -		colorValueString(r2, g2, b2), x1, y1, x2, y2, r, 0}) +	clr1 := colorValue(r1, g1, b1, "", "") +	clr2 := colorValue(r2, g2, b2, "", "") +	f.gradientList = append(f.gradientList, gradientType{tp, clr1.str, clr2.str, +		x1, y1, x2, y2, r, 0})  	f.outf("/Sh%d sh", pos)  } @@ -1352,6 +1370,13 @@ func (f *Fpdf) SetFontSize(size float64) {  	}  } +// GetFontSize returns the size of the current font in points followed by the +// size in the unit of measure specified in New(). The second value can be used +// as a line height value in drawing operations. +func (f *Fpdf) GetFontSize() (ptSize, unitSize float64) { +	return f.fontSizePt, f.fontSize +} +  // AddLink creates a new internal link and returns its identifier. An internal  // link is a clickable area which directs to another place within the document.  // The identifier can then be passed to Cell(), Write(), Image() or Link(). The @@ -1423,7 +1448,7 @@ func (f *Fpdf) Text(x, y float64, txtStr string) {  		s += " " + f.dounderline(x, y, txtStr)  	}  	if f.colorFlag { -		s = sprintf("q %s %s Q", f.textColor, s) +		s = sprintf("q %s %s Q", f.color.text.str, s)  	}  	f.out(s)  } @@ -1555,7 +1580,7 @@ func (f *Fpdf) CellFormat(w, h float64, txtStr string, borderStr string, ln int,  			dx = f.cMargin  		}  		if f.colorFlag { -			s.printf("q %s ", f.textColor) +			s.printf("q %s ", f.color.text.str)  		}  		txt2 := strings.Replace(txtStr, "\\", "\\\\", -1)  		txt2 = strings.Replace(txt2, "(", "\\(", -1) @@ -2028,6 +2053,11 @@ func (f *Fpdf) RegisterImage(fileStr, tp string) (info *ImageInfoType) {  	return info  } +// GetXY returns the abscissa and ordinate of the current position. +func (f *Fpdf) GetXY() (float64, float64) { +	return f.x, f.y +} +  // GetX returns the abscissa of the current position.  func (f *Fpdf) GetX() float64 {  	return f.x diff --git a/fpdf_test.go b/fpdf_test.go index c8bf2e3..32e4237 100644 --- a/fpdf_test.go +++ b/fpdf_test.go @@ -23,7 +23,6 @@ import (  	"io/ioutil"  	"os"  	"path/filepath" -	"regexp"  	"strings"  ) @@ -110,71 +109,6 @@ func strDelimit(str string, sepstr string, sepcount int) string {  	return str  } -type htmlSegmentType 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 -} - -// Returns a list of HTML tags and literal elements. This is done with regular -// expressions, so the result is only marginally better than useless. -// Adapted from http://www.fpdf.org/ -func htmlTokenize(htmlStr string) (list []htmlSegmentType) { -	list = make([]htmlSegmentType, 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 htmlSegmentType -		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) -		} -	} -	return -} -  func lorem() string {  	return "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod " +  		"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis " + @@ -513,96 +447,32 @@ func ExampleFpdf_tutorial05() {  	// Successfully generated pdf/tutorial05.pdf  } -// Internal and external links +// This example demonstrates internal and external links with and without basic +// HTML.  func ExampleFpdf_tutorial06() { -	var boldLvl, italicLvl, underscoreLvl int -	var hrefStr string  	pdf := gofpdf.New("P", "mm", "A4", cnFontDir) -	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" -		} -		pdf.SetFont("", styleStr, 0) -	} -	putLink := func(urlStr, txtStr string) { -		// Put a hyperlink -		pdf.SetTextColor(0, 0, 255) -		setStyle(0, 0, 1) -		pdf.WriteLinkString(5, txtStr, urlStr) -		setStyle(0, 0, -1) -		pdf.SetTextColor(0, 0, 0) -	} - -	writeHTML := func(htmlStr string) { -		list := htmlTokenize(htmlStr) -		var ok bool -		for _, el := range list { -			switch el.cat { -			case 'T': -				if len(hrefStr) > 0 { -					putLink(hrefStr, el.str) -					hrefStr = "" -				} else { -					pdf.Write(5, 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": -					pdf.Ln(5) -				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) - -				} -			} -		} -	} -	// First page +	// First page: manual local link  	pdf.AddPage() -	pdf.SetFont("Arial", "", 20) -	pdf.Write(5, "To find out what's new in this tutorial, click ") +	pdf.SetFont("Helvetica", "", 20) +	_, lineHt := pdf.GetFontSize() +	pdf.Write(lineHt, "To find out what's new in this tutorial, click ")  	pdf.SetFont("", "U", 0)  	link := pdf.AddLink() -	pdf.WriteLinkID(5, "here", link) +	pdf.WriteLinkID(lineHt, "here", link)  	pdf.SetFont("", "", 0) -	// Second page +	// Second page: image link and basic HTML with link  	pdf.AddPage()  	pdf.SetLink(link, 0, -1)  	pdf.Image(imageFile("logo.png"), 10, 12, 30, 0, false, "", 0, "http://www.fpdf.org")  	pdf.SetLeftMargin(45)  	pdf.SetFontSize(14) +	_, lineHt = pdf.GetFontSize()  	htmlStr := `You can now easily print text mixing different styles: <b>bold</b>, ` +  		`<i>italic</i>, <u>underlined</u>, or <b><i><u>all at once</u></i></b>!<br><br>` +  		`You can also insert links on text, such as ` +  		`<a href="http://www.fpdf.org">www.fpdf.org</a>, or on an image: click on the logo.` -	writeHTML(htmlStr) +	html := pdf.HtmlBasicNew() +	html.Write(lineHt, htmlStr)  	pdf.OutputAndClose(docWriter(pdf, 6))  	// Output:  	// Successfully generated pdf/tutorial06.pdf @@ -1008,7 +878,7 @@ func ExampleFpdf_tutorial17() {  	titleStr := "Transformations"  	titlePt := 36.0 -	titleHt := titlePt * 25.4 / 72.0 +	titleHt := pdf.PointConvert(titlePt)  	pdf.SetFont("Helvetica", "", titlePt)  	titleWd := pdf.GetStringWidth(titleStr)  	titleX := (210 - titleWd) / 2 @@ -1148,11 +1018,11 @@ func ExampleFpdf_tutorial18() {  func ExampleFpdf_tutorial19() {  	const (  		fontPtSize = 18.0 -		lineHt     = fontPtSize * 25.4 / 72.0  		wd         = 100.0  	)  	pdf := gofpdf.New("P", "mm", "A4", cnFontDir) // A4 210.0 x 297.0  	pdf.SetFont("Times", "", fontPtSize) +	_, lineHt := pdf.GetFontSize()  	pdf.AddPage()  	pdf.SetMargins(10, 10, 10)  	lines := pdf.SplitLines([]byte(lorem()), wd) @@ -1175,8 +1045,7 @@ func ExampleFpdf_tutorial19() {  // type generated by the jSignature web control.  func ExampleFpdf_tutorial20() {  	const ( -		fontPtSize = 18.0 -		lineHt     = fontPtSize * 25.4 / 72.0 +		fontPtSize = 16.0  		wd         = 100.0  		sigFileStr = "signature.svg"  	) @@ -1185,66 +1054,31 @@ func ExampleFpdf_tutorial20() {  		err error  	)  	pdf := gofpdf.New("P", "mm", "A4", cnFontDir) // A4 210.0 x 297.0 -	link := func(showStr, urlStr string) { -		pdf.SetFont("", "U", 0) -		pdf.SetTextColor(0, 0, 128) -		pdf.WriteLinkString(lineHt, showStr, urlStr) -		pdf.SetTextColor(0, 0, 0) -		pdf.SetFont("", "", 0) -	}  	pdf.SetFont("Times", "", fontPtSize) +	lineHt := pdf.PointConvert(fontPtSize)  	pdf.AddPage()  	pdf.SetMargins(10, 10, 10) -	pdf.Write(lineHt, "This example renders a simple ") -	link("SVG", "http://www.w3.org/TR/SVG/") -	pdf.Write(lineHt, " (scalable vector graphics) image that contains only "+ -		"basic path commands without any styling, color fill, reflection or "+ -		"endpoint closures. In particular, the type of vector graphic returned from a ") -	link("jSignature", "http://willowsystems.github.io/jSignature/#/demo/") -	pdf.Write(lineHt, " web control is supported and is used in this example.") -	pdf.Ln(3 * lineHt) +	htmlStr := `This example renders a simple ` + +		`<a href="http://www.w3.org/TR/SVG/">SVG</a> (scalable vector graphics) ` + +		`image that contains only basic path commands without any styling, ` + +		`color fill, reflection or endpoint closures. In particular, the ` + +		`type of vector graphic returned from a ` + +		`<a href="http://willowsystems.github.io/jSignature/#/demo/">jSignature</a> ` + +		`web control is supported and is used in this example.` +	html := pdf.HtmlBasicNew() +	html.Write(lineHt, htmlStr)  	sig, err = gofpdf.SvgBasicFileParse(imageFile(sigFileStr))  	if err == nil { -		scale := 150 / sig.Wd -		scaleY := 50 / sig.Ht +		scale := 100 / sig.Wd +		scaleY := 30 / sig.Ht  		if scale > scaleY {  			scale = scaleY  		} -		originX := (210.0 - scale*sig.Wd) / 2.0 -		originY := pdf.GetY() + 10 -		var x, y, newX, newY float64 -		var cx0, cy0, cx1, cy1 float64 -		var path []gofpdf.SvgBasicSegmentType -		var seg gofpdf.SvgBasicSegmentType -		val := func(arg int) (float64, float64) { -			return originX + scale*seg.Arg[arg], originY + scale*seg.Arg[arg+1] -		}  		pdf.SetLineCapStyle("round")  		pdf.SetLineWidth(0.25)  		pdf.SetDrawColor(0, 0, 128) -		for j := 0; j < len(sig.Segments) && pdf.Ok(); j++ { -			path = sig.Segments[j] -			for k := 0; k < len(path) && pdf.Ok(); k++ { -				seg = path[k] -				switch seg.Cmd { -				case 'M': -					x, y = val(0) -					pdf.SetXY(x, y) -				case 'L': -					newX, newY = val(0) -					pdf.Line(x, y, newX, newY) -					x, y = newX, newY -				case 'C': -					cx0, cy0 = val(0) -					cx1, cy1 = val(2) -					newX, newY = val(4) -					pdf.CurveCubic(x, y, cx0, cy0, newX, newY, cx1, cy1, "D") -					x, y = newX, newY -				default: -					pdf.SetErrorf("Unexpected path command '%c'", seg.Cmd) -				} -			} -		} +		pdf.SetXY((210.0-scale*sig.Wd)/2.0, pdf.GetY()+10) +		pdf.SvgBasicWrite(&sig, scale)  	} else {  		pdf.SetError(err)  	} diff --git a/htmlbasic.go b/htmlbasic.go new file mode 100644 index 0000000..b2a05e5 --- /dev/null +++ b/htmlbasic.go @@ -0,0 +1,196 @@ +/* + * 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" +) + +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) +		} +	} +	return +} + +// HtmlBasicType is used for rendering a very basic subset of HTML. It supports +// only the bold, italic and underscore attributes and hyperlinks. In the Link +// structure, the ClrR, ClrG and ClrB fields (0 through 255) define the color +// of hyperlinks. The Bold, Italic and Underscore values (0 for off, 1 for on) +// 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. The text can be encoded with a basic subset of HTML that includes tags +// for italic (I), bold (B) and underscore (U) attributes and hyperlinks. 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 +	for _, el := range list { +		switch el.Cat { +		case 'T': +			if len(hrefStr) > 0 { +				putLink(hrefStr, el.Str) +				hrefStr = "" +			} 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 "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) + +			} +		} +	} +} diff --git a/svgwrite.go b/svgwrite.go new file mode 100644 index 0000000..ea445c1 --- /dev/null +++ b/svgwrite.go @@ -0,0 +1,60 @@ +/* + * 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 + +// SvgBasicWrite renders the paths encoded in the basic SVG image specified by +// sb. The scale value is used to convert the coordinates in the path to the +// unit of measure specified in New(). The current position (as set with a call +// to SetXY()) is used as the origin of the image. The current line cap style +// (as set with SetLineCapStyle()), line width (as set with SetLineWidth()), +// and draw color (as set with SetDrawColor()) are used in drawing the image +// paths. +// +// See example 20 for a demonstration of this function. +func (f *Fpdf) SvgBasicWrite(sb *SvgBasicType, scale float64) { +	originX, originY := f.GetXY() +	var x, y, newX, newY float64 +	var cx0, cy0, cx1, cy1 float64 +	var path []SvgBasicSegmentType +	var seg SvgBasicSegmentType +	val := func(arg int) (float64, float64) { +		return originX + scale*seg.Arg[arg], originY + scale*seg.Arg[arg+1] +	} +	for j := 0; j < len(sb.Segments) && f.Ok(); j++ { +		path = sb.Segments[j] +		for k := 0; k < len(path) && f.Ok(); k++ { +			seg = path[k] +			switch seg.Cmd { +			case 'M': +				x, y = val(0) +				f.SetXY(x, y) +			case 'L': +				newX, newY = val(0) +				f.Line(x, y, newX, newY) +				x, y = newX, newY +			case 'C': +				cx0, cy0 = val(0) +				cx1, cy1 = val(2) +				newX, newY = val(4) +				f.CurveCubic(x, y, cx0, cy0, newX, newY, cx1, cy1, "D") +				x, y = newX, newY +			default: +				f.SetErrorf("Unexpected path command '%c'", seg.Cmd) +			} +		} +	} +} | 
