package calendar import ( "fmt" "strconv" "strings" ) // Terminfarben und die Synchronisierung. // // RFC 7986 schreibt für COLOR einen **CSS3-Farbnamen** vor ("tomato"), keinen // Hex-Wert. Genau daran hängt, ob die Farbe eines Termins auf dem Telefon // ankommt: DAVx5 liest die Eigenschaft über eine Namenstabelle und wirft // alles weg, was es dort nicht findet – ein "#ea4335" landet also nirgends, // und im Samsung-Kalender bliebe der Termin farblos. // // Deshalb zwei Eigenschaften nebeneinander: // // COLOR:tomato – der Standard, damit fremde Clients es verstehen // X-AETHER-COLOR:#ea4335 – der genaue Wert, damit *hier* die Palette stimmt // // Beim Lesen gewinnt X-AETHER-COLOR, wenn vorhanden; sonst wird der Name in // Hex zurückübersetzt. So geht in beide Richtungen nichts verloren: Ein // Termin, den das Telefon einfärbt, kommt hier mit seiner Farbe an, und // unsere Palette bleibt auf dem Telefon erkennbar – wenn auch auf den // nächstgelegenen benannten Ton gerundet. const ( propColor = "COLOR" propColorX = "X-AETHER-COLOR" ) // css3Colors ist eine Auswahl aus der CSS3-Namensliste – über den Farbkreis // verteilt, dazu die Töne, die Kalender-Clients üblicherweise selbst // vergeben. Nicht die vollständige Liste (147 Namen): Für "nächstgelegener // Ton" bringt jeder weitere Zwischenwert kaum etwas, und die Liste will // gepflegt werden. var css3Colors = map[string]string{ "black": "#000000", "dimgray": "#696969", "gray": "#808080", "darkgray": "#a9a9a9", "silver": "#c0c0c0", "white": "#ffffff", "maroon": "#800000", "brown": "#a52a2a", "firebrick": "#b22222", "crimson": "#dc143c", "red": "#ff0000", "tomato": "#ff6347", "coral": "#ff7f50", "orangered": "#ff4500", "darkorange": "#ff8c00", "orange": "#ffa500", "goldenrod": "#daa520", "gold": "#ffd700", "yellow": "#ffff00", "olive": "#808000", "yellowgreen": "#9acd32", "darkolivegreen": "#556b2f", "lawngreen": "#7cfc00", "limegreen": "#32cd32", "green": "#008000", "darkgreen": "#006400", "seagreen": "#2e8b57", "mediumseagreen": "#3cb371", "teal": "#008080", "darkcyan": "#008b8b", "lightseagreen": "#20b2aa", "turquoise": "#40e0d0", "cyan": "#00ffff", "deepskyblue": "#00bfff", "steelblue": "#4682b4", "cornflowerblue": "#6495ed", "royalblue": "#4169e1", "dodgerblue": "#1e90ff", "blue": "#0000ff", "mediumblue": "#0000cd", "navy": "#000080", "slateblue": "#6a5acd", "mediumpurple": "#9370db", "blueviolet": "#8a2be2", "darkviolet": "#9400d3", "purple": "#800080", "orchid": "#da70d6", "magenta": "#ff00ff", "mediumvioletred": "#c71585", "deeppink": "#ff1493", "hotpink": "#ff69b4", "pink": "#ffc0cb", "slategray": "#708090", "lightslategray": "#778899", "tan": "#d2b48c", "peru": "#cd853f", "sienna": "#a0522d", } func parseHex(s string) (r, g, b int, ok bool) { s = strings.TrimSpace(strings.TrimPrefix(strings.ToLower(s), "#")) if len(s) == 3 { // Kurzform #abc s = string([]byte{s[0], s[0], s[1], s[1], s[2], s[2]}) } if len(s) == 8 { // mit Alpha – der Kanal interessiert hier nicht s = s[:6] } if len(s) != 6 { return 0, 0, 0, false } v, err := strconv.ParseUint(s, 16, 32) if err != nil { return 0, 0, 0, false } return int(v>>16) & 0xff, int(v>>8) & 0xff, int(v) & 0xff, true } // nearestCSS3 findet den nächstgelegenen benannten Ton. Der Abstand wird im // RGB-Würfel gemessen – grob, aber für "welcher Name kommt dem am nächsten" // vollkommen ausreichend, und ohne Farbmetrik-Bibliothek im Gepäck. func nearestCSS3(hex string) string { r, g, b, ok := parseHex(hex) if !ok { return "" } best, bestDist := "", 1<<30 for name, ref := range css3Colors { rr, rg, rb, _ := parseHex(ref) d := (r-rr)*(r-rr) + (g-rg)*(g-rg) + (b-rb)*(b-rb) // Bei gleichem Abstand der alphabetisch erste Name – sonst wechselt // die Ausgabe zwischen zwei Läufen, weil map-Reihenfolge zufällig ist. if d < bestDist || (d == bestDist && name < best) { best, bestDist = name, d } } return best } // css3ToHex übersetzt zurück. Unbekannter Name: leer – der Termin erbt dann // die Kalenderfarbe, was allemal besser ist als eine geratene. func css3ToHex(name string) string { return css3Colors[strings.ToLower(strings.TrimSpace(name))] } // normalizeHex bringt eine Farbe auf die Schreibweise "#rrggbb". func normalizeHex(s string) string { r, g, b, ok := parseHex(s) if !ok { return "" } return fmt.Sprintf("#%02x%02x%02x", r, g, b) }