From d2998f76ccb685a28b28f42c98a408e6aa8f70e4 Mon Sep 17 00:00:00 2001 From: Zhangpengyu <18746442232@163.com> Date: Fri, 22 Apr 2022 18:28:03 +0800 Subject: [PATCH 1/5] Fix: Add support for Css variables Fix: Optimized code structure Optimized code structure Fix: Add support for Css variables --- src/context/stylesheets.ts | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/context/stylesheets.ts b/src/context/stylesheets.ts index 84e70dfe..ab5da065 100644 --- a/src/context/stylesheets.ts +++ b/src/context/stylesheets.ts @@ -5,11 +5,12 @@ export class StyleSheets { private rootSvg: Element private readonly loadExternalSheets: boolean private readonly styleSheets: CSSStyleSheet[] - + private cssValueMap: Map constructor(rootSvg: Element, loadExtSheets: boolean) { this.rootSvg = rootSvg this.loadExternalSheets = loadExtSheets this.styleSheets = [] + this.cssValueMap = new Map() } public async load(): Promise { @@ -88,6 +89,27 @@ export class StyleSheets { } } + getCssValue(selector: string): string | undefined { + const value: string = selector + .replace(/var\(/g, '') + .replace(/\)/g, '') + .replace(/^\s+|\s+$/g, '') + if (this.cssValueMap.get(value)) { + return this.cssValueMap.get(value) + } + for (const sheet of this.styleSheets) { + for (let i = 0; i < sheet.cssRules.length; i++) { + const rule = sheet.cssRules[i] as CSSStyleRule + const res = rule.style.getPropertyValue(value) + if (res) { + this.cssValueMap.set(value, res) + return res + } + } + } + return undefined + } + private static splitSelectorAtCommas(selectorText: string): string[] { const initialRegex = /,|["']/g const closingDoubleQuotesRegex = /[^\\]["]/g @@ -184,6 +206,17 @@ export class StyleSheets { const mostSpecificRule = matchingRules.reduce((previousValue, currentValue) => compare(previousValue, currentValue) === 1 ? previousValue : currentValue ) - return mostSpecificRule.style.getPropertyValue(propertyCss) || undefined + let resValue: string = mostSpecificRule.style.getPropertyValue(propertyCss) + const varReg = /var\(.*?\)/gi + if (resValue && varReg.test(resValue)) { + const cssValueList: RegExpMatchArray = resValue.match(varReg) || [] + cssValueList.map(cssValue => { + const res = this.getCssValue(cssValue) + if (res) { + resValue = resValue.replace(cssValue, res) + } + }) + } + return resValue || undefined } } From d3d9f205d04520f2ff462a24c9c7e57ec4030fb9 Mon Sep 17 00:00:00 2001 From: Zhangpengyu <18746442232@163.com> Date: Sun, 24 Apr 2022 12:01:34 +0800 Subject: [PATCH 2/5] Fix: Add test-unit for css-variables --- src/context/stylesheets.ts | 2 +- test/common/tests.js | 1 + test/specs/css-variables/reference.pdf | Bin 0 -> 3975 bytes test/specs/css-variables/spec.svg | 14 ++++++++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/specs/css-variables/reference.pdf create mode 100644 test/specs/css-variables/spec.svg diff --git a/src/context/stylesheets.ts b/src/context/stylesheets.ts index ab5da065..7c761729 100644 --- a/src/context/stylesheets.ts +++ b/src/context/stylesheets.ts @@ -213,7 +213,7 @@ export class StyleSheets { cssValueList.map(cssValue => { const res = this.getCssValue(cssValue) if (res) { - resValue = resValue.replace(cssValue, res) + resValue = resValue.replace(cssValue, res.replace(/^\s+|\s+$/g, '')) } }) } diff --git a/test/common/tests.js b/test/common/tests.js index d505b8b1..ba3e1ca1 100644 --- a/test/common/tests.js +++ b/test/common/tests.js @@ -15,6 +15,7 @@ window.tests = [ 'complete-organization-chart', 'complete-organization-chart-new', 'complete-social-network', + 'css-variables', 'custom-fonts', 'display-none-and-visibility-inheritance', 'duplicate-ids', diff --git a/test/specs/css-variables/reference.pdf b/test/specs/css-variables/reference.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3617ded5a9c1342f634fb1b10512e5ba964a0900 GIT binary patch literal 3975 zcmc&%?@r@J5dXeUF<;PuD(wB^zZF6vF}aIM6`DjxRdgR5XOlB>Y_Odc?wRfh`ZDdK zbjESOPC(%*A{;ATk7sv&J3ITE*@MfVcPtFW4*ve<`@c*9hir1gPEO46!>tDMGMsCM z|G7>}5cqY-%%Lu_Wgh7QgNt zX~6s?1^kBhBUs2ayJ9J7-%&TbafBfm?u?n)^FcT;o-yGW%5PpCiZ2l2oJknxXx3Bh zi-y7*QEz=wj^VT}OM#GfThWppj2G;%pF$ANX1ZF5ZW0#75sYux`I!BNzlML_1GFOz zdyR4wE;Kv?u=0aLzIYFWzHf-8uWc9C3*GuTOqLq1Vhq61Z5ZjcQ!xsCL~OqkYNXLk z@O}UUxnXZWF!pz>M;EhKtn;_`fuQua&T7vGVr_d*kszM75UUgT=1y1x*qi?a6$P(% z!v1hP)iN*nVjM4YaXicxVY-K4XqGt72gAz#en>&=CdHNceiodwAMld!~Gvq_7uN&#Xw`HdbHlPvk4<2QHIWu8SNUBb1Al{=VYeUIs_zrZrR zQ$M@)vyZv5%6=y&*bqPgfz^9Y0<8?S6p(wUWdTirS~d`YQphvt4vDeiHRs`iINS8^1z9rp@@F82`0t`}(<=AqOHtgKzdWHuB zDHtAP>I)p(RyLr#x`c4hYjiABt!24eB8Q`& z+gk2+N6*>P@_me&R!1q+CN3$RO^Z_bThq3;&`ZbO8q4)C;@TEHZ__zSQqkVhQmWy? zG7sYf^FQs2QT$EQ5}Cs+E1?PvjN5)X%b + + + + + + + In Different Class + In Same Class + Value With Space + RGB Color + Var With Space + + \ No newline at end of file From 40aa0aa2e146271c7884d3ff22c42adfc8a94c9e Mon Sep 17 00:00:00 2001 From: Zhangpengyu <18746442232@163.com> Date: Thu, 28 Apr 2022 18:55:48 +0800 Subject: [PATCH 3/5] fix: add inheritance of the css variables and fix css variable is not defined --- src/context/stylesheets.ts | 125 +++++++++++++++++++++++------- test/specs/css-variables/spec.svg | 36 ++++++--- 2 files changed, 125 insertions(+), 36 deletions(-) diff --git a/src/context/stylesheets.ts b/src/context/stylesheets.ts index 7c761729..fc609c16 100644 --- a/src/context/stylesheets.ts +++ b/src/context/stylesheets.ts @@ -1,16 +1,22 @@ import { compare as compareSpecificity } from 'specificity' import { nodeIs } from '../utils/node' +type CssVariable = { + startIndex: number + endIndex: number + originalCss: string + valueName: string[] +} export class StyleSheets { private rootSvg: Element private readonly loadExternalSheets: boolean private readonly styleSheets: CSSStyleSheet[] - private cssValueMap: Map + private cssVariableList: CssVariable[] constructor(rootSvg: Element, loadExtSheets: boolean) { this.rootSvg = rootSvg this.loadExternalSheets = loadExtSheets this.styleSheets = [] - this.cssValueMap = new Map() + this.cssVariableList = [] } public async load(): Promise { @@ -89,23 +95,64 @@ export class StyleSheets { } } - getCssValue(selector: string): string | undefined { - const value: string = selector - .replace(/var\(/g, '') - .replace(/\)/g, '') - .replace(/^\s+|\s+$/g, '') - if (this.cssValueMap.get(value)) { - return this.cssValueMap.get(value) - } - for (const sheet of this.styleSheets) { - for (let i = 0; i < sheet.cssRules.length; i++) { - const rule = sheet.cssRules[i] as CSSStyleRule - const res = rule.style.getPropertyValue(value) - if (res) { - this.cssValueMap.set(value, res) - return res + analysisCssVariables(selector: string, untreatedIndex: number): boolean { + const cssSelector = selector.slice(untreatedIndex) + let bracketsIndex = 0 + let varStart = 0 + let varEnd = 0 + let valueIndex = 0 + let valueName: string + let valueString: string + const varReg = /,(?![^(]*\))/ + if (cssSelector.indexOf('var(') >= 0) { + varStart = cssSelector.indexOf('var(') + for (let i = varStart + 4; i < cssSelector.length; i++) { + //get effective var() form selector + if (cssSelector[i] == '(') { + bracketsIndex++ + } + if (cssSelector[i] == ')') { + if (bracketsIndex == 0) { + //This is a effective var() + varEnd = i + 1 + valueString = cssSelector.slice(varStart, varEnd) + valueIndex = 0 + valueName = '' + //exclude 'var(' and ')' form cssValueString + for (let ch = 0; ch < valueString.length; ch++) { + if (valueString[ch] == 'v' && valueString.slice(ch, ch + 4) == 'var(') { + valueIndex++ + ch += 3 + continue + } + if (valueString[ch] == ')' && valueIndex > 0) { + valueIndex-- + continue + } + valueName += valueString[ch] + } + this.cssVariableList.push({ + startIndex: varStart + untreatedIndex, + endIndex: varEnd + untreatedIndex, + originalCss: valueString, + valueName: valueName.split(varReg) + }) + return this.analysisCssVariables(selector, untreatedIndex + varEnd) + } else { + bracketsIndex-- + } } } + //brackets number discord law + return false + } + //processing completed + return true + } + getCssValue(selector: string): string | undefined { + const value: string = selector.replace(/^\s+|\s+$/g, '') + this.cssVariableList = [] + if (this.analysisCssVariables(value, 0)) { } return undefined } @@ -206,17 +253,41 @@ export class StyleSheets { const mostSpecificRule = matchingRules.reduce((previousValue, currentValue) => compare(previousValue, currentValue) === 1 ? previousValue : currentValue ) - let resValue: string = mostSpecificRule.style.getPropertyValue(propertyCss) + const cssValue: string = mostSpecificRule.style.getPropertyValue(propertyCss) const varReg = /var\(.*?\)/gi - if (resValue && varReg.test(resValue)) { - const cssValueList: RegExpMatchArray = resValue.match(varReg) || [] - cssValueList.map(cssValue => { - const res = this.getCssValue(cssValue) - if (res) { - resValue = resValue.replace(cssValue, res.replace(/^\s+|\s+$/g, '')) - } - }) + if (cssValue && varReg.test(cssValue)) { + const originalValue = cssValue.replace(/^\s+|\s+$/g, '') + let res = originalValue + this.cssVariableList = [] + if (this.analysisCssVariables(originalValue, 0)) { + this.cssVariableList.map(CssVariable => { + for (let i = 0; i < CssVariable.valueName.length; i++) { + const name = CssVariable.valueName[i].replace(/^\s+|\s+$/g, '') + if (name.slice(0, 2) == '--') { + const css = getComputedStyle(node).getPropertyValue(name) + if (css) { + res = res.replace( + originalValue.substring(CssVariable.startIndex, CssVariable.endIndex), + css + ) + return + } + } else { + if (name) { + res = res.replace( + originalValue.substring(CssVariable.startIndex, CssVariable.endIndex), + name + ) + return + } + } + } + }) + return res + } else { + return cssValue + } } - return resValue || undefined + return cssValue || undefined } } diff --git a/test/specs/css-variables/spec.svg b/test/specs/css-variables/spec.svg index 595aaba1..93140777 100644 --- a/test/specs/css-variables/spec.svg +++ b/test/specs/css-variables/spec.svg @@ -1,14 +1,32 @@ - - - - + + Test Get variables globally + + Test RGB Color + + + Test normal use + + + + + Test Value With Space + + + - In Different Class - In Same Class - Value With Space - RGB Color - Var With Space + Test Var With Space + + + + Test Spare variable + + + Test Multiple variables + + + Test error value + \ No newline at end of file From 5df932c348a7707948fd780cb0ec84ad7dca4fc1 Mon Sep 17 00:00:00 2001 From: Zhangpengyu <18746442232@163.com> Date: Tue, 10 May 2022 17:30:03 +0800 Subject: [PATCH 4/5] Fix: Add support for Css variables --- src/context/stylesheets.ts | 153 +++++++++++------------------- test/specs/css-variables/spec.svg | 4 +- 2 files changed, 57 insertions(+), 100 deletions(-) diff --git a/src/context/stylesheets.ts b/src/context/stylesheets.ts index fc609c16..9b7aaf04 100644 --- a/src/context/stylesheets.ts +++ b/src/context/stylesheets.ts @@ -11,12 +11,10 @@ export class StyleSheets { private rootSvg: Element private readonly loadExternalSheets: boolean private readonly styleSheets: CSSStyleSheet[] - private cssVariableList: CssVariable[] constructor(rootSvg: Element, loadExtSheets: boolean) { this.rootSvg = rootSvg this.loadExternalSheets = loadExtSheets this.styleSheets = [] - this.cssVariableList = [] } public async load(): Promise { @@ -95,68 +93,6 @@ export class StyleSheets { } } - analysisCssVariables(selector: string, untreatedIndex: number): boolean { - const cssSelector = selector.slice(untreatedIndex) - let bracketsIndex = 0 - let varStart = 0 - let varEnd = 0 - let valueIndex = 0 - let valueName: string - let valueString: string - const varReg = /,(?![^(]*\))/ - if (cssSelector.indexOf('var(') >= 0) { - varStart = cssSelector.indexOf('var(') - for (let i = varStart + 4; i < cssSelector.length; i++) { - //get effective var() form selector - if (cssSelector[i] == '(') { - bracketsIndex++ - } - if (cssSelector[i] == ')') { - if (bracketsIndex == 0) { - //This is a effective var() - varEnd = i + 1 - valueString = cssSelector.slice(varStart, varEnd) - valueIndex = 0 - valueName = '' - //exclude 'var(' and ')' form cssValueString - for (let ch = 0; ch < valueString.length; ch++) { - if (valueString[ch] == 'v' && valueString.slice(ch, ch + 4) == 'var(') { - valueIndex++ - ch += 3 - continue - } - if (valueString[ch] == ')' && valueIndex > 0) { - valueIndex-- - continue - } - valueName += valueString[ch] - } - this.cssVariableList.push({ - startIndex: varStart + untreatedIndex, - endIndex: varEnd + untreatedIndex, - originalCss: valueString, - valueName: valueName.split(varReg) - }) - return this.analysisCssVariables(selector, untreatedIndex + varEnd) - } else { - bracketsIndex-- - } - } - } - //brackets number discord law - return false - } - //processing completed - return true - } - getCssValue(selector: string): string | undefined { - const value: string = selector.replace(/^\s+|\s+$/g, '') - this.cssVariableList = [] - if (this.analysisCssVariables(value, 0)) { - } - return undefined - } - private static splitSelectorAtCommas(selectorText: string): string[] { const initialRegex = /,|["']/g const closingDoubleQuotesRegex = /[^\\]["]/g @@ -253,41 +189,62 @@ export class StyleSheets { const mostSpecificRule = matchingRules.reduce((previousValue, currentValue) => compare(previousValue, currentValue) === 1 ? previousValue : currentValue ) - const cssValue: string = mostSpecificRule.style.getPropertyValue(propertyCss) - const varReg = /var\(.*?\)/gi - if (cssValue && varReg.test(cssValue)) { - const originalValue = cssValue.replace(/^\s+|\s+$/g, '') - let res = originalValue - this.cssVariableList = [] - if (this.analysisCssVariables(originalValue, 0)) { - this.cssVariableList.map(CssVariable => { - for (let i = 0; i < CssVariable.valueName.length; i++) { - const name = CssVariable.valueName[i].replace(/^\s+|\s+$/g, '') - if (name.slice(0, 2) == '--') { - const css = getComputedStyle(node).getPropertyValue(name) - if (css) { - res = res.replace( - originalValue.substring(CssVariable.startIndex, CssVariable.endIndex), - css - ) - return - } - } else { - if (name) { - res = res.replace( - originalValue.substring(CssVariable.startIndex, CssVariable.endIndex), - name - ) - return - } - } - } - }) - return res - } else { - return cssValue + const getPropertyCssValue = (propertyCss: string): string => { + //Screening fallback + let propertyValue = + propertyCss.indexOf('var(') == -1 + ? mostSpecificRule.style.getPropertyValue(propertyCss.trim()) + : propertyCss + const cssVariables: { + text: string //Varlables text + fallback: string // Varlables fallback + key: string //Varlables key + }[] = [] + for ( + let index = propertyValue.indexOf('var('), left = index + 4, bracketsLevels = 0; + index < propertyValue.length; + + ) { + // not found + if (index < 0) { + break + } + if (propertyValue.charAt(index) === ')') bracketsLevels-- + else if (propertyValue.charAt(index) === '(') bracketsLevels++ + if (bracketsLevels == 0 && index > left) { + const text = propertyValue.substring(left - 4, index + 1) + const inner = text.substring(4, text.length - 1) + const separator = inner.indexOf(',') + const length = inner.length + const sp = separator < 0 ? length : separator + cssVariables.push({ + text, //var(--one,var(--two,rgb(1,1,1))) + fallback: inner.substring(sp + 1), //var(--two,rgb(1,1,1)) + key: inner.substring(0, sp).trim() //--one + }) + //Process the next css Varlables + index = propertyValue.indexOf('var(', index) + left = index + 4 + continue + } + index++ + } + if (cssVariables.length === 0) { + //propertyCss is a normal cssValue return itself + //propertyCss is a cssVariable key return getPropertyValue() + return propertyValue || propertyCss.startsWith('--') ? propertyValue : propertyCss } + cssVariables.map(v => { + const value = getPropertyCssValue(v.key) + // Detect the need for fallback + propertyValue = propertyValue.replace( + v.text, + value ? value : getPropertyCssValue(v.fallback) + ) + }) + return propertyValue } - return cssValue || undefined + + return getPropertyCssValue(propertyCss) || undefined } } diff --git a/test/specs/css-variables/spec.svg b/test/specs/css-variables/spec.svg index 93140777..5b29d616 100644 --- a/test/specs/css-variables/spec.svg +++ b/test/specs/css-variables/spec.svg @@ -11,7 +11,7 @@ - Test Value With Space + Test Value With Space @@ -20,7 +20,7 @@ - Test Spare variable + Test Value fallback From fbdfebce2118989854ce417241948483674dca50 Mon Sep 17 00:00:00 2001 From: Zhangpengyu <18746442232@163.com> Date: Tue, 10 May 2022 17:34:05 +0800 Subject: [PATCH 5/5] Fix: Add support for Css variables --- src/context/stylesheets.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/context/stylesheets.ts b/src/context/stylesheets.ts index 9b7aaf04..eea79173 100644 --- a/src/context/stylesheets.ts +++ b/src/context/stylesheets.ts @@ -1,12 +1,6 @@ import { compare as compareSpecificity } from 'specificity' import { nodeIs } from '../utils/node' -type CssVariable = { - startIndex: number - endIndex: number - originalCss: string - valueName: string[] -} export class StyleSheets { private rootSvg: Element private readonly loadExternalSheets: boolean