mirror of
https://github.com/coalaura/ffwebp.git
synced 2025-09-08 13:59:54 +00:00
svg
This commit is contained in:
@@ -6,7 +6,7 @@ FFWebP is a small, single-binary CLI for converting images between formats, thin
|
|||||||
|
|
||||||
- Single binary: no external tools required
|
- Single binary: no external tools required
|
||||||
- Auto-detects input codec and infers output from the file extension
|
- Auto-detects input codec and infers output from the file extension
|
||||||
- Supports AVIF, BMP, GIF, ICO, JPEG, JPEG XL, PCX, PNG, PNM (PBM/PGM/PPM/PAM), PSD (decode-only), QOI, TGA, TIFF, WebP, and XBM
|
- Supports AVIF, BMP, GIF, ICO, JPEG, JPEG XL, PCX, PNG, PNM (PBM/PGM/PPM/PAM), PSD (decode-only), QOI, SVG (decode-only), TGA, TIFF, WebP, and XBM
|
||||||
- Lossy or lossless output with configurable quality
|
- Lossy or lossless output with configurable quality
|
||||||
- Thumbnail generation via Lanczos3 resampling
|
- Thumbnail generation via Lanczos3 resampling
|
||||||
- Per-codec flags for fine-grained control (see `ffwebp --help`)
|
- Per-codec flags for fine-grained control (see `ffwebp --help`)
|
||||||
|
4
go.mod
4
go.mod
@@ -17,6 +17,8 @@ require (
|
|||||||
github.com/samuel/go-pcx v0.0.0-20210515040514-6a5ce4d132f7
|
github.com/samuel/go-pcx v0.0.0-20210515040514-6a5ce4d132f7
|
||||||
github.com/sergeymakinen/go-ico v1.0.0-beta.0
|
github.com/sergeymakinen/go-ico v1.0.0-beta.0
|
||||||
github.com/spakin/netpbm v1.3.2
|
github.com/spakin/netpbm v1.3.2
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef
|
||||||
github.com/xyproto/xbm v1.0.0
|
github.com/xyproto/xbm v1.0.0
|
||||||
golang.org/x/image v0.18.0
|
golang.org/x/image v0.18.0
|
||||||
)
|
)
|
||||||
@@ -26,4 +28,6 @@ require (
|
|||||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||||
github.com/sergeymakinen/go-bmp v1.0.0 // indirect
|
github.com/sergeymakinen/go-bmp v1.0.0 // indirect
|
||||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 // indirect
|
||||||
|
golang.org/x/text v0.16.0 // indirect
|
||||||
)
|
)
|
||||||
|
8
go.sum
8
go.sum
@@ -33,6 +33,10 @@ github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3
|
|||||||
github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk=
|
github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk=
|
||||||
github.com/spakin/netpbm v1.3.2 h1:ZAb16Sw/+b4QeO9NokEvejVvbrYdF6DdcYJe0dKONL0=
|
github.com/spakin/netpbm v1.3.2 h1:ZAb16Sw/+b4QeO9NokEvejVvbrYdF6DdcYJe0dKONL0=
|
||||||
github.com/spakin/netpbm v1.3.2/go.mod h1:cVep9uXARFgAu2UU+0c+OE1J+eKmDN2hW0EN+tazkTA=
|
github.com/spakin/netpbm v1.3.2/go.mod h1:cVep9uXARFgAu2UU+0c+OE1J+eKmDN2hW0EN+tazkTA=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||||
@@ -43,6 +47,10 @@ github.com/xyproto/xbm v1.0.0 h1:R5/A2+yhyy4V2c626bdIVfS1UNwE3efhiEBuKXU6u3A=
|
|||||||
github.com/xyproto/xbm v1.0.0/go.mod h1:m2xrjsNmxuzBrx6gs4rSgxgpJWXS01j9KxYgjxhcnoc=
|
github.com/xyproto/xbm v1.0.0/go.mod h1:m2xrjsNmxuzBrx6gs4rSgxgpJWXS01j9KxYgjxhcnoc=
|
||||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||||
|
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 h1:DZshvxDdVoeKIbudAdFEKi+f70l51luSy/7b76ibTY0=
|
||||||
|
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
8
internal/builtins/svg.go
Normal file
8
internal/builtins/svg.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//go:build svg || core || full
|
||||||
|
// +build svg core full
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/coalaura/ffwebp/internal/codec/svg"
|
||||||
|
)
|
@@ -101,7 +101,7 @@ func (impl) Decode(reader io.Reader) (image.Image, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (impl) Encode(writer io.Writer, img image.Image, options opts.Common) error {
|
func (impl) Encode(writer io.Writer, img image.Image, options opts.Common) error {
|
||||||
logx.Printf("avif: quality=%d, quality-alpha=%d, speed=%d, chroma=%d\n", options.Quality, qualityA, speed, chroma)
|
logx.Printf("avif: quality=%d quality-alpha=%d speed=%d chroma=%d\n", options.Quality, qualityA, speed, chroma)
|
||||||
|
|
||||||
return avif.Encode(writer, img, avif.Options{
|
return avif.Encode(writer, img, avif.Options{
|
||||||
Quality: options.Quality,
|
Quality: options.Quality,
|
||||||
|
@@ -40,7 +40,7 @@ func (impl) Flags(flags []cli.Flag) []cli.Flag {
|
|||||||
return append(flags,
|
return append(flags,
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
Name: "jpegxl.effort",
|
Name: "jpegxl.effort",
|
||||||
Usage: "JPEG XL: encode effort (1=fast .. 10=slow). Default 7",
|
Usage: "JPEGXL: encode effort (1=fast .. 10=slow). Default 7",
|
||||||
Value: 7,
|
Value: 7,
|
||||||
Destination: &effort,
|
Destination: &effort,
|
||||||
Validator: func(value int) error {
|
Validator: func(value int) error {
|
||||||
|
@@ -64,7 +64,7 @@ func (impl) Sniff(reader io.ReaderAt) (int, []byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (impl) Decode(reader io.Reader) (image.Image, error) {
|
func (impl) Decode(reader io.Reader) (image.Image, error) {
|
||||||
logx.Printf("psd: skipMerged=%t\n", skipMerged)
|
logx.Printf("psd: skip-merged=%t\n", skipMerged)
|
||||||
|
|
||||||
img, _, err := psd.Decode(reader, &psd.DecodeOptions{
|
img, _, err := psd.Decode(reader, &psd.DecodeOptions{
|
||||||
SkipMergedImage: skipMerged,
|
SkipMergedImage: skipMerged,
|
||||||
|
163
internal/codec/svg/svg.go
Normal file
163
internal/codec/svg/svg.go
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package svg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/draw"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coalaura/ffwebp/internal/codec"
|
||||||
|
"github.com/coalaura/ffwebp/internal/logx"
|
||||||
|
"github.com/coalaura/ffwebp/internal/opts"
|
||||||
|
"github.com/srwiley/oksvg"
|
||||||
|
"github.com/srwiley/rasterx"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
svgWidth int
|
||||||
|
svgHeight int
|
||||||
|
svgBackground string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
codec.Register(impl{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type impl struct{}
|
||||||
|
|
||||||
|
func (impl) String() string {
|
||||||
|
return "svg"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (impl) Extensions() []string {
|
||||||
|
return []string{"svg"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (impl) CanEncode() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (impl) Flags(flags []cli.Flag) []cli.Flag {
|
||||||
|
return append(flags,
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "svg.width",
|
||||||
|
Usage: "SVG: output width in pixels (0 = auto)",
|
||||||
|
Value: 0,
|
||||||
|
Destination: &svgWidth,
|
||||||
|
},
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "svg.height",
|
||||||
|
Usage: "SVG: output height in pixels (0 = auto)",
|
||||||
|
Value: 0,
|
||||||
|
Destination: &svgHeight,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "svg.background",
|
||||||
|
Usage: "SVG: background color",
|
||||||
|
Value: "",
|
||||||
|
Destination: &svgBackground,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (impl) Sniff(reader io.ReaderAt) (int, []byte, error) {
|
||||||
|
buf := make([]byte, 128)
|
||||||
|
|
||||||
|
n, err := reader.ReadAt(buf, 0)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[:n]
|
||||||
|
|
||||||
|
if strings.Contains(strings.ToLower(string(buf)), "<svg") {
|
||||||
|
sniff := buf
|
||||||
|
|
||||||
|
if len(sniff) > 64 {
|
||||||
|
sniff = sniff[:64]
|
||||||
|
}
|
||||||
|
|
||||||
|
return 80, sniff, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (impl) Decode(r io.Reader) (image.Image, error) {
|
||||||
|
icon, err := oksvg.ReadIconStream(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vbw := int(icon.ViewBox.W)
|
||||||
|
vbh := int(icon.ViewBox.H)
|
||||||
|
|
||||||
|
var w, h int
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case svgWidth > 0 && svgHeight > 0:
|
||||||
|
w, h = svgWidth, svgHeight
|
||||||
|
case svgWidth > 0 && svgHeight == 0:
|
||||||
|
w = svgWidth
|
||||||
|
|
||||||
|
if vbw > 0 && vbh > 0 {
|
||||||
|
h = int(float64(svgWidth) * float64(vbh) / float64(vbw))
|
||||||
|
} else {
|
||||||
|
h = svgWidth
|
||||||
|
}
|
||||||
|
case svgHeight > 0 && svgWidth == 0:
|
||||||
|
h = svgHeight
|
||||||
|
|
||||||
|
if vbw > 0 && vbh > 0 {
|
||||||
|
w = int(float64(svgHeight) * float64(vbw) / float64(vbh))
|
||||||
|
} else {
|
||||||
|
w = svgHeight
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if vbw > 0 && vbh > 0 {
|
||||||
|
w, h = vbw, vbh
|
||||||
|
} else {
|
||||||
|
w, h = 256, 256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if w <= 0 {
|
||||||
|
w = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
if h <= 0 {
|
||||||
|
h = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
logx.Printf("svg: size=%dx%d background=%s\n", w, h, svgBackground)
|
||||||
|
|
||||||
|
rgba := image.NewRGBA(image.Rect(0, 0, w, h))
|
||||||
|
|
||||||
|
if svgBackground != "" && !strings.EqualFold(svgBackground, "transparent") {
|
||||||
|
bgc, err := oksvg.ParseSVGColor(svgBackground)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if bgc == nil {
|
||||||
|
return nil, fmt.Errorf("invalid svg.background: %s", svgBackground)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.Draw(rgba, rgba.Bounds(), &image.Uniform{bgc}, image.Point{}, draw.Src)
|
||||||
|
}
|
||||||
|
|
||||||
|
icon.SetTarget(0, 0, float64(w), float64(h))
|
||||||
|
|
||||||
|
scanner := rasterx.NewScannerGV(w, h, rgba, rgba.Bounds())
|
||||||
|
|
||||||
|
d := rasterx.NewDasher(w, h, scanner)
|
||||||
|
|
||||||
|
icon.Draw(d, 1.0)
|
||||||
|
|
||||||
|
return rgba, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (impl) Encode(w io.Writer, img image.Image, options opts.Common) error {
|
||||||
|
return errors.New("svg: encoding not supported")
|
||||||
|
}
|
72
test/image.svg
Normal file
72
test/image.svg
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg id="svg2" width="620" height="472" xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs id="defs4">
|
||||||
|
<path id="box1" d="m0 0h77v210h-77z" stroke="#000" stroke-width="2"/>
|
||||||
|
<path id="box2" d="m0 0h77v60h-77z" stroke="#000" stroke-width="2"/>
|
||||||
|
</defs>
|
||||||
|
<path id="bg" d="m0 0h620v472h-620z" fill="#fff"/>
|
||||||
|
<g id="g9" transform="translate(2 1)">
|
||||||
|
<use id="use11" fill="#fff" xlink:href="#box1"/>
|
||||||
|
<use id="use13" x="77" fill="#ff0" xlink:href="#box1"/>
|
||||||
|
<use id="use15" x="154" fill="#0ff" xlink:href="#box1"/>
|
||||||
|
<use id="use17" x="231" fill="#0f0" xlink:href="#box1"/>
|
||||||
|
<use id="use19" x="308" fill="#f0f" xlink:href="#box1"/>
|
||||||
|
<use id="use21" x="385" fill="red" xlink:href="#box1"/>
|
||||||
|
<use id="use23" x="462" fill="#00f" xlink:href="#box1"/>
|
||||||
|
<use id="use25" x="539" xlink:href="#box1"/>
|
||||||
|
</g>
|
||||||
|
<g id="g45" transform="translate(2 220)">
|
||||||
|
<use id="use47" fill="#0f0" xlink:href="#box2"/>
|
||||||
|
<use id="use49" x="77" fill="#0f0" xlink:href="#box2"/>
|
||||||
|
<use id="use51" x="154" fill="#0f0" xlink:href="#box2"/>
|
||||||
|
<use id="use53" x="231" fill="#0f0" xlink:href="#box2"/>
|
||||||
|
<use id="use55" x="308" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use57" x="385" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use59" x="462" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use61" x="539" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<text id="green100" x="30" y="35" fill="#fff" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">0.59</text>
|
||||||
|
</g>
|
||||||
|
<g id="g27" transform="translate(2 280)">
|
||||||
|
<use id="use29" fill="red" xlink:href="#box2"/>
|
||||||
|
<use id="use31" x="77" fill="red" xlink:href="#box2"/>
|
||||||
|
<use id="use33" x="154" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use35" x="231" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use37" x="308" fill="red" xlink:href="#box2"/>
|
||||||
|
<use id="use39" x="385" fill="red" xlink:href="#box2"/>
|
||||||
|
<use id="use41" x="462" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use43" x="539" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<text id="red100" x="20" y="35" fill="#fff" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">+0.30</text>
|
||||||
|
</g>
|
||||||
|
<g id="g63" transform="translate(2 340)">
|
||||||
|
<use id="use65" fill="#00f" xlink:href="#box2"/>
|
||||||
|
<use id="use67" x="77" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use69" x="154" fill="#00f" xlink:href="#box2"/>
|
||||||
|
<use id="use71" x="231" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use73" x="308" fill="#00f" xlink:href="#box2"/>
|
||||||
|
<use id="use75" x="385" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="use77" x="462" fill="#00f" xlink:href="#box2"/>
|
||||||
|
<use id="use79" x="539" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<text id="blue100" x="20" y="35" fill="#fff" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">+0.11</text>
|
||||||
|
</g>
|
||||||
|
<g id="g63" transform="translate(2 410)">
|
||||||
|
<use id="grey100" fill="#fff" xlink:href="#box2"/>
|
||||||
|
<use id="grey89" x="77" fill="#e3e3e3" xlink:href="#box2"/>
|
||||||
|
<use id="grey70" x="154" fill="#b2b2b2" xlink:href="#box2"/>
|
||||||
|
<use id="grey59" x="231" fill="#969696" xlink:href="#box2"/>
|
||||||
|
<use id="grey41" x="308" fill="#696969" xlink:href="#box2"/>
|
||||||
|
<use id="grey30" x="385" fill="#4d4d4d" xlink:href="#box2"/>
|
||||||
|
<use id="grey11" x="462" fill="#1c1c1c" xlink:href="#box2"/>
|
||||||
|
<use id="grey0" x="539" fill="#000" xlink:href="#box2"/>
|
||||||
|
<text id="txgrey100" x="20" y="35" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">100%</text>
|
||||||
|
<text id="txgrey89" x="102" y="35" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">89%</text>
|
||||||
|
<text id="txgrey70" x="179" y="35" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">70%</text>
|
||||||
|
<text id="txgrey59" x="256" y="35" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">59%</text>
|
||||||
|
<text id="txgrey41" x="333" y="35" fill="#fff" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">41%</text>
|
||||||
|
<text id="txgrey30" x="408" y="35" fill="#fff" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">30%</text>
|
||||||
|
<text id="txgrey11" x="487" y="35" fill="#fff" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">11%</text>
|
||||||
|
<text id="txgrey0" x="569" y="35" fill="#fff" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">0%</text>
|
||||||
|
</g>
|
||||||
|
<text id="text3446-0" x="90" y="184" fill="#fff" font-size="180" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="4" xml:space="preserve">TEST</text>
|
||||||
|
<text id="text3446" x="80" y="174" font-family="DejaVu Sans, Arial, Helvetica" stroke-width="4" xml:space="preserve"><tspan id="tspan3448" x="80" y="174" font-size="180">TEST</tspan></text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
Reference in New Issue
Block a user