From 79189a473eaecde4ac773771306aa53bc0940fa1 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 19 Dec 2020 18:01:14 -0600 Subject: [PATCH] svg decode more support --- src/pixie/fileformats/svg.nim | 169 +++++++++++++++++++++----------- tests/images/svg/triangle01.png | Bin 0 -> 4048 bytes tests/images/svg/triangle01.svg | 11 +++ tests/test_svg.nim | 20 ++-- 4 files changed, 136 insertions(+), 64 deletions(-) create mode 100644 tests/images/svg/triangle01.png create mode 100644 tests/images/svg/triangle01.svg diff --git a/src/pixie/fileformats/svg.nim b/src/pixie/fileformats/svg.nim index 6819ee3..61518b2 100644 --- a/src/pixie/fileformats/svg.nim +++ b/src/pixie/fileformats/svg.nim @@ -1,75 +1,130 @@ ## Load and Save SVG files. -import chroma, pixie/images, pixie/common, pixie/paths, vmath, xmlparser, xmltree, - strutils, strutils, bumpy +import chroma, pixie/images, pixie/common, pixie/paths, vmath, xmlparser, + xmltree, strutils, strutils, bumpy const svgSignature* = " + return + + case node.tag: + of "title": + discard + of "desc": + discard + of "g": + let ctx = decodeCtx(ctxStack[^1], node) + ctxStack.add(ctx) + for child in node: + img.draw(child, ctxStack) + discard ctxStack.pop() + of "path": + let + d = node.attr("d") + ctx = decodeCtx(ctxStack[^1], node) + if ctx.fill != ColorRGBA(): + let (bounds, fillImg) = fillPathBounds(d, ctx.fill, ctx.transform) + img.draw(fillImg, bounds.xy) + if ctx.stroke != ColorRGBA(): + let (bounds, strokeImg) = strokePathBounds( + d, ctx.stroke, ctx.strokeWidth, ctx.transform + ) + img.draw(strokeImg, bounds.xy) + of "rect": + let + ctx = decodeCtx(ctxStack[^1], node) + x = parseFloat(node.attr("x")) + y = parseFloat(node.attr("y")) + width = parseFloat(node.attr("width")) + height = parseFloat(node.attr("height")) + path = newPath() + path.moveTo(x, y) + path.lineTo(x + width, x) + path.lineTo(x + width, y + height) + path.lineTo(x, y + height) + path.closePath() + if ctx.fill != ColorRGBA(): + img.fillPath(path, ctx.fill, ctx.transform) + if ctx.stroke != ColorRGBA(): + img.strokePath(path, ctx.stroke, ctx.strokeWidth, ctx.transform) + else: + raise newException(PixieError, "Unsupported SVG tag: " & node.tag & ".") proc decodeSvg*(data: string): Image = ## Render SVG file and return the image. try: - let xml = parseXml(data) - if xml.tag != "svg": + let root = parseXml(data) + if root.tag != "svg": failInvalid() let - viewBox = xml.attr("viewBox") + viewBox = root.attr("viewBox") box = viewBox.split(" ") if parseInt(box[0]) != 0 or parseInt(box[1]) != 0: failInvalid() @@ -77,11 +132,11 @@ proc decodeSvg*(data: string): Image = let width = parseInt(box[2]) height = parseInt(box[3]) + var ctxStack = @[initCtx()] result = newImage(width, height) - var matStack = @[mat3()] - for n in xml: - result.draw(matStack, n) + for node in root: + result.draw(node, ctxStack) except PixieError as e: raise e except: - raise newException(PixieError, "Unable to load SVG") + raise newException(PixieError, "Unable to load SVG") diff --git a/tests/images/svg/triangle01.png b/tests/images/svg/triangle01.png new file mode 100644 index 0000000000000000000000000000000000000000..603107fdb2538ad2228050b3d8b2059ef4a3f233 GIT binary patch literal 4048 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 + + Example triangle01- simple example of a 'path' + A path that draws a triangle + + + + diff --git a/tests/test_svg.nim b/tests/test_svg.nim index d9b2361..1abc0bd 100644 --- a/tests/test_svg.nim +++ b/tests/test_svg.nim @@ -1,9 +1,15 @@ -import pixie/fileformats/svg, pixie +import pixie/fileformats/svg, pixie, strformat -let - original = readFile("tests/images/svg/Ghostscript_Tiger.svg") - image = decodeSvg(original) - gold = readImage("tests/images/svg/Ghostscript_Tiger.png") +const files = [ + "triangle01", + "Ghostscript_Tiger" +] -doAssert image.data == gold.data -# image.writeFile("tests/images/svg/Ghostscript_Tiger.png") +for file in files: + let + original = readFile(&"tests/images/svg/{file}.svg") + image = decodeSvg(original) + gold = readImage(&"tests/images/svg/{file}.png") + + doAssert image.data == gold.data + # image.writeFile(&"{file}.png")