diff --git a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGTextPathElementBridge.java b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGTextPathElementBridge.java index 56c244611..26f2e2f3a 100644 --- a/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGTextPathElementBridge.java +++ b/echosvg-bridge/src/main/java/io/sf/carte/echosvg/bridge/SVGTextPathElementBridge.java @@ -68,42 +68,58 @@ public void handleElement(BridgeContext ctx, Element e) { * @return The new TextPath. */ public TextPath createTextPath(BridgeContext ctx, Element textPathElement) { + Element pathElement; + // Check whether the textPath has a 'path' attribute + String path = textPathElement.getAttributeNS(null, SVG_PATH_ATTRIBUTE).trim(); + + if (path.isEmpty()) { + // get the referenced element + String uri = XLinkSupport.getXLinkHref(textPathElement); + pathElement = ctx.getReferencedElement(textPathElement, uri); + + if (pathElement == null || !SVG_NAMESPACE_URI.equals(pathElement.getNamespaceURI()) + || !pathElement.getLocalName().equals(SVG_PATH_TAG)) { + // couldn't find the referenced element + // or the referenced element was not a path + throw new BridgeException(ctx, textPathElement, ERR_URI_BAD_TARGET, + new Object[] { uri }); + } - // get the referenced element - String uri = XLinkSupport.getXLinkHref(textPathElement); - Element pathElement = ctx.getReferencedElement(textPathElement, uri); - - if ((pathElement == null) || (!SVG_NAMESPACE_URI.equals(pathElement.getNamespaceURI())) - || (!pathElement.getLocalName().equals(SVG_PATH_TAG))) { - // couldn't find the referenced element - // or the referenced element was not a path - throw new BridgeException(ctx, textPathElement, ERR_URI_BAD_TARGET, new Object[] { uri }); + // construct a shape for the referenced path element + path = pathElement.getAttributeNS(null, SVG_D_ATTRIBUTE); + if (path.isEmpty()) { + throw new BridgeException(ctx, pathElement, ERR_ATTRIBUTE_MISSING, + new Object[] { SVG_D_ATTRIBUTE }); + } + } else { + pathElement = textPathElement; } - // construct a shape for the referenced path element - String s = pathElement.getAttributeNS(null, SVG_D_ATTRIBUTE); Shape pathShape = null; - if (s.length() != 0) { - AWTPathProducer app = new AWTPathProducer(); - app.setWindingRule(CSSUtilities.convertFillRule(pathElement)); - try { - PathParser pathParser = new PathParser(app); - pathParser.parse(s); - } catch (ParseException pEx) { - throw new BridgeException(ctx, pathElement, pEx, ERR_ATTRIBUTE_VALUE_MALFORMED, - new Object[] { SVG_D_ATTRIBUTE }); - } finally { - pathShape = app.getShape(); + AWTPathProducer app = new AWTPathProducer(); + app.setWindingRule(CSSUtilities.convertFillRule(pathElement)); + try { + PathParser pathParser = new PathParser(app); + pathParser.parse(path); + } catch (ParseException pEx) { + final String attrName; + if (pathElement != textPathElement) { + attrName = SVG_D_ATTRIBUTE; + } else { + attrName = SVG_PATH_ATTRIBUTE; } - } else { - throw new BridgeException(ctx, pathElement, ERR_ATTRIBUTE_MISSING, new Object[] { SVG_D_ATTRIBUTE }); + throw new BridgeException(ctx, pathElement, pEx, ERR_ATTRIBUTE_VALUE_MALFORMED, + new Object[] { attrName }); + } finally { + pathShape = app.getShape(); } // if the reference path element has a transform apply the transform // to the path shape - s = pathElement.getAttributeNS(null, SVG_TRANSFORM_ATTRIBUTE); - if (s.length() != 0) { - AffineTransform tr = SVGUtilities.convertTransform(pathElement, SVG_TRANSFORM_ATTRIBUTE, s, ctx); + String s = pathElement.getAttributeNS(null, SVG_TRANSFORM_ATTRIBUTE); + if (!s.isEmpty()) { + AffineTransform tr = SVGUtilities.convertTransform(pathElement, SVG_TRANSFORM_ATTRIBUTE, + s, ctx); pathShape = tr.createTransformedShape(pathShape); } @@ -112,7 +128,7 @@ public TextPath createTextPath(BridgeContext ctx, Element textPathElement) { // set the start offset if specified s = textPathElement.getAttributeNS(null, SVG_START_OFFSET_ATTRIBUTE); - if (s.length() > 0) { + if (!s.isEmpty()) { float startOffset = 0; int percentIndex = s.indexOf('%'); if (percentIndex != -1) { 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 27d05f945..404fbb64f 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 @@ -914,7 +914,7 @@ public void testTextLength() throws TranscoderException, IOException { @Test public void testTextOnPath() throws TranscoderException, IOException { - test("samples/tests/spec/text/textOnPath.svg"); + testNV("samples/tests/spec/text/textOnPath.svg"); } @Test @@ -932,6 +932,26 @@ public void testTextOnPathSpaces() throws TranscoderException, IOException { test("samples/tests/spec/text/textOnPathSpaces.svg"); } + @Test + public void testTextOnPathPathAttr() throws TranscoderException, IOException { + testNV("samples/tests/spec/text/textOnPathPathAttr.svg"); + } + + @Test + public void testTextOnPathPathAttr2() throws TranscoderException, IOException { + testNV("samples/tests/spec/text/textOnPathPathAttr2.svg"); + } + + @Test + public void testTextOnPathPathAttr3() throws TranscoderException, IOException { + testNV("samples/tests/spec/text/textOnPathPathAttr3.svg"); + } + + @Test + public void testTextOnPathPathAttrSpaces() throws TranscoderException, IOException { + testNV("samples/tests/spec/text/textOnPathPathAttrSpaces.svg"); + } + @Test public void testTextPCDATA() throws TranscoderException, IOException { test("samples/tests/spec/text/textPCDATA.svg"); @@ -962,6 +982,11 @@ public void testVerticalTextOnPath() throws TranscoderException, IOException { test("samples/tests/spec/text/verticalTextOnPath.svg"); } + @Test + public void testVerticalTextOnPathPathAttr() throws TranscoderException, IOException { + testNV("samples/tests/spec/text/verticalTextOnPathPathAttr.svg"); + } + @Test public void testTextPosition() throws TranscoderException, IOException { test("samples/tests/spec/text/textPosition.svg"); 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 e98533d55..b8f95c672 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 @@ -945,7 +945,7 @@ public void testTextLength() throws TranscoderException, IOException { @Test public void testTextOnPath() throws TranscoderException, IOException { - test("samples/tests/spec/text/textOnPath.svg"); + testNV("samples/tests/spec/text/textOnPath.svg"); } @Test diff --git a/samples/tests/spec/text/textOnPath.svg b/samples/tests/spec/text/textOnPath.svg index 5939a8745..4fb22c2c2 100644 --- a/samples/tests/spec/text/textOnPath.svg +++ b/samples/tests/spec/text/textOnPath.svg @@ -1,6 +1,4 @@ - + + + + + + + + + + Text on a path test + + + + Text on a path test + + + + + + + + + + Text on a Path + + startOffset="0%" + text-anchor="start" + + + + + + Text on a Path + + startOffset="0%" + text-anchor="middle" + + + + + + Text on a Path + + startOffset="50%" + text-anchor="end" + + + + + + Text on a Path + + startOffset="50%" + text-anchor="start" + + + + + + Text on a Path + + startOffset="50%" + text-anchor="middle" + + + + + + Text on a Path + + startOffset="100%" + text-anchor="end" + + + + + + Text on a Path + + startOffset="35" + text-anchor="start" + + + + + + Text on a Path + + startOffset="35" + text-anchor="middle" + + + + + + Text on a Path + + startOffset="35" + text-anchor="end" + + + + + + super and subscripts + + baseline-shift="super" + and baseline-shift="sub" + + + + + + positive and negative + + baseline-shift="+/-20%" + + + + + + before path + on path after path + + + text before/after textPath + + + + + + + + + + diff --git a/samples/tests/spec/text/textOnPathPathAttr2.svg b/samples/tests/spec/text/textOnPathPathAttr2.svg new file mode 100644 index 000000000..6bd4face9 --- /dev/null +++ b/samples/tests/spec/text/textOnPathPathAttr2.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + Text on Path with trailing <tspan> + + Text on Path with trailing <tspan> + + + + Text with embedded textPath and multiple trailing tspan + + + + textPath has startOffset="10%" + + + + textPath with startOffset and text-anchor + + + + + + + This is simpletext on a pathwith nested tspan xxx all with different links. + + + + + This is simpletext on a pathwith nested tspan xxx all with different links. + + + + + This is simpletext on a pathwith nested tspan xxx all with different links. + + diff --git a/samples/tests/spec/text/textOnPathPathAttr3.svg b/samples/tests/spec/text/textOnPathPathAttr3.svg new file mode 100644 index 000000000..0dcc940c8 --- /dev/null +++ b/samples/tests/spec/text/textOnPathPathAttr3.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + Text on Path with for text with an 'x' + Text on Path with for text with an 'x' + + + + + + + + + Text with one textPath child and x="10" + + + Text on a path for text w/ 'x'. + + + + + + Text with one textPath child and x="100" + + + Text on a path for text w/ 'x'. + + + + + + Text with one textPath child and x="-100" y="10" + + + Text on a path for text w/ 'x'. + + diff --git a/samples/tests/spec/text/textOnPathPathAttrSpaces.svg b/samples/tests/spec/text/textOnPathPathAttrSpaces.svg new file mode 100644 index 000000000..2267fb616 --- /dev/null +++ b/samples/tests/spec/text/textOnPathPathAttrSpaces.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + Text on a path with spaces test + + + + Text on a path with adjusted spacing + + + + + + + + + + + + sample + + default spacing + + + + + + sample + + tspan x="10,30,50,75,95,110" + y="110" + + + + + + sample + + tspan dx="0,10,10,10,10,10" + + + + + + sample + + textLength="140" + + + + + + sample + + textLength="70" + + + + + + sample + + kerning="10" + + + + + + sample + + textLength="140" + lengthAdjust= + "spacingAndGlyphs" + + + + + + sample + + textLength="50" + lengthAdjust= + "spacingAndGlyphs" + + + + + + sample + + textLength="140", kerning="10" + lengthAdjust= + "spacingAndGlyphs" + + + + + + sample + + letter-spacing="-3" + + + + + + sample sample + + textLength="140" + word-spacing="2em" + + + + + + sample sample + + textLength="140" + word-spacing="-5" + lengthAdjust= + "spacingAndGlyphs" + + + + + + + + + + diff --git a/samples/tests/spec/text/verticalTextOnPathPathAttr.svg b/samples/tests/spec/text/verticalTextOnPathPathAttr.svg new file mode 100644 index 000000000..52c70e127 --- /dev/null +++ b/samples/tests/spec/text/verticalTextOnPathPathAttr.svg @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + Vertical text on a path test + + + + Vertical text on a path test + + + + + + + + + + Text on a Path + + startOffset="0%" + text-anchor="start" + glyph-orientation="auto" + + + + + + Text on a Path + + startOffset="0%" + text-anchor="middle" + glyph-orientation="0deg" + + + + + + Text on a Path + + startOffset="50%" + text-anchor="end" + glyph-orientation="180deg" + + + + + + Text on a Path + + startOffset="50%" + text-anchor="start" + glyph-orientation="270deg" + + + + + + Text on a Path + + startOffset="50%" + text-anchor="middle" + glyph-orientation="0deg" + + + + + + Text on a Path + + startOffset="100%" + text-anchor="end" + glyph-orientation="90deg" + + + + + + Text on a Path + + startOffset="35" + text-anchor="start" + glyph-orientation="180deg" + + + + + + Text on a Path + + startOffset="35" + text-anchor="middle" + + + + + + Text on a Path + + startOffset="35" + text-anchor="end" + glyph-orientation="90deg" + + + + + + super and subscripts + + baseline-shift="super" + and baseline-shift="sub" + glyph-orientation="auto" + + + + + + positive and negative + + baseline-shift="+/-20%" + glyph-orientation="auto" + + + + + + before path + on pathafter path + + + text before/after textPath + glyph-orientation="auto" + + + + + + + + + + diff --git a/test-references/samples/tests/spec/text/textOnPathPathAttr.png b/test-references/samples/tests/spec/text/textOnPathPathAttr.png new file mode 100644 index 000000000..e3cf8afc3 Binary files /dev/null and b/test-references/samples/tests/spec/text/textOnPathPathAttr.png differ diff --git a/test-references/samples/tests/spec/text/textOnPathPathAttr2.png b/test-references/samples/tests/spec/text/textOnPathPathAttr2.png new file mode 100644 index 000000000..203415b46 Binary files /dev/null and b/test-references/samples/tests/spec/text/textOnPathPathAttr2.png differ diff --git a/test-references/samples/tests/spec/text/textOnPathPathAttr3.png b/test-references/samples/tests/spec/text/textOnPathPathAttr3.png new file mode 100644 index 000000000..3572609ed Binary files /dev/null and b/test-references/samples/tests/spec/text/textOnPathPathAttr3.png differ diff --git a/test-references/samples/tests/spec/text/textOnPathPathAttrSpaces.png b/test-references/samples/tests/spec/text/textOnPathPathAttrSpaces.png new file mode 100644 index 000000000..6e9f31124 Binary files /dev/null and b/test-references/samples/tests/spec/text/textOnPathPathAttrSpaces.png differ diff --git a/test-references/samples/tests/spec/text/verticalTextOnPathPathAttr.png b/test-references/samples/tests/spec/text/verticalTextOnPathPathAttr.png new file mode 100644 index 000000000..5289e8e53 Binary files /dev/null and b/test-references/samples/tests/spec/text/verticalTextOnPathPathAttr.png differ