| // 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 |
| |
| // This program generates table.go from |
| // https://material.google.com/style/color.html |
| package main |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/format" |
| "image/color" |
| "io" |
| "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 := os.WriteFile("table.go", fmted, 0644); err != nil { |
| log.Fatalf("Error writing table.go: %s\n", err) |
| } |
| } |