[Module] SVG - Module (all OS)

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
User avatar
Thorsten1867
Addict
Addict
Posts: 1372
Joined: Wed Aug 24, 2005 4:02 pm
Location: Germany

[Module] SVG - Module (all OS)

Post by Thorsten1867 »

SVG - Module (all OS / 64Bit / DPI)

Using SVG images in PureBasic

The module is based on the code of VectorGraphic.pbi of STARGÅTE, who did all the preliminary work for converting SVG to vector drawing commands.

Code: Select all

; SVG::Load()        - similar to LoadImage()
; SVG::Resize()      - similar to ResizeImage()
;
; SVG::Catch()       - uses an already loaded SVG file (XML) 
; SVG::CatchMemory() - uses a SVG file (XML) loaded into memory
; SVG::CatchString() - reads the SVG data from a string
;
; SVG::DrawVector()  - converts a loaded SVG file to 'VectorDrawing' commands
;
; SVG::CreateIcon()  - create an icon file from an SVG image
Download: UseSVGImageModule.pbi
Last edited by Thorsten1867 on Mon Apr 20, 2020 4:37 pm, edited 1 time in total.
Translated with http://www.DeepL.com/Translator

Download of PureBasic - Modules
Download of PureBasic - Programs

[Windows 11 x64] [PB V5.7x]
User avatar
Thorsten1867
Addict
Addict
Posts: 1372
Joined: Wed Aug 24, 2005 4:02 pm
Location: Germany

Re: [Module] SVG - Module (all OS)

Post by Thorsten1867 »

Update: SVG::CreateIcon()
  • Windows (ICO): 16, 24, 32, 48, 64, 128, 256
  • MacOS (ICNS): 16, 32, 64, 128, 256, 512, 1024
Translated with http://www.DeepL.com/Translator

Download of PureBasic - Modules
Download of PureBasic - Programs

[Windows 11 x64] [PB V5.7x]
User avatar
Thorsten1867
Addict
Addict
Posts: 1372
Joined: Wed Aug 24, 2005 4:02 pm
Location: Germany

Re: [Module] SVG - Module (all OS)

Post by Thorsten1867 »

Added: Application example "SVG2Icon.exe" or "SVGIcon.app"

Image
Translated with http://www.DeepL.com/Translator

Download of PureBasic - Modules
Download of PureBasic - Programs

[Windows 11 x64] [PB V5.7x]
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2140
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: [Module] SVG - Module (all OS)

Post by Andre »

Looks very good, thank you! :D
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
Ziltch
User
User
Posts: 61
Joined: Sun Aug 03, 2003 12:05 am
Location: Australia

Re: [Module] SVG - Module (all OS)

Post by Ziltch »

Great work ! :D
User avatar
idle
Always Here
Always Here
Posts: 5964
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: [Module] SVG - Module (all OS)

Post by idle »

I'm working through fixing up text rendering Two bugs
Regex wasn't handling matrix values like 2.98023e-08
So I fixed the regex but even though it matches it doesn't return the right value, it extracts -8
"^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$"
So it's fixed with a while loop to extract the values

Second issue is harder to solve.
When you MovePathCursor(X,Y) those values are wrong and need transforming
in the test I'm using the source input values MovePathCursor(200 - VectorTextWidth(Text)/2, 100 - VectorTextHeight(Text)/2)
So need to work out how to resolve that from the transform matrix perhaps?

Code: Select all

DeclareModule SVG
  
  #Version  = 20042000
  #ModuleEx = 20041700
  
  EnumerationBinary ; Image Flags
    #Proportional    ; scales the image proportionally
    #ForceSizeRatio  ; uses the specified aspect ratio (even if #Proportional has been set)
    #KeepImage       ; keeps the previous image after resizing (only if #PB_Any was used)
    #CenterImage     ; 
    #CreateNoImage   ; loads the SVG file without creating an image [=> 'SVG::DrawVector()']
    #Windows         ; Icon: *.ico
    #MacOS           ; Icon: *.icns
  EndEnumeration

  ;- ===========================================================================
  ;-   DeclareModule
  ;- ===========================================================================

  Declare.i Load(Image.i, File.s, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0.0, Height.d=0.0, Font.i=#False)
  Declare.i Catch(Image.i, svgXML.i, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False) 
  Declare.i CatchMemory(Image.i, *MemorySVG, SizeSVG.i, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
  Declare.i CatchString(Image.i, StringSVG.s, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
  Declare.i Resize(Image.i, Width.d, Height.d, Flags.i=#False)
  Declare   DrawVector(Image.i, Width.d=0, Height.d=0, Flags.i=#False)
  
  CompilerIf Defined(Icon, #PB_Module)
    Declare.i CreateIcon(Image.i, File.s, Flags.i=#False)
  CompilerEndIf  
  
EndDeclareModule

Module SVG
  
  EnableExplicit
  
  ;- ============================================================================
  ;-   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 font_Structure 
    font.i
    FontFamily.s 
    fontsize.d
    fontWeight.d 
    fontstyle.s 
  EndStructure   
    
  Structure Font_Cache 
    Map fcache.font_Structure(65)
  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
  	font.font_Structure 
  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
  	
  	fonts.Font_Cache
  	
    CurrentStyle.Style_Structure
  	
  	StyleSheet.StyleSheet_Structure
  	ViewBox.ViewBox_Structure
  	Image.Image_Structure
  	
  EndStructure ;}
  Global NewMap SVG.SVG_Structure()
  
  ;- ============================================================================
  ;-   Module - Internal
  ;- ============================================================================ 
  
  Procedure.f dpiX(Num.i)
	  If Num > 0  
	    ProcedureReturn DesktopScaledX(Num)
	  EndIf   
	EndProcedure

	Procedure.f dpiY(Num.i)
	  If Num > 0  
	    ProcedureReturn DesktopScaledY(Num)
	  EndIf  
	EndProcedure
	
	
  Declare Draw(*Node, Indent.i=0)
  
  
  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
  
  ;- __________ 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+)(?:[eE][+-]?\d+)?$",#PB_RegularExpression_NoCase);"\-?\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
  					  Protected len,t=1,ct
  					  Protected str.s = RegularExpressionGroup(RegEx\Command, 2)	
  					  Protected *char.Unicode = @str 
  					  Protected *pos 
  					  While *char\u <> 0 
  					    *pos = *char  
  					    While *char\u <> ',' 
  					      If (*char\u >= '0' And *char\u <= '9')  
  					        len+1
  					        t=0
  					      ElseIf (*char\u = 'e' Or *char\u = 'E') 
  					        len+1
  					        t=0
  					      ElseIf (*char\u = '.' Or *char\u = '-' Or *char\u = '+')   
  					        len+1
  					        t=0
  					      ElseIf *char\u = 0  
  					        *char+2 
  					        Break 
  					      ElseIf t 
  					        *pos+2
  					      EndIf
  					      
  					      *char+2 
  					    Wend 
  					    If len 
  					      Select ct 
  					        Case 0
  					          A = ValD(PeekS(*pos,len)) 
  					        Case 1
  					          B =  ValD(PeekS(*pos,len))
  					        Case 2
  					          C =  ValD(PeekS(*pos,len))
  					        Case 3
  					          D = ValD(PeekS(*pos,len))
  					        Case 4 
  					          E =  ValD(PeekS(*pos,len))
  					        Case 5 
  					          F =  ValD(PeekS(*pos,len))
  					      EndSelect    
  					      ct+1 
  					      len=0
  					      t=1
  					    EndIf
  					    *char+2 
  					  Wend 
  						  						
;   						Debug RegularExpressionGroup(RegEx\Command, 2)						
;   						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)+")"
  					  ;transform="matrix(2.98023e-08,1,-1,5.96046e-08,300,-100)
  					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 "font-family" 
  		     \font\FontFamily = Parse_Attribute_String(*Node) 
  		  Case "font-size" 
  		     \font\fontsize =  Parse_Attribute_Value(*Node) 
  		  Case "font-weight"  
  		     \font\fontWeight =  Parse_Attribute_Value(*Node)
  		   Case "font-style" 
  		     \font\fontstyle = Parse_Attribute_String(*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
  
  ;- __________ Drawing __________
  
  Procedure   StyleSheets(Element.s="", Class.s="")
  	
  	With SVG()\CurrentStyle
  		
  		If Element And FindMapElement(SVG()\StyleSheet\Element(), Element) 
  			If FindMapElement(SVG()\StyleSheet\Element()\Class(), "")
  				Parse_Attribute_Style(SVG()\StyleSheet\Element()\Class()\Style)
  			EndIf
  			If Class And FindMapElement(SVG()\StyleSheet\Element()\Class(), Class)
  				Parse_Attribute_Style(SVG()\StyleSheet\Element()\Class()\Style)
  			EndIf
  		EndIf
  		If Class And FindMapElement(SVG()\StyleSheet\Element(), "") And FindMapElement(SVG()\StyleSheet\Element()\Class(), Class)
  			Parse_Attribute_Style(SVG()\StyleSheet\Element()\Class()\Style)
  		EndIf
  		
  	EndWith
  	
  EndProcedure
  
  Procedure   Rendering()
  	
  	With SVG()\CurrentStyle
  		
  		If \Opacity = 1.0
  			If \Fill <> -1
  				VectorSourceColor(\Fill | Int(255*\FillOpacity)<<24)
  				FillPath(\FillRule|#PB_Path_Preserve)
  			EndIf
  			If \Stroke <> -1
  				VectorSourceColor(\Stroke | Int(255*\StrokeOpacity)<<24)
  				If ArraySize(\StrokeDashArray()) <> -1
  					CustomDashPath(\StrokeWidth, \StrokeDashArray(), \StrokeLineCap|\StrokeLineJoin|#PB_Path_Preserve, \StrokeDashOffset)
  				Else
  					StrokePath(\StrokeWidth, \StrokeLineCap|\StrokeLineJoin|#PB_Path_Preserve)
  				EndIf
  			EndIf
  		ElseIf \Opacity > 0.0
  			BeginVectorLayer(255*\Opacity)
  			If \Fill <> -1
  				VectorSourceColor(\Fill | Int(255*\FillOpacity)<<24)
  				FillPath(\FillRule|#PB_Path_Preserve)
  			EndIf
  			If \Stroke <> -1
  				VectorSourceColor(\Stroke | Int(255*\StrokeOpacity)<<24)
  				If ArraySize(\StrokeDashArray()) <> -1
  					CustomDashPath(\StrokeWidth, \StrokeDashArray(), \StrokeLineCap|\StrokeLineJoin|#PB_Path_Preserve, \StrokeDashOffset)
  				Else
  					StrokePath(\StrokeWidth, \StrokeLineCap|\StrokeLineJoin|#PB_Path_Preserve)
  				EndIf
  			EndIf
  			EndVectorLayer()
  		EndIf
  		
  		ResetPath()
  	EndWith
  	
  EndProcedure
  
  Procedure   Draw_Group(*Node, Indent.i=0)
  	
  	Protected *ChildNode
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	BeginVectorLayer()
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "transform"
  					Parse_Attribute_Transform(XMLAttributeValue(*Node))
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	*ChildNode = ChildXMLNode(*Node)
  	While *ChildNode
  		Draw(*ChildNode, Indent+1)
  		*ChildNode = NextXMLNode(*ChildNode)
  	Wend
  	
  	EndVectorLayer()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Circle(*Node)
  	
  	Protected CX.d, CY.d, R.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("circle", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "cx"
  					CX = Parse_Attribute_Value(*Node)
  				Case "cy"
  					CY = Parse_Attribute_Value(*Node)
  				Case "r"
  					R = Parse_Attribute_Value(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	AddPathCircle(CX, CY, R)
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Ellipse(*Node)
  	
  	Protected CX.d, CY.d, RX.d, RY.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("ellipse", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "cx"
  					CX = Parse_Attribute_Value(*Node)
  				Case "cy"
  					CY = Parse_Attribute_Value(*Node)
  				Case "rx"
  					RX = Parse_Attribute_Value(*Node)
  				Case "ry"
  					RY = Parse_Attribute_Value(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	AddPathEllipse(CX, CY, RX, RY)
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Rectangle(*Node)
  	
  	Protected Width.d, Height.d, X.d=0.0, Y.d=0.0, RX.d=0.0, RY.d=0.0
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("rect", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "x"
  					X = Parse_Attribute_Value(*Node)
  				Case "y"
  					Y = Parse_Attribute_Value(*Node)
  				Case "width"
  					Width = Parse_Attribute_Value(*Node)
  				Case "height"
  					Height = Parse_Attribute_Value(*Node)
  				Case "rx"
  					RX = Parse_Attribute_Value(*Node)
  				Case "ry"
  					RY = Parse_Attribute_Value(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	If RX = 0.0 And RY = 0.0
  		AddPathBox(X, Y, Width, Height)
  	Else
  		If RX = 0.0 : RX = RY : EndIf
  		If RY = 0.0 : RY = RX : EndIf
  		If RX > Width/2 : RX = Width/2 : EndIf
  		If RY > Height/2 : RY = Height/2 : EndIf
  		If Width < 0 :  X + Width : Width = -Width : EndIf
  		If Height < 0 : Y + Height : Height = -Height : EndIf
  		MovePathCursor(X, Y+RY)
  		AddPathEllipse(RX, 0, RX, RY, 180, 270, #PB_Path_Relative)
  		AddPathEllipse(Width-2*RX, RY, RX, RY, 270, 360, #PB_Path_Relative|#PB_Path_Connected)
  		AddPathEllipse(-RX, Height-2*RY, RX, RY, 0, 90, #PB_Path_Relative|#PB_Path_Connected)
  		AddPathEllipse(-Width+2*RX, -RY, RX, RY, 90, 180, #PB_Path_Relative|#PB_Path_Connected)
  		ClosePath()
  	EndIf
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Line(*Node)
  	
  	Protected X1.d, Y1.d, X2.d, Y2.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("line", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "x1"
  					X1 = Parse_Attribute_Value(*Node)
  				Case "y1"
  					Y1 = Parse_Attribute_Value(*Node)
  				Case "x2"
  					X2 = Parse_Attribute_Value(*Node)
  				Case "y2"
  					Y2 = Parse_Attribute_Value(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	MovePathCursor(X1, Y1)
  	AddPathLine(X2, Y2)
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Text(*Node)
  	
  	Protected X.d, Y.d, Text.s,transform.s 
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("text", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	Text = GetXMLNodeText(*Node)
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "x"
  					X = Parse_Attribute_Value(*Node)
  				Case "y"
  					Y = Parse_Attribute_Value(*Node)
  				Case "transform" 
  				  transform = XMLAttributeValue(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	Protected sfontkey.s = svg()\CurrentStyle\font\FontFamily + svg()\CurrentStyle\font\fontsize + svg()\CurrentStyle\font\fontstyle
  	 	  	
  	If FindMapElement(svg()\fonts\fcache(),sfontkey) 
  	  VectorFont(SVG()\fonts\fcache()\font,svg()\CurrentStyle\font\fontsize) 
  	Else 
  	  AddMapElement(SVG()\fonts\fcache(),sfontkey) 
  	  
  	  Protected fstyle.i 
  	  
  	  If svg()\CurrentStyle\font\fontstyle = "italic" 
  	    fstyle = #PB_Font_Italic 
  	  EndIf  
  	  
  	  Protected ft = LoadFont(#PB_Any,svg()\CurrentStyle\font\FontFamily,svg()\CurrentStyle\font\fontsize,fstyle)
  	  If ft 
  	    SVG()\fonts\fcache()\font = FontID(ft)   
  	  EndIf 
  	  VectorFont(SVG()\fonts\fcache()\font,svg()\CurrentStyle\font\fontsize) 
  	EndIf

  	Rendering()
   	If transform <> "" 
   	  Parse_Attribute_Transform(transform)
   	EndIf 
  	;-problem  x coord is truncated vs requied tx  
   	
   	Debug x  	  
  	Debug "x " + StrD(200 - VectorTextWidth(Text)/2) 
  	Debug "y " + StrD(100 - 100 - VectorTextHeight(Text)/2)
  	  	
  	;MovePathCursor(x,y) ; borkes it up as x and y are truncated 
  	MovePathCursor(200 - VectorTextWidth(Text)/2, 100 - VectorTextHeight(Text)/2) ;yields the same as the test input 
  	DrawVectorText(Text) 
  	 	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Polyline(*Node)
  	Protected Points.s, X.d, Y.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("polyline", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If RegEx\Points = #Null
  		RegEx\Points = CreateRegularExpression(#PB_Any, "\-?\d+\.?\d*|\.\d+")
  	EndIf
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "points"
  					Points = Parse_Attribute_String(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	If ExamineRegularExpression(RegEx\Points, Points)
  		If NextRegularExpressionMatch(RegEx\Points) : X = ValD(RegularExpressionMatchString(RegEx\Points)) : EndIf
  		If NextRegularExpressionMatch(RegEx\Points) : Y = ValD(RegularExpressionMatchString(RegEx\Points)) : EndIf
  		MovePathCursor(X, Y)
  		Repeat
  			If NextRegularExpressionMatch(RegEx\Points) : X = ValD(RegularExpressionMatchString(RegEx\Points)) : Else : Break : EndIf
  			If NextRegularExpressionMatch(RegEx\Points) : Y = ValD(RegularExpressionMatchString(RegEx\Points)) : Else : Break : EndIf
  			AddPathLine(X, Y)
  		ForEver
  	EndIf
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Polygon(*Node)
  	Protected Points.s, X.d, Y.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("polygon", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If RegEx\Points = #Null
  		RegEx\Points = CreateRegularExpression(#PB_Any, "\-?\d+\.?\d*")
  	EndIf
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "points"
  					Points = Parse_Attribute_String(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	If ExamineRegularExpression(RegEx\Points, Points)
  		If NextRegularExpressionMatch(RegEx\Points) : X = ValD(RegularExpressionMatchString(RegEx\Points)) : EndIf
  		If NextRegularExpressionMatch(RegEx\Points) : Y = ValD(RegularExpressionMatchString(RegEx\Points)) : EndIf
  		MovePathCursor(X, Y)
  		Repeat
  			If NextRegularExpressionMatch(RegEx\Points) : X = ValD(RegularExpressionMatchString(RegEx\Points)) : Else : Break : EndIf
  			If NextRegularExpressionMatch(RegEx\Points) : Y = ValD(RegularExpressionMatchString(RegEx\Points)) : Else : Break : EndIf
  			AddPathLine(X, Y)
  		ForEver
  		ClosePath()
  	EndIf
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Path(*Node)
  	
  	Protected Path.s
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("path", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "d"
  					Path = Parse_Attribute_String(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	AddPathSegments(Path)
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Definition(*Node)
  	
  	Protected *ChildNode
  	
  	*ChildNode = ChildXMLNode(*Node)
  	While *ChildNode
  		Draw(*ChildNode)
  		*ChildNode = NextXMLNode(*ChildNode)
  	Wend
  	
  EndProcedure
  
  Procedure   Draw_Style(*Node)
  	Protected StyleSheet.s
  	Protected Element.s, Class.s, Style.s
  	
  	If RegEx\Definition = #Null
  		RegEx\Definition = CreateRegularExpression(#PB_Any, "(\w*)(?:\.(\w+))?\s*\{([^\}]*)\}")
  	EndIf
  	
  	If ChildXMLNode(*Node)
  		StyleSheet = GetXMLNodeText(ChildXMLNode(*Node))
  	Else
  		StyleSheet = GetXMLNodeText(*Node)
  	EndIf
  	
  	If ExamineRegularExpression(RegEx\Definition, StyleSheet)
  		While NextRegularExpressionMatch(RegEx\Definition)
  			Element = LCase(RegularExpressionGroup(RegEx\Definition, 1))
  			Class   = LCase(RegularExpressionGroup(RegEx\Definition, 2))
  			Style   = RegularExpressionGroup(RegEx\Definition, 3)
  			SVG()\StyleSheet\Element(Element)\Class(Class)\Style = Style
  		Wend
  	EndIf
  	
  EndProcedure
  
  Procedure   Draw(*Node, Indent.i=0)
  	Protected *ChildNode
  	
  	If XMLNodeType(*Node) = #PB_XML_Normal

  		Select LCase(GetXMLNodeName(*Node))
  			Case "circle"
  				Draw_Circle(*Node)
  			Case "ellipse"
  				Draw_Ellipse(*Node)
  			Case "rect"
  				Draw_Rectangle(*Node)
  			Case "line"
  				Draw_Line(*Node)
  			Case "polyline"
  				Draw_Polyline(*Node)
  			Case "polygon"
  				Draw_Polygon(*Node)
  			Case "path"
  				Draw_Path(*Node)
  			Case "text"
  				Draw_Text(*Node)
  			Case "g"
  				Draw_Group(*Node, Indent)
  			Case "defs"
  				Draw_Definition(*Node)
  			Case "style"
  			  Draw_Style(*Node)
  			Default
  				*ChildNode = ChildXMLNode(*Node)
  				While *ChildNode
  					Draw(*ChildNode, Indent + 1)
  					*ChildNode = NextXMLNode(*ChildNode)
  				Wend
  		EndSelect
  		
  	EndIf
  	
  EndProcedure
  
  Procedure   DrawSVGImage_(XML.i, OffSetX.d=0, OffsetY.d=0)
    Define.s Width$, Height$
    Define   *MainNode
    
  	With SVG()\CurrentStyle
  		\Opacity          = 1.0
  		\Fill             = $000000
  		\FillRule         = #PB_Path_Winding
  		\FillOpacity      = 1.0
  		\Stroke           = -1
  		\StrokeWidth      = 1.0
  		\StrokeLineCap    = #PB_Path_Default
  		\StrokeLineJoin   = #PB_Path_Default
  		FreeArray(\StrokeDashArray())
  		\StrokeDashOffset = 0.0
  		\StrokeOpacity    = 1.0
  	EndWith

  	If IsXML(XML)

  	  *MainNode = MainXMLNode(XML)  
  	  If *MainNode
  	     
      	BeginVectorLayer()
      	
      	If SVG()\ViewBox\State
      		ScaleCoordinates(SVG()\Image\Width / SVG()\ViewBox\Width, SVG()\Image\Height / SVG()\ViewBox\Height)
      		TranslateCoordinates(-SVG()\ViewBox\X + OffSetX, -SVG()\ViewBox\Y + OffsetY)
      		AddPathBox(SVG()\ViewBox\X, SVG()\ViewBox\Y, SVG()\ViewBox\Width, SVG()\ViewBox\Height)
      		ClipPath()
      	Else
      	  ScaleCoordinates(SVG()\Image\Width / SVG()\Width, SVG()\Image\Height / SVG()\Height)
      		AddPathBox(0, 0, SVG()\Width, SVG()\Height)
      		ClipPath()
      	EndIf
  	 
    	  Draw(*MainNode)

    	  EndVectorLayer()
    	  
    	EndIf
    	
  	EndIf
  	
  EndProcedure
  
  ;- __________ Tools __________  

  Procedure ParseViewBox(XML.i)
    Define.s viewBox$
    Define *MainNode
    
    If IsXML(XML)

    	*MainNode = MainXMLNode(XML)
    	If *MainNode
    	  
    	  viewBox$ = GetXMLAttribute(*MainNode, "viewBox")
    	  If viewBox$
    	    SVG()\ViewBox\X      = ValD(StringField(viewBox$, 1, " "))
    	    SVG()\ViewBox\Y      = ValD(StringField(viewBox$, 2, " "))
    	    SVG()\ViewBox\Width  = ValD(StringField(viewBox$, 3, " "))
    	    SVG()\ViewBox\Height = ValD(StringField(viewBox$, 4, " "))
    	    SVG()\Width          = Parse_Attribute_Length(GetXMLAttribute(*MainNode, "width"),  SVG()\ViewBox\Width)
    	    SVG()\Height         = Parse_Attribute_Length(GetXMLAttribute(*MainNode, "height"), SVG()\ViewBox\Height)
    	    If SVG()\Width = 0 Or SVG()\Height = 0
    	      SVG()\Width  = SVG()\ViewBox\Width
    	      SVG()\Height = SVG()\ViewBox\Height
    	    EndIf  
    	    SVG()\ViewBox\State  = #True
    	  Else
    	    SVG()\Width          = Parse_Attribute_Length(GetXMLAttribute(*MainNode, "width"))
    	    SVG()\Height         = Parse_Attribute_Length(GetXMLAttribute(*MainNode, "height"))
    	    SVG()\ViewBox\State  = #False
    	  EndIf

  		EndIf
  		
    EndIf
    
  EndProcedure
 
  Procedure AdjustSizeRatio()
    Define.f Factor, wFactor, hFactor
    
    If SVG()\Width = SVG()\Height
  
      If SVG()\Image\Height > SVG()\Image\Width
        SVG()\Image\Height = SVG()\Image\Width
      ElseIf SVG()\Image\Height < SVG()\Image\Width
        SVG()\Image\Width = SVG()\Image\Height
      EndIf

    Else
      
      wFactor = SVG()\Image\Width / SVG()\Width
      hFactor = SVG()\Image\Height/ SVG()\Height

      If hFactor > wFactor
        Factor = SVG()\Height / SVG()\Width
        SVG()\Image\Height = SVG()\Image\Width * Factor
      ElseIf hFactor < wFactor
        Factor = SVG()\Width / SVG()\Height
        SVG()\Image\Width = SVG()\Image\Height * Factor
      EndIf
 
    EndIf

  EndProcedure
  
  ;- ==========================================================================
  ;-   Module - Declared Procedures
  ;- ========================================================================== 
  
  Procedure   DrawVector(Image.i, Width.d=0, Height.d=0, Flags.i=#False)
    ; Flags: #Proportional / #ForceSizeRatio
    Define.f Factor, wFactor, hFactor, OffSetX, OffSetY
    Define.i XML
    
    If FindMapElement(SVG(), Str(Image))

      SVG()\Image\Width  = Width
      SVG()\Image\Height = Height
      
      If Flags & #ForceSizeRatio = #False
        If SVG()\Flags & #Proportional Or Flags & #Proportional
          
          AdjustSizeRatio()
          
          If SVG()\Flags & #CenterImage Or Flags & #CenterImage
            OffSetX = (Width  - SVG()\Image\Width)  / 2
            OffSetY = (Height - SVG()\Image\Height) / 2 
          EndIf
          
        EndIf  
      EndIf
      
      XML = ParseXML(#PB_Any, SVG()\strgXML)
      If IsXML(XML)
        
        DrawSVGImage_(XML, OffSetX, OffSetY)

        FreeXML(XML)
      EndIf   

    EndIf
    
  EndProcedure
  
  
  Procedure.i Resize(Image.i, Width.d, Height.d, Flags.i=#False)
    ; Flags: #Proportional / #ForceSizeRatio
    Define.f Factor, wFactor, hFactor
    Define.i XML
    
    If FindMapElement(SVG(), Str(Image))
      
      If SVG()\Image\PB_Any 
        ; An image loaded with #PB_Any must be freed and recreated
        If IsImage(Image)
          If Flags & #KeepImage = #False : FreeImage(Image) : EndIf
        EndIf
        Image = CreateImage(#PB_Any, Width, Height, SVG()\Image\Depth, #PB_Image_Transparent)
      Else  
        CreateImage(Image, Width, Height, SVG()\Image\Depth, #PB_Image_Transparent)
      EndIf
    
      If IsImage(Image)
        
        SVG()\Image\Width  = dpiX(Width)
        SVG()\Image\Height = dpiY(Height)
        
        If Flags & #ForceSizeRatio = #False
          If SVG()\Flags & #Proportional Or Flags  & #Proportional
            AdjustSizeRatio()
          EndIf  
        EndIf
        
        XML = ParseXML(#PB_Any, SVG()\strgXML)
        If IsXML(XML)
          
          If StartVectorDrawing(ImageVectorOutput(Image))

            If SVG()\Image\BackColor <> #PB_Image_Transparent
              VectorSourceColor(SVG()\Image\BackColor)
              FillVectorOutput()
            EndIf
            
        		If IsFont(SVG()\Font) : VectorFont(FontID(SVG()\Font)) : EndIf 
        		
        		DrawSVGImage_(XML)
        		
            StopVectorDrawing()
          EndIf
          
          FreeXML(XML)
        EndIf   

      EndIf

    EndIf
    
    ProcedureReturn Image
  EndProcedure
  
  
  Procedure.i Catch(Image.i, svgXML.i, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)  
    ; Flags:     #Proportional / #CreateNoImage
    ; BackColor: #PB_Image_Transparent
    Define.f Factor, wFactor, hFactor
    Define.i Result, Depth, PB_Any, OffSetX, OffSetY

    If IsXML(svgXML)
      
      ;{ Create Image
      If #PB_Image_Transparent Or Alpha(BackColor)
        Depth  = 32
        Result = CreateImage(Image, 16, 16, 32, #PB_Image_Transparent)
      Else
        Depth  = 24
        Result = CreateImage(Image, 16, 16, 24)
      EndIf 
      
      If Image = #PB_Any
        Image = Result
        PB_Any = #True
      EndIf ;}

      If AddMapElement(SVG(), Str(Image))

        SVG()\strgXML = ComposeXML(svgXML)
        SVG()\Font    = Font
        SVG()\Flags   = Flags

        SVG()\Image\Num    = Image
        SVG()\Image\Width  = Width
        SVG()\Image\Height = Height
        SVG()\Image\Depth  = Depth
        SVG()\Image\PB_Any = PB_Any
      
        ;{ ---- Get Image Data -----
        If StartVectorDrawing(ImageVectorOutput(Image))
          
          SVG()\ResX = VectorResolutionX()
          SVG()\ResY = VectorResolutionY()
          
          ParseViewBox(svgXML)
          
          StopVectorDrawing()
        EndIf ;}
        
        If Flags & #CreateNoImage
          If IsImage(Image) : FreeImage(Image) : EndIf
        EndIf 
        
        If SVG()\Image\Width = 0 Or SVG()\Image\Height = 0
          SVG()\Image\Width  = SVG()\Width
          SVG()\Image\Height = SVG()\Height
        EndIf
        
        Width  = dpiX(SVG()\Image\Width)
        Height = dpiY(SVG()\Image\Height)
        
        If Flags & #Proportional
          AdjustSizeRatio()
        EndIf  
        
        SVG()\Image\Width  = dpiX(SVG()\Image\Width)
        SVG()\Image\Height = dpiY(SVG()\Image\Height)
        
        If IsImage(Image) And Flags & #CreateNoImage = #False
        
          If SVG()\Image\Width And SVG()\Image\Height
            
            If Flags & #CenterImage
              
              ResizeImage(Image, Width, Height, #PB_Image_Raw)

              OffSetX = (Width  - SVG()\Image\Width)  / 2
              OffSetY = (Height - SVG()\Image\Height) / 2 
              
            Else  
              ResizeImage(Image, SVG()\Image\Width, SVG()\Image\Height, #PB_Image_Raw)
            EndIf
            
          EndIf
          
          If StartVectorDrawing(ImageVectorOutput(Image))
          
            If BackColor = #PB_Image_Transparent ;{ Backcolor
              SVG()\Image\BackColor = #PB_Image_Transparent
            Else  
              
              If Alpha(BackColor) = #False
                SVG()\Image\BackColor = BackColor | 255 << 24
              Else
                SVG()\Image\BackColor = BackColor
              EndIf
 
              VectorSourceColor(SVG()\Image\BackColor)
              FillVectorOutput()
              
            EndIf ;}
            
        		If IsFont(SVG()\Font) : VectorFont(FontID(SVG()\Font)) : EndIf 
        		
        		DrawSVGImage_(svgXML, OffSetX, OffSetY)
        		
            StopVectorDrawing()
          EndIf
          
        EndIf
        
      EndIf

      FreeXML(svgXML)
    EndIf
    
    ProcedureReturn Result
  EndProcedure
 
  Procedure.i CatchString(Image.i, StringSVG.s, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
    ; Flags:     #Proportional / #CreateNoImage
    ; BackColor: #PB_Image_Transparent
    Define.i XML, Result
    
    XML = ParseXML(#PB_Any, StringSVG)
    If IsXML(XML)
      Result = Catch(Image, XML, Flags, BackColor, Width, Height, Font)  
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  Procedure.i CatchMemory(Image.i, *MemorySVG, SizeSVG.i, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
    ; Flags:     #Proportional / #CreateNoImage
    ; BackColor: #PB_Image_Transparent
    Define.i XML, Result
    
    XML = CatchXML(#PB_Any, *MemorySVG, SizeSVG)
    If IsXML(XML)
      Result = Catch(Image, XML, Flags, BackColor, Width, Height, Font)  
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  
  Procedure.i Load(Image.i, File.s, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
    ; Flags:     #Proportional / #CreateNoImage
    ; BackColor: #PB_Image_Transparent
    Define.i XML, Result

    XML = LoadXML(#PB_Any, File)
    If IsXML(XML)
      Result = Catch(Image, XML, Flags, BackColor, Width, Height, Font)  
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  
  CompilerIf Defined(Icon, #PB_Module)
    
    Procedure.i CreateIcon(Image.i, File.s, Flags.i=#False)
      Define.i d, XML, Size, Icon, Image
      
      If FindMapElement(SVG(), Str(Image))

        If Flags & #Windows And Flags & #MacOS
          Icon = Icon::Create(#PB_Any, File, Icon::#Windows|Icon::#MacOS)
        ElseIf Flags & #MacOS
          Icon = Icon::Create(#PB_Any, File, Icon::#MacOS)
        Else
          Icon = Icon::Create(#PB_Any, File, Icon::#Windows)
        EndIf  

        If Icon
          
          Restore IconSize
          
          For d=1 To 9
            Read.i Size
            Image = SVG::Resize(Image, Size, Size, SVG::#Proportional|SVG::#CenterImage)
            Icon::AddImage(Icon, Image)
          Next
          
          Icon::Close(Icon)  
        EndIf
      
      EndIf
      
      ProcedureReturn  Image
    EndProcedure
    
    DataSection
      IconSize:
      Data.i 16, 24, 32, 48, 64, 128, 256, 512, 1024
    EndDataSection

  CompilerEndIf  

EndModule

;- ========  Module - Example ========

CompilerIf #PB_Compiler_IsMainFile
    
  LoadFont(0,"Impact",24,#PB_Font_Italic) 
  txt$ = "Hello" 
  If StartVectorDrawing(SvgVectorOutput("test.svg", 400, 200))  ;use pb commands to write svg 
    VectorFont(FontID(0),36)  
    AddPathBox(50, 50, 200, 50)
    AddPathBox(150, 75, 200, 50)
    VectorSourceColor(RGBA(255, 128, 0, 255))
    StrokePath(10)
    MovePathCursor(200 - VectorTextWidth(Txt$)/2, 100 - VectorTextHeight(Txt$)/2)
    VectorSourceColor(RGBA(0,255, 0, 255))
    DrawVectorText(txt$)
    
    VectorFont(FontID(0), 25)
    VectorSourceColor(RGBA(0, 0, 0, 80))
    Text$ = "The quick brown fox jumps over the lazy dog"
    
    For i = 1 To 6
      MovePathCursor(200 - VectorTextWidth(Text$)/2, 100 - VectorTextHeight(Text$)/2)
      DrawVectorText(Text$)
      RotateCoordinates(200, 100, 30)
    Next i
    
    StopVectorDrawing()
       
  EndIf
    
  OpenFile(0,"test.svg") 
  Global sout.s 
  sout = ReadString(0,#PB_UTF8 | #PB_File_IgnoreEOL,#PB_File_IgnoreEOL)
  Debug sout 
  CloseFile(0) 
  
  ;-run in browser 
  ;RunProgram("test.svg")
    
  img = SVG::Load(#PB_Any, "test.svg", #False, #PB_Image_Transparent)
  If img  
    OpenWindow(0,0,0,ImageWidth(img),ImageHeight(img),"test txt",#PB_Window_SystemMenu | #PB_Window_ScreenCentered) 
    ImageGadget(0,0,0,ImageWidth(img),ImageHeight(img),ImageID(img)) 
    Repeat 
    Until WaitWindowEvent() = #PB_Event_CloseWindow 
  Else 
    MessageRequester("oops","cant find image")
  EndIf   
  
CompilerEndIf 
User avatar
STARGÅTE
Addict
Addict
Posts: 2242
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [Module] SVG - Module (all OS)

Post by STARGÅTE »

I think the regex should be "[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?" without the ^ and $ because it should match all numbers in the transformation.
Then you don't need the loop. But I haven't tested it, because I'm not at home.
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
User avatar
idle
Always Here
Always Here
Posts: 5964
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: [Module] SVG - Module (all OS)

Post by idle »

STARGÅTE wrote: Sat Sep 27, 2025 12:55 pm I think the regex should be "[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?" without the ^ and $ because it should match all numbers in the transformation.
Then you don't need the loop. But I haven't tested it, because I'm not at home.
That didin't make any difference the expression is matching but not extracting.

solved it! the Y coordinate was off by the text height.
User avatar
idle
Always Here
Always Here
Posts: 5964
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: [Module] SVG - Module (all OS)

Post by idle »

Please test on linux and mac

Code: Select all

;/ ===============================
;/ =    UseSVGImageModule.pbi    =
;/ ===============================
;/
;/ [ PB V5.7x / 64Bit / All OS / DPI ]
;/ 
;/ based on "VectorGraphic.pbi" of STARGÅTE
;/ (https://www.purebasic.fr/german/viewtopic.php?f=4&t=22593)
;/ 
;/ © 2020 Thorsten Hoeppner (04/2020)
;/ 
; Last Update: 28.09.2025 
; idle 
; Added font cache for font size styles 
; fixed regular expression to match matrix floats 2.98023e-08, but still problem extracting
; fixed text coordinates but might be windows specific

; Update: 20.04.2020
; Added: CreateIcon()
; Added: Center proportional resized image (#CenterImage)
;

;{ ===== 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.
;}

;{ _____ Module - Commands _____

; SVG::Load()        - similar to LoadImage()
; SVG::Resize()      - similar to ResizeImage()
;
; SVG::Catch()       - uses an already loaded SVG file (XML) 
; SVG::CatchMemory() - uses a SVG file (XML) loaded into memory
; SVG::CatchString() - reads the SVG data from a string
;
; SVG::DrawVector()  - converts a loaded SVG file to 'VectorDrawing' commands
;
; SVG::CreateIcon()  - create an icon file from an SVG image

;}

; ***** If CreateIcon is not required, this line can be commented out. *****
;CompilerIf Not Defined(Icon, #PB_Module) : XIncludeFile "UseIconModule.pbi" : CompilerEndIf

DeclareModule SVG
  
  #Version  = 20042000
  #ModuleEx = 20041700
  
  EnumerationBinary ; Image Flags
    #Proportional    ; scales the image proportionally
    #ForceSizeRatio  ; uses the specified aspect ratio (even if #Proportional has been set)
    #KeepImage       ; keeps the previous image after resizing (only if #PB_Any was used)
    #CenterImage     ; 
    #CreateNoImage   ; loads the SVG file without creating an image [=> 'SVG::DrawVector()']
    #Windows         ; Icon: *.ico
    #MacOS           ; Icon: *.icns
  EndEnumeration

  ;- ===========================================================================
  ;-   DeclareModule
  ;- ===========================================================================

  Declare.i Load(Image.i, File.s, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0.0, Height.d=0.0, Font.i=#False)
  Declare.i Catch(Image.i, svgXML.i, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False) 
  Declare.i CatchMemory(Image.i, *MemorySVG, SizeSVG.i, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
  Declare.i CatchString(Image.i, StringSVG.s, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
  Declare.i Resize(Image.i, Width.d, Height.d, Flags.i=#False)
  Declare   DrawVector(Image.i, Width.d=0, Height.d=0, Flags.i=#False)
  
  CompilerIf Defined(Icon, #PB_Module)
    Declare.i CreateIcon(Image.i, File.s, Flags.i=#False)
  CompilerEndIf  
  
EndDeclareModule

Module SVG
  
  EnableExplicit
  
  ;- ============================================================================
  ;-   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 font_Structure 
    font.i
    FontFamily.s 
    fontsize.d
    fontWeight.d 
    fontstyle.s
    decoration.s
  EndStructure   
    
  Structure Font_Cache 
    Map fcache.font_Structure(65)
  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
  	font.font_Structure 
  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
  	
  	fonts.Font_Cache
  	
    CurrentStyle.Style_Structure
  	
  	StyleSheet.StyleSheet_Structure
  	ViewBox.ViewBox_Structure
  	Image.Image_Structure
  	
  EndStructure ;}
  Global NewMap SVG.SVG_Structure()
  
  ;- ============================================================================
  ;-   Module - Internal
  ;- ============================================================================ 
  
  Procedure.f dpiX(Num.i)
	  If Num > 0  
	    ProcedureReturn DesktopScaledX(Num)
	  EndIf   
	EndProcedure

	Procedure.f dpiY(Num.i)
	  If Num > 0  
	    ProcedureReturn DesktopScaledY(Num)
	  EndIf  
	EndProcedure
	
	
  Declare Draw(*Node, Indent.i=0)
  
  
  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
  
  ;- __________ 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,*vx.Double=0,*vy.Double=0)
  	; 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+)(?:[eE][+-]?\d+)?",#PB_RegularExpression_NoCase);"\-?\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
  					  Protected len,t=1,ct
  					  Protected str.s = RegularExpressionGroup(RegEx\Command, 2)	
  					  Protected *char.Unicode = @str 
  					  Protected *pos 
  					  While *char\u <> 0 
  					    *pos = *char  
  					    While *char\u <> ',' 
  					      If (*char\u >= '0' And *char\u <= '9')  
  					        len+1
  					        t=0
  					      ElseIf (*char\u = 'e' Or *char\u = 'E') 
  					        len+1
  					        t=0
  					      ElseIf (*char\u = '.' Or *char\u = '-' Or *char\u = '+')   
  					        len+1
  					        t=0
  					      ElseIf *char\u = 0  
  					        *char+2 
  					        Break 
  					      ElseIf t 
  					        *pos+2
  					      EndIf
  					      
  					      *char+2 
  					    Wend 
  					    If len 
  					      Select ct 
  					        Case 0
  					          A = ValD(PeekS(*pos,len)) 
  					        Case 1
  					          B =  ValD(PeekS(*pos,len))
  					        Case 2
  					          C =  ValD(PeekS(*pos,len))
  					        Case 3
  					          D = ValD(PeekS(*pos,len))
  					        Case 4 
  					          E =  ValD(PeekS(*pos,len))
  					        Case 5 
  					          F =  ValD(PeekS(*pos,len))
  					      EndSelect    
  					      ct+1 
  					      len=0
  					      t=1
  					    EndIf
  					    *char+2 
  					  Wend 
  						  						
;   						Debug RegularExpressionGroup(RegEx\Command, 2)						
;   						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 "text-decoration"
  		    \font\decoration  = Parse_Attribute_String(*Node)
  		  Case "font-family" 
  		     \font\FontFamily = Parse_Attribute_String(*Node) 
  		  Case "font-size" 
  		     \font\fontsize =  Parse_Attribute_Value(*Node) 
  		  Case "font-weight"  
  		     \font\fontWeight =  Parse_Attribute_Value(*Node)
  		   Case "font-style" 
  		     \font\fontstyle = Parse_Attribute_String(*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
  
  ;- __________ Drawing __________
  
  Procedure   StyleSheets(Element.s="", Class.s="")
  	
  	With SVG()\CurrentStyle
  		
  		If Element And FindMapElement(SVG()\StyleSheet\Element(), Element) 
  			If FindMapElement(SVG()\StyleSheet\Element()\Class(), "")
  				Parse_Attribute_Style(SVG()\StyleSheet\Element()\Class()\Style)
  			EndIf
  			If Class And FindMapElement(SVG()\StyleSheet\Element()\Class(), Class)
  				Parse_Attribute_Style(SVG()\StyleSheet\Element()\Class()\Style)
  			EndIf
  		EndIf
  		If Class And FindMapElement(SVG()\StyleSheet\Element(), "") And FindMapElement(SVG()\StyleSheet\Element()\Class(), Class)
  			Parse_Attribute_Style(SVG()\StyleSheet\Element()\Class()\Style)
  		EndIf
  		
  	EndWith
  	
  EndProcedure
  
  Procedure   Rendering()
  	
  	With SVG()\CurrentStyle
  		
  		If \Opacity = 1.0
  			If \Fill <> -1
  				VectorSourceColor(\Fill | Int(255*\FillOpacity)<<24)
  				FillPath(\FillRule|#PB_Path_Preserve)
  			EndIf
  			If \Stroke <> -1
  				VectorSourceColor(\Stroke | Int(255*\StrokeOpacity)<<24)
  				If ArraySize(\StrokeDashArray()) <> -1
  					CustomDashPath(\StrokeWidth, \StrokeDashArray(), \StrokeLineCap|\StrokeLineJoin|#PB_Path_Preserve, \StrokeDashOffset)
  				Else
  					StrokePath(\StrokeWidth, \StrokeLineCap|\StrokeLineJoin|#PB_Path_Preserve)
  				EndIf
  			EndIf
  		ElseIf \Opacity > 0.0
  			BeginVectorLayer(255*\Opacity)
  			If \Fill <> -1
  				VectorSourceColor(\Fill | Int(255*\FillOpacity)<<24)
  				FillPath(\FillRule|#PB_Path_Preserve)
  			EndIf
  			If \Stroke <> -1
  				VectorSourceColor(\Stroke | Int(255*\StrokeOpacity)<<24)
  				If ArraySize(\StrokeDashArray()) <> -1
  					CustomDashPath(\StrokeWidth, \StrokeDashArray(), \StrokeLineCap|\StrokeLineJoin|#PB_Path_Preserve, \StrokeDashOffset)
  				Else
  					StrokePath(\StrokeWidth, \StrokeLineCap|\StrokeLineJoin|#PB_Path_Preserve)
  				EndIf
  			EndIf
  			EndVectorLayer()
  		EndIf
  		
  		ResetPath()
  	EndWith
  	
  EndProcedure
  
  Procedure   Draw_Group(*Node, Indent.i=0)
  	
  	Protected *ChildNode
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	BeginVectorLayer()
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "transform"
  					Parse_Attribute_Transform(XMLAttributeValue(*Node))
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	*ChildNode = ChildXMLNode(*Node)
  	While *ChildNode
  		Draw(*ChildNode, Indent+1)
  		*ChildNode = NextXMLNode(*ChildNode)
  	Wend
  	
  	EndVectorLayer()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Circle(*Node)
  	
  	Protected CX.d, CY.d, R.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("circle", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "cx"
  					CX = Parse_Attribute_Value(*Node)
  				Case "cy"
  					CY = Parse_Attribute_Value(*Node)
  				Case "r"
  					R = Parse_Attribute_Value(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	AddPathCircle(CX, CY, R)
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Ellipse(*Node)
  	
  	Protected CX.d, CY.d, RX.d, RY.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("ellipse", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "cx"
  					CX = Parse_Attribute_Value(*Node)
  				Case "cy"
  					CY = Parse_Attribute_Value(*Node)
  				Case "rx"
  					RX = Parse_Attribute_Value(*Node)
  				Case "ry"
  					RY = Parse_Attribute_Value(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	AddPathEllipse(CX, CY, RX, RY)
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Rectangle(*Node)
  	
  	Protected Width.d, Height.d, X.d=0.0, Y.d=0.0, RX.d=0.0, RY.d=0.0
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("rect", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "x"
  					X = Parse_Attribute_Value(*Node)
  				Case "y"
  					Y = Parse_Attribute_Value(*Node)
  				Case "width"
  					Width = Parse_Attribute_Value(*Node)
  				Case "height"
  					Height = Parse_Attribute_Value(*Node)
  				Case "rx"
  					RX = Parse_Attribute_Value(*Node)
  				Case "ry"
  					RY = Parse_Attribute_Value(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	If RX = 0.0 And RY = 0.0
  		AddPathBox(X, Y, Width, Height)
  	Else
  		If RX = 0.0 : RX = RY : EndIf
  		If RY = 0.0 : RY = RX : EndIf
  		If RX > Width/2 : RX = Width/2 : EndIf
  		If RY > Height/2 : RY = Height/2 : EndIf
  		If Width < 0 :  X + Width : Width = -Width : EndIf
  		If Height < 0 : Y + Height : Height = -Height : EndIf
  		MovePathCursor(X, Y+RY)
  		AddPathEllipse(RX, 0, RX, RY, 180, 270, #PB_Path_Relative)
  		AddPathEllipse(Width-2*RX, RY, RX, RY, 270, 360, #PB_Path_Relative|#PB_Path_Connected)
  		AddPathEllipse(-RX, Height-2*RY, RX, RY, 0, 90, #PB_Path_Relative|#PB_Path_Connected)
  		AddPathEllipse(-Width+2*RX, -RY, RX, RY, 90, 180, #PB_Path_Relative|#PB_Path_Connected)
  		ClosePath()
  	EndIf
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Line(*Node)
  	
  	Protected X1.d, Y1.d, X2.d, Y2.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("line", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "x1"
  					X1 = Parse_Attribute_Value(*Node)
  				Case "y1"
  					Y1 = Parse_Attribute_Value(*Node)
  				Case "x2"
  					X2 = Parse_Attribute_Value(*Node)
  				Case "y2"
  					Y2 = Parse_Attribute_Value(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	MovePathCursor(X1, Y1)
  	AddPathLine(X2, Y2)
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Text(*Node)
  	
  	Protected X.d, Y.d, Text.s,transform.s 
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("text", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	Text = GetXMLNodeText(*Node)
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "x"
  					X = Parse_Attribute_Value(*Node)
  				Case "y"
  					Y = Parse_Attribute_Value(*Node)
  				Case "transform" 
  				  transform = XMLAttributeValue(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	Protected sfontkey.s = svg()\CurrentStyle\font\FontFamily + svg()\CurrentStyle\font\fontsize + svg()\CurrentStyle\font\fontstyle
  	 	  	
  	If FindMapElement(svg()\fonts\fcache(),sfontkey) 
  	  VectorFont(SVG()\fonts\fcache()\font,svg()\CurrentStyle\font\fontsize) 
  	Else 
  	  AddMapElement(SVG()\fonts\fcache(),sfontkey) 
  	  
  	  Protected fstyle.i 
  	  
  	  If svg()\CurrentStyle\font\fontstyle = "italic" 
  	    fstyle | #PB_Font_Italic 
  	  EndIf  
  	  If svg()\CurrentStyle\font\decoration = "underline" 
  	    fstyle | #PB_Font_Underline
  	  EndIf 
  	  If svg()\CurrentStyle\font\decoration = "line-through" 
  	    fstyle | #PB_Font_StrikeOut 
  	  EndIf   
  	  If SVG()\CurrentStyle\font\fontWeight > 400 
  	    fstyle | #PB_Font_Bold 
  	  EndIf   
  	    	  
  	  Protected ft = LoadFont(#PB_Any,svg()\CurrentStyle\font\FontFamily,svg()\CurrentStyle\font\fontsize,fstyle)
  	  If ft 
  	    SVG()\fonts\fcache()\font = FontID(ft)   
  	  EndIf 
  	  VectorFont(SVG()\fonts\fcache()\font,svg()\CurrentStyle\font\fontsize) 
  	EndIf
  	 	
  	Rendering()
   	If transform <> "" 
   	  Parse_Attribute_Transform(transform)
   	EndIf 
    	
  	MovePathCursor(x,y-VectorTextHeight(text))
  	  	  	  	
  	DrawVectorText(Text) 
  	 	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Polyline(*Node)
  	Protected Points.s, X.d, Y.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("polyline", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If RegEx\Points = #Null
  		RegEx\Points = CreateRegularExpression(#PB_Any, "\-?\d+\.?\d*|\.\d+")
  	EndIf
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "points"
  					Points = Parse_Attribute_String(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	If ExamineRegularExpression(RegEx\Points, Points)
  		If NextRegularExpressionMatch(RegEx\Points) : X = ValD(RegularExpressionMatchString(RegEx\Points)) : EndIf
  		If NextRegularExpressionMatch(RegEx\Points) : Y = ValD(RegularExpressionMatchString(RegEx\Points)) : EndIf
  		MovePathCursor(X, Y)
  		Repeat
  			If NextRegularExpressionMatch(RegEx\Points) : X = ValD(RegularExpressionMatchString(RegEx\Points)) : Else : Break : EndIf
  			If NextRegularExpressionMatch(RegEx\Points) : Y = ValD(RegularExpressionMatchString(RegEx\Points)) : Else : Break : EndIf
  			AddPathLine(X, Y)
  		ForEver
  	EndIf
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Polygon(*Node)
  	Protected Points.s, X.d, Y.d
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("polygon", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If RegEx\Points = #Null
  		RegEx\Points = CreateRegularExpression(#PB_Any, "\-?\d+\.?\d*")
  	EndIf
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "points"
  					Points = Parse_Attribute_String(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	If ExamineRegularExpression(RegEx\Points, Points)
  		If NextRegularExpressionMatch(RegEx\Points) : X = ValD(RegularExpressionMatchString(RegEx\Points)) : EndIf
  		If NextRegularExpressionMatch(RegEx\Points) : Y = ValD(RegularExpressionMatchString(RegEx\Points)) : EndIf
  		MovePathCursor(X, Y)
  		Repeat
  			If NextRegularExpressionMatch(RegEx\Points) : X = ValD(RegularExpressionMatchString(RegEx\Points)) : Else : Break : EndIf
  			If NextRegularExpressionMatch(RegEx\Points) : Y = ValD(RegularExpressionMatchString(RegEx\Points)) : Else : Break : EndIf
  			AddPathLine(X, Y)
  		ForEver
  		ClosePath()
  	EndIf
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Path(*Node)
  	
  	Protected Path.s
  	Protected PushedStyle.Style_Structure
  	
  	PushedStyle.Style_Structure = SVG()\CurrentStyle
  	SaveVectorState()
  	
  	StyleSheets("path", Trim(LCase(GetXMLAttribute(*Node, "class"))))
  	
  	If ExamineXMLAttributes(*Node)
  		While NextXMLAttribute(*Node)
  			Select LCase(XMLAttributeName(*Node))
  				Case "d"
  					Path = Parse_Attribute_String(*Node)
  				Default
  					Parse_Attribute_Presentation(*Node)
  			EndSelect
  		Wend
  	EndIf
  	
  	AddPathSegments(Path)
  	
  	Rendering()
  	
  	RestoreVectorState()
  	SVG()\CurrentStyle = PushedStyle
  	
  EndProcedure
  
  Procedure   Draw_Definition(*Node)
  	
  	Protected *ChildNode
  	
  	*ChildNode = ChildXMLNode(*Node)
  	While *ChildNode
  		Draw(*ChildNode)
  		*ChildNode = NextXMLNode(*ChildNode)
  	Wend
  	
  EndProcedure
  
  Procedure   Draw_Style(*Node)
  	Protected StyleSheet.s
  	Protected Element.s, Class.s, Style.s
  	
  	If RegEx\Definition = #Null
  		RegEx\Definition = CreateRegularExpression(#PB_Any, "(\w*)(?:\.(\w+))?\s*\{([^\}]*)\}")
  	EndIf
  	
  	If ChildXMLNode(*Node)
  		StyleSheet = GetXMLNodeText(ChildXMLNode(*Node))
  	Else
  		StyleSheet = GetXMLNodeText(*Node)
  	EndIf
  	
  	If ExamineRegularExpression(RegEx\Definition, StyleSheet)
  		While NextRegularExpressionMatch(RegEx\Definition)
  			Element = LCase(RegularExpressionGroup(RegEx\Definition, 1))
  			Class   = LCase(RegularExpressionGroup(RegEx\Definition, 2))
  			Style   = RegularExpressionGroup(RegEx\Definition, 3)
  			SVG()\StyleSheet\Element(Element)\Class(Class)\Style = Style
  		Wend
  	EndIf
  	
  EndProcedure
  
  Procedure   Draw(*Node, Indent.i=0)
  	Protected *ChildNode
  	
  	If XMLNodeType(*Node) = #PB_XML_Normal

  		Select LCase(GetXMLNodeName(*Node))
  			Case "circle"
  				Draw_Circle(*Node)
  			Case "ellipse"
  				Draw_Ellipse(*Node)
  			Case "rect"
  				Draw_Rectangle(*Node)
  			Case "line"
  				Draw_Line(*Node)
  			Case "polyline"
  				Draw_Polyline(*Node)
  			Case "polygon"
  				Draw_Polygon(*Node)
  			Case "path"
  				Draw_Path(*Node)
  			Case "text"
  				Draw_Text(*Node)
  			Case "g"
  				Draw_Group(*Node, Indent)
  			Case "defs"
  				Draw_Definition(*Node)
  			Case "style"
  			  Draw_Style(*Node)
  			Default
  				*ChildNode = ChildXMLNode(*Node)
  				While *ChildNode
  					Draw(*ChildNode, Indent + 1)
  					*ChildNode = NextXMLNode(*ChildNode)
  				Wend
  		EndSelect
  		
  	EndIf
  	
  EndProcedure
  
  Procedure   DrawSVGImage_(XML.i, OffSetX.d=0, OffsetY.d=0)
    Define.s Width$, Height$
    Define   *MainNode
    
  	With SVG()\CurrentStyle
  		\Opacity          = 1.0
  		\Fill             = $000000
  		\FillRule         = #PB_Path_Winding
  		\FillOpacity      = 1.0
  		\Stroke           = -1
  		\StrokeWidth      = 1.0
  		\StrokeLineCap    = #PB_Path_Default
  		\StrokeLineJoin   = #PB_Path_Default
  		FreeArray(\StrokeDashArray())
  		\StrokeDashOffset = 0.0
  		\StrokeOpacity    = 1.0
  	EndWith

  	If IsXML(XML)

  	  *MainNode = MainXMLNode(XML)  
  	  If *MainNode
  	     
      	BeginVectorLayer()
      	
      	If SVG()\ViewBox\State
      		ScaleCoordinates(SVG()\Image\Width / SVG()\ViewBox\Width, SVG()\Image\Height / SVG()\ViewBox\Height)
      		TranslateCoordinates(-SVG()\ViewBox\X + OffSetX, -SVG()\ViewBox\Y + OffsetY)
      		AddPathBox(SVG()\ViewBox\X, SVG()\ViewBox\Y, SVG()\ViewBox\Width, SVG()\ViewBox\Height)
      		ClipPath()
      	Else
      	  ScaleCoordinates(SVG()\Image\Width / SVG()\Width, SVG()\Image\Height / SVG()\Height)
      		AddPathBox(0, 0, SVG()\Width, SVG()\Height)
      		ClipPath()
      	EndIf
  	 
    	  Draw(*MainNode)

    	  EndVectorLayer()
    	  
    	EndIf
    	
  	EndIf
  	
  EndProcedure
  
  ;- __________ Tools __________  

  Procedure ParseViewBox(XML.i)
    Define.s viewBox$
    Define *MainNode
    
    If IsXML(XML)

    	*MainNode = MainXMLNode(XML)
    	If *MainNode
    	  
    	  viewBox$ = GetXMLAttribute(*MainNode, "viewBox")
    	  If viewBox$
    	    SVG()\ViewBox\X      = ValD(StringField(viewBox$, 1, " "))
    	    SVG()\ViewBox\Y      = ValD(StringField(viewBox$, 2, " "))
    	    SVG()\ViewBox\Width  = ValD(StringField(viewBox$, 3, " "))
    	    SVG()\ViewBox\Height = ValD(StringField(viewBox$, 4, " "))
    	    SVG()\Width          = Parse_Attribute_Length(GetXMLAttribute(*MainNode, "width"),  SVG()\ViewBox\Width)
    	    SVG()\Height         = Parse_Attribute_Length(GetXMLAttribute(*MainNode, "height"), SVG()\ViewBox\Height)
    	    If SVG()\Width = 0 Or SVG()\Height = 0
    	      SVG()\Width  = SVG()\ViewBox\Width
    	      SVG()\Height = SVG()\ViewBox\Height
    	    EndIf  
    	    SVG()\ViewBox\State  = #True
    	  Else
    	    SVG()\Width          = Parse_Attribute_Length(GetXMLAttribute(*MainNode, "width"))
    	    SVG()\Height         = Parse_Attribute_Length(GetXMLAttribute(*MainNode, "height"))
    	    SVG()\ViewBox\State  = #False
    	  EndIf

  		EndIf
  		
    EndIf
    
  EndProcedure
 
  Procedure AdjustSizeRatio()
    Define.f Factor, wFactor, hFactor
    
    If SVG()\Width = SVG()\Height
  
      If SVG()\Image\Height > SVG()\Image\Width
        SVG()\Image\Height = SVG()\Image\Width
      ElseIf SVG()\Image\Height < SVG()\Image\Width
        SVG()\Image\Width = SVG()\Image\Height
      EndIf

    Else
      
      wFactor = SVG()\Image\Width / SVG()\Width
      hFactor = SVG()\Image\Height/ SVG()\Height

      If hFactor > wFactor
        Factor = SVG()\Height / SVG()\Width
        SVG()\Image\Height = SVG()\Image\Width * Factor
      ElseIf hFactor < wFactor
        Factor = SVG()\Width / SVG()\Height
        SVG()\Image\Width = SVG()\Image\Height * Factor
      EndIf
 
    EndIf

  EndProcedure
  
  ;- ==========================================================================
  ;-   Module - Declared Procedures
  ;- ========================================================================== 
  
  Procedure   DrawVector(Image.i, Width.d=0, Height.d=0, Flags.i=#False)
    ; Flags: #Proportional / #ForceSizeRatio
    Define.f Factor, wFactor, hFactor, OffSetX, OffSetY
    Define.i XML
    
    If FindMapElement(SVG(), Str(Image))

      SVG()\Image\Width  = Width
      SVG()\Image\Height = Height
      
      If Flags & #ForceSizeRatio = #False
        If SVG()\Flags & #Proportional Or Flags & #Proportional
          
          AdjustSizeRatio()
          
          If SVG()\Flags & #CenterImage Or Flags & #CenterImage
            OffSetX = (Width  - SVG()\Image\Width)  / 2
            OffSetY = (Height - SVG()\Image\Height) / 2 
          EndIf
          
        EndIf  
      EndIf
      
      XML = ParseXML(#PB_Any, SVG()\strgXML)
      If IsXML(XML)
        
        DrawSVGImage_(XML, OffSetX, OffSetY)

        FreeXML(XML)
      EndIf   

    EndIf
    
  EndProcedure
  
  
  Procedure.i Resize(Image.i, Width.d, Height.d, Flags.i=#False)
    ; Flags: #Proportional / #ForceSizeRatio
    Define.f Factor, wFactor, hFactor
    Define.i XML
    
    If FindMapElement(SVG(), Str(Image))
      
      If SVG()\Image\PB_Any 
        ; An image loaded with #PB_Any must be freed and recreated
        If IsImage(Image)
          If Flags & #KeepImage = #False : FreeImage(Image) : EndIf
        EndIf
        Image = CreateImage(#PB_Any, Width, Height, SVG()\Image\Depth, #PB_Image_Transparent)
      Else  
        CreateImage(Image, Width, Height, SVG()\Image\Depth, #PB_Image_Transparent)
      EndIf
    
      If IsImage(Image)
        
        SVG()\Image\Width  = dpiX(Width)
        SVG()\Image\Height = dpiY(Height)
        
        If Flags & #ForceSizeRatio = #False
          If SVG()\Flags & #Proportional Or Flags  & #Proportional
            AdjustSizeRatio()
          EndIf  
        EndIf
        
        XML = ParseXML(#PB_Any, SVG()\strgXML)
        If IsXML(XML)
          
          If StartVectorDrawing(ImageVectorOutput(Image))

            If SVG()\Image\BackColor <> #PB_Image_Transparent
              VectorSourceColor(SVG()\Image\BackColor)
              FillVectorOutput()
            EndIf
            
        		If IsFont(SVG()\Font) : VectorFont(FontID(SVG()\Font)) : EndIf 
        		
        		DrawSVGImage_(XML)
        		
            StopVectorDrawing()
          EndIf
          
          FreeXML(XML)
        EndIf   

      EndIf

    EndIf
    
    ProcedureReturn Image
  EndProcedure
  
  
  Procedure.i Catch(Image.i, svgXML.i, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)  
    ; Flags:     #Proportional / #CreateNoImage
    ; BackColor: #PB_Image_Transparent
    Define.f Factor, wFactor, hFactor
    Define.i Result, Depth, PB_Any, OffSetX, OffSetY

    If IsXML(svgXML)
      
      ;{ Create Image
      If #PB_Image_Transparent Or Alpha(BackColor)
        Depth  = 32
        Result = CreateImage(Image, 16, 16, 32, #PB_Image_Transparent)
      Else
        Depth  = 24
        Result = CreateImage(Image, 16, 16, 24)
      EndIf 
      
      If Image = #PB_Any
        Image = Result
        PB_Any = #True
      EndIf ;}

      If AddMapElement(SVG(), Str(Image))

        SVG()\strgXML = ComposeXML(svgXML)
        SVG()\Font    = Font
        SVG()\Flags   = Flags

        SVG()\Image\Num    = Image
        SVG()\Image\Width  = Width
        SVG()\Image\Height = Height
        SVG()\Image\Depth  = Depth
        SVG()\Image\PB_Any = PB_Any
      
        ;{ ---- Get Image Data -----
        If StartVectorDrawing(ImageVectorOutput(Image))
          
          SVG()\ResX = VectorResolutionX()
          SVG()\ResY = VectorResolutionY()
          
          ParseViewBox(svgXML)
          
          StopVectorDrawing()
        EndIf ;}
        
        If Flags & #CreateNoImage
          If IsImage(Image) : FreeImage(Image) : EndIf
        EndIf 
        
        If SVG()\Image\Width = 0 Or SVG()\Image\Height = 0
          SVG()\Image\Width  = SVG()\Width
          SVG()\Image\Height = SVG()\Height
        EndIf
        
        Width  = dpiX(SVG()\Image\Width)
        Height = dpiY(SVG()\Image\Height)
        
        If Flags & #Proportional
          AdjustSizeRatio()
        EndIf  
        
        SVG()\Image\Width  = dpiX(SVG()\Image\Width)
        SVG()\Image\Height = dpiY(SVG()\Image\Height)
        
        If IsImage(Image) And Flags & #CreateNoImage = #False
        
          If SVG()\Image\Width And SVG()\Image\Height
            
            If Flags & #CenterImage
              
              ResizeImage(Image, Width, Height, #PB_Image_Raw)

              OffSetX = (Width  - SVG()\Image\Width)  / 2
              OffSetY = (Height - SVG()\Image\Height) / 2 
              
            Else  
              ResizeImage(Image, SVG()\Image\Width, SVG()\Image\Height, #PB_Image_Raw)
            EndIf
            
          EndIf
          
          If StartVectorDrawing(ImageVectorOutput(Image))
          
            If BackColor = #PB_Image_Transparent ;{ Backcolor
              SVG()\Image\BackColor = #PB_Image_Transparent
            Else  
              
              If Alpha(BackColor) = #False
                SVG()\Image\BackColor = BackColor | 255 << 24
              Else
                SVG()\Image\BackColor = BackColor
              EndIf
 
              VectorSourceColor(SVG()\Image\BackColor)
              FillVectorOutput()
              
            EndIf ;}
            
        		If IsFont(SVG()\Font) : VectorFont(FontID(SVG()\Font)) : EndIf 
        		
        		DrawSVGImage_(svgXML, OffSetX, OffSetY)
        		
            StopVectorDrawing()
          EndIf
          
        EndIf
        
      EndIf

      FreeXML(svgXML)
    EndIf
    
    ProcedureReturn Result
  EndProcedure
 
  Procedure.i CatchString(Image.i, StringSVG.s, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
    ; Flags:     #Proportional / #CreateNoImage
    ; BackColor: #PB_Image_Transparent
    Define.i XML, Result
    
    XML = ParseXML(#PB_Any, StringSVG)
    If IsXML(XML)
      Result = Catch(Image, XML, Flags, BackColor, Width, Height, Font)  
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  Procedure.i CatchMemory(Image.i, *MemorySVG, SizeSVG.i, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
    ; Flags:     #Proportional / #CreateNoImage
    ; BackColor: #PB_Image_Transparent
    Define.i XML, Result
    
    XML = CatchXML(#PB_Any, *MemorySVG, SizeSVG)
    If IsXML(XML)
      Result = Catch(Image, XML, Flags, BackColor, Width, Height, Font)  
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  
  Procedure.i Load(Image.i, File.s, Flags.i=#False, BackColor.q=#PB_Image_Transparent, Width.d=0, Height.d=0, Font.i=#False)
    ; Flags:     #Proportional / #CreateNoImage
    ; BackColor: #PB_Image_Transparent
    Define.i XML, Result

    XML = LoadXML(#PB_Any, File)
    If IsXML(XML)
      Result = Catch(Image, XML, Flags, BackColor, Width, Height, Font)  
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  
  CompilerIf Defined(Icon, #PB_Module)
    
    Procedure.i CreateIcon(Image.i, File.s, Flags.i=#False)
      Define.i d, XML, Size, Icon, Image
      
      If FindMapElement(SVG(), Str(Image))

        If Flags & #Windows And Flags & #MacOS
          Icon = Icon::Create(#PB_Any, File, Icon::#Windows|Icon::#MacOS)
        ElseIf Flags & #MacOS
          Icon = Icon::Create(#PB_Any, File, Icon::#MacOS)
        Else
          Icon = Icon::Create(#PB_Any, File, Icon::#Windows)
        EndIf  

        If Icon
          
          Restore IconSize
          
          For d=1 To 9
            Read.i Size
            Image = SVG::Resize(Image, Size, Size, SVG::#Proportional|SVG::#CenterImage)
            Icon::AddImage(Icon, Image)
          Next
          
          Icon::Close(Icon)  
        EndIf
      
      EndIf
      
      ProcedureReturn  Image
    EndProcedure
    
    DataSection
      IconSize:
      Data.i 16, 24, 32, 48, 64, 128, 256, 512, 1024
    EndDataSection

  CompilerEndIf  

EndModule

;- ========  Module - Example ========

CompilerIf #PB_Compiler_IsMainFile
    
  LoadFont(0,"Arial",24,#PB_Font_Italic | #PB_Font_Bold | #PB_Font_StrikeOut) 
  txt$ = "Hello" 
  If StartVectorDrawing(SvgVectorOutput("test.svg", 400, 200))  ;use pb commands to write svg 
    VectorFont(FontID(0),36)  
    AddPathBox(50, 50, 200, 50)
    AddPathBox(150, 75, 200, 50)
    VectorSourceColor(RGBA(255, 128, 0, 255))
    StrokePath(10)
    MovePathCursor(200 - VectorTextWidth(Txt$)/2, 100 - VectorTextHeight(Txt$)/2)
    VectorSourceColor(RGBA(0,255, 0, 255))
    DrawVectorText(txt$)
    
    VectorFont(FontID(0), 25)
    VectorSourceColor(RGBA(0, 0, 0, 80))
    Text$ = "The quick brown fox jumps over the lazy dog"
    
    For i = 1 To 4
      MovePathCursor(200 - VectorTextWidth(Text$)/2, 100 - VectorTextHeight(Text$)/2)
      DrawVectorText(Text$)
      RotateCoordinates(200, 100, 30)
    Next i
    
    VectorFont(FontID(0), 36)
    VectorSourceColor(RGBA(255, 128, 0, 255))
    ResetCoordinates()
    MovePathCursor(10,10) 
    DrawVectorText("Hello World")
    
    StopVectorDrawing()
       
  EndIf
    
  OpenFile(0,"test.svg") 
  Global sout.s 
  sout = ReadString(0,#PB_UTF8 | #PB_File_IgnoreEOL,#PB_File_IgnoreEOL)
  Debug sout 
  CloseFile(0) 
  
  ;-run in browser 
  RunProgram("test.svg")
    
  img = SVG::Load(#PB_Any, "test.svg", #False, #PB_Image_Transparent)
  If img  
    OpenWindow(0,0,0,ImageWidth(img),ImageHeight(img),"test txt",#PB_Window_SystemMenu | #PB_Window_ScreenCentered) 
    ImageGadget(0,0,0,ImageWidth(img),ImageHeight(img),ImageID(img)) 
    Repeat 
    Until WaitWindowEvent() = #PB_Event_CloseWindow 
  Else 
    MessageRequester("oops","cant find image")
  EndIf   
  
CompilerEndIf 
User avatar
STARGÅTE
Addict
Addict
Posts: 2242
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [Module] SVG - Module (all OS)

Post by STARGÅTE »

idle wrote: Sat Sep 27, 2025 10:00 pm
STARGÅTE wrote: Sat Sep 27, 2025 12:55 pm I think the regex should be "[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?" without the ^ and $ because it should match all numbers in the transformation.
Then you don't need the loop. But I haven't tested it, because I'm not at home.
That didin't make any difference the expression is matching but not extracting.
I don't understand what you mean. It works here:

Code: Select all

Define Transform.s = "2.98023e-08,1,-1,5.96046e-08,300,-100"

If CreateRegularExpression(1, "[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?",#PB_RegularExpression_NoCase);"\-?\d+\.?\d*|\-?\.\d+")
	If ExamineRegularExpression(1, Transform)
		While NextRegularExpressionMatch(1)
			Debug ValD(RegularExpressionMatchString(1))
		Wend
	EndIf
EndIf
0.0000000298023
1.0
-1.0
0.0000000596046
300.0
-100.0
bzw. there is a definition bug in the protected variables A to F:

Code: Select all

Protected A.d, B.d, C.d, D.d, E.d, F.f
F should also be .d
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
User avatar
idle
Always Here
Always Here
Posts: 5964
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: [Module] SVG - Module (all OS)

Post by idle »

Yes it works in my separate test too but that's not the issue. If you comment out the while loop and uncomment the code in the parse_Atrribute_transform

Code: Select all

;   						Debug RegularExpressionGroup(RegEx\Command, 2)						
;   						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

Then take a look at the rotation when a = 4 in with the test svg, when the text is orientated 90 degrees the parameter from the regular expression comes out as -8 for the y value and you get a deformed picture.
and apart from that subtracting the text height resolved the issues but that may need a compiler if since windows does things upside-down
User avatar
skinkairewalker
Enthusiast
Enthusiast
Posts: 793
Joined: Fri Dec 04, 2015 9:26 pm

Re: [Module] SVG - Module (all OS)

Post by skinkairewalker »

I’m trying to use resize, but it’s not working... what am I doing wrong?

Code: Select all


;- ========  Module - Example ========

CompilerIf #PB_Compiler_IsMainFile
    
  LoadFont(0,"Arial",24,#PB_Font_Italic | #PB_Font_Bold | #PB_Font_StrikeOut) 
  txt$ = "Hello" 
  If StartVectorDrawing(SvgVectorOutput("test.svg", 400, 200))  ;use pb commands to write svg 
    VectorFont(FontID(0),36)  
    AddPathBox(50, 50, 200, 50)
    AddPathBox(150, 75, 200, 50)
    VectorSourceColor(RGBA(255, 128, 0, 255))
    StrokePath(10)
    MovePathCursor(200 - VectorTextWidth(Txt$)/2, 100 - VectorTextHeight(Txt$)/2)
    VectorSourceColor(RGBA(0,255, 0, 255))
    DrawVectorText(txt$)
    
    VectorFont(FontID(0), 25)
   ; VectorSourceColor(RGBA(0, 0, 0, 80))
    Text$ = "The quick brown fox jumps over the lazy dog"
    
    For i = 1 To 4
      MovePathCursor(200 - VectorTextWidth(Text$)/2, 100 - VectorTextHeight(Text$)/2)
      DrawVectorText(Text$)
      RotateCoordinates(200, 100, 30)
    Next i
    
    VectorFont(FontID(0), 36)
    VectorSourceColor(RGBA(255, 128, 0, 255))
    ResetCoordinates()
    MovePathCursor(10,10) 
    DrawVectorText("Hello World")
    
    StopVectorDrawing()
       
  EndIf
    
  OpenFile(0,"test.svg") 
  Global sout.s 
  sout = ReadString(0,#PB_UTF8 | #PB_File_IgnoreEOL,#PB_File_IgnoreEOL)
 ; Debug sout 
  CloseFile(0) 
  
  ;-run in browser 
 ; RunProgram("test.svg")
    
  img = SVG::Load(#PB_Any, "test.svg", #False, #PB_Image_Transparent)
  If img  
    OpenWindow(0,0,0,ImageWidth(img),ImageHeight(img),"test txt",#PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget) 
    Debug Str(ImageWidth(img))+"x"+Str(ImageHeight(img))
    ImageGadget(0,0,0,ImageWidth(img),ImageHeight(img),ImageID(img),#PB_Image_Border) 
    Repeat 
      WEvent = WaitWindowEvent()
      
      Select WEvent
        Case #PB_Event_SizeWindow
          Debug Str(WindowWidth(0))+"x"+Str(WindowHeight(0))
           Debug Str(GadgetWidth(0))+"x"+Str(GadgetHeight(0))
          ResizeGadget(0,0,0,WindowWidth(0)-10,WindowHeight(0)-10)
          SVG::Resize(img, GadgetWidth(0), GadgetHeight(0))
      EndSelect    
      
    Until WEvent = #PB_Event_CloseWindow 
  Else 
    MessageRequester("oops","cant find image")
  EndIf   
  
CompilerEndIf 

User avatar
idle
Always Here
Always Here
Posts: 5964
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: [Module] SVG - Module (all OS)

Post by idle »

I haven't posted the fix yet. I will make a new thread for it it.
Post Reply