Kurt Jungs Auslieferungszustand
This commit is contained in:
246
svgbasic.go
Normal file
246
svgbasic.go
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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 ",
|
||||
"H", " H ", "h", " h ",
|
||||
"V", " V ", "v", " v ",
|
||||
"Q", " Q ", "q", " q ",
|
||||
"Z", " Z ", "z", " z ")
|
||||
}
|
||||
|
||||
// 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]
|
||||
case 'Q':
|
||||
x = seg.Arg[2]
|
||||
y = seg.Arg[3]
|
||||
case 'q':
|
||||
adjust(0, x, y)
|
||||
adjust(2, x, y)
|
||||
segPtr.Cmd = 'Q'
|
||||
x = segPtr.Arg[2]
|
||||
y = segPtr.Arg[3]
|
||||
case 'H':
|
||||
x = seg.Arg[0]
|
||||
case 'h':
|
||||
segPtr.Arg[0] += x
|
||||
segPtr.Cmd = 'H'
|
||||
x += seg.Arg[0]
|
||||
case 'V':
|
||||
y = seg.Arg[0]
|
||||
case 'v':
|
||||
segPtr.Arg[0] += y
|
||||
segPtr.Cmd = 'V'
|
||||
y += seg.Arg[0]
|
||||
case 'z':
|
||||
segPtr.Cmd = 'Z'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 'H', 'h': // Absolute/relative horizontal line to: x
|
||||
setup(1)
|
||||
case 'L', 'l': // Absolute/relative lineto: x, y
|
||||
setup(2)
|
||||
case 'Q', 'q': // Absolute/relative quadratic curve: x0, y0, x1, y1
|
||||
setup(4)
|
||||
case 'V', 'v': // Absolute/relative vertical line to: y
|
||||
setup(1)
|
||||
case 'Z', 'z': // closepath instruction (takes no arguments)
|
||||
segs = append(segs, seg)
|
||||
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), 'C' (absolute cubic Bézier curve: cx0, cy0, cx1, cy1,
|
||||
// x1,y1), 'Q' (absolute quadratic Bézier curve: x0, y0, x1, y1) and 'Z'
|
||||
// (closepath).
|
||||
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
|
||||
}
|
||||
|
||||
// SVGBasicFileParse parses a simple scalable vector graphics (SVG) file into a
|
||||
// basic descriptor. The SVGBasicWrite() example demonstrates this method.
|
||||
func SVGBasicFileParse(svgFileStr string) (sig SVGBasicType, err error) {
|
||||
var buf []byte
|
||||
buf, err = ioutil.ReadFile(svgFileStr)
|
||||
if err == nil {
|
||||
sig, err = SVGBasicParse(buf)
|
||||
}
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user