diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index 6d414f1b7..d7cc746bd 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -49,6 +49,7 @@ + diff --git a/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/AbstractSVGLength.java b/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/AbstractSVGLength.java index 97854996c..cf0f16c33 100644 --- a/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/AbstractSVGLength.java +++ b/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/AbstractSVGLength.java @@ -242,12 +242,9 @@ public Element getElement() { return getAssociatedElement(); } - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return getAssociatedElement().getSVGContext().getPixelUnitToMillimeter(); + public float getResolution() { + return getAssociatedElement().getSVGContext().getResolution(); } /** diff --git a/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/SVGOMAnimatedRect.java b/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/SVGOMAnimatedRect.java index 1705662f6..19e8130c1 100644 --- a/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/SVGOMAnimatedRect.java +++ b/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/SVGOMAnimatedRect.java @@ -18,17 +18,17 @@ */ package io.sf.carte.echosvg.anim.dom; +import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.svg.SVGAnimatedRect; import org.w3c.dom.svg.SVGRect; -import io.sf.carte.doc.style.css.CSSUnit; +import io.sf.carte.doc.style.css.CSSExpressionValue; +import io.sf.carte.doc.style.css.CSSTypedValue; import io.sf.carte.doc.style.css.CSSValue.CssType; import io.sf.carte.doc.style.css.property.Evaluator; -import io.sf.carte.doc.style.css.property.ExpressionValue; import io.sf.carte.doc.style.css.property.StyleValue; -import io.sf.carte.doc.style.css.property.TypedValue; import io.sf.carte.doc.style.css.property.ValueFactory; import io.sf.carte.doc.style.css.property.ValueList; import io.sf.carte.echosvg.anim.values.AnimatableRectValue; @@ -286,7 +286,7 @@ private boolean computeRectangle(StyleValue value, float[] numbers) throws DOMEx if (item.getCssValueType() != CssType.TYPED) { return false; } - TypedValue typed = (TypedValue) item; + CSSTypedValue typed = (CSSTypedValue) item; switch (item.getPrimitiveType()) { case NUMERIC: if (typed.getUnitType() != CSSUnit.CSS_NUMBER) { @@ -294,8 +294,8 @@ private boolean computeRectangle(StyleValue value, float[] numbers) throws DOMEx } break; case EXPRESSION: - Evaluator eval = new Evaluator(); - typed = eval.evaluateExpression((ExpressionValue) typed); + Evaluator eval = new Evaluator(CSSUnit.CSS_NUMBER); + typed = eval.evaluateExpression((CSSExpressionValue) typed); if (typed.getUnitType() != CSSUnit.CSS_NUMBER) { return false; } diff --git a/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/SVGOMElement.java b/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/SVGOMElement.java index bad75364f..6b67258c2 100644 --- a/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/SVGOMElement.java +++ b/echosvg-anim/src/main/java/io/sf/carte/echosvg/anim/dom/SVGOMElement.java @@ -877,12 +877,9 @@ public Element getElement() { return SVGOMElement.this; } - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return getSVGContext().getPixelUnitToMillimeter(); + public float getResolution() { + return getSVGContext().getResolution(); } /** diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/AbstractGraphicsNodeBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/AbstractGraphicsNodeBridge.java index 813cf3b45..d77737eb2 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/AbstractGraphicsNodeBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/AbstractGraphicsNodeBridge.java @@ -490,12 +490,9 @@ protected void fireBBoxChangeEvent() { // SVGContext implementation /////////////////////////////////////////// - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return ctx.getUserAgent().getPixelUnitToMillimeter(); + public float getResolution() { + return ctx.getUserAgent().getResolution(); } protected SoftReference bboxShape = null; diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/AnimatableGenericSVGBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/AnimatableGenericSVGBridge.java index 01a8f335f..bb74dae71 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/AnimatableGenericSVGBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/AnimatableGenericSVGBridge.java @@ -59,12 +59,9 @@ public void handleElement(BridgeContext ctx, Element e) { // SVGContext //////////////////////////////////////////////////////////// - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return ctx.getUserAgent().getPixelUnitToMillimeter(); + public float getResolution() { + return ctx.getUserAgent().getResolution(); } /** diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/BridgeContext.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/BridgeContext.java index 05bdca8c8..fc649e5bb 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/BridgeContext.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/BridgeContext.java @@ -70,8 +70,8 @@ import io.sf.carte.echosvg.dom.events.NodeEventTarget; import io.sf.carte.echosvg.dom.svg.SVGContext; import io.sf.carte.echosvg.dom.xbl.XBLManager; -import io.sf.carte.echosvg.ext.awt.color.StandardColorSpaces; import io.sf.carte.echosvg.ext.awt.color.ColorContext; +import io.sf.carte.echosvg.ext.awt.color.StandardColorSpaces; import io.sf.carte.echosvg.gvt.CompositeGraphicsNode; import io.sf.carte.echosvg.gvt.GraphicsNode; import io.sf.carte.echosvg.script.Interpreter; @@ -1830,12 +1830,23 @@ public float getBolderFontWeight(float f) { /** * Returns the size of a px CSS unit in millimeters. + * + * @deprecated Use {@link #getResolution()}. */ + @Deprecated @Override public float getPixelUnitToMillimeter() { return userAgent.getPixelUnitToMillimeter(); } + /** + * Returns the resolution in dpi. + */ + @Override + public float getResolution() { + return userAgent.getResolution(); + } + /** * Returns the medium font size. */ @@ -1877,6 +1888,14 @@ public void checkLoadExternalResource(ParsedURL resourceURL, ParsedURL docURL) t userAgent.checkLoadExternalResource(resourceURL, docURL); } + /** + * Get prefers-color-scheme. + */ + @Override + public String getPrefersColorScheme() { + return userAgent.getPrefersColorScheme(); + } + /** * Tells whether the given SVG document is dynamic. */ diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CSSFontFace.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CSSFontFace.java index 92d7b0ea1..0ba8313b6 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CSSFontFace.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CSSFontFace.java @@ -21,8 +21,8 @@ import java.util.LinkedList; import java.util.List; -import io.sf.carte.echosvg.css.dom.CSSValue.CssType; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; +import io.sf.carte.doc.style.css.CSSValue.CssType; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.FontFaceRule; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CSSUtilities.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CSSUtilities.java index 73b926d71..0d7c03d54 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CSSUtilities.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CSSUtilities.java @@ -28,10 +28,10 @@ import org.w3c.dom.Element; +import io.sf.carte.doc.style.css.CSSValue.CssType; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.echosvg.anim.dom.SVGOMDocument; import io.sf.carte.echosvg.constants.XMLConstants; -import io.sf.carte.echosvg.css.dom.CSSValue.CssType; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CursorManager.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CursorManager.java index 89114b1b7..4f6e33c3e 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CursorManager.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/CursorManager.java @@ -40,8 +40,8 @@ import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGPreserveAspectRatio; -import io.sf.carte.echosvg.css.dom.CSSValue.CssType; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; +import io.sf.carte.doc.style.css.CSSValue.CssType; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.dom.AbstractNode; diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/PaintServer.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/PaintServer.java index 8482d810c..de653a699 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/PaintServer.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/PaintServer.java @@ -32,8 +32,8 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.Element; -import io.sf.carte.echosvg.css.dom.CSSValue.CssType; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; +import io.sf.carte.doc.style.css.CSSValue.CssType; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; import io.sf.carte.echosvg.css.engine.value.ColorFunction; import io.sf.carte.echosvg.css.engine.value.ColorValue; @@ -305,7 +305,8 @@ public static Paint convertPaint(Element paintedElement, GraphicsNode paintedNod break; } } - throw new IllegalArgumentException("Paint argument is not an appropriate CSS value"); + throw new IllegalArgumentException( + "Paint argument is not an appropriate CSS value (" + paintDef.getCssText() + ")."); } /** @@ -652,7 +653,7 @@ private static Color convert3Color(ColorSpace space, ColorFunction c, float opac */ private static float resolveColorComponent(NumericValue item) { float f; - switch (item.getCSSUnit()) { + switch (item.getUnitType()) { case CSSUnit.CSS_NUMBER: f = item.getFloatValue(); if (f < 0f) { @@ -831,7 +832,7 @@ public static int convertStrokeLinejoin(Value v) { */ private static float resolveAlphaComponent(Value v) { float f; - switch (v.getCSSUnit()) { + switch (v.getUnitType()) { case CSSUnit.CSS_PERCENTAGE: f = v.getFloatValue(); f = (f > 100f) ? 100f : (f < 0f) ? 0f : f; diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGAnimationElementBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGAnimationElementBridge.java index 2104089b6..de11b0e5a 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGAnimationElementBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGAnimationElementBridge.java @@ -386,12 +386,9 @@ public void dispose() { // SVGContext /////////////////////////////////////////////////////////// - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return ctx.getUserAgent().getPixelUnitToMillimeter(); + public float getResolution() { + return ctx.getUserAgent().getResolution(); } @Override diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGAnimationEngine.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGAnimationEngine.java index 48ba03649..60e3cadcc 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGAnimationEngine.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGAnimationEngine.java @@ -38,6 +38,8 @@ import org.w3c.dom.svg.SVGLength; import org.w3c.dom.svg.SVGPreserveAspectRatio; +import io.sf.carte.doc.style.css.CSSValue.CssType; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.property.NumberValue; import io.sf.carte.echosvg.anim.AnimationEngine; import io.sf.carte.echosvg.anim.AnimationException; @@ -67,8 +69,6 @@ import io.sf.carte.echosvg.anim.values.AnimatableStringValue; import io.sf.carte.echosvg.anim.values.AnimatableValue; import io.sf.carte.echosvg.constants.XMLConstants; -import io.sf.carte.echosvg.css.dom.CSSValue.CssType; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; @@ -1203,7 +1203,7 @@ public AnimatableValue createValue(AnimationTarget target, String ns, String ln, */ @Override public AnimatableValue createValue(AnimationTarget target, String pn, Value v) { - switch (v.getCSSUnit()) { + switch (v.getUnitType()) { case CSSUnit.CSS_PERCENTAGE: return new AnimatableNumberOrPercentageValue(target, v.getFloatValue(), true); case CSSUnit.CSS_NUMBER: @@ -1767,7 +1767,7 @@ protected AnimatableValue createAnimatableValue(AnimationTarget target, String p return new AnimatableLengthOrIdentValue(target, v.getIdentifierValue()); } short pcInterp = target.getPercentageInterpretation(null, pn, true); - return new AnimatableLengthOrIdentValue(target, v.getCSSUnit(), v.getFloatValue(), pcInterp); + return new AnimatableLengthOrIdentValue(target, v.getUnitType(), v.getFloatValue(), pcInterp); } } @@ -1805,7 +1805,7 @@ protected class AnimatableAngleValueFactory extends CSSValueFactory { @Override protected AnimatableValue createAnimatableValue(AnimationTarget target, String pn, Value v) { short unit; - switch (v.getCSSUnit()) { + switch (v.getUnitType()) { case CSSUnit.CSS_NUMBER: case CSSUnit.CSS_DEG: unit = SVGAngle.SVG_ANGLETYPE_DEG; @@ -1818,7 +1818,7 @@ protected AnimatableValue createAnimatableValue(AnimationTarget target, String p break; default: try { - float f = NumberValue.floatValueConversion(v.getFloatValue(), v.getCSSUnit(), + float f = NumberValue.floatValueConversion(v.getFloatValue(), v.getUnitType(), CSSUnit.CSS_DEG); return new AnimatableAngleOrIdentValue(target, f, SVGAngle.SVG_ANGLETYPE_DEG); } catch (DOMException e) { @@ -1842,7 +1842,7 @@ protected AnimatableValue createAnimatableValue(AnimationTarget target, String p return new AnimatableAngleOrIdentValue(target, v.getIdentifierValue()); } short unit; - switch (v.getCSSUnit()) { + switch (v.getUnitType()) { case CSSUnit.CSS_NUMBER: case CSSUnit.CSS_DEG: unit = SVGAngle.SVG_ANGLETYPE_DEG; @@ -1855,7 +1855,7 @@ protected AnimatableValue createAnimatableValue(AnimationTarget target, String p break; default: try { - float f = NumberValue.floatValueConversion(v.getFloatValue(), v.getCSSUnit(), + float f = NumberValue.floatValueConversion(v.getFloatValue(), v.getUnitType(), CSSUnit.CSS_DEG); return new AnimatableAngleOrIdentValue(target, f, SVGAngle.SVG_ANGLETYPE_DEG); } catch (DOMException e) { diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGDescriptiveElementBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGDescriptiveElementBridge.java index 9a62c56f4..557bb4180 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGDescriptiveElementBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGDescriptiveElementBridge.java @@ -117,12 +117,9 @@ public void handleOtherAnimationChanged(String type) { // SVGContext implementation /////////////////////////////////////////// - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return theCtx.getUserAgent().getPixelUnitToMillimeter(); + public float getResolution() { + return theCtx.getUserAgent().getResolution(); } @Override diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGDocumentBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGDocumentBridge.java index abb28904b..0e69203db 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGDocumentBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGDocumentBridge.java @@ -203,12 +203,9 @@ public void dispose() { // SVGContext ////////////////////////////////////////////////////////// - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return ctx.getUserAgent().getPixelUnitToMillimeter(); + public float getResolution() { + return ctx.getUserAgent().getResolution(); } @Override diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGTextElementBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGTextElementBridge.java index c4cbd8486..6f1de47a0 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGTextElementBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGTextElementBridge.java @@ -51,6 +51,7 @@ import org.w3c.dom.svg.SVGTextContentElement; import org.w3c.dom.svg.SVGTextPositioningElement; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.property.NumberValue; import io.sf.carte.echosvg.anim.dom.AbstractSVGAnimatedLength; import io.sf.carte.echosvg.anim.dom.AnimatedLiveAttributeValue; @@ -61,7 +62,6 @@ import io.sf.carte.echosvg.anim.dom.SVGOMTextPositioningElement; import io.sf.carte.echosvg.bridge.StrokingTextPainter.TextRun; import io.sf.carte.echosvg.constants.XMLConstants; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngineEvent; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; @@ -1564,7 +1564,7 @@ protected Map getAttributeMap(BridgeContext ctx, Element elem GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_AUTO); break; case NUMERIC: - switch (val.getCSSUnit()) { + switch (val.getUnitType()) { case CSSUnit.CSS_DEG: case CSSUnit.CSS_NUMBER: result.put(GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION, @@ -1584,8 +1584,8 @@ protected Map getAttributeMap(BridgeContext ctx, Element elem val.getFloatValue() * 9 / 5); break; default: - if (CSSUnit.isAngleUnitType(val.getCSSUnit())) { - float f = NumberValue.floatValueConversion(val.getFloatValue(), val.getCSSUnit(), CSSUnit.CSS_DEG); + if (CSSUnit.isAngleUnitType(val.getUnitType())) { + float f = NumberValue.floatValueConversion(val.getFloatValue(), val.getUnitType(), CSSUnit.CSS_DEG); result.put(GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION, GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE); result.put(GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION_ANGLE, f); @@ -1601,11 +1601,11 @@ protected Map getAttributeMap(BridgeContext ctx, Element elem // glyph-orientation-horizontal val = CSSUtilities.getComputedStyle(element, SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX); - short unit = val.getCSSUnit(); + short unit = val.getUnitType(); if (unit == CSSUnit.CSS_NUMBER || unit == CSSUnit.CSS_DEG) { result.put(GVTAttributedCharacterIterator.TextAttribute.HORIZONTAL_ORIENTATION_ANGLE, val.getFloatValue()); } else if (CSSUnit.isAngleUnitType(unit)) { - float f = NumberValue.floatValueConversion(val.getFloatValue(), val.getCSSUnit(), CSSUnit.CSS_DEG); + float f = NumberValue.floatValueConversion(val.getFloatValue(), val.getUnitType(), CSSUnit.CSS_DEG); result.put(GVTAttributedCharacterIterator.TextAttribute.HORIZONTAL_ORIENTATION_ANGLE, f); } else { throw new IllegalStateException("unexpected value (H):" + val.getCssText()); @@ -1843,12 +1843,9 @@ public SVGTextElementBridge getTextBridge() { return textBridge; } - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return ctx.getUserAgent().getPixelUnitToMillimeter(); + public float getResolution() { + return ctx.getUserAgent().getResolution(); } /** diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/TextUtilities.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/TextUtilities.java index 95f0b5e34..7d124ceab 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/TextUtilities.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/TextUtilities.java @@ -25,7 +25,7 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.util.CSSConstants; diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UnitProcessor.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UnitProcessor.java index 5d12a1f4e..07bcff3a6 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UnitProcessor.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UnitProcessor.java @@ -310,12 +310,9 @@ public Element getElement() { return e; } - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { - return ctx.getUserAgent().getPixelUnitToMillimeter(); + public float getResolution() { + return ctx.getUserAgent().getResolution(); } /** diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UserAgent.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UserAgent.java index 2fcd675b0..935a9586e 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UserAgent.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UserAgent.java @@ -88,8 +88,13 @@ public interface UserAgent { /** * Returns the size of a px CSS unit in millimeters. + * + * @deprecated Use {@link #getResolution()}. */ - float getPixelUnitToMillimeter(); + @Deprecated + default float getPixelUnitToMillimeter() { + return 25.4f / getResolution(); + } /** * Returns the size of a px CSS unit in millimeters. This will be removed after @@ -102,6 +107,11 @@ default float getPixelToMM() { return getPixelUnitToMillimeter(); } + /** + * Returns the resolution in {@code dpi}. + */ + float getResolution(); + /** * Returns the medium font size. */ @@ -183,6 +193,13 @@ default float getPixelToMM() { */ String getMedia(); + /** + * Get prefers-color-scheme. + */ + default String getPrefersColorScheme() { + return "light"; + } + /** * Returns this user agent's alternate style-sheet title. */ diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UserAgentAdapter.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UserAgentAdapter.java index 86d849f3e..f7794bcd8 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UserAgentAdapter.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/UserAgentAdapter.java @@ -137,6 +137,14 @@ public float getPixelUnitToMillimeter() { return 0.26458333333333333333333333333333f; // 96dpi } + /** + * Returns the resolution in {@code dpi}. + */ + @Override + public float getResolution() { + return 96f; + } + /** * Returns the default font family. */ @@ -151,7 +159,7 @@ public String getDefaultFontFamily() { @Override public float getMediumFontSize() { // 9pt (72pt = 1in) - return 9f * 25.4f / (72f * getPixelUnitToMillimeter()); + return 9f * getResolution() / 72f; } /** diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/ViewBox.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/ViewBox.java index c5cbdb595..ab2764ca4 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/ViewBox.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/ViewBox.java @@ -20,6 +20,7 @@ import java.awt.geom.AffineTransform; +import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -28,12 +29,11 @@ import org.w3c.dom.svg.SVGAnimatedRect; import org.w3c.dom.svg.SVGPreserveAspectRatio; -import io.sf.carte.doc.style.css.CSSUnit; +import io.sf.carte.doc.style.css.CSSExpressionValue; +import io.sf.carte.doc.style.css.CSSTypedValue; import io.sf.carte.doc.style.css.CSSValue.CssType; import io.sf.carte.doc.style.css.property.Evaluator; -import io.sf.carte.doc.style.css.property.ExpressionValue; import io.sf.carte.doc.style.css.property.StyleValue; -import io.sf.carte.doc.style.css.property.TypedValue; import io.sf.carte.doc.style.css.property.ValueFactory; import io.sf.carte.doc.style.css.property.ValueList; import io.sf.carte.echosvg.anim.dom.SVGOMAnimatedRect; @@ -400,17 +400,17 @@ static boolean computeRectangle(StyleValue value, float[] numbers) throws DOMExc if (item.getCssValueType() != CssType.TYPED) { return false; } - TypedValue typed; + CSSTypedValue typed; switch (item.getPrimitiveType()) { case NUMERIC: - typed = (TypedValue) item; + typed = (CSSTypedValue) item; if (typed.getUnitType() != CSSUnit.CSS_NUMBER) { return false; } break; case EXPRESSION: - Evaluator eval = new Evaluator(); - typed = eval.evaluateExpression((ExpressionValue) item); + Evaluator eval = new Evaluator(CSSUnit.CSS_NUMBER); + typed = eval.evaluateExpression((CSSExpressionValue) item); if (typed.getUnitType() != CSSUnit.CSS_NUMBER) { return false; } diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/svg12/SVGFlowRootElementBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/svg12/SVGFlowRootElementBridge.java index ee8b02643..8a6061dda 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/svg12/SVGFlowRootElementBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/svg12/SVGFlowRootElementBridge.java @@ -40,6 +40,8 @@ import org.w3c.dom.events.Event; import org.w3c.dom.events.EventListener; +import io.sf.carte.doc.style.css.CSSValue; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.echosvg.anim.dom.SVGOMElement; import io.sf.carte.echosvg.anim.dom.SVGOMFlowRegionElement; import io.sf.carte.echosvg.anim.dom.XBLEventSupport; @@ -56,12 +58,10 @@ import io.sf.carte.echosvg.bridge.TextUtilities; import io.sf.carte.echosvg.bridge.UserAgent; import io.sf.carte.echosvg.constants.XMLConstants; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; import io.sf.carte.echosvg.css.engine.value.ComputedValue; import io.sf.carte.echosvg.css.engine.value.Value; -import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.svg12.LineHeightValue; import io.sf.carte.echosvg.css.engine.value.svg12.SVG12ValueConstants; import io.sf.carte.echosvg.dom.AbstractNode; @@ -849,7 +849,8 @@ public BlockInfo makeBlockInfo(BridgeContext ctx, Element element) { float indent = v.getFloatValue(); v = CSSUtilities.getComputedStyle(element, textAlignIndex); - if (v == ValueConstants.INHERIT_VALUE) { + if (v.getCssValueType() == CSSValue.CssType.KEYWORD) { + // inherit, unset, revert v = CSSUtilities.getComputedStyle(element, SVGCSSEngine.DIRECTION_INDEX); if (v.isIdentifier(CSSConstants.CSS_LTR_VALUE)) v = SVG12ValueConstants.START_VALUE; @@ -888,7 +889,7 @@ protected float getLineHeight(BridgeContext ctx, Element element, float fontSize initCSSPropertyIndexes(element); Value v = CSSUtilities.getComputedStyle(element, lineHeightIndex); - if (v == ValueConstants.INHERIT_VALUE || v.isIdentifier(CSSConstants.CSS_NORMAL_VALUE)) { + if (v.getCssValueType() == CSSValue.CssType.KEYWORD || v.isIdentifier(CSSConstants.CSS_NORMAL_VALUE)) { return fontSize * 1.1f; } diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/svg12/SVGSolidColorElementBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/svg12/SVGSolidColorElementBridge.java index 084bbcd5f..8609bff08 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/svg12/SVGSolidColorElementBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/svg12/SVGSolidColorElementBridge.java @@ -25,6 +25,7 @@ import org.w3c.dom.Element; +import io.sf.carte.doc.style.css.CSSValue.CssType; import io.sf.carte.echosvg.anim.dom.SVGOMDocument; import io.sf.carte.echosvg.bridge.AnimatableGenericSVGBridge; import io.sf.carte.echosvg.bridge.BridgeContext; @@ -33,7 +34,6 @@ import io.sf.carte.echosvg.bridge.ErrorConstants; import io.sf.carte.echosvg.bridge.PaintBridge; import io.sf.carte.echosvg.bridge.PaintServer; -import io.sf.carte.echosvg.css.dom.CSSValue.CssType; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; diff --git a/echosvg-bridge/src/test/java/io/sf/carte/echosvg/bridge/ModificationsTest.java b/echosvg-bridge/src/test/java/io/sf/carte/echosvg/bridge/ModificationsTest.java index 07098ad0e..2a59b32af 100644 --- a/echosvg-bridge/src/test/java/io/sf/carte/echosvg/bridge/ModificationsTest.java +++ b/echosvg-bridge/src/test/java/io/sf/carte/echosvg/bridge/ModificationsTest.java @@ -36,11 +36,11 @@ import org.w3c.dom.view.ViewCSS; import org.w3c.dom.views.DocumentView; +import io.sf.carte.doc.style.css.CSSValue.CssType; import io.sf.carte.echosvg.anim.dom.SVGDOMImplementation; import io.sf.carte.echosvg.anim.dom.SVGStylableElement; import io.sf.carte.echosvg.constants.XMLConstants; -import io.sf.carte.echosvg.css.dom.CSSValue; -import io.sf.carte.echosvg.css.dom.CSSValue.CssType; +import io.sf.carte.echosvg.css.engine.value.CSSVal; import io.sf.carte.echosvg.gvt.GraphicsNode; import io.sf.carte.echosvg.util.CSSConstants; import io.sf.carte.echosvg.util.SVGConstants; @@ -74,7 +74,7 @@ static CSSStyleValue updateValue(String ptyName, String ptyValue, String newValu ViewCSS view = (ViewCSS) ((DocumentView) doc).getDefaultView(); CSSStyleDeclaration cs = view.getComputedStyle(rect, null); - CSSValue val = (CSSValue) cs.getCSSStyleValue(ptyName); + CSSVal val = (CSSVal) cs.getCSSStyleValue(ptyName); assertNotNull(val); assertEquals(CssType.TYPED, val.getCssValueType()); diff --git a/echosvg-bridge/src/test/java/io/sf/carte/echosvg/bridge/PaintServerTest.java b/echosvg-bridge/src/test/java/io/sf/carte/echosvg/bridge/PaintServerTest.java index 30de55976..1cb4fbcad 100644 --- a/echosvg-bridge/src/test/java/io/sf/carte/echosvg/bridge/PaintServerTest.java +++ b/echosvg-bridge/src/test/java/io/sf/carte/echosvg/bridge/PaintServerTest.java @@ -38,9 +38,9 @@ import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGSVGElement; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.echosvg.anim.dom.SVGDOMImplementation; import io.sf.carte.echosvg.constants.XMLConstants; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.value.ColorValue; @@ -323,6 +323,35 @@ public void testConvertLCh() { assertSame(StandardColorSpaces.getRec2020(), context.getColorSpace()); } + @Test + public void testConvertLCh_sRGB() { + Color color = convertPaint(CSSConstants.CSS_FILL_PROPERTY, "lch(48% 77 33)", null); + assertNotNull(color); + float[] comp = new float[3]; + color.getColorComponents(comp); + assertEquals(0.835736f, comp[0], 1e-5f); // sRGB + assertEquals(0.17189053f, comp[1], 1e-5f); // sRGB + assertEquals(0.18918473f, comp[2], 1e-5f); // sRGB + assertEquals(255, color.getAlpha()); + + assertNull(context.getColorSpace()); + } + + @Test + public void testConvertLCh_var() { + Color color = convertPaint(CSSConstants.CSS_FILL_PROPERTY, "var(--color);--color:lch(48% 77 33)", + null); + assertNotNull(color); + float[] comp = new float[3]; + color.getColorComponents(comp); + assertEquals(0.835736f, comp[0], 1e-5f); // sRGB + assertEquals(0.17189053f, comp[1], 1e-5f); // sRGB + assertEquals(0.18918473f, comp[2], 1e-5f); // sRGB + assertEquals(255, color.getAlpha()); + + assertNull(context.getColorSpace()); + } + @Test public void testConvertLCh_99Pcnt() { Color color = convertPaint(CSSConstants.CSS_FILL_PROPERTY, "lch(99% 110 60)", null); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/CSSSecurityException.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/CSSSecurityException.java new file mode 100644 index 000000000..22cfcefc1 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/CSSSecurityException.java @@ -0,0 +1,65 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css; + +/** + * A CSS-related security exception. + */ +public class CSSSecurityException extends SecurityException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a {@code CSSSecurityException} with no detail message. + */ + public CSSSecurityException() { + super(); + } + + /** + * Creates a {@code CSSSecurityException} with the specified cause and a detail + * message which typically contains the class and detail message of cause. + * + * @param cause the cause. + */ + public CSSSecurityException(Throwable cause) { + super(cause); + } + + /** + * Constructs a {@code CSSSecurityException} with a detail message. + * + * @param message the message. + */ + public CSSSecurityException(String message) { + super(message); + } + + /** + * Creates a {@code CSSSecurityException} with the specified detail message and + * cause. + * + * @param message the message. + * @param cause the cause. + */ + public CSSSecurityException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMComputedStyle.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMComputedStyle.java index 4cfa82f2c..e7a7986e4 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMComputedStyle.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMComputedStyle.java @@ -27,6 +27,7 @@ import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; +import io.sf.carte.echosvg.css.engine.value.CSSVal; import io.sf.carte.echosvg.css.engine.value.Value; /** @@ -58,7 +59,7 @@ public class CSSOMComputedStyle implements CSSStyleDeclaration { /** * The CSS values. */ - protected Map values = new HashMap<>(); + protected Map values = new HashMap<>(); /** * Creates a new computed style. @@ -110,12 +111,12 @@ public String getPropertyValue(String propertyName) { } @Override - public CSSValue getCSSStyleValue(String propertyName) { + public CSSVal getCSSStyleValue(String propertyName) { return getCSSValue(propertyName); } - public CSSValue getCSSValue(String propertyName) { - CSSValue result = values.get(propertyName); + public CSSVal getCSSValue(String propertyName) { + CSSVal result = values.get(propertyName); if (result == null) { int idx = cssEngine.getPropertyIndex(propertyName); if (idx != -1) { @@ -187,7 +188,7 @@ public CSSRule getParentRule() { /** * Creates a CSSValue to manage the value at the given index. */ - protected CSSValue createCSSValue(int idx) { + protected CSSVal createCSSValue(int idx) { return new ComputedCSSValue(idx); } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGColor.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGColor.java index 63c91db66..4781d274d 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGColor.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGColor.java @@ -26,6 +26,7 @@ import org.w3c.dom.svg.SVGNumber; import org.w3c.dom.svg.SVGNumberList; +import io.sf.carte.echosvg.css.engine.value.CSSVal; import io.sf.carte.echosvg.css.engine.value.ColorFunction; import io.sf.carte.echosvg.css.engine.value.ColorValue; import io.sf.carte.echosvg.css.engine.value.NumericValue; @@ -44,7 +45,7 @@ * @version $Id$ */ @SuppressWarnings("removal") -public class CSSOMSVGColor implements CSSValue, CSSColorValue, SVGICCColor, SVGNumberList { +public class CSSOMSVGColor implements CSSVal, CSSColorValue, SVGICCColor, SVGNumberList { /** * The associated value. @@ -126,6 +127,11 @@ public String getURIValue() throws DOMException { return valueProvider.getValue().getURIValue(); } + @Override + public Value clone() { + return valueProvider.getValue().clone(); + } + /** * DOM: Implements {@link org.w3c.dom.svg.SVGColor#getColorType()}. */ diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGComputedStyle.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGComputedStyle.java index 1dd5ee7de..1b7c923c6 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGComputedStyle.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGComputedStyle.java @@ -21,6 +21,7 @@ import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; +import io.sf.carte.echosvg.css.engine.value.CSSVal; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.svg.SVGColorManager; import io.sf.carte.echosvg.css.engine.value.svg.SVGPaintManager; @@ -45,7 +46,7 @@ public CSSOMSVGComputedStyle(CSSEngine e, CSSStylableElement elt, String pseudoE * Creates a CSSValue to manage the value at the given index. */ @Override - protected CSSValue createCSSValue(int idx) { + protected CSSVal createCSSValue(int idx) { if (idx > SVGCSSEngine.FINAL_INDEX) { if (cssEngine.getValueManagers()[idx] instanceof SVGPaintManager) { return new ComputedCSSPaintValue(idx); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGStyleDeclaration.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGStyleDeclaration.java index d942e17e5..0baf3163b 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGStyleDeclaration.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMSVGStyleDeclaration.java @@ -24,6 +24,7 @@ import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueModificationHandler; +import io.sf.carte.echosvg.css.engine.value.CSSVal; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.svg.SVGColorManager; import io.sf.carte.echosvg.css.engine.value.svg.SVGPaintManager; @@ -56,7 +57,7 @@ public CSSOMSVGStyleDeclaration(ValueProvider vp, CSSRule parent, CSSEngine eng) * Creates the CSS value associated with the given property. */ @Override - protected CSSValue createCSSValue(String name) { + protected CSSVal createCSSValue(String name) { int idx = cssEngine.getPropertyIndex(name); if (idx > SVGCSSEngine.FINAL_INDEX) { if (cssEngine.getValueManagers()[idx] instanceof SVGPaintManager) { diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMStyleDeclaration.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMStyleDeclaration.java index 1ed9456d5..c299c4be7 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMStyleDeclaration.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMStyleDeclaration.java @@ -26,6 +26,7 @@ import org.w3c.dom.DOMException; import io.sf.carte.echosvg.css.engine.value.AbstractValueModificationHandler; +import io.sf.carte.echosvg.css.engine.value.CSSVal; import io.sf.carte.echosvg.css.engine.value.Value; /** @@ -57,7 +58,7 @@ public class CSSOMStyleDeclaration implements CSSStyleDeclaration { /** * The values. */ - protected Map values; + protected Map values; /** * Creates a new style declaration. @@ -99,7 +100,7 @@ public String getPropertyValue(String propertyName) { } @Override - public CSSValue getCSSStyleValue(String propertyName) { + public CSSVal getCSSStyleValue(String propertyName) { Value value = valueProvider.getValue(propertyName); if (value == null) { return null; @@ -158,8 +159,8 @@ public CSSRule getParentRule() { /** * Gets the CSS value associated with the given property. */ - protected CSSValue getCSSValue(String name) { - CSSValue result = null; + protected CSSVal getCSSValue(String name) { + CSSVal result = null; if (values != null) { result = values.get(name); } @@ -176,7 +177,7 @@ protected CSSValue getCSSValue(String name) { /** * Creates the CSS value associated with the given property. */ - protected CSSValue createCSSValue(String name) { + protected CSSVal createCSSValue(String name) { return new StyleDeclarationValue(name); } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMValue.java index fe3109d44..ec7193bfe 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSOMValue.java @@ -109,7 +109,7 @@ public float getFloatValue() throws DOMException { * Converts the actual float value to the given unit type. */ public static float convertFloatValue(short unitType, Value value) { - if (value.getCSSUnit() == unitType) { + if (value.getUnitType() == unitType) { return value.getFloatValue(); } switch (unitType) { @@ -153,7 +153,7 @@ public static float convertFloatValue(short unitType, Value value) { case CSSUnit.CSS_KHZ: return tokHertz(value); default: - return NumberValue.floatValueConversion(value.getFloatValue(), value.getCSSUnit(), + return NumberValue.floatValueConversion(value.getFloatValue(), value.getUnitType(), unitType); } throw new DOMException(DOMException.INVALID_ACCESS_ERR, ""); @@ -163,7 +163,7 @@ public static float convertFloatValue(short unitType, Value value) { * Converts the current value into centimeters. */ protected static float toCentimeters(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_CM: return value.getFloatValue(); case CSSUnit.CSS_MM: @@ -183,7 +183,7 @@ protected static float toCentimeters(Value value) { * Converts the current value into inches. */ protected static float toInches(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_CM: return (value.getFloatValue() / 2.54f); case CSSUnit.CSS_MM: @@ -203,7 +203,7 @@ protected static float toInches(Value value) { * Converts the current value into millimeters. */ protected static float toMillimeters(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_CM: return (value.getFloatValue() * 10); case CSSUnit.CSS_MM: @@ -223,7 +223,7 @@ protected static float toMillimeters(Value value) { * Converts the current value into points. */ protected static float toPoints(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_CM: return (value.getFloatValue() * 72 / 2.54f); case CSSUnit.CSS_MM: @@ -243,7 +243,7 @@ protected static float toPoints(Value value) { * Converts the current value into picas. */ protected static float toPicas(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_CM: return (value.getFloatValue() * 6 / 2.54f); case CSSUnit.CSS_MM: @@ -263,7 +263,7 @@ protected static float toPicas(Value value) { * Converts the current value into degrees. */ protected static float toDegrees(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_DEG: return value.getFloatValue(); case CSSUnit.CSS_RAD: @@ -279,7 +279,7 @@ protected static float toDegrees(Value value) { * Converts the current value into radians. */ protected static float toRadians(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_DEG: return (value.getFloatValue() * 5 / 9); // todo ?? case CSSUnit.CSS_RAD: @@ -295,7 +295,7 @@ protected static float toRadians(Value value) { * Converts the current value into gradians. */ protected static float toGradians(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_DEG: return (float) (value.getFloatValue() * Math.PI / 180); // todo ???? case CSSUnit.CSS_RAD: @@ -311,7 +311,7 @@ protected static float toGradians(Value value) { * Converts the current value into milliseconds. */ protected static float toMilliseconds(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_MS: return value.getFloatValue(); case CSSUnit.CSS_S: @@ -325,7 +325,7 @@ protected static float toMilliseconds(Value value) { * Converts the current value into seconds. */ protected static float toSeconds(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_MS: return (value.getFloatValue() / 1000); case CSSUnit.CSS_S: @@ -339,7 +339,7 @@ protected static float toSeconds(Value value) { * Converts the current value into Hertz. */ protected static float toHertz(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_HZ: return value.getFloatValue(); case CSSUnit.CSS_KHZ: @@ -353,7 +353,7 @@ protected static float toHertz(Value value) { * Converts the current value into kHertz. */ protected static float tokHertz(Value value) { - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_HZ: return (value.getFloatValue() * 1000); case CSSUnit.CSS_KHZ: @@ -369,8 +369,8 @@ public String getIdentifierValue() throws DOMException { } @Override - public short getCSSUnit() { - return valueProvider.getValue().getCSSUnit(); + public short getUnitType() { + return valueProvider.getValue().getUnitType(); } @Override diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSValue.java deleted file mode 100644 index c7471969b..000000000 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/dom/CSSValue.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - - See the NOTICE file distributed with this work for additional - information regarding copyright ownership. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ -package io.sf.carte.echosvg.css.dom; - -import org.w3c.css.om.typed.CSSKeywordValue; -import org.w3c.css.om.typed.CSSStringValue; -import org.w3c.css.om.typed.CSSStyleValue; -import org.w3c.css.om.unit.CSSUnit; -import org.w3c.dom.DOMException; - -import io.sf.carte.doc.style.css.property.KeywordValue; -import io.sf.carte.echosvg.css.engine.value.ListValue; -import io.sf.carte.echosvg.css.engine.value.NumericValue; -import io.sf.carte.echosvg.css.engine.value.StringValue; - -/** - * A gateway value, useful in the transition to Typed OM. - * - * @author See Git history. - * @version $Id$ - */ -public interface CSSValue extends CSSStyleValue { - - /** - * The main categories of values. - */ - enum CssType { - - /** - * A CSS-wide keyword like {@code inherit}. - */ - KEYWORD, - - /** - *

- * A vehicle towards a final value, of a CSS type that cannot be anticipated. - *

- *

- * Example: {@code var()} or {@code attr()}. - *

- *

- * (note that {@code attr()} has two components, a main one whose type - * could be anticipated, and a fallback that could be of a different type). - *

- */ - PROXY, - - /** - * A typed primitive value, includes numbers and identifiers. - */ - TYPED, - - /** - * A list of values. - *

- * You can cast to {@link ListValue}. - *

- */ - LIST, - - /** - * A shorthand property. - */ - SHORTHAND - } - - /** - * The type of value. For keywords, it is the keyword. - */ - enum Type { - /** - * Unknown type, probably a system default or a compat value. - */ - UNKNOWN, - - /** - * {@code inherit} keyword. - */ - INHERIT, - - /** - * {@code initial} keyword. - */ - INITIAL, - - /** - * {@code unset} keyword. - */ - UNSET, - - /** - * {@code revert} keyword. - */ - REVERT, - - /** - * Numeric type (excludes {@code calc()} which is an {@link #EXPRESSION}). - *

- * Cast to {@link NumericValue}. - *

- */ - NUMERIC, - - /** - * String. - *

- * Cast to {@link StringValue}. - *

- */ - STRING, - - /** - * Identifier. - *

- * Cast to {@link KeywordValue}. - *

- */ - IDENT, - - /** - * Color. - */ - COLOR, - - /** - * Color-Mix function. - */ - COLOR_MIX, - - /** - * URI ({@code url()}). - *

- * Cast to {@link CSSStringValue}. - *

- */ - URI, - - /** - * {@code rect()} function. - */ - RECT, - - /** - * An expression with algebraic syntax (i.e. calc()). - */ - EXPRESSION, - - /** - * Gradient function. - */ - GRADIENT, - - /** - * CSS counter() function. - */ - COUNTER, - - /** - * CSS counters() function. - */ - COUNTERS, - - /** - * cubic-bezier() easing function. - */ - CUBIC_BEZIER, - - /** - * steps() easing function. - */ - STEPS, - - /** - * Function. - */ - FUNCTION, - - /** - * Mathematical function (as defined by CSS Values and Units). - */ - MATH_FUNCTION, - - /** - * Unicode range. - */ - UNICODE_RANGE, - - /** - * Unicode character. - */ - UNICODE_CHARACTER, - - /** - * Unicode wildcard. - */ - UNICODE_WILDCARD, - - /** - * Element reference. - *

- * Cast to {@link CSSKeywordValue}. - *

- */ - ELEMENT_REFERENCE, - - /** - * Ratio value. - */ - RATIO, - - /** - * {@code attr()} function. - */ - ATTR, - - /** - * Custom property reference. - */ - VAR, - - /** - * Environment variable. - */ - ENV, - - /** - * Lexical value. - */ - LEXICAL, - - /** - * For this library's internal use. - */ - INTERNAL, - - /** - * Invalid (non-primitive and non-keyword) value. - *

- * The value is either a list or a shorthand. - *

- */ - INVALID - - } - - /** - * Get the general category to which this value belongs. - * - * @return the general value type. - */ - CssType getCssValueType(); - - /** - * Get the primitive type. - * - * @return the primitive type. - */ - Type getPrimitiveType(); - - /** - * Gets the css unit as in CSS4J's {@code CSSUnit}. - *

- * If the value has no valid CSS unit, returns {@code CSSUnit.CSS_INVALID}. - *

- * - * @return the css unit as in CSS4J's {@code CSSUnit}. - */ - default short getCSSUnit() { - return CSSUnit.CSS_INVALID; - } - - /** - * Set this value to the result of parsing the argument. - * - * @param cssText a CSS serialization to set this value. - * @throws DOMException - */ - void setCssText(String cssText) throws DOMException; - - /** - * Get a parsable representation of this value. - * - * @return the CSS serialization of this value. - */ - String getCssText(); - - /** - * Convenience method that either returns the float value, if the value is - * numeric, or throws an exception. - * - * @return the float value. - */ - float getFloatValue(); - - /** - * Convenience method that either returns an identifier or throws an exception. - * - * @exception DOMException INVALID_ACCESS_ERR: Raised if the value doesn't - * contain an identifier value. - */ - String getIdentifierValue() throws DOMException; - - /** - * If this value can be used where a string is expected, get the value. - * - * @return the string value, without the commas. - * @exception DOMException INVALID_ACCESS_ERR: Raised if the value doesn't - * contain a String. - */ - String getStringValue() throws DOMException; - - /** - * Convenience method that either returns a String or URI or throws an exception. - * - * @exception DOMException INVALID_ACCESS_ERR: Raised if the value doesn't - * contain a String nor a URI value. - */ - String getURIValue() throws DOMException; - - /** - * If this value is a list or contains components, the number of - * CSSStyleValues in the list. The range of valid values of the - * indices is 0 to length-1 inclusive. - * - * @return the number of components, or {@code 0} if this value is not a list - * nor does it contain components. - */ - int getLength(); - - /** - * If this value is a list, give the item corresponding to the requested index. - * If there is no item at such index, return {@code null} If this object is not - * a list and the index is {@code 0}, return itself. - * - * @param index the index on the list. - * @return the item, or {@code null} if there is no item on that index. - */ - default CSSValue item(int index) { - return index == 0 ? this : null; - } - -} diff --git a/test-resources/io/sf/carte/echosvg/transcoder/image/resources/px2mm.svg b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSCircularityException.java similarity index 62% rename from test-resources/io/sf/carte/echosvg/transcoder/image/resources/px2mm.svg rename to echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSCircularityException.java index c4b0deb5b..0ed6b2e46 100644 --- a/test-resources/io/sf/carte/echosvg/transcoder/image/resources/px2mm.svg +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSCircularityException.java @@ -1,7 +1,5 @@ - - +/* - - + */ +package io.sf.carte.echosvg.css.engine; - +import io.sf.carte.echosvg.css.CSSSecurityException; - +/** + * A circularity was detected. + */ +public class CSSCircularityException extends CSSSecurityException { - + private static final long serialVersionUID = 1L; + + public CSSCircularityException(String message) { + super(message); + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSContext.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSContext.java index e423b7561..2e41fc98d 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSContext.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSContext.java @@ -58,8 +58,13 @@ public interface CSSContext { /** * Returns the size of a px CSS unit in millimeters. + * + * @deprecated Use {@link #getResolution()}. */ - float getPixelUnitToMillimeter(); + @Deprecated + default float getPixelUnitToMillimeter() { + return 25.4f / getResolution(); + } /** * Returns the size of a px CSS unit in millimeters. This will be removed after @@ -72,6 +77,11 @@ default float getPixelToMillimeter() { return getPixelUnitToMillimeter(); } + /** + * Returns the resolution in dpi. + */ + float getResolution(); + /** * Returns the medium font size. */ @@ -105,6 +115,11 @@ default float getPixelToMillimeter() { */ void checkLoadExternalResource(ParsedURL resourceURL, ParsedURL docURL) throws SecurityException; + /** + * Get prefers-color-scheme. + */ + String getPrefersColorScheme(); + /** * Returns true if the document is dynamic, false otherwise. */ diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSEngine.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSEngine.java index bba9949bd..41568bcfe 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSEngine.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSEngine.java @@ -18,13 +18,19 @@ */ package io.sf.carte.echosvg.css.engine; +import java.awt.GraphicsEnvironment; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Set; import org.w3c.dom.Attr; @@ -39,24 +45,53 @@ import org.w3c.dom.events.MutationEvent; import io.sf.carte.doc.style.css.BooleanCondition; +import io.sf.carte.doc.style.css.CSSCanvas; +import io.sf.carte.doc.style.css.CSSDocument; import io.sf.carte.doc.style.css.CSSRule; +import io.sf.carte.doc.style.css.CSSTypedValue; +import io.sf.carte.doc.style.css.CSSUnit; +import io.sf.carte.doc.style.css.CSSValue; +import io.sf.carte.doc.style.css.CSSValue.Type; +import io.sf.carte.doc.style.css.CSSValueSyntax; +import io.sf.carte.doc.style.css.CSSValueSyntax.Match; import io.sf.carte.doc.style.css.MediaQueryList; import io.sf.carte.doc.style.css.SelectorMatcher; +import io.sf.carte.doc.style.css.StyleDatabase; +import io.sf.carte.doc.style.css.UnitStringToId; +import io.sf.carte.doc.style.css.nsac.ArgumentCondition; import io.sf.carte.doc.style.css.nsac.AttributeCondition; +import io.sf.carte.doc.style.css.nsac.CSSBudgetException; import io.sf.carte.doc.style.css.nsac.CSSHandler; +import io.sf.carte.doc.style.css.nsac.CSSParseException; +import io.sf.carte.doc.style.css.nsac.CombinatorCondition; +import io.sf.carte.doc.style.css.nsac.CombinatorSelector; import io.sf.carte.doc.style.css.nsac.Condition; +import io.sf.carte.doc.style.css.nsac.ConditionalSelector; +import io.sf.carte.doc.style.css.nsac.DeclarationCondition; import io.sf.carte.doc.style.css.nsac.InputSource; import io.sf.carte.doc.style.css.nsac.LexicalUnit; +import io.sf.carte.doc.style.css.nsac.LexicalUnit.LexicalType; import io.sf.carte.doc.style.css.nsac.PageSelectorList; import io.sf.carte.doc.style.css.nsac.Parser; import io.sf.carte.doc.style.css.nsac.ParserControl; import io.sf.carte.doc.style.css.nsac.Selector; +import io.sf.carte.doc.style.css.nsac.SelectorFunction; import io.sf.carte.doc.style.css.nsac.SelectorList; +import io.sf.carte.doc.style.css.om.AbstractCSSCanvas; +import io.sf.carte.doc.style.css.om.AbstractStyleDatabase; +import io.sf.carte.doc.style.css.om.CSSOMParser; import io.sf.carte.doc.style.css.om.Specificity; import io.sf.carte.doc.style.css.parser.AttributeConditionVisitor; -import io.sf.carte.doc.style.css.parser.CSSParser; +import io.sf.carte.doc.style.css.parser.ParseHelper; +import io.sf.carte.doc.style.css.parser.SyntaxParser; +import io.sf.carte.doc.style.css.property.ValueFactory; +import io.sf.carte.echosvg.css.CSSSecurityException; +import io.sf.carte.echosvg.css.engine.value.CSSProxyValueException; import io.sf.carte.echosvg.css.engine.value.ComputedValue; import io.sf.carte.echosvg.css.engine.value.InheritValue; +import io.sf.carte.echosvg.css.engine.value.LexicalValue; +import io.sf.carte.echosvg.css.engine.value.PendingValue; +import io.sf.carte.echosvg.css.engine.value.PropertyDefinition; import io.sf.carte.echosvg.css.engine.value.ShorthandManager; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -66,8 +101,10 @@ /** * This is the base class for all the CSS engines. * - * @author Stephane Hillion - * @author For later modifications, see Git history. + *

+ * Original author: Stephane Hillion. + * For later modifications, see Git history. + *

* @version $Id$ */ public abstract class CSSEngine { @@ -136,6 +173,10 @@ public static CSSStylableElement getParentCSSStylableElement(Element elt) { */ protected CSSContext cssContext; + private EngineStyleDatabase styleDb; + + private CSSCanvas csscanvas; + /** * The associated document. */ @@ -209,7 +250,7 @@ public static CSSStylableElement getParentCSSStylableElement(Element elt) { /** * The media to use to cascade properties. */ - protected MediaQueryList media; + private String medium; /** * The DOM nodes which contains StyleSheets. @@ -338,6 +379,13 @@ public static CSSStylableElement getParentCSSStylableElement(Element elt) { */ protected Set selectorAttributes; + /** + * The map from custom property names to their definitions. + */ + private HashMap propertyDefinitionMap = null; + + private static final int INITIAL_CUSTOM_PTY_SET_SIZE = 1; // Initial set is never used + /** * Used to fire a change event for all the properties. */ @@ -374,6 +422,8 @@ protected CSSEngine(Document doc, ParsedURL uri, Parser p, ValueManager[] vm, Sh classNamespaceURI = cns; classLocalName = cln; cssContext = ctx; + styleDb = new EngineStyleDatabase(); + csscanvas = new EngineCSSCanvas(); isCSSNavigableDocument = doc instanceof CSSNavigableDocument; @@ -429,6 +479,177 @@ protected CSSEngine(Document doc, ParsedURL uri, Parser p, ValueManager[] vm, Sh } } + private class EngineStyleDatabase extends AbstractStyleDatabase { + + private static final long serialVersionUID = 1L; + + private final List fonts = getAvailableFontList(); + + private List getAvailableFontList() { + return Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment() + .getAvailableFontFamilyNames()); + } + + @Override + public String getDefaultGenericFontFamily() { + return cssContext.getDefaultFontFamily().getStringValue(); + } + + @Override + public String getDefaultGenericFontFamily(String genericFamily) { + return genericFamily; + } + + @Override + public boolean isFontFaceName(String requestedFamily) { + for (FontFaceRule ffRule : fontFaces) { + StyleMap sm = ffRule.getStyleMap(); + int pidx = getPropertyIndex(CSSConstants.CSS_FONT_FAMILY_PROPERTY); + Value fontFamily = sm.getValue(pidx); + if (fontFamily != null && fontFamily.getStringValue().equalsIgnoreCase(requestedFamily)) { + return true; + } + } + return false; + } + + @Override + public int getColorDepth() { + // We do not have the actual Graphics2D here, but we try + GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); + java.awt.GraphicsConfiguration gConfiguration = genv.getDefaultScreenDevice() + .getDefaultConfiguration(); + int bpc = 255; + if (gConfiguration != null) { + int[] comp = gConfiguration.getColorModel().getComponentSize(); + for (int i = 0; i < 3; i++) { + if (bpc > comp[i]) { + bpc = comp[i]; + } + } + } + return bpc; + } + + @Override + public float getDeviceHeight() { + return cssContext.getViewport(element).getHeight(); + } + + @Override + public float getDeviceWidth() { + return cssContext.getViewport(element).getWidth(); + } + + @Override + protected boolean isFontFamilyAvailable(String fontFamily) { + return fonts.contains(fontFamily); + } + + @Override + public CSSTypedValue getInitialColor() { + String pcs = cssContext.getPrefersColorScheme(); + return pcs != null && "dark".equals(pcs) ? + darkmodeInitialColor() : super.getInitialColor(); + } + + private CSSTypedValue darkmodeInitialColor() { + return (CSSTypedValue) new ValueFactory().parseProperty("#fff"); + } + + @Override + public boolean supports(SelectorList selectors) { + for (Selector selector : selectors) { + if (!supports(selector)) { + return false; + } + } + return true; + } + + private boolean supports(Selector selector) { + if (selector != null) { + switch (selector.getSelectorType()) { + case CHILD: + case DESCENDANT: + case DIRECT_ADJACENT: + case SUBSEQUENT_SIBLING: + CombinatorSelector combSel = (CombinatorSelector) selector; + return supports(combSel.getSelector()) + && supports(combSel.getSecondSelector()); + case CONDITIONAL: + ConditionalSelector condSel = (ConditionalSelector) selector; + return supports(condSel.getSimpleSelector()) + && supports(condSel.getCondition()); + case COLUMN_COMBINATOR: + return false; + default: + } + } + return true; + } + + private boolean supports(Condition condition) { + switch (condition.getConditionType()) { + case AND: + CombinatorCondition combCond = (CombinatorCondition) condition; + return supports(combCond.getFirstCondition()) + && supports(combCond.getSecondCondition()); + case SELECTOR_ARGUMENT: + ArgumentCondition argCond = (ArgumentCondition) condition; + SelectorList selist = argCond.getSelectors(); + return selist == null || supports(selist); + default: + return true; + } + } + + } + + private class EngineCSSCanvas extends AbstractCSSCanvas { + + @Override + public CSSDocument getDocument() { + return null; + } + + @Override + public StyleDatabase getStyleDatabase() { + return styleDb; + } + + @Override + protected String getOverflowBlock() { + return "none"; + } + + @Override + protected String getOverflowInline() { + return "none"; + } + + @Override + protected String getPointerAccuracy() { + return "none"; + } + + /** + * The desire for light or dark color schemes. + * + * @return the {@code prefers-color-scheme} feature + */ + @Override + protected String getPrefersColorScheme() { + return cssContext.getPrefersColorScheme(); + } + + @Override + protected float getResolution() { + return cssContext.getResolution(); + } + + } + /** * Adds event listeners to the document to track CSS changes. */ @@ -604,15 +825,7 @@ public List getFontFaces() { * Sets the media to use to compute the styles. */ public void setMedia(String str) { - try { - media = parser.parseMediaQueryList(str, null); - } catch (Exception e) { - String m = e.getMessage(); - if (m == null) - m = ""; - String s = Messages.formatMessage("media.error", new Object[] { str, m }); - throw new DOMException(DOMException.SYNTAX_ERR, s); - } + medium = str != null ? str.toLowerCase(Locale.ROOT) : null; } /** @@ -673,7 +886,7 @@ public StyleMap getCascadedStyleMap(CSSStylableElement elt, String pseudo) { SelectorMatcher matcher = new SVGSelectorMatcher(elt); if (pseudo != null) { - CSSParser parser = new CSSParser(); + Parser parser = createCSSParser(); Condition pseCond = parser.parsePseudoElement(pseudo); matcher.setPseudoElement(pseCond); } @@ -702,7 +915,12 @@ public void property(String pname, LexicalUnit lu, boolean important) { int idx = getPropertyIndex(pname); if (idx != -1) { ValueManager vm = valueManagers[idx]; - Value v = vm.createValue(lu, CSSEngine.this); + Value v; + try { + v = vm.createValue(lu, CSSEngine.this); + } catch (CSSProxyValueException e) { + v = new LexicalValue(lu); + } putAuthorProperty(result, idx, v, important, StyleMap.NON_CSS_ORIGIN); return; } @@ -712,6 +930,14 @@ public void property(String pname, LexicalUnit lu, boolean important) { // Shorthand value shorthandManagers[idx].setValues(CSSEngine.this, this, lu, important); } + + @Override + public void pendingValue(String pname, PendingValue v, boolean important) { + int idx = getPropertyIndex(pname); + if (idx != -1) { // line-height can be -1 + putAuthorProperty(result, idx, v, important, StyleMap.NON_CSS_ORIGIN); + } + } }; NamedNodeMap attrs = elt.getAttributes(); @@ -800,6 +1026,14 @@ && mediaMatch(ss.getMedia())) { result.putOrigin(idx, StyleMap.OVERRIDE_ORIGIN); } } + + // Custom properties + Map customProp = over.getCustomProperties(); + if (customProp != null) { + for (Map.Entry entry : customProp.entrySet()) { + result.putCustomProperty(entry.getKey(), entry.getValue()); + } + } } } } finally { @@ -810,11 +1044,16 @@ && mediaMatch(ss.getMedia())) { return result; } + private Parser createCSSParser() { + return new CSSOMParser(); + } + /** * Returns the computed style of the given element/pseudo for the property * corresponding to the given index. */ - public Value getComputedStyle(CSSStylableElement elt, String pseudo, int propidx) { + public Value getComputedStyle(CSSStylableElement elt, String pseudo, int propidx) + throws CSSSecurityException { StyleMap sm = elt.getComputedStyleMap(pseudo); if (sm == null) { sm = getCascadedStyleMap(elt, pseudo); @@ -829,21 +1068,48 @@ public Value getComputedStyle(CSSStylableElement elt, String pseudo, int propidx ValueManager vm = valueManagers[propidx]; CSSStylableElement p = getParentCSSStylableElement(elt); if (value == null) { - if ((p == null) || !vm.isInheritedProperty()) + if (p == null || !vm.isInheritedProperty()) { result = vm.getDefaultValue(); - } else if ((p != null) && (value == InheritValue.INSTANCE)) { - result = null; + } + } else if (value.getPrimitiveType() == Type.LEXICAL) { + LexicalValue var = (LexicalValue) value; + LexicalUnit lunit = replaceLexicalValue(sm, var.getLexicalUnit(), elt, p, propidx); + if (lunit != null) { + result = vm.createValue(lunit, this); + if (result == null || result.getCssValueType() == CSSValue.CssType.KEYWORD) { + result = initialOrNull(p != null, vm, result); + } + } else { + result = initialOrNull(p != null, vm, null); + } + } else if (value.getPrimitiveType() == Type.INTERNAL) { + PendingValue pending = (PendingValue) value; + if (substitutePendingShorthand(sm, pending, elt, p, propidx)) { + result = sm.getValue(propidx); + } else { + result = initialOrNull(p != null, vm, null); + } + } else if (value.getCssValueType() == CSSValue.CssType.KEYWORD) { + result = initialOrNull(p != null, vm, value); + } + + if (result != null) { + // Maybe it is a relative value. + result = vm.computeValue(elt, pseudo, this, propidx, sm, result); + if (result == null) { + // calc() gave invalid result + result = initialOrNull(p != null, vm, null); + } } + if (result == null) { // Value is 'inherit' and p != null. // The pseudo class is not propagated. result = getComputedStyle(p, null, propidx); sm.putParentRelative(propidx, true); sm.putInherited(propidx, true); - } else { - // Maybe is it a relative value. - result = vm.computeValue(elt, pseudo, this, propidx, sm, result); } + if (value == null) { sm.putValue(propidx, result); sm.putNullCascaded(propidx, true); @@ -858,6 +1124,563 @@ public Value getComputedStyle(CSSStylableElement elt, String pseudo, int propidx return result; } + private static Value initialOrNull(boolean hasParent, ValueManager vm, Value value) { + Value result; + if (hasParent && (vm.isInheritedProperty() || value == InheritValue.getInstance())) { + result = null; + } else { + result = vm.getDefaultValue(); + } + return result; + } + + /** + * Substitute the {@code PROXY} values in a lexical value. + * + * @param sm the style map. + * @param lexicalUnit the lexical value. + * @param elt the element for which the value is computed. + * @param parent the parent element, or {@code null} if no parent. + * @param propIdx the property index. + * @return the replaced lexical unit. + * @throws DOMException + * @throws CSSCircularityException if a circularity was found while evaluating + * custom properties. + * @throws CSSResourceLimitException if the limit of recursions or allowed + * substitutions was exceeded. + */ + private LexicalUnit replaceLexicalValue(StyleMap sm, LexicalUnit lexicalUnit, CSSStylableElement elt, + CSSStylableElement parent, int propIdx) throws CSSSecurityException { + HashSet customPropertySet = new HashSet<>(INITIAL_CUSTOM_PTY_SET_SIZE); + + CounterRef counter = new CounterRef(); + + LexicalUnit lunit = lexicalUnit.clone(); + LexicalUnit replUnit; + try { + replUnit = replaceLexicalProxy(sm, lunit, elt, parent, counter, customPropertySet, propIdx); + } catch (CSSSecurityException e) { + throw e; + } catch (DOMException e) { + displayOrThrowError(e); + return null; + } + + if (replUnit != null && replUnit.getLexicalUnitType() == LexicalType.EMPTY) { + replUnit = null; + } + + return replUnit; + } + + /** + * Given a lexical value, replace all occurrences of the {@code VAR} and + * {@code ATTR} lexical types with the values of the corresponding custom + * properties or attributes, and incrementing the supplied counter. + * + * @param sm the style map. + * @param lexval the lexical value. + * @param elt the element for which the value is computed. + * @param parent the parent element, or {@code null} if no parent. + * @param counter the substitution and recursion counter. + * @param customPtySet the set of custom property names, to prevent circular + * dependencies. + * @param propIdx the property index. + * @return the replaced lexical unit. + * @throws DOMException + * @throws CSSCircularityException if a circularity was found while evaluating + * custom properties. + * @throws CSSResourceLimitException if the limit of recursions or allowed + * substitutions was exceeded. + */ + private LexicalUnit replaceLexicalProxy(StyleMap sm, LexicalUnit lexval, CSSStylableElement elt, + CSSStylableElement parent, CounterRef counter, Set customPtySet, int propIdx) + throws DOMException, CSSSecurityException { + final int REPLACE_COUNT_LIMIT = 0x20000; // Number of allowed lexical substitutions + + /* + * Prepare a working set of traversed custom properties + */ + Set ptySet = new HashSet<>(customPtySet.size() + 8); + ptySet.addAll(customPtySet); + + /* + * Replace the PROXY (var(), attr()) values in the lexical chain + */ + LexicalUnit lu = lexval; + do { + if (lu.getLexicalUnitType() == LexicalType.VAR) { + LexicalUnit newlu; + LexicalUnit param = lu.getParameters(); + String propertyName = param.getStringValue(); // Property name + param = param.getNextLexicalUnit(); // Comma? + if (param != null) { + param = param.getNextLexicalUnit(); // Fallback + } + + /* + * Obtain a value and replace this var() in the lexical chain + */ + newlu = getCustomPropertyValueOrFallback(sm, propertyName, param, parent, counter, ptySet); + + boolean isLexval = lu == lexval; + if (newlu == null) { + // The current lexical unit can be removed + lu = lu.remove(); + if (isLexval) { + // We are processing the first in the lexical chain, re-assign + lexval = lu; + } + continue; + } + + if (newlu.getLexicalUnitType() != LexicalType.EMPTY) { + // We do not want to mess with a declared value, so clone it + newlu = newlu.clone(); + try { + counter.replaceCounter += lu.countReplaceBy(newlu); + } catch (CSSBudgetException e) { + throw createVarResourceLimitException(propertyName, e); + } + if (counter.replaceCounter >= REPLACE_COUNT_LIMIT) { + throw createVarResourceLimitException(propertyName); + } + lu = newlu; + if (isLexval) { + // We are processing the first in the lexical chain, re-assign + lexval = newlu; + } + // Can we reset the circularity safeguard? + LexicalType ltype = lu.getLexicalUnitType(); + if (ltype != LexicalType.VAR && ltype != LexicalType.ATTR) { + ptySet.clear(); + ptySet.addAll(customPtySet); + } + } else { + // The current lexical unit can be removed + lu = lu.remove(); + if (isLexval) { + // We are processing the first in the lexical chain, re-assign + lexval = lu; + } + } + continue; + } else if (lu.getLexicalUnitType() == LexicalType.ATTR) { + if (valueManagers[propIdx].allowsURL()) { + return null; + } + boolean isLexval = lu == lexval; + LexicalUnit newlu = replacementAttrUnit(sm, lu, elt, parent, counter, ptySet, propIdx); + try { + counter.replaceCounter += lu.countReplaceBy(newlu); + } catch (CSSBudgetException e) { + throw createAttrResourceLimitException(e); + } + if (counter.replaceCounter >= REPLACE_COUNT_LIMIT) { + throw createAttrResourceLimitException(); + } + + if (newlu == null) { + // The current lexical unit can be removed + lu = lu.remove(); + if (isLexval) { + // We are processing the first in the lexical chain, re-assign + lexval = lu; + } + continue; + } + + if (newlu.getLexicalUnitType() != LexicalType.EMPTY) { + // We do not want to mess with a declared value, so clone it + newlu = newlu.clone(); + try { + counter.replaceCounter += lu.countReplaceBy(newlu); + } catch (CSSBudgetException e) { + throw createAttrResourceLimitException(e); + } + if (counter.replaceCounter >= REPLACE_COUNT_LIMIT) { + throw createAttrResourceLimitException(); + } + lu = newlu; + if (isLexval) { + // We are processing the first in the lexical chain, re-assign + lexval = newlu; + } + } else { + // The current lexical unit can be removed + lu = lu.remove(); + if (isLexval) { + // We are processing the first in the lexical chain, re-assign + lexval = lu; + } + } + continue; + } else { + LexicalUnit param = lu.getParameters(); + if (param != null || (param = lu.getSubValues()) != null) { + // Ignore return value (it is a parameter or a sub-value) + replaceLexicalProxy(sm, param, elt, parent, counter, ptySet, propIdx); + } + } + lu = lu.getNextLexicalUnit(); + } while (lu != null); + + return lexval; + } + + /** + * Obtain the (lexical) value of a custom property and replace any {@code VAR} + * unit in it, applying the fallback if necessary. + * + * @param sm + * @param customProperty the custom property name. + * @param fallbackLU the custom property fallback. + * @param parent the parent element, or {@code null} if no parent. + * @param counter the counter. + * @param customPtySet the set of custom property names, to prevent circular + * dependencies. + * @return the value of {@code customProperty} or the fallback if there is no + * value. + * @throws DOMException + * @throws CSSCircularityException if a circularity was found while evaluating + * custom properties. + * @throws CSSResourceLimitException if the limit of recursions or allowed + * substitutions was exceeded. + */ + private LexicalUnit getCustomPropertyValueOrFallback(StyleMap sm, String customProperty, LexicalUnit fallbackLU, + CSSStylableElement parent, CounterRef counter, Set customPtySet) + throws DOMException, CSSSecurityException { + if (!customPtySet.add(customProperty)) { + throw new CSSCircularityException( + "Circularity evaluating custom property " + customProperty + ": " + customPtySet.toString()); + } + + LexicalUnit custom = getCustomProperty(sm, customProperty, parent); + + if (custom != null) { + if (counter.increment()) { + return custom; + } else { + throw createVarResourceLimitException(customProperty); + } + } + + // customProperty is null, no circularity. + customPtySet.remove(customProperty); + + // Fallback + return fallbackLU; + } + + private LexicalUnit getCustomProperty(StyleMap sm, String name, CSSStylableElement parent) { + // First, try to obtain a possible property definition from a @property rule + PropertyDefinition definition = getPropertyDefinition(name); + boolean inherits = definition == null || definition.inherits(); + + LexicalUnit custom; + while ((custom = sm.getCustomProperty(name)) == null && inherits) { + if (parent != null) { + sm = parent.getComputedStyleMap(null); + if (sm == null) { + sm = getCascadedStyleMap(parent, null); + parent.setComputedStyleMap(null, sm); + } + parent = getParentCSSStylableElement(parent); + } else { + break; + } + } + + if (custom == null) { + if (definition != null) { + custom = definition.getInitialValue(); + } + } else if (definition != null) { + CSSValueSyntax syntax = definition.getSyntax(); + // syntax is never null + if (custom.matches(syntax) == Match.FALSE) { + custom = definition.getInitialValue(); + } + } + + return custom; + } + + private PropertyDefinition getPropertyDefinition(String name) { + return propertyDefinitionMap == null ? null : propertyDefinitionMap.get(name); + } + + /** + * Perform a lexical substitution on a pending shorthand value. + * + * @param sm the style map. + * @param pending the pending longhand value. + * @param elt the element for which the value is computed. + * @param parent the parent element, or {@code null} if no parent. + * @param propIdx the property index. + * @return {@code true} if the shorthand was replaced successfully. + * @throws DOMException + * @throws CSSCircularityException if a circularity was found while evaluating + * custom properties. + * @throws CSSResourceLimitException if the limit of recursions or allowed + * substitutions was exceeded. + */ + private boolean substitutePendingShorthand(StyleMap sm, PendingValue pending, CSSStylableElement elt, + CSSStylableElement parent, int propIdx) throws DOMException, CSSSecurityException { + LexicalUnit lunit = replaceLexicalProxy(sm, pending.getLexicalUnit().clone(), elt, parent, + new CounterRef(), new HashSet<>(INITIAL_CUSTOM_PTY_SET_SIZE), propIdx); + boolean ret = lunit != null ? + setShorthandLonghands(sm, pending.getShorthandName(), lunit, sm.isImportant(propIdx)) : false; + return ret; + } + + private boolean setShorthandLonghands(StyleMap sm, String propertyName, LexicalUnit value, + boolean important) throws DOMException { + try { + int idx = getShorthandIndex(propertyName); + if (idx == -1) + return false; // Unknown property... + // Shorthand value + shorthandManagers[idx].setValues(CSSEngine.this, new ShorthandManager.PropertyHandler() { + + @Override + public void property(String pname, LexicalUnit value, boolean important) { + int idx = getPropertyIndex(pname); + if (idx != -1) { + Value oldv = sm.getValue(idx); + if (oldv == null || oldv.getPrimitiveType() == Type.INTERNAL) { + ValueManager vm = valueManagers[idx]; + Value v = vm.createValue(value, CSSEngine.this); + sm.putValue(idx, v); + // sm.putImportant(idx, important); // already done + } // else the value was set later + } else { + // This can be removed + throw new IllegalStateException("Unknown pending value."); + } + } + + @Override + public void pendingValue(String name, PendingValue value, boolean important) { + throw new IllegalStateException("Cannot set pending values after replacement."); + } + + }, value, important); + return true; + } catch (DOMException e) { + // Report error + DOMException ex = new DOMException(e.code, "Error setting shorthand " + propertyName); + ex.initCause(e); + displayOrThrowError(ex); + return false; + } + } + + private LexicalUnit replacementAttrUnit(StyleMap sm, LexicalUnit attr, CSSStylableElement elt, + CSSStylableElement parent, CounterRef counter, Set ptySet, int propIdx) { + // Obtain attribute name and type (if set) + LexicalUnit lu = attr.getParameters(); + if (lu.getLexicalUnitType() != LexicalType.IDENT) { + valueSyntaxError("Unexpected attribute name (" + lu.getCssText() + ") in " + attr.getCssText()); + return null; + } + String attrname = lu.getStringValue(); + String attrtype; + lu = lu.getNextLexicalUnit(); + if (lu != null) { + if (lu.getLexicalUnitType() != LexicalType.OPERATOR_COMMA) { + switch (lu.getLexicalUnitType()) { + case IDENT: + attrtype = lu.getStringValue().toLowerCase(Locale.ROOT); + break; + case OPERATOR_MOD: + attrtype = "%"; + break; + default: + valueSyntaxError( + "Unexpected attribute type (" + lu.getCssText() + ") in " + attr.getCssText()); + return null; + } + lu = lu.getNextLexicalUnit(); + if (lu != null) { + if (lu.getLexicalUnitType() != LexicalType.OPERATOR_COMMA) { + valueSyntaxError( + "Expected comma, found: " + lu.getCssText() + " in " + attr.getCssText()); + return null; + } + lu = lu.getNextLexicalUnit(); + } + } else { + lu = lu.getNextLexicalUnit(); + if (lu == null) { + // Ending with comma is wrong syntax + valueSyntaxError("Unexpected end after comma in value " + attr.getCssText()); + return null; + } + attrtype = null; + } + } else { + attrtype = null; + } + + // Obtain the attribute value + String attrvalue = elt.getAttribute(attrname); + + Parser parser = createCSSParser(); + + /* + * string type is a special case + */ + if (attrtype == null || "string".equalsIgnoreCase(attrtype)) { + String s = ParseHelper.quote(attrvalue, '"'); + LexicalUnit substValue; + try { + substValue = parser.parsePropertyValue(new StringReader(s)); + } catch (IOException e) { + // This won't happen + substValue = null; + } catch (CSSParseException e) { + // Possibly a budget error + valueSyntaxError("Unexpected error parsing: " + s.substring(0, Math.min(s.length(), 255)), e); + // Process fallback + substValue = lu; + if (substValue != null) { + substValue = substValue.clone(); + } else { + try { + return parser.parsePropertyValue(new StringReader("")); + } catch (CSSParseException | IOException e1) { + } + } + } + // No further processing required + return replaceLexicalProxy(sm, substValue, elt, parent, counter, ptySet, propIdx); + } + + if (!attrvalue.isEmpty()) { + /* + * Non-string types + */ + attrvalue = attrvalue.trim(); + + // Let's see if the type is an actual type or an unit suffix + if (attrtype.length() <= 2 || UnitStringToId.unitFromString(attrtype) != CSSUnit.CSS_OTHER) { + attrvalue += attrtype; + } + + LexicalUnit substValue; + try { + substValue = parser.parsePropertyValue(new StringReader(attrvalue)); + } catch (IOException e) { + // This won't happen + substValue = null; + } catch (CSSParseException e) { + valueSyntaxError("Error parsing attribute '" + attrname + "', value: " + attrvalue, e); + // Return fallback + if (lu != null) { + return replaceLexicalProxy(sm, lu.clone(), elt, parent, counter, ptySet, propIdx); + } + return null; + } + try { + substValue = replaceLexicalProxy(sm, substValue, elt, parent, counter, ptySet, propIdx); + } catch (Exception e) { + valueSyntaxError("Circularity: " + attr.getCssText() + " references " + substValue.getCssText(), e); + // Return fallback + substValue = null; + } + if (substValue != null) { + substValue = replaceLexicalProxy(sm, substValue, elt, parent, counter, ptySet, propIdx); + // Now check that the value is of the correct type. + // + // If the attribute type length is 1 or 2, type can only be a unit suffix + // and there is no need to check. + if (attrtype.length() > 2 && !unitMatchesAttrType(substValue, attrtype)) { + substValue = null; + valueSyntaxError("Attribute value does not match type (" + attrtype + ")."); + } else { + return substValue; + } + } else { + // Fallback + if (lu != null) { + substValue = replaceLexicalProxy(sm, lu.clone(), elt, parent, counter, ptySet, propIdx); + } + return substValue; + } + } + + // Return fallback + return lu == null ? null : replaceLexicalProxy(sm, lu, elt, parent, counter, ptySet, propIdx); + } + + private static boolean unitMatchesAttrType(LexicalUnit lunit, String attrtype) { + int len = attrtype.length(); + if (len == 1) { + return "%".equals(attrtype) && lunit.getCssUnit() == CSSUnit.CSS_PERCENTAGE; + } else if (len == 2) { + return attrtype.equalsIgnoreCase(lunit.getDimensionUnitText()); + } + if ("ident".equalsIgnoreCase(attrtype)) { + attrtype = "custom-ident"; + } + CSSValueSyntax syn = SyntaxParser.createSimpleSyntax(attrtype); + if (syn == null) { + // Could be a 3-4 letter unit suffix, or an error + return attrtype.equalsIgnoreCase(lunit.getDimensionUnitText()); + } + if (syn.getCategory() == CSSValueSyntax.Category.url) { + // There is no syntax for security reasons + return false; + } + return lunit.matches(syn) == Match.TRUE; + } + + private void valueSyntaxError(String message) { + DOMException ex = new DOMException(DOMException.SYNTAX_ERR, message); + displayOrThrowError(ex); + } + + private void valueSyntaxError(String message, Throwable cause) { + DOMException ex = new DOMException(DOMException.SYNTAX_ERR, message); + ex.initCause(cause); + displayOrThrowError(ex); + } + + private CSSResourceLimitException createVarResourceLimitException(String propertyName) { + return createResourceLimitException( + "Resource limit hit while replacing custom property: " + propertyName); + } + + private CSSResourceLimitException createVarResourceLimitException(String propertyName, Throwable cause) { + return createResourceLimitException( + "Resource limit hit while replacing custom property " + propertyName, cause); + } + + private CSSResourceLimitException createAttrResourceLimitException() { + return createResourceLimitException("Resource limit hit while replacing attr() property."); + } + + private CSSResourceLimitException createAttrResourceLimitException(Throwable e) { + return createResourceLimitException( + "Resource limit hit while replacing attr() property.", e); + } + + private CSSResourceLimitException createResourceLimitException(String message) { + return new CSSResourceLimitException(message); + } + + private CSSResourceLimitException createResourceLimitException(String message, Throwable e) { + return new CSSResourceLimitException(message, e); + } + + private void displayOrThrowError(RuntimeException ex) { + if (userAgent == null) { + throw ex; + } + userAgent.displayError(ex); + } + /** * Returns the document CSSStyleSheetNodes in a list. This list is updated as * the document is modified. @@ -977,6 +1800,11 @@ public void property(String pname, LexicalUnit lu, boolean important) { // Shorthand value shorthandManagers[idx].setValues(CSSEngine.this, this, lu, important); } + + @Override + public void pendingValue(String name, PendingValue value, boolean important) { + dst.setMainProperty(name, value, important); + } }; ph.property(pname, lu, important); } catch (Exception e) { @@ -1005,8 +1833,9 @@ public void property(String pname, LexicalUnit lu, boolean important) { */ public Value parsePropertyValue(CSSStylableElement elt, String prop, String value) { int idx = getPropertyIndex(prop); - if (idx == -1) + if (idx == -1) { return null; + } ValueManager vm = valueManagers[idx]; try { element = elt; @@ -1306,10 +2135,126 @@ protected void addMatchingRules(List rules, StyleSheet ss, SelectorMatcher addMatchingRules(rules, mr, matcher); } break; + + case SupportsRule.TYPE: + SupportsRule sr = (SupportsRule) r; + if (sr.supports) { + addMatchingRules(rules, sr, matcher); + } + break; } } } + /** + * Whether the given media list matches the media list of this CSSEngine object. + */ + protected boolean mediaMatch(MediaQueryList ml) { + if (medium == null || ml == null || ml.isAllMedia()) { + return true; + } + return ml.matches(medium, csscanvas); + } + + private void setSupports(SupportsRule sr) { + BooleanCondition condition = sr.getCondition(); + if (condition != null) { + try { + sr.supports = supports(condition); + return; + } catch (Exception e) { + } + } + sr.supports = false; + } + + private boolean supports(BooleanCondition condition) { + switch (condition.getType()) { + case PREDICATE: + DeclarationCondition declCond = (DeclarationCondition) condition; + return supports(declCond.getName(), declCond.getValue()); + case AND: + List subcond = condition.getSubConditions(); + if (subcond == null) { + // No conditions inside and() + DOMException ex = new DOMException(DOMException.SYNTAX_ERR, "No conditions inside and()."); + userAgent.displayError(ex); + return false; + } + Iterator it = subcond.iterator(); + while (it.hasNext()) { + if (!supports(it.next())) { + return false; + } + } + return true; + case NOT: + BooleanCondition nested = condition.getNestedCondition(); + if (nested == null) { + // No condition inside not() + DOMException ex = new DOMException(DOMException.SYNTAX_ERR, "No condition inside not()."); + userAgent.displayError(ex); + return false; + } + return !supports(nested); + case OR: + subcond = condition.getSubConditions(); + if (subcond == null) { + // No conditions inside or() + DOMException ex = new DOMException(DOMException.SYNTAX_ERR, "No conditions inside or()."); + userAgent.displayError(ex); + return false; + } + it = subcond.iterator(); + while (it.hasNext()) { + if (supports(it.next())) { + return true; + } + } + break; + case SELECTOR_FUNCTION: + SelectorFunction selCond = (SelectorFunction) condition; + return styleDb.supports(selCond.getSelectors()); + case OTHER: + break; + } + + return false; + } + + private boolean supports(String property, LexicalUnit value) { + int idx = getPropertyIndex(property); + if (idx != -1) { + try { + valueManagers[idx].createValue(value, CSSEngine.this); + return true; + } catch (Exception e) { + } + return false; + } + + idx = getShorthandIndex(property); + if (idx != -1) { + try { + shorthandManagers[idx].setValues(this, new ShorthandManager.PropertyHandler() { + + @Override + public void property(String name, LexicalUnit value, boolean important) { + } + + @Override + public void pendingValue(String name, PendingValue value, boolean important) { + } + + }, value, false); + return true; + } catch (Exception e) { + } + } + + return false; + } + /** * Adds the rules contained in the given list to a stylemap. */ @@ -1324,6 +2269,14 @@ protected void addRules(SelectorMatcher matcher, StyleMap sm, ArrayList ru for (int i = 0; i < len; i++) { putAuthorProperty(sm, sd.getIndex(i), sd.getValue(i), sd.getPriority(i), origin); } + + // Custom properties + Map customProp = sd.getCustomProperties(); + if (customProp != null) { + for (Map.Entry entry : customProp.entrySet()) { + sm.putCustomProperty(entry.getKey(), entry.getValue()); + } + } } } else { for (Rule rule : rules) { @@ -1382,16 +2335,6 @@ protected void sortRules(ArrayList rules, SelectorMatcher matcher) { } } - /** - * Whether the given media list matches the media list of this CSSEngine object. - */ - protected boolean mediaMatch(MediaQueryList ml) { - if (media == null || ml == null || media.isAllMedia()) { - return true; - } - return ml.matches(media); - } - /** * To parse a style declaration. */ @@ -1430,6 +2373,19 @@ public void property(String name, LexicalUnit value, boolean important) { } } + @Override + public void lexicalProperty(String name, LexicalUnit value, boolean important) { + styleMap.putCustomProperty(name, value); + } + + @Override + public void pendingValue(String name, PendingValue v, boolean important) { + int idx = getPropertyIndex(name); + if (idx != -1) { // line-height can be -1 + putAuthorProperty(styleMap, idx, v, important, StyleMap.INLINE_AUTHOR_ORIGIN); + } + } + } /** @@ -1455,6 +2411,19 @@ public void property(String name, LexicalUnit value, boolean important) { } } + @Override + public void lexicalProperty(String name, LexicalUnit value, boolean important) { + styleDeclaration.setCustomProperty(name, value, important); + } + + @Override + public void pendingValue(String name, PendingValue value, boolean important) { + int idx = getPropertyIndex(name); + if (idx != -1) { // line-height can be -1 + styleDeclaration.append(value, idx, important); + } + } + } /** @@ -1466,6 +2435,8 @@ protected class StyleSheetDocumentHandler extends DocumentAdapter implements Sho protected StyleRule styleRule; protected StyleDeclaration styleDeclaration; + private PropertyDefinitionImpl currentPropertyDefinition = null; + private int ignoredForRule = 0; @Override @@ -1651,28 +2622,53 @@ public void endFeatureMap() { public void startProperty(String name) { if (ignoredForRule == 0) { ignoredForRule = CSSRule.PROPERTY_RULE; + } else { + return; + } + + if (propertyDefinitionMap == null) { + propertyDefinitionMap = new HashMap<>(); } + + currentPropertyDefinition = new PropertyDefinitionImpl(name); } @Override public void endProperty(boolean discard) { - if (ignoredForRule == CSSRule.PROPERTY_RULE) { - ignoredForRule = 0; + if (ignoredForRule != CSSRule.PROPERTY_RULE) { + return; + } + + if (!discard) { + propertyDefinitionMap.put(currentPropertyDefinition.getName(), currentPropertyDefinition); } + + currentPropertyDefinition = null; + ignoredForRule = 0; } @Override public void startSupports(BooleanCondition condition) { - if (ignoredForRule == 0) { - ignoredForRule = CSSRule.SUPPORTS_RULE; + if (ignoredForRule > 0) { + return; } + + SupportsRule sr = new SupportsRule(condition); + + setSupports(sr); + + sr.setParent(styleSheet); + styleSheet.append(sr); + styleSheet = sr; } @Override public void endSupports(BooleanCondition condition) { - if (ignoredForRule == CSSRule.SUPPORTS_RULE) { - ignoredForRule = 0; + if (ignoredForRule > 0) { + return; } + + styleSheet = styleSheet.getParent(); } @Override @@ -1742,6 +2738,55 @@ public void property(String name, LexicalUnit value, boolean important) { } } + @Override + public void lexicalProperty(String name, LexicalUnit value, boolean important) { + if (ignoredForRule == CSSRule.PROPERTY_RULE) { + propertyRuleDescriptor(name, value, important); + return; + } else if (ignoredForRule > 0) { + return; + } + + styleDeclaration.setCustomProperty(name, value, important); + } + + private void propertyRuleDescriptor(String name, LexicalUnit value, boolean important) { + switch (name) { + case "inherits": + currentPropertyDefinition.setInherits(!"false".equalsIgnoreCase(value.getStringValue())); + break; + case "initial-value": + currentPropertyDefinition.setInitialValue(value); + break; + case "syntax": + String s = value.getStringValue(); + if (s == null) { + s = "*"; + } + SyntaxParser synParser = new SyntaxParser(); + CSSValueSyntax syn; + try { + syn = synParser.parseSyntax(s); + } catch (Exception e) { + syn = synParser.parseSyntax("*"); + } + currentPropertyDefinition.setSyntax(syn); + break; + } + } + + @Override + public void pendingValue(String name, PendingValue v, boolean important) { + if (ignoredForRule > 0) { + return; + } + + int i = getPropertyIndex(name); + if (i != -1) { // line-height can be -1 + styleDeclaration.append(v, i, important); + } + } + } /** @@ -2274,6 +3319,22 @@ public void property(String name, LexicalUnit value, boolean important) { } } + @Override + public void pendingValue(String name, PendingValue v, boolean important) { + int i = getPropertyIndex(name); + if (styleMap.isImportant(i)) { + // The previous value is important, and a value + // from a style attribute cannot be important... + return; + } + + updatedProperties[i] = true; + + styleMap.putMask(i, 0); + styleMap.putValue(i, v); + styleMap.putOrigin(i, StyleMap.INLINE_AUTHOR_ORIGIN); + } + } /** @@ -2299,8 +3360,7 @@ protected void nonCSSPresentationalHintUpdated(CSSStylableElement elt, StyleMap case MutationEvent.MODIFICATION: element = elt; try { - LexicalUnit lu; - lu = parser.parsePropertyValue(new StringReader(newValue)); + LexicalUnit lu = parser.parsePropertyValue(new StringReader(newValue)); ValueManager vm = valueManagers[idx]; Value v = vm.createValue(lu, CSSEngine.this); style.putMask(idx, 0); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSResourceLimitException.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSResourceLimitException.java new file mode 100644 index 000000000..44d4a4cc2 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CSSResourceLimitException.java @@ -0,0 +1,38 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine; + +import io.sf.carte.echosvg.css.CSSSecurityException; + +/** + * A resource limit was reached. + */ +public class CSSResourceLimitException extends CSSSecurityException { + + private static final long serialVersionUID = 1L; + + public CSSResourceLimitException(String message) { + super(message); + } + + public CSSResourceLimitException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CounterRef.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CounterRef.java new file mode 100644 index 000000000..3e63a56f7 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/CounterRef.java @@ -0,0 +1,53 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine; + +/** + * A counter to prevent excessive recursions and replacements. + */ +class CounterRef { + + public CounterRef() { + super(); + } + + + private static final int MAX_RECURSION = 512; + + // Recursion counter + private int counter = 0; + + // Counter for replaceBy() + int replaceCounter = 0; + + boolean increment() { + counter++; + if (isInRange()) { + return true; + } + // Give a small margin for further operations + counter -= 8; + return false; + } + + private boolean isInRange() { + return counter < MAX_RECURSION; + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/PropertyDefinitionImpl.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/PropertyDefinitionImpl.java new file mode 100644 index 000000000..019882cd4 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/PropertyDefinitionImpl.java @@ -0,0 +1,88 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine; + +import io.sf.carte.doc.style.css.CSSValueSyntax; +import io.sf.carte.doc.style.css.nsac.LexicalUnit; +import io.sf.carte.echosvg.css.engine.value.PropertyDefinition; + +/** + * Implementation of a property definition. + */ +class PropertyDefinitionImpl implements PropertyDefinition { + + private final String name; + + boolean inherits = true; + + private CSSValueSyntax syntax; + + private LexicalUnit initialValue; + + PropertyDefinitionImpl(String name) { + super(); + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean inherits() { + return inherits; + } + + void setInherits(boolean inherits) { + this.inherits = inherits; + } + + @Override + public LexicalUnit getInitialValue() { + return initialValue; + } + + void setInitialValue(LexicalUnit initialValue) { + this.initialValue = initialValue; + } + + @Override + public CSSValueSyntax getSyntax() { + return syntax; + } + + void setSyntax(CSSValueSyntax syntax) { + this.syntax = syntax; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(48); + buf.append("@property ").append(name).append(" {\n"); + buf.append(" syntax: \"").append(syntax.toString()).append("\";\n"); + buf.append(" inherits: ").append(Boolean.toString(inherits)).append(";\n"); + if (initialValue != null) { + buf.append(" initial-value: ").append(initialValue.toString()).append(";\n"); + } + buf.append("}\n"); + return buf.toString(); + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleDeclaration.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleDeclaration.java index 6f5927acb..1d8a68e42 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleDeclaration.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleDeclaration.java @@ -18,6 +18,14 @@ */ package io.sf.carte.echosvg.css.engine; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import io.sf.carte.doc.style.css.CSSValue.Type; +import io.sf.carte.doc.style.css.nsac.LexicalUnit; +import io.sf.carte.echosvg.css.engine.value.PendingValue; import io.sf.carte.echosvg.css.engine.value.Value; /** @@ -51,6 +59,11 @@ public class StyleDeclaration { */ protected int count; + /** + * Custom property map. + */ + private Map customProperties = null; + /** * Returns the number of values in the declaration. */ @@ -129,6 +142,7 @@ public void append(Value v, int idx, boolean prio) { indexes = newidx; priorities = newprio; } + for (int i = 0; i < count; i++) { if (indexes[i] == idx) { // Replace existing property values, @@ -140,23 +154,75 @@ public void append(Value v, int idx, boolean prio) { return; } } + values[count] = v; indexes[count] = idx; priorities[count] = prio; count++; } + /** + * Set a custom property value in the declaration. + * + * @param name the custom property name. + * @param value the custom property value. + * @param important the priority. + */ + public void setCustomProperty(String name, LexicalUnit value, boolean important) { + if (customProperties == null) { + customProperties = new HashMap<>(); + } + + customProperties.put(name, value); + } + + /** + * Get the map of custom properties. + * + * @return the custom property map, or {@code null} if there are no custom + * properties. + */ + public Map getCustomProperties() { + return customProperties; + } + /** * Returns a printable representation of this style rule. */ public String toString(CSSEngine eng) { - StringBuilder sb = new StringBuilder(count * 8); + Set pendingShorthands = null; + StringBuilder sb = new StringBuilder(count * 8 + 32); for (int i = 0; i < count; i++) { - sb.append(eng.getPropertyName(indexes[i])); - sb.append(": "); - sb.append(values[i]); - sb.append(";\n"); + Value value = values[i]; + if (value.getPrimitiveType() != Type.INTERNAL) { + sb.append(eng.getPropertyName(indexes[i])); + sb.append(": "); + sb.append(value); + sb.append(";\n"); + } else { + if (pendingShorthands == null) { + pendingShorthands = new HashSet<>(); + } + PendingValue pending = (PendingValue) value; + String name = pending.getShorthandName(); + if (pendingShorthands.add(name)) { + sb.append(name); + sb.append(": "); + sb.append(pending.getLexicalUnit().toString()); + sb.append(";\n"); + } + } } + + if (customProperties != null) { + for (Map.Entry entry : customProperties.entrySet()) { + sb.append(entry.getKey()); + sb.append(": "); + sb.append(entry.getValue().toString()); + sb.append(";\n"); + } + } + return sb.toString(); } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleMap.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleMap.java index 634ae5a8f..d8824ba98 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleMap.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleMap.java @@ -18,6 +18,14 @@ */ package io.sf.carte.echosvg.css.engine; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import io.sf.carte.doc.style.css.CSSValue.Type; +import io.sf.carte.doc.style.css.nsac.LexicalUnit; +import io.sf.carte.echosvg.css.engine.value.PendingValue; import io.sf.carte.echosvg.css.engine.value.Value; /** @@ -67,6 +75,11 @@ public class StyleMap { */ protected int[] masks; + /** + * Custom property map. + */ + private Map customProperties = null; + /** * Whether the values of this map cannot be re-cascaded. */ @@ -371,27 +384,79 @@ public void putViewportRelative(int i, boolean b) { masks[i] &= ~VIEWPORT_RELATIVE_MASK; } + /** + * Set a custom property value. + * + * @param name the custom property name. + * @param value the custom property value. + */ + public void putCustomProperty(String name, LexicalUnit value) { + if (customProperties == null) { + customProperties = new HashMap<>(); + } + + customProperties.put(name, value); + } + + /** + * Get the value of a custom property. + * + * @param name the custom property name. + * @return the custom property value, or {@code null} if there is no value + * defined for that custom property. + */ + public LexicalUnit getCustomProperty(String name) { + return customProperties != null ? customProperties.get(name) : null; + } + /** * Returns a printable representation of this style map. */ public String toString(CSSEngine eng) { + Set pendingShorthands = null; // Note that values.length should always be equal to // eng.getNumberOfProperties() for StyleMaps that were created // by that CSSEngine. int nSlots = values.length; - StringBuilder sb = new StringBuilder(nSlots * 8); + StringBuilder sb = new StringBuilder(nSlots * 8 + 32); for (int i = 0; i < nSlots; i++) { Value v = values[i]; if (v == null) continue; - sb.append(eng.getPropertyName(i)); - sb.append(": "); - sb.append(v); - if (isImportant(i)) - sb.append(" !important"); - sb.append(";\n"); + if (v.getPrimitiveType() != Type.INTERNAL) { + sb.append(eng.getPropertyName(i)); + sb.append(": "); + sb.append(v); + if (isImportant(i)) + sb.append(" !important"); + sb.append(";\n"); + } else { + if (pendingShorthands == null) { + pendingShorthands = new HashSet<>(); + } + PendingValue pending = (PendingValue) v; + String name = pending.getShorthandName(); + if (pendingShorthands.add(name)) { + sb.append(name); + sb.append(": "); + sb.append(pending.getLexicalUnit().toString()); + if (isImportant(i)) + sb.append(" !important"); + sb.append(";\n"); + } + } + } + + if (customProperties != null) { + for (Map.Entry entry : customProperties.entrySet()) { + sb.append(entry.getKey()); + sb.append(": "); + sb.append(entry.getValue().toString()); + sb.append(";\n"); + } } + return sb.toString(); } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleSheet.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleSheet.java index 664d4eace..bb5392afc 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleSheet.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/StyleSheet.java @@ -18,6 +18,8 @@ */ package io.sf.carte.echosvg.css.engine; +import org.w3c.dom.DOMException; + import io.sf.carte.doc.style.css.MediaQueryList; /** @@ -129,6 +131,24 @@ public Rule getRule(int i) { return rules[i]; } + /** + * Removes a CSS rule from the CSS rule list at index. + * + * @param index the rule list index at which the rule must be removed. + * @throws DOMException INDEX_SIZE_ERR if index is greater than or + * equal to {@link #getSize()}. + */ + public void deleteRule(int index) throws DOMException { + if (index < 0 || index >= size) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, "Invalid index: " + index + '.'); + } + for (int i = index; i < size - 1; i++) { + rules[i] = rules[i + 1]; + } + size--; + rules[size] = null; + } + /** * Clears the content. */ diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/SupportsRule.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/SupportsRule.java new file mode 100644 index 000000000..fb8b195c7 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/SupportsRule.java @@ -0,0 +1,114 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine; + +import org.w3c.css.om.CSSRule; +import org.w3c.dom.DOMException; + +import io.sf.carte.doc.style.css.BooleanCondition; +import io.sf.carte.doc.style.css.nsac.CSSBudgetException; +import io.sf.carte.doc.style.css.nsac.CSSException; +import io.sf.carte.doc.style.css.om.CSSOMParser; +import io.sf.carte.doc.style.css.parser.CSSParser; + +/** + * This class represents a {@code @supports} CSS rule. + * + * @version $Id$ + */ +public class SupportsRule extends StyleSheet implements Rule { + + /** + * The type constant. + */ + public static final short TYPE = CSSRule.SUPPORTS_RULE; + + /** + * The media list. + */ + private BooleanCondition condition; + + boolean supports; + + public SupportsRule(BooleanCondition condition) { + super(); + this.condition = condition; + } + + /** + * Returns a constant identifying the rule type. + */ + @Override + public short getType() { + return TYPE; + } + + public BooleanCondition getCondition() { + return condition; + } + + public String getConditionText() { + return condition != null ? condition.toString() : ""; + } + + public void setConditionText(String conditionText) throws DOMException { + parseConditionText(conditionText); + } + + /** + * Parse the condition text. + * + * @param conditionText the condition text. + */ + private void parseConditionText(String conditionText) throws DOMException { + CSSParser parser = new CSSOMParser(); + try { + condition = parser.parseSupportsCondition(conditionText, null); + } catch (CSSBudgetException e) { + DOMException ex = new DOMException(DOMException.NOT_SUPPORTED_ERR, + "Limit found while parsing condition " + conditionText); + ex.initCause(e); + throw ex; + } catch (CSSException e) { + DOMException ex = new DOMException(DOMException.SYNTAX_ERR, + "Error parsing condition: " + conditionText); + ex.initCause(e); + throw ex; + } + } + + /** + * Returns a printable representation of this media rule. + */ + @Override + public String toString(CSSEngine eng) { + StringBuilder sb = new StringBuilder(); + sb.append("@supports"); + if (condition != null) { + sb.append(condition.toString()); + } + sb.append(" {\n"); + for (int i = 0; i < size; i++) { + sb.append(rules[i].toString(eng)); + } + sb.append("}\n"); + return sb.toString(); + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractColorManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractColorManager.java index 588c030ae..76aa3381b 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractColorManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractColorManager.java @@ -26,15 +26,17 @@ import io.sf.carte.doc.style.css.CSSColor; import io.sf.carte.doc.style.css.CSSTypedValue; +import io.sf.carte.doc.style.css.CSSValue; import io.sf.carte.doc.style.css.CSSValue.CssType; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.CSSParseException; import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.doc.style.css.nsac.LexicalUnit.LexicalType; import io.sf.carte.doc.style.css.parser.CSSParser; import io.sf.carte.doc.style.css.property.NumberValue; +import io.sf.carte.doc.style.css.property.PercentageEvaluator; import io.sf.carte.doc.style.css.property.StyleValue; import io.sf.carte.doc.style.css.property.ValueFactory; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; @@ -192,9 +194,11 @@ public Value createValue(LexicalUnit lunit, CSSEngine engine) throws DOMExceptio } case RGBCOLOR: return createRGBColor(lunit); - default: + case IDENT: // Clone so colors can be modified return super.createValue(lunit, engine).clone(); + default: + return super.createValue(lunit, engine); } } @@ -220,7 +224,7 @@ private LexicalUnit reparseColor(String colorSerialization) throws DOMException @Override public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engine, int idx, StyleMap sm, Value value) { - if (value.getPrimitiveType() == Type.IDENT) { + if (value.getPrimitiveType() == CSSValue.Type.IDENT) { String ident = ((AbstractStringValue) value).getValue(); // Search for a direct computed value. Value v = (Value) computedValues.get(ident); @@ -350,6 +354,19 @@ protected NumericValue createRGBColorComponent(LexicalUnit lu) throws DOMExcepti case PERCENTAGE: return new FloatValue(CSSUnit.CSS_PERCENTAGE, lu.getFloatValue()); + case VAR: + case ATTR: + throw new CSSProxyValueException(); + + case CALC: + Value calc = createCalc(lu); + if (calc.getCssValueType() == CSSValue.CssType.PROXY) { + throw new CSSProxyValueException(); + } else if (calc.getPrimitiveType() != Type.EXPRESSION) { + break; + } + return evaluateComponentExpression((CalcValue) calc); + default: } throw createInvalidRGBComponentUnitDOMException(lu.getLexicalUnitType()); @@ -369,9 +386,28 @@ protected NumericValue createColorComponent(LexicalUnit lu) throws DOMException case PERCENTAGE: return new FloatValue(CSSUnit.CSS_PERCENTAGE, lu.getFloatValue()); + case VAR: + case ATTR: + throw new CSSProxyValueException(); + + case CALC: + Value calc = createCalc(lu); + if (calc.getCssValueType() == CSSValue.CssType.PROXY) { + throw new CSSProxyValueException(); + } else if (calc.getPrimitiveType() != Type.EXPRESSION) { + break; + } + return evaluateComponentExpression((CalcValue) calc); + default: } - throw createInvalidRGBComponentUnitDOMException(lu.getLexicalUnitType()); + throw createInvalidComponentUnitDOMException(lu.getLexicalUnitType()); + } + + private FloatValue evaluateComponentExpression(CalcValue calc) { + PercentageEvaluator eval = new PercentageEvaluator(); + CSSTypedValue result = eval.evaluateExpression(calc.getExpressionDelegate()); + return new FloatValue(CSSUnit.CSS_NUMBER, result.getFloatValue(CSSUnit.CSS_NUMBER)); } /** @@ -388,4 +424,10 @@ private DOMException createInvalidRGBComponentUnitDOMException(LexicalType lexic return new DOMException(DOMException.NOT_SUPPORTED_ERR, s); } + private DOMException createInvalidComponentUnitDOMException(LexicalType lexicalType) { + Object[] p = { getPropertyName(), lexicalType.toString() }; + String s = Messages.formatMessage("invalid.color.component.unit", p); + return new DOMException(DOMException.NOT_SUPPORTED_ERR, s); + } + } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValue.java index e806cdb64..275db095c 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValue.java @@ -19,15 +19,11 @@ package io.sf.carte.echosvg.css.engine.value; import org.w3c.css.om.typed.CSSCounterValue; -import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; /** - * This class provides an abstract implementation of the Value interface. + * This class provides an abstract implementation of the CSSValue interface. * - *

- * Original author: Stephane Hillion. - *

* @version $Id$ */ public abstract class AbstractValue implements Value, Cloneable { @@ -39,16 +35,6 @@ public CssType getCssValueType() { return Value.CssType.TYPED; } - @Override - public Type getPrimitiveType() { - throw createDOMException(); - } - - @Override - public short getCSSUnit() { - return CSSUnit.CSS_INVALID; - } - @Override public void setCssText(String cssText) throws DOMException { throw createDOMException(); @@ -137,9 +123,9 @@ public boolean equals(Object obj) { return true; if (obj == null) return false; - if (!(obj instanceof Value)) + if (!(obj instanceof CSSVal)) return false; - Value other = (Value) obj; + CSSVal other = (CSSVal) obj; return getPrimitiveType() == other.getPrimitiveType(); } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueFactory.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueFactory.java index 70d1e007e..520e86b92 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueFactory.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueFactory.java @@ -20,8 +20,8 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.util.ParsedURL; /** diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueList.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueList.java index 9dbc762de..7ecd9ef81 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueList.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueList.java @@ -34,7 +34,7 @@ public class AbstractValueList extends ComponentValue implement /** * The items. */ - private ArrayList items; + ArrayList items; /** * The list separator. @@ -50,6 +50,8 @@ public AbstractValueList() { /** * Creates a ListValue with the given separator. + * + * @param s the separator. */ public AbstractValueList(char s) { this(s, 5); @@ -57,6 +59,9 @@ public AbstractValueList(char s) { /** * Creates a ListValue with the given separator and an initial capacity. + * + * @param s the separator. + * @param initialCapacity the initial capacity. */ public AbstractValueList(char s, int initialCapacity) { separator = s; diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueManager.java index 2a6c764f8..8e3db5179 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/AbstractValueManager.java @@ -21,7 +21,11 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; +import io.sf.carte.doc.style.css.CSSExpressionValue; +import io.sf.carte.doc.style.css.CSSValue.Type; +import io.sf.carte.doc.style.css.nsac.LexicalUnit; +import io.sf.carte.doc.style.css.property.StyleValue; +import io.sf.carte.doc.style.css.property.ValueFactory; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; @@ -50,6 +54,41 @@ public Value createStringValue(Type type, String value, CSSEngine engine) throws throw createDOMException(); } + protected Value createCalc(LexicalUnit lu) throws DOMException { + LexicalUnit lunit; + if (lu.getNextLexicalUnit() != null) { + lunit = lu.shallowClone(); + } else { + lunit = lu; + } + ValueFactory vf = new ValueFactory(); + StyleValue cssValue = vf.createCSSValue(lunit); + + Type pType = cssValue.getPrimitiveType(); + if (pType != Type.EXPRESSION) { + if (pType == Type.LEXICAL) { + if (lunit.getPreviousLexicalUnit() != null || lunit.isParameter()) { + throw new CSSProxyValueException(); + } + return createLexicalValue(lunit); + } + createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); + } + + CalcValue calc = new CalcValue((CSSExpressionValue) cssValue) { + + @Override + protected FloatValue absoluteValue(CSSStylableElement elt, String pseudo, CSSEngine engine, + int idx, StyleMap sm, FloatValue relative) { + return (FloatValue) AbstractValueManager.this.computeValue(elt, pseudo, engine, idx, sm, + relative); + } + + }; + + return calc; + } + /** * Implements * {@link ValueManager#computeValue(CSSStylableElement,String,CSSEngine,int,StyleMap,Value)}. @@ -68,7 +107,7 @@ public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engin } protected float lengthValue(Value cv) { - short unit = cv.getCSSUnit(); + short unit = cv.getUnitType(); if (!CSSUnit.isLengthUnitType(unit) && unit != CSSUnit.CSS_NUMBER) { throw createDOMException(unit); } @@ -85,4 +124,11 @@ protected DOMException createDOMException(int unit) { return new DOMException(DOMException.INVALID_ACCESS_ERR, s); } + protected Value createLexicalValue(LexicalUnit lu) throws CSSProxyValueException { + if (lu.getPreviousLexicalUnit() != null || lu.isParameter()) { + throw new CSSProxyValueException(); + } + return new LexicalValue(lu); + } + } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CSSProxyValueException.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CSSProxyValueException.java new file mode 100644 index 000000000..4d01e3ae7 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CSSProxyValueException.java @@ -0,0 +1,37 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +import org.w3c.dom.DOMException; + +/** + * A PROXY value was found. + *

+ * This class is intended for internal use by this implementation. + *

+ */ +public class CSSProxyValueException extends DOMException { + + private static final long serialVersionUID = 1L; + + public CSSProxyValueException() { + super(DOMException.INVALID_ACCESS_ERR, "Found a PROXY value."); + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CSSVal.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CSSVal.java new file mode 100644 index 000000000..162e11a8a --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CSSVal.java @@ -0,0 +1,114 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +import java.io.IOException; + +import org.w3c.css.om.typed.CSSStyleValue; +import org.w3c.dom.DOMException; + +import io.sf.carte.doc.style.css.CSSValueSyntax; +import io.sf.carte.doc.style.css.CSSValueSyntax.Match; +import io.sf.carte.util.SimpleWriter; + +/** + * A gateway value between CSS Typed OM and an internal representation. + * + * @author See Git history. + * @version $Id$ + */ +public interface CSSVal extends io.sf.carte.doc.style.css.CSSValue, CSSStyleValue { + + /** + * If this value is a list or contains components, the number of + * CSSStyleValues in the list. The range of valid values of the + * indices is 0 to length-1 inclusive. + * + * @return the number of components, or {@code 0} if this value is not a list + * nor does it contain components. + */ + int getLength(); + + /** + * If this value is a list, give the item corresponding to the requested index. + * If there is no item at such index, return {@code null} If this object is not + * a list and the index is {@code 0}, return itself. + * + * @param index the index on the list. + * @return the item, or {@code null} if there is no item on that index. + */ + default CSSVal item(int index) { + return index == 0 ? this : null; + } + + /** + * Convenience method that either returns an identifier or throws an exception. + * + * @exception DOMException INVALID_ACCESS_ERR: Raised if the value doesn't + * contain an identifier value. + */ + String getIdentifierValue() throws DOMException; + + /** + * If this value can be used where a string is expected, get the value. + * + * @return the string value, without the commas. + * @exception DOMException INVALID_ACCESS_ERR: Raised if the value doesn't + * contain a String. + */ + String getStringValue() throws DOMException; + + /** + * Convenience method that either returns a String or URI or throws an exception. + * + * @exception DOMException INVALID_ACCESS_ERR: Raised if the value doesn't + * contain a String nor a URI value. + */ + String getURIValue() throws DOMException; + + /** + * Convenience method that either returns the float value, if the value is + * numeric, or throws an exception. + * + * @return the float value. + */ + float getFloatValue(); + + @Override + default void writeCssText(SimpleWriter wri) throws IOException { + wri.write(getCssText()); + } + + @Override + default Match matches(CSSValueSyntax syntax) { + return Match.PENDING; + } + + /** + * Create and return a copy of this object. + *

+ * If this object is unmodifiable, the clone will be modifiable. + *

+ * + * @return a modifiable copy of this object. + */ + @Override + CSSVal clone(); + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CalcValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CalcValue.java new file mode 100644 index 000000000..4abdc57e9 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/CalcValue.java @@ -0,0 +1,138 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +import org.w3c.css.om.typed.CSSUnitValue; +import org.w3c.css.om.unit.CSSUnit; +import org.w3c.dom.DOMException; + +import io.sf.carte.doc.style.css.CSSExpressionValue; +import io.sf.carte.doc.style.css.CSSTypedValue; +import io.sf.carte.doc.style.css.property.Evaluator; +import io.sf.carte.doc.style.css.property.NumberValue; +import io.sf.carte.echosvg.css.engine.CSSEngine; +import io.sf.carte.echosvg.css.engine.CSSStylableElement; +import io.sf.carte.echosvg.css.engine.StyleMap; + +/** + * {@code calc()} value. + * + * @author See Git history. + * @version $Id$ + */ +public class CalcValue extends NumericValue { + + private CSSExpressionValue expressionDelegate; + + /** + * Creates a new value. + */ + public CalcValue(CSSExpressionValue expr) { + super(); + this.expressionDelegate = expr; + } + + @Override + public Type getPrimitiveType() { + return Type.EXPRESSION; + } + + @Override + public String getCssText() { + return expressionDelegate.getCssText(); + } + + @Override + short getCSSUnit() { + return expressionDelegate.computeUnitType(); + } + + public CSSExpressionValue getExpressionDelegate() { + return expressionDelegate; + } + + public FloatValue evaluate(CSSStylableElement elt, String pseudo, CSSEngine engine, int idx, StyleMap sm, + final short unit) throws DOMException { + Evaluator eval = new Evaluator(unit) { + + @Override + protected CSSTypedValue absoluteTypedValue(CSSTypedValue typed) { + if (CSSUnit.isRelativeLengthUnitType(typed.getUnitType())) { + FloatValue relative = new FloatValue(typed.getUnitType(), + typed.getFloatValue(typed.getUnitType())); + FloatValue abs = CalcValue.this.absoluteValue(elt, pseudo, engine, idx, sm, relative); + short u; + if (abs.getUnitType() != CSSUnit.CSS_NUMBER) { + u = abs.getUnitType(); + } else { + u = unit; + } + return NumberValue.createCSSNumberValue(u, abs.getFloatValue()); + } else { + return typed; + } + } + + @Override + protected float percentage(CSSTypedValue typed, short resultType) throws DOMException { + FloatValue relative = new FloatValue(typed.getUnitType(), typed.getFloatValue(typed.getUnitType())); + FloatValue abs = CalcValue.this.absoluteValue(elt, pseudo, engine, idx, sm, relative); + return NumberValue.floatValueConversion(abs.getFloatValue(), abs.getUnitType(), resultType); + } + + }; + + CSSTypedValue typed = eval.evaluateExpression(expressionDelegate); + if (typed.getPrimitiveType() != Type.NUMERIC) { + throw new DOMException(DOMException.INVALID_STATE_ERR, + "Unexpected calc() result: " + typed.getCssText()); + } + + float f; + short u; + if (typed.getUnitType() == CSSUnit.CSS_NUMBER) { + // Plain number can be interpreted as px or deg according to context + u = CSSUnit.CSS_NUMBER; + f = typed.getFloatValue(CSSUnit.CSS_NUMBER); + } else { + u = unit; + f = typed.getFloatValue(unit); + } + + return new FloatValue(u, f); + } + + protected FloatValue absoluteValue(CSSStylableElement elt, String pseudo, CSSEngine engine, int idx, + StyleMap sm, FloatValue relative) throws DOMException { + return relative; + } + + @Override + public CSSUnitValue to(String unit) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not supported."); + } + + @Override + public CalcValue clone() { + CalcValue clon = (CalcValue) super.clone(); + clon.expressionDelegate = expressionDelegate; + return clon; + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ColorFunction.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ColorFunction.java index d4012a2b2..99397b5f8 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ColorFunction.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ColorFunction.java @@ -167,7 +167,7 @@ public String getCssText() { * @throws DOMSyntaxException if the value is inadequate for a component. */ private NumericValue numericComponent(NumericValue ch) throws DOMSyntaxException { - if (ch.getCSSUnit() != CSSUnit.CSS_PERCENTAGE && ch.getCSSUnit() != CSSUnit.CSS_NUMBER) { + if (ch.getUnitType() != CSSUnit.CSS_PERCENTAGE && ch.getUnitType() != CSSUnit.CSS_NUMBER) { throw new DOMSyntaxException("color() component must be a number or percentage."); } componentize(ch); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ColorValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ColorValue.java index 1d0acfc72..f3c729f54 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ColorValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ColorValue.java @@ -29,7 +29,7 @@ * @author See Git history. * @version $Id$ */ -public abstract class ColorValue extends ComponentValue implements CSSColorValue { +public abstract class ColorValue extends ComponentValue implements TypedValue, CSSColorValue { public static final String RGB_FUNCTION = "rgb"; @@ -134,7 +134,7 @@ public void setAlpha(CSSNumericValue alpha) throws DOMSyntaxException { private void setAlphaChannel(CSSNumericValue alpha) throws DOMSyntaxException { NumericValue a = (NumericValue) alpha; - if (a.getCSSUnit() != CSSUnit.CSS_PERCENTAGE && a.getCSSUnit() != CSSUnit.CSS_NUMBER) { + if (a.getUnitType() != CSSUnit.CSS_PERCENTAGE && a.getUnitType() != CSSUnit.CSS_NUMBER) { throw new DOMSyntaxException("Alpha channel must be a number or percentage."); } componentize(a); @@ -144,7 +144,7 @@ private void setAlphaChannel(CSSNumericValue alpha) throws DOMSyntaxException { } boolean isOpaque() { - switch (alpha.getCSSUnit()) { + switch (alpha.getUnitType()) { case CSSUnit.CSS_NUMBER: return alpha.getFloatValue() == 1f; case CSSUnit.CSS_PERCENTAGE: diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ComputedValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ComputedValue.java index 0fb8ed4b4..0b8fe9080 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ComputedValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ComputedValue.java @@ -91,8 +91,8 @@ public Type getPrimitiveType() { } @Override - public short getCSSUnit() { - return computedValue.getCSSUnit(); + public short getUnitType() { + return computedValue.getUnitType(); } @Override diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/FloatValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/FloatValue.java index 572839a3b..26cb3cef7 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/FloatValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/FloatValue.java @@ -28,10 +28,11 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSNumberValue; +import io.sf.carte.doc.style.css.UnitStringToId; import io.sf.carte.doc.style.css.nsac.CSSParseException; import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.doc.style.css.parser.CSSParser; -import io.sf.carte.doc.style.css.parser.ParseHelper; import io.sf.carte.doc.style.css.property.NumberValue; /** @@ -40,7 +41,7 @@ * @author See Git history. * @version $Id$ */ -public class FloatValue extends NumericValue implements CSSUnitValue { +public class FloatValue extends NumericValue implements CSSNumberValue, CSSUnitValue { /** * Returns the CSS text associated with the given type/value pair. @@ -80,6 +81,16 @@ public static FloatValue createConstant(short unit, float value) { */ private short unitType; + /** + * True if this value is the result of a calculation + */ + private boolean calculated = false; + + /** + * True if this number is in the same unit as was specified. + */ + private boolean specified = true; + /** * Creates a new value. * @@ -100,7 +111,7 @@ public Type getPrimitiveType() { } @Override - public short getCSSUnit() { + public short getUnitType() { return unitType; } @@ -181,12 +192,51 @@ public void setCssText(String cssText) throws DOMException { } } + @Override + public boolean isCalculatedNumber() { + return calculated; + } + /** - * Returns a printable representation of this value. + * Sets whether this number is the result of a calculation. + * + * @param calculated {@code true} if this number was calculated. */ @Override - public String toString() { - return getCssText(); + public void setCalculatedNumber(boolean calculated) { + this.calculated = calculated; + this.specified = this.specified && !calculated; + } + + @Override + public void setExpectInteger() throws DOMException { + if (getUnitType() != CSSUnit.CSS_NUMBER) { + super.setExpectInteger(); + } else if (calculated) { + floatValue = Math.round(floatValue); + } else if (!isInteger()) { + super.setExpectInteger(); + } + } + + private boolean isInteger() { + return Math.rint(floatValue) == floatValue; + } + + @Override + public void roundToInteger() throws DOMException { + setExpectInteger(); + floatValue = Math.round(floatValue); + } + + @Override + public boolean isNegativeNumber() { + return floatValue < 0f; + } + + @Override + public boolean isNumberZero() { + return floatValue == 0f; } @Override @@ -206,81 +256,21 @@ public boolean equals(Object obj) { if (!(obj instanceof Value)) return false; Value other = (Value) obj; - return other.getCSSUnit() == unitType + return other.getUnitType() == unitType && Float.floatToIntBits(floatValue) == Float.floatToIntBits(other.getFloatValue()); } @Override public CSSUnitValue to(String unit) { - short destUnit = ParseHelper.unitFromString(unit); + short destUnit = UnitStringToId.unitFromString(unit); float destValue = NumberValue.floatValueConversion(floatValue, unitType, destUnit); FloatValue toVal = new FloatValue(destUnit, destValue); return toVal; } @Override - public CSSNumericType type() { - return new NumericType(); - } - - class NumericType implements CSSNumericType { - - @Override - public int getLength() { - return CSSUnit.isLengthUnitType(unitType) ? 1 : 0; - } - - @Override - public int getAngle() { - return CSSUnit.isAngleUnitType(unitType) ? 1 : 0; - } - - @Override - public int getTime() { - return CSSUnit.isTimeUnitType(unitType) ? 1 : 0; - } - - @Override - public int getFrequency() { - return unitType == CSSUnit.CSS_HZ || unitType == CSSUnit.CSS_KHZ ? 1 : 0; - } - - @Override - public int getResolution() { - return CSSUnit.isResolutionUnitType(unitType) ? 1 : 0; - } - - @Override - public int getFlex() { - return unitType == CSSUnit.CSS_FR ? 1 : 0; - } - - @Override - public int getPercent() { - return unitType == CSSUnit.CSS_PERCENTAGE ? 1 : 0; - } - - @Override - public CSSNumericBaseType getPercentHint() { - CSSNumericBaseType baseType = null; - if (CSSUnit.isLengthUnitType(unitType)) { - baseType = CSSNumericBaseType.length; - } else if (unitType == CSSUnit.CSS_PERCENTAGE) { - baseType = CSSNumericBaseType.percent; - } else if (CSSUnit.isTimeUnitType(unitType)) { - baseType = CSSNumericBaseType.time; - } else if (CSSUnit.isAngleUnitType(unitType)) { - baseType = CSSNumericBaseType.angle; - } else if (CSSUnit.isResolutionUnitType(unitType)) { - baseType = CSSNumericBaseType.resolution; - } else if (unitType == CSSUnit.CSS_HZ || unitType == CSSUnit.CSS_KHZ) { - baseType = CSSNumericBaseType.frequency; - } else if (unitType == CSSUnit.CSS_FR) { - baseType = CSSNumericBaseType.flex; - } - return baseType; - } - + short getCSSUnit() { + return unitType; } @Override diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/IdentifierManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/IdentifierManager.java index 2b54976c6..e59010bf2 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/IdentifierManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/IdentifierManager.java @@ -22,8 +22,8 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; /** @@ -44,9 +44,6 @@ public abstract class IdentifierManager extends AbstractValueManager { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case IDENT: String s = lu.getStringValue().toLowerCase(Locale.ROOT).intern(); Object v = getIdentifiers().get(s); @@ -55,6 +52,22 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { } return (Value) v; + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lu); + default: throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ImmutableUnitValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ImmutableUnitValue.java index dde6aadfc..c54bcbb63 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ImmutableUnitValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ImmutableUnitValue.java @@ -47,7 +47,7 @@ public void setValue(String cssText) throws DOMException { @Override public FloatValue clone() { - return new FloatValue(getCSSUnit(), getFloatValue()); + return new FloatValue(getUnitType(), getFloatValue()); } } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/InheritValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/InheritValue.java index 3b036b598..711d045be 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/InheritValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/InheritValue.java @@ -18,9 +18,6 @@ */ package io.sf.carte.echosvg.css.engine.value; -import org.w3c.api.DOMTypeException; -import org.w3c.css.om.typed.CSSKeywordValue; - /** * This singleton class represents the 'inherit' value. * @@ -30,7 +27,7 @@ *

* @version $Id$ */ -public class InheritValue extends AbstractValue implements CSSKeywordValue { +public final class InheritValue extends KeywordValue { /** * The only instance of this class. @@ -40,15 +37,7 @@ public class InheritValue extends AbstractValue implements CSSKeywordValue { /** * Creates a new InheritValue object. */ - protected InheritValue() { - } - - /** - * A string representation of the current value. - */ - @Override - public String getCssText() { - return getValue(); + InheritValue() { } @Override @@ -56,19 +45,6 @@ public String getValue() { return "inherit"; } - @Override - public void setValue(String value) throws DOMTypeException { - throw new DOMTypeException("Not supported."); - } - - /** - * A code defining the type of the value. - */ - @Override - public CssType getCssValueType() { - return CssType.KEYWORD; - } - @Override public Type getPrimitiveType() { return Type.INHERIT; @@ -80,11 +56,12 @@ public InheritValue clone() { } /** - * Returns a printable representation of this object. + * Get the instance of {@code inherit}. + * + * @return the instance of {@code inherit}. */ - @Override - public String toString() { - return getCssText(); + public static Value getInstance() { + return INSTANCE; } } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/KeywordValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/KeywordValue.java new file mode 100644 index 000000000..8795e6d1c --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/KeywordValue.java @@ -0,0 +1,57 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +import org.w3c.api.DOMTypeException; +import org.w3c.css.om.typed.CSSKeywordValue; + +/** + * A CSS-wide non-primitive keyword value: 'inherit', 'unset', 'revert'. + * + */ +abstract class KeywordValue extends AbstractValue implements CSSKeywordValue { + + /** + * Creates a new UnsetValue object. + */ + protected KeywordValue() { + } + + /** + * A string representation of the current value. + */ + @Override + public String getCssText() { + return getValue(); + } + + @Override + public void setValue(String value) throws DOMTypeException { + throw new DOMTypeException("Not supported."); + } + + /** + * A code defining the type of the value. + */ + @Override + public CssType getCssValueType() { + return CssType.KEYWORD; + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LCHColorValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LCHColorValue.java index 46d273651..0181def91 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LCHColorValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LCHColorValue.java @@ -124,7 +124,7 @@ public void setL(CSSNumericValue lightness) throws DOMSyntaxException { */ private NumericValue component(CSSNumericValue c) throws DOMSyntaxException { NumericValue ch = (NumericValue) c; - if (ch.getCSSUnit() != CSSUnit.CSS_PERCENTAGE && ch.getCSSUnit() != CSSUnit.CSS_NUMBER) { + if (ch.getUnitType() != CSSUnit.CSS_PERCENTAGE && ch.getUnitType() != CSSUnit.CSS_NUMBER) { throw new DOMSyntaxException("LC component must be a number or percentage."); } if (ch.handler != null) { @@ -162,7 +162,7 @@ public void setH(CSSNumericValue h) throws DOMSyntaxException { private NumericValue hueComponent(CSSNumericValue h) { NumericValue ch = (NumericValue) h; - if (ch.getCSSUnit() != CSSUnit.CSS_NUMBER && !CSSUnit.isAngleUnitType(ch.getCSSUnit())) { + if (ch.getUnitType() != CSSUnit.CSS_NUMBER && !CSSUnit.isAngleUnitType(ch.getUnitType())) { throw new DOMSyntaxException("Hue component must be a number or angle."); } if (ch.handler != null) { diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LabColorValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LabColorValue.java index 17cb92ed2..29e71e1e0 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LabColorValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LabColorValue.java @@ -124,7 +124,7 @@ public void setL(CSSNumericValue lightness) throws DOMSyntaxException { */ private NumericValue component(CSSNumericValue c) throws DOMSyntaxException { NumericValue ch = (NumericValue) c; - if (ch.getCSSUnit() != CSSUnit.CSS_PERCENTAGE && ch.getCSSUnit() != CSSUnit.CSS_NUMBER) { + if (ch.getUnitType() != CSSUnit.CSS_PERCENTAGE && ch.getUnitType() != CSSUnit.CSS_NUMBER) { throw new DOMSyntaxException("Lab component must be a number or percentage."); } if (ch.handler != null) { diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LengthManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LengthManager.java index 48c5072d8..69b7e2674 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LengthManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LengthManager.java @@ -21,10 +21,10 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.doc.style.css.property.NumberValue; import io.sf.carte.echosvg.css.Viewport; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSContext; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; @@ -64,6 +64,23 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { case PERCENTAGE: return new FloatValue(CSSUnit.CSS_PERCENTAGE, lu.getFloatValue()); + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lu); + + case CALC: + return createCalc(lu); + default: break; } @@ -96,38 +113,41 @@ public Value createFloatValue(short type, float floatValue) throws DOMException public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engine, int idx, StyleMap sm, Value value) { if (value.getPrimitiveType() != Type.NUMERIC) { - return value; + if (value.getPrimitiveType() != Type.EXPRESSION) { + return value; + } + try { + return evaluateCalc((CalcValue) value, elt, pseudo, engine, idx, sm, CSSUnit.CSS_PX); + } catch (Exception e) { + return isInheritedProperty() ? null : getDefaultValue(); + } } - switch (value.getCSSUnit()) { + CSSContext ctx; + switch (value.getUnitType()) { case CSSUnit.CSS_NUMBER: case CSSUnit.CSS_PX: return value; case CSSUnit.CSS_MM: - CSSContext ctx = engine.getCSSContext(); float v = value.getFloatValue(); - return new FloatValue(CSSUnit.CSS_NUMBER, v / ctx.getPixelUnitToMillimeter()); + return new FloatValue(CSSUnit.CSS_NUMBER, v * 3.779527559055f); case CSSUnit.CSS_CM: - ctx = engine.getCSSContext(); v = value.getFloatValue(); - return new FloatValue(CSSUnit.CSS_NUMBER, v * 10f / ctx.getPixelUnitToMillimeter()); + return new FloatValue(CSSUnit.CSS_NUMBER, v * 37.79527559055f); case CSSUnit.CSS_IN: - ctx = engine.getCSSContext(); v = value.getFloatValue(); - return new FloatValue(CSSUnit.CSS_NUMBER, v * 25.4f / ctx.getPixelUnitToMillimeter()); + return new FloatValue(CSSUnit.CSS_NUMBER, v * 96f); case CSSUnit.CSS_PT: - ctx = engine.getCSSContext(); v = value.getFloatValue(); - return new FloatValue(CSSUnit.CSS_NUMBER, v * 25.4f / (72f * ctx.getPixelUnitToMillimeter())); + return new FloatValue(CSSUnit.CSS_NUMBER, v / 0.75f); case CSSUnit.CSS_PC: - ctx = engine.getCSSContext(); v = value.getFloatValue(); - return new FloatValue(CSSUnit.CSS_NUMBER, (v * 25.4f / (6f * ctx.getPixelUnitToMillimeter()))); + return new FloatValue(CSSUnit.CSS_NUMBER, v * 16f); case CSSUnit.CSS_EM: sm.putFontSizeRelative(idx, true); @@ -247,8 +267,8 @@ public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engin // Maybe it is one of the new absolute length units try { value = new FloatValue(CSSUnit.CSS_NUMBER, - NumberValue.floatValueConversion(value.getFloatValue(), value.getCSSUnit(), CSSUnit.CSS_MM) - / engine.getCSSContext().getPixelUnitToMillimeter()); + NumberValue.floatValueConversion(value.getFloatValue(), value.getUnitType(), + CSSUnit.CSS_PX)); } catch (DOMException e) { } } @@ -256,6 +276,11 @@ public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engin return value; } + protected Value evaluateCalc(CalcValue value, CSSStylableElement elt, String pseudo, CSSEngine engine, + int idx, StyleMap sm, short destUnit) throws DOMException { + return value.evaluate(elt, pseudo, engine, idx, sm, destUnit); + } + // // Orientation enumeration // diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LexicalHelper.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LexicalHelper.java new file mode 100644 index 000000000..5e70ce3ca --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LexicalHelper.java @@ -0,0 +1,43 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +import java.io.IOException; +import java.io.StringReader; + +import io.sf.carte.doc.style.css.nsac.CSSParseException; +import io.sf.carte.doc.style.css.nsac.LexicalUnit; +import io.sf.carte.doc.style.css.parser.CSSParser; + +/** + * Lexical Helper + * + * @version $Id$ + */ +class LexicalHelper { + + public static LexicalUnit parsePropertyValue(String value) { + try { + return new CSSParser().parsePropertyValue(new StringReader(value)); + } catch (CSSParseException | IOException e) { + return null; + } + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LexicalValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LexicalValue.java new file mode 100644 index 000000000..e26f7e321 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/LexicalValue.java @@ -0,0 +1,71 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +import io.sf.carte.doc.style.css.nsac.LexicalUnit; + +/** + * A value containing {@code var()} or any PROXY value. + * + * @version $Id$ + */ +public class LexicalValue extends AbstractValue { + + private LexicalUnit lunit = null; + + /** + * Creates a new LexicalValue object. + * + * @param params the lexical unit with the {@code var()} function(s). + */ + public LexicalValue(LexicalUnit lunit) throws IllegalArgumentException { + super(); + this.lunit = lunit; + } + + /** + * A code defining the type of the value. + */ + @Override + public CssType getCssValueType() { + return CssType.PROXY; + } + + @Override + public Type getPrimitiveType() { + return Type.LEXICAL; + } + + public LexicalUnit getLexicalUnit() { + return lunit; + } + + @Override + public String getCssText() { + return lunit.toString(); + } + + @Override + public LexicalValue clone() { + LexicalValue clon = (LexicalValue) super.clone(); + clon.lunit = lunit; + return clon; + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ListValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ListValue.java index d6aabf205..5d52eda02 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ListValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ListValue.java @@ -18,6 +18,10 @@ */ package io.sf.carte.echosvg.css.engine.value; +import java.util.Iterator; + +import org.w3c.dom.DOMException; + /** * This class represents a list of values. * @@ -25,6 +29,7 @@ * @version $Id$ */ public class ListValue extends AbstractValueList { + /** * Creates a ListValue. */ @@ -34,15 +39,20 @@ public ListValue() { /** * Creates a ListValue with the given separator. + * + * @param s the separator. */ public ListValue(char s) { super(s); } /** - * Creates a ListValue with the given separator. + * Creates a ListValue with the given separator and initial capacity. + * + * @param s the separator. + * @param initialCapacity the initial capacity. */ - ListValue(char s, int initialCapacity) { + public ListValue(char s, int initialCapacity) { super(s, initialCapacity); } @@ -53,4 +63,96 @@ public void append(Value v) { add(v); } + /** + * Create an unmodifiable view of this list value. + * + * @return the unmodifiable view. + */ + public ListValue createUnmodifiableView() { + return new UnmodifiableListValue(); + } + + private class UnmodifiableListValue extends ListValue { + UnmodifiableListValue() { + super(ListValue.this.getSeparatorChar(), 1); + } + + @Override + public char getSeparatorChar() { + return ListValue.this.getSeparatorChar(); + } + + @Override + public String getCssText() { + return ListValue.this.getCssText(); + } + + @Override + public Value item(int index) { + return ListValue.this.item(index); + } + + @Override + public int getLength() { + return ListValue.this.getLength(); + } + + @Override + public Iterator iterator() { + return new Iterator() { + Iterator iter = ListValue.this.items.iterator(); + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public Value next() { + return iter.next(); + } + + }; + } + + @Override + public boolean isEmpty() { + return ListValue.this.isEmpty(); + } + + @Override + public AbstractValueList clone() { + return ListValue.this.clone(); + } + + @Override + public boolean add(Value value) { + throw createNoModificationDOMException(); + } + + @Override + public void clear() { + throw createNoModificationDOMException(); + } + + @Override + public Value remove(int index) { + throw createNoModificationDOMException(); + } + + @Override + public Value set(int index, Value value) { + throw createNoModificationDOMException(); + } + + /** + * Creates a NO_MODIFICATION_ALLOWED_ERR exception. + */ + private DOMException createNoModificationDOMException() { + return new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, + "Cannot modify this list."); + } + + } + } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/NumericValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/NumericValue.java index 24568e3b5..c2b44c6d6 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/NumericValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/NumericValue.java @@ -19,6 +19,7 @@ package io.sf.carte.echosvg.css.engine.value; import org.w3c.css.om.typed.CSSNumericValue; +import org.w3c.css.om.unit.CSSUnit; /** * Base class for numeric values. @@ -26,7 +27,7 @@ * @author See Git history. * @version $Id$ */ -public abstract class NumericValue extends AbstractValue implements CSSNumericValue { +public abstract class NumericValue extends AbstractValue implements TypedValue, CSSNumericValue { /** * Creates a new value. @@ -36,6 +37,77 @@ public NumericValue() { } @Override - public abstract NumericValue clone(); + public NumericValue clone() { + return (NumericValue) super.clone(); + } + + abstract short getCSSUnit(); + + @Override + public CSSNumericType type() { + return new NumericType(); + } + + class NumericType implements CSSNumericType { + + @Override + public int getLength() { + return CSSUnit.isLengthUnitType(getCSSUnit()) ? 1 : 0; + } + + @Override + public int getAngle() { + return CSSUnit.isAngleUnitType(getCSSUnit()) ? 1 : 0; + } + + @Override + public int getTime() { + return CSSUnit.isTimeUnitType(getCSSUnit()) ? 1 : 0; + } + + @Override + public int getFrequency() { + short unit = getCSSUnit(); + return unit == CSSUnit.CSS_HZ || unit == CSSUnit.CSS_KHZ ? 1 : 0; + } + + @Override + public int getResolution() { + return CSSUnit.isResolutionUnitType(getCSSUnit()) ? 1 : 0; + } + + @Override + public int getFlex() { + return getCSSUnit() == CSSUnit.CSS_FR ? 1 : 0; + } + + @Override + public int getPercent() { + return getCSSUnit() == CSSUnit.CSS_PERCENTAGE ? 1 : 0; + } + + @Override + public CSSNumericBaseType getPercentHint() { + short unitType = getCSSUnit(); + CSSNumericBaseType baseType = null; + if (CSSUnit.isLengthUnitType(unitType)) { + baseType = CSSNumericBaseType.length; + } else if (unitType == CSSUnit.CSS_PERCENTAGE) { + baseType = CSSNumericBaseType.percent; + } else if (CSSUnit.isTimeUnitType(unitType)) { + baseType = CSSNumericBaseType.time; + } else if (CSSUnit.isAngleUnitType(unitType)) { + baseType = CSSNumericBaseType.angle; + } else if (CSSUnit.isResolutionUnitType(unitType)) { + baseType = CSSNumericBaseType.resolution; + } else if (unitType == CSSUnit.CSS_HZ || unitType == CSSUnit.CSS_KHZ) { + baseType = CSSNumericBaseType.frequency; + } else if (unitType == CSSUnit.CSS_FR) { + baseType = CSSNumericBaseType.flex; + } + return baseType; + } + + } } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/PendingValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/PendingValue.java new file mode 100644 index 000000000..2bbbb8378 --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/PendingValue.java @@ -0,0 +1,65 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +import io.sf.carte.doc.style.css.nsac.LexicalUnit; + +/** + * A value meaning that a longhand property is pending the PROXY substitution of + * a shorthand value. + * + * @version $Id$ + */ +public class PendingValue extends LexicalValue { + + private String shorthandName; + + /** + * Creates a new PendingValue object. + * + * @param shorthandName the shorthand name. + * @param the lexical unit that is pending substitution. + */ + public PendingValue(String shorthandName, LexicalUnit lunit) throws IllegalArgumentException { + super(lunit); + this.shorthandName = shorthandName; + } + + @Override + public Type getPrimitiveType() { + return Type.INTERNAL; + } + + public String getShorthandName() { + return shorthandName; + } + + @Override + public String getCssText() { + return ""; + } + + @Override + public PendingValue clone() { + PendingValue clon = (PendingValue) super.clone(); + clon.shorthandName = shorthandName; + return clon; + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/PropertyDefinition.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/PropertyDefinition.java new file mode 100644 index 000000000..b441ebbda --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/PropertyDefinition.java @@ -0,0 +1,57 @@ +/* + * This software adapts interfaces defined by CSS Properties and Values API Level 1 + * (https://www.w3.org/TR/css-properties-values-api-1/). + * Copyright © 2020 W3C® (MIT, ERCIM, Keio, Beihang). + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + */ +/* + * SPDX-License-Identifier: W3C-20150513 + */ + +package io.sf.carte.echosvg.css.engine.value; + +import io.sf.carte.doc.style.css.CSSValueSyntax; +import io.sf.carte.doc.style.css.nsac.LexicalUnit; + +/** + * A property definition. + *

+ * See CSS + * Properties and Values API Level 1. + *

+ */ +public interface PropertyDefinition { + + /** + * Gets the property name. + * + * @return the property name. + */ + String getName(); + + /** + * Whether the property inherits or not. + * + * @return {@code true} if the property inherits. + */ + boolean inherits(); + + /** + * The initial value associated with the property. + * + * @return the initial value, or {@code null} if none was specified. + */ + LexicalUnit getInitialValue(); + + /** + * The syntax associated with the property. + *

+ * If the syntax descriptor was not recognized, {@code *} will be used. + *

+ * + * @return the syntax. Cannot be {@code null}. + */ + CSSValueSyntax getSyntax(); + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RGBColorValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RGBColorValue.java index e53a9d73a..3145b2f40 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RGBColorValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RGBColorValue.java @@ -173,9 +173,9 @@ public static RGBColorValue createLegacy(NumericValue r, NumericValue g, Numeric g = legacyRange(g); b = legacyRange(b); RGBColorValue rgb = new RGBColorValue(r, g, b); - rgb.pcntSpecified = r.getCSSUnit() == CSSUnit.CSS_PERCENTAGE - || g.getCSSUnit() == CSSUnit.CSS_PERCENTAGE - || b.getCSSUnit() == CSSUnit.CSS_PERCENTAGE; + rgb.pcntSpecified = r.getUnitType() == CSSUnit.CSS_PERCENTAGE + || g.getUnitType() == CSSUnit.CSS_PERCENTAGE + || b.getUnitType() == CSSUnit.CSS_PERCENTAGE; return rgb; } @@ -189,18 +189,18 @@ public static RGBColorValue createLegacy(NumericValue r, NumericValue g, Numeric */ private static NumericValue constantLegacyRange(NumericValue ch) throws DOMSyntaxException, IllegalArgumentException { - if (ch.getCSSUnit() == CSSUnit.CSS_NUMBER) { + if (ch.getUnitType() == CSSUnit.CSS_NUMBER) { if (ch.getPrimitiveType() == Type.NUMERIC) { ch = new ImmutableUnitValue(CSSUnit.CSS_NUMBER, ch.getFloatValue() / 255f); } else { throw new IllegalArgumentException("Cannot normalize value to [0,1] now: " + ch.getCssText()); } - } else if (ch.getCSSUnit() != CSSUnit.CSS_PERCENTAGE) { + } else if (ch.getUnitType() != CSSUnit.CSS_PERCENTAGE) { throw new DOMSyntaxException("RGB component must be a number or percentage, not a " - + CSSUnit.dimensionUnitString(ch.getCSSUnit()) + '.'); + + CSSUnit.dimensionUnitString(ch.getUnitType()) + '.'); } if (ch.handler != null) { - ch = new ImmutableUnitValue(ch.getCSSUnit(), ch.getFloatValue()); + ch = new ImmutableUnitValue(ch.getUnitType(), ch.getFloatValue()); } return ch; } @@ -215,15 +215,15 @@ private static NumericValue constantLegacyRange(NumericValue ch) */ private static NumericValue legacyRange(NumericValue ch) throws DOMSyntaxException, IllegalArgumentException { - if (ch.getCSSUnit() == CSSUnit.CSS_NUMBER) { + if (ch.getUnitType() == CSSUnit.CSS_NUMBER) { if (ch.getPrimitiveType() == Type.NUMERIC) { ch.setFloatValue(ch.getFloatValue() / 255f); } else { throw new IllegalArgumentException("Cannot normalize value to [0,1] now: " + ch.getCssText()); } - } else if (ch.getCSSUnit() != CSSUnit.CSS_PERCENTAGE) { + } else if (ch.getUnitType() != CSSUnit.CSS_PERCENTAGE) { throw new DOMSyntaxException("RGB component must be a number or percentage, not a " - + CSSUnit.dimensionUnitString(ch.getCSSUnit()) + '.'); + + CSSUnit.dimensionUnitString(ch.getUnitType()) + '.'); } return ch; } @@ -279,7 +279,7 @@ private String rgbComponentText(NumericValue comp, DecimalFormat df) { } float f; - if (comp.getCSSUnit() == CSSUnit.CSS_NUMBER) { + if (comp.getUnitType() == CSSUnit.CSS_NUMBER) { f = comp.getFloatValue() * 255f; } else { f = comp.getFloatValue() * 2.55f; @@ -288,7 +288,7 @@ private String rgbComponentText(NumericValue comp, DecimalFormat df) { } private String alphaComponentText(NumericValue alpha, DecimalFormat df) { - if (alphaPcntSpecified || alpha.getCSSUnit() == CSSUnit.CSS_NUMBER) { + if (alphaPcntSpecified || alpha.getUnitType() == CSSUnit.CSS_NUMBER) { return alpha.getCssText(); } @@ -320,7 +320,7 @@ public NumericValue getB() { * * @return the red component. */ - public Value getRed() { + public NumericValue getRed() { return red; } @@ -329,7 +329,7 @@ public Value getRed() { * * @return the green component. */ - public Value getGreen() { + public NumericValue getGreen() { return green; } @@ -338,7 +338,7 @@ public Value getGreen() { * * @return the blue component. */ - public Value getBlue() { + public NumericValue getBlue() { return blue; } @@ -354,7 +354,7 @@ public void setR(double r) { public void setR(CSSNumericValue r) throws DOMSyntaxException { red = component(r); componentChanged(red); - pcntSpecified = red.getCSSUnit() == CSSUnit.CSS_PERCENTAGE; + pcntSpecified = red.getUnitType() == CSSUnit.CSS_PERCENTAGE; } /** @@ -366,9 +366,9 @@ public void setR(CSSNumericValue r) throws DOMSyntaxException { */ private NumericValue component(CSSNumericValue c) throws DOMSyntaxException { NumericValue ch = (NumericValue) c; - if (ch.getCSSUnit() != CSSUnit.CSS_PERCENTAGE && ch.getCSSUnit() != CSSUnit.CSS_NUMBER) { + if (ch.getUnitType() != CSSUnit.CSS_PERCENTAGE && ch.getUnitType() != CSSUnit.CSS_NUMBER) { throw new DOMSyntaxException("RGB component must be a number or percentage, not a " - + CSSUnit.dimensionUnitString(ch.getCSSUnit()) + '.'); + + CSSUnit.dimensionUnitString(ch.getUnitType()) + '.'); } if (ch.handler != null) { ch = ch.clone(); @@ -411,7 +411,7 @@ public int getLength() throws DOMException { } @Override - public Value item(int index) throws DOMException { + public NumericValue item(int index) throws DOMException { switch (index) { case 0: return getR(); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RectManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RectManager.java index 8a5d79264..e32fd6cb3 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RectManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RectManager.java @@ -21,8 +21,8 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; @@ -49,34 +49,53 @@ public abstract class RectManager extends LengthManager { * Implements {@link ValueManager#createValue(LexicalUnit,CSSEngine)}. */ @Override - public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { - switch (lu.getLexicalUnitType()) { + public Value createValue(final LexicalUnit lunit, CSSEngine engine) throws DOMException { + switch (lunit.getLexicalUnitType()) { case FUNCTION: - if (!lu.getFunctionName().equalsIgnoreCase("rect")) { + // This case could be removed + if (!lunit.getFunctionName().equalsIgnoreCase("rect")) { break; } case RECT_FUNCTION: - lu = lu.getParameters(); - Value top = createRectComponent(lu); - lu = lu.getNextLexicalUnit(); - if (lu == null || lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { - throw createMalformedRectDOMException(); - } - lu = lu.getNextLexicalUnit(); - Value right = createRectComponent(lu); - lu = lu.getNextLexicalUnit(); - if (lu == null || lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { - throw createMalformedRectDOMException(); + LexicalUnit lu = lunit.getParameters(); + try { + Value top = createRectComponent(lu); + lu = lu.getNextLexicalUnit(); + if (lu == null || lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { + throw createMalformedRectDOMException(); + } + lu = lu.getNextLexicalUnit(); + Value right = createRectComponent(lu); + lu = lu.getNextLexicalUnit(); + if (lu == null || lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { + throw createMalformedRectDOMException(); + } + lu = lu.getNextLexicalUnit(); + Value bottom = createRectComponent(lu); + lu = lu.getNextLexicalUnit(); + if (lu == null || lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { + throw createMalformedRectDOMException(); + } + lu = lu.getNextLexicalUnit(); + Value left = createRectComponent(lu); + return new RectValue(top, right, bottom, left); + } catch (CSSProxyValueException e) { + return createLexicalValue(lunit); } - lu = lu.getNextLexicalUnit(); - Value bottom = createRectComponent(lu); - lu = lu.getNextLexicalUnit(); - if (lu == null || lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { - throw createMalformedRectDOMException(); - } - lu = lu.getNextLexicalUnit(); - Value left = createRectComponent(lu); - return new RectValue(top, right, bottom, left); + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lunit); + default: break; } @@ -90,14 +109,26 @@ private Value createRectComponent(LexicalUnit lu) throws DOMException { return ValueConstants.AUTO_VALUE; } break; + case DIMENSION: return createLength(lu); + case INTEGER: return new FloatValue(CSSUnit.CSS_NUMBER, lu.getIntegerValue()); + case REAL: return new FloatValue(CSSUnit.CSS_NUMBER, lu.getFloatValue()); + case PERCENTAGE: return new FloatValue(CSSUnit.CSS_PERCENTAGE, lu.getFloatValue()); + + case VAR: + case ATTR: + throw new CSSProxyValueException(); + + case CALC: + return createCalc(lu); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RectValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RectValue.java index f6dd3d454..b48bb532b 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RectValue.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RectValue.java @@ -113,7 +113,7 @@ void setTop(Value top) { * @throws DOMSyntaxException if the value is inadequate for a component. */ Value component(Value c) throws DOMSyntaxException { - short unit = c.getCSSUnit(); + short unit = c.getUnitType(); if (unit != CSSUnit.CSS_PERCENTAGE && unit != CSSUnit.CSS_NUMBER && !CSSUnit.isLengthUnitType(unit) && c.getPrimitiveType() != Type.IDENT) { throw new DOMSyntaxException("rect() component must be a length or percentage."); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RevertValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RevertValue.java new file mode 100644 index 000000000..b69367d7c --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/RevertValue.java @@ -0,0 +1,62 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +/** + * This singleton class represents the 'revert' value. + * + */ +public final class RevertValue extends KeywordValue { + + /** + * The only instance of this class. + */ + private static final RevertValue INSTANCE = new RevertValue(); + + /** + * Creates a new UnsetValue object. + */ + RevertValue() { + } + + @Override + public String getValue() { + return "revert"; + } + + @Override + public Type getPrimitiveType() { + return Type.REVERT; + } + + @Override + public RevertValue clone() { + return INSTANCE; + } + + /** + * Get the instance of {@code revert}. + * + * @return the instance of {@code revert}. + */ + public static Value getInstance() { + return INSTANCE; + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ShorthandManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ShorthandManager.java index 3d60ecada..6568f9d74 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ShorthandManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ShorthandManager.java @@ -27,8 +27,10 @@ * This interface represents the objects which provide support for shorthand * properties. * - * @author Stephane Hillion - * @author For later modifications, see Git history. + *

+ * Original author: Stephane Hillion. + * For later modifications, see Git history. + *

* @version $Id$ */ public interface ShorthandManager { @@ -53,7 +55,7 @@ public interface ShorthandManager { * * @param eng The current CSSEngine. * @param ph The property handler to use. - * @param lu The SAC lexical unit used to create the value. + * @param lu The NSAC lexical unit used to create the value. * @param imp The property priority. */ void setValues(CSSEngine eng, PropertyHandler ph, LexicalUnit lu, boolean imp) throws DOMException; @@ -65,6 +67,17 @@ interface PropertyHandler { void property(String name, LexicalUnit value, boolean important); + /** + * Process a longhand value that points to a shorthand that is pending lexical + * substitution. + * + * @param name the longhand property name. + * @param value the pending value that contains the shorthand's lexical + * value. + * @param important the priority. + */ + void pendingValue(String name, PendingValue value, boolean important); + } } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/TypedValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/TypedValue.java new file mode 100644 index 000000000..bd4193caa --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/TypedValue.java @@ -0,0 +1,78 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +import org.w3c.css.om.unit.CSSUnit; +import org.w3c.dom.DOMException; + +import io.sf.carte.doc.style.css.RGBAColor; +import io.sf.carte.doc.style.css.property.NumberValue; + +/** + * A gateway value between Typed OM and its internal representation. + * + * @version $Id$ + */ +public interface TypedValue extends Value, io.sf.carte.doc.style.css.CSSTypedValue { + + @Override + default short getUnitType() { + return CSSUnit.CSS_INVALID; + } + + @Override + default void setExpectInteger() throws DOMException { + throw new DOMException(DOMException.TYPE_MISMATCH_ERR, + "Expected an integer, found type " + getPrimitiveType()); + } + + @Override + default void setFloatValue(short unitType, float floatValue) throws DOMException { + setFloatValue(NumberValue.floatValueConversion(floatValue, unitType, getUnitType())); + } + + @Override + default float getFloatValue(short unitType) throws DOMException { + return NumberValue.floatValueConversion(getFloatValue(), getUnitType(), unitType); + } + + @Override + default void setStringValue(Type stringType, String stringValue) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Please use setValue()"); + } + + @Override + default RGBAColor toRGBColor() throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Please use getColorValue()"); + } + + @Override + default boolean isCalculatedNumber() { + return false; + } + + @Override + default boolean isNumberZero() { + return false; + } + + @Override + TypedValue clone(); + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/UnsetValue.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/UnsetValue.java new file mode 100644 index 000000000..a8f0ff68c --- /dev/null +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/UnsetValue.java @@ -0,0 +1,62 @@ +/* + + See the NOTICE file distributed with this work for additional + information regarding copyright ownership. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package io.sf.carte.echosvg.css.engine.value; + +/** + * This singleton class represents the 'unset' value. + * + */ +public final class UnsetValue extends KeywordValue { + + /** + * The only instance of this class. + */ + private static final UnsetValue INSTANCE = new UnsetValue(); + + /** + * Creates a new UnsetValue object. + */ + UnsetValue() { + } + + @Override + public String getValue() { + return "unset"; + } + + @Override + public Type getPrimitiveType() { + return Type.UNSET; + } + + @Override + public UnsetValue clone() { + return INSTANCE; + } + + /** + * Get the instance of {@code unset}. + * + * @return the instance of {@code unset}. + */ + public static Value getInstance() { + return INSTANCE; + } + +} diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/Value.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/Value.java index c5f71d553..4c94c2686 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/Value.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/Value.java @@ -19,17 +19,62 @@ package io.sf.carte.echosvg.css.engine.value; import org.w3c.css.om.typed.CSSCounterValue; +import org.w3c.css.om.typed.CSSStyleValue; +import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; -import io.sf.carte.echosvg.css.dom.CSSValue; - /** * This interface represents a property value. * * @author See Git history. * @version $Id$ */ -public interface Value extends CSSValue { +public interface Value extends CSSVal, CSSStyleValue { + + /** + * Gets the css unit as in CSS4J's {@code CSSUnit}. + *

+ * If the value has no valid CSS unit, returns {@code CSSUnit.CSS_INVALID}. + *

+ * + * @return the css unit as in CSS4J's {@code CSSUnit}. + */ + default short getUnitType() { + return CSSUnit.CSS_INVALID; + } + + /** + * Set the modification handler. + * + * @param handler the modification handler. + */ + void setModificationHandler(ValueModificationHandler handler); + + /** + * Get the modification handler. + * + * @return the modification handler, or {@code null} if there is no handler. + */ + ValueModificationHandler getModificationHandler(); + + /** + * Is this value a component? + * + * @return {@code true} if the value is a component. + */ + default boolean isComponent() { + return false; + } + + /** + * Do this value represent the given identifier? + * + * @param internedIdent the interned identifier string. + * @return {@code true} if the value is a component. + */ + default boolean isIdentifier(String internedIdent) { + return false; + } /** * Convenience method that either returns an identifier or throws an exception. @@ -43,6 +88,14 @@ default String getIdentifier() throws DOMException { return getIdentifierValue(); } + /** + * If this value is a unit value, set the float value. + * + * @param value the new value, in the current unit. + * @throws DOMException if the value is not a unit value. + */ + void setFloatValue(float value) throws DOMException; + /** * If this value is a list, give the item corresponding to the requested index. * If there is no item at such index, return {@code null} If this object is not @@ -81,47 +134,6 @@ default Value item(int index) { */ ColorValue getColorValue() throws DOMException; - /** - * If this value is a unit value, set the float value. - * - * @param value the new value, in the current unit. - * @throws DOMException if the value is not a unit value. - */ - void setFloatValue(float value) throws DOMException; - - /** - * Set the modification handler. - * - * @param handler the modification handler. - */ - void setModificationHandler(ValueModificationHandler handler); - - /** - * Get the modification handler. - * - * @return the modification handler, or {@code null} if there is no handler. - */ - ValueModificationHandler getModificationHandler(); - - /** - * Is this value a component? - * - * @return {@code true} if the value is a component. - */ - default boolean isComponent() { - return false; - } - - /** - * Do this value represent the given identifier? - * - * @param internedIdent the interned identifier string. - * @return {@code true} if the value is a component. - */ - default boolean isIdentifier(String internedIdent) { - return false; - } - /** * Create and return a copy of this object. *

@@ -130,6 +142,7 @@ default boolean isIdentifier(String internedIdent) { * * @return a modifiable copy of this object. */ + @Override Value clone(); } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ValueConstants.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ValueConstants.java index 6a03e46bc..2a604c578 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ValueConstants.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ValueConstants.java @@ -20,6 +20,7 @@ import org.w3c.css.om.unit.CSSUnit; +import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.echosvg.util.CSSConstants; /** @@ -101,7 +102,7 @@ public interface ValueConstants { /** * The 'inherit' value. */ - Value INHERIT_VALUE = InheritValue.INSTANCE; + Value INHERIT_VALUE = InheritValue.getInstance(); /** * The 'all' keyword. @@ -828,4 +829,9 @@ public interface ValueConstants { */ Value TRANSPARENT_RGB_VALUE = RGBColorValue.createConstant(NUMBER_0, NUMBER_0, NUMBER_0, NUMBER_0); + /** + * A lexical unit with value zero. + */ + LexicalUnit ZERO_LEXICAL_UNIT = LexicalHelper.parsePropertyValue("0"); + } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ValueManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ValueManager.java index 8fee9a3a8..4eb295fc7 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ValueManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/ValueManager.java @@ -21,8 +21,8 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; @@ -59,6 +59,19 @@ public interface ValueManager { */ boolean isAdditiveProperty(); + /** + * Allows URL values. + *

+ * If this property accepts URL values, then {@code attr()} values cannot be + * used. + *

+ * + * @return {@code true} if URL values are allowed by this property. + */ + default boolean allowsURL() { + return false; + } + /** * Returns the type of value this manager handles. This should be one of the * TYPE_* constants defined in {@link io.sf.carte.echosvg.util.SVGTypes}. diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/ClipManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/ClipManager.java index d40f3c4c2..6b740ce7b 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/ClipManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/ClipManager.java @@ -20,11 +20,12 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; -import io.sf.carte.echosvg.css.engine.value.InheritValue; import io.sf.carte.echosvg.css.engine.value.RectManager; +import io.sf.carte.echosvg.css.engine.value.RevertValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -96,13 +97,28 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return InheritValue.INSTANCE; - case IDENT: if (lu.getStringValue().equalsIgnoreCase(CSSConstants.CSS_AUTO_VALUE)) { return ValueConstants.AUTO_VALUE; } + break; + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lu); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/CursorManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/CursorManager.java index 3a393a584..7a3698bce 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/CursorManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/CursorManager.java @@ -22,15 +22,17 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; import io.sf.carte.echosvg.css.engine.value.ListValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.StringMap; import io.sf.carte.echosvg.css.engine.value.URIValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -95,6 +97,11 @@ public boolean isAdditiveProperty() { return false; } + @Override + public boolean allowsURL() { + return true; + } + /** * Implements {@link ValueManager#getPropertyType()}. */ @@ -123,12 +130,10 @@ public Value getDefaultValue() { * Implements {@link ValueManager#createValue(LexicalUnit,CSSEngine)}. */ @Override - public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { + public Value createValue(final LexicalUnit lunit, CSSEngine engine) throws DOMException { ListValue result = new ListValue(); - switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - + LexicalUnit lu = lunit; + switch (lunit.getLexicalUnitType()) { case URI: do { result.append( @@ -138,6 +143,9 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { throw createMalformedLexicalUnitDOMException(); } if (lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { + if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.VAR) { + return createLexicalValue(lunit); + } throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); } lu = lu.getNextLexicalUnit(); @@ -146,6 +154,9 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { } } while (lu.getLexicalUnitType() == LexicalUnit.LexicalType.URI); if (lu.getLexicalUnitType() != LexicalUnit.LexicalType.IDENT) { + if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.VAR) { + return createLexicalValue(lunit); + } throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); } // Fall through... @@ -159,6 +170,22 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { result.append((Value) v); lu = lu.getNextLexicalUnit(); break; + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + return createLexicalValue(lunit); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontFamilyManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontFamilyManager.java index 750c7c1fc..a3a784523 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontFamilyManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontFamilyManager.java @@ -31,8 +31,10 @@ import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; import io.sf.carte.echosvg.css.engine.value.IdentValue; import io.sf.carte.echosvg.css.engine.value.ListValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.StringMap; import io.sf.carte.echosvg.css.engine.value.StringValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -53,11 +55,14 @@ public class FontFamilyManager extends AbstractValueManager { /** * The default value. */ - protected static final ListValue DEFAULT_VALUE = new ListValue(); - static { - DEFAULT_VALUE.append(new StringValue("Arial")); - DEFAULT_VALUE.append(new StringValue("Helvetica")); - DEFAULT_VALUE.append(IdentValue.createConstant(CSSConstants.CSS_SANS_SERIF_VALUE)); + protected static final ListValue DEFAULT_VALUE = createDefaultValue(); + + private static ListValue createDefaultValue() { + ListValue def = new ListValue(',', 4); + def.append(new StringValue("Arial")); + def.append(new StringValue("Helvetica")); + def.append(IdentValue.createConstant(CSSConstants.CSS_SANS_SERIF_VALUE)); + return def.createUnmodifiableView(); } /** @@ -117,7 +122,6 @@ public String getPropertyName() { */ @Override public Value getDefaultValue() { - // Do not clone this value return DEFAULT_VALUE; } @@ -125,18 +129,32 @@ public Value getDefaultValue() { * Implements {@link ValueManager#createValue(LexicalUnit,CSSEngine)}. */ @Override - public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { - switch (lu.getLexicalUnitType()) { + public Value createValue(final LexicalUnit lunit, CSSEngine engine) throws DOMException { + switch (lunit.getLexicalUnitType()) { case INHERIT: return ValueConstants.INHERIT_VALUE; + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lunit); + default: - throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); + throw createInvalidLexicalUnitDOMException(lunit.getLexicalUnitType()); case IDENT: case STRING: } ListValue result = new ListValue(); + LexicalUnit lu = lunit; for (;;) { switch (lu.getLexicalUnitType()) { case STRING: @@ -171,12 +189,22 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { result.append((v != null) ? v : new StringValue(id)); } break; + + case VAR: + case ATTR: + return createLexicalValue(lunit); + default: } - if (lu == null) + if (lu == null) { return result; - if (lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) + } + if (lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { + if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.VAR) { + return createLexicalValue(lunit); + } throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); + } lu = lu.getNextLexicalUnit(); if (lu == null) throw createMalformedLexicalUnitDOMException(); @@ -185,13 +213,7 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { private boolean isIdentOrNumber(LexicalUnit lu) { LexicalType type = lu.getLexicalUnitType(); - switch (type) { - case IDENT: - case INTEGER: - return true; - default: - return false; - } + return type == LexicalType.IDENT || type == LexicalType.INTEGER; } /** diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontShorthandManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontShorthandManager.java index aa4641841..5846bab8e 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontShorthandManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontShorthandManager.java @@ -26,15 +26,30 @@ import java.util.Set; import org.w3c.css.om.unit.CSSUnit; +import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSExpressionValue; +import io.sf.carte.doc.style.css.CSSValue; +import io.sf.carte.doc.style.css.CSSValue.Type; +import io.sf.carte.doc.style.css.CSSValueSyntax.Match; import io.sf.carte.doc.style.css.nsac.CSSParseException; import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.doc.style.css.parser.CSSParser; +import io.sf.carte.doc.style.css.parser.SyntaxParser; +import io.sf.carte.doc.style.css.property.StyleValue; +import io.sf.carte.doc.style.css.property.ValueFactory; import io.sf.carte.echosvg.css.engine.CSSEngine; +import io.sf.carte.echosvg.css.engine.CSSStylableElement; +import io.sf.carte.echosvg.css.engine.StyleMap; import io.sf.carte.echosvg.css.engine.value.AbstractValueFactory; +import io.sf.carte.echosvg.css.engine.value.CSSProxyValueException; +import io.sf.carte.echosvg.css.engine.value.CalcValue; +import io.sf.carte.echosvg.css.engine.value.FloatValue; import io.sf.carte.echosvg.css.engine.value.IdentifierManager; +import io.sf.carte.echosvg.css.engine.value.PendingValue; import io.sf.carte.echosvg.css.engine.value.ShorthandManager; import io.sf.carte.echosvg.css.engine.value.StringMap; +import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueManager; import io.sf.carte.echosvg.util.CSSConstants; @@ -146,17 +161,29 @@ public void handleSystemFont(CSSEngine eng, ShorthandManager.PropertyHandler ph, * {@link ShorthandManager#setValues(CSSEngine,ShorthandManager.PropertyHandler,LexicalUnit,boolean)}. */ @Override - public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, LexicalUnit lu, boolean imp) { - switch (lu.getLexicalUnitType()) { - case INHERIT: - return; + public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, final LexicalUnit lunit, + boolean imp) { + switch (lunit.getLexicalUnitType()) { case IDENT: { - String s = lu.getStringValue().toLowerCase(Locale.ROOT); + String s = lunit.getStringValue().toLowerCase(Locale.ROOT); if (values.contains(s)) { handleSystemFont(eng, ph, s, imp); return; } + break; } + + case INHERIT: + case UNSET: + case REVERT: + return; + + case VAR: + case ATTR: + setPendingLonghands(eng, ph, lunit, imp, + eng.getPropertyIndex(CSSConstants.CSS_LINE_HEIGHT_PROPERTY)); + return; + default: break; } @@ -190,6 +217,7 @@ public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, Lexica // These are all optional. boolean svwDone = false; + LexicalUnit lu = lunit; LexicalUnit intLU = null; while (!svwDone && (lu != null)) { switch (lu.getLexicalUnitType()) { @@ -237,6 +265,11 @@ public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, Lexica svwDone = true; break; + case VAR: + case ATTR: + setPendingLonghands(eng, ph, lunit, imp, lh); + return; + default: // All other must be size,'/line-height', family svwDone = true; break; @@ -270,6 +303,23 @@ public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, Lexica fontSize = lu; lu = lu.getNextLexicalUnit(); break; + + case VAR: + case ATTR: + setPendingLonghands(eng, ph, lunit, imp, lh); + return; + + case CALC: + Value calc = createFontSizeCalc(lu); + if (calc.getCssValueType() == CSSValue.CssType.PROXY) { + throw new CSSProxyValueException(); + } else if (calc.getPrimitiveType() != Type.EXPRESSION || ((CalcValue) calc).getExpressionDelegate() + .matches(new SyntaxParser().parseSyntax("")) != Match.TRUE) { + throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); + } + fontSize = lu; + lu = lu.getNextLexicalUnit(); + default: break; } @@ -308,6 +358,12 @@ public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, Lexica lineHeight = lu; lu = lu.getNextLexicalUnit(); break; + + case VAR: + case ATTR: + setPendingLonghands(eng, ph, lunit, imp, lh); + return; + default: break; } @@ -336,4 +392,40 @@ public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, Lexica } } + private Value createFontSizeCalc(LexicalUnit lu) throws DOMException { + ValueFactory vf = new ValueFactory(); + StyleValue cssValue = vf.createCSSValue(lu.shallowClone()); + + Type pType = cssValue.getPrimitiveType(); + if (pType != Type.EXPRESSION) { + createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); + } + + CalcValue calc = new CalcValue((CSSExpressionValue) cssValue) { + + @Override + protected FloatValue absoluteValue(CSSStylableElement elt, String pseudo, CSSEngine engine, + int idx, StyleMap sm, FloatValue relative) { + return (FloatValue) new FontSizeManager().computeValue(elt, pseudo, engine, idx, sm, + relative); + } + + }; + + return calc; + } + + private void setPendingLonghands(CSSEngine eng, PropertyHandler ph, LexicalUnit lunit, boolean imp, + int lh) { + PendingValue pending = new PendingValue(getPropertyName(), lunit); + ph.pendingValue(CSSConstants.CSS_FONT_FAMILY_PROPERTY, pending, imp); + ph.pendingValue(CSSConstants.CSS_FONT_STYLE_PROPERTY, pending, imp); + ph.pendingValue(CSSConstants.CSS_FONT_VARIANT_PROPERTY, pending, imp); + ph.pendingValue(CSSConstants.CSS_FONT_WEIGHT_PROPERTY, pending, imp); + ph.pendingValue(CSSConstants.CSS_FONT_SIZE_PROPERTY, pending, imp); + if (lh != -1) { + ph.pendingValue(CSSConstants.CSS_LINE_HEIGHT_PROPERTY, pending, imp); + } + } + } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontSizeAdjustManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontSizeAdjustManager.java index a48ed888b..f482a5277 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontSizeAdjustManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontSizeAdjustManager.java @@ -21,11 +21,14 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; +import io.sf.carte.echosvg.css.engine.value.CalcValue; import io.sf.carte.echosvg.css.engine.value.FloatValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -97,9 +100,6 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case INTEGER: return new FloatValue(CSSUnit.CSS_NUMBER, lu.getIntegerValue()); @@ -112,6 +112,30 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { } throw createInvalidIdentifierDOMException(lu.getStringValue()); + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lu); + + case CALC: + Value calc = createCalc(lu); + if (calc.getPrimitiveType() != Type.EXPRESSION) { + // In principle this means that a var() was found + return calc; + } + return ((CalcValue) calc).evaluate(null, null, engine, -1, null, CSSUnit.CSS_NUMBER); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontSizeManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontSizeManager.java index abb6c7c2e..adbf4ba31 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontSizeManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontSizeManager.java @@ -23,14 +23,15 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.doc.style.css.property.NumberValue; import io.sf.carte.echosvg.css.Viewport; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSContext; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; +import io.sf.carte.echosvg.css.engine.value.CalcValue; import io.sf.carte.echosvg.css.engine.value.FloatValue; import io.sf.carte.echosvg.css.engine.value.IdentifierManager; import io.sf.carte.echosvg.css.engine.value.LengthManager; @@ -130,9 +131,6 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case IDENT: String s = lu.getStringValue().toLowerCase(Locale.ROOT).intern(); Object v = values.get(s); @@ -140,9 +138,14 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { throw createInvalidIdentifierDOMException(s); } return (Value) v; + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + default: break; } + return super.createValue(lu, engine); } @@ -168,36 +171,32 @@ public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engin float scale = 1.0f; boolean doParentRelative = false; - if (value.getPrimitiveType() == Type.NUMERIC) { - switch (value.getCSSUnit()) { + Type pType = value.getPrimitiveType(); + if (pType == Type.NUMERIC) { + switch (value.getUnitType()) { case CSSUnit.CSS_NUMBER: case CSSUnit.CSS_PX: return value; case CSSUnit.CSS_MM: - CSSContext ctx = engine.getCSSContext(); float v = lengthValue(value); - return new FloatValue(CSSUnit.CSS_NUMBER, v / ctx.getPixelUnitToMillimeter()); + return new FloatValue(CSSUnit.CSS_NUMBER, v * 3.779527559055f); case CSSUnit.CSS_CM: - ctx = engine.getCSSContext(); v = lengthValue(value); - return new FloatValue(CSSUnit.CSS_NUMBER, v * 10f / ctx.getPixelUnitToMillimeter()); + return new FloatValue(CSSUnit.CSS_NUMBER, v * 37.79527559055f); case CSSUnit.CSS_IN: - ctx = engine.getCSSContext(); v = lengthValue(value); - return new FloatValue(CSSUnit.CSS_NUMBER, v * 25.4f / ctx.getPixelUnitToMillimeter()); + return new FloatValue(CSSUnit.CSS_NUMBER, v * 96f); case CSSUnit.CSS_PT: - ctx = engine.getCSSContext(); v = lengthValue(value); - return new FloatValue(CSSUnit.CSS_NUMBER, v * 25.4f / (72f * ctx.getPixelUnitToMillimeter())); + return new FloatValue(CSSUnit.CSS_NUMBER, v / 0.75f); case CSSUnit.CSS_PC: - ctx = engine.getCSSContext(); v = lengthValue(value); - return new FloatValue(CSSUnit.CSS_NUMBER, (v * 25.4f / (6f * ctx.getPixelUnitToMillimeter()))); + return new FloatValue(CSSUnit.CSS_NUMBER, v * 16f); case CSSUnit.CSS_EM: doParentRelative = true; @@ -272,12 +271,16 @@ public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engin return new FloatValue(CSSUnit.CSS_NUMBER, v * max * 0.01f); default: // Maybe it is one of the new absolute length units - try { - return new FloatValue(CSSUnit.CSS_NUMBER, - NumberValue.floatValueConversion(value.getFloatValue(), value.getCSSUnit(), - CSSUnit.CSS_MM) / engine.getCSSContext().getPixelUnitToMillimeter()); - } catch (DOMException e) { - } + return new FloatValue(CSSUnit.CSS_NUMBER, + NumberValue.floatValueConversion(value.getFloatValue(), value.getUnitType(), + CSSUnit.CSS_PX)); + } + } else if (pType == Type.EXPRESSION) { + try { + Value calc = evaluateCalc((CalcValue) value, elt, pseudo, engine, idx, sm, CSSUnit.CSS_PX); + return new FloatValue(CSSUnit.CSS_NUMBER, calc.getFloatValue()); + } catch (Exception e) { + return isInheritedProperty() ? null : getDefaultValue(); } } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontStretchManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontStretchManager.java index 4f7a22945..16b55e722 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontStretchManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontStretchManager.java @@ -18,7 +18,7 @@ */ package io.sf.carte.echosvg.css.engine.value.css2; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontWeightManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontWeightManager.java index 67b1de391..2b19e01b0 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontWeightManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/FontWeightManager.java @@ -21,8 +21,8 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSContext; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/SrcManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/SrcManager.java index 23b188ccc..1fb189139 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/SrcManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/SrcManager.java @@ -26,9 +26,11 @@ import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.IdentifierManager; import io.sf.carte.echosvg.css.engine.value.ListValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.StringMap; import io.sf.carte.echosvg.css.engine.value.StringValue; import io.sf.carte.echosvg.css.engine.value.URIValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -83,6 +85,11 @@ public boolean isAdditiveProperty() { return false; } + @Override + public boolean allowsURL() { + return true; + } + /** * Implements {@link ValueManager#getPropertyType()}. */ @@ -113,14 +120,26 @@ public Value getDefaultValue() { * Implements {@link ValueManager#createValue(LexicalUnit,CSSEngine)}. */ @Override - public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { + public Value createValue(final LexicalUnit lunit, CSSEngine engine) throws DOMException { - switch (lu.getLexicalUnitType()) { + switch (lunit.getLexicalUnitType()) { case INHERIT: return ValueConstants.INHERIT_VALUE; + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + return createLexicalValue(lunit); + default: - throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); + throw createInvalidLexicalUnitDOMException(lunit.getLexicalUnitType()); case IDENT: case STRING: @@ -128,6 +147,7 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { } ListValue result = new ListValue(); + LexicalUnit lu = lunit; for (;;) { switch (lu.getLexicalUnitType()) { case STRING: @@ -140,36 +160,51 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { result.append(new URIValue(lu.getStringValue(), uri)); lu = lu.getNextLexicalUnit(); - if ((lu != null) && (lu.getLexicalUnitType() == LexicalUnit.LexicalType.FUNCTION)) { - if (!lu.getFunctionName().equalsIgnoreCase("format")) { + if (lu != null) { + switch (lu.getLexicalUnitType()) { + case FUNCTION: + if (!lu.getFunctionName().equalsIgnoreCase("format")) { + break; + } + // Format really does us no good so just ignore it. + + // TODO: Should probably turn this into a ListValue + // and append the format function CSS Value. + lu = lu.getNextLexicalUnit(); + break; + case VAR: + return createLexicalValue(lunit); + default: break; } - // Format really does us no good so just ignore it. - - // TODO: Should probably turn this into a ListValue - // and append the format function CSS Value. - lu = lu.getNextLexicalUnit(); } break; case IDENT: StringBuilder sb = new StringBuilder(lu.getStringValue()); lu = lu.getNextLexicalUnit(); - if (lu != null && lu.getLexicalUnitType() == LexicalUnit.LexicalType.IDENT) { - do { - sb.append(' '); - sb.append(lu.getStringValue()); - lu = lu.getNextLexicalUnit(); - } while (lu != null && lu.getLexicalUnitType() == LexicalUnit.LexicalType.IDENT); - result.append(new StringValue(sb.toString())); - } else { - String id = sb.toString(); - String s = id.toLowerCase(Locale.ROOT).intern(); - Value v = (Value) values.get(s); - result.append((v != null) ? v : new StringValue(id)); + if (lu != null) { + if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.IDENT) { + do { + sb.append(' '); + sb.append(lu.getStringValue()); + lu = lu.getNextLexicalUnit(); + } while (lu != null && lu.getLexicalUnitType() == LexicalUnit.LexicalType.IDENT); + result.append(new StringValue(sb.toString())); + } else if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.VAR) { + return createLexicalValue(lunit); + } else { + String id = sb.toString(); + String s = id.toLowerCase(Locale.ROOT).intern(); + Value v = (Value) values.get(s); + result.append(v != null ? v : new StringValue(id)); + } } break; + case VAR: + return createLexicalValue(lunit); + default: break; @@ -178,6 +213,9 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { return result; } if (lu.getLexicalUnitType() != LexicalUnit.LexicalType.OPERATOR_COMMA) { + if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.VAR) { + return createLexicalValue(lunit); + } throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); } lu = lu.getNextLexicalUnit(); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/TextDecorationManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/TextDecorationManager.java index b2b8fc9e5..a9f4c1bf3 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/TextDecorationManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/css2/TextDecorationManager.java @@ -22,15 +22,18 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; import io.sf.carte.echosvg.css.engine.value.ListValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.StringMap; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; +import io.sf.carte.echosvg.css.engine.value.LexicalValue; import io.sf.carte.echosvg.util.CSSConstants; import io.sf.carte.echosvg.util.SVGTypes; @@ -108,18 +111,17 @@ public Value getDefaultValue() { * Implements {@link ValueManager#createValue(LexicalUnit,CSSEngine)}. */ @Override - public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { - switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - + public Value createValue(final LexicalUnit lunit, CSSEngine engine) throws DOMException { + switch (lunit.getLexicalUnitType()) { case IDENT: - if (lu.getStringValue().equalsIgnoreCase(CSSConstants.CSS_NONE_VALUE)) { + if (lunit.getStringValue().equalsIgnoreCase(CSSConstants.CSS_NONE_VALUE)) { return ValueConstants.NONE_VALUE; } ListValue lv = new ListValue(' '); + LexicalUnit lu = lunit; do { - if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.IDENT) { + switch (lu.getLexicalUnitType()) { + case IDENT: String s = lu.getStringValue().toLowerCase(Locale.ROOT).intern(); Object obj = values.get(s); if (obj == null) { @@ -127,17 +129,36 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { } lv.append((Value) obj); lu = lu.getNextLexicalUnit(); - } else { - throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); + break; + case VAR: + case ATTR: + return new LexicalValue(lunit); + default: + throw createInvalidLexicalUnitDOMException(lunit.getLexicalUnitType()); } - } while (lu != null); return lv; + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return new LexicalValue(lunit); + default: break; } - throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); + throw createInvalidLexicalUnitDOMException(lunit.getLexicalUnitType()); } @Override diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/BaselineShiftManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/BaselineShiftManager.java index 35b0e7fee..eec2585f8 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/BaselineShiftManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/BaselineShiftManager.java @@ -23,8 +23,8 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; @@ -112,18 +112,20 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case IDENT: Object v = values.get(lu.getStringValue().toLowerCase(Locale.ROOT).intern()); if (v == null) { throw createInvalidIdentifierDOMException(lu.getStringValue()); } return (Value) v; + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + default: break; } + return super.createValue(lu, engine); } @@ -146,7 +148,7 @@ public Value createStringValue(Type type, String value, CSSEngine engine) throws @Override public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engine, int idx, StyleMap sm, Value value) { - if (value.getCSSUnit() == CSSUnit.CSS_PERCENTAGE) { + if (value.getUnitType() == CSSUnit.CSS_PERCENTAGE) { sm.putLineHeightRelative(idx, true); int fsi = engine.getLineHeightIndex(); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/ClipPathManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/ClipPathManager.java index e2a0d8f86..e55913711 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/ClipPathManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/ClipPathManager.java @@ -20,11 +20,13 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.URIValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -74,6 +76,11 @@ public boolean isAdditiveProperty() { return false; } + @Override + public boolean allowsURL() { + return true; + } + /** * Implements {@link ValueManager#getPropertyType()}. */ @@ -96,9 +103,6 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case URI: return new URIValue(lu.getStringValue(), resolveURI(engine.getCSSBaseURI(), lu.getStringValue())); @@ -106,6 +110,23 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { if (lu.getStringValue().equalsIgnoreCase(CSSConstants.CSS_NONE_VALUE)) { return ValueConstants.NONE_VALUE.clone(); } + throw createInvalidIdentifierDOMException(lu.getStringValue()); + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + return createLexicalValue(lu); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/ColorProfileManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/ColorProfileManager.java index 53dd6b950..741ee9706 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/ColorProfileManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/ColorProfileManager.java @@ -22,12 +22,14 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; import io.sf.carte.echosvg.css.engine.value.IdentValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.URIValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -77,6 +79,11 @@ public boolean isAdditiveProperty() { return false; } + @Override + public boolean allowsURL() { + return true; + } + /** * Implements {@link ValueManager#getPropertyType()}. */ @@ -99,9 +106,6 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case IDENT: String s = lu.getStringValue().toLowerCase(Locale.ROOT); if (s.equals(CSSConstants.CSS_AUTO_VALUE)) { @@ -114,6 +118,22 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { case URI: return new URIValue(lu.getStringValue(), resolveURI(engine.getCSSBaseURI(), lu.getStringValue())); + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + return createLexicalValue(lu); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/EnableBackgroundManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/EnableBackgroundManager.java index ceb0500fd..daae4912b 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/EnableBackgroundManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/EnableBackgroundManager.java @@ -22,8 +22,8 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; @@ -105,12 +105,6 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - - default: - throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); - case IDENT: String id = lu.getStringValue().toLowerCase(Locale.ROOT).intern(); if (id == CSSConstants.CSS_ACCUMULATE_VALUE) { @@ -134,6 +128,12 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { result.append(super.createValue(lu, engine)); } return result; + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + default: + throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); } } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/FilterManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/FilterManager.java index 97557d101..3f0184b4a 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/FilterManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/FilterManager.java @@ -20,11 +20,13 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.URIValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -74,6 +76,11 @@ public boolean isAdditiveProperty() { return false; } + @Override + public boolean allowsURL() { + return true; + } + /** * Implements {@link ValueManager#getPropertyType()}. */ @@ -96,9 +103,6 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case URI: return new URIValue(lu.getStringValue(), resolveURI(engine.getCSSBaseURI(), lu.getStringValue())); @@ -108,6 +112,21 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { } throw createInvalidIdentifierDOMException(lu.getStringValue()); + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + return createLexicalValue(lu); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/GlyphOrientationManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/GlyphOrientationManager.java index 817a69488..8f907b5c0 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/GlyphOrientationManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/GlyphOrientationManager.java @@ -21,11 +21,15 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.doc.style.css.property.NumberValue; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; +import io.sf.carte.echosvg.css.engine.value.CalcValue; import io.sf.carte.echosvg.css.engine.value.FloatValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -81,9 +85,6 @@ public int getPropertyType() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case DIMENSION: switch (lu.getCssUnit()) { case CSSUnit.CSS_DEG: @@ -95,16 +96,46 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { case CSSUnit.CSS_TURN: return new FloatValue(CSSUnit.CSS_DEG, lu.getFloatValue() * 360f); } + break; - // For SVG angle properties unit defaults to 'deg'. + // For SVG angle properties unit defaults to 'deg'. case INTEGER: { int n = lu.getIntegerValue(); return new FloatValue(CSSUnit.CSS_DEG, n); } + case REAL: { float n = lu.getFloatValue(); return new FloatValue(CSSUnit.CSS_DEG, n); } + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lu); + + case CALC: + Value calc = createCalc(lu); + if (calc.getPrimitiveType() != Type.EXPRESSION) { + return calc; + } + FloatValue f = ((CalcValue) calc).evaluate(null, null, engine, -1, null, CSSUnit.CSS_DEG); + if (f.getUnitType() == CSSUnit.CSS_NUMBER) { + f = new FloatValue(CSSUnit.CSS_DEG, f.getFloatValue()); + } + return f; + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/GlyphOrientationVerticalManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/GlyphOrientationVerticalManager.java index f0513416a..d0fd797f7 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/GlyphOrientationVerticalManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/GlyphOrientationVerticalManager.java @@ -20,8 +20,8 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/KerningManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/KerningManager.java index 74dd4d9a9..f51a609c8 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/KerningManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/KerningManager.java @@ -20,8 +20,8 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.LengthManager; import io.sf.carte.echosvg.css.engine.value.Value; @@ -95,14 +95,15 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case IDENT: if (lu.getStringValue().equalsIgnoreCase(CSSConstants.CSS_AUTO_VALUE)) { return ValueConstants.AUTO_VALUE; } throw createInvalidIdentifierDOMException(lu.getStringValue()); + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MarkerManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MarkerManager.java index b1e3b6b14..8c6ca693b 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MarkerManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MarkerManager.java @@ -20,11 +20,13 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.URIValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -78,6 +80,11 @@ public boolean isAdditiveProperty() { return false; } + @Override + public boolean allowsURL() { + return true; + } + /** * Implements {@link ValueManager#getPropertyType()}. */ @@ -108,9 +115,6 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case URI: return new URIValue(lu.getStringValue(), resolveURI(engine.getCSSBaseURI(), lu.getStringValue())); @@ -118,6 +122,22 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { if (lu.getStringValue().equalsIgnoreCase(CSSConstants.CSS_NONE_VALUE)) { return ValueConstants.NONE_VALUE; } + throw createInvalidIdentifierDOMException(lu.getStringValue()); + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + return createLexicalValue(lu); default: break; diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MarkerShorthandManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MarkerShorthandManager.java index 727770868..c94181f82 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MarkerShorthandManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MarkerShorthandManager.java @@ -18,11 +18,17 @@ */ package io.sf.carte.echosvg.css.engine.value.svg; +import java.io.IOException; +import java.io.StringReader; + import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.nsac.CSSParseException; import io.sf.carte.doc.style.css.nsac.LexicalUnit; +import io.sf.carte.doc.style.css.parser.CSSParser; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueFactory; +import io.sf.carte.echosvg.css.engine.value.PendingValue; import io.sf.carte.echosvg.css.engine.value.ShorthandManager; import io.sf.carte.echosvg.css.engine.value.ValueManager; import io.sf.carte.echosvg.util.CSSConstants; @@ -31,8 +37,10 @@ * This class represents an object which provide support for the 'marker' * shorthand properties. * - * @author Stephane Hillion - * @author For later modifications, see Git history. + *

+ * Original author: Stephane Hillion. + * For later modifications, see Git history. + *

* @version $Id$ */ public class MarkerShorthandManager extends AbstractValueFactory implements ShorthandManager { @@ -68,9 +76,33 @@ public boolean isAdditiveProperty() { @Override public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, LexicalUnit lu, boolean imp) throws DOMException { + switch (lu.getLexicalUnitType()) { + case VAR: + setPendingLonghands(eng, ph, lu, imp); + /* fall-through */ + case INHERIT: + case UNSET: + return; + case INITIAL: + // none + try { + lu = new CSSParser().parsePropertyValue(new StringReader(CSSConstants.CSS_NONE_VALUE)); + } catch (CSSParseException | IOException e) { + } + default: + break; + } + ph.property(CSSConstants.CSS_MARKER_END_PROPERTY, lu, imp); ph.property(CSSConstants.CSS_MARKER_MID_PROPERTY, lu, imp); ph.property(CSSConstants.CSS_MARKER_START_PROPERTY, lu, imp); } + private void setPendingLonghands(CSSEngine eng, PropertyHandler ph, LexicalUnit lu, boolean imp) { + PendingValue pending = new PendingValue(getPropertyName(), lu); + ph.pendingValue(CSSConstants.CSS_MARKER_END_PROPERTY, pending, imp); + ph.pendingValue(CSSConstants.CSS_MARKER_MID_PROPERTY, pending, imp); + ph.pendingValue(CSSConstants.CSS_MARKER_START_PROPERTY, pending, imp); + } + } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MaskManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MaskManager.java index 7181b4807..813a76a56 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MaskManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/MaskManager.java @@ -20,11 +20,13 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; +import io.sf.carte.echosvg.css.engine.value.RevertValue; import io.sf.carte.echosvg.css.engine.value.URIValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -66,6 +68,11 @@ public boolean isAdditiveProperty() { return false; } + @Override + public boolean allowsURL() { + return true; + } + /** * Implements {@link ValueManager#getPropertyType()}. */ @@ -96,9 +103,6 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case URI: return new URIValue(lu.getStringValue(), resolveURI(engine.getCSSBaseURI(), lu.getStringValue())); @@ -106,6 +110,22 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { if (lu.getStringValue().equalsIgnoreCase(CSSConstants.CSS_NONE_VALUE)) { return ValueConstants.NONE_VALUE; } + throw createInvalidIdentifierDOMException(lu.getStringValue()); + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + return createLexicalValue(lu); default: break; diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/OpacityManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/OpacityManager.java index da37217b3..7466b1ef1 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/OpacityManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/OpacityManager.java @@ -21,10 +21,16 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; +import io.sf.carte.echosvg.css.engine.value.CSSProxyValueException; +import io.sf.carte.echosvg.css.engine.value.CalcValue; import io.sf.carte.echosvg.css.engine.value.FloatValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -113,15 +119,37 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case INTEGER: return new FloatValue(CSSUnit.CSS_NUMBER, lu.getIntegerValue()); case REAL: return new FloatValue(CSSUnit.CSS_NUMBER, lu.getFloatValue()); + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lu); + + case CALC: + Value calc = createCalc(lu); + if (calc.getCssValueType() == CSSValue.CssType.PROXY) { + throw new CSSProxyValueException(); + } else if (calc.getPrimitiveType() != Type.EXPRESSION) { + break; + } + return ((CalcValue) calc).evaluate(null, null, engine, -1, null, CSSUnit.CSS_NUMBER); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/SVGPaintManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/SVGPaintManager.java index f31d64e74..0f59bd6bd 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/SVGPaintManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/SVGPaintManager.java @@ -84,6 +84,11 @@ public boolean isAdditiveProperty() { return true; } + @Override + public boolean allowsURL() { + return true; + } + /** * Implements {@link ValueManager#getPropertyType()}. */ diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/SpacingManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/SpacingManager.java index 85c7f57f9..64fcfdc58 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/SpacingManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/SpacingManager.java @@ -20,8 +20,8 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.LengthManager; import io.sf.carte.echosvg.css.engine.value.Value; @@ -115,6 +115,7 @@ public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { return ValueConstants.NORMAL_VALUE; } throw createInvalidIdentifierDOMException(lu.getStringValue()); + default: break; } diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/StrokeDasharrayManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/StrokeDasharrayManager.java index fa976a1f1..0bf1cf7dd 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/StrokeDasharrayManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/StrokeDasharrayManager.java @@ -20,13 +20,15 @@ import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; -import io.sf.carte.echosvg.css.dom.CSSValue.Type; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.StyleMap; import io.sf.carte.echosvg.css.engine.value.LengthManager; import io.sf.carte.echosvg.css.engine.value.ListValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -96,25 +98,48 @@ public Value getDefaultValue() { * Implements {@link ValueManager#createValue(LexicalUnit,CSSEngine)}. */ @Override - public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { - switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - + public Value createValue(final LexicalUnit lunit, CSSEngine engine) throws DOMException { + switch (lunit.getLexicalUnitType()) { case IDENT: - if (lu.getStringValue().equalsIgnoreCase(CSSConstants.CSS_NONE_VALUE)) { + if (lunit.getStringValue().equalsIgnoreCase(CSSConstants.CSS_NONE_VALUE)) { return ValueConstants.NONE_VALUE; } - throw createInvalidIdentifierDOMException(lu.getStringValue()); + throw createInvalidIdentifierDOMException(lunit.getStringValue()); + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lunit); default: + /* + * "A list of comma and/or white space separated s and s + * that specify the lengths of alternating dashes and gaps." + */ ListValue lv = new ListValue(' '); + LexicalUnit lu = lunit; do { Value v = super.createValue(lu, engine); lv.append(v); lu = lu.getNextLexicalUnit(); - if (lu != null && lu.getLexicalUnitType() == LexicalUnit.LexicalType.OPERATOR_COMMA) { - lu = lu.getNextLexicalUnit(); + if (lu != null) { + if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.OPERATOR_COMMA) { + lu = lu.getNextLexicalUnit(); + } + if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.VAR) { + return createLexicalValue(lunit); + } } } while (lu != null); return lv; diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/StrokeMiterlimitManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/StrokeMiterlimitManager.java index 7a3203431..5e44e7de3 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/StrokeMiterlimitManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg/StrokeMiterlimitManager.java @@ -21,10 +21,16 @@ import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; +import io.sf.carte.doc.style.css.CSSValue; +import io.sf.carte.doc.style.css.CSSValue.Type; import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueManager; +import io.sf.carte.echosvg.css.engine.value.CSSProxyValueException; +import io.sf.carte.echosvg.css.engine.value.CalcValue; import io.sf.carte.echosvg.css.engine.value.FloatValue; +import io.sf.carte.echosvg.css.engine.value.RevertValue; +import io.sf.carte.echosvg.css.engine.value.UnsetValue; import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; @@ -96,18 +102,40 @@ public Value getDefaultValue() { @Override public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; - case INTEGER: return new FloatValue(CSSUnit.CSS_NUMBER, lu.getIntegerValue()); case REAL: return new FloatValue(CSSUnit.CSS_NUMBER, lu.getFloatValue()); + case INHERIT: + return ValueConstants.INHERIT_VALUE; + + case UNSET: + return UnsetValue.getInstance(); + + case REVERT: + return RevertValue.getInstance(); + + case INITIAL: + return getDefaultValue(); + + case VAR: + case ATTR: + return createLexicalValue(lu); + + case CALC: + Value calc = createCalc(lu); + if (calc.getCssValueType() == CSSValue.CssType.PROXY) { + throw new CSSProxyValueException(); + } else if (calc.getPrimitiveType() != Type.EXPRESSION) { + break; + } + return ((CalcValue) calc).evaluate(null, null, engine, -1, null, CSSUnit.CSS_NUMBER); + default: - throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); } + throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); } /** diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg12/LineHeightManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg12/LineHeightManager.java index 1e1061361..082b33e78 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg12/LineHeightManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg12/LineHeightManager.java @@ -105,14 +105,16 @@ public Value getDefaultValue() { public Value createValue(LexicalUnit lu, CSSEngine engine) throws DOMException { switch (lu.getLexicalUnitType()) { - case INHERIT: - return ValueConstants.INHERIT_VALUE; case IDENT: { String s = lu.getStringValue().toLowerCase(Locale.ROOT); if (CSSConstants.CSS_NORMAL_VALUE.equals(s)) return SVG12ValueConstants.NORMAL_VALUE; throw createInvalidIdentifierDOMException(lu.getStringValue()); } + + case INHERIT: + return ValueConstants.INHERIT_VALUE; + default: return super.createValue(lu, engine); } @@ -137,7 +139,7 @@ public Value computeValue(CSSStylableElement elt, String pseudo, CSSEngine engin if (value.getCssValueType() != Value.CssType.TYPED) return value; - switch (value.getCSSUnit()) { + switch (value.getUnitType()) { case CSSUnit.CSS_NUMBER: return new LineHeightValue(CSSUnit.CSS_NUMBER, value.getFloatValue(), true); diff --git a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg12/MarginShorthandManager.java b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg12/MarginShorthandManager.java index c4c4d8986..b9a209bf5 100644 --- a/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg12/MarginShorthandManager.java +++ b/echosvg-css/src/main/java/io/sf/carte/echosvg/css/engine/value/svg12/MarginShorthandManager.java @@ -21,9 +21,12 @@ import org.w3c.dom.DOMException; import io.sf.carte.doc.style.css.nsac.LexicalUnit; +import io.sf.carte.doc.style.css.nsac.LexicalUnit.LexicalType; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.AbstractValueFactory; +import io.sf.carte.echosvg.css.engine.value.PendingValue; import io.sf.carte.echosvg.css.engine.value.ShorthandManager; +import io.sf.carte.echosvg.css.engine.value.ValueConstants; import io.sf.carte.echosvg.css.engine.value.ValueManager; import io.sf.carte.echosvg.util.SVG12CSSConstants; @@ -31,8 +34,10 @@ * This class represents an object which provide support for the 'margin' * shorthand property. * - * @author Stephane Hillion - * @author For later modifications, see Git history. + *

+ * Original author: Stephane Hillion. + * For later modifications, see Git history. + *

* @version $Id$ */ public class MarginShorthandManager extends AbstractValueFactory implements ShorthandManager { @@ -69,14 +74,40 @@ public boolean isAdditiveProperty() { * {@link ShorthandManager#setValues(CSSEngine,ShorthandManager.PropertyHandler,LexicalUnit,boolean)}. */ @Override - public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, LexicalUnit lu, boolean imp) - throws DOMException { - if (lu.getLexicalUnitType() == LexicalUnit.LexicalType.INHERIT) + public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, final LexicalUnit lunit, + boolean imp) throws DOMException { + switch (lunit.getLexicalUnitType()) { + case INHERIT: return; + case UNSET: + case REVERT: + case INITIAL: + // Set defaults + LexicalUnit luZero = ValueConstants.ZERO_LEXICAL_UNIT; + ph.property(SVG12CSSConstants.CSS_MARGIN_TOP_PROPERTY, luZero, imp); + ph.property(SVG12CSSConstants.CSS_MARGIN_RIGHT_PROPERTY, luZero, imp); + ph.property(SVG12CSSConstants.CSS_MARGIN_BOTTOM_PROPERTY, luZero, imp); + ph.property(SVG12CSSConstants.CSS_MARGIN_LEFT_PROPERTY, luZero, imp); + break; + + case VAR: + case ATTR: + setPendingLonghands(eng, ph, lunit, imp); + return; + + default: + break; + } + + LexicalUnit lu = lunit; LexicalUnit[] lus = new LexicalUnit[4]; int cnt = 0; while (lu != null) { + if (lu.getLexicalUnitType() == LexicalType.VAR) { + setPendingLonghands(eng, ph, lunit, imp); + return; + } if (cnt == 4) throw createInvalidLexicalUnitDOMException(lu.getLexicalUnitType()); lus[cnt++] = lu; @@ -102,4 +133,12 @@ public void setValues(CSSEngine eng, ShorthandManager.PropertyHandler ph, Lexica ph.property(SVG12CSSConstants.CSS_MARGIN_LEFT_PROPERTY, lus[3], imp); } + private void setPendingLonghands(CSSEngine eng, PropertyHandler ph, LexicalUnit lunit, boolean imp) { + PendingValue pending = new PendingValue(getPropertyName(), lunit); + ph.pendingValue(SVG12CSSConstants.CSS_MARGIN_TOP_PROPERTY, pending, imp); + ph.pendingValue(SVG12CSSConstants.CSS_MARGIN_RIGHT_PROPERTY, pending, imp); + ph.pendingValue(SVG12CSSConstants.CSS_MARGIN_BOTTOM_PROPERTY, pending, imp); + ph.pendingValue(SVG12CSSConstants.CSS_MARGIN_LEFT_PROPERTY, pending, imp); + } + } diff --git a/echosvg-css/src/main/resources/io/sf/carte/echosvg/css/engine/value/resources/Messages.properties b/echosvg-css/src/main/resources/io/sf/carte/echosvg/css/engine/value/resources/Messages.properties index 30f1da964..ac273ea58 100644 --- a/echosvg-css/src/main/resources/io/sf/carte/echosvg/css/engine/value/resources/Messages.properties +++ b/echosvg-css/src/main/resources/io/sf/carte/echosvg/css/engine/value/resources/Messages.properties @@ -29,7 +29,7 @@ The given string type ({1}) is invalid for \ the "{0}" property. invalid.identifier = \ -The "{1}" identifier is not a valid value for the "{0}" property. +The "{1}" identifier is not a valid value for the "{0}" property. # !!! choices limited to 30 (java.text bug). @@ -45,8 +45,11 @@ The given CSS primitive value ({1}) represents an invalid type for the "{0}". invalid.float.value = \ The number ''{1}'' represents an invalid value for the ''{0}'' property. +invalid.color.component.unit = \ +The "{0}" property does not support color values with type #{1} components. + invalid.rgb.component.unit = \ -The "{0}" property does not support RGB values with type #{1} components. +The "{0}" property does not support RGB values with type #{1} components. malformed.lexical.unit = \ A malformed value was assigned to a "{0}" property. diff --git a/echosvg-dom/src/main/java/io/sf/carte/echosvg/dom/ExtensibleDOMImplementation.java b/echosvg-dom/src/main/java/io/sf/carte/echosvg/dom/ExtensibleDOMImplementation.java index 80c6908fb..379665809 100644 --- a/echosvg-dom/src/main/java/io/sf/carte/echosvg/dom/ExtensibleDOMImplementation.java +++ b/echosvg-dom/src/main/java/io/sf/carte/echosvg/dom/ExtensibleDOMImplementation.java @@ -35,7 +35,7 @@ import org.w3c.dom.view.ViewCSS; import io.sf.carte.doc.style.css.nsac.Parser; -import io.sf.carte.doc.style.css.parser.CSSParser; +import io.sf.carte.doc.style.css.om.CSSOMParser; import io.sf.carte.echosvg.css.engine.CSSContext; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.value.ShorthandManager; @@ -117,7 +117,7 @@ public void registerCustomCSSShorthandManager(ShorthandManager sm) { * Creates new CSSEngine and attach it to the document. */ public CSSEngine createCSSEngine(AbstractStylableDocument doc, CSSContext ctx) { - Parser p = new CSSParser(); + Parser p = new CSSOMParser(); ValueManager[] vms; if (customValueManagers == null) { diff --git a/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/UnitProcessor.java b/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/UnitProcessor.java index a6ceac1ff..d7f9cb77f 100644 --- a/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/UnitProcessor.java +++ b/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/UnitProcessor.java @@ -144,15 +144,15 @@ public static float svgToUserSpace(float v, short type, short d, Context ctx) { case SVGLength.SVG_LENGTHTYPE_PX: return v; case SVGLength.SVG_LENGTHTYPE_MM: - return (v / ctx.getPixelUnitToMillimeter()); + return v * 3.779527559055f; // 96 / 25.4 case SVGLength.SVG_LENGTHTYPE_CM: - return (v * 10f / ctx.getPixelUnitToMillimeter()); + return v * 37.79527559055f; // 96 / 2.54 case SVGLength.SVG_LENGTHTYPE_IN: - return (v * 25.4f / ctx.getPixelUnitToMillimeter()); + return v * 96f; case SVGLength.SVG_LENGTHTYPE_PT: - return (v * 25.4f / (72f * ctx.getPixelUnitToMillimeter())); + return v / 0.75f; // Mult. by 96 / 72 case SVGLength.SVG_LENGTHTYPE_PC: - return (v * 25.4f / (6f * ctx.getPixelUnitToMillimeter())); + return v * 16f; // 96 / 6 case SVGLength.SVG_LENGTHTYPE_EMS: return emsToPixels(v, d, ctx); case SVGLength.SVG_LENGTHTYPE_EXS: @@ -179,15 +179,15 @@ public static float userSpaceToSVG(float v, short type, short d, Context ctx) { case SVGLength.SVG_LENGTHTYPE_PX: return v; case SVGLength.SVG_LENGTHTYPE_MM: - return (v * ctx.getPixelUnitToMillimeter()); + return v * 0.26458333333333f; // 25.4/96 case SVGLength.SVG_LENGTHTYPE_CM: - return (v * ctx.getPixelUnitToMillimeter() / 10f); + return v * 0.026458333333333f; // 2.54/96 case SVGLength.SVG_LENGTHTYPE_IN: - return (v * ctx.getPixelUnitToMillimeter() / 25.4f); + return v / 96f; case SVGLength.SVG_LENGTHTYPE_PT: - return (v * (72f * ctx.getPixelUnitToMillimeter()) / 25.4f); + return v * 0.75f; // 72/96 case SVGLength.SVG_LENGTHTYPE_PC: - return (v * (6f * ctx.getPixelUnitToMillimeter()) / 25.4f); + return v / 16f; case SVGLength.SVG_LENGTHTYPE_EMS: return pixelsToEms(v, d, ctx); case SVGLength.SVG_LENGTHTYPE_EXS: @@ -415,20 +415,9 @@ public interface Context { Element getElement(); /** - * Returns the size of a px CSS unit in millimeters. + * Returns the resolution in {@code dpi}. */ - float getPixelUnitToMillimeter(); - - /** - * Returns the size of a px CSS unit in millimeters. This will be removed after - * next release. - * - * @see #getPixelUnitToMillimeter() - */ - @Deprecated(forRemoval = true) - default float getPixelToMM() { - return getPixelUnitToMillimeter(); - } + float getResolution(); /** * Returns the font-size value. diff --git a/echosvg-svg-dom/src/main/java/io/sf/carte/echosvg/dom/svg/SVGContext.java b/echosvg-svg-dom/src/main/java/io/sf/carte/echosvg/dom/svg/SVGContext.java index 86e7fbe14..564b86013 100644 --- a/echosvg-svg-dom/src/main/java/io/sf/carte/echosvg/dom/svg/SVGContext.java +++ b/echosvg-svg-dom/src/main/java/io/sf/carte/echosvg/dom/svg/SVGContext.java @@ -38,8 +38,13 @@ public interface SVGContext { /** * Returns the size of a px CSS unit in millimeters. + * + * @deprecated Use {@link #getResolution()} instead. */ - float getPixelUnitToMillimeter(); + @Deprecated + default float getPixelUnitToMillimeter() { + return 25.4f / getResolution(); + } /** * Returns the size of a px CSS unit in millimeters. This will be removed after @@ -52,6 +57,11 @@ default float getPixelToMM() { return getPixelUnitToMillimeter(); } + /** + * Returns the resolution in dpi. + */ + float getResolution(); + /** * Returns the tight bounding box in current user space (i.e., after application * of the transform attribute, if any) on the geometry of all contained graphics diff --git a/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/JSVGComponent.java b/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/JSVGComponent.java index 6732d549d..8404058e0 100644 --- a/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/JSVGComponent.java +++ b/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/JSVGComponent.java @@ -2505,6 +2505,25 @@ public void run() { } } + @Override + public float getResolution() { + if (EventQueue.isDispatchThread()) { + return userAgent.getResolution(); + } else { + class Query implements Runnable { + float result; + + @Override + public void run() { + result = userAgent.getResolution(); + } + } + Query q = new Query(); + invokeAndWait(q); + return q.result; + } + } + /** * Returns the default font family. */ @@ -3229,15 +3248,12 @@ public boolean showConfirm(String message) { return JSVGComponent.this.showConfirm(message); } - /** - * Returns the size of a px CSS unit in millimeters. - */ @Override - public float getPixelUnitToMillimeter() { + public float getResolution() { if (svgUserAgent != null) { - return svgUserAgent.getPixelUnitToMillimeter(); + return svgUserAgent.getResolution(); } - return 0.264583333333333333333f; // 96 dpi + return 96f; // 96 dpi } /** @@ -3260,7 +3276,7 @@ public float getMediumFontSize() { return svgUserAgent.getMediumFontSize(); } // 9pt (72pt = 1in) - return 9f * 25.4f / (72f * getPixelUnitToMillimeter()); + return 9f * getResolution() / 72f; } /** diff --git a/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/SVGUserAgent.java b/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/SVGUserAgent.java index 217b20b32..4fee75493 100644 --- a/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/SVGUserAgent.java +++ b/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/SVGUserAgent.java @@ -72,8 +72,13 @@ public interface SVGUserAgent { /** * Returns the size of a px CSS unit in millimeters. + * + * @deprecated Use {@link #getResolution()}. */ - float getPixelUnitToMillimeter(); + @Deprecated + default float getPixelUnitToMillimeter() { + return 25.4f / getResolution(); + } /** * Returns the size of a px CSS unit in millimeters. This will be removed after @@ -86,6 +91,11 @@ default float getPixelToMM() { return getPixelUnitToMillimeter(); } + /** + * Returns the resolution in {@code dpi}. + */ + float getResolution(); + /** * Returns the default font family. */ diff --git a/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/SVGUserAgentAdapter.java b/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/SVGUserAgentAdapter.java index fde735d65..e7624f7b7 100644 --- a/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/SVGUserAgentAdapter.java +++ b/echosvg-swing/src/main/java/io/sf/carte/echosvg/swing/svg/SVGUserAgentAdapter.java @@ -119,6 +119,11 @@ public float getPixelUnitToMillimeter() { return 0.26458333333333333333333333333333f; // 96dpi } + @Override + public float getResolution() { + return 96f; + } + /** * Returns the default font family. */ @@ -132,8 +137,8 @@ public String getDefaultFontFamily() { */ @Override public float getMediumFontSize() { - // 9pt (72pt == 1in) - return 9f * 25.4f / (72f * getPixelUnitToMillimeter()); + // 9pt (72pt = 1in) + return 9f * getResolution() / 72f; } /** diff --git a/echosvg-test/src/main/java/io/sf/carte/echosvg/test/image/ImageCompareUtil.java b/echosvg-test/src/main/java/io/sf/carte/echosvg/test/image/ImageCompareUtil.java index 07a09fda5..c0866ece7 100644 --- a/echosvg-test/src/main/java/io/sf/carte/echosvg/test/image/ImageCompareUtil.java +++ b/echosvg-test/src/main/java/io/sf/carte/echosvg/test/image/ImageCompareUtil.java @@ -182,6 +182,10 @@ public String compare(float allowedPercentBelowThreshold, float allowedPercentOv return null; } + if (result < ImageComparator.DIFFERENT_PIXELS_BELOW_THRESHOLD) { + return ImageComparator.getResultDescription(result); + } + // We are in error (images are different: produce an image // with the two images side by side as well as a diff image) BufferedImage diff = ImageComparator.createDiffImage(imageA, imageB); diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/AbstractBypassRenderingCheck.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/AbstractBypassRenderingCheck.java index e1daa589d..6f17afcd5 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/AbstractBypassRenderingCheck.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/AbstractBypassRenderingCheck.java @@ -56,7 +56,7 @@ public class AbstractBypassRenderingCheck { static final String BROWSER_MEDIA = "screen"; - static final String PRINT_MEDIUM = "print"; + static final String PRINT_MEDIA = "print"; void test(String file) throws TranscoderException, IOException { test(file, 0); @@ -82,7 +82,7 @@ void test(String file, int expectedErrorCount, boolean validating) */ void testPrint(String file, int expectedErrorCount) throws TranscoderException, IOException { - test(file, PRINT_MEDIUM, false, null, null, true, expectedErrorCount); + test(file, PRINT_MEDIA, false, null, null, true, expectedErrorCount); } /** diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/AbstractSamplesRendering.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/AbstractSamplesRendering.java index 70d7db02a..5e80ae588 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/AbstractSamplesRendering.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/AbstractSamplesRendering.java @@ -183,6 +183,15 @@ void testAlternateSheet(String file, String alt, boolean validating) runner.runTest(getBelowThresholdAllowed(), getOverThresholdAllowed()); } + void testDarkMode(String file) + throws TranscoderException, IOException { + RenderingTest runner = new RenderingTest(); + runner.setValidating(Boolean.FALSE); + runner.setDarkMode(true); + runner.setFile(file); + runner.runTest(getBelowThresholdAllowed(), getOverThresholdAllowed()); + } + void testUserSheet(String file, boolean validating) throws TranscoderException, IOException { AltUserSheetRenderingTest runner = new AltUserSheetRenderingTest(); runner.setValidating(validating); diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/MermaidRenderingTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/MermaidRenderingTest.java index 02e2df121..580ac0ebf 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/MermaidRenderingTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/MermaidRenderingTest.java @@ -112,7 +112,7 @@ public void testC4Dynamic() throws TranscoderException, IOException { @Test public void testClass() throws TranscoderException, IOException { - testMermaid("samples/tests/spec2/foreign/mermaid-class.svg", 1); + testMermaid("samples/tests/spec2/foreign/mermaid-class.svg"); } @Test @@ -137,17 +137,17 @@ public void testFlowChartCyrillic() throws TranscoderException, IOException { @Test public void testGantt() throws TranscoderException, IOException { - testMermaid("samples/tests/spec2/foreign/mermaid-gantt.svg", 6); + testMermaid("samples/tests/spec2/foreign/mermaid-gantt.svg", 1); } @Test public void testGitGraph() throws TranscoderException, IOException { - testMermaid("samples/tests/spec2/foreign/mermaid-git-graph.svg", 1); + testMermaid("samples/tests/spec2/foreign/mermaid-git-graph.svg"); } @Test public void testJourney() throws TranscoderException, IOException { - testMermaid("samples/tests/spec2/foreign/mermaid-journey.svg", 9); + testMermaid("samples/tests/spec2/foreign/mermaid-journey.svg", 7); } @Test diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/ResolutionPxMmRenderingTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/ResolutionPxMmRenderingTest.java index 66d4e3eb7..ba7673a5d 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/ResolutionPxMmRenderingTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/ResolutionPxMmRenderingTest.java @@ -24,7 +24,7 @@ /** * Checks for regressions in rendering of SVG with varying resolution. * - * @see io.sf.carte.echosvg.transcoder.image.test.PixelToMMTest + * @see io.sf.carte.echosvg.transcoder.image.test.ResolutionTest * * @author See Git history. * @version $Id$ @@ -55,6 +55,7 @@ protected CharSequence getImageSuffix() { /** * Returns the ImageTranscoder the Test should use */ + @SuppressWarnings("deprecation") @Override ImageTranscoder getTestImageTranscoder() { ImageTranscoder t = super.getTestImageTranscoder(); diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SVGRenderingAccuracyTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SVGRenderingAccuracyTest.java index e3ecd3032..c6eb6e6a4 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SVGRenderingAccuracyTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SVGRenderingAccuracyTest.java @@ -79,6 +79,10 @@ public class SVGRenderingAccuracyTest extends AbstractRenderingAccuracyTest { */ private String media; + /* + * Enable or disable dark mode. + */ + private boolean darkMode = false; // Batik uses 9 private static final int DEFAULT_COMPRESSION_LEVEL = 9; @@ -154,6 +158,15 @@ public void setMedia(String media) { this.media = media; } + /** + * Enable or disable the dark mode. + * + * @param darkMode {@code true} to enable the dark mode. + */ + public void setDarkMode(boolean darkMode) { + this.darkMode = darkMode; + } + /** * Set the compression level. * @@ -204,7 +217,7 @@ protected CharSequence getImageSuffix() { boolean nonDefCompr = getCompressionLevel() != getDefaultCompressionLevel(); boolean nonDefMedia = media != null && !DEFAULT_MEDIUM.equals(media); - if (nonDefCompr && nonDefMedia && tEXt == null && iTXt == null && zTXt == null) { + if (nonDefCompr && nonDefMedia && !darkMode && tEXt == null && iTXt == null && zTXt == null) { return ""; } @@ -212,6 +225,11 @@ protected CharSequence getImageSuffix() { if (nonDefMedia) { buf.append("-").append(media); } + + if (darkMode) { + buf.append("-dark"); + } + if (nonDefCompr) { buf.append("-z").append(getCompressionLevel()); } @@ -277,7 +295,14 @@ protected void checkErrorHandler(ErrorHandler errorHandler) { ImageTranscoder getTestImageTranscoder() { ImageTranscoder t = createTestImageTranscoder(); t.addTranscodingHint(ImageTranscoder.KEY_FORCE_TRANSPARENT_WHITE, Boolean.FALSE); - t.addTranscodingHint(ImageTranscoder.KEY_BACKGROUND_COLOR, new Color(0, 0, 0, 0)); + + if (darkMode) { + // Opaque background for dark mode + t.addTranscodingHint(ImageTranscoder.KEY_BACKGROUND_COLOR, new Color(0, 0, 0, 255)); + } else { + t.addTranscodingHint(ImageTranscoder.KEY_BACKGROUND_COLOR, new Color(0, 0, 0, 0)); + } + t.addTranscodingHint(SVGAbstractTranscoder.KEY_EXECUTE_ONLOAD, Boolean.TRUE); if (validate) { @@ -292,6 +317,10 @@ ImageTranscoder getTestImageTranscoder() { t.addTranscodingHint(SVGAbstractTranscoder.KEY_MEDIA, media); } + if (darkMode) { + t.addTranscodingHint(SVGAbstractTranscoder.KEY_PREFERS_COLOR_SCHEME, "dark"); + } + if (getCompressionLevel() != getDefaultCompressionLevel()) { t.addTranscodingHint(PNGTranscoder.KEY_COMPRESSION_LEVEL, getCompressionLevel()); } diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SamplesSpec2RenderingTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SamplesSpec2RenderingTest.java index 89537b76f..56faa3772 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SamplesSpec2RenderingTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SamplesSpec2RenderingTest.java @@ -18,11 +18,17 @@ */ package io.sf.carte.echosvg.test.svg; +import static org.junit.jupiter.api.Assertions.assertThrows; + import java.io.IOException; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import io.sf.carte.echosvg.css.engine.CSSCircularityException; +import io.sf.carte.echosvg.css.engine.CSSResourceLimitException; import io.sf.carte.echosvg.test.TestFonts; import io.sf.carte.echosvg.transcoder.TranscoderException; @@ -45,6 +51,100 @@ public static void setUpBeforeClass() throws Exception { TestFonts.loadTestFonts(); } + /* + * Security + */ + + /** + * Check the behaviour on attr() circularity. + * + *

+ * If the test runs for more than a few seconds, the test failed. + *

+ * + * @throws TranscoderException + * @throws IOException + */ + @Test + @Timeout(value = 2500, unit = TimeUnit.MILLISECONDS) + public void testAttrCircularity() throws TranscoderException, IOException { + assertThrows(CSSCircularityException.class, + () -> testNVErrIgnore("samples/tests/spec2/security/attrCircularity.svg", BROWSER_MEDIA, 0)); + } + + /** + * Test a Billion Laughs DoS attack against the var() implementation. + * + *

+ * If the test runs for more than 3 seconds, either the computer is really slow + * or the test failed. + *

+ * + * @throws TranscoderException + * @throws IOException + */ + @Test + @Timeout(value = 3000, unit = TimeUnit.MILLISECONDS) + public void testVarBLA() throws TranscoderException, IOException { + assertThrows(CSSResourceLimitException.class, + () -> testNVErrIgnore("samples/tests/spec2/security/varBillionLaughsAttack.svg", + BROWSER_MEDIA, 0)); + } + + /** + * Test a Billion Laughs DoS attack against the var() implementation, fallback + * variant. + * + *

+ * If the test runs for more than 3 seconds, either the computer is really slow + * or the test failed. + *

+ * + * @throws TranscoderException + * @throws IOException + */ + @Test + @Timeout(value = 3000, unit = TimeUnit.MILLISECONDS) + public void testVarBLAFallback() throws TranscoderException, IOException { + assertThrows(CSSResourceLimitException.class, + () -> testNVErrIgnore("samples/tests/spec2/security/varBLAFallback.svg", + BROWSER_MEDIA, 0)); + } + + /** + * Check the behaviour on var() circularity. + * + *

+ * If the test runs for more than a few seconds, the test failed. + *

+ * + * @throws TranscoderException + * @throws IOException + */ + @Test + @Timeout(value = 2500, unit = TimeUnit.MILLISECONDS) + public void testVarCircularity() throws TranscoderException, IOException { + assertThrows(CSSCircularityException.class, + () -> testNVErrIgnore("samples/tests/spec2/security/varCircularity.svg", BROWSER_MEDIA, 0)); + } + + /** + * Check the behaviour on var() fallback circularity. + * + *

+ * If the test runs for more than a few seconds, the test failed. + *

+ * + * @throws TranscoderException + * @throws IOException + */ + @Test + @Timeout(value = 2500, unit = TimeUnit.MILLISECONDS) + public void testVarFallbackCircularity() throws TranscoderException, IOException { + assertThrows(CSSCircularityException.class, () -> testNVErrIgnore( + "samples/tests/spec2/security/varFallbackCircularity.svg", BROWSER_MEDIA, 0)); + } + /* * Namespaceless href */ @@ -56,6 +156,31 @@ public void testHref() throws TranscoderException, IOException { /* * CSS3 Styling */ + @Test + public void testAttrValues() throws TranscoderException, IOException { + testNV("samples/tests/spec2/styling/attrValues.svg"); + } + + @Test + public void testConditionalRules() throws TranscoderException, IOException { + testNV("samples/tests/spec2/styling/conditionalRules.svg"); + } + + @Test + public void testConditionalRulesDark() throws TranscoderException, IOException { + testDarkMode("samples/tests/spec2/styling/conditionalRules.svg"); + } + + @Test + public void testConditionalRulesAlternate() throws TranscoderException, IOException { + testAlternateSheet("samples/tests/spec2/styling/conditionalRules.svg", "Gray", false); + } + + @Test + public void testConditionalRulesPrint() throws TranscoderException, IOException { + testNVErrIgnore("samples/tests/spec2/styling/conditionalRules.svg", PRINT_MEDIA, 0); + } + @Test public void testMermaidColor4() throws TranscoderException, IOException { testNV("samples/tests/spec2/styling/mermaid-color4.svg"); diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SamplesSpecRenderingTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SamplesSpecRenderingTest.java index e72ba0243..2b8ff0ca9 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SamplesSpecRenderingTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/SamplesSpecRenderingTest.java @@ -523,6 +523,7 @@ public void testRenderingPaintOpacity() throws TranscoderException, IOException test("samples/tests/spec/rendering/paintOpacity.svg"); } + @Test public void testRenderingResolutionPxMM() throws TranscoderException, IOException { testResolutionPxMM("samples/tests/spec/rendering/resolution.svg", 0.25f); } diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/StyleBypassRenderingTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/StyleBypassRenderingTest.java index 1b03a9f29..eab48a6cd 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/StyleBypassRenderingTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/test/svg/StyleBypassRenderingTest.java @@ -1340,28 +1340,28 @@ public void testBug19363() throws TranscoderException, IOException { @Test public void testCSS3_All() throws TranscoderException, IOException { testAllInputSources("samples/tests/spec2/styling/css3.html", null, false, null, null, - false, 4); + false, 0); } @Test public void testCSS3Print() throws TranscoderException, IOException { - testPrint("samples/tests/spec2/styling/css3.html", 4); + testPrint("samples/tests/spec2/styling/css3.html", 0); } @Test public void testCSS3AlternateStylesheet() throws TranscoderException, IOException { - testAlternate("samples/tests/spec2/styling/css3.html", "Gray", false, 4); + testAlternate("samples/tests/spec2/styling/css3.html", "Gray", false, 0); } @Test public void testCSS3Dark() throws TranscoderException, IOException { - testDark("samples/tests/spec2/styling/css3.html", 4); + testDark("samples/tests/spec2/styling/css3.html", 0); } @Test public void testCSS3_Selector() throws TranscoderException, IOException { test("samples/tests/spec2/styling/css3.html", SVGRenderingAccuracyTest.DEFAULT_MEDIUM, - false, null, "#theSVG", true, 4); + false, null, "#theSVG", true, 0); } } diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/AbstractImageTranscoderTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/AbstractImageTranscoderTest.java index f227dd31f..4b6e212cf 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/AbstractImageTranscoderTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/AbstractImageTranscoderTest.java @@ -275,6 +275,10 @@ private boolean compareImage(BufferedImage img) throws TranscoderException, IOEx ByteArrayOutputStream out = new ByteArrayOutputStream(2048); TranscoderOutput output = new TranscoderOutput(out); PNGTranscoder t = new PNGTranscoder(); + Map hints = createTranscodingHints(); + if (hints != null) { + t.setTranscodingHints(hints); + } t.writeImage(img, output); byte[] imgData = out.toByteArray(); diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/PixelToMMTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/ResolutionTest.java similarity index 66% rename from echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/PixelToMMTest.java rename to echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/ResolutionTest.java index 716a530f7..a3107543d 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/PixelToMMTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/transcoder/image/test/ResolutionTest.java @@ -29,14 +29,17 @@ import io.sf.carte.echosvg.transcoder.TranscodingHints.Key; /** - * Test the ImageTranscoder with the KEY_PIXEL_UNIT_TO_MILLIMETER transcoding - * hint. + * Test the ImageTranscoder with the KEY_RESOLUTION_DPI transcoding hint. * - * @author Thierry Kormann - * @author For later modifications, see Git history. + *

+ * Based on PixelToMM test by + * Thierry Kormann. For + * later modifications, see Git history. + *

+ * * @version $Id$ */ -public class PixelToMMTest extends AbstractImageTranscoderTest { +public class ResolutionTest extends AbstractImageTranscoderTest { /** The URI of the input image. */ private String inputURI; @@ -44,33 +47,33 @@ public class PixelToMMTest extends AbstractImageTranscoderTest { /** The URI of the reference image. */ private String refImageURI; - /** The pixel to mm factor. */ - private float px2mm; + /** The resolution. */ + private float resolution; @Test public void test96dpi() throws TranscoderException { - testPixelToMM("test-resources/io/sf/carte/echosvg/transcoder/image/resources/px2mm.svg", - "test-references/io/sf/carte/echosvg/transcoder/image/px2mm96dpi.png", 0.2645833f); + testResolution("test-resources/io/sf/carte/echosvg/transcoder/image/resources/resolution.svg", + "test-references/io/sf/carte/echosvg/transcoder/image/resolution96dpi.png", 96f); } @Test public void test72dpi() throws TranscoderException { - testPixelToMM("test-resources/io/sf/carte/echosvg/transcoder/image/resources/px2mm.svg", - "test-references/io/sf/carte/echosvg/transcoder/image/px2mm72dpi.png", 0.3528f); + testResolution("test-resources/io/sf/carte/echosvg/transcoder/image/resources/resolution.svg", + "test-references/io/sf/carte/echosvg/transcoder/image/resolution72dpi.png", 72f); } /** - * Runs a new PixelToMMTest. + * Runs a new ResolutionTest. * * @param inputURI the URI of the input image * @param refImageURI the URI of the reference image - * @param px2mm the pixel to mm conversion factor - * @throws TranscoderException + * @param resolution the resolution + * @throws TranscoderException */ - private void testPixelToMM(String inputURI, String refImageURI, float px2mm) throws TranscoderException { + private void testResolution(String inputURI, String refImageURI, float resolution) throws TranscoderException { this.inputURI = inputURI; this.refImageURI = refImageURI; - this.px2mm = px2mm; + this.resolution = resolution; runTest(); } @@ -88,7 +91,7 @@ protected TranscoderInput createTranscoderInput() { @Override protected Map createTranscodingHints() { Map hints = new HashMap<>(3); - hints.put(SVGAbstractTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, px2mm); + hints.put(SVGAbstractTranscoder.KEY_RESOLUTION_DPI, resolution); return hints; } diff --git a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/SVGAbstractTranscoder.java b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/SVGAbstractTranscoder.java index 996bace25..142b92898 100644 --- a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/SVGAbstractTranscoder.java +++ b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/SVGAbstractTranscoder.java @@ -895,6 +895,35 @@ protected void setImageSize(float docWidth, float docHeight) { */ public static final TranscodingHints.Key KEY_MEDIA = new StringKey(); + /** + * The preferred color scheme key. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key:KEY_PREFERS_COLOR_SCHEME
Value:String
Default:"light"
Required:No
Description:Specify the preferred color scheme to use in + * CSS media queries.
+ */ + public static final TranscodingHints.Key KEY_PREFERS_COLOR_SCHEME = new StringKey(); + /** * The CSS selector key. * @@ -1012,29 +1041,38 @@ protected void setImageSize(float docWidth, float docHeight) { /** * The number of millimeters in each pixel key. - *
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Key:KEY_PIXEL_UNIT_TO_MILLIMETER
Value:Float
Default:0.264583
Required:No
Description:Specify the size of a px CSS unit in millimeters.
+ *

+ * Using a concept of physical pixels instead of CSS pixels is a bad idea which + * leads to distorted shapes, unexpected font sizes and wrong aspect ratios. + * Physical pixels were removed from Web standards decades ago, please use + * {@code KEY_RESOLUTION_DPI} instead. + *

+ * @deprecated as of EchoSVG 2.0 + * @see #KEY_RESOLUTION_DPI + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key:KEY_PIXEL_UNIT_TO_MILLIMETER
Value:Float
Default:0.264583
Required:No
Description:Specify the size of a px CSS unit in + * millimeters.
*/ public static final TranscodingHints.Key KEY_PIXEL_UNIT_TO_MILLIMETER = new FloatKey(); @@ -1071,6 +1109,36 @@ protected void setImageSize(float docWidth, float docHeight) { @Deprecated public static final TranscodingHints.Key KEY_PIXEL_TO_MM = KEY_PIXEL_UNIT_TO_MILLIMETER; + /** + * The resolution expressed in {@code dpi} key. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key:KEY_RESOLUTION_DPI
Value:Float
Default:96
Required:No
Description:The resolution expressed in {@code dpi}. If + * not set, implementations may check for {@code KEY_PIXEL_UNIT_TO_MILLIMETER} + * and compute the resolution from that value.
+ */ + public static final TranscodingHints.Key KEY_RESOLUTION_DPI = new FloatKey(); + /** * The 'onload' execution key. * @@ -1318,9 +1386,32 @@ public float getPixelUnitToMillimeter() { return (Float) obj; } + obj = SVGAbstractTranscoder.this.hints.get(KEY_RESOLUTION_DPI); + if (obj != null) { + return 25.4f / ((Float) obj).floatValue(); + } + return super.getPixelUnitToMillimeter(); } + /** + * Returns the resolution in dpi. + */ + @Override + public float getResolution() { + Object obj = SVGAbstractTranscoder.this.hints.get(KEY_RESOLUTION_DPI); + if (obj != null) { + return (Float) obj; + } + + obj = SVGAbstractTranscoder.this.hints.get(KEY_PIXEL_UNIT_TO_MILLIMETER); + if (obj != null) { + return 25.4f / ((Float) obj).floatValue(); + } + + return 96f; + } + /** * Returns the user language specified in the TranscodingHints or * "en" (english) if any. @@ -1346,6 +1437,15 @@ public String getMedia() { return super.getMedia(); } + @Override + public String getPrefersColorScheme() { + String s = (String) hints.get(KEY_PREFERS_COLOR_SCHEME); + if (s != null) + return s; + + return super.getPrefersColorScheme(); + } + /** * Returns the default font family. */ diff --git a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/JPEGTranscoder.java b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/JPEGTranscoder.java index 84473209d..47197f596 100644 --- a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/JPEGTranscoder.java +++ b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/JPEGTranscoder.java @@ -87,9 +87,9 @@ public void writeImage(BufferedImage img, TranscoderOutput output) throws Transc ImageWriter writer = ImageWriterRegistry.getInstance().getWriterFor("image/jpeg"); ImageWriterParams params = new ImageWriterParams(); params.setJPEGQuality(quality, true); - float PixSzMM = getUserAgent().getPixelUnitToMillimeter(); - int PixSzInch = (int) (25.4 / PixSzMM + 0.5); - params.setResolution(PixSzInch); + float resol = getUserAgent().getResolution(); + int iResol = Math.round(resol); + params.setResolution(iResol); writer.writeImage(img, ostream, params); ostream.flush(); } catch (IOException ex) { diff --git a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/PNGTranscoderImageIOWriteAdapter.java b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/PNGTranscoderImageIOWriteAdapter.java index 9e02fd373..5c9a77bff 100644 --- a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/PNGTranscoderImageIOWriteAdapter.java +++ b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/PNGTranscoderImageIOWriteAdapter.java @@ -86,9 +86,8 @@ public void writeImage(PNGTranscoder transcoder, BufferedImage img, TranscoderOu params.setCompressedText(text); } - float PixSzMM = transcoder.getUserAgent().getPixelUnitToMillimeter(); - int PixSzInch = (int) (25.4 / PixSzMM + 0.5); - params.setResolution(PixSzInch); + float resol = transcoder.getUserAgent().getResolution(); + params.setResolution(Math.round(resol)); try { OutputStream ostream = output.getOutputStream(); diff --git a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/PNGTranscoderInternalCodecWriteAdapter.java b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/PNGTranscoderInternalCodecWriteAdapter.java index bf43089b7..3237b8900 100644 --- a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/PNGTranscoderInternalCodecWriteAdapter.java +++ b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/PNGTranscoderInternalCodecWriteAdapter.java @@ -103,10 +103,10 @@ public void writeImage(PNGTranscoder transcoder, BufferedImage img, TranscoderOu params.setCompressedText(text); } - float PixSzMM = transcoder.getUserAgent().getPixelUnitToMillimeter(); - // num Pixs in 1 Meter - int numPix = (int) ((1000 / PixSzMM) + 0.5); - params.setPhysicalDimension(numPix, numPix, 1); // 1 means 'pix/meter' + float resol = transcoder.getUserAgent().getResolution(); + // number of pixels in 1 Meter + int numPix = Math.round(resol / 0.0254f); + params.setPhysicalDimension(numPix, numPix, 1); // 1 means 'pixels/meter' try { OutputStream ostream = output.getOutputStream(); diff --git a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/TIFFTranscoderImageIOWriteAdapter.java b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/TIFFTranscoderImageIOWriteAdapter.java index 77f480d01..a57437a0f 100644 --- a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/TIFFTranscoderImageIOWriteAdapter.java +++ b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/image/TIFFTranscoderImageIOWriteAdapter.java @@ -60,9 +60,8 @@ public void writeImage(TIFFTranscoder transcoder, BufferedImage img, TranscoderO ImageWriter writer = ImageWriterRegistry.getInstance().getWriterFor("image/tiff"); ImageWriterParams params = new ImageWriterParams(); - float PixSzMM = transcoder.getUserAgent().getPixelUnitToMillimeter(); - int PixSzInch = (int) (25.4 / PixSzMM + 0.5); - params.setResolution(PixSzInch); + float resol = transcoder.getUserAgent().getResolution(); + params.setResolution(Math.round(resol)); if (hints.containsKey(TIFFTranscoder.KEY_COMPRESSION_METHOD)) { String method = (String) hints.get(TIFFTranscoder.KEY_COMPRESSION_METHOD); diff --git a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/impl/SizingHelper.java b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/impl/SizingHelper.java index edceac0f9..62c5e8567 100644 --- a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/impl/SizingHelper.java +++ b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/impl/SizingHelper.java @@ -18,15 +18,15 @@ */ package io.sf.carte.echosvg.transcoder.impl; +import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.DOMException; import org.w3c.dom.Element; -import io.sf.carte.doc.style.css.CSSUnit; +import io.sf.carte.doc.style.css.CSSExpressionValue; +import io.sf.carte.doc.style.css.CSSTypedValue; import io.sf.carte.doc.style.css.CSSValue.CssType; import io.sf.carte.doc.style.css.property.Evaluator; -import io.sf.carte.doc.style.css.property.ExpressionValue; import io.sf.carte.doc.style.css.property.StyleValue; -import io.sf.carte.doc.style.css.property.TypedValue; import io.sf.carte.doc.style.css.property.ValueFactory; import io.sf.carte.doc.style.css.property.ValueList; import io.sf.carte.echosvg.transcoder.TranscoderException; @@ -139,17 +139,17 @@ static boolean computeRectangle(StyleValue value, float[] numbers) throws DOMExc if (item.getCssValueType() != CssType.TYPED) { return false; } - TypedValue typed; + CSSTypedValue typed; switch (item.getPrimitiveType()) { case NUMERIC: - typed = (TypedValue) item; + typed = (CSSTypedValue) item; if (typed.getUnitType() != CSSUnit.CSS_NUMBER) { return false; } break; case EXPRESSION: - Evaluator eval = new Evaluator(); - typed = eval.evaluateExpression((ExpressionValue) item); + Evaluator eval = new Evaluator(CSSUnit.CSS_NUMBER); + typed = eval.evaluateExpression((CSSExpressionValue) item); if (typed.getUnitType() != CSSUnit.CSS_NUMBER) { return false; } @@ -170,10 +170,10 @@ static float floatValue(String number) throws TranscoderException, DOMException throw new TranscoderException("Leave value unchanged."); } - TypedValue typed; + CSSTypedValue typed; switch (value.getPrimitiveType()) { case NUMERIC: - typed = (TypedValue) value; + typed = (CSSTypedValue) value; if (typed.getUnitType() != CSSUnit.CSS_NUMBER) { if (CSSUnit.isRelativeLengthUnitType(typed.getUnitType())) { throw new TranscoderException("Leave value unchanged."); @@ -183,8 +183,8 @@ static float floatValue(String number) throws TranscoderException, DOMException } break; case EXPRESSION: - Evaluator eval = new Evaluator(); - typed = eval.evaluateExpression((ExpressionValue) value); + Evaluator eval = new Evaluator(CSSUnit.CSS_NUMBER); + typed = eval.evaluateExpression((CSSExpressionValue) value); if (typed.getUnitType() != CSSUnit.CSS_NUMBER) { if (CSSUnit.isRelativeLengthUnitType(typed.getUnitType())) { throw new TranscoderException("Leave value unchanged."); diff --git a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/print/PrintTranscoder.java b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/print/PrintTranscoder.java index 334015050..ed5604b01 100644 --- a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/print/PrintTranscoder.java +++ b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/print/PrintTranscoder.java @@ -676,6 +676,7 @@ private void drawError(Graphics g, Exception e) { public static final String USAGE = "java io.sf.carte.echosvg.transcoder.print.PrintTranscoder "; + @SuppressWarnings("deprecation") public static void main(String[] args) throws Exception { if (args.length < 1) { System.err.println(USAGE); diff --git a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/util/CSSTranscodingHelper.java b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/util/CSSTranscodingHelper.java index 7531b9c77..ad7c75645 100644 --- a/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/util/CSSTranscodingHelper.java +++ b/echosvg-transcoder/src/main/java/io/sf/carte/echosvg/transcoder/util/CSSTranscodingHelper.java @@ -36,6 +36,7 @@ import java.util.Locale; import java.util.Set; +import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; @@ -59,7 +60,6 @@ import io.sf.carte.doc.style.css.CSSMediaException; import io.sf.carte.doc.style.css.CSSStyleSheetFactory; import io.sf.carte.doc.style.css.CSSTypedValue; -import io.sf.carte.doc.style.css.CSSUnit; import io.sf.carte.doc.style.css.CSSValue; import io.sf.carte.doc.style.css.CSSValue.CssType; import io.sf.carte.doc.style.css.StyleDatabase; @@ -69,6 +69,7 @@ import io.sf.carte.doc.style.css.nsac.CombinatorSelector; import io.sf.carte.doc.style.css.nsac.Condition; import io.sf.carte.doc.style.css.nsac.ConditionalSelector; +import io.sf.carte.doc.style.css.nsac.LexicalUnit; import io.sf.carte.doc.style.css.nsac.Selector; import io.sf.carte.doc.style.css.nsac.SelectorList; import io.sf.carte.doc.style.css.om.AbstractCSSCanvas; @@ -1101,7 +1102,9 @@ public CSSTypedValue getInitialColor() { } @Override - public boolean supports(String property, CSSValue value) { + public boolean supports(String property, LexicalUnit lunit) { + ValueFactory valueFactory = new ValueFactory(); + CSSValue value = valueFactory.createCSSValue(lunit); if ("color".equalsIgnoreCase(property) || "background-color".equalsIgnoreCase(property)) { return supportsColor(value); @@ -1183,8 +1186,16 @@ private boolean supports(Condition condition) { private class MyCanvas extends AbstractCSSCanvas { + private CSSDocument document; + protected MyCanvas(CSSDocument doc) { - super(doc); + super(); + document = doc; + } + + @Override + public CSSDocument getDocument() { + return document; } @Override diff --git a/gradle.properties b/gradle.properties index e26045328..6b7b86c05 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Dependency versions checkstyleVersion=10.18.0 grGitVersion=5.2.2 -css4jVersion=4.4 +css4jVersion=5.0 css4jAwtVersion=4.0 xmlDtdVersion=4.3 rhinoVersion=1.7.15 diff --git a/samples/tests/spec/text/verticalText.svg b/samples/tests/spec/text/verticalText.svg index b01f04cd1..4b38423b7 100644 --- a/samples/tests/spec/text/verticalText.svg +++ b/samples/tests/spec/text/verticalText.svg @@ -77,13 +77,13 @@ Writing top to bottom画像 Glyph Orientation:auto画像 - Glyph Orientation:0画像 - Glyph Orientation:90画像 - Glyph Orientation:180画像 - Glyph Orientation:270画像 Embedded <tspan> element Embedded orientated element diff --git a/samples/tests/spec2/security/attrCircularity.svg b/samples/tests/spec2/security/attrCircularity.svg new file mode 100644 index 000000000..9364ac1dd --- /dev/null +++ b/samples/tests/spec2/security/attrCircularity.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + +attr() fallback circularity + + + + + + + + + + + +attr() fallback circularity + + diff --git a/samples/tests/spec2/security/varBLAFallback.svg b/samples/tests/spec2/security/varBLAFallback.svg new file mode 100644 index 000000000..c855deb70 --- /dev/null +++ b/samples/tests/spec2/security/varBLAFallback.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + +Billion Laughs DoS attack against var() fallback + + + + + + + + + + + +Billion laughs attack against var() fallback + + diff --git a/samples/tests/spec2/security/varBillionLaughsAttack.svg b/samples/tests/spec2/security/varBillionLaughsAttack.svg new file mode 100644 index 000000000..4af8bb045 --- /dev/null +++ b/samples/tests/spec2/security/varBillionLaughsAttack.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + +Billion Laughs DoS attack + + + + + + + + + + + +Billion laughs attack against var() + + diff --git a/samples/tests/spec2/security/varCircularity.svg b/samples/tests/spec2/security/varCircularity.svg new file mode 100644 index 000000000..d91a14539 --- /dev/null +++ b/samples/tests/spec2/security/varCircularity.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + +var() circularity + + + + + + + + + + + +var() circularity + + diff --git a/samples/tests/spec2/security/varFallbackCircularity.svg b/samples/tests/spec2/security/varFallbackCircularity.svg new file mode 100644 index 000000000..bd6280821 --- /dev/null +++ b/samples/tests/spec2/security/varFallbackCircularity.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + +var() circularity + + + + + + + + + + + +var() fallback circularity + + diff --git a/samples/tests/spec2/styling/attrValues.svg b/samples/tests/spec2/styling/attrValues.svg new file mode 100644 index 000000000..2d7e30acd --- /dev/null +++ b/samples/tests/spec2/styling/attrValues.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + Media Queries + Conditional rules + Color Level 4 / calc() + Custom Properties + + + diff --git a/samples/tests/spec2/styling/conditionalRules.svg b/samples/tests/spec2/styling/conditionalRules.svg new file mode 100644 index 000000000..06b63d46e --- /dev/null +++ b/samples/tests/spec2/styling/conditionalRules.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Media Queries + Conditional rules + Color Level 4 / calc() + Custom Properties + + + diff --git a/samples/unsupportedRules.svg b/samples/unsupportedRules.svg index ba9f43290..bc2171f95 100644 --- a/samples/unsupportedRules.svg +++ b/samples/unsupportedRules.svg @@ -19,16 +19,17 @@ --> - +