SVG to PB VectorDrawing Codes
Posted: Mon Sep 15, 2025 1:31 pm
Hi all,
I needed a project that would convert SVG files into PB VectorDrawing codes.
Using Capilot ai, I applied these changes to the Thorsten1867 SVG module.
viewtopic.php?p=552623&hilit=UseSVGImageModule#p552623
Of course, according to Capilot, there are still 6 more steps to becoming a professional, but for now, it's 90% complete and working properly.
Now, just give this module a SVG file and it will generate PureBasic code for you.
I needed a project that would convert SVG files into PB VectorDrawing codes.
Using Capilot ai, I applied these changes to the Thorsten1867 SVG module.
viewtopic.php?p=552623&hilit=UseSVGImageModule#p552623
Of course, according to Capilot, there are still 6 more steps to becoming a professional, but for now, it's 90% complete and working properly.
Now, just give this module a SVG file and it will generate PureBasic code for you.

Code: Select all
;{ ===== MIT License =====
; Copyright (c) 2019 Martin Guttmann (alias STARGÅTE)
; Copyright (c) 2020 Thorsten Hoeppner
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;}
DeclareModule SVG2PB
Declare.s GenerateVectorCodeFromFile(FilePath.s)
EndDeclareModule
Module SVG2PB
;- ============================================================================
;- Module - Constants / Structures
;- ============================================================================
Structure RegEx_Structure ;{ RegEx\...
Color.i
Command.i
Definition.i
Length.i
Points.i
Style.i
Value.i
EndStructure ;}
Global RegEx.RegEx_Structure
Structure ViewBox_Structure ;{ SVG()\ViewBox\...
X.d
Y.d
Width.d
Height.d
State.i
EndStructure ;}
Structure Image_Structure ;{ SVG()\Image\...
Num.i
Width.d
Height.d
Depth.i
BackColor.q
PB_Any.i
EndStructure ;}
Structure Style_Structure ;{ SVG()\CurrentStyle\...
Opacity.d
Fill.i
FillRule.i
FillOpacity.d
Stroke.i
StrokeWidth.d
StrokeOpacity.d
StrokeLineCap.i
StrokeLineJoin.i
Array StrokeDashArray.d(0)
StrokeDashOffset.d
EndStructure ;}
Structure StyleSheet_Class_Structure ;{ SVG()\StyleSheet\Element()\...
Style.s
EndStructure ;}
Structure StyleSheet_Element_Structure ;{ SVG()\StyleSheet\Element()\...
Map Class.StyleSheet_Class_Structure()
EndStructure ;}
Structure StyleSheet_Structure ;{ SVG()\StyleSheet\...
Map Element.StyleSheet_Element_Structure()
EndStructure ;}
Structure SVG_Structure ;{ SVG('image')\...
ResX.d
ResY.d
Width.d
Height.d
strgXML.s
Font.i
Flags.i
CurrentStyle.Style_Structure
StyleSheet.StyleSheet_Structure
ViewBox.ViewBox_Structure
Image.Image_Structure
EndStructure ;}
Global NewMap SVG.SVG_Structure()
Procedure.i X11Color_(Name.s)
Select LCase(Name)
Case "aliceblue"
ProcedureReturn $FFF8F0
Case "antiquewhite"
ProcedureReturn $D7EBFA
Case "aqua"
ProcedureReturn $FFFF00
Case "aquamarine"
ProcedureReturn $D4FF7F
Case "azure"
ProcedureReturn $FFFFF0
Case "beige"
ProcedureReturn $DCF5F5
Case "bisque"
ProcedureReturn $C4E4FF
Case "black"
ProcedureReturn $000000
Case "blanchedalmond"
ProcedureReturn $CDEBFF
Case "blue"
ProcedureReturn $FF0000
Case "blueviolet"
ProcedureReturn $E22B8A
Case "brown"
ProcedureReturn $2A2AA5
Case "burlywood"
ProcedureReturn $87B8DE
Case "cadetblue"
ProcedureReturn $A09E5F
Case "chartreuse"
ProcedureReturn $00FF7F
Case "chocolate"
ProcedureReturn $1E69D2
Case "coral"
ProcedureReturn $507FFF
Case "cornflowerblue"
ProcedureReturn $ED9564
Case "cornsilk"
ProcedureReturn $DCF8FF
Case "crimson"
ProcedureReturn $3C14DC
Case "cyan"
ProcedureReturn $FFFF00
Case "darkblue"
ProcedureReturn $8B0000
Case "darkcyan"
ProcedureReturn $8B8B00
Case "darkgoldenrod"
ProcedureReturn $0B86B8
Case "darkgray", "darkgrey"
ProcedureReturn $A9A9A9
Case "darkgreen"
ProcedureReturn $006400
Case "darkkhaki"
ProcedureReturn $6BB7BD
Case "darkmagenta"
ProcedureReturn $8B008B
Case "darkolivegreen"
ProcedureReturn $2F6B55
Case "darkorange"
ProcedureReturn $008CFF
Case "darkorchid"
ProcedureReturn $CC3299
Case "darkred"
ProcedureReturn $00008B
Case "darksalmon"
ProcedureReturn $7A96E9
Case "darkseagreen"
ProcedureReturn $8FBC8F
Case "darkslateblue"
ProcedureReturn $8B3D48
Case "darkslategrey", "darkslategray"
ProcedureReturn $4F4F2F
Case "darkturquoise"
ProcedureReturn $D1CE00
Case "darkviolet"
ProcedureReturn $D30094
Case "deeppink"
ProcedureReturn $9314FF
Case "deepskyblue"
ProcedureReturn $FFBF00
Case "dimgray", "dimgrey"
ProcedureReturn $696969
Case "dodgerblue"
ProcedureReturn $FF901E
Case "firebrick"
ProcedureReturn $2222B2
Case "floralwhite"
ProcedureReturn $F0FAFF
Case "forestgreen"
ProcedureReturn $228B22
Case "fuchsia"
ProcedureReturn $FF00FF
Case "gainsboro"
ProcedureReturn $DCDCDC
Case "ghostwhite"
ProcedureReturn $FFF8F8
Case "gold"
ProcedureReturn $00D7FF
Case "goldenrod"
ProcedureReturn $20A5DA
Case "gray", "grey"
ProcedureReturn $808080
Case "green"
ProcedureReturn $008000
Case "greenyellow"
ProcedureReturn $2FFFAD
Case "honeydew"
ProcedureReturn $F0FFF0
Case "hotpink"
ProcedureReturn $B469FF
Case "indianred"
ProcedureReturn $5C5CCD
Case "indigo"
ProcedureReturn $82004B
Case "ivory"
ProcedureReturn $F0FFFF
Case "khaki"
ProcedureReturn $8CE6F0
Case "lavender"
ProcedureReturn $FAE6E6
Case "lavenderblush"
ProcedureReturn $F5F0FF
Case "lawngreen"
ProcedureReturn $00FC7C
Case "lemonchiffon"
ProcedureReturn $CDFAFF
Case "lightblue"
ProcedureReturn $E6D8AD
Case "lightcoral"
ProcedureReturn $8080F0
Case "lightcyan"
ProcedureReturn $FFFFE0
Case "lightgoldenrodyellow"
ProcedureReturn $D2FAFA
Case "lightgray","lightgrey"
ProcedureReturn $D3D3D3
Case "lightgreen"
ProcedureReturn $90EE90
Case "lightpink"
ProcedureReturn $C1B6FF
Case "lightsalmon"
ProcedureReturn $7AA0FF
Case "lightseagreen"
ProcedureReturn $AAB220
Case "lightskyblue"
ProcedureReturn $FACE87
Case "lightslategray", "lightslategrey"
ProcedureReturn $998877
Case "lightsteelblue"
ProcedureReturn $DEC4B0
Case "lightyellow"
ProcedureReturn $E0FFFF
Case "lime"
ProcedureReturn $00FF00
Case "limegreen"
ProcedureReturn $32CD32
Case "linen"
ProcedureReturn $E6F0FA
Case "magenta"
ProcedureReturn $FF00FF
Case "maroon"
ProcedureReturn $000080
Case "mediumaquamarine"
ProcedureReturn $AACD66
Case "mediumblue"
ProcedureReturn $CD0000
Case "mediumorchid"
ProcedureReturn $D355BA
Case "mediumpurple"
ProcedureReturn $DB7093
Case "mediumseagreen"
ProcedureReturn $71B33C
Case "mediumslateblue"
ProcedureReturn $EE687B
Case "mediumspringgreen"
ProcedureReturn $9AFA00
Case "mediumturquoise"
ProcedureReturn $CCD148
Case "mediumvioletred"
ProcedureReturn $8515C7
Case "midnightblue"
ProcedureReturn $701919
Case "mintcream"
ProcedureReturn $FAFFF5
Case "mistyrose"
ProcedureReturn $E1E4FF
Case "moccasin"
ProcedureReturn $B5E4FF
Case "navajowhite"
ProcedureReturn $ADDEFF
Case "navy"
ProcedureReturn $800000
Case "oldlace"
ProcedureReturn $E6F5FD
Case "olive"
ProcedureReturn $008080
Case "olivedrab"
ProcedureReturn $238E6B
Case "orange"
ProcedureReturn $00A5FF
Case "orangered"
ProcedureReturn $0045FF
Case "orchid"
ProcedureReturn $D670DA
Case "palegoldenrod"
ProcedureReturn $AAE8EE
Case "palegreen"
ProcedureReturn $98FB98
Case "paleturquoise"
ProcedureReturn $EEEEAF
Case "palevioletred"
ProcedureReturn $9370DB
Case "papayawhip"
ProcedureReturn $D5EFFF
Case "peachpuff"
ProcedureReturn $B9DAFF
Case "peru"
ProcedureReturn $3F85CD
Case "pink"
ProcedureReturn $CBC0FF
Case "plum"
ProcedureReturn $DDA0DD
Case "powderblue"
ProcedureReturn $E6E0B0
Case "purple"
ProcedureReturn $800080
Case "red"
ProcedureReturn $0000FF
Case "rosybrown"
ProcedureReturn $8F8FBC
Case "royalblue"
ProcedureReturn $E16941
Case "saddlebrown"
ProcedureReturn $13458B
Case "salmon"
ProcedureReturn $7280FA
Case "sandybrown"
ProcedureReturn $60A4F4
Case "seagreen"
ProcedureReturn $578B2E
Case "seashell"
ProcedureReturn $EEF5FF
Case "sienna"
ProcedureReturn $2D52A0
Case "silver"
ProcedureReturn $C0C0C0
Case "skyblue"
ProcedureReturn $EBCE87
Case "slateblue"
ProcedureReturn $CD5A6A
Case "slategray", "slategrey"
ProcedureReturn $908070
Case "snow"
ProcedureReturn $FAFAFF
Case "springgreen"
ProcedureReturn $7FFF00
Case "steelblue"
ProcedureReturn $B48246
Case "tan"
ProcedureReturn $8CB4D2
Case "teal"
ProcedureReturn $808000
Case "thistle"
ProcedureReturn $D8BFD8
Case "tomato"
ProcedureReturn $4763FF
Case "turquoise"
ProcedureReturn $D0E040
Case "violet"
ProcedureReturn $EE82EE
Case "wheat"
ProcedureReturn $B3DEF5
Case "white"
ProcedureReturn $FFFFFF
Case "whitesmoke"
ProcedureReturn $F5F5F5
Case "yellow"
ProcedureReturn $00FFFF
Case "yellowgreen"
ProcedureReturn $32CD9A
Default
ProcedureReturn #PB_Default
EndSelect
EndProcedure
Procedure TransformCoordinates(A.d, B.d, C.d, D.d, E.d, F.d, System.i=#PB_Coordinate_User)
; Single matrices:
; Translation: {{1, 0, Tx}, {0, 1, Ty}, {0, 0, 1}}
; Rotation: {{Cos[Phi], -Sin[Phi], 0}, {Sin[Phi], Cos[Phi], 0}, {0, 0, 1}}
; Skew: {{1+Tan[ThetaX]*Tan[ThetaY], Tan[ThetaX], 0}, {Tan[ThetaY], 1, 0}, {0, 0, 1}}
; Scale: {{Sx, 0, 0}, {0, Sy, 0}, {0, 0, 1}}
; Transformation = Translation * Rotation * Skew * Scale
; Transformation(A,B,C,D,E,F) -> Translation(Tx,Ty) * Rotation(Phi) * Skew(ThetaX,ThetaY) * Scale(Sx,Sy)
; Simplification: ThetaY = 0, because ThetaY can be represented with ThetaX together with Scale and Rotation
Protected Tx.d, Ty.d, Phi.d, ThetaX.d, Sx.d, Sy.d
Protected Column.d = 1.0 / Sqr( A*A + B*B )
Tx = E
Ty = F
Sx = A*A*Column + B*B*Column
Sy = A*D*Column - B*C*Column
Phi.d = Degree(ATan2(A*Column, B*Column))
ThetaX.d = Degree(ATan((-A*C-B*D)/(B*C-A*D)))
TranslateCoordinates(Tx, Ty, System)
RotateCoordinates(0.0, 0.0, Phi, System)
SkewCoordinates(ThetaX, 0.0, System)
ScaleCoordinates(Sx, Sy, System)
EndProcedure
Declare.s Export_Circle_Code(*Node)
Declare.s Export_Rect_Code(*Node)
Declare.s Export_Path_Code(*Node)
Declare.s Export_Line_Code(*Node)
Declare.s Export_Text_Code(*Node)
Declare.s Export_Polygon_Code(*Node)
Declare.s Export_Polyline_Code(*Node)
Declare.s Export_Ellipse_Code(*Node)
;- __________ Parsing _________
Procedure.i Parse_Attribute_Color(String.s)
; http://www.w3.org/TR/2008/REC-SVGTiny12-20081222/painting.html#colorSyntax
Protected Color.s
If RegEx\Color = #Null
RegEx\Color = CreateRegularExpression(#PB_Any, "((?<=\#)[0-9A-Fa-f]{3}\b) | ((?<=\#)[0-9A-Fa-f]{6}\b) | (rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)) | (rgb\(\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\))", #PB_RegularExpression_Extended)
EndIf
If ExamineRegularExpression(RegEx\Color, String) And NextRegularExpressionMatch(RegEx\Color)
If RegularExpressionGroup(RegEx\Color, 1) ; Three digit hex - #rgb
Color.s = RegularExpressionGroup(RegEx\Color, 1)
ProcedureReturn RGB(Val("$"+Mid(Color,1,1))*17, Val("$"+Mid(Color,2,1))*17, Val("$"+Mid(Color,3,1))*17)
EndIf
If RegularExpressionGroup(RegEx\Color, 2) ; Six digit hex - #rrggbb
Color.s = RegularExpressionGroup(RegEx\Color, 2)
ProcedureReturn RGB(Val("$"+Mid(Color,1,2)), Val("$"+Mid(Color,3,2)), Val("$"+Mid(Color,5,2)))
EndIf
If RegularExpressionGroup(RegEx\Color, 3) ; Integer functional - rgb(rrr, ggg, bbb)
ProcedureReturn RGB(Val(RegularExpressionGroup(RegEx\Color, 4)), Val(RegularExpressionGroup(RegEx\Color, 5)), Val(RegularExpressionGroup(RegEx\Color, 6)))
EndIf
If RegularExpressionGroup(RegEx\Color, 7) ; Integer functional - rgb(rrr, ggg, bbb)
ProcedureReturn RGB(2.55*ValD(RegularExpressionGroup(RegEx\Color, 8)), 2.55*ValD(RegularExpressionGroup(RegEx\Color, 9)), 2.55*ValD(RegularExpressionGroup(RegEx\Color, 10)))
EndIf
EndIf
ProcedureReturn X11Color_(String)
EndProcedure
Procedure Parse_Attribute_Transform(String.s)
; http://www.w3.org/TR/2008/REC-SVGTiny12-20081222/coords.html#TransformAttribute
Protected X.d, Y.d, Angle.d
Protected A.d, B.d, C.d, D.d, E.d, F.f
If RegEx\Command = #Null
RegEx\Command = CreateRegularExpression(#PB_Any, "(\w+)\(([^\)]*)\)")
EndIf
If RegEx\Value = #Null
RegEx\Value = CreateRegularExpression(#PB_Any, "\-?\d+\.?\d*|\-?\.\d+")
EndIf
If ExamineRegularExpression(RegEx\Command, String)
While NextRegularExpressionMatch(RegEx\Command)
Select LCase(RegularExpressionGroup(RegEx\Command, 1))
Case "matrix"
If ExamineRegularExpression(RegEx\Value, RegularExpressionGroup(RegEx\Command, 2))
A = 0.0 : B = 0.0 : C = 0.0 : D = 0.0 : E = 0.0 : F = 0.0
If NextRegularExpressionMatch(RegEx\Value) : A = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : B = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : C = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : D = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : E = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : F = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
TransformCoordinates(A, B, C, D, E, F) ;: Debug "matrix("+StrD(A)+", "+StrD(B)+", "+StrD(C)+", "+StrD(D)+", "+StrD(E)+", "+StrD(F)+")"
EndIf
Case "translate"
If ExamineRegularExpression(RegEx\Value, RegularExpressionGroup(RegEx\Command, 2))
X = 0.0 : Y = 0.0
If NextRegularExpressionMatch(RegEx\Value) : X = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : Y = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
TranslateCoordinates(X, Y) ;: Debug "translate("+StrD(X)+", "+StrD(Y)+")"
EndIf
Case "scale"
If ExamineRegularExpression(RegEx\Value, RegularExpressionGroup(RegEx\Command, 2))
X = 0.0 : Y = 0.0
If NextRegularExpressionMatch(RegEx\Value) : X = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : Y = ValD(RegularExpressionMatchString(RegEx\Value)) : Else : Y = X : EndIf
ScaleCoordinates(X, Y) ;: Debug "scale("+StrD(X)+", "+StrD(Y)+")"
EndIf
Case "rotate"
If ExamineRegularExpression(RegEx\Value, RegularExpressionGroup(RegEx\Command, 2))
X = 0.0 : Y = 0.0 : Angle = 0.0
If NextRegularExpressionMatch(RegEx\Value) : Angle = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : X = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
If NextRegularExpressionMatch(RegEx\Value) : Y = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
RotateCoordinates(X, Y, Angle) ;: Debug "rotate("+StrD(Angle)+", "+StrD(X)+", "+StrD(Y)+")"
EndIf
Case "skewx"
If ExamineRegularExpression(RegEx\Value, RegularExpressionGroup(RegEx\Command, 2))
Angle = 0.0
If NextRegularExpressionMatch(RegEx\Value) : Angle = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
SkewCoordinates(Angle, 0) ;: Debug "skewX("+StrD(Angle)+")"
EndIf
Case "skewy"
If ExamineRegularExpression(RegEx\Value, RegularExpressionGroup(RegEx\Command, 2))
Angle = 0.0
If NextRegularExpressionMatch(RegEx\Value) : Angle = ValD(RegularExpressionMatchString(RegEx\Value)) : EndIf
SkewCoordinates(0, Angle) ;: Debug "skewY("+StrD(Angle)+")"
EndIf
EndSelect
Wend
EndIf
ProcedureReturn ValD(String)
EndProcedure
Procedure.d Parse_Attribute_Value(*Node, String.s="")
If String = ""
String = XMLAttributeValue(*Node)
EndIf
ProcedureReturn ValD(String)
EndProcedure
Procedure Parse_Attribute_Style(String.s) ; , *Style.Style_Structure=#Null
If RegEx\Style = #Null
RegEx\Style = CreateRegularExpression(#PB_Any, "([\w\-]+)\s*\:\s*([^;]*);?")
EndIf
;If Not *Style = #Null
; *Style = SVG()\CurrentStyle
;EndIf
With SVG()\CurrentStyle
If ExamineRegularExpression(RegEx\Style, String)
While NextRegularExpressionMatch(RegEx\Style)
Select LCase(RegularExpressionGroup(RegEx\Style, 1))
Case "opacity"
\Opacity = Parse_Attribute_Value(#Null, RegularExpressionGroup(RegEx\Style, 2))
Case "fill"
\Fill = Parse_Attribute_Color(RegularExpressionGroup(RegEx\Style, 2))
Case "fill-rule"
Select LCase(Trim(RegularExpressionGroup(RegEx\Style, 2)))
Case "nonzero"
\FillRule = #PB_Path_Winding
Case "evenodd"
\FillRule = #PB_Path_Default
EndSelect
Case "fill-opacity"
\FillOpacity = Parse_Attribute_Value(#Null, RegularExpressionGroup(RegEx\Style, 2))
Case "stroke"
\Stroke = Parse_Attribute_Color(RegularExpressionGroup(RegEx\Style, 2))
Case "stroke-opacity"
\StrokeOpacity = Parse_Attribute_Value(#Null, RegularExpressionGroup(RegEx\Style, 2))
Case "stroke-width"
\StrokeWidth = Parse_Attribute_Value(#Null, RegularExpressionGroup(RegEx\Style, 2))
Case "stroke-linecap"
Select LCase(Trim(RegularExpressionGroup(RegEx\Style, 2)))
Case "butt"
\StrokeLineCap = #PB_Path_Default
Case "round"
\StrokeLineCap = #PB_Path_RoundEnd
Case "square"
\StrokeLineCap = #PB_Path_SquareEnd
EndSelect
Case "stroke-linejoin"
Select LCase(Trim(RegularExpressionGroup(RegEx\Style, 2)))
Case "miter"
\StrokeLineJoin = #PB_Path_Default
Case "round"
\StrokeLineJoin = #PB_Path_RoundCorner
Case "bevel"
\StrokeLineJoin = #PB_Path_DiagonalCorner
EndSelect
EndSelect
Wend
EndIf
EndWith
EndProcedure
Procedure.d Parse_Attribute_Length(String.s, Reference.d=0.0)
Protected Value.d
If Not RegEx\Length
RegEx\Length = CreateRegularExpression(#PB_Any, "(\d+\.?\d*|\.\d+)\s*(px|in|cm|mm|pt|\%)?", #PB_RegularExpression_NoCase)
EndIf
If ExamineRegularExpression(RegEx\Length, String) And NextRegularExpressionMatch(RegEx\Length)
Value = ValD(RegularExpressionGroup(RegEx\Length, 1))
Select LCase(RegularExpressionGroup(RegEx\Length, 2))
Case "px"
ProcedureReturn Value
Case "in"
ProcedureReturn Value * SVG()\ResX
Case "cm"
ProcedureReturn Value * SVG()\ResX / 2.54
Case "mm"
ProcedureReturn Value * SVG()\ResX / 25.4
Case "pt"
ProcedureReturn Value * SVG()\ResX / 72.0
Case "%"
ProcedureReturn Reference * Value / 100.0
EndSelect
EndIf
ProcedureReturn Value
EndProcedure
Procedure.s Parse_Attribute_String(*Node, String.s="")
If String = ""
String = XMLAttributeValue(*Node)
EndIf
ProcedureReturn String
EndProcedure
Procedure.i Parse_Attribute_Presentation(*Node)
If RegEx\Value = #Null
RegEx\Value = CreateRegularExpression(#PB_Any, "\-?\d+\.?\d*|\-?\.\d+")
EndIf
With SVG()\CurrentStyle
Select LCase(XMLAttributeName(*Node))
Case "transform"
Parse_Attribute_Transform(XMLAttributeValue(*Node))
Case "style"
Parse_Attribute_Style(XMLAttributeValue(*Node))
Case "opacity"
\Opacity = Parse_Attribute_Value(*Node)
Case "stroke"
\Stroke = Parse_Attribute_Color(XMLAttributeValue(*Node))
Case "stroke-opacity"
\StrokeOpacity = Parse_Attribute_Value(*Node)
Case "stroke-width"
\StrokeWidth = Parse_Attribute_Value(*Node)
Case "stroke-linecap"
Select LCase(Trim(XMLAttributeValue(*Node)))
Case "butt"
\StrokeLineCap = #PB_Path_Default
Case "round"
\StrokeLineCap = #PB_Path_RoundEnd
Case "square"
\StrokeLineCap = #PB_Path_SquareEnd
EndSelect
Case "stroke-linejoin"
Select LCase(Trim(XMLAttributeValue(*Node)))
Case "miter"
\StrokeLineJoin = #PB_Path_Default
Case "round"
\StrokeLineJoin = #PB_Path_RoundCorner
Case "bevel"
\StrokeLineJoin = #PB_Path_DiagonalCorner
EndSelect
Case "stroke-dashoffset"
\StrokeDashOffset = Parse_Attribute_Value(*Node)
Case "stroke-dasharray"
If ExamineRegularExpression(RegEx\Value, XMLAttributeValue(*Node))
While NextRegularExpressionMatch(RegEx\Value)
If ArraySize(\StrokeDashArray()) = -1
Dim \StrokeDashArray(0)
Else
ReDim \StrokeDashArray(ArraySize(\StrokeDashArray())+1)
EndIf
\StrokeDashArray(ArraySize(\StrokeDashArray())) = ValD(RegularExpressionMatchString(RegEx\Value))
Wend
EndIf
Case "fill"
\Fill = Parse_Attribute_Color(XMLAttributeValue(*Node))
Case "fill-opacity"
\FillOpacity = Parse_Attribute_Value(*Node)
Case "fill-rule"
Select LCase(Trim(XMLAttributeValue(*Node)))
Case "nonzero"
\FillRule = #PB_Path_Winding
Case "evenodd"
\FillRule = #PB_Path_Default
EndSelect
EndSelect
EndWith
EndProcedure
;-----------------------------------------;
Procedure GetColorRGBA(Color.l, Opacity.d, *r.Integer, *g.Integer, *b.Integer, *a.Integer)
If Opacity = 0.0 : Opacity = 1.0 : EndIf
*r\i = Red(Color)
*g\i = Green(Color)
*b\i = Blue(Color)
*a\i = Int(Opacity * 255)
EndProcedure
Procedure.s RGBA_Code(r, g, b, a)
ProcedureReturn " VectorSourceColor(RGBA(" + r + "," + g + "," + b + "," + a + "))" + #CRLF$
EndProcedure
Procedure.s AddFillAndStroke(PathCode.s, FillColor.l, FillOpacity.d, StrokeColor.l, StrokeOpacity.d, StrokeWidth.d)
Protected Code.s = ""
Protected r, g, b, a
If FillColor = -1 And StrokeColor = -1
FillColor = RGB(0, 0, 0)
FillOpacity = 1.0
EndIf
If FillColor <> -1
GetColorRGBA(FillColor, FillOpacity, @r, @g, @b, @a)
Code + RGBA_Code(r, g, b, a)
Code + PathCode + " FillPath()" + #CRLF$
EndIf
If StrokeColor <> -1
GetColorRGBA(StrokeColor, StrokeOpacity, @r, @g, @b, @a)
Code + RGBA_Code(r, g, b, a)
Code + PathCode + " StrokePath(" + StrD(StrokeWidth) + ")" + #CRLF$
EndIf
ProcedureReturn Code
EndProcedure
Procedure.s ExportNodeCode(*Node)
Protected Code.s = ""
Protected Name.s = LCase(GetXMLNodeName(*Node))
Select Name
Case "circle"
Code + Export_Circle_Code(*Node)
Case "rect"
Code + Export_Rect_Code(*Node)
Case "line"
Code + Export_Line_Code(*Node)
Case "path"
Code + Export_Path_Code(*Node)
Case "text"
Code + Export_Text_Code(*Node)
Case "polygon"
Code + Export_Polygon_Code(*Node)
Case "polyline"
Code + Export_Polyline_Code(*Node)
Case "ellipse"
Code + Export_Ellipse_Code(*Node)
Case "g"
Protected *Child = ChildXMLNode(*Node)
While *Child
Code + ExportNodeCode(*Child)
*Child = NextXMLNode(*Child)
Wend
ProcedureReturn Code
EndSelect
*Child = ChildXMLNode(*Node)
While *Child
Code + ExportNodeCode(*Child)
*Child = NextXMLNode(*Child)
Wend
ProcedureReturn Code
EndProcedure
Procedure.s Export_Circle_Code(*Node)
Protected cx.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "cx"), 0.0)
Protected cy.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "cy"), 0.0)
Protected r.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "r"), 0.0)
Protected FillColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "fill"))
Protected StrokeColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "stroke"))
Protected FillOpacity.d = Parse_Attribute_Value(*Node, "fill-opacity")
Protected StrokeOpacity.d = Parse_Attribute_Value(*Node, "stroke-opacity")
Protected StrokeWidth.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "stroke-width"), 1.0)
Protected PathCode.s = " AddPathCircle(" + StrD(cx) + ", " + StrD(cy) + ", " + StrD(r) + ")" + #CRLF$
ProcedureReturn AddFillAndStroke(PathCode, FillColor, FillOpacity, StrokeColor, StrokeOpacity, StrokeWidth)
EndProcedure
Procedure.s Export_Rect_Code(*Node)
Protected x.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "x"), 0.0)
Protected y.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "y"), 0.0)
Protected w.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "width"), 0.0)
Protected h.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "height"), 0.0)
Protected rx.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "rx"), 0.0)
Protected ry.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "ry"), 0.0)
Protected FillColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "fill"))
Protected StrokeColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "stroke"))
Protected FillOpacity.d = Parse_Attribute_Value(*Node, "fill-opacity")
Protected StrokeOpacity.d = Parse_Attribute_Value(*Node, "stroke-opacity")
Protected StrokeWidth.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "stroke-width"), 1.0)
Protected PathCode.s
If rx > 0.0 Or ry > 0.0
PathCode = " AddPathRoundedBox(" + StrD(x) + ", " + StrD(y) + ", " + StrD(w) + ", " + StrD(h) + ", " + StrD(rx) + ", " + StrD(ry) + ")" + #CRLF$
Else
PathCode = " AddPathBox(" + StrD(x) + ", " + StrD(y) + ", " + StrD(w) + ", " + StrD(h) + ")" + #CRLF$
EndIf
ProcedureReturn AddFillAndStroke(PathCode, FillColor, FillOpacity, StrokeColor, StrokeOpacity, StrokeWidth)
EndProcedure
Procedure.s Export_Path_Code(*Node)
Protected d.s = GetXMLAttribute(*Node, "d")
If d = "" : ProcedureReturn "" : EndIf
Protected FillColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "fill"))
Protected StrokeColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "stroke"))
Protected FillOpacity.d = Parse_Attribute_Value(*Node, "fill-opacity")
Protected StrokeOpacity.d = Parse_Attribute_Value(*Node, "stroke-opacity")
Protected StrokeWidth.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "stroke-width"), 1.0)
Protected PathCode.s = " AddPathSegments(" + Chr(34) + d + Chr(34) + ")" + #CRLF$
ProcedureReturn AddFillAndStroke(PathCode, FillColor, FillOpacity, StrokeColor, StrokeOpacity, StrokeWidth)
EndProcedure
Procedure.s Export_Line_Code(*Node)
Protected x1.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "x1"), 0.0)
Protected y1.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "y1"), 0.0)
Protected x2.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "x2"), 0.0)
Protected y2.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "y2"), 0.0)
Protected StrokeColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "stroke"))
Protected StrokeOpacity.d = Parse_Attribute_Value(*Node, "stroke-opacity")
Protected StrokeWidth.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "stroke-width"), 1.0)
Protected r, g, b, a
Protected Code.s = ""
If StrokeColor <> -1
GetColorRGBA(StrokeColor, StrokeOpacity, @r, @g, @b, @a)
Code + RGBA_Code(r, g, b, a)
Code + " AddPathLine(" + StrD(x1) + ", " + StrD(y1) + ", " + StrD(x2) + ", " + StrD(y2) + ")" + #CRLF$
Code + " StrokePath(" + StrD(StrokeWidth) + ")" + #CRLF$
EndIf
ProcedureReturn Code
EndProcedure
Procedure.s Export_Text_Code(*Node)
Protected x.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "x"), 0.0)
Protected y.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "y"), 0.0)
Protected fontSize.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "font-size"), 12.0)
Protected FillColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "fill"))
Protected FillOpacity.d = Parse_Attribute_Value(*Node, "fill-opacity")
Protected r, g, b, a
Protected Code.s = ""
If FillColor <> -1
GetColorRGBA(FillColor, FillOpacity, @r, @g, @b, @a)
Code + RGBA_Code(r, g, b, a)
EndIf
Protected text.s = GetXMLNodeText(*Node)
If text = "" : ProcedureReturn "" : EndIf
Code + " DrawText(" + StrD(x) + ", " + StrD(y) + ", " + Chr(34) + text + Chr(34) + ", " + StrD(fontSize) + ")" + #CRLF$
ProcedureReturn Code
EndProcedure
Procedure.s Export_Polygon_Code(*Node)
Protected points.s = GetXMLAttribute(*Node, "points")
If points = "" : ProcedureReturn "" : EndIf
Protected FillColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "fill"))
Protected StrokeColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "stroke"))
Protected FillOpacity.d = Parse_Attribute_Value(*Node, "fill-opacity")
Protected StrokeOpacity.d = Parse_Attribute_Value(*Node, "stroke-opacity")
Protected StrokeWidth.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "stroke-width"), 1.0)
Protected PathCode.s = " AddPathPolygon(" + Chr(34) + points + Chr(34) + ")" + #CRLF$
ProcedureReturn AddFillAndStroke(PathCode, FillColor, FillOpacity, StrokeColor, StrokeOpacity, StrokeWidth)
EndProcedure
Procedure.s Export_Polyline_Code(*Node)
Protected points.s = GetXMLAttribute(*Node, "points")
If points = "" : ProcedureReturn "" : EndIf
Protected StrokeColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "stroke"))
Protected StrokeOpacity.d = Parse_Attribute_Value(*Node, "stroke-opacity")
Protected StrokeWidth.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "stroke-width"), 1.0)
Protected r, g, b, a
Protected Code.s = ""
If StrokeColor <> -1
GetColorRGBA(StrokeColor, StrokeOpacity, @r, @g, @b, @a)
Code + RGBA_Code(r, g, b, a)
Code + " AddPathPolyline(" + Chr(34) + points + Chr(34) + ")" + #CRLF$
Code + " StrokePath(" + StrD(StrokeWidth) + ")" + #CRLF$
EndIf
ProcedureReturn Code
EndProcedure
Procedure.s Export_Ellipse_Code(*Node)
Protected cx.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "cx"), 0.0)
Protected cy.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "cy"), 0.0)
Protected rx.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "rx"), 0.0)
Protected ry.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "ry"), 0.0)
Protected FillColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "fill"))
Protected StrokeColor.l = Parse_Attribute_Color(GetXMLAttribute(*Node, "stroke"))
Protected FillOpacity.d = Parse_Attribute_Value(*Node, "fill-opacity")
Protected StrokeOpacity.d = Parse_Attribute_Value(*Node, "stroke-opacity")
Protected StrokeWidth.d = Parse_Attribute_Length(GetXMLAttribute(*Node, "stroke-width"), 1.0)
Protected PathCode.s = " AddPathEllipse(" + StrD(cx) + ", " + StrD(cy) + ", " + StrD(rx) + ", " + StrD(ry) + ")" + #CRLF$
ProcedureReturn AddFillAndStroke(PathCode, FillColor, FillOpacity, StrokeColor, StrokeOpacity, StrokeWidth)
EndProcedure
Procedure.s GenerateVectorCodeFromFile(FilePath.s)
Protected SVGContent.s
If FileSize(FilePath) <= 0
ProcedureReturn
EndIf
If ReadFile(0, FilePath)
While Not Eof(0)
SVGContent + ReadString(0, #PB_File_IgnoreEOL) + #CRLF$
Wend
CloseFile(0)
Else
ProcedureReturn "false"
EndIf
Protected XML.i = ParseXML(#PB_Any, SVGContent)
If Not IsXML(XML)
ProcedureReturn "false"
EndIf
Protected *RootNode = MainXMLNode(XML)
If *RootNode = 0
FreeXML(XML)
ProcedureReturn "false"
EndIf
Protected Code.s = "; Start Drawing" + #CRLF$
Code + "If StartVectorDrawing(CanvasVectorOutput(#Canvas))" + #CRLF$
Code + " VectorSourceColor($FF000000)" + #CRLF$
Code + ExportNodeCode(*RootNode)
Code + " StopVectorDrawing()" + #CRLF$
Code + "EndIf" + #CRLF$
FreeXML(XML)
ProcedureReturn Code
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
Define Code.s = SVG2PB::GenerateVectorCodeFromFile("Test.svg")
Debug Code
CompilerEndIf