...

Source file src/golang.org/x/image/colornames/gen.go

Documentation: golang.org/x/image/colornames

     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  //go:build ignore
     6  
     7  // This program generates table.go from
     8  // https://www.w3.org/TR/SVG11/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  		c := m[k]
   147  		fmt.Fprintf(w, "%q:color.RGBA{%#02x, %#02x, %#02x, %#02x}, // rgb(%d, %d, %d)\n",
   148  			k, c.R, c.G, c.B, c.A, c.R, c.G, c.B)
   149  	}
   150  	fmt.Fprintln(w, "}\n")
   151  	fmt.Fprintln(w, "// Names contains the color names defined in the SVG 1.1 spec.")
   152  	fmt.Fprintln(w, "var Names = []string{")
   153  	for _, k := range keys {
   154  		fmt.Fprintf(w, "%q,\n", k)
   155  	}
   156  	fmt.Fprintln(w, "}\n")
   157  	fmt.Fprintln(w, "var (")
   158  	for _, k := range keys {
   159  		c := m[k]
   160  		// Make the upper case version of k: "Darkred" instead of "darkred".
   161  		k = string(k[0]-0x20) + k[1:]
   162  		fmt.Fprintf(w, "%s=color.RGBA{%#02x, %#02x, %#02x, %#02x} // rgb(%d, %d, %d)\n",
   163  			k, c.R, c.G, c.B, c.A, c.R, c.G, c.B)
   164  	}
   165  	fmt.Fprintln(w, ")")
   166  }
   167  
   168  const url = "https://www.w3.org/TR/SVG11/types.html"
   169  
   170  func main() {
   171  	res, err := http.Get(url)
   172  	if err != nil {
   173  		log.Fatalf("Couldn't read from %s: %s\n", url, err)
   174  	}
   175  	defer res.Body.Close()
   176  
   177  	tree, err := html.Parse(res.Body)
   178  	if err != nil {
   179  		log.Fatalf("Couldn't parse %s: %s\n", url, err)
   180  	}
   181  
   182  	colors, err := extractSVGColors(tree)
   183  	if err != nil {
   184  		log.Fatalf("Couldn't extract colors: %s\n", err)
   185  	}
   186  
   187  	buf := &bytes.Buffer{}
   188  	writeColorNames(buf, colors)
   189  	fmted, err := format.Source(buf.Bytes())
   190  	if err != nil {
   191  		log.Fatalf("Error while formatting code: %s\n", err)
   192  	}
   193  
   194  	if err := ioutil.WriteFile("table.go", fmted, 0644); err != nil {
   195  		log.Fatalf("Error writing table.go: %s\n", err)
   196  	}
   197  }
   198  

View as plain text