summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKurt Jung <kurt.w.jung@code.google.com>2014-01-28 15:35:50 -0500
committerKurt Jung <kurt.w.jung@code.google.com>2014-01-28 15:35:50 -0500
commit5dd5cb88814c86919e18527229269e5d1bb82dbc (patch)
tree082ff43cf862806e02fb501279851a94341747fc
parentbdfc192ed30699289db33ed8f8a9b5b268cb654b (diff)
Support for unstyled, path-only SVG images of the type generated by the jSignature web control.
-rw-r--r--doc.go44
-rw-r--r--fpdf.go2
-rw-r--r--fpdf_test.go132
-rw-r--r--image/signature.svg43
-rw-r--r--svgbasic.go210
5 files changed, 393 insertions, 38 deletions
diff --git a/doc.go b/doc.go
index ff372f8..1dad3f2 100644
--- a/doc.go
+++ b/doc.go
@@ -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
@@ -15,21 +15,8 @@
*/
/*
-Package gofpdf implements a PDF document generator.
-
-This package's code and documentation are closely derived from the FPDF library
-created by Olivier Plathey, and a number of font and image resources are copied
-directly from it. Drawing support is adapted from the FPDF geometric figures
-script by David Hernández Sanz. Transparency support is adapted from the FPDF
-transparency script by Martin Hall-May. Support for gradients and clipping is
-adapted from FPDF scripts by Andreas Würmser. Support for outline bookmarks is
-adapted from Olivier Plathey by Manuel Cornes. Support for transformations is
-adapted from the FPDF transformation script by Moritz Wagner and Andreas
-Würmser. Lawrence Kesteloot provided code to allow an image's extent to be
-determined prior to placement. Bruno Michel has provided valuable assistance
-with the code.
-
-The FPDF website is http://www.fpdf.org/.
+Package gofpdf implements a PDF document generator with high level support for
+text, drawing and images.
Features
@@ -57,10 +44,31 @@ Features
• Clipping
+• Basic path-only SVG images
+
gofpdf has no dependencies other than the Go standard library. All tests pass
on Linux, Mac and Windows platforms. Like FPDF version 1.7, from which gofpdf
-is derived, this package does not yet support UTF-8 source text. gofpdf is
-copyrighted by Kurt Jung and is released under the MIT License.
+is derived, this package does not yet support UTF-8 source text.
+
+Acknowledgments
+
+This package's code and documentation are closely derived from the FPDF library
+created by Olivier Plathey, and a number of font and image resources are copied
+directly from it. Drawing support is adapted from the FPDF geometric figures
+script by David Hernández Sanz. Transparency support is adapted from the FPDF
+transparency script by Martin Hall-May. Support for gradients and clipping is
+adapted from FPDF scripts by Andreas Würmser. Support for outline bookmarks is
+adapted from Olivier Plathey by Manuel Cornes. Support for transformations is
+adapted from the FPDF transformation script by Moritz Wagner and Andreas
+Würmser. Lawrence Kesteloot provided code to allow an image's extent to be
+determined prior to placement. Bruno Michel has provided valuable assistance
+with the code.
+
+The FPDF website is http://www.fpdf.org/.
+
+License
+
+gofpdf is copyrighted by Kurt Jung and is released under the MIT License.
Installation
diff --git a/fpdf.go b/fpdf.go
index 30cf006..c2625d0 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -826,7 +826,7 @@ func (f *Fpdf) Curve(x0, y0, cx, cy, x1, y1 float64, styleStr string) {
// the current draw color, line width, and cap style centered on the curve's
// path. Filling uses the current fill color.
//
-// See tutorial 11 for an example of this function.
+// See tutorials 11 and 20 for examples of this function.
func (f *Fpdf) CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string) {
f.point(x0, y0)
f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k,
diff --git a/fpdf_test.go b/fpdf_test.go
index d70875a..c8bf2e3 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -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
@@ -88,6 +88,18 @@ func docWriter(pdf *gofpdf.Fpdf, idx int) *pdfWriter {
return pw
}
+func imageFile(fileStr string) string {
+ return filepath.Join(cnImgDir, fileStr)
+}
+
+func fontFile(fileStr string) string {
+ return filepath.Join(cnFontDir, fileStr)
+}
+
+func textFile(fileStr string) string {
+ return filepath.Join(cnTextDir, fileStr)
+}
+
// Convert 'ABCDEFG' to, for example, 'A,BCD,EFG'
func strDelimit(str string, sepstr string, sepcount int) string {
pos := len(str) - sepcount
@@ -187,7 +199,7 @@ func ExampleFpdf_tutorial01() {
func ExampleFpdf_tutorial02() {
pdf := gofpdf.New("P", "mm", "A4", cnFontDir)
pdf.SetHeaderFunc(func() {
- pdf.Image(cnImgDir+"/logo.png", 10, 6, 30, 0, false, "", 0, "")
+ pdf.Image(imageFile("logo.png"), 10, 6, 30, 0, false, "", 0, "")
pdf.SetY(5)
pdf.SetFont("Arial", "B", 15)
pdf.Cell(80, 0, "")
@@ -274,8 +286,8 @@ func ExampleFpdf_tutorial03() {
chapterTitle(chapNum, titleStr)
chapterBody(fileStr)
}
- printChapter(1, "A RUNAWAY REEF", cnTextDir+"/20k_c1.txt")
- printChapter(2, "THE PROS AND CONS", cnTextDir+"/20k_c2.txt")
+ printChapter(1, "A RUNAWAY REEF", textFile("20k_c1.txt"))
+ printChapter(2, "THE PROS AND CONS", textFile("20k_c2.txt"))
pdf.OutputAndClose(docWriter(pdf, 3))
// Output:
// Successfully generated pdf/tutorial03.pdf
@@ -374,8 +386,8 @@ func ExampleFpdf_tutorial04() {
// Page number
pdf.CellFormat(0, 10, fmt.Sprintf("Page %d", pdf.PageNo()), "", 0, "C", false, 0, "")
})
- printChapter(1, "A RUNAWAY REEF", cnTextDir+"/20k_c1.txt")
- printChapter(2, "THE PROS AND CONS", cnTextDir+"/20k_c2.txt")
+ printChapter(1, "A RUNAWAY REEF", textFile("20k_c1.txt"))
+ printChapter(2, "THE PROS AND CONS", textFile("20k_c2.txt"))
pdf.OutputAndClose(docWriter(pdf, 4))
// Output:
// Successfully generated pdf/tutorial04.pdf
@@ -488,7 +500,7 @@ func ExampleFpdf_tutorial05() {
}
pdf.CellFormat(wSum, 0, "", "T", 0, "", false, 0, "")
}
- loadData(cnTextDir + "/countries.txt")
+ loadData(textFile("countries.txt"))
pdf.SetFont("Arial", "", 14)
pdf.AddPage()
basicTable()
@@ -583,7 +595,7 @@ func ExampleFpdf_tutorial06() {
// Second page
pdf.AddPage()
pdf.SetLink(link, 0, -1)
- pdf.Image(cnImgDir+"/logo.png", 10, 12, 30, 0, false, "", 0, "http://www.fpdf.org")
+ pdf.Image(imageFile("logo.png"), 10, 12, 30, 0, false, "", 0, "http://www.fpdf.org")
pdf.SetLeftMargin(45)
pdf.SetFontSize(14)
htmlStr := `You can now easily print text mixing different styles: <b>bold</b>, ` +
@@ -613,15 +625,15 @@ func ExampleFpdf_tutorial08() {
pdf := gofpdf.New("P", "mm", "A4", cnFontDir)
pdf.AddPage()
pdf.SetFont("Arial", "", 11)
- pdf.Image(cnImgDir+"/logo.png", 10, 10, 30, 0, false, "", 0, "")
+ pdf.Image(imageFile("logo.png"), 10, 10, 30, 0, false, "", 0, "")
pdf.Text(50, 20, "logo.png")
- pdf.Image(cnImgDir+"/logo.gif", 10, 40, 30, 0, false, "", 0, "")
+ pdf.Image(imageFile("logo.gif"), 10, 40, 30, 0, false, "", 0, "")
pdf.Text(50, 50, "logo.gif")
- pdf.Image(cnImgDir+"/logo-gray.png", 10, 70, 30, 0, false, "", 0, "")
+ pdf.Image(imageFile("logo-gray.png"), 10, 70, 30, 0, false, "", 0, "")
pdf.Text(50, 80, "logo-gray.png")
- pdf.Image(cnImgDir+"/logo-rgb.png", 10, 100, 30, 0, false, "", 0, "")
+ pdf.Image(imageFile("logo-rgb.png"), 10, 100, 30, 0, false, "", 0, "")
pdf.Text(50, 110, "logo-rgb.png")
- pdf.Image(cnImgDir+"/logo.jpg", 10, 130, 30, 0, false, "", 0, "")
+ pdf.Image(imageFile("logo.jpg"), 10, 130, 30, 0, false, "", 0, "")
pdf.Text(50, 140, "logo.jpg")
pdf.OutputAndClose(docWriter(pdf, 8))
// Output:
@@ -673,9 +685,9 @@ func ExampleFpdf_tutorial09() {
pdf.SetFont("Times", "", 12)
for j := 0; j < 20; j++ {
if j == 1 {
- pdf.Image(cnImgDir+"/fpdf.png", -1, 0, colWd, 0, true, "", 0, "")
+ pdf.Image(imageFile("fpdf.png"), -1, 0, colWd, 0, true, "", 0, "")
} else if j == 5 {
- pdf.Image(cnImgDir+"/golang-gopher.png", -1, 0, colWd, 0, true, "", 0, "")
+ pdf.Image(imageFile("golang-gopher.png"), -1, 0, colWd, 0, true, "", 0, "")
}
pdf.MultiCell(colWd, 5, loremStr, "", "", false)
pdf.Ln(-1)
@@ -687,7 +699,7 @@ func ExampleFpdf_tutorial09() {
// Test the corner cases as reported by the gocov tool
func ExampleFpdf_tutorial10() {
- gofpdf.MakeFont(cnFontDir+"/calligra.ttf", cnFontDir+"/cp1252.map", cnFontDir, nil, true)
+ gofpdf.MakeFont(fontFile("calligra.ttf"), fontFile("cp1252.map"), cnFontDir, nil, true)
pdf := gofpdf.New("", "", "", "")
pdf.SetFontLocation(cnFontDir)
pdf.SetTitle("世界", true)
@@ -822,7 +834,7 @@ func ExampleFpdf_tutorial12() {
pdf.SetXY(x, y+2)
pdf.CellFormat(rectW, rectH, "A", "", 0, "C", false, 0, "")
pdf.SetAlpha(0.5, modeList[j])
- pdf.Image(cnImgDir+"/golang-gopher.png", x-gapX, y, rectW+2*gapX, 0, false, "", 0, "")
+ pdf.Image(imageFile("golang-gopher.png"), x-gapX, y, rectW+2*gapX, 0, false, "", 0, "")
pdf.SetAlpha(1.0, "Normal")
x += rectW + gapX
j++
@@ -887,7 +899,7 @@ func ExampleFpdf_tutorial14() {
y += 28
pdf.ClipEllipse(26, y+10, 16, 10, true)
- pdf.Image(cnImgDir+"/logo.jpg", 10, y, 32, 0, false, "JPG", 0, "")
+ pdf.Image(imageFile("logo.jpg"), 10, y, 32, 0, false, "JPG", 0, "")
pdf.ClipEnd()
pdf.ClipCircle(60, y+10, 10, true)
@@ -1105,7 +1117,7 @@ func ExampleFpdf_tutorial18() {
pdf.SetMargins(10, 10, 10)
pdf.SetFont("Helvetica", "", 15)
for j, str := range fileList {
- fileStr = filepath.Join(cnImgDir, str)
+ fileStr = imageFile(str)
infoPtr = pdf.RegisterImage(fileStr, "")
imgWd, imgHt = infoPtr.Extent()
switch j {
@@ -1158,3 +1170,85 @@ func ExampleFpdf_tutorial19() {
// Output:
// Successfully generated pdf/tutorial19.pdf
}
+
+// This example demonstrates how to render a simple path-only SVG image of the
+// type generated by the jSignature web control.
+func ExampleFpdf_tutorial20() {
+ const (
+ fontPtSize = 18.0
+ lineHt = fontPtSize * 25.4 / 72.0
+ wd = 100.0
+ sigFileStr = "signature.svg"
+ )
+ var (
+ sig gofpdf.SvgBasicType
+ 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)
+ 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)
+ sig, err = gofpdf.SvgBasicFileParse(imageFile(sigFileStr))
+ if err == nil {
+ scale := 150 / sig.Wd
+ scaleY := 50 / 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)
+ }
+ }
+ }
+ } else {
+ pdf.SetError(err)
+ }
+ pdf.OutputAndClose(docWriter(pdf, 20))
+ // Output:
+ // Successfully generated pdf/tutorial20.pdf
+}
diff --git a/image/signature.svg b/image/signature.svg
new file mode 100644
index 0000000..cdbb4af
--- /dev/null
+++ b/image/signature.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="4010" height="570">
+ <path
+ d="m 243.83517,126.31141 c 50.21866,26.50627 126.75595,-3.87395 151.46369,-35.94162 24.70774,-32.067668 10.41159,-51.980992 -16.41089,-58.072772 -26.82248,-6.09178 -42.17661,10.494756 -57.68933,33.13636 -15.51272,22.641602 -22.3395,49.640452 -26.04984,89.319922 -3.71034,39.67947 -2.57151,103.67609 -16.65175,144.69433 -14.08024,41.01824 -40.79012,59.98014 -80.0876,71.50199 -39.29748,11.52185 -96.60749,6.67866 -126.372403,-24.20296 -29.764916,-30.88162 -24.693439,-84.55794 0.62001,-107.05538 25.313453,-22.49744 49.916583,-21.8463 53.386943,18.74456 3.47036,40.59086 -77.347631,106.99944 -102.157291,102.59574 -24.80966,-4.4037 -9.193082,0 -13.789623,0" />
+ <path
+ d="m 544.84659,276.92526 c 16.02735,-13.54645 29.98118,-31.39768 32.51139,-52.84016 1.4974,-10.24434 -1.94415,-22.06454 -11.64632,-27.13714 -22.66026,-14.31032 -51.37591,-9.68663 -75.27088,-1.12303 -29.57925,11.19994 -56.66841,28.48168 -80.87983,48.69857 -15.56749,13.48245 -30.31327,28.65126 -40.17476,46.87113 -10.56334,25.04076 5.93271,58.80187 33.76392,62.35287 20.97698,3.69753 41.31134,-5.15667 59.15595,-15.26739 23.68053,-12.07041 45.37059,-27.68722 65.11773,-45.42392 5.85816,-4.99109 11.66007,-10.04795 17.4228,-15.14884" />
+ <path
+ d="m 614.57518,235.67736 c -39.71057,22.89155 -90.21146,57.33437 -110.62013,81.46882 -20.40867,24.13445 1.74636,32.16167 21.51346,33.96557 19.7671,1.8039 40.22731,-5.81242 63.90833,-18.83825 23.68102,-13.02583 54.88215,-39.05314 73.09016,-60.24005 18.20801,-21.18691 37.84165,-41.69088 43.78312,-57.46575 5.94147,-15.77487 6.81292,-20.38898 -2.3045,-26.03079" />
+ <path
+ d="m 586.09449,351.56431 c 59.46452,-49.53559 110.50457,-110.94455 181.15663,-145.53797 21.55772,-14.53068 69.21456,-30.38164 74.52476,8.12435 2.15457,32.87115 -24.99905,56.36851 -41.3729,81.85669 -11.33394,14.91071 -34.85688,47.22341 -38.29416,51.12061 38.40925,-59.07476 94.0006,-105.23813 152.94098,-142.74425 21.53324,-14.17578 76.3474,-17.732 63.14039,22.44093 -6.44013,30.30909 -29.58192,52.20309 -51.31162,72.13403 -22.35245,20.18703 -11.93986,55.81184 21.21245,48.89199 28.04358,-1.72994 49.86098,-21.80465 73.62848,-34.51546 51.0484,-39.90132 89.3284,-97.12351 150.3008,-123.60116 30.7216,-12.02494 70.5868,-23.14241 98.2954,1.38478 22.9075,21.32517 9.8573,56.2678 -7.0675,76.93906 -18.1931,26.99594 -44.6741,46.96256 -74.8471,58.94304 -34.0583,20.33195 -79.3725,18.48794 -110.8061,-6.03301 -1.0838,-0.69666 -2.2533,-1.23366 -3.4004,-1.81269" />
+ <path
+ d="m 1154.6667,159.26245 c -19.2615,48.72397 -39.2763,98.0821 -71.8076,139.71403 -28.4578,44.00142 -46.4445,94.33551 -79.7245,135.30113 -23.78316,32.93768 -48.80204,65.54924 -80.01549,91.86673 -19.47895,6.1922 -20.04137,-20.7923 -14.66105,-32.91267 12.83153,-36.25521 37.22095,-66.57625 54.90924,-100.3885 22.07476,-38.69659 54.0525,-69.56182 85.7438,-100.24739" />
+ <path
+ d="m 1076.8889,323.15134 c 41.6055,37.74324 106.1656,40.87499 155.2476,16.47425 79.7307,-33.80513 147.627,-88.69981 215.7912,-140.91414 38.5692,-31.19936 75.8068,-66.58379 98.2999,-111.44269 5.2858,-16.340836 11.2358,-38.191353 -2.3042,-51.988823 -24.8579,-9.392422 -44.7459,19.21292 -62.7133,32.466761 -42.206,41.159272 -75.9104,90.348772 -105.7076,140.969902 -18.1332,33.9424 -36.3441,60.40033 -45.1009,105.84746 -4.8684,25.26636 17.3519,40.01019 38.489,36.24378 27.2382,-1.27906 52.1821,-14.06224 77.2642,-23.5167 20.2617,-9.77194 38.052,-23.75443 55.7341,-37.47313" />
+ <path
+ d="m 1518.5556,231.48467 c 45.3671,35.48031 84.7434,37.23796 135.7328,25.17331 14.925,-4.86754 25.7515,-17.97315 30.1445,-32.73996 6.2047,-12.9545 -0.2899,-27.91555 -12.929,-33.8982 -19.5006,-12.36806 -44.6888,-10.55607 -64.9092,-0.92535 -33.4028,14.45512 -59.4353,40.93033 -84.563,66.45243 -13.9248,16.55902 -22.671,38.06079 -22.9956,59.74639 0.7543,12.73146 10.7427,22.65661 22.5942,25.94215 28.6381,11.95988 61.6748,18.05546 91.8527,8.36881 20.1474,-7.27059 39.868,-15.91987 58.8894,-25.73255 22.7573,-13.36221 41.3454,-32.41648 60.072,-50.72036" />
+ <path
+ d="m 1789.3889,357.87356 c 28.6993,3.03127 63.3496,2.62457 82.7159,-22.45172 21.6447,-19.99229 43.1941,-48.10952 35.6329,-79.45221 -4.1368,-22.17497 -33.5802,-38.22486 -52.1616,-22.03003 -31.7488,22.47063 -31.372,70.25063 -12.8813,101.12391 23.6727,38.55114 74.0602,50.51158 116.164,41.35476 54.0203,-10.31179 104.4208,-53.87389 110.9357,-110.27278 0.7888,-32.83995 0.7757,-65.72269 4.9445,-98.36663 3.9298,-42.7466 9.9471,-89.084404 39.2166,-122.675001 17.104,-18.491716 51.2258,-26.923352 69.4545,-5.555968 14.6931,19.194858 3.0296,45.294939 -10.7419,61.486039 -16.3416,25.1385 -48.7197,29.13643 -75.3811,36.23743 -26.4792,5.77658 -58.6276,2.75073 -76.1034,-20.51069 -9.6512,-10.71312 -20.4603,-22.76827 -20.6837,-38.053992" />
+ <path
+ d="m 2254.6667,137.04023 c -3.9166,0.81691 -6.5582,5.39919 -5.219,9.18763 1.2777,3.63173 4.3682,6.64885 8.1357,7.58096 2.108,0.12035 4.1149,-1.67454 4.2375,-3.78089 0.7885,-3.73488 0.2701,-7.91813 -2.1144,-10.99661 -1.2461,-1.37634 -3.1464,-2.37225 -5.0398,-1.99109" />
+ <path
+ d="m 2228.9722,184.9569 c -29.9457,39.95653 -58.8628,80.7409 -86.118,122.56461 -5.4378,11.43956 -6.9218,27.24913 2.6137,36.94004 11.5344,8.50012 26.6093,3.95387 39.1614,0.62975 31.967,-8.46295 63.2786,-21.86206 88.264,-43.97857 15.2095,-12.48824 29.1196,-26.55137 41.4956,-41.85028" />
+ <path
+ d="m 2496.7771,218.06883 c -3.3292,-19.2489 -24.4513,-30.55681 -42.8081,-28.63327 -39.0192,-1.47124 -74.6768,18.22839 -106.1126,39.27122 -27.4601,19.28281 -52.9628,45.12902 -62.0025,78.30592 -2.9108,24.00347 18.676,49.02117 43.417,46.57579 41.4005,-0.49369 78.4941,-23.55071 108.3115,-50.5202 26.5153,-22.3804 52.8121,-49.50782 59.1947,-84.99946" />
+ <path
+ d="m 2524.8056,241.2069 c -48.3542,24.84206 -95.4271,57.89871 -121.9801,106.67545 -38.548,57.86235 -85.0736,109.83278 -130.7587,162.02483 -14.3416,13.79585 -31.8449,28.78802 -52.8206,28.38924 -22.4201,-5.81296 -20.145,-35.44311 -3.7589,-46.92382 18.3472,-20.65863 39.7594,-38.46949 61.3752,-55.55681 37.3089,-27.01631 74.2659,-55.34905 116.5753,-74.2272 45.8819,-21.27356 92.5741,-41.44894 133.4511,-71.77058" />
+ <path
+ d="m 2571.3333,191.2069 c -11.728,39.55572 -31.7885,76.05727 -54.1537,110.51619 -11.367,17.27415 -23.5479,34.0347 -36.8185,49.90047" />
+ <path
+ d="m 2481.0556,349.54023 c 39.4203,-54.93792 84.2673,-108.82208 144.2119,-142.13528 20.4555,-10.53089 43.6781,-13.18946 66.1233,-16.24748 15.4075,-2.13385 35.1588,8.90133 33.4692,26.24623 -2.9508,16.37726 -14.16,29.73489 -22.9962,43.3486 -13.3021,19.00229 -30.5436,36.66009 -36.611,59.58253 -4.3581,18.38699 14.1449,34.91044 31.9547,29.10574 16.8023,-4.83584 31.073,-15.60156 46.3125,-23.86514 23.356,-14.17855 45.5509,-30.69352 63.9244,-51.0352" />
+ <path
+ d="m 2954.6667,291.2069 c 22.3825,-18.04222 43.6601,-40.96167 49.1836,-70.14766 1.6596,-16.7076 -14.1662,-31.70685 -30.5059,-31.31677 -38.4972,-6.9588 -76.9683,9.1156 -108.0208,30.84253 -27.6063,19.08076 -55.1974,41.48489 -69.2262,72.82154 -7.1903,18.94072 -2.0574,44.40278 17.325,53.92146 22.3966,12.66022 50.5757,7.55107 71.8376,-5.02642 25.3952,-13.7463 46.7256,-33.50699 69.4067,-51.09468" />
+ <path
+ d="m 3041.4722,235.65134 c -34.9663,24.39777 -159.9904,94.46573 -91.1404,111.19476 49.5078,12.02932 120.689,-54.55505 178.2367,-101.75128 57.5477,-47.19623 164.1423,-136.34884 176.2724,-205.706174 12.1301,-69.357335 -35.8274,25.038279 -57.6617,62.402284 -21.8343,37.36401 -53.1817,114.5822 -93.4244,175.68115 -55.9422,84.93494 14.2268,97.70016 78.695,54.07139 43.4593,-29.41104 56.0463,-39.23804 79.8558,-64.64213" />
+ <path
+ d="m 3128.9722,178.01245 c 51.3284,2.8018 102.806,4.27265 154.1667,1.38889" />
+ <path
+ d="m 3368.5556,180.79023 c -26.0554,38.94967 -53.443,76.90675 -75.3826,118.80876 -8.2374,15.73252 -12.8945,40.55439 6.2938,47.64921 25.3545,6.43589 34.5616,-2.95338 49.294,-7.34667 48.0292,-19.31164 87.8533,-54.50776 122.854,-93.37814 15.42,-20.923 30.5124,-42.09003 46.9408,-62.26134" />
+ <path
+ d="m 3520.6389,183.56801 c -31.3786,34.96488 -59.3857,72.92099 -84.9995,112.24284 -6.288,13.61689 -16.1644,29.28735 -9.8599,44.58789 7.0942,13.41865 25.2642,10.69636 37.6171,7.93765 31.8778,-9.70921 58.1068,-31.49347 83.8755,-51.77583 20.5616,-16.72078 38.3203,-36.60992 53.5974,-58.18739 14.5478,-17.73424 29.2159,-35.93086 39.6787,-56.3568 4.934,-18.95097 -28.1547,-19.20088 -17.7187,-0.12487 9.5405,17.82935 32.3392,18.53167 50.1021,19.48194 16.3122,-3.37266 23.3898,15.5346 12.9603,26.5127 -6.8718,8.02389 -13.7478,15.769 -19.1588,24.9829 -16.8576,24.69747 -36.847,48.68268 -44.6449,78.10881 -3.0723,19.33042 19.5849,33.62972 36.7818,27.38476 35.6706,-8.34057 67.9833,-26.49985 99.2689,-44.93349" />
+ <path
+ d="m 3772.7222,231.48467 c 33.1817,22.33062 74.828,35.62344 114.8149,27.5851 15.6821,-3.20582 30.2525,-10.71137 43.1524,-19.942 17.3248,-14.2012 16.9209,-44.79145 -2.1675,-57.28603 -13.4925,-8.89012 -30.6833,-5.87659 -45.6599,-3.46265 -33.5763,7.00049 -61.9142,28.29846 -85.8989,51.8761 -25.4017,22.73023 -42.112,57.70516 -36.9155,92.17467 6.3894,18.1743 25.3211,28.36102 43.6991,29.78005 26.397,2.30348 52.8197,-3.96515 77.6246,-12.547 39.816,-14.49663 75.2011,-38.73414 108.0175,-65.12268" />
+</svg>
diff --git a/svgbasic.go b/svgbasic.go
new file mode 100644
index 0000000..4c58f95
--- /dev/null
+++ b/svgbasic.go
@@ -0,0 +1,210 @@
+/*
+ * 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 (
+ "encoding/xml"
+ "fmt"
+ "io/ioutil"
+ "strconv"
+ "strings"
+)
+
+var pathCmdSub *strings.Replacer
+
+func init() {
+ // Handle permitted constructions like "100L200,230"
+ pathCmdSub = strings.NewReplacer(",", " ",
+ "L", " L ", "l", " l ",
+ "C", " C ", "c", " c ",
+ "M", " M ", "m", " m ")
+}
+
+// SvgBasicSegmentType describes a single curve or position segment
+type SvgBasicSegmentType struct {
+ Cmd byte // See http://www.w3.org/TR/SVG/paths.html for path command structure
+ Arg [6]float64
+}
+
+func absolutizePath(segs []SvgBasicSegmentType) {
+ var x, y float64
+ var segPtr *SvgBasicSegmentType
+ adjust := func(pos int, adjX, adjY float64) {
+ segPtr.Arg[pos] += adjX
+ segPtr.Arg[pos+1] += adjY
+ }
+ for j, seg := range segs {
+ segPtr = &segs[j]
+ if j == 0 && seg.Cmd == 'm' {
+ segPtr.Cmd = 'M'
+ }
+ switch segPtr.Cmd {
+ case 'M':
+ x = seg.Arg[0]
+ y = seg.Arg[1]
+ case 'm':
+ adjust(0, x, y)
+ segPtr.Cmd = 'M'
+ x = segPtr.Arg[0]
+ y = segPtr.Arg[1]
+ case 'L':
+ x = seg.Arg[0]
+ y = seg.Arg[1]
+ case 'l':
+ adjust(0, x, y)
+ segPtr.Cmd = 'L'
+ x = segPtr.Arg[0]
+ y = segPtr.Arg[1]
+ case 'C':
+ x = seg.Arg[4]
+ y = seg.Arg[5]
+ case 'c':
+ adjust(0, x, y)
+ adjust(2, x, y)
+ adjust(4, x, y)
+ segPtr.Cmd = 'C'
+ x = segPtr.Arg[4]
+ y = segPtr.Arg[5]
+ }
+ }
+}
+
+func pathParse(pathStr string) (segs []SvgBasicSegmentType, err error) {
+ var seg SvgBasicSegmentType
+ var j, argJ, argCount, prevArgCount int
+ setup := func(n int) {
+ // It is not strictly necessary to clear arguments, but result may be clearer
+ // to caller
+ for j := 0; j < len(seg.Arg); j++ {
+ seg.Arg[j] = 0.0
+ }
+ argJ = 0
+ argCount = n
+ prevArgCount = n
+ }
+ var str string
+ var c byte
+ pathStr = pathCmdSub.Replace(pathStr)
+ strList := strings.Fields(pathStr)
+ count := len(strList)
+ for j = 0; j < count && err == nil; j++ {
+ str = strList[j]
+ if argCount == 0 { // Look for path command or argument continuation
+ c = str[0]
+ if c == '-' || (c >= '0' && c <= '9') { // More arguments
+ if j > 0 {
+ setup(prevArgCount)
+ // Repeat previous action
+ if seg.Cmd == 'M' {
+ seg.Cmd = 'L'
+ } else if seg.Cmd == 'm' {
+ seg.Cmd = 'l'
+ }
+ } else {
+ err = fmt.Errorf("expecting SVG path command at first position, got %s", str)
+ }
+ }
+ }
+ if err == nil {
+ if argCount == 0 {
+ seg.Cmd = str[0]
+ switch seg.Cmd {
+ case 'M', 'm': // Absolute/relative moveto: x, y
+ setup(2)
+ case 'C', 'c': // Absolute/relative Bézier curve: cx0, cy0, cx1, cy1, x1, y1
+ setup(6)
+ case 'L', 'l': // Absolute/relative lineto: x, y
+ setup(2)
+ default:
+ err = fmt.Errorf("expecting SVG path command at position %d, got %s", j, str)
+ }
+ } else {
+ seg.Arg[argJ], err = strconv.ParseFloat(str, 64)
+ if err == nil {
+ argJ++
+ argCount--
+ if argCount == 0 {
+ segs = append(segs, seg)
+ }
+ }
+ }
+ }
+ }
+ if err == nil {
+ if argCount == 0 {
+ absolutizePath(segs)
+ } else {
+ err = fmt.Errorf("Expecting additional (%d) numeric arguments", argCount)
+ }
+ }
+ return
+}
+
+// SvgBasicType aggregates the information needed to describe a multi-segment
+// basic vector image
+type SvgBasicType struct {
+ Wd, Ht float64
+ Segments [][]SvgBasicSegmentType
+}
+
+// SvgBasicParse parses a simple scalable vector graphics (SVG) buffer into a
+// descriptor. Only a small subset of the SVG standard, in particular the path
+// information generated by jSignature, is supported. The returned path data
+// includes only the commands 'M' (absolute moveto: x, y), 'L' (absolute
+// lineto: x, y), and 'C' (absolute cubic Bézier curve: cx0, cy0, cx1, cy1,
+// x1,y1).
+func SvgBasicParse(buf []byte) (sig SvgBasicType, err error) {
+ type pathType struct {
+ D string `xml:"d,attr"`
+ }
+ type srcType struct {
+ Wd float64 `xml:"width,attr"`
+ Ht float64 `xml:"height,attr"`
+ Paths []pathType `xml:"path"`
+ }
+ var src srcType
+ err = xml.Unmarshal(buf, &src)
+ if err == nil {
+ if src.Wd > 0 && src.Ht > 0 {
+ sig.Wd, sig.Ht = src.Wd, src.Ht
+ var segs []SvgBasicSegmentType
+ for _, path := range src.Paths {
+ if err == nil {
+ segs, err = pathParse(path.D)
+ if err == nil {
+ sig.Segments = append(sig.Segments, segs)
+ }
+ }
+ }
+ } else {
+ err = fmt.Errorf("Unacceptable values for basic SVG extent: %.2f x %.2f",
+ sig.Wd, sig.Ht)
+ }
+ }
+ return
+}
+
+// SvgBasicParse parses a simple scalable vector graphics (SVG) file into a
+// basic descriptor. See SvgBasicParse for additional comments.
+func SvgBasicFileParse(svgFileStr string) (sig SvgBasicType, err error) {
+ var buf []byte
+ buf, err = ioutil.ReadFile(svgFileStr)
+ if err == nil {
+ sig, err = SvgBasicParse(buf)
+ }
+ return
+}