Skip to content

Commit 2470e94

Browse files
committed
Add SVGDoc and SVGSerialize functions for SVG document creation and
serialization. [Feature] Signed-off-by: Markus Alexander Kuppe <[email protected]>
1 parent d66e661 commit 2470e94

File tree

2 files changed

+109
-1
lines changed

2 files changed

+109
-1
lines changed

modules/SVG.tla

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ Group(children, attrs) ==
122122
Svg(children, attrs) ==
123123
SVGElem("svg", attrs, children, "")
124124

125+
(**************************************************************************)
126+
(* Creates a complete SVG document with viewBox and additional attributes.*)
127+
(* *)
128+
(* Parameters: *)
129+
(* children: A sequence of SVG elements to include in the document *)
130+
(* vbX, vbY: The x,y coordinates of the top-left corner of the viewBox *)
131+
(* vbW, vbH: The width and height of the viewBox *)
132+
(* attrs: Additional attributes to merge with the SVG root element *)
133+
(* *)
134+
(* The viewBox defines the coordinate system and viewport dimensions for *)
135+
(* the SVG content. This is essential for proper scaling and positioning *)
136+
(* of elements within the document. *)
137+
(**************************************************************************)
138+
SVGDoc(children, vbX, vbY, vbW, vbH, attrs) ==
139+
LET svgAttrs == ("xmlns:xlink" :> "http://www.w3.org/1999/xlink" @@
140+
"xmlns" :> "http://www.w3.org/2000/svg" @@
141+
"viewBox" :> ToString(vbX) \o " " \o
142+
ToString(vbY) \o " " \o
143+
ToString(vbW) \o " " \o
144+
ToString(vbH)) IN
145+
Svg(<<children>>, Merge(svgAttrs, attrs))
146+
125147
(**************************************************************************)
126148
(* Convert an SVG element record into its string representation. *)
127149
(* *)
@@ -132,7 +154,7 @@ Svg(children, attrs) ==
132154
(* slow. *)
133155
(**************************************************************************)
134156
SVGElemToString(elem) ==
135-
TRUE
157+
TRUE
136158

137159
-------------------------------------------------------------------------------
138160

@@ -190,4 +212,33 @@ PointOnLine(from, to, segment) ==
190212
[x |-> from.x + ((to.x - from.x) \div segment),
191213
y |-> from.y + ((to.y - from.y) \div segment)]
192214

215+
-------------------------------------------------------------------------------
216+
217+
(**************************************************************************)
218+
(* Serializes an SVG element to a file on disk. *)
219+
(* *)
220+
(* Parameters: *)
221+
(* svg: The SVG element/document to serialize *)
222+
(* frameNamePrefix: String prefix for the output filename *)
223+
(* frameNumber: Numeric identifier appended to create unique files *)
224+
(* *)
225+
(* Creates a file named "<frameNamePrefix><frameNumber>.svg" containing *)
226+
(* the serialized SVG content. This is useful for generating animation *)
227+
(* frames or saving visualization snapshots. *)
228+
(* *)
229+
(* Example usage: *)
230+
(* SVGSerialize(SVGDoc(myElements, 0, 0, 800, 600, <<>>), *)
231+
(* "svg_frame_", TLCGet("level")) *)
232+
(* *)
233+
(* This creates files like: svg_frame_1.svg, *)
234+
(* svg_frame_2.svg, etc. *)
235+
(**************************************************************************)
236+
SVGSerialize(svg, frameNamePrefix, frameNumber) ==
237+
LET IO == INSTANCE IOUtils IN
238+
IO!Serialize(
239+
SVGElemToString(svg),
240+
frameNamePrefix \o ToString(frameNumber) \o ".svg",
241+
[format |-> "TXT", charset |-> "UTF-8",
242+
openOptions |-> <<"WRITE", "CREATE", "TRUNCATE_EXISTING">>])
243+
193244
=============================================================================

tests/SVGTests.tla

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,62 @@ ASSUME(LET
138138
IN
139139
AssertEq(SVGElemToString(elem), "<text x='0' y='0'>&lt;&lt;1, 2, 3&gt;&gt;</text>"))
140140

141+
(******************************************************************************)
142+
(* Test SVGDoc operator *)
143+
(******************************************************************************)
144+
145+
\* Test SVGDoc with basic elements and viewBox
146+
ASSUME(LET
147+
circle == Circle(50, 50, 20, [fill |-> "blue"])
148+
rect == Rect(10, 10, 30, 40, [fill |-> "red"])
149+
doc == SVGDoc(<<circle, rect>>, 0, 0, 100, 100, [width |-> "200", height |-> "200"])
150+
expected == [ name |-> "svg",
151+
attrs |-> ("xmlns:xlink" :> "http://www.w3.org/1999/xlink" @@
152+
"xmlns" :> "http://www.w3.org/2000/svg" @@
153+
"viewBox" :> "0 0 100 100" @@
154+
"width" :> "200" @@
155+
"height" :> "200"),
156+
children |-> <<<<circle, rect>>>>,
157+
innerText |-> ""] IN
158+
AssertEq(doc, expected))
159+
160+
\* Test SVGDoc with empty children and no additional attributes
161+
ASSUME(LET
162+
doc == SVGDoc(<<>>, 10, 20, 800, 600, <<>>)
163+
expected == [ name |-> "svg",
164+
attrs |-> ("xmlns:xlink" :> "http://www.w3.org/1999/xlink" @@
165+
"xmlns" :> "http://www.w3.org/2000/svg" @@
166+
"viewBox" :> "10 20 800 600"),
167+
children |-> <<<<>>>>,
168+
innerText |-> ""] IN
169+
AssertEq(doc, expected))
170+
171+
\* Test SVGDoc string conversion
172+
ASSUME(LET
173+
text == Text(25, 25, "Hello SVG", [fill |-> "green"])
174+
doc == SVGDoc(text, 0, 0, 50, 50, [id |-> "test-svg"])
175+
expectedString == "<svg id='test-svg' xmlns:xlink='http://www.w3.org/1999/xlink' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 50 50'><text x='25' y='25' fill='green'>Hello SVG</text></svg>" IN
176+
AssertEq(SVGElemToString(doc), expectedString))
177+
178+
(******************************************************************************)
179+
(* Test SVGSerialize operator *)
180+
(******************************************************************************)
181+
182+
\* Test SVGSerialize creates a file (we can't directly test file creation in TLA+,
183+
\* but we can test that the operator doesn't crash and returns the expected result)
184+
ASSUME(LET
185+
circle == Circle(25, 25, 15, [fill |-> "purple"])
186+
doc == SVGDoc(circle, 0, 0, 50, 50, [width |-> "100", height |-> "100"])
187+
result == SVGSerialize(doc, "test_frame_", 1) IN
188+
\* SVGSerialize should return TRUE if successful (based on IOUtils pattern)
189+
result.exitValue = 0)
190+
191+
\* Test SVGSerialize with different frame numbers
192+
ASSUME(LET
193+
line == Line(0, 0, 50, 50, "stroke" :> "black" @@ "stroke-width" :> "2")
194+
doc == SVGDoc(line, 0, 0, 50, 50, <<>>)
195+
result == SVGSerialize(doc, "animation_", 42) IN
196+
result.exitValue = 0)
197+
141198

142199
=============================================================================

0 commit comments

Comments
 (0)