2020# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121# SOFTWARE.
2222
23- import xmltree, strtabs, sequtils, strutils, sugar
23+ import xmltree, strtabs, sequtils, strutils, sugar, tables
2424import utils
2525
2626type
4848 fill: Color
4949 stroke: Color
5050 stroke_width: float64
51+ clip: Box2
52+ has_clip: bool
5153 case kind: ShapeKind :
5254 of ShapeRect :
5355 rect_pos: Vec2
7678 VectorRender2 * = object
7779 size* : Index2
7880 shapes: seq [Shape ]
79-
81+
8082 stroke* : Color
8183 stroke_width* : float64
8284 fill* : Color
85+ has_clip* : bool
86+ clip* : Box2
8387
8488 font_size* : float64
8589 font_family* : string
@@ -101,6 +105,9 @@ proc add_style(shape: Shape, ren: VectorRender2): Shape =
101105 result .fill = ren.fill
102106 result .stroke = ren.stroke
103107 result .stroke_width = ren.stroke_width
108+ if ren.has_clip:
109+ result .has_clip = true
110+ result .clip = ren.clip
104111
105112proc background * (ren: var VectorRender2 , color: Color ) =
106113 ren.shapes.add (Shape (kind: ShapeBackground , fill: color))
@@ -129,7 +136,7 @@ proc text*(ren: var VectorRender2,
129136 pos: Vec2 ,
130137 text: string ,
131138 x_align: Align = AlignStart ,
132- y_align: Align = AlignEnd ,
139+ y_align: Align = AlignStart ,
133140 rotation: Deg = Deg (0 )) =
134141 ren.shapes.add (Shape (kind: ShapeText ,
135142 text_pos: pos, text: text,
@@ -151,6 +158,10 @@ proc end_path*(ren: var VectorRender2, close: bool = false) =
151158 ren.shapes.add (Shape (kind: ShapePath , path: ren.path).add_style (ren))
152159 ren.path = @ []
153160
161+ proc clip_region * (ren: var VectorRender2 , clip: Box2 ) =
162+ ren.clip = clip
163+ ren.has_clip = true
164+
154165proc to_svg * (color: Color ): string =
155166 if color.a == 0 :
156167 return " none"
@@ -168,11 +179,27 @@ proc to_svg*(color: Color): string =
168179 result &= $ (color.a * 100 ) & " %"
169180 result &= " )"
170181
171- proc add_style (node: XmlNode , shape: Shape ): XmlNode =
182+ proc add_style (node: XmlNode ,
183+ shape: Shape ,
184+ defs: var seq [XmlNode ],
185+ clips: var Table [Box2 , string ]): XmlNode =
172186 result = node
173187 result .attrs[" stroke-width" ] = $ shape.stroke_width
174188 result .attrs[" stroke" ] = shape.stroke.to_svg ()
175189 result .attrs[" fill" ] = shape.fill.to_svg ()
190+ if shape.has_clip:
191+ if shape.clip notin clips:
192+ let name = " clip" & $ clips.len
193+ defs.add (new_xml_tree (" clipPath" , [
194+ new_xml_tree (" rect" , [], to_xml_attributes ({
195+ " x" : $ shape.clip.min.x,
196+ " y" : $ shape.clip.min.y,
197+ " width" : $ shape.clip.size.x,
198+ " height" : $ shape.clip.size.y
199+ }))
200+ ], to_xml_attributes ({" id" : name})))
201+ clips[shape.clip] = name
202+ result .attrs[" clip-path" ] = " url(#" & clips[shape.clip] & " )"
176203
177204proc to_text_anchor (align: Align ): string =
178205 case align:
@@ -195,28 +222,31 @@ proc to_svg(point: PathPoint): string =
195222 of PathClose :
196223 return " Z"
197224
198- proc to_svg * (shape: Shape , size: Index2 ): XmlNode =
225+ proc to_svg * (shape: Shape ,
226+ size: Index2 ,
227+ defs: var seq [XmlNode ],
228+ clips: var Table [Box2 , string ]): XmlNode =
199229 case shape.kind:
200230 of ShapeCircle :
201231 return new_xml_tree (" circle" , [], to_xml_attributes ({
202232 " cx" : $ shape.circle_pos.x,
203233 " cy" : $ shape.circle_pos.y,
204234 " r" : $ shape.circle_radius,
205- })).add_style (shape)
235+ })).add_style (shape, defs, clips )
206236 of ShapeRect :
207237 return new_xml_tree (" rect" , [], to_xml_attributes ({
208238 " x" : $ shape.rect_pos.x,
209239 " y" : $ shape.rect_pos.y,
210240 " width" : $ shape.rect_size.x,
211241 " height" : $ shape.rect_size.y,
212- })).add_style (shape)
242+ })).add_style (shape, defs, clips )
213243 of ShapeLine :
214244 return new_xml_tree (" line" , [], to_xml_attributes ({
215245 " x1" : $ shape.line_a.x,
216246 " y1" : $ shape.line_a.y,
217247 " x2" : $ shape.line_b.x,
218248 " y2" : $ shape.line_b.y,
219- })).add_style (shape)
249+ })).add_style (shape, defs, clips )
220250 of ShapeText :
221251 var attrs = to_xml_attributes ({
222252 " font-size" : $ shape.font_size,
@@ -230,26 +260,36 @@ proc to_svg*(shape: Shape, size: Index2): XmlNode =
230260 else :
231261 attrs[" x" ] = $ shape.text_pos.x
232262 attrs[" y" ] = $ shape.text_pos.y
233- return new_xml_tree (" text" , [new_text (shape.text)], attrs).add_style (shape)
263+ return new_xml_tree (" text" , [new_text (shape.text)], attrs).add_style (shape, defs, clips )
234264 of ShapePath :
235265 return new_xml_tree (" path" , [], to_xml_attributes ({
236266 " d" : shape.path.map (to_svg).join (" " )
237- })).add_style (shape)
267+ })).add_style (shape, defs, clips )
238268 of ShapeBackground :
239269 return new_xml_tree (" rect" , [], to_xml_attributes ({
240270 " x" : " 0" , " y" : " 0" ,
241271 " width" : $ size.x, " height" : $ size.y,
242- })).add_style (shape)
272+ })).add_style (shape, defs, clips )
243273 of ShapeEllipse :
244274 return new_xml_tree (" ellipse" , [], to_xml_attributes ({
245275 " cx" : $ shape.ellipse_pos.x,
246276 " cy" : $ shape.ellipse_pos.y,
247277 " rx" : $ shape.ellipse_size.x,
248278 " ry" : $ shape.ellipse_size.y
249- })).add_style (shape)
279+ })).add_style (shape, defs, clips )
250280
251281proc to_svg * (ren: VectorRender2 ): XmlNode =
252- new_xml_tree (" svg" , ren.shapes.map (shape => to_svg (shape, ren.size)), to_xml_attributes ({
282+ var
283+ defs: seq [XmlNode ] = @ []
284+ clips = init_table [Box2 , string ]()
285+ body: seq [XmlNode ] = @ []
286+ for shape in ren.shapes:
287+ body.add (to_svg (shape, ren.size, defs, clips))
288+ if defs.len > 0 :
289+ let defs_section = new_xml_tree (" defs" , defs)
290+ body = @ [defs_section] & body
291+
292+ return new_xml_tree (" svg" , body, to_xml_attributes ({
253293 " width" : $ ren.size.x,
254294 " height" : $ ren.size.y,
255295 " xmlns" : " http://www.w3.org/2000/svg"
0 commit comments