github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/image/colornames/gen.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build ignore 6 7 // This program generates table.go from 8 // http://www.w3.org/TR/SVG/types.html#ColorKeywords 9 package main 10 11 import ( 12 "bytes" 13 "fmt" 14 "go/format" 15 "image/color" 16 "io" 17 "io/ioutil" 18 "log" 19 "net/http" 20 "regexp" 21 "sort" 22 "strconv" 23 "strings" 24 25 "golang.org/x/net/html" 26 "golang.org/x/net/html/atom" 27 ) 28 29 // matchFunc matches HTML nodes. 30 type matchFunc func(*html.Node) bool 31 32 // appendAll recursively traverses the parse tree rooted under the provided 33 // node and appends all nodes matched by the matchFunc to dst. 34 func appendAll(dst []*html.Node, n *html.Node, mf matchFunc) []*html.Node { 35 if mf(n) { 36 dst = append(dst, n) 37 } 38 for c := n.FirstChild; c != nil; c = c.NextSibling { 39 dst = appendAll(dst, c, mf) 40 } 41 return dst 42 } 43 44 // matchAtom returns a matchFunc that matches a Node with the specified Atom. 45 func matchAtom(a atom.Atom) matchFunc { 46 return func(n *html.Node) bool { 47 return n.DataAtom == a 48 } 49 } 50 51 // matchAtomAttr returns a matchFunc that matches a Node with the specified 52 // Atom and a html.Attribute's namespace, key and value. 53 func matchAtomAttr(a atom.Atom, namespace, key, value string) matchFunc { 54 return func(n *html.Node) bool { 55 return n.DataAtom == a && getAttr(n, namespace, key) == value 56 } 57 } 58 59 // getAttr fetches the value of a html.Attribute for a given namespace and key. 60 func getAttr(n *html.Node, namespace, key string) string { 61 for _, attr := range n.Attr { 62 if attr.Namespace == namespace && attr.Key == key { 63 return attr.Val 64 } 65 } 66 return "" 67 } 68 69 // re extracts RGB values from strings like "rgb( 0, 223, 128)". 70 var re = regexp.MustCompile(`rgb\(\s*([0-9]+),\s*([0-9]+),\s*([0-9]+)\)`) 71 72 // parseRGB parses a color from a string like "rgb( 0, 233, 128)". It sets 73 // the alpha value of the color to full opacity. 74 func parseRGB(s string) (color.RGBA, error) { 75 m := re.FindStringSubmatch(s) 76 if m == nil { 77 return color.RGBA{}, fmt.Errorf("malformed color: %q", s) 78 } 79 var rgb [3]uint8 80 for i, t := range m[1:] { 81 num, err := strconv.ParseUint(t, 10, 8) 82 if err != nil { 83 return color.RGBA{}, fmt.Errorf("malformed value %q in %q: %s", t, s, err) 84 } 85 rgb[i] = uint8(num) 86 } 87 return color.RGBA{rgb[0], rgb[1], rgb[2], 0xFF}, nil 88 } 89 90 // extractSVGColors extracts named colors from the parse tree of the SVG 1.1 91 // spec HTML document "Chapter 4: Basic data types and interfaces". 92 func extractSVGColors(tree *html.Node) (map[string]color.RGBA, error) { 93 ret := make(map[string]color.RGBA) 94 95 // Find the tables which store the color keywords in the parse tree. 96 colorTables := appendAll(nil, tree, func(n *html.Node) bool { 97 return n.DataAtom == atom.Table && strings.Contains(getAttr(n, "", "summary"), "color keywords part") 98 }) 99 100 for _, table := range colorTables { 101 // Color names and values are stored in TextNodes within spans in each row. 102 for _, tr := range appendAll(nil, table, matchAtom(atom.Tr)) { 103 nameSpan := appendAll(nil, tr, matchAtomAttr(atom.Span, "", "class", "prop-value")) 104 valueSpan := appendAll(nil, tr, matchAtomAttr(atom.Span, "", "class", "color-keyword-value")) 105 106 // Since SVG 1.1 defines an odd number of colors, the last row 107 // in the second table does not have contents. We skip it. 108 if len(nameSpan) != 1 || len(valueSpan) != 1 { 109 continue 110 } 111 n, v := nameSpan[0].FirstChild, valueSpan[0].FirstChild 112 // This sanity checks for the existence of TextNodes under spans. 113 if n == nil || n.Type != html.TextNode || v == nil || v.Type != html.TextNode { 114 return nil, fmt.Errorf("extractSVGColors: couldn't find name/value text nodes") 115 } 116 val, err := parseRGB(v.Data) 117 if err != nil { 118 return nil, fmt.Errorf("extractSVGColors: couldn't parse name/value %q/%q: %s", n.Data, v.Data, err) 119 } 120 ret[n.Data] = val 121 } 122 } 123 return ret, nil 124 } 125 126 const preamble = `// generated by go generate; DO NOT EDIT. 127 128 package colornames 129 130 import "image/color" 131 132 ` 133 134 // WriteColorNames writes table.go. 135 func writeColorNames(w io.Writer, m map[string]color.RGBA) { 136 keys := make([]string, 0, len(m)) 137 for k := range m { 138 keys = append(keys, k) 139 } 140 sort.Strings(keys) 141 142 fmt.Fprintln(w, preamble) 143 fmt.Fprintln(w, "// Map contains named colors defined in the SVG 1.1 spec.") 144 fmt.Fprintln(w, "var Map = map[string]color.RGBA{") 145 for _, k := range keys { 146 fmt.Fprintf(w, "%q:color.RGBA{%#02x, %#02x, %#02x, %#02x}, // rgb(%d, %d, %d)\n", 147 k, m[k].R, m[k].G, m[k].B, m[k].A, m[k].R, m[k].G, m[k].B) 148 } 149 fmt.Fprintln(w, "}\n") 150 fmt.Fprintln(w, "// Names contains the color names defined in the SVG 1.1 spec.") 151 fmt.Fprintln(w, "var Names = []string{") 152 for _, k := range keys { 153 fmt.Fprintf(w, "%q,\n", k) 154 } 155 fmt.Fprintln(w, "}") 156 } 157 158 const url = "http://www.w3.org/TR/SVG/types.html" 159 160 func main() { 161 res, err := http.Get(url) 162 if err != nil { 163 log.Fatalf("Couldn't read from %s: %s\n", url, err) 164 } 165 defer res.Body.Close() 166 167 tree, err := html.Parse(res.Body) 168 if err != nil { 169 log.Fatalf("Couldn't parse %s: %s\n", url, err) 170 } 171 172 colors, err := extractSVGColors(tree) 173 if err != nil { 174 log.Fatalf("Couldn't extract colors: %s\n", err) 175 } 176 177 buf := &bytes.Buffer{} 178 writeColorNames(buf, colors) 179 fmted, err := format.Source(buf.Bytes()) 180 if err != nil { 181 log.Fatalf("Error while formatting code: %s\n", err) 182 } 183 184 if err := ioutil.WriteFile("table.go", fmted, 0644); err != nil { 185 log.Fatalf("Error writing table.go: %s\n", err) 186 } 187 }