summaryrefslogtreecommitdiff
path: root/svgbasic.go
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 /svgbasic.go
parentbdfc192ed30699289db33ed8f8a9b5b268cb654b (diff)
Support for unstyled, path-only SVG images of the type generated by the jSignature web control.
Diffstat (limited to 'svgbasic.go')
-rw-r--r--svgbasic.go210
1 files changed, 210 insertions, 0 deletions
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
+}