blob: 3969d8be8b0dedb6716a5f826ff45852dd07736f [file] [log] [blame]
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
// +build ignore
// This program generates table.go from
// https://material.google.com/style/color.html
package main
import (
"bytes"
"fmt"
"go/format"
"image/color"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
type matchFunc func(*html.Node) bool
func appendAll(dst []*html.Node, n *html.Node, mf matchFunc) []*html.Node {
if mf(n) {
dst = append(dst, n)
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
dst = appendAll(dst, c, mf)
}
return dst
}
func match(a atom.Atom, namespace, key, value string) matchFunc {
return func(n *html.Node) bool {
return n.DataAtom == a && strings.HasPrefix(getAttr(n, namespace, key), value)
}
}
func getAttr(n *html.Node, namespace, key string) string {
for _, attr := range n.Attr {
if attr.Namespace == namespace && attr.Key == key {
return attr.Val
}
}
return ""
}
func getText(n *html.Node) string {
if n.FirstChild == nil || n.FirstChild.Type != html.TextNode {
return ""
}
return n.FirstChild.Data
}
var unhex = [256]byte{
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'a': 10,
'b': 11,
'c': 12,
'd': 13,
'e': 14,
'f': 15,
}
func parseRGB(s string) color.RGBA {
if len(s) != 7 || s[0] != '#' {
return color.RGBA{}
}
return color.RGBA{
R: unhex[s[1]]<<4 | unhex[s[2]],
G: unhex[s[3]]<<4 | unhex[s[4]],
B: unhex[s[5]]<<4 | unhex[s[6]],
A: 0xff,
}
}
type entry struct {
name string
rgba color.RGBA
}
func extractColors(tree *html.Node) (ret []entry) {
for _, table := range appendAll(nil, tree, match(atom.Section, "", "class", "color-group")) {
name := ""
for _, nameNode := range appendAll(nil, table, match(atom.Span, "", "class", "name")) {
name = strings.Replace(getText(nameNode), " ", "", -1)
break
}
shades := appendAll(nil, table, match(atom.Span, "", "class", "shade"))
hexes := appendAll(nil, table, match(atom.Span, "", "class", "hex"))
if len(shades) != len(hexes) || len(shades) == 0 {
continue
}
// Remove the duplicated 500 shade at the start of the list.
if getText(shades[0]) == "500" {
shades, hexes = shades[1:], hexes[1:]
}
for i, shade := range shades {
ret = append(ret, entry{
name + getText(shade),
parseRGB(getText(hexes[i])),
})
}
}
return ret
}
const preamble = `// generated by go generate; DO NOT EDIT.
package colornames
import "image/color"
`
func writeColorNames(w io.Writer, entries []entry) {
fmt.Fprintln(w, preamble)
fmt.Fprintln(w, "// Map contains named colors defined in the Material Design style guide.")
fmt.Fprintln(w, "var Map = map[string]color.RGBA{")
for _, e := range entries {
c := e.rgba
fmt.Fprintf(w, "%q:color.RGBA{%#02x, %#02x, %#02x, %#02x}, // rgb(%d, %d, %d)\n",
e.name, c.R, c.G, c.B, c.A, c.R, c.G, c.B)
}
fmt.Fprintln(w, "}\n")
fmt.Fprintln(w, "// Names contains the color names defined in the Material Design style guide.")
fmt.Fprintln(w, "var Names = []string{")
for _, e := range entries {
fmt.Fprintf(w, "%q,\n", e.name)
}
fmt.Fprintln(w, "}\n")
fmt.Fprintln(w, "var (")
for _, e := range entries {
c := e.rgba
fmt.Fprintf(w, "%s=color.RGBA{%#02x, %#02x, %#02x, %#02x} // rgb(%d, %d, %d)\n",
e.name, c.R, c.G, c.B, c.A, c.R, c.G, c.B)
}
fmt.Fprintln(w, ")")
}
const url = "https://material.google.com/style/color.html"
func main() {
res, err := http.Get(url)
if err != nil {
log.Fatalf("Couldn't read from %s: %s\n", url, err)
}
defer res.Body.Close()
tree, err := html.Parse(res.Body)
if err != nil {
log.Fatalf("Couldn't parse %s: %s\n", url, err)
}
buf := &bytes.Buffer{}
writeColorNames(buf, extractColors(tree))
fmted, err := format.Source(buf.Bytes())
if err != nil {
log.Fatalf("Error while formatting code: %s\n", err)
}
if err := ioutil.WriteFile("table.go", fmted, 0644); err != nil {
log.Fatalf("Error writing table.go: %s\n", err)
}
}