Skip to content

Commit

Permalink
Add support for text color, multiple fonts, versioning
Browse files Browse the repository at this point in the history
  • Loading branch information
Erik Veld committed Sep 16, 2021
1 parent 895f301 commit 4f430d0
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 32 deletions.
4 changes: 2 additions & 2 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ var generateCmd = &cobra.Command{
filename := fmt.Sprintf("%s-%s", file, hash)

outputPath := fmt.Sprintf("%s/%s.png", filepath.Dir(outputPath), filename)
err = generator.Generate(template, outputPath)
err = generator.Generate(template, version, outputPath)
if err != nil {
log.Fatal(err)
}
Expand All @@ -66,7 +66,7 @@ var generateCmd = &cobra.Command{
log.Fatal(err)
}

err = generator.Generate(template, outputPath)
err = generator.Generate(template, version, outputPath)
if err != nil {
log.Fatal(err)
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import (
"github.com/spf13/cobra"
)

const version = "v0.2.0"

var rootCmd = &cobra.Command{
Use: "picasso",
Short: "Picasso generates graphical assets from HCL2 templates",
}

func init() {
rootCmd.AddCommand(generateCmd)
rootCmd.AddCommand(versionCmd)
}

// Execute runs the main command.
Expand Down
16 changes: 16 additions & 0 deletions cmd/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of picasso",
Long: `All software has versions. This is picasso's`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Picasso %s\n", version)
},
}
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ module github.com/eveld/picasso
go 1.15

require (
github.com/AndreKR/multiface v0.0.0-20190725194701-b414aa6424a8
github.com/disintegration/imaging v1.6.2
github.com/flopp/go-findfont v0.0.0-20210823125129-5637e70e133f
github.com/fogleman/gg v1.3.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/hashicorp/go-getter v1.5.2
github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80
github.com/kr/pretty v0.1.0
github.com/spf13/cobra v1.1.1
github.com/zachomedia/go-bdf v0.0.0-20210522061406-1a147053be95 // indirect
github.com/zclconf/go-cty v1.7.1
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e
golang.org/x/mod v0.1.0
)
10 changes: 9 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreKR/multiface v0.0.0-20190725194701-b414aa6424a8 h1:Hqhi94Vh24pMnPXH4NkH7N7JNxxiSlV5ANo3227RVDY=
github.com/AndreKR/multiface v0.0.0-20190725194701-b414aa6424a8/go.mod h1:F4/sRjlOnpYMDwGUhf9wFxPeM69ZsJw8q4x8258R6LE=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
Expand Down Expand Up @@ -58,6 +60,8 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flopp/go-findfont v0.0.0-20210823125129-5637e70e133f h1:WIZWCW/44n2tK78JzllkM1iNxRqJv8C/X+DGv3jb8L8=
github.com/flopp/go-findfont v0.0.0-20210823125129-5637e70e133f/go.mod h1:wKKxRDjD024Rh7VMwoU90i6ikQRCr+JTHB5n4Ejkqvw=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
Expand Down Expand Up @@ -240,6 +244,8 @@ github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/zachomedia/go-bdf v0.0.0-20210522061406-1a147053be95 h1:93c2U94xDTBIxVHTf3SlkvrtC5O3754NK728n6YWGh0=
github.com/zachomedia/go-bdf v0.0.0-20210522061406-1a147053be95/go.mod h1:FWqHpmEj39kZYjkb4y+GkFRwJofD3lP2k8ataoNlo2Y=
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v1.7.1 h1:AvsC01GMhMLFL8CgEYdHGM+yLnnDOwhPAYcgTkeF0Gw=
github.com/zclconf/go-cty v1.7.1/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o=
Expand All @@ -264,8 +270,9 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e h1:PzJMNfFQx+QO9hrC1GwZ4BoPGeNGhfeQEgcQFArEjPk=
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand All @@ -277,6 +284,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down
150 changes: 132 additions & 18 deletions pkg/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,95 @@ package pkg
import (
"bytes"
"encoding/base64"
"fmt"
"image"
"image/color"
"io/ioutil"
"strings"

"github.com/AndreKR/multiface"
"github.com/disintegration/imaging"
"github.com/flopp/go-findfont"
"github.com/fogleman/gg"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/mod/semver"
)

func ReadFont(inputPath string) (*truetype.Font, error) {
foundPath, err := findfont.Find(inputPath)
if err != nil {
return nil, err
}

// load the font with the freetype library
fontData, err := ioutil.ReadFile(foundPath)
if err != nil {
return nil, err
}
font, err := truetype.Parse(fontData)
if err != nil {
return nil, err
}

return font, nil
}

func CreateFont(fontPaths []string, size float64) (font.Face, error) {
face := new(multiface.Face)
opts := &truetype.Options{Size: size, DPI: 72}

for _, fontPath := range fontPaths {
font, err := ReadFont(fontPath)
if err != nil {
return nil, err
}
fc := truetype.NewFace(font, opts)
face.AddTruetypeFace(fc, font)
}

return face, nil
}

// Generate turns a template into an image.
func Generate(template *Template, outputPath string) error {
func Generate(template *Template, version string, outputPath string) error {
dc := gg.NewContext(template.Output.Width, template.Output.Height)

if len(template.Picasso) > 0 {
templateVersion := template.Picasso[len(template.Picasso)-1].Version
if semver.Compare(version, templateVersion) < 0 {
return fmt.Errorf("a newer version of picasso is needed to render this template: want %s, found %s", templateVersion, version)
}
}

for _, layer := range template.Layers {
switch layer.Type {
case "rectangle":
switch layer.Color.Type {
case "hex":
dc.SetHexColor(layer.Color.Value)
case "gradient":
g := gg.NewLinearGradient(float64(layer.Color.Start.X), float64(layer.Color.Start.Y), float64(layer.Color.End.X), float64(layer.Color.End.Y))
for _, stop := range layer.Color.Stops {
c, err := ParseHexColor(stop.Value)
if err != nil {
return err
}
g.AddColorStop(float64(stop.Position), c)
}
dc.SetFillStyle(g)
if strings.HasPrefix(layer.Color, "#") {
dc.SetHexColor(layer.Color)
} else if strings.HasPrefix(layer.Color, "rgb(") {
fmt.Println("rgb is not yet implemented")
} else if strings.HasPrefix(layer.Color, "rgba(") {
fmt.Println("rgba is not yet implemented")
} else if strings.HasPrefix(layer.Color, "linear-gradient(") {
// TODO: convert degrees/angle into start/stop coordinate
// g := gg.NewLinearGradient(float64(layer.Color.Start.X), float64(layer.Color.Start.Y), float64(layer.Color.End.X), float64(layer.Color.End.Y))
// for _, stop := range layer.Color.Stops {
// c, err := ParseHexColor(stop.Value)
// if err != nil {
// return err
// }
// g.AddColorStop(float64(stop.Position), c)
// }
// dc.SetFillStyle(g)
fmt.Println("linear-gradient is not yet implemented")
} else if strings.HasPrefix(layer.Color, "radial-gradient(") {
fmt.Println("radial-gradient is not yet implemented")
} else {
// add a check for color names here later
fmt.Println("other colors are not yet implemented")
}

dc.DrawRectangle(float64(layer.X), float64(layer.Y), float64(layer.Width), float64(layer.Height))
dc.Fill()

Expand Down Expand Up @@ -60,16 +121,69 @@ func Generate(template *Template, outputPath string) error {
size := float64(layer.Size)
px := size

dc.SetColor(color.White)
if strings.HasPrefix(layer.Color, "#") {
dc.SetHexColor(layer.Color)
} else {
dc.SetColor(color.White)
}

// if err := dc.LoadFontFace(layer.Font, size); err != nil {
// return err
// }

if err := dc.LoadFontFace(layer.Font, size); err != nil {
fontPaths := strings.Split(layer.Font, ",")
face, err := CreateFont(fontPaths, size)
if err != nil {
return err
}

dc.SetFontFace(face)

ax := 0.0
ay := 0.0
align := gg.AlignLeft

if layer.Anchor != "" {
switch layer.Anchor {
case "CENTER":
ax = 0.5
ay = 0.5
align = gg.AlignCenter
case "TOP":
ax = 0.5
ay = 0.0
align = gg.AlignCenter
case "TOP_LEFT":
ax = 0.0
ay = 0.0
align = gg.AlignLeft
case "TOP_RIGHT":
ax = 1.0
ay = 0.0
align = gg.AlignRight
case "BOTTOM":
ax = 0.5
ay = 1.0
align = gg.AlignCenter
case "BOTTOM_LEFT":
ax = 0.0
ay = 1.0
align = gg.AlignLeft
case "BOTTOM_RIGHT":
ax = 1.0
ay = 1.0
align = gg.AlignRight
default:
ax = 0.0
ay = 0.0
align = gg.AlignLeft
}
}

if layer.Width != 0 {
dc.DrawStringWrapped(layer.Content, float64(layer.X), float64(layer.Y)+px, 0, 0, float64(layer.Width), 1.5, gg.AlignLeft)
dc.DrawStringWrapped(layer.Content, float64(layer.X), float64(layer.Y)+px, 0, 0, float64(layer.Width), 1.5, align)
} else {
dc.DrawString(layer.Content, float64(layer.X), float64(layer.Y)+px)
dc.DrawStringAnchored(layer.Content, float64(layer.X), float64(layer.Y)+px, ax, ay)
}
}
}
Expand Down
16 changes: 7 additions & 9 deletions pkg/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import (
"github.com/zclconf/go-cty/cty/function"
)

type Picasso struct {
Version string `hcl:"version,optional"`
}

type Layer struct {
Type string `hcl:"type,label"`
Name string `hcl:"name,label"`
Expand All @@ -28,15 +32,8 @@ type Layer struct {
Height int `hcl:"height,optional"`
Size int `hcl:"size,optional"`
Font string `hcl:"font,optional"`
Color *Color `hcl:"color,block"`
}

type Color struct {
Type string `hcl:"type,label"`
Value string `hcl:"value,optional"`
Start *Position `hcl:"start,block"`
End *Position `hcl:"end,block"`
Stops []Stop `hcl:"stop,block"`
Color string `hcl:"color,optional"`
Anchor string `hcl:"anchor,optional"`
}

type Position struct {
Expand All @@ -56,6 +53,7 @@ type Output struct {
}

type Template struct {
Picasso []Picasso `hcl:"picasso,block"`
Output Output `hcl:"output,block"`
Layers []Layer `hcl:"layer,block"`
Variables []Variable `hcl:"variable,block"`
Expand Down

0 comments on commit 4f430d0

Please sign in to comment.