Skip to content

Commit

Permalink
Experimental embed svg font functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
drahoslove committed Jan 7, 2018
1 parent c1e5ede commit 1b49270
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 23 deletions.
4 changes: 2 additions & 2 deletions draw2dsvg/fileutil.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package draw2dsvg

import (
"os"
"encoding/xml"
_ "errors"
"os"
)

func SaveToSvgFile(filePath string, svg *Svg) error {
Expand All @@ -19,4 +19,4 @@ func SaveToSvgFile(filePath string, svg *Svg) error {
err = encoder.Encode(svg)

return err
}
}
77 changes: 59 additions & 18 deletions draw2dsvg/gc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
package draw2dsvg

import (
"fmt"
"github.com/golang/freetype/truetype"
"github.com/llgcode/draw2d"
"github.com/llgcode/draw2d/draw2dbase"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
"image"
"log"
"strings"
"math"
"github.com/golang/freetype/truetype"
"golang.org/x/image/math/fixed"
"golang.org/x/image/font"
"strconv"
"fmt"
"strings"
)

type drawType int
Expand Down Expand Up @@ -133,12 +133,16 @@ func (gc *GraphicContext) drawString(text string, drawType drawType, x, y float6
svgText.FontSize = gc.Current.FontSize
svgText.X = x
svgText.Y = y
svgText.FontFamily = "" // TODO set font
svgText.FontFamily = gc.Current.FontData.Name

if gc.svg.fontMode == SvgFontMode {
gc.embedSvgFont(text)
}

// link to group
group.Texts = []*Text{&svgText}
left, _, right, _ := gc.GetStringBounds(text)
return right-left
return right - left
}

// Creates new group from current context
Expand Down Expand Up @@ -170,20 +174,59 @@ func (gc *GraphicContext) newGroup(drawType drawType) *Group {
return &group
}

///////////////////////////////////////
// TODO implement following methods (or remove if not neccesary)
// Embed svg font definition to svg tree itself
func (gc *GraphicContext) embedSvgFont(text string) {
fontName := gc.Current.FontData.Name
gc.loadCurrentFont()

// find or create font Element
svgFont := (*Font)(nil)
for _, font := range gc.svg.Fonts {
if font.Name == fontName {
svgFont = font
break
}
}
if svgFont == nil {
// create new
svgFont = &Font{}
// and link
gc.svg.Fonts = append(gc.svg.Fonts, svgFont)
}

// SetFontData sets the current FontData
func (gc *GraphicContext) SetFontData(fontData draw2d.FontData) {
// fill with glyphs

gc.Save()
defer gc.Restore()
gc.SetFontSize(2048)
defer gc.SetDPI(gc.GetDPI())
gc.SetDPI(92)
filling:
for _, rune := range text {
for _, g := range svgFont.Glyphs {
if g.Rune == Rune(rune) {
continue filling
}
}
glyph := gc.glyphCache.Fetch(gc, gc.GetFontName(), rune)
// glyphCache.Load indirectly calls CreateStringPath for single rune string

glypPath := glyph.Path.VerticalFlip() // svg font glyphs have oposite y axe
svgFont.Glyphs = append(svgFont.Glyphs, &Glyph{
Rune: Rune(rune),
Desc: toSvgPathDesc(glypPath),
HorizAdvX: glyph.Width,
})
}

}
// set attrs
svgFont.Id = "font-" + strconv.Itoa(len(gc.svg.Fonts))
svgFont.Name = fontName

// GetFontData gets the current FontData
func (gc *GraphicContext) GetFontData() draw2d.FontData {
return draw2d.FontData{}
// TODO use css @font-face with id instead of this
svgFont.Face = &Face{Family: fontName, Units: 2048, HorizAdvX: 2048}
}


// NOTE following functions copied from dwra2d{img|gl}
// TODO move them all to common draw2dbase?

Expand Down Expand Up @@ -218,7 +261,6 @@ func (gc *GraphicContext) CreateStringPath(s string, x, y float64) (cursor float
return x - startx
}


// GetStringBounds returns the approximate pixel bounds of the string s at x, y.
// The the left edge of the em square of the first character of s
// and the baseline intersect at 0, 0 in the returned coordinates.
Expand Down Expand Up @@ -311,7 +353,6 @@ func (gc *GraphicContext) SetFontSize(fontSize float64) {
gc.recalc()
}


///////////////////////////////////////
// TODO implement following methods (or remove if not neccesary)

Expand Down
52 changes: 49 additions & 3 deletions draw2dsvg/svg.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,30 @@ import (

/* svg elements */

type FontMode int

const (
SysFontMode FontMode = 1 << iota
LinkFontMode
SvgFontMode
CssFontMode
PathFontMode
)

type Svg struct {
XMLName xml.Name `xml:"svg"`
Xmlns string `xml:"xmlns,attr"`
Groups []*Group `xml:"g"`
XMLName xml.Name `xml:"svg"`
Xmlns string `xml:"xmlns,attr"`
Fonts []*Font `xml:"defs>font"`
Groups []*Group `xml:"g"`
fontMode FontMode
FillStroke
}

func NewSvg() *Svg {
return &Svg{
Xmlns: "http://www.w3.org/2000/svg",
FillStroke: FillStroke{Fill: "none", Stroke: "none"},
fontMode: SvgFontMode,
}
}

Expand All @@ -45,8 +58,41 @@ type Text struct {
Style string `xml:"style,attr,omitempty"`
}

type Font struct {
Identity
Face *Face `xml:"font-face"`
Glyphs []*Glyph `xml:"glyph"`
}

type Face struct {
Family string `xml:"font-family,attr"`
Units int `xml:"units-per-em,attr"`
HorizAdvX float64 `xml:"horiz-adv-x,attr"`
// TODO add other attrs, like style, variant, weight...
}

type Glyph struct {
Rune Rune `xml:"unicode,attr"`
Desc string `xml:"d,attr"`
HorizAdvX float64 `xml:"horiz-adv-x,attr"`
}

type Rune rune

func (r Rune) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
return xml.Attr{
Name: name,
Value: string(rune(r)),
}, nil
}

/* shared attrs */

type Identity struct {
Id string `xml:"id,attr"`
Name string `xml:"name,attr"`
}

type Position struct {
X float64 `xml:"x,attr,omitempty"`
Y float64 `xml:"y,attr,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions draw2dsvg/xml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestXml(t *testing.T) {
}}

expectedOut := `<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="none">
<defs></defs>
<g>
<g></g>
<g></g>
Expand Down
Binary file modified output/samples/geometry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions path.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,34 @@ func (p *Path) String() string {
}
return s
}

// Returns new Path with flipped y axes
func (path *Path) VerticalFlip() *Path {
p := path.Copy()
j := 0
for _, cmd := range p.Components {
switch cmd {
case MoveToCmp, LineToCmp:
p.Points[j+1] = -p.Points[j+1]
j = j + 2
case QuadCurveToCmp:
p.Points[j+1] = -p.Points[j+1]
p.Points[j+3] = -p.Points[j+3]
j = j + 4
case CubicCurveToCmp:
p.Points[j+1] = -p.Points[j+1]
p.Points[j+3] = -p.Points[j+3]
p.Points[j+5] = -p.Points[j+5]
j = j + 6
case ArcToCmp:
p.Points[j+1] = -p.Points[j+1]
p.Points[j+3] = -p.Points[j+3]
p.Points[j+4] = -p.Points[j+4] // start angle
p.Points[j+5] = -p.Points[j+5] // angle
j = j + 6
case CloseCmp:
}
}
p.y = -p.y
return p
}
9 changes: 9 additions & 0 deletions samples/geometry/geometry.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ func CubicCurve(gc draw2d.GraphicContext, x, y, width, height float64) {
}

// FillString draws a filled and stroked string.
// And filles/stroked path created from string. Which may have different - unselectable - output in non raster gc implementations.
func FillString(gc draw2d.GraphicContext, x, y, width, height float64) {
sx, sy := width/100, height/100
gc.Save()
Expand All @@ -215,6 +216,14 @@ func FillString(gc draw2d.GraphicContext, x, y, width, height float64) {
gc.SetStrokeColor(color.NRGBA{0x33, 0x33, 0xff, 0xff})
gc.SetLineWidth(height / 100)
gc.StrokeString("Hug")

gc.Translate(-(w + sx), sy*24)
w = gc.CreateStringPath("Hug", 0, 0)
gc.Fill()
gc.Translate(w+sx, 0)
gc.CreateStringPath("Hug", 0, 0)
path := gc.GetPath()
gc.Stroke((&path).VerticalFlip())
gc.Restore()
}

Expand Down

0 comments on commit 1b49270

Please sign in to comment.