Skip to content

Commit

Permalink
transcoder: allow SVG embedded into XHTML in XMLAbstractTranscoder.
Browse files Browse the repository at this point in the history
Also, try to use a SVG 1.2 implementation when a 1.2 document is found in a DOM
document coming from a version 1.1 implementation.

Fixes #69
Fixes #68
  • Loading branch information
carlosame committed Dec 9, 2022
1 parent f0cd992 commit ffb1092
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,33 +64,36 @@ public void runTest(String uri, String parameter) throws MalformedURLException {
/**
* Gives a chance to the subclass to control the construction of the reference
* PNG file from the svgFile name The refImgURL is built as: getRefImagePrefix()
* + svgDir + getRefImageSuffix() + svgFile
* + svgDir + getRefImageSuffix() + svgFile + parameter + getImageSuffix() +
* PNG_EXTENSION
*/
@Override
protected String buildRefImgURL(String svgDir, String svgFile) {
return getRefImagePrefix() + svgDir + getRefImageSuffix() + svgFile + parameter + PNG_EXTENSION;
return getRefImagePrefix() + svgDir + getRefImageSuffix() + svgFile + parameter + getImageSuffix()
+ PNG_EXTENSION;
}

/**
* Gives a chance to the subclass to control the construction of the variation
* URL, which is built as: getVariationPrefix() + svgDir + getVariationSuffix()
* + svgFile + parameter + PNG_EXTENSION
* + svgFile + parameter + getImageSuffix() + PNG_EXTENSION
*/
@Override
public String[] buildVariationURLs(String svgDir, String svgFile) {
String[] platforms = getVariationPlatforms();
String[] urls = new String[platforms.length + 1];
urls[0] = getVariationPrefix() + svgDir + getVariationSuffix() + svgFile + parameter + PNG_EXTENSION;
urls[0] = getVariationPrefix() + svgDir + getVariationSuffix() + svgFile + parameter + getImageSuffix()
+ PNG_EXTENSION;
for (int i = 0; i < platforms.length; i++) {
urls[i + 1] = getVariationPrefix() + svgDir + getVariationSuffix() + svgFile + parameter + '_'
+ platforms[i] + PNG_EXTENSION;
urls[i + 1] = getVariationPrefix() + svgDir + getVariationSuffix() + svgFile + parameter + getImageSuffix()
+ '_' + platforms[i] + PNG_EXTENSION;
}
return urls;
}

@Override
protected String buildSaveVariationPath(String svgDir, String svgFile) {
return getSaveVariationPrefix() + svgDir + getSaveVariationSuffix() + svgFile + parameter;
return getSaveVariationPrefix() + svgDir + getSaveVariationSuffix() + svgFile + parameter + getImageSuffix();
}

/**
Expand All @@ -101,7 +104,7 @@ protected String buildSaveVariationPath(String svgDir, String svgFile) {
@Override
public String buildCandidateReferenceFile(String svgDir, String svgFile) {
return getCandidateReferencePrefix() + svgDir + getCandidateReferenceSuffix() + svgFile + parameter
+ PNG_EXTENSION;
+ getImageSuffix() + PNG_EXTENSION;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public abstract class PreconfiguredRenderingTest extends SVGRenderingAccuracyTes

public static final String SVG_EXTENSION = ".svg";
public static final String SVGZ_EXTENSION = ".svgz";
private static final String HTML_EXTENSION = ".html";
private static final String XHTML_EXTENSION = ".xhtml";

public static final char PATH_SEPARATOR = '/';

Expand Down Expand Up @@ -175,6 +177,10 @@ protected String[] breakSVGFile(String svgFile) {
ret[2] = SVG_EXTENSION;
} else if (svgFile.endsWith(SVGZ_EXTENSION)) {
ret[2] = SVGZ_EXTENSION;
} else if (svgFile.endsWith(HTML_EXTENSION)) {
ret[2] = HTML_EXTENSION;
} else if (svgFile.endsWith(XHTML_EXTENSION)) {
ret[2] = XHTML_EXTENSION;
} else {
throw new IllegalArgumentException(svgFile);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,11 @@ public void testStyleElement() throws TranscoderException, IOException {
test("samples/tests/spec/styling/styleElement.svg");
}

@Test
public void testXHTMLEmbed() throws TranscoderException, IOException {
testXHTML("samples/tests/spec/styling/css2.xhtml");
}

/*
* Text
*/
Expand Down Expand Up @@ -1521,6 +1526,24 @@ private void test(String file, boolean validating) throws TranscoderException, I
runner.runTest(0.00001f, 0.00001f);
}

/**
* Test the rendering of a SVG image inside an XHTML document.
*
* <p>
* A small percentage of different pixels is allowed during the comparison to a
* reference image.
* </p>
*
* @param file the HTML file to test.
* @throws TranscoderException
* @throws IOException
*/
private void testXHTML(String file) throws TranscoderException, IOException {
RenderingTest runner = new XHTMLRenderingAccuracyTest();
runner.setFile(file);
runner.runTest(0.000001f, 0.000001f);
}

/**
* Dynamic test of the rendering of a SVG file.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1437,9 +1437,6 @@ private void test(String file, String medium, boolean darkMode, boolean validati

private class BypassRenderingTest extends RenderingTest {

private static final String HTML_EXTENSION = ".html";
private static final String XHTML_EXTENSION = ".xhtml";

private final String medium;

/**
Expand Down Expand Up @@ -1488,43 +1485,6 @@ protected void encode(URL srcURL, FileOutputStream fos) throws TranscoderExcepti
helper.transcode(re, uri, dst, null);
}

@Override
protected String[] breakSVGFile(String svgFile) {
if (svgFile == null) {
throw new IllegalArgumentException(svgFile);
}

String[] ret = new String[3];

if (svgFile.endsWith(SVG_EXTENSION)) {
ret[2] = SVG_EXTENSION;
} else if (svgFile.endsWith(SVGZ_EXTENSION)) {
ret[2] = SVGZ_EXTENSION;
} else if (svgFile.endsWith(HTML_EXTENSION)) {
ret[2] = HTML_EXTENSION;
} else if (svgFile.endsWith(XHTML_EXTENSION)) {
ret[2] = XHTML_EXTENSION;
} else {
throw new IllegalArgumentException(svgFile);
}

svgFile = svgFile.substring(0, svgFile.length() - ret[2].length());

int fileNameStart = svgFile.lastIndexOf(PATH_SEPARATOR);
String svgDir = "";
if (fileNameStart != -1) {
if (svgFile.length() < fileNameStart + 2) {
// Nothing after PATH_SEPARATOR
throw new IllegalArgumentException(svgFile);
}
svgDir = svgFile.substring(0, fileNameStart + 1);
svgFile = svgFile.substring(fileNameStart + 1);
}
ret[0] = svgDir;
ret[1] = svgFile;
return ret;
}

protected String getImageSuffix() {
if (!DEFAULT_MEDIUM.equals(medium)) {
return '-' + medium + getDarkModeSuffix();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
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.test.svg;

import io.sf.carte.echosvg.transcoder.XMLAbstractTranscoder;
import io.sf.carte.echosvg.transcoder.image.ImageTranscoder;

/**
* Checks for regressions in rendering of SVG inside an XHTML document.
*
* @author See Git history.
* @version $Id$
*/
public class XHTMLRenderingAccuracyTest extends RenderingTest {

public XHTMLRenderingAccuracyTest() {
super();
setValidating(false);
}

/**
* Returns the <code>ImageTranscoder</code> the Test should use
*/
@Override
ImageTranscoder getTestImageTranscoder() {
ImageTranscoder t = super.getTestImageTranscoder();
t.addTranscodingHint(XMLAbstractTranscoder.KEY_DOCUMENT_ELEMENT_NAMESPACE_URI,
"http://www.w3.org/1999/xhtml");
t.addTranscodingHint(XMLAbstractTranscoder.KEY_DOCUMENT_ELEMENT, "html");
return t;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,17 @@
import java.util.Collections;
import java.util.List;

import org.w3c.dom.Attr;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.svg.SVGSVGElement;

import io.sf.carte.echosvg.anim.dom.SAXSVGDocumentFactory;
import io.sf.carte.echosvg.anim.dom.SVG12DOMImplementation;
import io.sf.carte.echosvg.anim.dom.SVGDOMImplementation;
import io.sf.carte.echosvg.anim.dom.SVGOMDocument;
import io.sf.carte.echosvg.bridge.BaseScriptingEnvironment;
Expand All @@ -48,7 +54,6 @@
import io.sf.carte.echosvg.bridge.UserAgentAdapter;
import io.sf.carte.echosvg.bridge.ViewBox;
import io.sf.carte.echosvg.bridge.svg12.SVG12BridgeContext;
import io.sf.carte.echosvg.dom.util.DOMUtilities;
import io.sf.carte.echosvg.dom.util.DocumentFactory;
import io.sf.carte.echosvg.gvt.CanvasGraphicsNode;
import io.sf.carte.echosvg.gvt.CompositeGraphicsNode;
Expand Down Expand Up @@ -121,7 +126,6 @@ protected SVGAbstractTranscoder() {

hints.put(KEY_DOCUMENT_ELEMENT_NAMESPACE_URI, SVGConstants.SVG_NAMESPACE_URI);
hints.put(KEY_DOCUMENT_ELEMENT, SVGConstants.SVG_SVG_TAG);
hints.put(KEY_DOM_IMPLEMENTATION, SVGDOMImplementation.getDOMImplementation());
hints.put(KEY_MEDIA, "screen");
hints.put(KEY_DEFAULT_FONT_FAMILY, DEFAULT_DEFAULT_FONT_FAMILY);
hints.put(KEY_EXECUTE_ONLOAD, Boolean.FALSE);
Expand Down Expand Up @@ -200,31 +204,26 @@ public void transcode(TranscoderInput input, TranscoderOutput output) throws Tra
/**
* Transcodes the specified Document as an image in the specified output.
*
* @param document the document to transcode
* @param uri the uri of the document or null if any
* @param output the ouput where to transcode
* @param document the document to transcode. Cannot be {@code null}.
* @param uri the uri of the document or {@code null} if any.
* @param output the ouput where to transcode.
* @exception TranscoderException if an error occured while transcoding
*/
@Override
protected void transcode(Document document, String uri, TranscoderOutput output) throws TranscoderException {

if ((document != null) && !(document.getImplementation() instanceof SVGDOMImplementation)) {
DOMImplementation impl;
impl = (DOMImplementation) hints.get(KEY_DOM_IMPLEMENTATION);
// impl = SVGDOMImplementation.getDOMImplementation();
document = DOMUtilities.deepCloneDocument(document, impl);
if (uri != null) {
ParsedURL url = new ParsedURL(uri);
((SVGOMDocument) document).setParsedURL(url);
}
SVGOMDocument svgDoc;
// document is assumed to be non-null here
if (!(document instanceof SVGOMDocument)) {
svgDoc = getSVGDocument(document, uri);
} else {
svgDoc = (SVGOMDocument) document;
}

if (hints.containsKey(KEY_WIDTH))
width = (Float) hints.get(KEY_WIDTH);
if (hints.containsKey(KEY_HEIGHT))
height = (Float) hints.get(KEY_HEIGHT);

SVGOMDocument svgDoc = (SVGOMDocument) document;
SVGSVGElement root = svgDoc.getRootElement();
ctx = createBridgeContext(svgDoc);

Expand Down Expand Up @@ -321,6 +320,85 @@ protected void transcode(Document document, String uri, TranscoderOutput output)
this.root = gvtRoot;
}

private SVGOMDocument getSVGDocument(Document document, String uri) {
// Obtain the document element and DocumentType
DocumentType docType;
Element docElm = document.getDocumentElement();
// Check whether the document element is a SVG element anyway
if (docElm.getNamespaceURI() != SVGDOMImplementation.SVG_NAMESPACE_URI
&& !"SVG".equalsIgnoreCase(docElm.getTagName())) {
// Not a SVG document, get the first SVG element
docElm = (Element) document.getElementsByTagNameNS("*", SVGConstants.SVG_SVG_TAG).item(0);
docType = null;
} else {
docType = document.getDoctype();
}

// Obtain a DOM implementation
DOMImplementation impl;
impl = (DOMImplementation) hints.get(KEY_DOM_IMPLEMENTATION);
if (impl == null) {
// Look for the version attribute of the root element
if ("1.2".equals(docElm.getAttribute(SVGConstants.SVG_VERSION_ATTRIBUTE))) {
impl = SVG12DOMImplementation.getDOMImplementation();
} else {
impl = SVGDOMImplementation.getDOMImplementation();
}
}

// Clone the document
SVGOMDocument svgDocument = (SVGOMDocument) deepCloneDocument(document, docElm, docType, impl);

if (uri != null) {
ParsedURL url = new ParsedURL(uri);
svgDocument.setParsedURL(url);
}

return svgDocument;
}

private static Document deepCloneDocument(Document doc, Element root, DocumentType docType,
DOMImplementation impl) {
Document result = impl.createDocument(root.getNamespaceURI(), root.getNodeName(), docType);
Element rroot = result.getDocumentElement();

// Let's see if the designed root node is also the origin's document root
boolean before = root.getParentNode().getNodeType() == Node.DOCUMENT_NODE;
Node firstNode;
if (before) {
firstNode = doc.getFirstChild();
} else {
firstNode = root;
}

for (Node n = firstNode; n != null; n = n.getNextSibling()) {
if (n == root) {
before = false;
if (root.hasAttributes()) {
NamedNodeMap attr = root.getAttributes();
int len = attr.getLength();
for (int i = 0; i < len; i++) {
rroot.setAttributeNode((Attr) result.importNode(attr.item(i), true));
}
}
for (Node c = root.getFirstChild(); c != null; c = c.getNextSibling()) {
rroot.appendChild(result.importNode(c, true));
}
} else {
short type = n.getNodeType();
if (type == Node.COMMENT_NODE || type == Node.PROCESSING_INSTRUCTION_NODE) {
if (before) {
result.insertBefore(result.importNode(n, true), rroot);
} else {
result.appendChild(result.importNode(n, true));
}
}
}
}

return result;
}

protected CanvasGraphicsNode getCanvasGraphicsNode(GraphicsNode gn) {
if (!(gn instanceof CompositeGraphicsNode))
return null;
Expand Down
Loading

0 comments on commit ffb1092

Please sign in to comment.