Coordinate Translation Logic in Graphviz2Drawio: Converting SVG Offsets to Absolute Positions
Graphviz2Drawio parses the global SVG transform attribute to extract translation offsets and applies them to every coordinate via the CoordsTranslate class, converting relative Graphviz positions into absolute Draw.io coordinates.
The coordinate translation logic in graphviz2drawio bridges the gap between Graphviz's SVG output format and Draw.io's absolute positioning requirements. When Graphviz generates an SVG diagram, it wraps the entire drawing in a <g> element with a global transform attribute that shifts the coordinate system. The hbmartin/graphviz2drawio repository implements a centralized translation utility to handle this offset consistently across nodes, edges, and text elements.
Understanding the SVG Coordinate Challenge
Graphviz outputs diagrams as SVG where all shape-specific coordinates are relative to a global offset. The root <svg> element contains a transform="translate(x y)" attribute that shifts the entire diagram. Without accounting for this offset, individual elements like rectangles, ellipses, and polygons would be positioned incorrectly relative to the canvas origin.
The library must parse this global transform once and apply it to every subsequent coordinate read from individual SVG nodes. This ensures that the final Draw.io XML contains absolute positions that match the original Graphviz layout.
The CoordsTranslate Class: Core of the Translation Logic
All coordinate translation functionality is encapsulated in the CoordsTranslate class located in [graphviz2drawio/models/CoordsTranslate.py](https://github.com/hbmartin/graphviz2drawio/blob/master/graphviz2drawio/models/CoordsTranslate.py). This class stores the global X and Y offsets and provides methods to apply them to various coordinate types.
Parsing the Global Transform String
The from_svg_transform static method extracts the offset values from the SVG transform attribute:
@staticmethod
def from_svg_transform(transform: str) -> "CoordsTranslate":
x, y = transform.split("translate(")[1].split(")")[0].split(" ")
return CoordsTranslate(x=float(x), y=float(y))
This method receives a string like "translate(120.0 45.0)", parses the numeric values, and returns a CoordsTranslate instance initialized with the extracted offsets.
Applying Offsets to Coordinates
The translate method shifts raw coordinates by the stored global offset:
def translate(self, x: float | str, y: float | str) -> tuple[float, float]:
return float(x) + self.x, float(y) + self.y
This method accepts both float and str inputs, converting them automatically before applying the offset. Shape factories call this method for every coordinate extracted from SVG attributes.
For text baseline adjustments, the complex_translate method handles complex number representations:
def complex_translate(self, cnum: complex) -> complex:
return complex(cnum.real + self.x, cnum.imag + self.y)
This is used specifically for text positioning where offsets are represented as complex numbers.
Integration Across the Conversion Pipeline
The CoordsTranslate instance is created once during SVG parsing and propagated through all factory classes that generate Draw.io elements.
Root SVG Parsing
In [graphviz2drawio/models/SvgParser.py](https://github.com/hbmartin/graphviz2drawio/blob/master/graphviz2drawio/models/SvgParser.py), the root <svg> element is examined to extract the global transform:
coords = CoordsTranslate.from_svg_transform(root.attrib["transform"])
This coords object is then passed to all subsequent processing steps.
Node Coordinate Translation
The NodeFactory in [graphviz2drawio/mx/NodeFactory.py](https://github.com/hbmartin/graphviz2drawio/blob/master/graphviz2drawio/mx/NodeFactory.py) receives the coords instance and forwards it to RectFactory helpers. Inside [graphviz2drawio/mx/RectFactory.py](https://github.com/hbmartin/graphviz2drawio/blob/master/graphviz2drawio/mx/RectFactory.py), every shape helper calls coords.translate before constructing Rect objects:
x, y = coords.translate(attrib["x"], attrib["y"])
This ensures that rectangles, images, and ellipses are positioned absolutely on the Draw.io canvas.
Edge and Curve Translation
Similarly, EdgeFactory in [graphviz2drawio/mx/EdgeFactory.py](https://github.com/hbmartin/graphviz2drawio/blob/master/graphviz2drawio/mx/EdgeFactory.py) passes the coords object to CurveFactory. The curve factory uses coords.translate to shift every control point of Bezier paths, ensuring that edges connect nodes at the correct absolute positions.
Text Baseline Adjustment
In NodeFactory._extract_texts, after reading the first <text> element, the code computes a complex offset and applies coords.complex_translate(complex(x, y)). This aligns the text baseline with the global SVG origin while maintaining the relative positioning specified by Graphviz.
Practical Code Examples
Creating a CoordsTranslate Instance from SVG Transform
from graphviz2drawio.models.CoordsTranslate import CoordsTranslate
transform = "translate(120.0 45.0)"
coords = CoordsTranslate.from_svg_transform(transform)
print(coords.x, coords.y) # Output: 120.0 45.0
Translating a Point for Shape Construction
# Raw coordinates from SVG polygon point "30,20"
raw_x, raw_y = "30", "20"
# Apply global offset (120.0, 45.0)
x, y = coords.translate(raw_x, raw_y)
# Result: (150.0, 65.0)
print(f"Absolute position: ({x}, {y})")
Using Translation in Node Factory Context
from graphviz2drawio.mx.NodeFactory import NodeFactory
from graphviz2drawio.models.CoordsTranslate import CoordsTranslate
# Initialize with parsed transform
coords = CoordsTranslate.from_svg_transform("translate(10 20)")
node_factory = NodeFactory(coords)
# All nodes created by this factory will have coordinates
# automatically adjusted by (10, 20)
Summary
- Global offset extraction: The
CoordsTranslate.from_svg_transformmethod parses the SVG root'stranslate(x y)attribute to capture the global diagram offset. - Centralized translation logic: The
CoordsTranslateclass ingraphviz2drawio/models/CoordsTranslate.pyprovides typed methods (translate,complex_translate) that apply offsets to both numeric and string coordinates. - Pipeline integration: The same
CoordsTranslateinstance is propagated fromSvgParserthroughNodeFactory,EdgeFactory, andRectFactory, ensuring consistent absolute positioning for nodes, edges, and text. - Multi-format support: The class handles standard
(x, y)tuples for shapes and complex numbers for text baseline offsets, accommodating all Graphviz SVG output variations.
Frequently Asked Questions
Why does Graphviz use a global transform attribute instead of absolute coordinates?
Graphviz generates SVG diagrams with a global translate transform to simplify internal coordinate calculations during layout algorithms. This approach allows Graphviz to compute positions relative to a local origin while rendering, then shift the entire diagram to its final viewport position with a single transform attribute. The graphviz2drawio library compensates for this design choice by parsing the transform and applying it to every element.
What happens if the SVG transform attribute is missing or malformed?
The current implementation in CoordsTranslate.from_svg_transform assumes the transform string follows the standard "translate(x y)" format. If the attribute is missing or does not contain the expected pattern, the string splitting logic would raise an IndexError or ValueError. Production usage should validate the SVG structure before parsing, as Graphviz consistently outputs the transform attribute in its SVG generation.
How does CoordsTranslate handle both string and float inputs?
The translate method accepts a union type float | str for both x and y parameters. When called, it explicitly converts inputs using float(x) and float(y) before adding the global offset. This design eliminates the need for callers to pre-convert SVG attribute values (which are always strings in XML) while also supporting numeric inputs from internal calculations. The same flexibility applies to complex_translate for text positioning.
Is the translation logic applied to all SVG elements equally?
Yes, the translation logic is applied consistently across all geometric elements, but through different code paths depending on element type. Nodes use coords.translate in RectFactory for rectangles, images, and ellipses. Edges apply the same method in CurveFactory for Bezier control points. Text elements use coords.complex_translate for baseline offsets. This unified approach ensures that nodes, edges, labels, and curves maintain their relative positions while shifting to absolute coordinates on the Draw.io canvas.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →