|
| 1 | +// embed -- including the contents of an SVG within your own |
| 2 | +// +build !appengine |
| 3 | + |
| 4 | +package main |
| 5 | + |
| 6 | +import ( |
| 7 | + "encoding/xml" |
| 8 | + "flag" |
| 9 | + "fmt" |
| 10 | + "io" |
| 11 | + "os" |
| 12 | + |
| 13 | + svg "github.com/ajstarks/svgo" |
| 14 | +) |
| 15 | + |
| 16 | +var ( |
| 17 | + canvas = svg.New(os.Stdout) |
| 18 | + filename string |
| 19 | +) |
| 20 | + |
| 21 | +var ( |
| 22 | + titleHeight = 64 |
| 23 | + borderSize = 4 |
| 24 | + borderfmt = "fill:transparent;stroke:red;stroke-width:%d" |
| 25 | + titlefmt = `text-anchor:middle;width:%d;font-size:32px` |
| 26 | + groupfmt = `transform="translate(%d, %d)"` |
| 27 | +) |
| 28 | + |
| 29 | +func embed() error { |
| 30 | + // load and parse svg |
| 31 | + f, err := os.Open(filename) |
| 32 | + if err != nil { |
| 33 | + return err |
| 34 | + } |
| 35 | + s, err := parseSVG(f) |
| 36 | + if err != nil { |
| 37 | + return err |
| 38 | + } |
| 39 | + width := s.Width + 2*borderSize |
| 40 | + height := s.Height + titleHeight + 2*borderSize |
| 41 | + // create svg |
| 42 | + canvas.Start(width, height) |
| 43 | + placeHeader(width, height, f.Name()) |
| 44 | + // embed svg |
| 45 | + placeSVG(s) |
| 46 | + return nil |
| 47 | +} |
| 48 | + |
| 49 | +// SVG contains the parsed attributes and xml from the given file. |
| 50 | +type SVG struct { |
| 51 | + // Width and Height are attributes of the <svg> tag |
| 52 | + Width int `xml:"width,attr"` |
| 53 | + Height int `xml:"height,attr"` |
| 54 | + // Doc is all all of the contents within the <svg> tags, specified by the |
| 55 | + // `innerxml` struct tag |
| 56 | + Doc []byte `xml:",innerxml"` |
| 57 | +} |
| 58 | + |
| 59 | +func parseSVG(src io.Reader) (SVG, error) { |
| 60 | + var s SVG |
| 61 | + data, err := io.ReadAll(src) |
| 62 | + if err != nil { |
| 63 | + return SVG{}, err |
| 64 | + } |
| 65 | + if xml.Unmarshal(data, &s); err != nil { |
| 66 | + return SVG{}, err |
| 67 | + } |
| 68 | + return s, nil |
| 69 | +} |
| 70 | + |
| 71 | +func placeHeader(width, height int, name string) { |
| 72 | + // add border |
| 73 | + canvas.Rect(0, 0, width, height, fmt.Sprintf(borderfmt, borderSize)) |
| 74 | + // add title from file name |
| 75 | + canvas.Text(width/2, titleHeight*3/4, name, fmt.Sprintf(titlefmt, width)) |
| 76 | +} |
| 77 | + |
| 78 | +func placeSVG(s SVG) { |
| 79 | + // create clip path of svg size |
| 80 | + canvas.Group(`clip-path="url(#embed)"`, fmt.Sprintf(groupfmt, borderSize, titleHeight+borderSize)) |
| 81 | + canvas.ClipPath(`id="embed"`) |
| 82 | + canvas.Rect(0, 0, s.Width, s.Height) |
| 83 | + canvas.ClipEnd() |
| 84 | + // append embedded svg |
| 85 | + canvas.Writer.Write(s.Doc) |
| 86 | + canvas.Gend() |
| 87 | + canvas.End() |
| 88 | +} |
| 89 | + |
| 90 | +func init() { |
| 91 | + flag.StringVar(&filename, "f", "embed.svg", "file name") |
| 92 | + flag.Parse() |
| 93 | +} |
| 94 | + |
| 95 | +func main() { |
| 96 | + if err := embed(); err != nil { |
| 97 | + fmt.Fprintf(os.Stdout, "error: %v\n", err) |
| 98 | + os.Exit(1) |
| 99 | + } |
| 100 | +} |
0 commit comments