pax_global_header00006660000000000000000000000064150421334630014513gustar00rootroot0000000000000052 comment=7f2e4ebef22c7227921a650173d10c872728763b png2svg-1.7.0/000077500000000000000000000000001504213346300131065ustar00rootroot00000000000000png2svg-1.7.0/.github/000077500000000000000000000000001504213346300144465ustar00rootroot00000000000000png2svg-1.7.0/.github/workflows/000077500000000000000000000000001504213346300165035ustar00rootroot00000000000000png2svg-1.7.0/.github/workflows/build.yml000066400000000000000000000020271504213346300203260ustar00rootroot00000000000000on: [push, pull_request, workflow_dispatch] name: Build env: GO111MODULE: on jobs: test: strategy: matrix: go-version: [1.18.x, 1.19.x, 1.20.x, 1.21.x] os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - name: Install Go uses: actions/setup-go@v5 with: go-version: stable - name: Checkout code uses: actions/checkout@v4 - name: Test run: go test ./... test-cache: runs-on: ubuntu-latest steps: - name: Install Go uses: actions/setup-go@v5 with: go-version: 1.21.x - name: Checkout code uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: | ~/go/pkg/mod # Module download cache ~/.cache/go-build # Build cache (Linux) ~/Library/Caches/go-build # Build cache (Mac) key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Test run: go test ./... png2svg-1.7.0/.gitignore000066400000000000000000000003451504213346300151000ustar00rootroot00000000000000* !*.* !*/ *.o *.swp *.tmp *.bak *.pro.user *.dblite *.so *.swo *.ao *.pyc *.orig *.pb.go *~ ._* *.nfs.* .vscode *CMakeFiles* .DS_Store _obj _test _testmain.go !img/* cmd/png2svg/*.svg include.txt cmd/png2svg/png2svg.* .DS_Store png2svg-1.7.0/.ignore000066400000000000000000000000071504213346300143670ustar00rootroot00000000000000vendor png2svg-1.7.0/LICENSE000066400000000000000000000026721504213346300141220ustar00rootroot00000000000000Copyright 2022 Alexander F. Rødseth Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. png2svg-1.7.0/README.md000066400000000000000000000165241504213346300143750ustar00rootroot00000000000000# png2svg ![Build](https://github.com/xyproto/png2svg/workflows/Build/badge.svg) [![GoDoc](https://godoc.org/github.com/xyproto/png2svg?status.svg)](http://godoc.org/github.com/xyproto/png2svg) [![Go Report Card](https://goreportcard.com/badge/github.com/xyproto/png2svg)](https://goreportcard.com/report/github.com/xyproto/png2svg) Go module and command line utility for converting small PNG images to SVG Tiny 1.2. ## Features and limitations * Draws rectangles for each region in the PNG image that can be covered by a rectangle. * The remaining pixels are drawn with a rectangle for each pixel. * This is not an efficient representation of PNG images! * The conversion may be useful if you have a small PNG image or icons at sizes around 32x32, and wish to scale them up and print them out without artifacts. * The utility is fast for small images, but larger images will take an unreasonable amount of time to convert, creating SVG files many megabytes in size. This could potentially also be used for benchmarking the single-core performance of a CPU. * The resulting SVG images can be opened directly in a browser like Firefox or Chromium, and may look sharper and crisper than small PNG or JPEG images that are smoothed/blurred by the browser, by default (this can be configured with CSS, though). * The default crispiness of how SVG images are displayed may be useful for displaying "pixel art" style graphics in the browser. * Written in pure Go, with no runtime dependencies on any external library or utility. * Handles transparent PNG images by not drawing SVG elements for the transparent regions. * For creating SVG images that draws a rectangle for each and every pixel, instead of also using larger rectangles, use the `-p` flag. ## Image Comparison | 192x192 PNG image (16 colors) | 192x192 SVG image (16 colors) | 192x192 SVG image (optimized with [svgo](https://github.com/svg/svgo)) | | ----------------------------- | ------------------------------ | ---------------------------------------------------------------------- | | 8.2 KiB | 193 KiB | 66 KiB | | ![png](img/spaceships.png) | ![svg](img/spaceships4096.svg) | ![svgopt](img/spaceships_opt.svg) | The spaceships are drawn by [wuhu](https://opengameart.org/content/spaceships-1) (CC-BY 3.0). Try zooming in on the images. Most browsers will keep the SVG image crisp when zooming in, but blur the PNG image. For keeping PNG images crisp, this CSS can be used, but this is not normally needed for SVG images: ```css image-rendering: -moz-crisp-edges; /* Firefox */ image-rendering: -o-crisp-edges; /* Opera */ image-rendering: -webkit-optimize-contrast; /* Webkit (non-standard naming) */ image-rendering: crisp-edges; -ms-interpolation-mode: nearest-neighbor; /* IE (non-standard property) */ ``` Right now, Chrome does not support `image-rendering: crisp-edges`, while Firefox does not support `image-rendering: pixelated`. This may change over time, check out the excellent [caniuse.com](https://caniuse.com/css-crisp-edges) page. Using SVG to get crisp images has the advantage of not relying on CSS that may differ from browser to browser. Other comparisons: | 302x240 PNG image | 302x240 SVG image (limited to 4096 colors) | 302x240 SVG (optimized with [svgo](https://github.com/svg/svgo)) | |----------------------------|--------------------------------------------|------------------------------------------------------------------| | 176 KiB | 3.1 MiB | 934 KiB | | ![png](img/rainforest.png) | ![svg](img/rainforest4096.svg) | ![svgopt](img/rainforest_opt.svg) | With palette reduction: | `-n 96` + svgo | `-n 32` + svgo | `-n 16` + svgo | `-n 4` + svgo | |------------------------------------------|------------------------------------------|------------------------------------------|----------------------------------------| | 516 KiB | 356 KiB | 369 KiB | 139 KiB | | ![96 colors](img/rainforest_96c_opt.svg) | ![32 colors](img/rainforest_32c_opt.svg) | ![16 colors](img/rainforest_16c_opt.svg) | ![8 colors](img/rainforest_4c_opt.svg) | Note that fewer colors does not always result in smaller images, because it depends on the shapes of the resulting areas with the same colors, and not just on having few colors. | 64x64 PNG image | 64x64 SVG image (one rectangle per pixel) | 64x64 SVG image (4096 colors) | 64x64 SVG image (rectangles >1px are colored pink) | 64x64 SVG image (optimized with [svgo](https://github.com/svg/svgo)) | |------------------------|-------------------------------------------|--------------------------------|----------------------------------------------------|----------------------------------------------------------------------| | 4.1 KiB | 172 KiB | 74 KiB | | 25 KiB | | ![png](img/glenda.png) | ![svgpixel](img/glenda_singlepixel.svg) | ![svg4096](img/glenda4096.svg) | ![svgpink](img/glenda_pink.svg) | ![svgopt](img/glenda_opt.svg) | The rainforest image is from [Wikipedia](https://en.wikipedia.org/wiki/Landscape). The Glenda bunny is from [9p.io](https://9p.io/plan9/glenda.html). ## Q&A **Q:** Why 4096 colors?
**A:** Because representing colors on the short form (`#000` as opposed to `#000000`) makes it possible to express 4096 unique colors. **Q:** Does this mean that I can make an entire web page in SVG, with photos and everything?
**A:** Yes! This is not the intended use of `png2svg`, but it might work out if the images are kept small. **Q:** Can I use this for QR codes?
**A:** Yes! **Q:** Can I use `png2svg` together with `svgo` to create assets for a game that only uses vector graphics?
**A:** Yes! If the images are kept small. **Q:** Are these questions just made up, or did someone actually ask this?
**A:** Look behind you, a three headed monkey! ## Installation For Go 1.17 or later: go install github.com/xyproto/png2svg/cmd/png2svg@latest ## Example usage Generate an SVG image with as few rectangles as possible (`-o` for "output"): png2svg -o output.svg input.png Generate an SVG image with one rectangle per pixel: png2svg -p -o output.svg input.png Generate an SVG image where the output is limited to 4096 unique colors (`-l` for "limit"): png2svg -l -o output.svg input.png Like above, but with progress information while the image is being generated: png2svg -v -l -o output.svg input.png Same as above, but also reduce the number of colors to 32: png2svg -v -l -n 32 -o output.svg input.png ## Packaging status [![Packaging status](https://repology.org/badge/vertical-allrepos/png2svg.svg)](https://repology.org/project/png2svg/versions) ## General information * Version: 1.7.0 * Author: Alexander F. Rødseth <xyproto@archlinux.org> * License: BSD-3 png2svg-1.7.0/TODO.md000066400000000000000000000005531504213346300142000ustar00rootroot00000000000000# TODO - [ ] Make the distinction between 4096 colors and color quantization clearer in the code. - [ ] Combine rectangles that are next to each other, and of the same color, into polygons. - [ ] Divide larger images into 128x128 tiles when converting. - [ ] Experiment with tile sizes, to see if it increases performance. - [ ] Benchmark and profile some more. png2svg-1.7.0/box.go000066400000000000000000000120541504213346300142270ustar00rootroot00000000000000// Package png2svg can convert images from PNG to TinySVG by drawing rectangles package png2svg import ( "fmt" "math/rand" "strconv" ) // Box represents a box with the following properties: // * position (x, y) // * size (w, h) // * color (r, g, b, a) type Box struct { x, y int w, h int r, g, b, a int } // CreateRandomBox randomly searches for a place for a 1x1 size box. // Note: If checkIfPossible is true, the function continue running until // it either finds a free spot or no spots are available. func (pi *PixelImage) CreateRandomBox(checkIfPossible bool) *Box { w := 1 h := 1 var x, y, r, g, b, a int for !checkIfPossible || !pi.Done(0, 0) { // Find a random placement for (x,y), for a box of size (1,1) x = rand.Intn(pi.w) y = rand.Intn(pi.h) if pi.verbose { fmt.Printf("Random box at (%d, %d)\n", x, y) } if pi.Covered(x, y) { continue } r, g, b, a = pi.At2(x, y) break } // Create a box at that placement, with width 1 and height 1 // Return the box return &Box{x, y, w, h, r, g, b, a} } // CreateBox creates a 1x1 box at the given location, if it's not already covered func (pi *PixelImage) CreateBox(x, y int) *Box { if pi.Covered(x, y) { panic("CreateBox at location that was already covered") } w, h := 1, 1 r, g, b, a := pi.At2(x, y) // Create a box at that placement, with width 1 and height 1 // Return the box return &Box{x, y, w, h, r, g, b, a} } // ExpandLeft will expand a box 1 pixel to the left, // if all new pixels have the same color func (pi *PixelImage) ExpandLeft(bo *Box) bool { // Loop from box top left (-1,0) to box bot left (-1,0) x := bo.x - 1 if x <= 0 { return false } for y := bo.y; y < (bo.y + bo.h); y++ { r, g, b, a := pi.At2(x, y) if (r != bo.r) || (g != bo.g) || (b != bo.b) || (a != bo.a) { return false } } // Expand the box 1 pixel to the left bo.w++ bo.x-- return true } // ExpandUp will expand a box 1 pixel upwards, // if all new pixels have the same color func (pi *PixelImage) ExpandUp(bo *Box) bool { // Loop from box top left to box top right y := bo.y - 1 if y <= 0 { return false } for x := bo.x; x < (bo.x + bo.w); x++ { r, g, b, a := pi.At2(x, y) if (r != bo.r) || (g != bo.g) || (b != bo.b) || (a != bo.a) { return false } } // Expand the box 1 pixel up bo.h++ bo.y-- return true } // ExpandRight will expand a box 1 pixel to the right, // if all new pixels have the same color func (pi *PixelImage) ExpandRight(bo *Box) bool { // Loop from box top right (+1,0) to box bot right (+1,0) x := bo.x + bo.w //+ 1 if x >= pi.w { return false } for y := bo.y; y < (bo.y + bo.h); y++ { r, g, b, a := pi.At2(x, y) if (r != bo.r) || (g != bo.g) || (b != bo.b) || (a != bo.a) { return false } } // Expand the box 1 pixel to the right bo.w++ return true } // ExpandDown will expand a box 1 pixel downwards, // if all new pixels have the same color func (pi *PixelImage) ExpandDown(bo *Box) bool { // Loop from box bot left to box bot right y := bo.y + bo.h //+ 1 if y >= pi.h { return false } for x := bo.x; x < (bo.x + bo.w); x++ { r, g, b, a := pi.At2(x, y) if (r != bo.r) || (g != bo.g) || (b != bo.b) || (a != bo.a) { return false } } // Expand the box 1 pixel down bo.h++ return true } // ExpandOnce tries to expand the box to the right and downwards, once func (pi *PixelImage) ExpandOnce(bo *Box) bool { if pi.ExpandRight(bo) { return true } return pi.ExpandDown(bo) } // Expand tries to expand the box to the right and downwards, until it can't expand any more. // Returns true if the box was expanded at least once. func (pi *PixelImage) Expand(bo *Box) (expanded bool) { for { if !pi.ExpandOnce(bo) { break } expanded = true } return } // singleHex returns a single digit hex number, as a string // the numbers are not rounded, just floored func singleHex(x byte) string { hex := strconv.FormatInt(int64(x), 16) if len(hex) < 2 { return "0" + hex // Prepend "0" if the hex representation is a single digit } return hex } // shortColorString returns a string representing a color on the short form "#000" func shortColorString(r, g, b byte) string { return "#" + singleHex(r) + singleHex(g) + singleHex(b) } // CoverBox creates rectangles in the SVG image, and also marks the pixels as covered // if pink is true, the rectangles will be pink // if optimizeColors is true, the color strings will be shortened (and quantized) func (pi *PixelImage) CoverBox(bo *Box, pink bool, optimizeColors bool) { // Draw the rectangle rect := pi.svgTag.AddRect(bo.x, bo.y, bo.w, bo.h) // Generate a fill color string var colorString string if pink { if optimizeColors { colorString = "#b38" } else { colorString = "#bb3388" } } else if optimizeColors { colorString = shortColorString(byte(bo.r), byte(bo.g), byte(bo.b)) } else { colorString = fmt.Sprintf("#%02x%02x%02x", bo.r, bo.g, bo.b) } // Set the fill color rect.Fill(colorString) // Mark all covered pixels in the PixelImage for y := bo.y; y < (bo.y + bo.h); y++ { for x := bo.x; x < (bo.x + bo.w); x++ { pi.pixels[y*pi.w+x].covered = true } } } png2svg-1.7.0/cmd/000077500000000000000000000000001504213346300136515ustar00rootroot00000000000000png2svg-1.7.0/cmd/png2svg/000077500000000000000000000000001504213346300152375ustar00rootroot00000000000000png2svg-1.7.0/cmd/png2svg/main.go000066400000000000000000000117401504213346300165150ustar00rootroot00000000000000// Package main is the main package for the png2svg utility package main import ( "errors" "fmt" "os" "github.com/urfave/cli/v2" "github.com/xyproto/palgen" "github.com/xyproto/png2svg" ) // Config contains the results of parsing the flags and arguments type Config struct { inputFilename string outputFilename string colorOptimize bool colorPink bool limit bool quantize bool singlePixelRectangles bool verbose bool version bool palReduction int } func main() { var config Config app := &cli.App{ Name: "png2svg", Usage: "Convert PNG images to SVG format", Flags: []cli.Flag{ &cli.StringFlag{ Name: "o", Value: "-", Usage: "SVG output filename", Destination: &config.outputFilename, }, &cli.BoolFlag{ Name: "p", Usage: "use only single pixel rectangles", Destination: &config.singlePixelRectangles, }, &cli.BoolFlag{ Name: "c", Usage: "color expanded rectangles pink", Destination: &config.colorPink, }, &cli.BoolFlag{ Name: "v", Usage: "verbose", Destination: &config.verbose, }, &cli.BoolFlag{ Name: "version, V", Usage: "print the version", Aliases: []string{"V"}, Destination: &config.version, }, &cli.BoolFlag{ Name: "l", Usage: "limit colors to a maximum of 4096 (#abcdef -> #ace)", Destination: &config.limit, }, &cli.BoolFlag{ Name: "q", Usage: "deprecated (same as -l)", Destination: &config.quantize, }, &cli.BoolFlag{ Name: "z", Usage: "deprecated (same as -l)", Destination: &config.colorOptimize, }, &cli.IntFlag{ Name: "n", Value: 0, Usage: "reduce the palette to N colors", Destination: &config.palReduction, }, }, Action: func(c *cli.Context) error { if c.Bool("version") { fmt.Println(png2svg.VersionString) return nil } config.limit = config.limit || config.quantize || config.colorOptimize if config.colorPink { config.singlePixelRectangles = false } if c.Args().Len() == 0 { return errors.New("an input PNG filename is required") } config.inputFilename = c.Args().First() return Run(&config) }, } err := app.Run(os.Args) if err != nil { fmt.Fprintf(os.Stderr, "error: %s\n", err.Error()) os.Exit(1) } } // Run performs the user-selected operations func Run(c *Config) error { var ( box *png2svg.Box x, y int expanded bool lastx, lasty int lastLine int // one message per line / y coordinate done bool ) img, err := png2svg.ReadPNG(c.inputFilename, c.verbose) if err != nil { return err } if c.palReduction > 0 { img, err = palgen.Reduce(img, c.palReduction) if err != nil { return fmt.Errorf("could not reduce the palette of the given image to a maximum of %d colors", c.palReduction) } } height := img.Bounds().Max.Y - img.Bounds().Min.Y pi := png2svg.NewPixelImage(img, c.verbose) pi.SetColorOptimize(c.limit) percentage := 0 lastPercentage := 0 if !c.singlePixelRectangles { if c.verbose { fmt.Print("Placing rectangles... 0%") } // Cover pixels by creating expanding rectangles, as long as there are uncovered pixels for !c.singlePixelRectangles && !done { // Select the first uncovered pixel, searching from the given coordinate x, y = pi.FirstUncovered(lastx, lasty) if c.verbose && y != lastLine { lastPercentage = percentage percentage = int((float64(y) / float64(height)) * 100.0) png2svg.Erase(len(fmt.Sprintf("%d%%", lastPercentage))) fmt.Printf("%d%%", percentage) lastLine = y } // Create a box at that location box = pi.CreateBox(x, y) // Expand the box to the right and downwards, until it can not expand anymore expanded = pi.Expand(box) // Use the expanded box. Color pink if it is > 1x1, and colorPink is true pi.CoverBox(box, expanded && c.colorPink, c.limit) // Check if we are done, searching from the current x,y done = pi.Done(x, y) } if c.verbose { png2svg.Erase(len(fmt.Sprintf("%d%%", lastPercentage))) fmt.Println("100%") } } if c.singlePixelRectangles { // Cover all remaining pixels with rectangles of size 1x1 if c.verbose { percentage = 0 lastPercentage = 0 fmt.Print("Placing 1x1 rectangles... 0%") pi.CoverAllPixelsCallback(func(currentIndex, totalLength int) { lastPercentage = percentage percentage = int((float64(currentIndex) / float64(totalLength)) * 100.0) png2svg.Erase(len(fmt.Sprintf("%d%%", lastPercentage))) fmt.Printf("%d%%", percentage) }, 1024) // update the status for every 1024 pixels (+ at the start and end) png2svg.Erase(len(fmt.Sprintf("%d%%", lastPercentage))) fmt.Println("100%") } else { pi.CoverAllPixels() } } // Write the SVG image to outputFilename return pi.WriteSVG(c.outputFilename) } png2svg-1.7.0/go.mod000066400000000000000000000010251504213346300142120ustar00rootroot00000000000000module github.com/xyproto/png2svg go 1.18 require ( github.com/urfave/cli/v2 v2.27.7 github.com/xyproto/palgen v1.6.1 github.com/xyproto/tinysvg v1.2.1 ) require ( github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/peterhellberg/gfx v0.0.0-20250602150231-2e41f5fad310 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect github.com/xyproto/burnpal v1.0.0 // indirect ) png2svg-1.7.0/go.sum000066400000000000000000000036131504213346300142440ustar00rootroot00000000000000github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/peterhellberg/gfx v0.0.0-20191011152831-b91a0de61948/go.mod h1:itlt/V2ABb7sDu42VXQHXmQ4KOdrEbFZDVIaHsRcwak= github.com/peterhellberg/gfx v0.0.0-20240717094052-4fa835cea5a4/go.mod h1:ipo3f7y1+RHNR32fj+4TETou6OF4VXvJBhb9rwncKuw= github.com/peterhellberg/gfx v0.0.0-20250602150231-2e41f5fad310 h1:GYpS3eYKhZmxRlKKAMaSRM/V+L6IPFlYd+IB/kc7TmU= github.com/peterhellberg/gfx v0.0.0-20250602150231-2e41f5fad310/go.mod h1:ipo3f7y1+RHNR32fj+4TETou6OF4VXvJBhb9rwncKuw= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/burnpal v1.0.0 h1:cwiMfJdQKRuTWbg6XjCjBNd7c0TLCpfbxLW5bQNzU+c= github.com/xyproto/burnpal v1.0.0/go.mod h1:j3S/tDDOEkIChz0sWDqO3zXOgIjz/6nhuc+JrmZNLnA= github.com/xyproto/palgen v1.6.1 h1:TmVtbNZ6VAPzTBxFDFm0Fxk5Xw+NVdnl1xmJuvzFHsI= github.com/xyproto/palgen v1.6.1/go.mod h1:HOIPtsPHOgpGTSLifWenL1Q6C+Sq/QoC8REct5J8OPI= github.com/xyproto/tinysvg v1.2.1 h1:rj2+8LwTYwSpi3elIKsWJd3fjNeFeso5GIUXCzbDWuU= github.com/xyproto/tinysvg v1.2.1/go.mod h1:DKgmaYuFIvJab9ug4nH4ZG356VtUaKXG2mUU07GIurs= png2svg-1.7.0/images.sh000077500000000000000000000010631504213346300147120ustar00rootroot00000000000000#!/bin/sh (cd cmd/png2svg; go build -mod=vendor -v) for x in 128 96 64 32 16 8 6 4 2; do cmd/png2svg/png2svg -n ${x} -v -l -o img/rainforest_${x}c.svg img/rainforest.png echo svgo svgo --multipass img/rainforest_${x}c.svg -o img/rainforest_${x}c_opt.svg done cmd/png2svg/png2svg -v -l -o img/rainforest4096.svg img/rainforest.png echo svgo svgo --multipass img/rainforest4096.svg -o img/rainforest_opt.svg cmd/png2svg/png2svg -v -l -o img/spaceships4096.svg img/spaceships.png echo svgo svgo --multipass img/spaceships4096.svg -o img/spaceships_opt.svg png2svg-1.7.0/img/000077500000000000000000000000001504213346300136625ustar00rootroot00000000000000png2svg-1.7.0/img/glenda.png000066400000000000000000000043371504213346300156310ustar00rootroot00000000000000PNG  IHDR@@.gAMA a cHRMz&u0`:pQ<bKGD̿ pHYs  tIME 62è0IIDATXÕWkl~sl&88M KaeBiJF55SѵTNbӤut$ukK(K ŹĎo9~Ď;z$_罜B`qONZۆDޚ8èZC 'Z4lckfnW~h?n[f6Y)+ͯ8ސe|ba^5'}o.-׷6 ٷ{}+gП8c͡_Z) 2u>w͙~CӜK?yJ D34ԼPޖKĞE+']7g82;j[.I]?"XdFkeG b[ؿj`ū=N%J M&бfNxzGϏ_kܹsU6]tWi}wu`UL=U:fV.ob̊5s҂`xR.%m7L EL`1{Ui J{: s6Ζopkyk4umWn`JSPe*Yu C-  m=Y̘"BM.//cWgtE Sqdu^.9|8mWZ@\ȫgpʚ"1ZLO{2}:xmG[jT%0wCoz/zjy+'SZig0oe2@3aG1 lY$5/A \M^:D#hₒJvm!-3L̽=O+CsU)ErNƛl+T Le񲾌TR Iw]x28 QZL`M"5ukQҁL!kq$a%=j&ElR"/\0Rf,)LZL k| ,Lh ,3.`K*) (DV-:A{LKzcz/4A -̆Y`{VU_ٓҍPJg#? IQJd-IәYKekc? m_~IMS!usM?%p lʀ - p4b:ԝyU%h=QU>FSh ԯ) nrxo504a괣GǗڑY£_H$5kغg|z_ in!lw 6=1]VK4:HS{oX xoda{M%ͧ xʻ I8+a`wTy#L*HB7{Z{>U'!fU[{rF&~e۝O.n0A ""{E}ٸ43S6? >`<6wѱkZH`j:=R; Ѯ&kgwۃpM0]:2zU8nwZmUjw+ Jdr{F}.j|-/ \heOgZ~jB> F?RtUn;png2svg-1.7.0/img/glenda_opt.svg000066400000000000000000000522311504213346300165220ustar00rootroot00000000000000png2svg-1.7.0/img/glenda_opt.svgz000066400000000000000000000072021504213346300167120ustar00rootroot00000000000000?aglenda_opt.svg\r8sYJrg]1`e^:l<$PV@_|y{~z~77~z˽v]w8^~ټӇ?_nϟ|9sNoƟ̓y1O[w?3{wd|^镻^o܍W;2˪Vx:)z]d>!1,0N~TKKXGtG෈@` lQ3^ 01p X׷f)0Իh! K߶`#WQ4^zH%ČLD 4CZ#PR0t PW$U ?_~ϧdPܞ_o0/li՟- HCc4E:+Z%539GNX=ӪupX9eJc_ب#@=\5v؊s+O@13tVlZ#@3њ1~v"zX@@]UD) 4b $IɁ1ȲPl!=x2d-)B0͜^-[QMAZ14)V-9#76(M[@y83J9Tv]J{LqZPŦUG9wp|Vh vfPG)UyҤMA4p!P {2Ɣ$ULc[!Uh)t | A/{RIe0K~UKQPSЍ{~(k#_(" hwv[0ڗXHj=A&oƍAPM0[hI)$1v5Mt!eKuXV## 8G`I]Ni.[ B>5L-*"xL~ 3ӆE <P lSQ^ 9GacS0XfZb~y0dtSoC!cI#bv,$lQc[?~DcX_0TvUc0/m}`g#:KCI_ Q.zh+Jm 2gkAD_L2]P12CE24]Րw|7B:Knt-E5 zOm`m쨨e*h(zKKJmWfHI,̊%| f0f!K!YNڀ4פ:iC~xx(󄾊S)CX^ٌ=u':]8 dC;ӧOd/}qw\!^kie<<𻗼)S1p2üb-/ҳ>c۽ dE\̼풟ݠZWV7APP^2MY &LAO$Zd4ڽ2b `zAɼFGvALY|yn'RUZRM#Mc2~V?P{=DrwULǓb#Έ >Ȯ0,OOǡh3tR4JQ2R H 6DEES' i%諊kEήX95+rHz]-+ ]aP(ZgZ`2Uݢw&d(B6Zƃl(G7QsMfW##Jq#˸oW l ].ddBBɇUZ>ːFLYI憝 $i ~3}>8\'*.mcV'eYbWͨv__ݶ>#!>jNAwe̝ ٝlNoB^G ΒCp@cr|Gp 'e^.xaS5e ggAտx+hPIh9n䋶'ԾL%6ZYii[$ }[i?wc/%o*gv4zb7^tcAv2Ѕ~r>+DۋF[G1{h hus̵HɊD@_J'S WSوcR͚'jT&lr&{bpLk#uD5-ϔWգZt` : (SPw FMaޮ&Ö, }DmPlk-{Mu;YYc.Uqܾ0F [%䪂Y[< #@CT 7Cz(4i Is,po 4;XLfh2iӧŋz=$#+#xۼ`wu%CB^ofؽ5" d\Tx%Vb~C t=ܵ##'#T՜@j4{<5ڥf;e2 [qO[wkA͑E]#w/^r.ZJ*+)'7S=Vc)H28E+= Vzٷ[B{Hwnn1'=xywj݇P>LPN'oZw۩3¯;c}4IOӱnMIi{fpzzaHUxImQ60 ]) {Ѕfұ1V(=uUym;zk"K@̸k,B7'h"1Zhw_g䘞Џ łp7,\0J!7 6*Vt#Ug)(P1{7^?jqaWeV0ᚂ9+ \2-YLTz?|Po>vg/'RjA;ĉli&Ch؇<됅kǯ \ic,sjc@<) n9n%hꖗTG(izn;Yi ~dC_Ax*h m"z:b>P){.,<34P5>oTPDU4;jjOnxCC8F -Lj؎f=ihAѮO%Q{s{*-_8l Peunz gH\̾T\ff' ͷzܰSNˇ8l;Zr yJViwPSt_(!"m  ~gۥ:?"/A/Mꨁ; BP&Tpng2svg-1.7.0/img/glenda_pink.svg000066400000000000000000002116211504213346300166610ustar00rootroot00000000000000png2svg-1.7.0/img/glenda_singlepixel.svg000066400000000000000000005147241504213346300202550ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest.png000066400000000000000000005253561504213346300165640ustar00rootroot00000000000000PNG  IHDR.1agAMA a cHRMz&u0`:pQ<bKGD pHYs.#.#x?vIDATxLweiyss:WГ E  $d>YlJ-A@&2y{s;V[Zj󜽟gg?o]_=~`xt{q|'%c~t< fKj+UrQBb;Mv`! Ϳ׾?by߇>l9}@V\!*yE*!29oj|7o쒳ߵm hssJ###,wq{]oܚ@lsDp>r``]_B4ԃb`j9CYL:{7n\e dl{W 1'OZXXv˙}SKKs"^,rBl6+rEq30ƜR$mrb0 A A`.'%f22>gpee}BnXwj$SB>x왥93ihGwufZ{)"M9w?##CccoluiAa%IʲLD)e@@ + H  iLuYuLa+(T nO"|N(0Lt $UMDQAF(u8}@ns=_;o~odEBALc+EEQvJJiq2?t*$Z~K!GA`H$IB;$kBJ2 ׵eY 0 @,Qf2F1Q*)"GqhZ1,#iyÏ7a7ZV0c߶ٙ客ã,˚R]1~&$Ir^Rywfgx'ч?~U֟}9}}BVQ=;)]__W-oݼy+gۍ3DQYZX416y ZmL) :<4;~ɵZ=yWm]IY˕_HL8}ǝY\\~^QmGab,=$F FFMtqa+ƅ&+D"?=("ɜ 48}sݻkY$aEQ(`~jϔ=϶)&o5{BEC z;yu京{s8Ռk5krL{$[DU[mGaI΁eˆ]u٬ߞY4QUc{YE\ 8b!4p% "YSJyw8A@GQxxݧ"3CwTSD 0bbD %,~\,qNMu+ժ$K/ғOhv~\,40lZ1P "+t-J"."LsG})J)cinF&q΄J0tLc[QQ&aHDR8 "$ 3FeIZ^CY%QZ)q{r+^tenen9a?V$e)v=7W*' ?qa]'U'_?~?3~#._,rՁ˵e[BvGt337j_zs;.EQIa$mvcG;zrmu k**cLSBᡫW> SSSišMKV/]o~?77?44Ҳ„ Z4q\XX?:.,:PT偀QreIdY,"BBضq DɊ<<2$!– $5["cn@W%ϭnwd|nBݻN4Rڕ7/`!]ÙL̩{;_[P͚vj5}?\ִHc/f}2tǞn64@JDv߾1AiJ%$D*8~W|; O>]ccc?|<[bbǜqѓ$igຢzgF?~h=vG>8;NW[!!\ ÐbA(<ϛ@!MSlfrAY&I6a Γ$2@lH8?~ܨ۷fW`Ev))Llxvi[^׻f$(Wfa*QݽollAE_5I??}S O>ıSGb6͋nolw擓S[ތrtDKKnrաFsq䱳gOyf=Xi4ZKKIaTQT \Gr1 W>7n\XRz Wy^$Bt~F' IbYVSd2>Q4u4L\ǃJ"p]\0*8 X.j.no7q\ѵjtnr6r`|,$B#CbD|g]0?;=txuvh`A(ۥЩ;TTe~~i ]um!hqimWE.XE}ɛn$it!v^&c$I& !(4M{vcߑCZ-6ukb׃#%o@b!$۷$Up||=>x,KatUlRZ*?=bۯr_P+eʩ$Q%I,ɘ4 Yr] Us?/=#L=GPT.WCKDf,E|6Xn&k0d59i&AUS!aI"4al6k\&j7mu꺩7jfV6ѡH [va)f;o߼K/|9[_]sm;(4{Y*;% _ԩ'_|4Ru\OsBDJy#!}ęӇ:vPm8kfU(f3+K@poy_|f{kձMNArR*\nw3KK zvz)ػQxVc=]<<݈ MFBa'55~>M 缘Z&R`$u{axQIX&W0>CGN7 2P]?zo/_jvlAI2Cܻ{d&A,WCc붛8I̙;r-C1BX$@8g8N cL(@4-ۊ``j8}̉cdBCs={wя[_iLzy+T9lΟ>tk[[lwW:VR\a(J\\oo?BOOOg,Q}kkϽZu]<ii2T)l5V0P{vODzŗ!C#VkQQ(W?hA$Gq!@ Hس]$jlǎr<[/ )ceQJGU1Ɛ ae, ju# xbAm 0"?idڍ֡CK{vTU!߬o5uU&&Ɗ̳?'hMVŋ'Ϋ2Ͻhurw扣i;vP?o><.B`AU6Lt{~3ѕ7.]>>vġ4ac5ϣZZ*5MѪnI0AKD1TSj6uͰzȷo[B\׵@m3*T߳l'ɄA1~ /s=wyr04C0*H"RY\ nCua0t??[ol533f6`[V R v7 ]$\Pq@i"+R/76؞B)3lP!躭 j3`1q/c4 Oic6P;[kx{_yWHXVϵWo^bX.R.u [kfE"^r-g~tƵe2ȱšf۲|\.Kʏ\6{a稪[@lTUUV~JDm6k[JmiijgEad$L֠B@YBqd%4. p>66L$xyÇ>$[=Ι&Htq΅4M Bq뺞$ Gu[IT,wūl{c>4EW/]4Q)4z}Qwyڵ[AZ`6S^P 2!|H+W}R5תCVWL>eշ3ŋ]kk{HX! BƆwNO;+_z}koA3ԵmeoiZ6Qٷ~&n EVjپcE(eA4UQAXV޳>{z EffKR><\Rdч/V#DVdg>K3!M)0SEP`U*rr6)r6cI.㉄E_vc2 ]yl&ZRdB.G$*Jߛm}E&" =LFZ4ŧݎe{ٿP\zmdqW( UP$9$w}BEU TFGG\$;G[ $ʕn~oA^o(:=ydFv͕zPE z]8ok~{wO~˪<7XX\[^R59MYc몤FZe0}?a"EqBϘY@SI'i;|_{^VWFG+rNslm$IٙRh3Ka@n5UeJc$PJ B$1&HIbL$IDѡTyױVzZ]f`xj_<(dk"tK[Y}"ci{w{}ާ7ԪcJu{{#|}/糙ԮwuoR͘}˖| ha~!3Ϙzr&8(r\T{?K:~tTUwV~ϝ[l+oX\_߾xiױ%Ii0#s+uYUL) &[)eE"IJ,MU$ٶ_,` Cw]zC$qz={Ͻ\! "@>HXE``Gۛ׮]k+X4lm`矾={3mշ׷>666)1$1 j5#nss*I6P}\ibrrj|szNKe`Xc~v[K++[M{cg=۶7uZ+./H0Læmiط-?ǎT3"߉ow}0 a @ͭϽ&l߾igg'mA6c)e)/ V*FQo6eE%B!!D9 %+vܑ??'>y`߾vY)dEu;ϔJ{dqi[[[k_ h{{B( ccc (BX\!8or@%@3oߚ=q'}unW,8oݪo;JF38C$]Q& ;s[K  $Wױ}?3fZ'> G{ϻ۷oBr3c_xoݚ6㩩IKVS(0vۡ4<:2Z/-oVkwkLcihpo*.ƀsAWWAˊ}{hh@UB.[}vwgppqɩC+|c}2>cG?4eH8^<7vVssANޱѴ޺X_YY= Ҝ,M}OT dEXU(agL֤%I *Jv˜~1XȷB.[^z᥯}W/]?fnI6ȃ^U\ﴺ `4I]^2J2$iЙuM#| HSJ)-ionmm;*﫪J#@E&iJݨؿo|Ds}55'''-!˗׶h4BgO`hxW._ @TRP(?9B9!R$I€HTQ퀥[V 0 ˅ÇzcKbLȋ0NBRMi G1L#$0$ 9MSe,=x`m[>˟>uzX 0JifRX|)QU$I~{}*LV<گ}bWnCp>Y-sZ5߮4 fUkJy$aYV∪< b!世*V?;;.8vh6-Jׯ__[[wm٦a2_{JZ(O/__fy=r٫g)D4Ui#mv{)tnnMN.ҫ%Eg>[ oj5ʕm<7??XXjs/lk2gLաibJ7^)v oվ%TYO=G%q,$4&8⾗ w%oVױB8$+ajriM~܋Ft}j(V +4B WUMtyQڴdeIo7(1)KO) @vKUU%XŠ@4Av ! CWtlZ3EWO(BGn|')=ͱ°o4Mx4A?~vtlsW@ 9IJ)q@( 0F;0BPaŧi4.^%I&|6|s q p )Ad|qFRΰ$-Ub1jR?}Bd" a,aṶۯ{tVH ޺ko'&v^lYa "N~M01:=ҕT*L8k_7G4Cq]@%BR)II@0HX&xa~~rb4 ]KbTe޳8~׾k~~xsum}߇?󊬚/7 E"FRʢ ߐU͍zc+/%i<3sS[ox?skfnZ\,-/+W/Jio\76Jf'#B<,.7[u<81]'|k=ū;v+q9<5MD`T*H|#!iHєRtqlXEQ8#*PUcL$@afM0W#xO) !)EF)Pp^{//3} livs_uoSlFz&+8ժi/w6jL=? iűSTiwzE$I$XL0N_ڪ8#cLL3Ƣ$nno[vd\}v .WfJDP GqX,{yGܼu}u}Sk_v}||h[}꨺dKS200hjT)d2fz)'X!SdN'XYZyc8⡑ͭG}{;._ERX*mȒݎ"M8MS `td;i4Vnšj?c\.mr}:q< }],˲|X_?rxtfF0Z+) ں}ԩ㙌 ?|JZ,/.]t1N^Ϻλ(wԶ=`xwOBLL s7ٳwR_tkcG,Z(duPu@2&<[m Pah3٣Gx"(3#0M2DHa9%@^xMiIrc/["qIx$J0BD|)c{if.B t]1G4)CUU0ƄB$B$N3Fw1&IPWkMUgggd!jظkkvB8 S(eWj>WY] heyZ-Bӄ jj!7M=p]0VzArdUu<\c;`LUMY6v:3مW |sAd24ͧ411@QˊSU%I8N'GGir <" hc=< }KBk5zZjw[m$qgs=wɊA0Fu~̢'NR|CrH!RȊL丞Μg6Fcv7.ʶ4 T3FA̧.׷7?Td)J8 %J$~m XTIf,vn_} rYRAr'~sߚ[` BD[_pg޷wZ|xL5E,zkj3w}Kxeeqhk҅&xC=dd3_R`􄦻Guѣ=[^h)ݻgT0e$߳ҋk+7U "8"'^P$( >sMCVۛ ̬4hbrpࡓGLcya0nZRB8/8bY )@X-niPTs9(=QAX,8c!a]xS!8%`) N#!B c=rdl|kvGS5 L( e"syq.|rdM`xW^|?|Y~…^S uMsH0N0aݾ={fR2& 04M)gQb(ax_Y^t~\vqYכJ1Zܷ-Lݬʅ[\\P$sm۽^?#q$2ڭyn躁WU(vX#c;팙IӘs!BS|/D @=vƭo|ovۿV+N#uҘ5Zf599_}{~~w 8"e%IhXpoaf:P pݰmuh4IUmםصk|lb~aQHKkngem'~p676{$J(*Q麒鲂^};ޅ FZ~J}~>1}p"S15>T`r{zƔߪoaF6 h<>ɹu='&'^ofMIU(x4UIC{ǻG};=So^<{ffaqa٬<<<xͫu;pK?8z`cY-ݶ{)w#%qT(¨I)fs>}cxМ޿る斬(V{{m}_f. À%[roJG7?8!'ƫg'.66֚棏>AZ0LF2$GFN:hhCl>#Ɂ|A.5##g~JS.8# Td AbNF pmw}CC?$)r\*|[k녜ƫK7oW/,-^+3s{p2ju#a;^r4= " RJ`%ʄ` cL1i$dY" !` %IX};IdIȶa"4jm5fJQlՍf}ߧɊ{nqlphJDKd8Vߖ%5B?OnI c< +D$N8I($NӴX*^z…/? QT]V(L"N.\z77 _gK@=={TU@HDSt9o5-Ωi w$5 #0Q溮n說6 HpɔRvgONLȣ 啅z!O=CӇJà..-ݼyB! oklmn}'8zx˹‘鱡`)51!a (f3,=UN?6\iu-JM"(cfIucnyqZLO4X7t@kfPHdvv^z.]gfo/,qfHQ0UUJ##契Ǧ`gnPFjG}GObH&Vuм#8F~eE1<*d5PU+ (2UDsBq9@ XRvI sC{Г$H70w<|K/`h"7c5 aj=MQs٬iR׵9gǎVkNC10a42qJS37sFeINdg$ɘ#^h& $!Ig8KY $i6X5Y>{ @8v!'q4B+em޾Q-+|35YIo l7øY-mh*.唬i o4[BQ4,)TU?MS D?"{[ ߵ{]Kղ-+bZ\8W~ cP@Y((|A("b#('AڍNxwO.+x3W$r\o# 8 (M>xpol}{oV}Gƃݷ4V{'~ݍ͵oEs^ =K׈IM&MRqDdBB?tjQg,MU[[V){!eײdU1 `.cW.]fi)j7nD:r "x.D32F߬j"--c vM8}L " $k&lN=PyqˋONL2kAuh,aތ BvݝVmGƚ$˜ 1`BᜦR8 BTjr3C㣣z]dAPXi җ~'@s##CC#<}ks>Og҈*D}fRf3vGYol7-BVUUp CP*aS]79`P^.aB BB4>>X]] \vP(` 8a |1Jiq#Dlۅ$Q*8Ȓjc{^j*kcc.--ZNZ/\x>޽KIRrQmoo/-/--v0v[e gϞ>}F"  芎1,X}cotZ/b>[HYoDA/J軷o~ླO~xz=48,ݱtsN ]>|h]<ؑۿ4nmDuԑJ5nV'VEAX |KBJ1362Ę,nW*5qZid>JƱ뮮' UUy```kcs{{Ee<+?fYBv?1>ގv]>;i5[Vc d0VH( |D[Utv Dh'G;{Ԟwu(c1Norjv;E P{jTæ@D~w5#sZ.zmNTEc BD&"C S(DHr5t Iiv4ͺ9=woeM󥧟z믾V*`Bk_O\ŷWfh\~}bշ!4L $ɹc43c*@U0 $EQ&ixIK\eY MNNʲ| ƨDPאָ+an膦Ll+&)CV4*I2sH^H)P"A'R*훳7_}㵗_{套_S??~W^_Xl6ڀQ1RŢhwW~W/>cv V$麱##!eY"Է|x›þnիW$ (ҵOqR'?۷o_v3؎ 2v=>6zp_)ʟ &'F7={t/P4UQ(s|& èZ- @}_?<}XS0=7\0!rƸ3N$I0c{g҄ۻ¹qlkW[rqkKxEID<5ƕr^>_~qee^I QGB,$jBΐMT:53$ٛ7fmuMR5sbӃHƊXf};W(#,~Xov$ŒbdLV ݋Vڞh4MӬ7ƌl.\1 j̙wժb1{G0u{ҴTLNkJ&Mk`:=6Qҗj78v5=pia{j7f=qrpj LSO=~ աZU:0Pr$4]l;AIbHZ[i:k/T믾<=cV{Z[464ѵ{8nזo,mnl@m[IDUE3تwzQ2i8 grb0/ZGle58Tf2&GDMIqrڵ@>g{d٬kc |?c}O@J@d674_?in>5z6Q*q8Naᙻeon[D gr#{ XV-o,ݚʔ;}س(0&s  RudIO~#\f 18`B@9)BDq[*?_? ?x 3^HP691Q 34lvPћͦk{s]QeL5gY\"s.IEv5MBELi!tc1F|RJ#?Ѵtp)>'|ч|衇̭b1EcrDML$ȤiE C ۶q rA9vP( &,f6@`R(0SwyW.rREur ErvX@!+g{߿|cK$db;c=s?^u EJ6VZ"ٙaԉzcƖBC{ ;,rG5ImO\xYn[R1Ҍ0Q\ڞa5#w$$ۖ2˕UU ܈vZ*)}!:E,j"x s&Fyə{ΘVN =](GNn6r8NFQBDBYq7QolL(Kf8VϖeմbJvrrH)(B4A1OTZ[ݎܭN;0n;嫛k k2"!0!A8wVwsssbb?D92@%"R =6Zm\}{ Bc_Z;;51b,(BiDR% ۶B$1ƒ8'+ǎm\piEyGQEQ0 w#44m6GF>ȑC]SiLk Aiĝf')(Y31N@mN) w6m6 ˚cahrڗ<{_W/;1M}vK˵kkrʕ'?񱧞~iML [Hnb2>>:P~BIp9J0kDpmp``p``Jergx32iۖe8ADIrB1"II&TUUEQhAȊB( TMժj5NbR1숡"H"0vbz=k1"QQ !4MBt]{y'gfol6#YUMM4I) MR& Nba;BjBB,j@޳W1qIV<<=MSa~9PUtI94I!A@B; ۢ*b' !P%c4eQUJ Dd^yɇWS|ڭƥu]M=Mo}[Ǖjq B, sC&"0\_ΚCN:(\p򙬤Qʽ'%Y dʁ6iCNgc"a]/*O/,lOY[XZuw:SƂ!ԬÇQ|'4 scspfZM{pt~`шض=TF)Dlhx=,`%cwyW\Ҍ „H{Q䙽E@_HRX巷mFcvڝvJ%=gS$@!*(bR.S'Ͻ13Է[V;4 Rs_ƸSׯ_v;W^eGAI) %1P$I d2Q)fFk9 `Izkkk+c&&­fPtQJ`$B@ z(!i;b@̲Vwpn_lnn-.כ۝Ngf~n}/.m˺:2-{{w[5ճy}r|4U]`& 9w׮n,hR.( IlvSԲ.(eѸnkDtU }I^os ;s\- "$˩I^8^ZV{rr\*anFQ$gLȑîki@X2YNR$q=˥`֭,8ڽ} ܷ])`qqJlu5̼B9AHĩF~\u<2Z u"9h̲zcS$wl`ftD@pcݳ>K~'tqb1 wPD4IQa!F/_m"J9B$v2x,qHv; 8 G ޺=u{4)!@J` 'IJS \.S*)Rťr9˅qB)l; O!INJȲQ$Z<61h6;w3non5M_X2.]֛Lp]3QA%ISD R0d=0"ZnP͍ͅٹvk,K@ q(@NC;"DTB@" "l.>cL88g&/./ml C}^"X]Z^c)Dmk<+):=Iʲ:2:wN#*B È2.MUT (4MRb)>r+`XF1T 3 תl^^y~V|rzsw,v>:6Pȭ-.]39Q@CIQԌlr:<2ͫ uwqz͆ch1BCG`;D9z1e8+r;oV.X ES5]W%PB #Pr B4͛ lV2ftzB"yq% ӄaDTpF)\PJ*s8Q S8`Lp8#EEiȲEQx\U)&a$`3.8Hy 19p8go" iLOO{al,frB3T]U5fj0"3wƕAU!QvD* iJi1&8ƙlB%  H#C4u] eY 1@|;Vv!pGiwBt{{6X ڵK&qNoݾz~oGo(5+H@#VՊYCQ!I[H{O2dk,-F.h Vc=_ZkPyHTQ4xoS߼jluaFaɏi4jJ47?gYAEa@$81:'q+.2sav}E@P-z$tM%f @Ia`/OHs)Yf\Aٵk,N0Bt-qʢO}SN\L$I^<4XuzJŲUJ@"sM# dM5F6 WVb"c`ap4?|S2NW"8MDA(x,J5|MSdi~ 09njrUd 0Sr)Ir.R.(g;[|Gy[)|GdA ` , (lUov; )?-EhʒFaJt]YeYCIJcYuMɕK(D0"q )j-IR!\@v)m .mv ~;&?EM dחꍭW_{RUrN Z|jbVzՍtppCTA`jFT"][^>Ѓfn͕kH6{NG}ʙvȖ{}rȹX[ߊi21ܹL$)d CW^Bp q)M]kx *eZH>}K/96RIkdž8\gIy @0e)eM3300$XVXp/*j9VHcj:cq#cձ4WWWUߋ9",nuNy4Aء, TU\&CL/&9Sw$4Fg5eӈ#q@E2Q0)' H@N˜QJ)$0 Hǜ E)e Qnu8^2kT+th@AnMTUY7z;)KWW74MIdf7~rO# D ~R *rPCA rviw|0@6 !H0AHɒ,+kݣ l!kd E1z ABʲ04( kZT$#C3h|.@`FD0covp3ssݿ7ׯ_8zx6[ZY\HC:՚9'6Q!TJuNcsx w o^ysF7 A-P˖,JJ.oQFItdlhGb2~1"f2mQKf72R) ˚vMnmo MDNJ!$BV$Yq!@@0;ː,M M!תL$2,Uc####~֛۲*r~GTjFa.rvj4e Dމ4e4! .1Bpq@>B,-(+K+PF`dk#qɄ!vtA@DRGÇ7;DJZƕBH $Eu;|P-W0I?{ @68L6m6B|v}rrرvaRԷxe"1]&,(bx269p:$ռ0/4Z;<:˃CB "1BEW{Q|݋˜k+KK ?~/!D=~mRrJjL~fO>m'rgueLulwyq%zz.F IC8ILg0۴Vs1+ffa~{7,lymQA|y#TM)&Fm޻?Guqz[q2,poѣGI BI5$a&\eQ_ev<.%WB5!s4r!a2F0@]阣8=\,,vAGZ9ҳӑ㙎!P7nlZIGQڿraiA/onn#e16 ömU>_׮t:$^xy=!4j~쬯/onv//(VZh:힝kWZxh( e1K)@bLA12PBx(Y:8Z\hJ%TE a9BY8?2s0C)5 㯩W4Mb@!WOhZ&#-(iox hm[@* "B[Qkh^$c㢰,B @)-{#!yĞ...J(ZJFRJ)&z&A+%h=yDEN)#E1R0ƚfDgY!V 1 qu[bYq ( sy!3w"fEn A³2,n/M_ϴXsBT"`Z&EQe BYJ>(MSq4Ek1Zk9F09F"??_]$ݯ{OӱjCn ! U&% ;w /y_jxtOCM AӚ3yUɓh ~y>s]\\,{csǶ(,ZdY'8eG띥_߾b>;}('0JzfUR?`pqkuu@VMqph4JeP)y@\鷿k;ח;%G(=GA zFqTbǗsmZ6H Uy{4?RΟ>>, ۴48wmsr9 v{>w,Xɸ??=@*gZiavi@dJ (W+ !J۶0Sːr"TJiY d@i,I  *Olx<,(86 4Mι:m0@ERaW `6B7Jryr`{N^$,׶M+RWӫ %BR& L3pk 1P9,nv\NF9P(/2 YT.{iYIld2Jsf0lPO"뺽^/Mze#TJ)W,B%8_\%{7۝oMUF@Bڸy~ !qEEye2D}~t ςj{pYxIeyƛGLJI}m5mvNN`s۲0A8'kkw:??ãݽkk+7__|֭N~o_^^fIV*;?[; ps4(=}(ZR)DqhliiIHl sV j=kuJˋ~;9>|٣zv}g4Fnܿmф`L ^xq"+?W쇟~T((s2NXR }/{e,ʡĽk µݽӅZIiy+F~뺎mYS0&YV <N㇟Rfi^7g\u.d³p~ٽ8;=] =ϲ,V1::=JRM UZh}D=Sϯ$K`&/QP*wƜ7|[ qy]ZX!r_J@$ mo`LVW4OPH5JԴM4bc[@"OLJ~aqJb"-+)$"l~ʹ]*m]DqdRC 9 .ƶ(={wx-^ e,-Z)ib)BGJq%fW1fYBѫ& Gc֊Ee<4P 8R@aQVp 4ULFAABG)P"$s(yi\YY P+!dTn;$ 'z%K30f r JKaBy8ϲb}cS#x~yQ*WL|bǾ_j4aPJm۹i1WYxE`l[>hssy{(H6Y|rvf;w,kT7qɹ9՗i0 ]rikylcs!g[oqrtF_;IpxypK_{E/ſ,bss3NRL{w.fyD?CBHj)YtlϜΦbh2R6PRmX 30'iiL#IDATN_y6L?/_||;? ߓJCqE^Z^MϴJB$Q4LԨy+@?Mgo\ ,τ`<__ܿʃ^qJ0SL;A8I3 lԵnl&B Ӷ"o5QV9%7޸~ƿ޽MQyZ'O|yoĉyHrb}}}o8C$]\h[6ÃC5BXJ8nZH ɥ.h>O<͒vhR`wwmWsbgH>z;Nvwg_xz7+1aյ51q(-%PH4 Zо8Z\vW?RBg/0 !yNZK)JضczyEVԸr|R4 bLdysԨ/,/=~n FNk0yO#ۍl[o^y(16#yZJԫ~7(ֶ6?'T߼ywɧOKw/g׵@<+ c~$gJ.N~2'GSϳ,LumTnyٮh׃( %e Iov{/>}G{k.-Dq  #E)c  Xi;mۆeDIfcZrsՎ]m4 Q "Oxxp"/0 ܶ,۲f0 l6M*c,R;S.VWN<ϕCxeVڬH%ajlZZ^RZ8''`niYJ*cR @QSZ&) ,6 +y0 *c8T?}Lz,gEۖq yUSDc4ιiB!TZ}? C%k-ZkFX DBԒ#TReA0ã<@QO)&^L@Mcݽ#|q dlc?⬳" QZ[Y}Y\s!^,-5776^X[naPUeŅw1D+hgϞ~[ tttB+ϋȇt_99EDZ*Kmۢ)e,7$Ԛ!gln/ɟگ~׿G}BAiGåggg`aobt¢ RF LNzʄ< ,P )cpmuB4K^=_yc2>{ʃ0&OΟloM3qAbĶL /"_ߺv+t6-4r)׷6fQYSVjQDeRէӔ0\YAV`x(C !RJ9Բr^I2!#TJI , aRj7݋mz"EnU+>oի4I(R*)eJ+j5!A,Z}giV`2l:u+ӓ[ov//?q=bJDPJ)1bdY&7JxL(@+/9/7քRq&XiZh%TY<V}ee`8(0NgׯLflLzv~ysv 5rY{]+Ϗo,A`x2m엇l:A-NN_K"'{vtT3,Saj?8ؽyZ'_cgw>}(e;ˣ7޸!T<˶ww/\Nr4 FA\y:6 yb9Fӱ !qV/UZ0mq n1Nko|}/B yW>yhkiS һw_o_.5R#9ϖp> KO0fq Q(-Q'u`t"0aI9H^n" -o{rK8v`6o|cc{0(1QZ\Zv+KyPEny! Y1dbdeDgbVeB(Q<0fԕ!a hߵkU?IY\>w ]5$Ϧq!a2RfURTHY[=[HJH]4 |2V}qFm`?4lDԒ޾o8NH(2{GS2┝bkq#fN)$'SZYY쳇jEPR [ ol6c̋"|\^nU*+U?';_MtqDi*TfA~/Q<'?4N;w?Z"à 8a8"ZV.GI65zAD&<}π0bQlܿvm!3,!F>88* ^Z[^핛ׯQF~u{YtfLpw/!A}|G?y㧷ԛՋaaTHϻzCd"n^B>~h2 ߿|vǟbk! 0(>96LyJuccs; fg7;fcs4?Vo}`P+hZv$Sv "q._Ks(.a 67;{{/z5Z2ni ,&~,0QYXYZSʥVRaHEE^_)W0R DlUjje3 0*:+ǗݾR@*E $[N)M+G5ƈRZ_ߣ˼x˟o{-F훯{~Es+UTdsZׄr(fk+Z8L,ÎienTe.%\,:::LJ,ed4///Oh4hkc4G=:=;Y(h(Z F!1%*hL"9 ˳$M !W^{z~rxbl`iձm|vl8mE5"+_gɣGq-FyQK'O,..S a%/N0 V8fHpzrz6E˭JȤhyA^YiYGmj: 훷Ly9Mex>?xǻ?}',+R%f_9EY.qpTh+\X†J%l>8%ؖ1(8?'I*"R(ز4ˊ׿F 0ăR7+U~R:,ot{Aw[7hcB?aO>~jJ/^Y6jw{{{%K"I4'J@l\ۺ}|c?(Q5"eDFQ)Hx8"To?oI`48`ƹ<;H0a0 ͆(Wr>\Z_Y=ztsIEI!6RU\ =.̭Ӭ!̲,sJi3hTk4I%Fm))cñ{um0Vr=Kcs)˾ W_Q+EcA, ̲,q<y FDh 2TkX^*BdYƑB(-2 #Th%y)R!"~%˲H6/4Ç ݳ Y5dZj ƕAQ)Eض{%dQd3Kex2MX`'/-svBO!Hxn=-/Ӭ(%<LFyn-$IoϞpγ"7L3Ͳr 8ρR`2-t'gJ%ΆeBAO>6{2P7 7ڋO(7|\-N$y[%Iퟟݼyxlxcܺs;VcKKK{/,<}f{/og Y\YV FRr\*sbHljuN(QQ4~<, k'<@^T+M{Imk H3Lz})a7y^h+2qAB1f,#Ӵ4ISF} /R>::FG?6}"iARJ'rmMy+Ւa` *dHΕ!.mۀhFȕP.LLӰ Bk(apx0&퍵|괡FBZTAe~u&A1NӴ(Mk]H N (yqkŖoI|8ov\M@q`WsnR5|2L11Jiln3BB:2)k̨r:So,1T*(JY]^%)w jEe/(I0ҦmT*(iDJŀ*כۯo@ʱǚ7[]lL#[ jJ)cb0\AZjBLg|ܸs~~D@.ZfFqwMbJ'''zZ1q=;Ä9È Jrv\[@Yl! iJ+(I#gǣ`q34JDXFި DONJ~ 80 +*4 a9w-,<ʺn@0Llii[~ koݽy>`̥iI!m*×~)ACѕc|P|ωREڲVm–[o/Vk6 4"0A!ױ eX̧a8ϳTJdRVa2Tc (#&cJ: ]v/"[^YY^[E[=^h:y(FBjι@p !^(!e QeqF0Eq|k۴2Fܒ<J\ &EϚgpEQ\LjANkWfpoj..:}fWӚ?^7߽G/wz`Q< UYx,ϳ;wo{~IiDݻܟ6Y.~{]>8>]_y7^?T$<8a^}Xj4q<쳇7n-+Ř`J1{qZ-&ǂ^Yqi,BćRj) _^7_X*힝P,^a\i\an|'`,%7 QhPjt29 X+o~7ܻQ%hUa0Q}EǶi%IQׅHjPViq*{gY<˃ʶl3 j .1"UU_]^Np<Ĭ"J!(Q)_F`F^,u4χkkͣgxa^ߕwnjԫ ſG5Z.gQVf*WW7#BFj~)hw*_./7r>zW?G2Y`\˕nJ]+zY )tQI,l ٤"rɈ5M",VA0K)e|iZ][j-JY?oּ"Y-iŎkUǵOO~n;'jk(mv,Bh%Wsa ҲlBhDI}EC$QRUjjA6ă;)jPCg(tҋ8&a-Ϸ$ܒ*Wl,x^YYäן(W^r(QpXERiJJ.dqqV1 #K8U~RRf r^y^T_|SDyJAT(f\YKS)[jB9WLf@a"RfXf+ y(1S0y2luqFJ A#8MӋ3!rR8mYf6Zi_)@fy%T.+% a;-a%c%a d ̹PJ jJ eTk-lkqqh @M CJr]+~xt$$0-!TaR Ԉs%Z%iX C1Hqa0VY$RFe2 AY 8 >lj}}reO7>zҨ6MЊBZ^qZh?iI6%4 5⢋0JB`8 d4dM1D,✣pmh5YQN{/zJZ5 zu{}{Y798g6/WjӼ[[ka̦hh'&$d'[ICP5ku^,+FɯQTĨ!jei9MRڽ8_-8ΆYnh0O1E^"޽s߰($S3BYT*Foqq1L#ykc劏 HP0̱M7Ɣ0j:ŴZ!jA(U䕘3LyMa8eƓyzegi""ZGA7`> B5M#LpEWPZpJeYvcg;ϧ#h&z>+NY*7LLfaB(!UB޺v>螆iÌbpd>k;/?}<=߳\S4L:j lRL@s lˢEDk!@ (yM4ЩV 컕rIbyyEJYdb|墼rf][X\֪D&Is@kɑVCJϳ~clxAq{Pw&ѮBH0rèRGt屈FQūUJmYPWmǪ5ܒ_{q8~VO^ f@)B\.3qvqy2 "͋"/reYӰϸҤ?;N S2 08;amm^tϰ<Ň/<@Q6LR*ղ}e;GGgJ+ .xaR7޿WqOfэN0"qX.5X/8I~a,K)5*Uk~O{c EBuwv*7n^=2syٽw[oS,3tMp8Z%ɢ4J 1hW͵JZ^a(M#.7VW؎OZjg˗=JJ@[yFY~ypf1Q_zw.ovzv8Rc xNR*)Rҵ}Yh@wn5m?b!yVnZVz q4ML:>=*92JٴMHiѭŅd,H,lJUd9ҐK `iB0@RJ'l:MJɃ ܶYdY !>0˳ %Y2M4)Vsg’PBxQhyu Pk cL4B"@ F2CU*W `Ji;1!r*5~ɴ ǵKcXY)0D1F2FMô 2X|. .JSLdB)@ r Z`'nw}\ F pެ߸q^9`Lσ2Jy4Lv+ɠݩij!y7.}!Eg]׎^ ])<){w~(y("u.Dn7W$yz~$_/;=  {/={Tv(q&i98R:׿$ƒCaX=NSg Hh)16,7L4Ͷ# fYN|EAGk;|9997OtEsRq}y|<~qAsj+)/NP$4̤Y^ud:;; RtťLǽghemUBg亏?8Mj՞M1^!if=N(**btLq0B@$I!KP2RQ-ץQ΢0)rP$Q#4LH`f4 sMX14ʊYEt H)5¼R*cPi&kàzrz2RkvK (Ke`F "T:@cTR_Xx>ejcjR3ɋS2FzWLFiYkix/.Qշ/./8gQ߽2I4M)+y1:Kֳqf!qc1'"`[ĦIkQY #EUYg6J)<Ɠp8VDQl50L,"5M$Eys;]~xydiuY H^ eISų$@is@-lNdKJ\0xmm8y\B4ϳ?7ujzAqL[j8"JC D@u.8kdĥ3N}T4OyT: %-/WUx~y< E^Fk 7c]u zG)$ah8 H,ӆ@o_6y~~%a43M0L*`zvojlt/zP*ARq-`ƀFKH$HA]~; $5M"DÄhs)\ b W+aX!W|\)saX<Biڎ5OMc48~ː".҈2 5R+%`L3l]_+9(W -"q89BSl^Pʮ_y.$/ Y>{r8&Io׳?l^HZ^vqEZoV!Q,O sk (cR*Q8C-g1ͥ%gS99T[tpCP|&_Ylk哣|:iVa3 lf1\MjDiY|ݻ7o,zgq3 BV"2J%H!XX"P 4qN޸쥩tlQ2J6< ;;NklO~bai\ȲTk0@j%`L92 bZ RZ@m1#³SE/!Rk ()Q h$s%87}_+--.bڶ-ˋPaVc VJK`a@ YT8qm(E^*WM)V[[FBKA^GʂM)r(I@ a|sN Ri1"A(VVI4,{2ZMLidbmuΝ0NW, {7onT.^ qYNxl.Hg0ʗ8Y5fA^Õ XZnK^4 QpstUfXV/'4T8}m}s'_M/Nz)ʍ{R$W#;(>powPO&q.670ɽIܙ K靝B6yHbjT$R:nɱ,Պ_+Y!DA<c,lxLihdHswto]ۡ=8Ŭ\w]cafٲ˗mM'i@fɔ/,>\)@,?YvW4 gls}?i<xaXD*aB%en@w^]][*Mw}y"xjuuΝ۟~aD`6_%ocsV0Lj0z "#R c[Z/yaPZKI ΁t;$QB \i@sR | E0*,)_?| ʬF] Ӱmc9B1S)~iAqȲVZH)RWRm;hbh8tLϱ|Fz HFТȥ(VB@(@!ZUJI-)]DZ Fe 0L)Vkn+\8JggsXld웖A{cJLt:xpkR Ck4'cNIiyZr˩ij ,t0i-ZYaƣ K_y5TiFWv<}<ܽO rW-=::N\(w 4y?iW׮୍ipWťko)>;9_E9xt;Z\eYV*WԴlI,Nrcc9 L"liFI cBXvnteϠh2 .Q+s(;i~Iܬw.To.f~lmoz6]%p6*k 0GQBI^8Kf}g??\_䣏-ì+h6o\g~S^QpЯV+kk!(,v# xqfZRl̲M"I% ! Bjyy+*%3RBk}łNZcaqキܶrGlmm |qƲ8fl_sz{eib)++KKKzn^a Q"J (x:3YEQ<JIFzZ)%$ PZ+ aj !w~7?yrp4pwvFqVcc^ B ߯Z5 hPE7(<ƽl>=ZZ0jJm;KK+y<O a<ȶ Kx)t.̓Y8h6a)ƃdoloƬ ݋ӳӜ1pVw΁l;nnJJFs̲,(a``İjFAg9Lj D~!J4MSeڔ2(Հ M {}EQ @KTJj AF*QH ߽stxxq9@L4$ "wnx0n+@Y:,3"d>KUt;YXKe.›;V2fòptLr*5i_8Z\\;9ȳ7ˆ R/Rf%lc9TaRb8.Xl4Z/o<x2e,+zTݪc<xcmTb#/4Tfg/'Ņei#hiM(cgb)J,q;;iW:}I&̉x<3 /ϻqC(NNdzIuA0xKwW^?{ѽ_y&O5qQF7?OQ|kM5PyƕḎʼnB1Ji4+qR,OƓRcyQ|щ!,J95GI 7_?;=<][(ri8$+Vb2̦s4)UQZk( ,3Ӥ)^dWb4 F&cq(NZ\ov 57ʕǟ>V6zpbt9ghg]BT:=^N_FCРnH~moG}vqֵM}mjӟy[[_?-R;;˵[g`{ZU&ĥM8˕x2~*9PEy°SFvfiu=|&ǿ̧/+KMAk7.//"__$FIBmǔٳ7n9p|OONR4o&N M`y0A-zٳE1Eǧ L;ynu!!(S. vVf"qc鿚v;LONo9`DlEvKؒ-d4։k%\.|0.ydM%u0 vu TyeeWht! ,$A[7ɓpq:-$<:;>FN FM&Iv޽ryxrd+Yoz5б+,4@9@ĵ:/3(!p"(*[kml[~z GexF6%( íο_Z?QI`Z*+ qO[o~#l=^6p\W3G;;{ So~5G琯OZlu//FzB nPPnw2Bki5ZNQHeaZ ]A0j%EfPPLUe(3`YTe)4sJiYf!eݹř3N\Ƿ$Y"B Rvj1yZh0Zc< |S!" EhRl2ɒ\keZZ\!p2M la=*=\cP1hcH2j$2]e2J˪*#2%Al$y^'M)_+~c6⍾8{ܓKĞ\~n^\|j^-jWU.,*w:o߸w??/%'}:N.{V/_}za O}z9<~2}fI/h'gg$ۿWtHlm\.nUÓ{{c6ϲA:L|?\m?|h//{F.u]uyJUUptu~z\.}?z?|xquFDso,Kqz|ܩ i~G7~gVtϏ 7#Q7!wL k|ke.7EAݼyk2Q$y\9_03;[`Bh8;;'[[Y`x>_0Aexf׉Vkii,eը׻p4Ҧik#ֺ(nREj6YF_q?q^F#jۋlFT뿚fzݩ7eo&+ JA^wzJn{mk1O`D5N:v< u~1Sr5NҥxX^q= Z::k~%U0M;I^b6 +u 'puE;u^Njzggo,\ϛ-H1–c6bR6f3M"ժGPk-ei:è^4Ƙת )%!cUc.//RJ8$53^xfzi]ggbsݼ\Jn!dQ%ͷq8?]gOno_O?OJQvXRaƨ\ˋ̓7hZMήVU}r|q2~fCZŗ8ŧ_=|_j*owW{{7՗7͆~cx4˧V5MEyxsl"^h 4yU.,]ۻ{0n}Ǐ/ϋ xa ټdY"n(w}m&+(_Κ7ӳ鴠s*?8}ȕVXgc(l?m[\^@F^)hzogo}! 㫉Z}VƤBq='(r14EF ܲ,.n /]+ hJ FTT( xyB`&X i@lx k~V,-s@!FrU6()b`{ujM\e1Rj缬0 Z`Jk:J G p<ʓS)nZYQrk+&~cS BZZ[!- E2Gwy{8b/@l@w~qBRj.bvvA軏<~_?jDA|ϳJ>@,}Ο=;k7?i7/>RVZ)CAQOE?E~J#Wy5Di$(UU/5#%V5XN0MB*Š_7YŢQ9!`<s9.7ڄUxjv>Zpovxtbgo)R~yxyU&IX.zp8b&1tEFx,shm]G5 /??=z~$N[jˤ(?&p{]!&;.)H* FU9>8׌~;cY[fՏ^^nxCIK!Lɚ ec=eԙVU.|r!_|qZy|[Ͼ>"8n?MWUi0N(A"(IDB@(X Yj6fip1͒TT] )*Bl1!0Ҙًk%iEe1JRI&A#iJJt[,-v'S׷zv1r y;2! k5B{R#P+]smvNUaD4FWHa:YGz0,0ւ{IE YRXP,d~w? 2ulUH|J҃on;Ngylyr֖H,w}ҀsXV,_f??5{_|ԎGvG + "?K{olV~Wv2q==j//b`pr:vwVl8ZDQTkŜPZoT_JJ˭ B)eI"Q0g.udL6kaUf! }(rM7A88ß/aoWpBk_?x;\o;A3NRcR2e6Ll2mYiN(h4\LCP[ZƛUl^Wy~q!$I]<˲t=VpyBX]X&4YK֦$Uy^Bǩ*hxJU@to5mfY=&WV# w0e;BDk[{||gdS!1RYSIc놆532V+$@8_HDԩ!$}NH& 0t:&* ǡjk{Ah,@ZJ q?GMZ?Gǧ {_g_}}+w7yzgUL9e.[$Ń N/0DX)RJqˏfŋϣ/⫢Q$I8jA/fy:m.Ak,+['~]fQyp}블8i*rQºb\Uf]U- R~4L2ƄVZBK{e)rS $ɥ(ғ˫H7mN/n]q1lǎ|𭷎^<|뽇gb 8>fQZ>}-T({QdEsIUNw_wO޽}3 ~O޵T-ˋINFEnBX48Y Hd~dmm*WFE+S ºvI5j) )EEQya,GQ;xr+Qʚ*ɲ:֕T@9h[7oՎ'δQw|fc }q,R*l6RkbD)% MrB44)x Alo vO~nzur|ѨhQ~%^goS/VJb^kmQϲ,MZ-(Kɨ8RbQ0NѬmrKEe⎫Ax!%R|>ī:=o$i(ˋXi|eB&2`4'sef3VERBBm TkP z'׮|1`4cZiL%Ͼ!rwxVeI-jf_z%'#mg]hU%AWZ *0Pi)5,˼TYzN eNu~G;ۻE?>kBhk6Gqx|~q~yYI?[_~xh ~s+uEZhу l=a@)Bx`Z;wx% t:Ф)"rח81a&rq[l_=բj+!u:6DrҎ,{Go@,.GOx1Hi:͐Wdl:j48ejʠJ){sxJ ~l6ZQWƊzuzvw^3sm͘[F%tV9M3*fܳpǣeY>~9bVgQk87q٤|QT&9sިDN{Ԫ l۽6lvwk=uz[L'ggFۭkHA[__Ty37&.U9gu]hxY}[ݧj+clz yl֍1^zIgCxu2@`!@1“Mge%6 yUcID)e8Ɛ Ajcc(E^Vv;oi=0=./֜skRFdY*!_+V͊i)XP,eY޽w@[x9g8:nDKJ JJ 1cZk je^710$nw~5mroqv3DflmYQl^7!0Vk e:RE)q9*+ }n#K/_ N?W޿.Ep~~:47t_^sVS<Rl. r_9t<<'3(Jii \^&(c)81$(rh49A1pim|p`ZO_VI8IaIbzp1~( ` RYXFv'˯ֳ]m2GJh.B1vnO~`NOXn9|Z/޻ktl9>ȏ?W2 k㽽=u9*9>yrՙUQAv:|vӋF_%Yվw|\dAs4+8B eǼ E#џ~O>5:^`pRo|ZVzg/N[ z}`eW "Hjӯ_Y@)|ZO+Yt:mh?] 5uzr?}{3ty~,|{wHV:ǢhwvfK7wZ~\"!\*@= !BYX$,0vwݘ{N"rqA~|Kdœ_ , E _kRu Z:L磢ܬE0X)z8^AJ֣f-^>cz5z\[s{`ΠfeTs~7erAUÓ|D>YI*[K iv Z~i4/RQy]>%HV"ϒ"MLwoݾUXTsga|8tNOO0ź(T8 a@6bctZ-E7~dZ))B* %@TT RB@DUqD R.,@QdE|7j͊r 0@Y `LTFk ppڈjXJh1S(ZijMBls- k|@mMӊQ qeh6] vkB(΁yê*$^]N|dzBiZ(e8|-˪^,h1VJ" !asf-TeU ,FDY̲ c! Kj xyVI$@7HyK&adzSu5:I׿aO7W7?{hڝj1lmy(/AθSJ{^:>oz{UL|ћw>vjX7~YT\/jut9jeM!".΃Ͽ8Ӎw-x8̒a*2@0rY^߬7^z&*Pr]n׮eάFzzлMI䬵^ jPc52b<5sV{@l\ۙ ea$PUjesV2m93mY`Ǘyne8aPIuUI+$-ͦ0MS@)RJ|'Ƙֶ,+Q) $`u4,4ui;Htq )f]TVyQf"af1Eu\yQ; g s[`ڪ AeEXl+-F7lt lWǩܺO'i ]|{/;z^(VaR:>hpTz>=ω!RQXGW`kq *PVv<{}0Iz x`E$eltG<]C6EY',++)d͌߅&RZRRj/&(0UUR ?m5^έf_}d |<?Ur!tn-3{(jWJcm;Z큙Aip0ף5q_j'|7ޠ\֠#& XPo"l5ً(&omךfʦnbQSU/uq։$,~Z0n+5%2`FɧʨNoք{xscHVռ|9i"L,&yfʍTJ̭Y"Mlt麠.ؿlz[*Y~Lu*Ft,JUrY9 b]H\N(赭/J*-ʲ2J碬jlAfiu8"ɒ1w]dU*$Rm绮Í*12"/Ů{;v7ߕY/|QDzo!!7޻Z =Y*?xN-ɓ͛o1ϟ_͙_44&8K7ALyw憍B`_^C iQZhju:gRQ0AsmMҢj*U vvYV&Vh58J \Ɋ3YǷn;9:":wN.Ik1Bv^/4 [\Wՠ3`^=))szsWβb[co 덦uz޺xxۇ娒|a>$zEdRt~dvcg614,fE4b5r8Np9`"WaV{{[M:r\J;ny5/ܺr<s X,by;;nupƽh0ϟg}vq~rzyGdy1L}'*b(+PV"d1* |Ye%4DfjQD4jzX.U)0&b#Ly֠M)b?t~l '㳋W”UUB+^fZ xUZFA u\dYðBj%B (Bdıy 1A!B1U')PȢ(#F+e4{)D`ֳ#cɠ~Y?8Z'd3vZx8^./^˙[WfѲg,$yj|w=RʵBEŜq!JJ&7nt$`Gk-ZJAyȪ*[Xi !|Qg6PJL6ŊQZDYdf QCKx$͵RZ溎 !BI*czݶ֊3__M_jgo;aR q,.OeQu\.V1(bYDDA^kmVy*Yp][q֠nB?ݛoprO>cl6owszv~t4FܹqUwo<'il-x &ѯ~YRWZd'ݛMe% A{WD:9=YW ap(Q J BNYV~rΪ0S㳣W'e4dԑfq޽[%`~{Vvw9ųn۹}CfUVA " S`z=0Te cZ 펔*K3#ԬGJ!0FfPk%sF5<-Qa F~aĢLu7MOmaFڽt>N.!wX+ҝv{:oU6ή]NRώsPk!~^4ee՚Zn/NPB,+TeYBk H BP7D! ~SN"AY&?hO&Fz[WJZJ9Z^]( }YҪ*Jǫ z"֫u~v5Yi`1#)e^dJ s5ZNoo8,+\(;Mi5X9'_<\2ZA+U'c+ @wgxa ^u"TIߺuaGy^ xz^?8;{juyY0Ji>uF(_gӫruhDEUfi'_~٩`ggp|vv51^v$'Gro ^aHp<{IN&QkZ99wm4'qf= "xٓ' H<rt4nbunxvj3RUr;@iANlvz9넸dŚc`t9q}_5N'+͆"L)[̗Ao`I"eBK94УX,t)<'j \7lzJ! Zt>NO裧׿;=:'q Bh,ZKB⺋2Ί1y(K%x]ezZ0nw\7k,cz.kW`$JqFEZTk5F@.beM=SfvzBU>:=;*wzj,cx2,fks9wYx͊OK)Z#y/.W˰㕔FVZ Mq #Z1=73FVeY`Xǣ +) i~?ϫJPBcZkq4{=`1(tzrPJ(B1EVKY 0$JmўJy9q=Fn঱dl͛@3:V]{{oum8\$^'u2yׯ (MxS<If5}RT"L5[o톾Wa`Zz"nݺ/>uVV( yl:DAԴԡb&{eˑ6Գ*nܼw|>_-WYrB)@1I;wmQU#fz;;ۻ TVJk 7/#6FJ!RZk8b [i?siR1⸎GaW%Yf!C?K3RRApL1"4uByBHJdNF3}`m! )w@ʢh6UYEQ0ZűcRRBJ)k"H[7]/0Ɣe@ k5ו*͊t=\fz945b m:Ȫ Z`8KB"(5޹{e,ZkqB0FP 0j7E5 `UUspBV h~$1@Mb+4g 6 )Ԏ90ͳ` "w B -@ QZX**/֨BkWFbL1BV4N93N8I$R70/^_)[~gqYB;^EǏ-q~q4K,jzN{&S}ֵFT;?uj>l4YVpϗM;ܣzGgF6U)!ؔZp-äVOWxgg0je2Jg/4Kbg_Y֠,W_=ͳl2fm1Fڵkׯ`w{W &ָV ps)nEl;61 Дlc2!˲>r]\ZFA_7Arc}W#\};it0X 8ZV!ƌ`R(qz}-t>B8,~3AMgS8@#d)2-֛@P(!BrDÜ8)ó$͖ޠeZ >{サ[g'f37d-2 JBiz[FK|-A\-c* ΝϞ=Ôe׿"!$ }%Xt8WUeU7FgYJ)aݫ hB)LY LhYJ!Ue(7nܸx-{SJBj4;zTj8I0Ei 5^gEj!z#IR)%ƄRJ ڼrqܓRv-Eh87/^|VSfqvuu64Q# ZRVEZ-?iJ1ڠwrR`Jލ^^ Zo67$+ie^Qt-w>}l2I6Y{`ň,Ji*4BE{־뻋 Z0 m6$Y]B(X!Ta©nu߽XE!|hi"`*_?y]Q ,T$&<7ܾ٫뵈n./ϖ`Z/ eBHsyYVw}{6VYmqRRJ!"(0%-kJ4,뗕 %izZT,)F֬0Jha³(#֖zsC*:h7޼,^'i+IӢ[ u]6LKj4],qbGfS§_O;tiVwn޼V+^Ӆ6Y.elD2oSΉ bQ7|Ul Eqh:yZ糬[=) ׁyџ?OWO ꕨ*U)!0AuYUN1ˬeYe #)! :\.ڭU ;+HHzFPRioꭠ 6;;=Яr8c >{d5"Ռ(vvjQ0֛|[0B2@[c>;65hJI.&R$r'mQl9Aq YA1b>CR(BuYYiU8ou̘Ii]ۛyB)BB2u]{m@bBĴAB L֡\H2𚆇if"lrΣ ?Ǡ߽˓tYBN} 2bx4)˪ "-ro) n7pg^<}7~˟zբlBS",M&!k)` #6ޜ.V9l1mvf$<[S^\ Ӫ;-XۋWb[F 1 .8ahR拢(Ȓ<)0Y<'RBȪ(9y_?؊HX:.ΓP:[w,WJθ50QQ^_˿uP7y'KρMZh\BlON;.&L\ ǡbk%}UVЦ%'SB1EEqgkǔb:]fI^P2jnMνH8yņ9OwYwlze^JjE3tsq5 /7}ɉu.k]|8rքh%+B@E)ҦiUB`#\ niy{hKԜ!`զ̫ E!hc!Q*e4FDJ5cH!`.ES8)"c$I0ZեVAV ⹬*3STY ~WqN&V~Ib5X.,1Ө)edi$j&MKFYnq)sz~A5\\y{g8{k_o>/NONR)FԈN;O/&R[+Yd A:K^ޝu?fNԜ< c \)!In- Aby]oEUbw׋Y8d>uAw;_nV`Z ;˫'gZm乐B`b(iZaDe. ,[{7z F6tzyލt{uqke,RΠ='7Irs),FE< Ԣ N'f@l'~fD֤LM歽Z蝝,ZTwWjX*ZM_TeA h6[WWCc "O˗S`N`_\VUu`z~q r9u\EnV+ T^kJd~sqɔS2v?gO7JrШxy&2F~qJ@Jfy7Ea! &Y!RVH`, P= F É`Ǩ(2[UeZ-r}gU Pɿ7!M4ap޽7./jv:{_y ߜOgERelUQHkm]Lg^WWBlJGGG,cgh̩7qV~=BGRۻZݝLǞoK@f{^ E,۬⪒UYYnȲ,k9YEZ 1B &RȖiYbnkM<jrx1: ʪ /_z[Y !R ݽmNxE! BVdoJM/6Vǿu\lGNp~KmAW^$M$"¨HܬҀ Ɔ9Qݨ>8a{{[VڤN>GWÛ7nA]O~y~fɬ[nFRi~~t:[/./bkdhJ)FW4);r3"o Y]#8X,6OQVe'XtyxwYz5hcVo>,W&Je!Mx8P)@)Yk!!`dQXθrUZ TErdrb]"B YWi\q0߇σkjemn퇷~'?ծx<8/uIRUrt9^V{ޠ^-VCi.'Rg!џAW[۵ ݰ(_Nƍ贚w786xݽ~QϟO_ty?6|V5 qAB`GY'yWBQJjfg(eV[B(A$IS/}+s9#`P|ji-Dq%qE&ցn4 0-1GE4bLLQegA8O<o\FG뢸#Fq̲jDvx>VT:ONN/wZ$^tF,/I30NՕ"J`*QXVn(7,Myo'|IUgG;ߧ͆{jZnGZa.{A.#͒|I1:<\;8I. WYЭF#yj%̲DdZ@ Z&7M(4+]O0R wy{YKn Ӌ۷-f,-uYVaDg458 TZKu//Jq%$Ƙq!z{`sJ.E*C,wz.<+RȀF|r[y.|7F#C7<) D w!K2](֔|yUk7 al{seUIlyݰXi$f['zG;,J`H4Ԅ !B{Q4xBRJxiBwRfl4ZhZcg @ZC#\),EA#leǗgȮ]`r4=sT[w1,2/޾yNLf7<==DXZLWb˳ӓl`4hBI臕[ݭ q?, \nj;ą*! ҕ )dݿkؘtD{޾{.sϥ~"^?OEO<Ϋ;߸1*۬;O|Szz7Eqz.o7o;;rn8MAl{LFV}g^6ڵkRt[2fԡAjŔ?_̒x3O1&`w-)dQ3UݚNFg/?_a %v:!@a&9׮}xQB钖9h4VV"˰YZ, -8~}hI- jE)M1zZ5EZd٨6M@j^'no8=?2RVt|j~lw "Sfo@x5po1?qُ*fE~Ӭ\e6Hn^DT1截|=r^3ΕFqZR*Ġ, -w;cf[Hx1,,ꫯz2U<`ЙNeQ-l2^@6eWMZxb5/I-y710I_LPJ5l~(k4#?rVgY)s%eZe#D?KsۖXwbs9|{9{Muc*VHJBh4`l0?ـ_ Բ;HjI$EX$+ުuars10砠<3H@aRm[E1,@ $-$V XYV;BgE[`G֚Vvxs׿nWgE=z|G?~^׽j/2䋒@N\0K[ڠ[[ w(7tE0 Ɠ禎\Qi j%%t1 Y^_4-Ia?䣏KJl~í,R/XNoԛ &n*Us͍S($E3Ҩʢ6Lu$p ӪW]l}c? ei]_l__M Vb_Dq2 %d iڠP]/m+yN\/}2Ua|n{n4JzoC/p]j˜twwa@ QQ,2E v[jY 7"[5fח8dcY`^W8}F;뫽~ϨTDEOy`Y#~H㐡ƋAZng<];]7/2CN`̒("9@$h(P<11B1 0te<X X][}| h/ܓyr/gn"@g58lTϮ/ίi&7&Q)zQﺏ77$FTR( <9oloևj5q$YJi ; qF6I"\UUR;<%ҩli'?A~xcm}'?Uhxt%ҲK\8st|ڨ p4\k|hD #'L,b_.NARE8'@"A&ת?(c\ \h Ir!JD ๑$I8 /p9`uuUdg90;6(pj['6hcK*ۿ#0er~vX-w޺h&hLG,gГ8WeB`B AFV5U$88;oKz @-6eYϢͅ*qV&@ $$HYN9 ŝw._w'pEQxf֬} Qn/'.9ւ(Bw1$K96iA8F` T(XBH2/3lIxg\.V֚2. -x[5^)6q^w}_]5{uvu޸ٯ_\L36M 0t.NWVXeiTM*JJpa}޿)_|f/?yN2\c̱䚦yx<)MA2-a;o OfJď$.l8/N'N,5PmDEtu=z/.m{᧟~",lpDy0ńRc6M2^ Fo<.孭Iy˥=_ʊ Y*rZYPZ,}|:)7v*FI|pExv` ,">p8 J%W0!Q))DjE d)8^ytYXVZC/ ]Y5dAJy6ɋU8q"/K$\:浪!pq'a&F^T+͗3pƞԌ^r..¤o׬ZF;zi v 7 I es=O4C,è," R3M,PeVu_*AIܽV"7%E4XH F8O//1+JYo{ҕtSJP.#D8 pw*I.Nf"PB/N[_ߛ}{Gl~Ad$gPtzcbaB X疥a*Ւiq{dzB{Պ5-$ơY.VJn "' =0G9[O>|7 pkVhG p6RX۞D 8J7'8rxmr"H7E% EC*QJ1Rʊ8L1&׽ȕje1Cec[mu iq$K]P4^˼@$ ]YYq?Fm8#U L$oԪ8,yݵx2S%dN}Y6lwq9o?x~#E~"I#$Ih*hlm] aWW##U(c&E1 p0J %d28Μa0t Sw: e *5E #Jr Ct/pa%EppÇ8|A..VR xʪ*rzܨcӒE 9 .gZ,I8VK(GaaW+FQevkխCɻ^ Ek't6t04au{v~>AQ\ժV[Tsgpc'ib9Nc*s.?rbJ%!I(zv}ˑۻa80'O߄![a94 fͫy軥R5Z@vMG#Td*lonmo{ 7탃eKξ(I^p,?B/j6_ )(^_<~+ENƍRYKg%y:a<L0a 9)M8 V*lIFj>x!fS'h0)H!r'x2Ԫ`@P A1ƖioSZgS*AV,O{dI8MqIZ$`d zyz!pD&kpB `T՛7j# ZXΟ>'Y^7$IT8az6M%*_˯|ջ<|Awzrr;=FyPLMn5+a $8Eua/v׶[zV:ӳs'V:_ xື)}^gJaQ!U򔁅TuLŁ*b@gK%dAU9Eoؒ$Ѯ5_RRArw{w1_`EƎvEWr^/EڋJɼw6%(\E7%Cp>?su 4Mk"N&΍FGhu*Y_ong:TjE]L8MG˜$qT||#`mcy0 =L2إ.歕a/"aLAAF ?ZI2ێb' @'y`e 0KLÃ7oxE0?L(sg/;͕.1gJu#U//[av2+HYuq<46Fl2ƳZyX@$bagW\1Ň;yγ~}O?,FOeWo-`.̦KC͍8;=;y?Y?|2.eQ` (ȂQ-T*bYNȋRjZfQGy\.A,ˮv<G{;nDaJ(@^U$ -LSo d (,0 0G((9BlTdY\[_g8v(Ɗ*/+Ž;N;~v'!YGOy9<$$0u jF(At" &A, 2SJWƯ5Ucoo[VoX<_?~sU2'(zGE)jJk=_^/w"@)Z6]olXV4u} kd,YuWATlmcu}{;N2ݮ hX i4r9ΪRw{j<]__^,[a<ϧydh&+hZUݽ}ì_1P=wow  /?/^k/B' % QWO̙g2mvZ]QrXכՕUS7$AyY96$0Mլ4%Uft]e9,0\@2ַ6JFҮMg7V>;f)o躌u]Fq\{wȿt" ,˅)Ϟ>%A\58Xh0u*YNA@B9Aw&-c>&bsݲTZRELӔJ"ٳ2Y4V+/?) ,,gV/^,(rokݓ rs\1N:8smﭫ:`p x8u2[-kqDKJM JmVg畲QeݽML$ yPw=<< 4Q#H  ũ,+I^k43Mt0tv@.O^xQ._|}| n֖e #K޽sxq(y!͒o~5 8= R^m5H9 .{XՕ~>:dm`z!x:т- RJ,qItzAd׵ \c8z{eE8I8 0Go^?{BVIx>G$o>0N+咥0-\˂A{n`sWi1ATRw_.&8t="3D_%ǂ f2\M߳}xR͖]MniWȰ.:W(uW[knah"˷nݨKEY.Qwy/4,(;#ʫ~(RTo8i4+6j+iV$qul f߽d6b[P Qbv-rUo7DU&{*j@*eZ/BPL,*=7kzɪ4ÁV2<[AgVp޹c"1L d.xֽ8^,kq'AR%iJ>eI*x̴Z-Iݝa՗ȓ˙¨LBèCFdYBH H)Uci"+˂ $6^aR8OeRo,1JY%oc˲oM jmg?Wd9 3{ ˲](Hz{ߙ-"핦:q~vGMI%ifgk+go^2- 8AvSYfvɔ{ɣ_}ӫl{A v(Μo+ۇ+(,}|]T,Mz+ |F%tgphUZo#Z-W^^ŤZ-]zwCcMSB{;ڵۇx_,~yyumw5sluV54NUUyeXpcrrt]WU=JxD8$P]ۢ, "d$͖k/[wgOH.pLYYϒTSE Ȣ J >^TYy*|U_mCGqt*IÂUHjA謷fh8WVρe-,0C \<#9'jU1,T0 SׇG3 I`\Ȳz0UZ |wmuu2&aN(KbY`(벪(BNAVengë2X[om@UG%k]]\;[Nnl2h})o(QYQ5hZn8ͣ(pIT,DǷu h&XuR1r!:Pu06v>i2)JE~L~kg,qm0@ܽ߾z<蟆6+Ip ͎_o<>Z`RJr< ̙Vmr1OrX&I^d@Nq L2+ $Y<ᒋkL$<[_]?zϰ8 #ɽVf5ǝei"'2 &n6+U˯ U@$*Uy8@444(ƣ8$&S{Ed t|qFU#Tl5]802mwQr f9'rao֭rl6[0ij T*W!'Aϗe%qD0%1K<0Ƣ 8K;Os?<=?vW|2yo%;Ψq\P,(7jvs(8a`ḡ,Ea^k|=[|8J T$r~xXן_35X[m3' ⨗n<<7*)yh;o^^E^VڍPL !٬`pzq>% gaY? 1Ey2E4$Ѷ4Xd9˲nX8ѢeI^.<8owV0R79:l5:`XIrF,|_eCS )(Tom]@V8=<)-f3< \)Uy)l XxnRV{UM?_z (,(h[ qt8FbA`}ϖa\1$M{ů^zn \R5``ӝMXUE.,8IU̧8O,]R6۽,M1$2U-}v^TeHK^3u3G^.1_!.;)l$ЪҜc ),E)b)Kl< \4(OB|u65(*iqd>2AP A! /xգݽVyɰIFͥL{no9~Rwo_/XZ:<= %IwЏih'Y>>olmfQo'Z.}Ӭ'2N7oFVYt Vڧ'Fc~szt0G0K;N*ΝDIwUv3MYb|>o NLyNzJ^OUolGa4,%Y@ŋcdr\]JܓcR$I < (1!()WKY)bB0%vs}-" a<#a-nVQeIEd>'ž/jsg`m!Yw;8tLUvP`jUnϑ;HU\W$E;>7j mgvri`t48 'aRRJdʬ$Sk_O '_ZeO|o޼yX) x0^_x}j^,UE'9'z鳧9w;|:NeHJRT 0Ъ4qԋ")W/k , r2^bz^3Lg!۳UbwvVUMS4)0"RY`X(ˤ$>9/]_]MF<Ëi<")E75%7v3r|I "5޼=~_x v{_/ >zvu5]Fjֽ1e7V-ShŔvRo~`{ޛ#p'4H=AW;NeE·a"/Q2M&("d_M$U$ŃkPy ?`{O=hyblJUfi&KaP2i_6Knښ$2 4OYȹnVX~>[,0\A)BG$XuocYK)߾{@.CU17n?ެ6L?:CYfB>D0% 0_ɜjRdSS7#)2` sg99 ./?[H/%%ߏryqqb,9*R$%R0B t;7ۣXzHӱO X^lWIP)ߺ{_%D(AJhZzm8yJҎӠZkm&Q!A`eeeZueegW?_;߽\䅿Gx%书+O%> tVZC$4WtZgN^Lu$ujY˂(\^Ly$Idj/(À (Je||d3_vwn7<. dxnR@Pjުۮ+MSZfoxh4+'#&'#@nrj*TK5 REX.P>?Yq@$IhFiA |?6t}L<0qDa:z&$qfI7I"'!UKQ%Ҩ5A~"E(eF!` U(,0`8P13D }c Tt4F񒰹a2]DbRgN!n vgǧRɐxH!4( eYTJ?cYvwo|1aYvum]͋xB)Ue҂1EV$V$fr.޽K5X?;~l8-W0wuCREwgO9s? B&XÓ'/G7Y_tpy9t&@TBxnc"/"$t)JATO=λ+ J"U [5QӚmɽ4c64#sQ7+X/ݝUYdT?<8' E I$adF^Y$Y*/^맿3Kx6VŘO_=qD6M+q!`^Xied px*e(p$r>p ϰ2B`;Ҕ?9{vӭgz|pN<qĜ~T\]?ӌc`vSUpn`}SEB@`Y hNUoi}t}@Js)r-#+'O^C2Y46ִzܛY`ԥL! C!b^\@B!!!"iu[2] kn ,f ]TQ`k}^N9AMZy6z q CSG90rDAժwB<@AjWǗ%U}YE6*f,L@K?9bej1<{px lQP"qH,brsCseI 0e`8>48;s-7XYVʥJ%(+hA!sȲҢ(B*+a k{G7 V-F3ufkgpfٲ%jd險-fN(PLIA8LoܰLTQ M(1"!EQKhs@RJٚLx43,*RTiϟz{V.^{<H7xeL"8M]B㣧 @;sݙ$.S@3 ''A8$ru'|vtӵd)A8"H"ϳnaf9,!$iBPQB I!jXllJ4%MZ!ˋS{*FWQYFa<cJlg($("S-+%)d;˫^޸qk2)q8v[ퟴ:GONOOG^TK%n5=;0" 2.K쭃4uxe;fRux#Ks,ɯ^_s?(5M3] Wsŕk5h0Eɳݭӓβ,M@Ve j0,=7H W e@?~th+U͐[6F *JilZemy8t2w^EA " ` WmDk;-^ Y6ֶ"Z/_+Ɓ(3Iꤙ0dпePd>KR W4d;j#­[wR]Uu+(8gXF?s\D~䵻 4#~OP]Zכ uK;X+k ncq(u24(E޴ Xc8rBqUtq'$7WY`]N{  ːѸ;z=uCPx2`FSIVr25-BatҔ#j۬T(NRJ1]NgN󂰠'pPPBJ/֊U@ߏ/W_&)8E,#h^LԒUOY [צ eja})k_k\dinlmԾbh@9c0VsuW2o{\dYl6+a^u]LMf*Z8l~uia 0'~vpťsNZ=ӹ9^`fʊaכ#iJ(uVڲ,njc2_o.O!z7[񃇫H #*|Kʰ4bQFDDۙr$0TT]aB ,ɲ0ri{iWfZ6)0psD @X2nl8 e9 ]mr"XzW? )lX^n,)b޵CǞ2ݹqƠ7,4-H4N0DB*UUc$)./UO8 6"?N;;gO  j}PZP*Qq|zxgWWG/O.*URc Fz3V UDYDeKT8  "/I1,!4^(,+ϖyY2y2O3"Z<`,M\];G *JgBo&IcI1 ;7}٫zNL)k-l>WuV-VI^bAA7IP5p_'$ƎG$_znVwc<ϖDy>Q@y>ʋhdeOH"(Jg45n{\*>2W<$4rV>;? O6QE[*d9ͫa›w^P gZfe9y| cu]$2U%3U7(< %lI=3!4qYV.xQPTUT%U?H0qEI0DxYQF!ȪVE0g.hbiV޺s(*p 145(J ++Nsg/ Z<5K8M#S) * eIJDQX.}'Ng~q ԪI+Vc\Q tV 2WPUSVuEVBț^nfl4 }b0)YB Z@SU@ FHQ$^xQIMSʃ0xRn蚞QdBuVKm3(-0)# LMr|/r &.r0i')'7oo8I13 qla޽{d ui"fn*<6FߠBaA1˒, L^ /ݟh^ 4,scwk`cYTebDIJggax"ݏðTmK!'2=kwk{LR1i2aզ(rDgy@˒;i*,wJo?Uܯ>;\0EA(c9 ^UVE2œ'qJeHvsB y AgTU'~7^r=v݈RNef@VoScJ)a( 9)/@(J(ʳe4K \,ˈ﹑&?F4MIsK;Mt]iw/, JD)MtI h/$sA-wf+ϟw{wqY=yzPAyY޿\Qʺz媾}06,(Io,5N)

y铙$$ GÇriZ6,Y7ՆyE05壏QoAe9JҴr)@_|rDJ^E_U!H Z YHA9-Q  Qxe@PEQ8AbL5(`~Ƒ=KzigBpTfB.0YGI,X][d!C$~mﵾyN?\bLC7d9Xqo<3(izb5VjdMǙi)|wvXa2B&tFh1[&Qzuj3a"kvYT>mGg\_ V!DGG'xV2 c@(qk YYNyb\nFdI`]|GVT2jeExqB*hE@QpO(fs2u.{g\._%Q E.&a)ܿ_玬GK'DQx^74C%Qd dlh?}4]SΨ67U^eE- Ple9:~9[:_?y8,K%۩x7k;;[Bi0"wMu-)YgOOXS5Y(ӤHPǨ\I(|qٓx G1R bZ 0¤H6KQv7pdnvyrEM8nl|'f__CԬY7yVd)Z.8*WN^ TQA JA| EQ޲*?,F @UUV0 \w1 5L=bӒxp,$$GqG,'qLXV(( 2lb\Y,@"5C#T͎zlÓ*os"' ύt>]^߹qzIhR5Y* f\4. a/.U!~rgUҖd1 jUcksc<ьj֚9㓳 ?LϿý4(Mۭ&ˈ *%Xh 4[|15JFQ]aXzmLREu^L C^^Iµ,I<zArP1r.?if޸g'| ntlO g}wvuuXNض\Qa2BY!dY 9/*+Vl<$.IF$Iy'x,r`"G2]I@ (' >|JPZkX!*pHnu֚O~urwVvKWu/~/>zqZ"$f W$qZR:GSQL9*0wQ잞^| oadB{>o~]:z[$!.#?ɓwuQnom3@8molInJPd^f}\jY>ӟTm\\+YU=y^IbK(K*phTb8~X4 O?&rHr,dX$@A<'1yݬV%c8A*vs]e)5ZjTʊjbT$I4RF1] Ʃ*7n_fjNl*kg 뫱mOvv6YH,~˳(],zpR]Pȍb/.T$GB(Pβ*! ,Fpteyݭ8:mre9uskQfIcȜɒ%I)EQxAjeE@ ɲ[na7iYТ[,y]q]ax4{;;=/p0h8LӔ|rH(EQ\)W1"$IbZ:dh%YQš)RɊ Bx`۲:[n{s9\o7/󥯬BPG @ 6! PG(B_҇+ t(! MѢe*+w9^^ w=s1Ǫ͐2ƪ9A㗓inKljwL\eXVݪ7qQ*fAWXDc)9,<]Njʈ dkûܭj`$p}GDۮt\r?oݺn{ nb;gәcYW NXMh]L@ ]8.sQܸyd<̧0O!5qpwO.zt^Ro&l^7~K%ڽ,ʜqf Qmݼ(w><')bB !0ւQ˴q-Tad;w,npegks vWѢsF͊Z]om/D{vRuЮ$;8., S6v^چuεfqtx0_X @40{w_}Wvo]tkg{QLhFR*5FXJ+R ia\HW4OҊ+O ຾E* e;֠X8.|Kr#D)甉RI h8> ~iRPJ5zs,*Z4KAڶA,c\UBkȨBQ)(0Mh2*nQYsfB@myR xyn*$l^..sV~.P6ڍn^ӧ3:Wn\/Yf7o\u(Y޼"O9~뻟~?"^D'CFNˬ(s @ve*7666k4ѵ: rrx>77_<},GFA2@wӼ䋧{W8<$*\qnb x%vl"Y%Fv;N FiEQ&eE F s4`[wvk`0^,,WQ\NqYAmkFhp8L&Xh1gy D,-] ۈ8hL!!~=huz#+J >RjˬEqUEk!xz<[[-!?Fx0Ej5MӐBE@pY Ӱqf<͓V9"5.Ѣ߻J&Ѡ(* jVyL<;ͥ,Z[\G018WGGOO߻]2Fuڮm$0/ӂ?Oſ|(~iRٓ,Ţ,U&,)5@_ƫZD_ܚŧ'iu||qxt J9Sʲލ,O8eP.f,Jė֨HJ϶ώ^+|Νpټ(YyRT{NK?KN7[D81M; 0"&! RJq.Ԇa15Jk8Idzyd V'@$Yq<:Ķ.a¹JbtЇb1ھS+۾ 4Uۮc^` 67W)/b]r{{;۞zދ?AUô|O?R#a{Ε(l{x&Fs2$M$`LB%JȊ46M$EQyh[vr%cwRV 2b`8OYEBBTImb J+,$w<˿</Ix<\2&, P* \j5aPLkvU1!ASVeڿ~zLu$5Ljul:^,{:Њ_b!ZVhٖeYyQh׵yVupj BIYP$5`ujRz|p*mY|*XM0Vʵ'IvϞ?\hUZt147nځ\ٽ#Qd◿x!( Vo [|:ZNU>'^Xϒg_Vr} jA^E(W^ݭ.mtg@hcN]GixALfw[1W6ڽQ2tU*[kQ0Ήi6(N0'Q!!7,CiC* 1Rq6 Knw/AV{Vr6oМibZ2f7>Ҳ5D'LfYgIfxQ2GiNj_p{aY7'8365tEu|Lf4/ byt9崢2+N@@v¶M/=z4M_Ue9B %iYZDQ8t2MiqJS1΃ \]YuL' \4l6NkmݬyIZ4MGSngl,WNO.ez/Nu s4XEatLܨ °1Nj担E&Ңճ2aϴN~ON2d"\j"S!4kBYUy5ok5rm F{7g=?<|cLZ]ׯ=~8+=iV v~+c̆|kiX"atQNٳIV&˨ƆeBϢ@PP:bR(G"!hӂR֨7=ǃ߼SoQX^g4n\X,V=Mô$-@ 1dLOr1m4..z*eV>'?9y}) gG\L$Q6 (e 0l2VbЧThw l'E.0L"B %TJIiZ +J)Zq%*iKl>{a(,(x KU#*ʉULs*dQeB^ZLJ˶X l4ϟMUdY^&pL=Z _#P9Z'iaXe"epyUBڶM!\bDĂKJ: C)|x \j*SDMDAͶ B@7MF|(K*$mll[ox<Ϗ_ ),Rmo0O ኖ8 CθZj}y9Xr]ls|Nyq%E_b[[qztx1lDQ50Lu7֞<q &Ҳ`ܻ=:=?zo}hYtΝv^bTC5Kju9<#&)ONż"t|hBWz;x±NY..Gxd( IךIlZId6rwwr'kB86vdzY)7VV=-@C!T  ;wo_ ,fGlzuc}_Ue@W4L9Db<YNVi, E>sf|6L= tB/NX_QfPuPer!cx6-"afq{֭]ɯ~¦\I 4Dcd HeP=/xIKg\kVT%,\Ϫ5jJc߹~s9 p|z2 Ιze*hJonyx$7v`o떁'Gɬ,J?0I!fTiU!BB)Zo\kM)y-4,dYxTnшsBfiyU*(ȨT ,˲(KIfi- 'ygZ4ɧA8I,Ib-@pj^4˕ $Ȗ Eha3(H&JNg|aZcѐ x~6^Nˢ =B !BCk ,{p8q v֨i0|7r6~TI!z[v^LSqv6c$Z,IEE$vWqVRNw}%/痃,K^6Ng0p2c"4 B{K wA¨n}b QGK+8Ov|0 ֽW2&qRHȴJ <`pg"v+YVۛ-F[Fd"{F˻]'͵[{{[x6MXY~@ͫ~7mٯyt g!6YB_9;ð,LD 1嫯Ç_ӗO/-3 Y@cR JpƵhڵP+ f~ VU J LT -{=_ B;/F&[^YYLY:M/v7$Eഷ쇾TΆiY25(+ʜUh-CB6%0 )Ee|0 I<j ]EYt=TĂq1\0F?6 DZx}Om;RYJ)˲pVJ2,c/24AozlqZ^Qv~2גng>Ii11@HFJy%iƠtƣyT ME:s&^oie 3Qٜ3? 1/]^뮬NgB(۲Ļ{[`6?66?|@rc}}omͽՋ|.ZMj??z6]!YK18<9 P*M44RkТrjOhà(iMl3(޹N^ZMbabôJyṾQ/seEVA|:^y1+*6F#|O fE[-;W}k;/~jktqigfEdE4cڶ嬮nF^7Z_:`WODqLu-8(JW^r*,/j|;}Gd% 0-ym.La''WWGy?|r1&viql~[CˢV75ƭͦe'ù`cmY,ȲFie,dY'"^NY+FAܽ"8NfdYf[R<.hT IѤj/oy_HNY}W)B BMhԃ[AG'4XLZh^LG1:Y,VJ1 yO&â91 8b6&i{KnES24Cm㸦mQ,ǮJPFL+Q#;Ub91jQ*&AA/ΫcU prޭW&̈A`i- cALH 7MAQ5AH]>PV{6-r@u4Fk<.bˆBt$Ccp|CɧS-ty,mO~3)peİ.BD#hŰ(@ՆUgTUƖc<]Kk!k!Ę l"/Ɠd4L..g?xtI $`2n5|tͿon* <\ꃽ+2?h"2L@Nvgw^i;gHY-u*`6Np櫯߿s|r$cj59BӲp1){xhY8˙e2/y-^ZN`歽㲬,//=}W 1<' H!(t}W0rbxx|ifRj bXEQFiQ(Ldvmк0 "\@qu4Rad6K0 ˲`C΅vBMFxYna6PUe'|uq ^ 0@:4D4 Ef+[,ƣKJ c_=n[{;dݥ>8)SUɗ~%xn j[h%UE a `yh1S6M |n4xXZ!yX1tFhl4c+8*Su)&|{'iYrz-**M4f('qzq}oiy4N&cVuݢ(\, q87 A^2!Ë׮ݰ-g0 .&*1TK_CX r]kwoget2%`sGh< ہS |c&f]la5֓44tʝ,Vm AX`qa&f;˲87[ͦVkW*l@h dvQViDE4BB1#dYfIJ,븦eqʣy Eūd'kJeB ѨuVg{w~'gYlo{EVQo9+K0~[x PEĨ$mAd9fJ$}0!0׈&iVqõ Hv,LJ1 <{l:Oeza+-4b(uKޒTѨ՚anIi㸦Z2lN.~=˒n.&9~ɣU^t:a=/ 1/y뭰޸ÿan?yz |q~>_ L#U\jTRBc\pJJiΥeEP _UAPASj;8݊"D*6׷͆}o~~ozE_e&tgݼkwmvky0c&U 'NaA&O- [6,XVYXKFFɳZ]W[tX|>, 3b4ee@ֽai6=&`irmwpG-o?ۏ*rz--xܽ~>ӣ)p2edY+9,}3USd*hA VX0MB`;NqTz4@pe7ޟ-@d~#w?2wWn;<SF@}~5G (-4 &i:sVUy©2oChU2߯Ȳ2Mf+P>{~x:1a/?^ 4U@>|b42c8nƔkIo|[?oX0!aH0&"&P@4K 5=4(+EJKe&e8A{p2eKk 㝝_2 1^!hlږmOgH ilIrx8cƄLGpPq\J,hYЪ ͳ(ɓ2Mjk-@_}8\Re"-'l\l(OMx_~Vڶ$ɏ:V[P?:<]aXlg:Jh6a&BUTRaA9,~S gCeYy9EDxS Uoy\ln:تڐZ<{pŧ/ KrqoSV ގ)*Yhc%}s^_y+<;ɳt]ٶ{?Z^diG{߸okW4O>G !<\H bۖZY^绎iBh\1m#tA6i[鴤,J3d`fڎa{qZ-1]}hڤVnet6gY]OKP:>p,g,t.Fj 8jEA%׌J ,8&mځ_SBf4Nw~O iUQ\z?dNLpbVdyF9mty**@ `d\)wΗ_~ n)<9^̕R zeT)0@ QsGh=&a $6q͆wَZhuO h.AFYd͵`!huSB D\#x)8q4 yh%F4IVRSLaȢ(fr_쟞^\.Ϟ\ߨ44#y~1 n]'_<|<'?>8|~.BRPJ9 zժ*V Jbbt FaLϿlp$%Nw^3]tGd:t::F^J ^4?>Ҋ+*4l"t:W׷quE2rDw쮮-u"J\Caz!1t]~45l<};?տW'?~~y0,bMp*{ӓ諯FA>Ls5 0PcL RIYYUeUyU j8]6y^ͳv,&UQɼ,;˖ApCsg×aLDĴpq؈(Ro]U\b .b>M+q1)%aP͵˒)mň$:}9 \0Ct/x/ҍ]ش^/ {Q_O4Gli+w=r:qΦӝ]e:/o\$\pӶ,.qR+NON)՜jێubOlJs2]v|҇ۻh?24뷾"O?`ph/}4l춾||gSb Տp}}o_w>|?y`a_ɧ U")|MނHv:|f_ds>,L+wzx`1ogٓg^82Ok2P@YBmb DB %R #1s&`ǓzWW7VWN.s\߿b!V8:=8,O7{[ۆe'}QPʒgS9l2ͳ41 !HHf;FXRQ52&1јn- KZA=߇WMqT1'? BF^PpLpǣ(*<+hyUEN Òal9HdH:Bv!c%^sg㵚fEf p]߮t>q6?:zM/74E[zu$8Z*KFb0togg/};K<{`mCp`^PGq8` [.ş?:;;8uxOeG+QI+% UM382K`&=%.*S0<4FPOl4deQ9h% 2m eaE.zzp7oֿEcdA> :.ΫˍF]p6;@#,dg՚uZ4BJ,u=˽^  (OveZ~'倪Kσ*v~v~~6i.caTiﻎ bcU5/h^n5NYGK#N+h^a]w5WtR}A+mxqNzq.\G7[-90jEQ:!8_K뵚BUzA5!RiUVVcb=~s.YTʢ`Q.a{ommk!,x -x0^fEٌJlz<:J_>xl\ހeZWtp:B7'l6N Su_=<=;N/y{:[;u~v3u,@r8=&B#u=bNaPϋsỨ̸F]Wi L񽽽iezta:Aݹs?qkoll_~ŕ\Z ׷V0IӲK~G2J+4L - 1ᬂ۶4g) CF YH066!DR2SEUmYfb6F0 vv󌞟 =g%RQJhv|/$Z)Ҧa"L4L`,j_eU07p,ÖBĪ$,M2߳'t1M 0hUQYiUVYZe34mӶ2N/-OĢ, c 15!"E jm,1PdU؁3`zz{wecgА+7o0O??qZTY$iQT,˅aeYds NyΖ]B4Y^K):bJbL.O9TR98/rDY26l2ip*V7lˍ6 WJ+VnS.(4@vqWQyyZb%z2.+0^:_^.8&(?꣟%e@ٗ3,J+@HcRjsIe R-pѲ`,\Bi`s.['Yq>GX,o8ׯ7;: b%ы烇9Ò`gi!I,oin1+a-ees c }7՚~.VHPq$KsdXwVWzh6.!RJiلPhV ۲mVʫd2)! *9#d x:s-&PA4҂RqTZ)d+k+۾@ дR6;֭[70Ui')=9 Ҽ*˲lC)z&rL/Zm4VWnnkќصVΆgg1(vd^pjW_}ѣAT*TU v]-[FVkN3~'"Bp0g^cRQ:Q4k0EQr^]-"¢*B8Z@IKƹ lx<6 5q Q8kA?|xZdu4TB%Y7ݸ-ufw-p|֛A{?~r͢^l ?ofVie.7(-+Z#!0M_ ؆iPR0TwϞ.I @PCP*R!!%"Rj#[_{1 J4@^<+^w)_}cs4~,(qgѮ/|flMX.8i:!`bZtԔڕ*W;&EVbhPJM !a缬 mŤ,Rx2"))@ ,cm+l{8aaUY6u4A@L8ZiK AlbH-afUZk^R^ kf*D@Ж,qZʊR=s`n2g\qb"׷W%dEY7I߼[Ó JuRIY 0 }hP7Ҽ88lJ[7O)W'/ZYJݦٮ+ځN\r+Z@8R::)ˏ^$ @( eK*P$ o5FD BA0@[7}GZ9;KfJx8(D =yj[w_GawIiͳ\h1ڭ DK'-BPG鲪°{ڣ%"m 4ADXڎGZa" K7;~VE^hU_kݸI:a-Sk)fLI92Z@bVVxeUkaUeUe]Ę:=t`8G l?yEV{sҪ`HK@eւZ=NF@i]yY}(NGs&n`1º֪6`+vxto еx%!P)R!ZK UȳV#-P-ʢJA1"1LˆP8aZ2iD)E@aW L $&1qS( R)ư%Jqz~NKl2Gj͍wMn[o*jRĊTM+xv~M`}sk{g{e_"- Ne ŋ* uL(Ub"lrAŦ * v˒|yb۶b: Q{x6E%LS0k-uQYZk{7,\L|<*F@s,(Do,]fIyK 2c)X[u:4--)<^s, e5Ux,$NCw0kU{,YR/*bZ FqmhM&˜  ZpؖDPzx! ,n_X>gU8NR4!lvw߻nEUZ4vh4)K~yhu٬nHB)m6c"+NFYwZ奥bIV++^sZ^7/ ́my@r͛ח"qũ`B _nZcF[WA`'ih_v)-hY}wr%^./w4F\z;ɳE 󩠕6 4ZJuU=+2ʲƨ,(4cʴR@c VH J| JIm\pFt::xyR@jTєCl&kbXI z\HV&& F4kAHu/:J2%`y3\-/'J 6޽uyq6/}o?>w?xjY`wgyRg99:'Z]I<.2q2VicَSQ'qYI6VgG,XPmmo !Jzw".OFuZiwbIZA! ʴԂuu2Km \_MPt" +MlߚG"^<O]W9`}o=y4}i]ӣrtq> |oue*,) _yRJ H%Rfe,5* #Dp-a"5PHi5T`8 _ ( 2(& 0-ƘZeJ4>P eѵ7ꝶóf#nFѼ`^.B~}xrp4%qRdœGSAq.`bHVkO<tmy{m%m|rb> Ӵ_q lfY^@)uv BW*0BqV#@VZiך!6i0 RI5A@<Cm6-*,'aՃYAvq ;b/²o^7FYQDvF /g9P^k%Q|zz6 BWNk1s*s~}Y.3MZze`$#!ANA LD,3!zr 콽fΥ(\4EZS``-B%?iHfbVR d~cl5L&6f~6L'B dERb-uQ"Eeq$|ϋ4@=E 0g]v.s)8B2ab 8yx\ΦI:?,)^\E"mbVe$ &XkaشcB(D!!BA B My`"!V!#!(gJ(,"TUj)0FNGJ~q>D%<+<+ݪJ=vm6bM8 C;d0y2{Jtv|L牬By@n ^LV6>'`d<}߽w^>gd4< 13V 49G'Ͷof[^]aB9^%,dB nm~A1cK5bukt( Ն:զWe6嵖pUQhB18mymYy0Z늊c)`xJʲX2/M1uݐ NײL^gͺ+EwC)4NBTZU pL^9OTvw7~ &_Ofl E&E;r׭ŋVRIFޙE`\lKKARƢ8{nh_~Ujf,+ݵV0zTm4m>[#g/t>;M^>BPY%[{F_LsIDATJfB@!he#tn^h7kW?ǧ7 GYk(A8.`6Mbi(:{ki2mԼ_/5TV(dbd:[PMj})ƐyŘT Z)P@  !@wlt$C yhQb׾v}{4n}uqP n_='FtZef1}GO $ $US-dLl@ ^z@Ǔ1gBKE~mme]0O8CږZJP&Fذ׬u!4 16 uB@Q))Gi!Jq H%")U!T*DbCsf۞EifŸk{^,$2H,b°^0;%ga,/7]!%(2)l7WkW"5$ ~CJE\ s#^˳IFܼyiAa""8r2Y4\`n_JMiҲyGkm-:;Nx8ti>|4*ۦusw%^}>f(0J6IƳ1e\WvOipkwFG$Z<HF}Z,KF5|~7 RZ7߾?s*/4 YF?sʎcVW6̈|~cwv]jk`<@pF_`iĩ啽_}+j[p8mCpGɫnx$,Eo~`sуEQږiUYi&%WZ8- ð0qB R",4LBYP+TRɯJBHDA 5TRp`|-d5! X1 Jƫnnw 0&1$ahNBJӰI)d.&ɋܴ&,L|{EVթ~M <ǬO1 N/J2zeuq<+fKii<W\-sկy^{}{{W(NҚ[ j^Z{Y1Lq^Ѫϳ Pz0O{W*06?վmo?z٩9hDiU~mKYq,lp+YV;G'O>ٮݸZ2F3mfʪeҚ^Ltjlry9 Oܾyq^hRA[ׯo<~rwN_~cٜ$)d~qqυ mG: mBysJaU0l! 1h"+Sb!R)Δeaa׵~]ru8N8mSI3e~x>B4lmyU| !Zy|^޺fմ|Ǐ_[hp1& Jfj69yeYQ׮W<Q흭(J?d6b š/B,//=N 6_~lZkֺ( vH B \YY2MCIY%iYaRJ.P)9m6W_vR)ev%Iܽ{EmEQg-XC}EyUnPj6V6|׊x{ ~bnl駟 x6̋Lx8QR۶;/GlZk4I`@_iZOL|wxg}tq> _~FMdqյMree-ϫdQEE" 1??IJm|ߩ =˽sp0\^/bJfg)htr*N/d>ɋU JZ'̹4/ ͵dZM߯+@M[ښgN{6FIWeYVpր36Bo~{:ihU ZkQa#㇏_`ϋT!tT[Tke,2ٔ6X[_ رl1 $ZIR !CBu@z۶I)`1}/R*"+TqM{W=z6|'O4n6~ހpy]r?Ϋ*A:k(r$ʕtQ][Ptp1MrջËᕽ(lۻʙ_ dB\6*ˢl0FwLذ J [ʲm/1if^FUb۶s!<5̲M!E,V֖ғd6 oyeҦcb@ ^@kգ_wmkMGa`e+VW?V9ρ++oq7ԟ|~F2!yp`- k| ?/ou㣟>\ժW_y>&fu@`0,{2;n։YY^ۨw/N/gx#)铗63W'ÉH<ssʘib"p0&﮹:Q_y yxr9Ub1Y")%lv 'nl$b媛=kfNî kb>| tDYL{$?eˈV7WZFo˲Zwxݿ{f|a@$-r?F9;GS6Ko1Py($iڬkO^%dbȈs世eiq&!4M[.Ca@]So޼ 'BhVRQr+q Y*ˤyQ H~6f 8Qd]tV+Nojt1-NOpujV5 \ǔ1qիWPv}5e$ Z'LYR9e TE6MB Ɔ*q.0X5h`$K2%TbE.<ϓ8]n1ƪQ9-9cV4M~y,KelҨ(xVŒsfAtv$coI(uYO? ̱K[vƍWvv>8::0/|`V7T[5׶ լ+67NN[oݽo7t:>4O_g!#b*Ix8 %GSADRO3a[ጇ'Y.IrZ&cZ(', "`IƔ"ݑm5rIF!0dBLU4ܺ}>:;^EE`nj|lW_B?Oх@{C~kcVU>>xѣ/l>yr@) .jr@5dB)G\6귾ygV׻}1VtP9". .aI0RB`A !oya,f!c (@fQ}]W-(Hy$ah!$Yz `Dd$+YyƚekYJ>&-z'Q~GZE|?+u(}l8_'r1Pt!D J 056 SÐR$ya2&0XV$j$_ĀVkc4r0ۭmٜ$E^Ђ @$eز*$7-=M8"w\1(RQڶiZg\,˳"PtBeҟ -/&q|csݶ싋h<y(p4\]Y}y0lfW6󂞟'p8?{\-3E($hAgYQkmB"Un*ϟf˃AQ_nlr>d:uʝSk299AHso$1DRx RPݦ0cTA Ɉ1JX[[By6 C5$ $9cr:!$,% BdigRr,'I[r SKӸR)A䄲`jJH\<Rئii8Ͳ@11I\EA9V9M(rQ[xs&H:꺄gs󥷜O۽$QĮܼiְ\_sEz+ IFu:Ѩ7zVV?gh6n-EgPPF3$9-(bks_pMA)0 ,?N @"%Hrf p(iR!hm֝ltlv]r7ʍr^pu'R]g/_LJ@'86417m'纡S~91"IQTMA,\@o[±?9{W74pYUE riõ,$C^Yyєvv8/ Z5L2-SM*$Ӓ\uNDs'F!Ħ'"XI"4=<8 BOՋ~KFwﯬQ4R]Bb6[<~,ϣb\.UucYq;u~vPyY:S* !P"+,aMI<9idY"$~ƺC9"YR8y3ܲ}r{ν\#?m]28gZ޷|Xx*eYf:@SgÌF -cFR.UL?Nju 3߰$?g I \J%\.a.=ˠ٨f4kzᲐ%ep1JCPk$gq޾y?QX"Xr]a<5**?޾{}<;;t?n[LG8:>mX.;c醁0.HY $Yq9a)$, !Ʊ2BҰJ#JANJ.2a * .) N"˹P1ҫ1 \P>iOTG3_#Wy2!eyfyA(Ca 8`@!r܉d`ajTeS-`OOg3[5Eé7WW8] n:M5U%Eh;6[b9 Z<4#PٳBQa൵rac:o\}j֞<4hA*#QiG@lIX`;ePoI&iΩr~> ,k4Jy*}x|-[o'GY/ri\qh!+WK) ,CɎ^.c !PI74lc,BH!dbU\FBPJ$` MVUB^We#E[F+ˋb;%0Rm{t|j[FZLg6eNgQ9FzREƔՆ%P.*jHq1 Sb2!Fzb>LB^&a% d4LS1 Vw7ʕ?};9^() 0eJ2F+kC{9ޜ죏ӱ>D2#!6 ""Ib @FaDH$Pp%$Iq&$ & IJ, ĩHx$~Ƕ%:bbh(8gdQ1Mmq1I )-(DsJVskgV+*8]r">'ԚeT'|kׯVFGQF@C/"NiV !9Βr6m{<[E(lt͛۝N#i7bum9P%Rdp1OH0Qʭ8}l矝-'TV,o2,ś5BG7a~ rY6/,2MR y.D RZB%,! DQHP H HPHB.HF)T :ʨ0Ez!G\tY1eWSke}] ۇ/x_oe{{2yrTH, +s9GED7qF B@C.i^ r ^,kWvֶ:xz2U=v%&X]^JHT Ѳ `m<93'IaךͶh9lGt)-ˑ{'/Ӵ,Uތvc}rwG7n_o8;,ܒ: #QDH0"$ 㼽!o}`2iQIĶA |g0%XלKn5Kx47O]ˮj-0-Ki[Re8Ihf*KrxId^RH91$Id,acOӴ ÄE9)$8Byh6?|dz^|f(;;񄊂s`:"gqpjFA8 nHU \ġy ]ǘuZ rew͎c)J{m:YBxql$Ë?di *X[iE˥rniXɘI3K )ȣG^H[mݏ>`,Rq #QBBte:$(Bp0Ȳ$cEp (CV pTUW HV6tS-)2ZԜ!_zKÉ+7^5uთkx$I4m }YioڟFYLLXqiV*ٲ)!@ CK¨C㪄 L 7^}}'F 苗^{DQ͍pw(aE`5UPBۀE" RlH_,F~lɇ±ǵxl?/PlH$26v|~n +ut8 +ϳv~x:1Vl0 !z,OSU]6tIH7t4@pj4,+#T[r"I FAȅp.B.@ B9I]$ P΁ %$YbĹi뭬NAYzpTx@tr-y%A`pu5%hD$ `bkh'8=xr,GLJ'Rs>eK~xY"YniYD22^<9#)6ҌĴE *J!TfLR<œ٤FU Mޜo:_ ~8$)$* 93%t$/aL$"B/' B"E㊢@ F('%E,KV2a!ݿ7? &be'ILz~4 ZrN'X.|ww771*`H^H`a $-< K[P΢Z_~oGht6J#U Q'_K۵+7^{KZtA stu[w)˓dHa+_{+ f_ypʵt:K?_ʮE_/ч|}b0-1WLeȒhXm I]޼ŸNcȩnY$+F(2mF:^U2zkeI%Y>]Ns%OѱH ssJ-@8NC%eEK( t2Q6 J~܏i$+F^n? mEr( pt˼HO?bs/{V$I׮nX'\c/IdPu7͋ѡnՍ¨Yga NeӐhW^AIfچl(nc?aHei`U/e׷/t29:B! ):PdAH/-fqEd>| 5ѧ\wGOExlxfr$ B/>ɣs kvjf}y( J"i:'e4 8j5iG4f,[NTV, *qG$/2UCkk+j\)7-֖Zi2yK[,L0M K.+_JB.(S.P/_EL%]7EQAJsj*,^aII2M @"Qzq11l;I"*D7ie!fir᝜]zUpD!cZ.q aE$G,%4p. (JUSE*1.ˊA92&њk^$`x~>>:I_g3002 [7=q EӃJq|vRGfO'vK/EtUU={)!^I I Nc׮oa5_olo=y>* `*y- `{[É{cۚ"*ljsz|MD.Muomu7{~|qmU~kY0tMhlllT740-)&i8 `{o@G _\à j&!4cCST A)+(^;5Fnj﫪dtZQw;ݫWzwxIB h4ltڽ^  ^ZA0$~q@/Yq 1]Z7bK-"ü(Zf[c9T*%rg)Qu-\$anQMlj*!%HXR4egwً$I+x8ȒjcYU,Mb鸪\H@(L4')) ^B#\m$'Z\dv|xgk5֭F Gh7fe@Pj˥"+L0ijQ1,7JP ADS,/XĔ3U¢ˊC 1 <,ISQ]ieT~ PCcM7)V֤_~KB|8TJGsWl<NNvlXw^ٜ[nWM,h4WG_ӕˈEd(\"(@$ 0HPդ$ FÅW{&ͣ} ީh]SMm$V ^~q4A*+b{s `Ynh3t,~ݺ{cm9 T.-9kSj2Va!@E^,8pVSm^k4wz-iG .8DgO3OSeYgZU/e&zCSJ5U_e)~)C|(_"P\/;*\roݾyzl5:!E%qa ޼}K7bi`14 -蝛kE<8 T4;;Џb2\Ԫ덦"qø?`vu_z핷|__ڗk唐0$YBmaT9 #- 244#iWv8ĺ[ӕn_1O=ZfG2==.o}VVqX.×'V [Apm1ƛw5ƹ[~7^Whٳ՝>=+Lp ""$CJV4)d,^p)!BU4ѯz0iӉY ,G|ms̒'^bJQYx1 !T, I4Nl=~tV!@oUXAd[ P1$YVV:nّ$L(QURq4 J@ZeIdYVkl飇O9?<+SY}?-ElV{Ee{{;V{0 n6[ZiU a&e;x)$@K/J_֨Fu(I—q8OK߳Z+ &9Mw~wJOcY252u"h! EG Wd@VM*[Y]k,$j*t\PUMAlؖȤ)%vV<:H\#ˢx[b$Y54IdE3v~/x@߿zSmWٮmoo,7&fvz0Ld:(+2fbz[6Wn("$흵޼|^_o.bA ĆegH@ABk^kkwÏW?mFqܗ/4ePhZVIJ(B/?E>n4*v~gLVaMKUlvm]O[>UK[*-}p1骤RXd)vV*7nK2`:Bb5N,eI#2J)d_M!u $*eqjR 訟 ݸ~7 vh6Òaإl6Lv 1g ^h-9K1(E^h8(4 /8X]]ٿ !(#annږݸ{Ҭ?yD-Ev8\I3NJ@ֵl+I*|vv^NNN?‚BZnTJճ4ɊX:9jBn@I$lPU̓`7+wy2" 7׻h9h2<>_yma04" 譕w7~+nե2e,O8΂(|Zwy-?$iՑduJI\r VX0oyIHep}u ,~㟷VXR_ńFǧl>'?w<>t񓣵O~ŧu:m;Ms6-4 ^_qh|hY~p~6۝?Y)kɼ"*9L@E@_{ǟ}8Y[3kҳHV2(=aթxE~7j[JeveQ._7Yӌ\NΎ&(ɀ,#;2V^&|#1BP,cH0RB\Vb^,ڪb<*)l6(k=,Ƶl3Efh:M_1&e\F % 0I0e#SN9kɊM &2eXAEj8S5q-(EZ @1 S@yDZajT?}y1Ʋʳkvyq2 TtF Ef5 =T @6 %; #Г4-  PU9MF3}[7Fə7_*gij7\M(lƑVY묬5[Njl4j+dzͶnX&I(\t['/=k^_oƭ7+ Dw4”CP ̓ˊիWO^6n;Y ӱKhx^;([/^RLà??wG|zJpZK?Pd6;oJӋ㵍ί㣗8Vd '1eP /_pyK{g.J6oWz{}`4Ssj8fò k9΍+__1YryP1 +lno55k+;'O4yINI;EA dGNgS 횆W*i**"RϋIA% ȲvUYWr0~ݭB FJrXrϟ^[ /Qgg HT ܸbp6 B?8T[0(4/Κi|4w_]_;<=Hjw #,;VLOޕbzc˔>oX@ӝ9:`0&(xڭpDIhh]iΆK{H/ j5fTMB1gspfYfTcSZl2DeЛMKd46$4 9bwhy^k/t i$j^.)a˥*r.nfm J:88O/^j$qBaZF$ @~i.$IB"C(  U!I(b痹DQ[FqL]ZvWy$%,+Ru{5olջmdxι $wW?RȪm[rŴ,MS+jk}}ݴ5ǵYC, P, =yJrn(fȦmxy,k z0f*5z2`Di^ٽnnm^VݽkW677I{eeucRDe9X H҄rN(M$"i 5R-]gXouw~׾WV[׶+}Jßn $K[soEfH9aU6*;W\Wmԍu׮:O7o~x4Uu_jT˽}(׭sJ=x8'|Zkvӹ˥0]s0>qE@ r$4ô,# #IiV45Y[4VwKz,t Wkh)ΖsEZRPF(Ći^h8T.LQIs$8͊#' ZdL9˲L)KDpaٶ,cIY.?|1E}E)Hy.`rJ>_Ώ/"!\FEFYMi]]]do|/-sglLipN5C?Ն괂0hcaJ2H9(3iEq[+WZ^^Y]ݸv}/߽zkİv囷_[Y2-"J[Q3$%afKAA(RJ g)F4/)l HD1ݫ~{rq<ϋׯmmv^?V͏>~ӟ~xqq{E2) [ mۿy2^y"&e9iZۿb<4XN]O ծ,tyXW99IN4#aT3V8*2H@yK!0~l|Ńj 'Ͼxt:OgSPP~x8u*)ənTH ZN R Gqd(molo;>:})+Щ8momo QFqLh f!NIQdi&iF1i' ECjt;ג<|~OtZ5ˮ8*gݯ{ѧ^aiV[{Wwk,՛.3IպL/e/ Q*>:?;~7}W=xk‡!Ȧx|ͦyF٬E: 'ӹ)^?8;/x;oܨwjVRlUt[e0^8Y`q 8Sr20׭M"x6T11LPu@8'w&8+ *P@!hII$e *a6( χY\SDEra p,aUHsS7aoemsk>O?C$]7ZxخSC.{^{4=3\ɩZ^P?I/KwwVoz]V`ykb014EĀP(ztd2o6$6KA4-^$4)Ohkp8.)B/Ff/^.皩.}``xFgNom}}ܪ7?GOXij++yZ[[_vZZY.Y%' #IQ13B 3M$0 i SNG':rjeNozwr7߆:xp˦McӍ{_{wnnnٲ\Pm rGO?{xpr18JIp.NfgGKU_ FQT,ԩ7o];?9u]uZ]AIn}ӪV-7Ofd1LWȲiEF助)Z)XFKYdž-A>wQ-6&sV gjέ6ׯ/g%KW1Ð" C EBSdr??LK Y_WAdYTuLϛI2gaMGEjTʒ#\oԚpfk%YaiPZlma0 I0MIQ[uz9󐐳Z-k7ZI @ieTʖbr!̢8 ʕV) di֭1tnUJ֍5\LhMfpw}K3-vhktVڽ-"CjƝZ$(>{t>Qr>kZVvoZ}yW ƨ9YLZd"[ug]Y$9?7׺iJX.8Jn(7v{˶`9J/7>?grZ]'MSӰ8d<)e")(WmSuIL1Rp* ((VWnvq1^f͂spյ5/>Â{W7y;ܾv㊢cBSaHsVt5*QFR4'q%PV\2NfyS˃[R[YKd2ONjU`YOgލNo?zSMRE `R2 `ƌs$5K\jfAFf9FFˌ~p0^%%1 8=>>8onSBZne?9 ֠'ih*DFY6 O{_<&3IUd$aF}~>pJ(2sOdQZ[9ܹ/SƊz40thX5ԍkW+l:mc@֘Ȓl{~XN8Jb\AQ\25d6#Z8 `8iF ":8e_e1 ȃ88)_}Nޤ,c,i7jIPܺ1DZH'y]5|:VeJp> t.>j^ akm S+תvF{{w_{y, ~DY$I#o u+պ+[22^rlQm(X!a荧Kq@b5ҷL_zIVVeUׯ4ne|֮b$}ews{es{gGQT$O~gO/ǣѰ( +qaXvM'OcNoim+eCS9cb>r`iZY1LŔPiy c FƵ] Y@FUEVェY bXh;kE_Qh9ݿ?R?h20 QVT,K2L0$ d,I@ akt6T B~Z8i*B50E͍Z}q>zw]]W1I޽q/zY)W$iPJK[.aEi6[n,pyPjHtxt\ʿޏYt.ߪ&ɲsoT*@r^ U+.+RZI+v6;f]۔&4ρ㚎"?;<$O N]o*`2UUlZ:h|~+wuEM^{έk%s ΒMg^vÑRpXTe*XJ &RrRՙ-'N GZŢB;;g ZrI |e˪f,'>\Z50m*ʳ2YRviNIV D([""8~NkeYlFQXx<[O~G?htpvvF ͋l<`,N,MbxQ40$JHJ9ل]mwx2q@VZTRV-Y% <6PAHK@! {>YRk`x @͈AfQUUfaZr$F-VT~cYfO>toge4Ie3(RNLR\%#ȼEW[vA#UFYy?ԚUb`l:kdIT-+5/$/HdR-\ػcފnW_}t 2ŪoIn'G ,f ` /9&; @@!R;RZHQy1:)~޶UrO[+pLYE^]ݨ__`xYdن嘪J28j5J%qMT(bnk2QcY˗_y 읟.,\.aZ-BAi`ww6ަnrŭ7HiJ~֭۷oMӍ,'k+=UVTEj+7_RթT\Yiyr0?h6J8ڜ4)IǴl?JDp׵/jll)O"(ȶj-)ͳjysj?ejXժ]r-M$ (Ƴ "R(,Ɍfzvq8XT@b泙$Y JDRayR,^1bߟ"HFv[QAG_暊0jf; "*KS6kՊ떖t4lH*핵.x::X>Of\Ksxz d!bel˰tq~1VJPBqԛ-$TlV/jr P(-a^oi:nmYl:L+'ONJܒFQ`),vѪD\d]UG`}{`%|?P+eEU19E$E׮ΧI[M]5%^s =-5Zm<j(rӊ(|BZR)$xy<NR)1Mq[$ &i$HQI²}9(4 cmmuR<%'(WuxsծڢoukT$C A4&:p]@tу|x>P! E/#0`4qnGK*,x:>9HF^/+6tu1DQ4̰:^ŠbXgJ Yrܲ$mZ6,KcŢ\*W0TIm|o޸+ڛ ?M Di(iNT~NGey% Y'K9B@eN^6O(Bt]!$IX7n >yh 3RR&(gbGixag YÝ퟿Ӄ/PtRkQ_ƍW^3[/}*NY4"`EZ\[$M(QdͶ]M4lR%M3Jzf,N%GܱLi;vumƸC@]F<ϋFrwmll^^Chr>Bp Z͖㔢0T$ٶKB2If\pxv 'OM_%_{?t2fmmYbYƬ^se44 He4?=/)H )ΕU+z^jw/ S¥먝vۮugǯz;y!0V@" ,y{9UvrwKIɂ3X4NuKaXa%"a(*yŊlӭJ٦F-[`D4Yr(+!:j*h4{.ԟ%dʶn<7,;ɲYQ`$/4v쒪FӲK<}y8j~qylZZrͽmSW5}:m^_x`:PqJk*I X|xEp^'qJclmrh^]#HK,~X,EY[[ 1L44ӵ˵jEAE1!֨%g<~-fi"rsXSHk3BQ'cCW]UTi% ,Us EҏҴ yIT+[&@VxeBb{ Mo:nniB,Kad%e [ QQdkyoXɒlv)Ib:/meŪ4kÁ|!ㅪ"2%F'qt4͗zEH^KXB"KВkZfcH|"H@(@T*l&t68NDAIreXA%ˢsO&0\cmdiBLpQ`o7`8 4M [ZD8Ia,qgN+/r$ʓHdTT2l8%28CXBQFwV C?=?[,NKT[I-sStUѓǏ<%\v ]7,}9giVz"I,V5J܋iVAc" 7x"lABϏ{ٓ/zX,w_UӴgөnɓ[۶v;=(|6jJ9MWρXΞ?\ptnu^ P%fx:*naRuׯݍC"Kj5'Sbr1:ϟ?--RWw! hZ&qx~6H{Oॲ o>YﺝĊzl Vל \9ꔿW ! $$! CMQt5msmZqZTddUW;{ry d%EQ0Ǯ Gl(:_|EQfZ^)"N@&@GQTC ؖI(@5jjY2ANjɱ U"MbUW$&IH*@IH"(iaa:jݽ5`oVpk D:l6^[];~qcȨjEx~deB!nJe7 M248*d4ȳYRʜJ;݆a8\PB,M('k?ߎbo8 il6 AqZZv{QIӋV_X+`Yia(akat^SqfӉyO=9;VnCaEaO,3L^֢4M`0!)d>mB¸0սk榩qӂi 8OP{;.JOe?-2- l<Ƴ7o߼yBD kYBYڭ!ZiVJٶpW+[;N/2@Tß+%xou '2B_7$t꫊tx4)e2ܒ8fm:^ γ"OҴT.S& @wt*.8C\XyWuX2y۫lF L[NCA_~*֊.d "YVbvPd+e*9\W+ΖC0$RJ ja\\LqF@0-֬sR1U5= c?Hȳ!S7?$Iż|jrV9e0EEyA eئnۖ]) ]zɔ"TSwvH]^Ya$Vh:U}zxp|~" oyW}~V ˑ׷a:\@ le$1t!\7 ϟk6˙ʺ&hZw3,]pbW坽zRb Q( c,2dmrg,gtWdjl۷ʶK|ws7 ?t]#di&+J64m6?(RY[VUYQ+%Y'RYz.1e A(_!iY˳8)yoaiYz+s_ؖgpyj0DX~Vcki~hmYKnqVbEh!8ve4,nG3T1M=w~FkӖq!z۝%eʒH. )tS1 Ieai e,)o%A5"4ᰫjRY'ͪ+uM ]{#eqRj8GG( IS5'ԐdWoC!$hHxl~s}?k#u"ˢEق bS  {iVv{,[GY4Lb/O?9؛_(Áᇏ1<_֞bFagV?.[E(#tfhȯ#x6MǢ$~gMx6 3"./.DV JAcf+$Hýwo/6UV$ m0OCKlSduZ U=9cY 5D"~wuÇaoݿO7_&q폿!x]3,V/_/p4)2uU]ϟO9+<:y߼88:}[<-˧_&UMڝѠ?G0`U}o?[n^ M`=mKAih"^ f{*5??~%g-9*Uq_iia/\ڶ]{r|կ N'L⵻qK$04&)/$4 @AŠnX@E1F7eQF{dtKa(/OL2+>VzWArKtڭr VUݱ IU4*v** ^OueZf:PeĘ@)}pgx,ܼw־s7}x`f WOch:>8ڃu&,MfWoΣxDa޽b~st$:SF)TIDAT2EF7߼xƣ=,)W3A7םN熮ݿpYڎiK꺶$ɳUi8nnbRm5-4[onWk$ 8Rq녦+eΖ7e$5ن eq ]>z8eYaeY.W", V-ӑ,;][QaVnۆm70޼yk9WXQ96`rh40qZ^^8l6Z š-g4W,QEpMeTqVn6IN&Ru 뜉AT[x d*߹y: YKkjx[(ӝ|qvyMҌ`uړNg,Jg"4 B!sMV-FP ̾~8 gk6% !`Wdi{{^2lNRQtUGI-fɰ\|onAc >>|9WUi[յ󳝩Xf^ۆ?g?{qm7_nI%fI~/W6fhBb,B ޽{. WbY6MRWWW XE>DVS(Q(#6(,E,*2L﹍4jVʪ(+,QLH8 "MK]lYZ CP3EQb,"˲lDQ,˲Ѱ`umyEI߽;/ e"+"ܴ6XU|<:iMEyQWb6b&,%eˣ~gq'an޽^oFqhajB BI3e5$PWcn[e)}lh<i'|%ѻWy)j]neU%yO8}*x2βnqha}1YYeB<gNmGk[$&p4 0 b??ߝHYow`:i<UL2f%r;dY^%cۛZo]/a;'rϖ zߺ;unNB~We;fzA{oߝ&Bc,^zb몓!xv5՚/6?zu麱#jVB(ڦ B阦~h4MƉa(2ϒR@j;B"%$*I(ۉ",enZ|c`( 1ZlGA xw$c^#,iP@U]ɲ4.""t۾纛uYUEHҴr^\] H*F$ZMy%Keᰵsg!ێȢIcڟ? ^x{tūSUF;@@n(Uk4{EeYM{t;dϟifl4^׭kZ䥢(<~Aǟ׹fj޺5j6_o?ݞNN ? ^L=}Xӵ]bPsR1۝c<)8дOz^lg7p8JQ7BT?y߻Ciꊳ$i)4]v,3t,nٺQVQEAi y\&'*24=bM6V]lh3 ȒZ׀s.4뷧":SRݖB< wϟ=Io[ !P4E1 (^~$YYj%I$˃aOUt@PP[ PP"-i%14 Ђ1&b~Wwi`U^+US$Vc{XQ, Heͅ EY,ID4m7gd}vy;)B]Uw!APc( 79jۛ6-G0[Eլhuv8eQ,קib72&DREQMJ0 Ui]T5"OhQPRpV!">uW8M%yQPEƊ40U?,WN]RbhQ\ Hܺ5[q]tlEamq7;lyM#u]1f7sƀ$߼?mw *D <͐0FQ\YE2VO_z` !zbH5iaY'VogeΎN6r#)Rdy.jܟOEh*Ik2ٗx}ݽ)Crl$8ٺ>Џzw$͏:f5?.]üfnr7SAa'JBI6~&sBݚ:Z8˸(r$B($"ƈɆArsa8$* Xumk,/tGXeբ #l`8gy E@Mh@-Jyܾ}lhhEIfz}/"E׀֖adIĪJSeUW-Y!pkrZSMUmQ,cmZIHi~GAt8(%Y,VʋLՔޤݶE P7gϟĊvwgVVf~$xv`(a) ̓\`y0 a\@?}{hw>Dȋz]!aaJ8Z;(j׳e~Oұ[V7V׶e8 |kqzׯ//8^G|ٺ~uwx^6e؆V{=MS߼~``jB@$QDEQ@EQP,ߕEũEM],VG~IIP4T+ VSX3壓QIp@`o{pxpxx"4:{r6i_\ *Ʃ*mAp-kݲeE9 nlRIUAh7mۖ\9b8qˊ3,(i9m3L\EGI#ɒY>"[4_kNo H T- B 0P;o,J!OdeTY={(BLv+I41mr3ʸ 90k// YGw>0J|?8NUC۹`ut\&͹ž*D {,TUUDDQY%a|6,4u$p IkN{lEa$;O=}*IvlbEYee fȣqvdi@za%eWmA1ɓ6?>AjndͧϞf 5E<6oߞVq<[+P <†\l=zr#Iu͍aʹZU޼9l4:M,O!=hD7G`0맃o8ZNɁ拫j){ffܪ*;AH "l6ZI5圣͎ۮQ%YE+ӰlQQdG޻w98KAd5|szsu3yz0rUSe(@(H膁9͒<[Ӕ$ a 0tϑ .oMDqNWb#mڢL] pBQ "fQi㽽1BfeQ&$P1)NnEZ [R J#$DA5$Q ʖfiFjIf8bmڽvPL^Y+jOC$0t0@DiH2 JiUY9~C\ng!z 2,cr9788xfqNI^v:YĄ<͡UIcᨧAa<-W34TY iyWUW/GQůHY>9ywvDzAXf5NIei^7}?Mq}VjUU_nn-vsiG!|(h(`^-q((ku-";`-c>v_~}G<v~WfAD첼qPt4c o4M!'v|Mjb.K ,woȧp0׿}͛s$rqx^Hʲm[(0<} "TUVwPVn0n櫪f"S4VUqrVY^3$ϋ[NV&l E1tqҜ(IF$y'!b,Kdgo^bPJZ떃?;.@o"+IݐL˾rjY-Q`vѠbTձa\@** X3D3qIƋra ZEAmKmU p:n BmX7U"BeQP! 𚒼J,< "1gH4ې뀉QXf*ϹmiN7Nf48`%{Ã=B罽uEgi25])H&jR ].~`p~yA;%a~}z~UZׁGd[@Sij8м ]A(-`o1 pATJV,OO/v[bAvv˲ȯ.VecvEpp06'|Go~=x %΋ܙ._oX BEJR(n;s"DzlY6!a7_?Q{2KN:MnFɝfK,UCͱv[ ɲjQd٤|񌆣|mۆG  hJZn7p!8dmwd*"MI6/鳯_Y BX GS$A+`v<%Iva]Al-e6 JTfu0V#Ps,WMц<(J8 * %8̳DaZUi@0Uwx2?˓rB9NUEYq0>^-g?He 883 նujI:,h}x4 CBƽPlsOnP EY4$?vm'/ia̴T U]1^E*6즃zwgRW/0sr(Ir)˒^UMY^ɲp=EQ:n7xd7^ !bq-᰿^/fݽ{-.//viu* RRWB͠Ғ$0cZX/iƛ uX(yղ=7jXREQd??QF$Io7+CW5U!ޞaae7`, ?( ڃa2*|oıX7ߜ)xmôTwN98_8,k z]]7O_m$4A&*B 4,aEI;:jv a_$tє-ۼ: }0Ve%sq5Y,ʌT(J,q8|y\,f: rZJ@RԫPeY =$7``뫙$⯞DMGƒ,)*ƶn-Dz|oq]S0H`لHt dqXmI Դf(ee!cQiw8l:IKݲ$#ZHL4wN; 喭sQ(K>_;~ieHҔVu`4*X񼠮ijv>,]?KWД$v^^6m^I+Xh44j~u3dfqiي*Ϊͭ(TtEXtcommentFile source: http://commons.wikimedia.org/wiki/File:Rainforest_Fatu_Hiva.jpg %tEXtdate:create2018-12-29T01:19:42+01:00ʏX%tEXtdate:modify2013-11-01T14:49:55+01:00IENDB`png2svg-1.7.0/img/rainforest4096.svg000066400000000000000000136362511504213346300171230ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_128c.svg000066400000000000000000073542341504213346300173360ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_128c_opt.svg000066400000000000000000022212001504213346300201750ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_16c.svg000066400000000000000000045035141504213346300172440ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_16c_opt.svg000066400000000000000000013107061504213346300201220ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_2c.svg000066400000000000000000000003241504213346300171420ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_2c_opt.svg000066400000000000000000000002171504213346300200250ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_32c.svg000066400000000000000000043726151504213346300172500ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_32c_opt.svg000066400000000000000000012655571504213346300201350ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_4c.svg000066400000000000000000015342351504213346300171620ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_4c_opt.svg000066400000000000000000004113161504213346300200350ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_64c.svg000066400000000000000000070453341504213346300172530ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_64c_opt.svg000066400000000000000000021140421504213346300201210ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_6c.svg000066400000000000000000027124051504213346300171620ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_6c_opt.svg000066400000000000000000007016621504213346300200450ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_8c.svg000066400000000000000000025526571504213346300171770ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_8c_opt.svg000066400000000000000000006430011504213346300200370ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_96c.svg000066400000000000000000063543041504213346300172570ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_96c_opt.svg000066400000000000000000017570101504213346300201350ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_opt.svg000066400000000000000000034341351504213346300174560ustar00rootroot00000000000000png2svg-1.7.0/img/rainforest_opt.svgz000066400000000000000000004116571504213346300176510ustar00rootroot00000000000000jL!۸©uwצV9|K>Qa̳ؔiư]փF+ʺwiݴwN=э~[<^땦})ZI~&G?C?d|"E)Լl w{5f5Y;%H\71DWj J]zd ?~N m}&1^ŕm(~' y;a92˓dž]Y;=QE~Z!W&簅uҧ\!4^;9C9e[0)\P50ㆇs{$kcf>26t}5Bo]py݆~/+,s/Sz/\oPqD:(k' 5n*pc)̏8ͧx>jt1OAl+/ FiN7 xV-f㋫a0py ]a@՟hsoܐi{.|/su-P9v@'k,GOM:q@.}M'wNu9A.Q9w٥'l.֣a6s+߆9w/vKS-z/ߧ(GA]l^<qkLxN9sQE=hy[$X[cTO*ŏ KmgG #qD[G%k?w3UGrCmb*b! )CoC- sГ&b' Z]MzՍs]=Τ?8/z'Ө 'ssYa'[t$07 Hv{;$nH8r6x<+#O+<(N}wbm:*7XE;Wm?l4:xG3W5kv`ht:F\J:I>W!MW )hॆMl}+"ZU_UwpYt.ƿ8Ő+<,&:'ᚹH y 'wB ϣ?O41[If&0`R,- ET\Z2 uv`2LiqB:N ξIQ6 mL!f1U1%D3Q|hF% alKdz$lXʫ /^_6҃Up0~ϼ9NyC@<` %r!C*TdF 1Mr.[! 4Ζp-r c!y^C&Wg< \Zoă!rU2 (ʟFLydՙgjjqM3R|67's0= _G=Zn "E^19A ]H4Qj$%! 2!GX\Ŗ5BZoZQFbV:! _CB+vCvF-Oxab/|%iK05occ~ay33m{_| nzC2`I 4@K gU^:bp2qe/5 bV`敽5EƗ@` "NqA4:N9ZpP*<%\!Pd)^9 @u;utn ښ,H(gθ{$>,%$ۆGj4#dgm&9LhIY)EGʆ3"<'gb!`DA@j8~5JvC[ LDK%~)jh=U!kTtIUK>.O&-D<xѐ3U rz؈ˆs@|) Z{cEWB9g3:[9WٖIHy9A#jh |V\D#ar'nOZ^!I7NjW'CŔ萗%bm9 /zZu4VÊ18FP݆M$wM{uJ_T}p`G:LD^#(Q+ϙvJ1_Lq/@ P Y*ml JY+` tM9uZ`\BB)Õeb/m-䯟`\GY𞚌$CE71R$c➸/ZZu~?Maj|8ZuUM ]%1.'N%yfՙJήq@fTJPQE{Y&)*2.jWUg@W& &bAң]NɶN&QGN3 #ZqU_q R8޻$uy~V0J޸Wt]^iYR/i7q7Ur/wlWLK ڽ{8X_ aJ 5+ z] Vts%6u)YWce$ބ`W7:6vUm~Qi1LUW)_nqzf@]ey+q-Z !P}P4V 5!Yqk plWP)<:ČN,_Np%07W I/7oud.O^ pTJxe~j_D;a*GL{?W+M-v)L+WY R9@Z`U|/[M[n8 lShsxA6ѩвYPq(4)K3bu8gŸJ/,?8*oBU.*; N6A#Q$Ŗ3^%Uz4% eE0Cx+&BYgQ6]E­2I"#µj @yPE3f50",$$=%8Ww5b4F&Dq s6GIEãnNrdp-k$vҔNƚkd8h2BzCZ."5bwVL%MsK[oX"ąW`[r>utH)p>Srf;m^눧t #]{[<r.?/fe(Q|ȧ*Nȋ{mYa9U6-ͫ,u88!*:a9qG6i$'=fiAıEC#R֪ǵi,R4}Ʊ̖WZU*GxQŖ->6VlA/ϨV(Y"nPbr)1i:C "D;@|qݜЎUUYś0YQp[䐻P܁C}fu Zb5|nrwh5cLJSy76tb^iGfvS *6NDnP9dR.B+x3@Wz/B\T~4b9JŴU50jPfٜ%2#wrk7U7"n}$>qU{6{FRN2i wU9GH1\B?+W+|ϰS%M5S\ԔmeYhQ>cRs4 `! =$ '*dY ,w5Ma ѴOs0` ~({hfAr UUnGȪ3}4^DA`^i,fS*^ U&df8pgx$Lm4؜* \|OJK,We[ѐŦSķOhQwMZFjwyKܭ]XIR5֋{L wNQ\q#AE5rRwQY%/>m=Lr[XIŘ\@mU b|(h@# b]<DICGtc Z"Ct|C5?@H43V 13GRb 9bT:6(eҎ )e EU5R/Kz)`'wƨp LIC"3_x na -`Wi=ib!Ӳբr?7Nt˩׹=Pnx-V#g\'a09c$FX.k` 1~+TP&}7/ns*2e.3b'P˾[i=W|Έd Ȓ IST]G5T ~ocq3⌿MHʨ}#0D_?; x ѴfhR~. z(ǽ˸T^zW 4L\2nsxP4c.~͈ ӇUYPo^; o}΂&_~qzB1yjl,CRm~z巩_OazٟV}t g2W0WNxB/-^sU( [Í36j!z[EU-P [vM,MtE$+C鬫 4Jy6^F+/E_KI9 GF\sc YQub%"4俥VhgrQU\S/kpNIjx>K.FnrzҾqC6liwA8I&w1;9zE[k]riq}`?0t;U):m?zZ}ֈM{ 0}d[2fp`Zxz(v +dCzĠ޸',L#d6,y]H]"'Ǵ=Ղ< fT >P/杙 / 4idPruϸbo - |5?b7 6Sh)&"29b,3Zԉ.]7R;?ȣ4;`HwE5=~tb&Xx+=cTzQv&Ե2q{3tEVvNLȢdOkq:/i| *WwiI@SoEMO)f>.1pkW' . :0-fcaTi lp=!LӣG(כ!?*KOì1Z[e07_ngmuBM3Niw?Xj新 yMaF]^>Ը2UUpz43>$Q,~|'FmQ 5i3.ś5-EKg&5k ^q;9]o,bgW}HM{*&Y yOQC[ MHA%k.`vA6`>w[4)9--fܝ3ǡB-Vq| ~kZ*Fv](cP<;X&-.n4LJeH,IWnÍsEe6-+ZnV٪l3eD\|y@hMc`wY "c5Dl|E{סa¸V5&Z=/'Dm@v$g s0zB2ȉ6]?u[ۍ,sЋiӶpPSY=iw)^w4ƩWTt>]8T~!jQN 6+ [24R]*z/,T}ӴzGA5Z>w G <0ԑCIқ@lAjN[H@D-NP<^zňUsq;TYұh,s2R;sYD'5%:{qXBZ6m<5,H`[1y{>jߤ`1h,O괥/m%:g;W6S(aFCd56j 7Ui CPPUBJ`5K )N "h|%-' ШFiѥ?.r!0X uz4SkaHZC O5:TB;׀iOJ/&p,ta4*]3dqyRYk1Fg;r΃7, 29hR?ŊpLsDF>Ք"z{`pЫ%dr z09&+6&d{b`\4<' ^9țhUk%YywBn6QSXC^0Kڅ,ヌtON}!K6Р'G X%PlQI}E SvVwQi[£տN?vRV /~J4_-1?)1/*peݭ hCF(aA,1l <雺ͱߎw)xBBk H-a@ܖψƩkmp yjcԽPpKh7/R7Z%C`?476MLh+oJKKGM쩤*Jv\+Sma 0J֔eWMZ.A,T87BdO;[D-iO5D0JMHm8eDa -vEK,ȥ˩=\RaR-#)lZ?6ChVY\okE:5"=4FGzf7MY}[:֋[Mh_M&пhQ UovpB4=3&-&u~o%Zkt ,Oaėl/ZV jks{R voȺ- eQP9U }}t,qGGGFh%hs[Nvj APΦyn w?J2Za OO#y\;ZB3D$iR0@[C{m/ `%->瑽dUXMr' X$4фZۥ= yF*G:ؙ~z^:0A9iw C0%伄R ;za?M{f6L}paMt6:tg#QĦnh7*553`Dz;bom h[Z`EfWţS*mQ- M$Pq7e+ ЫVv=v?pb~cMM»*z<Z.[1glqJPW)é- \{9jHzKsJ-_6hD lZ=45 ~nl'r5 ^ch: %1#h O4ΖФ̳ \= Q&rvY/w-Az٤ƉH˵W_uq]h &jxeIkL$ ؠU{ؿp6h&%%f8a3[oqڢJana9*;& m`I#9p Z0DM+;tPeY 6v_+ R 8[Ҕo$RnM̈4_y>6hq{w<en9JAbw;ZQJ ww>襆mhPM.pmp8TB.̖nEbf0 Y&QꋠE¥q`ck4]z-5(eYi 544iV%`?xP\XȎrUMZģj&BۉPhXyTJ/&* 0\נ*4uPs{s 4UR PZ1)Љ7"\)Ή3P:hv59?̡:3 kHFj{jurUW6mL[ 8>D.Fq4L<[q[U*81aJG㣚p 0E3bԪa- |)X<~T?ńjM[&@Х%zgEP:n:`- <WR@`NN6bOr0DM!,[Sr{DaE??zX6$Aq>_Ғ6H_/`~17y(c׾$B Q%ET mSA+gW{w Q9LJV8o?Kh|ַa^N[Zva"u3Oİj(:{QF9X0ENtð/5W[p&;)ƧQ'R6Ӈ*/El"NKܵnݕ'f'!W.C)Hiޤ'l%M&%n֟NkN Ϧ% m*Q?V9?REx9 8ߞ}[mLq^WUt-}=b¶~kz9/̘:Qm_n#hö!tav'zopl0 J|Tꕇ L(*?Yb#[VnUom(83V'^ϥ8I a1#xU-q8Xj!ڒŝMw6~i~go ԡpF 792χh/ 1FxCd#ѝ^ okx@l$`Gc}8[RF)燵+dEEVW#~r^ِVh(㐃Is;m¿>y3mJwo#Xא &Ky֞Cz6(&XSF\xy\`N0 AFp W4=vR GdhcY)9Baom_ZV1xx_Ȱy*ibђʌ@xI ٤N2FimY%SMnz\E(l,/:QQ\ ZIϝŽ덎/n['s r3JGvP _ixdPPu*q 2AArGhSڙQт"[%p =0K >jCʡN\29}7Z*o萳ɺ\ȞyϿ1\28S~WzDqؽ}.1H. M+dW{!;ØiwRVawE .>Dc\?uSyU]UC,MkCEPE6:RR802c-҃=\rAV5X pwVRzFՄP8F3-TcPI5W1XC-'#^l*)ï%pBDL\4E1"]ZScE`Zň&.kR=g$VaIg]p\iGnKTL*6_jn2;!1cb(G2e E`}rGyᑪ+وc&UFE-:%&-N2:"5 u!w*zQI1hP^ JqlDEWU n;EE4 0RC},|G08rR=1btke}lMT@yW)8ba^qEYtUT}Z/fN4GlGms0:uI6V!S#܆:g 562QUjWĸaU*j 9H8ÊJ:L2)!9UgPaKSC *mBL75S Xq4CLe}Wd#="=_^:>aP6 181 (\ g+& OiLPzJLv1+1hs/M1)rS\u'ψ::03Hdž!& 61Nn@(dH3jۏfoي?xD{fqce>JS Ji_8}"{-9EQ@[Fvt[/mv!@h.-uÒeCF$ZSld`} ;h뵔3|%-*N!iZT45}S8♡p=f|Oo[KyZ=uRʄmgTtSw;禄^tUy2`9{s$8ryͶ$GsQ# JbW$ӻ@pP;^tF @[g SLϝHU )#[Lt$VZeױJчf}x1ԛZ"xUN5QFASw)FS'(0ў7zjNh^F߱98<+Gş9!c@]t5Uf.}ciC*#WFF]̊OAJmFla6xtVŜxr`sUָ'9"v<=ћGHEK{x^`e[U9=^J eNMFA'Q'}lNړx,LEL^ [a1ITp_¨ VYhG$!L#b`͵!xXu=+d YpzCTԿאzW<=)^ S$yJî3V -n V9;r b55gIPլel`; ?Ĉl1ۂ@~ff:U hf&Qj^)%J%-7Y` %a 8FH g_ @~w;RJpz.5ѫWܣH̐ IӅA D X ̬0 :<9)RC0v@\(?r2yaС{.i / /s,(QgɄiF 2x [#.JrX Xb%$,+ym+(Il *3sE@A]Nb@é2i(FՔ=]{1- mI]̨N0HL bkn#EB0ĬڢcmmZBBlMd,'qn\W!sG\Y)z av&݈٦r4*k$ UV~ GM9QʃFV0L n_ ?F,7 U+0h <<SNͽLɦ xLDw^b1Bfc>b ߌ}@`4gl7X1GbfT$*Pg̍'OKYUu vb- "iq&ky jό0| ) _`M\:ˉP\1j8dh=z81x]գX^ePEa53hA)? $[%>W]`㘊F ?UB(K>MC~O6kF0Jy=+buٲCHoNzM.r qOn`ߣ(czFi CiY]l2ac)i6-M`)Q|4kHNJN3qfp?lɜl[4m h԰C!Ŧ84|g(&kMu (X`zI0pmaB7VjJ)gSKHKSz>fq#AT13Sv#TS$GbyUk1GXqDžl'˾rɌQ-+vʮn0M̒9i6V.:,f;?NX'GN=u8xpJ+Bi ftL$ ܲ |+ijWO4&H)8$[dABݟC@ m2mi-Ș1\hkb-Dvm5:1~9YĦk9+ l!?ԇֱx̒XrlhA" l}'\@L(Kr4Z>&*zFS-*h@(N&8|c3M4s{:SM^ns>]+{0hEV .;%{40z﷊eQ ᬷAL>φʓTL-ãK„Hk`W7a_:3AI$DK(S [E%:e58KVI~ٟ]ϫQI_8D^Q.gǎQ"Eϱ:+X)OCG/+%ȟ"H)G:5(j;(6W헱m5GC9]Jg*mHTYaUtšSx'bEi (@B|51xߐMWv`;KF:S|=kLzmz+zL%~uoNو8:!-/ o,BBaCu$ h4/҈k}D>&?~t yZPJ r{4X1nj.m#\'WKSaq%Q=͝6ɷ,܊:V$|Q_sEv2@Ý#JO;Tsj]&ƌE\M?{Z3Q}_3MO7Tb UT-&j]KiYo.V`{MZ[ 4&+DU)D&'HL rX7|ζ ئ 5RbO4i1RD6":e,J4K i JnC4U5`]=IcQErKSnI\jӝG-NP Jk Zkn˃^KY4[>қ^hYüBVEFG-uYϏc69\ d|PǏqL H5;׼m"0s\q<|(z̢D vLT nmg{$ Yfjӣ%mMwЍH_Ops$5[426ʘOHG(Դ SoqmVE5h?xڌPsh2AP⩼.[XZ/]H$``*X>+^jiF5A3s(mXKUwSr"ɉ>'rn u*ެ5g|,нܢ _@\ 㩿GƠI4G L7ch]WY h1O,7^|9$[rkf{!BP+K==:RMSZmĵx]O]}!S  Uf=kܜ7o(1:%ԿG:VcTAw$ 0(1կA Uo! o gyAǸ$f_|دXX,~>+oU$=FEYU PђEj w HvfMCZ@{J6YE(nS5S=-qN 26@M19ad;;@_u=*~XD+O%Cޱv-.*_WfGX4AxCSJ6d"EORO{b{ΕF\ nmG}8$6N$DWBSV!@)gk8. X7/H;d3Ւ=.]/ h}? IJ.'5Ps/y?!IIPb( Wha_'q֛Q,pimB?~k 2=d|bjHDo/Vdbd} -22k؈M;0"%\e]сktt<a.Qbq;(t/uUha%xfPT QI"L,i\ 5,NjH4[1k8.PڔӟmU a3R # QQӒq 9\/b^ NPnXV`Cma6K&ָj_Knq0yHEnJ *yQx.\. hXSi eZ.3@KqܱLfX%j>زs j(&ˌHRaڹM8-~% |orҤ0t_M PP45u)mPө `$΅)sig XŎ:nX6I XPxNiq9LPyF+^ř3܄06R4s=4WWr 'A?bJTq&{*{;Pc>ҴQH8Xt мR2.o3@$]9'vo\Z^3ČX(^* fR4zŠmm4NV!-PԶTV):R_XX^.j=oɫd4{u '[ۿ ~7^v&0;gw(N$׮YZLlށ@Z-$S9@#8†vU)wMߺvRz@_sGpb[n[lyv0P6]G%ActkrZ= sqXۺ0lbzz뙀tcOL\}RKΟ3 71k?wPTlAY1ah 4R HТJ6[*NNv6>D1zmU$~Ojh=0Dq3,-p_۸**ΐz-<šm+ph\ζ![n9SA@zFI\FYa5}FNu֕CL!D7 x0k?[h^㍫ҍ䏣UqZ1}>U6ht@W͘'SӬ 覌.C(˴Q ֔Xϋ*'-EՉ'dU;6<[3y: 0\m5UtS"ݒѺ! TzsUj$ĢhVf} "jBT>[Ua?%| XZ\8/YB&oocfYdj4W|s)eG-Q51b,MK蘦,fՉхy@uV8Au P年 Ʀ]],q&Ъ ;U*XAH]U՘C[W*QzE3-ΡkK2x F*>Cm$fR&[j#FC$qgg-hI_0dqfސ.sI?D!ጶg`riqyVdFy"[AkxY 3nmB+}QUX~ |Mdd7AZa5"5:_LLq-{]1OF 3->4Lklwc$FSȫ`MxV $G[.Ur)"/QՄLtI:nO<֬*&P4)drm4sʹ V c9N3r]~-эl1.TqE TKebS$-mzЌ9F9j.:jjI }ܕMt_?{w)wf8Y^0y=vO+ ,M=6BG߆ 7b Ccn Fb]ۛ;wUվg""2G`ِHe|Oeޖlw nEO~}rl!?A'JC|™ia] }$ʠ0,|_FzxQ'bZm98BݤSăͽʺhcފsIv\r9 HA0rÒ[Pu sE,$sU'"rK\xGL"q(ܖd]^๭|o4p@F+*#ogJIBxW9 o~mЯR\!tX,|-(PPfةh0M]ŵ@5K!t!;RkWL ,*̟Yy2֮oL.n[26g=o^bZ.Yb)3R]1[FEj?\W;‹-| EVq)ꖸz׻ceTv*=嗅?Yxb܄NttFx4!RU*m?l*Sg0q_Th`ij^Fļp oQ`Jk]GI|V~NlC4i#PZb5 ML=[!%'cCWQi㮗*ْ=[mb4w0qHiQ C־Y4ቱm/_@ ~^߶ߊXom^wWܑK iX&FBpy^6\1(GJTCꥮ'9nTX䁱kEbE'uN)1~GŌbL4@Qblx؟UlɄ8mޥõ?@ &U/mz޲ڪt?1v?zhC`_vh8#fQ6]=]䍕C8[(JsLKmϳ%ut{Xy?xlgU?9_ޅ4tWd[ۅ!DnOs@5p?ϻkQt_O !;ܳEF%ޝay{2CzTk ׶vIdͱc|K-sϤNoiJ7VdFs$Hz E{v fedgTf,tP-eh>oa\a*G 3t %hه{vUY~ jγ=֠_ex *!Wlh$Y ڳr3rn?LC⬙<|5eί/k4FTڷMl v5}[8k[ޣ>EF9&\ooR -Moͮ 5 : @v(^a ϙ[L Zx;[mWm"uMd>  dfdhu4|~7_Pbq/曵V?y7%&R>nz"ji;iL~h IەS^ICV&tn5UԒT[FBYC[fFTqK ؞ZG njZJ/_zRyqbNB %"SD84 cAV:EzJ%b2(yd7h(Q9Ӷ꿩2nw a|׹햭e"&!5T`6nzn#v WZ:yRYa"B,,CSך~h/rikŲ9D}Մ?uuʃ֎fmΗۓ&oڟ?&6;^b(Mp:0 -;Eb{4yN]tt'iKf*[)^4#Mzh9J݆r@`%dLvMе8tx|pHʰ /H6JՀv'.aS k\,gߌf"/Ymdi,MJK`Bs*n_ȃ[ˊbywKjM{-^bzQ><\[p]ixpE:!MA54LQ؝^E#P\yX1j.*Rs%g\ьv^Uu;1]6t^[ahWn60fV!TZLC@5癀ֵB-&[MbW u1, UPz.Ua}_Ӡ;u-dص5c#SDmxX-܇-s39x0mKM!Em\Uxkن؃PȌ.zu۠<]g?S|-F`y%?Tݐ9**Sރ~Q;fx"j/̾E8蟎/ye~f;Fg Ț[k<֋V/oTi|}y bR ж@aǖ).ϓz1[M&WbF| iJ(ٮ yTn,3 )D fT>?gu~DSH9(Q-fEL5p:=j3wS"-!0^;:{l,iט&Uƫy5mM,=e21&ь>kuF) k(W)}5\ޠ:lŔNfR,mb)^ Ғͱ;xjqj~K}H̨bMj@i ƌYլfo]TKW5 44m˴* ƫio[.O&xm))o`Ln('Jv5AsA9@~ iz TX+62Fo `aRdآ([ $Nۢz71.b>92Tu PlWUNP0ЙQ܀r蛓5/K`rew#V Y on]_m-56t{ouczc,a5 k g6evwôoo;ZC'm"_;H7xcw!T^Rؠi|ѷl_fsլܞJZޟ3jo'0OK\uRw >tB{aϖ`ZUd•RP |؊v\3Ws/UtF=qIݳl]`VYDm%i(EM wY/VѓiA+G44?GXqg0xe!*yo#;Vب߼8N (k]x\JOc1#J{@ֵZ\pK[w:^RJj}r-!X YqZEiC뺌F<1+gq*[APMh,Cg厝؉^F'^.g?hqR#En3Crx!113/!l5&~\Q\US|anFP`"R^`d$DeaGbzΈh!M#^ww)iWFm8޵4U[8i#@,oPUwJHa9zո Eqޣ~,P_'1J 8=Ah04&\w9/G/^Pi 么?w5%cv嚼`k8ʾ^An`[o0]n67y7!V,3|-!^IU};2;OCQ=qz#[16*9(N4E)^dVZ1Y}%( L.'jqM f4/>E,(Js"[Quبʞa/:<&ۋHdDUCgKռ(f VW ^ŨHql1D ?adrWl WF:NbA$ ;r%Nix4Sy2T K9#ko`@am 0&bJdX4ekpVzruw9d45'`=/vXF\y?*r%bOmsrJb@azoFL2;od_SZzKRd@)=21kޞuc:-ܴ}e$# 'aiq rBAR2r$dELct/p$ED&i- J ~/qS9[-d" S2koWc5"1A\&1hDpZJ3s_>_p|}>޸(~,ْ#Qkgg|*K=9o뇩P:Ew$%Y&*0cSMs%"_, E?#-·s|Q7?1d3hGf<Vr0J3|n;gD~ A𭣊b3=1J\m{h!p]CAA۠_}h[1ooVą⋔ӿcwst/LWhH! *Zwg411W8v7+eP%M6:=QiN~멡B͇i6`~H8DTQVEE1ȡܺu=[U忉-q݀fD|e*}3fMޣM-ͻ 68ԏ-<`3q[7$-Dӟ::&SyRTCV/Qpq5w&Jó؇ј ѫk.)1bCb7Qbo-XgFEKzD/ٰ^gjG6;CtAO,xlMyv40hА ~۾.Ư08l|h-;Ґ/ ZtK}4 ޒ`E^~@AGIY[\{PE@Md?beTotw4w; u[v^1MW|nY'Bq sQ?gvD #gЧnGA-ZM ,G vQ2Юϻx\Wdp?z'"\ӣW ;;c^C ؚ듟}|iZCi^Ձmt揆6Z =3 T{VGj61n)8lm 7( |gG1-e3frיo))0=(~( >DD`kJ[sysQo;XMC_ɂ]<]||ޏmyd5[+$zkIdPE~g /spc>TW(_ (eFvlO'hE=_g$t7n weGc?*z(ZT;7aY5. G~?hۻZ%whw;f);-w~U֛ef:MtZ|VYD-YaTEdyC| DIئxS'ִ٘=M %rz QJo$uQkM~ehSM7\-jV[ $Zn9'ǞQ_;jHcрZ-mfJ< n*N!`S:L_m~ȏ  s.{fd-:/vBPy1 M-!|Gh>Wn[4Ѩl8/ktgd~hh~'Qmto`V埿d6P@1-ѢA[.2{TFQ =j,g^a-2Tw.޺{mmYprSLgc=;L;c>Kț}KX&r3ƟϼKǻO~ʣqX+R߉s,4 ]KPǯpWnm)ХHVK}MZ?S+8ъE'3*B˗Ւ4ŎO-ٛx7AK(:-G骥SO˚hQ#[ٷ&O|= u I(/ik}+.WUhfܞeƂGz.A} Q_a9I7ݚF-FnzPG ΏbEBUZ)K -M?*ݯ+ SjRf+BEտwiݪ~R,&z޾=tAm❜Udon/|ZG&b-ӕwraQvޭsrit-;Zb5l`YjtZVG-;z1n7;* MꢱbLUt"kFQÞjŵԢiM(̣wZ-UZm|n=0ƀP}C At48װU; wϏÇr8 Qy fYG%Ow2Xubo^d463[G]O9C5^|r=W{Qu٠b/O|{*D !I蹉]'ʲMq43:!4o:MiO]h^|ܠxMwf3⬚k2 n41uѲS3ѯ"M[,sľ@n${:OӰ\GQKV\쩘pw@zjJ b}~jUI4oqi4:W52mȴR ֦e#`#X (L0DptPQjWFZ\;0N~MYXO_RGAMRZT5MZbi*-D7;]u~@Mfm1Հk9ݻE( Jp[?Li2hyG2fU1)~o䭅hj !Ӧ4MSoʸ}(5 TnҒ^֢Q[hZ#0jHk,>LOc+ᳺv-VU |7DYZT}Iu*Q~,;w׫ls^-Egf_,Z^H:]N新o>2 6M^d)s-hm35z,L=U.VfLp;._ P j,PĀ<ϧ5a u>hkl=X@s]U*qPGbI"/ǣ~~pǩqz[li:M`Mi&ҏ/d5l8֖y\bs3&Z"]r D6 @t_W/ a-yvbZGJRYԡ18>- j/lxu8R'd۹}~K509A\&> E@)j(27=e.O3v K\~}\SU)Xöht\dypVyL}}M#v%~k;gZk^2$UJx>jܛvzjI勮5Ldl[ª-w!vƬK\r.^$hew7l>(;omZ)mzw1m-i,q`RAUP1^uI{OL fH kHx4dNզw3a5ͩn|{$j +5O ?]m@[G3"Gdm(d ll Z ML_Ţ̳ ®.ibmߐj.aMW0Ow`Tg`jX-XHZ+ oʟ om(Sr&ZߝwlGY\ Gc-3=q[bFg5ٷd_j>;b d:$P8w&n2)ִ-wB߹7??k 5k.Y Mް-+D9 9aXjAdHV9ܩgone~kްv&c 0%|t Gw8jbtr i1H^mIOGlSSп47;{ ⨍R'8ahE G(cl//OnAr5]q![wy5jBI@j\hqK ".86"ڣ.d˶5U(ق+=oAig o$^ˤv%F:Qvƣ韦-_ji7/:T%1\MY!o!d2"}nP bSfGyV  )vF1*h^EХamg8T ,;g<'[ 37QeG)eӯi%T=>Nmkfh+Cb{-hAC|͠xGOY)PQG~~׼'a;Ϗδ"ʂeu]z4]J$Q?罣rԡz'Yi[sl+{JiZٔ cKY8.q\e6?Qٲ-vyˋ Us8T۳0ɣxK9hZF|-{ױ{NNjzjgG9M8>CN9 WwSMHv]4M-zmw{gY7|y|INݣT@ﰫ]_A}\Qiž5i)5LVzÔ?|~Ҧv;-# [FbiA9,[җ?R6a6yЍqո_-15-uԃ4j6ӻnaRCG~ ~r?O򝾂FwBHaorD9Rxe0}&ze!bvTG~;M^?J׵0 }z|nԿGpRGwi\՝^DYnel[Z!jiPŒLjt/Mq`yFR|OwVߩPG@[K;ȃ5HY9NQ UiErZRq7߉4l;gKŔ~Zmc==BsW:-V_5xiTye GRG*th,b&&aV6hpkA>0j9cY"m$)=ُ0Ŗ &-jkxDUH?J`T믾)_jbi/M67+,9^х+\bjAGssߵU/@}`P)-nU=? b"g $_MC-S܏zM ɖk-qv-!f~73k<n(8q&Y5Bݔ&a'=&5`"-Z tEk,J .?8F Y:?WI7Ij (azԢQ5V~MCnHc/sRs_i":WI/]9S-Q& 2|V%4,6b7>x?/f%J.OyŨwwN/Yg&U1'~3.A >_)_ 0+tw Yت͚CF[%"vY MBޛ 9϶u|8ܭ<֬[>۶-z1K~b|Nɐ-5{94gUe`q|8#?mߕ3hSo?Cz@uտP]G3g_;5ēto5,|VBd`+ßa֖lуUmgƢﯠ"k@K *Ҭ-n|18V(v䬹aRQ05A]|yE".{Rw_T(Eg\ǰC|B%]8&cxXwvexf{3]se':F4Mʻ</Ѧ.az Ŷ,.Eӗ Es^awSzOۙ{ƺu#~{@+_2,iqaq2s l ]|^DqЇ!.:x& C%,9 V1lj= [gu zH.e䋢EUm[ǃ/U(.Zm}?>\7hq6\E̽ϽOq]>緢ӊ88/>T܎$ٰ{~wk/%,d[pNCf!j?{Sb<Ӷ"!z܍/vp+Wi7;pYAKTkpq#wY>>z|n[d& f~ 8v/)ہjxoR/l9x5g{ J<ɹE(sWלD}07 4&yDqoLx#WN:q5E@y̭ͺF?y^hv8jxa5έ{4I._d"&ĚϐlR&CQN:H|Rhy&츝x&ͮf<64h \‰fEe@txQtۿOG.y-iنűk\ KD\ss||1tPUP18:DCu[g5p51'?,nPYa A P@, ɖ. &_QLgdjdyT9vz6%~~z4Nk0ݿ6ғkE€`S7oS<kF' .c>>ܯ 6 yCI}^={p]ݣ0OcDhpE2׿j,]DFv$tR$>0T' rڸqϹY44թO8早/HӶoKYvX2ӯtQ{ >pM 4e.B`x˵dҠRMb.qn wOMƫ+7W+.=6ŨBhiq JreSkiQG 3= 5/OfHKkٔ}hksXvP[1!_XtܨSQv6V]QLu2g˃ O^ULDNC #fsEY\DvڟrgzzpVѲ̐YE4٣,^ӣ_rxm]%->2fS~bp%9Rfr9.,Ud&hɨp#a'q}sELkM%qPg0'+K)--1BI QKZ3ubeIqV(RG0j00Ԡ'Fb` {:4"zD*(1Mv'gzI ǔuGA][f %]U&)X ҳ8X8kLh| }/.(0Bʎ\ȒFrU~{%F`8U NMVS0`uJp6w@]$Vˆr/[(\ ],Ar,$CrR=ȡ$@vzpߐ(DN9xG"=&vF(IAǒ1sbzO9詠zgL}PL2U/#"-YAB~ِUH@tp_uys*1C⁜FolѮT'7eAFz+ty bB'v n(}Q1|"2\5U~dI |^VC # i@'u5d}mw6ݙ3%z]PHhU&;2CneOSL+E's*&{Bcy:EM95!n$R\ZڝհivX 17Ϗ*aCq(``ٗ8҉Cl"S-Ӣ w)Ez γ~IO@:LG JK1@~ -\s,ɻ ejt2=u]ACFDG$&BVEk…@]Ă#ޓuӜ=ĥ h1(Ctv,U9## 2H(}H D_TtѰ  Y= /KYk xRaҕp@6|B*Jg xȰ K*Ѹ ōUMW-[%,p@_;[Fa[ yO˝u`,kBnjs̆\H#-nڬL1ïr=֩A@i@{ >2Dx`GuRG |?=߄ q@1nHHBvOeP>(JUEԲI~dgbAzk!ފMDFqoINHw ŏ 4.aS", n*2O#*l% !sX Z(=hBif,p!`օGdf{ZU͢HFiWHpdOix2 ##Ϫ5#S9v(KA!3c[?V2EPZKqÈ1 Ȕi:i_ӠSg;*RUM. Z0qO}MC!"B)Pِ6I* ټHm{ JS '~ X!Ax k avђ[ g tdVW 2,@-%ԩb~+θD{5[- xL#ڷq[!V3h@ M9UI-?C\NS;_4YA@<")b@b#jN\ean,V=LV7*D b(ۥ*~lXKDǪ-T@%^ĎQQ#;= W՗տq,"JL4 pXɘehw|%,C ORˬ㨌CR̀qn]d(юxFU w{|>MEJX7jB @՜.O>9R0wpnqίovIpMVP(#H; ;gEP1rY!c r2YZbsGrSz I~"i^I_jx{E=!FHV Ɋ&Mlе#${5܁g&\r[.J*nb:[%kY;(98%T4Jde飼${u p ǯSEf1[;t7砷[ܙA"SRE4cָzIA)VGkTpMāaciBId࠳LbU"3:b"4Upy5[UV`\H8oZ"91 .,DVs9cuh f}@jl(vRGżI Y7yZՄD)y}-7TȘw ilgԵqY[;;vbhԽL?8Qycsݖ83U3X4+5᷄?۳ pU; B+Y\UPnGf]iP;ḻPk`K78-%hpS>K~O(fI pmrzIy e%W-1_ ohDH! 4qojָO'/: !9:qKub]ND+\Uٛer~! ฑ@N/X\v P2݀zdQB 3b`gQSVk@`_+'%frnT$P5.#xjG+mߥ{0oVzVtޅPr |x'CEnzyUMCo W>~,`lɔ8 J?]U }<ʠ)?;Rj^Ǔ59n,D"8fʖ+RhLix3Gk4p*xiy a^nDUNU-Q=|9n*XP胙AX՞|B&ӥUlL5kPm˧݅ʄ9W]~SRzB:6nC 3 #SbJiu)+LS/LEۉD;lԀ1` E Bkpգeύy!Q=؅2jy&nf-SW޼]Cj*>ϭy 奯Ք DbbIm PsqfbhU񴥴@ͻ.\9XKkYhT;rRU9mH{H yu~6(|+EH`P(%lWl$\W m sgWi-<7y35Wҡly>7|5~wUP( 6 :ZH>ǒ%#q`)$'}gRYDW, ~`eWd`+Z(Q@*E1l %сj9V7KYos[|.e KuD[ xU8A0{؟%5,- dx $ޅJcBLbzT}snٰ{-n[j-LЦx#R;eK1LXp􈚣TE R aXh5;JU7 G!}Yz/sz_4/Y<1H'Y@<k&X:Ҍ& (y@bJ*YpX׫cφζ0? ~_(GSBxpM~ycUͯATHE3RB?/o`!*{H\1G-8!\ >!t=O6:{YAhvk:osP87!R&T\MQ8XAl2;,6xi35}ZC;2:tobUotI**tAVK;UV]-qB`ܓU#]j8zw{{॓Wܔ1؜N{<ڳT0c\Z:vg9s< |hZ%VK48{C0o^ۖzaQE4|fYŎ i bL s<4TԠn=V}E̖W &6jͯfC)g BsR&l?׳?Zu}prIZ<i،=E@q%>hfçK޴_g#\67V& !g U*5J,_T61d}{;a: 9[ y0JEsk^su}Օ#wu=ݏnDc?Q6gfuX>J7K/ܽƎt/l5řXhbq+"F%q~,F@;:^1Yȕp4P~Z1SANqGNW A=%_5>8νkK[8<*̖1U¹[l Ud缥eѴu0f9 GPEܪ>$̀;dWiX ւFQU5\F\0ހ5Dh6"YrO@p_0"G3$ V1ClU_ΎlmlQ"oð X?՜z6 i1][4͢2qJqEWSX > 8饎b*S@d(PUlT L\]pg] f0$sQ"Yn[7ΕEMFp ;Cvx ƒdAD]H5uHq+PF#ŲC%mKf}⯅H; B -0Nm+UhߢhT"$`NL9_8@ I! Q8SPk9v>B_%M4Id  -P/c+[/ğ-6F;+30:80$6~?} XAֻxjO-H7 ըUh9TTb*ӿ+v;"p%_N Zl|Ø˥bDdTlVC+WT&׊S 6Ix_ 5=.>We'f bIP1P~#HSBQx1Am9Rz_3דh\]dd+n57TX'T+.{t^$ ݈Ez!o+Cް s>rnd!: `EeopF["{xPP4W2٩Sω܃~]ݯc[b06_ Wr㺉\ٹpT-m Săi $?2dPIKF8km_mMpkfE֚q D]egqiEw͏HZdGګus&.boIFr([HuYq[A3-h\9kleOt9YР sh<`83(7E& XZ1p] N;bpOXhxOn2+XW.L>K&l%3,{Щ N_Gw0< "O0c[ )g/lsfE͑.:4?L\MiH B-4S䙘 lu/y;(b7zxqC 2P'jϙQ-6 `4WE|֊>Evpr$kC'S1QT \׬ %&Uo9Ub'T^st*k ?B$f0u⅒cy`Հ8_;'5v]LKeD2u V%ۛ:*2;)U\-\d]-ͤ\Ep--}XCq-r | s7FCa^eeW$ڔ(!h"xw_&8J{(zLx/sX* FFӫgx}{ԇf0.E ynb@}ntZsd.8SS4)DzA*M}ԗ/q~pvS߽PjsDa\KOpoM,` jW H4֋_UFZ_%_$K]q!^C{08k!/1Vy)HՍ ;hAЧ^aak@b >YݫPmQC`.(bHHOy1roagJ_!bW7Gѐp^*%lTnxb6l(JC})fQ$}Aw[˃\Dp9Rxf 2jneb) CpFY@mIݑ͇c)Fzp֬h{]8|1_h44pZ\YN<kQ`35kFym}!_hM0bYo tlj1|t_Tϭ]c1Yv3T.`x>?t;-GwdS@/0h[mǨPNqhᙌv!ض䃅E[N</<2?J~#`3WlVi tF `iM6]Qҵ֦V"9hTRӖ86g 3@ ~MR 1 ?7`(%ՅVee0cv14\Ō7{^1jT"aꈕ'$ƙ$55`>I'Kz&H.'hjc`:~+&q"ѡSˈ̦^tU6y\`FyNa<=*ynѠ a9E7Pv3Z9u}I%A4"[`!фxzw4R5H1* ![5uHZiw\Cڪ`u eʍ3[Mۊ$a*e1LO/t!Ⱥƅ4;A\n0{Ɠhx2>ĝzVux9o2w֮O~Ӄi"':'pCUbU$.;6wk(/øg&D;_>@1Pb*oM!s[W," #/D>2K.!lf F>-Vu;,Ӑߚ5i*Ma5T,#"aΦW1;qq- 7XaKCP:zblH>j#-2hW-Y4Q DӜ=@ѻA͂-X#%q$#a <><Lkc%'+BbƫPkNU2i7jVϷ7.};߅5{+" ZI*2ٝCP$Z+%}ʞTPV.>;WX=?љ;+BRZ Zq,_<i=KFu²V`4%8ߤτ%,#Ecwf&(83ÙUwSBьjcv-Hjc[1qNWS#΄s+k*VUq vH<.q)DKs1ٗFKeX#eu@ Uq Ήlb;V{]2\LgBjJz`Xa2[>iN<=K8ajDn eC:#U&cU'U-jEViQYEZt-Xw=1o'nUy\ T4_]vBa HU45R! (5 OK#-` m`]}qTE%NjjDSgmWYA<}$Wm4O(žSPGò3Qgq=n%3bLd9^Ыo7h)/7$K2,<_*,ST2I}og^-ylR ҅/ gDpŰr6].ӷd3]aV7~NzqB['$vn`q]ͺCi jn-!BWK Ye!*e/wtgG }2q>X49ʛD-Q^@#1h!Jlm_G<4G_g LߖFXW?οkLIW/R'qq~E^ e]4GP2w)C210B2zF,)C 58Iò$SA5Ltd1[ҚA.ɀ$dctZl9)Z Ɗmpxfmnw𥉉]<땐2TqB#Q 4]DI:W+`NS"ԺQ>=:Ю-b\XBVe }%%o4]xcYEfKJP׆sut:O]?W|qhukEFYYHA7eZ^FlvXT9w=4m&x{v/kV[Cd vrU+fM+g3PĚkېAuFQv8Rg ª$[k(\߲E1+_5=B2x[-I Ih~"RY [齳B3=,SȜ(iV_#Eq4]r".?Ǽ*.ؔjXŮ-5ϋӐ9!Fՙ̞FЙMs"S dtxeGBנ1F@Cބb%Wa*,&ؒe,A e֗GKR ¨, :'< -:Q,X@vd]J%9bj5 :c84\Go uyS񐈛f3ْ-/z/duF^c`5(*DxOݹ^y`# kϪHS8$5Z|GܘO4:K19*-r~(n˨%TlBod:DkPNɊvѤ03#4hL\,5JG;'5 XfF'B\-Ƹ ra*\C̍,CaTL+JE&l )n([/j0ܕ|흙Ow~:njIcs]sKpo/F)5 ƒP8vͫ~ndNNx[Ʒ~62MYr|їϚF]K"l R:~@ 5&Z3 Hs_aªBӠ EH5_"Mzb:*1G`{d:"۷Uwٟ`25--cE^)_? \.$*X- eZ;s -]~ݘ!m$.9KHq²D&ƾB Qu՟̇\ت, x}ɒj掄HB竜\4." rPqЎR#TDp522Y=8~AY aaH|_ $O1<8K@g;A`pj 3&3nJg#b@gA'ؖ$|,wc.Π9h*==Vl=Ph[t G72UE1`nXAsL`%>SU!5*'aTl7h\n}T1yA8~K~w '_56.83\$p߻Su9/')_\bIo3hllwcۥUU%ffrpQՊ7s!}*3Y]U-S ܏4µ$b<;cغՁӯRົpZ6s?Uaժ$Z5gO0ؠYŔQ|Lje\o4\BJ qhQM齃)THTg"8uT('/׃ٸA\P &I.򫬇h}ԩVUt\LlFCg%m;ͷ\rcuX|  Ռ"V=C% (eF/FyJb`ZU5oZ\%/>vRkO̾98>C*wzpI PcQWr_ʉ~R¾Mܯ2R**b\|juÇU)Y/]i rS;A xh55{Iȸ tG\~"n H>W 8\\1~W7;2ZL;7agTd. Wj1Wm/9'e FGlGx0[[/`lѹܲX{l(4Y^N)5P*ӅeAPiup E7T zMK@l[|(]!|qKq^TLdV@wlDN}nT%6;DC>G o"5ۂ^FXDf.pS/|L7q뮊gty*J.H&˫&q &`#z < y'.u՗vk؆+uyMp4pZ럺](1*wqDTa9BWLz,VF`m2 IJX[>CцdÝ٬mcI1RٷL䒾N%ySΓ~W(}aG7G4T`aoB>I Od`m4T)py[&qpSwH:]׆xX)fnI*fBB䐉eႻcDռ,V"H5;vޖ ihـ?ow'Ma1# Z?ԽSRI{y<>ah=IcrLx|*H.qoAnbܣ7 ʹ YRȮ‚PNU]Q}SGc ط6cQ-e&՛ CQŦ[.6ٰBy|j.+ÊVt]Q)BxsS=tK:wjٺ w_:[9k]E8( 2L@"Uu8+ AAݔu6_,Zİkz~Qe=ngpeV۱ .Q h>?tq-Nb&#+ A8R#Z "ODw){[ҫȄ¡\©Q2PCҁxln}~uZ VuUVҎVq[ZjGQuQG{XY;П1pofBW5 IT_0Q&IV[ ;KK|pUE5aF1H< t}+(a,` &a L ͳI%]Qjdq' wӘLh NHoek5jS"_izaOSWXF V+s FU3_F\md'zC d#1eā؜췝݅ vfWB>Јj+9 𸖮t_*ea=ͬVŶUm`>ݍ( , zy ^͈KeWcL6T8bSh ~v30&yi Z#[k,3ωkrֵa hlNaD-c];fx%‰:(ΦPӛl/_ G2 u":FoC>lhCSM0@$ `. :8)W˗#ۤueUuD`_ 왞^o!adhm8*Oۈ|ʰ[cKJ;3t5%6n1ǔ܎s 0 :d2ظLJE`'=5?ӺwӴRmU"SO!ڰiqi;8j/`~#(Ծ[IC`C9wX)z Gl*ӀY&q*?"7W:-7yn˃q&懘 ,#Dxeş6c.&< T=vD@EgQ|TY=OqZﰞ1$FLq-Ki<ȸ s0"V"dgM0$IP2lVjQ&*VJqZ(Hȧ9FSյ?-w@{JbFS="C!ÐNFuCXǽa#9ei_DF0vs?2Mň[>I BZcZG"yM 812]!fʦl:4M1?jFt9ơ0QQt/fzZL9,Iʉh} dfj] Pp{u+jcW65滫Wؑ Zʃ Em8MkLꩯidYb2DkUu/>\PZ1z^FM0YX N 6e 8Dzs4&j,"ݘsDST|Ϧ&ۿCs;">#LFiKȮK_!+`  x"Z>Xh4{'ϢoĢsͺ+ ܐX .*jMJLOCUa{B{R`@f]S@ )k)A+b-Efg#b||8֘Yyи|_V}3wdMj]uzϯFQ*. ոx\C'sފ*DB˷b/)v{R!ӃIiu~+{C* 6iұ ?MMRFB-YtO9%fd* 'U[A0RIWب-9s-ʪQlYqbL>*36*b0͛6-Cqh|rijFFTd+ɊGhD4E$=c`]Kx7,zx jhH^ޟxLr R=lIG@Oj)dK]֐@41()} +"9mKQQbE]ƐsUD29%oY϶f";yL-]t,gƷmbFN~Յe8\ 0{=D*di1g*vO nɢqX(>lO pI,謙ɚ0DlE/܅ ޼Ҽ'9JpD5m!*urO8 telǟDn\6bHt"b-hl]YC;n*@rmiqFju<$h v5 i!j Ш.q*pqzmVg 0(IFR67N0VKRrgo"|@E-c?VBՐCg'؈ގE+%;`RV]ƕjR]U43.c1Ygy=-[yp>^8k4XޠيB} 4ud컦)ERERzr偔V#xØ0]t9#7z q317KɁI"IB~0 EdVhXh;ҿb &2oK|㾏TAcD!n[tupY;c/2rHi};ܤ]v^jb0BDw'r|(Q:5-:o.df p؀VpOYCĽ/ٍ5N (]ͅ)OXMa/C>8xEP{ 2qXg>G;u:fˇΜΒ|z|0 O! Rz ,;3awhF&,D6Y5df/nVq-IUɺl}tlԊ"s̜9u,֡jH7l'Ce/OlǍ?fQ(*vS _kkT|OPB/YҩҲ@4E+Tr2Ërx*KYфW֢|}ؠ~A '7W!.iHTnPvQ‘$ixzOQ#HUMli*TX6EUfSxW'Β7zo[EX9rK^w64y[l@رad%݃,a x3)MmkFjݡWf:biu5J1D)Pr)LD,U)`f@XT :>YtF[G;ؚNIr C-;%e<3V/zK%W "RӪakFm,TyHL)% nbA|#"7gK 4r3Eq#LJX |\ \`(baik Tb1 6(nsem zwD2']K_eP4QQB YKzPEU7HYmpN&롪eWU A쎜=$u.IeM\iSZUdqȜxޤ(LnԪM@62%kۉy8=ʣԀUf'NK)؍j`(霪DU#K#, $A' 5NȂ]\/dH+WY#9ԣWòv d>}%QSJBY@-J- _ŜZ̭H@1V}_svUׂW >q k4W|ha; {'8"L\b H[qOe X"@o5,BE4Ӹ˘zM|zt?!wD[|3*9juJ~,{ rF1(Ay _٬$wSc~]KzWܫP _2Bv}IJށaL oT1ϵnm``* {* ,\Є DcFkNMU1Yma2y0AA$;`deSFRvBeBl~xCBTdYlޤ㥊eb &>`G]hOfG]N%"ty4 ' Hz_u6bssk~@b~3DϚ ).N}@6ŪI7 I-auMyf̈́:(:zeb=CqP䌳>JT)D!L4R:q[1j3-@VTdZN?Rm|Um]w@Pu~v*cۨ#nwS3̧%`PB?q9J̊Jߧ1*ŨD<ܚ8C(͜ RY8U_u f?XANbi-=/jUC:"0pn S)gpK)71򿯦(bZնbdA  (a]]:yK- [J$z#i GpdH "Ҥn#k 1bBk~V^S7d7& 1U<BUIѕ@Bݧ}c du ]k;zLڇv0)pѲ„CFY1SpF"n-՝]3*ZU =9sye@nCY LHbJUL&fLU)q]0aۋu YU]8K- nIc$!|p%D}J3T=87kдH[;"hMfy‘ )<;6AZhE˄hSE3&:dw1CYjBP*UBzƧE K}5!벪)qZC2c5A\^N,Q^z\hօ!>:!^GaגBpgh!;Sݙc(hV،Thה k=Wej,{ 2,KhT!Hco-q,V>ćқ+Ks @͠['H=}u ќ))|-qc0s\q$k ߏ5&W'̼#.6Î6^ve Qѓu8x vp&,$Y(%ɂ_;{TxBy[qb4 ڙʪ7YdQn;&WIdBXDxVgwtԱi9Ƀ!\fM^ٱZ, rw3Ү ⺮ݫ/ sLѴVit oQPmEc 7wA[ )@Ro20R* 4j܍XBLF'2X*KnN}qw-SʵүH&ϨS=e ̑k4Wk֛qâgY?3*nqA4M Eb3u\?oy%-j6dNg6]K2ׇ/$!JrySa ;`0jaňg 1;UGA z\ם,ݰ% Z٣Q*E `7f~]u4g;|V$YU{$bb:wVjT9gn;P> E(CC׆0y,c7P>3ap5)[$~ʅ4xx:G^S,lcʸ ;FLه܂xUˢH0,$w&Knj;6XrT{wI,TdpU*f#!Q<\f%ʮ\g!dY9ez௩f"|HΓXaChIՋ=m=*=].+kZI!t ٛ IHj^ >iE IW G-2`_̙Z3x'bnFS5SEZ|Cj" ;Xi2(@gY~`uû8ɲn"XwqF珁4vBҲIԪ]!ί""K:B+"~'MkxIgiBHb7{+ ]jTݺuխ6oF w8XNRuTu6{"G<^tRaLETzg[=d(%hEYTO: Ч[U:Pm,;/(R"KN29b(I]WרDAto@:ekb Z8j@FUl~->:Jς2 35,]1r?=2%I7\5ta.aƲ™.`!%Vp@]L٨ a.YR4EhdNկEO`gZ"jXo9rMGbT*Da,moрj]*gJD%x_ĭnX/␑uw2ر&( OUQ]rbua@CUB^0pbuj\p73=#3$]^1%}R{uq6&BU `zˋƊnb@TMyG_w4DE(#hB_3ئxREb{bi+Mi0WjPlM(Cy4|1GPeXO}A>[(sMa˓:hx\i$.R}2Ƴy] E_acu:줞Uzth~\g>`bd;wޫ(oamKTx2;BJl=qjiwB%ޖ[4XXF~–lX3K`o>!N=.9XO %܎M%!cyTț`'G3\S,Zo}CnGmzq _b涜΍q$Xo+Ds*8bCh_\xF݀kAh '։J-*XmWVE9@S>p?!y2phߪFReϡ\˰_nYH1>>_!Y"x1C<ǎ^:ox/O:O,,<3ZηVq& ;Y(iZ=l8nJat.DuԊ]&ިI:NAyf,UH(_GH8a1z hɪ>1VZx/|}r=RU̷ndb9>R ƣZFPt4,ؿȕx8-.[k\O NߒLmyfWF}{;G?X֫e c i+d1[,9úiU_ӫs<*e~AWiŘKU2SSil¦,9(Mu"5dz'1!ǘ[b0:ʜ&rgAA.(as 'ziWM~ϻv]m"V2S9eȏ:*nzNae'qɨ>8B-zAEk3QqPߝ'>Ї1|o_~q+'5v&j3@:O^k\OZ#qR̢+ )sIvw'^_/M38VĉQ%+9 !>Eb"gk>s'-;alMHnLelǓ75^,h .̨I[c$;8<6jJ O[l~h;. NWj 8L}:0JcPkγpkm_Q$ɀNO6 A+l8H囔.х8ЁvA, WK܉2 PW>Sy4/PǼhf'lp4Ym2m-[x MK*^\/ R(pP尐fidr;8qpʓbdM%%<@Vm`21#q9 FfT1*)ՙ@i47tnq'L y< ا*MXbUxi0O-yquRlTԌp#m^"8ŋGm2?L*bN-V1!gyor\Oj ɚWzmvjV6%I* lsHFjJ-1.~!yBZ%G#>^\{H~b 'XVLlU"cptX"48 X=eqUBᔙp?-TI;"hwzbASGb*;yX1X~2?\xTϖD2pU m 銨W}(JK JWF 68W4BllޒՊ5(ۮɝf>}wV-6fd~3}$@7DŹx]sa8e<'9fz 2y8Hdw3jБ}o#hm0j7[wچDHPO0rXNp-Y4_;~3$r[IѼv*̢tzXLgfFϚ: ;gAۈVkv {6U}ZǤSSrVEc{V|ehRy x@ =Z~x&]egM 5K!=v!B-MUPӱ™k>߱C,ݶdػ;eSr{M*avϠ|mgj h,S'ɜzj{QU.CnD yL>VR D8=|v!̢jO+##U;<[ 5G{D%o[@kqvnky ӞMvk8a9Ӟ|i,.w"Oh}mѫf2qS7]Tַ<yfN7(gy9^:il/_bͤ(㏯jSZGɛv=j~lMӧzF,|7*.YA[4+W@"*=](GݸzNz5ZHꡇ dMHJ|0rFӾEf ma"{>'<5md2;YbD?) Le9Zj U8\ЇM&]8\M݉i%ֈ[ǿ[r5_*hpMvw_lדeMM.dIyZldCW"6mJ1 ͕k䈢@w䴁4'/crGLX,C쎅#-O 6p/eVʕLr"gE+ Of' <&b*,U¶$WhMN)2AX-7 vvqrT`a8~LND%`DXJPfG>&vF"6˅-AW\c0?j0d6]P`bs&fRi,_ca8 [8,@r0bcB>;;|/\/P2XkfH30<~-\]0g`v:MDwMiXQv~?u!$j[+^$ei0o87CG)8m]쳛įaM'Z݌ y8>6<vUSO 'z @1ʇ|D@s?=)"98xxպ=NB;ls,"тb@FEb|˭*Vkzk{e qXz6aH F)^3?ۅboYjqF /zc]Alhk"9~z k]5&VVwǒy~A}Qv<¨>7lvݿK4zW7|:O(-HOp {%¿=|HS- iʾAB<^N]X56(fi*`度7;|R`})s`;ol[s(TyS$غfl F&` I&`g$m:ꙻi/LsR"n3wZXUmȏ/Y6S=i3D?d/㕁vt1mjr*#pjCJ]%u] 8>X3MB kj˚\ w!2h>>(S_% i`w'ONvo/XD&UR!LfEg.=1 DkjSf{$hskc">K2Q6F1`Yݹ#TJ)GLK6_m;dDwY~["`(ƴvm;{~0JZɻ%D ={Qs[vGHf ο] oR)(hh[Yt0_l`*s^PWyS&E?Y .d|H1bg!8/4l͠ck [So|$7 ^pba+ΨGsd(_&߻kHJo0*%}8ƉKExy(^!$0JTPѕPcNw0fq1g&|U&BW΀{!HwM/RF­${*R$}@ ,^7)efEݪF)%uegc;} 7X# z!+pp"re9DeL0 %\jdk9i|Gv}&mRqXѫ'դ']^@ *4éB8<:/+bk4sO@ZM=c{JkF)AװMLjZyC&l ɒه#ڈzw ~/ k3Ñ,y}r/ (sԗbGE;oQF^lDI~~uc?bB1aX9)is027?O(zC֨I|EF"e:3 0>Σyy$ @~!g*vv5_kpHBF1尖=&R 㛷" mT ,pv.̇8*Hbtjwy5o{!)G*1ihY$?j~v٥3lodc8o\mPUfXi:J>Nq=#M$ lVEw" r[ګ<`'M\Ϫmȓpa,WY )D/bb.b5ۍ/YADJrl.Ze x#Rp(LxzqHjIB(-57'ri'\:LrfHV-7X2`ݑ ^;2HiHHܴ1KO4qy)LC'i)muIYb/>nIq2q,=fΕQgC^bcBKq Bk Elb>MqBtCU`eUC#<3б[`>깄H1[x! ;p-^rkQ 92q|WɆ!W8moPrb/tOW?U7(ZbKik3HVl +̴>41!Lg_*9؃2V!cTxc;{Ǧ-$?0^4{Q>5-Ug̋!+~2ɑl-Lsr֎HW{wSJƲAC$AܭhH2|n߿'Jl|msגJ\K/hIJt1(|^vp!S +hϓθsLcbN~6t=yy,gd6sw _Fx/$R`oWheɴdj!ی.PcI82Y(~$˦9?XC~dxY4X|ĹU^V>c$9FtH$xam6x<%w-(H [%]q:ֻ8a "yyPD)vyw@Om2 (6 iץ|#"oiAQDR_kϤ2V$eϐ`uIp ^?X:NaAOߐoz>#h&Eq8Ǐ ?Rl%$K2(*emI(G^PT;$XmʠޘB`o7g7hݼH J pD( *z{ؘ3%^,{³Hhѥ鄉 ~%q%zI7dVM)*z!=cZOHWQ6u4 'T 1vLO]OgchF0=凮٤gwؘ/8M9Ǻm9Q,2l!u< `7BvMΝ(Qx#~dv~ާ!!!2`rH|x͜ڂTcs#!e7{ڰBS`Kvgɯcgc)Ϫe)55ꓶ^5_&rfkLK%HL}n󙺮ܨΒ9k:sO`Ds16Ƽ_,xW4'LIArEZ+se')Yt"|M诘vi eί1R_ÈF@%f, `UJguV/V޳N37ZV ~CN>x/eHVXe"+5^~tXsx#MLRcu/)D#.bhLյLn^bB4W{%tlCe[8S,*#U`&k8JޓUk9zC 9+k^0@'],31d,D}HCh6aGdqDD%_}p*5Qa|@4N*//v_a/N$u 8Tb){˼X0vZF蘎%H/} :FC7cfCiXb2$\Yv#xy3zR\t:-W#bȷG3HmT#@R!l1myx5 {D+q:DC* S1TA .1ֳA{Ak*#&ukHhԕX4 JpR!DD\a*59She=UӹD0@<vnoB4<ݿ"fZngʡsvu9!}ZB1=J 檿:pD_(Nq#iqBoө}.)JgC  ))v;BhNhv`}:ᤇ=u5eqom}ivthiJY4_ 89Nc}\k^a5g3'!R߲ 5͙Qv?@ȎgL"6Ԩ{= 0> q2Fղ}p!8r$Y21`bYUWA<):;XRkޫJ* `[]5C;pF qA啶ו^glʞ-1 JԱڲu^~*W:Il89Spg^MJO}!uD4.!A"{!p!`+3HHNTl[q^b>0yaCCЗw6>Upy}Mrw,ɚ?XY5ʥh`h{ݖ6  *롴n[k2 #(o#00%Zh=-Tu%YU_0Ql;X|Կ1u/Ǽ[LDaUDpҬ |&W|Cvo.a*>Ĝ|(jT~̑<lzO$Wxf< Xp*kWf&-%/7W)}Εؐ+ \ķ6uI*隲XWVʎkGC*AP d_RcR^%]vE[ta~ )Z)6)Vs@xqJ\WdMu(`6̲mLÝzN֣Z\ԟ"SH\:%1Z=qAr{KsTM`Ҟ6tRPОqM3NIm5ۥ\(# 0qXqmEaro/elڹ9xx9BPikpU; + :{Ol}L5h,A8B]m,ɩIx{l9ovDFMEk%5Ie.; Dy*TX.kO|XdYxJ6իTUtafr:aS{zi5;ۍBX65CGgt% #AmY]WGSSjOvOV1frٓMbd.Uڌ'љj]|BC\ܤ!gQ F gXyzJ{Ǽ>lƞ1uX%xQ^ 8h?6 A[ u-RYALƚ[UCa,I/k+\nnDT9#o,-@[y`Z{Qu~t -Cϥ@V* MB?^4A恀<`;H񣩰~,WIl~Z̀؉L@^2M K@3=l쉤4j~43J(ksM3y5S%"Vm L6IY4xx2p]f*Rko vS2Ūi׀.l>-uks= g% ~ٱ>ǫ̑fd ͖t* }"B\ E3G@Ѳ3* ES郋gŤ {KEрbgX3:jSji퀪֪wgffQ零.,a%/d|jTIGFك}j߱ zcgD0bfIn% f/:|mߚgգJ?:(ClN<SG]Xn[삢!fj%ANnb+Fr4-rq<)B@R `&h5kϙu'LY)dE>ABP,kC8>ue€I\lAW-q XȽfבTljfӒy@PD)kqnurlq{mI MFz:"m֯@ӱ4T0Ҋ.HOc.ݹw`B_ԪhәX}m*ޣB)x~`>%ǖZ(Gr e:4֥V%!~/5za/_򌇸݅zII5xG#p*>MrƠa,F8!g-ķd*3oAzMzM1f5b'έuY NbI8 ~T. Ih|"vt|*e0bbqq&p5 S! JM#6`0A3?L?ȎmX=/1lE筚S9{foEK:U(ѓF%*m;~JpWR7L uX`HF9Gf[~Jgi_z geO.мF*R`u g)w8N՝BS׆Fo^̙FfuYq)۳C!>ʱ28h1U`Ib0hd $€{B~݂nH5sT+7FHSD-F/ZD %aJg=eiFK鋝ya'<yLĵz/=Cd:)hZC|}b5̮0EG d@Yި|VY# n0jiZ- QEk jCi+;d$NR4>7<`svGhʫG+#g>vnޱF"ۏmʎ;Kb$XSiRQ$XHS1YIJ=tB{κ0={NV!⭐ +ړbB,<ɫ*(}m_ISJݕ r~0{'ɘ2|&DZ00YRO'z~bJ>Mhxvk gacwM d`"@i!9nk5:dKl;y)UM~k=_$Iut;:~!髆-f)tPWv'7<9jאWSD=[k)8gLsR57؉G{ehm_Q34~7 eY{Ŭ}F. ''E8yhJL,{@{ikƸI51y/P6c-A"󐳏S-ivW%g`Wpe#\[sV5V*yB%b.3ꀽ=ֈ 4 "f_皾%$VT#4Le) _-㻆>Øg$)v!*Oe'a?b~ J<2i^_텹FmPR%7O@ BX@Z5qƐd6sS)ƢӒ_7=5P7K )lR{~tqX\t >N.6hmJpr ^ *!iӃq-eEN) }Bߦ/Mj¾X?=.+$]QwxdY9}8O&9ځxI -7vP{W+_T}%ANge4HY /Uchœ3m!sNX|w^@}x('~yZ?oX8B٘IaeZS vo>N,. sfIϴ%.ګyg#3v Q6挿ǵTK7pc+MtxG8CSfɻoMouǡWGQf%mS~ǯE$=[>[d[/N2[f͊N`Y_]̑FÃDz'+O_l>7\?&C bx]Rx]1'q [LEׇsbnC 7g=͢4Zv8}ʐ/)b%ioBIl=`oxq?xrYY&^v^M6st`,~d6n&b [ ̸* h2Xp9kMc9.`/RLwۦ)Z̎Fn>LCQΩl nDR'9ϤⲶҿ!*2|*hWu@ߎ)#6/+6ڈ#'U\TB|=bF'1/Qg(LE*Ma$\ԁqvU 4r*1\Nk `s Q.7ViJ-=Y4 Ka~~Ev *ːSOSaSC=iR[̍˽ kl,~WkfWǪ)s iC 1昔ASZm J6fё金1;iM1@`J*պ-^"" %fAP:Œ'ϛ{cqzth/Lk0YzԶA:1 P+%wE_P1$>{SI9j̨H;ghT ݎ6t?"p\U\Lqc4h 3[B~L:_>NMɄ1s$7%ݵW.-3FdQg0 _fGST$(neZ)2Y CcWcNBnR~*ذs_:~Ӱ=J}s"'@0T텲Q^ mS VUb+xt x{( :+{ C*=F;&ƋT򫎞fmE21;v/LGwd؋QNjQ7o`kbmGx@))ز zTraj3m>i0>o~ù"̒qcWhQ 68 .c 2K+vZN׿$_k>z66K̸\uex<.w8-X k|R{z.i]K$UfY .jblw ;]+6(Q-0%@D3I#h{ pbHv9Rnv/x+~-(G 2R"K*AY&6U=6l;1fv\,PmHG0>A /f.iM} QѲba^}k#`“[ V3OZeoƩ `ϖ<AFTye^Fqt[bMp2G zn/kpmQ J"y]6GbuᠠX!apX--Qbn{eT5[!~<4'Z=p}\15u0ȳ5F0|e-U1ͣrvDK`e[7 f0;UDCp]oA}dꈘeD.`N/ůG<'(p LeEX;YVD&t<'Ų+"=uHhcaQ  4.OGܚk«\c`F q<OX'ѿdIdw ZS.sy8K_#o}Ge"ν %+zw-TK &&\Ӹ:!"IuŘޯa.HFXL 1, ֔m\DI#~- \F(Yp{n|{hes1C%lda x zLGaZt̯3J4N=%?lc|دV޲5( ͠ڜxe/C lϐCBɜ ؖ 5j$Qs:.8 3Cci?$"GmwY¶,5~dj>u7 4qZmw!*lAm.YtU)RD7SsJ]骴ak6>4SKX;ȠtIpCa3v/fGwR֭/o=G+/_ x*(dg*fUQ27M ;qv =hS U`]!~6|͸IWxiKފ $iĻ% VYONz2q@+:쉲hd5ԓ<|&;ыƕas g55b2bfRhŶRD63$:lP"YID۰l$/N p#5ab[l JW(K@nUE]<ϝ/Vn"GЏu9ģ珞3l8x/O+dc`M\@A;곕Z8o {&3"DQg*S>Q@yQ"J휌S<354)WVyq o%Q^3߰oL.PB#g5&829mt*JDS^ZН& m uvcMB>N[< {Qڒ[ЃUv %v+(DREf4Y}&a'+KՅgJksPWҳ@pov™JmTbTgpqϮX7hP/JˑCh JHM^<=9/זYw'8uKswӺ` O4UN|ԸuDB;{tHJK 'tݧZVN|-ZnP2"&z(y(AsT>zTJarGz aTӎ( ,͌%j ne]Җ(wսskl/:qv@Wk,2 -#vQSH/i5vvRzGSCG2T ZpVS?;p>yACRMw :n\ѰVʐn kɶ?n*6Y Ͽ%pbt Fl-+YYd tkQnH)! ݢfjLs+,3-#I4K2D$"{HsD~V1'>:f? {eF|'|ll%;vSb%(8vV,5\ z}!t6%YmEQ'J5 A{q9'N{.!ZD 1 0x`NiE7 ց<4XM ~O/ [3ZAfvZh>]{hv$qůYh~䰆RJ|!LCʼ!P|g>]u2H$Oې'*9{W͛Mm,abE^j\VYW[ҕⰶTY Bɂ+u[f\f# luokr5ǹJR.iA||HP 5e4LN[ ~MB=ʼn8҃lU xb@Y&Lۛ-7dFkɊz񑳾-ҧjΦEmAGU,e0͔\>PjTY 1m::kNuqdXC0+cHYb]MV.]=lGpd%ѹ,"5tyF٢|z/+bjjHfvвyL>T7%ek&ITG-Y @iO Nԉf ފ! m 6&DѕcۥH$$oW 5|AhBQ )r4Gj-]H6w=^L$a`,,B$?3[Jk%ݫmnȂl2m`AB]x{J8j¢^-pc/eִVvZJnEì)NVK?[,Zŷx!bJ >$m6lvЙvEVX-.f)xG{VGڈ؝bzvpׯ-d7Q*n5zr:Vkû=IJ;i$TLA2bAEY/$,C2 L1j.\iԏ'Tgy0:dPcГ晪Ja| ^.'&:!؅s&JIC#@Wd7S Çэ:rjnckdbC5LJ8_A`hKHoDŽ2D úCiU Ur)>s=c]8zh⎮8G pٖ&5Vɘ.žVujj#T}.L7;K4WK0"DE(yنKmyBNT݃2^FjNNq4)ψMTQt=Γr@hqWq!#a>ٜȴq۞R&ک,hBC)$iQ!7 MYAOf()6eUFw!mH!Nh棸F97"@m#v\:%UK|6(⋖D%LL|lЇ=bSszzQ*z.q^[.ޔoKߜ߬] r(R $I}4M[S{~/g8 |_owb"}v[&͸0`  +'&) $) Hp5GFXyʆd r=L \[09|iQCiFaG<9)L#DgI F0it]8H|[2HJ ]kɩ)[G;Тkjq-WV&TfP¯H Mi (YISl#Cx$>ِ)0jgRR;X"Dۙ< %$`BgY4<xEtL̴. ؜K%=Go;u.'us'oCr VL}{ ~l8E czrЌtڵ \y:q5<GL(k$5(; d\kJt) |׆?M{Jd7P8j6& ,L)RN:c06KZV "zeIRpUqX2[Wlcw @:\&ARB,f'\iĩq#|:*_(_o8GxW@m`8S]9QB@.z +0Xc)w +]a@[@ rM5"o.Nܱ̰ىmiIt^,#9|pT%#W1oi59Ǘ| GCVMLƲkcJ-)!)p +Qf+FiYS:Q)@͑:[0O%i^<{=9CL99uߴ| iޞjsKo1UI?V 7Av #b_[q|̷#|]2ΩT֜iǗ̖iW-.s7 #'bݦmp3y2قqLe>Z8\%B( ~0_-.ʾ?ÁFՉF`3l?̣o"ȹg)c&"ͥ"Ωk=XhD\4K"68`-Y` S5˹8q95V"Z{Ȧ\v{6O'bfo$|R 9D味 05柙agBrҶџQ6j(t42kcjr5]ĠǐVE&z~6;^n57;xkp~ï= n5jF/n=۵﹞~jҺr],Ax[O3j c5B- :-T8鲓F.U,GBsZire.o wX׀VB6 )59 祈/K๣bWC놅K%/<\H. bt،kQZR8qlJ ]1Vs xz@P]Ǚ> u}-/9Eg3 'GgKxcpZttיckNePR_NO r%jrr߆W1+ `1|_WiVfJ ]1,$X%r }c=nŠǴ¨Ee1HN1`|2 #?T+T"bhgCZk>(8 DPtxyeH(;*w &K5m=c 2Z*eԞ{e=?)J:Ym͑2-d6jomԣl1XQ ¾Ƿ)8P^8gޔ '5>חBʣ9^R`,a= e}&~DڢBKD,N]Sc`[w_UKXʜ%0޸d Tm9+[ra]Irc2bZެ/7]@ejWI,T EYg r-dKKx#AwTS9MqwWׇY{W',?ύpn6) ZhYK hp F͎AY H։lYD6mj0*P6= ӡW0>w%wZ" P[Y>r9@ꗎNaY2a R}{k.Q(FkS|$!0T$nnRB{S-u![s&sJirU&Y9) V+D]؆D/J 2bx,UP.4~Cz j+&{s-0f|Y"kxZqΧY_xnt8$' eIHFx砰CUo$[@rj#1(=_[yf_ UڜӰ35 Dx@[ux`.袹5HksƆCU)5 KgsІ+m4ws. ׯ%&aaY*Q2OOϋ d$fzg-wJw 3+.v:Ml(DYG4D5#@hʖ#`PXUbec+k#Oa_v}!߆V V*YD5TeHp_XXo4008bxHy8iP0)19,CI@xxnmȉJ(,p ) f蝐OJE=\x=0qqBJo݂rkb " :y(`R7b]i46}k/('7fM)'Բ&we#S%Tsi \bƙ7&Uo4=ᐃ>1s;fRPܴ.g,x(VZg"mK!ytmh6^RGML❝_>?$<_k}߿_1:@LFB&wegEh?S꣕1ygyw6 )vЉf{(Z"yKy/|y [:'CB#x N ^PuE;F~ͧ]"F/] 9W͜?D8!Ap]r~# S^옟{ΒB\ \|1jmǐ?k!<)O܄9[LevCP(h̒6É|\^+Q"?>'U^SREj1G bt:[#ån+s|'.p9&Ska3ǏgoMcF \Jhc{c BȄ_saKƧc)<NU\k15\) "L\Ny[ۆ=F6gN:ҏ׏Pk|֒zɬ{B'ԲL |ؿÁ5f^ѥU1tʿ'qQe۾<|NFpd}f/k]/0 E &Q{ĆCijwvxpǤkGs8Yi,{렘Hfg "aw7#1ϊ!J-9}WZX f[˵BqcvlP"~D5Kᑵ9jeh 4Z A=^FE$F2~)|P|KED.=XY.빐duPyhLeW 5iF&]L4!]5e‚D/nvaoDzFwL|(FsDKh< v##(=[k?HWךC[=>Io=uqQz2I9/vcFH%DxfW෫آ?xVUA|du\@tGB)hMCea¨η ?@ IӫsoͼԒ~\i\r %-YJ+}ýx\,aJ7~9]bک8+P$AFPpjzhڠs{)' p3>Z]cvrذED?#('S+ϣJyFlDkO'˂輳ɾb#2.ꟁp69%)*ˆxDr D9F:G:Ma ~TSVd ¦I6#7M#~d~XqN]Iȥc>4ˊ!I'̓\pp|}d`㔆}הw٥* טM51EFVFɚV'h(z\H^xTgHî,B3+"Xf]ML^JswDj1ToOE(biy@iYM5t¥#9ͶaUK{x.pJ- MNېB<-0KfN+]%dp 7@" *сwm@*Ž e0Q YpsHHI ;1zw,!,^j [g[pÒN{-a[.d ¨$Y7ϗyS)ŀ34ufz4*VipI 7EWkXFɽ͌ ߿_STN B#iIjƍnJJJxm:W2^*@26+&wXkQ-dVJݫOE4`0X9՚2Qq`]~ϋu t{ϑM 3k~,˜o㖭x _ڸZ  _D+#yMJbIac@3# 2R:'*CoD= ܳ4 춙:a}jݚ /cpcBkѭ^P$]}Ӭ<eCWDvE E1IU]x{5O{3D[{vp}Πj/ݟ'+~T&槎3=oeYߟtz=ڭ:%Q nrd \7}2.x^WN!ºc\[~H>$pgJAI"Zy\Znӵ!aLjt4HN@d* y|#RU,0g1R%*q_4f]Lh74!cFul%&|yۂm;r|ɃkJ97]%yIBeձ^ SƂC7k?dȃ"4-dujFJ $˽Gŋ! rsUp3@șgpuBrK@aU9YIJ U%AE$&cNwq^G\v6)I$lcz66GAbR'50نF';=A_ۜ_c<SZ~ [Z|j"}@9&aj4fpW>T-27)rw[=<(j qd^XH`%EBy }#̵!?Q|ɊTG x0b0rNB.Ii}|1+님v: M^4ջVZlץBYBH) fV+]Y4SEp*zbH> E ~4b1ڨHK$gdB{怑o1kPhR% ?a֛;@Ҕ>{}fo/.4)$r bžjN"Bt [I@kF%[5Z"tuۆ@8qoKҨ4eйrqMVZb ­bs$ܘ[Qi[A=$o:O{*<`L[r1WD4Sac6+ , I '1X e#G.)ebǀ>zG7٥.+̠\&0AظoiXpT P+r!~M'.ŪmgMGHBTFbf3KNLSPåtH)/Cj$WtW Z_E ߐx.X1԰]<#uLWds%z^c*-5Se醏7&s$2e"@a i*cU"\bt]D]r}X4f$355Ehg] ZlY|s*7=U6+`]%*^{t%oi?=cx3=ykxҲh؜f] ;|4jMO?AhGOl~Z^I6)JƨKݶh[%GX N a"Cp\#C##NŞ+N*ꄵNPSG9הz0;f$b)Oh je/I0< #Ybn|Tɐ9y"6*mighk7&z­_֯)!Fp@~9r7sBඦYcu d?:ޮ6d^?dX+cS7D ]Pr*iIr~&R=F<o8mPދ yM3ޥ(#t.L)({fk$ErYKeGBSTcg:k*>LcMeF-|E0*=v!Mh=Y>'vEpNf+ó'lqs(6,B s38V;<<KP #|I;>8ֿxR޴CADpb,]?)yN ۫/ HRl/uմe)f AJwk͜ 5_?)q@%mbeBiRe`0No b4\AϵRx2 kMp߄-dc~9stXVU f2DE1fQ2m|i*R'p8JM䳦&FX"Y ;Q/}5Bܟz\Q/P#'L5c3:x5IGvZsÅp{V{YP pyW⣦UӹH>b5_/7 )A-w8 ᩒaU8ϛJEu=o^@cq똱ejmou }8<>=_S06Xx_^AH`Bֶ[YP@.dJ<u 3QMsԖ,r!&Ut03g%_,Iux}z`] Jg`Ţ YixO|o2[(,:XQV,mY o 2 hp% 4[VC5AD"OwJRB ߗ1 .ݭS*c6S 0; a#-Ndp:?T *_5kP+SH([w$Q`"sqC_GEㆸ x1޹MWhb8ѵ9E:!* ?s R(B_=Ns)ϒa 6(zmFȒ# Ka1]ul!&ܨޠ b7Zu'{FScbD ,7sk(daUm)Hq'i(!6|j9m3菴)蒒k=0?z#ɰe0&hv7 >tv2,jwXVCQfw4S>vYȇ=n Q7Y”ON*-x[6s[M;)247q[(1WM`X0PǓx؊BI.rI*? #nDosZgX]ƭ؏_><ޑ$G"eĉ&|t&{iq[0Q/9il?̭qpeY0wה[ȠWp v}H9]·+}jutf<_FZ?7Cܓa7 jw煥DS+V2a{RO^>眎Y㱀 2}'Ƶ8\4>jSJM_NN&TFr6K}6Yf &?p:#|hv>͋hڲ"g{ED>5gϵ^c0U`^^C6W`0$i@_@=Mz҅1;L8`A: %*yH5M\uRGJT)"r,ʵ^#þ$zX |l's#?3 n3,*t Տ&~L*ѥ81:3m1Pn1Uhd &rF.C]mJ;ީYuO`8*b t.A>FT) YƩ aC3D:M) ("yc>zf~9~^(BHCCgGe-Pb!X%'ZqJ{|Ex@AlݲѓS:#}E_c |Qn/?29*lL=I$k<_bCS>+DddJ w}tWь=K)A`{}]G:pIHK?Ty+@Z5 .4m*]F/`o)ԛP\*.ls՛rd3:PA|bo@j`edq#Aݫ@fI(\Sp7K*`Dbe7E2W(?02 ×HKG>Xgn8J5TXc H1G'~X #&3fKi4rcC>LItG5`$q'%6Zb0 ah6tXB٤ zAbZuX+6ʦ0jPg~%`9iݢTwԌ')CH*B|J' Z"-CCտ]e.Ld2)kL,CvSh~J5%pɜ>#Z3[`X+,QGiS= G_XmR=z+0pHbehHs sޕ(}4jCb)e<2!j0/]rp َ'ͣ["YwVP?$2 :Q ܢ:A3KG+ `XՖC4bAn0[A D4*yU}=4&if 7:SWWn!TmP=C90*5&m"q-MRT}3*^väpCTx@ٖхҫ5^OYcof57(C ֠ u5xDE3ZA*UiSbUNLU "ʚ^{V ZTc&ugX9ZWODeH4@`d:v NFYkUů5 (v#;HfJPf\ϛdwr9ʀ+㐺غJH@WIbAB)E EO1&FPg3&ʐ++!&Nlj@lyW$^?N޸l5T%(AU,OҊtjϬ$:V^R0']X?ꭟBV6}^~;0/"Hi@V&a9X4 3 AJJ3DBF6;$ %Frf |hI4[pj8MCICV[ yӎL-s[5}SZL5/˓ļJ] ˝_߳mOLF֨!.ŭG}'M\N$uYȱ8j t+]"Ţ\kٍc:ԘIJ Mk00t]mԨ 1' .Rl >hoӓmW!S_q51nEb_$g|Y/7PK).ޖ'2Mt.s() 4BEk"UcQeAČ =KQ(J 0 #DT"ɔDT4fĦ >uc܊|2&̋x5>Aϟh.g$6 H^z~ucbIH#QTW¹ZYP՚S -.`,=oL8ԓ"}Q@F!@;a(\qӼiNm\m"\`[:dOdMHh@K5e<}ԤJg JȈ0)`#3sZ, ϫ6K14`mwѦ n%f@n.hF+jg9a#Ч穰z`)}CȡЮTΡ KQt_ȘVNNGUT+laDuHi 5 Q'T;_̐HxxGvw`fRًY "-yhJb.`DAYLE6>I"k f%e-cujkRo_FbTj"re'zm4;?? B` ط< t~QJ($2ےx!fyI+RC^ZzgO#|Y)'{6!ĩay[@ٮl`Al1Sana.'D HQ$\/3+m bq]NAG 9ُE aAki'О)!.f ;  04qś a E MRĭXx5llx[k_ M֗lBdSbl3w漿' ڍ!-'',7mY!׊C+SqZ ӂ5, ZoF`F:{`qZVIn؛ƛp44YmnHMcҙѐ VJP[<񷗳AsNڠ"3tܻ[P8_~0^\Cq>}Ɣs=qYGC4߭ MR\ws9 wllXߠR7`hh<"uٻk }Oe橽pHXo4-Q OC5?HqފqdٝJ,$Ƹqclleķh(TVD0$u OP -jͱQ^SmP74e 3"jP0}6u:sݩ~8#[Ne^WjL{ [n?i;q{mDp/΍շ96*r[ӈc79#40ˮ˻/*,ר orz`rA8Ad}|^1/քsTDi*»x/`[^S<㪷~lҏӧKvZ 87mrڻui?CbPD,%rzznxڬ xX|!P@6L>T #㿻j<mYRҳbt|@հԇ>TElu[0} ORe=442$NoɆY3nOOlTxU_T)iI?[/ba:J.[R(6 ՑbEYOU9v-Y. /6QAC Je~|NJZc3ktѭcH&Rek{\ς0w}phtuE?ܑ0ka0>е^ٳj=&sg_t+Lo&m#|@u,|F v( p\ײѿ= >a5k'b,scOٌ%KկZ7GB{0;,}}Ұ7j 5oFp@mYq<ۿ8_&}ϯ[9OU&˿y5v,or8+#^1 k~#srkTn.vF*BAaQaI^NcfGAܶɎ]GNb)noo+=OVtB)hz=U= [l!7rbwϠH]]br}]uȮ.CkX~vyz&{v37G eœct18lޏ"9{&jRЂa:9&xO[x_?O/H4=7M`uH u%Q:ȣ{p 26xM}O. 36^J|sי0x_J!(jN}t Yp#Mqk4e\wKs5 J"vI>Wt<򤢏{,?N]\O6/Oi+9ն;dgwu\9blh nGtlƳ9Ƹf; 3piFf$CO"2~4 OӨuN#JXˍT%HqP!.$2t#E3TYb@.”`;Ccjkk  ۮJTEtGN}CYLROpIݲbz E~> ֈu޺6?Tq ,FMIlۄP ÓY8F4~?~#cY|9ު:ۀ"sr0kׯ:qφV,>>q}v#?(30U.ybJ& y(v*>x}o|oW*´ D9 ,ED:@Q7t娦^sʄҷ {95DV~/$q_+ehCO%G RgЪFd%q =xjmFF0 OTkH9eYzr|=y/L2Nf*~$W#2\tiHLڣ~ob3 5,y1 У={]?nޓa#S 1=Ts"AjO"+;9ݏKE==5.;lC'@v[Udwt=qO)vȹE1Ҕ7V5MtWl rY@Xx_]8;bv{-RZ2@Qu]VY?\zز-^ޣmתzA ;>s| I>DDD:q\"f03<vVBmzc+Ə\CN(  G*X:*&wA~.P8(Sv 2}_"1u 0&=7WJc /^A$GmpiܞcvC$]"\|?wE]q򏿤t4dNN7Kd[\L"%H4XBވcX  YL]S_`\ oyWǷIL7؇nkd+bAlF4\!(*~5R9.4a<X Dk8^#ȃyMu"=ݤs4\zO5=KwuCnxH~'B'חgkׯkJ k, ݩarŰةy 7NArd_a'\A]ZȈy1 :8|`sXqMIZwD WøxP 0AQ!cJl%` QFƄ"k c&⫕ *2]8gX0xzox}@M'mR17,0:WnO{?SXA3:t%p6XG}qK`t;*5:1:1>ct@څ8DWqZ52IAք)8x֠p.fj8i28,"lP#Wd"œbTWnxC~̗Hxw#Ш 1Xu>ߦ[&Г _a C=Ƃ?ajD +5 KTZ*c4vs]tY'6 $G/ b8%#< ԵFp邆"(eTT.}_h-~lO(a/w]2Q1v1iJZ?}1q{A#@/ @2_kp>M5zy[HАNr8K_2`?c~"l*M 1١np-2My?˞ŞM@> wL5zc }zT TTѴFLm- czVl8Zv= J#]tӂBП g)%1|?kG ȀFuXkxF9VyL$"z%ta Ð:kk+ULܛ~dBCM}\̑ _"xecL8 ;KFDPA c]f4WTm ,#sAIJY[-z"j4wz{5m~r\uEb1o.-#zMi-N_ܒ" ^f2%y}eecl%UꡐL.Fvx +YQZSy+vS !Ko|ϰEOZ #6=LZލAo0`]-dUj&S^:LQDXs靃WDb%Z-eCi(iG8 >hfqs;!VwC:VOٶ,Ƥcu^)t:G^%kŷ_cW(M^J?\9Yc EZƿiJȯpثVX\b}`t~Yk:c/:LL(2SHVr 6;F#op|1íF5Q&Vk^ئ&vLقOWq)eH^Vh5 }YյP;&0̃K%z5Y' Ÿc~ 8=z90Ahv#Y" `Su;ha%pd!qْ@r\d}Z_z'|!wY 395,w6GhXs' kOR-w2lɨ<ᲴA1r*dj z֗Ac+ oS_f3+5JDyeut<  cxڿy4u1Gz*==,̺Kj 2EAw \rc5T\"Rje xT;c ~sV43V@ֹ䁶r8xzׄc=U{djMHq .B\E=C4}9=s r,0OF"\X)H/TOJijlBD #Th ]d3L)\'ȸBT̑z;l)ql$ǡ0kHP*I _:"oף߃ 5^:)zF.+ 9N*t.Zɵ9X{$CX9Aãq;W 53[,rV }WuzXlo0eiEHZ߿Ex%%HS߃^rH!*!iQBlR8٪`%6ѧ>80" k<HgJ^BTiSz2YSmsͨX'T$6}58Pl=J^Vi!5HqҒf&cAa+ kCX5VUYxH-nUUX{06qy.̅uHi.{D1Ud e3".n5JNT(q"N {P7!X4UKQ7t*}()'{8=pUls#+}PL#//\tz@o)NMKQ[_:;%+ SC V0j|wkGEvᘐ*53ȆxAQX9([D;A2A?`lXGkyG 渍_}?_]pKNYD{ ߦ{ @5$nX!(C *R1ρ3lVa{~G*J?rR' lZ)=Qd,klu$RC (bnD/i,aQƧ,x > AG( Fx>Jp-+ G1B!c ZfˠalSAaMؠ:4 ! h{OHT]UQNjI5ƌt-$㤩um{~q|KP[B%WjH̵00遭A55rHQNl%"tk>Ջ.ޤ #0!J5iJ JS,^+ú0`>#VFluA8edI ?x~~Kn\ylrmEK)br6z$UBP!OnσRnB瓽J& 1,l1j,#tAURMQtCJR? (hk@$hyJR`'y+><@HSi%=}:Ύx-A^P3T6Eb]*ra,ҍP%i-$2׫h <'sꐜ\)R#QUȢ4s'"V Is7"X?$|tX֚4d8NBrt)$8Px ZB;%Zpcd~6/F<}|힌_3Q c$BmMD-c5$!WJ%׼bz.bJ(-*HPaSDI1LG=l\rЁ{`c3cZ _52b )k| N_C0~P7fȥQ$%PV aJ\Fc7HO + wa5NC٣ FQ[31o]jAoWS2NPhP"|@Mf`%*ϳ֌@vl1b&#̀w44ݣ.S"v 6Xc}V> 0g+FD~r&J;ULϑ6BvmAh KzjgHOU(*MLNkFO1 \ 6ѧhapaԕ7OUSx,B{Bz.I'7ЂVV!$lVa ] O|i4bU H~Q:`(Q&E.Ϡ%?^NrX4/FWR6B0paMNS{< #a}\ 6[vF2C6v jSaή是Vue)bnh4)_s]k>Ɲwk[@ZHAoؖ|R[N܍ J;MV sZdapMht1rz\ _geq. :Q/%1 7'ZJ%Ѩ%wDy6G-z=!KI`qԑc* ǚ WcoKovruG IYui?29MV)rwVxƈM'^skzdѻhLcNfPZQ1TڪR8dsPp{Fn傝8-BcAkyZ7ksuޞBj_n47ŐB V<%{]܂Y"`ɚ昮| [& _ Z_#2&rf\8=B&NnwYLʆ9eR#:PkZC=YLHehx=yZ % :<س GR_Mb'(6qm/>ڶmp?•ɚk;aN*{>щ’ZV7ac `UՌSjbM/m_zt8biGU}CR{Aڥj !q(9Z_Z-׮A\'Fdj.0 M]?bp(ѪMZīSRYWIAi<q}b3%v!.`!PQ:9z9ܶkYpb)ntp=ȵl<@xt7l<`JR'0SUլdSZ;uq5־+3#цT#VC*?1&:$۞ aXٯ阬;jI6I]h0M:13Џ_4aexӱS ,q /\1jM x9.X_e~[rgLgIRNDð,HD=/jrַ ?pyoc U{@foͰ?fDE5)w%PʌE=ۮ~%ϖl J4WS 2г? S3Ӭ;Jl= lj"KVy ]h*5Y&  ErmNB+tFQR]Ŵ8cȤuˊ,kjf9ͽ^ꪋRo<66m]kǗjB,ZZF$-%)9g+TBbⒷ‹] caWZ|BD\  <}/~13)g8sw69 A{`5IUL28_z:|Gq`kHr+kjCɸZmw~P3n[J a9QK)"y4ŒԂ}CQ\ Wr8\Hܛo8U]”1$aM UK 5%<"zyFt aM폋R5j I<1gbW\m fxVqA(@VNbn\Mr)wt#Ϣ9eC?ΉJ1cBTU8_]%X8**C$c=ȹާŹ'1 @onѴVELvd25J *P<弬n4CQ8``LS] "׌둃JrAwאz%Azk+,CBGJׄ |IE+^1(U!M3_bUzfh&!Y_Ҡ(8^ϣhI}==o9p ߻cͦYچݨ,[0[=ͷ$e'mFm6& \$~Qtf8| R7 ~j_R[o0/ ӥTz]_eŎ0kZmM2 9o H؁-O0.ߍh2;m? lW`-OSo:5l2>쯇Fh-t{>BOqEgE?F khH*VicMV6 ÿ֐&ezlp\n"D4"foGmcTŪM`b3_k]3C<:=s{]Čtu*;5I>Z}Oa$C4G$ 3ON30o4֧QZ:Ft0J73clOƌdysAbJϯ;A7'ٞnHcS"◢}0zU&23|Ii4Opwcs&,MR)EVs՚w&Ç_YX8n(}p2G u*(G)0Mƞ cA@pbbh_60h*ҋ |pԅ;N݂:K]\2g9!|mʛ d~2|;IOu;FHY\'m0jR_ >& H៟ ;b9kϷ] 7hVԂpK,_ۃ}IUA>&!cZݚ< },{.5)X1"Lg`0=mx{Zf^YDyEK^os}XC=&)c.qucG Q` =!”;2㨑1ml]*iX@o(:G'3CcLv-ǒƘ3NClɦ!3.t 2$)iULGh{@lSjz`qӯDr; t5#_Rą"!BOn`MPzKYU_zGۂ/ VH=UMnȑZTd< IsgBM*r]o(8u9JJX>f%%\v26f <+D7^S:aZD͌egTtry8&ZObrm$聯!ZrVH5ҁH(X af9dLjHFѼzx9^E/hҚ5 F>gf1a^WwY^G=0d:89m*J,Z5#gJ=epB䉃fM[q+E3䍆Ȏ3{nÛ,z\9xyGҢ WQJV/]孉l`Sth e 9oBM`c=Z K{k ~}|q~?NzpCw{A t&xH)_ZR_TEZq @<\֚lF b5{@M< =®wmo!Z61AKPf7J#c[sjM`8?@jY"9N^kAӪp>L-֨H低&?7Zc.` )ƚgX~0UqG׻68G5=+bq]53h.tPS@`(H&)bA_vk ;tTܚ39r헴.cԛ0e#k@AyVS1:_ŌܙeωGu z$ܥ6YwKmuFcT.W~/ 酘?a˂m[<k=Bu!(l;8ԞI+Ag rkK'SϷ y-4Ki)b3dQ3~80Ͻ{VR- bE׆-2k Tfz_AmiF$TW/bWw#+Kha/)k;SY-Q PXK]ǽ%/avgj_QDl+SbNM 7| U+t;RT~cw]SSwM8{i̗0 fS At-v~PL?/m?nA#-һgo P^a=Fg "zoںңg'{nky˜mW{MV&:3ZS]]kźMsj䄻XFWd< B*, J\vQ5EuSbT[07EҳOMs?sAV7zV_ zx<iQOwjH?CmF}j]hwgxڊ﷥D{8&cp2@|>chWYyzƠfuq>-gEC{˪2rɖmY* /m 7^IC< м%i Sm$(GA|aU^Z6rMpb֪q&'}ivq؃s-7da;_R8?r]i- GYON{3~q-9}Ou ]o=u@8>$qyޖsgѤX'W_Կ'*f:.$"U¹l|ob)>NR8^Vu{2Dbve{&z<$nt '\ ]1~g cMSf{reI\9V\W|(Rtr`/]W]`(4Ȑ*?ocfU? @jB^0ztQg1h?4* ^?ʪ,/=W ߼^+䵰:Qs|GZʇ, W5_~W+U]*q+?yuPMLbHh1Ti=f:|o db)& 5Zh5|%:d\WӨb=5v}{C):ڳ,#:/whlL1U]+_lxRSnjyWIݘGjcI%-pca-zxXFy!AV:SMcꪦo?S%-;(8~N3I-4:EY .FcpnZoyR#bT{#4ռ0h$!CC: = LWlŴ$ ;4-y!O\2 v٣,0me]̀Ql4\raM1>Ȓ1 (-@:o5 D{5ܖWcEA#=75BP8)AiN}ޢ}Ήx}CJsPpsgٳ|=_TןkZOD\y oR 9l:YyVvF|4ַuO^ܑM}_1 q:}m / W! 1Ėz8q?a]|$*ZOg6U'粇y5ѱ| GCYIU_f=2yqL:?0DOx*?/[~5d>m|usܣZE0h >|X8lc%]"1Un_'9,s+"Q/O)m?-kd=SqEdc9] 6B mUT{_?"Ns и8{4.3E +">}B"L xMi%6-uovf1 hsDnbܹSkﴨM .Ca-&5A_4ѡVNbG6󖔼BL5N#5D6a!87|koݶ$mK`l W=e{s7I קoփ9 :K'a8G׀wz|ݑ LrG9?Ryy\ R,>a~YUJH3SWp>W2Y R2vWWK0՟rl]1պa: `2ل| [&pm#^Z|h3}yJׂae[?_)v=Aqc%¸%X;N!hƴmOίªC v\azRb|تr݂cmBCr#>SteMћA@7 Eu |bx}ˡAEhVⷖ''ѹy^m'z&MS_e!2 Qz <0|{wH?u4ԉdDO?cIƪ֍LX\TeZ71E2 f+<RD{ :dWAűR_'ĕ\ jeN)8p"Q{  듂a.Z<Fty`zZj{zû26nf>Oc^?ݛ^Wl#~ .z?nt $DV0[ueTOw$1jgW}=i<vpr};'>mXɒcYgk/P ,!~b'b-e/}w]w$An'ev6JjaڲZA:+Y ?(Dx*^-—)]Z6ԆHG4%J6rxby`Qձt [T,ef7>[3"Ƞ\{r ǡ4$*MY`on ITHtOA)Ta +|U }k%Mm_4A&BH5"+β55mmGv[1;wº8; yϩ^,J3'_ 6Q&r]ȴ5i_ H˫)bkXb3(=fiK6ghm=_s`[V؇4:aucQ͋Yve'bxS~s$LD/dR? &f\t&͞#{E ] 8so%9G oL0Q%X^_L'̸lh$rTNLI=4|~v[by!݊M'R;7 M:^h\`%\ Ra D, _{Ad=dLXbCn%QgZ9xx4cV"hEiaj>yiӭ LZVRpRSSKj1 lB^k>TK . Ñ]_s1`mKtA#p6InP?ھc>Hm_8ە9 z -YGq Of۟'HeA.<a3_ٗ%M$xMº\$£qPs*V" !O93j=7qT'r@aW]r|{ǛRU7%0KANt%(HfJA5-k4BB_ Ym6a|qƟ8klD[=}g.O;ǹ͵Iy*H7s9HV!FH:5m`ѩ∨-lvQј)LjbKX^L>䡁PSiwkȶ+axUl)Z)T̳ suCH`)2p{(kRb$a[H5GS&N[Y#NhH:05Q1sH+lgrWn>ֿH23xf aRp|1F5)Ab#(Y_a^wJo噞jR Kؒf 3S֪=5te6O,^k##()"_|\cHTZ70@7PJR+6yώbjL'ƴ`@?2pd,Vʢ235ZM4" Tb}GF@V='jE lcxhslCإCjjft۟$ FjڏAvw8VcNOLWy0ϊig&oţV1{{5|"= {@A物l3g}z Ɓe՞Vv)pbd"GBu?c`QYO [bU4(`*Yě. <ZYp6$wrLr;hM9KZ CB2]0Rl_8uNLރ* uF(IM2>198:Sn"9+Q T axw*L&xY閍~5j_ϯėhZ[>uZ>Zְx @5[/MH 70iƼÖ,H ,AFc8W#­e01 hBŧ4C8iu-}-܍pјò~Ů@>D4D 2Hk=k߿~?q%(6&nɕ'Ҝ9}˾XXx]BΏ%(^h4}"1q-sZ&'9ocmO_[sv=6PL[AuTQ}* B;u!d1&uI&ēQ3 |0XNw+3=%GN؃Ur{v5ƿLRdz0K[wA@iTg_$gؠoVd1AED7E荰 Uk&H- VS:d_G&: Qt-RNs {* CH%n1HK*G,h?;WV&EB.4Xy:Vp($z&Nha뒯[aZR7-[d=R`p5wf)J ٖ&A$W!%*Rٔ_G?{0Ti쫽&n{Xbs!H_XىS}0O-]^a M 4ԧ7 !xcnC_αsk1C\FYђǣx3ܒm0RwG[٩*e>SsUG NTԀ&{a!ECi"!3n1^vkfGB=ty_hzU/uNN '/-=,Nڞs~&mxM@pL|d՞}4$be]R. >m50t2WYȣvZ'']h[=yim{\W6Bq#8Y1Tqa5%E!5& l NS T<&.VzZ>?UFuYo+c7ʶ3<.$Eq0m P=mu4n/VU?hڵ@WȾw:RL-p1y#B We.gKcv"aɖܤzP 㖨 10[& Pq:yÕ~:- Kb_ڪZfQh.;b~ M;|_qΥ­蠶8q\Ǣ{s U[4+q-.+;jY*3GX[Yql~ڮ )`D4P;< {ߝP;V_N}\^j2-~w96=щ9w=l[O'ΜZR \mnl13^ZOKQ>3>@E\H 1Yͽ)ѣ;~Ng+'=Dz"k/,Km^0l#kC 8g]-,pwKn{: (L* i76"L)7J! 6ɞGLT~L'PML?252ͳq%<كsu`g/~: Q48%. QlfvٲVs{fen O>SvX;gMDԛ6e‹ڝɚgojҎ^Jy <_cVE9T0xiýs$?qc ]eh} k'ث a2))a].At ~'ZM;~ EkmOŸV {CDZ =0T~9nmQN"Q=mvrI^ƒ-<xAAt& LW’iXEhEϬ#EaOJz[Ucw[7FJ>Z/OaAH47Saڪx2nByc.Ն*g[lmGuMMȾCj~?ks=%Ec55n>ߤEq'uPc䅟kREhxVdBE舵lr'`U(Z2󨍎x ۅ z<%e!Hڷb<pfjK`Wh=]ivw~8{ O[*~NrR[P,~WK=])kkӄWJ*F8T7]8iBȿn_?%ph~כ5x6dj|֎nXf2-'{eþ$m7Ϋk{Սo%[q^B3=@\Я]gb1ARk\{\$az$ @;=S#3in'Vo + |W_F`~kS ܛYO\;9t`*cOQr'U?̀/F~Jn qL=?~2D |ZRSWAѺ='8X1N1D"o yk28MQxH=֦.tOjjb'HatT^5@]{R]ezR%-yJSi{NZL[=o3V6ƥed-жi4IM"D֭cSP]<;=\=xOn|%!w vO&=h,ABv2æZ]EkvYjxOB{(dBˍ{GDlrnk.T\xvOs;ڊmNJ'2T}IkK8[fH7o\r^7(fL|wEp 2Ӥ$?"DvWԳ#Q zړ17؂GR3A>"'P ha-m:6`qϯ>[>HSrCmb`9[z FI~dHg(Q'B/*+G>op*wvydiȃ<5ʈ_=@W,pJ&=Pul<*fD.'BTHlo׵8՞A*P/0E'@\;zgcP1_",YHފpVxm!ͣ=p㡲79Њ Rh&t\upj|(I| fYsx<ԋa4qU%K&}ژ&0o#27X!D4DI.zɣ4kufG)ft5_ _]WwN +Γj}Hb&l씆^}0/C Q%^n)Z ,PVOl{-̶bZsþϕ2]ϯ;a*JH6 a7b`7+GSsl<`g1Mӏ + w+^+&Q8* &#띪[bz$ôE҈`!޵ޢ2òWTh&̪P_8 [LPKLd4%gmw$] iw1rn/' 7=Bd}AhHլ8D7$uP-x |-\Vwa:R᠍杝B?4M`I(v6M4TE3D*qWb@BK=xku>y2 R)%>i G#QV}&/+7q\(ЉGq0gF"e#nXdAwP~@;orQy PPG<b^rMF9H<Wʼ_M_>Miu8 "VȾR-gLaf' ^ҞExB/-`rSLD)r߰5"QCi; Hv$$WF2u9s<4Z^&!H@%H3p4pgVDO GƣDZ#34ZL).G |QP򐸩aEcW?apȾAd#VZ62 61}HdE\rn4 bXCx;r(ֆISp FQ98 ]5#'FwfbqO>6|+BRD_{luh񘢎u1ҟq;}9R_I*}((&t|%;rmFu)-@@#9#O'DINд/bGQAdwjȳ!塸=2|D<Ă`ٖw1RJ9#R>Lq)c]qx !79@2גgKjDFK`~̄1^fY Xc BL$1d,ElV[`y5 cȓb זl=PFO-TglsŔU"a8 ei[P.E=kn8ș{gl)vyo7k+O+"EKQid1$Ha l2>š04Ψ觌HY;Vsnfp儎v!2}J( l熲d}gw%0s+r^D0g|HS0dPl@5 ʃ0qw*ˈ&z*HIygU Uo^TEt{ZȷU,aS2?*ld 1{qʜm4r(CJnDPضa"r|6TЃ1 uCPA(U+C|Rq^/fyZiz"P3E җ)Ճt5XB{.斎.hQ %JdfLp02XU`(PwKv?-u12=vK#Bea$j)c彅F0TW"Gv-Dm,Y*KȠf5RㄌI1a~."qOnw=ժD<)])0GO fD^F]B>!^!L((ZUHZԦJ1=O|;HxTFT9dҖR#[sw2jmfP a6q4>lE4zbϽ~Nٶ)V MؿtF^ѧդs!(+2Mayޤ |94*)>CY͋&=,KuH۷FM(ΠYiK,҃4Oclxڸ T&=G{˺$EPphQd&g76P[k*H|; ԹYB ?-m6~ oN 0Y:$a{E + jQҲ~u3JUO.('܋ &⯍s`oB}8'EgpM&NVgՂ|sTn 1+(($9r?`qpqsCp{ USU+vcm/7ͨJgv72;', <rr iۛꩡuYQ-V daA>h@eݮA2T_먠>cbpt<(wj؟?G?&55#sG~v|1m:;g}z=W(aӛ =e%_>2@Fԩpl H& yLmX ?cS X0~Lө3kXnP5  WB'Ҝ^\[TKۆǾe붓P+t{<}&$hy=;F1&;q/[bl ''5z:1"Yx${L 4boNV1{1S6ᴪ B,RW*{*&fzNJFs٪BZuE ѵeRu衰 EUu)O >"n bť=<ʒs ]YuV"]^31܋南kBTN$+ipNggbm=vz!L0׬bnR ݥo/\݊'ŞÇ]r.pxR$nb1k:dyӿ_ZmB]8png2svg-1.7.0/img/spaceships.png000066400000000000000000000131721504213346300165360ustar00rootroot00000000000000PNG  IHDRq4gAMA a cHRMz&u0`:pQ<0PLTE@A5)*#\_Om²)FVRI{g2/uGFcբtRNS@fbKGDHtIME  -R:IDATxϏu{zIXVW/)S@0KJN[[5E!Ux j dIr0Y=bx07J^t0X|}?j)ɀ*ޙW^Uu~hY0mO^}t?Rn?,Oz΀Kd9sM+(Y4 4z5`A.|ҾRmDl>w>~ Pl{.} >׵*ӆ/;?+t]T8@T>7MOL8G$rxiXFƧ2mo[s{]@˜ ُ$ca R;Y ̋xl8$FP@ l5 n>20V0 L4h%>199 zL<W Il, }H()500Q0$ 1LbҚք^h` rϚRcBH>۩>,ř'vB 2\W~ A"R-1|Z <H؊@"$ 2R;Fx=2h&-/" 8s>P/65m2 'ILVx~I4΄0K- Ddfy4Xn$ZtVQhk`nJ+:AѺE?@ RαSan,/a($@ jFX|uhjGQ`  F c$`ȍİo U4CR#,Hj|51x!{$f TښÇe0hH%#غcHJ3 wAG5-@.@;<~xrcf{tl\Dؙ k˦b`PUg$y|~0s?e#7Pm^(r@:S%%})“rdPG\rR@G2l0TL-`$2Uo@?"t6@@"`VKQ¢QRe6 Y) VN(8/ mQr\*y[Ij 6m&` ˦R^%`aKds`̤|0h$ʣxx`w; +;ShJ@P&/YP)NF' M͚13`7\H7W)}}IH*S&;**[.}&^dT}@aɔNa럄$WXMf< c0DQU <*ý$p}@5 ).MCڴH$4Eb bQ~ϻ_oz@ ww|O=o:>Z[9J#8l<7; 6l!&TBvV0##PD0-iKu!y\5_\J9g{}֤n *QS6ҷ$ w(7[G0}ݥ,t}8 Mr.[|_ O:WQyVg}c#Z k)CMUT9q=5T*5nFl$ow,>M6T/b |Qd` +i-kt`u}ỳw8[[^ܮLb- isݫ59@#Θɗ_^9r|r6m._kdvޮ b@?KHy6-Z/V)E–@34N` AgQG0 @91<":*LRt њxDSFM!2(UFmV*Bk0&hġ̀lG5-V׏D_X _?ֺ߱pg\Dw{Ī?4ma~z_|o~Noˎkp@'8el N})؟O'='tzsn;ke%,U 46$Xȏ2)$F GG_9ӣ+=:y=n$cR~#.W1}x!hEKvq O? 6ڽ&= E¿[;Yvg/}.2mE^;yOwv `MEڀ8nqauRP2ׯbr2^??6 4uLfYFHRVJ@ @;PlK[5-_`v p=KU ߥvWm?ݛM*"-d\q98d@ޣ܍ { qF_CLsOYi>0`P0=(wۡѶ 6cx F7 HT(r k?]S9v 嘋 z"DLzinP?e2v@}\Q\8  `-'rK c?59`t d Nyϝ\(pNݳ*cY5p#۹M0cn!Kp28uF3&dSvtjf!Á|~q+6n\0Y]G nJ(O?YE˅]&,D a2] +''j;Cr;N?N M $l2'TP*@Y( ^sB(;؝n:U  |Z1&h+Nڝh3˔+FqL*+=|h=x5n^&PireP@wciv]Ĺ "\x`'JU٢*w`$a8:A^ϡ`f 91Hh,|(!(Qhyһ1;qJ6vnI.d\"9MD'vXN'fmk.+qx m:o޻+8 {}qz|ؘhѿS\>SL#;t}߼~WgF"x p#")C(5Jqp}y^X*֘Ywr#vPy+aTgrgÈtK:* YEC'* N0`JXsoTfxvh]pܚ۟h|^Ϋ݂l:Lh_';/cڙߦo|h'Q%tEXtdate:create2020-09-22T12:11:35+00:00 %tEXtdate:modify2020-09-22T12:11:35+00:00>IENDB`png2svg-1.7.0/img/spaceships4096.svg000066400000000000000000005657141504213346300171120ustar00rootroot00000000000000png2svg-1.7.0/img/spaceships_opt.svg000066400000000000000000001705011504213346300174330ustar00rootroot00000000000000png2svg-1.7.0/img/spaceships_opt.svgz000066400000000000000000000317061504213346300176300ustar00rootroot00000000000000>aspaceships_opt.svg]ɮ%DZəٰ춤'e uU%w7oNL2㉈?w?˷ԣ?\~O?}.pO?n_~?o??ۏ_ԗR1ecήץղ4,͍bd*)O2w'G. ^T-7Ѫ^*˞rMӬ[SvZ8锽x⥟˧#8ͥiVgOpu.C?הzXLV7Dbn+-sJd#m76BX- ) L,ctМ#"}MJ:u2l~{uC,WT:>O"y`OmVd3D7볇0Ev߲;Oz@X8 q) sb׆hWGѦuρ8ʥ3]x  qrϴ90S4DY嬤竺1f颳tE?d@f\&g˯䇖>rS`SE/c<]W̻bo2:._ggߠ}O4w&W;xSӻ%ﶧOأ M 3 #NBcwj]_qfyT8<[ToXۆEߖD>g}U;O^=}fȲ~G=4LӾć^nRkg+35cgOSSK oݞ/k+1xS,&tVbݤCqtمOCJT3 ~R<]-xJZ^Rbueה#Z&\.=59uE'M][NgXPl iQ4ُK|A䍃; dE{aWo~ ?j_Y+am^1R+4i&y8 Bx+Mݜ(S>QH 7o3-h[[m70Gn?jZ-R?<ݲ-EhKV7u{U+^\I"u嫒=K 5$N e_k(.9(ؕa^X.zFB3z=DE*/pf^v'9Y{QdDNVuy襋,.WMG?GmE}o6^!N5,֔ni!1DN RnDBp" է'L6yS DC@g`._ _^s=7dp>lywҥ \礰k8^! g =Z(>y nzaOwj E4Hv.T5~Y zzUQ9/0BKwO~0'Z;A=&ÙrMw⺲:'By[s MTl?$,ѳ?\ GBy92m 0Q_%=,|gz,*}J)t !9M6# g:򓆇qu;(yN.r51UN[YUZt7tTfΒLwtJ'rkm̔ጿ*_ތx{'C)F'ɀ&7+}3Wx+׹A8 ik{p\}ϦI]p@UZnX>Ɏ< 2Yt' ӏ<>8sMxǕ :{/;[7~[1a!#nYd{M2M2Gp?߼t"Y)8XR NA"&y0k%IJmz@ëg!Y s1Z2{n` T*%0\+20Qn4-ΔǕQ=^QI0U1\, O3n%3ps4`Aݛ%)F|ҹp.|~^6I&q[דXvvAU =p@ OfhC?`A{XWmS0]%w*|;ܒl(ٴ#d[@LBPmUSF ޥ5h6h)q~>K$HΡ# !4JFӔ6~[E$aP^2[GL䁡цrkɘa6{'Bǘ"q/ԾҘ܀K{965¸ٓC2VG}ҔG+" x`ગAvjd*h"Ps=GԸ b;g,XZpڐxycsZ֘_ Z_5Ky;(';_T%2j>/rSXgF2FG9n(Jq|HDPZ 7slIK bns 0 .t‚i\a#^̑$T8ϾY4 y$*i `$|7]& y%dlbaJ3P<])PdS~p.t5֎XWk؊wG \A ͞'FK &U~@4wmlBVW%?,`tF:ؕ _g$v0RX۹mCJEG !$aѺH#TR[h2" ǩYwxxм©Q+`٠:g^>ΒF:~tL|fI Xa:,V"3͹!nwqlRFl Al(n+zѡ6f:FA"RmT0GD/k,#MrzvuX} U]Hlͮ9`v.;b3#*glE%Y)] i` #9Ѷ5 U 4 FG&ܴ }yT8%QHZF?T B쪃).UWG\5c( `?a4;@nr3Y;Eoqڑ(el %w\y$`p+4Nj`Wlgtenero ^̷Ab9pu[2 E'yC^NLކM;O n\VY-'֝烞62&ʆYd)A wU#ETd BoOLe=Oih{"T6 *T)<԰aI!D!p`/oí ^b1VtV%rzb˹ ];* Ui_JXQ:w&o/k&g# 69LVJ?jT-^{@ckz{@aH,\%o7Rd_Mlz5pԌ=.]1 yBWl^(&S"8%mp28RJdw2=5I~J8F?WKh[jm+c˻9T.(X uh֫:#H80[zYFXh؄iFXz8=e@ܣ P y FK ef x[՝#+}ҿ`IֶPYtXX<{*Uծ:;M}c1g&v*£V9 F#srgXH\>?K<7\"24O\$\S00U+ZNa1 ˢj+JGƽL= 8r\}!ΝS#ܖP$"٦۰C"#>O9AmpoP|V:6>s(x}=!yתfXxo`4J y7պ \G^k4~;˖Y!qh-y+PhSh 6΁.z)IWw)?Bѧs2\OxxQSU62@o R\A<~K,9;evRB"R~-v% VL8 KuqٻB,:ef[gȿj)4׺Ew=e* J@ RbXsصw۩޻#nÔ(g)һqeen;b&w!\7%I=C Q'P3Ѝ;ZvE ŭ"U:h55 x15M\vP`LyƐS gaA9jj@ rŝsٙOew4e-Nj;EZw }L.6^VvfUd@~dg,6s(M|G\J0Ȳ8XE=lJZrs?GAuMyһYHR9gRoON<0}QS"{ҹ$b%N+)eunXcF}vuIPviIo%ʟ5E-sb p=5i[rdBhM٢mGlk.Wd.{aE7D9m[{bV˄?Xs59rEܰՍDЮuCܱf8# !:(>,IC$Bs!Q7:#zs$PY8闭D#<8\j3Cb7Rvw\_|aIhؽeNf5潮ͻC}^u۱O~Xt~wO7ߔDTN%9b-C 5/vRSpĵ./\D!=,(L5ϊ[l tx k,"aUL)UQ􎠧PظB^uXw[9d3e97y åI-ɼ%ñh ('{il#_Wj O_";vvz*0&g\O;DHAD%r|-TT p*S.OMvҶ 9cr1 w{V&[68?Չ^YDBglIe{ - HsG[1Jƭlf 1YeSP7Np#>{o~,X uh=w !nִ [zsVC 1pps;ŝ,wUg_Y@`a?OAĈ Oc IF +Q?Xoy`98_P沸JI;U1xV \ޅ{DT[GcjD0shu ZPZ6cf~|R=酦:_][FKͥ\X--sEIXP"EYB+ܣHh##q[SVZT[m}}x`Kxf֠/e2ᎢASVT"g@fu&1ӧBnwlRa =q Ka -dM>%͍'#YPe Ib?@KzT(zVԽuCj_ť ^5PJ{c'Fvjw$")$f80߳opО, Jz ׃:?,{1.Tg}/X;5)*$A?Jӣا}QZ Ձ>N7Z<̤88z֐ӛu5jZ/RNTer2"2ShȠ)ų h?n{q9~8}i`&vOXH ^n(R7 ΃iSߟ[KŎ# QRU5cY9دIzH4|rWƳEov'~Tz%' o(DlKS#UqPTa`,LN!%VٝřB| PS07_BHо>Y6tBiQ]]ɛ'&L2#,)n0:P% %nq`w% {F-I;)ms^6EŲ\,;Rs"Ӈ 5FK ^U:CjYj::U).$3 QH뱭ܛs4u"w Ҵ' bW׬_v!՛aW6fl 0olS偝PKu8O{M<3z+䲾ZBW>hvTPXIž*\^/ 浈qZ^ꦗ6#!Цy=%;8rvC8j1O_j6AzKeo>mܺ9E0@kLVaŠzk H`(-_!,$eu1H ;J>cԍ_'ZI VpƯzutUOڐkNBfeDԉBB4(Xkm1M-v`w#nf)0fS1wcq٧6Ԩ]cHzBMS@M-Pa*-cEexQ[5dIARe㯾 &HOw.W;d?ڐaSXB5ChN;<0<ٽِ3֥ ~z+/FIdoC:g@1 Ёifd娋U炡L6&}¼Kw?D}gǀʳ^8jp cQD+%F9v!iD0 7^'яtOj:у8hGvFik`Ol`}sHwca-LQ~˰:Ѓ1c/EJ25e LA+;gʵ;|-E!9.:vA"c9:YKU<%3;Teݔ/*k@8M7w~MRIq!@^{+%Se/.2a/෍^pS24 Iψpge 0);[dKt$Vi?0$*(Kj{ԩyMԶJpmX5g*; `9eGĖ F 0R(\kX”J G "Rq=ŽZ׽ӍG9%P< E%f9H(aS*- 2^S`M{5XI:3TS'_,X)BvlHso~Zv+~REv&XTIW{P#ʾe!>(@AߛjaWLf95QM6|_ȃ8R ,_9G.'sU!QdKmFx#=.\GB3!m2 N,IMrU5Ty[n./w]zeqҎkhhzS_/LL$6MaXI(3޹*̼Ji'a^,uv_'Xz"ѺXi,eWe`[$L'hT䁈b54+dXjT#*CO_gGZ*A4k?@A KHP^ty~Vh)9 &1Ht镤 QKU!8*ŞFzQ}⍁"Mlqoɭ0x҅acوQP }d))5H:pHz01OnXSlvT<3-79.o 1#yN!KqQ k$py7S֯M,~9r7t"Q~U$͛ Y2 EOx^j}Jw}MM.M: p@Y&Xz\8u 6"(Q6׷Ą~JD6ܧӡkx,EB،ܰqɰ ٭1h\X _iS1*-X69l\ K=VŢdK% N(IIl;d_<{#bJ-5'-HCpo3gYկjZ{[U 5nL˵+r-2a^_V T]Y!Ap\JF- SO;\jQuþʣW)nQ:s?QhAѱRզ]C1qciݒ& @ˮTRu.1uRJDUy* pQ.jn೭s*% }2'j?8'fXJ0<."I{]@<?S0Y#o^0XF4X?Ԑ&cD(N݂cQr31XAYXKl%w)e5; f1V.zip _g`QR-w>*:{NS/v)fuo0O,gT`0ʹ]1ˈ[]CgE;VKݍvDdJ5̬hMY8&a/Prm|Me7b ۮ^_Zk{g`P~]z;e>RimFP-xrdY # Vc%nr]T;0Y+ky+ZAs'v9'w par{*Ħ`:o1Zv@pRĝS ٵRZ]$1p{xPP^(3A![OķVvxmnR;f1kg ;J3z蔵C婪H'ͣlf?=."_nz4(Nԭh B6uH!LK #mڙK*z""dr_sah,+{X+FYjK Z|֏U8\񽕱l%QzGԩ !rNS9 gdFMTe ܮQ+|Go"ucun? 9Ȳd#ʙÓB9$8-嬉'\EHa|s }e(T7#EesQ˳%9ő=un27:KJLS;.QA;y[ZVXbe1[tj UY2˔t rU巼Z'&z)x}9U%󲤂v?]պ~ShTҟؑ+^#SVp'JVtTp ʸ~V&Y\۹!D< R&G#-ڌa3opG'yx{oJ}f) E!pZB.Xڐ^kFW"j-etKϒ pnݺԩR!n9v>;ۡae"5G ս6%U$ ΋B^mntmI{AApng2svg-1.7.0/pixelimage.go000066400000000000000000000317111504213346300155640ustar00rootroot00000000000000package png2svg import ( "bytes" "errors" "fmt" "image" "image/color" "image/png" "os" "strings" "github.com/xyproto/tinysvg" ) // Pixel represents a pixel at position (x,y) // with color (r,g,b,a) // and a bool for if this pixel has been covered by an SVG shape yet type Pixel struct { x int y int r int g int b int a int covered bool } // Pixels is a slice of pointers to Pixel type Pixels []*Pixel // PixelImage contains the data needed to convert a PNG to an SVG: // pixels (with an overview of which pixels are covered) and // an SVG document, starting with the document and root tag + // colorOptimize, for if only 4096 colors should be used // (short hex color strings, like #fff). type PixelImage struct { document *tinysvg.Document svgTag *tinysvg.Tag pixels Pixels w int h int verbose bool colorOptimize bool } // SetColorOptimize can be used to set the colorOptimize flag, // for using only 4096 colors. func (pi *PixelImage) SetColorOptimize(enabled bool) { pi.colorOptimize = enabled } // ReadPNG tries to read the given PNG image filename and returns and image.Image // and an error. If verbose is true, some basic information is printed to stdout. func ReadPNG(filename string, verbose bool) (image.Image, error) { if verbose { fmt.Printf("Reading %s", filename) defer fmt.Println() } f, err := os.Open(filename) if err != nil { return nil, err } defer f.Close() img, err := png.Decode(f) if err != nil { return nil, err } if verbose { fmt.Printf(" (%dx%d)", img.Bounds().Max.X-img.Bounds().Min.X, img.Bounds().Max.Y-img.Bounds().Min.Y) } return img, nil } // Erase characters on the terminal func Erase(n int) { fmt.Print(strings.Repeat("\b", n)) } // NewPixelImage initializes a new PixelImage struct, // given an image.Image. func NewPixelImage(img image.Image, verbose bool) *PixelImage { width := img.Bounds().Max.X - img.Bounds().Min.X height := img.Bounds().Max.Y - img.Bounds().Min.Y pixels := make(Pixels, width*height) var c color.NRGBA if verbose { fmt.Print("Interpreting image... 0%") } percentage := 0 lastPercentage := 0 i := 0 lastLine := img.Bounds().Max.Y for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { if verbose && y != lastLine { lastPercentage = percentage percentage = int((float64(y) / float64(height)) * 100.0) Erase(len(fmt.Sprintf("%d%%", lastPercentage))) fmt.Printf("%d%%", percentage) lastLine = y } for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { c = color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) // Mark transparent pixels as already being "covered" (alpha == 0) pixels[i] = &Pixel{x, y, int(c.R), int(c.G), int(c.B), int(c.A), c.A == 0} i++ } } // Create a new XML document with a new SVG tag document, svgTag := tinysvg.NewTinySVG(width, height) if verbose { Erase(len(fmt.Sprintf("%d%%", lastPercentage))) fmt.Println("100%") } return &PixelImage{ document: document, svgTag: svgTag, pixels: pixels, w: width, h: height, verbose: verbose, colorOptimize: false, } } // Done checks if all pixels are covered, in terms of being represented by an SVG element // searches from the given x and y coordinate func (pi *PixelImage) Done(startx, starty int) bool { for y := starty; y < pi.h; y++ { for x := startx; x < pi.w; x++ { i := y*pi.w + x if !pi.pixels[i].covered { return false } } // Start at the beginning of the line when searching the rest of the lines startx = 0 } return true } // At returns the RGB color at the given coordinate func (pi *PixelImage) At(x, y int) (r, g, b int) { i := y*pi.w + x //if i >= len(pi.pixels) { // panic("At out of bounds, too large coordinate") //} p := *pi.pixels[i] return p.r, p.g, p.b } // At2 returns the RGBA color at the given coordinate func (pi *PixelImage) At2(x, y int) (r, g, b, a int) { i := y*pi.w + x //if i >= len(pi.pixels) { // panic("At out of bounds, too large coordinate") //} p := *pi.pixels[i] return p.r, p.g, p.b, p.a } // Covered returns true if the pixel at the given coordinate is already covered by SVG elements func (pi *PixelImage) Covered(x, y int) bool { i := y*pi.w + x p := *pi.pixels[i] return p.covered } // CoverAllPixels will cover all pixels that are not yet covered by an SVG element, // by creating a rectangle per pixel. func (pi *PixelImage) CoverAllPixels() { coverCount := 0 for _, p := range pi.pixels { if !(*p).covered { pi.svgTag.Pixel((*p).x, (*p).y, int((*p).r), int((*p).g), int((*p).b)) (*p).covered = true coverCount++ } } if pi.verbose { fmt.Printf("Covered %d pixels with 1x1 rectangles.\n", coverCount) } } // CoverAllPixelsCallback will cover all pixels that are not yet covered by an SVG element, // by creating a rectangle per pixel. Also takes a callback function that will be called // with which pixel index the program is at and also the total pixels, for each Nth pixels (and at the start and end). func (pi *PixelImage) CoverAllPixelsCallback(callbackFunc func(int, int), Nth int) { coverCount := 0 l := len(pi.pixels) callbackFunc(0, l) for i, p := range pi.pixels { if !(*p).covered { pi.svgTag.Pixel((*p).x, (*p).y, int((*p).r), int((*p).g), int((*p).b)) (*p).covered = true coverCount++ } if i%Nth == 0 { callbackFunc(i, l) } } callbackFunc(l-1, l) if pi.verbose { fmt.Printf("Covered %d pixels with 1x1 rectangles.\n", coverCount) } } // FirstUncovered will find the first pixel that is not covered by an SVG element, // starting from (startx,starty), searching row-wise, downwards. func (pi *PixelImage) FirstUncovered(startx, starty int) (int, int) { for y := starty; y < pi.h; y++ { for x := startx; x < pi.w; x++ { if !pi.pixels[y*pi.w+x].covered { return x, y } } // Start at the beginning of the line when searching the rest of the lines startx = 0 } // This should never happen, except when debugging panic("All pixels are covered") } // colorFromLine will extract the fill color from a svg rect line. // " tags // This is not the prettiest function, but it works. // TODO: Rewrite, to make it prettier // TODO: Benchmark func groupLinesByFillColor(lines [][]byte, colorOptimize bool) [][]byte { // Group lines by fill color var ( groupedLines = make(map[string][][]byte) fillColor, shortenedFillColor []byte found bool ) for i, line := range lines { fillColor, shortenedFillColor, found = colorFromLine(line, colorOptimize) if !found { // skip continue } // Erase this line. The grouped lines will be inserted at the first empty line. lines[i] = make([]byte, 0) // // Convert from []byte to string because map keys can't be []byte cs := string(shortenedFillColor) if _, ok := groupedLines[cs]; !ok { // Start an empty line groupedLines[cs] = make([][]byte, 0) } if string(fillColor) != cs { line = bytes.Replace(line, fillColor, shortenedFillColor, 1) } line = append(line, '>') groupedLines[cs] = append(groupedLines[cs], line) } //for k, _ := range groupedLines { // fmt.Println("COLOR: ", string(k)) //} // Build a string of all lines with fillcolor, grouped by fillcolor, inside tags var ( buf bytes.Buffer from []byte gfill = []byte("") spacefill = []byte(" fill=\"") ) for key, lines := range groupedLines { if len(lines) > 1 { buf.Write(gfill) //fmt.Printf("WRITING KEY %s\n", key) buf.WriteString(key) buf.Write(closing) for _, line := range lines { from = append(spacefill, key...) buf.Write(bytes.Replace(line, append(from, '"'), []byte{}, 1)) } buf.Write([]byte("")) } else { buf.Write(lines[0]) } } // Insert the contents in the first non-empty slice of lines for i, line := range lines { if len(line) == 0 { lines[i] = buf.Bytes() break } } // Return lines, some of them empty. One of them is a really long line with the above contents. return lines } // Bytes returns the rendered SVG document as bytes func (pi *PixelImage) Bytes() []byte { if pi.verbose { fmt.Print("Rendering SVG...") } // Render the SVG document // TODO: pi.document.WriteTo also exists, and might be faster svgDocument := pi.document.Bytes() if pi.verbose { fmt.Println("ok") fmt.Print("Grouping elements by color...") } // TODO: Make the code related to grouping both faster and more readable // Group lines by fill color, insert tags lines := bytes.Split(svgDocument, []byte(">")) lines = groupLinesByFillColor(lines, pi.colorOptimize) for i, line := range lines { if len(line) > 0 && !bytes.HasSuffix(line, []byte(">")) { lines[i] = append(line, '>') } } // Use the line contents as the new svgDocument svgDocument = bytes.Join(lines, []byte{}) if pi.verbose { fmt.Println("ok") fmt.Print("Additional optimizations...") } // Only non-destructive and spec-conforming optimizations goes here // NOTE: Removing width and height for "1" gave incorrect results in GIMP. // NOTE: GIMP complains about the width and height not being set, but it is set. // Remove all newlines // Remove all spaces before closing tags // Remove double spaces // Remove empty x attributes // Remove empty y attributes // Remove empty width attributes // Remove empty height attributes // Remove single spaces between tags svgDocument = bytes.Replace(svgDocument, []byte("\n"), []byte{}, -1) svgDocument = bytes.Replace(svgDocument, []byte(" />"), []byte("/>"), -1) svgDocument = bytes.Replace(svgDocument, []byte(" "), []byte(" "), -1) svgDocument = bytes.Replace(svgDocument, []byte(" x=\"0\""), []byte{}, -1) svgDocument = bytes.Replace(svgDocument, []byte(" y=\"0\""), []byte{}, -1) svgDocument = bytes.Replace(svgDocument, []byte(" width=\"0\""), []byte{}, -1) svgDocument = bytes.Replace(svgDocument, []byte(" height=\"0\""), []byte{}, -1) svgDocument = bytes.Replace(svgDocument, []byte("> <"), []byte("><"), -1) // Replacement of colors that are not shortened, colors that has been shortened // and color names to even shorter strings. colorReplacements := map[string][]byte{ "#f0ffff": []byte("azure"), "#f5f5dc": []byte("beige"), "#ffe4c4": []byte("bisque"), "#a52a2a": []byte("brown"), "#ff7f50": []byte("coral"), "#ffd700": []byte("gold"), "#808080": []byte("gray"), // "grey" is also possible "#008000": []byte("green"), "#4b0082": []byte("indigo"), "#fffff0": []byte("ivory"), "#f0e68c": []byte("khaki"), "#faf0e6": []byte("linen"), "#800000": []byte("maroon"), "#000080": []byte("navy"), "#808000": []byte("olive"), "#ffa500": []byte("orange"), "#da70d6": []byte("orchid"), "#cd853f": []byte("peru"), "#ffc0cb": []byte("pink"), "#dda0dd": []byte("plum"), "#800080": []byte("purple"), "#f00": []byte("red"), "#fa8072": []byte("salmon"), "#a0522d": []byte("sienna"), "#c0c0c0": []byte("silver"), "#fffafa": []byte("snow"), "#d2b48c": []byte("tan"), "#008080": []byte("teal"), "#ff6347": []byte("tomato"), "#ee82ee": []byte("violet"), "#f5deb3": []byte("wheat"), } // Replace colors with the shorter version for k, v := range colorReplacements { svgDocument = bytes.Replace(svgDocument, []byte(k), v, -1) } if pi.verbose { fmt.Println("ok") } return svgDocument } // WriteSVG will save the current SVG document to a file func (pi *PixelImage) WriteSVG(filename string) error { var ( err error f *os.File ) if !pi.Done(0, 0) { return errors.New("the SVG representation does not cover all pixels") } if filename == "-" { f = os.Stdout // Turn off verbose messages, so that they don't end up in the SVG output pi.verbose = false } else { f, err = os.Create(filename) if err != nil { return err } defer f.Close() } // Write the generated SVG image to file or to stdout if _, err = f.Write(pi.Bytes()); err != nil { return err } return nil } png2svg-1.7.0/pixelimage_test.go000066400000000000000000000035151504213346300166240ustar00rootroot00000000000000package png2svg import ( "image/color" "testing" ) func TestRainforestPixelColorMatch(t *testing.T) { const filename = "img/rainforest.png" const targetX, targetY = 10, 1 // Read the image img, err := ReadPNG(filename, false) if err != nil { t.Fatalf("Failed to read PNG file: %v", err) } // Initialize PixelImage pixelImage := NewPixelImage(img, false) // Get the original pixel color directly from the image originalColor := color.NRGBAModel.Convert(img.At(targetX, targetY)).(color.NRGBA) // Get the pixel at (10, 1) from PixelImage pixel := pixelImage.pixels[targetY*pixelImage.w+targetX] // Check if the color of the pixel matches the original image's pixel color if pixel.r != int(originalColor.R) || pixel.g != int(originalColor.G) || pixel.b != int(originalColor.B) { t.Errorf("Pixel at (%d,%d) has incorrect color: got (R: %d, G: %d, B: %d), want (R: %d, G: %d, B: %d)", targetX, targetY, pixel.r, pixel.g, pixel.b, originalColor.R, originalColor.G, originalColor.B) } } func TestRainforestPixelColorSpecific(t *testing.T) { const filename = "img/rainforest.png" const targetX, targetY = 10, 1 const expectedRed, expectedGreen, expectedBlue = 0x0c, 0, 0 // Assuming the pixel should be #0c0000 // Read the image img, err := ReadPNG(filename, false) if err != nil { t.Fatalf("Failed to read PNG file: %v", err) } // Initialize PixelImage pixelImage := NewPixelImage(img, false) // Get the pixel at (10, 1) pixel := pixelImage.pixels[targetY*pixelImage.w+targetX] // Check if the color of the pixel matches the expected values if pixel.r != expectedRed || pixel.g != expectedGreen || pixel.b != expectedBlue { t.Errorf("Pixel at (%d,%d) has incorrect color: got (R: %d, G: %d, B: %d), want (R: %d, G: %d, B: %d)", targetX, targetY, pixel.r, pixel.g, pixel.b, expectedRed, expectedGreen, expectedBlue) } } png2svg-1.7.0/release.sh000077500000000000000000000033751504213346300150750ustar00rootroot00000000000000#!/bin/sh # # Create release tarballs/zip for 64-bit linux, BSD and Plan9 + 64-bit ARM + raspberry pi 2/3 # name=png2svg version=$(./version.sh | cut -d' ' -f5 | cut -d, -f1) echo "Version $version" echo 'Compiling...' cd cmd/png2svg export GOARCH=amd64 echo '* Linux' GOOS=linux go build -mod=vendor -o $name.linux echo '* Plan9' GOOS=plan9 go build -mod=vendor -o $name.plan9 echo '* macOS AMD64' GOOS=darwin go build -mod=vendor -o $name.macos_amd64 echo '* macOS ARM64' GOOS=darwin GOARCH=arm64 go build -mod=vendor -o $name.macos_arm64 echo '* FreeBSD' GOOS=freebsd go build -mod=vendor -o $name.freebsd echo '* NetBSD' GOOS=netbsd go build -mod=vendor -o $name.netbsd echo '* OpenBSD' GOOS=openbsd go build -mod=vendor -o $name.openbsd echo '* Linux ARM64' GOOS=linux GOARCH=arm64 go build -mod=vendor -o $name.linux_arm64 echo '* RPI 2/3/4' GOOS=linux GOARCH=arm GOARM=7 go build -mod=vendor -o $name.rpi echo '* Linux static w/ upx' CGO_ENABLED=0 GOOS=linux go build -mod=vendor -v -trimpath -ldflags "-s" -a -o $name.linux_static && upx $name.linux_static # Compress the Linux releases with xz for p in linux linux_arm64 rpi linux_static; do echo "Compressing $name-$version.$p.tar.xz" mkdir "$name-$version-$p" cp $name.$p "$name-$version-$p/$name" cp ../../LICENSE "$name-$version-$p/" tar Jcf "../../$name-$version-$p.tar.xz" "$name-$version-$p/" rm -r "$name-$version-$p" rm $name.$p done # Compress the other tarballs with gz for p in macos_amd64 macos_arm64 freebsd netbsd openbsd plan9; do echo "Compressing $name-$version.$p.tar.gz" mkdir "$name-$version-$p" cp $name.$p "$name-$version-$p/$name" cp ../../LICENSE "$name-$version-$p/" tar zcf "../../$name-$version-$p.tar.gz" "$name-$version-$p/" rm -r "$name-$version-$p" rm $name.$p done png2svg-1.7.0/shortencolor.go000066400000000000000000000016071504213346300161620ustar00rootroot00000000000000package png2svg // shortenColorLossy optimizes hexadecimal color strings in a lossy way func shortenColorLossy(hexColorBytes []byte) []byte { if len(hexColorBytes) != 7 { // Only accept colors in the format #aabbcc return hexColorBytes } // Just keep the first digits of each 2 digit hex number return []byte{'#', hexColorBytes[1], hexColorBytes[3], hexColorBytes[5]} } // shortenColorLossless optimizes hexadecimal color strings in a lossless way func shortenColorLossless(hexColorBytes []byte) []byte { if len(hexColorBytes) != 7 { // Only accept colors in the format #aabbcc return hexColorBytes } // Check for lossless compression if hexColorBytes[1] == hexColorBytes[2] && hexColorBytes[3] == hexColorBytes[4] && hexColorBytes[5] == hexColorBytes[6] { return []byte{'#', hexColorBytes[1], hexColorBytes[3], hexColorBytes[5]} } // Return the original color return hexColorBytes } png2svg-1.7.0/shortencolor_test.go000066400000000000000000000036161504213346300172230ustar00rootroot00000000000000package png2svg import ( "reflect" "testing" ) func TestShortenColor(t *testing.T) { tests := []struct { name string hexColorBytes []byte colorOptimize bool expectedOutput []byte }{ { name: "No shorten #0c0000 with colorOptimize false", hexColorBytes: []byte("#0c0000"), colorOptimize: false, expectedOutput: []byte("#0c0000"), }, { name: "Lossy shorten #0c0000 with colorOptimize true", hexColorBytes: []byte("#0c0000"), colorOptimize: true, expectedOutput: []byte("#000"), // Rounded to nearest single hex digit equivalent }, { name: "Lossless shorten #ffffff with colorOptimize true", hexColorBytes: []byte("#ffffff"), colorOptimize: true, expectedOutput: []byte("#fff"), // Lossless compression as each pair is identical }, { name: "No shorten #ffffff with colorOptimize false", hexColorBytes: []byte("#ffffff"), colorOptimize: false, expectedOutput: []byte("#fff"), // Should still shorten losslessly }, { name: "Lossy shorten #112233 with colorOptimize true", hexColorBytes: []byte("#112233"), colorOptimize: true, expectedOutput: []byte("#123"), // Each pair different, so simplified to nearest single hex equivalent }, { name: "No shorten #123456 with colorOptimize false", hexColorBytes: []byte("#123456"), colorOptimize: false, expectedOutput: []byte("#123456"), // No simplification as no pairs are identical }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var result []byte if tt.colorOptimize { result = shortenColorLossy(tt.hexColorBytes) } else { result = shortenColorLossless(tt.hexColorBytes) } if !reflect.DeepEqual(result, tt.expectedOutput) { t.Errorf("shortenColor(%s, %v) = %s, want %s", tt.hexColorBytes, tt.colorOptimize, result, tt.expectedOutput) } }) } } png2svg-1.7.0/testdata/000077500000000000000000000000001504213346300147175ustar00rootroot00000000000000png2svg-1.7.0/testdata/README.md000066400000000000000000000001011504213346300161660ustar00rootroot00000000000000bonzomatic and this icon is released under the Unlicense license png2svg-1.7.0/testdata/jumpline16.png000066400000000000000000000015751504213346300174270ustar00rootroot00000000000000PNG  IHDR(-SPLTE۶mmmIII$$$mI$mI$mmII$$mI$۶mmII$$mmII$$۶mmmIII$$ےmmIIm$$mmII$$mmII$$II$$$$۶۶mmImI$I$ےmmII$m$mmII$$mmII$$II$$$$ےmmIIm$$ImmII$$mmmII$$mmII$$II$$$$۶mmmIII$ےmImm$mI$mI$I$$۶۶mmImI$Iے۶mIm$mm۶I$mI۶$I$$ےmImm$IImI$mmmI$mI$I$$۶۶mmImI$I$ےmmII$m$ےmImm$II$mIm$Im$۶ےmmII$m$ImmII$$mmIm$Im$ImIm$I$mے۶mIm$mII$mےIm$Im$ےmmIIm$$ImmII$$mےmImImI*8IDATc`G }4}@B /@P-*O -Pk; w$IENDB`png2svg-1.7.0/vendor/000077500000000000000000000000001504213346300144035ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/000077500000000000000000000000001504213346300164425ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/cpuguy83/000077500000000000000000000000001504213346300201315ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/cpuguy83/go-md2man/000077500000000000000000000000001504213346300217125ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/cpuguy83/go-md2man/v2/000077500000000000000000000000001504213346300222415ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/cpuguy83/go-md2man/v2/LICENSE.md000066400000000000000000000020651504213346300236500ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Brian Goff Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. png2svg-1.7.0/vendor/github.com/cpuguy83/go-md2man/v2/md2man/000077500000000000000000000000001504213346300234175ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go000066400000000000000000000026411504213346300250370ustar00rootroot00000000000000package md2man import ( "fmt" "io" "os" "strings" "github.com/russross/blackfriday/v2" ) func fmtListFlags(flags blackfriday.ListType) string { knownFlags := []struct { name string flag blackfriday.ListType }{ {"ListTypeOrdered", blackfriday.ListTypeOrdered}, {"ListTypeDefinition", blackfriday.ListTypeDefinition}, {"ListTypeTerm", blackfriday.ListTypeTerm}, {"ListItemContainsBlock", blackfriday.ListItemContainsBlock}, {"ListItemBeginningOfList", blackfriday.ListItemBeginningOfList}, {"ListItemEndOfList", blackfriday.ListItemEndOfList}, } var f []string for _, kf := range knownFlags { if flags&kf.flag != 0 { f = append(f, kf.name) flags &^= kf.flag } } if flags != 0 { f = append(f, fmt.Sprintf("Unknown(%#x)", flags)) } return strings.Join(f, "|") } type debugDecorator struct { blackfriday.Renderer } func depth(node *blackfriday.Node) int { d := 0 for n := node.Parent; n != nil; n = n.Parent { d++ } return d } func (d *debugDecorator) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { fmt.Fprintf(os.Stderr, "%s%s %v %v\n", strings.Repeat(" ", depth(node)), map[bool]string{true: "+", false: "-"}[entering], node, fmtListFlags(node.ListFlags)) var b strings.Builder status := d.Renderer.RenderNode(io.MultiWriter(&b, w), node, entering) if b.Len() > 0 { fmt.Fprintf(os.Stderr, ">> %q\n", b.String()) } return status } png2svg-1.7.0/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go000066400000000000000000000011041504213346300251200ustar00rootroot00000000000000// Package md2man aims in converting markdown into roff (man pages). package md2man import ( "os" "strconv" "github.com/russross/blackfriday/v2" ) // Render converts a markdown document into a roff formatted document. func Render(doc []byte) []byte { renderer := NewRoffRenderer() var r blackfriday.Renderer = renderer if v, _ := strconv.ParseBool(os.Getenv("MD2MAN_DEBUG")); v { r = &debugDecorator{Renderer: r} } return blackfriday.Run(doc, []blackfriday.Option{ blackfriday.WithRenderer(r), blackfriday.WithExtensions(renderer.GetExtensions()), }...) } png2svg-1.7.0/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go000066400000000000000000000266161504213346300247150ustar00rootroot00000000000000package md2man import ( "bufio" "bytes" "fmt" "io" "os" "strings" "github.com/russross/blackfriday/v2" ) // roffRenderer implements the blackfriday.Renderer interface for creating // roff format (manpages) from markdown text type roffRenderer struct { listCounters []int firstHeader bool listDepth int } const ( titleHeader = ".TH " topLevelHeader = "\n\n.SH " secondLevelHdr = "\n.SH " otherHeader = "\n.SS " crTag = "\n" emphTag = "\\fI" emphCloseTag = "\\fP" strongTag = "\\fB" strongCloseTag = "\\fP" breakTag = "\n.br\n" paraTag = "\n.PP\n" hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" linkTag = "\n\\[la]" linkCloseTag = "\\[ra]" codespanTag = "\\fB" codespanCloseTag = "\\fR" codeTag = "\n.EX\n" codeCloseTag = ".EE\n" // Do not prepend a newline character since code blocks, by definition, include a newline already (or at least as how blackfriday gives us on). quoteTag = "\n.PP\n.RS\n" quoteCloseTag = "\n.RE\n" listTag = "\n.RS\n" listCloseTag = ".RE\n" dtTag = "\n.TP\n" dd2Tag = "\n" tableStart = "\n.TS\nallbox;\n" tableEnd = ".TE\n" tableCellStart = "T{\n" tableCellEnd = "\nT}" tablePreprocessor = `'\" t` ) // NewRoffRenderer creates a new blackfriday Renderer for generating roff documents // from markdown func NewRoffRenderer() *roffRenderer { return &roffRenderer{} } // GetExtensions returns the list of extensions used by this renderer implementation func (*roffRenderer) GetExtensions() blackfriday.Extensions { return blackfriday.NoIntraEmphasis | blackfriday.Tables | blackfriday.FencedCode | blackfriday.SpaceHeadings | blackfriday.Footnotes | blackfriday.Titleblock | blackfriday.DefinitionLists } // RenderHeader handles outputting the header at document start func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) { // We need to walk the tree to check if there are any tables. // If there are, we need to enable the roff table preprocessor. ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus { if node.Type == blackfriday.Table { out(w, tablePreprocessor+"\n") return blackfriday.Terminate } return blackfriday.GoToNext }) // disable hyphenation out(w, ".nh\n") } // RenderFooter handles outputting the footer at the document end; the roff // renderer has no footer information func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) { } // RenderNode is called for each node in a markdown document; based on the node // type the equivalent roff output is sent to the writer func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { walkAction := blackfriday.GoToNext switch node.Type { case blackfriday.Text: // Special case: format the NAME section as required for proper whatis parsing. // Refer to the lexgrog(1) and groff_man(7) manual pages for details. if node.Parent != nil && node.Parent.Type == blackfriday.Paragraph && node.Parent.Prev != nil && node.Parent.Prev.Type == blackfriday.Heading && node.Parent.Prev.FirstChild != nil && bytes.EqualFold(node.Parent.Prev.FirstChild.Literal, []byte("NAME")) { before, after, found := bytesCut(node.Literal, []byte(" - ")) escapeSpecialChars(w, before) if found { out(w, ` \- `) escapeSpecialChars(w, after) } } else { escapeSpecialChars(w, node.Literal) } case blackfriday.Softbreak: out(w, crTag) case blackfriday.Hardbreak: out(w, breakTag) case blackfriday.Emph: if entering { out(w, emphTag) } else { out(w, emphCloseTag) } case blackfriday.Strong: if entering { out(w, strongTag) } else { out(w, strongCloseTag) } case blackfriday.Link: // Don't render the link text for automatic links, because this // will only duplicate the URL in the roff output. // See https://daringfireball.net/projects/markdown/syntax#autolink if !bytes.Equal(node.LinkData.Destination, node.FirstChild.Literal) { out(w, string(node.FirstChild.Literal)) } // Hyphens in a link must be escaped to avoid word-wrap in the rendered man page. escapedLink := strings.ReplaceAll(string(node.LinkData.Destination), "-", "\\-") out(w, linkTag+escapedLink+linkCloseTag) walkAction = blackfriday.SkipChildren case blackfriday.Image: // ignore images walkAction = blackfriday.SkipChildren case blackfriday.Code: out(w, codespanTag) escapeSpecialChars(w, node.Literal) out(w, codespanCloseTag) case blackfriday.Document: break case blackfriday.Paragraph: if entering { if r.listDepth > 0 { // roff .PP markers break lists if node.Prev != nil { // continued paragraph if node.Prev.Type == blackfriday.List && node.Prev.ListFlags&blackfriday.ListTypeDefinition == 0 { out(w, ".IP\n") } else { out(w, crTag) } } } else if node.Prev != nil && node.Prev.Type == blackfriday.Heading { out(w, crTag) } else { out(w, paraTag) } } else { if node.Next == nil || node.Next.Type != blackfriday.List { out(w, crTag) } } case blackfriday.BlockQuote: if entering { out(w, quoteTag) } else { out(w, quoteCloseTag) } case blackfriday.Heading: r.handleHeading(w, node, entering) case blackfriday.HorizontalRule: out(w, hruleTag) case blackfriday.List: r.handleList(w, node, entering) case blackfriday.Item: r.handleItem(w, node, entering) case blackfriday.CodeBlock: out(w, codeTag) escapeSpecialChars(w, node.Literal) out(w, codeCloseTag) case blackfriday.Table: r.handleTable(w, node, entering) case blackfriday.TableHead: case blackfriday.TableBody: case blackfriday.TableRow: // no action as cell entries do all the nroff formatting return blackfriday.GoToNext case blackfriday.TableCell: r.handleTableCell(w, node, entering) case blackfriday.HTMLSpan: // ignore other HTML tags case blackfriday.HTMLBlock: if bytes.HasPrefix(node.Literal, []byte("|" processingInstruction = "[<][?].*?[?][>]" singleQuotedValue = "'[^']*'" tagName = "[A-Za-z][A-Za-z0-9-]*" unquotedValue = "[^\"'=<>`\\x00-\\x20]+" ) // HTMLRendererParameters is a collection of supplementary parameters tweaking // the behavior of various parts of HTML renderer. type HTMLRendererParameters struct { // Prepend this text to each relative URL. AbsolutePrefix string // Add this text to each footnote anchor, to ensure uniqueness. FootnoteAnchorPrefix string // Show this text inside the tag for a footnote return link, if the // HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string // [return] is used. FootnoteReturnLinkContents string // If set, add this text to the front of each Heading ID, to ensure // uniqueness. HeadingIDPrefix string // If set, add this text to the back of each Heading ID, to ensure uniqueness. HeadingIDSuffix string // Increase heading levels: if the offset is 1,

becomes

etc. // Negative offset is also valid. // Resulting levels are clipped between 1 and 6. HeadingLevelOffset int Title string // Document title (used if CompletePage is set) CSS string // Optional CSS file URL (used if CompletePage is set) Icon string // Optional icon file URL (used if CompletePage is set) Flags HTMLFlags // Flags allow customizing this renderer's behavior } // HTMLRenderer is a type that implements the Renderer interface for HTML output. // // Do not create this directly, instead use the NewHTMLRenderer function. type HTMLRenderer struct { HTMLRendererParameters closeTag string // how to end singleton tags: either " />" or ">" // Track heading IDs to prevent ID collision in a single generation. headingIDs map[string]int lastOutputLen int disableTags int sr *SPRenderer } const ( xhtmlClose = " />" htmlClose = ">" ) // NewHTMLRenderer creates and configures an HTMLRenderer object, which // satisfies the Renderer interface. func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer { // configure the rendering engine closeTag := htmlClose if params.Flags&UseXHTML != 0 { closeTag = xhtmlClose } if params.FootnoteReturnLinkContents == "" { // U+FE0E is VARIATION SELECTOR-15. // It suppresses automatic emoji presentation of the preceding // U+21A9 LEFTWARDS ARROW WITH HOOK on iOS and iPadOS. params.FootnoteReturnLinkContents = "↩\ufe0e" } return &HTMLRenderer{ HTMLRendererParameters: params, closeTag: closeTag, headingIDs: make(map[string]int), sr: NewSmartypantsRenderer(params.Flags), } } func isHTMLTag(tag []byte, tagname string) bool { found, _ := findHTMLTagPos(tag, tagname) return found } // Look for a character, but ignore it when it's in any kind of quotes, it // might be JavaScript func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int { inSingleQuote := false inDoubleQuote := false inGraveQuote := false i := start for i < len(html) { switch { case html[i] == char && !inSingleQuote && !inDoubleQuote && !inGraveQuote: return i case html[i] == '\'': inSingleQuote = !inSingleQuote case html[i] == '"': inDoubleQuote = !inDoubleQuote case html[i] == '`': inGraveQuote = !inGraveQuote } i++ } return start } func findHTMLTagPos(tag []byte, tagname string) (bool, int) { i := 0 if i < len(tag) && tag[0] != '<' { return false, -1 } i++ i = skipSpace(tag, i) if i < len(tag) && tag[i] == '/' { i++ } i = skipSpace(tag, i) j := 0 for ; i < len(tag); i, j = i+1, j+1 { if j >= len(tagname) { break } if strings.ToLower(string(tag[i]))[0] != tagname[j] { return false, -1 } } if i == len(tag) { return false, -1 } rightAngle := skipUntilCharIgnoreQuotes(tag, i, '>') if rightAngle >= i { return true, rightAngle } return false, -1 } func skipSpace(tag []byte, i int) int { for i < len(tag) && isspace(tag[i]) { i++ } return i } func isRelativeLink(link []byte) (yes bool) { // a tag begin with '#' if link[0] == '#' { return true } // link begin with '/' but not '//', the second maybe a protocol relative link if len(link) >= 2 && link[0] == '/' && link[1] != '/' { return true } // only the root '/' if len(link) == 1 && link[0] == '/' { return true } // current directory : begin with "./" if bytes.HasPrefix(link, []byte("./")) { return true } // parent directory : begin with "../" if bytes.HasPrefix(link, []byte("../")) { return true } return false } func (r *HTMLRenderer) ensureUniqueHeadingID(id string) string { for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] { tmp := fmt.Sprintf("%s-%d", id, count+1) if _, tmpFound := r.headingIDs[tmp]; !tmpFound { r.headingIDs[id] = count + 1 id = tmp } else { id = id + "-1" } } if _, found := r.headingIDs[id]; !found { r.headingIDs[id] = 0 } return id } func (r *HTMLRenderer) addAbsPrefix(link []byte) []byte { if r.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' { newDest := r.AbsolutePrefix if link[0] != '/' { newDest += "/" } newDest += string(link) return []byte(newDest) } return link } func appendLinkAttrs(attrs []string, flags HTMLFlags, link []byte) []string { if isRelativeLink(link) { return attrs } val := []string{} if flags&NofollowLinks != 0 { val = append(val, "nofollow") } if flags&NoreferrerLinks != 0 { val = append(val, "noreferrer") } if flags&NoopenerLinks != 0 { val = append(val, "noopener") } if flags&HrefTargetBlank != 0 { attrs = append(attrs, "target=\"_blank\"") } if len(val) == 0 { return attrs } attr := fmt.Sprintf("rel=%q", strings.Join(val, " ")) return append(attrs, attr) } func isMailto(link []byte) bool { return bytes.HasPrefix(link, []byte("mailto:")) } func needSkipLink(flags HTMLFlags, dest []byte) bool { if flags&SkipLinks != 0 { return true } return flags&Safelink != 0 && !isSafeLink(dest) && !isMailto(dest) } func isSmartypantable(node *Node) bool { pt := node.Parent.Type return pt != Link && pt != CodeBlock && pt != Code } func appendLanguageAttr(attrs []string, info []byte) []string { if len(info) == 0 { return attrs } endOfLang := bytes.IndexAny(info, "\t ") if endOfLang < 0 { endOfLang = len(info) } return append(attrs, fmt.Sprintf("class=\"language-%s\"", info[:endOfLang])) } func (r *HTMLRenderer) tag(w io.Writer, name []byte, attrs []string) { w.Write(name) if len(attrs) > 0 { w.Write(spaceBytes) w.Write([]byte(strings.Join(attrs, " "))) } w.Write(gtBytes) r.lastOutputLen = 1 } func footnoteRef(prefix string, node *Node) []byte { urlFrag := prefix + string(slugify(node.Destination)) anchor := fmt.Sprintf(`%d`, urlFrag, node.NoteID) return []byte(fmt.Sprintf(`%s`, urlFrag, anchor)) } func footnoteItem(prefix string, slug []byte) []byte { return []byte(fmt.Sprintf(`
  • `, prefix, slug)) } func footnoteReturnLink(prefix, returnLink string, slug []byte) []byte { const format = ` %s` return []byte(fmt.Sprintf(format, prefix, slug, returnLink)) } func itemOpenCR(node *Node) bool { if node.Prev == nil { return false } ld := node.Parent.ListData return !ld.Tight && ld.ListFlags&ListTypeDefinition == 0 } func skipParagraphTags(node *Node) bool { grandparent := node.Parent.Parent if grandparent == nil || grandparent.Type != List { return false } tightOrTerm := grandparent.Tight || node.Parent.ListFlags&ListTypeTerm != 0 return grandparent.Type == List && tightOrTerm } func cellAlignment(align CellAlignFlags) string { switch align { case TableAlignmentLeft: return "left" case TableAlignmentRight: return "right" case TableAlignmentCenter: return "center" default: return "" } } func (r *HTMLRenderer) out(w io.Writer, text []byte) { if r.disableTags > 0 { w.Write(htmlTagRe.ReplaceAll(text, []byte{})) } else { w.Write(text) } r.lastOutputLen = len(text) } func (r *HTMLRenderer) cr(w io.Writer) { if r.lastOutputLen > 0 { r.out(w, nlBytes) } } var ( nlBytes = []byte{'\n'} gtBytes = []byte{'>'} spaceBytes = []byte{' '} ) var ( brTag = []byte("
    ") brXHTMLTag = []byte("
    ") emTag = []byte("") emCloseTag = []byte("") strongTag = []byte("") strongCloseTag = []byte("") delTag = []byte("") delCloseTag = []byte("") ttTag = []byte("") ttCloseTag = []byte("") aTag = []byte("") preTag = []byte("
    ")
    	preCloseTag        = []byte("
    ") codeTag = []byte("") codeCloseTag = []byte("") pTag = []byte("

    ") pCloseTag = []byte("

    ") blockquoteTag = []byte("
    ") blockquoteCloseTag = []byte("
    ") hrTag = []byte("
    ") hrXHTMLTag = []byte("
    ") ulTag = []byte("
      ") ulCloseTag = []byte("
    ") olTag = []byte("
      ") olCloseTag = []byte("
    ") dlTag = []byte("
    ") dlCloseTag = []byte("
    ") liTag = []byte("
  • ") liCloseTag = []byte("
  • ") ddTag = []byte("
    ") ddCloseTag = []byte("
    ") dtTag = []byte("
    ") dtCloseTag = []byte("
    ") tableTag = []byte("") tableCloseTag = []byte("
    ") tdTag = []byte("") thTag = []byte("") theadTag = []byte("") theadCloseTag = []byte("") tbodyTag = []byte("") tbodyCloseTag = []byte("") trTag = []byte("") trCloseTag = []byte("") h1Tag = []byte("") h2Tag = []byte("") h3Tag = []byte("") h4Tag = []byte("") h5Tag = []byte("") h6Tag = []byte("") footnotesDivBytes = []byte("\n
    \n\n") footnotesCloseDivBytes = []byte("\n
    \n") ) func headingTagsFromLevel(level int) ([]byte, []byte) { if level <= 1 { return h1Tag, h1CloseTag } switch level { case 2: return h2Tag, h2CloseTag case 3: return h3Tag, h3CloseTag case 4: return h4Tag, h4CloseTag case 5: return h5Tag, h5CloseTag } return h6Tag, h6CloseTag } func (r *HTMLRenderer) outHRTag(w io.Writer) { if r.Flags&UseXHTML == 0 { r.out(w, hrTag) } else { r.out(w, hrXHTMLTag) } } // RenderNode is a default renderer of a single node of a syntax tree. For // block nodes it will be called twice: first time with entering=true, second // time with entering=false, so that it could know when it's working on an open // tag and when on close. It writes the result to w. // // The return value is a way to tell the calling walker to adjust its walk // pattern: e.g. it can terminate the traversal by returning Terminate. Or it // can ask the walker to skip a subtree of this node by returning SkipChildren. // The typical behavior is to return GoToNext, which asks for the usual // traversal to the next node. func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus { attrs := []string{} switch node.Type { case Text: if r.Flags&Smartypants != 0 { var tmp bytes.Buffer escapeHTML(&tmp, node.Literal) r.sr.Process(w, tmp.Bytes()) } else { if node.Parent.Type == Link { escLink(w, node.Literal) } else { escapeHTML(w, node.Literal) } } case Softbreak: r.cr(w) // TODO: make it configurable via out(renderer.softbreak) case Hardbreak: if r.Flags&UseXHTML == 0 { r.out(w, brTag) } else { r.out(w, brXHTMLTag) } r.cr(w) case Emph: if entering { r.out(w, emTag) } else { r.out(w, emCloseTag) } case Strong: if entering { r.out(w, strongTag) } else { r.out(w, strongCloseTag) } case Del: if entering { r.out(w, delTag) } else { r.out(w, delCloseTag) } case HTMLSpan: if r.Flags&SkipHTML != 0 { break } r.out(w, node.Literal) case Link: // mark it but don't link it if it is not a safe link: no smartypants dest := node.LinkData.Destination if needSkipLink(r.Flags, dest) { if entering { r.out(w, ttTag) } else { r.out(w, ttCloseTag) } } else { if entering { dest = r.addAbsPrefix(dest) var hrefBuf bytes.Buffer hrefBuf.WriteString("href=\"") escLink(&hrefBuf, dest) hrefBuf.WriteByte('"') attrs = append(attrs, hrefBuf.String()) if node.NoteID != 0 { r.out(w, footnoteRef(r.FootnoteAnchorPrefix, node)) break } attrs = appendLinkAttrs(attrs, r.Flags, dest) if len(node.LinkData.Title) > 0 { var titleBuff bytes.Buffer titleBuff.WriteString("title=\"") escapeHTML(&titleBuff, node.LinkData.Title) titleBuff.WriteByte('"') attrs = append(attrs, titleBuff.String()) } r.tag(w, aTag, attrs) } else { if node.NoteID != 0 { break } r.out(w, aCloseTag) } } case Image: if r.Flags&SkipImages != 0 { return SkipChildren } if entering { dest := node.LinkData.Destination dest = r.addAbsPrefix(dest) if r.disableTags == 0 { //if options.safe && potentiallyUnsafe(dest) { //out(w, ``)
				//} else {
				r.out(w, []byte(`<img src=`)) } } case Code: r.out(w, codeTag) escapeAllHTML(w, node.Literal) r.out(w, codeCloseTag) case Document: break case Paragraph: if skipParagraphTags(node) { break } if entering { // TODO: untangle this clusterfuck about when the newlines need // to be added and when not. if node.Prev != nil { switch node.Prev.Type { case HTMLBlock, List, Paragraph, Heading, CodeBlock, BlockQuote, HorizontalRule: r.cr(w) } } if node.Parent.Type == BlockQuote && node.Prev == nil { r.cr(w) } r.out(w, pTag) } else { r.out(w, pCloseTag) if !(node.Parent.Type == Item && node.Next == nil) { r.cr(w) } } case BlockQuote: if entering { r.cr(w) r.out(w, blockquoteTag) } else { r.out(w, blockquoteCloseTag) r.cr(w) } case HTMLBlock: if r.Flags&SkipHTML != 0 { break } r.cr(w) r.out(w, node.Literal) r.cr(w) case Heading: headingLevel := r.HTMLRendererParameters.HeadingLevelOffset + node.Level openTag, closeTag := headingTagsFromLevel(headingLevel) if entering { if node.IsTitleblock { attrs = append(attrs, `class="title"`) } if node.HeadingID != "" { id := r.ensureUniqueHeadingID(node.HeadingID) if r.HeadingIDPrefix != "" { id = r.HeadingIDPrefix + id } if r.HeadingIDSuffix != "" { id = id + r.HeadingIDSuffix } attrs = append(attrs, fmt.Sprintf(`id="%s"`, id)) } r.cr(w) r.tag(w, openTag, attrs) } else { r.out(w, closeTag) if !(node.Parent.Type == Item && node.Next == nil) { r.cr(w) } } case HorizontalRule: r.cr(w) r.outHRTag(w) r.cr(w) case List: openTag := ulTag closeTag := ulCloseTag if node.ListFlags&ListTypeOrdered != 0 { openTag = olTag closeTag = olCloseTag } if node.ListFlags&ListTypeDefinition != 0 { openTag = dlTag closeTag = dlCloseTag } if entering { if node.IsFootnotesList { r.out(w, footnotesDivBytes) r.outHRTag(w) r.cr(w) } r.cr(w) if node.Parent.Type == Item && node.Parent.Parent.Tight { r.cr(w) } r.tag(w, openTag[:len(openTag)-1], attrs) r.cr(w) } else { r.out(w, closeTag) //cr(w) //if node.parent.Type != Item { // cr(w) //} if node.Parent.Type == Item && node.Next != nil { r.cr(w) } if node.Parent.Type == Document || node.Parent.Type == BlockQuote { r.cr(w) } if node.IsFootnotesList { r.out(w, footnotesCloseDivBytes) } } case Item: openTag := liTag closeTag := liCloseTag if node.ListFlags&ListTypeDefinition != 0 { openTag = ddTag closeTag = ddCloseTag } if node.ListFlags&ListTypeTerm != 0 { openTag = dtTag closeTag = dtCloseTag } if entering { if itemOpenCR(node) { r.cr(w) } if node.ListData.RefLink != nil { slug := slugify(node.ListData.RefLink) r.out(w, footnoteItem(r.FootnoteAnchorPrefix, slug)) break } r.out(w, openTag) } else { if node.ListData.RefLink != nil { slug := slugify(node.ListData.RefLink) if r.Flags&FootnoteReturnLinks != 0 { r.out(w, footnoteReturnLink(r.FootnoteAnchorPrefix, r.FootnoteReturnLinkContents, slug)) } } r.out(w, closeTag) r.cr(w) } case CodeBlock: attrs = appendLanguageAttr(attrs, node.Info) r.cr(w) r.out(w, preTag) r.tag(w, codeTag[:len(codeTag)-1], attrs) escapeAllHTML(w, node.Literal) r.out(w, codeCloseTag) r.out(w, preCloseTag) if node.Parent.Type != Item { r.cr(w) } case Table: if entering { r.cr(w) r.out(w, tableTag) } else { r.out(w, tableCloseTag) r.cr(w) } case TableCell: openTag := tdTag closeTag := tdCloseTag if node.IsHeader { openTag = thTag closeTag = thCloseTag } if entering { align := cellAlignment(node.Align) if align != "" { attrs = append(attrs, fmt.Sprintf(`align="%s"`, align)) } if node.Prev == nil { r.cr(w) } r.tag(w, openTag, attrs) } else { r.out(w, closeTag) r.cr(w) } case TableHead: if entering { r.cr(w) r.out(w, theadTag) } else { r.out(w, theadCloseTag) r.cr(w) } case TableBody: if entering { r.cr(w) r.out(w, tbodyTag) // XXX: this is to adhere to a rather silly test. Should fix test. if node.FirstChild == nil { r.cr(w) } } else { r.out(w, tbodyCloseTag) r.cr(w) } case TableRow: if entering { r.cr(w) r.out(w, trTag) } else { r.out(w, trCloseTag) r.cr(w) } default: panic("Unknown node type " + node.Type.String()) } return GoToNext } // RenderHeader writes HTML document preamble and TOC if requested. func (r *HTMLRenderer) RenderHeader(w io.Writer, ast *Node) { r.writeDocumentHeader(w) if r.Flags&TOC != 0 { r.writeTOC(w, ast) } } // RenderFooter writes HTML document footer. func (r *HTMLRenderer) RenderFooter(w io.Writer, ast *Node) { if r.Flags&CompletePage == 0 { return } io.WriteString(w, "\n\n\n") } func (r *HTMLRenderer) writeDocumentHeader(w io.Writer) { if r.Flags&CompletePage == 0 { return } ending := "" if r.Flags&UseXHTML != 0 { io.WriteString(w, "\n") io.WriteString(w, "\n") ending = " /" } else { io.WriteString(w, "\n") io.WriteString(w, "\n") } io.WriteString(w, "\n") io.WriteString(w, " ") if r.Flags&Smartypants != 0 { r.sr.Process(w, []byte(r.Title)) } else { escapeHTML(w, []byte(r.Title)) } io.WriteString(w, "\n") io.WriteString(w, " \n") io.WriteString(w, " \n") if r.CSS != "" { io.WriteString(w, " \n") } if r.Icon != "" { io.WriteString(w, " \n") } io.WriteString(w, "\n") io.WriteString(w, "\n\n") } func (r *HTMLRenderer) writeTOC(w io.Writer, ast *Node) { buf := bytes.Buffer{} inHeading := false tocLevel := 0 headingCount := 0 ast.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Heading && !node.HeadingData.IsTitleblock { inHeading = entering if entering { node.HeadingID = fmt.Sprintf("toc_%d", headingCount) if node.Level == tocLevel { buf.WriteString("\n\n
  • ") } else if node.Level < tocLevel { for node.Level < tocLevel { tocLevel-- buf.WriteString("
  • \n") } buf.WriteString("\n\n
  • ") } else { for node.Level > tocLevel { tocLevel++ buf.WriteString("\n") } if buf.Len() > 0 { io.WriteString(w, "\n") } r.lastOutputLen = buf.Len() } png2svg-1.7.0/vendor/github.com/russross/blackfriday/v2/inline.go000066400000000000000000000623401504213346300247610ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Functions to parse inline elements. // package blackfriday import ( "bytes" "regexp" "strconv" ) var ( urlRe = `((https?|ftp):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+` anchorRe = regexp.MustCompile(`^(]+")?\s?>` + urlRe + `<\/a>)`) // https://www.w3.org/TR/html5/syntax.html#character-references // highest unicode code point in 17 planes (2^20): 1,114,112d = // 7 dec digits or 6 hex digits // named entity references can be 2-31 characters with stuff like < // at one end and ∳ at the other. There // are also sometimes numbers at the end, although this isn't inherent // in the specification; there are never numbers anywhere else in // current character references, though; see ¾ and ▒, etc. // https://www.w3.org/TR/html5/syntax.html#named-character-references // // entity := "&" (named group | number ref) ";" // named group := [a-zA-Z]{2,31}[0-9]{0,2} // number ref := "#" (dec ref | hex ref) // dec ref := [0-9]{1,7} // hex ref := ("x" | "X") [0-9a-fA-F]{1,6} htmlEntityRe = regexp.MustCompile(`&([a-zA-Z]{2,31}[0-9]{0,2}|#([0-9]{1,7}|[xX][0-9a-fA-F]{1,6}));`) ) // Functions to parse text within a block // Each function returns the number of chars taken care of // data is the complete block being rendered // offset is the number of valid chars before the current cursor func (p *Markdown) inline(currBlock *Node, data []byte) { // handlers might call us recursively: enforce a maximum depth if p.nesting >= p.maxNesting || len(data) == 0 { return } p.nesting++ beg, end := 0, 0 for end < len(data) { handler := p.inlineCallback[data[end]] if handler != nil { if consumed, node := handler(p, data, end); consumed == 0 { // No action from the callback. end++ } else { // Copy inactive chars into the output. currBlock.AppendChild(text(data[beg:end])) if node != nil { currBlock.AppendChild(node) } // Skip past whatever the callback used. beg = end + consumed end = beg } } else { end++ } } if beg < len(data) { if data[end-1] == '\n' { end-- } currBlock.AppendChild(text(data[beg:end])) } p.nesting-- } // single and double emphasis parsing func emphasis(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] c := data[0] if len(data) > 2 && data[1] != c { // whitespace cannot follow an opening emphasis; // strikethrough only takes two characters '~~' if c == '~' || isspace(data[1]) { return 0, nil } ret, node := helperEmphasis(p, data[1:], c) if ret == 0 { return 0, nil } return ret + 1, node } if len(data) > 3 && data[1] == c && data[2] != c { if isspace(data[2]) { return 0, nil } ret, node := helperDoubleEmphasis(p, data[2:], c) if ret == 0 { return 0, nil } return ret + 2, node } if len(data) > 4 && data[1] == c && data[2] == c && data[3] != c { if c == '~' || isspace(data[3]) { return 0, nil } ret, node := helperTripleEmphasis(p, data, 3, c) if ret == 0 { return 0, nil } return ret + 3, node } return 0, nil } func codeSpan(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] nb := 0 // count the number of backticks in the delimiter for nb < len(data) && data[nb] == '`' { nb++ } // find the next delimiter i, end := 0, 0 for end = nb; end < len(data) && i < nb; end++ { if data[end] == '`' { i++ } else { i = 0 } } // no matching delimiter? if i < nb && end >= len(data) { return 0, nil } // trim outside whitespace fBegin := nb for fBegin < end && data[fBegin] == ' ' { fBegin++ } fEnd := end - nb for fEnd > fBegin && data[fEnd-1] == ' ' { fEnd-- } // render the code span if fBegin != fEnd { code := NewNode(Code) code.Literal = data[fBegin:fEnd] return end, code } return end, nil } // newline preceded by two spaces becomes
    func maybeLineBreak(p *Markdown, data []byte, offset int) (int, *Node) { origOffset := offset for offset < len(data) && data[offset] == ' ' { offset++ } if offset < len(data) && data[offset] == '\n' { if offset-origOffset >= 2 { return offset - origOffset + 1, NewNode(Hardbreak) } return offset - origOffset, nil } return 0, nil } // newline without two spaces works when HardLineBreak is enabled func lineBreak(p *Markdown, data []byte, offset int) (int, *Node) { if p.extensions&HardLineBreak != 0 { return 1, NewNode(Hardbreak) } return 0, nil } type linkType int const ( linkNormal linkType = iota linkImg linkDeferredFootnote linkInlineFootnote ) func isReferenceStyleLink(data []byte, pos int, t linkType) bool { if t == linkDeferredFootnote { return false } return pos < len(data)-1 && data[pos] == '[' && data[pos+1] != '^' } func maybeImage(p *Markdown, data []byte, offset int) (int, *Node) { if offset < len(data)-1 && data[offset+1] == '[' { return link(p, data, offset) } return 0, nil } func maybeInlineFootnote(p *Markdown, data []byte, offset int) (int, *Node) { if offset < len(data)-1 && data[offset+1] == '[' { return link(p, data, offset) } return 0, nil } // '[': parse a link or an image or a footnote func link(p *Markdown, data []byte, offset int) (int, *Node) { // no links allowed inside regular links, footnote, and deferred footnotes if p.insideLink && (offset > 0 && data[offset-1] == '[' || len(data)-1 > offset && data[offset+1] == '^') { return 0, nil } var t linkType switch { // special case: ![^text] == deferred footnote (that follows something with // an exclamation point) case p.extensions&Footnotes != 0 && len(data)-1 > offset && data[offset+1] == '^': t = linkDeferredFootnote // ![alt] == image case offset >= 0 && data[offset] == '!': t = linkImg offset++ // ^[text] == inline footnote // [^refId] == deferred footnote case p.extensions&Footnotes != 0: if offset >= 0 && data[offset] == '^' { t = linkInlineFootnote offset++ } else if len(data)-1 > offset && data[offset+1] == '^' { t = linkDeferredFootnote } // [text] == regular link default: t = linkNormal } data = data[offset:] var ( i = 1 noteID int title, link, altContent []byte textHasNl = false ) if t == linkDeferredFootnote { i++ } // look for the matching closing bracket for level := 1; level > 0 && i < len(data); i++ { switch { case data[i] == '\n': textHasNl = true case isBackslashEscaped(data, i): continue case data[i] == '[': level++ case data[i] == ']': level-- if level <= 0 { i-- // compensate for extra i++ in for loop } } } if i >= len(data) { return 0, nil } txtE := i i++ var footnoteNode *Node // skip any amount of whitespace or newline // (this is much more lax than original markdown syntax) for i < len(data) && isspace(data[i]) { i++ } // inline style link switch { case i < len(data) && data[i] == '(': // skip initial whitespace i++ for i < len(data) && isspace(data[i]) { i++ } linkB := i // look for link end: ' " ) findlinkend: for i < len(data) { switch { case data[i] == '\\': i += 2 case data[i] == ')' || data[i] == '\'' || data[i] == '"': break findlinkend default: i++ } } if i >= len(data) { return 0, nil } linkE := i // look for title end if present titleB, titleE := 0, 0 if data[i] == '\'' || data[i] == '"' { i++ titleB = i findtitleend: for i < len(data) { switch { case data[i] == '\\': i += 2 case data[i] == ')': break findtitleend default: i++ } } if i >= len(data) { return 0, nil } // skip whitespace after title titleE = i - 1 for titleE > titleB && isspace(data[titleE]) { titleE-- } // check for closing quote presence if data[titleE] != '\'' && data[titleE] != '"' { titleB, titleE = 0, 0 linkE = i } } // remove whitespace at the end of the link for linkE > linkB && isspace(data[linkE-1]) { linkE-- } // remove optional angle brackets around the link if data[linkB] == '<' { linkB++ } if data[linkE-1] == '>' { linkE-- } // build escaped link and title if linkE > linkB { link = data[linkB:linkE] } if titleE > titleB { title = data[titleB:titleE] } i++ // reference style link case isReferenceStyleLink(data, i, t): var id []byte altContentConsidered := false // look for the id i++ linkB := i for i < len(data) && data[i] != ']' { i++ } if i >= len(data) { return 0, nil } linkE := i // find the reference if linkB == linkE { if textHasNl { var b bytes.Buffer for j := 1; j < txtE; j++ { switch { case data[j] != '\n': b.WriteByte(data[j]) case data[j-1] != ' ': b.WriteByte(' ') } } id = b.Bytes() } else { id = data[1:txtE] altContentConsidered = true } } else { id = data[linkB:linkE] } // find the reference with matching id lr, ok := p.getRef(string(id)) if !ok { return 0, nil } // keep link and title from reference link = lr.link title = lr.title if altContentConsidered { altContent = lr.text } i++ // shortcut reference style link or reference or inline footnote default: var id []byte // craft the id if textHasNl { var b bytes.Buffer for j := 1; j < txtE; j++ { switch { case data[j] != '\n': b.WriteByte(data[j]) case data[j-1] != ' ': b.WriteByte(' ') } } id = b.Bytes() } else { if t == linkDeferredFootnote { id = data[2:txtE] // get rid of the ^ } else { id = data[1:txtE] } } footnoteNode = NewNode(Item) if t == linkInlineFootnote { // create a new reference noteID = len(p.notes) + 1 var fragment []byte if len(id) > 0 { if len(id) < 16 { fragment = make([]byte, len(id)) } else { fragment = make([]byte, 16) } copy(fragment, slugify(id)) } else { fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteID))...) } ref := &reference{ noteID: noteID, hasBlock: false, link: fragment, title: id, footnote: footnoteNode, } p.notes = append(p.notes, ref) link = ref.link title = ref.title } else { // find the reference with matching id lr, ok := p.getRef(string(id)) if !ok { return 0, nil } if t == linkDeferredFootnote { lr.noteID = len(p.notes) + 1 lr.footnote = footnoteNode p.notes = append(p.notes, lr) } // keep link and title from reference link = lr.link // if inline footnote, title == footnote contents title = lr.title noteID = lr.noteID } // rewind the whitespace i = txtE + 1 } var uLink []byte if t == linkNormal || t == linkImg { if len(link) > 0 { var uLinkBuf bytes.Buffer unescapeText(&uLinkBuf, link) uLink = uLinkBuf.Bytes() } // links need something to click on and somewhere to go if len(uLink) == 0 || (t == linkNormal && txtE <= 1) { return 0, nil } } // call the relevant rendering function var linkNode *Node switch t { case linkNormal: linkNode = NewNode(Link) linkNode.Destination = normalizeURI(uLink) linkNode.Title = title if len(altContent) > 0 { linkNode.AppendChild(text(altContent)) } else { // links cannot contain other links, so turn off link parsing // temporarily and recurse insideLink := p.insideLink p.insideLink = true p.inline(linkNode, data[1:txtE]) p.insideLink = insideLink } case linkImg: linkNode = NewNode(Image) linkNode.Destination = uLink linkNode.Title = title linkNode.AppendChild(text(data[1:txtE])) i++ case linkInlineFootnote, linkDeferredFootnote: linkNode = NewNode(Link) linkNode.Destination = link linkNode.Title = title linkNode.NoteID = noteID linkNode.Footnote = footnoteNode if t == linkInlineFootnote { i++ } default: return 0, nil } return i, linkNode } func (p *Markdown) inlineHTMLComment(data []byte) int { if len(data) < 5 { return 0 } if data[0] != '<' || data[1] != '!' || data[2] != '-' || data[3] != '-' { return 0 } i := 5 // scan for an end-of-comment marker, across lines if necessary for i < len(data) && !(data[i-2] == '-' && data[i-1] == '-' && data[i] == '>') { i++ } // no end-of-comment marker if i >= len(data) { return 0 } return i + 1 } func stripMailto(link []byte) []byte { if bytes.HasPrefix(link, []byte("mailto://")) { return link[9:] } else if bytes.HasPrefix(link, []byte("mailto:")) { return link[7:] } else { return link } } // autolinkType specifies a kind of autolink that gets detected. type autolinkType int // These are the possible flag values for the autolink renderer. const ( notAutolink autolinkType = iota normalAutolink emailAutolink ) // '<' when tags or autolinks are allowed func leftAngle(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] altype, end := tagLength(data) if size := p.inlineHTMLComment(data); size > 0 { end = size } if end > 2 { if altype != notAutolink { var uLink bytes.Buffer unescapeText(&uLink, data[1:end+1-2]) if uLink.Len() > 0 { link := uLink.Bytes() node := NewNode(Link) node.Destination = link if altype == emailAutolink { node.Destination = append([]byte("mailto:"), link...) } node.AppendChild(text(stripMailto(link))) return end, node } } else { htmlTag := NewNode(HTMLSpan) htmlTag.Literal = data[:end] return end, htmlTag } } return end, nil } // '\\' backslash escape var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~") func escape(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] if len(data) > 1 { if p.extensions&BackslashLineBreak != 0 && data[1] == '\n' { return 2, NewNode(Hardbreak) } if bytes.IndexByte(escapeChars, data[1]) < 0 { return 0, nil } return 2, text(data[1:2]) } return 2, nil } func unescapeText(ob *bytes.Buffer, src []byte) { i := 0 for i < len(src) { org := i for i < len(src) && src[i] != '\\' { i++ } if i > org { ob.Write(src[org:i]) } if i+1 >= len(src) { break } ob.WriteByte(src[i+1]) i += 2 } } // '&' escaped when it doesn't belong to an entity // valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; func entity(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] end := 1 if end < len(data) && data[end] == '#' { end++ } for end < len(data) && isalnum(data[end]) { end++ } if end < len(data) && data[end] == ';' { end++ // real entity } else { return 0, nil // lone '&' } ent := data[:end] // undo & escaping or it will be converted to &amp; by another // escaper in the renderer if bytes.Equal(ent, []byte("&")) { ent = []byte{'&'} } return end, text(ent) } func linkEndsWithEntity(data []byte, linkEnd int) bool { entityRanges := htmlEntityRe.FindAllIndex(data[:linkEnd], -1) return entityRanges != nil && entityRanges[len(entityRanges)-1][1] == linkEnd } // hasPrefixCaseInsensitive is a custom implementation of // strings.HasPrefix(strings.ToLower(s), prefix) // we rolled our own because ToLower pulls in a huge machinery of lowercasing // anything from Unicode and that's very slow. Since this func will only be // used on ASCII protocol prefixes, we can take shortcuts. func hasPrefixCaseInsensitive(s, prefix []byte) bool { if len(s) < len(prefix) { return false } delta := byte('a' - 'A') for i, b := range prefix { if b != s[i] && b != s[i]+delta { return false } } return true } var protocolPrefixes = [][]byte{ []byte("http://"), []byte("https://"), []byte("ftp://"), []byte("file://"), []byte("mailto:"), } const shortestPrefix = 6 // len("ftp://"), the shortest of the above func maybeAutoLink(p *Markdown, data []byte, offset int) (int, *Node) { // quick check to rule out most false hits if p.insideLink || len(data) < offset+shortestPrefix { return 0, nil } for _, prefix := range protocolPrefixes { endOfHead := offset + 8 // 8 is the len() of the longest prefix if endOfHead > len(data) { endOfHead = len(data) } if hasPrefixCaseInsensitive(data[offset:endOfHead], prefix) { return autoLink(p, data, offset) } } return 0, nil } func autoLink(p *Markdown, data []byte, offset int) (int, *Node) { // Now a more expensive check to see if we're not inside an anchor element anchorStart := offset offsetFromAnchor := 0 for anchorStart > 0 && data[anchorStart] != '<' { anchorStart-- offsetFromAnchor++ } anchorStr := anchorRe.Find(data[anchorStart:]) if anchorStr != nil { anchorClose := NewNode(HTMLSpan) anchorClose.Literal = anchorStr[offsetFromAnchor:] return len(anchorStr) - offsetFromAnchor, anchorClose } // scan backward for a word boundary rewind := 0 for offset-rewind > 0 && rewind <= 7 && isletter(data[offset-rewind-1]) { rewind++ } if rewind > 6 { // longest supported protocol is "mailto" which has 6 letters return 0, nil } origData := data data = data[offset-rewind:] if !isSafeLink(data) { return 0, nil } linkEnd := 0 for linkEnd < len(data) && !isEndOfLink(data[linkEnd]) { linkEnd++ } // Skip punctuation at the end of the link if (data[linkEnd-1] == '.' || data[linkEnd-1] == ',') && data[linkEnd-2] != '\\' { linkEnd-- } // But don't skip semicolon if it's a part of escaped entity: if data[linkEnd-1] == ';' && data[linkEnd-2] != '\\' && !linkEndsWithEntity(data, linkEnd) { linkEnd-- } // See if the link finishes with a punctuation sign that can be closed. var copen byte switch data[linkEnd-1] { case '"': copen = '"' case '\'': copen = '\'' case ')': copen = '(' case ']': copen = '[' case '}': copen = '{' default: copen = 0 } if copen != 0 { bufEnd := offset - rewind + linkEnd - 2 openDelim := 1 /* Try to close the final punctuation sign in this same line; * if we managed to close it outside of the URL, that means that it's * not part of the URL. If it closes inside the URL, that means it * is part of the URL. * * Examples: * * foo http://www.pokemon.com/Pikachu_(Electric) bar * => http://www.pokemon.com/Pikachu_(Electric) * * foo (http://www.pokemon.com/Pikachu_(Electric)) bar * => http://www.pokemon.com/Pikachu_(Electric) * * foo http://www.pokemon.com/Pikachu_(Electric)) bar * => http://www.pokemon.com/Pikachu_(Electric)) * * (foo http://www.pokemon.com/Pikachu_(Electric)) bar * => foo http://www.pokemon.com/Pikachu_(Electric) */ for bufEnd >= 0 && origData[bufEnd] != '\n' && openDelim != 0 { if origData[bufEnd] == data[linkEnd-1] { openDelim++ } if origData[bufEnd] == copen { openDelim-- } bufEnd-- } if openDelim == 0 { linkEnd-- } } var uLink bytes.Buffer unescapeText(&uLink, data[:linkEnd]) if uLink.Len() > 0 { node := NewNode(Link) node.Destination = uLink.Bytes() node.AppendChild(text(uLink.Bytes())) return linkEnd, node } return linkEnd, nil } func isEndOfLink(char byte) bool { return isspace(char) || char == '<' } var validUris = [][]byte{[]byte("http://"), []byte("https://"), []byte("ftp://"), []byte("mailto://")} var validPaths = [][]byte{[]byte("/"), []byte("./"), []byte("../")} func isSafeLink(link []byte) bool { for _, path := range validPaths { if len(link) >= len(path) && bytes.Equal(link[:len(path)], path) { if len(link) == len(path) { return true } else if isalnum(link[len(path)]) { return true } } } for _, prefix := range validUris { // TODO: handle unicode here // case-insensitive prefix test if len(link) > len(prefix) && bytes.Equal(bytes.ToLower(link[:len(prefix)]), prefix) && isalnum(link[len(prefix)]) { return true } } return false } // return the length of the given tag, or 0 is it's not valid func tagLength(data []byte) (autolink autolinkType, end int) { var i, j int // a valid tag can't be shorter than 3 chars if len(data) < 3 { return notAutolink, 0 } // begins with a '<' optionally followed by '/', followed by letter or number if data[0] != '<' { return notAutolink, 0 } if data[1] == '/' { i = 2 } else { i = 1 } if !isalnum(data[i]) { return notAutolink, 0 } // scheme test autolink = notAutolink // try to find the beginning of an URI for i < len(data) && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-') { i++ } if i > 1 && i < len(data) && data[i] == '@' { if j = isMailtoAutoLink(data[i:]); j != 0 { return emailAutolink, i + j } } if i > 2 && i < len(data) && data[i] == ':' { autolink = normalAutolink i++ } // complete autolink test: no whitespace or ' or " switch { case i >= len(data): autolink = notAutolink case autolink != notAutolink: j = i for i < len(data) { if data[i] == '\\' { i += 2 } else if data[i] == '>' || data[i] == '\'' || data[i] == '"' || isspace(data[i]) { break } else { i++ } } if i >= len(data) { return autolink, 0 } if i > j && data[i] == '>' { return autolink, i + 1 } // one of the forbidden chars has been found autolink = notAutolink } i += bytes.IndexByte(data[i:], '>') if i < 0 { return autolink, 0 } return autolink, i + 1 } // look for the address part of a mail autolink and '>' // this is less strict than the original markdown e-mail address matching func isMailtoAutoLink(data []byte) int { nb := 0 // address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' for i := 0; i < len(data); i++ { if isalnum(data[i]) { continue } switch data[i] { case '@': nb++ case '-', '.', '_': break case '>': if nb == 1 { return i + 1 } return 0 default: return 0 } } return 0 } // look for the next emph char, skipping other constructs func helperFindEmphChar(data []byte, c byte) int { i := 0 for i < len(data) { for i < len(data) && data[i] != c && data[i] != '`' && data[i] != '[' { i++ } if i >= len(data) { return 0 } // do not count escaped chars if i != 0 && data[i-1] == '\\' { i++ continue } if data[i] == c { return i } if data[i] == '`' { // skip a code span tmpI := 0 i++ for i < len(data) && data[i] != '`' { if tmpI == 0 && data[i] == c { tmpI = i } i++ } if i >= len(data) { return tmpI } i++ } else if data[i] == '[' { // skip a link tmpI := 0 i++ for i < len(data) && data[i] != ']' { if tmpI == 0 && data[i] == c { tmpI = i } i++ } i++ for i < len(data) && (data[i] == ' ' || data[i] == '\n') { i++ } if i >= len(data) { return tmpI } if data[i] != '[' && data[i] != '(' { // not a link if tmpI > 0 { return tmpI } continue } cc := data[i] i++ for i < len(data) && data[i] != cc { if tmpI == 0 && data[i] == c { return i } i++ } if i >= len(data) { return tmpI } i++ } } return 0 } func helperEmphasis(p *Markdown, data []byte, c byte) (int, *Node) { i := 0 // skip one symbol if coming from emph3 if len(data) > 1 && data[0] == c && data[1] == c { i = 1 } for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length if i >= len(data) { return 0, nil } if i+1 < len(data) && data[i+1] == c { i++ continue } if data[i] == c && !isspace(data[i-1]) { if p.extensions&NoIntraEmphasis != 0 { if !(i+1 == len(data) || isspace(data[i+1]) || ispunct(data[i+1])) { continue } } emph := NewNode(Emph) p.inline(emph, data[:i]) return i + 1, emph } } return 0, nil } func helperDoubleEmphasis(p *Markdown, data []byte, c byte) (int, *Node) { i := 0 for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !isspace(data[i-1]) { nodeType := Strong if c == '~' { nodeType = Del } node := NewNode(nodeType) p.inline(node, data[:i]) return i + 2, node } i++ } return 0, nil } func helperTripleEmphasis(p *Markdown, data []byte, offset int, c byte) (int, *Node) { i := 0 origData := data data = data[offset:] for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length // skip whitespace preceded symbols if data[i] != c || isspace(data[i-1]) { continue } switch { case i+2 < len(data) && data[i+1] == c && data[i+2] == c: // triple symbol found strong := NewNode(Strong) em := NewNode(Emph) strong.AppendChild(em) p.inline(em, data[:i]) return i + 3, strong case (i+1 < len(data) && data[i+1] == c): // double symbol found, hand over to emph1 length, node := helperEmphasis(p, origData[offset-2:], c) if length == 0 { return 0, nil } return length - 2, node default: // single symbol found, hand over to emph2 length, node := helperDoubleEmphasis(p, origData[offset-1:], c) if length == 0 { return 0, nil } return length - 1, node } } return 0, nil } func text(s []byte) *Node { node := NewNode(Text) node.Literal = s return node } func normalizeURI(s []byte) []byte { return s // TODO: implement } png2svg-1.7.0/vendor/github.com/russross/blackfriday/v2/markdown.go000066400000000000000000000633341504213346300253310ustar00rootroot00000000000000// Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. package blackfriday import ( "bytes" "fmt" "io" "strings" "unicode/utf8" ) // // Markdown parsing and processing // // Version string of the package. Appears in the rendered document when // CompletePage flag is on. const Version = "2.0" // Extensions is a bitwise or'ed collection of enabled Blackfriday's // extensions. type Extensions int // These are the supported markdown parsing extensions. // OR these values together to select multiple extensions. const ( NoExtensions Extensions = 0 NoIntraEmphasis Extensions = 1 << iota // Ignore emphasis markers inside words Tables // Render tables FencedCode // Render fenced code blocks Autolink // Detect embedded URLs that are not explicitly marked Strikethrough // Strikethrough text using ~~test~~ LaxHTMLBlocks // Loosen up HTML block parsing rules SpaceHeadings // Be strict about prefix heading rules HardLineBreak // Translate newlines into line breaks TabSizeEight // Expand tabs to eight spaces instead of four Footnotes // Pandoc-style footnotes NoEmptyLineBeforeBlock // No need to insert an empty line to start a (code, quote, ordered list, unordered list) block HeadingIDs // specify heading IDs with {#id} Titleblock // Titleblock ala pandoc AutoHeadingIDs // Create the heading ID from the text BackslashLineBreak // Translate trailing backslashes into line breaks DefinitionLists // Render definition lists CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants | SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | BackslashLineBreak | DefinitionLists ) // ListType contains bitwise or'ed flags for list and list item objects. type ListType int // These are the possible flag values for the ListItem renderer. // Multiple flag values may be ORed together. // These are mostly of interest if you are writing a new output format. const ( ListTypeOrdered ListType = 1 << iota ListTypeDefinition ListTypeTerm ListItemContainsBlock ListItemBeginningOfList // TODO: figure out if this is of any use now ListItemEndOfList ) // CellAlignFlags holds a type of alignment in a table cell. type CellAlignFlags int // These are the possible flag values for the table cell renderer. // Only a single one of these values will be used; they are not ORed together. // These are mostly of interest if you are writing a new output format. const ( TableAlignmentLeft CellAlignFlags = 1 << iota TableAlignmentRight TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight) ) // The size of a tab stop. const ( TabSizeDefault = 4 TabSizeDouble = 8 ) // blockTags is a set of tags that are recognized as HTML block tags. // Any of these can be included in markdown text without special escaping. var blockTags = map[string]struct{}{ "blockquote": {}, "del": {}, "div": {}, "dl": {}, "fieldset": {}, "form": {}, "h1": {}, "h2": {}, "h3": {}, "h4": {}, "h5": {}, "h6": {}, "iframe": {}, "ins": {}, "math": {}, "noscript": {}, "ol": {}, "pre": {}, "p": {}, "script": {}, "style": {}, "table": {}, "ul": {}, // HTML5 "address": {}, "article": {}, "aside": {}, "canvas": {}, "figcaption": {}, "figure": {}, "footer": {}, "header": {}, "hgroup": {}, "main": {}, "nav": {}, "output": {}, "progress": {}, "section": {}, "video": {}, } // Renderer is the rendering interface. This is mostly of interest if you are // implementing a new rendering format. // // Only an HTML implementation is provided in this repository, see the README // for external implementations. type Renderer interface { // RenderNode is the main rendering method. It will be called once for // every leaf node and twice for every non-leaf node (first with // entering=true, then with entering=false). The method should write its // rendition of the node to the supplied writer w. RenderNode(w io.Writer, node *Node, entering bool) WalkStatus // RenderHeader is a method that allows the renderer to produce some // content preceding the main body of the output document. The header is // understood in the broad sense here. For example, the default HTML // renderer will write not only the HTML document preamble, but also the // table of contents if it was requested. // // The method will be passed an entire document tree, in case a particular // implementation needs to inspect it to produce output. // // The output should be written to the supplied writer w. If your // implementation has no header to write, supply an empty implementation. RenderHeader(w io.Writer, ast *Node) // RenderFooter is a symmetric counterpart of RenderHeader. RenderFooter(w io.Writer, ast *Node) } // Callback functions for inline parsing. One such function is defined // for each character that triggers a response when parsing inline data. type inlineParser func(p *Markdown, data []byte, offset int) (int, *Node) // Markdown is a type that holds extensions and the runtime state used by // Parse, and the renderer. You can not use it directly, construct it with New. type Markdown struct { renderer Renderer referenceOverride ReferenceOverrideFunc refs map[string]*reference inlineCallback [256]inlineParser extensions Extensions nesting int maxNesting int insideLink bool // Footnotes need to be ordered as well as available to quickly check for // presence. If a ref is also a footnote, it's stored both in refs and here // in notes. Slice is nil if footnotes not enabled. notes []*reference doc *Node tip *Node // = doc oldTip *Node lastMatchedContainer *Node // = doc allClosed bool } func (p *Markdown) getRef(refid string) (ref *reference, found bool) { if p.referenceOverride != nil { r, overridden := p.referenceOverride(refid) if overridden { if r == nil { return nil, false } return &reference{ link: []byte(r.Link), title: []byte(r.Title), noteID: 0, hasBlock: false, text: []byte(r.Text)}, true } } // refs are case insensitive ref, found = p.refs[strings.ToLower(refid)] return ref, found } func (p *Markdown) finalize(block *Node) { above := block.Parent block.open = false p.tip = above } func (p *Markdown) addChild(node NodeType, offset uint32) *Node { return p.addExistingChild(NewNode(node), offset) } func (p *Markdown) addExistingChild(node *Node, offset uint32) *Node { for !p.tip.canContain(node.Type) { p.finalize(p.tip) } p.tip.AppendChild(node) p.tip = node return node } func (p *Markdown) closeUnmatchedBlocks() { if !p.allClosed { for p.oldTip != p.lastMatchedContainer { parent := p.oldTip.Parent p.finalize(p.oldTip) p.oldTip = parent } p.allClosed = true } } // // // Public interface // // // Reference represents the details of a link. // See the documentation in Options for more details on use-case. type Reference struct { // Link is usually the URL the reference points to. Link string // Title is the alternate text describing the link in more detail. Title string // Text is the optional text to override the ref with if the syntax used was // [refid][] Text string } // ReferenceOverrideFunc is expected to be called with a reference string and // return either a valid Reference type that the reference string maps to or // nil. If overridden is false, the default reference logic will be executed. // See the documentation in Options for more details on use-case. type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool) // New constructs a Markdown processor. You can use the same With* functions as // for Run() to customize parser's behavior and the renderer. func New(opts ...Option) *Markdown { var p Markdown for _, opt := range opts { opt(&p) } p.refs = make(map[string]*reference) p.maxNesting = 16 p.insideLink = false docNode := NewNode(Document) p.doc = docNode p.tip = docNode p.oldTip = docNode p.lastMatchedContainer = docNode p.allClosed = true // register inline parsers p.inlineCallback[' '] = maybeLineBreak p.inlineCallback['*'] = emphasis p.inlineCallback['_'] = emphasis if p.extensions&Strikethrough != 0 { p.inlineCallback['~'] = emphasis } p.inlineCallback['`'] = codeSpan p.inlineCallback['\n'] = lineBreak p.inlineCallback['['] = link p.inlineCallback['<'] = leftAngle p.inlineCallback['\\'] = escape p.inlineCallback['&'] = entity p.inlineCallback['!'] = maybeImage p.inlineCallback['^'] = maybeInlineFootnote if p.extensions&Autolink != 0 { p.inlineCallback['h'] = maybeAutoLink p.inlineCallback['m'] = maybeAutoLink p.inlineCallback['f'] = maybeAutoLink p.inlineCallback['H'] = maybeAutoLink p.inlineCallback['M'] = maybeAutoLink p.inlineCallback['F'] = maybeAutoLink } if p.extensions&Footnotes != 0 { p.notes = make([]*reference, 0) } return &p } // Option customizes the Markdown processor's default behavior. type Option func(*Markdown) // WithRenderer allows you to override the default renderer. func WithRenderer(r Renderer) Option { return func(p *Markdown) { p.renderer = r } } // WithExtensions allows you to pick some of the many extensions provided by // Blackfriday. You can bitwise OR them. func WithExtensions(e Extensions) Option { return func(p *Markdown) { p.extensions = e } } // WithNoExtensions turns off all extensions and custom behavior. func WithNoExtensions() Option { return func(p *Markdown) { p.extensions = NoExtensions p.renderer = NewHTMLRenderer(HTMLRendererParameters{ Flags: HTMLFlagsNone, }) } } // WithRefOverride sets an optional function callback that is called every // time a reference is resolved. // // In Markdown, the link reference syntax can be made to resolve a link to // a reference instead of an inline URL, in one of the following ways: // // * [link text][refid] // * [refid][] // // Usually, the refid is defined at the bottom of the Markdown document. If // this override function is provided, the refid is passed to the override // function first, before consulting the defined refids at the bottom. If // the override function indicates an override did not occur, the refids at // the bottom will be used to fill in the link details. func WithRefOverride(o ReferenceOverrideFunc) Option { return func(p *Markdown) { p.referenceOverride = o } } // Run is the main entry point to Blackfriday. It parses and renders a // block of markdown-encoded text. // // The simplest invocation of Run takes one argument, input: // output := Run(input) // This will parse the input with CommonExtensions enabled and render it with // the default HTMLRenderer (with CommonHTMLFlags). // // Variadic arguments opts can customize the default behavior. Since Markdown // type does not contain exported fields, you can not use it directly. Instead, // use the With* functions. For example, this will call the most basic // functionality, with no extensions: // output := Run(input, WithNoExtensions()) // // You can use any number of With* arguments, even contradicting ones. They // will be applied in order of appearance and the latter will override the // former: // output := Run(input, WithNoExtensions(), WithExtensions(exts), // WithRenderer(yourRenderer)) func Run(input []byte, opts ...Option) []byte { r := NewHTMLRenderer(HTMLRendererParameters{ Flags: CommonHTMLFlags, }) optList := []Option{WithRenderer(r), WithExtensions(CommonExtensions)} optList = append(optList, opts...) parser := New(optList...) ast := parser.Parse(input) var buf bytes.Buffer parser.renderer.RenderHeader(&buf, ast) ast.Walk(func(node *Node, entering bool) WalkStatus { return parser.renderer.RenderNode(&buf, node, entering) }) parser.renderer.RenderFooter(&buf, ast) return buf.Bytes() } // Parse is an entry point to the parsing part of Blackfriday. It takes an // input markdown document and produces a syntax tree for its contents. This // tree can then be rendered with a default or custom renderer, or // analyzed/transformed by the caller to whatever non-standard needs they have. // The return value is the root node of the syntax tree. func (p *Markdown) Parse(input []byte) *Node { p.block(input) // Walk the tree and finish up some of unfinished blocks for p.tip != nil { p.finalize(p.tip) } // Walk the tree again and process inline markdown in each block p.doc.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Paragraph || node.Type == Heading || node.Type == TableCell { p.inline(node, node.content) node.content = nil } return GoToNext }) p.parseRefsToAST() return p.doc } func (p *Markdown) parseRefsToAST() { if p.extensions&Footnotes == 0 || len(p.notes) == 0 { return } p.tip = p.doc block := p.addBlock(List, nil) block.IsFootnotesList = true block.ListFlags = ListTypeOrdered flags := ListItemBeginningOfList // Note: this loop is intentionally explicit, not range-form. This is // because the body of the loop will append nested footnotes to p.notes and // we need to process those late additions. Range form would only walk over // the fixed initial set. for i := 0; i < len(p.notes); i++ { ref := p.notes[i] p.addExistingChild(ref.footnote, 0) block := ref.footnote block.ListFlags = flags | ListTypeOrdered block.RefLink = ref.link if ref.hasBlock { flags |= ListItemContainsBlock p.block(ref.title) } else { p.inline(block, ref.title) } flags &^= ListItemBeginningOfList | ListItemContainsBlock } above := block.Parent finalizeList(block) p.tip = above block.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Paragraph || node.Type == Heading { p.inline(node, node.content) node.content = nil } return GoToNext }) } // // Link references // // This section implements support for references that (usually) appear // as footnotes in a document, and can be referenced anywhere in the document. // The basic format is: // // [1]: http://www.google.com/ "Google" // [2]: http://www.github.com/ "Github" // // Anywhere in the document, the reference can be linked by referring to its // label, i.e., 1 and 2 in this example, as in: // // This library is hosted on [Github][2], a git hosting site. // // Actual footnotes as specified in Pandoc and supported by some other Markdown // libraries such as php-markdown are also taken care of. They look like this: // // This sentence needs a bit of further explanation.[^note] // // [^note]: This is the explanation. // // Footnotes should be placed at the end of the document in an ordered list. // Finally, there are inline footnotes such as: // // Inline footnotes^[Also supported.] provide a quick inline explanation, // but are rendered at the bottom of the document. // // reference holds all information necessary for a reference-style links or // footnotes. // // Consider this markdown with reference-style links: // // [link][ref] // // [ref]: /url/ "tooltip title" // // It will be ultimately converted to this HTML: // //

    link

    // // And a reference structure will be populated as follows: // // p.refs["ref"] = &reference{ // link: "/url/", // title: "tooltip title", // } // // Alternatively, reference can contain information about a footnote. Consider // this markdown: // // Text needing a footnote.[^a] // // [^a]: This is the note // // A reference structure will be populated as follows: // // p.refs["a"] = &reference{ // link: "a", // title: "This is the note", // noteID: , // } // // TODO: As you can see, it begs for splitting into two dedicated structures // for refs and for footnotes. type reference struct { link []byte title []byte noteID int // 0 if not a footnote ref hasBlock bool footnote *Node // a link to the Item node within a list of footnotes text []byte // only gets populated by refOverride feature with Reference.Text } func (r *reference) String() string { return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}", r.link, r.title, r.text, r.noteID, r.hasBlock) } // Check whether or not data starts with a reference link. // If so, it is parsed and stored in the list of references // (in the render struct). // Returns the number of bytes to skip to move past it, // or zero if the first line is not a reference. func isReference(p *Markdown, data []byte, tabSize int) int { // up to 3 optional leading spaces if len(data) < 4 { return 0 } i := 0 for i < 3 && data[i] == ' ' { i++ } noteID := 0 // id part: anything but a newline between brackets if data[i] != '[' { return 0 } i++ if p.extensions&Footnotes != 0 { if i < len(data) && data[i] == '^' { // we can set it to anything here because the proper noteIds will // be assigned later during the second pass. It just has to be != 0 noteID = 1 i++ } } idOffset := i for i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != ']' { i++ } if i >= len(data) || data[i] != ']' { return 0 } idEnd := i // footnotes can have empty ID, like this: [^], but a reference can not be // empty like this: []. Break early if it's not a footnote and there's no ID if noteID == 0 && idOffset == idEnd { return 0 } // spacer: colon (space | tab)* newline? (space | tab)* i++ if i >= len(data) || data[i] != ':' { return 0 } i++ for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i < len(data) && (data[i] == '\n' || data[i] == '\r') { i++ if i < len(data) && data[i] == '\n' && data[i-1] == '\r' { i++ } } for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i >= len(data) { return 0 } var ( linkOffset, linkEnd int titleOffset, titleEnd int lineEnd int raw []byte hasBlock bool ) if p.extensions&Footnotes != 0 && noteID != 0 { linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize) lineEnd = linkEnd } else { linkOffset, linkEnd, titleOffset, titleEnd, lineEnd = scanLinkRef(p, data, i) } if lineEnd == 0 { return 0 } // a valid ref has been found ref := &reference{ noteID: noteID, hasBlock: hasBlock, } if noteID > 0 { // reusing the link field for the id since footnotes don't have links ref.link = data[idOffset:idEnd] // if footnote, it's not really a title, it's the contained text ref.title = raw } else { ref.link = data[linkOffset:linkEnd] ref.title = data[titleOffset:titleEnd] } // id matches are case-insensitive id := string(bytes.ToLower(data[idOffset:idEnd])) p.refs[id] = ref return lineEnd } func scanLinkRef(p *Markdown, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) { // link: whitespace-free sequence, optionally between angle brackets if data[i] == '<' { i++ } linkOffset = i for i < len(data) && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' { i++ } linkEnd = i if data[linkOffset] == '<' && data[linkEnd-1] == '>' { linkOffset++ linkEnd-- } // optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(' { return } // compute end-of-line if i >= len(data) || data[i] == '\r' || data[i] == '\n' { lineEnd = i } if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' { lineEnd++ } // optional (space|tab)* spacer after a newline if lineEnd > 0 { i = lineEnd + 1 for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } } // optional title: any non-newline sequence enclosed in '"() alone on its line if i+1 < len(data) && (data[i] == '\'' || data[i] == '"' || data[i] == '(') { i++ titleOffset = i // look for EOL for i < len(data) && data[i] != '\n' && data[i] != '\r' { i++ } if i+1 < len(data) && data[i] == '\n' && data[i+1] == '\r' { titleEnd = i + 1 } else { titleEnd = i } // step back i-- for i > titleOffset && (data[i] == ' ' || data[i] == '\t') { i-- } if i > titleOffset && (data[i] == '\'' || data[i] == '"' || data[i] == ')') { lineEnd = titleEnd titleEnd = i } } return } // The first bit of this logic is the same as Parser.listItem, but the rest // is much simpler. This function simply finds the entire block and shifts it // over by one tab if it is indeed a block (just returns the line if it's not). // blockEnd is the end of the section in the input buffer, and contents is the // extracted text that was shifted over one tab. It will need to be rendered at // the end of the document. func scanFootnote(p *Markdown, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) { if i == 0 || len(data) == 0 { return } // skip leading whitespace on first line for i < len(data) && data[i] == ' ' { i++ } blockStart = i // find the end of the line blockEnd = i for i < len(data) && data[i-1] != '\n' { i++ } // get working buffer var raw bytes.Buffer // put the first line into the working buffer raw.Write(data[blockEnd:i]) blockEnd = i // process the following lines containsBlankLine := false gatherLines: for blockEnd < len(data) { i++ // find the end of this line for i < len(data) && data[i-1] != '\n' { i++ } // if it is an empty line, guess that it is part of this item // and move on to the next line if p.isEmpty(data[blockEnd:i]) > 0 { containsBlankLine = true blockEnd = i continue } n := 0 if n = isIndented(data[blockEnd:i], indentSize); n == 0 { // this is the end of the block. // we don't want to include this last line in the index. break gatherLines } // if there were blank lines before this one, insert a new one now if containsBlankLine { raw.WriteByte('\n') containsBlankLine = false } // get rid of that first tab, write to buffer raw.Write(data[blockEnd+n : i]) hasBlock = true blockEnd = i } if data[blockEnd-1] != '\n' { raw.WriteByte('\n') } contents = raw.Bytes() return } // // // Miscellaneous helper functions // // // Test if a character is a punctuation symbol. // Taken from a private function in regexp in the stdlib. func ispunct(c byte) bool { for _, r := range []byte("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") { if c == r { return true } } return false } // Test if a character is a whitespace character. func isspace(c byte) bool { return ishorizontalspace(c) || isverticalspace(c) } // Test if a character is a horizontal whitespace character. func ishorizontalspace(c byte) bool { return c == ' ' || c == '\t' } // Test if a character is a vertical character. func isverticalspace(c byte) bool { return c == '\n' || c == '\r' || c == '\f' || c == '\v' } // Test if a character is letter. func isletter(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } // Test if a character is a letter or a digit. // TODO: check when this is looking for ASCII alnum and when it should use unicode func isalnum(c byte) bool { return (c >= '0' && c <= '9') || isletter(c) } // Replace tab characters with spaces, aligning to the next TAB_SIZE column. // always ends output with a newline func expandTabs(out *bytes.Buffer, line []byte, tabSize int) { // first, check for common cases: no tabs, or only tabs at beginning of line i, prefix := 0, 0 slowcase := false for i = 0; i < len(line); i++ { if line[i] == '\t' { if prefix == i { prefix++ } else { slowcase = true break } } } // no need to decode runes if all tabs are at the beginning of the line if !slowcase { for i = 0; i < prefix*tabSize; i++ { out.WriteByte(' ') } out.Write(line[prefix:]) return } // the slow case: we need to count runes to figure out how // many spaces to insert for each tab column := 0 i = 0 for i < len(line) { start := i for i < len(line) && line[i] != '\t' { _, size := utf8.DecodeRune(line[i:]) i += size column++ } if i > start { out.Write(line[start:i]) } if i >= len(line) { break } for { out.WriteByte(' ') column++ if column%tabSize == 0 { break } } i++ } } // Find if a line counts as indented or not. // Returns number of characters the indent is (0 = not indented). func isIndented(data []byte, indentSize int) int { if len(data) == 0 { return 0 } if data[0] == '\t' { return 1 } if len(data) < indentSize { return 0 } for i := 0; i < indentSize; i++ { if data[i] != ' ' { return 0 } } return indentSize } // Create a url-safe slug for fragments func slugify(in []byte) []byte { if len(in) == 0 { return in } out := make([]byte, 0, len(in)) sym := false for _, ch := range in { if isalnum(ch) { sym = false out = append(out, ch) } else if sym { continue } else { out = append(out, '-') sym = true } } var a, b int var ch byte for a, ch = range out { if ch != '-' { break } } for b = len(out) - 1; b > 0; b-- { if out[b] != '-' { break } } return out[a : b+1] } png2svg-1.7.0/vendor/github.com/russross/blackfriday/v2/node.go000066400000000000000000000212021504213346300244200ustar00rootroot00000000000000package blackfriday import ( "bytes" "fmt" ) // NodeType specifies a type of a single node of a syntax tree. Usually one // node (and its type) corresponds to a single markdown feature, e.g. emphasis // or code block. type NodeType int // Constants for identifying different types of nodes. See NodeType. const ( Document NodeType = iota BlockQuote List Item Paragraph Heading HorizontalRule Emph Strong Del Link Image Text HTMLBlock CodeBlock Softbreak Hardbreak Code HTMLSpan Table TableCell TableHead TableBody TableRow ) var nodeTypeNames = []string{ Document: "Document", BlockQuote: "BlockQuote", List: "List", Item: "Item", Paragraph: "Paragraph", Heading: "Heading", HorizontalRule: "HorizontalRule", Emph: "Emph", Strong: "Strong", Del: "Del", Link: "Link", Image: "Image", Text: "Text", HTMLBlock: "HTMLBlock", CodeBlock: "CodeBlock", Softbreak: "Softbreak", Hardbreak: "Hardbreak", Code: "Code", HTMLSpan: "HTMLSpan", Table: "Table", TableCell: "TableCell", TableHead: "TableHead", TableBody: "TableBody", TableRow: "TableRow", } func (t NodeType) String() string { return nodeTypeNames[t] } // ListData contains fields relevant to a List and Item node type. type ListData struct { ListFlags ListType Tight bool // Skip

    s around list item data if true BulletChar byte // '*', '+' or '-' in bullet lists Delimiter byte // '.' or ')' after the number in ordered lists RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering IsFootnotesList bool // This is a list of footnotes } // LinkData contains fields relevant to a Link node type. type LinkData struct { Destination []byte // Destination is what goes into a href Title []byte // Title is the tooltip thing that goes in a title attribute NoteID int // NoteID contains a serial number of a footnote, zero if it's not a footnote Footnote *Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil. } // CodeBlockData contains fields relevant to a CodeBlock node type. type CodeBlockData struct { IsFenced bool // Specifies whether it's a fenced code block or an indented one Info []byte // This holds the info string FenceChar byte FenceLength int FenceOffset int } // TableCellData contains fields relevant to a TableCell node type. type TableCellData struct { IsHeader bool // This tells if it's under the header row Align CellAlignFlags // This holds the value for align attribute } // HeadingData contains fields relevant to a Heading node type. type HeadingData struct { Level int // This holds the heading level number HeadingID string // This might hold heading ID, if present IsTitleblock bool // Specifies whether it's a title block } // Node is a single element in the abstract syntax tree of the parsed document. // It holds connections to the structurally neighboring nodes and, for certain // types of nodes, additional information that might be needed when rendering. type Node struct { Type NodeType // Determines the type of the node Parent *Node // Points to the parent FirstChild *Node // Points to the first child, if any LastChild *Node // Points to the last child, if any Prev *Node // Previous sibling; nil if it's the first child Next *Node // Next sibling; nil if it's the last child Literal []byte // Text contents of the leaf nodes HeadingData // Populated if Type is Heading ListData // Populated if Type is List CodeBlockData // Populated if Type is CodeBlock LinkData // Populated if Type is Link TableCellData // Populated if Type is TableCell content []byte // Markdown content of the block nodes open bool // Specifies an open block node that has not been finished to process yet } // NewNode allocates a node of a specified type. func NewNode(typ NodeType) *Node { return &Node{ Type: typ, open: true, } } func (n *Node) String() string { ellipsis := "" snippet := n.Literal if len(snippet) > 16 { snippet = snippet[:16] ellipsis = "..." } return fmt.Sprintf("%s: '%s%s'", n.Type, snippet, ellipsis) } // Unlink removes node 'n' from the tree. // It panics if the node is nil. func (n *Node) Unlink() { if n.Prev != nil { n.Prev.Next = n.Next } else if n.Parent != nil { n.Parent.FirstChild = n.Next } if n.Next != nil { n.Next.Prev = n.Prev } else if n.Parent != nil { n.Parent.LastChild = n.Prev } n.Parent = nil n.Next = nil n.Prev = nil } // AppendChild adds a node 'child' as a child of 'n'. // It panics if either node is nil. func (n *Node) AppendChild(child *Node) { child.Unlink() child.Parent = n if n.LastChild != nil { n.LastChild.Next = child child.Prev = n.LastChild n.LastChild = child } else { n.FirstChild = child n.LastChild = child } } // InsertBefore inserts 'sibling' immediately before 'n'. // It panics if either node is nil. func (n *Node) InsertBefore(sibling *Node) { sibling.Unlink() sibling.Prev = n.Prev if sibling.Prev != nil { sibling.Prev.Next = sibling } sibling.Next = n n.Prev = sibling sibling.Parent = n.Parent if sibling.Prev == nil { sibling.Parent.FirstChild = sibling } } // IsContainer returns true if 'n' can contain children. func (n *Node) IsContainer() bool { switch n.Type { case Document: fallthrough case BlockQuote: fallthrough case List: fallthrough case Item: fallthrough case Paragraph: fallthrough case Heading: fallthrough case Emph: fallthrough case Strong: fallthrough case Del: fallthrough case Link: fallthrough case Image: fallthrough case Table: fallthrough case TableHead: fallthrough case TableBody: fallthrough case TableRow: fallthrough case TableCell: return true default: return false } } // IsLeaf returns true if 'n' is a leaf node. func (n *Node) IsLeaf() bool { return !n.IsContainer() } func (n *Node) canContain(t NodeType) bool { if n.Type == List { return t == Item } if n.Type == Document || n.Type == BlockQuote || n.Type == Item { return t != Item } if n.Type == Table { return t == TableHead || t == TableBody } if n.Type == TableHead || n.Type == TableBody { return t == TableRow } if n.Type == TableRow { return t == TableCell } return false } // WalkStatus allows NodeVisitor to have some control over the tree traversal. // It is returned from NodeVisitor and different values allow Node.Walk to // decide which node to go to next. type WalkStatus int const ( // GoToNext is the default traversal of every node. GoToNext WalkStatus = iota // SkipChildren tells walker to skip all children of current node. SkipChildren // Terminate tells walker to terminate the traversal. Terminate ) // NodeVisitor is a callback to be called when traversing the syntax tree. // Called twice for every node: once with entering=true when the branch is // first visited, then with entering=false after all the children are done. type NodeVisitor func(node *Node, entering bool) WalkStatus // Walk is a convenience method that instantiates a walker and starts a // traversal of subtree rooted at n. func (n *Node) Walk(visitor NodeVisitor) { w := newNodeWalker(n) for w.current != nil { status := visitor(w.current, w.entering) switch status { case GoToNext: w.next() case SkipChildren: w.entering = false w.next() case Terminate: return } } } type nodeWalker struct { current *Node root *Node entering bool } func newNodeWalker(root *Node) *nodeWalker { return &nodeWalker{ current: root, root: root, entering: true, } } func (nw *nodeWalker) next() { if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root { nw.current = nil return } if nw.entering && nw.current.IsContainer() { if nw.current.FirstChild != nil { nw.current = nw.current.FirstChild nw.entering = true } else { nw.entering = false } } else if nw.current.Next == nil { nw.current = nw.current.Parent nw.entering = false } else { nw.current = nw.current.Next nw.entering = true } } func dump(ast *Node) { fmt.Println(dumpString(ast)) } func dumpR(ast *Node, depth int) string { if ast == nil { return "" } indent := bytes.Repeat([]byte("\t"), depth) content := ast.Literal if content == nil { content = ast.content } result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content) for n := ast.FirstChild; n != nil; n = n.Next { result += dumpR(n, depth+1) } return result } func dumpString(ast *Node) string { return dumpR(ast, 0) } png2svg-1.7.0/vendor/github.com/russross/blackfriday/v2/smartypants.go000066400000000000000000000264341504213346300260740ustar00rootroot00000000000000// // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // // SmartyPants rendering // // package blackfriday import ( "bytes" "io" ) // SPRenderer is a struct containing state of a Smartypants renderer. type SPRenderer struct { inSingleQuote bool inDoubleQuote bool callbacks [256]smartCallback } func wordBoundary(c byte) bool { return c == 0 || isspace(c) || ispunct(c) } func tolower(c byte) byte { if c >= 'A' && c <= 'Z' { return c - 'A' + 'a' } return c } func isdigit(c byte) bool { return c >= '0' && c <= '9' } func smartQuoteHelper(out *bytes.Buffer, previousChar byte, nextChar byte, quote byte, isOpen *bool, addNBSP bool) bool { // edge of the buffer is likely to be a tag that we don't get to see, // so we treat it like text sometimes // enumerate all sixteen possibilities for (previousChar, nextChar) // each can be one of {0, space, punct, other} switch { case previousChar == 0 && nextChar == 0: // context is not any help here, so toggle *isOpen = !*isOpen case isspace(previousChar) && nextChar == 0: // [ "] might be [ "foo...] *isOpen = true case ispunct(previousChar) && nextChar == 0: // [!"] hmm... could be [Run!"] or [("...] *isOpen = false case /* isnormal(previousChar) && */ nextChar == 0: // [a"] is probably a close *isOpen = false case previousChar == 0 && isspace(nextChar): // [" ] might be [...foo" ] *isOpen = false case isspace(previousChar) && isspace(nextChar): // [ " ] context is not any help here, so toggle *isOpen = !*isOpen case ispunct(previousChar) && isspace(nextChar): // [!" ] is probably a close *isOpen = false case /* isnormal(previousChar) && */ isspace(nextChar): // [a" ] this is one of the easy cases *isOpen = false case previousChar == 0 && ispunct(nextChar): // ["!] hmm... could be ["$1.95] or ["!...] *isOpen = false case isspace(previousChar) && ispunct(nextChar): // [ "!] looks more like [ "$1.95] *isOpen = true case ispunct(previousChar) && ispunct(nextChar): // [!"!] context is not any help here, so toggle *isOpen = !*isOpen case /* isnormal(previousChar) && */ ispunct(nextChar): // [a"!] is probably a close *isOpen = false case previousChar == 0 /* && isnormal(nextChar) */ : // ["a] is probably an open *isOpen = true case isspace(previousChar) /* && isnormal(nextChar) */ : // [ "a] this is one of the easy cases *isOpen = true case ispunct(previousChar) /* && isnormal(nextChar) */ : // [!"a] is probably an open *isOpen = true default: // [a'b] maybe a contraction? *isOpen = false } // Note that with the limited lookahead, this non-breaking // space will also be appended to single double quotes. if addNBSP && !*isOpen { out.WriteString(" ") } out.WriteByte('&') if *isOpen { out.WriteByte('l') } else { out.WriteByte('r') } out.WriteByte(quote) out.WriteString("quo;") if addNBSP && *isOpen { out.WriteString(" ") } return true } func (r *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 { t1 := tolower(text[1]) if t1 == '\'' { nextChar := byte(0) if len(text) >= 3 { nextChar = text[2] } if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) { return 1 } } if (t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && (len(text) < 3 || wordBoundary(text[2])) { out.WriteString("’") return 0 } if len(text) >= 3 { t2 := tolower(text[2]) if ((t1 == 'r' && t2 == 'e') || (t1 == 'l' && t2 == 'l') || (t1 == 'v' && t2 == 'e')) && (len(text) < 4 || wordBoundary(text[3])) { out.WriteString("’") return 0 } } } nextChar := byte(0) if len(text) > 1 { nextChar = text[1] } if smartQuoteHelper(out, previousChar, nextChar, 's', &r.inSingleQuote, false) { return 0 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 { t1 := tolower(text[1]) t2 := tolower(text[2]) if t1 == 'c' && t2 == ')' { out.WriteString("©") return 2 } if t1 == 'r' && t2 == ')' { out.WriteString("®") return 2 } if len(text) >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')' { out.WriteString("™") return 3 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 { if text[1] == '-' { out.WriteString("—") return 1 } if wordBoundary(previousChar) && wordBoundary(text[1]) { out.WriteString("–") return 0 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 && text[1] == '-' && text[2] == '-' { out.WriteString("—") return 2 } if len(text) >= 2 && text[1] == '-' { out.WriteString("–") return 1 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte, addNBSP bool) int { if bytes.HasPrefix(text, []byte(""")) { nextChar := byte(0) if len(text) >= 7 { nextChar = text[6] } if smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, addNBSP) { return 5 } } if bytes.HasPrefix(text, []byte("�")) { return 3 } out.WriteByte('&') return 0 } func (r *SPRenderer) smartAmp(angledQuotes, addNBSP bool) func(*bytes.Buffer, byte, []byte) int { var quote byte = 'd' if angledQuotes { quote = 'a' } return func(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartAmpVariant(out, previousChar, text, quote, addNBSP) } } func (r *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 && text[1] == '.' && text[2] == '.' { out.WriteString("…") return 2 } if len(text) >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.' { out.WriteString("…") return 4 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 && text[1] == '`' { nextChar := byte(0) if len(text) >= 3 { nextChar = text[2] } if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) { return 1 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte, text []byte) int { if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 { // is it of the form digits/digits(word boundary)?, i.e., \d+/\d+\b // note: check for regular slash (/) or fraction slash (⁄, 0x2044, or 0xe2 81 84 in utf-8) // and avoid changing dates like 1/23/2005 into fractions. numEnd := 0 for len(text) > numEnd && isdigit(text[numEnd]) { numEnd++ } if numEnd == 0 { out.WriteByte(text[0]) return 0 } denStart := numEnd + 1 if len(text) > numEnd+3 && text[numEnd] == 0xe2 && text[numEnd+1] == 0x81 && text[numEnd+2] == 0x84 { denStart = numEnd + 3 } else if len(text) < numEnd+2 || text[numEnd] != '/' { out.WriteByte(text[0]) return 0 } denEnd := denStart for len(text) > denEnd && isdigit(text[denEnd]) { denEnd++ } if denEnd == denStart { out.WriteByte(text[0]) return 0 } if len(text) == denEnd || wordBoundary(text[denEnd]) && text[denEnd] != '/' { out.WriteString("") out.Write(text[:numEnd]) out.WriteString("") out.Write(text[denStart:denEnd]) out.WriteString("") return denEnd - 1 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartNumber(out *bytes.Buffer, previousChar byte, text []byte) int { if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 { if text[0] == '1' && text[1] == '/' && text[2] == '2' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' { out.WriteString("½") return 2 } } if text[0] == '1' && text[1] == '/' && text[2] == '4' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h') { out.WriteString("¼") return 2 } } if text[0] == '3' && text[1] == '/' && text[2] == '4' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's') { out.WriteString("¾") return 2 } } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDoubleQuoteVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int { nextChar := byte(0) if len(text) > 1 { nextChar = text[1] } if !smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, false) { out.WriteString(""") } return 0 } func (r *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartDoubleQuoteVariant(out, previousChar, text, 'd') } func (r *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartDoubleQuoteVariant(out, previousChar, text, 'a') } func (r *SPRenderer) smartLeftAngle(out *bytes.Buffer, previousChar byte, text []byte) int { i := 0 for i < len(text) && text[i] != '>' { i++ } out.Write(text[:i+1]) return i } type smartCallback func(out *bytes.Buffer, previousChar byte, text []byte) int // NewSmartypantsRenderer constructs a Smartypants renderer object. func NewSmartypantsRenderer(flags HTMLFlags) *SPRenderer { var ( r SPRenderer smartAmpAngled = r.smartAmp(true, false) smartAmpAngledNBSP = r.smartAmp(true, true) smartAmpRegular = r.smartAmp(false, false) smartAmpRegularNBSP = r.smartAmp(false, true) addNBSP = flags&SmartypantsQuotesNBSP != 0 ) if flags&SmartypantsAngledQuotes == 0 { r.callbacks['"'] = r.smartDoubleQuote if !addNBSP { r.callbacks['&'] = smartAmpRegular } else { r.callbacks['&'] = smartAmpRegularNBSP } } else { r.callbacks['"'] = r.smartAngledDoubleQuote if !addNBSP { r.callbacks['&'] = smartAmpAngled } else { r.callbacks['&'] = smartAmpAngledNBSP } } r.callbacks['\''] = r.smartSingleQuote r.callbacks['('] = r.smartParens if flags&SmartypantsDashes != 0 { if flags&SmartypantsLatexDashes == 0 { r.callbacks['-'] = r.smartDash } else { r.callbacks['-'] = r.smartDashLatex } } r.callbacks['.'] = r.smartPeriod if flags&SmartypantsFractions == 0 { r.callbacks['1'] = r.smartNumber r.callbacks['3'] = r.smartNumber } else { for ch := '1'; ch <= '9'; ch++ { r.callbacks[ch] = r.smartNumberGeneric } } r.callbacks['<'] = r.smartLeftAngle r.callbacks['`'] = r.smartBacktick return &r } // Process is the entry point of the Smartypants renderer. func (r *SPRenderer) Process(w io.Writer, text []byte) { mark := 0 for i := 0; i < len(text); i++ { if action := r.callbacks[text[i]]; action != nil { if i > mark { w.Write(text[mark:i]) } previousChar := byte(0) if i > 0 { previousChar = text[i-1] } var tmp bytes.Buffer i += action(&tmp, previousChar, text[i:]) w.Write(tmp.Bytes()) mark = i + 1 } } if mark < len(text) { w.Write(text[mark:]) } } png2svg-1.7.0/vendor/github.com/urfave/000077500000000000000000000000001504213346300177325ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/urfave/cli/000077500000000000000000000000001504213346300205015ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/urfave/cli/v2/000077500000000000000000000000001504213346300210305ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/urfave/cli/v2/.flake8000066400000000000000000000000371504213346300222030ustar00rootroot00000000000000[flake8] max-line-length = 120 png2svg-1.7.0/vendor/github.com/urfave/cli/v2/.gitignore000066400000000000000000000003201504213346300230130ustar00rootroot00000000000000*.coverprofile *.exe *.orig .*envrc .envrc .idea # goimports is installed here if not available /.local/ /site/ coverage.txt internal/*/built-example vendor /cmd/urfave-cli-genflags/urfave-cli-genflags *.exe png2svg-1.7.0/vendor/github.com/urfave/cli/v2/.golangci.yaml000066400000000000000000000001231504213346300235510ustar00rootroot00000000000000# https://golangci-lint.run/usage/configuration/ linters: enable: - misspell png2svg-1.7.0/vendor/github.com/urfave/cli/v2/CODE_OF_CONDUCT.md000066400000000000000000000063041504213346300236320ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting urfave-governance@googlegroups.com, a members-only group that is world-postable. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org png2svg-1.7.0/vendor/github.com/urfave/cli/v2/README.md000066400000000000000000000016411504213346300223110ustar00rootroot00000000000000# cli [![Run Tests](https://github.com/urfave/cli/actions/workflows/cli.yml/badge.svg?branch=v2-maint)](https://github.com/urfave/cli/actions/workflows/cli.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/urfave/cli/v2.svg)](https://pkg.go.dev/github.com/urfave/cli/v2) [![Go Report Card](https://goreportcard.com/badge/github.com/urfave/cli/v2)](https://goreportcard.com/report/github.com/urfave/cli/v2) [![codecov](https://codecov.io/gh/urfave/cli/branch/v2-maint/graph/badge.svg?token=t9YGWLh05g)](https://app.codecov.io/gh/urfave/cli/tree/v2-maint) cli is a simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. ## Documentation More documentation is available in [`./docs`](./docs) or the hosted documentation site at . ## License See [`LICENSE`](./LICENSE) png2svg-1.7.0/vendor/github.com/urfave/cli/v2/app.go000066400000000000000000000352301504213346300221420ustar00rootroot00000000000000package cli import ( "context" "flag" "fmt" "io" "os" "path/filepath" "sort" "strings" "time" ) const suggestDidYouMeanTemplate = "Did you mean %q?" var ( changeLogURL = "https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md" appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." errInvalidActionType = NewExitError("ERROR invalid Action type. "+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("See %s", appActionDeprecationURL), 2) ignoreFlagPrefix = "test." // this is to ignore test flags when adding flags from other packages SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate ) // App is the main structure of a cli application. It is recommended that // an app be created with the cli.NewApp() function type App struct { // The name of the program. Defaults to path.Base(os.Args[0]) Name string // Full name of command for help, defaults to Name HelpName string // Description of the program. Usage string // Text to override the USAGE section of help UsageText string // Whether this command supports arguments Args bool // Description of the program argument format. ArgsUsage string // Version of the program Version string // Description of the program Description string // DefaultCommand is the (optional) name of a command // to run if no command names are passed as CLI arguments. DefaultCommand string // List of commands to execute Commands []*Command // List of flags to parse Flags []Flag // Boolean to enable bash completion commands EnableBashCompletion bool // Boolean to hide built-in help command and help flag HideHelp bool // Boolean to hide built-in help command but keep help flag. // Ignored if HideHelp is true. HideHelpCommand bool // Boolean to hide built-in version flag and the VERSION section of help HideVersion bool // categories contains the categorized commands and is populated on app startup categories CommandCategories // flagCategories contains the categorized flags and is populated on app startup flagCategories FlagCategories // An action to execute when the shell completion flag is set BashComplete BashCompleteFunc // An action to execute before any subcommands are run, but after the context is ready // If a non-nil error is returned, no subcommands are run Before BeforeFunc // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc // The action to execute when no subcommands are specified Action ActionFunc // Execute this function if the proper command cannot be found CommandNotFound CommandNotFoundFunc // Execute this function if a usage error occurs OnUsageError OnUsageErrorFunc // Execute this function when an invalid flag is accessed from the context InvalidFlagAccessHandler InvalidFlagAccessFunc // Compilation date Compiled time.Time // List of all authors who contributed Authors []*Author // Copyright of the binary if any Copyright string // Reader reader to write input to (useful for tests) Reader io.Reader // Writer writer to write output to Writer io.Writer // ErrWriter writes error output ErrWriter io.Writer // ExitErrHandler processes any error encountered while running an App before // it is returned to the caller. If no function is provided, HandleExitCoder // is used as the default behavior. ExitErrHandler ExitErrHandlerFunc // Other custom info Metadata map[string]interface{} // Carries a function which returns app specific info. ExtraInfo func() map[string]string // CustomAppHelpTemplate the text template for app help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomAppHelpTemplate string // SliceFlagSeparator is used to customize the separator for SliceFlag, the default is "," SliceFlagSeparator string // DisableSliceFlagSeparator is used to disable SliceFlagSeparator, the default is false DisableSliceFlagSeparator bool // Boolean to enable short-option handling so user can combine several // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool // Enable suggestions for commands and flags Suggest bool // Allows global flags set by libraries which use flag.XXXVar(...) directly // to be parsed through this library AllowExtFlags bool // Treat all flags as normal arguments if true SkipFlagParsing bool didSetup bool separator separatorSpec rootCommand *Command } type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string type SuggestCommandFunc func(commands []*Command, provided string) string // Tries to find out when this binary was compiled. // Returns the current time if it fails to find it. func compileTime() time.Time { info, err := os.Stat(os.Args[0]) if err != nil { return time.Now() } return info.ModTime() } // NewApp creates a new cli Application with some reasonable defaults for Name, // Usage, Version and Action. func NewApp() *App { return &App{ Name: filepath.Base(os.Args[0]), Usage: "A new cli application", UsageText: "", BashComplete: DefaultAppComplete, Action: helpCommand.Action, Compiled: compileTime(), Reader: os.Stdin, Writer: os.Stdout, ErrWriter: os.Stderr, } } // Setup runs initialization code to ensure all data structures are ready for // `Run` or inspection prior to `Run`. It is internally called by `Run`, but // will return early if setup has already happened. func (a *App) Setup() { if a.didSetup { return } a.didSetup = true if a.Name == "" { a.Name = filepath.Base(os.Args[0]) } if a.HelpName == "" { a.HelpName = a.Name } if a.Usage == "" { a.Usage = "A new cli application" } if a.Version == "" { a.HideVersion = true } if a.BashComplete == nil { a.BashComplete = DefaultAppComplete } if a.Action == nil { a.Action = helpCommand.Action } if a.Compiled == (time.Time{}) { a.Compiled = compileTime() } if a.Reader == nil { a.Reader = os.Stdin } if a.Writer == nil { a.Writer = os.Stdout } if a.ErrWriter == nil { a.ErrWriter = os.Stderr } if a.AllowExtFlags { // add global flags added by other packages flag.VisitAll(func(f *flag.Flag) { // skip test flags if !strings.HasPrefix(f.Name, ignoreFlagPrefix) { a.Flags = append(a.Flags, &extFlag{f}) } }) } if len(a.SliceFlagSeparator) != 0 { a.separator.customized = true a.separator.sep = a.SliceFlagSeparator } if a.DisableSliceFlagSeparator { a.separator.customized = true a.separator.disabled = true } for _, c := range a.Commands { cname := c.Name if c.HelpName != "" { cname = c.HelpName } c.separator = a.separator c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname) c.flagCategories = newFlagCategoriesFromFlags(c.Flags) } if a.Command(helpCommand.Name) == nil && !a.HideHelp { if !a.HideHelpCommand { a.appendCommand(helpCommand) } if HelpFlag != nil { a.appendFlag(HelpFlag) } } if !a.HideVersion { a.appendFlag(VersionFlag) } a.categories = newCommandCategories() for _, command := range a.Commands { a.categories.AddCommand(command.Category, command) } sort.Sort(a.categories.(*commandCategories)) a.flagCategories = newFlagCategoriesFromFlags(a.Flags) if a.Metadata == nil { a.Metadata = make(map[string]interface{}) } } func (a *App) newRootCommand() *Command { return &Command{ Name: a.Name, Usage: a.Usage, UsageText: a.UsageText, Description: a.Description, ArgsUsage: a.ArgsUsage, BashComplete: a.BashComplete, Before: a.Before, After: a.After, Action: a.Action, OnUsageError: a.OnUsageError, Subcommands: a.Commands, Flags: a.Flags, flagCategories: a.flagCategories, HideHelp: a.HideHelp, HideHelpCommand: a.HideHelpCommand, UseShortOptionHandling: a.UseShortOptionHandling, HelpName: a.HelpName, CustomHelpTemplate: a.CustomAppHelpTemplate, categories: a.categories, SkipFlagParsing: a.SkipFlagParsing, isRoot: true, separator: a.separator, } } func (a *App) newFlagSet() (*flag.FlagSet, error) { return flagSet(a.Name, a.Flags, a.separator) } func (a *App) useShortOptionHandling() bool { return a.UseShortOptionHandling } // Run is the entry point to the cli app. Parses the arguments slice and routes // to the proper flag/args combination func (a *App) Run(arguments []string) (err error) { return a.RunContext(context.Background(), arguments) } // RunContext is like Run except it takes a Context that will be // passed to its commands and sub-commands. Through this, you can // propagate timeouts and cancellation requests func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { a.Setup() // handle the completion flag separately from the flagset since // completion could be attempted after a flag, but before its value was put // on the command line. this causes the flagset to interpret the completion // flag name as the value of the flag before it which is undesirable // note that we can only do this because the shell autocomplete function // always appends the completion flag at the end of the command shellComplete, arguments := checkShellCompleteFlag(a, arguments) cCtx := NewContext(a, nil, &Context{Context: ctx}) cCtx.shellComplete = shellComplete a.rootCommand = a.newRootCommand() cCtx.Command = a.rootCommand if err := checkDuplicatedCmds(a.rootCommand); err != nil { return err } return a.rootCommand.Run(cCtx, arguments...) } // RunAsSubcommand is for legacy/compatibility purposes only. New code should only // use App.RunContext. This function is slated to be removed in v3. func (a *App) RunAsSubcommand(ctx *Context) (err error) { a.Setup() cCtx := NewContext(a, nil, ctx) cCtx.shellComplete = ctx.shellComplete a.rootCommand = a.newRootCommand() cCtx.Command = a.rootCommand return a.rootCommand.Run(cCtx, ctx.Args().Slice()...) } func (a *App) suggestFlagFromError(err error, command string) (string, error) { flag, parseErr := flagFromError(err) if parseErr != nil { return "", err } flags := a.Flags hideHelp := a.HideHelp if command != "" { cmd := a.Command(command) if cmd == nil { return "", err } flags = cmd.Flags hideHelp = hideHelp || cmd.HideHelp } if SuggestFlag == nil { return "", err } suggestion := SuggestFlag(flags, flag, hideHelp) if len(suggestion) == 0 { return "", err } return fmt.Sprintf(SuggestDidYouMeanTemplate+"\n\n", suggestion), nil } // RunAndExitOnError calls .Run() and exits non-zero if an error was returned // // Deprecated: instead you should return an error that fulfills cli.ExitCoder // to cli.App.Run. This will cause the application to exit with the given error // code in the cli.ExitCoder func (a *App) RunAndExitOnError() { if err := a.Run(os.Args); err != nil { _, _ = fmt.Fprintln(a.ErrWriter, err) OsExiter(1) } } // Command returns the named command on App. Returns nil if the command does not exist func (a *App) Command(name string) *Command { for _, c := range a.Commands { if c.HasName(name) { return c } } return nil } // VisibleCategories returns a slice of categories and commands that are // Hidden=false func (a *App) VisibleCategories() []CommandCategory { ret := []CommandCategory{} for _, category := range a.categories.Categories() { if visible := func() CommandCategory { if len(category.VisibleCommands()) > 0 { return category } return nil }(); visible != nil { ret = append(ret, visible) } } return ret } // VisibleCommands returns a slice of the Commands with Hidden=false func (a *App) VisibleCommands() []*Command { var ret []*Command for _, command := range a.Commands { if !command.Hidden { ret = append(ret, command) } } return ret } // VisibleFlagCategories returns a slice containing all the categories with the flags they contain func (a *App) VisibleFlagCategories() []VisibleFlagCategory { if a.flagCategories == nil { return []VisibleFlagCategory{} } return a.flagCategories.VisibleCategories() } // VisibleFlags returns a slice of the Flags with Hidden=false func (a *App) VisibleFlags() []Flag { return visibleFlags(a.Flags) } func (a *App) appendFlag(fl Flag) { if !hasFlag(a.Flags, fl) { a.Flags = append(a.Flags, fl) } } func (a *App) appendCommand(c *Command) { if !hasCommand(a.Commands, c) { a.Commands = append(a.Commands, c) } } func (a *App) handleExitCoder(cCtx *Context, err error) { if a.ExitErrHandler != nil { a.ExitErrHandler(cCtx, err) } else { HandleExitCoder(err) } } func (a *App) argsWithDefaultCommand(oldArgs Args) Args { if a.DefaultCommand != "" { rawArgs := append([]string{a.DefaultCommand}, oldArgs.Slice()...) newArgs := args(rawArgs) return &newArgs } return oldArgs } func runFlagActions(c *Context, fs []Flag) error { for _, f := range fs { isSet := false for _, name := range f.Names() { if c.IsSet(name) { isSet = true break } } if isSet { if af, ok := f.(ActionableFlag); ok { if err := af.RunAction(c); err != nil { return err } } } } return nil } // Author represents someone who has contributed to a cli project. type Author struct { Name string // The Authors name Email string // The Authors email } // String makes Author comply to the Stringer interface, to allow an easy print in the templating process func (a *Author) String() string { e := "" if a.Email != "" { e = " <" + a.Email + ">" } return fmt.Sprintf("%v%v", a.Name, e) } // HandleAction attempts to figure out which Action signature was used. If // it's an ActionFunc or a func with the legacy signature for Action, the func // is run! func HandleAction(action interface{}, cCtx *Context) (err error) { switch a := action.(type) { case ActionFunc: return a(cCtx) case func(*Context) error: return a(cCtx) case func(*Context): // deprecated function signature a(cCtx) return nil } return errInvalidActionType } func checkStringSliceIncludes(want string, sSlice []string) bool { found := false for _, s := range sSlice { if want == s { found = true break } } return found } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/args.go000066400000000000000000000020031504213346300223060ustar00rootroot00000000000000package cli type Args interface { // Get returns the nth argument, or else a blank string Get(n int) string // First returns the first argument, or else a blank string First() string // Tail returns the rest of the arguments (not the first one) // or else an empty string slice Tail() []string // Len returns the length of the wrapped slice Len() int // Present checks if there are any arguments present Present() bool // Slice returns a copy of the internal slice Slice() []string } type args []string func (a *args) Get(n int) string { if len(*a) > n { return (*a)[n] } return "" } func (a *args) First() string { return a.Get(0) } func (a *args) Tail() []string { if a.Len() >= 2 { tail := []string((*a)[1:]) ret := make([]string, len(tail)) copy(ret, tail) return ret } return []string{} } func (a *args) Len() int { return len(*a) } func (a *args) Present() bool { return a.Len() != 0 } func (a *args) Slice() []string { ret := make([]string, len(*a)) copy(ret, *a) return ret } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/category.go000066400000000000000000000102451504213346300231760ustar00rootroot00000000000000package cli import "sort" // CommandCategories interface allows for category manipulation type CommandCategories interface { // AddCommand adds a command to a category, creating a new category if necessary. AddCommand(category string, command *Command) // Categories returns a slice of categories sorted by name Categories() []CommandCategory } type commandCategories []*commandCategory func newCommandCategories() CommandCategories { ret := commandCategories([]*commandCategory{}) return &ret } func (c *commandCategories) Less(i, j int) bool { return lexicographicLess((*c)[i].Name(), (*c)[j].Name()) } func (c *commandCategories) Len() int { return len(*c) } func (c *commandCategories) Swap(i, j int) { (*c)[i], (*c)[j] = (*c)[j], (*c)[i] } func (c *commandCategories) AddCommand(category string, command *Command) { for _, commandCategory := range []*commandCategory(*c) { if commandCategory.name == category { commandCategory.commands = append(commandCategory.commands, command) return } } newVal := append(*c, &commandCategory{name: category, commands: []*Command{command}}) *c = newVal } func (c *commandCategories) Categories() []CommandCategory { ret := make([]CommandCategory, len(*c)) for i, cat := range *c { ret[i] = cat } return ret } // CommandCategory is a category containing commands. type CommandCategory interface { // Name returns the category name string Name() string // VisibleCommands returns a slice of the Commands with Hidden=false VisibleCommands() []*Command } type commandCategory struct { name string commands []*Command } func (c *commandCategory) Name() string { return c.name } func (c *commandCategory) VisibleCommands() []*Command { if c.commands == nil { c.commands = []*Command{} } var ret []*Command for _, command := range c.commands { if !command.Hidden { ret = append(ret, command) } } return ret } // FlagCategories interface allows for category manipulation type FlagCategories interface { // AddFlags adds a flag to a category, creating a new category if necessary. AddFlag(category string, fl Flag) // VisibleCategories returns a slice of visible flag categories sorted by name VisibleCategories() []VisibleFlagCategory } type defaultFlagCategories struct { m map[string]*defaultVisibleFlagCategory } func newFlagCategories() FlagCategories { return &defaultFlagCategories{ m: map[string]*defaultVisibleFlagCategory{}, } } func newFlagCategoriesFromFlags(fs []Flag) FlagCategories { fc := newFlagCategories() var categorized bool for _, fl := range fs { if cf, ok := fl.(CategorizableFlag); ok { if cat := cf.GetCategory(); cat != "" && cf.IsVisible() { fc.AddFlag(cat, cf) categorized = true } } } if categorized { for _, fl := range fs { if cf, ok := fl.(CategorizableFlag); ok { if cf.GetCategory() == "" && cf.IsVisible() { fc.AddFlag("", fl) } } } } return fc } func (f *defaultFlagCategories) AddFlag(category string, fl Flag) { if _, ok := f.m[category]; !ok { f.m[category] = &defaultVisibleFlagCategory{name: category, m: map[string]Flag{}} } f.m[category].m[fl.String()] = fl } func (f *defaultFlagCategories) VisibleCategories() []VisibleFlagCategory { catNames := []string{} for name := range f.m { catNames = append(catNames, name) } sort.Strings(catNames) ret := make([]VisibleFlagCategory, len(catNames)) for i, name := range catNames { ret[i] = f.m[name] } return ret } // VisibleFlagCategory is a category containing flags. type VisibleFlagCategory interface { // Name returns the category name string Name() string // Flags returns a slice of VisibleFlag sorted by name Flags() []VisibleFlag } type defaultVisibleFlagCategory struct { name string m map[string]Flag } func (fc *defaultVisibleFlagCategory) Name() string { return fc.name } func (fc *defaultVisibleFlagCategory) Flags() []VisibleFlag { vfNames := []string{} for flName, fl := range fc.m { if vf, ok := fl.(VisibleFlag); ok { if vf.IsVisible() { vfNames = append(vfNames, flName) } } } sort.Strings(vfNames) ret := make([]VisibleFlag, len(vfNames)) for i, flName := range vfNames { ret[i] = fc.m[flName].(VisibleFlag) } return ret } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/cli.go000066400000000000000000000012321504213346300221240ustar00rootroot00000000000000// Package cli provides a minimal framework for creating and organizing command line // Go applications. cli is designed to be easy to understand and write, the most simple // cli application can be written as follows: // // func main() { // (&cli.App{}).Run(os.Args) // } // // Of course this application does not do much, so let's make this an actual application: // // func main() { // app := &cli.App{ // Name: "greet", // Usage: "say a greeting", // Action: func(c *cli.Context) error { // fmt.Println("Greetings") // return nil // }, // } // // app.Run(os.Args) // } package cli //go:generate make -C cmd/urfave-cli-genflags run png2svg-1.7.0/vendor/github.com/urfave/cli/v2/command.go000066400000000000000000000235301504213346300230000ustar00rootroot00000000000000package cli import ( "flag" "fmt" "reflect" "sort" "strings" ) // Command is a subcommand for a cli.App. type Command struct { // The name of the command Name string // A list of aliases for the command Aliases []string // A short description of the usage of this command Usage string // Custom text to show on USAGE section of help UsageText string // A longer explanation of how the command works Description string // Whether this command supports arguments Args bool // A short description of the arguments of this command ArgsUsage string // The category the command is part of Category string // The function to call when checking for bash command completions BashComplete BashCompleteFunc // An action to execute before any sub-subcommands are run, but after the context is ready // If a non-nil error is returned, no sub-subcommands are run Before BeforeFunc // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc // The function to call when this command is invoked Action ActionFunc // Execute this function if a usage error occurs. OnUsageError OnUsageErrorFunc // List of child commands Subcommands []*Command // List of flags to parse Flags []Flag flagCategories FlagCategories // Treat all flags as normal arguments if true SkipFlagParsing bool // Boolean to hide built-in help command and help flag HideHelp bool // Boolean to hide built-in help command but keep help flag // Ignored if HideHelp is true. HideHelpCommand bool // Boolean to hide this command from help or completion Hidden bool // Boolean to enable short-option handling so user can combine several // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool // Full name of command for help, defaults to full command name, including parent commands. HelpName string commandNamePath []string // CustomHelpTemplate the text template for the command help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomHelpTemplate string // categories contains the categorized commands and is populated on app startup categories CommandCategories // if this is a root "special" command isRoot bool separator separatorSpec } type Commands []*Command type CommandsByName []*Command func (c CommandsByName) Len() int { return len(c) } func (c CommandsByName) Less(i, j int) bool { return lexicographicLess(c[i].Name, c[j].Name) } func (c CommandsByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] } // FullName returns the full name of the command. // For subcommands this ensures that parent commands are part of the command path func (c *Command) FullName() string { if c.commandNamePath == nil { return c.Name } return strings.Join(c.commandNamePath, " ") } func (cmd *Command) Command(name string) *Command { for _, c := range cmd.Subcommands { if c.HasName(name) { return c } } return nil } func (c *Command) setup(ctx *Context) { if c.Command(helpCommand.Name) == nil && !c.HideHelp { if !c.HideHelpCommand { c.Subcommands = append(c.Subcommands, helpCommand) } } if !c.HideHelp && HelpFlag != nil { // append help to flags c.appendFlag(HelpFlag) } if ctx.App.UseShortOptionHandling { c.UseShortOptionHandling = true } c.categories = newCommandCategories() for _, command := range c.Subcommands { c.categories.AddCommand(command.Category, command) } sort.Sort(c.categories.(*commandCategories)) for _, scmd := range c.Subcommands { if scmd.HelpName == "" { scmd.HelpName = fmt.Sprintf("%s %s", c.HelpName, scmd.Name) } scmd.separator = c.separator } if c.BashComplete == nil { c.BashComplete = DefaultCompleteWithFlags(c) } } func (c *Command) Run(cCtx *Context, arguments ...string) (err error) { if !c.isRoot { c.setup(cCtx) if err := checkDuplicatedCmds(c); err != nil { return err } } a := args(arguments) set, err := c.parseFlags(&a, cCtx.shellComplete) cCtx.flagSet = set if checkCompletions(cCtx) { return nil } if err != nil { if c.OnUsageError != nil { err = c.OnUsageError(cCtx, err, !c.isRoot) cCtx.App.handleExitCoder(cCtx, err) return err } _, _ = fmt.Fprintf(cCtx.App.Writer, "%s %s\n\n", "Incorrect Usage:", err.Error()) if cCtx.App.Suggest { if suggestion, err := c.suggestFlagFromError(err, ""); err == nil { fmt.Fprintf(cCtx.App.Writer, "%s", suggestion) } } if !c.HideHelp { if c.isRoot { _ = ShowAppHelp(cCtx) } else { _ = ShowCommandHelp(cCtx.parentContext, c.Name) } } return err } if checkHelp(cCtx) { return helpCommand.Action(cCtx) } if c.isRoot && !cCtx.App.HideVersion && checkVersion(cCtx) { ShowVersion(cCtx) return nil } if c.After != nil && !cCtx.shellComplete { defer func() { afterErr := c.After(cCtx) if afterErr != nil { cCtx.App.handleExitCoder(cCtx, err) if err != nil { err = newMultiError(err, afterErr) } else { err = afterErr } } }() } cerr := cCtx.checkRequiredFlags(c.Flags) if cerr != nil { _ = helpCommand.Action(cCtx) return cerr } if c.Before != nil && !cCtx.shellComplete { beforeErr := c.Before(cCtx) if beforeErr != nil { cCtx.App.handleExitCoder(cCtx, beforeErr) err = beforeErr return err } } if err = runFlagActions(cCtx, c.Flags); err != nil { return err } var cmd *Command args := cCtx.Args() if args.Present() { name := args.First() cmd = c.Command(name) if cmd == nil { hasDefault := cCtx.App.DefaultCommand != "" isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames()) var ( isDefaultSubcommand = false defaultHasSubcommands = false ) if hasDefault { dc := cCtx.App.Command(cCtx.App.DefaultCommand) defaultHasSubcommands = len(dc.Subcommands) > 0 for _, dcSub := range dc.Subcommands { if checkStringSliceIncludes(name, dcSub.Names()) { isDefaultSubcommand = true break } } } if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) { argsWithDefault := cCtx.App.argsWithDefaultCommand(args) if !reflect.DeepEqual(args, argsWithDefault) { cmd = cCtx.App.rootCommand.Command(argsWithDefault.First()) } } } } else if c.isRoot && cCtx.App.DefaultCommand != "" { if dc := cCtx.App.Command(cCtx.App.DefaultCommand); dc != c { cmd = dc } } if cmd != nil { newcCtx := NewContext(cCtx.App, nil, cCtx) newcCtx.Command = cmd return cmd.Run(newcCtx, cCtx.Args().Slice()...) } if c.Action == nil { c.Action = helpCommand.Action } err = c.Action(cCtx) cCtx.App.handleExitCoder(cCtx, err) return err } func (c *Command) newFlagSet() (*flag.FlagSet, error) { return flagSet(c.Name, c.Flags, c.separator) } func (c *Command) useShortOptionHandling() bool { return c.UseShortOptionHandling } func (c *Command) suggestFlagFromError(err error, command string) (string, error) { flag, parseErr := flagFromError(err) if parseErr != nil { return "", err } flags := c.Flags hideHelp := c.HideHelp if command != "" { cmd := c.Command(command) if cmd == nil { return "", err } flags = cmd.Flags hideHelp = hideHelp || cmd.HideHelp } suggestion := SuggestFlag(flags, flag, hideHelp) if len(suggestion) == 0 { return "", err } return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion) + "\n\n", nil } func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) { set, err := c.newFlagSet() if err != nil { return nil, err } if c.SkipFlagParsing { return set, set.Parse(append([]string{"--"}, args.Tail()...)) } err = parseIter(set, c, args.Tail(), shellComplete) if err != nil { return nil, err } err = normalizeFlags(c.Flags, set) if err != nil { return nil, err } return set, nil } // Names returns the names including short names and aliases. func (c *Command) Names() []string { return append([]string{c.Name}, c.Aliases...) } // HasName returns true if Command.Name matches given name func (c *Command) HasName(name string) bool { for _, n := range c.Names() { if n == name { return true } } return false } // VisibleCategories returns a slice of categories and commands that are // Hidden=false func (c *Command) VisibleCategories() []CommandCategory { ret := []CommandCategory{} for _, category := range c.categories.Categories() { if visible := func() CommandCategory { if len(category.VisibleCommands()) > 0 { return category } return nil }(); visible != nil { ret = append(ret, visible) } } return ret } // VisibleCommands returns a slice of the Commands with Hidden=false func (c *Command) VisibleCommands() []*Command { var ret []*Command for _, command := range c.Subcommands { if !command.Hidden { ret = append(ret, command) } } return ret } // VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain func (c *Command) VisibleFlagCategories() []VisibleFlagCategory { if c.flagCategories == nil { c.flagCategories = newFlagCategoriesFromFlags(c.Flags) } return c.flagCategories.VisibleCategories() } // VisibleFlags returns a slice of the Flags with Hidden=false func (c *Command) VisibleFlags() []Flag { return visibleFlags(c.Flags) } func (c *Command) appendFlag(fl Flag) { if !hasFlag(c.Flags, fl) { c.Flags = append(c.Flags, fl) } } func hasCommand(commands []*Command, command *Command) bool { for _, existing := range commands { if command == existing { return true } } return false } func checkDuplicatedCmds(parent *Command) error { seen := make(map[string]struct{}) for _, c := range parent.Subcommands { for _, name := range c.Names() { if _, exists := seen[name]; exists { return fmt.Errorf("parent command [%s] has duplicated subcommand name or alias: %s", parent.Name, name) } seen[name] = struct{}{} } } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/context.go000066400000000000000000000131321504213346300230430ustar00rootroot00000000000000package cli import ( "context" "flag" "fmt" "strings" ) // Context is a type that is passed through to // each Handler action in a cli application. Context // can be used to retrieve context-specific args and // parsed command-line options. type Context struct { context.Context App *App Command *Command shellComplete bool flagSet *flag.FlagSet parentContext *Context } // NewContext creates a new context. For use in when invoking an App or Command action. func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { c := &Context{App: app, flagSet: set, parentContext: parentCtx} if parentCtx != nil { c.Context = parentCtx.Context c.shellComplete = parentCtx.shellComplete if parentCtx.flagSet == nil { parentCtx.flagSet = &flag.FlagSet{} } } c.Command = &Command{} if c.Context == nil { c.Context = context.Background() } return c } // NumFlags returns the number of flags set func (cCtx *Context) NumFlags() int { return cCtx.flagSet.NFlag() } // Set sets a context flag to a value. func (cCtx *Context) Set(name, value string) error { if fs := cCtx.lookupFlagSet(name); fs != nil { return fs.Set(name, value) } return fmt.Errorf("no such flag -%s", name) } // IsSet determines if the flag was actually set func (cCtx *Context) IsSet(name string) bool { if fs := cCtx.lookupFlagSet(name); fs != nil { isSet := false fs.Visit(func(f *flag.Flag) { if f.Name == name { isSet = true } }) if isSet { return true } f := cCtx.lookupFlag(name) if f == nil { return false } if f.IsSet() { return true } // now redo flagset search on aliases aliases := f.Names() fs.Visit(func(f *flag.Flag) { for _, alias := range aliases { if f.Name == alias { isSet = true } } }) if isSet { return true } } return false } // LocalFlagNames returns a slice of flag names used in this context. func (cCtx *Context) LocalFlagNames() []string { var names []string cCtx.flagSet.Visit(makeFlagNameVisitor(&names)) // Check the flags which have been set via env or file if cCtx.Command != nil && cCtx.Command.Flags != nil { for _, f := range cCtx.Command.Flags { if f.IsSet() { names = append(names, f.Names()...) } } } // Sort out the duplicates since flag could be set via multiple // paths m := map[string]struct{}{} var unames []string for _, name := range names { if _, ok := m[name]; !ok { m[name] = struct{}{} unames = append(unames, name) } } return unames } // FlagNames returns a slice of flag names used by the this context and all of // its parent contexts. func (cCtx *Context) FlagNames() []string { var names []string for _, pCtx := range cCtx.Lineage() { names = append(names, pCtx.LocalFlagNames()...) } return names } // Lineage returns *this* context and all of its ancestor contexts in order from // child to parent func (cCtx *Context) Lineage() []*Context { var lineage []*Context for cur := cCtx; cur != nil; cur = cur.parentContext { lineage = append(lineage, cur) } return lineage } // Count returns the num of occurrences of this flag func (cCtx *Context) Count(name string) int { if fs := cCtx.lookupFlagSet(name); fs != nil { if cf, ok := fs.Lookup(name).Value.(Countable); ok { return cf.Count() } } return 0 } // Value returns the value of the flag corresponding to `name` func (cCtx *Context) Value(name string) interface{} { if fs := cCtx.lookupFlagSet(name); fs != nil { return fs.Lookup(name).Value.(flag.Getter).Get() } return nil } // Args returns the command line arguments associated with the context. func (cCtx *Context) Args() Args { ret := args(cCtx.flagSet.Args()) return &ret } // NArg returns the number of the command line arguments. func (cCtx *Context) NArg() int { return cCtx.Args().Len() } func (cCtx *Context) lookupFlag(name string) Flag { for _, c := range cCtx.Lineage() { if c.Command == nil { continue } for _, f := range c.Command.Flags { for _, n := range f.Names() { if n == name { return f } } } } if cCtx.App != nil { for _, f := range cCtx.App.Flags { for _, n := range f.Names() { if n == name { return f } } } } return nil } func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet { for _, c := range cCtx.Lineage() { if c.flagSet == nil { continue } if f := c.flagSet.Lookup(name); f != nil { return c.flagSet } } cCtx.onInvalidFlag(name) return nil } func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { var flagPresent bool var flagName string flagNames := f.Names() flagName = flagNames[0] for _, key := range flagNames { if cCtx.IsSet(strings.TrimSpace(key)) { flagPresent = true } } if !flagPresent && flagName != "" { missingFlags = append(missingFlags, flagName) } } } if len(missingFlags) != 0 { return &errRequiredFlags{missingFlags: missingFlags} } return nil } func (cCtx *Context) onInvalidFlag(name string) { for cCtx != nil { if cCtx.App != nil && cCtx.App.InvalidFlagAccessHandler != nil { cCtx.App.InvalidFlagAccessHandler(cCtx, name) break } cCtx = cCtx.parentContext } } func makeFlagNameVisitor(names *[]string) func(*flag.Flag) { return func(f *flag.Flag) { nameParts := strings.Split(f.Name, ",") name := strings.TrimSpace(nameParts[0]) for _, part := range nameParts { part = strings.TrimSpace(part) if len(part) > len(name) { name = part } } if name != "" { *names = append(*names, name) } } } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/docs.go000066400000000000000000000111041504213346300223040ustar00rootroot00000000000000//go:build !urfave_cli_no_docs // +build !urfave_cli_no_docs package cli import ( "bytes" "fmt" "io" "sort" "strings" "text/template" "github.com/cpuguy83/go-md2man/v2/md2man" ) // ToMarkdown creates a markdown string for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToMarkdown() (string, error) { var w bytes.Buffer if err := a.writeDocTemplate(&w, 0); err != nil { return "", err } return w.String(), nil } // ToMan creates a man page string with section number for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToManWithSection(sectionNumber int) (string, error) { var w bytes.Buffer if err := a.writeDocTemplate(&w, sectionNumber); err != nil { return "", err } man := md2man.Render(w.Bytes()) return string(man), nil } // ToMan creates a man page string for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToMan() (string, error) { man, err := a.ToManWithSection(8) return man, err } type cliTemplate struct { App *App SectionNum int Commands []string GlobalArgs []string SynopsisArgs []string } func (a *App) writeDocTemplate(w io.Writer, sectionNum int) error { const name = "cli" t, err := template.New(name).Parse(MarkdownDocTemplate) if err != nil { return err } return t.ExecuteTemplate(w, name, &cliTemplate{ App: a, SectionNum: sectionNum, Commands: prepareCommands(a.Commands, 0), GlobalArgs: prepareArgsWithValues(a.VisibleFlags()), SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()), }) } func prepareCommands(commands []*Command, level int) []string { var coms []string for _, command := range commands { if command.Hidden { continue } usageText := prepareUsageText(command) usage := prepareUsage(command, usageText) prepared := fmt.Sprintf("%s %s\n\n%s%s", strings.Repeat("#", level+2), strings.Join(command.Names(), ", "), usage, usageText, ) flags := prepareArgsWithValues(command.VisibleFlags()) if len(flags) > 0 { prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n")) } coms = append(coms, prepared) // recursively iterate subcommands if len(command.Subcommands) > 0 { coms = append( coms, prepareCommands(command.Subcommands, level+1)..., ) } } return coms } func prepareArgsWithValues(flags []Flag) []string { return prepareFlags(flags, ", ", "**", "**", `""`, true) } func prepareArgsSynopsis(flags []Flag) []string { return prepareFlags(flags, "|", "[", "]", "[value]", false) } func prepareFlags( flags []Flag, sep, opener, closer, value string, addDetails bool, ) []string { args := []string{} for _, f := range flags { flag, ok := f.(DocGenerationFlag) if !ok { continue } modifiedArg := opener for _, s := range flag.Names() { trimmed := strings.TrimSpace(s) if len(modifiedArg) > len(opener) { modifiedArg += sep } if len(trimmed) > 1 { modifiedArg += fmt.Sprintf("--%s", trimmed) } else { modifiedArg += fmt.Sprintf("-%s", trimmed) } } modifiedArg += closer if flag.TakesValue() { modifiedArg += fmt.Sprintf("=%s", value) } if addDetails { modifiedArg += flagDetails(flag) } args = append(args, modifiedArg+"\n") } sort.Strings(args) return args } // flagDetails returns a string containing the flags metadata func flagDetails(flag DocGenerationFlag) string { description := flag.GetUsage() if flag.TakesValue() { defaultText := flag.GetDefaultText() if defaultText == "" { defaultText = flag.GetValue() } if defaultText != "" { description += " (default: " + defaultText + ")" } } return ": " + description } func prepareUsageText(command *Command) string { if command.UsageText == "" { return "" } // Remove leading and trailing newlines preparedUsageText := strings.Trim(command.UsageText, "\n") var usageText string if strings.Contains(preparedUsageText, "\n") { // Format multi-line string as a code block using the 4 space schema to allow for embedded markdown such // that it will not break the continuous code block. for _, ln := range strings.Split(preparedUsageText, "\n") { usageText += fmt.Sprintf(" %s\n", ln) } } else { // Style a single line as a note usageText = fmt.Sprintf(">%s\n", preparedUsageText) } return usageText } func prepareUsage(command *Command, usageText string) string { if command.Usage == "" { return "" } usage := command.Usage + "\n" // Add a newline to the Usage IFF there is a UsageText if usageText != "" { usage += "\n" } return usage } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/errors.go000066400000000000000000000102011504213346300226650ustar00rootroot00000000000000package cli import ( "fmt" "io" "os" "strings" ) // OsExiter is the function used when the app exits. If not set defaults to os.Exit. var OsExiter = os.Exit // ErrWriter is used to write errors to the user. This can be anything // implementing the io.Writer interface and defaults to os.Stderr. var ErrWriter io.Writer = os.Stderr // MultiError is an error that wraps multiple errors. type MultiError interface { error Errors() []error } // newMultiError creates a new MultiError. Pass in one or more errors. func newMultiError(err ...error) MultiError { ret := multiError(err) return &ret } type multiError []error // Error implements the error interface. func (m *multiError) Error() string { errs := make([]string, len(*m)) for i, err := range *m { errs[i] = err.Error() } return strings.Join(errs, "\n") } // Errors returns a copy of the errors slice func (m *multiError) Errors() []error { errs := make([]error, len(*m)) for _, err := range *m { errs = append(errs, err) } return errs } type requiredFlagsErr interface { error getMissingFlags() []string } type errRequiredFlags struct { missingFlags []string } func (e *errRequiredFlags) Error() string { numberOfMissingFlags := len(e.missingFlags) if numberOfMissingFlags == 1 { return fmt.Sprintf("Required flag %q not set", e.missingFlags[0]) } joinedMissingFlags := strings.Join(e.missingFlags, ", ") return fmt.Sprintf("Required flags %q not set", joinedMissingFlags) } func (e *errRequiredFlags) getMissingFlags() []string { return e.missingFlags } // ErrorFormatter is the interface that will suitably format the error output type ErrorFormatter interface { Format(s fmt.State, verb rune) } // ExitCoder is the interface checked by `App` and `Command` for a custom exit // code type ExitCoder interface { error ExitCode() int } type exitError struct { exitCode int err error } // NewExitError calls Exit to create a new ExitCoder. // // Deprecated: This function is a duplicate of Exit and will eventually be removed. func NewExitError(message interface{}, exitCode int) ExitCoder { return Exit(message, exitCode) } // Exit wraps a message and exit code into an error, which by default is // handled with a call to os.Exit during default error handling. // // This is the simplest way to trigger a non-zero exit code for an App without // having to call os.Exit manually. During testing, this behavior can be avoided // by overriding the ExitErrHandler function on an App or the package-global // OsExiter function. func Exit(message interface{}, exitCode int) ExitCoder { var err error switch e := message.(type) { case ErrorFormatter: err = fmt.Errorf("%+v", message) case error: err = e default: err = fmt.Errorf("%+v", message) } return &exitError{ err: err, exitCode: exitCode, } } func (ee *exitError) Error() string { return ee.err.Error() } func (ee *exitError) ExitCode() int { return ee.exitCode } func (ee *exitError) Unwrap() error { return ee.err } // HandleExitCoder handles errors implementing ExitCoder by printing their // message and calling OsExiter with the given exit code. // // If the given error instead implements MultiError, each error will be checked // for the ExitCoder interface, and OsExiter will be called with the last exit // code found, or exit code 1 if no ExitCoder is found. // // This function is the default error-handling behavior for an App. func HandleExitCoder(err error) { if err == nil { return } if exitErr, ok := err.(ExitCoder); ok { if err.Error() != "" { if _, ok := exitErr.(ErrorFormatter); ok { _, _ = fmt.Fprintf(ErrWriter, "%+v\n", err) } else { _, _ = fmt.Fprintln(ErrWriter, err) } } OsExiter(exitErr.ExitCode()) return } if multiErr, ok := err.(MultiError); ok { code := handleMultiError(multiErr) OsExiter(code) return } } func handleMultiError(multiErr MultiError) int { code := 1 for _, merr := range multiErr.Errors() { if multiErr2, ok := merr.(MultiError); ok { code = handleMultiError(multiErr2) } else if merr != nil { fmt.Fprintln(ErrWriter, merr) if exitErr, ok := merr.(ExitCoder); ok { code = exitErr.ExitCode() } } } return code } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/fish.go000066400000000000000000000101151504213346300223060ustar00rootroot00000000000000package cli import ( "bytes" "fmt" "io" "strings" "text/template" ) // ToFishCompletion creates a fish completion string for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToFishCompletion() (string, error) { var w bytes.Buffer if err := a.writeFishCompletionTemplate(&w); err != nil { return "", err } return w.String(), nil } type fishCompletionTemplate struct { App *App Completions []string AllCommands []string } func (a *App) writeFishCompletionTemplate(w io.Writer) error { const name = "cli" t, err := template.New(name).Parse(FishCompletionTemplate) if err != nil { return err } allCommands := []string{} // Add global flags completions := a.prepareFishFlags(a.VisibleFlags(), allCommands) // Add help flag if !a.HideHelp { completions = append( completions, a.prepareFishFlags([]Flag{HelpFlag}, allCommands)..., ) } // Add version flag if !a.HideVersion { completions = append( completions, a.prepareFishFlags([]Flag{VersionFlag}, allCommands)..., ) } // Add commands and their flags completions = append( completions, a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})..., ) return t.ExecuteTemplate(w, name, &fishCompletionTemplate{ App: a, Completions: completions, AllCommands: allCommands, }) } func (a *App) prepareFishCommands(commands []*Command, allCommands *[]string, previousCommands []string) []string { completions := []string{} for _, command := range commands { if command.Hidden { continue } var completion strings.Builder completion.WriteString(fmt.Sprintf( "complete -r -c %s -n '%s' -a '%s'", a.Name, a.fishSubcommandHelper(previousCommands), strings.Join(command.Names(), " "), )) if command.Usage != "" { completion.WriteString(fmt.Sprintf(" -d '%s'", escapeSingleQuotes(command.Usage))) } if !command.HideHelp { completions = append( completions, a.prepareFishFlags([]Flag{HelpFlag}, command.Names())..., ) } *allCommands = append(*allCommands, command.Names()...) completions = append(completions, completion.String()) completions = append( completions, a.prepareFishFlags(command.VisibleFlags(), command.Names())..., ) // recursively iterate subcommands if len(command.Subcommands) > 0 { completions = append( completions, a.prepareFishCommands( command.Subcommands, allCommands, command.Names(), )..., ) } } return completions } func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string { completions := []string{} for _, f := range flags { flag, ok := f.(DocGenerationFlag) if !ok { continue } completion := &strings.Builder{} completion.WriteString(fmt.Sprintf( "complete -c %s -n '%s'", a.Name, a.fishSubcommandHelper(previousCommands), )) fishAddFileFlag(f, completion) for idx, opt := range flag.Names() { if idx == 0 { completion.WriteString(fmt.Sprintf( " -l %s", strings.TrimSpace(opt), )) } else { completion.WriteString(fmt.Sprintf( " -s %s", strings.TrimSpace(opt), )) } } if flag.TakesValue() { completion.WriteString(" -r") } if flag.GetUsage() != "" { completion.WriteString(fmt.Sprintf(" -d '%s'", escapeSingleQuotes(flag.GetUsage()))) } completions = append(completions, completion.String()) } return completions } func fishAddFileFlag(flag Flag, completion *strings.Builder) { switch f := flag.(type) { case *GenericFlag: if f.TakesFile { return } case *StringFlag: if f.TakesFile { return } case *StringSliceFlag: if f.TakesFile { return } case *PathFlag: if f.TakesFile { return } } completion.WriteString(" -f") } func (a *App) fishSubcommandHelper(allCommands []string) string { fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name) if len(allCommands) > 0 { fishHelper = fmt.Sprintf( "__fish_seen_subcommand_from %s", strings.Join(allCommands, " "), ) } return fishHelper } func escapeSingleQuotes(input string) string { return strings.Replace(input, `'`, `\'`, -1) } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag-spec.yaml000066400000000000000000000061201504213346300235540ustar00rootroot00000000000000# NOTE: this file is used by the tool defined in # ./cmd/urfave-cli-genflags/main.go which uses the # `Spec` type that maps to this file structure. flag_types: bool: struct_fields: - name: Count type: int pointer: true - name: DisableDefaultText type: bool - name: Action type: "func(*Context, bool) error" float64: struct_fields: - name: Action type: "func(*Context, float64) error" Float64Slice: value_pointer: true skip_interfaces: - fmt.Stringer struct_fields: - name: separator type: separatorSpec - name: Action type: "func(*Context, []float64) error" int: struct_fields: - name: Base type: int - name: Action type: "func(*Context, int) error" IntSlice: value_pointer: true skip_interfaces: - fmt.Stringer struct_fields: - name: separator type: separatorSpec - name: Action type: "func(*Context, []int) error" int64: struct_fields: - name: Base type: int - name: Action type: "func(*Context, int64) error" Int64Slice: value_pointer: true skip_interfaces: - fmt.Stringer struct_fields: - name: separator type: separatorSpec - name: Action type: "func(*Context, []int64) error" uint: struct_fields: - name: Base type: int - name: Action type: "func(*Context, uint) error" UintSlice: value_pointer: true skip_interfaces: - fmt.Stringer struct_fields: - name: separator type: separatorSpec - name: Action type: "func(*Context, []uint) error" uint64: struct_fields: - name: Base type: int - name: Action type: "func(*Context, uint64) error" Uint64Slice: value_pointer: true skip_interfaces: - fmt.Stringer struct_fields: - name: separator type: separatorSpec - name: Action type: "func(*Context, []uint64) error" string: struct_fields: - name: TakesFile type: bool - name: Action type: "func(*Context, string) error" StringSlice: value_pointer: true skip_interfaces: - fmt.Stringer struct_fields: - name: separator type: separatorSpec - name: TakesFile type: bool - name: Action type: "func(*Context, []string) error" - name: KeepSpace type: bool time.Duration: struct_fields: - name: Action type: "func(*Context, time.Duration) error" Timestamp: value_pointer: true struct_fields: - name: Layout type: string - name: Timezone type: "*time.Location" - name: Action type: "func(*Context, *time.Time) error" Generic: no_destination_pointer: true struct_fields: - name: TakesFile type: bool - name: Action type: "func(*Context, interface{}) error" Path: struct_fields: - name: TakesFile type: bool - name: Action type: "func(*Context, Path) error" png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag.go000066400000000000000000000237021504213346300222740ustar00rootroot00000000000000package cli import ( "errors" "flag" "fmt" "io" "os" "regexp" "runtime" "strings" "syscall" "time" ) const defaultPlaceholder = "value" const ( defaultSliceFlagSeparator = "," disableSliceFlagSeparator = false ) var ( slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano()) commaWhitespace = regexp.MustCompile("[, ]+.*") ) // BashCompletionFlag enables bash-completion for all commands and subcommands var BashCompletionFlag Flag = &BoolFlag{ Name: "generate-bash-completion", Hidden: true, } // VersionFlag prints the version for the application var VersionFlag Flag = &BoolFlag{ Name: "version", Aliases: []string{"v"}, Usage: "print the version", DisableDefaultText: true, } // HelpFlag prints the help for all commands and subcommands. // Set to nil to disable the flag. The subcommand // will still be added unless HideHelp or HideHelpCommand is set to true. var HelpFlag Flag = &BoolFlag{ Name: "help", Aliases: []string{"h"}, Usage: "show help", DisableDefaultText: true, } // FlagStringer converts a flag definition to a string. This is used by help // to display a flag. var FlagStringer FlagStringFunc = stringifyFlag // Serializer is used to circumvent the limitations of flag.FlagSet.Set type Serializer interface { Serialize() string } // FlagNamePrefixer converts a full flag name and its placeholder into the help // message flag prefix. This is used by the default FlagStringer. var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames // FlagEnvHinter annotates flag help message with the environment variable // details. This is used by the default FlagStringer. var FlagEnvHinter FlagEnvHintFunc = withEnvHint // FlagFileHinter annotates flag help message with the environment variable // details. This is used by the default FlagStringer. var FlagFileHinter FlagFileHintFunc = withFileHint // FlagsByName is a slice of Flag. type FlagsByName []Flag func (f FlagsByName) Len() int { return len(f) } func (f FlagsByName) Less(i, j int) bool { if len(f[j].Names()) == 0 { return false } else if len(f[i].Names()) == 0 { return true } return lexicographicLess(f[i].Names()[0], f[j].Names()[0]) } func (f FlagsByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } // ActionableFlag is an interface that wraps Flag interface and RunAction operation. type ActionableFlag interface { Flag RunAction(*Context) error } // Flag is a common interface related to parsing flags in cli. // For more advanced flag parsing techniques, it is recommended that // this interface be implemented. type Flag interface { fmt.Stringer // Apply Flag settings to the given flag set Apply(*flag.FlagSet) error Names() []string IsSet() bool } // RequiredFlag is an interface that allows us to mark flags as required // it allows flags required flags to be backwards compatible with the Flag interface type RequiredFlag interface { Flag IsRequired() bool } // DocGenerationFlag is an interface that allows documentation generation for the flag type DocGenerationFlag interface { Flag // TakesValue returns true if the flag takes a value, otherwise false TakesValue() bool // GetUsage returns the usage string for the flag GetUsage() string // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. GetValue() string // GetDefaultText returns the default text for this flag GetDefaultText() string // GetEnvVars returns the env vars for this flag GetEnvVars() []string } // DocGenerationSliceFlag extends DocGenerationFlag for slice-based flags. type DocGenerationSliceFlag interface { DocGenerationFlag // IsSliceFlag returns true for flags that can be given multiple times. IsSliceFlag() bool } // VisibleFlag is an interface that allows to check if a flag is visible type VisibleFlag interface { Flag // IsVisible returns true if the flag is not hidden, otherwise false IsVisible() bool } // CategorizableFlag is an interface that allows us to potentially // use a flag in a categorized representation. type CategorizableFlag interface { VisibleFlag GetCategory() string } // Countable is an interface to enable detection of flag values which support // repetitive flags type Countable interface { Count() int } func flagSet(name string, flags []Flag, spec separatorSpec) (*flag.FlagSet, error) { set := flag.NewFlagSet(name, flag.ContinueOnError) for _, f := range flags { if c, ok := f.(customizedSeparator); ok { c.WithSeparatorSpec(spec) } if err := f.Apply(set); err != nil { return nil, err } } set.SetOutput(io.Discard) return set, nil } func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { switch ff.Value.(type) { case Serializer: _ = set.Set(name, ff.Value.(Serializer).Serialize()) default: _ = set.Set(name, ff.Value.String()) } } func normalizeFlags(flags []Flag, set *flag.FlagSet) error { visited := make(map[string]bool) set.Visit(func(f *flag.Flag) { visited[f.Name] = true }) for _, f := range flags { parts := f.Names() if len(parts) == 1 { continue } var ff *flag.Flag for _, name := range parts { name = strings.Trim(name, " ") if visited[name] { if ff != nil { return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) } ff = set.Lookup(name) } } if ff == nil { continue } for _, name := range parts { name = strings.Trim(name, " ") if !visited[name] { copyFlag(name, ff, set) } } } return nil } func visibleFlags(fl []Flag) []Flag { var visible []Flag for _, f := range fl { if vf, ok := f.(VisibleFlag); ok && vf.IsVisible() { visible = append(visible, f) } } return visible } func prefixFor(name string) (prefix string) { if len(name) == 1 { prefix = "-" } else { prefix = "--" } return } // Returns the placeholder, if any, and the unquoted usage string. func unquoteUsage(usage string) (string, string) { for i := 0; i < len(usage); i++ { if usage[i] == '`' { for j := i + 1; j < len(usage); j++ { if usage[j] == '`' { name := usage[i+1 : j] usage = usage[:i] + name + usage[j+1:] return name, usage } } break } } return "", usage } func prefixedNames(names []string, placeholder string) string { var prefixed string for i, name := range names { if name == "" { continue } prefixed += prefixFor(name) + name if placeholder != "" { prefixed += " " + placeholder } if i < len(names)-1 { prefixed += ", " } } return prefixed } func envFormat(envVars []string, prefix, sep, suffix string) string { if len(envVars) > 0 { return fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(envVars, sep), suffix) } return "" } func defaultEnvFormat(envVars []string) string { return envFormat(envVars, "$", ", $", "") } func withEnvHint(envVars []string, str string) string { envText := "" if runtime.GOOS != "windows" || os.Getenv("PSHOME") != "" { envText = defaultEnvFormat(envVars) } else { envText = envFormat(envVars, "%", "%, %", "%") } return str + envText } func FlagNames(name string, aliases []string) []string { var ret []string for _, part := range append([]string{name}, aliases...) { // v1 -> v2 migration warning zone: // Strip off anything after the first found comma or space, which // *hopefully* makes it a tiny bit more obvious that unexpected behavior is // caused by using the v1 form of stringly typed "Name". ret = append(ret, commaWhitespace.ReplaceAllString(part, "")) } return ret } func withFileHint(filePath, str string) string { fileText := "" if filePath != "" { fileText = fmt.Sprintf(" [%s]", filePath) } return str + fileText } func formatDefault(format string) string { return " (default: " + format + ")" } func stringifyFlag(f Flag) string { // enforce DocGeneration interface on flags to avoid reflection df, ok := f.(DocGenerationFlag) if !ok { return "" } placeholder, usage := unquoteUsage(df.GetUsage()) needsPlaceholder := df.TakesValue() if needsPlaceholder && placeholder == "" { placeholder = defaultPlaceholder } defaultValueString := "" // set default text for all flags except bool flags // for bool flags display default text if DisableDefaultText is not // set if bf, ok := f.(*BoolFlag); !ok || !bf.DisableDefaultText { if s := df.GetDefaultText(); s != "" { defaultValueString = fmt.Sprintf(formatDefault("%s"), s) } } usageWithDefault := strings.TrimSpace(usage + defaultValueString) pn := prefixedNames(df.Names(), placeholder) sliceFlag, ok := f.(DocGenerationSliceFlag) if ok && sliceFlag.IsSliceFlag() { pn = pn + " [ " + pn + " ]" } return withEnvHint(df.GetEnvVars(), fmt.Sprintf("%s\t%s", pn, usageWithDefault)) } func hasFlag(flags []Flag, fl Flag) bool { for _, existing := range flags { if fl == existing { return true } } return false } // Return the first value from a list of environment variables and files // (which may or may not exist), a description of where the value was found, // and a boolean which is true if a value was found. func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhere string, found bool) { for _, envVar := range envVars { envVar = strings.TrimSpace(envVar) if value, found := syscall.Getenv(envVar); found { return value, fmt.Sprintf("environment variable %q", envVar), true } } for _, fileVar := range strings.Split(filePath, ",") { if fileVar != "" { if data, err := os.ReadFile(fileVar); err == nil { return string(data), fmt.Sprintf("file %q", filePath), true } } } return "", "", false } type customizedSeparator interface { WithSeparatorSpec(separatorSpec) } type separatorSpec struct { sep string disabled bool customized bool } func (s separatorSpec) flagSplitMultiValues(val string) []string { var ( disabled bool = s.disabled sep string = s.sep ) if !s.customized { disabled = disableSliceFlagSeparator sep = defaultSliceFlagSeparator } if disabled { return []string{val} } return strings.Split(val, sep) } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_bool.go000066400000000000000000000074671504213346300233210ustar00rootroot00000000000000package cli import ( "errors" "flag" "fmt" "strconv" ) // boolValue needs to implement the boolFlag internal interface in flag // to be able to capture bool fields and values // // type boolFlag interface { // Value // IsBoolFlag() bool // } type boolValue struct { destination *bool count *int } func newBoolValue(val bool, p *bool, count *int) *boolValue { *p = val return &boolValue{ destination: p, count: count, } } func (b *boolValue) Set(s string) error { v, err := strconv.ParseBool(s) if err != nil { err = errors.New("parse error") return err } *b.destination = v if b.count != nil { *b.count = *b.count + 1 } return err } func (b *boolValue) Get() interface{} { return *b.destination } func (b *boolValue) String() string { if b.destination != nil { return strconv.FormatBool(*b.destination) } return strconv.FormatBool(false) } func (b *boolValue) IsBoolFlag() bool { return true } func (b *boolValue) Count() int { if b.count != nil && *b.count > 0 { return *b.count } return 0 } // TakesValue returns true of the flag takes a value, otherwise false func (f *BoolFlag) TakesValue() bool { return false } // GetUsage returns the usage string for the flag func (f *BoolFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *BoolFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *BoolFlag) GetValue() string { return "" } // GetDefaultText returns the default text for this flag func (f *BoolFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } if f.defaultValueSet { return fmt.Sprintf("%v", f.defaultValue) } return fmt.Sprintf("%v", f.Value) } // GetEnvVars returns the env vars for this flag func (f *BoolFlag) GetEnvVars() []string { return f.EnvVars } // RunAction executes flag action if set func (f *BoolFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Bool(f.Name)) } return nil } // Apply populates the flag given the flag set and environment func (f *BoolFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valBool, err := strconv.ParseBool(val) if err != nil { return fmt.Errorf("could not parse %q as bool value from %s for flag %s: %s", val, source, f.Name, err) } f.Value = valBool } else { // empty value implies that the env is defined but set to empty string, we have to assume that this is // what the user wants. If user doesnt want this then the env needs to be deleted or the flag removed from // file f.Value = false } f.HasBeenSet = true } count := f.Count dest := f.Destination if count == nil { count = new(int) } // since count will be incremented for each alias as well // subtract number of aliases from overall count *count -= len(f.Aliases) if dest == nil { dest = new(bool) } for _, name := range f.Names() { value := newBoolValue(f.Value, dest, count) set.Var(value, name, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *BoolFlag) Get(ctx *Context) bool { return ctx.Bool(f.Name) } // Bool looks up the value of a local BoolFlag, returns // false if not found func (cCtx *Context) Bool(name string) bool { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupBool(name, fs) } return false } func lookupBool(name string, set *flag.FlagSet) bool { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseBool(f.Value.String()) if err != nil { return false } return parsed } return false } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_duration.go000066400000000000000000000047221504213346300242020ustar00rootroot00000000000000package cli import ( "flag" "fmt" "time" ) // TakesValue returns true of the flag takes a value, otherwise false func (f *DurationFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *DurationFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *DurationFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *DurationFlag) GetValue() string { return f.Value.String() } // GetDefaultText returns the default text for this flag func (f *DurationFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } if f.defaultValueSet { return f.defaultValue.String() } return f.Value.String() } // GetEnvVars returns the env vars for this flag func (f *DurationFlag) GetEnvVars() []string { return f.EnvVars } // Apply populates the flag given the flag set and environment func (f *DurationFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valDuration, err := time.ParseDuration(val) if err != nil { return fmt.Errorf("could not parse %q as duration value from %s for flag %s: %s", val, source, f.Name, err) } f.Value = valDuration f.HasBeenSet = true } } for _, name := range f.Names() { if f.Destination != nil { set.DurationVar(f.Destination, name, f.Value, f.Usage) continue } set.Duration(name, f.Value, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *DurationFlag) Get(ctx *Context) time.Duration { return ctx.Duration(f.Name) } // RunAction executes flag action if set func (f *DurationFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Duration(f.Name)) } return nil } // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (cCtx *Context) Duration(name string) time.Duration { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupDuration(name, fs) } return 0 } func lookupDuration(name string, set *flag.FlagSet) time.Duration { f := set.Lookup(name) if f != nil { parsed, err := time.ParseDuration(f.Value.String()) if err != nil { return 0 } return parsed } return 0 } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_ext.go000066400000000000000000000013071504213346300231510ustar00rootroot00000000000000package cli import "flag" type extFlag struct { f *flag.Flag } func (e *extFlag) Apply(fs *flag.FlagSet) error { fs.Var(e.f.Value, e.f.Name, e.f.Usage) return nil } func (e *extFlag) Names() []string { return []string{e.f.Name} } func (e *extFlag) IsSet() bool { return false } func (e *extFlag) String() string { return FlagStringer(e) } func (e *extFlag) IsVisible() bool { return true } func (e *extFlag) TakesValue() bool { return false } func (e *extFlag) GetUsage() string { return e.f.Usage } func (e *extFlag) GetValue() string { return e.f.Value.String() } func (e *extFlag) GetDefaultText() string { return e.f.DefValue } func (e *extFlag) GetEnvVars() []string { return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_float64.go000066400000000000000000000046111504213346300236310ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // TakesValue returns true of the flag takes a value, otherwise false func (f *Float64Flag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *Float64Flag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *Float64Flag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Float64Flag) GetValue() string { return fmt.Sprintf("%v", f.Value) } // GetDefaultText returns the default text for this flag func (f *Float64Flag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } if f.defaultValueSet { return fmt.Sprintf("%v", f.defaultValue) } return fmt.Sprintf("%v", f.Value) } // GetEnvVars returns the env vars for this flag func (f *Float64Flag) GetEnvVars() []string { return f.EnvVars } // Apply populates the flag given the flag set and environment func (f *Float64Flag) Apply(set *flag.FlagSet) error { f.defaultValue = f.Value f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valFloat, err := strconv.ParseFloat(val, 64) if err != nil { return fmt.Errorf("could not parse %q as float64 value from %s for flag %s: %s", val, source, f.Name, err) } f.Value = valFloat f.HasBeenSet = true } } for _, name := range f.Names() { if f.Destination != nil { set.Float64Var(f.Destination, name, f.Value, f.Usage) continue } set.Float64(name, f.Value, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *Float64Flag) Get(ctx *Context) float64 { return ctx.Float64(f.Name) } // RunAction executes flag action if set func (f *Float64Flag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Float64(f.Name)) } return nil } // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (cCtx *Context) Float64(name string) float64 { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupFloat64(name, fs) } return 0 } func lookupFloat64(name string, set *flag.FlagSet) float64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseFloat(f.Value.String(), 64) if err != nil { return 0 } return parsed } return 0 } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_float64_slice.go000066400000000000000000000125561504213346300250170ustar00rootroot00000000000000package cli import ( "encoding/json" "flag" "fmt" "strconv" "strings" ) // Float64Slice wraps []float64 to satisfy flag.Value type Float64Slice struct { slice []float64 separator separatorSpec hasBeenSet bool } // NewFloat64Slice makes a *Float64Slice with default values func NewFloat64Slice(defaults ...float64) *Float64Slice { return &Float64Slice{slice: append([]float64{}, defaults...)} } // clone allocate a copy of self object func (f *Float64Slice) clone() *Float64Slice { n := &Float64Slice{ slice: make([]float64, len(f.slice)), hasBeenSet: f.hasBeenSet, } copy(n.slice, f.slice) return n } func (f *Float64Slice) WithSeparatorSpec(spec separatorSpec) { f.separator = spec } // Set parses the value into a float64 and appends it to the list of values func (f *Float64Slice) Set(value string) error { if !f.hasBeenSet { f.slice = []float64{} f.hasBeenSet = true } if strings.HasPrefix(value, slPfx) { // Deserializing assumes overwrite _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &f.slice) f.hasBeenSet = true return nil } for _, s := range f.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64) if err != nil { return err } f.slice = append(f.slice, tmp) } return nil } // String returns a readable representation of this value (for usage defaults) func (f *Float64Slice) String() string { v := f.slice if v == nil { // treat nil the same as zero length non-nil v = make([]float64, 0) } return fmt.Sprintf("%#v", v) } // Serialize allows Float64Slice to fulfill Serializer func (f *Float64Slice) Serialize() string { jsonBytes, _ := json.Marshal(f.slice) return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) } // Value returns the slice of float64s set by this flag func (f *Float64Slice) Value() []float64 { return f.slice } // Get returns the slice of float64s set by this flag func (f *Float64Slice) Get() interface{} { return *f } // String returns a readable representation of this value // (for usage defaults) func (f *Float64SliceFlag) String() string { return FlagStringer(f) } // TakesValue returns true if the flag takes a value, otherwise false func (f *Float64SliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *Float64SliceFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *Float64SliceFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Float64SliceFlag) GetValue() string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), ".")) } } return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag func (f *Float64SliceFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } return f.GetValue() } // GetEnvVars returns the env vars for this flag func (f *Float64SliceFlag) GetEnvVars() []string { return f.EnvVars } // IsSliceFlag implements DocGenerationSliceFlag. func (f *Float64SliceFlag) IsSliceFlag() bool { return true } // Apply populates the flag given the flag set and environment func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { // apply any default if f.Destination != nil && f.Value != nil { f.Destination.slice = make([]float64, len(f.Value.slice)) copy(f.Destination.slice, f.Value.slice) } // resolve setValue (what we will assign to the set) var setValue *Float64Slice switch { case f.Destination != nil: setValue = f.Destination case f.Value != nil: setValue = f.Value.clone() default: setValue = new(Float64Slice) setValue.WithSeparatorSpec(f.separator) } if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as float64 slice value from %s for flag %s: %s", val, source, f.Name, err) } } // Set this to false so that we reset the slice if we then set values from // flags that have already been set by the environment. setValue.hasBeenSet = false f.HasBeenSet = true } } for _, name := range f.Names() { set.Var(setValue, name, f.Usage) } return nil } func (f *Float64SliceFlag) WithSeparatorSpec(spec separatorSpec) { f.separator = spec } // Get returns the flag’s value in the given Context. func (f *Float64SliceFlag) Get(ctx *Context) []float64 { return ctx.Float64Slice(f.Name) } // RunAction executes flag action if set func (f *Float64SliceFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Float64Slice(f.Name)) } return nil } // Float64Slice looks up the value of a local Float64SliceFlag, returns // nil if not found func (cCtx *Context) Float64Slice(name string) []float64 { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupFloat64Slice(name, fs) } return nil } func lookupFloat64Slice(name string, set *flag.FlagSet) []float64 { f := set.Lookup(name) if f != nil { if slice, ok := unwrapFlagValue(f.Value).(*Float64Slice); ok { return slice.Value() } } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_generic.go000066400000000000000000000054331504213346300237710ustar00rootroot00000000000000package cli import ( "flag" "fmt" ) // Generic is a generic parseable type identified by a specific flag type Generic interface { Set(value string) error String() string } type stringGeneric struct { value string } func (s *stringGeneric) Set(value string) error { s.value = value return nil } func (s *stringGeneric) String() string { return s.value } // TakesValue returns true of the flag takes a value, otherwise false func (f *GenericFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *GenericFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *GenericFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *GenericFlag) GetValue() string { if f.Value != nil { return f.Value.String() } return "" } // GetDefaultText returns the default text for this flag func (f *GenericFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } val := f.Value if f.defaultValueSet { val = f.defaultValue } if val != nil { return val.String() } return "" } // GetEnvVars returns the env vars for this flag func (f *GenericFlag) GetEnvVars() []string { return f.EnvVars } // Apply takes the flagset and calls Set on the generic flag with the value // provided by the user for parsing by the flag func (f *GenericFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it if f.Value != nil { f.defaultValue = &stringGeneric{value: f.Value.String()} f.defaultValueSet = true } if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { if err := f.Value.Set(val); err != nil { return fmt.Errorf("could not parse %q from %s as value for flag %s: %s", val, source, f.Name, err) } f.HasBeenSet = true } } for _, name := range f.Names() { if f.Destination != nil { set.Var(f.Destination, name, f.Usage) continue } set.Var(f.Value, name, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *GenericFlag) Get(ctx *Context) interface{} { return ctx.Generic(f.Name) } // RunAction executes flag action if set func (f *GenericFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Generic(f.Name)) } return nil } // Generic looks up the value of a local GenericFlag, returns // nil if not found func (cCtx *Context) Generic(name string) interface{} { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupGeneric(name, fs) } return nil } func lookupGeneric(name string, set *flag.FlagSet) interface{} { if f := set.Lookup(name); f != nil { return f.Value } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_int.go000066400000000000000000000046061504213346300231500ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // TakesValue returns true of the flag takes a value, otherwise false func (f *IntFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *IntFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *IntFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *IntFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } // GetDefaultText returns the default text for this flag func (f *IntFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } if f.defaultValueSet { return fmt.Sprintf("%d", f.defaultValue) } return fmt.Sprintf("%d", f.Value) } // GetEnvVars returns the env vars for this flag func (f *IntFlag) GetEnvVars() []string { return f.EnvVars } // Apply populates the flag given the flag set and environment func (f *IntFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valInt, err := strconv.ParseInt(val, f.Base, 64) if err != nil { return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err) } f.Value = int(valInt) f.HasBeenSet = true } } for _, name := range f.Names() { if f.Destination != nil { set.IntVar(f.Destination, name, f.Value, f.Usage) continue } set.Int(name, f.Value, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *IntFlag) Get(ctx *Context) int { return ctx.Int(f.Name) } // RunAction executes flag action if set func (f *IntFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Int(f.Name)) } return nil } // Int looks up the value of a local IntFlag, returns // 0 if not found func (cCtx *Context) Int(name string) int { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt(name, fs) } return 0 } func lookupInt(name string, set *flag.FlagSet) int { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) if err != nil { return 0 } return int(parsed) } return 0 } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_int64.go000066400000000000000000000046451504213346300233250ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // TakesValue returns true of the flag takes a value, otherwise false func (f *Int64Flag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *Int64Flag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *Int64Flag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Int64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } // GetDefaultText returns the default text for this flag func (f *Int64Flag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } if f.defaultValueSet { return fmt.Sprintf("%d", f.defaultValue) } return fmt.Sprintf("%d", f.Value) } // GetEnvVars returns the env vars for this flag func (f *Int64Flag) GetEnvVars() []string { return f.EnvVars } // Apply populates the flag given the flag set and environment func (f *Int64Flag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valInt, err := strconv.ParseInt(val, f.Base, 64) if err != nil { return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err) } f.Value = valInt f.HasBeenSet = true } } for _, name := range f.Names() { if f.Destination != nil { set.Int64Var(f.Destination, name, f.Value, f.Usage) continue } set.Int64(name, f.Value, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *Int64Flag) Get(ctx *Context) int64 { return ctx.Int64(f.Name) } // RunAction executes flag action if set func (f *Int64Flag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Int64(f.Name)) } return nil } // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (cCtx *Context) Int64(name string) int64 { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt64(name, fs) } return 0 } func lookupInt64(name string, set *flag.FlagSet) int64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) if err != nil { return 0 } return parsed } return 0 } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_int64_slice.go000066400000000000000000000122771504213346300245040ustar00rootroot00000000000000package cli import ( "encoding/json" "flag" "fmt" "strconv" "strings" ) // Int64Slice wraps []int64 to satisfy flag.Value type Int64Slice struct { slice []int64 separator separatorSpec hasBeenSet bool } // NewInt64Slice makes an *Int64Slice with default values func NewInt64Slice(defaults ...int64) *Int64Slice { return &Int64Slice{slice: append([]int64{}, defaults...)} } // clone allocate a copy of self object func (i *Int64Slice) clone() *Int64Slice { n := &Int64Slice{ slice: make([]int64, len(i.slice)), hasBeenSet: i.hasBeenSet, } copy(n.slice, i.slice) return n } func (i *Int64Slice) WithSeparatorSpec(spec separatorSpec) { i.separator = spec } // Set parses the value into an integer and appends it to the list of values func (i *Int64Slice) Set(value string) error { if !i.hasBeenSet { i.slice = []int64{} i.hasBeenSet = true } if strings.HasPrefix(value, slPfx) { // Deserializing assumes overwrite _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice) i.hasBeenSet = true return nil } for _, s := range i.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) if err != nil { return err } i.slice = append(i.slice, tmp) } return nil } // String returns a readable representation of this value (for usage defaults) func (i *Int64Slice) String() string { v := i.slice if v == nil { // treat nil the same as zero length non-nil v = make([]int64, 0) } return fmt.Sprintf("%#v", v) } // Serialize allows Int64Slice to fulfill Serializer func (i *Int64Slice) Serialize() string { jsonBytes, _ := json.Marshal(i.slice) return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) } // Value returns the slice of ints set by this flag func (i *Int64Slice) Value() []int64 { return i.slice } // Get returns the slice of ints set by this flag func (i *Int64Slice) Get() interface{} { return *i } // String returns a readable representation of this value // (for usage defaults) func (f *Int64SliceFlag) String() string { return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false func (f *Int64SliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *Int64SliceFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *Int64SliceFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Int64SliceFlag) GetValue() string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strconv.FormatInt(i, 10)) } } return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag func (f *Int64SliceFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } return f.GetValue() } // GetEnvVars returns the env vars for this flag func (f *Int64SliceFlag) GetEnvVars() []string { return f.EnvVars } // IsSliceFlag implements DocGenerationSliceFlag. func (f *Int64SliceFlag) IsSliceFlag() bool { return true } // Apply populates the flag given the flag set and environment func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { // apply any default if f.Destination != nil && f.Value != nil { f.Destination.slice = make([]int64, len(f.Value.slice)) copy(f.Destination.slice, f.Value.slice) } // resolve setValue (what we will assign to the set) var setValue *Int64Slice switch { case f.Destination != nil: setValue = f.Destination case f.Value != nil: setValue = f.Value.clone() default: setValue = new(Int64Slice) setValue.WithSeparatorSpec(f.separator) } if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as int64 slice value from %s for flag %s: %s", val, source, f.Name, err) } } // Set this to false so that we reset the slice if we then set values from // flags that have already been set by the environment. setValue.hasBeenSet = false f.HasBeenSet = true } for _, name := range f.Names() { set.Var(setValue, name, f.Usage) } return nil } func (f *Int64SliceFlag) WithSeparatorSpec(spec separatorSpec) { f.separator = spec } // Get returns the flag’s value in the given Context. func (f *Int64SliceFlag) Get(ctx *Context) []int64 { return ctx.Int64Slice(f.Name) } // RunAction executes flag action if set func (f *Int64SliceFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Int64Slice(f.Name)) } return nil } // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (cCtx *Context) Int64Slice(name string) []int64 { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt64Slice(name, fs) } return nil } func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { f := set.Lookup(name) if f != nil { if slice, ok := unwrapFlagValue(f.Value).(*Int64Slice); ok { return slice.Value() } } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_int_slice.go000066400000000000000000000125431504213346300243260ustar00rootroot00000000000000package cli import ( "encoding/json" "flag" "fmt" "strconv" "strings" ) // IntSlice wraps []int to satisfy flag.Value type IntSlice struct { slice []int separator separatorSpec hasBeenSet bool } // NewIntSlice makes an *IntSlice with default values func NewIntSlice(defaults ...int) *IntSlice { return &IntSlice{slice: append([]int{}, defaults...)} } // clone allocate a copy of self object func (i *IntSlice) clone() *IntSlice { n := &IntSlice{ slice: make([]int, len(i.slice)), hasBeenSet: i.hasBeenSet, } copy(n.slice, i.slice) return n } // TODO: Consistently have specific Set function for Int64 and Float64 ? // SetInt directly adds an integer to the list of values func (i *IntSlice) SetInt(value int) { if !i.hasBeenSet { i.slice = []int{} i.hasBeenSet = true } i.slice = append(i.slice, value) } func (i *IntSlice) WithSeparatorSpec(spec separatorSpec) { i.separator = spec } // Set parses the value into an integer and appends it to the list of values func (i *IntSlice) Set(value string) error { if !i.hasBeenSet { i.slice = []int{} i.hasBeenSet = true } if strings.HasPrefix(value, slPfx) { // Deserializing assumes overwrite _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice) i.hasBeenSet = true return nil } for _, s := range i.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) if err != nil { return err } i.slice = append(i.slice, int(tmp)) } return nil } // String returns a readable representation of this value (for usage defaults) func (i *IntSlice) String() string { v := i.slice if v == nil { // treat nil the same as zero length non-nil v = make([]int, 0) } return fmt.Sprintf("%#v", v) } // Serialize allows IntSlice to fulfill Serializer func (i *IntSlice) Serialize() string { jsonBytes, _ := json.Marshal(i.slice) return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) } // Value returns the slice of ints set by this flag func (i *IntSlice) Value() []int { return i.slice } // Get returns the slice of ints set by this flag func (i *IntSlice) Get() interface{} { return *i } // String returns a readable representation of this value // (for usage defaults) func (f *IntSliceFlag) String() string { return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false func (f *IntSliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *IntSliceFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *IntSliceFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *IntSliceFlag) GetValue() string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strconv.Itoa(i)) } } return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag func (f *IntSliceFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } return f.GetValue() } // GetEnvVars returns the env vars for this flag func (f *IntSliceFlag) GetEnvVars() []string { return f.EnvVars } // IsSliceFlag implements DocGenerationSliceFlag. func (f *IntSliceFlag) IsSliceFlag() bool { return true } // Apply populates the flag given the flag set and environment func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { // apply any default if f.Destination != nil && f.Value != nil { f.Destination.slice = make([]int, len(f.Value.slice)) copy(f.Destination.slice, f.Value.slice) } // resolve setValue (what we will assign to the set) var setValue *IntSlice switch { case f.Destination != nil: setValue = f.Destination case f.Value != nil: setValue = f.Value.clone() default: setValue = new(IntSlice) setValue.WithSeparatorSpec(f.separator) } if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as int slice value from %s for flag %s: %s", val, source, f.Name, err) } } // Set this to false so that we reset the slice if we then set values from // flags that have already been set by the environment. setValue.hasBeenSet = false f.HasBeenSet = true } for _, name := range f.Names() { set.Var(setValue, name, f.Usage) } return nil } func (f *IntSliceFlag) WithSeparatorSpec(spec separatorSpec) { f.separator = spec } // Get returns the flag’s value in the given Context. func (f *IntSliceFlag) Get(ctx *Context) []int { return ctx.IntSlice(f.Name) } // RunAction executes flag action if set func (f *IntSliceFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.IntSlice(f.Name)) } return nil } // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (cCtx *Context) IntSlice(name string) []int { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupIntSlice(name, fs) } return nil } func lookupIntSlice(name string, set *flag.FlagSet) []int { f := set.Lookup(name) if f != nil { if slice, ok := unwrapFlagValue(f.Value).(*IntSlice); ok { return slice.Value() } } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_path.go000066400000000000000000000042021504213346300233020ustar00rootroot00000000000000package cli import ( "flag" "fmt" ) type Path = string // TakesValue returns true of the flag takes a value, otherwise false func (f *PathFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *PathFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *PathFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *PathFlag) GetValue() string { return f.Value } // GetDefaultText returns the default text for this flag func (f *PathFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } val := f.Value if f.defaultValueSet { val = f.defaultValue } if val == "" { return val } return fmt.Sprintf("%q", val) } // GetEnvVars returns the env vars for this flag func (f *PathFlag) GetEnvVars() []string { return f.EnvVars } // Apply populates the flag given the flag set and environment func (f *PathFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value f.defaultValueSet = true if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { f.Value = val f.HasBeenSet = true } for _, name := range f.Names() { if f.Destination != nil { set.StringVar(f.Destination, name, f.Value, f.Usage) continue } set.String(name, f.Value, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *PathFlag) Get(ctx *Context) string { return ctx.Path(f.Name) } // RunAction executes flag action if set func (f *PathFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Path(f.Name)) } return nil } // Path looks up the value of a local PathFlag, returns // "" if not found func (cCtx *Context) Path(name string) string { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupPath(name, fs) } return "" } func lookupPath(name string, set *flag.FlagSet) string { if f := set.Lookup(name); f != nil { return f.Value.String() } return "" } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_string.go000066400000000000000000000042161504213346300236610ustar00rootroot00000000000000package cli import ( "flag" "fmt" ) // TakesValue returns true of the flag takes a value, otherwise false func (f *StringFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *StringFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *StringFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *StringFlag) GetValue() string { return f.Value } // GetDefaultText returns the default text for this flag func (f *StringFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } val := f.Value if f.defaultValueSet { val = f.defaultValue } if val == "" { return val } return fmt.Sprintf("%q", val) } // GetEnvVars returns the env vars for this flag func (f *StringFlag) GetEnvVars() []string { return f.EnvVars } // Apply populates the flag given the flag set and environment func (f *StringFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value f.defaultValueSet = true if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { f.Value = val f.HasBeenSet = true } for _, name := range f.Names() { if f.Destination != nil { set.StringVar(f.Destination, name, f.Value, f.Usage) continue } set.String(name, f.Value, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *StringFlag) Get(ctx *Context) string { return ctx.String(f.Name) } // RunAction executes flag action if set func (f *StringFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.String(f.Name)) } return nil } // String looks up the value of a local StringFlag, returns // "" if not found func (cCtx *Context) String(name string) string { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupString(name, fs) } return "" } func lookupString(name string, set *flag.FlagSet) string { if f := set.Lookup(name); f != nil { return f.Value.String() } return "" } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_string_slice.go000066400000000000000000000122571504213346300250440ustar00rootroot00000000000000package cli import ( "encoding/json" "flag" "fmt" "strconv" "strings" ) // StringSlice wraps a []string to satisfy flag.Value type StringSlice struct { slice []string separator separatorSpec hasBeenSet bool keepSpace bool } // NewStringSlice creates a *StringSlice with default values func NewStringSlice(defaults ...string) *StringSlice { return &StringSlice{slice: append([]string{}, defaults...)} } // clone allocate a copy of self object func (s *StringSlice) clone() *StringSlice { n := &StringSlice{ slice: make([]string, len(s.slice)), hasBeenSet: s.hasBeenSet, } copy(n.slice, s.slice) return n } // Set appends the string value to the list of values func (s *StringSlice) Set(value string) error { if !s.hasBeenSet { s.slice = []string{} s.hasBeenSet = true } if strings.HasPrefix(value, slPfx) { // Deserializing assumes overwrite _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &s.slice) s.hasBeenSet = true return nil } for _, t := range s.separator.flagSplitMultiValues(value) { if !s.keepSpace { t = strings.TrimSpace(t) } s.slice = append(s.slice, t) } return nil } func (s *StringSlice) WithSeparatorSpec(spec separatorSpec) { s.separator = spec } // String returns a readable representation of this value (for usage defaults) func (s *StringSlice) String() string { return fmt.Sprintf("%s", s.slice) } // Serialize allows StringSlice to fulfill Serializer func (s *StringSlice) Serialize() string { jsonBytes, _ := json.Marshal(s.slice) return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) } // Value returns the slice of strings set by this flag func (s *StringSlice) Value() []string { return s.slice } // Get returns the slice of strings set by this flag func (s *StringSlice) Get() interface{} { return *s } // String returns a readable representation of this value // (for usage defaults) func (f *StringSliceFlag) String() string { return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false func (f *StringSliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *StringSliceFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *StringSliceFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *StringSliceFlag) GetValue() string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, s := range f.Value.Value() { if len(s) > 0 { defaultVals = append(defaultVals, strconv.Quote(s)) } } } return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag func (f *StringSliceFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } return f.GetValue() } // GetEnvVars returns the env vars for this flag func (f *StringSliceFlag) GetEnvVars() []string { return f.EnvVars } // IsSliceFlag implements DocGenerationSliceFlag. func (f *StringSliceFlag) IsSliceFlag() bool { return true } // Apply populates the flag given the flag set and environment func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { // apply any default if f.Destination != nil && f.Value != nil { f.Destination.slice = make([]string, len(f.Value.slice)) copy(f.Destination.slice, f.Value.slice) } // resolve setValue (what we will assign to the set) var setValue *StringSlice switch { case f.Destination != nil: setValue = f.Destination case f.Value != nil: setValue = f.Value.clone() default: setValue = new(StringSlice) } setValue.WithSeparatorSpec(f.separator) setValue.keepSpace = f.KeepSpace if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { for _, s := range f.separator.flagSplitMultiValues(val) { if !f.KeepSpace { s = strings.TrimSpace(s) } if err := setValue.Set(s); err != nil { return fmt.Errorf("could not parse %q as string value from %s for flag %s: %s", val, source, f.Name, err) } } // Set this to false so that we reset the slice if we then set values from // flags that have already been set by the environment. setValue.hasBeenSet = false f.HasBeenSet = true } for _, name := range f.Names() { set.Var(setValue, name, f.Usage) } return nil } func (f *StringSliceFlag) WithSeparatorSpec(spec separatorSpec) { f.separator = spec } // Get returns the flag’s value in the given Context. func (f *StringSliceFlag) Get(ctx *Context) []string { return ctx.StringSlice(f.Name) } // RunAction executes flag action if set func (f *StringSliceFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.StringSlice(f.Name)) } return nil } // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (cCtx *Context) StringSlice(name string) []string { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupStringSlice(name, fs) } return nil } func lookupStringSlice(name string, set *flag.FlagSet) []string { f := set.Lookup(name) if f != nil { if slice, ok := unwrapFlagValue(f.Value).(*StringSlice); ok { return slice.Value() } } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_timestamp.go000066400000000000000000000106201504213346300243520ustar00rootroot00000000000000package cli import ( "flag" "fmt" "time" ) // Timestamp wrap to satisfy golang's flag interface. type Timestamp struct { timestamp *time.Time hasBeenSet bool layout string location *time.Location } // Timestamp constructor func NewTimestamp(timestamp time.Time) *Timestamp { return &Timestamp{timestamp: ×tamp} } // Set the timestamp value directly func (t *Timestamp) SetTimestamp(value time.Time) { if !t.hasBeenSet { t.timestamp = &value t.hasBeenSet = true } } // Set the timestamp string layout for future parsing func (t *Timestamp) SetLayout(layout string) { t.layout = layout } // Set perceived timezone of the to-be parsed time string func (t *Timestamp) SetLocation(loc *time.Location) { t.location = loc } // Parses the string value to timestamp func (t *Timestamp) Set(value string) error { var timestamp time.Time var err error if t.location != nil { timestamp, err = time.ParseInLocation(t.layout, value, t.location) } else { timestamp, err = time.Parse(t.layout, value) } if err != nil { return err } t.timestamp = ×tamp t.hasBeenSet = true return nil } // String returns a readable representation of this value (for usage defaults) func (t *Timestamp) String() string { return fmt.Sprintf("%#v", t.timestamp) } // Value returns the timestamp value stored in the flag func (t *Timestamp) Value() *time.Time { return t.timestamp } // Get returns the flag structure func (t *Timestamp) Get() interface{} { return *t } // clone timestamp func (t *Timestamp) clone() *Timestamp { tc := &Timestamp{ timestamp: nil, hasBeenSet: t.hasBeenSet, layout: t.layout, location: nil, } if t.timestamp != nil { tts := *t.timestamp tc.timestamp = &tts } if t.location != nil { loc := *t.location tc.location = &loc } return tc } // TakesValue returns true of the flag takes a value, otherwise false func (f *TimestampFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *TimestampFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *TimestampFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *TimestampFlag) GetValue() string { if f.Value != nil && f.Value.timestamp != nil { return f.Value.timestamp.String() } return "" } // GetDefaultText returns the default text for this flag func (f *TimestampFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } val := f.Value if f.defaultValueSet { val = f.defaultValue } if val != nil && val.timestamp != nil { return val.timestamp.String() } return "" } // GetEnvVars returns the env vars for this flag func (f *TimestampFlag) GetEnvVars() []string { return f.EnvVars } // Apply populates the flag given the flag set and environment func (f *TimestampFlag) Apply(set *flag.FlagSet) error { if f.Layout == "" { return fmt.Errorf("timestamp Layout is required") } if f.Value == nil { f.Value = &Timestamp{} } f.Value.SetLayout(f.Layout) f.Value.SetLocation(f.Timezone) f.defaultValue = f.Value.clone() f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if err := f.Value.Set(val); err != nil { return fmt.Errorf("could not parse %q as timestamp value from %s for flag %s: %s", val, source, f.Name, err) } f.HasBeenSet = true } if f.Destination != nil { *f.Destination = *f.Value } for _, name := range f.Names() { if f.Destination != nil { set.Var(f.Destination, name, f.Usage) continue } set.Var(f.Value, name, f.Usage) } return nil } // Get returns the flag’s value in the given Context. func (f *TimestampFlag) Get(ctx *Context) *time.Time { return ctx.Timestamp(f.Name) } // RunAction executes flag action if set func (f *TimestampFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Timestamp(f.Name)) } return nil } // Timestamp gets the timestamp from a flag name func (cCtx *Context) Timestamp(name string) *time.Time { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupTimestamp(name, fs) } return nil } // Fetches the timestamp value from the local timestampWrap func lookupTimestamp(name string, set *flag.FlagSet) *time.Time { f := set.Lookup(name) if f != nil { return (f.Value.(*Timestamp)).Value() } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_uint.go000066400000000000000000000046371504213346300233410ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // TakesValue returns true of the flag takes a value, otherwise false func (f *UintFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *UintFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *UintFlag) GetCategory() string { return f.Category } // Apply populates the flag given the flag set and environment func (f *UintFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valInt, err := strconv.ParseUint(val, f.Base, 64) if err != nil { return fmt.Errorf("could not parse %q as uint value from %s for flag %s: %s", val, source, f.Name, err) } f.Value = uint(valInt) f.HasBeenSet = true } } for _, name := range f.Names() { if f.Destination != nil { set.UintVar(f.Destination, name, f.Value, f.Usage) continue } set.Uint(name, f.Value, f.Usage) } return nil } // RunAction executes flag action if set func (f *UintFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Uint(f.Name)) } return nil } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *UintFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } // GetDefaultText returns the default text for this flag func (f *UintFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } if f.defaultValueSet { return fmt.Sprintf("%d", f.defaultValue) } return fmt.Sprintf("%d", f.Value) } // GetEnvVars returns the env vars for this flag func (f *UintFlag) GetEnvVars() []string { return f.EnvVars } // Get returns the flag’s value in the given Context. func (f *UintFlag) Get(ctx *Context) uint { return ctx.Uint(f.Name) } // Uint looks up the value of a local UintFlag, returns // 0 if not found func (cCtx *Context) Uint(name string) uint { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupUint(name, fs) } return 0 } func lookupUint(name string, set *flag.FlagSet) uint { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) if err != nil { return 0 } return uint(parsed) } return 0 } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_uint64.go000066400000000000000000000046771504213346300235170ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // TakesValue returns true of the flag takes a value, otherwise false func (f *Uint64Flag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *Uint64Flag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *Uint64Flag) GetCategory() string { return f.Category } // Apply populates the flag given the flag set and environment func (f *Uint64Flag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valInt, err := strconv.ParseUint(val, f.Base, 64) if err != nil { return fmt.Errorf("could not parse %q as uint64 value from %s for flag %s: %s", val, source, f.Name, err) } f.Value = valInt f.HasBeenSet = true } } for _, name := range f.Names() { if f.Destination != nil { set.Uint64Var(f.Destination, name, f.Value, f.Usage) continue } set.Uint64(name, f.Value, f.Usage) } return nil } // RunAction executes flag action if set func (f *Uint64Flag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Uint64(f.Name)) } return nil } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Uint64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } // GetDefaultText returns the default text for this flag func (f *Uint64Flag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } if f.defaultValueSet { return fmt.Sprintf("%d", f.defaultValue) } return fmt.Sprintf("%d", f.Value) } // GetEnvVars returns the env vars for this flag func (f *Uint64Flag) GetEnvVars() []string { return f.EnvVars } // Get returns the flag’s value in the given Context. func (f *Uint64Flag) Get(ctx *Context) uint64 { return ctx.Uint64(f.Name) } // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (cCtx *Context) Uint64(name string) uint64 { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupUint64(name, fs) } return 0 } func lookupUint64(name string, set *flag.FlagSet) uint64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) if err != nil { return 0 } return parsed } return 0 } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_uint64_slice.go000066400000000000000000000126311504213346300246630ustar00rootroot00000000000000package cli import ( "encoding/json" "flag" "fmt" "strconv" "strings" ) // Uint64Slice wraps []int64 to satisfy flag.Value type Uint64Slice struct { slice []uint64 separator separatorSpec hasBeenSet bool } // NewUint64Slice makes an *Uint64Slice with default values func NewUint64Slice(defaults ...uint64) *Uint64Slice { return &Uint64Slice{slice: append([]uint64{}, defaults...)} } // clone allocate a copy of self object func (i *Uint64Slice) clone() *Uint64Slice { n := &Uint64Slice{ slice: make([]uint64, len(i.slice)), hasBeenSet: i.hasBeenSet, } copy(n.slice, i.slice) return n } // Set parses the value into an integer and appends it to the list of values func (i *Uint64Slice) Set(value string) error { if !i.hasBeenSet { i.slice = []uint64{} i.hasBeenSet = true } if strings.HasPrefix(value, slPfx) { // Deserializing assumes overwrite _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice) i.hasBeenSet = true return nil } for _, s := range i.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 64) if err != nil { return err } i.slice = append(i.slice, tmp) } return nil } func (i *Uint64Slice) WithSeparatorSpec(spec separatorSpec) { i.separator = spec } // String returns a readable representation of this value (for usage defaults) func (i *Uint64Slice) String() string { v := i.slice if v == nil { // treat nil the same as zero length non-nil v = make([]uint64, 0) } str := fmt.Sprintf("%d", v) str = strings.Replace(str, " ", ", ", -1) str = strings.Replace(str, "[", "{", -1) str = strings.Replace(str, "]", "}", -1) return fmt.Sprintf("[]uint64%s", str) } // Serialize allows Uint64Slice to fulfill Serializer func (i *Uint64Slice) Serialize() string { jsonBytes, _ := json.Marshal(i.slice) return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) } // Value returns the slice of ints set by this flag func (i *Uint64Slice) Value() []uint64 { return i.slice } // Get returns the slice of ints set by this flag func (i *Uint64Slice) Get() interface{} { return *i } // String returns a readable representation of this value // (for usage defaults) func (f *Uint64SliceFlag) String() string { return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false func (f *Uint64SliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *Uint64SliceFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *Uint64SliceFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *Uint64SliceFlag) GetValue() string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strconv.FormatUint(i, 10)) } } return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag func (f *Uint64SliceFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } return f.GetValue() } // GetEnvVars returns the env vars for this flag func (f *Uint64SliceFlag) GetEnvVars() []string { return f.EnvVars } // IsSliceFlag implements DocGenerationSliceFlag. func (f *Uint64SliceFlag) IsSliceFlag() bool { return true } // Apply populates the flag given the flag set and environment func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error { // apply any default if f.Destination != nil && f.Value != nil { f.Destination.slice = make([]uint64, len(f.Value.slice)) copy(f.Destination.slice, f.Value.slice) } // resolve setValue (what we will assign to the set) var setValue *Uint64Slice switch { case f.Destination != nil: setValue = f.Destination case f.Value != nil: setValue = f.Value.clone() default: setValue = new(Uint64Slice) setValue.WithSeparatorSpec(f.separator) } if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as uint64 slice value from %s for flag %s: %s", val, source, f.Name, err) } } // Set this to false so that we reset the slice if we then set values from // flags that have already been set by the environment. setValue.hasBeenSet = false f.HasBeenSet = true } for _, name := range f.Names() { set.Var(setValue, name, f.Usage) } return nil } func (f *Uint64SliceFlag) WithSeparatorSpec(spec separatorSpec) { f.separator = spec } // Get returns the flag’s value in the given Context. func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 { return ctx.Uint64Slice(f.Name) } // RunAction executes flag action if set func (f *Uint64SliceFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.Uint64Slice(f.Name)) } return nil } // Uint64Slice looks up the value of a local Uint64SliceFlag, returns // nil if not found func (cCtx *Context) Uint64Slice(name string) []uint64 { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupUint64Slice(name, fs) } return nil } func lookupUint64Slice(name string, set *flag.FlagSet) []uint64 { f := set.Lookup(name) if f != nil { if slice, ok := unwrapFlagValue(f.Value).(*Uint64Slice); ok { return slice.Value() } } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/flag_uint_slice.go000066400000000000000000000131211504213346300245040ustar00rootroot00000000000000package cli import ( "encoding/json" "flag" "fmt" "strconv" "strings" ) // UintSlice wraps []int to satisfy flag.Value type UintSlice struct { slice []uint separator separatorSpec hasBeenSet bool } // NewUintSlice makes an *UintSlice with default values func NewUintSlice(defaults ...uint) *UintSlice { return &UintSlice{slice: append([]uint{}, defaults...)} } // clone allocate a copy of self object func (i *UintSlice) clone() *UintSlice { n := &UintSlice{ slice: make([]uint, len(i.slice)), hasBeenSet: i.hasBeenSet, } copy(n.slice, i.slice) return n } // TODO: Consistently have specific Set function for Int64 and Float64 ? // SetInt directly adds an integer to the list of values func (i *UintSlice) SetUint(value uint) { if !i.hasBeenSet { i.slice = []uint{} i.hasBeenSet = true } i.slice = append(i.slice, value) } // Set parses the value into an integer and appends it to the list of values func (i *UintSlice) Set(value string) error { if !i.hasBeenSet { i.slice = []uint{} i.hasBeenSet = true } if strings.HasPrefix(value, slPfx) { // Deserializing assumes overwrite _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice) i.hasBeenSet = true return nil } for _, s := range i.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 32) if err != nil { return err } i.slice = append(i.slice, uint(tmp)) } return nil } func (i *UintSlice) WithSeparatorSpec(spec separatorSpec) { i.separator = spec } // String returns a readable representation of this value (for usage defaults) func (i *UintSlice) String() string { v := i.slice if v == nil { // treat nil the same as zero length non-nil v = make([]uint, 0) } str := fmt.Sprintf("%d", v) str = strings.Replace(str, " ", ", ", -1) str = strings.Replace(str, "[", "{", -1) str = strings.Replace(str, "]", "}", -1) return fmt.Sprintf("[]uint%s", str) } // Serialize allows UintSlice to fulfill Serializer func (i *UintSlice) Serialize() string { jsonBytes, _ := json.Marshal(i.slice) return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) } // Value returns the slice of ints set by this flag func (i *UintSlice) Value() []uint { return i.slice } // Get returns the slice of ints set by this flag func (i *UintSlice) Get() interface{} { return *i } // String returns a readable representation of this value // (for usage defaults) func (f *UintSliceFlag) String() string { return FlagStringer(f) } // TakesValue returns true of the flag takes a value, otherwise false func (f *UintSliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f *UintSliceFlag) GetUsage() string { return f.Usage } // GetCategory returns the category for the flag func (f *UintSliceFlag) GetCategory() string { return f.Category } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *UintSliceFlag) GetValue() string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strconv.FormatUint(uint64(i), 10)) } } return strings.Join(defaultVals, ", ") } // GetDefaultText returns the default text for this flag func (f *UintSliceFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } return f.GetValue() } // GetEnvVars returns the env vars for this flag func (f *UintSliceFlag) GetEnvVars() []string { return f.EnvVars } // IsSliceFlag implements DocGenerationSliceFlag. func (f *UintSliceFlag) IsSliceFlag() bool { return true } // Apply populates the flag given the flag set and environment func (f *UintSliceFlag) Apply(set *flag.FlagSet) error { // apply any default if f.Destination != nil && f.Value != nil { f.Destination.slice = make([]uint, len(f.Value.slice)) copy(f.Destination.slice, f.Value.slice) } // resolve setValue (what we will assign to the set) var setValue *UintSlice switch { case f.Destination != nil: setValue = f.Destination case f.Value != nil: setValue = f.Value.clone() default: setValue = new(UintSlice) setValue.WithSeparatorSpec(f.separator) } if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as uint slice value from %s for flag %s: %s", val, source, f.Name, err) } } // Set this to false so that we reset the slice if we then set values from // flags that have already been set by the environment. setValue.hasBeenSet = false f.HasBeenSet = true } for _, name := range f.Names() { set.Var(setValue, name, f.Usage) } return nil } func (f *UintSliceFlag) WithSeparatorSpec(spec separatorSpec) { f.separator = spec } // Get returns the flag’s value in the given Context. func (f *UintSliceFlag) Get(ctx *Context) []uint { return ctx.UintSlice(f.Name) } // RunAction executes flag action if set func (f *UintSliceFlag) RunAction(c *Context) error { if f.Action != nil { return f.Action(c, c.UintSlice(f.Name)) } return nil } // UintSlice looks up the value of a local UintSliceFlag, returns // nil if not found func (cCtx *Context) UintSlice(name string) []uint { if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupUintSlice(name, fs) } return nil } func lookupUintSlice(name string, set *flag.FlagSet) []uint { f := set.Lookup(name) if f != nil { if slice, ok := unwrapFlagValue(f.Value).(*UintSlice); ok { return slice.Value() } } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/funcs.go000066400000000000000000000041171504213346300225000ustar00rootroot00000000000000package cli // BashCompleteFunc is an action to execute when the shell completion flag is set type BashCompleteFunc func(*Context) // BeforeFunc is an action to execute before any subcommands are run, but after // the context is ready if a non-nil error is returned, no subcommands are run type BeforeFunc func(*Context) error // AfterFunc is an action to execute after any subcommands are run, but after the // subcommand has finished it is run even if Action() panics type AfterFunc func(*Context) error // ActionFunc is the action to execute when no subcommands are specified type ActionFunc func(*Context) error // CommandNotFoundFunc is executed if the proper command cannot be found type CommandNotFoundFunc func(*Context, string) // OnUsageErrorFunc is executed if a usage error occurs. This is useful for displaying // customized usage error messages. This function is able to replace the // original error messages. If this function is not set, the "Incorrect usage" // is displayed and the execution is interrupted. type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error // InvalidFlagAccessFunc is executed when an invalid flag is accessed from the context. type InvalidFlagAccessFunc func(*Context, string) // ExitErrHandlerFunc is executed if provided in order to handle exitError values // returned by Actions and Before/After functions. type ExitErrHandlerFunc func(cCtx *Context, err error) // FlagStringFunc is used by the help generation to display a flag, which is // expected to be a single line. type FlagStringFunc func(Flag) string // FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix // text for a flag's full name. type FlagNamePrefixFunc func(fullName []string, placeholder string) string // FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help // with the environment variable details. type FlagEnvHintFunc func(envVars []string, str string) string // FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help // with the file path details. type FlagFileHintFunc func(filePath, str string) string png2svg-1.7.0/vendor/github.com/urfave/cli/v2/godoc-current.txt000066400000000000000000002530061504213346300243520ustar00rootroot00000000000000package cli // import "github.com/urfave/cli/v2" Package cli provides a minimal framework for creating and organizing command line Go applications. cli is designed to be easy to understand and write, the most simple cli application can be written as follows: func main() { (&cli.App{}).Run(os.Args) } Of course this application does not do much, so let's make this an actual application: func main() { app := &cli.App{ Name: "greet", Usage: "say a greeting", Action: func(c *cli.Context) error { fmt.Println("Greetings") return nil }, } app.Run(os.Args) } VARIABLES var ( SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate ) var AppHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} VERSION: {{.Version}}{{end}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}} {{- if len .Authors}} AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}} COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}} COPYRIGHT: {{template "copyrightTemplate" .}}{{end}} ` AppHelpTemplate is the text template for the Default help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. var CommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: {{template "usageTemplate" .}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} OPTIONS:{{template "visibleFlagTemplate" .}}{{end}} ` CommandHelpTemplate is the text template for the command help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. var ErrWriter io.Writer = os.Stderr ErrWriter is used to write errors to the user. This can be anything implementing the io.Writer interface and defaults to os.Stderr. var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' for i in (commandline -opc) if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} return 1 end end return 0 end {{ range $v := .Completions }}{{ $v }} {{ end }}` var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }} {{end}}# NAME {{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }} # SYNOPSIS {{ .App.Name }} {{ if .SynopsisArgs }} ` + "```" + ` {{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + ` {{ end }}{{ if .App.Description }} # DESCRIPTION {{ .App.Description }} {{ end }} **Usage**: ` + "```" + `{{ if .App.UsageText }} {{ .App.UsageText }} {{ else }} {{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] {{ end }}` + "```" + ` {{ if .GlobalArgs }} # GLOBAL OPTIONS {{ range $v := .GlobalArgs }} {{ $v }}{{ end }} {{ end }}{{ if .Commands }} # COMMANDS {{ range $v := .Commands }} {{ $v }}{{ end }}{{ end }}` var OsExiter = os.Exit OsExiter is the function used when the app exits. If not set defaults to os.Exit. var SubcommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: {{template "usageTemplate" .}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} OPTIONS:{{template "visibleFlagTemplate" .}}{{end}} ` SubcommandHelpTemplate is the text template for the subcommand help topic. cli.go uses text/template to render templates. You can render custom help text by setting this variable. var VersionPrinter = printVersion VersionPrinter prints the version for the App var HelpPrinter helpPrinter = printHelp HelpPrinter is a function that writes the help output. If not set explicitly, this calls HelpPrinterCustom using only the default template functions. If custom logic for printing help is required, this function can be overridden. If the ExtraInfo field is defined on an App, this function should not be modified, as HelpPrinterCustom will be used directly in order to capture the extra information. var HelpPrinterCustom helpPrinterCustom = printHelpCustom HelpPrinterCustom is a function that writes the help output. It is used as the default implementation of HelpPrinter, and may be called directly if the ExtraInfo field is set on an App. In the default implementation, if the customFuncs argument contains a "wrapAt" key, which is a function which takes no arguments and returns an int, this int value will be used to produce a "wrap" function used by the default template to wrap long lines. FUNCTIONS func DefaultAppComplete(cCtx *Context) DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) func FlagNames(name string, aliases []string) []string func HandleAction(action interface{}, cCtx *Context) (err error) HandleAction attempts to figure out which Action signature was used. If it's an ActionFunc or a func with the legacy signature for Action, the func is run! func HandleExitCoder(err error) HandleExitCoder handles errors implementing ExitCoder by printing their message and calling OsExiter with the given exit code. If the given error instead implements MultiError, each error will be checked for the ExitCoder interface, and OsExiter will be called with the last exit code found, or exit code 1 if no ExitCoder is found. This function is the default error-handling behavior for an App. func ShowAppHelp(cCtx *Context) error ShowAppHelp is an action that displays the help. func ShowAppHelpAndExit(c *Context, exitCode int) ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. func ShowCommandCompletions(ctx *Context, command string) ShowCommandCompletions prints the custom completions for a given command func ShowCommandHelp(ctx *Context, command string) error ShowCommandHelp prints help for the given command func ShowCommandHelpAndExit(c *Context, command string, code int) ShowCommandHelpAndExit - exits with code after showing help func ShowCompletions(cCtx *Context) ShowCompletions prints the lists of commands within a given context func ShowSubcommandHelp(cCtx *Context) error ShowSubcommandHelp prints help for the given subcommand func ShowSubcommandHelpAndExit(c *Context, exitCode int) ShowSubcommandHelpAndExit - Prints help for the given subcommand and exits with exit code. func ShowVersion(cCtx *Context) ShowVersion prints the version number of the App TYPES type ActionFunc func(*Context) error ActionFunc is the action to execute when no subcommands are specified type ActionableFlag interface { Flag RunAction(*Context) error } ActionableFlag is an interface that wraps Flag interface and RunAction operation. type AfterFunc func(*Context) error AfterFunc is an action to execute after any subcommands are run, but after the subcommand has finished it is run even if Action() panics type App struct { // The name of the program. Defaults to path.Base(os.Args[0]) Name string // Full name of command for help, defaults to Name HelpName string // Description of the program. Usage string // Text to override the USAGE section of help UsageText string // Whether this command supports arguments Args bool // Description of the program argument format. ArgsUsage string // Version of the program Version string // Description of the program Description string // DefaultCommand is the (optional) name of a command // to run if no command names are passed as CLI arguments. DefaultCommand string // List of commands to execute Commands []*Command // List of flags to parse Flags []Flag // Boolean to enable bash completion commands EnableBashCompletion bool // Boolean to hide built-in help command and help flag HideHelp bool // Boolean to hide built-in help command but keep help flag. // Ignored if HideHelp is true. HideHelpCommand bool // Boolean to hide built-in version flag and the VERSION section of help HideVersion bool // An action to execute when the shell completion flag is set BashComplete BashCompleteFunc // An action to execute before any subcommands are run, but after the context is ready // If a non-nil error is returned, no subcommands are run Before BeforeFunc // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc // The action to execute when no subcommands are specified Action ActionFunc // Execute this function if the proper command cannot be found CommandNotFound CommandNotFoundFunc // Execute this function if a usage error occurs OnUsageError OnUsageErrorFunc // Execute this function when an invalid flag is accessed from the context InvalidFlagAccessHandler InvalidFlagAccessFunc // Compilation date Compiled time.Time // List of all authors who contributed Authors []*Author // Copyright of the binary if any Copyright string // Reader reader to write input to (useful for tests) Reader io.Reader // Writer writer to write output to Writer io.Writer // ErrWriter writes error output ErrWriter io.Writer // ExitErrHandler processes any error encountered while running an App before // it is returned to the caller. If no function is provided, HandleExitCoder // is used as the default behavior. ExitErrHandler ExitErrHandlerFunc // Other custom info Metadata map[string]interface{} // Carries a function which returns app specific info. ExtraInfo func() map[string]string // CustomAppHelpTemplate the text template for app help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomAppHelpTemplate string // SliceFlagSeparator is used to customize the separator for SliceFlag, the default is "," SliceFlagSeparator string // DisableSliceFlagSeparator is used to disable SliceFlagSeparator, the default is false DisableSliceFlagSeparator bool // Boolean to enable short-option handling so user can combine several // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool // Enable suggestions for commands and flags Suggest bool // Allows global flags set by libraries which use flag.XXXVar(...) directly // to be parsed through this library AllowExtFlags bool // Treat all flags as normal arguments if true SkipFlagParsing bool // Has unexported fields. } App is the main structure of a cli application. It is recommended that an app be created with the cli.NewApp() function func NewApp() *App NewApp creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. func (a *App) Command(name string) *Command Command returns the named command on App. Returns nil if the command does not exist func (a *App) Run(arguments []string) (err error) Run is the entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination func (a *App) RunAndExitOnError() RunAndExitOnError calls .Run() and exits non-zero if an error was returned Deprecated: instead you should return an error that fulfills cli.ExitCoder to cli.App.Run. This will cause the application to exit with the given error code in the cli.ExitCoder func (a *App) RunAsSubcommand(ctx *Context) (err error) RunAsSubcommand is for legacy/compatibility purposes only. New code should only use App.RunContext. This function is slated to be removed in v3. func (a *App) RunContext(ctx context.Context, arguments []string) (err error) RunContext is like Run except it takes a Context that will be passed to its commands and sub-commands. Through this, you can propagate timeouts and cancellation requests func (a *App) Setup() Setup runs initialization code to ensure all data structures are ready for `Run` or inspection prior to `Run`. It is internally called by `Run`, but will return early if setup has already happened. func (a *App) ToFishCompletion() (string, error) ToFishCompletion creates a fish completion string for the `*App` The function errors if either parsing or writing of the string fails. func (a *App) ToMan() (string, error) ToMan creates a man page string for the `*App` The function errors if either parsing or writing of the string fails. func (a *App) ToManWithSection(sectionNumber int) (string, error) ToMan creates a man page string with section number for the `*App` The function errors if either parsing or writing of the string fails. func (a *App) ToMarkdown() (string, error) ToMarkdown creates a markdown string for the `*App` The function errors if either parsing or writing of the string fails. func (a *App) VisibleCategories() []CommandCategory VisibleCategories returns a slice of categories and commands that are Hidden=false func (a *App) VisibleCommands() []*Command VisibleCommands returns a slice of the Commands with Hidden=false func (a *App) VisibleFlagCategories() []VisibleFlagCategory VisibleFlagCategories returns a slice containing all the categories with the flags they contain func (a *App) VisibleFlags() []Flag VisibleFlags returns a slice of the Flags with Hidden=false type Args interface { // Get returns the nth argument, or else a blank string Get(n int) string // First returns the first argument, or else a blank string First() string // Tail returns the rest of the arguments (not the first one) // or else an empty string slice Tail() []string // Len returns the length of the wrapped slice Len() int // Present checks if there are any arguments present Present() bool // Slice returns a copy of the internal slice Slice() []string } type Author struct { Name string // The Authors name Email string // The Authors email } Author represents someone who has contributed to a cli project. func (a *Author) String() string String makes Author comply to the Stringer interface, to allow an easy print in the templating process type BashCompleteFunc func(*Context) BashCompleteFunc is an action to execute when the shell completion flag is set type BeforeFunc func(*Context) error BeforeFunc is an action to execute before any subcommands are run, but after the context is ready if a non-nil error is returned, no subcommands are run type BoolFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value bool Destination *bool Aliases []string EnvVars []string Count *int DisableDefaultText bool Action func(*Context, bool) error // Has unexported fields. } BoolFlag is a flag with type bool func (f *BoolFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *BoolFlag) Get(ctx *Context) bool Get returns the flag’s value in the given Context. func (f *BoolFlag) GetCategory() string GetCategory returns the category for the flag func (f *BoolFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *BoolFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *BoolFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *BoolFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *BoolFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *BoolFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *BoolFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *BoolFlag) Names() []string Names returns the names of the flag func (f *BoolFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *BoolFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *BoolFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type CategorizableFlag interface { VisibleFlag GetCategory() string } CategorizableFlag is an interface that allows us to potentially use a flag in a categorized representation. type Command struct { // The name of the command Name string // A list of aliases for the command Aliases []string // A short description of the usage of this command Usage string // Custom text to show on USAGE section of help UsageText string // A longer explanation of how the command works Description string // Whether this command supports arguments Args bool // A short description of the arguments of this command ArgsUsage string // The category the command is part of Category string // The function to call when checking for bash command completions BashComplete BashCompleteFunc // An action to execute before any sub-subcommands are run, but after the context is ready // If a non-nil error is returned, no sub-subcommands are run Before BeforeFunc // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc // The function to call when this command is invoked Action ActionFunc // Execute this function if a usage error occurs. OnUsageError OnUsageErrorFunc // List of child commands Subcommands []*Command // List of flags to parse Flags []Flag // Treat all flags as normal arguments if true SkipFlagParsing bool // Boolean to hide built-in help command and help flag HideHelp bool // Boolean to hide built-in help command but keep help flag // Ignored if HideHelp is true. HideHelpCommand bool // Boolean to hide this command from help or completion Hidden bool // Boolean to enable short-option handling so user can combine several // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool // Full name of command for help, defaults to full command name, including parent commands. HelpName string // CustomHelpTemplate the text template for the command help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomHelpTemplate string // Has unexported fields. } Command is a subcommand for a cli.App. func (cmd *Command) Command(name string) *Command func (c *Command) FullName() string FullName returns the full name of the command. For subcommands this ensures that parent commands are part of the command path func (c *Command) HasName(name string) bool HasName returns true if Command.Name matches given name func (c *Command) Names() []string Names returns the names including short names and aliases. func (c *Command) Run(cCtx *Context, arguments ...string) (err error) func (c *Command) VisibleCategories() []CommandCategory VisibleCategories returns a slice of categories and commands that are Hidden=false func (c *Command) VisibleCommands() []*Command VisibleCommands returns a slice of the Commands with Hidden=false func (c *Command) VisibleFlagCategories() []VisibleFlagCategory VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain func (c *Command) VisibleFlags() []Flag VisibleFlags returns a slice of the Flags with Hidden=false type CommandCategories interface { // AddCommand adds a command to a category, creating a new category if necessary. AddCommand(category string, command *Command) // Categories returns a slice of categories sorted by name Categories() []CommandCategory } CommandCategories interface allows for category manipulation type CommandCategory interface { // Name returns the category name string Name() string // VisibleCommands returns a slice of the Commands with Hidden=false VisibleCommands() []*Command } CommandCategory is a category containing commands. type CommandNotFoundFunc func(*Context, string) CommandNotFoundFunc is executed if the proper command cannot be found type Commands []*Command type CommandsByName []*Command func (c CommandsByName) Len() int func (c CommandsByName) Less(i, j int) bool func (c CommandsByName) Swap(i, j int) type Context struct { context.Context App *App Command *Command // Has unexported fields. } Context is a type that is passed through to each Handler action in a cli application. Context can be used to retrieve context-specific args and parsed command-line options. func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context NewContext creates a new context. For use in when invoking an App or Command action. func (cCtx *Context) Args() Args Args returns the command line arguments associated with the context. func (cCtx *Context) Bool(name string) bool Bool looks up the value of a local BoolFlag, returns false if not found func (cCtx *Context) Count(name string) int Count returns the num of occurrences of this flag func (cCtx *Context) Duration(name string) time.Duration Duration looks up the value of a local DurationFlag, returns 0 if not found func (cCtx *Context) FlagNames() []string FlagNames returns a slice of flag names used by the this context and all of its parent contexts. func (cCtx *Context) Float64(name string) float64 Float64 looks up the value of a local Float64Flag, returns 0 if not found func (cCtx *Context) Float64Slice(name string) []float64 Float64Slice looks up the value of a local Float64SliceFlag, returns nil if not found func (cCtx *Context) Generic(name string) interface{} Generic looks up the value of a local GenericFlag, returns nil if not found func (cCtx *Context) Int(name string) int Int looks up the value of a local IntFlag, returns 0 if not found func (cCtx *Context) Int64(name string) int64 Int64 looks up the value of a local Int64Flag, returns 0 if not found func (cCtx *Context) Int64Slice(name string) []int64 Int64Slice looks up the value of a local Int64SliceFlag, returns nil if not found func (cCtx *Context) IntSlice(name string) []int IntSlice looks up the value of a local IntSliceFlag, returns nil if not found func (cCtx *Context) IsSet(name string) bool IsSet determines if the flag was actually set func (cCtx *Context) Lineage() []*Context Lineage returns *this* context and all of its ancestor contexts in order from child to parent func (cCtx *Context) LocalFlagNames() []string LocalFlagNames returns a slice of flag names used in this context. func (cCtx *Context) NArg() int NArg returns the number of the command line arguments. func (cCtx *Context) NumFlags() int NumFlags returns the number of flags set func (cCtx *Context) Path(name string) string Path looks up the value of a local PathFlag, returns "" if not found func (cCtx *Context) Set(name, value string) error Set sets a context flag to a value. func (cCtx *Context) String(name string) string String looks up the value of a local StringFlag, returns "" if not found func (cCtx *Context) StringSlice(name string) []string StringSlice looks up the value of a local StringSliceFlag, returns nil if not found func (cCtx *Context) Timestamp(name string) *time.Time Timestamp gets the timestamp from a flag name func (cCtx *Context) Uint(name string) uint Uint looks up the value of a local UintFlag, returns 0 if not found func (cCtx *Context) Uint64(name string) uint64 Uint64 looks up the value of a local Uint64Flag, returns 0 if not found func (cCtx *Context) Uint64Slice(name string) []uint64 Uint64Slice looks up the value of a local Uint64SliceFlag, returns nil if not found func (cCtx *Context) UintSlice(name string) []uint UintSlice looks up the value of a local UintSliceFlag, returns nil if not found func (cCtx *Context) Value(name string) interface{} Value returns the value of the flag corresponding to `name` type Countable interface { Count() int } Countable is an interface to enable detection of flag values which support repetitive flags type DocGenerationFlag interface { Flag // TakesValue returns true if the flag takes a value, otherwise false TakesValue() bool // GetUsage returns the usage string for the flag GetUsage() string // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. GetValue() string // GetDefaultText returns the default text for this flag GetDefaultText() string // GetEnvVars returns the env vars for this flag GetEnvVars() []string } DocGenerationFlag is an interface that allows documentation generation for the flag type DocGenerationSliceFlag interface { DocGenerationFlag // IsSliceFlag returns true for flags that can be given multiple times. IsSliceFlag() bool } DocGenerationSliceFlag extends DocGenerationFlag for slice-based flags. type DurationFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value time.Duration Destination *time.Duration Aliases []string EnvVars []string Action func(*Context, time.Duration) error // Has unexported fields. } DurationFlag is a flag with type time.Duration func (f *DurationFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *DurationFlag) Get(ctx *Context) time.Duration Get returns the flag’s value in the given Context. func (f *DurationFlag) GetCategory() string GetCategory returns the category for the flag func (f *DurationFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *DurationFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *DurationFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *DurationFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *DurationFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *DurationFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *DurationFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *DurationFlag) Names() []string Names returns the names of the flag func (f *DurationFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *DurationFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *DurationFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type ErrorFormatter interface { Format(s fmt.State, verb rune) } ErrorFormatter is the interface that will suitably format the error output type ExitCoder interface { error ExitCode() int } ExitCoder is the interface checked by `App` and `Command` for a custom exit code func Exit(message interface{}, exitCode int) ExitCoder Exit wraps a message and exit code into an error, which by default is handled with a call to os.Exit during default error handling. This is the simplest way to trigger a non-zero exit code for an App without having to call os.Exit manually. During testing, this behavior can be avoided by overriding the ExitErrHandler function on an App or the package-global OsExiter function. func NewExitError(message interface{}, exitCode int) ExitCoder NewExitError calls Exit to create a new ExitCoder. Deprecated: This function is a duplicate of Exit and will eventually be removed. type ExitErrHandlerFunc func(cCtx *Context, err error) ExitErrHandlerFunc is executed if provided in order to handle exitError values returned by Actions and Before/After functions. type Flag interface { fmt.Stringer // Apply Flag settings to the given flag set Apply(*flag.FlagSet) error Names() []string IsSet() bool } Flag is a common interface related to parsing flags in cli. For more advanced flag parsing techniques, it is recommended that this interface be implemented. var BashCompletionFlag Flag = &BoolFlag{ Name: "generate-bash-completion", Hidden: true, } BashCompletionFlag enables bash-completion for all commands and subcommands var HelpFlag Flag = &BoolFlag{ Name: "help", Aliases: []string{"h"}, Usage: "show help", DisableDefaultText: true, } HelpFlag prints the help for all commands and subcommands. Set to nil to disable the flag. The subcommand will still be added unless HideHelp or HideHelpCommand is set to true. var VersionFlag Flag = &BoolFlag{ Name: "version", Aliases: []string{"v"}, Usage: "print the version", DisableDefaultText: true, } VersionFlag prints the version for the application type FlagCategories interface { // AddFlags adds a flag to a category, creating a new category if necessary. AddFlag(category string, fl Flag) // VisibleCategories returns a slice of visible flag categories sorted by name VisibleCategories() []VisibleFlagCategory } FlagCategories interface allows for category manipulation type FlagEnvHintFunc func(envVars []string, str string) string FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help with the environment variable details. var FlagEnvHinter FlagEnvHintFunc = withEnvHint FlagEnvHinter annotates flag help message with the environment variable details. This is used by the default FlagStringer. type FlagFileHintFunc func(filePath, str string) string FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help with the file path details. var FlagFileHinter FlagFileHintFunc = withFileHint FlagFileHinter annotates flag help message with the environment variable details. This is used by the default FlagStringer. type FlagNamePrefixFunc func(fullName []string, placeholder string) string FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix text for a flag's full name. var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames FlagNamePrefixer converts a full flag name and its placeholder into the help message flag prefix. This is used by the default FlagStringer. type FlagStringFunc func(Flag) string FlagStringFunc is used by the help generation to display a flag, which is expected to be a single line. var FlagStringer FlagStringFunc = stringifyFlag FlagStringer converts a flag definition to a string. This is used by help to display a flag. type FlagsByName []Flag FlagsByName is a slice of Flag. func (f FlagsByName) Len() int func (f FlagsByName) Less(i, j int) bool func (f FlagsByName) Swap(i, j int) type Float64Flag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value float64 Destination *float64 Aliases []string EnvVars []string Action func(*Context, float64) error // Has unexported fields. } Float64Flag is a flag with type float64 func (f *Float64Flag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *Float64Flag) Get(ctx *Context) float64 Get returns the flag’s value in the given Context. func (f *Float64Flag) GetCategory() string GetCategory returns the category for the flag func (f *Float64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *Float64Flag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *Float64Flag) GetUsage() string GetUsage returns the usage string for the flag func (f *Float64Flag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *Float64Flag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *Float64Flag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *Float64Flag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *Float64Flag) Names() []string Names returns the names of the flag func (f *Float64Flag) RunAction(c *Context) error RunAction executes flag action if set func (f *Float64Flag) String() string String returns a readable representation of this value (for usage defaults) func (f *Float64Flag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type Float64Slice struct { // Has unexported fields. } Float64Slice wraps []float64 to satisfy flag.Value func NewFloat64Slice(defaults ...float64) *Float64Slice NewFloat64Slice makes a *Float64Slice with default values func (f *Float64Slice) Get() interface{} Get returns the slice of float64s set by this flag func (f *Float64Slice) Serialize() string Serialize allows Float64Slice to fulfill Serializer func (f *Float64Slice) Set(value string) error Set parses the value into a float64 and appends it to the list of values func (f *Float64Slice) String() string String returns a readable representation of this value (for usage defaults) func (f *Float64Slice) Value() []float64 Value returns the slice of float64s set by this flag func (f *Float64Slice) WithSeparatorSpec(spec separatorSpec) type Float64SliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *Float64Slice Destination *Float64Slice Aliases []string EnvVars []string Action func(*Context, []float64) error // Has unexported fields. } Float64SliceFlag is a flag with type *Float64Slice func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *Float64SliceFlag) Get(ctx *Context) []float64 Get returns the flag’s value in the given Context. func (f *Float64SliceFlag) GetCategory() string GetCategory returns the category for the flag func (f *Float64SliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *Float64SliceFlag) GetDestination() []float64 func (f *Float64SliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *Float64SliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *Float64SliceFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *Float64SliceFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *Float64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *Float64SliceFlag) IsSliceFlag() bool IsSliceFlag implements DocGenerationSliceFlag. func (f *Float64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *Float64SliceFlag) Names() []string Names returns the names of the flag func (f *Float64SliceFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *Float64SliceFlag) SetDestination(slice []float64) func (f *Float64SliceFlag) SetValue(slice []float64) func (f *Float64SliceFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *Float64SliceFlag) TakesValue() bool TakesValue returns true if the flag takes a value, otherwise false func (f *Float64SliceFlag) WithSeparatorSpec(spec separatorSpec) type Generic interface { Set(value string) error String() string } Generic is a generic parseable type identified by a specific flag type GenericFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value Generic Destination Generic Aliases []string EnvVars []string TakesFile bool Action func(*Context, interface{}) error // Has unexported fields. } GenericFlag is a flag with type Generic func (f *GenericFlag) Apply(set *flag.FlagSet) error Apply takes the flagset and calls Set on the generic flag with the value provided by the user for parsing by the flag func (f *GenericFlag) Get(ctx *Context) interface{} Get returns the flag’s value in the given Context. func (f *GenericFlag) GetCategory() string GetCategory returns the category for the flag func (f *GenericFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *GenericFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *GenericFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *GenericFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *GenericFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *GenericFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *GenericFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *GenericFlag) Names() []string Names returns the names of the flag func (f *GenericFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *GenericFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *GenericFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type Int64Flag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value int64 Destination *int64 Aliases []string EnvVars []string Base int Action func(*Context, int64) error // Has unexported fields. } Int64Flag is a flag with type int64 func (f *Int64Flag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *Int64Flag) Get(ctx *Context) int64 Get returns the flag’s value in the given Context. func (f *Int64Flag) GetCategory() string GetCategory returns the category for the flag func (f *Int64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *Int64Flag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *Int64Flag) GetUsage() string GetUsage returns the usage string for the flag func (f *Int64Flag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *Int64Flag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *Int64Flag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *Int64Flag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *Int64Flag) Names() []string Names returns the names of the flag func (f *Int64Flag) RunAction(c *Context) error RunAction executes flag action if set func (f *Int64Flag) String() string String returns a readable representation of this value (for usage defaults) func (f *Int64Flag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type Int64Slice struct { // Has unexported fields. } Int64Slice wraps []int64 to satisfy flag.Value func NewInt64Slice(defaults ...int64) *Int64Slice NewInt64Slice makes an *Int64Slice with default values func (i *Int64Slice) Get() interface{} Get returns the slice of ints set by this flag func (i *Int64Slice) Serialize() string Serialize allows Int64Slice to fulfill Serializer func (i *Int64Slice) Set(value string) error Set parses the value into an integer and appends it to the list of values func (i *Int64Slice) String() string String returns a readable representation of this value (for usage defaults) func (i *Int64Slice) Value() []int64 Value returns the slice of ints set by this flag func (i *Int64Slice) WithSeparatorSpec(spec separatorSpec) type Int64SliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *Int64Slice Destination *Int64Slice Aliases []string EnvVars []string Action func(*Context, []int64) error // Has unexported fields. } Int64SliceFlag is a flag with type *Int64Slice func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *Int64SliceFlag) Get(ctx *Context) []int64 Get returns the flag’s value in the given Context. func (f *Int64SliceFlag) GetCategory() string GetCategory returns the category for the flag func (f *Int64SliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *Int64SliceFlag) GetDestination() []int64 func (f *Int64SliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *Int64SliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *Int64SliceFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *Int64SliceFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *Int64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *Int64SliceFlag) IsSliceFlag() bool IsSliceFlag implements DocGenerationSliceFlag. func (f *Int64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *Int64SliceFlag) Names() []string Names returns the names of the flag func (f *Int64SliceFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *Int64SliceFlag) SetDestination(slice []int64) func (f *Int64SliceFlag) SetValue(slice []int64) func (f *Int64SliceFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *Int64SliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false func (f *Int64SliceFlag) WithSeparatorSpec(spec separatorSpec) type IntFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value int Destination *int Aliases []string EnvVars []string Base int Action func(*Context, int) error // Has unexported fields. } IntFlag is a flag with type int func (f *IntFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *IntFlag) Get(ctx *Context) int Get returns the flag’s value in the given Context. func (f *IntFlag) GetCategory() string GetCategory returns the category for the flag func (f *IntFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *IntFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *IntFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *IntFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *IntFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *IntFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *IntFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *IntFlag) Names() []string Names returns the names of the flag func (f *IntFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *IntFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *IntFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type IntSlice struct { // Has unexported fields. } IntSlice wraps []int to satisfy flag.Value func NewIntSlice(defaults ...int) *IntSlice NewIntSlice makes an *IntSlice with default values func (i *IntSlice) Get() interface{} Get returns the slice of ints set by this flag func (i *IntSlice) Serialize() string Serialize allows IntSlice to fulfill Serializer func (i *IntSlice) Set(value string) error Set parses the value into an integer and appends it to the list of values func (i *IntSlice) SetInt(value int) TODO: Consistently have specific Set function for Int64 and Float64 ? SetInt directly adds an integer to the list of values func (i *IntSlice) String() string String returns a readable representation of this value (for usage defaults) func (i *IntSlice) Value() []int Value returns the slice of ints set by this flag func (i *IntSlice) WithSeparatorSpec(spec separatorSpec) type IntSliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *IntSlice Destination *IntSlice Aliases []string EnvVars []string Action func(*Context, []int) error // Has unexported fields. } IntSliceFlag is a flag with type *IntSlice func (f *IntSliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *IntSliceFlag) Get(ctx *Context) []int Get returns the flag’s value in the given Context. func (f *IntSliceFlag) GetCategory() string GetCategory returns the category for the flag func (f *IntSliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *IntSliceFlag) GetDestination() []int func (f *IntSliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *IntSliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *IntSliceFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *IntSliceFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *IntSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *IntSliceFlag) IsSliceFlag() bool IsSliceFlag implements DocGenerationSliceFlag. func (f *IntSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *IntSliceFlag) Names() []string Names returns the names of the flag func (f *IntSliceFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *IntSliceFlag) SetDestination(slice []int) func (f *IntSliceFlag) SetValue(slice []int) func (f *IntSliceFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *IntSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false func (f *IntSliceFlag) WithSeparatorSpec(spec separatorSpec) type InvalidFlagAccessFunc func(*Context, string) InvalidFlagAccessFunc is executed when an invalid flag is accessed from the context. type MultiError interface { error Errors() []error } MultiError is an error that wraps multiple errors. type MultiFloat64Flag = SliceFlag[*Float64SliceFlag, []float64, float64] MultiFloat64Flag extends Float64SliceFlag with support for using slices directly, as Value and/or Destination. See also SliceFlag. type MultiInt64Flag = SliceFlag[*Int64SliceFlag, []int64, int64] MultiInt64Flag extends Int64SliceFlag with support for using slices directly, as Value and/or Destination. See also SliceFlag. type MultiIntFlag = SliceFlag[*IntSliceFlag, []int, int] MultiIntFlag extends IntSliceFlag with support for using slices directly, as Value and/or Destination. See also SliceFlag. type MultiStringFlag = SliceFlag[*StringSliceFlag, []string, string] MultiStringFlag extends StringSliceFlag with support for using slices directly, as Value and/or Destination. See also SliceFlag. type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error OnUsageErrorFunc is executed if a usage error occurs. This is useful for displaying customized usage error messages. This function is able to replace the original error messages. If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted. type Path = string type PathFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value Path Destination *Path Aliases []string EnvVars []string TakesFile bool Action func(*Context, Path) error // Has unexported fields. } PathFlag is a flag with type Path func (f *PathFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *PathFlag) Get(ctx *Context) string Get returns the flag’s value in the given Context. func (f *PathFlag) GetCategory() string GetCategory returns the category for the flag func (f *PathFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *PathFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *PathFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *PathFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *PathFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *PathFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *PathFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *PathFlag) Names() []string Names returns the names of the flag func (f *PathFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *PathFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *PathFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type RequiredFlag interface { Flag IsRequired() bool } RequiredFlag is an interface that allows us to mark flags as required it allows flags required flags to be backwards compatible with the Flag interface type Serializer interface { Serialize() string } Serializer is used to circumvent the limitations of flag.FlagSet.Set type SliceFlag[T SliceFlagTarget[E], S ~[]E, E any] struct { Target T Value S Destination *S } SliceFlag extends implementations like StringSliceFlag and IntSliceFlag with support for using slices directly, as Value and/or Destination. See also SliceFlagTarget, MultiStringFlag, MultiFloat64Flag, MultiInt64Flag, MultiIntFlag. func (x *SliceFlag[T, S, E]) Apply(set *flag.FlagSet) error func (x *SliceFlag[T, S, E]) GetCategory() string func (x *SliceFlag[T, S, E]) GetDefaultText() string func (x *SliceFlag[T, S, E]) GetDestination() S func (x *SliceFlag[T, S, E]) GetEnvVars() []string func (x *SliceFlag[T, S, E]) GetUsage() string func (x *SliceFlag[T, S, E]) GetValue() string func (x *SliceFlag[T, S, E]) IsRequired() bool func (x *SliceFlag[T, S, E]) IsSet() bool func (x *SliceFlag[T, S, E]) IsVisible() bool func (x *SliceFlag[T, S, E]) Names() []string func (x *SliceFlag[T, S, E]) SetDestination(slice S) func (x *SliceFlag[T, S, E]) SetValue(slice S) func (x *SliceFlag[T, S, E]) String() string func (x *SliceFlag[T, S, E]) TakesValue() bool type SliceFlagTarget[E any] interface { Flag RequiredFlag DocGenerationFlag VisibleFlag CategorizableFlag // SetValue should propagate the given slice to the target, ideally as a new value. // Note that a nil slice should nil/clear any existing value (modelled as ~[]E). SetValue(slice []E) // SetDestination should propagate the given slice to the target, ideally as a new value. // Note that a nil slice should nil/clear any existing value (modelled as ~*[]E). SetDestination(slice []E) // GetDestination should return the current value referenced by any destination, or nil if nil/unset. GetDestination() []E } SliceFlagTarget models a target implementation for use with SliceFlag. The three methods, SetValue, SetDestination, and GetDestination, are necessary to propagate Value and Destination, where Value is propagated inwards (initially), and Destination is propagated outwards (on every update). type StringFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value string Destination *string Aliases []string EnvVars []string TakesFile bool Action func(*Context, string) error // Has unexported fields. } StringFlag is a flag with type string func (f *StringFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *StringFlag) Get(ctx *Context) string Get returns the flag’s value in the given Context. func (f *StringFlag) GetCategory() string GetCategory returns the category for the flag func (f *StringFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *StringFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *StringFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *StringFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *StringFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *StringFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *StringFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *StringFlag) Names() []string Names returns the names of the flag func (f *StringFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *StringFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *StringFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type StringSlice struct { // Has unexported fields. } StringSlice wraps a []string to satisfy flag.Value func NewStringSlice(defaults ...string) *StringSlice NewStringSlice creates a *StringSlice with default values func (s *StringSlice) Get() interface{} Get returns the slice of strings set by this flag func (s *StringSlice) Serialize() string Serialize allows StringSlice to fulfill Serializer func (s *StringSlice) Set(value string) error Set appends the string value to the list of values func (s *StringSlice) String() string String returns a readable representation of this value (for usage defaults) func (s *StringSlice) Value() []string Value returns the slice of strings set by this flag func (s *StringSlice) WithSeparatorSpec(spec separatorSpec) type StringSliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *StringSlice Destination *StringSlice Aliases []string EnvVars []string TakesFile bool Action func(*Context, []string) error KeepSpace bool // Has unexported fields. } StringSliceFlag is a flag with type *StringSlice func (f *StringSliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *StringSliceFlag) Get(ctx *Context) []string Get returns the flag’s value in the given Context. func (f *StringSliceFlag) GetCategory() string GetCategory returns the category for the flag func (f *StringSliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *StringSliceFlag) GetDestination() []string func (f *StringSliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *StringSliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *StringSliceFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *StringSliceFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *StringSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *StringSliceFlag) IsSliceFlag() bool IsSliceFlag implements DocGenerationSliceFlag. func (f *StringSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *StringSliceFlag) Names() []string Names returns the names of the flag func (f *StringSliceFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *StringSliceFlag) SetDestination(slice []string) func (f *StringSliceFlag) SetValue(slice []string) func (f *StringSliceFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *StringSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false func (f *StringSliceFlag) WithSeparatorSpec(spec separatorSpec) type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string type Timestamp struct { // Has unexported fields. } Timestamp wrap to satisfy golang's flag interface. func NewTimestamp(timestamp time.Time) *Timestamp Timestamp constructor func (t *Timestamp) Get() interface{} Get returns the flag structure func (t *Timestamp) Set(value string) error Parses the string value to timestamp func (t *Timestamp) SetLayout(layout string) Set the timestamp string layout for future parsing func (t *Timestamp) SetLocation(loc *time.Location) Set perceived timezone of the to-be parsed time string func (t *Timestamp) SetTimestamp(value time.Time) Set the timestamp value directly func (t *Timestamp) String() string String returns a readable representation of this value (for usage defaults) func (t *Timestamp) Value() *time.Time Value returns the timestamp value stored in the flag type TimestampFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *Timestamp Destination *Timestamp Aliases []string EnvVars []string Layout string Timezone *time.Location Action func(*Context, *time.Time) error // Has unexported fields. } TimestampFlag is a flag with type *Timestamp func (f *TimestampFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *TimestampFlag) Get(ctx *Context) *time.Time Get returns the flag’s value in the given Context. func (f *TimestampFlag) GetCategory() string GetCategory returns the category for the flag func (f *TimestampFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *TimestampFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *TimestampFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *TimestampFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *TimestampFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *TimestampFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *TimestampFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *TimestampFlag) Names() []string Names returns the names of the flag func (f *TimestampFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *TimestampFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *TimestampFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type Uint64Flag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value uint64 Destination *uint64 Aliases []string EnvVars []string Base int Action func(*Context, uint64) error // Has unexported fields. } Uint64Flag is a flag with type uint64 func (f *Uint64Flag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *Uint64Flag) Get(ctx *Context) uint64 Get returns the flag’s value in the given Context. func (f *Uint64Flag) GetCategory() string GetCategory returns the category for the flag func (f *Uint64Flag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *Uint64Flag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *Uint64Flag) GetUsage() string GetUsage returns the usage string for the flag func (f *Uint64Flag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *Uint64Flag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *Uint64Flag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *Uint64Flag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *Uint64Flag) Names() []string Names returns the names of the flag func (f *Uint64Flag) RunAction(c *Context) error RunAction executes flag action if set func (f *Uint64Flag) String() string String returns a readable representation of this value (for usage defaults) func (f *Uint64Flag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type Uint64Slice struct { // Has unexported fields. } Uint64Slice wraps []int64 to satisfy flag.Value func NewUint64Slice(defaults ...uint64) *Uint64Slice NewUint64Slice makes an *Uint64Slice with default values func (i *Uint64Slice) Get() interface{} Get returns the slice of ints set by this flag func (i *Uint64Slice) Serialize() string Serialize allows Uint64Slice to fulfill Serializer func (i *Uint64Slice) Set(value string) error Set parses the value into an integer and appends it to the list of values func (i *Uint64Slice) String() string String returns a readable representation of this value (for usage defaults) func (i *Uint64Slice) Value() []uint64 Value returns the slice of ints set by this flag func (i *Uint64Slice) WithSeparatorSpec(spec separatorSpec) type Uint64SliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *Uint64Slice Destination *Uint64Slice Aliases []string EnvVars []string Action func(*Context, []uint64) error // Has unexported fields. } Uint64SliceFlag is a flag with type *Uint64Slice func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 Get returns the flag’s value in the given Context. func (f *Uint64SliceFlag) GetCategory() string GetCategory returns the category for the flag func (f *Uint64SliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *Uint64SliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *Uint64SliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *Uint64SliceFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *Uint64SliceFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *Uint64SliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *Uint64SliceFlag) IsSliceFlag() bool IsSliceFlag implements DocGenerationSliceFlag. func (f *Uint64SliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *Uint64SliceFlag) Names() []string Names returns the names of the flag func (f *Uint64SliceFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *Uint64SliceFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *Uint64SliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false func (f *Uint64SliceFlag) WithSeparatorSpec(spec separatorSpec) type UintFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value uint Destination *uint Aliases []string EnvVars []string Base int Action func(*Context, uint) error // Has unexported fields. } UintFlag is a flag with type uint func (f *UintFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *UintFlag) Get(ctx *Context) uint Get returns the flag’s value in the given Context. func (f *UintFlag) GetCategory() string GetCategory returns the category for the flag func (f *UintFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *UintFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *UintFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *UintFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *UintFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *UintFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *UintFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *UintFlag) Names() []string Names returns the names of the flag func (f *UintFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *UintFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *UintFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false type UintSlice struct { // Has unexported fields. } UintSlice wraps []int to satisfy flag.Value func NewUintSlice(defaults ...uint) *UintSlice NewUintSlice makes an *UintSlice with default values func (i *UintSlice) Get() interface{} Get returns the slice of ints set by this flag func (i *UintSlice) Serialize() string Serialize allows UintSlice to fulfill Serializer func (i *UintSlice) Set(value string) error Set parses the value into an integer and appends it to the list of values func (i *UintSlice) SetUint(value uint) TODO: Consistently have specific Set function for Int64 and Float64 ? SetInt directly adds an integer to the list of values func (i *UintSlice) String() string String returns a readable representation of this value (for usage defaults) func (i *UintSlice) Value() []uint Value returns the slice of ints set by this flag func (i *UintSlice) WithSeparatorSpec(spec separatorSpec) type UintSliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *UintSlice Destination *UintSlice Aliases []string EnvVars []string Action func(*Context, []uint) error // Has unexported fields. } UintSliceFlag is a flag with type *UintSlice func (f *UintSliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment func (f *UintSliceFlag) Get(ctx *Context) []uint Get returns the flag’s value in the given Context. func (f *UintSliceFlag) GetCategory() string GetCategory returns the category for the flag func (f *UintSliceFlag) GetDefaultText() string GetDefaultText returns the default text for this flag func (f *UintSliceFlag) GetEnvVars() []string GetEnvVars returns the env vars for this flag func (f *UintSliceFlag) GetUsage() string GetUsage returns the usage string for the flag func (f *UintSliceFlag) GetValue() string GetValue returns the flags value as string representation and an empty string if the flag takes no value at all. func (f *UintSliceFlag) IsRequired() bool IsRequired returns whether or not the flag is required func (f *UintSliceFlag) IsSet() bool IsSet returns whether or not the flag has been set through env or file func (f *UintSliceFlag) IsSliceFlag() bool IsSliceFlag implements DocGenerationSliceFlag. func (f *UintSliceFlag) IsVisible() bool IsVisible returns true if the flag is not hidden, otherwise false func (f *UintSliceFlag) Names() []string Names returns the names of the flag func (f *UintSliceFlag) RunAction(c *Context) error RunAction executes flag action if set func (f *UintSliceFlag) String() string String returns a readable representation of this value (for usage defaults) func (f *UintSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false func (f *UintSliceFlag) WithSeparatorSpec(spec separatorSpec) type VisibleFlag interface { Flag // IsVisible returns true if the flag is not hidden, otherwise false IsVisible() bool } VisibleFlag is an interface that allows to check if a flag is visible type VisibleFlagCategory interface { // Name returns the category name string Name() string // Flags returns a slice of VisibleFlag sorted by name Flags() []VisibleFlag } VisibleFlagCategory is a category containing flags. package altsrc // import "github.com/urfave/cli/v2/altsrc" FUNCTIONS func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error ApplyInputSourceValues iterates over all provided flags and executes ApplyInputSourceValue on flags implementing the FlagInputSourceExtension interface to initialize these flags to an alternate input source. func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new input source based on the func provided. If there is no error it will then apply the new input source to any flags that are supported by the input source func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is no error it will then apply the new input source to any flags that are supported by the input source func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) NewJSONSourceFromFlagFunc returns a func that takes a cli.Context and returns an InputSourceContext suitable for retrieving config variables from a file containing JSON data with the file name defined by the given flag. func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context. func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context. TYPES type BoolFlag struct { *cli.BoolFlag // Has unexported fields. } BoolFlag is the flag type that wraps cli.BoolFlag to allow for other values to be specified func NewBoolFlag(fl *cli.BoolFlag) *BoolFlag NewBoolFlag creates a new BoolFlag func (f *BoolFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped BoolFlag.Apply func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a Bool value to the flagSet if required type DurationFlag struct { *cli.DurationFlag // Has unexported fields. } DurationFlag is the flag type that wraps cli.DurationFlag to allow for other values to be specified func NewDurationFlag(fl *cli.DurationFlag) *DurationFlag NewDurationFlag creates a new DurationFlag func (f *DurationFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped DurationFlag.Apply func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a Duration value to the flagSet if required type FlagInputSourceExtension interface { cli.Flag ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error } FlagInputSourceExtension is an extension interface of cli.Flag that allows a value to be set on the existing parsed flags. type Float64Flag struct { *cli.Float64Flag // Has unexported fields. } Float64Flag is the flag type that wraps cli.Float64Flag to allow for other values to be specified func NewFloat64Flag(fl *cli.Float64Flag) *Float64Flag NewFloat64Flag creates a new Float64Flag func (f *Float64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Float64Flag.Apply func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a Float64 value to the flagSet if required type Float64SliceFlag struct { *cli.Float64SliceFlag // Has unexported fields. } Float64SliceFlag is the flag type that wraps cli.Float64SliceFlag to allow for other values to be specified func NewFloat64SliceFlag(fl *cli.Float64SliceFlag) *Float64SliceFlag NewFloat64SliceFlag creates a new Float64SliceFlag func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Float64SliceFlag.Apply func (f *Float64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a Float64Slice value if required type GenericFlag struct { *cli.GenericFlag // Has unexported fields. } GenericFlag is the flag type that wraps cli.GenericFlag to allow for other values to be specified func NewGenericFlag(fl *cli.GenericFlag) *GenericFlag NewGenericFlag creates a new GenericFlag func (f *GenericFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped GenericFlag.Apply func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a generic value to the flagSet if required type InputSourceContext interface { Source() string Int(name string) (int, error) Int64(name string) (int64, error) Uint(name string) (uint, error) Uint64(name string) (uint64, error) Duration(name string) (time.Duration, error) Float64(name string) (float64, error) String(name string) (string, error) StringSlice(name string) ([]string, error) IntSlice(name string) ([]int, error) Int64Slice(name string) ([]int64, error) Float64Slice(name string) ([]float64, error) Generic(name string) (cli.Generic, error) Bool(name string) (bool, error) // Has unexported methods. } InputSourceContext is an interface used to allow other input sources to be implemented as needed. Source returns an identifier for the input source. In case of file source it should return path to the file. func NewJSONSource(data []byte) (InputSourceContext, error) NewJSONSource returns an InputSourceContext suitable for retrieving config variables from raw JSON data. func NewJSONSourceFromFile(f string) (InputSourceContext, error) NewJSONSourceFromFile returns an InputSourceContext suitable for retrieving config variables from a file (or url) containing JSON data. func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) NewJSONSourceFromReader returns an InputSourceContext suitable for retrieving config variables from an io.Reader that returns JSON data. func NewTomlSourceFromFile(file string) (InputSourceContext, error) NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. func NewYamlSourceFromFile(file string) (InputSourceContext, error) NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. type Int64Flag struct { *cli.Int64Flag // Has unexported fields. } Int64Flag is the flag type that wraps cli.Int64Flag to allow for other values to be specified func NewInt64Flag(fl *cli.Int64Flag) *Int64Flag NewInt64Flag creates a new Int64Flag func (f *Int64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Int64Flag.Apply func (f *Int64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error type Int64SliceFlag struct { *cli.Int64SliceFlag // Has unexported fields. } Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow for other values to be specified func NewInt64SliceFlag(fl *cli.Int64SliceFlag) *Int64SliceFlag NewInt64SliceFlag creates a new Int64SliceFlag func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Int64SliceFlag.Apply func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a Int64Slice value if required type IntFlag struct { *cli.IntFlag // Has unexported fields. } IntFlag is the flag type that wraps cli.IntFlag to allow for other values to be specified func NewIntFlag(fl *cli.IntFlag) *IntFlag NewIntFlag creates a new IntFlag func (f *IntFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped IntFlag.Apply func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a int value to the flagSet if required type IntSliceFlag struct { *cli.IntSliceFlag // Has unexported fields. } IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow for other values to be specified func NewIntSliceFlag(fl *cli.IntSliceFlag) *IntSliceFlag NewIntSliceFlag creates a new IntSliceFlag func (f *IntSliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped IntSliceFlag.Apply func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a IntSlice value if required type MapInputSource struct { // Has unexported fields. } MapInputSource implements InputSourceContext to return data from the map that is loaded. func NewMapInputSource(file string, valueMap map[interface{}]interface{}) *MapInputSource NewMapInputSource creates a new MapInputSource for implementing custom input sources. func (fsm *MapInputSource) Bool(name string) (bool, error) Bool returns an bool from the map otherwise returns false func (fsm *MapInputSource) Duration(name string) (time.Duration, error) Duration returns a duration from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Float64(name string) (float64, error) Float64 returns an float64 from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Float64Slice(name string) ([]float64, error) Float64Slice returns an []float64 from the map if it exists otherwise returns nil func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) Generic returns an cli.Generic from the map if it exists otherwise returns nil func (fsm *MapInputSource) Int(name string) (int, error) Int returns an int from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Int64(name string) (int64, error) Int64 returns an int64 from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Int64Slice(name string) ([]int64, error) Int64Slice returns an []int64 from the map if it exists otherwise returns nil func (fsm *MapInputSource) IntSlice(name string) ([]int, error) IntSlice returns an []int from the map if it exists otherwise returns nil func (fsm *MapInputSource) Source() string Source returns the path of the source file func (fsm *MapInputSource) String(name string) (string, error) String returns a string from the map if it exists otherwise returns an empty string func (fsm *MapInputSource) StringSlice(name string) ([]string, error) StringSlice returns an []string from the map if it exists otherwise returns nil func (fsm *MapInputSource) Uint(name string) (uint, error) Int64 returns an int64 from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Uint64(name string) (uint64, error) UInt64 returns an uint64 from the map if it exists otherwise returns 0 type PathFlag struct { *cli.PathFlag // Has unexported fields. } PathFlag is the flag type that wraps cli.PathFlag to allow for other values to be specified func NewPathFlag(fl *cli.PathFlag) *PathFlag NewPathFlag creates a new PathFlag func (f *PathFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped PathFlag.Apply func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a Path value to the flagSet if required type StringFlag struct { *cli.StringFlag // Has unexported fields. } StringFlag is the flag type that wraps cli.StringFlag to allow for other values to be specified func NewStringFlag(fl *cli.StringFlag) *StringFlag NewStringFlag creates a new StringFlag func (f *StringFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped StringFlag.Apply func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a String value to the flagSet if required type StringSliceFlag struct { *cli.StringSliceFlag // Has unexported fields. } StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow for other values to be specified func NewStringSliceFlag(fl *cli.StringSliceFlag) *StringSliceFlag NewStringSliceFlag creates a new StringSliceFlag func (f *StringSliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped StringSliceFlag.Apply func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error ApplyInputSourceValue applies a StringSlice value to the flagSet if required type Uint64Flag struct { *cli.Uint64Flag // Has unexported fields. } Uint64Flag is the flag type that wraps cli.Uint64Flag to allow for other values to be specified func NewUint64Flag(fl *cli.Uint64Flag) *Uint64Flag NewUint64Flag creates a new Uint64Flag func (f *Uint64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Uint64Flag.Apply func (f *Uint64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error type UintFlag struct { *cli.UintFlag // Has unexported fields. } UintFlag is the flag type that wraps cli.UintFlag to allow for other values to be specified func NewUintFlag(fl *cli.UintFlag) *UintFlag NewUintFlag creates a new UintFlag func (f *UintFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped UintFlag.Apply func (f *UintFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error png2svg-1.7.0/vendor/github.com/urfave/cli/v2/help.go000066400000000000000000000357121504213346300223170ustar00rootroot00000000000000package cli import ( "fmt" "io" "os" "strings" "text/tabwriter" "text/template" "unicode/utf8" ) const ( helpName = "help" helpAlias = "h" ) // this instance is to avoid recursion in the ShowCommandHelp which can // add a help command again var helpCommandDontUse = &Command{ Name: helpName, Aliases: []string{helpAlias}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", } var helpCommand = &Command{ Name: helpName, Aliases: []string{helpAlias}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", Action: func(cCtx *Context) error { args := cCtx.Args() argsPresent := args.First() != "" firstArg := args.First() // This action can be triggered by a "default" action of a command // or via cmd.Run when cmd == helpCmd. So we have following possibilities // // 1 $ app // 2 $ app help // 3 $ app foo // 4 $ app help foo // 5 $ app foo help // 6 $ app foo -h (with no other sub-commands nor flags defined) // Case 4. when executing a help command set the context to parent // to allow resolution of subsequent args. This will transform // $ app help foo // to // $ app foo // which will then be handled as case 3 if cCtx.Command.Name == helpName || cCtx.Command.Name == helpAlias { cCtx = cCtx.parentContext } // Case 4. $ app help foo // foo is the command for which help needs to be shown if argsPresent { return ShowCommandHelp(cCtx, firstArg) } // Case 1 & 2 // Special case when running help on main app itself as opposed to individual // commands/subcommands if cCtx.parentContext.App == nil { _ = ShowAppHelp(cCtx) return nil } // Case 3, 5 if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp && !cCtx.Command.HideHelpCommand) || (len(cCtx.Command.Subcommands) == 0 && cCtx.Command.HideHelp) { templ := cCtx.Command.CustomHelpTemplate if templ == "" { templ = CommandHelpTemplate } HelpPrinter(cCtx.App.Writer, templ, cCtx.Command) return nil } // Case 6, handling incorporated in the callee itself return ShowSubcommandHelp(cCtx) }, } // Prints help for the App or Command type helpPrinter func(w io.Writer, templ string, data interface{}) // Prints help for the App or Command with custom template function. type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) // HelpPrinter is a function that writes the help output. If not set explicitly, // this calls HelpPrinterCustom using only the default template functions. // // If custom logic for printing help is required, this function can be // overridden. If the ExtraInfo field is defined on an App, this function // should not be modified, as HelpPrinterCustom will be used directly in order // to capture the extra information. var HelpPrinter helpPrinter = printHelp // HelpPrinterCustom is a function that writes the help output. It is used as // the default implementation of HelpPrinter, and may be called directly if // the ExtraInfo field is set on an App. // // In the default implementation, if the customFuncs argument contains a // "wrapAt" key, which is a function which takes no arguments and returns // an int, this int value will be used to produce a "wrap" function used // by the default template to wrap long lines. var HelpPrinterCustom helpPrinterCustom = printHelpCustom // VersionPrinter prints the version for the App var VersionPrinter = printVersion // ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. func ShowAppHelpAndExit(c *Context, exitCode int) { _ = ShowAppHelp(c) os.Exit(exitCode) } // ShowAppHelp is an action that displays the help. func ShowAppHelp(cCtx *Context) error { tpl := cCtx.App.CustomAppHelpTemplate if tpl == "" { tpl = AppHelpTemplate } if cCtx.App.ExtraInfo == nil { HelpPrinter(cCtx.App.Writer, tpl, cCtx.App) return nil } customAppData := func() map[string]interface{} { return map[string]interface{}{ "ExtraInfo": cCtx.App.ExtraInfo, } } HelpPrinterCustom(cCtx.App.Writer, tpl, cCtx.App, customAppData()) return nil } // DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultAppComplete(cCtx *Context) { DefaultCompleteWithFlags(nil)(cCtx) } func printCommandSuggestions(commands []*Command, writer io.Writer) { for _, command := range commands { if command.Hidden { continue } if strings.HasSuffix(os.Getenv("0"), "zsh") { for _, name := range command.Names() { _, _ = fmt.Fprintf(writer, "%s:%s\n", name, command.Usage) } } else { for _, name := range command.Names() { _, _ = fmt.Fprintf(writer, "%s\n", name) } } } } func cliArgContains(flagName string) bool { for _, name := range strings.Split(flagName, ",") { name = strings.TrimSpace(name) count := utf8.RuneCountInString(name) if count > 2 { count = 2 } flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) for _, a := range os.Args { if a == flag { return true } } } return false } func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { cur := strings.TrimPrefix(lastArg, "-") cur = strings.TrimPrefix(cur, "-") for _, flag := range flags { if bflag, ok := flag.(*BoolFlag); ok && bflag.Hidden { continue } for _, name := range flag.Names() { name = strings.TrimSpace(name) // this will get total count utf8 letters in flag name count := utf8.RuneCountInString(name) if count > 2 { count = 2 // reuse this count to generate single - or -- in flag completion } // if flag name has more than one utf8 letter and last argument in cli has -- prefix then // skip flag completion for short flags example -v or -x if strings.HasPrefix(lastArg, "--") && count == 1 { continue } // match if last argument matches this flag and it is not repeated if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(name) { flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) _, _ = fmt.Fprintln(writer, flagCompletion) } } } } func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) { return func(cCtx *Context) { var lastArg string // TODO: This shouldnt depend on os.Args rather it should // depend on root arguments passed to App if len(os.Args) > 2 { lastArg = os.Args[len(os.Args)-2] } if lastArg != "" { if strings.HasPrefix(lastArg, "-") { if cmd != nil { printFlagSuggestions(lastArg, cmd.Flags, cCtx.App.Writer) return } printFlagSuggestions(lastArg, cCtx.App.Flags, cCtx.App.Writer) return } } if cmd != nil { printCommandSuggestions(cmd.Subcommands, cCtx.App.Writer) return } printCommandSuggestions(cCtx.Command.Subcommands, cCtx.App.Writer) } } // ShowCommandHelpAndExit - exits with code after showing help func ShowCommandHelpAndExit(c *Context, command string, code int) { _ = ShowCommandHelp(c, command) os.Exit(code) } // ShowCommandHelp prints help for the given command func ShowCommandHelp(ctx *Context, command string) error { commands := ctx.App.Commands if ctx.Command.Subcommands != nil { commands = ctx.Command.Subcommands } for _, c := range commands { if c.HasName(command) { if !ctx.App.HideHelpCommand && !c.HasName(helpName) && len(c.Subcommands) != 0 && c.Command(helpName) == nil { c.Subcommands = append(c.Subcommands, helpCommandDontUse) } if !ctx.App.HideHelp && HelpFlag != nil { c.appendFlag(HelpFlag) } templ := c.CustomHelpTemplate if templ == "" { if len(c.Subcommands) == 0 { templ = CommandHelpTemplate } else { templ = SubcommandHelpTemplate } } HelpPrinter(ctx.App.Writer, templ, c) return nil } } if ctx.App.CommandNotFound == nil { errMsg := fmt.Sprintf("No help topic for '%v'", command) if ctx.App.Suggest && SuggestCommand != nil { if suggestion := SuggestCommand(ctx.Command.Subcommands, command); suggestion != "" { errMsg += ". " + suggestion } } return Exit(errMsg, 3) } ctx.App.CommandNotFound(ctx, command) return nil } // ShowSubcommandHelpAndExit - Prints help for the given subcommand and exits with exit code. func ShowSubcommandHelpAndExit(c *Context, exitCode int) { _ = ShowSubcommandHelp(c) os.Exit(exitCode) } // ShowSubcommandHelp prints help for the given subcommand func ShowSubcommandHelp(cCtx *Context) error { if cCtx == nil { return nil } // use custom template when provided (fixes #1703) templ := SubcommandHelpTemplate if cCtx.Command != nil && cCtx.Command.CustomHelpTemplate != "" { templ = cCtx.Command.CustomHelpTemplate } HelpPrinter(cCtx.App.Writer, templ, cCtx.Command) return nil } // ShowVersion prints the version number of the App func ShowVersion(cCtx *Context) { VersionPrinter(cCtx) } func printVersion(cCtx *Context) { _, _ = fmt.Fprintf(cCtx.App.Writer, "%v version %v\n", cCtx.App.Name, cCtx.App.Version) } // ShowCompletions prints the lists of commands within a given context func ShowCompletions(cCtx *Context) { c := cCtx.Command if c != nil && c.BashComplete != nil { c.BashComplete(cCtx) } } // ShowCommandCompletions prints the custom completions for a given command func ShowCommandCompletions(ctx *Context, command string) { c := ctx.Command.Command(command) if c != nil { if c.BashComplete != nil { c.BashComplete(ctx) } else { DefaultCompleteWithFlags(c)(ctx) } } } // printHelpCustom is the default implementation of HelpPrinterCustom. // // The customFuncs map will be combined with a default template.FuncMap to // allow using arbitrary functions in template rendering. func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs map[string]interface{}) { const maxLineLength = 10000 funcMap := template.FuncMap{ "join": strings.Join, "subtract": subtract, "indent": indent, "nindent": nindent, "trim": strings.TrimSpace, "wrap": func(input string, offset int) string { return wrap(input, offset, maxLineLength) }, "offset": offset, "offsetCommands": offsetCommands, } if customFuncs["wrapAt"] != nil { if wa, ok := customFuncs["wrapAt"]; ok { if waf, ok := wa.(func() int); ok { wrapAt := waf() customFuncs["wrap"] = func(input string, offset int) string { return wrap(input, offset, wrapAt) } } } } for key, value := range customFuncs { funcMap[key] = value } w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) templates := map[string]string{ "helpNameTemplate": helpNameTemplate, "usageTemplate": usageTemplate, "descriptionTemplate": descriptionTemplate, "visibleCommandTemplate": visibleCommandTemplate, "copyrightTemplate": copyrightTemplate, "versionTemplate": versionTemplate, "visibleFlagCategoryTemplate": visibleFlagCategoryTemplate, "visibleFlagTemplate": visibleFlagTemplate, "visibleGlobalFlagCategoryTemplate": strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1), "authorsTemplate": authorsTemplate, "visibleCommandCategoryTemplate": visibleCommandCategoryTemplate, } for name, value := range templates { if _, err := t.New(name).Parse(value); err != nil { if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { _, _ = fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) } } } err := t.Execute(w, data) if err != nil { // If the writer is closed, t.Execute will fail, and there's nothing // we can do to recover. if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { _, _ = fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) } return } _ = w.Flush() } func printHelp(out io.Writer, templ string, data interface{}) { HelpPrinterCustom(out, templ, data, nil) } func checkVersion(cCtx *Context) bool { found := false for _, name := range VersionFlag.Names() { if cCtx.Bool(name) { found = true } } return found } func checkHelp(cCtx *Context) bool { if HelpFlag == nil { return false } found := false for _, name := range HelpFlag.Names() { if cCtx.Bool(name) { found = true break } } return found } func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { if !a.EnableBashCompletion { return false, arguments } pos := len(arguments) - 1 lastArg := arguments[pos] if lastArg != "--generate-bash-completion" { return false, arguments } for _, arg := range arguments { // If arguments include "--", shell completion is disabled // because after "--" only positional arguments are accepted. // https://unix.stackexchange.com/a/11382 if arg == "--" { return false, arguments } } return true, arguments[:pos] } func checkCompletions(cCtx *Context) bool { if !cCtx.shellComplete { return false } if args := cCtx.Args(); args.Present() { name := args.First() if cmd := cCtx.Command.Command(name); cmd != nil { // let the command handle the completion return false } } ShowCompletions(cCtx) return true } func subtract(a, b int) int { return a - b } func indent(spaces int, v string) string { pad := strings.Repeat(" ", spaces) return pad + strings.Replace(v, "\n", "\n"+pad, -1) } func nindent(spaces int, v string) string { return "\n" + indent(spaces, v) } func wrap(input string, offset int, wrapAt int) string { var ss []string lines := strings.Split(input, "\n") padding := strings.Repeat(" ", offset) for i, line := range lines { if line == "" { ss = append(ss, line) } else { wrapped := wrapLine(line, offset, wrapAt, padding) if i == 0 { ss = append(ss, wrapped) } else { ss = append(ss, padding+wrapped) } } } return strings.Join(ss, "\n") } func wrapLine(input string, offset int, wrapAt int, padding string) string { if wrapAt <= offset || len(input) <= wrapAt-offset { return input } lineWidth := wrapAt - offset words := strings.Fields(input) if len(words) == 0 { return input } wrapped := words[0] spaceLeft := lineWidth - len(wrapped) for _, word := range words[1:] { if len(word)+1 > spaceLeft { wrapped += "\n" + padding + word spaceLeft = lineWidth - len(word) } else { wrapped += " " + word spaceLeft -= 1 + len(word) } } return wrapped } func offset(input string, fixed int) int { return len(input) + fixed } // this function tries to find the max width of the names column // so say we have the following rows for help // // foo1, foo2, foo3 some string here // bar1, b2 some other string here // // We want to offset the 2nd row usage by some amount so that everything // is aligned // // foo1, foo2, foo3 some string here // bar1, b2 some other string here // // to find that offset we find the length of all the rows and use the max // to calculate the offset func offsetCommands(cmds []*Command, fixed int) int { var max int = 0 for _, cmd := range cmds { s := strings.Join(cmd.Names(), ", ") if len(s) > max { max = len(s) } } return max + fixed } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/mkdocs-reqs.txt000066400000000000000000000001771504213346300240260ustar00rootroot00000000000000mkdocs-git-revision-date-localized-plugin~=1.0 mkdocs-material-extensions~=1.0 mkdocs-material~=8.2 mkdocs~=1.3 pygments~=2.12 png2svg-1.7.0/vendor/github.com/urfave/cli/v2/mkdocs.yml000066400000000000000000000066111504213346300230370ustar00rootroot00000000000000# NOTE: the mkdocs dependencies will need to be installed out of # band until this whole thing gets more automated: # # pip install -r mkdocs-reqs.txt # site_name: urfave/cli site_url: https://cli.urfave.org/ repo_url: https://github.com/urfave/cli edit_uri: edit/main/docs/ nav: - Home: - Welcome: index.md - Contributing: CONTRIBUTING.md - Code of Conduct: CODE_OF_CONDUCT.md - Releasing: RELEASING.md - Security: SECURITY.md - Migrate v1 to v2: migrate-v1-to-v2.md - v2 Manual: - Getting Started: v2/getting-started.md - Migrating From Older Releases: v2/migrating-from-older-releases.md - Examples: - Greet: v2/examples/greet.md - Arguments: v2/examples/arguments.md - Flags: v2/examples/flags.md - Subcommands: v2/examples/subcommands.md - Subcommands Categories: v2/examples/subcommands-categories.md - Exit Codes: v2/examples/exit-codes.md - Combining Short Options: v2/examples/combining-short-options.md - Bash Completions: v2/examples/bash-completions.md - Generated Help Text: v2/examples/generated-help-text.md - Version Flag: v2/examples/version-flag.md - Timestamp Flag: v2/examples/timestamp-flag.md - Suggestions: v2/examples/suggestions.md - Full API Example: v2/examples/full-api-example.md - v1 Manual: - Getting Started: v1/getting-started.md - Migrating to v2: v1/migrating-to-v2.md - Examples: - Greet: v1/examples/greet.md - Arguments: v1/examples/arguments.md - Flags: v1/examples/flags.md - Subcommands: v1/examples/subcommands.md - Subcommands (Categories): v1/examples/subcommands-categories.md - Exit Codes: v1/examples/exit-codes.md - Combining Short Options: v1/examples/combining-short-options.md - Bash Completions: v1/examples/bash-completions.md - Generated Help Text: v1/examples/generated-help-text.md - Version Flag: v1/examples/version-flag.md theme: name: material palette: - media: "(prefers-color-scheme: light)" scheme: default toggle: icon: material/brightness-4 name: dark mode - media: "(prefers-color-scheme: dark)" scheme: slate toggle: icon: material/brightness-7 name: light mode features: - content.code.annotate - navigation.top - navigation.instant - navigation.expand - navigation.sections - navigation.tabs - navigation.tabs.sticky plugins: - git-revision-date-localized - search - tags # NOTE: this is the recommended configuration from # https://squidfunk.github.io/mkdocs-material/setup/extensions/#recommended-configuration markdown_extensions: - abbr - admonition - attr_list - def_list - footnotes - meta - md_in_html - toc: permalink: true - pymdownx.arithmatex: generic: true - pymdownx.betterem: smart_enable: all - pymdownx.caret - pymdownx.details - pymdownx.emoji: emoji_index: !!python/name:materialx.emoji.twemoji emoji_generator: !!python/name:materialx.emoji.to_svg - pymdownx.highlight - pymdownx.inlinehilite - pymdownx.keys - pymdownx.mark - pymdownx.smartsymbols - pymdownx.superfences - pymdownx.tabbed: alternate_style: true - pymdownx.tasklist: custom_checkbox: true - pymdownx.tilde png2svg-1.7.0/vendor/github.com/urfave/cli/v2/parse.go000066400000000000000000000053701504213346300224760ustar00rootroot00000000000000package cli import ( "flag" "strings" ) type iterativeParser interface { newFlagSet() (*flag.FlagSet, error) useShortOptionHandling() bool } // To enable short-option handling (e.g., "-it" vs "-i -t") we have to // iteratively catch parsing errors. This way we achieve LR parsing without // transforming any arguments. Otherwise, there is no way we can discriminate // combined short options from common arguments that should be left untouched. // Pass `shellComplete` to continue parsing options on failure during shell // completion when, the user-supplied options may be incomplete. func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComplete bool) error { for { err := set.Parse(args) if !ip.useShortOptionHandling() || err == nil { if shellComplete { return nil } return err } trimmed, trimErr := flagFromError(err) if trimErr != nil { return err } // regenerate the initial args with the split short opts argsWereSplit := false for i, arg := range args { // skip args that are not part of the error message if name := strings.TrimLeft(arg, "-"); name != trimmed { continue } // if we can't split, the error was accurate shortOpts := splitShortOptions(set, arg) if len(shortOpts) == 1 { return err } // swap current argument with the split version // do not include args that parsed correctly so far as it would // trigger Value.Set() on those args and would result in // duplicates for slice type flags args = append(shortOpts, args[i+1:]...) argsWereSplit = true break } // This should be an impossible to reach code path, but in case the arg // splitting failed to happen, this will prevent infinite loops if !argsWereSplit { return err } } } const providedButNotDefinedErrMsg = "flag provided but not defined: -" // flagFromError tries to parse a provided flag from an error message. If the // parsing fials, it returns the input error and an empty string func flagFromError(err error) (string, error) { errStr := err.Error() trimmed := strings.TrimPrefix(errStr, providedButNotDefinedErrMsg) if errStr == trimmed { return "", err } return trimmed, nil } func splitShortOptions(set *flag.FlagSet, arg string) []string { shortFlagsExist := func(s string) bool { for _, c := range s[1:] { if f := set.Lookup(string(c)); f == nil { return false } } return true } if !isSplittable(arg) || !shortFlagsExist(arg) { return []string{arg} } separated := make([]string, 0, len(arg)-1) for _, flagChar := range arg[1:] { separated = append(separated, "-"+string(flagChar)) } return separated } func isSplittable(flagArg string) bool { return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2 } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/sliceflag.go000066400000000000000000000231601504213346300233120ustar00rootroot00000000000000package cli import ( "flag" "reflect" ) type ( // SliceFlag extends implementations like StringSliceFlag and IntSliceFlag with support for using slices directly, // as Value and/or Destination. // See also SliceFlagTarget, MultiStringFlag, MultiFloat64Flag, MultiInt64Flag, MultiIntFlag. SliceFlag[T SliceFlagTarget[E], S ~[]E, E any] struct { Target T Value S Destination *S } // SliceFlagTarget models a target implementation for use with SliceFlag. // The three methods, SetValue, SetDestination, and GetDestination, are necessary to propagate Value and // Destination, where Value is propagated inwards (initially), and Destination is propagated outwards (on every // update). SliceFlagTarget[E any] interface { Flag RequiredFlag DocGenerationFlag VisibleFlag CategorizableFlag // SetValue should propagate the given slice to the target, ideally as a new value. // Note that a nil slice should nil/clear any existing value (modelled as ~[]E). SetValue(slice []E) // SetDestination should propagate the given slice to the target, ideally as a new value. // Note that a nil slice should nil/clear any existing value (modelled as ~*[]E). SetDestination(slice []E) // GetDestination should return the current value referenced by any destination, or nil if nil/unset. GetDestination() []E } // MultiStringFlag extends StringSliceFlag with support for using slices directly, as Value and/or Destination. // See also SliceFlag. MultiStringFlag = SliceFlag[*StringSliceFlag, []string, string] // MultiFloat64Flag extends Float64SliceFlag with support for using slices directly, as Value and/or Destination. // See also SliceFlag. MultiFloat64Flag = SliceFlag[*Float64SliceFlag, []float64, float64] // MultiInt64Flag extends Int64SliceFlag with support for using slices directly, as Value and/or Destination. // See also SliceFlag. MultiInt64Flag = SliceFlag[*Int64SliceFlag, []int64, int64] // MultiIntFlag extends IntSliceFlag with support for using slices directly, as Value and/or Destination. // See also SliceFlag. MultiIntFlag = SliceFlag[*IntSliceFlag, []int, int] flagValueHook struct { value Generic hook func() } ) var ( // compile time assertions _ SliceFlagTarget[string] = (*StringSliceFlag)(nil) _ SliceFlagTarget[string] = (*SliceFlag[*StringSliceFlag, []string, string])(nil) _ SliceFlagTarget[string] = (*MultiStringFlag)(nil) _ SliceFlagTarget[float64] = (*MultiFloat64Flag)(nil) _ SliceFlagTarget[int64] = (*MultiInt64Flag)(nil) _ SliceFlagTarget[int] = (*MultiIntFlag)(nil) _ Generic = (*flagValueHook)(nil) _ Serializer = (*flagValueHook)(nil) ) func (x *SliceFlag[T, S, E]) Apply(set *flag.FlagSet) error { x.Target.SetValue(x.convertSlice(x.Value)) destination := x.Destination if destination == nil { x.Target.SetDestination(nil) return x.Target.Apply(set) } x.Target.SetDestination(x.convertSlice(*destination)) return applyFlagValueHook(set, x.Target.Apply, func() { *destination = x.Target.GetDestination() }) } func (x *SliceFlag[T, S, E]) convertSlice(slice S) []E { result := make([]E, len(slice)) copy(result, slice) return result } func (x *SliceFlag[T, S, E]) SetValue(slice S) { x.Value = slice } func (x *SliceFlag[T, S, E]) SetDestination(slice S) { if slice != nil { x.Destination = &slice } else { x.Destination = nil } } func (x *SliceFlag[T, S, E]) GetDestination() S { if destination := x.Destination; destination != nil { return *destination } return nil } func (x *SliceFlag[T, S, E]) String() string { return x.Target.String() } func (x *SliceFlag[T, S, E]) Names() []string { return x.Target.Names() } func (x *SliceFlag[T, S, E]) IsSet() bool { return x.Target.IsSet() } func (x *SliceFlag[T, S, E]) IsRequired() bool { return x.Target.IsRequired() } func (x *SliceFlag[T, S, E]) TakesValue() bool { return x.Target.TakesValue() } func (x *SliceFlag[T, S, E]) GetUsage() string { return x.Target.GetUsage() } func (x *SliceFlag[T, S, E]) GetValue() string { return x.Target.GetValue() } func (x *SliceFlag[T, S, E]) GetDefaultText() string { return x.Target.GetDefaultText() } func (x *SliceFlag[T, S, E]) GetEnvVars() []string { return x.Target.GetEnvVars() } func (x *SliceFlag[T, S, E]) IsVisible() bool { return x.Target.IsVisible() } func (x *SliceFlag[T, S, E]) GetCategory() string { return x.Target.GetCategory() } func (x *flagValueHook) Set(value string) error { if err := x.value.Set(value); err != nil { return err } x.hook() return nil } func (x *flagValueHook) String() string { // note: this is necessary due to the way Go's flag package handles defaults isZeroValue := func(f flag.Value, v string) bool { /* https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/flag/flag.go;drc=2580d0e08d5e9f979b943758d3c49877fb2324cb;l=453 Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Build a zero value of the flag's Value type, and see if the // result of calling its String method equals the value passed in. // This works unless the Value type is itself an interface type. typ := reflect.TypeOf(f) var z reflect.Value if typ.Kind() == reflect.Pointer { z = reflect.New(typ.Elem()) } else { z = reflect.Zero(typ) } return v == z.Interface().(flag.Value).String() } if x.value != nil { // only return non-empty if not the same string as returned by the zero value if s := x.value.String(); !isZeroValue(x.value, s) { return s } } return `` } func (x *flagValueHook) Serialize() string { if value, ok := x.value.(Serializer); ok { return value.Serialize() } return x.String() } // applyFlagValueHook wraps calls apply then wraps flags to call a hook function on update and after initial apply. func applyFlagValueHook(set *flag.FlagSet, apply func(set *flag.FlagSet) error, hook func()) error { if apply == nil || set == nil || hook == nil { panic(`invalid input`) } var tmp flag.FlagSet if err := apply(&tmp); err != nil { return err } tmp.VisitAll(func(f *flag.Flag) { set.Var(&flagValueHook{value: f.Value, hook: hook}, f.Name, f.Usage) }) hook() return nil } // newSliceFlagValue is for implementing SliceFlagTarget.SetValue and SliceFlagTarget.SetDestination. // It's e.g. as part of StringSliceFlag.SetValue, using the factory NewStringSlice. func newSliceFlagValue[R any, S ~[]E, E any](factory func(defaults ...E) *R, defaults S) *R { if defaults == nil { return nil } return factory(defaults...) } // unwrapFlagValue strips any/all *flagValueHook wrappers. func unwrapFlagValue(v flag.Value) flag.Value { for { h, ok := v.(*flagValueHook) if !ok { return v } v = h.value } } // NOTE: the methods below are in this file to make use of the build constraint func (f *Float64SliceFlag) SetValue(slice []float64) { f.Value = newSliceFlagValue(NewFloat64Slice, slice) } func (f *Float64SliceFlag) SetDestination(slice []float64) { f.Destination = newSliceFlagValue(NewFloat64Slice, slice) } func (f *Float64SliceFlag) GetDestination() []float64 { if destination := f.Destination; destination != nil { return destination.Value() } return nil } func (f *Int64SliceFlag) SetValue(slice []int64) { f.Value = newSliceFlagValue(NewInt64Slice, slice) } func (f *Int64SliceFlag) SetDestination(slice []int64) { f.Destination = newSliceFlagValue(NewInt64Slice, slice) } func (f *Int64SliceFlag) GetDestination() []int64 { if destination := f.Destination; destination != nil { return destination.Value() } return nil } func (f *IntSliceFlag) SetValue(slice []int) { f.Value = newSliceFlagValue(NewIntSlice, slice) } func (f *IntSliceFlag) SetDestination(slice []int) { f.Destination = newSliceFlagValue(NewIntSlice, slice) } func (f *IntSliceFlag) GetDestination() []int { if destination := f.Destination; destination != nil { return destination.Value() } return nil } func (f *StringSliceFlag) SetValue(slice []string) { f.Value = newSliceFlagValue(NewStringSlice, slice) } func (f *StringSliceFlag) SetDestination(slice []string) { f.Destination = newSliceFlagValue(NewStringSlice, slice) } func (f *StringSliceFlag) GetDestination() []string { if destination := f.Destination; destination != nil { return destination.Value() } return nil } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/sort.go000066400000000000000000000010101504213346300223360ustar00rootroot00000000000000package cli import "unicode" // lexicographicLess compares strings alphabetically considering case. func lexicographicLess(i, j string) bool { iRunes := []rune(i) jRunes := []rune(j) lenShared := len(iRunes) if lenShared > len(jRunes) { lenShared = len(jRunes) } for index := 0; index < lenShared; index++ { ir := iRunes[index] jr := jRunes[index] if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr { return lir < ljr } if ir != jr { return ir < jr } } return i < j } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/suggestions.go000066400000000000000000000030501504213346300237270ustar00rootroot00000000000000//go:build !urfave_cli_no_suggest // +build !urfave_cli_no_suggest package cli import ( "fmt" "github.com/xrash/smetrics" ) func init() { SuggestFlag = suggestFlag SuggestCommand = suggestCommand } func jaroWinkler(a, b string) float64 { // magic values are from https://github.com/xrash/smetrics/blob/039620a656736e6ad994090895784a7af15e0b80/jaro-winkler.go#L8 const ( boostThreshold = 0.7 prefixSize = 4 ) return smetrics.JaroWinkler(a, b, boostThreshold, prefixSize) } func suggestFlag(flags []Flag, provided string, hideHelp bool) string { distance := 0.0 suggestion := "" for _, flag := range flags { flagNames := flag.Names() if !hideHelp && HelpFlag != nil { flagNames = append(flagNames, HelpFlag.Names()...) } for _, name := range flagNames { newDistance := jaroWinkler(name, provided) if newDistance > distance { distance = newDistance suggestion = name } } } if len(suggestion) == 1 { suggestion = "-" + suggestion } else if len(suggestion) > 1 { suggestion = "--" + suggestion } return suggestion } // suggestCommand takes a list of commands and a provided string to suggest a // command name func suggestCommand(commands []*Command, provided string) (suggestion string) { distance := 0.0 for _, command := range commands { for _, name := range append(command.Names(), helpName, helpAlias) { newDistance := jaroWinkler(name, provided) if newDistance > distance { distance = newDistance suggestion = name } } } return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion) } png2svg-1.7.0/vendor/github.com/urfave/cli/v2/template.go000066400000000000000000000115571504213346300232030ustar00rootroot00000000000000package cli var helpNameTemplate = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}` var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}` var descriptionTemplate = `{{wrap .Description 3}}` var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: {{range $index, $author := .Authors}}{{if $index}} {{end}}{{$author}}{{end}}` var visibleCommandTemplate = `{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}` var visibleCommandCategoryTemplate = `{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{template "visibleCommandTemplate" .}}{{end}}{{end}}` var visibleFlagCategoryTemplate = `{{range .VisibleFlagCategories}} {{if .Name}}{{.Name}} {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}} {{else}}{{$e}} {{end}}{{end}}{{end}}` var visibleFlagTemplate = `{{range $i, $e := .VisibleFlags}} {{wrap $e.String 6}}{{end}}` var versionTemplate = `{{if .Version}}{{if not .HideVersion}} VERSION: {{.Version}}{{end}}{{end}}` var copyrightTemplate = `{{wrap .Copyright 3}}` // AppHelpTemplate is the text template for the Default help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var AppHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}}{{if .ArgsUsage}} {{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} VERSION: {{.Version}}{{end}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}} {{- if len .Authors}} AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}} COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}} COPYRIGHT: {{template "copyrightTemplate" .}}{{end}} ` // CommandHelpTemplate is the text template for the command help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var CommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: {{template "usageTemplate" .}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} OPTIONS:{{template "visibleFlagTemplate" .}}{{end}} ` // SubcommandHelpTemplate is the text template for the subcommand help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var SubcommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: {{template "usageTemplate" .}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} OPTIONS:{{template "visibleFlagTemplate" .}}{{end}} ` var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }} {{end}}# NAME {{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }} # SYNOPSIS {{ .App.Name }} {{ if .SynopsisArgs }} ` + "```" + ` {{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + ` {{ end }}{{ if .App.Description }} # DESCRIPTION {{ .App.Description }} {{ end }} **Usage**: ` + "```" + `{{ if .App.UsageText }} {{ .App.UsageText }} {{ else }} {{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] {{ end }}` + "```" + ` {{ if .GlobalArgs }} # GLOBAL OPTIONS {{ range $v := .GlobalArgs }} {{ $v }}{{ end }} {{ end }}{{ if .Commands }} # COMMANDS {{ range $v := .Commands }} {{ $v }}{{ end }}{{ end }}` var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' for i in (commandline -opc) if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} return 1 end end return 0 end {{ range $v := .Completions }}{{ $v }} {{ end }}` png2svg-1.7.0/vendor/github.com/urfave/cli/v2/zz_generated.flags.go000066400000000000000000000416621504213346300251440ustar00rootroot00000000000000// WARNING: this file is generated. DO NOT EDIT package cli import "time" // Float64SliceFlag is a flag with type *Float64Slice type Float64SliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *Float64Slice Destination *Float64Slice Aliases []string EnvVars []string defaultValue *Float64Slice defaultValueSet bool separator separatorSpec Action func(*Context, []float64) error } // IsSet returns whether or not the flag has been set through env or file func (f *Float64SliceFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *Float64SliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *Float64SliceFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *Float64SliceFlag) IsVisible() bool { return !f.Hidden } // GenericFlag is a flag with type Generic type GenericFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value Generic Destination Generic Aliases []string EnvVars []string defaultValue Generic defaultValueSet bool TakesFile bool Action func(*Context, interface{}) error } // String returns a readable representation of this value (for usage defaults) func (f *GenericFlag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *GenericFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *GenericFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *GenericFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *GenericFlag) IsVisible() bool { return !f.Hidden } // Int64SliceFlag is a flag with type *Int64Slice type Int64SliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *Int64Slice Destination *Int64Slice Aliases []string EnvVars []string defaultValue *Int64Slice defaultValueSet bool separator separatorSpec Action func(*Context, []int64) error } // IsSet returns whether or not the flag has been set through env or file func (f *Int64SliceFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *Int64SliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *Int64SliceFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *Int64SliceFlag) IsVisible() bool { return !f.Hidden } // IntSliceFlag is a flag with type *IntSlice type IntSliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *IntSlice Destination *IntSlice Aliases []string EnvVars []string defaultValue *IntSlice defaultValueSet bool separator separatorSpec Action func(*Context, []int) error } // IsSet returns whether or not the flag has been set through env or file func (f *IntSliceFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *IntSliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *IntSliceFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *IntSliceFlag) IsVisible() bool { return !f.Hidden } // PathFlag is a flag with type Path type PathFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value Path Destination *Path Aliases []string EnvVars []string defaultValue Path defaultValueSet bool TakesFile bool Action func(*Context, Path) error } // String returns a readable representation of this value (for usage defaults) func (f *PathFlag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *PathFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *PathFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *PathFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *PathFlag) IsVisible() bool { return !f.Hidden } // StringSliceFlag is a flag with type *StringSlice type StringSliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *StringSlice Destination *StringSlice Aliases []string EnvVars []string defaultValue *StringSlice defaultValueSet bool separator separatorSpec TakesFile bool Action func(*Context, []string) error KeepSpace bool } // IsSet returns whether or not the flag has been set through env or file func (f *StringSliceFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *StringSliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *StringSliceFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *StringSliceFlag) IsVisible() bool { return !f.Hidden } // TimestampFlag is a flag with type *Timestamp type TimestampFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *Timestamp Destination *Timestamp Aliases []string EnvVars []string defaultValue *Timestamp defaultValueSet bool Layout string Timezone *time.Location Action func(*Context, *time.Time) error } // String returns a readable representation of this value (for usage defaults) func (f *TimestampFlag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *TimestampFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *TimestampFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *TimestampFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *TimestampFlag) IsVisible() bool { return !f.Hidden } // Uint64SliceFlag is a flag with type *Uint64Slice type Uint64SliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *Uint64Slice Destination *Uint64Slice Aliases []string EnvVars []string defaultValue *Uint64Slice defaultValueSet bool separator separatorSpec Action func(*Context, []uint64) error } // IsSet returns whether or not the flag has been set through env or file func (f *Uint64SliceFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *Uint64SliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *Uint64SliceFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *Uint64SliceFlag) IsVisible() bool { return !f.Hidden } // UintSliceFlag is a flag with type *UintSlice type UintSliceFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value *UintSlice Destination *UintSlice Aliases []string EnvVars []string defaultValue *UintSlice defaultValueSet bool separator separatorSpec Action func(*Context, []uint) error } // IsSet returns whether or not the flag has been set through env or file func (f *UintSliceFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *UintSliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *UintSliceFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *UintSliceFlag) IsVisible() bool { return !f.Hidden } // BoolFlag is a flag with type bool type BoolFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value bool Destination *bool Aliases []string EnvVars []string defaultValue bool defaultValueSet bool Count *int DisableDefaultText bool Action func(*Context, bool) error } // String returns a readable representation of this value (for usage defaults) func (f *BoolFlag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *BoolFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *BoolFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *BoolFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *BoolFlag) IsVisible() bool { return !f.Hidden } // Float64Flag is a flag with type float64 type Float64Flag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value float64 Destination *float64 Aliases []string EnvVars []string defaultValue float64 defaultValueSet bool Action func(*Context, float64) error } // String returns a readable representation of this value (for usage defaults) func (f *Float64Flag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *Float64Flag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *Float64Flag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *Float64Flag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *Float64Flag) IsVisible() bool { return !f.Hidden } // IntFlag is a flag with type int type IntFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value int Destination *int Aliases []string EnvVars []string defaultValue int defaultValueSet bool Base int Action func(*Context, int) error } // String returns a readable representation of this value (for usage defaults) func (f *IntFlag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *IntFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *IntFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *IntFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *IntFlag) IsVisible() bool { return !f.Hidden } // Int64Flag is a flag with type int64 type Int64Flag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value int64 Destination *int64 Aliases []string EnvVars []string defaultValue int64 defaultValueSet bool Base int Action func(*Context, int64) error } // String returns a readable representation of this value (for usage defaults) func (f *Int64Flag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *Int64Flag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *Int64Flag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *Int64Flag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *Int64Flag) IsVisible() bool { return !f.Hidden } // StringFlag is a flag with type string type StringFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value string Destination *string Aliases []string EnvVars []string defaultValue string defaultValueSet bool TakesFile bool Action func(*Context, string) error } // String returns a readable representation of this value (for usage defaults) func (f *StringFlag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *StringFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *StringFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *StringFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *StringFlag) IsVisible() bool { return !f.Hidden } // DurationFlag is a flag with type time.Duration type DurationFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value time.Duration Destination *time.Duration Aliases []string EnvVars []string defaultValue time.Duration defaultValueSet bool Action func(*Context, time.Duration) error } // String returns a readable representation of this value (for usage defaults) func (f *DurationFlag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *DurationFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *DurationFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *DurationFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *DurationFlag) IsVisible() bool { return !f.Hidden } // UintFlag is a flag with type uint type UintFlag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value uint Destination *uint Aliases []string EnvVars []string defaultValue uint defaultValueSet bool Base int Action func(*Context, uint) error } // String returns a readable representation of this value (for usage defaults) func (f *UintFlag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *UintFlag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *UintFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *UintFlag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *UintFlag) IsVisible() bool { return !f.Hidden } // Uint64Flag is a flag with type uint64 type Uint64Flag struct { Name string Category string DefaultText string FilePath string Usage string Required bool Hidden bool HasBeenSet bool Value uint64 Destination *uint64 Aliases []string EnvVars []string defaultValue uint64 defaultValueSet bool Base int Action func(*Context, uint64) error } // String returns a readable representation of this value (for usage defaults) func (f *Uint64Flag) String() string { return FlagStringer(f) } // IsSet returns whether or not the flag has been set through env or file func (f *Uint64Flag) IsSet() bool { return f.HasBeenSet } // Names returns the names of the flag func (f *Uint64Flag) Names() []string { return FlagNames(f.Name, f.Aliases) } // IsRequired returns whether or not the flag is required func (f *Uint64Flag) IsRequired() bool { return f.Required } // IsVisible returns true if the flag is not hidden, otherwise false func (f *Uint64Flag) IsVisible() bool { return !f.Hidden } // vim:ro png2svg-1.7.0/vendor/github.com/xrash/000077500000000000000000000000001504213346300175675ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/xrash/smetrics/000077500000000000000000000000001504213346300214205ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/xrash/smetrics/.travis.yml000066400000000000000000000001531504213346300235300ustar00rootroot00000000000000language: go go: - 1.11 - 1.12 - 1.13 - 1.14.x - master script: - cd tests && make png2svg-1.7.0/vendor/github.com/xrash/smetrics/README.md000066400000000000000000000022721504213346300227020ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/xrash/smetrics.svg?branch=master)](http://travis-ci.org/xrash/smetrics) # smetrics `smetrics` is "string metrics". Package smetrics provides a bunch of algorithms for calculating the distance between strings. There are implementations for calculating the popular Levenshtein distance (aka Edit Distance or Wagner-Fischer), as well as the Jaro distance, the Jaro-Winkler distance, and more. # How to import ```go import "github.com/xrash/smetrics" ``` # Documentation Go to [https://pkg.go.dev/github.com/xrash/smetrics](https://pkg.go.dev/github.com/xrash/smetrics) for complete documentation. # Example ```go package main import ( "github.com/xrash/smetrics" ) func main() { smetrics.WagnerFischer("POTATO", "POTATTO", 1, 1, 2) smetrics.WagnerFischer("MOUSE", "HOUSE", 2, 2, 4) smetrics.Ukkonen("POTATO", "POTATTO", 1, 1, 2) smetrics.Ukkonen("MOUSE", "HOUSE", 2, 2, 4) smetrics.Jaro("AL", "AL") smetrics.Jaro("MARTHA", "MARHTA") smetrics.JaroWinkler("AL", "AL", 0.7, 4) smetrics.JaroWinkler("MARTHA", "MARHTA", 0.7, 4) smetrics.Soundex("Euler") smetrics.Soundex("Ellery") smetrics.Hamming("aaa", "aaa") smetrics.Hamming("aaa", "aab") } ``` png2svg-1.7.0/vendor/github.com/xrash/smetrics/doc.go000066400000000000000000000012571504213346300225210ustar00rootroot00000000000000/* Package smetrics provides a bunch of algorithms for calculating the distance between strings. There are implementations for calculating the popular Levenshtein distance (aka Edit Distance or Wagner-Fischer), as well as the Jaro distance, the Jaro-Winkler distance, and more. For the Levenshtein distance, you can use the functions WagnerFischer() and Ukkonen(). Read the documentation on these functions. For the Jaro and Jaro-Winkler algorithms, check the functions Jaro() and JaroWinkler(). Read the documentation on these functions. For the Soundex algorithm, check the function Soundex(). For the Hamming distance algorithm, check the function Hamming(). */ package smetrics png2svg-1.7.0/vendor/github.com/xrash/smetrics/hamming.go000066400000000000000000000010401504213346300233620ustar00rootroot00000000000000package smetrics import ( "fmt" ) // The Hamming distance is the minimum number of substitutions required to change string A into string B. Both strings must have the same size. If the strings have different sizes, the function returns an error. func Hamming(a, b string) (int, error) { al := len(a) bl := len(b) if al != bl { return -1, fmt.Errorf("strings are not equal (len(a)=%d, len(b)=%d)", al, bl) } var difference = 0 for i := range a { if a[i] != b[i] { difference = difference + 1 } } return difference, nil } png2svg-1.7.0/vendor/github.com/xrash/smetrics/jaro-winkler.go000066400000000000000000000017471504213346300243640ustar00rootroot00000000000000package smetrics import ( "math" ) // The Jaro-Winkler distance. The result is 1 for equal strings, and 0 for completely different strings. It is commonly used on Record Linkage stuff, thus it tries to be accurate for common typos when writing real names such as person names and street names. // Jaro-Winkler is a modification of the Jaro algorithm. It works by first running Jaro, then boosting the score of exact matches at the beginning of the strings. Because of that, it introduces two more parameters: the boostThreshold and the prefixSize. These are commonly set to 0.7 and 4, respectively. func JaroWinkler(a, b string, boostThreshold float64, prefixSize int) float64 { j := Jaro(a, b) if j <= boostThreshold { return j } prefixSize = int(math.Min(float64(len(a)), math.Min(float64(prefixSize), float64(len(b))))) var prefixMatch float64 for i := 0; i < prefixSize; i++ { if a[i] == b[i] { prefixMatch++ } else { break } } return j + 0.1*prefixMatch*(1.0-j) } png2svg-1.7.0/vendor/github.com/xrash/smetrics/jaro.go000066400000000000000000000040011504213346300226750ustar00rootroot00000000000000package smetrics import ( "math" ) // The Jaro distance. The result is 1 for equal strings, and 0 for completely different strings. func Jaro(a, b string) float64 { // If both strings are zero-length, they are completely equal, // therefore return 1. if len(a) == 0 && len(b) == 0 { return 1 } // If one string is zero-length, strings are completely different, // therefore return 0. if len(a) == 0 || len(b) == 0 { return 0 } // Define the necessary variables for the algorithm. la := float64(len(a)) lb := float64(len(b)) matchRange := int(math.Max(0, math.Floor(math.Max(la, lb)/2.0)-1)) matchesA := make([]bool, len(a)) matchesB := make([]bool, len(b)) var matches float64 = 0 // Step 1: Matches // Loop through each character of the first string, // looking for a matching character in the second string. for i := 0; i < len(a); i++ { start := int(math.Max(0, float64(i-matchRange))) end := int(math.Min(lb-1, float64(i+matchRange))) for j := start; j <= end; j++ { if matchesB[j] { continue } if a[i] == b[j] { matchesA[i] = true matchesB[j] = true matches++ break } } } // If there are no matches, strings are completely different, // therefore return 0. if matches == 0 { return 0 } // Step 2: Transpositions // Loop through the matches' arrays, looking for // unaligned matches. Count the number of unaligned matches. unaligned := 0 j := 0 for i := 0; i < len(a); i++ { if !matchesA[i] { continue } for !matchesB[j] { j++ } if a[i] != b[j] { unaligned++ } j++ } // The number of unaligned matches divided by two, is the number of _transpositions_. transpositions := math.Floor(float64(unaligned) / 2) // Jaro distance is the average between these three numbers: // 1. matches / length of string A // 2. matches / length of string B // 3. (matches - transpositions/matches) // So, all that divided by three is the final result. return ((matches / la) + (matches / lb) + ((matches - transpositions) / matches)) / 3.0 } png2svg-1.7.0/vendor/github.com/xrash/smetrics/soundex.go000066400000000000000000000020141504213346300234310ustar00rootroot00000000000000package smetrics import ( "strings" ) // The Soundex encoding. It is a phonetic algorithm that considers how the words sound in English. Soundex maps a string to a 4-byte code consisting of the first letter of the original string and three numbers. Strings that sound similar should map to the same code. func Soundex(s string) string { b := strings.Builder{} b.Grow(4) p := s[0] if p <= 'z' && p >= 'a' { p -= 32 // convert to uppercase } b.WriteByte(p) n := 0 for i := 1; i < len(s); i++ { c := s[i] if c <= 'z' && c >= 'a' { c -= 32 // convert to uppercase } else if c < 'A' || c > 'Z' { continue } if c == p { continue } p = c switch c { case 'B', 'P', 'F', 'V': c = '1' case 'C', 'S', 'K', 'G', 'J', 'Q', 'X', 'Z': c = '2' case 'D', 'T': c = '3' case 'L': c = '4' case 'M', 'N': c = '5' case 'R': c = '6' default: continue } b.WriteByte(c) n++ if n == 3 { break } } for i := n; i < 3; i++ { b.WriteByte('0') } return b.String() } png2svg-1.7.0/vendor/github.com/xrash/smetrics/ukkonen.go000066400000000000000000000044261504213346300234270ustar00rootroot00000000000000package smetrics import ( "math" ) // The Ukkonen algorithm for calculating the Levenshtein distance. The algorithm is described in http://www.cs.helsinki.fi/u/ukkonen/InfCont85.PDF, or in docs/InfCont85.PDF. It runs on O(t . min(m, n)) where t is the actual distance between strings a and b. It needs O(min(t, m, n)) space. This function might be preferred over WagnerFischer() for *very* similar strings. But test it out yourself. // The first two parameters are the two strings to be compared. The last three parameters are the insertion cost, the deletion cost and the substitution cost. These are normally defined as 1, 1 and 2 respectively. func Ukkonen(a, b string, icost, dcost, scost int) int { var lowerCost int if icost < dcost && icost < scost { lowerCost = icost } else if dcost < scost { lowerCost = dcost } else { lowerCost = scost } infinite := math.MaxInt32 / 2 var r []int var k, kprime, p, t int var ins, del, sub int if len(a) > len(b) { t = (len(a) - len(b) + 1) * lowerCost } else { t = (len(b) - len(a) + 1) * lowerCost } for { if (t / lowerCost) < (len(b) - len(a)) { continue } // This is the right damn thing since the original Ukkonen // paper minimizes the expression result only, but the uncommented version // doesn't need to deal with floats so it's faster. // p = int(math.Floor(0.5*((float64(t)/float64(lowerCost)) - float64(len(b) - len(a))))) p = ((t / lowerCost) - (len(b) - len(a))) / 2 k = -p kprime = k rowlength := (len(b) - len(a)) + (2 * p) r = make([]int, rowlength+2) for i := 0; i < rowlength+2; i++ { r[i] = infinite } for i := 0; i <= len(a); i++ { for j := 0; j <= rowlength; j++ { if i == j+k && i == 0 { r[j] = 0 } else { if j-1 < 0 { ins = infinite } else { ins = r[j-1] + icost } del = r[j+1] + dcost sub = r[j] + scost if i-1 < 0 || i-1 >= len(a) || j+k-1 >= len(b) || j+k-1 < 0 { sub = infinite } else if a[i-1] == b[j+k-1] { sub = r[j] } if ins < del && ins < sub { r[j] = ins } else if del < sub { r[j] = del } else { r[j] = sub } } } k++ } if r[(len(b)-len(a))+(2*p)+kprime] <= t { break } else { t *= 2 } } return r[(len(b)-len(a))+(2*p)+kprime] } png2svg-1.7.0/vendor/github.com/xrash/smetrics/wagner-fischer.go000066400000000000000000000022301504213346300246500ustar00rootroot00000000000000package smetrics // The Wagner-Fischer algorithm for calculating the Levenshtein distance. // The first two parameters are the two strings to be compared. The last three parameters are the insertion cost, the deletion cost and the substitution cost. These are normally defined as 1, 1 and 2 respectively. func WagnerFischer(a, b string, icost, dcost, scost int) int { // Allocate both rows. row1 := make([]int, len(b)+1) row2 := make([]int, len(b)+1) var tmp []int // Initialize the first row. for i := 1; i <= len(b); i++ { row1[i] = i * icost } // For each row... for i := 1; i <= len(a); i++ { row2[0] = i * dcost // For each column... for j := 1; j <= len(b); j++ { if a[i-1] == b[j-1] { row2[j] = row1[j-1] } else { ins := row2[j-1] + icost del := row1[j] + dcost sub := row1[j-1] + scost if ins < del && ins < sub { row2[j] = ins } else if del < sub { row2[j] = del } else { row2[j] = sub } } } // Swap the rows at the end of each row. tmp = row1 row1 = row2 row2 = tmp } // Because we swapped the rows, the final result is in row1 instead of row2. return row1[len(row1)-1] } png2svg-1.7.0/vendor/github.com/xyproto/000077500000000000000000000000001504213346300201665ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/xyproto/burnpal/000077500000000000000000000000001504213346300216315ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/xyproto/burnpal/.gitignore000066400000000000000000000003521504213346300236210ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out cmd/generate/*.png cmd/generate/generate png2svg-1.7.0/vendor/github.com/xyproto/burnpal/.travis.yml000066400000000000000000000000711504213346300237400ustar00rootroot00000000000000language: go go: - "1.11" - "1.12" - "1.13" png2svg-1.7.0/vendor/github.com/xyproto/burnpal/README.md000066400000000000000000000012471504213346300231140ustar00rootroot00000000000000# burnpal [![GoDoc](https://godoc.org/github.com/xyproto/burnpal?status.svg)](http://godoc.org/github.com/xyproto/burnpal) A predefined palette, inspired by my old pixling program for DOS. ### Features and limitations * Compatible with [peterhellberg/gfx](https://github.com/peterhellberg/gfx). * Compatible with [img/color/Palette](https://golang.org/pkg/image/color/#Palette). ### Renders ![palette](img/palette.png) ![palette](img/burn-unsorted.png) ![palette](img/gfx-burn-palette.png) ![palette](img/gfx-burn-simplex.png) ### Example use * See the code in `cmd/generate`. ### General information * Version: 1.0.0 * License: BSD-3 * Author: Alexander F. Rødseth png2svg-1.7.0/vendor/github.com/xyproto/burnpal/burnpal.go000066400000000000000000000211761504213346300236320ustar00rootroot00000000000000package burnpal import ( "image/color" ) // Palette is a slice of NRGBA colors type Palette []color.NRGBA // Pal is the default palette from the open source drawing program // named "Burn". https://burn.sf.net/ var Pal = Palette{ {0x0, 0x0, 0x0, 0xFF}, // 0 {0x50, 0x50, 0x50, 0xFF}, // 1 {0x88, 0x88, 0x88, 0xFF}, // 2 {0x0, 0x0, 0x0, 0xFF}, // 3 {0x8, 0x8, 0x8, 0xFF}, // 4 {0x10, 0x10, 0x10, 0xFF}, // 5 {0x18, 0x18, 0x18, 0xFF}, // 6 {0x24, 0x24, 0x24, 0xFF}, // 7 {0x2C, 0x2C, 0x2C, 0xFF}, // 8 {0x34, 0x34, 0x34, 0xFF}, // 9 {0x3C, 0x3C, 0x3C, 0xFF}, // 10 {0x48, 0x48, 0x48, 0xFF}, // 11 {0x50, 0x50, 0x50, 0xFF}, // 12 {0x58, 0x58, 0x58, 0xFF}, // 13 {0x60, 0x60, 0x60, 0xFF}, // 14 {0x6C, 0x6C, 0x6C, 0xFF}, // 15 {0x74, 0x74, 0x74, 0xFF}, // 16 {0x7C, 0x7C, 0x7C, 0xFF}, // 17 {0x84, 0x84, 0x84, 0xFF}, // 18 {0x90, 0x90, 0x90, 0xFF}, // 19 {0x98, 0x98, 0x98, 0xFF}, // 20 {0xA0, 0xA0, 0xA0, 0xFF}, // 21 {0xA8, 0xA8, 0xA8, 0xFF}, // 22 {0xB4, 0xB4, 0xB4, 0xFF}, // 23 {0xBC, 0xBC, 0xBC, 0xFF}, // 24 {0xC4, 0xC4, 0xC4, 0xFF}, // 25 {0xCC, 0xCC, 0xCC, 0xFF}, // 26 {0xD8, 0xD8, 0xD8, 0xFF}, // 27 {0xE0, 0xE0, 0xE0, 0xFF}, // 28 {0xE8, 0xE8, 0xE8, 0xFF}, // 29 {0xF0, 0xF0, 0xF0, 0xFF}, // 30 {0xFC, 0xFC, 0xFC, 0xFF}, // 31 {0xFC, 0xFC, 0xFC, 0xFF}, // 32 {0xFC, 0xF0, 0xF0, 0xFF}, // 33 {0xFC, 0xE8, 0xE8, 0xFF}, // 34 {0xFC, 0xE0, 0xE0, 0xFF}, // 35 {0xFC, 0xD8, 0xD8, 0xFF}, // 36 {0xFC, 0xD0, 0xD0, 0xFF}, // 37 {0xFC, 0xC8, 0xC8, 0xFF}, // 38 {0xFC, 0xC0, 0xC0, 0xFF}, // 39 {0xFC, 0xB8, 0xB8, 0xFF}, // 40 {0xFC, 0xB0, 0xB0, 0xFF}, // 41 {0xFC, 0xA8, 0xA8, 0xFF}, // 42 {0xFC, 0xA0, 0xA0, 0xFF}, // 43 {0xFC, 0x98, 0x98, 0xFF}, // 44 {0xFC, 0x90, 0x90, 0xFF}, // 45 {0xFC, 0x88, 0x88, 0xFF}, // 46 {0xFC, 0x80, 0x80, 0xFF}, // 47 {0xFC, 0x78, 0x78, 0xFF}, // 48 {0xFC, 0x70, 0x70, 0xFF}, // 49 {0xFC, 0x68, 0x68, 0xFF}, // 50 {0xFC, 0x60, 0x60, 0xFF}, // 51 {0xFC, 0x58, 0x58, 0xFF}, // 52 {0xFC, 0x50, 0x50, 0xFF}, // 53 {0xFC, 0x48, 0x48, 0xFF}, // 54 {0xFC, 0x40, 0x40, 0xFF}, // 55 {0xFC, 0x38, 0x38, 0xFF}, // 56 {0xFC, 0x30, 0x30, 0xFF}, // 57 {0xFC, 0x28, 0x28, 0xFF}, // 58 {0xFC, 0x20, 0x20, 0xFF}, // 59 {0xFC, 0x18, 0x18, 0xFF}, // 60 {0xFC, 0x10, 0x10, 0xFF}, // 61 {0xFC, 0x8, 0x8, 0xFF}, // 62 {0xFC, 0x0, 0x0, 0xFF}, // 63 {0xFC, 0x0, 0x0, 0xFF}, // 64 {0xFC, 0x8, 0x0, 0xFF}, // 65 {0xFC, 0x10, 0x0, 0xFF}, // 66 {0xFC, 0x18, 0x0, 0xFF}, // 67 {0xFC, 0x20, 0x0, 0xFF}, // 68 {0xFC, 0x28, 0x0, 0xFF}, // 69 {0xFC, 0x30, 0x0, 0xFF}, // 70 {0xFC, 0x38, 0x0, 0xFF}, // 71 {0xFC, 0x40, 0x0, 0xFF}, // 72 {0xFC, 0x48, 0x0, 0xFF}, // 73 {0xFC, 0x50, 0x0, 0xFF}, // 74 {0xFC, 0x58, 0x0, 0xFF}, // 75 {0xFC, 0x60, 0x0, 0xFF}, // 76 {0xFC, 0x68, 0x0, 0xFF}, // 77 {0xFC, 0x70, 0x0, 0xFF}, // 78 {0xFC, 0x78, 0x0, 0xFF}, // 79 {0xFC, 0x80, 0x0, 0xFF}, // 80 {0xFC, 0x88, 0x0, 0xFF}, // 81 {0xFC, 0x90, 0x0, 0xFF}, // 82 {0xFC, 0x98, 0x0, 0xFF}, // 83 {0xFC, 0xA0, 0x0, 0xFF}, // 84 {0xFC, 0xA8, 0x0, 0xFF}, // 85 {0xFC, 0xB0, 0x0, 0xFF}, // 86 {0xFC, 0xB8, 0x0, 0xFF}, // 87 {0xFC, 0xC0, 0x0, 0xFF}, // 88 {0xFC, 0xC8, 0x0, 0xFF}, // 89 {0xFC, 0xD0, 0x0, 0xFF}, // 90 {0xFC, 0xD8, 0x0, 0xFF}, // 91 {0xFC, 0xE0, 0x0, 0xFF}, // 92 {0xFC, 0xE8, 0x0, 0xFF}, // 93 {0xFC, 0xF0, 0x0, 0xFF}, // 94 {0xFC, 0xFC, 0x0, 0xFF}, // 95 {0xFC, 0xFC, 0x0, 0xFF}, // 96 {0xF0, 0xF8, 0x4, 0xFF}, // 97 {0xE8, 0xF4, 0x8, 0xFF}, // 98 {0xE0, 0xF0, 0xC, 0xFF}, // 99 {0xD8, 0xEC, 0x10, 0xFF}, // 100 {0xD0, 0xE8, 0x14, 0xFF}, // 101 {0xC8, 0xE4, 0x18, 0xFF}, // 102 {0xC0, 0xE0, 0x1C, 0xFF}, // 103 {0xB8, 0xDC, 0x20, 0xFF}, // 104 {0xB0, 0xD8, 0x24, 0xFF}, // 105 {0xA8, 0xD4, 0x28, 0xFF}, // 106 {0xA0, 0xD0, 0x2C, 0xFF}, // 107 {0x98, 0xCC, 0x30, 0xFF}, // 108 {0x90, 0xC8, 0x34, 0xFF}, // 109 {0x88, 0xC4, 0x38, 0xFF}, // 110 {0x80, 0xC0, 0x3C, 0xFF}, // 111 {0x78, 0xBC, 0x40, 0xFF}, // 112 {0x70, 0xB8, 0x44, 0xFF}, // 113 {0x68, 0xB4, 0x48, 0xFF}, // 114 {0x60, 0xB0, 0x4C, 0xFF}, // 115 {0x58, 0xAC, 0x50, 0xFF}, // 116 {0x50, 0xA8, 0x54, 0xFF}, // 117 {0x48, 0xA4, 0x58, 0xFF}, // 118 {0x40, 0xA0, 0x5C, 0xFF}, // 119 {0x38, 0x9C, 0x60, 0xFF}, // 120 {0x30, 0x98, 0x64, 0xFF}, // 121 {0x28, 0x94, 0x68, 0xFF}, // 122 {0x20, 0x90, 0x6C, 0xFF}, // 123 {0x18, 0x8C, 0x70, 0xFF}, // 124 {0x10, 0x88, 0x74, 0xFF}, // 125 {0x8, 0x84, 0x78, 0xFF}, // 126 {0x0, 0x80, 0x80, 0xFF}, // 127 {0x0, 0x80, 0x80, 0xFF}, // 128 {0x0, 0x7C, 0x84, 0xFF}, // 129 {0x4, 0x7C, 0x88, 0xFF}, // 130 {0x4, 0x78, 0x8C, 0xFF}, // 131 {0x8, 0x78, 0x90, 0xFF}, // 132 {0xC, 0x78, 0x94, 0xFF}, // 133 {0xC, 0x74, 0x98, 0xFF}, // 134 {0x10, 0x74, 0x9C, 0xFF}, // 135 {0x14, 0x70, 0xA0, 0xFF}, // 136 {0x14, 0x70, 0xA4, 0xFF}, // 137 {0x18, 0x70, 0xA8, 0xFF}, // 138 {0x1C, 0x6C, 0xAC, 0xFF}, // 139 {0x1C, 0x6C, 0xB0, 0xFF}, // 140 {0x20, 0x68, 0xB4, 0xFF}, // 141 {0x24, 0x68, 0xB8, 0xFF}, // 142 {0x24, 0x68, 0xBC, 0xFF}, // 143 {0x28, 0x64, 0xC0, 0xFF}, // 144 {0x28, 0x64, 0xC4, 0xFF}, // 145 {0x2C, 0x64, 0xC8, 0xFF}, // 146 {0x30, 0x60, 0xCC, 0xFF}, // 147 {0x30, 0x60, 0xD0, 0xFF}, // 148 {0x34, 0x5C, 0xD4, 0xFF}, // 149 {0x38, 0x5C, 0xD8, 0xFF}, // 150 {0x38, 0x5C, 0xDC, 0xFF}, // 151 {0x3C, 0x58, 0xE0, 0xFF}, // 152 {0x40, 0x58, 0xE4, 0xFF}, // 153 {0x40, 0x54, 0xE8, 0xFF}, // 154 {0x44, 0x54, 0xEC, 0xFF}, // 155 {0x48, 0x54, 0xF0, 0xFF}, // 156 {0x48, 0x50, 0xF4, 0xFF}, // 157 {0x4C, 0x50, 0xF8, 0xFF}, // 158 {0x50, 0x50, 0xFC, 0xFF}, // 159 {0x50, 0x50, 0xFC, 0xFF}, // 160 {0x54, 0x54, 0xF8, 0xFF}, // 161 {0x58, 0x58, 0xF8, 0xFF}, // 162 {0x60, 0x60, 0xF4, 0xFF}, // 163 {0x64, 0x64, 0xF4, 0xFF}, // 164 {0x68, 0x68, 0xF0, 0xFF}, // 165 {0x70, 0x70, 0xF0, 0xFF}, // 166 {0x74, 0x74, 0xF0, 0xFF}, // 167 {0x7C, 0x7C, 0xEC, 0xFF}, // 168 {0x80, 0x80, 0xEC, 0xFF}, // 169 {0x84, 0x84, 0xE8, 0xFF}, // 170 {0x8C, 0x8C, 0xE8, 0xFF}, // 171 {0x90, 0x90, 0xE4, 0xFF}, // 172 {0x98, 0x98, 0xE4, 0xFF}, // 173 {0x9C, 0x9C, 0xE4, 0xFF}, // 174 {0xA0, 0xA0, 0xE0, 0xFF}, // 175 {0xA8, 0xA8, 0xE0, 0xFF}, // 176 {0xAC, 0xAC, 0xDC, 0xFF}, // 177 {0xB0, 0xB0, 0xDC, 0xFF}, // 178 {0xB8, 0xB8, 0xDC, 0xFF}, // 179 {0xBC, 0xBC, 0xD8, 0xFF}, // 180 {0xC4, 0xC4, 0xD8, 0xFF}, // 181 {0xC8, 0xC8, 0xD4, 0xFF}, // 182 {0xCC, 0xCC, 0xD4, 0xFF}, // 183 {0xD4, 0xD4, 0xD0, 0xFF}, // 184 {0xD8, 0xD8, 0xD0, 0xFF}, // 185 {0xE0, 0xE0, 0xD0, 0xFF}, // 186 {0xE4, 0xE4, 0xCC, 0xFF}, // 187 {0xE8, 0xE8, 0xCC, 0xFF}, // 188 {0xF0, 0xF0, 0xC8, 0xFF}, // 189 {0xF4, 0xF4, 0xC8, 0xFF}, // 190 {0xFC, 0xFC, 0xC8, 0xFF}, // 191 {0xFC, 0xFC, 0xC8, 0xFF}, // 192 {0xF8, 0xF0, 0xC0, 0xFF}, // 193 {0xF4, 0xE8, 0xB8, 0xFF}, // 194 {0xF0, 0xE0, 0xB4, 0xFF}, // 195 {0xEC, 0xD8, 0xAC, 0xFF}, // 196 {0xE8, 0xD0, 0xA4, 0xFF}, // 197 {0xE4, 0xC8, 0xA0, 0xFF}, // 198 {0xE0, 0xC0, 0x98, 0xFF}, // 199 {0xDC, 0xB8, 0x94, 0xFF}, // 200 {0xD8, 0xB0, 0x8C, 0xFF}, // 201 {0xD4, 0xA8, 0x84, 0xFF}, // 202 {0xD0, 0xA0, 0x80, 0xFF}, // 203 {0xCC, 0x98, 0x78, 0xFF}, // 204 {0xC8, 0x90, 0x74, 0xFF}, // 205 {0xC4, 0x88, 0x6C, 0xFF}, // 206 {0xC0, 0x80, 0x64, 0xFF}, // 207 {0xBC, 0x78, 0x60, 0xFF}, // 208 {0xB8, 0x70, 0x58, 0xFF}, // 209 {0xB4, 0x68, 0x50, 0xFF}, // 210 {0xB0, 0x60, 0x4C, 0xFF}, // 211 {0xAC, 0x58, 0x44, 0xFF}, // 212 {0xA8, 0x50, 0x40, 0xFF}, // 213 {0xA4, 0x48, 0x38, 0xFF}, // 214 {0xA0, 0x40, 0x30, 0xFF}, // 215 {0x9C, 0x38, 0x2C, 0xFF}, // 216 {0x98, 0x30, 0x24, 0xFF}, // 217 {0x94, 0x28, 0x20, 0xFF}, // 218 {0x90, 0x20, 0x18, 0xFF}, // 219 {0x8C, 0x18, 0x10, 0xFF}, // 220 {0x88, 0x10, 0xC, 0xFF}, // 221 {0x84, 0x8, 0x4, 0xFF}, // 222 {0x80, 0x0, 0x0, 0xFF}, // 223 {0x80, 0x0, 0x0, 0xFF}, // 224 {0x84, 0xC, 0x10, 0xFF}, // 225 {0x88, 0x18, 0x20, 0xFF}, // 226 {0x90, 0x28, 0x30, 0xFF}, // 227 {0x94, 0x34, 0x40, 0xFF}, // 228 {0x98, 0x44, 0x54, 0xFF}, // 229 {0xA0, 0x50, 0x64, 0xFF}, // 230 {0xA4, 0x60, 0x74, 0xFF}, // 231 {0xA8, 0x6C, 0x84, 0xFF}, // 232 {0xB0, 0x7C, 0x94, 0xFF}, // 233 {0xB4, 0x88, 0xA8, 0xFF}, // 234 {0xB8, 0x98, 0xB8, 0xFF}, // 235 {0xC0, 0xA4, 0xC8, 0xFF}, // 236 {0xC4, 0xB4, 0xD8, 0xFF}, // 237 {0xC8, 0xC0, 0xE8, 0xFF}, // 238 {0xD0, 0xD0, 0xFC, 0xFF}, // 239 {0xD0, 0xD0, 0xFC, 0xFF}, // 240 {0xC0, 0xC0, 0xE8, 0xFF}, // 241 {0xB4, 0xB4, 0xD8, 0xFF}, // 242 {0xA4, 0xA4, 0xC8, 0xFF}, // 243 {0x98, 0x98, 0xB8, 0xFF}, // 244 {0x88, 0x88, 0xA4, 0xFF}, // 245 {0x7C, 0x7C, 0x94, 0xFF}, // 246 {0x6C, 0x6C, 0x84, 0xFF}, // 247 {0x60, 0x60, 0x74, 0xFF}, // 248 {0x50, 0x50, 0x64, 0xFF}, // 249 {0x44, 0x44, 0x50, 0xFF}, // 250 {0x34, 0x34, 0x40, 0xFF}, // 251 {0x28, 0x28, 0x30, 0xFF}, // 252 {0x18, 0x18, 0x20, 0xFF}, // 253 {0xC, 0xC, 0x10, 0xFF}, // 254 {0x0, 0x0, 0x0, 0xFF}, // 255 } png2svg-1.7.0/vendor/github.com/xyproto/burnpal/gfx.go000066400000000000000000000005021504213346300227410ustar00rootroot00000000000000package burnpal import ( "github.com/peterhellberg/gfx" "image/color" ) // GfxPalette returns the palette as a gfx.Palette func GfxPalette() gfx.Palette { return gfx.Palette(Pal) } // ColorPalette returns the palette as a color.Palette func ColorPalette() color.Palette { return gfx.Palette(Pal).AsColorPalette() } png2svg-1.7.0/vendor/github.com/xyproto/palgen/000077500000000000000000000000001504213346300214345ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/xyproto/palgen/.gitignore000066400000000000000000000012111504213346300234170ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out cmd/png16/png16 cmd/png256/png256 cmd/png2gpl/png2gpl cmd/png2act/png2act cmd/png2png/png2png cmd/pngn/pngn cmd/png2act/*.act cmd/png2gpl/burn.gpl testdata/*.gpl internal/burn/burn internal/general/general internal/plan9/plan9 internal/websafe/websafe internal/burn/*.png internal/general/*.png internal/plan9/*.png internal/websafe/*.png internal/burn/burn.gpl internal/general/burn.gpl internal/plan9/plan9.gpl internal/websafe/websafe.gpl .DS_Store png2svg-1.7.0/vendor/github.com/xyproto/palgen/README.md000066400000000000000000000120431504213346300227130ustar00rootroot00000000000000# palgen ![Build](https://github.com/xyproto/palgen/workflows/Build/badge.svg) [![GoDoc](https://godoc.org/github.com/xyproto/palgen?status.svg)](http://godoc.org/github.com/xyproto/palgen) [![Go Report Card](https://goreportcard.com/badge/github.com/xyproto/palgen)](https://goreportcard.com/report/github.com/xyproto/palgen) ## Render, extract and save palettes Given an image, create a palette of N colors, or convert True Color images to indexed ones. Palettes can also be rendered as images. ### Included utilities * `png256`, for converting a True Color PNG image to an indexed PNG image, with a custom palette of 256 colors. * `png2png`, for extracting a palette from a True Color PNG image and write the palette as an indexed 256 color PNG image. * `png2gpl`, for extracting a palette from a True Color PNG image and write the palette as a GIMP palette file (`.gpl`). * `png2act`, for extracting a palette from a True Color PNG image and write the palette as a Photoshop palette file (`.act`). ### Palette extraction | source image | extracted 256 color palette | | :---: | :---: | | ![png](testdata/splash.png) | ![png](testdata/splash_pal.png) | | ![png](testdata/tm_small.png) | ![png](testdata/tm_small_pal.png) | The palette can be extracted and saved as a PNG image, using `png2png`, or as a GIMP palette, using `png2gpl`. Palettes can be sorted by hue, luminance and chroma, using the HCL colorspace and the [go-colorful](https://github.com/lucasb-eyer/go-colorful) package, with the included `palgen.Sort` function. The above palettes are sorted with this method. ### Features and limitations * Can generate palettes of N colors relatively quickly. * The palette is generated by first grouping colors into N intensity levels and then use the median color of each group. * The generated palette is not 100% optimal (based on how the human eye is more sensitive for green etc), but it's usable. * Can export any given `color.Palette` to a GIMP `.gpl` palette file. * Can export any given `color.Palette` to a Photoshop `.act` palette file. * Can convert True Color `image.Image` images to indexed `image.Paletted` images. ### Example use ```go // Read a PNG file imageData, err := os.Open("input.png") if err != nil { return err } // Decode the PNG image img, err := png.Decode(imageData) if err != nil { return err } // Generate a palette with 256 colors pal, err := palgen.Generate(img, 256) if err != nil { return err } // Output a .gpl palette file with the name "Untitled" err = palgen.Save(pal, "output.gpl", "Untitled") if err != nil { return err } ``` ### Image comparison The palettes are generated by palgen | 8 color palette | 16 color palette | 32 color palette | 64 color palette | 128 color palette | 256 color palette | original | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | ![png](testdata/splash8.png) | ![png](testdata/splash16.png) | ![png](testdata/splash32.png) | ![png](testdata/splash64.png) | ![png](testdata/splash128.png) | ![png](testdata/splash256.png) | ![png](testdata/splash.png) | | ![png](testdata/tm_small8.png) | ![png](testdata/tm_small16.png) | ![png](testdata/tm_small32.png) | ![png](testdata/tm_small64.png) | ![png](testdata/tm_small128.png) | ![png](testdata/tm_small256.png) | ![png](testdata/tm_small.png) | ### Algorithm A palette is generated by first dividing the colors in the image into N groups, sorted by intensity. For each group, the median value is used. When the number of colors in a group is even, the average of the two center colors are used as the median, but the two center colors are saved for later and added to the generated palette if there are duplicate colors, adding the most used colors first. The generated palette may be shorter than N if there are not enough colors in the given image. As far as I know, no other software uses this algorithm. It works fine, and is relatively fast, but there even better algorithms out there if you are looking for the optimal palette and want to adjust for which colors the human eye are most sensitive for. As far as I am aware, this is a unique algorithm that has not been thought of or implemented before (create an issue if not), so I'll hereby name it "Rodseth's Median Intensity Algorithm" or RMIA for short. ### Render existing palettes to images `palgen` can also be used for rendering palettes to images. Here are the two built-in palettes in the Go standard library, with and without the colors sorted: #### [Plan9](https://golang.org/pkg/image/color/palette/#Plan9) | Sorted | Original | | :---: | :---: | | ![png](img/plan9.png) | ![png](img/plan9_unsorted.png) | #### [WebSafe](https://golang.org/pkg/image/color/palette/#WebSafe) | Sorted | Original | | :---: | :---: | | ![png](img/websafe.png) | ![png](img/websafe_unsorted.png) | And one extra: #### [Burn](https://github.com/xyproto/burnpal) | Sorted | Original | | :---: | :---: | | ![png](img/burn.png) | ![png](img/burn_unsorted.png) | ### General info * Version: 1.6.1 * License: BSD-3 * Author: Alexander F. Rødseth <xyproto@archlinux.org> png2svg-1.7.0/vendor/github.com/xyproto/palgen/TODO.md000066400000000000000000000005721504213346300225270ustar00rootroot00000000000000# Plans - [ ] If the extracted palette from an image results in colors where some of the colors are used just on a very few pixels, consider using a different color in the palette. Example image, where the blue color perhaps should have been something else: https://raw.githubusercontent.com/xyproto/png2svg/241a8a9d43e7ff974f0a41cad2416df606e7be2c/img/rainforest_4c.svg png2svg-1.7.0/vendor/github.com/xyproto/palgen/act_palette.go000066400000000000000000000013431504213346300242510ustar00rootroot00000000000000package palgen import ( "bytes" "image/color" "os" ) // ACT converts a given palette to the Photoshop ACT Palette Format (.act) // There is no header, just either 768 or 772 bytes of color data. // 256 * 3 = 768. The four extra bytes can be 16-bit color count + 16 bit transparent color index. func ACT(pal color.Palette) []byte { var buf bytes.Buffer // Output the colors for _, c := range pal { cn := c.(color.RGBA) buf.WriteByte(cn.R) buf.WriteByte(cn.G) buf.WriteByte(cn.B) } // Return the generated string return buf.Bytes() } // SaveACT can save a palette to file in the Photoship ACT Palette Format (.act) func SaveACT(pal color.Palette, filename string) error { return os.WriteFile(filename, ACT(pal), 0644) } png2svg-1.7.0/vendor/github.com/xyproto/palgen/basic16.go000066400000000000000000000013671504213346300232220ustar00rootroot00000000000000package palgen import ( "image/color" ) // BasicPalette16 is a basic 16-color palette var BasicPalette16 = [16][3]byte{ {0x0, 0x0, 0x0}, // 0 {191, 0x0, 0x0}, // 1 {0x0, 191, 0x0}, // 2 {191, 191, 0x0}, // 3 {0x0, 0x0, 191}, // 4 {191, 0x0, 191}, // 5 {0x0, 191, 191}, // 6 {191, 191, 191}, // 7 {0x40, 0x40, 0x40}, // 8 {0xff, 0x40, 0x40}, // 9 {0x40, 0xff, 0x40}, // 10 {0xff, 0xff, 0x40}, // 11 {96, 96, 0xff}, // 12 {0xff, 0x40, 0xff}, // 13 {0x40, 0xff, 0xff}, // 14 {0xff, 0xff, 0xff}, // 15 } // BasicPalette can return a basic 16 color palette func BasicPalette() (pal color.Palette) { for _, rgb := range BasicPalette16 { pal = append(pal, color.NRGBA{rgb[0], rgb[1], rgb[2], 255}) } return pal } png2svg-1.7.0/vendor/github.com/xyproto/palgen/convert.go000066400000000000000000000055221504213346300234470ustar00rootroot00000000000000package palgen import ( "errors" "image" "image/color" "image/color/palette" "github.com/xyproto/burnpal" ) // TODO: Create a custom Paletted type, based on image/Paletted, that can take > 256 colors // ConvertCustom can convert an image from True Color to a <=256 color paletted image, given a custom palette. func ConvertCustom(img image.Image, pal color.Palette) (image.Image, error) { if len(pal) > 256 { return nil, errors.New("can reduce to a maximum of 256 colors") } palImg := image.NewPaletted(img.Bounds(), pal) // For each pixel, go through each color in the palette and pick out the closest one. for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { sourceColor := img.At(x, y) colorIndex := uint8(pal.Index(sourceColor)) palImg.SetColorIndex(x, y, colorIndex) } } return palImg, nil } // Convert can convert an image from True Color to a 256 color paletted image. // The palette is automatically extracted from the image. func Convert(img image.Image) (image.Image, error) { customPalette, err := Generate(img, 256) if err != nil { return nil, err } // This should never happen if len(customPalette) > 256 { return nil, errors.New("the generated palette has too many colors") } // Sort using the HCL colorspace Sort(customPalette) // Return a new Paletted image return ConvertCustom(img, customPalette) } // Reduce can convert an image from True Color to a N color paletted image. // The palette is automatically extracted from the image. func Reduce(img image.Image, n int) (image.Image, error) { customPalette, err := GenerateUpTo(img, n) if err != nil { return nil, err } // This should never happen if len(customPalette) > n { return nil, errors.New("the generated palette has too many colors") } // Sort using the HCL colorspace Sort(customPalette) // Return a new Paletted image return ConvertCustom(img, customPalette) } // ConvertGeneral can convert an image from True Color to a 256 color paletted image, using a general palette. func ConvertGeneral(img image.Image) (image.Image, error) { return ConvertCustom(img, GeneralPalette()) } // ConvertPlan9 can convert an image from True Color to a 256 color paletted image, using the Plan9 palette from the Go standard library. func ConvertPlan9(img image.Image) (image.Image, error) { return ConvertCustom(img, palette.Plan9) } // ConvertBasic can convert an image from True Color to a 16 color paletted image, using the 16 basic terminal colors func ConvertBasic(img image.Image) (image.Image, error) { return ConvertCustom(img, BasicPalette()) } // ConvertBurn can convert an image from True Color to a 256 color paletted image, using the Burn palette from github.com/xyproto/burnpal. func ConvertBurn(img image.Image) (image.Image, error) { return ConvertCustom(img, burnpal.ColorPalette()) } png2svg-1.7.0/vendor/github.com/xyproto/palgen/general256.go000066400000000000000000000107321504213346300236400ustar00rootroot00000000000000package palgen import ( "image/color" ) // GeneralPalette256 is a an OK standard palette var GeneralPalette256 = [256][3]byte{ {0, 0, 0}, {0, 0, 102}, {0, 0, 204}, {0, 23, 51}, {0, 23, 153}, {0, 23, 255}, {0, 46, 0}, {0, 46, 102}, {0, 46, 204}, {0, 69, 51}, {0, 69, 153}, {0, 69, 255}, {0, 92, 0}, {0, 92, 102}, {0, 92, 204}, {0, 115, 51}, {0, 115, 153}, {0, 115, 255}, {0, 139, 0}, {0, 139, 102}, {0, 139, 204}, {0, 162, 51}, {0, 162, 153}, {0, 162, 255}, {0, 185, 0}, {0, 185, 102}, {0, 185, 204}, {0, 208, 51}, {0, 208, 153}, {0, 208, 255}, {0, 231, 0}, {0, 231, 102}, {0, 231, 204}, {0, 255, 51}, {0, 255, 153}, {0, 255, 255}, {42, 0, 51}, {42, 0, 153}, {42, 0, 255}, {42, 23, 0}, {42, 23, 102}, {42, 23, 204}, {42, 46, 51}, {42, 46, 153}, {42, 46, 255}, {42, 69, 0}, {42, 69, 102}, {42, 69, 204}, {42, 92, 51}, {42, 92, 153}, {42, 92, 255}, {42, 115, 0}, {42, 115, 102}, {42, 115, 204}, {42, 139, 51}, {42, 139, 153}, {42, 139, 255}, {42, 162, 0}, {42, 162, 102}, {42, 162, 204}, {42, 185, 51}, {42, 185, 153}, {42, 185, 255}, {42, 208, 0}, {42, 208, 102}, {42, 208, 204}, {42, 231, 51}, {42, 231, 153}, {42, 231, 255}, {42, 255, 0}, {42, 255, 102}, {42, 255, 204}, {85, 0, 0}, {85, 0, 102}, {85, 0, 204}, {85, 23, 51}, {85, 23, 153}, {85, 23, 255}, {85, 46, 0}, {85, 46, 102}, {85, 46, 204}, {85, 69, 51}, {85, 69, 153}, {85, 69, 255}, {85, 92, 0}, {85, 92, 102}, {85, 92, 204}, {85, 115, 51}, {85, 115, 153}, {85, 115, 255}, {85, 139, 0}, {85, 139, 102}, {85, 139, 204}, {85, 162, 51}, {85, 162, 153}, {85, 162, 255}, {85, 185, 0}, {85, 185, 102}, {85, 185, 204}, {85, 208, 51}, {85, 208, 153}, {85, 208, 255}, {85, 231, 0}, {85, 231, 102}, {85, 231, 204}, {85, 255, 51}, {85, 255, 153}, {85, 255, 255}, {127, 0, 51}, {127, 0, 153}, {127, 0, 255}, {127, 23, 0}, {127, 23, 102}, {127, 23, 204}, {127, 46, 51}, {127, 46, 153}, {127, 46, 255}, {127, 69, 0}, {127, 69, 102}, {127, 69, 204}, {127, 92, 51}, {127, 92, 153}, {127, 92, 255}, {127, 115, 0}, {127, 115, 102}, {127, 115, 204}, {127, 139, 51}, {127, 139, 153}, {127, 139, 255}, {127, 162, 0}, {127, 162, 102}, {127, 162, 204}, {127, 185, 51}, {127, 185, 153}, {127, 185, 255}, {127, 208, 0}, {127, 208, 102}, {127, 208, 204}, {127, 231, 51}, {127, 231, 153}, {127, 231, 255}, {127, 255, 0}, {127, 255, 102}, {127, 255, 204}, {170, 0, 0}, {170, 0, 102}, {170, 0, 204}, {170, 23, 51}, {170, 23, 153}, {170, 23, 255}, {170, 46, 0}, {170, 46, 102}, {170, 46, 204}, {170, 69, 51}, {170, 69, 153}, {170, 69, 255}, {170, 92, 0}, {170, 92, 102}, {170, 92, 204}, {170, 115, 51}, {170, 115, 153}, {170, 115, 255}, {170, 139, 0}, {170, 139, 102}, {170, 139, 204}, {170, 162, 51}, {170, 162, 153}, {170, 162, 255}, {170, 185, 0}, {170, 185, 102}, {170, 185, 204}, {170, 208, 51}, {170, 208, 153}, {170, 208, 255}, {170, 231, 0}, {170, 231, 102}, {170, 231, 204}, {170, 255, 51}, {170, 255, 153}, {170, 255, 255}, {212, 0, 51}, {212, 0, 153}, {212, 0, 255}, {212, 23, 0}, {212, 23, 102}, {212, 23, 204}, {212, 46, 51}, {212, 46, 153}, {212, 46, 255}, {212, 69, 0}, {212, 69, 102}, {212, 69, 204}, {212, 92, 51}, {212, 92, 153}, {212, 92, 255}, {212, 115, 0}, {212, 115, 102}, {212, 115, 204}, {212, 139, 51}, {212, 139, 153}, {212, 139, 255}, {212, 162, 0}, {212, 162, 102}, {212, 162, 204}, {212, 185, 51}, {212, 185, 153}, {212, 185, 255}, {212, 208, 0}, {212, 208, 102}, {212, 208, 204}, {212, 231, 51}, {212, 231, 153}, {212, 231, 255}, {212, 255, 0}, {212, 255, 102}, {212, 255, 204}, {255, 0, 0}, {255, 0, 102}, {255, 0, 204}, {255, 23, 51}, {255, 23, 153}, {255, 23, 255}, {255, 46, 0}, {255, 46, 102}, {255, 46, 204}, {255, 69, 51}, {255, 69, 153}, {255, 69, 255}, {255, 92, 0}, {255, 92, 102}, {255, 92, 204}, {255, 115, 51}, {255, 115, 153}, {255, 115, 255}, {255, 139, 0}, {255, 139, 102}, {255, 139, 204}, {255, 162, 51}, {255, 162, 153}, {255, 162, 255}, {255, 185, 0}, {255, 185, 102}, {255, 185, 204}, {255, 208, 51}, {255, 208, 153}, {255, 208, 255}, {255, 231, 0}, {255, 231, 102}, {255, 231, 204}, {255, 255, 51}, {255, 255, 153}, {255, 255, 255}, {204, 204, 204}, {153, 153, 153}, {102, 102, 102}, {51, 51, 51}, } // GeneralPalette can return a pretty general 256 color palette func GeneralPalette() (pal color.Palette) { for _, rgb := range GeneralPalette256 { pal = append(pal, color.NRGBA{rgb[0], rgb[1], rgb[2], 255}) } return pal } png2svg-1.7.0/vendor/github.com/xyproto/palgen/generate.go000066400000000000000000000225111504213346300235560ustar00rootroot00000000000000package palgen import ( "errors" "image" "image/color" "math" "sort" ) // SortablePalette is a slice of color.Color that can be sorted with sort.Sort, by euclidian distance for R, G and B type SortablePalette []color.Color // Length from RGB (0, 0, 0) func colorLength(c color.Color) float64 { r := c.(color.RGBA) return math.Sqrt(float64(r.R*r.R + r.G*r.G + r.B*r.B)) // + r.A*r.A)) } func (a SortablePalette) Len() int { return len(a) } func (a SortablePalette) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a SortablePalette) Less(i, j int) bool { return colorLength(a[i]) < colorLength(a[j]) } // Median finds not the average but the median color func Median(colors []color.Color) (color.Color, error) { length := len(colors) if length == 0 { return nil, errors.New("can't find the median of an empty slice of colors") } if len(colors) == 1 { return colors[0], nil } // 1. Sort the colors sp := SortablePalette(colors) sort.Sort(sp) // 2. Select the center one, if odd if length%2 != 0 { centerPos := length / 2 return sp[centerPos], nil } // 3. If the numbers are even, select the two center one and take the average of those centerPos1 := (length / 2) - 1 centerPos2 := length / 2 c1 := sp[centerPos1].(color.RGBA) c2 := sp[centerPos2].(color.RGBA) r := (c1.R + c2.R) / 2.0 g := (c1.G + c2.G) / 2.0 b := (c1.B + c2.B) / 2.0 a := (c1.A + c2.A) / 2.0 // return the new color return color.RGBA{r, g, b, a}, nil } // Median3 finds not the average but the median color. Returns three colors if the number of colors is even (average, first and second color in the center). func Median3(colors []color.Color) (color.Color, color.Color, color.Color, error) { length := len(colors) if length == 0 { return nil, nil, nil, errors.New("can't find the median of an empty slice of colors") } if len(colors) == 1 { return colors[0], colors[0], colors[0], nil } // 1. Sort the colors sp := SortablePalette(colors) sort.Sort(sp) // 2. Select the center one, if odd if length%2 != 0 { centerPos := length / 2 // Return the center color, thrice return sp[centerPos], sp[centerPos], sp[centerPos], nil } // 3. If the numbers are even, select the two center one and take the average of those centerPos1 := (length / 2) - 1 centerPos2 := length / 2 c1 := sp[centerPos1].(color.RGBA) c2 := sp[centerPos2].(color.RGBA) r := (c1.R + c2.R) / 2.0 g := (c1.G + c2.G) / 2.0 b := (c1.B + c2.B) / 2.0 a := (c1.A + c2.A) / 2.0 averageColor := color.RGBA{r, g, b, a} // Also return the two center colors return averageColor, sp[centerPos2], sp[centerPos1], nil } // Generate can generate a palette with N colors, given an image func Generate(img image.Image, N int) (color.Palette, error) { groups := make(map[int][]color.Color) already := make(map[color.Color]bool) numberOfPixels := (img.Bounds().Max.Y - img.Bounds().Min.Y) * (img.Bounds().Max.X - img.Bounds().Min.X) levelCounter := make(map[int]float64, numberOfPixels) colorCounter := make(map[color.RGBA]float64, numberOfPixels) // Pick out the colors from the image, per intensity level, and store them in the groups map for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { c := img.At(x, y) gc := color.GrayModel.Convert(c).(color.Gray) rgba := color.RGBAModel.Convert(c).(color.RGBA) level := int(float64(gc.Y) / (255.0 / float64(N-1))) alreadyColor, ok := already[rgba] if !alreadyColor || !ok { groups[level] = append(groups[level], rgba) already[rgba] = true } // Count the different levels used if counter, ok := levelCounter[level]; ok { levelCounter[level] = counter + 1.0 } else { levelCounter[level] = 1.0 } // Count the different colors used if counter, ok := colorCounter[rgba]; ok { colorCounter[rgba] = counter + 1.0 } else { colorCounter[rgba] = 1.0 } } } // Reset the map for if colors are already appended to a slice already = make(map[color.Color]bool) already2 := make(map[color.Color]bool) var extrapal color.Palette // Remove the group of colors with the least used intensity level minCoverage := 1.0 minCoverageKey := 0 found := false for intensityLevelKey := range groups { coverage := levelCounter[intensityLevelKey] / float64(numberOfPixels) if coverage < minCoverage { minCoverage = coverage minCoverageKey = intensityLevelKey found = true } } if found { delete(groups, minCoverageKey) } // Find the median color for each intensity level var pal color.Palette for _, colors := range groups { // Find the median color of a group of colors of a certain intensity //medianColor, err := Median(colors) medianColor1, medianColor2, medianColor3, err := Median3(colors) if err != nil { return nil, err } // Add the medianColor1 to the palette, if it's not already there alreadyColor, ok := already[medianColor1] if !alreadyColor || !ok { pal = append(pal, medianColor1) already[medianColor1] = true } // Add medianColor2 and medianColor3 to the extra palette, if they are not already in it alreadyColor2, ok := already2[medianColor2] if !alreadyColor2 || !ok { extrapal = append(extrapal, medianColor2) already2[medianColor2] = true } alreadyColor2, ok = already2[medianColor3] if !alreadyColor2 || !ok { extrapal = append(extrapal, medianColor3) already2[medianColor3] = true } } // If there are not enough colors in the generated palette (N+1, because we will remove one), add colors from extrapal for (len(pal) < N) && (len(extrapal) > 0) { var ( maxUsage = -1.0 mostFrequentIndex = -1 ) // Loop through extrapal to find the most frequently used color for i, extraColor := range extrapal { rgbaExtra := color.RGBAModel.Convert(extraColor).(color.RGBA) if usage, ok := colorCounter[rgbaExtra]; ok { if usage > maxUsage { maxUsage = usage mostFrequentIndex = i } } } if mostFrequentIndex == -1 { // not found break } // Use the most frequently used color from the palette of extra colors c := extrapal[mostFrequentIndex] // Add the color to the palette, if it's not already there alreadyColor, ok := already[c] if !alreadyColor || !ok { pal = append(pal, c) already[c] = true } // Remove this color from extrapal extrapal = append(extrapal[:mostFrequentIndex], extrapal[mostFrequentIndex+1:]...) } // Now remove the one extra color that covers the least amount of pixels of the image that has been converted to the current palette if len(pal) > N { minCoverage := 1.0 minCoverageIndex := -1 for i := 0; i < len(pal); i++ { c := color.RGBAModel.Convert(pal[i]).(color.RGBA) coverage := colorCounter[c] / float64(numberOfPixels) if coverage < minCoverage { minCoverage = coverage minCoverageIndex = i } } if minCoverageIndex != -1 { // Replace i with the last element pal[minCoverageIndex] = pal[len(pal)-1] // Remove the last element pal = pal[:len(pal)-1] } } // Return the generated palette return pal, nil } // GenerateUpTo can generate a palette with up to N colors, given an image func GenerateUpTo(img image.Image, N int) (color.Palette, error) { groups := make(map[int][]color.Color) already := make(map[color.Color]bool) numberOfPixels := (img.Bounds().Max.Y - img.Bounds().Min.Y) * (img.Bounds().Max.X - img.Bounds().Min.X) levelCounter := make(map[int]float64, numberOfPixels) colorCounter := make(map[color.RGBA]float64, numberOfPixels) // Pick out the colors from the image, per intensity level, and store them in the groups map for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { c := img.At(x, y) gc := color.GrayModel.Convert(c).(color.Gray) rgba := color.RGBAModel.Convert(c).(color.RGBA) level := int(float64(gc.Y) / (255.0 / float64(N-1))) alreadyColor, ok := already[rgba] if !alreadyColor || !ok { groups[level] = append(groups[level], rgba) already[rgba] = true } // Count the different levels used if counter, ok := levelCounter[level]; ok { levelCounter[level] = counter + 1.0 } else { levelCounter[level] = 1.0 } // Count the different colors used if counter, ok := colorCounter[rgba]; ok { colorCounter[rgba] = counter + 1.0 } else { colorCounter[rgba] = 1.0 } } } // Remove the group of colors with the least used intensity level minCoverage := 1.0 minCoverageKey := 0 found := false for intensityLevelKey := range groups { coverage := levelCounter[intensityLevelKey] / float64(numberOfPixels) if coverage < minCoverage { minCoverage = coverage minCoverageKey = intensityLevelKey found = true } } if found { delete(groups, minCoverageKey) } // Reset the map for if colors are already appended to a slice already = make(map[color.Color]bool) // Find the median color for each intensity level var pal color.Palette for _, colors := range groups { // Find the median color of a group of colors of a certain intensity //medianColor, err := Median(colors) medianColor1, _, _, err := Median3(colors) if err != nil { return nil, err } // Add the medianColor1 to the palette, if it's not already there alreadyColor, ok := already[medianColor1] if !alreadyColor || !ok { pal = append(pal, medianColor1) already[medianColor1] = true } } // Return the generated palette return pal, nil } png2svg-1.7.0/vendor/github.com/xyproto/palgen/gpl_palette.go000066400000000000000000000022231504213346300242620ustar00rootroot00000000000000package palgen import ( "errors" "fmt" "image/color" "os" "strings" ) const softwareID = "github.com/xyproto/palgen" // GPL converts a given palette to the GIMP Palette Format (.gpl) // The given name will be used as the palette name in the header func GPL(pal color.Palette, paletteName string) (string, error) { var sb strings.Builder // Prepare a header sb.WriteString("GIMP Palette\nName: ") sb.WriteString(paletteName) sb.WriteString("\nColumns: 4\n# ") sb.WriteString(softwareID) sb.WriteString("\n") // Output the colors for i, c := range pal { cn, ok := c.(color.RGBA) if !ok { return "", errors.New("colors in the given palette must be color.RGBA compatible") } sb.WriteString(fmt.Sprintf("%3d %3d %3d\t%d\n", cn.R, cn.G, cn.B, i)) } // Return the generated string return sb.String(), nil } // SaveGPL can save a palette to file in the GIMP Palette Format (.gpl) // The given name will be used as the palette name in the header func SaveGPL(pal color.Palette, filename, paletteName string) error { GPLString, err := GPL(pal, paletteName) if err != nil { return err } return os.WriteFile(filename, []byte(GPLString), 0644) } png2svg-1.7.0/vendor/github.com/xyproto/palgen/hclsort.go000066400000000000000000000016161504213346300234450ustar00rootroot00000000000000package palgen import ( "github.com/lucasb-eyer/go-colorful" "image/color" "sort" ) // HCLSortablePalette is a slice of color.Color that can be sorted with sort.Sort, by h, c and l values type HCLSortablePalette []color.Color func (a HCLSortablePalette) Len() int { return len(a) } func (a HCLSortablePalette) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a HCLSortablePalette) Less(i, j int) bool { // Create colorful.Color structs ci, _ := colorful.MakeColor(a[i]) cj, _ := colorful.MakeColor(a[j]) // Convert the colors to HCL h1, c1, l1 := ci.Hcl() h2, c2, l2 := cj.Hcl() // Compare the H, C and L values if h1 == h2 { if c1 == c2 { return l1 < l2 } return c1 < c2 } return h1 < h2 } // Sort the palette by luminance, hue and then chroma func Sort(pal color.Palette) { //tmp := HCLSortablePalette(pal) sort.Sort(HCLSortablePalette(pal)) //return color.Palette(tmp) } png2svg-1.7.0/vendor/github.com/xyproto/palgen/render.go000066400000000000000000000044271504213346300232510ustar00rootroot00000000000000package palgen import ( "image" "image/color" ) // Render a given palette as an image, with blocks of 16x16 pixels and 32 colors per row of blocks. func Render(pal color.Palette) image.Image { return RenderWithConfig(pal, 16, 32) } // RenderWithConfig can render a given palette as an image that shows each color as a square, lined up in rows. // blockSize is the size of the color block per color, in pixels (16 is default) // colorsPerRow is the number of color blocks per row (32 is default) func RenderWithConfig(pal color.Palette, blockSize, colorsPerRow int) image.Image { // Remove the alpha for i, c := range pal { rgba := color.RGBAModel.Convert(c).(color.RGBA) rgba.A = 255 pal[i] = rgba } // The first color is now the darkest one darkIndex := uint8(0) // Let each row have colorsPerRow blocks w := colorsPerRow h := len(pal) / w leftover := len(pal) % w if leftover > 0 { h++ } // "pixel" width and height pw := blockSize ph := blockSize // TODO: Clean up everything that has to do with borderSize and support // custom border sizes. // size of border, ish borderSize := 2 upLeft := image.Point{0, 0} lowRight := image.Point{w*pw + (borderSize-1)*2, h*ph + (borderSize-1)*2} // Create a new image, where a square is painted per color in the palette palImage := image.NewPaletted(image.Rectangle{upLeft, lowRight}, pal) for y := upLeft.Y; y < lowRight.Y; y++ { for x := lowRight.X; x < lowRight.X; x++ { if x < (borderSize-1) || y < (borderSize-1) || x >= (lowRight.X-(borderSize-1)) || y >= (lowRight.Y-(borderSize-1)) { palImage.SetColorIndex(x, y, darkIndex) } } } // Set color for each pixel. colorIndex := uint8(0) OUT: // For each palette color block for y := 0; y < h; y++ { for x := 0; x < w; x++ { // For each pixel in the block of size pw x ph for by := 0; by < ph; by++ { for bx := 0; bx < pw; bx++ { if bx <= borderSize-2 || by <= borderSize-2 || bx >= pw-(borderSize-1) || by >= ph-(borderSize-1) { palImage.SetColorIndex((x*pw)+bx+(borderSize-1), (y*ph)+by+(borderSize-1), darkIndex) } else { palImage.SetColorIndex((x*pw)+bx+(borderSize-1), (y*ph)+by+(borderSize-1), colorIndex) } } } colorIndex++ if int(colorIndex) >= len(pal) { break OUT } } } return palImage } png2svg-1.7.0/vendor/github.com/xyproto/tinysvg/000077500000000000000000000000001504213346300216715ustar00rootroot00000000000000png2svg-1.7.0/vendor/github.com/xyproto/tinysvg/.gitignore000066400000000000000000000001251504213346300236570ustar00rootroot00000000000000* !*.* !*/ *.o *.swp *.tmp *.bak *.pro.user *CMakeFiles* *.dblite *.so .vscode *.svg png2svg-1.7.0/vendor/github.com/xyproto/tinysvg/README.md000066400000000000000000000013101504213346300231430ustar00rootroot00000000000000# tinysvg [![Build](https://github.com/xyproto/tinysvg/actions/workflows/build.yml/badge.svg)](https://github.com/xyproto/tinysvg/actions/workflows/build.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/xyproto/tinysvg)](https://goreportcard.com/report/github.com/xyproto/tinysvg) [![GoDoc](https://godoc.org/github.com/xyproto/tinysvg?status.svg)](https://godoc.org/github.com/xyproto/tinysvg) Construct SVG documents and images using Go. This package mainly uses `[]byte` slices instead of strings, and does not indent the generated SVG data, for performance and compactness. ## General info * Version: 1.2.1 * Author: Alexander F. Rødseth <xyproto@archlinux.org> * License: BSD-3 png2svg-1.7.0/vendor/github.com/xyproto/tinysvg/document.go000066400000000000000000000034011504213346300240340ustar00rootroot00000000000000package tinysvg import ( "io" "os" ) // Document is an XML document, with a title and a root tag type Document struct { title []byte root *Tag } // NewDocument creates a new XML/HTML/SVG image, with a root tag. // If rootTagName contains "<" or ">", it can be used for preceding declarations, // like or . // Returns a pointer to a Document. func NewDocument(title, rootTagName []byte) *Document { var image Document image.title = title rootTag := NewTag(rootTagName) image.root = rootTag return &image } // GetTag searches all tags for the given name func (image *Document) GetTag(name []byte) (*Tag, error) { return image.root.GetTag(name) } // GetRoot returns the root tag of the image func (image *Document) GetRoot() *Tag { return image.root } // AddContent adds content to the body tag. // Returns the body tag and nil if successful. // Returns and an error if no body tag is found, else nil. func (image *Document) AddContent(content []byte) (*Tag, error) { body, err := image.root.GetTag([]byte("body")) if err == nil { body.AddContent(content) } return body, err } // Bytes renders the image as an XML document func (image *Document) Bytes() []byte { return image.root.Bytes() } // String renders the image as an XML document func (image *Document) String() string { return image.root.String() } // SaveSVG will save the current image as an SVG file func (image *Document) SaveSVG(filename string) error { return os.WriteFile(filename, image.Bytes(), 0644) } // WriteTo will write the current image to the given io.Writer. // Returns bytes written and possibly an error. // This also fullfills the io.WriterTo interface. func (image *Document) WriteTo(w io.Writer) (int64, error) { return image.root.WriteTo(w) } png2svg-1.7.0/vendor/github.com/xyproto/tinysvg/tags.go000066400000000000000000000240721504213346300231630ustar00rootroot00000000000000// Package tinysvg has structs and functions for creating and rendering TinySVG images package tinysvg // Everything here deals with bytes, not strings // TODO: Add a Write function that takes an io.Writer so that the image can be written as it is generated. import ( "bytes" "fmt" "io" ) // Tag represents an XML tag, as part of a larger XML document type Tag struct { name []byte content []byte lastContent []byte xmlContent []byte attrs map[string][]byte nextSibling *Tag // siblings firstChild *Tag // first child } var ( gt = []byte{'>'} lt = []byte{'<'} ltSlash = []byte("") equalEscapedQuote = []byte("=\"") escapedQuoteSpace = []byte("\" ") space = []byte{' '} ) // NewTag creates a new tag based on the given name. // "name" is what will appear right after "<" when rendering as XML/HTML/SVG. func NewTag(name []byte) *Tag { var tag Tag tag.name = name tag.attrs = make(map[string][]byte) return &tag } // AddNewTag adds a new tag to another tag. This will place it one step lower // in the hierarchy of tags. You can for example add a body tag to an html tag. func (tag *Tag) AddNewTag(name []byte) *Tag { child := NewTag(name) tag.AddChild(child) return child } // AddTag adds a tag to another tag func (tag *Tag) AddTag(child *Tag) { tag.AddChild(child) } // AddAttrib adds an attribute to a tag, for instance "size" and "20" func (tag *Tag) AddAttrib(attrName string, attrValue []byte) { tag.attrs[attrName] = attrValue } // AddAttribMap adds attributes based on a given map func (tag *Tag) AddAttribMap(attrMap map[string][]byte) { //attrName string, attrValue []byte) { for attrName, attrValue := range attrMap { tag.attrs[attrName] = attrValue } } // AddSingularAttrib adds attribute without a value func (tag *Tag) AddSingularAttrib(attrName string) { tag.attrs[attrName] = nil } // GetAttrString returns a []byte that represents all the attribute keys and // values of a tag. This can be used when generating XML, SVG or HTML. func (tag *Tag) GetAttrString() []byte { ret := make([]byte, 0) for key, value := range tag.attrs { if value == nil { ret = append(ret, key...) ret = append(ret, ' ') } else { ret = append(ret, key...) ret = append(ret, equalEscapedQuote...) // =\" ret = append(ret, value...) ret = append(ret, escapedQuoteSpace...) // \" } } if len(ret) > 0 { ret = ret[:len(ret)-1] } return ret } // getFlatXML renders XML. // This will generate a []byte for a tag, non-recursively. func (tag *Tag) getFlatXML() []byte { // For the root tag if (len(tag.name) > 0) && (tag.name[0] == '<') { ret := make([]byte, 0, len(tag.name)+len(tag.content)+len(tag.xmlContent)+len(tag.lastContent)) ret = append(ret, tag.name...) ret = append(ret, tag.content...) ret = append(ret, tag.xmlContent...) ret = append(ret, tag.lastContent...) return ret } // For indenting spacing := make([]byte, 0) // Generate the XML based on the tag attrs := tag.GetAttrString() ret := make([]byte, 0) ret = append(ret, spacing...) ret = append(ret, '<') ret = append(ret, tag.name...) if len(attrs) > 0 { ret = append(ret, ' ') ret = append(ret, attrs...) } if (len(tag.content) == 0) && (len(tag.xmlContent) == 0) && (len(tag.lastContent) == 0) { ret = append(ret, []byte(spaceSlashGt)...) // /> } else { if len(tag.xmlContent) > 0 { if tag.xmlContent[0] != ' ' { ret = append(ret, '>') ret = append(ret, spacing...) ret = append(ret, tag.xmlContent...) ret = append(ret, spacing...) ret = append(ret, ltSlash...) // ') } else { ret = append(ret, '>') ret = append(ret, tag.xmlContent...) ret = append(ret, spacing...) ret = append(ret, ltSlash...) // ') } } else { ret = append(ret, '>') ret = append(ret, tag.content...) ret = append(ret, tag.lastContent...) ret = append(ret, ltSlash...) // ') } } return ret } // writeFlatXML renders an XML tag to an io.Writer. // This will generate a bytes for a tag, non-recursively. func (tag *Tag) writeFlatXML(w io.Writer) (n int64, err error) { nameLen := len(tag.name) if nameLen > 0 && tag.name[0] == '<' { // root tag parts := [][]byte{tag.name, tag.content, tag.xmlContent, tag.lastContent} for _, part := range parts { if len(part) > 0 { x, err := w.Write(part) n += int64(x) if err != nil { return n, err } } } return n, nil } attrs := tag.GetAttrString() attrsLen := len(attrs) contentLen := len(tag.content) xmlContentLen := len(tag.xmlContent) lastContentLen := len(tag.lastContent) ltLen := len(lt) gtLen := len(gt) spaceLen := len(space) spaceSlashGtLen := len(spaceSlashGt) ltSlashLen := len(ltSlash) totalSize := ltLen + nameLen + gtLen if attrsLen > 0 { totalSize += spaceLen + attrsLen } isEmpty := contentLen == 0 && xmlContentLen == 0 && lastContentLen == 0 if isEmpty { totalSize += spaceSlashGtLen - gtLen } else { totalSize += contentLen + xmlContentLen + lastContentLen totalSize += ltSlashLen + nameLen + gtLen } buf := make([]byte, 0, totalSize) buf = append(buf, lt...) buf = append(buf, tag.name...) if attrsLen > 0 { buf = append(buf, space...) buf = append(buf, attrs...) } if isEmpty { buf = append(buf, spaceSlashGt...) } else { buf = append(buf, gt...) if xmlContentLen > 0 { buf = append(buf, tag.xmlContent...) } else { buf = append(buf, tag.content...) buf = append(buf, tag.lastContent...) } buf = append(buf, ltSlash...) buf = append(buf, tag.name...) buf = append(buf, gt...) } x, err := w.Write(buf) return int64(x), err } // GetChildren returns all children for a given tag. // Returns a slice of pointers to tags. func (tag *Tag) GetChildren() []*Tag { var children []*Tag current := tag.firstChild for current != nil { children = append(children, current) current = current.nextSibling } return children } // AddChild adds a tag as a child to another tag func (tag *Tag) AddChild(child *Tag) { if tag.firstChild == nil { tag.firstChild = child return } lastChild := tag.LastChild() child.nextSibling = nil lastChild.nextSibling = child } // AddContent adds text to a tag. // This is what will appear between two tag markers, for example: // content // If the tag contains child tags, they will be rendered after this content. func (tag *Tag) AddContent(content []byte) { tag.content = append(tag.content, content...) } // AppendContent appends content to the end of the existing content of a tag func (tag *Tag) AppendContent(content []byte) { tag.lastContent = append(tag.lastContent, content...) } // AddLastContent appends content to the end of the existing content of a tag. // Deprecated. func (tag *Tag) AddLastContent(content []byte) { tag.AppendContent(content) } // CountChildren returns the number of children a tag has func (tag *Tag) CountChildren() int { child := tag.firstChild if child == nil { return 0 } count := 1 if child.nextSibling == nil { return count } child = child.nextSibling for child != nil { count++ child = child.nextSibling } return count } // CountSiblings returns the number of siblings a tag has func (tag *Tag) CountSiblings() int { sib := tag.nextSibling if sib == nil { return 0 } count := 1 if sib.nextSibling == nil { return count } sib = sib.nextSibling for sib != nil { count++ sib = sib.nextSibling } return count } // LastChild returns the last child of a tag func (tag *Tag) LastChild() *Tag { child := tag.firstChild for child.nextSibling != nil { child = child.nextSibling } return child } // GetTag finds a tag by name and returns an error if not found. // Returns the first tag that matches. func (tag *Tag) GetTag(name []byte) (*Tag, error) { if bytes.Index(tag.name, name) == 0 { return tag, nil } couldNotFindError := fmt.Errorf("could not find tag: %s", name) if tag.CountChildren() == 0 { // No children. Not found so far. return nil, couldNotFindError } child := tag.firstChild for child != nil { found, err := child.GetTag(name) if err == nil { return found, err } child = child.nextSibling } return nil, couldNotFindError } // ShallowCopy creates a copy of a tag, but uses the same attribute map! func (tag *Tag) ShallowCopy() *Tag { var nt Tag nt.name = tag.name nt.content = tag.content nt.lastContent = tag.lastContent nt.xmlContent = tag.xmlContent nt.attrs = tag.attrs nt.nextSibling = tag.nextSibling nt.firstChild = tag.firstChild return &nt } // Bytes (previously getXMLRecursively) renders XML for a tag, recursively. // The generated XML is returned as a []byte. func (tag *Tag) Bytes() []byte { if tag.CountChildren() == 0 { return tag.getFlatXML() } var ( content []byte xmlContent []byte child = tag.firstChild ) for child != nil { xmlContent = child.Bytes() if len(xmlContent) > 0 { content = append(content, xmlContent...) } child = child.nextSibling } tagCopy := tag.ShallowCopy() tagCopy.xmlContent = append(tagCopy.xmlContent, tag.content...) tagCopy.xmlContent = append(tagCopy.xmlContent, content...) tagCopy.xmlContent = append(tagCopy.xmlContent, tag.lastContent...) return tagCopy.getFlatXML() } // WriteTo renders XML for a tag, recursively. // The generated XML is written to the given io.Writer. // This also fullfills the io.WriterTo interface. func (tag *Tag) WriteTo(w io.Writer) (n int64, err error) { if tag.CountChildren() == 0 { return tag.writeFlatXML(w) } var ( content bytes.Buffer child = tag.firstChild ) for child != nil { child.WriteTo(&content) child = child.nextSibling } tagCopy := tag.ShallowCopy() tagCopy.xmlContent = append(tagCopy.xmlContent, tag.content...) tagCopy.xmlContent = append(tagCopy.xmlContent, content.Bytes()...) tagCopy.xmlContent = append(tagCopy.xmlContent, tag.lastContent...) return tagCopy.writeFlatXML(w) } // String returns the XML contents as a string func (tag *Tag) String() string { return string(tag.Bytes()) } png2svg-1.7.0/vendor/github.com/xyproto/tinysvg/tinysvg.go000066400000000000000000000400601504213346300237230ustar00rootroot00000000000000// Package tinysvg supports generating and writing TinySVG 1.2 images // // Some function names are suffixed with "2" if they take structs instead of ints/floats, // "i" if they take ints and "f" if they take floats. Using generics might be an option. package tinysvg import ( "bytes" "errors" "fmt" "strconv" "strings" ) const ( TRANSPARENT = 0.0 OPAQUE = 1.0 NO = iota YES AUTO ) type ( Vec2 struct { X, Y float64 } Pos Vec2 Radius Vec2 Size struct { W, H float64 } Color struct { R, G, B int // red, green, blue (0..255) A float64 // alpha, 0.0..1.0 N string // name (optional, will override the above values) } Font struct { Family string Size int } YesNoAuto int ) const ( xmlVersionEncoding = `` xmlNS = "http://www.w3.org/2000/svg" svgTag = "svg" svgProfile = "tiny" svgVersion = "1.2" ) var ( ErrPair = errors.New("position pairs must be exactly two comma separated numbers") sizeOne = Size{1.0, 1.0} ) // NewTinySVG will create a new TinySVG document, where the width and height is defined in pixels, using the "px" suffix. func NewTinySVG(w, h int) (*Document, *Tag) { page := NewDocument([]byte{}, []byte(xmlVersionEncoding)) svg := page.root.AddNewTag([]byte(svgTag)) // Add attributes to the tag svg.AddAttribMap(map[string][]byte{ "xmlns": []byte(xmlNS), "version": []byte(svgVersion), "baseProfile": []byte(svgProfile), "viewBox": []byte(fmt.Sprintf("%d %d %d %d", 0, 0, w, h)), "width": []byte(fmt.Sprintf("%dpx", w)), "height": []byte(fmt.Sprintf("%dpx", h)), }) return page, svg } // NewTinySVG2 creates new TinySVG 1.2 image. Pos and Size defines the viewbox func NewTinySVG2(p *Pos, s *Size) (*Document, *Tag) { // No page title is needed when building an SVG tag tree page := NewDocument([]byte{}, []byte(xmlVersionEncoding)) // No longer needed for TinySVG 1.2. See: https://www.w3.org/TR/SVGTiny12/intro.html#defining // // Add the root tag svg := page.root.AddNewTag([]byte(svgTag)) // Add attributes to the tag svg.AddAttribMap(map[string][]byte{ "xmlns": []byte(xmlNS), "version": []byte(svgVersion), "baseProfile": []byte(svgProfile), "viewBox": []byte(fmt.Sprintf("%f %f %f %f", p.X, p.Y, s.W, s.H)), }) return page, svg } // f2b converts the given float64 to a string representation. // .0 or .000000 suffixes are stripped. // The string is returned as a byte slice. func f2b(x float64) []byte { return []byte(strings.TrimSuffix(strings.TrimSuffix(fmt.Sprintf("%f", x), ".0"), ".000000")) } // Rect2 creates a rectangle, given x and y position, width and height. // No color is being set. func (svg *Tag) Rect2(p *Pos, s *Size, c *Color) *Tag { rect := svg.AddNewTag([]byte("rect")) rect.AddAttribMap(map[string][]byte{ "x": f2b(p.X), "y": f2b(p.Y), "width": f2b(s.W), "height": f2b(s.H), }) rect.Fill2(c) return rect } // RoundedRect2 a rectangle, given x and y position, width and height. // No color is being set. func (svg *Tag) RoundedRect2(p *Pos, r *Radius, s *Size, c *Color) *Tag { rect := svg.AddNewTag([]byte("rect")) rect.AddAttribMap(map[string][]byte{ "x": f2b(p.X), "y": f2b(p.Y), "rx": f2b(r.X), "ry": f2b(r.Y), "width": f2b(s.W), "height": f2b(s.H), }) rect.Fill2(c) return rect } // Text2 adds text. No color is being set func (svg *Tag) Text2(p *Pos, f *Font, message string, c *Color) *Tag { text := svg.AddNewTag([]byte("text")) text.AddAttribMap(map[string][]byte{ "x": f2b(p.X), "y": f2b(p.Y), "font-family": []byte(f.Family), "font-size": []byte(strconv.Itoa(f.Size)), }) text.Fill2(c) text.AddContent([]byte(message)) return text } // Circle2 adds a circle, given a position, radius and color func (svg *Tag) Circle2(p *Pos, radius int, c *Color) *Tag { circle := svg.AddNewTag([]byte("circle")) circle.AddAttribMap(map[string][]byte{ "cx": f2b(p.X), "cy": f2b(p.Y), "r": []byte(strconv.Itoa(radius)), }) circle.Fill2(c) return circle } // Circlef adds a circle, given a position, radius and color func (svg *Tag) Circlef(p *Pos, radius float64, c *Color) *Tag { circle := svg.AddNewTag([]byte("circle")) circle.AddAttribMap(map[string][]byte{ "cx": f2b(p.X), "cy": f2b(p.Y), "r": f2b(radius), }) circle.Fill2(c) return circle } // Ellipse2 adds an ellipse with a given position (x,y) and radius (rx, ry). func (svg *Tag) Ellipse2(p *Pos, r *Radius, c *Color) *Tag { ellipse := svg.AddNewTag([]byte("ellipse")) ellipse.AddAttribMap(map[string][]byte{ "cx": f2b(p.X), "cy": f2b(p.Y), "rx": f2b(r.X), "ry": f2b(r.Y), }) ellipse.Fill2(c) return ellipse } // Line2 adds a line from (x1, y1) to (x2, y2) with a given stroke width and color func (svg *Tag) Line2(p1, p2 *Pos, thickness int, c *Color) *Tag { line := svg.AddNewTag([]byte("line")) line.AddAttribMap(map[string][]byte{ "x1": f2b(p1.X), "y1": f2b(p1.Y), "x2": f2b(p2.X), "y2": f2b(p2.Y), }) line.Thickness(thickness) line.Stroke2(c) return line } // Triangle2 adds a colored triangle func (svg *Tag) Triangle2(p1, p2, p3 *Pos, c *Color) *Tag { triangle := svg.AddNewTag([]byte("path")) triangle.AddAttrib("d", []byte(fmt.Sprintf("M %f %f L %f %f L %f %f L %f %f", p1.X, p1.Y, p2.X, p2.Y, p3.X, p3.Y, p1.X, p1.Y))) triangle.Fill2(c) return triangle } // Poly2 adds a colored path with 4 points func (svg *Tag) Poly2(p1, p2, p3, p4 *Pos, c *Color) *Tag { poly4 := svg.AddNewTag([]byte("path")) poly4.AddAttrib("d", []byte(fmt.Sprintf("M %f %f L %f %f L %f %f L %f %f L %f %f", p1.X, p1.Y, p2.X, p2.Y, p3.X, p3.Y, p4.X, p4.Y, p1.X, p1.Y))) poly4.Fill2(c) return poly4 } // Fill2 selects the fill color that will be used when drawing func (svg *Tag) Fill2(c *Color) { // If no color name is given and the color is transparent, don't set a fill color if (c == nil) || (len(c.N) == 0 && c.A == TRANSPARENT) { return } svg.AddAttrib("fill", c.Bytes()) } // Stroke2 selects the stroke color that will be used when drawing func (svg *Tag) Stroke2(c *Color) { // If no color name is given and the color is transparent, don't set a stroke color if (c == nil) || (len(c.N) == 0 && c.A == TRANSPARENT) { return } svg.AddAttrib("stroke", c.Bytes()) } // RGBBytes converts r, g and b (integers in the range 0..255) // to a color string on the form "#nnnnnn", returned as a byte slice. // May also return colors strings on the form "#nnn". func RGBBytes(r, g, b int) []byte { rs := strconv.FormatInt(int64(r), 16) gs := strconv.FormatInt(int64(g), 16) bs := strconv.FormatInt(int64(b), 16) if len(rs) == 1 && len(gs) == 1 && len(bs) == 1 { // short form return []byte("#" + rs + gs + bs) } // long form return []byte(fmt.Sprintf("#%02x%02x%02x", r, g, b)) } // RGBABytes converts integers r, g and b (the color) and also // a given alpha (opacity) to a color-string on the form // "rgba(255, 255, 255, 1.0)". func RGBABytes(r, g, b int, a float64) []byte { return []byte(fmt.Sprintf("rgba(%d, %d, %d, %f)", r, g, b, a)) } // RGB creates a new Color with the given red, green and blue values. // The colors are in the range 0..255. func RGB(r, g, b int) *Color { return &Color{r, g, b, OPAQUE, ""} } // RGBA creates a new Color with the given red, green, blue and alpha values. // Alpha is between 0 and 1, the rest are 0..255. // For the alpha value, 0 is transparent and 1 is opaque. func RGBA(r, g, b int, a float64) *Color { return &Color{r, g, b, a, ""} } // ColorByName creates a new Color with a given name, like "blue" func ColorByName(name string) *Color { return &Color{N: name} } // NewColor is the same as ColorByName func NewColor(name string) *Color { return ColorByName(name) } // Bytes returns the color as an RGB (#1234FF) byte slice, // or as an RGBA (rgba(0, 1, 2 ,3)) byte slice. func (c *Color) Bytes() []byte { // Return an empty string if nil if c == nil { return make([]byte, 0) } // Return the name, if specified if len(c.N) != 0 { return []byte(c.N) } // Return a regular RGB string if alpha is 1.0 if c.A == OPAQUE { // Generate a rgb string return RGBBytes(c.R, c.G, c.B) } // Generate a rgba string if alpha is < 1.0 return RGBABytes(c.R, c.G, c.B, c.A) } // --- Convenience functions and functions for backward compatibility --- func NewTinySVGi(x, y, w, h int) (*Document, *Tag) { return NewTinySVG2(&Pos{float64(x), float64(y)}, &Size{float64(w), float64(h)}) } func NewTinySVGf(x, y, w, h float64) (*Document, *Tag) { return NewTinySVG2(&Pos{x, y}, &Size{w, h}) } // AddRect adds a rectangle, given x and y position, width and height. // No color is being set. func (svg *Tag) AddRect(x, y, w, h int) *Tag { return svg.Rect2(&Pos{float64(x), float64(y)}, &Size{float64(w), float64(h)}, nil) } // AddRoundedRect adds a rectangle, given x and y position, radius x, radius y, width and height. // No color is being set. func (svg *Tag) AddRoundedRect(x, y, rx, ry, w, h int) *Tag { return svg.RoundedRect2(&Pos{float64(x), float64(y)}, &Radius{float64(rx), float64(ry)}, &Size{float64(w), float64(h)}, nil) } // AddRectf adds a rectangle, given x and y position, width and height. // No color is being set. func (svg *Tag) AddRectf(x, y, w, h float64) *Tag { return svg.Rect2(&Pos{x, y}, &Size{w, h}, nil) } // AddRoundedRectf adds a rectangle, given x and y position, radius x, radius y, width and height. // No color is being set. func (svg *Tag) AddRoundedRectf(x, y, rx, ry, w, h float64) *Tag { return svg.RoundedRect2(&Pos{x, y}, &Radius{rx, ry}, &Size{w, h}, nil) } // AddText adds text. No color is being set func (svg *Tag) AddText(x, y, fontSize int, fontFamily, text string) *Tag { return svg.Text2(&Pos{float64(x), float64(y)}, &Font{fontFamily, fontSize}, text, nil) } // Box adds a rectangle, given x and y position, width, height and color func (svg *Tag) Box(x, y, w, h int, color string) *Tag { return svg.Rect2(&Pos{float64(x), float64(y)}, &Size{float64(w), float64(h)}, ColorByName(color)) } // AddCircle adds a circle Add a circle, given a position (x, y) and a radius. // No color is being set. func (svg *Tag) AddCircle(x, y, radius int) *Tag { return svg.Circle2(&Pos{float64(x), float64(y)}, radius, nil) } // AddCirclef adds a circle Add a circle, given a position (x, y) and a radius. // No color is being set. func (svg *Tag) AddCirclef(x, y, radius float64) *Tag { return svg.Circlef(&Pos{x, y}, radius, nil) } // AddEllipse adds an ellipse with a given position (x,y) and radius (rx, ry). // No color is being set. func (svg *Tag) AddEllipse(x, y, rx, ry int) *Tag { return svg.Ellipse2(&Pos{float64(x), float64(y)}, &Radius{float64(rx), float64(ry)}, nil) } // AddEllipsef adds an ellipse with a given position (x,y) and radius (rx, ry). // No color is being set. func (svg *Tag) AddEllipsef(x, y, rx, ry float64) *Tag { return svg.Ellipse2(&Pos{x, y}, &Radius{rx, ry}, nil) } // Line adds a line from (x1, y1) to (x2, y2) with a given stroke width and color func (svg *Tag) Line(x1, y1, x2, y2, thickness int, color string) *Tag { return svg.Line2(&Pos{float64(x1), float64(y1)}, &Pos{float64(x2), float64(y2)}, thickness, ColorByName(color)) } // Triangle adds a colored triangle func (svg *Tag) Triangle(x1, y1, x2, y2, x3, y3 int, color string) *Tag { return svg.Triangle2(&Pos{float64(x1), float64(y1)}, &Pos{float64(x2), float64(y2)}, &Pos{float64(x3), float64(y3)}, ColorByName(color)) } // Poly4 adds a colored path with 4 points func (svg *Tag) Poly4(x1, y1, x2, y2, x3, y3, x4, y4 int, color string) *Tag { return svg.Poly2(&Pos{float64(x1), float64(y1)}, &Pos{float64(x2), float64(y2)}, &Pos{float64(x3), float64(y3)}, &Pos{float64(x4), float64(y4)}, ColorByName(color)) } // Circle adds a circle, given x and y position, radius and color func (svg *Tag) Circle(x, y, radius int, color string) *Tag { return svg.Circle2(&Pos{float64(x), float64(y)}, radius, ColorByName(color)) } // Ellipse adds an ellipse, given x and y position, radiuses and color func (svg *Tag) Ellipse(x, y, xr, yr int, color string) *Tag { return svg.Ellipse2(&Pos{float64(x), float64(y)}, &Radius{float64(xr), float64(yr)}, ColorByName(color)) } // Fill selects the fill color that will be used when drawing func (svg *Tag) Fill(color string) { svg.AddAttrib("fill", []byte(color)) } // ColorBytes converts r, g and b (integers in the range 0..255) // to a color string on the form "#nnnnnn". func ColorBytes(r, g, b int) []byte { return RGB(r, g, b).Bytes() } // ColorBytesAlpha converts integers r, g and b (the color) and also // a given alpha (opacity) to a color-string on the form // "rgba(255, 255, 255, 1.0)". func ColorBytesAlpha(r, g, b int, a float64) []byte { return RGBA(r, g, b, a).Bytes() } // Pixel creates a rectangle that is 1 wide with the given color. // Note that the size of the "pixel" depends on how large the viewBox is. func (svg *Tag) Pixel(x, y, r, g, b int) *Tag { return svg.Rect2(&Pos{float64(x), float64(y)}, &sizeOne, RGB(r, g, b)) } // AlphaDot creates a small circle that can be transparent. // Takes a position (x, y) and a color (r, g, b, a). func (svg *Tag) AlphaDot(x, y, r, g, b int, a float32) *Tag { return svg.Circle2(&Pos{float64(x), float64(y)}, 1, RGBA(r, g, b, float64(a))) } // Dot adds a small colored circle. // Takes a position (x, y) and a color (r, g, b). func (svg *Tag) Dot(x, y, r, g, b int) *Tag { return svg.Circle2(&Pos{float64(x), float64(y)}, 1, RGB(r, g, b)) } // Text adds text, with a color func (svg *Tag) Text(x, y, fontSize int, fontFamily, text, color string) *Tag { return svg.Text2(&Pos{float64(x), float64(y)}, &Font{fontFamily, fontSize}, text, ColorByName(color)) } // NewYesNoAuto will create a new Yes/No/Auto struct. If auto is true, it overrides the yes value. func NewYesNoAuto(yes bool, auto bool) YesNoAuto { if auto { return AUTO } if yes { return YES } return NO } func (yna YesNoAuto) Bytes() []byte { switch yna { case YES: return []byte("true") case AUTO: return []byte("auto") default: return []byte("false") } } // Focusable sets the "focusable" attribute to either true, false or auto // If "auto" is true, it overrides the value of "yes". func (svg *Tag) Focusable(yes bool, auto bool) { svg.AddAttrib("focusable", NewYesNoAuto(yes, auto).Bytes()) } // Thickness sets the stroke-width attribute func (svg *Tag) Thickness(thickness int) { svg.AddAttrib("stroke-width", []byte(strconv.Itoa(thickness))) } // Polyline adds a set of connected straight lines, an open shape func (svg *Tag) Polyline(points []*Pos, c *Color) *Tag { polyline := svg.AddNewTag([]byte("polyline")) var buf bytes.Buffer lastIndex := len(points) - 1 for i, p := range points { buf.Write(f2b(p.X)) buf.WriteByte(',') buf.Write(f2b(p.Y)) if i != lastIndex { buf.WriteByte(' ') } } polyline.AddAttrib("points", buf.Bytes()) return polyline } // Polygon adds a set of connected straight lines, a closed shape func (svg *Tag) Polygon(points []*Pos, c *Color) *Tag { polygon := svg.AddNewTag([]byte("polygon")) var buf bytes.Buffer lastIndex := len(points) - 1 for i, p := range points { buf.Write(f2b(p.X)) buf.WriteByte(',') buf.Write(f2b(p.Y)) if i != lastIndex { buf.WriteByte(' ') } } polygon.AddAttrib("points", buf.Bytes()) polygon.Fill2(c) return polygon } func NewPos(xString, yString string) (*Pos, error) { x, err := strconv.ParseFloat(xString, 64) if err != nil { return nil, err } y, err := strconv.ParseFloat(yString, 64) if err != nil { return nil, err } return &Pos{x, y}, nil } func NewPosf(x, y float64) *Pos { return &Pos{x, y} } func PointsFromString(pointString string) ([]*Pos, error) { points := make([]*Pos, 0) for _, positionPair := range strings.Split(pointString, " ") { elements := strings.Split(positionPair, ",") if len(elements) != 2 { return nil, ErrPair } p, err := NewPos(elements[0], elements[1]) if err != nil { return nil, err } points = append(points, p) } return points, nil } // Describe can be used for adding a description to the SVG header func (svg *Tag) Describe(description string) { desc := svg.AddNewTag([]byte("desc")) desc.AddContent([]byte(description)) } png2svg-1.7.0/vendor/modules.txt000066400000000000000000000015221504213346300166140ustar00rootroot00000000000000# github.com/cpuguy83/go-md2man/v2 v2.0.7 ## explicit; go 1.12 github.com/cpuguy83/go-md2man/v2/md2man # github.com/lucasb-eyer/go-colorful v1.2.0 ## explicit; go 1.12 github.com/lucasb-eyer/go-colorful # github.com/peterhellberg/gfx v0.0.0-20250602150231-2e41f5fad310 ## explicit; go 1.17 github.com/peterhellberg/gfx # github.com/russross/blackfriday/v2 v2.1.0 ## explicit github.com/russross/blackfriday/v2 # github.com/urfave/cli/v2 v2.27.7 ## explicit; go 1.18 github.com/urfave/cli/v2 # github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 ## explicit; go 1.15 github.com/xrash/smetrics # github.com/xyproto/burnpal v1.0.0 ## explicit; go 1.11 github.com/xyproto/burnpal # github.com/xyproto/palgen v1.6.1 ## explicit; go 1.16 github.com/xyproto/palgen # github.com/xyproto/tinysvg v1.2.1 ## explicit; go 1.9 github.com/xyproto/tinysvg png2svg-1.7.0/version.go000066400000000000000000000001721504213346300151220ustar00rootroot00000000000000package png2svg // VersionString contains the package name and the current version const VersionString = "png2svg 1.7.0" png2svg-1.7.0/version.sh000077500000000000000000000011051504213346300151270ustar00rootroot00000000000000#!/bin/sh -e # # Self-modifying script that updates the version numbers # # The current version goes here, as the default value VERSION=${1:-'1.7.0'} if [ -z "$1" ]; then echo "The current version is $VERSION, pass the new version as the first argument if you wish to change it" exit 0 fi echo "Setting the version to $VERSION" # Set the version in various files setconf README.md '* Version' $VERSION setconf version.go "const VersionString" "\"png2svg "$VERSION"\"" # Update the version in this script sed -i "s/[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*/$VERSION/g" "$0"