From f921628c24a195ce310e3cf08498273300ec3d6b Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA <i.tkomiya@gmail.com>
Date: Sat, 14 Dec 2019 14:28:52 +0900
Subject: [PATCH] Support SVG Image

---
 README.rst           |  2 +-
 imagesize.py         | 35 +++++++++++++++++++++++++++++++++++
 test/images/test.svg |  4 ++++
 test/test_get.py     |  5 +++++
 4 files changed, 45 insertions(+), 1 deletion(-)
 create mode 100644 test/images/test.svg

diff --git a/README.rst b/README.rst
index 3bd5b54..1545bf3 100644
--- a/README.rst
+++ b/README.rst
@@ -4,7 +4,7 @@ imagesize
 .. image:: https://travis-ci.org/shibukawa/imagesize_py.svg?branch=master
     :target: https://travis-ci.org/shibukawa/imagesize_py
 
-This module analyzes JPEG/JPEG 2000/PNG/GIF/TIFF image headers and returns image size.
+This module analyzes JPEG/JPEG 2000/PNG/GIF/TIFF/SVG image headers and returns image size.
 
 .. code:: python
 
diff --git a/imagesize.py b/imagesize.py
index 2717240..1c3b82d 100644
--- a/imagesize.py
+++ b/imagesize.py
@@ -1,4 +1,6 @@
+import re
 import struct
+from xml.etree import ElementTree
 
 _UNIT_KM = -3
 _UNIT_100M = -2
@@ -52,6 +54,30 @@ def _convertToDPI(density, unit):
     return density
 
 
+def _convertToPx(value):
+    matched = re.match(r"(\d+)(?:\.\d)?([a-z]*)$", value)
+    if not matched:
+        raise ValueError("unknown length value: %s" % value)
+    else:
+        length, unit = matched.groups()
+        if unit == "":
+            return int(length)
+        elif unit == "cm":
+            return int(length) * 96 / 2.54
+        elif unit == "mm":
+            return int(length) * 96 / 2.54 / 10
+        elif unit == "in":
+            return int(length) * 96
+        elif unit == "pc":
+            return int(length) * 96 / 6
+        elif unit == "pt":
+            return int(length) * 96 / 6
+        elif unit == "px":
+            return int(length)
+        else:
+            raise ValueError("unknown unit type: %s" % unit)
+
+
 def get(filepath):
     """
     Return (width, height) for a given img file content
@@ -147,6 +173,15 @@ def get(filepath):
                     break
             if width == -1 or height == -1:
                 raise ValueError("Invalid TIFF file: width and/or height IDS entries are missing.")
+        # handle SVGs
+        elif size >= 5 and head.startswith(b'<?xml'):
+            try:
+                fhandle.seek(0)
+                root = ElementTree.parse(fhandle).getroot()
+                width = _convertToPx(root.attrib["width"])
+                height = _convertToPx(root.attrib["height"])
+            except Exception:
+                raise ValueError("Invalid SVG file")
 
     return width, height
 
diff --git a/test/images/test.svg b/test/images/test.svg
new file mode 100644
index 0000000..28d4b39
--- /dev/null
+++ b/test/images/test.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="60" width="90">
+    <circle cx="40" cy="40" r="24" style="stroke:#000000; fill:#ffffff"/>
+</svg>
diff --git a/test/test_get.py b/test/test_get.py
index c24cd49..70a6309 100644
--- a/test/test_get.py
+++ b/test/test_get.py
@@ -33,6 +33,11 @@ class GetTest(unittest.TestCase):
         self.assertEqual(width, 802)
         self.assertEqual(height, 670)
 
+    def test_load_svg(self):
+        width, height = imagesize.get(os.path.join(imagedir, "test.svg"))
+        self.assertEqual(width, 90)
+        self.assertEqual(height, 60)
+
     def test_littleendien_tiff(self):
         width, height = imagesize.get(os.path.join(imagedir, "multipage_tiff_example.tif"))
         self.assertEqual(width, 800)
-- 
2.39.0.windows.2

