From 315e747a4c067752dfc69d3d2d6dc23f0c30d8c0 Mon Sep 17 00:00:00 2001 From: kaa Date: Wed, 3 Jul 2024 08:41:58 -0700 Subject: Public release. --- old/2/dl.go.bup | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100755 old/2/dl.go.bup (limited to 'old/2/dl.go.bup') diff --git a/old/2/dl.go.bup b/old/2/dl.go.bup new file mode 100755 index 0000000..7562f48 --- /dev/null +++ b/old/2/dl.go.bup @@ -0,0 +1,247 @@ +/* Parse typography.com, find typeface file names. */ +/* Missing some styles. Use https://www.typography.com/api/v1/product_lines/100054?include=multipurpose_styles,office_styles,screen_smart_styles. */ +package main + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "io" + "io/fs" + "log" + "net/http" + "os" + "regexp" + "strconv" + "strings" +) + +const ( + base = "https://typography.com/api/v1" +) + +func request(url string) []byte { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Fatal(err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Fatal(err) + } + + rt, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + resp.Body.Close() + + return rt +} + +func dlThenRead(name, url string) []byte { + _, err := os.Stat(name) + if errors.Is(err, fs.ErrNotExist) { + log.Print(err) + out, err := os.Create(name) + if err != nil { + log.Fatal(err) + } + defer out.Close() + + outWriter := bufio.NewWriter(out) + _, err = outWriter.Write(request(url)) + if err != nil { + log.Fatal(err) + } + _ = outWriter.Flush() + } else if err != nil { + log.Fatal(err) + } + + in, err := os.Open(name) + if err != nil { + log.Fatal(err) + } + defer in.Close() + + inReader := bufio.NewReader(in) + data, err := io.ReadAll(inReader) + if err != nil { + log.Fatal(err) + } + + return data +} + +func parseAndPrint(shortName, css string) { + /* Grep for lines containing CSS rules with font names, */ + for _, line := range strings.Split(css, "\n") { + match, err := regexp.Match("font-family:.*", []byte(line)) + if err != nil { + log.Fatal(err) + } + if match { + /* Remove rule boilerplate. */ + line = strings.Replace(line, " font-family: ", "", 1) + line = strings.Replace(line, ", Fallback, Courier;", "", 1) + + /* Split list of file names. */ + for _, style := range strings.Split(line, ", ") { + /* TDB or TDA should become TD. Decided + against the complexity of regexp for this. */ + style = strings.Replace(style, "TDB", "TD", 1) + style = strings.Replace(style, "TDA", "TD", 1) + + /* Each style has two files: one with most + of the glpyhs, another with just the ' '. + Both are necessary, and need merging later. */ + fmt.Println(shortName, style) + fmt.Println(shortName, style+"-Space") + } + } + } +} + +func traverse(name, shortName string, id string) { + //fmt.Printf("%s %s %s\n", id, name, shortName) + + /* Create a directory for each typeface. The short name + used in URLs contains no spaces. It works well for this use. */ + _, err := os.Stat(shortName) + if err != nil { + err = os.Mkdir(shortName, 0666) + if err != nil { + log.Fatal(err) + } + } + err = os.Chdir(shortName) + defer os.Chdir("..") + + /* The long name of the typeface is worth keeping. */ + _, err = os.Stat("name") + if errors.Is(err, fs.ErrNotExist) { + out, err := os.Create("name") + if err != nil { + log.Fatal(err) + } + defer out.Close() + + _, err = out.Write([]byte(name)) + if err != nil { + log.Fatal(err) + } + } + + /* Using the ID passed in from the initial JSON, retrieve new + JSON. This contains details on the actual typeface. */ + /* Parsing for OTF names in progress. */ + detailsJson := dlThenRead(id, base+"/product_lines/"+id+ + "?include=special_offers,multipurpose_styles,office_styles,screen_smart_styles&quantity=1&serializer=array") + var details interface{} + json.Unmarshal(detailsJson, &details) + m := details.(map[string]interface{}) + + /* The important field is an alphabetic identifier. It must be used + in the following step. Hoop jumping. */ + alphaId := m["desktop_try_code"].(string) + + /* This JSON contains information about how the typeface + should be layed out. Embedded is a set of CSS rules. + The CSS rules contain the file names of each style of + the typeface. */ + layoutJson := dlThenRead(alphaId, base+"/try/layout/"+ + alphaId) + var layout interface{} + json.Unmarshal(layoutJson, &layout) + layoutMap := layout.(map[string]interface{}) + + /* Interesting data is a few layers deep. */ + for _, mL2 := range layoutMap { + for _, mL3 := range mL2.(map[string]interface{}) { + switch mL3.(type) { + case map[string]interface{}: + mL4 := mL3.(map[string]interface{}) + layoutL2Json := []byte(mL4["layout_data"].(string)) + var layoutL2 interface{} + json.Unmarshal(layoutL2Json, &layoutL2) + + /* Send the CSS for parsing. */ + parseAndPrint(shortName, + layoutL2.(map[string]interface{})["css"].(string)) + case string: + default: + } + } + } + + /* OTF names. Base names are available as woff. */ + otf := m["multipurpose_styles"].(map[string]interface{})["styles"].([]interface{}) + for _, otf := range otf { + style := otf.(map[string]interface{})["file_name"].(string) + style = strings.TrimSuffix(style, ".otf") + style = strings.Replace(style, "-SC", "-TD", 1) + if !strings.Contains(style, "-TD") || strings.Contains(style,"_TD") || + shortName == "whitney" { + continue + } + fmt.Println(shortName, style) + fmt.Println(shortName, style+"-Space") + } +} + +func main() { + /* findByName serves as the index to each of + the typefaces. If it doesn't exist, download it. */ + _, err := os.Stat("findByName") + if errors.Is(err, fs.ErrNotExist) { + log.Print(err) + out, err := os.Create("findByName") + if err != nil { + log.Fatal(err) + } + defer out.Close() + + outWriter := bufio.NewWriter(out) + _, err = outWriter.Write(request(base + + "/findByName?serializer=array")) + if err != nil { + log.Fatal(err) + } + _ = outWriter.Flush() + } + + in, err := os.Open("findByName") + if err != nil { + log.Fatal(err) + } + defer in.Close() + + inReader := bufio.NewReader(in) + indexJson, err := io.ReadAll(inReader) + if err != nil { + log.Fatal(err) + } + + /* findByName is in JSON. */ + var index interface{} + err = json.Unmarshal(indexJson, &index) + if err != nil { + log.Fatal(err) + } + + /* The actually interesting data is a few levels deep. */ + m := index.(map[string]interface{}) + for _, mL2 := range m { + for _, mL3 := range mL2.([]interface{}) { + arr := mL3.(map[string]interface{}) + /* "name" is the full name, "slug" is the short name, + "type_id" is the identifier used in the traversal. */ + traverse(arr["name"].(string), + arr["slug"].(string), + strconv.Itoa(int(arr["type_id"].(float64)))) + } + } +} -- cgit v1.2.3