diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9977fff..2fa1bfa 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,12 +6,18 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest, windows-latest]
+        nim-version: ['1.4.x', 'stable']
+        include:
+          - nim-version: '1.4.x'
+          - nim-version: 'stable'
 
     runs-on: ${{ matrix.os }}
 
     steps:
     - uses: actions/checkout@v2
     - uses: jiro4989/setup-nim-action@v1
+      with:
+        nim-version: ${{ matrix.nim-version }}
     - run: nimble test -d:release -y
     - run: nimble test -d:release -d:pixieNoSimd -y
     - run: nimble test --gc:orc -d:release -y
diff --git a/pixie.nimble b/pixie.nimble
index d8cf7b3..2336455 100644
--- a/pixie.nimble
+++ b/pixie.nimble
@@ -1,14 +1,14 @@
-version     = "3.1.2"
+version     = "3.1.3"
 author      = "Andre von Houck and Ryan Oldenburg"
 description = "Full-featured 2d graphics library for Nim."
 license     = "MIT"
 
 srcDir = "src"
 
-requires "nim >= 1.4.0"
+requires "nim >= 1.4.8"
 requires "vmath >= 1.1.0"
 requires "chroma >= 0.2.5"
-requires "zippy >= 0.7.4"
+requires "zippy >= 0.8.1"
 requires "flatty >= 0.2.2"
 requires "nimsimd >= 1.0.0"
 requires "bumpy >= 1.0.3"
diff --git a/src/pixie/fileformats/gif.nim b/src/pixie/fileformats/gif.nim
index b2ee795..1b89302 100644
--- a/src/pixie/fileformats/gif.nim
+++ b/src/pixie/fileformats/gif.nim
@@ -101,7 +101,10 @@ proc decodeGif*(data: string): Image {.raises: [PixieError].} =
 
       # Turn full lzw data into bit stream.
       var
-        bs = initBitStream(lzwData)
+        bs = BitStreamReader(
+          src: cast[ptr UncheckedArray[uint8]](lzwData[0].addr),
+          len: lzwData.len
+        )
         bitSize = lzwMinBitSize + 1
         currentCodeTableMax = (1 shl (bitSize)) - 1
         codeLast: int = -1
@@ -110,10 +113,10 @@ proc decodeGif*(data: string): Image {.raises: [PixieError].} =
 
       # Main decode loop.
       while codeLast != endCode:
-        if bs.pos + bitSize.int > bs.data.len * 8: failInvalid()
+        if bs.pos + bitSize.int > bs.len * 8: failInvalid()
         var
           # Read variable bits out of the table.
-          codeId = bs.readBits(bitSize).int
+          codeId = bs.readBits(bitSize.int).int
           # Some time we need to carry over table information.
           carryOver: seq[int]
 
diff --git a/src/pixie/fileformats/png.nim b/src/pixie/fileformats/png.nim
index cfd65a2..fb96177 100644
--- a/src/pixie/fileformats/png.nim
+++ b/src/pixie/fileformats/png.nim
@@ -165,14 +165,31 @@ proc unfilter(
       discard # Not possible, parseHeader validates
 
 proc decodeImageData(
+  data: string,
   header: PngHeader,
   palette: seq[ColorRGB],
-  transparency, data: string
+  transparency: string,
+  idats: seq[(int, int)]
 ): seq[ColorRGBA] =
+  if idats.len == 0:
+    failInvalid()
+
   result.setLen(header.width * header.height)
 
   let
-    uncompressed = try: uncompress(data) except ZippyError: failInvalid()
+    uncompressed =
+      if idats.len > 1:
+        var imageData: string
+        for (start, len) in idats:
+          let op = imageData.len
+          imageData.setLen(imageData.len + len)
+          copyMem(imageData[op].addr, data[start].unsafeAddr, len)
+        try: uncompress(imageData) except ZippyError: failInvalid()
+      else:
+        let
+          (start, len) = idats[0]
+          p = data[start].unsafeAddr
+        try: uncompress(p, len) except ZippyError: failInvalid()
     valuesPerPixel =
       case header.colorType:
       of 0: 1
@@ -340,7 +357,8 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} =
     counts = ChunkCounts()
     header: PngHeader
     palette: seq[ColorRGB]
-    transparency, imageData: string
+    transparency: string
+    idats: seq[(int, int)]
     prevChunkType: string
 
   # First chunk must be IHDR
@@ -402,9 +420,7 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} =
         failInvalid()
       if header.colorType == 3 and counts.PLTE == 0:
         failInvalid()
-      let op = imageData.len
-      imageData.setLen(imageData.len + chunkLen)
-      copyMem(imageData[op].addr, data[pos].unsafeAddr, chunkLen)
+      idats.add((pos, chunkLen))
     of "IEND":
       if chunkLen != 0:
         failInvalid()
@@ -432,7 +448,7 @@ proc decodePngRaw*(data: string): Png {.raises: [PixieError].} =
   result.width = header.width
   result.height = header.height
   result.channels = 4
-  result.data = decodeImageData(header, palette, transparency, imageData)
+  result.data = decodeImageData(data, header, palette, transparency, idats)
 
 proc decodePng*(data: string): Image {.raises: [PixieError].} =
   ## Decodes the PNG data into an Image.
diff --git a/src/pixie/fileformats/svg.nim b/src/pixie/fileformats/svg.nim
index 015276f..d45f0dc 100644
--- a/src/pixie/fileformats/svg.nim
+++ b/src/pixie/fileformats/svg.nim
@@ -560,6 +560,48 @@ proc draw(img: Image, node: XmlNode, ctxStack: var seq[Ctx]) =
   except:
     raise currentExceptionAsPixieError()
 
+proc decodeSvg*(
+  data: string, width = 0, height = 0
+): Image {.raises: [PixieError].} =
+  ## Render SVG XML and return the image. Defaults to the SVG's view box size.
+  try:
+    let root = parseXml(data)
+    if root.tag != "svg":
+      failInvalid()
+
+    let
+      viewBox = root.attr("viewBox")
+      box = viewBox.split(" ")
+      viewBoxMinX = parseInt(box[0])
+      viewBoxMinY = parseInt(box[1])
+      viewBoxWidth = parseInt(box[2])
+      viewBoxHeight = parseInt(box[3])
+
+    var rootCtx = initCtx()
+    rootCtx = decodeCtx(rootCtx, root)
+
+    if viewBoxMinX != 0 or viewBoxMinY != 0:
+      let viewBoxMin = vec2(-viewBoxMinX.float32, -viewBoxMinY.float32)
+      rootCtx.transform = rootCtx.transform * translate(viewBoxMin)
+
+    if width == 0 and height == 0: # Default to the view box size
+      result = newImage(viewBoxWidth, viewBoxHeight)
+    else:
+      result = newImage(width, height)
+
+      let
+        scaleX = width.float32 / viewBoxWidth.float32
+        scaleY = height.float32 / viewBoxHeight.float32
+      rootCtx.transform = rootCtx.transform * scale(vec2(scaleX, scaleY))
+
+    var ctxStack = @[rootCtx]
+    for node in root:
+      result.draw(node, ctxStack)
+  except PixieError as e:
+    raise e
+  except:
+    raise newException(PixieError, "Unable to load SVG")
+
 proc decodeSvg*(
   root: XmlNode, width = 0, height = 0
 ): Image {.raises: [PixieError].} =
@@ -600,14 +642,3 @@ proc decodeSvg*(
     raise e
   except:
     raise newException(PixieError, "Unable to load SVG")
-
-proc decodeSvg*(
-  data: string, width = 0, height = 0
-): Image {.raises: [PixieError].} =
-  ## Render SVG data and return the image. Defaults to the SVG's view box size.
-  let root =
-    try:
-      parseXml(data)
-    except:
-      raise currentExceptionAsPixieError()
-  decodeSvg(root)
diff --git a/tests/fileformats/svg/diffs/dragon2.png b/tests/fileformats/svg/diffs/dragon2.png
index ac80807..04163e3 100644
Binary files a/tests/fileformats/svg/diffs/dragon2.png and b/tests/fileformats/svg/diffs/dragon2.png differ
diff --git a/tests/fileformats/svg/emojitwo.png b/tests/fileformats/svg/emojitwo.png
index 06e65a5..9ff9e7b 100644
Binary files a/tests/fileformats/svg/emojitwo.png and b/tests/fileformats/svg/emojitwo.png differ
diff --git a/tests/fileformats/svg/flat-color-icons.png b/tests/fileformats/svg/flat-color-icons.png
index a20e17f..4adbef3 100644
Binary files a/tests/fileformats/svg/flat-color-icons.png and b/tests/fileformats/svg/flat-color-icons.png differ
diff --git a/tests/fileformats/svg/ionicons.png b/tests/fileformats/svg/ionicons.png
index f757375..c0565c0 100644
Binary files a/tests/fileformats/svg/ionicons.png and b/tests/fileformats/svg/ionicons.png differ
diff --git a/tests/fileformats/svg/noto-emoji.png b/tests/fileformats/svg/noto-emoji.png
index 832c484..2960dcd 100644
Binary files a/tests/fileformats/svg/noto-emoji.png and b/tests/fileformats/svg/noto-emoji.png differ
diff --git a/tests/fileformats/svg/openmoji.png b/tests/fileformats/svg/openmoji.png
index c2f3695..1669e25 100644
Binary files a/tests/fileformats/svg/openmoji.png and b/tests/fileformats/svg/openmoji.png differ
diff --git a/tests/fileformats/svg/rendered/dragon2.png b/tests/fileformats/svg/rendered/dragon2.png
index 8f67649..a62ddc3 100644
Binary files a/tests/fileformats/svg/rendered/dragon2.png and b/tests/fileformats/svg/rendered/dragon2.png differ
diff --git a/tests/fileformats/svg/simple-icons.png b/tests/fileformats/svg/simple-icons.png
index 713afac..c240a57 100644
Binary files a/tests/fileformats/svg/simple-icons.png and b/tests/fileformats/svg/simple-icons.png differ
diff --git a/tests/fileformats/svg/tabler-icons.png b/tests/fileformats/svg/tabler-icons.png
index 01f016e..afb16a6 100644
Binary files a/tests/fileformats/svg/tabler-icons.png and b/tests/fileformats/svg/tabler-icons.png differ
diff --git a/tests/fileformats/svg/twbs-icons.png b/tests/fileformats/svg/twbs-icons.png
index abef7c7..cc31391 100644
Binary files a/tests/fileformats/svg/twbs-icons.png and b/tests/fileformats/svg/twbs-icons.png differ
diff --git a/tests/fileformats/svg/twemoji.png b/tests/fileformats/svg/twemoji.png
index 78ad6ff..d4483dc 100644
Binary files a/tests/fileformats/svg/twemoji.png and b/tests/fileformats/svg/twemoji.png differ