SVG to PB VectorDrawing Codes

Share your advanced PureBasic knowledge/code with the community.
punak
User
User
Posts: 94
Joined: Tue Sep 07, 2021 12:08 pm

SVG to PB VectorDrawing Codes

Post by punak »

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. :D

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

Last edited by punak on Tue Sep 16, 2025 2:10 am, edited 3 times in total.
punak
User
User
Posts: 94
Joined: Tue Sep 07, 2021 12:08 pm

Re: SVG to PB VectorDrawing Codes

Post by punak »

Sample code generated:

Code: Select all


Enumeration 
  #Canvas
EndEnumeration

If OpenWindow(0, 0, 0, 800, 600, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(0, 0, 0, 800, 600)
  
  ; Start Drawing
  If StartVectorDrawing(CanvasVectorOutput(#Canvas))
    VectorSourceColor($FF000000)
    AddPathSegments("m399.3125 67.855469h-285.132812c-37.671876 0-68.210938 30.539062-68.210938 68.210937v284.554688h421.554688v-284.554688c0-37.671875-30.539063-68.210937-68.210938-68.210937zm0 0")
    VectorSourceColor(RGBA(239,225,249,255))
    FillPath()
    AddPathSegments("m45.96875 383.179688h421.554688v37.652343h-421.554688zm0 0")
    VectorSourceColor(RGBA(215,198,229,255))
    FillPath()
    AddPathSegments("m132.410156 67.855469c-37.664062 0-68.207031 30.539062-68.207031 68.214843v284.550782h-17.488281v-284.550782c0-37.675781 30.539062-68.214843 68.21875-68.214843zm0 0")
    VectorSourceColor(RGBA(215,198,229,255))
    FillPath()
    AddPathSegments("m388.859375 360.320312h-264.230469c-21.164062 0-38.324218-17.15625-38.324218-38.324218v-175.480469c0-21.164063 17.160156-38.324219 38.324218-38.324219h264.230469c21.164063 0 38.324219 17.160156 38.324219 38.324219v175.480469c0 21.167968-17.15625 38.324218-38.324219 38.324218zm0 0")
    VectorSourceColor(RGBA(195,237,252,255))
    FillPath()
    AddPathSegments("m475.144531 400.660156h-436.796875c-16.167968 0-29.277344 13.105469-29.277344 29.273438 0 16.167968 13.109376 29.273437 29.277344 29.273437h436.796875c16.167969 0 29.273438-13.105469 29.273438-29.273437 0-16.167969-13.105469-29.273438-29.273438-29.273438zm0 0")
    VectorSourceColor(RGBA(239,225,249,255))
    FillPath()
    AddPathSegments("m500.585938 444.417969c-5.046876 8.835937-14.546876 14.789062-25.445313 14.789062h-436.792969c-8.089844 0-15.40625-3.273437-20.707031-8.570312-5.296875-5.296875-8.570313-12.617188-8.570313-20.703125 0-16.164063 13.101563-29.265625 29.277344-29.265625h12.199219c-2.445313 4.265625-3.832031 9.207031-3.832031 14.476562 0 8.085938 3.277344 15.40625 8.574218 20.703125 5.296876 5.296875 12.617188 8.570313 20.703126 8.570313zm0 0")
    VectorSourceColor(RGBA(215,198,229,255))
    FillPath()
    AddPathSegments("m311.175781 429.933594h-108.863281c-16.167969 0-29.273438-13.105469-29.273438-29.273438h167.410157c.003906 16.167969-13.105469 29.273438-29.273438 29.273438zm0 0")
    VectorSourceColor(RGBA(215,198,229,255))
    FillPath()
    AddPathSegments("m469.652344 8.082031h-111.058594c-18.375 0-33.269531 14.894531-33.269531 33.269531v76.667969c0 18.375 14.894531 33.269531 33.269531 33.269531h111.058594c18.375 0 33.269531-14.894531 33.269531-33.269531v-76.667969c0-18.375-14.898437-33.269531-33.269531-33.269531zm0 0")
    VectorSourceColor(RGBA(255,85,126,255))
    FillPath()
    AddPathSegments("m418.4375 208.773438h-87.285156c-15.414063 0-27.90625 12.496093-27.90625 27.90625v69.242187c0 15.414063 12.492187 27.90625 27.90625 27.90625h87.285156c15.414062 0 27.90625-12.492187 27.90625-27.90625v-69.242187c0-15.410157-12.492188-27.90625-27.90625-27.90625zm0 0")
    VectorSourceColor(RGBA(128,226,157,255))
    FillPath()
    AddPathSegments("m216.457031 134.296875h-164.550781c-24.480469 0-44.324219 19.84375-44.324219 44.324219v89.644531c0 24.480469 19.84375 44.324219 44.324219 44.324219h164.550781c24.476563 0 44.324219-19.84375 44.324219-44.324219v-89.644531c0-24.480469-19.847656-44.324219-44.324219-44.324219zm0 0")
    VectorSourceColor(RGBA(97,126,209,255))
    FillPath()
    AddPathSegments("m475.144531 393.078125h-.039062v-234.582031c19.953125-2.671875 35.398437-19.800782 35.398437-40.476563v-76.667969c0-22.527343-18.328125-40.851562-40.851562-40.851562h-111.058594c-22.523438 0-40.851562 18.324219-40.851562 40.851562v18.921876h-125.390626c-4.1875 0-7.582031 3.394531-7.582031 7.582031s3.394531 7.582031 7.582031 7.582031h125.390626v25.171875h-193.109376c-17.867187 0-33.886718 10.289063-41.4375 26.109375h-28.910156c1.980469-12.839844 8.007813-24.667969 17.433594-33.921875 11.402344-11.195313 26.480469-17.359375 42.460938-17.359375h47.84375c4.1875 0 7.582031-3.394531 7.582031-7.582031s-3.394531-7.582031-7.582031-7.582031h-47.84375c-19.976563 0-38.828126 7.707031-53.082032 21.699218-12.761718 12.527344-20.535156 28.832032-22.296875 46.421875-22.296875 5.824219-38.800781 26.132813-38.800781 50.226563v89.644531c0 23.945313 16.304688 44.144531 38.390625 50.109375v74.703125h-.042969c-20.324218 0-36.859375 16.535156-36.859375 36.855469 0 20.324218 16.535157 36.855468 36.859375 36.855468h364.140625c4.1875 0 7.582031-3.394531 7.582031-7.582031 0-4.183593-3.394531-7.578125-7.582031-7.578125h-364.140625c-11.960937 0-21.695312-9.734375-21.695312-21.695312 0-11.960938 9.734375-21.691406 21.695312-21.691406h7.535156c.03125 0 .058594.003906.089844.003906h120.273438c3.507812 16.695312 18.34375 29.269531 36.066406 29.269531h108.863281c17.722657 0 32.558594-12.574219 36.066407-29.269531h120.28125c.027343 0 .058593-.003906.085937-.003906h7.535156c11.960938 0 21.691407 9.730468 21.691407 21.691406 0 11.960937-9.730469 21.695312-21.691407 21.695312h-42.328125c-4.1875 0-7.582031 3.394532-7.582031 7.578125 0 4.1875 3.394531 7.582031 7.582031 7.582031h42.328125c20.324219 0 36.855469-16.53125 36.855469-36.855468 0-20.320313-16.535156-36.855469-36.855469-36.855469zm-116.550781-377.414063h111.058594c14.164062 0 25.6875 11.523438 25.6875 25.6875v76.667969c0 14.164063-11.523438 25.6875-25.6875 25.6875h-111.058594c-14.164062 0-25.6875-11.523437-25.6875-25.6875v-76.667969c0-14.164062 11.523438-25.6875 25.6875-25.6875zm-233.960938 100.105469h193.109376v2.25c0 22.527344 18.324218 40.851563 40.851562 40.851563h61.011719v42.351562c-.390625-.011718-.777344-.027344-1.167969-.027344h-87.285156c-19.566406 0-35.488282 15.917969-35.488282 35.488282v69.242187c0 19.566407 15.921876 35.488281 35.488282 35.488281h81.507812c-5.734375 6.976563-14.429687 11.324219-23.796875 11.324219h-264.230469c-16.953124 0-30.742187-13.789062-30.742187-30.742187v-1.824219h122.566406c28.621094 0 51.90625-23.285156 51.90625-51.90625v-89.644531c0-28.621094-23.285156-51.90625-51.90625-51.90625h-115.347656c5.726563-6.792969 14.246094-10.945313 23.523437-10.945313zm293.804688 210.476563h-87.285156c-11.207032 0-20.324219-9.117188-20.324219-20.324219v-69.242187c0-11.207032 9.117187-20.324219 20.324219-20.324219h87.285156c11.207031 0 20.324219 9.121093 20.324219 20.324219v69.242187c0 11.207031-9.117188 20.324219-20.324219 20.324219zm-403.273438-57.980469v-89.644531c0-20.257813 16.480469-36.742188 36.742188-36.742188h164.550781c20.257813 0 36.742188 16.484375 36.742188 36.742188v89.644531c0 20.257813-16.484375 36.742187-36.742188 36.742187h-164.550781c-20.261719 0-36.742188-16.484374-36.742188-36.742187zm38.390626 51.90625h25.171874v1.824219c0 25.3125 20.59375 45.90625 45.90625 45.90625h264.230469c18.816407 0 35.824219-11.746094 42.683594-29.011719 13.097656-5.226563 22.378906-18.027344 22.378906-32.96875v-69.242187c0-13.679688-7.789062-25.570313-19.160156-31.492188v-46.316406h25.171875v234.207031h-406.382812zm257.621093 102.179687h-108.863281c-9.292969 0-17.238281-5.875-20.324219-14.105468h149.511719c-3.082031 8.230468-11.027344 14.105468-20.324219 14.105468zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m209.34375 180.09375h17.117188c4.183593 0 7.578124-3.394531 7.578124-7.582031 0-4.183594-3.394531-7.578125-7.578124-7.578125h-17.117188c-4.183594 0-7.582031 3.394531-7.582031 7.578125 0 4.1875 3.398437 7.582031 7.582031 7.582031zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m113.539062 180.09375h68.578126c4.1875 0 7.582031-3.394531 7.582031-7.582031 0-4.183594-3.394531-7.578125-7.582031-7.578125h-68.578126c-4.1875 0-7.582031 3.394531-7.582031 7.578125 0 4.1875 3.390625 7.582031 7.582031 7.582031zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m41.902344 180.09375h44.40625c4.1875 0 7.582031-3.394531 7.582031-7.582031 0-4.183594-3.394531-7.578125-7.582031-7.578125h-44.40625c-4.1875 0-7.582032 3.394531-7.582032 7.578125 0 4.1875 3.394532 7.582031 7.582032 7.582031zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m226.460938 266.792969h-83.605469c-4.1875 0-7.582031 3.394531-7.582031 7.578125 0 4.1875 3.394531 7.582031 7.582031 7.582031h83.605469c4.183593 0 7.578124-3.394531 7.578124-7.582031 0-4.183594-3.394531-7.578125-7.578124-7.578125zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m112.527344 266.792969h-25.140625c-4.1875 0-7.582031 3.394531-7.582031 7.578125 0 4.1875 3.394531 7.582031 7.582031 7.582031h25.140625c4.1875 0 7.582031-3.394531 7.582031-7.582031 0-4.183594-3.394531-7.578125-7.582031-7.578125zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m64.644531 274.371094c0-4.183594-3.394531-7.578125-7.582031-7.578125h-15.160156c-4.1875 0-7.582032 3.394531-7.582032 7.578125 0 4.1875 3.394532 7.582031 7.582032 7.582031h15.160156c4.1875 0 7.582031-3.394531 7.582031-7.582031zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m226.460938 232.835938h-33.179688c-4.1875 0-7.582031 3.394531-7.582031 7.582031s3.394531 7.582031 7.582031 7.582031h33.179688c4.183593 0 7.578124-3.394531 7.578124-7.582031s-3.394531-7.582031-7.578124-7.582031zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m41.902344 248h121.050781c4.1875 0 7.582031-3.394531 7.582031-7.582031s-3.394531-7.582031-7.582031-7.582031h-121.050781c-4.1875 0-7.582032 3.394531-7.582032 7.582031s3.394532 7.582031 7.582032 7.582031zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m86.949219 206.46875c0 4.183594 3.394531 7.578125 7.582031 7.578125h131.929688c4.183593 0 7.578124-3.394531 7.578124-7.578125 0-4.1875-3.394531-7.582031-7.578124-7.582031h-131.929688c-4.1875 0-7.582031 3.390625-7.582031 7.582031zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m41.902344 214.046875h22.300781c4.1875 0 7.582031-3.394531 7.582031-7.578125 0-4.1875-3.394531-7.582031-7.582031-7.582031h-22.300781c-4.1875 0-7.582032 3.394531-7.582032 7.582031 0 4.183594 3.394532 7.578125 7.582032 7.578125zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m436.480469 117.851562c1.480469 1.480469 3.421875 2.21875 5.363281 2.21875 1.9375 0 3.878906-.738281 5.359375-2.21875l32.804687-32.804687c1.421876-1.421875 2.222657-3.351563 2.222657-5.359375 0-2.011719-.800781-3.941406-2.222657-5.363281l-32.804687-32.800781c-2.960937-2.960938-7.761719-2.960938-10.722656 0-2.960938 2.960937-2.960938 7.761718 0 10.722656l27.445312 27.441406-27.445312 27.441406c-2.960938 2.960938-2.960938 7.761719 0 10.722656zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m381.039062 117.851562c1.484376 1.480469 3.421876 2.21875 5.363282 2.21875s3.882812-.738281 5.363281-2.21875c2.960937-2.960937 2.960937-7.761718 0-10.722656l-27.445313-27.441406 27.445313-27.441406c2.960937-2.960938 2.960937-7.761719 0-10.722656-2.960937-2.960938-7.761719-2.960938-10.722656 0l-32.804688 32.800781c-1.421875 1.421875-2.222656 3.351562-2.222656 5.363281 0 2.007812.800781 3.9375 2.222656 5.359375zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m402.027344 136.964844c.527344.113281 1.054687.164062 1.574218.164062 3.507813 0 6.65625-2.445312 7.410157-6.015625l21.058593-99.722656c.867188-4.097656-1.753906-8.121094-5.851562-8.984375-4.09375-.863281-8.121094 1.753906-8.984375 5.851562l-21.058594 99.722657c-.867187 4.097656 1.753907 8.121093 5.851563 8.984375zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    AddPathSegments("m412.660156 233.382812c-3.679687-2.003906-8.28125-.648437-10.289062 3.027344l-31.046875 56.945313-18.074219-20.953125c-2.734375-3.171875-7.523438-3.523438-10.691406-.789063-3.171875 2.734375-3.523438 7.523438-.789063 10.691407l25.222657 29.25c1.453124 1.675781 3.550781 2.628906 5.742187 2.628906.269531 0 .535156-.015625.804687-.042969 2.476563-.261719 4.660157-1.722656 5.855469-3.910156l36.292969-66.558594c2.003906-3.679687.648438-8.285156-3.027344-10.289063zm0 0")
    VectorSourceColor(RGBA(0,0,0,255))
    FillPath()
    StopVectorDrawing()
  EndIf
  
  
  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf


User avatar
J. Baker
Addict
Addict
Posts: 2188
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: SVG to PB VectorDrawing Codes

Post by J. Baker »

That's pretty cool! Thanks for sharing!
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef


Even the vine knows it surroundings but the man with eyes does not.
User avatar
STARGÅTE
Addict
Addict
Posts: 2236
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: SVG to PB VectorDrawing Codes

Post by STARGÅTE »

Dear punak,

I would appreciate it if you would respect the license, when you post a close copy of an other source code which is published explicit under the MIT licence, which states:
"The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software."

In the age of AI, first authors are quickly forgotten. In this case here, I recognize my code style immediately.
It's not meant badly, only the desire for recognition in the code itself, as it is given in Thorsten's code.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
punak
User
User
Posts: 94
Joined: Tue Sep 07, 2021 12:08 pm

Re: SVG to PB VectorDrawing Codes

Post by punak »

STARGÅTE wrote: Mon Sep 15, 2025 10:14 pm Dear punak,

I would appreciate it if you would respect the license, when you post a close copy of an other source code which is published explicit under the MIT licence, which states:
"The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software."

In the age of AI, first authors are quickly forgotten. In this case here, I recognize my code style immediately.
It's not meant badly, only the desire for recognition in the code itself, as it is given in Thorsten's code.
oh yes yes you are right.rest assured, it was not intentional.I will correct it soon.

I was actually planning to get help from your knowledge or Thorsten1867 as the main developers of this module , because I am not a skilled programmer and I do not have detailed knowledge of how svg files work.I think this module is useful, but I'm sure it's not accurate. do you have any ideas for improving it?
I use the output of this module to draw various shapes with the Editor Factory module.It covers simple SVG files well, but not more complex ones. Getting help from Capilot is really difficult.
for example, I want the output of the generated code to be a procedure that has x,y,w,h inputs to draw the vector shape.
dige
Addict
Addict
Posts: 1413
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: SVG to PB VectorDrawing Codes

Post by dige »

Thank you punak!

I tried it, but I get the following error when creating the code: AddPathPolygon () is not a function
Tested with PB 6.30B2 x64 Windows..
"Daddy, I'll run faster, then it is not so far..."
punak
User
User
Posts: 94
Joined: Tue Sep 07, 2021 12:08 pm

Re: SVG to PB VectorDrawing Codes

Post by punak »

dige wrote: Tue Sep 16, 2025 10:01 am Thank you punak!

I tried it, but I get the following error when creating the code: AddPathPolygon () is not a function
Tested with PB 6.30B2 x64 Windows..
Thanks for your test. I am currently updating the module with the help of AI.more items will be added and improved in the next version.
Justin
Addict
Addict
Posts: 951
Joined: Sat Apr 26, 2003 2:49 pm

Re: SVG to PB VectorDrawing Codes

Post by Justin »

This is great but i think it needs the x, y, w, h parameters.
Justin
Addict
Addict
Posts: 951
Joined: Sat Apr 26, 2003 2:49 pm

Re: SVG to PB VectorDrawing Codes

Post by Justin »

I asked chatgpt and it came with this, rewrites the path string returned by GenerateVectorCodeFromFile with the new x, y, width and height. Ideally it should do it in one pass in the GenerateVectorCodeFromFile function, but this seems to work as a quick workaround.

Code: Select all

EnableExplicit

Structure PathCommand
  cmd$        ; the SVG command (M,L,C,Q,H,V,Z)
  List coords.d() ; numbers following the command
EndStructure

Procedure.s ScaleSvgPath(x.d, y.d, w.d, h.d, absolute.i, path$)
  Protected newPath$, scaleX.d, scaleY.d
  Protected minX.d = 1E9, minY.d = 1E9, maxX.d = -1E9, maxY.d = -1E9
  Protected i, j, token$, num$, c, nx.d, ny.d
  Protected cmd$
  
  ; --- Step 1: tokenize path into commands ---
  Protected path2$ = ReplaceString(path$, ",", " ")
  Protected len = Len(path2$)
  Protected numbers$ = "0123456789+-.eE"
  
  NewList commands.PathCommand()
  Define *pc.PathCommand
  
  i = 1
  While i <= len
    token$ = Mid(path2$, i, 1)
    If FindString("MmLlHhVvCcQqZz", token$)
      AddElement(commands())
      commands()\cmd$ = UCase(token$)
      i + 1
    ElseIf FindString(numbers$, token$)
      j = i
      While j <= len And FindString(numbers$, Mid(path2$, j, 1))
        j + 1
      Wend
      num$ = Trim(Mid(path2$, i, j - i))
      If num$ <> ""
        AddElement(commands()\coords())
        commands()\coords() = ValD(num$)
      EndIf
      i = j
    Else
      i + 1
    EndIf
  Wend
  
  ; --- Step 2: bounding box (only XY pairs) ---
  ForEach commands()
    Select commands()\cmd$
      Case "M","L","C","Q"
        c = 0
        ForEach commands()\coords()
          If c % 2 = 0 ; X
            If commands()\coords() < minX : minX = commands()\coords() : EndIf
            If commands()\coords() > maxX : maxX = commands()\coords() : EndIf
          Else ; Y
            If commands()\coords() < minY : minY = commands()\coords() : EndIf
            If commands()\coords() > maxY : maxY = commands()\coords() : EndIf
          EndIf
          c + 1
        Next
      Case "H"
        ForEach commands()\coords()
          If commands()\coords() < minX : minX = commands()\coords() : EndIf
          If commands()\coords() > maxX : maxX = commands()\coords() : EndIf
        Next
      Case "V"
        ForEach commands()\coords()
          If commands()\coords() < minY : minY = commands()\coords() : EndIf
          If commands()\coords() > maxY : maxY = commands()\coords() : EndIf
        Next
    EndSelect
  Next
  
  If maxX <= minX Or maxY <= minY
    ProcedureReturn "" ; invalid path
  EndIf
  
  scaleX = w / (maxX - minX)
  scaleY = h / (maxY - minY)
  
  ; --- Step 3: rebuild transformed path ---
  ForEach commands()
    newPath$ + commands()\cmd$
    Select commands()\cmd$
      Case "M","L","C","Q"
        c = 0
        ForEach commands()\coords()
          If c % 2 = 0 ; X
            nx = (commands()\coords() - minX) * scaleX
            If absolute : nx + x : EndIf
            newPath$ + " " + StrD(nx, 3)
          Else ; Y
            ny = (commands()\coords() - minY) * scaleY
            If absolute : ny + y : EndIf
            newPath$ + " " + StrD(ny, 3)
          EndIf
          c + 1
        Next
      Case "H"
        ForEach commands()\coords()
          nx = (commands()\coords() - minX) * scaleX
          If absolute : nx + x : EndIf
          newPath$ + " " + StrD(nx, 3)
        Next
      Case "V"
        ForEach commands()\coords()
          ny = (commands()\coords() - minY) * scaleY
          If absolute : ny + y : EndIf
          newPath$ + " " + StrD(ny, 3)
        Next
      Case "Z"
        newPath$ + " "
    EndSelect
  Next
  
  ProcedureReturn Trim(newPath$)
EndProcedure


; --- Example ---

Enumeration 
  #Canvas
EndEnumeration

Define.l Event

If OpenWindow(0, 0, 0, 800, 600, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	CanvasGadget(0, 0, 0, 800, 600)
	  
	; Start Drawing
	Define.s p
	If StartVectorDrawing(CanvasVectorOutput(#Canvas))
	  VectorSourceColor($FF000000)
	  
	  AddPathBox(100, 100, 40, 40)
	
	  p = "M19 6C20.5 7.5 21 10 21 12C21 14 20.5 16.5 19 18M16 8.99998C16.5 9.49998 17 10.5 17 12C17 13.5 16.5 14.5 16 15M3 10.5V13.5C3 14.6046 3.5 15.5 5.5 16C7.5 16.5 9 21 12 21C14 21 14 3 12 3C9 3 7.5 7.5 5.5 8C3.5 8.5 3 9.39543 3 10.5Z"
	  
	  p = ScaleSvgPath(100, 100, 40, 40, #True, p)
	  AddPathSegments(p)
	  VectorSourceColor(RGBA(0,0,0,255))
	
	  StrokePath(1.5)
	  StopVectorDrawing()
	EndIf
	  
	Repeat
		Event = WaitWindowEvent()
	Until Event = #PB_Event_CloseWindow
EndIf
punak
User
User
Posts: 94
Joined: Tue Sep 07, 2021 12:08 pm

Re: SVG to PB VectorDrawing Codes

Post by punak »

Justin wrote: Wed Sep 17, 2025 1:36 pm This is great but i think it needs the x, y, w, h parameters.
Hi, thanks for your comment. I used the following functions to resize, move and rotate the generated vector image and it worked correctly.

ScaleCoordinates,TranslateCoordinates,RotateCoordinates
Justin
Addict
Addict
Posts: 951
Joined: Sat Apr 26, 2003 2:49 pm

Re: SVG to PB VectorDrawing Codes

Post by Justin »

How do you set a defined width and height?
Post Reply