package d2svgimport ()// pruneThemeCSS specializes a complete ThemeCSS stylesheet to the rendered SVG// fragments. ThemeCSS itself remains available with its complete rule set.func pruneThemeCSS( string, ...string) string {returnpruneThemeCSSWithClasses(, nil, ...)}// The font scan can supply body classes so only the small background fragments// need a second XML pass. The map belongs to the current Render call.func pruneThemeCSSWithClasses( string, map[string]bool, ...string) string {if == "" {return }// appendix.Append adds this separator after Render, without an inline color. // Its text color uses the separate .appendix rule, which is never pruned.if == nil { = make(map[string]bool) } ["stroke-B2"] = truefor , := range { := xml.NewDecoder(strings.NewReader())for { , := .Token()if == io.EOF {break }if != nil {// Preserve the existing output for custom/malformed SVG fragments.return } , := .(xml.StartElement)if ! {continue }ifstrings.EqualFold(.Name.Local, "script") {return }for , := range .Attr {// Custom icon SVG can mutate classes at runtime. Keep all rules // when scripts, event handlers, or SMIL make class use dynamic. := strings.ToLower(.Name.Local)ifstrings.HasPrefix(, "on") || == "attributename" && strings.EqualFold(strings.TrimSpace(.Value), "class") {return }if == "href" { := strings.TrimSpace(.Value)ifstrings.EqualFold(.Name.Local, "use") && !strings.HasPrefix(, "#") || strings.HasPrefix(strings.ToLower(), "javascript:") {return } }if .Name.Local == "class" {for , := rangestrings.Fields(.Value) {ifisPaletteClass() { [] = true } } } } } }returnpruneThemeRules(, )}// Scan the simple palette selectors emitted by ThemeCSS, leaving all other// rules intact. This recognizes the same grammar as the original regexp,// including its ASCII whitespace and class names, without repeatedly matching// the complete stylesheet and then matching each rule again.func pruneThemeRules( string, map[string]bool) string {varstrings.Builder := 0for := 0; < len(); { := strings.IndexByte([:], '{')if < 0 {break } := + = + 1 , , := themeRuleSelector(, )if ! {continue } := strings.IndexAny([:], "{}")if < 0 {break } += if [] != '}' {continue } = + 1if !isPaletteClass() || [] {continue }for > && themeRuleWhitespace([-1]) { -- }if .Cap() == 0 { .Grow(len()) } .WriteString([:]) = }if == 0 {return } .WriteString([:])return .String()}func themeRuleSelector( string, int) ( int, string, bool) { = for > 0 && themeRuleWord([-1]) { -- }if == 0 || [-1] != '.' {return0, "", false } = [:] := ""ifstrings.HasPrefix(, "sketch-overlay-") { = [len("sketch-overlay-"):] -- } else {for , := range []string{"fill-", "stroke-", "background-color-", "color-"} {ifstrings.HasPrefix(, ) { = [len():]break } }// The scoped form has exactly one space between its two classes.if < 3 || [-2] != ' ' {return0, "", false } := - 2 = for > 0 && themeRuleWord([-1]) { -- }if == || == 0 || [-1] != '.' {return0, "", false } -- }if == "" {return0, "", false }for := range {if := []; !( >= 'A' && <= 'Z' || >= '0' && <= '9') {return0, "", false } }return , , true}func themeRuleWord( byte) bool {return >= 'a' && <= 'z' || >= 'A' && <= 'Z' || >= '0' && <= '9' || == '_' || == '-'}func themeRuleWhitespace( byte) bool {return == ' ' || == '\t' || == '\n' || == '\r' || == '\f'}func isPaletteClass( string) bool {for , := range []string{"fill-", "stroke-", "background-color-", "color-", "sketch-overlay-"} {if , := strings.CutPrefix(, ); {returncolor.IsThemeColor() } }returnfalse}
The pages are generated with Goldsv0.8.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.