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 0000000..603107f Binary files /dev/null and b/tests/images/svg/triangle01.png differ diff --git a/tests/images/svg/triangle01.svg b/tests/images/svg/triangle01.svg new file mode 100644 index 0000000..ef65f9b --- /dev/null +++ b/tests/images/svg/triangle01.svg @@ -0,0 +1,11 @@ + + + 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")