Page 1 of 1

Save SVG

Posted: Wed Aug 25, 2021 4:25 pm
by [blendman]
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 :)

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
an example of result :
Image

Re: Save SVG

Posted: Wed Aug 25, 2021 7:36 pm
by Seymour Clufley
This might be of interest to you.