123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /*
- * 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
- }
|