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 f85e0c37d..5c4a408e2 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 @@ -357,6 +357,11 @@ public float getRootFontSize() { return getAssociatedElement().getSVGContext().getRootFontSize(); } + @Override + public float getRootXHeight() { + return 0.5f; + } + @Override public float getRootLineHeight() { return getAssociatedElement().getSVGContext().getRootLineHeight(); 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 6c09dc156..154b8f057 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 @@ -912,6 +912,11 @@ public float getXHeight() { return 0.5f; } + @Override + public float getRootXHeight() { + return 0.5f; + } + /** * Returns the viewport width used to compute units. */ 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 b0f407712..884387965 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 @@ -23,6 +23,7 @@ import java.awt.geom.Rectangle2D; import java.lang.ref.SoftReference; +import org.w3c.css.om.unit.CSSUnit; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.events.DocumentEvent; @@ -35,9 +36,12 @@ import io.sf.carte.echosvg.anim.dom.AnimatedLiveAttributeValue; import io.sf.carte.echosvg.anim.dom.SVGOMAnimatedTransformList; import io.sf.carte.echosvg.anim.dom.SVGOMElement; +import io.sf.carte.echosvg.css.CSSSecurityException; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSEngineEvent; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; +import io.sf.carte.echosvg.css.engine.value.LengthManager; +import io.sf.carte.echosvg.css.engine.value.Value; import io.sf.carte.echosvg.dom.events.AbstractEvent; import io.sf.carte.echosvg.dom.svg.AbstractSVGTransformList; import io.sf.carte.echosvg.dom.svg.LiveAttributeException; @@ -603,7 +607,7 @@ public float getViewportHeight() { */ @Override public float getFontSize() { - return CSSUtilities.getComputedStyle(e, SVGCSSEngine.FONT_SIZE_INDEX).getFloatValue(); + return fontSizeValue(e); } /** @@ -611,19 +615,51 @@ public float getFontSize() { */ @Override public float getLineHeight() { - return CSSUtilities.getComputedStyle(e, SVGCSSEngine.LINE_HEIGHT_INDEX).getFloatValue(); + return lineHeightValue(e); } @Override public float getRootFontSize() { Element root = e.getOwnerDocument().getDocumentElement(); - return CSSUtilities.getComputedStyle(root, SVGCSSEngine.FONT_SIZE_INDEX).getFloatValue(); + return fontSizeValue(root); } @Override public float getRootLineHeight() { Element root = e.getOwnerDocument().getDocumentElement(); - return CSSUtilities.getComputedStyle(root, SVGCSSEngine.LINE_HEIGHT_INDEX).getFloatValue(); + return lineHeightValue(root); + } + + private float fontSizeValue(Element elt) { + try { + return CSSUtilities.getComputedStyle(elt, SVGCSSEngine.FONT_SIZE_INDEX).getFloatValue(); + } catch (CSSSecurityException ex) { + } + + if (ctx != null) { + return ctx.getUserAgent().getMediumFontSize(); + } + + return 12f; + } + + private float lineHeightValue(Element elt) { + Value v; + try { + v = CSSUtilities.getComputedStyle(elt, SVGCSSEngine.LINE_HEIGHT_INDEX); + } catch (CSSSecurityException ex) { + return fontSizeValue(elt) * LengthManager.DEFAULT_LINE_HEIGHT; + } + + float fv = v.getFloatValue(); + short unit = v.getUnitType(); + if (unit == CSSUnit.CSS_NUMBER) { + float fs = fontSizeValue(elt); + return fs * fv; + } + + // Must be pixels + return fv; } } 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 0d7c03d54..6e5282e4f 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 @@ -32,6 +32,7 @@ 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.CSSSecurityException; import io.sf.carte.echosvg.css.engine.CSSEngine; import io.sf.carte.echosvg.css.engine.CSSStylableElement; import io.sf.carte.echosvg.css.engine.SVGCSSEngine; @@ -78,7 +79,7 @@ public static CSSEngine getCSSEngine(Element e) { /** * Returns the computed style of the given property. */ - public static Value getComputedStyle(Element e, int property) { + public static Value getComputedStyle(Element e, int property) throws CSSSecurityException { CSSEngine engine = getCSSEngine(e); if (engine == null) return null; 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 4efc7948d..98ce62227 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 @@ -348,6 +348,11 @@ public float getXHeight() { return 0.5f; } + @Override + public float getRootXHeight() { + return 0.5f; + } + /** * Returns the viewport width used to compute units. */ 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 d2201336c..084b544bb 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 @@ -56,7 +56,7 @@ public abstract class LengthManager extends AbstractValueManager { * on a path". Web browsers use a default based on the font family, close to 1.2 *
*/ - protected static final float DEFAULT_LINE_HEIGHT = 1.1f; + public static final float DEFAULT_LINE_HEIGHT = 1.1f; /** * precomputed square-root of 2.0 diff --git a/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/DefaultLengthHandler.java b/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/DefaultLengthHandler.java index 98ed55e81..a00e1e4aa 100644 --- a/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/DefaultLengthHandler.java +++ b/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/DefaultLengthHandler.java @@ -99,6 +99,21 @@ public void percentage() throws ParseException { setUnit(CSSUnit.CSS_PERCENTAGE); } + @Override + public void rem() throws ParseException { + setUnit(CSSUnit.CSS_REM); + } + + @Override + public void rex() throws ParseException { + setUnit(CSSUnit.CSS_REX); + } + + @Override + public void rlh() throws ParseException { + setUnit(CSSUnit.CSS_RLH); + } + @Override public void vh() throws ParseException { setUnit(CSSUnit.CSS_VH); diff --git a/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/LengthHandler.java b/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/LengthHandler.java index d585fa65d..7b99870de 100644 --- a/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/LengthHandler.java +++ b/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/LengthHandler.java @@ -114,6 +114,27 @@ public interface LengthHandler { */ void percentage() throws ParseException; + /** + * Invoked when 'rem' has been parsed. + * + * @exception ParseException if an error occurs while processing the length + */ + void rem() throws ParseException; + + /** + * Invoked when 'rex' has been parsed. + * + * @exception ParseException if an error occurs while processing the length + */ + void rex() throws ParseException; + + /** + * Invoked when 'rlh' has been parsed. + * + * @exception ParseException if an error occurs while processing the length + */ + void rlh() throws ParseException; + /** * Invoked when 'vh' has been parsed. * diff --git a/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/LengthParser.java b/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/LengthParser.java index 5779b2c7c..7f4338698 100644 --- a/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/LengthParser.java +++ b/echosvg-parser/src/main/java/io/sf/carte/echosvg/parser/LengthParser.java @@ -450,6 +450,37 @@ protected void parseLength() throws ParseException, IOException { lengthHandler.percentage(); current = reader.read(); break; + case 'r': + current = reader.read(); + switch (current) { + case 'e': + current = reader.read(); + switch (current) { + case 'm': + lengthHandler.rem(); + current = reader.read(); + break; + case 'x': + lengthHandler.rex(); + current = reader.read(); + break; + default: + reportUnexpectedCharacterError(current); + } + break; + case 'l': + current = reader.read(); + if (current != 'h') { + reportCharacterExpectedError('h', current); + break; + } + lengthHandler.rlh(); + current = reader.read(); + break; + default: + reportUnexpectedCharacterError(current); + } + break; case 'v': current = reader.read(); switch (current) { @@ -462,6 +493,7 @@ protected void parseLength() throws ParseException, IOException { current = reader.read(); break; case 'm': + current = reader.read(); switch (current) { case 'i': current = reader.read(); 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 2b968b681..eb22a1d70 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 @@ -143,6 +143,12 @@ public static float cssToUserSpace(float v, short type, short d, Context ctx) { return exsToPixels(v, d, ctx); case CSSUnit.CSS_LH: return lhToPixels(v, d, ctx); + case CSSUnit.CSS_REM: + return remToPixels(v, d, ctx); + case CSSUnit.CSS_REX: + return rexToPixels(v, d, ctx); + case CSSUnit.CSS_RLH: + return rlhToPixels(v, d, ctx); case CSSUnit.CSS_VW: return vwToPixels(v, d, ctx); case CSSUnit.CSS_VH: @@ -201,6 +207,12 @@ public static float userSpaceToCSS(float v, short type, short d, Context ctx) { return pixelsToExs(v, d, ctx); case CSSUnit.CSS_LH: return pixelsToLh(v, d, ctx); + case CSSUnit.CSS_REM: + return pixelsToRem(v, d, ctx); + case CSSUnit.CSS_REX: + return pixelsToRex(v, d, ctx); + case CSSUnit.CSS_RLH: + return pixelsToRlh(v, d, ctx); case CSSUnit.CSS_VW: return pixelsToVw(v, d, ctx); case CSSUnit.CSS_VH: @@ -354,6 +366,30 @@ protected static float remToPixels(float v, short d, Context ctx) { return v * ctx.getRootFontSize(); } + /** + * Converts user units to rex units. + * + * @param v the value to convert + * @param d HORIZONTAL_LENGTH, VERTICAL_LENGTH, or OTHER_LENGTH + * @param ctx the context + */ + protected static float pixelsToRex(float v, short d, Context ctx) { + float xh = ctx.getRootXHeight(); + return v / xh / ctx.getRootFontSize(); + } + + /** + * Converts rex units to user units. + * + * @param v the value to convert + * @param d HORIZONTAL_LENGTH, VERTICAL_LENGTH, or OTHER_LENGTH + * @param ctx the context + */ + protected static float rexToPixels(float v, short d, Context ctx) { + float xh = ctx.getRootXHeight(); + return v * xh * ctx.getRootFontSize(); + } + /** * Converts user units to rlh units. * @@ -495,6 +531,11 @@ public interface Context { */ float getRootFontSize(); + /** + * Returns the x-height value of the :root element. + */ + float getRootXHeight(); + /** * Returns the line-height value of the :root element. */ diff --git a/echosvg-test/src/test/java/io/sf/carte/echosvg/parser/test/LengthParserTest.java b/echosvg-test/src/test/java/io/sf/carte/echosvg/parser/test/LengthParserTest.java index 757fbb131..796f150c4 100644 --- a/echosvg-test/src/test/java/io/sf/carte/echosvg/parser/test/LengthParserTest.java +++ b/echosvg-test/src/test/java/io/sf/carte/echosvg/parser/test/LengthParserTest.java @@ -73,6 +73,20 @@ public void test() throws Exception { testLengthParser("-0px", "0.0px"); testLengthParser("0000%", "0.0%"); + + testLengthParser("1rem", "1.0rem"); + + testLengthParser("02rex", "2.0rex"); + + testLengthParser("2rlh", "2.0rlh"); + + testLengthParser(".2vh", "0.2vh"); + + testLengthParser("2vw", "2.0vw"); + + testLengthParser("2.667vmin", "2.667vmin"); + + testLengthParser("2.3vmax", "2.3vmax"); } private class TestHandler extends DefaultLengthHandler { @@ -143,6 +157,21 @@ public void percentage() throws ParseException { buffer.append("%"); } + @Override + public void rem() throws ParseException { + buffer.append("rem"); + } + + @Override + public void rex() throws ParseException { + buffer.append("rex"); + } + + @Override + public void rlh() throws ParseException { + buffer.append("rlh"); + } + @Override public void vh() throws ParseException { buffer.append("vh"); diff --git a/test-references/io/sf/carte/echosvg/transcoder/image/resolution72dpi.png b/test-references/io/sf/carte/echosvg/transcoder/image/resolution72dpi.png index d87b6504a..f92af8791 100644 Binary files a/test-references/io/sf/carte/echosvg/transcoder/image/resolution72dpi.png and b/test-references/io/sf/carte/echosvg/transcoder/image/resolution72dpi.png differ diff --git a/test-references/io/sf/carte/echosvg/transcoder/image/resolution96dpi.png b/test-references/io/sf/carte/echosvg/transcoder/image/resolution96dpi.png index 1cac04fa7..3cc22a484 100644 Binary files a/test-references/io/sf/carte/echosvg/transcoder/image/resolution96dpi.png and b/test-references/io/sf/carte/echosvg/transcoder/image/resolution96dpi.png differ diff --git a/test-resources/io/sf/carte/echosvg/transcoder/image/resources/resolution.svg b/test-resources/io/sf/carte/echosvg/transcoder/image/resources/resolution.svg index 60cdf4c68..197cbfe98 100644 --- a/test-resources/io/sf/carte/echosvg/transcoder/image/resources/resolution.svg +++ b/test-resources/io/sf/carte/echosvg/transcoder/image/resources/resolution.svg @@ -22,29 +22,61 @@