import xml.etree.cElementTree as ET

def inline_svg_images(svg) -> str:
    """
    Inline IMAGE tag refs in graphviz/dot -> SVG generated files.

    Convert all image tag refs directly under g tags like:

    <g id="node1" class="node">
        <image xlink:href="/tmp/node4.svg" width="45px" height="76px" preserveAspectRatio="xMinYMin meet" x="76" y="-80"/>
    </g>

    to

    <g id="node1" class="node">
        <svg width="49.0px" height="80.8px" preserveAspectRatio="xMinYMin meet" x="76" y="-80">
            XYZ
        </svg>
    </g>

    where XYZ is taken from ref'd svg image file:

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
      "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <!-- Created with matplotlib (http://matplotlib.org/) -->
    <svg height="80.826687pt" version="1.1" viewBox="0 0 49.008672 80.826687" width="49.008672pt"
         xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
        XYZ
    </svg>

    Note that width/height must be taken from ref'd svg image file. The <image/> tag
    values seem a bit off.

    :param svg: SVG string with <image/> tags.
    :return: svg with <image/> tags replaced with content of referenced svg image files.
    """
    ns = {"svg": "http://www.w3.org/2000/svg"}
    root = ET.fromstring(svg)
    tree = ET.ElementTree(root)
    parent_map = {c: p for p in tree.iter() for c in p}

    # Find all image tags in document (must use svg namespace)
    image_tags = tree.findall(".//svg:g/svg:image", ns)
    for img in image_tags:
        # load ref'd image and get svg root
        filename = img.attrib["{http://www.w3.org/1999/xlink}href"]
        with open(filename) as f:
            imgsvg = f.read()
        imgroot = ET.fromstring(imgsvg)
        for k,v in img.attrib.items(): # copy IMAGE tag attributes to svg from image file
            if k not in {"width", "height", "{http://www.w3.org/1999/xlink}href"}:
                imgroot.attrib[k] = v
        del imgroot.attrib["viewBox"]
        # replace IMAGE with SVG tag
        p = parent_map[img]
        # print("BEFORE " + ', '.join([str(c) for c in p]))
        p.append(imgroot)
        p.remove(img)
        # print("AFTER " + ', '.join([str(c) for c in p]))

    ET.register_namespace('', "http://www.w3.org/2000/svg")
    ET.register_namespace('xlink', "http://www.w3.org/1999/xlink")
    xml_str = ET.tostring(root).decode()
    return xml_str


def get_SVG_shape(filename):
    """
    Sample line from SVG file from which we can get w,h:
    <svg height="122.511795pt" version="1.1" viewBox="0 0 451.265312 122.511795"
         width="451.265312pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="...">

    Hmm...Seems we don't need this anymore but I will leave it just in case
    """
    with open(filename, "r") as f:
        for line in f.readlines():
            if line.startswith("<svg "):
                args = line[len("<svg "):].split()
                d = {}
                for arg in args:
                    a = arg.split('=')
                    if len(a) == 2:
                        d[a[0]] = a[1].strip('"').strip('pt')
                return float(d['width']), float(d['height'])


if __name__ == '__main__':
    # test rig
    with open("/tmp/foo.svg") as f:
        svg = f.read()

    svg2 = inline_svg_images(svg)
    print(svg2)

    with open("/tmp/bar.svg", "w") as f:
        f.write(svg2)

@@ -28,16 +28,68 @@
 ee as ET
+%0Afrom numbers import Number%0Afrom typing import Tuple
 %0A%0Adef in
@@ -204,24 +204,29 @@
 Convert all 
+.svg 
 image tag re
@@ -483,19 +483,17 @@
 width=%224
-9.0
+5
 px%22 heig
@@ -500,14 +500,46 @@
 ht=%22
-80.8px
+76px%22 viewBox=%220 0 49.008672 80.826687
 %22 pr
@@ -625,24 +625,25 @@
 %3E%0A    %3C/g%3E%0A%0A
+%0A
     where XY
@@ -1166,77 +1166,97 @@
 ken 
-from ref'd svg image file. The %3Cimage/%3E tag%0A    values seem
+image ref tag and put onto svg tag. We%0A    also need the viewBox or it gets clipped
  a bit
- off
 .%0A%0A 
@@ -1762,16 +1762,19 @@
         
+svg
 filename
@@ -1835,32 +1835,35 @@
       with open(
+svg
 filename) as f:%0A
@@ -2051,27 +2051,8 @@
 in %7B
-%22width%22, %22height%22, 
 %22%7Bht
@@ -2611,16 +2611,40 @@
 ilename)
+ -%3E Tuple%5BNumber,Number%5D
 :%0A    %22%22
@@ -3307,16 +3307,16 @@
 p('pt')%0A
-
         
@@ -3370,16 +3370,91 @@
 ht'%5D)%0A%0A%0A
+def myround(v,ndigits=2):%0A    return format(v, '.' + str(ndigits) + 'f')%0A%0A%0A
 if __nam

update utils so merge SVG gets width/height right. add viewBox attr too. Add myround() function.