| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- // Copyright 2017 The oksvg Authors. All rights reserved.
- // created: 2/12/2017 by S.R.Wiley
- //
- // utils.go implements translation of an SVG2.0 path into a rasterx Path.
- package oksvg
- import (
- "errors"
- "image/color"
- "strconv"
- "strings"
- "github.com/srwiley/rasterx"
- "golang.org/x/image/colornames"
- )
- // unitSuffixes are suffixes sometimes applied to the width and height attributes
- // of the svg element.
- var unitSuffixes = []string{"cm", "mm", "px", "pt"}
- func parseColorValue(v string) (uint8, error) {
- if v[len(v)-1] == '%' {
- n, err := strconv.Atoi(strings.TrimSpace(v[:len(v)-1]))
- if err != nil {
- return 0, err
- }
- return uint8(n * 0xFF / 100), nil
- }
- n, err := strconv.Atoi(strings.TrimSpace(v))
- if n > 255 {
- n = 255
- }
- return uint8(n), err
- }
- // trimSuffixes removes unitSuffixes from any number that is not just numeric
- func trimSuffixes(a string) (b string) {
- if a == "" || (a[len(a)-1] >= '0' && a[len(a)-1] <= '9') {
- return a
- }
- b = a
- for _, v := range unitSuffixes {
- b = strings.TrimSuffix(b, v)
- }
- return
- }
- // parseFloat is a helper function that strips suffixes before passing to strconv.ParseFloat
- func parseFloat(s string, bitSize int) (float64, error) {
- val := trimSuffixes(s)
- return strconv.ParseFloat(val, bitSize)
- }
- // splitOnCommaOrSpace returns a list of strings after splitting the input on comma and space delimiters
- func splitOnCommaOrSpace(s string) []string {
- return strings.FieldsFunc(s,
- func(r rune) bool {
- return r == ',' || r == ' '
- })
- }
- func parseClasses(data string) (map[string]styleAttribute, error) {
- res := map[string]styleAttribute{}
- arr := strings.Split(data, "}")
- for _, v := range arr {
- v = strings.TrimSpace(v)
- if v == "" {
- continue
- }
- valueIndex := strings.Index(v, "{")
- if valueIndex == -1 || valueIndex == len(v)-1 {
- return res, errors.New(v + "}: invalid map format in class definitions")
- }
- classesStr := v[:valueIndex]
- attrStr := v[valueIndex+1:]
- attrMap, err := parseAttrs(attrStr)
- if err != nil {
- return res, err
- }
- classes := strings.Split(classesStr, ",")
- for _, class := range classes {
- class = strings.TrimSpace(class)
- if len(class) > 0 && class[0] == '.' {
- class = class[1:]
- }
- for attrKey, attrVal := range attrMap {
- if res[class] == nil {
- res[class] = make(styleAttribute, len(attrMap))
- }
- res[class][attrKey] = attrVal
- }
- }
- }
- return res, nil
- }
- func parseAttrs(attrStr string) (styleAttribute, error) {
- arr := strings.Split(attrStr, ";")
- res := make(styleAttribute, len(arr))
- for _, kv := range arr {
- kv = strings.TrimSpace(kv)
- if kv == "" {
- continue
- }
- tmp := strings.SplitN(kv, ":", 2)
- if len(tmp) != 2 {
- return res, errors.New(kv + ": invalid attribute format")
- }
- k := strings.TrimSpace(tmp[0])
- v := strings.TrimSpace(tmp[1])
- res[k] = v
- }
- return res, nil
- }
- func readFraction(v string) (f float64, err error) {
- v = strings.TrimSpace(v)
- d := 1.0
- if strings.HasSuffix(v, "%") {
- d = 100
- v = strings.TrimSuffix(v, "%")
- }
- f, err = parseFloat(v, 64)
- f /= d
- // Is this is an unnecessary restriction? For now fractions can be all values not just in the range [0,1]
- // if f > 1 {
- // f = 1
- // } else if f < 0 {
- // f = 0
- // }
- return
- }
- // getColor is a helper function to get the background color
- // if ReadGradUrl needs it.
- func getColor(clr interface{}) color.Color {
- switch c := clr.(type) {
- case rasterx.Gradient: // This is a bit lazy but oh well
- for _, s := range c.Stops {
- if s.StopColor != nil {
- return s.StopColor
- }
- }
- case color.NRGBA:
- return c
- }
- return colornames.Black
- }
- func localizeGradIfStopClrNil(g *rasterx.Gradient, defaultColor interface{}) (grad rasterx.Gradient) {
- grad = *g
- for _, s := range grad.Stops {
- if s.StopColor == nil { // This means we need copy the gradient's Stop slice
- // and fill in the default color
- // Copy the stops
- stops := make([]rasterx.GradStop, len(grad.Stops))
- copy(stops, grad.Stops)
- grad.Stops = stops
- // Use the background color when a stop color is nil
- clr := getColor(defaultColor)
- for i, s := range stops {
- if s.StopColor == nil {
- grad.Stops[i].StopColor = clr
- }
- }
- break // Only need to do this once
- }
- }
- return
- }
|