Save SVG
Posted: Wed Aug 25, 2021 4:25 pm
Hi
I have found some codes to loadSVG, but I haven' t found a code to save an SVG document.
So, I have try to start a SaveSVG() procedure.
It doesn't cover all the SVG features, but it work for the main vectordrawing lib features :
- shape : box, circle, ellipse, line, curve, text
- Paint : fill, stroke (not dash or point for the moment)
- color + alpha (fill and stroke)
- Stroke flag : #PB_Path_RoundEnd, #PB_Path_SquareEnd, #PB_Path_RoundCorner
For the moment it doesn't save gradient and other feature like transformation coordonate (scale, rotation...).
I hope to add it soon.
I will integrated this code in my open-source 2D vectordrawing application "cartoon" (made in purebasic of course
), because it's easier to create vector drawing with this app and export it as svg will be interesting maybe.
Cheers
an example of result :

I have found some codes to loadSVG, but I haven' t found a code to save an SVG document.
So, I have try to start a SaveSVG() procedure.
It doesn't cover all the SVG features, but it work for the main vectordrawing lib features :
- shape : box, circle, ellipse, line, curve, text
- Paint : fill, stroke (not dash or point for the moment)
- color + alpha (fill and stroke)
- Stroke flag : #PB_Path_RoundEnd, #PB_Path_SquareEnd, #PB_Path_RoundCorner
For the moment it doesn't save gradient and other feature like transformation coordonate (scale, rotation...).
I hope to add it soon.
I will integrated this code in my open-source 2D vectordrawing application "cartoon" (made in purebasic of course

Cheers

Code: Select all
; Save SVG from vectorlib
; blendman 2021
Enumeration
; shape typ
#ShapeTypBox = 0
#ShapeTypCircle
#ShapeTypLine
#ShapeTypCurve
#ShapeTypEllipse
#ShapeTypText
; stroketyp
#StrokeTypFill = 0
#StrokeTypStroke
#StrokeTypDash
#StrokeTypPoint
EndEnumeration
Structure sShape
Startx.w
Starty.w
x.w
y.w
w.w
h.w
text$
; for curve
x1.w
y1.w
x2.w
y2.w
EndStructure
Structure sSVG_line
Segment$ ; infos about the shape
shapetyp.a ; 0 = rect,1=circle,2=line,3=curve
shape.sShape
Strokecolor.i
strokesize.w
strokeColorTyp.a ; 0=Color, 1 = gradient linear, 2= gradient circular, 3= texture
strokeTyp.a ; 0=fill, 1=stroke, 2= dash, 3=point
strokeflag.a
strokeAlpha.a
EndStructure
Global NewList SVGLine.sSvg_Line()
Procedure VD_AddShape(typ.a,startx.d,starty.d,x.d,y.d,w.d,h.d,color,stroketyp.a,size.w,
alpha.a=255,txt$="",strokeflag=#PB_Path_RoundEnd,x1=0,y1=0,x2=0,y2=0)
;MovePathCursor(x, y)
Select typ
Case #ShapeTypBox
AddPathBox(x,y,w,h)
Case #ShapeTypCircle
AddPathCircle(x,y,w)
Case #ShapeTypEllipse
AddPathEllipse(x,y,w,h)
Case #ShapeTypLine
MovePathCursor(startx, starty)
AddPathLine(x,y)
Case #ShapeTypCurve
MovePathCursor(startx,starty)
AddPathCurve(x,y,x1,y1,x2,y2)
Case #ShapeTypText
VectorFont(FontID(0),size)
VectorSourceColor(RGBA(Red(color),Green(color),Blue(color), alpha))
MovePathCursor(startx,starty)
DrawVectorText(txt$)
EndSelect
If typ <> #ShapeTypText
VectorSourceColor(RGBA(Red(color),Green(color),Blue(color), alpha))
If stroketyp = #StrokeTypFill
FillPath()
ElseIf stroketyp = #StrokeTypStroke
StrokePath(size, strokeflag)
EndIf
EndIf
; then save the shape
AddElement(SVGLine())
SVGLine()\Segment$ = PathSegments()
SVGLine()\shapetyp = typ ; ellipse
SVGLine()\Strokecolor = color
SVGLine()\strokesize = size
SVGLine()\strokeTyp = stroketyp ; stroke
SVGLine()\strokeColorTyp = 0 ; for the moment only color (not gradient...)
SVGLine()\strokeflag = strokeflag
SVGLine()\strokeAlpha = alpha
SVGLine()\shape\startx = startx
SVGLine()\shape\starty = starty
SVGLine()\shape\x = x
SVGLine()\shape\y = y
SVGLine()\shape\w = w
SVGLine()\shape\h = h
SVGLine()\shape\text$ = txt$
; for curve
SVGLine()\shape\x1 = x1
SVGLine()\shape\y1 = y1
SVGLine()\shape\x2 = x2
SVGLine()\shape\y2 = y2
EndProcedure
Procedure Save_SVG(Title$="",desc$="",w=1200,h=400,zoom=100)
;date$ = FormatDate("%yyyy%mm%dd%hh%ii%ss", Date())
date$ = FormatDate("%yyyy%mm%dd", Date())
z.d = zoom*0.01
file$ = "TestSVG"+date$+".svg"
If CreateFile(0,GetCurrentDirectory()+file$)
d$ = Chr(34)
; save infos for svg
WriteStringN(0, "<?xml version="+d$+"1.0"+d$+" encoding="+d$+"utf-8"+d$+" ?>")
WriteStringN(0, "<!-- Created with Purebasic -->")
WriteStringN(0, "")
WriteStringN(0, "<svg xmlns="+d$+"http://www.w3.org/2000/svg"+d$+" version="+d$+"1.1"+d$+" width="+d$+Str(w*z)+d$+" height="+d$+Str(h*z)+d$+">")
WriteStringN(0, " <title>"+Title$+"</title>")
WriteStringN(0, " <desc>"+desc$+"</desc>")
; Here, save the informations about shape, color...
ForEach SVGLine()
With SVGLine()
text$= " "
; color
color$ = d$+"rgb("+Str(Red(\Strokecolor))+","+Str(Green(\Strokecolor))+","+Str(Blue(\Strokecolor))+")"+d$
; fill-opacity="0.5" stroke-opacity="0.8"
stroketyp$ = "fill="+color$
If \strokeTyp = #StrokeTypStroke And \shapetyp <> #ShapeTypText
stroketyp$ = "fill="+d$+"none"+d$+" stroke="+color$+" stroke-width="+d$+Str(\strokesize)+d$+" stroke-opacity="+d$+StrD(\strokeAlpha/255,2)+d$
If \strokeflag =#PB_Path_RoundEnd Or \strokeflag = #PB_Path_RoundCorner
stroketyp$ + " stroke-linecap="+d$+ "round"+d$ + " stroke-linejoin="+d$+ "round"+d$
ElseIf \strokeflag =#PB_Path_SquareEnd
stroketyp$ + " stroke-linecap="+d$+ "square"+d$+ " stroke-linejoin="+d$+ "square"+d$
EndIf
ElseIf \strokeTyp = #StrokeTypFill
stroketyp$ +" fill-opacity="+d$+StrD(\strokeAlpha/255,2)+d$
If \shapetyp <> #ShapeTypText
If \strokeflag =#PB_Path_RoundEnd
stroketyp$ + " stroke-linejoin="+d$+ "round"+d$
ElseIf \strokeflag =#PB_Path_SquareEnd
stroketyp$ + " stroke-linejoin="+d$+ "square"+d$
EndIf
EndIf
EndIf
Select \shapetyp
Case #ShapeTypBox
text$ +"<rect x="+d$+Str(\shape\x)+d$+" y="+d$+Str(\shape\y)+d$+" width="+d$+Str(\shape\w)+d$+" height="+d$+Str(\shape\h)+d$+
" "+stroketyp$+" />"
WriteStringN(0, text$)
Case #ShapeTypCircle
text$ +"<circle cx="+d$+Str(\shape\x)+d$+" cy="+d$+Str(\shape\y)+d$+" r="+d$+Str(\shape\w)+d$+" "+stroketyp$+" />"
WriteStringN(0, text$)
Case #ShapeTypLine
text$ +"<line x1="+d$+Str(\shape\startx)+d$+" x2="+d$+Str(\shape\x)+d$+" y1="+d$+Str(\shape\Starty)+d$+" y2="+d$+Str(\shape\y)+
d$+" "+stroketyp$+" />"
WriteStringN(0, text$)
Case #ShapeTypCurve
; <path d="M 10 10 C 20 20, 40 20, 50 10"/>
text$ +"<path d="+d$+"M "+Str(\shape\startx)+" "+Str(\shape\Starty)+" C "+Str(\shape\x)+" "+Str(\shape\y)+" "+
Str(\shape\x1)+" "+Str(\shape\y1)+" "+Str(\shape\x2)+" "+Str(\shape\y2)+d$
text$+" "+stroketyp$+" />"
WriteStringN(0, text$)
Case #ShapeTypText
; <text x="10" y="10">Hello World!</text>
text$ +"<text dominant-baseline="+d$+"hanging"+d$+" x="+d$+Str(\shape\startx)+d$+" y="+d$+Str(\shape\starty)+d$+" font-size="+d$+Str(\strokesize)+d$+" "+stroketyp$+" >"+\shape\text$+"</text>"
WriteStringN(0, text$)
Case #ShapeTypEllipse
; text$ =" <g transform="+d$+"translate("+Str(\shape\x)+" "+Str(\shape\y)+")"+d$+">"
; WriteStringN(0, text$)
text$ +"<ellipse cx="+d$+Str(\shape\x)+d$+" cy="+d$+Str(\shape\y)+d$+" rx="+d$+Str(\shape\w)+d$+" ry="+d$+Str(\shape\h)+d$+" "+stroketyp$+" />"
WriteStringN(0, text$)
; WriteStringN(0, " </g>")
EndSelect
EndWith
Next
WriteStringN(0, "</svg>")
CloseFile(0)
EndIf
; RunProgram(file$)
EndProcedure
winw = 1000
winh = 600
If OpenWindow(0, 0, 0, winw, winh, "SaveSVG", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 0, 0, winw, winh)
LoadFont(0, "Times new roman", 20)
If StartVectorDrawing(CanvasVectorOutput(0))
; ellipse
VD_AddShape(#ShapeTypEllipse,0,0,112,116,34,45,RGB(255, 0, 0),#StrokeTypStroke,10,255)
; box with rounded stroke
VD_AddShape(#ShapeTypBox,0,0,50,50,200,100,RGB(210,150,130),#StrokeTypStroke,40,220,"",#PB_Path_RoundCorner)
VD_AddShape(#ShapeTypBox,0,0,70,150,260,120,RGB(20,240,230),#StrokeTypFill,10,90,"",#PB_Path_SquareEnd)
; circle
VD_AddShape(#ShapeTypCircle,0,0,80,60,50,100,RGB(0,150,130),#StrokeTypStroke,30,120)
; Line
VD_AddShape(#ShapeTypLine,10,30,80,60,0,0,RGB(0,0,130),#StrokeTypStroke,15,120)
VD_AddShape(#ShapeTypCurve,510,130,580,160,0,0,RGB(0,220,130),#StrokeTypStroke,25,190,"",#PB_Path_RoundEnd,710,170,680,50)
; VD_AddShape(#ShapeTypBox,0,0,50,100,30,30,RGB(160,160,160),#StrokeTypFill,40,255)
; text should be improved (there is an offset in y in browser)
VD_AddShape(#ShapeTypText,50,100,0,0,0,0,RGB(0,20,10),#StrokeTypFill,60,190,"Purebasic SVG !")
StopVectorDrawing()
EndIf
Save_SVG("SVG purebasic", "a SVG example made with purebasic" )
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf