[FEAT] use Maven a build system root tool

This commit is contained in:
Edouard DUPIN 2024-06-07 09:28:33 +02:00
parent 563cb8957f
commit eb4570dd72
35 changed files with 2380 additions and 1658 deletions

View File

@ -1,37 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="resources"/>
<classpathentry kind="src" path="src"/>
<classpathentry including="**/*.java" kind="src" output="out/eclipse/classes-test" path="test/src">
<classpathentry including="**/*.java" kind="src" output="out/maven/classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="out/maven/test-classes" path="test/src">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-14">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21">
<attributes>
<attribute name="module" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5">
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="test" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-png-encoder">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-exml">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-egami">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="out/eclipse/classes"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
<classpathentry kind="output" path="out/maven/classes"/>
</classpath>

View File

@ -1,35 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>atriasoft-esvg</name>
<name>esvg</name>
<comment></comment>
<projects>
<project>atriasoft-esvg</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
<filteredResources>
<filter>
<id>1646149232192</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -38,7 +38,7 @@ def configure(target, my_module):
'src/org/atriasoft/esvg/Base.java',
'src/org/atriasoft/esvg/Text.java',
'src/org/atriasoft/esvg/RadialGradient.java',
'src/org/atriasoft/esvg/internal/Log.java',
'src/org/atriasoft/esvg/internal/LOGGER.java',
'src/org/atriasoft/esvg/font/Kerning.java',
'src/org/atriasoft/esvg/font/Glyph.java',
'src/org/atriasoft/esvg/Renderer.java',

144
pom.xml Normal file
View File

@ -0,0 +1,144 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.atriasoft</groupId>
<artifactId>esvg</artifactId>
<version>0.1.0</version>
<properties>
<maven.compiler.version>3.13.0</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.dependency.version>3.1.1</maven.dependency.version>
</properties>
<repositories>
<repository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/org.atriasoft/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/org.atriasoft/maven</url>
</repository>
<snapshotRepository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/org.atriasoft/maven</url>
</snapshotRepository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>org.atriasoft</groupId>
<artifactId>exml</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.atriasoft</groupId>
<artifactId>png-encoder</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.atriasoft</groupId>
<artifactId>egami</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.0-M2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0-M2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>${basedir}/resources</directory>
</resource>
</resources>
<testSourceDirectory>test/src</testSourceDirectory>
<directory>${project.basedir}/out/maven/</directory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<!--<encoding>${project.build.sourceEncoding}</encoding>-->
</configuration>
</plugin>
<!-- Create the source bundle -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- junit results -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- Java-doc generation for stand-alone site -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
</configuration>
</plugin>
</plugins>
</build>
<!-- Generate Java-docs As Part Of Project Reports -->
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<show>public</show>
</configuration>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -6,8 +6,7 @@ open module org.atriasoft.esvg {
exports org.atriasoft.esvg;
exports org.atriasoft.esvg.font;
exports org.atriasoft.esvg.render;
requires transitive org.atriasoft.reggol;
requires transitive org.atriasoft.etk;
requires transitive org.atriasoft.exml;
requires org.atriasoft.pngencoder;

View File

@ -2,7 +2,6 @@ package org.atriasoft.esvg;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.Color;
import org.atriasoft.etk.Distance;
import org.atriasoft.etk.math.FMath;
@ -11,6 +10,8 @@ import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.etk.util.Pair;
import org.atriasoft.exml.model.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -19,6 +20,7 @@ import org.atriasoft.exml.model.XmlElement;
*/
public class Base {
static final Logger LOGGER = LoggerFactory.getLogger(Base.class);
public static float kappa90 = 0.5522847493f; //!< proportional lenght to the radius of a bezier handle for 90° arcs.
@ -30,7 +32,7 @@ public class Base {
}
posStart += base.length();
if (value.length() < posStart + 2) {
Log.error("Not enought spece in the String to have transform value for ' (' or '()' in '" + value + "'");
LOGGER.error("Not enought spece in the String to have transform value for ' (' or '()' in '" + value + "'");
return "";
}
if (value.charAt(posStart) == '(') {
@ -39,19 +41,19 @@ public class Base {
} else if (value.charAt(posStart) == ' ' && value.charAt(posStart + 1) == '(') {
posStart += 2;
} else {
Log.error("Can not indexOf ' (' or '(' in '" + value.substring(posStart) + "' for '" + value + "'");
LOGGER.error("Can not indexOf ' (' or '(' in '" + value.substring(posStart) + "' for '" + value + "'");
return "";
}
if (value.length() < posStart + 1) {
Log.error("Not enought spece in the String to have transform value for ')' in '" + value + "'");
LOGGER.error("Not enought spece in the String to have transform value for ')' in '" + value + "'");
return "";
}
int posEnd = value.indexOf(')', posStart);
final int posEnd = value.indexOf(')', posStart);
if (posEnd == -1) {
Log.error("Missing element ')' in '" + value + "' for " + base);
LOGGER.error("Missing element ')' in '" + value + "' for " + base);
return "";
}
Log.verbose("indexOf : '" + value.substring(posStart, posEnd) + "' for " + base);
LOGGER.trace("indexOf : '" + value.substring(posStart, posEnd) + "' for " + base);
return value.substring(posStart, posEnd);
}
@ -83,10 +85,14 @@ public class Base {
* @param level Level of the tree
*/
void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.warning(spacingDist(level) + "DRAW esvg::Base ... ==> No drawing availlable");
LOGGER.warn(spacingDist(level) + "DRAW esvg::Base ... ==> No drawing availlable");
}
public void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans) {
public void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans) {
drawShapePoints(out, recurtionMax, threshold, basicTrans, 1);
}
@ -95,10 +101,15 @@ public class Base {
* @param out where the lines are added
* @param recurtionMax interpolation recurtion max
* @param threshold threshold to stop recurtion
* @param basicTrans Parant transformation of the environement
* @param basicTrans Parant transformation of the environement
* @param level Level of the tree
*/
void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
}
@ -117,22 +128,24 @@ public class Base {
*/
Pair<Color, String> parseColor(final String inputData) {
Pair<Color, String> localColor = new Pair<>(Color.WHITE, "");
if (inputData.length() > 4 && inputData.charAt(0) == 'u' && inputData.charAt(1) == 'r' && inputData.charAt(2) == 'l' && inputData.charAt(3) == '(') {
if (inputData.length() > 4 && inputData.charAt(0) == 'u' && inputData.charAt(1) == 'r'
&& inputData.charAt(2) == 'l' && inputData.charAt(3) == '(') {
if (inputData.charAt(4) == '#') {
String color = inputData.substring(5, inputData.length() - 1);
final String color = inputData.substring(5, inputData.length() - 1);
localColor = new Pair<>(Color.NONE, color);
} else {
Log.error("Problem in parsing the color : '" + inputData + "' == > url(XXX) is not supported now ...");
LOGGER.error(
"Problem in parsing the color : '" + inputData + "' == > url(XXX) is not supported now ...");
}
} else {
try {
localColor = new Pair<>(Color.valueOf256(inputData), "");
} catch (Exception e) {
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Log.verbose("Parse color : \"" + inputData + "\" == > " + localColor.first + " " + localColor.second);
LOGGER.trace("Parse color : \"" + inputData + "\" == > " + localColor.first + " " + localColor.second);
return localColor;
}
@ -142,31 +155,21 @@ public class Base {
* @return standard number of pixels
*/
float parseLength(final String dataInput) {
Pair<Float, Distance> value = parseLength2(dataInput);
Log.verbose(" lenght : '" + value.first + "' => unit=" + value.second);
float fontsize = 20.0f;
switch (value.second) {
case POURCENT:
return value.first;// / 100.0 * this.paint.viewPort.x();
case ELEMENT:
return value.first * fontsize;
case EX:
return value.first / 2.0f * fontsize;
case PIXEL:
return value.first;
case POINT:
return value.first * 1.25f;
case PC:
return value.first * 15.0f;
case MILLIMETER:
return value.first * 3.543307f;
case CENTIMETER:
return value.first * 35.43307f;
case INCH:
return value.first * 90.0f;
default:
return 0.0f;
}
final Pair<Float, Distance> value = parseLength2(dataInput);
LOGGER.trace(" lenght : '" + value.first + "' => unit=" + value.second);
final float fontsize = 20.0f;
return switch (value.second) {
case POURCENT -> value.first; // / 100.0 * this.paint.viewPort.x();
case ELEMENT -> value.first * fontsize;
case EX -> value.first / 2.0f * fontsize;
case PIXEL -> value.first;
case POINT -> value.first * 1.25f;
case PC -> value.first * 15.0f;
case MILLIMETER -> value.first * 3.543307f;
case CENTIMETER -> value.first * 35.43307f;
case INCH -> value.first * 90.0f;
default -> 0.0f;
};
}
Pair<Float, Distance> parseLength2(String config) {
@ -235,7 +238,7 @@ public class Base {
} else {
if (content.length() != 0) {
this.paint.stroke = parseColor(content);
Log.error("Parse color : " + this.paint.stroke);
LOGGER.error("Parse color : " + this.paint.stroke);
}
content = element.getAttribute("stroke-width", "");
if (content.length() != 0) {
@ -253,7 +256,7 @@ public class Base {
if (content.equals("none")) {
// OK, Nothing to do ...
} else {
Log.todo(" 'stroke-dasharray' not implemented ...");
LOGGER.info("TODO 'stroke-dasharray' not implemented ...");
}
}
content = element.getAttribute("stroke-linecap", "");
@ -266,7 +269,7 @@ public class Base {
this.paint.lineCap = CapMode.SQUARE;
} else {
this.paint.lineCap = CapMode.BUTT;
Log.error("not know stroke-linecap value : '" + content + "', not in [butt,round,square]");
LOGGER.error("not know stroke-linecap value : '" + content + "', not in [butt,round,square]");
}
}
content = element.getAttribute("stroke-linejoin", "");
@ -279,12 +282,12 @@ public class Base {
this.paint.lineJoin = JoinMode.BEVEL;
} else {
this.paint.lineJoin = JoinMode.MITER;
Log.error("not know stroke-linejoin value : '" + content + "', not in [miter,round,bevel]");
LOGGER.error("not know stroke-linejoin value : '" + content + "', not in [miter,round,bevel]");
}
}
content = element.getAttribute("stroke-miterlimit", "");
if (content.length() != 0) {
float tmp = parseLength(content);
final float tmp = parseLength(content);
this.paint.miterLimit = FMath.max(0.0f, tmp);
}
}
@ -309,7 +312,7 @@ public class Base {
} else if (content.equals("evenodd")) {
this.paint.flagEvenOdd = true;
} else {
Log.error("not know fill-rule value : \"" + content + "\", not in [nonzero,evenodd]");
LOGGER.error("not know fill-rule value : \"" + content + "\", not in [nonzero,evenodd]");
}
}
// ---------------- opacity ----------------
@ -329,51 +332,56 @@ public class Base {
if (inputString.length() == 0) {
return;
}
Log.verbose("indexOf transform : '" + inputString + "'");
LOGGER.trace("indexOf transform : '" + inputString + "'");
inputString = inputString.replace(',', ' ');
Log.verbose("indexOf transform : '" + inputString + "'");
LOGGER.trace("indexOf transform : '" + inputString + "'");
// need to indexOf elements in order ...
String data = Base.extractTransformData(inputString, "matrix");
if (data.length() != 0) {
double[] matrix = FMath.getTableDouble(data, " ", 6);
final double[] matrix = FMath.getTableDouble(data, " ", 6);
if (matrix != null) {
this.transformMatrix = new Matrix2x3f(matrix);
// indexOf a matrix : simply exit ...
return;
}
Log.error("Parsing matrix() with wrong data ... '" + data + "'");
LOGGER.error("Parsing matrix() with wrong data ... '" + data + "'");
}
data = Base.extractTransformData(inputString, "translate");
if (data.length() != 0) {
float[] elements = FMath.getTableFloat(data, " ", 2);
final float[] elements = FMath.getTableFloat(data, " ", 2);
if (elements != null) {
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createTranslate(new Vector2f(elements[0], elements[1])));
Log.verbose("Translate : " + elements[0] + ", " + elements[1]);
this.transformMatrix = this.transformMatrix
.multiply(Matrix2x3f.createTranslate(new Vector2f(elements[0], elements[1])));
LOGGER.trace("Translate : " + elements[0] + ", " + elements[1]);
} else {
float elem = Float.parseFloat(data);
final float elem = Float.parseFloat(data);
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createTranslate(new Vector2f(elem, 0)));
}
}
data = Base.extractTransformData(inputString, "scale");
if (data.length() != 0) {
float[] elements = FMath.getTableFloat(data, " ", 2);
final float[] elements = FMath.getTableFloat(data, " ", 2);
if (elements != null) {
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createScale(new Vector2f(elements[0], elements[1])));
Log.verbose("Translate : " + elements[0] + ", " + elements[1]);
this.transformMatrix = this.transformMatrix
.multiply(Matrix2x3f.createScale(new Vector2f(elements[0], elements[1])));
LOGGER.trace("Translate : " + elements[0] + ", " + elements[1]);
} else {
float elem = Float.parseFloat(data);
final float elem = Float.parseFloat(data);
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createScale(elem));
}
}
data = Base.extractTransformData(inputString, "rotate");
if (data.length() != 0) {
float[] elements = FMath.getTableFloat(data, " ", 3);
final float[] elements = FMath.getTableFloat(data, " ", 3);
if (elements != null) {
float angle = (float) Math.toRadians(elements[0]);
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createTranslate(new Vector2f(-elements[1], -elements[2])));
final float angle = (float) Math.toRadians(elements[0]);
this.transformMatrix = this.transformMatrix
.multiply(Matrix2x3f.createTranslate(new Vector2f(-elements[1], -elements[2])));
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createRotate(angle));
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createTranslate(new Vector2f(elements[1], elements[2])));
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createScale(new Vector2f(elements[0], elements[1])));
this.transformMatrix = this.transformMatrix
.multiply(Matrix2x3f.createTranslate(new Vector2f(elements[1], elements[2])));
this.transformMatrix = this.transformMatrix
.multiply(Matrix2x3f.createScale(new Vector2f(elements[0], elements[1])));
} else {
float elem = Float.parseFloat(data);
elem = (float) Math.toRadians(elem);
@ -455,7 +463,7 @@ public class Base {
}
protected String spacingDist(final int spacing) {
StringBuilder out = new StringBuilder();
final StringBuilder out = new StringBuilder();
for (int iii = 0; iii < spacing; iii++) {
out.append(" ");
}

View File

@ -3,7 +3,6 @@ package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.DynamicColor;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.Point;
@ -14,6 +13,8 @@ import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -22,55 +23,61 @@ import org.atriasoft.exml.model.XmlElement;
*/
public class Circle extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Circle.class);
private Vector2f position; //!< Position of the Circle
private float radius; //!< Radius of the Circle
public Circle(final PaintState parentPaintState) {
super(parentPaintState);
}
public Circle(final Vector2f position, final float radius, final PaintState parentPaintState) {
super(parentPaintState);
this.position = position;
this.radius = radius;
}
private PathModel createPath() {
PathModel out = new PathModel();
final PathModel out = new PathModel();
out.moveTo(false, this.position.addX(this.radius));
out.curveTo(false, this.position.add(this.radius, this.radius * Base.kappa90), this.position.add(this.radius * Base.kappa90, this.radius), this.position.addY(this.radius));
out.curveTo(false, this.position.add(-this.radius * Base.kappa90, this.radius), this.position.add(-this.radius, this.radius * Base.kappa90), this.position.lessX(this.radius));
out.curveTo(false, this.position.add(-this.radius, -this.radius * Base.kappa90), this.position.add(-this.radius * Base.kappa90, -this.radius), this.position.lessY(this.radius));
out.curveTo(false, this.position.add(this.radius * Base.kappa90, -this.radius), this.position.add(this.radius, -this.radius * Base.kappa90), this.position.addX(this.radius));
out.curveTo(false, this.position.add(this.radius, this.radius * Base.kappa90),
this.position.add(this.radius * Base.kappa90, this.radius), this.position.addY(this.radius));
out.curveTo(false, this.position.add(-this.radius * Base.kappa90, this.radius),
this.position.add(-this.radius, this.radius * Base.kappa90), this.position.lessX(this.radius));
out.curveTo(false, this.position.add(-this.radius, -this.radius * Base.kappa90),
this.position.add(-this.radius * Base.kappa90, -this.radius), this.position.lessY(this.radius));
out.curveTo(false, this.position.add(this.radius * Base.kappa90, -this.radius),
this.position.add(this.radius, -this.radius * Base.kappa90), this.position.addX(this.radius));
out.close();
return out;
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "Circle " + this.position + " radius=" + this.radius);
LOGGER.debug(spacingDist(spacing) + "Circle " + this.position + " radius=" + this.radius);
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::Circle");
LOGGER.trace(spacingDist(level) + "DRAW esvg::Circle");
if (this.radius <= 0.0f) {
Log.verbose(spacingDist(level + 1) + "Too small radius" + this.radius);
LOGGER.trace(spacingDist(level + 1) + "Too small radius" + this.radius);
return;
}
PathModel listElement = createPath();
final PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix;
mtx = mtx.multiply(basicTrans);
PointList listPoints = new PointList();
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(),
myRenderer.getInterpolationThreshold());
//listPoints.applyMatrix(mtx);
SegmentList listSegmentFill = new SegmentList();
SegmentList listSegmentStroke = new SegmentList();
Weight tmpFill = new Weight();
Weight tmpStroke = new Weight();
DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
final SegmentList listSegmentFill = new SegmentList();
final SegmentList listSegmentStroke = new SegmentList();
final Weight tmpFill = new Weight();
final Weight tmpStroke = new Weight();
final DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
DynamicColor colorStroke = null;
if (this.paint.strokeWidth > 0.0f) {
colorStroke = DynamicColor.createColor(this.paint.stroke, mtx);
@ -85,7 +92,8 @@ public class Circle extends Base {
}
// check if we need to display stroke:
if (colorStroke != null) {
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap,
this.paint.lineJoin, this.paint.miterLimit);
colorStroke.setViewPort(listSegmentStroke.getViewPort());
listSegmentStroke.applyMatrix(mtx);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
@ -96,25 +104,30 @@ public class Circle extends Base {
//myRenderer.addDebugSegment(listSegmentFill);
//myRenderer.addDebugSegment(listSegmentStroke);
}
@Override
public void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW Shape esvg::Circle");
PathModel listElement = createPath();
public void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW Shape esvg::Circle");
final PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix;
mtx = mtx.multiply(basicTrans);
PointList listPoints = new PointList();
listPoints = listElement.generateListPoints(level, recurtionMax, threshold);
listPoints.applyMatrix(mtx);
for (List<Point> it : listPoints.data) {
List<Vector2f> listPoint = new ArrayList<>();
for (Point itDot : it) {
for (final List<Point> it : listPoints.data) {
final List<Vector2f> listPoint = new ArrayList<>();
for (final Point itDot : it) {
listPoint.add(itDot.pos);
}
out.add(listPoint);
}
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
this.radius = 0.0f;
@ -124,10 +137,10 @@ public class Circle extends Base {
}
parseTransform(element);
parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
String content = element.getAttribute("cx", "");
if (content.length() != 0) {
this.position = this.position.withX(parseLength(content));
@ -138,13 +151,13 @@ public class Circle extends Base {
}
content = element.getAttribute("r", "");
if (content.length() == 0) {
Log.error("Circle \"r\" is not present");
LOGGER.error("Circle \"r\" is not present");
return false;
}
this.radius = parseLength(content);
if (0 > this.radius) {
this.radius = 0;
Log.error("Circle \"r\" is negative");
LOGGER.error("Circle \"r\" is negative");
return false;
}
sizeMax.value = new Vector2f(this.position.x() + this.radius, this.position.y() + this.radius);

View File

@ -2,7 +2,7 @@ package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.DynamicColor;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.Point;
@ -13,6 +13,8 @@ import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -21,55 +23,61 @@ import org.atriasoft.exml.model.XmlElement;
*/
public class Ellipse extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Ellipse.class);
private Vector2f c; //!< Center property of the ellipse
private Vector2f r; //!< Radius property of the ellipse
public Ellipse(final PaintState parentPaintState) {
super(parentPaintState);
}
public Ellipse(final Vector2f center, final Vector2f radius, final PaintState parentPaintState) {
super(parentPaintState);
this.c = center;
this.r = radius;
}
PathModel createPath() {
PathModel out = new PathModel();
final PathModel out = new PathModel();
out.moveTo(false, this.c.add(this.r.x(), 0.0f));
out.curveTo(false, this.c.add(this.r.x(), this.r.y() * Base.kappa90), this.c.add(this.r.x() * Base.kappa90, this.r.y()), this.c.add(0.0f, this.r.y()));
out.curveTo(false, this.c.add(-this.r.x() * Base.kappa90, this.r.y()), this.c.add(-this.r.x(), this.r.y() * Base.kappa90), this.c.add(-this.r.x(), 0.0f));
out.curveTo(false, this.c.add(-this.r.x(), -this.r.y() * Base.kappa90), this.c.add(-this.r.x() * Base.kappa90, -this.r.y()), this.c.add(0.0f, -this.r.y()));
out.curveTo(false, this.c.add(this.r.x() * Base.kappa90, -this.r.y()), this.c.add(this.r.x(), -this.r.y() * Base.kappa90), this.c.add(this.r.x(), 0.0f));
out.curveTo(false, this.c.add(this.r.x(), this.r.y() * Base.kappa90),
this.c.add(this.r.x() * Base.kappa90, this.r.y()), this.c.add(0.0f, this.r.y()));
out.curveTo(false, this.c.add(-this.r.x() * Base.kappa90, this.r.y()),
this.c.add(-this.r.x(), this.r.y() * Base.kappa90), this.c.add(-this.r.x(), 0.0f));
out.curveTo(false, this.c.add(-this.r.x(), -this.r.y() * Base.kappa90),
this.c.add(-this.r.x() * Base.kappa90, -this.r.y()), this.c.add(0.0f, -this.r.y()));
out.curveTo(false, this.c.add(this.r.x() * Base.kappa90, -this.r.y()),
this.c.add(this.r.x(), -this.r.y() * Base.kappa90), this.c.add(this.r.x(), 0.0f));
out.close();
return out;
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "Ellipse c=" + this.c + " r=" + this.r);
LOGGER.debug(spacingDist(spacing) + "Ellipse c=" + this.c + " r=" + this.r);
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::Ellipse");
LOGGER.trace(spacingDist(level) + "DRAW esvg::Ellipse");
if (this.r.x() <= 0.0f || this.r.y() <= 0.0f) {
Log.verbose(spacingDist(level + 1) + "Too small radius" + this.r);
LOGGER.trace(spacingDist(level + 1) + "Too small radius" + this.r);
return;
}
PathModel listElement = createPath();
final PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix;
mtx = mtx.multiply(basicTrans);
PointList listPoints = new PointList();
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(),
myRenderer.getInterpolationThreshold());
//listPoints.applyMatrix(mtx);
SegmentList listSegmentFill = new SegmentList();
SegmentList listSegmentStroke = new SegmentList();
Weight tmpFill = new Weight();
Weight tmpStroke = new Weight();
DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
final SegmentList listSegmentFill = new SegmentList();
final SegmentList listSegmentStroke = new SegmentList();
final Weight tmpFill = new Weight();
final Weight tmpStroke = new Weight();
final DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
DynamicColor colorStroke = null;
if (this.paint.strokeWidth > 0.0f) {
colorStroke = DynamicColor.createColor(this.paint.stroke, mtx);
@ -84,7 +92,8 @@ public class Ellipse extends Base {
}
// check if we need to display stroke:
if (colorStroke != null) {
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap,
this.paint.lineJoin, this.paint.miterLimit);
colorStroke.setViewPort(listSegmentStroke.getViewPort());
listSegmentStroke.applyMatrix(mtx);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
@ -95,25 +104,30 @@ public class Ellipse extends Base {
//myRenderer.addDebugSegment(listSegmentFill);
//myRenderer.addDebugSegment(listSegmentStroke)
}
@Override
public void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW Shape esvg::Ellipse");
PathModel listElement = createPath();
public void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW Shape esvg::Ellipse");
final PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix;
mtx = mtx.multiply(basicTrans);
PointList listPoints;
listPoints = listElement.generateListPoints(level, recurtionMax, threshold);
listPoints.applyMatrix(mtx);
for (List<Point> it : listPoints.data) {
List<Vector2f> listPoint = new ArrayList<>();
for (Point itDot : it) {
for (final List<Point> it : listPoints.data) {
final List<Vector2f> listPoint = new ArrayList<>();
for (final Point itDot : it) {
listPoint.add(itDot.pos);
}
out.add(listPoint);
}
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
if (element == null) {
@ -121,13 +135,13 @@ public class Ellipse extends Base {
}
parseTransform(element);
parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
this.c = Vector2f.ZERO;
this.r = Vector2f.ZERO;
String content = element.getAttribute("cx", "");
if (content.length() != 0) {
this.c = this.c.withX(parseLength(content));
@ -138,18 +152,18 @@ public class Ellipse extends Base {
}
content = element.getAttribute("rx", "");
if (content.length() == 0) {
Log.error("Ellipse \"rx\" is not present");
LOGGER.error("Ellipse \"rx\" is not present");
return false;
}
this.r = this.r.withX(parseLength(content));
content = element.getAttribute("ry", "");
if (content.length() == 0) {
Log.error("Ellipse \"ry\" is not present");
LOGGER.error("Ellipse \"ry\" is not present");
return false;
}
this.r = this.r.withY(parseLength(content));
sizeMax.value = new Vector2f(this.c.x() + this.r.x(), this.c.y() + this.r.y());
return true;
}
}

View File

@ -5,7 +5,6 @@ import java.util.List;
import org.atriasoft.aknot.exception.AknotException;
import org.atriasoft.egami.ImageFloatRGBA;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.Uri;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
@ -16,8 +15,11 @@ import org.atriasoft.exml.exception.ExmlException;
import org.atriasoft.exml.exception.ExmlNodeDoesNotExist;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EsvgDocument extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(EsvgDocument.class);
private boolean loadOK = false;
private final List<Base> refList = new ArrayList<>();
private Vector2f size = Vector2f.ZERO;
@ -25,19 +27,19 @@ public class EsvgDocument extends Base {
private String title = ""; //!< sub-element list
private Uri uri = null; //!< reference elements ...
private String version = "0.0";
public EsvgDocument() {
}
public EsvgDocument(final Vector2i size) {
this.size = new Vector2f(size.x(), size.y());
}
public void addElement(final Base elem) {
this.subElementList.add(elem);
}
/**
* change all style in a xml atribute
*/
@ -55,7 +57,7 @@ public class EsvgDocument extends Base {
for (final String it1 : listStyle) {
final String[] value = it1.split(":");
if (value.length != 2) {
Log.error("parsing style with a wrong patern : " + it1 + " missing ':'");
LOGGER.error("parsing style with a wrong patern : " + it1 + " missing ':'");
continue;
}
// TODO Check if the attibute already exist ...
@ -70,7 +72,7 @@ public class EsvgDocument extends Base {
}
return true;
}
public void clear() {
this.uri = null;
this.version = "0.0";
@ -78,54 +80,59 @@ public class EsvgDocument extends Base {
this.paint.clear();
this.size = Vector2f.ZERO;
}
/**
* Display all the node in the svg file.
*/
public void displayDebug() {
Log.debug("Main SVG: size=" + this.size);
Log.debug(" refs:");
for (int iii = 0; iii < this.refList.size(); iii++) {
if (this.refList.get(iii) != null) {
this.refList.get(iii).display(2);
LOGGER.debug("Main SVG: size=" + this.size);
LOGGER.debug(" refs:");
for (final Base element : this.refList) {
if (element != null) {
element.display(2);
}
}
Log.debug(" Nodes:");
for (int iii = 0; iii < this.subElementList.size(); iii++) {
if (this.subElementList.get(iii) != null) {
this.subElementList.get(iii).display(2);
LOGGER.debug(" Nodes:");
for (final Base element : this.subElementList) {
if (element != null) {
element.display(2);
}
}
}
@Override
protected void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
for (int iii = 0; iii < this.subElementList.size(); iii++) {
if (this.subElementList.get(iii) != null) {
this.subElementList.get(iii).draw(myRenderer, basicTrans);
for (final Base element : this.subElementList) {
if (element != null) {
element.draw(myRenderer, basicTrans);
}
}
}
@Override
protected void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW shape EsvgDocument");
protected void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW shape EsvgDocument");
for (final Base it : this.subElementList) {
if (it != null) {
it.drawShapePoints(out, recurtionMax, threshold, basicTrans, level + 1);
}
}
}
// TODO remove this fucntion : use generic function ...
public Vector2f getDefinedSize() {
return this.size;
}
public List<List<Vector2f>> getLines() {
return getLines(new Vector2f(256, 256));
}
public List<List<Vector2f>> getLines(Vector2f size) {
final List<List<Vector2f>> out = new ArrayList<>();
if (size.x() <= 0) {
@ -134,16 +141,17 @@ public class EsvgDocument extends Base {
if (size.y() <= 0) {
size = size.withY(this.size.y());
}
Log.debug("lineification size " + size);
LOGGER.debug("lineification size " + size);
// create the first element matrix modification ...
final Matrix2x3f basicTrans = Matrix2x3f.IDENTITY.multiply(Matrix2x3f.createScale(new Vector2f(size.x() / this.size.x(), size.y() / this.size.y())));
final Matrix2x3f basicTrans = Matrix2x3f.IDENTITY
.multiply(Matrix2x3f.createScale(new Vector2f(size.x() / this.size.x(), size.y() / this.size.y())));
drawShapePoints(out, 10, 0.25f, basicTrans);
return out;
}
public Base getReference(final String name) {
if (name.isEmpty()) {
Log.error("request a reference with no name ... ");
LOGGER.error("request a reference with no name ... ");
return null;
}
for (final Base it : this.refList) {
@ -154,14 +162,14 @@ public class EsvgDocument extends Base {
return it;
}
}
Log.error("Can not find reference name : '" + name + "'");
LOGGER.error("Can not find reference name : '" + name + "'");
return null;
}
public boolean isLoadOk() {
return this.loadOK;
}
/*
//! @previous
public List<Color> renderImageFloatRGB(final Vector2i size) {
@ -174,7 +182,7 @@ public class EsvgDocument extends Base {
}
return out;
}
//! @previous
public List<Color<uint8t,4>> renderImageU8RGBA(final Vector2i size) {
List<Color> data = renderImageFloatRGBA(size);
@ -186,7 +194,7 @@ public class EsvgDocument extends Base {
}
return out;
}
//! @previous
public List<Color<uint8t,3>> renderImageU8RGB(final Vector2i size) {
List<Color> data = renderImageFloatRGBA(size);
@ -230,7 +238,7 @@ public class EsvgDocument extends Base {
}
return this.loadOK;
}
/**
* parse a string that contain an svg stream
* @param data Data to parse
@ -262,11 +270,11 @@ public class EsvgDocument extends Base {
}
return this.loadOK;
}
public boolean parseXMLData(final XmlElement root) {
return parseXMLData(root, false);
}
public boolean parseXMLData(final XmlElement root, final boolean isReference) {
// get the svg version :
this.version = root.getAttribute("version", "");
@ -277,11 +285,11 @@ public class EsvgDocument extends Base {
pos = parseXmlPosition(root);
this.size = parseXmlSize(root);
parsePaintAttr(root);
Log.verbose("parsed .ROOT trans: " + this.transformMatrix);
LOGGER.trace("parsed .ROOT trans: " + this.transformMatrix);
} else {
Log.verbose("Parse Reference section ... (no attibute)");
LOGGER.trace("Parse Reference section ... (no attibute)");
}
Vector2f maxSize = Vector2f.ZERO;
final Dynamic<Vector2f> size = new Dynamic<>(Vector2f.ZERO);
// parse all sub node:
@ -294,7 +302,7 @@ public class EsvgDocument extends Base {
if (child.getValue().equals("g")) {
elementParser = new Group(this.paint);
} else if (child.getValue().equals("a")) {
Log.info("Note : 'a' balise is parsed like a g balise ...");
LOGGER.info("Note : 'a' balise is parsed like a g balise ...");
elementParser = new Group(this.paint);
} else if (child.getValue().equals("title")) {
this.title = "TODO : set the title here ...";
@ -317,19 +325,19 @@ public class EsvgDocument extends Base {
elementParser = new Text(this.paint);
} else if (child.getValue().equals("radialGradient")) {
if (!isReference) {
Log.error("'" + child.getValue() + "' node must not be defined outside a defs Section");
LOGGER.error("'" + child.getValue() + "' node must not be defined outside a defs Section");
continue;
}
elementParser = new RadialGradient(this.paint);
} else if (child.getValue().equals("linearGradient")) {
if (!isReference) {
Log.error("'" + child.getValue() + "' node must not be defined outside a defs Section");
LOGGER.error("'" + child.getValue() + "' node must not be defined outside a defs Section");
continue;
}
elementParser = new LinearGradient(this.paint);
} else if (child.getValue().equals("defs")) {
if (isReference) {
Log.error("'" + child.getValue() + "' node must not be defined in a defs Section");
LOGGER.error("'" + child.getValue() + "' node must not be defined in a defs Section");
continue;
}
final boolean retRefs = parseXMLData(child, true);
@ -342,14 +350,15 @@ public class EsvgDocument extends Base {
// Node ignore : generaly inkscape data
continue;
} else {
Log.error("node not suported : '" + child.getValue() + "' must be [title,g,a,path,rect,circle,ellipse,line,polyline,polygon,text,metadata]");
LOGGER.error("node not suported : '" + child.getValue()
+ "' must be [title,g,a,path,rect,circle,ellipse,line,polyline,polygon,text,metadata]");
}
if (elementParser == null) {
Log.error("error on node: '" + child.getValue() + "' allocation error or not supported ...");
LOGGER.error("error on node: '" + child.getValue() + "' allocation error or not supported ...");
continue;
}
if (!elementParser.parseXML(child, this.transformMatrix, size)) {
Log.error("error on node: '" + child.getValue() + "' Sub Parsing ERROR");
LOGGER.error("error on node: '" + child.getValue() + "' Sub Parsing ERROR");
elementParser = null;
continue;
}
@ -376,12 +385,12 @@ public class EsvgDocument extends Base {
}
return true;
}
/*
public float[][] renderImageFloat(final Vector2i size) {
return renderImageFloat(size, false);
}
public float[][] renderImageFloat(Vector2i size, final boolean visualDebug) {
if (size == null) {
size = new Vector2i((int) this.size.x(), (int) this.size.y());
@ -393,12 +402,12 @@ public class EsvgDocument extends Base {
size = size.withY((int) this.size.y());
}
}
Log.debug("Generate size " + size);
LOGGER.debug("Generate size " + size);
Renderer renderedElement = new Renderer(size, this, visualDebug);
// create the first element matrix modification ...
Matrix2x3f basicTrans = Matrix2x3f.IDENTITY.multiply(Matrix2x3f.createScale(new Vector2f(size.x() / this.size.x(), size.y() / this.size.y())));
draw(renderedElement, basicTrans);
// direct return the generated data ...
return renderedElement.getData();
}
@ -411,7 +420,7 @@ public class EsvgDocument extends Base {
public ImageFloatRGBA renderImageFloatRGBA(final Vector2i size) {
return renderImageFloatRGBA(size, false);
}
public ImageFloatRGBA renderImageFloatRGBA(Vector2i size, final boolean visualDebug) {
if (size == null) {
size = new Vector2i((int) this.size.x(), (int) this.size.y());
@ -424,19 +433,20 @@ public class EsvgDocument extends Base {
}
}
if (size.x() <= 0) {
Log.error("Generate size " + size);
LOGGER.error("Generate size " + size);
}
if (size.y() <= 0) {
Log.error("Generate size " + size);
LOGGER.error("Generate size " + size);
}
Log.verbose("Generate size " + size);
LOGGER.trace("Generate size " + size);
final Renderer renderedElement = new Renderer(size, this, visualDebug);
// create the first element matrix modification ...
final Matrix2x3f basicTrans = Matrix2x3f.IDENTITY.multiply(Matrix2x3f.createScale(new Vector2f(size.x() / this.size.x(), size.y() / this.size.y())));
final Matrix2x3f basicTrans = Matrix2x3f.IDENTITY
.multiply(Matrix2x3f.createScale(new Vector2f(size.x() / this.size.x(), size.y() / this.size.y())));
draw(renderedElement, basicTrans);
// direct return the generated data ...
return renderedElement.getData();
}
}

View File

@ -8,7 +8,6 @@ import java.util.Map;
import org.atriasoft.aknot.exception.AknotException;
import org.atriasoft.esvg.font.Glyph;
import org.atriasoft.esvg.font.Kerning;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.RenderingConfig;
import org.atriasoft.esvg.render.Weight;
@ -21,6 +20,8 @@ import org.atriasoft.exml.Exml;
import org.atriasoft.exml.exception.ExmlException;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// https://www.w3.org/TR/SVGTiny12/fonts.html
@ -52,11 +53,11 @@ import org.atriasoft.exml.model.XmlNode;
<------------------------> : advance.x
<------------> : sizeTexture.x
<---> : bearing.x
_
*----------------------* ^ ==> calculateFontRealHeight(fontSize);
| | | ^ ==> getAscent(fontSize);
@ -72,12 +73,13 @@ import org.atriasoft.exml.model.XmlNode;
| | | |==> getDescent(fontSize);
| | | |
*----------------------* | |
*/
public class EsvgFont {
static final Logger LOGGER = LoggerFactory.getLogger(EsvgFont.class);
/**
* Load the file that might contain the svg
* @param uri File of the svg
@ -99,24 +101,25 @@ public class EsvgFont {
return null;
}
if (!(doc instanceof final XmlElement root)) {
Log.error("can not load the SVG font ==> wrong root node");
LOGGER.error("can not load the SVG font ==> wrong root node");
return null;
}
if (!root.existNode("svg") || !(root.getNodeNoExcept("svg") instanceof final XmlElement svgNode)) {
Log.error("can not load Node <svg> in svg document");
LOGGER.error("can not load Node <svg> in svg document");
return null;
}
if (!svgNode.existNode("defs") || !(svgNode.getNodeNoExcept("defs") instanceof final XmlElement defsNode)) {
Log.error("can not load Node <defs> in svg document");
LOGGER.error("can not load Node <defs> in svg document");
return null;
}
if (!defsNode.existNode("font") || !(defsNode.getNodeNoExcept("font") instanceof final XmlElement fontElement)) {
Log.error("can not load Node <font> in svg document");
if (!defsNode.existNode("font")
|| !(defsNode.getNodeNoExcept("font") instanceof final XmlElement fontElement)) {
LOGGER.error("can not load Node <font> in svg document");
return null;
}
font.horizAdvX = Integer.parseInt(fontElement.getAttribute("horiz-adv-x", "100"));
int nbGlyph = 0;
for (final XmlNode values : fontElement.getNodes()) {
if (values.getValue().equals("font-face")) {
@ -157,7 +160,7 @@ public class EsvgFont {
for (final XmlNode values : fontElement.getNodes()) {
if (values.getValue().equals("glyph")) {
nbGlyph++;
//Log.info("find flyph: " + nbGlyph);
//LOGGER.info("find flyph: " + nbGlyph);
final Glyph tmp = Glyph.valueOf(values.toElement(), font);
if (tmp != null) {
font.glyphs.put(tmp.getUnicodeValue(), tmp);
@ -169,7 +172,7 @@ public class EsvgFont {
} else if (values.getValue().equals("font-face")) {
// already done ...
} else {
Log.warning("unsupported node name :" + values.getValue());
LOGGER.warn("unsupported node name :" + values.getValue());
}
}
for (final XmlNode values : fontElement.getNodes()) {
@ -188,18 +191,18 @@ public class EsvgFont {
final String[] g2Splited = g2.split(",");
// create the list of kerning of the next elements
final List<Kerning> elementsKerning = new ArrayList<>();
for (int iii = 0; iii < g2Splited.length; iii++) {
for (final String element : g2Splited) {
for (final Map.Entry<Integer, Glyph> entry : font.glyphs.entrySet()) {
if (entry.getValue().getName().equals(g2Splited[iii])) {
if (entry.getValue().getName().equals(element)) {
elementsKerning.add(new Kerning(offset, entry.getKey()));
break;
}
}
}
// add it on the
for (int iii = 0; iii < g1Splited.length; iii++) {
for (final String element : g1Splited) {
for (final Map.Entry<Integer, Glyph> entry : font.glyphs.entrySet()) {
if (entry.getValue().getName().equals(g1Splited[iii])) {
if (entry.getValue().getName().equals(element)) {
entry.getValue().addKerning(elementsKerning);
font.hasKerning = true;
break;
@ -211,7 +214,7 @@ public class EsvgFont {
}
return font;
}
// The maximum accented height of the font within the font coordinate system.
private int ascent = 800; // this is the height of the font (on top...)
private int[] bbox = { -879, -545, 1767, 934 };
@ -235,7 +238,7 @@ public class EsvgFont {
private int unitsPerEm = 1000; // full size of the font
// The height of lowercase glyphs in the font within the font coordinate system.
private int xHeight = 450;
/**
* Get the font real size use (height) for all the characters.
* @param fontSize size of the font the user require
@ -243,33 +246,33 @@ public class EsvgFont {
*/
public int calculateFontRealHeight(final int fontSize) {
return fontSize * this.unitsPerEm / this.capHeight;
}
public float calculateFontSizeWithHeight(final float fontHeight) {
return fontHeight * this.capHeight / this.unitsPerEm;
}
public Vector2f calculateRenderOffset(final int fontSize) {
final int realSize = calculateFontRealHeight(fontSize);
final float deltaY = realSize * this.ascent / this.unitsPerEm;
return new Vector2f(0, deltaY);
}
public float calculateSclaleFactor(final int fontSize) {
final int realSize = calculateFontRealHeight(fontSize);
return (float) realSize / (float) this.unitsPerEm;
}
public Vector2i calculateTextSize(final int fontSize, final String data) {
final boolean withKerning = true;
final int widthOut = calculateWidth(data, fontSize, withKerning);
final int realSize = calculateFontRealHeight(fontSize);
return new Vector2i(widthOut, realSize);
/*
float scale = (float) realSize / (float) this.unitsPerEm;
int offsetWriting = 0;
int lastValue = 0;
for (char uVal : data.toCharArray()) {
@ -282,7 +285,7 @@ public class EsvgFont {
offsetWriting -= glyph.getKerning(lastValue) * scale;
lastValue = uVal;
}
float advenceXLocal = glyph.getHorizAdvX() * scale;
// No generation of output ...
offsetWriting += advenceXLocal;
@ -290,7 +293,7 @@ public class EsvgFont {
return new Vector2i(offsetWriting, realSize);
*/
}
public int calculateWidth(final int uVal, final int fontSize) {
final Glyph glyph = getGlyph(uVal);
if (glyph == null) {
@ -300,15 +303,15 @@ public class EsvgFont {
final float scale = (float) realSize / (float) this.unitsPerEm;
return (int) (glyph.getHorizAdvX() * scale);
}
public int calculateWidth(final String uVal, final int fontSize) {
return calculateWidth(uVal, fontSize, true);
}
public int calculateWidth(final String data, final int fontSize, final boolean withKerning) {
final int realSize = calculateFontRealHeight(fontSize);
final float scale = (float) realSize / (float) this.unitsPerEm;
//Log.error("scale =" + scale+ " font size = " + fontSize + " realSize=" + realSize);
//LOGGER.error("scale =" + scale+ " font size = " + fontSize + " realSize=" + realSize);
float offsetWriting = 0;
int lastValue = 0;
for (final char uVal : data.toCharArray()) {
@ -321,14 +324,14 @@ public class EsvgFont {
offsetWriting -= glyph.getKerning(lastValue) * scale;
lastValue = uVal;
}
final float advenceXLocal = glyph.getHorizAdvX() * scale;
offsetWriting += advenceXLocal;
//Log.error("offset X =" + offsetWriting + " + " + advenceXLocal + " " + uVal);
//LOGGER.error("offset X =" + offsetWriting + " + " + advenceXLocal + " " + uVal);
}
return (int) offsetWriting;
}
/**
* Get the rendering size of the specific glyph (size rendered in the Weight class).
* @param unicodeValue Unicode value to render
@ -338,11 +341,11 @@ public class EsvgFont {
public Vector2i calculateWidthRendering(final Integer unicodeValue, final int fontSize) {
return new Vector2i(calculateWidth(unicodeValue, fontSize), calculateFontRealHeight(fontSize));
}
public int getDescent() {
return this.descent;
}
public Glyph getGlyph(final int glyphIndex) {
final Glyph out = this.glyphs.get(glyphIndex);
if (out == null) {
@ -350,7 +353,7 @@ public class EsvgFont {
}
return out;
}
public Glyph getGlyphNullIfMissing(final int glyphIndex) {
final Glyph out = this.glyphs.get(glyphIndex);
if (out == null) {
@ -358,11 +361,11 @@ public class EsvgFont {
}
return out;
}
public int getHorizAdvX() {
return this.horizAdvX;
}
/**
* Get the number of available glyph in the Font
* @return the glyph count.
@ -370,11 +373,11 @@ public class EsvgFont {
public int getNumGlyphs() {
return this.glyphs.size();
}
public float getUnitsPerEm() {
return this.unitsPerEm;
}
/**
* Check if the font have some kerning data
* @return true if kerning is availlable.
@ -382,7 +385,7 @@ public class EsvgFont {
public boolean hasKerning() {
return this.hasKerning;
}
public Weight render(final int uVal, final int fontSize) {
final int realSize = calculateFontRealHeight(fontSize);
final Glyph glyph = getGlyph(uVal);
@ -391,7 +394,8 @@ public class EsvgFont {
}
final float scale = (float) realSize / (float) this.unitsPerEm;
final RenderingConfig config = new RenderingConfig(10, 0.25f, 8);
final Matrix2x3f transform = Matrix2x3f.createTranslate(new Vector2f(0, -this.descent)).multiply(Matrix2x3f.createScale(scale));
final Matrix2x3f transform = Matrix2x3f.createTranslate(new Vector2f(0, -this.descent))
.multiply(Matrix2x3f.createScale(scale));
final PathModel model = glyph.getModel();
if (model == null) {
return null;
@ -399,19 +403,19 @@ public class EsvgFont {
final Weight data = glyph.getModel().drawFill(calculateWidthRendering(uVal, fontSize), transform, 8, config);
return data;
}
public Weight render(final String uVal, final int fontSize) {
return render(uVal, fontSize, true);
}
public Weight render(final String data, final int fontSize, final boolean withKerning) {
final int widthOut = calculateWidth(data, fontSize, withKerning);
final int realSize = calculateFontRealHeight(fontSize);
final float scale = (float) realSize / (float) this.unitsPerEm;
final Weight weight = new Weight(new Vector2i(widthOut, realSize));
float offsetWriting = 0;
int lastValue = 0;
for (final char uVal : data.toCharArray()) {
@ -422,21 +426,23 @@ public class EsvgFont {
}
if (withKerning) {
offsetWriting -= glyph.getKerning(lastValue) * scale;
Log.info(" ==> kerning offset = " + (glyph.getKerning(lastValue) * scale));
LOGGER.info(" ==> kerning offset = " + (glyph.getKerning(lastValue) * scale));
lastValue = uVal;
}
final float advenceXLocal = glyph.getHorizAdvX() * scale;
final RenderingConfig config = new RenderingConfig(10, 0.25f, 8);
final Matrix2x3f transform = Matrix2x3f.createTranslate(new Vector2f(0, -this.descent)).multiply(Matrix2x3f.createScale(scale));
final Matrix2x3f transform = Matrix2x3f.createTranslate(new Vector2f(0, -this.descent))
.multiply(Matrix2x3f.createScale(scale));
final PathModel model = glyph.getModel();
if (model != null) {
final Weight redered = model.drawFill(calculateWidthRendering((int) uVal, fontSize), transform, 8, config);
final Weight redered = model.drawFill(calculateWidthRendering((int) uVal, fontSize), transform, 8,
config);
weight.fusion(redered, (int) offsetWriting, 0);
}
offsetWriting += advenceXLocal;
}
return weight;
}

View File

@ -2,59 +2,62 @@ package org.atriasoft.esvg;
import org.atriasoft.egami.ImageByte;
import org.atriasoft.egami.ToolImage;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.etk.Color;
import org.atriasoft.etk.Configs;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.math.Vector2i;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Graphic context is used to manage dynamic
* Graphic context is used to manage dynamic
* @author heero
*
*/
public class GraphicContext {
static final Logger LOGGER = LoggerFactory.getLogger(GraphicContext.class);
private EsvgDocument document = new EsvgDocument();
PaintState paintState;
private PathModel path = null;
private Vector2i size = Vector2i.VALUE_32;
public GraphicContext() {
clear();
}
public Vector2i calculateTextSize(final String data) {
return FontCache.getFont(Configs.getConfigFonts().getName(), false, false).calculateTextSize(Configs.getConfigFonts().getSize(), data);
return FontCache.getFont(Configs.getConfigFonts().getName(), false, false)
.calculateTextSize(Configs.getConfigFonts().getSize(), data);
}
public void circle(final Vector2f position, final float radius) {
this.document.addElement(new Circle(position, radius, this.paintState.clone()));
}
public void clear() {
this.document = new EsvgDocument(this.size);
this.paintState = new PaintState();
}
/**
* Clear the fill color (disable fill ==> better that set it transparent)
*/
public void clearColorFill() {
this.paintState.fill = null;
}
/**
* Clear the Stroke color (disable stroke)
*/
public void clearColorStroke() {
this.paintState.clearStroke();
}
public void ellipse(final Vector2f center, final Vector2f radius) {
this.document.addElement(new Ellipse(center, radius, this.paintState.clone()));
}
/**
* Get the fill color.
* @return fill color.
@ -62,7 +65,7 @@ public class GraphicContext {
public Color getColorFill() {
return this.paintState.getFill();
}
/**
* Get the stroke color.
* @return Stroke color.
@ -70,85 +73,86 @@ public class GraphicContext {
public Color getColorStroke() {
return this.paintState.getStroke();
}
public CapMode getLineCap() {
return this.paintState.getLineCap();
}
public JoinMode getLineJoin() {
return this.paintState.getLineJoin();
}
public float getMiterLimit() {
return this.paintState.getMiterLimit();
}
public float getOpacity() {
return this.paintState.getOpacity();
}
public float getStrokeWidth() {
return this.paintState.getStrokeWidth();
}
public int getTextHeight() {
return getTextHeight(Configs.getConfigFonts().getSize());
}
public int getTextSize() {
return Configs.getConfigFonts().getSize();
}
public int getTextHeight(final float height) {
return FontCache.getFont(Configs.getConfigFonts().getName(), false, false).calculateFontRealHeight((int) height);
return FontCache.getFont(Configs.getConfigFonts().getName(), false, false)
.calculateFontRealHeight((int) height);
}
public void line(final Vector2f origin, final Vector2f destination) {
this.document.addElement(new Line(origin, destination, this.paintState.clone()));
}
public void lineRel(final Vector2f origin, final Vector2f relativeDestination) {
this.document.addElement(new Line(origin, origin.add(relativeDestination), this.paintState.clone()));
}
public void pathLine(final Vector2f pos) {
if (this.path == null) {
Log.error("Empty path... Need call pathStart() before");
LOGGER.error("Empty path... Need call pathStart() before");
return;
}
this.path.lineTo(false, pos);
}
public void pathLineTo(final Vector2f pos) {
if (this.path == null) {
Log.error("Empty path... Need call pathStart() before");
LOGGER.error("Empty path... Need call pathStart() before");
return;
}
this.path.lineTo(true, pos);
}
public void pathMove(final Vector2f pos) {
if (this.path == null) {
Log.error("Empty path... Need call pathStart() before");
LOGGER.error("Empty path... Need call pathStart() before");
return;
}
this.path.moveTo(false, pos);
}
public void pathMoveTo(final Vector2f pos) {
if (this.path == null) {
Log.error("Empty path... Need call pathStart() before");
LOGGER.error("Empty path... Need call pathStart() before");
return;
}
this.path.moveTo(true, pos);
}
public void pathStart() {
pathStop();
this.path = new PathModel();
}
public void pathStop() {
if (this.path == null) {
return;
@ -157,7 +161,7 @@ public class GraphicContext {
this.document.addElement(new Path(this.path, this.paintState.clone()));
this.path = null;
}
public void pathStopLinked() {
if (this.path == null) {
return;
@ -166,43 +170,43 @@ public class GraphicContext {
this.document.addElement(new Path(this.path, this.paintState.clone()));
this.path = null;
}
public void rectangle(final Vector2f position, final Vector2f destination) {
if (this.path != null) {
Log.error("Path not empty ... Need call pathStart() before");
LOGGER.error("Path not empty ... Need call pathStart() before");
pathStop();
}
this.document.addElement(new Rectangle(position, destination.less(position), this.paintState.clone()));
}
public void rectangleRounded(final Vector2f position, final Vector2f destination, final Vector2f ruound) {
if (this.path != null) {
Log.error("Path not empty ... Need call pathStart() before");
LOGGER.error("Path not empty ... Need call pathStart() before");
pathStop();
}
this.document.addElement(new Rectangle(position, destination.less(position), ruound, this.paintState.clone()));
}
public void rectangleRoundedWidth(final Vector2f position, final Vector2f width, final Vector2f ruound) {
if (this.path != null) {
Log.error("Path not empty ... Need call pathStart() before");
LOGGER.error("Path not empty ... Need call pathStart() before");
pathStop();
}
this.document.addElement(new Rectangle(position, width, ruound, this.paintState.clone()));
}
public void rectangleWidth(final Vector2f position, final Vector2f width) {
if (this.path != null) {
Log.error("Path not empty ... Need call pathStart() before");
LOGGER.error("Path not empty ... Need call pathStart() before");
pathStop();
}
this.document.addElement(new Rectangle(position, width, this.paintState.clone()));
}
public ImageByte render() {
return ToolImage.convertImageByte(this.document.renderImageFloatRGBA(null));
}
/**
* set the fill color
* @param color Color to set on fill
@ -211,7 +215,7 @@ public class GraphicContext {
public void setColorFill(final Color color) {
this.paintState.setFill(color);
}
/**
* set the stroke color
* @param color Color to set on stroke
@ -220,23 +224,23 @@ public class GraphicContext {
public void setColorStroke(final Color color) {
this.paintState.setStroke(color);
}
public void setLineCap(final CapMode lineCap) {
this.paintState.setLineCap(lineCap);
}
public void setLineJoin(final JoinMode lineJoin) {
this.paintState.setLineJoin(lineJoin);
}
public void setMiterLimit(final float miterLimit) {
this.paintState.setMiterLimit(miterLimit);
}
public void setOpacity(final float opacity) {
this.paintState.setOpacity(opacity);
}
/**
* Set global size of the Graphic context (output render size)
* @param xxx Width of the image
@ -246,7 +250,7 @@ public class GraphicContext {
public void setSize(final int xxx, final int yyy) {
setSize(new Vector2i(xxx, yyy));
}
/**
* Set global size of the Graphic contexct (output render size)
* @param vector2i New size of the image
@ -256,17 +260,18 @@ public class GraphicContext {
this.size = size;
clear();
}
public void setStrokeWidth(final float strokeWidth) {
this.paintState.setStrokeWidth(strokeWidth);
}
public void text(final Vector2f position, final float height, final String data) {
this.document.addElement(new Text(position, Configs.getConfigFonts().getName(), height, data, this.paintState.clone()));
this.document.addElement(
new Text(position, Configs.getConfigFonts().getName(), height, data, this.paintState.clone()));
}
public void text(final Vector2f position, final String data) {
text(position, Configs.getConfigFonts().getSize(), data);
}
}

View File

@ -3,12 +3,13 @@ package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -16,44 +17,51 @@ import org.atriasoft.exml.model.XmlNode;
* @license MPL v2.0 (see license file)
*/
public class Group extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Group.class);
private final List<Base> subElementList = new ArrayList<>(); //!< sub elements ...
public Group(final PaintState parentPaintState) {
super(parentPaintState);
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "Group (START) fill=" + this.paint.fill.first + "/" + this.paint.fill.second + " stroke=" + this.paint.stroke.first + "/" + this.paint.stroke.second
+ " stroke-width=" + this.paint.strokeWidth);
LOGGER.debug(spacingDist(spacing) + "Group (START) fill=" + this.paint.fill.first + "/" + this.paint.fill.second
+ " stroke=" + this.paint.stroke.first + "/" + this.paint.stroke.second + " stroke-width="
+ this.paint.strokeWidth);
for (final Base it : this.subElementList) {
if (it != null) {
it.display(spacing + 1);
}
}
Log.debug(spacingDist(spacing) + "Group (STOP)");
LOGGER.debug(spacingDist(spacing) + "Group (STOP)");
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::group");
for (Base it : this.subElementList) {
LOGGER.trace(spacingDist(level) + "DRAW esvg::group");
for (final Base it : this.subElementList) {
if (it != null) {
it.draw(myRenderer, basicTrans, level + 1);
}
}
}
@Override
public void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW shape esvg::group");
for (Base it : this.subElementList) {
public void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW shape esvg::group");
for (final Base it : this.subElementList) {
if (it != null) {
it.drawShapePoints(out, recurtionMax, threshold, basicTrans, level + 1);
}
}
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
if (element == null) {
@ -66,18 +74,18 @@ public class Group extends Base {
pos = parseXmlPosition(element);
size = parseXmlSize(element);
parsePaintAttr(element);
Log.verbose("parsed G1. trans : " + this.transformMatrix);
LOGGER.trace("parsed G1. trans : " + this.transformMatrix);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
Log.verbose("parsed G2. trans : " + this.transformMatrix);
LOGGER.trace("parsed G2. trans : " + this.transformMatrix);
sizeMax.value = Vector2f.ZERO;
Dynamic<Vector2f> tmpPos = new Dynamic<Vector2f>(Vector2f.ZERO);
final Dynamic<Vector2f> tmpPos = new Dynamic<>(Vector2f.ZERO);
// parse all sub node :
for (XmlNode it : element.getNodes()) {
if (!(it instanceof XmlElement child)) {
for (final XmlNode it : element.getNodes()) {
if (!(it instanceof final XmlElement child)) {
// can be a comment ...
continue;
}
@ -103,14 +111,15 @@ public class Group extends Base {
} else if (child.getValue().equals("text")) {
elementParser = new Text(this.paint);
} else {
Log.error("node not suported : '" + child.getValue() + "' must be [g,a,path,rect,circle,ellipse,line,polyline,polygon,text]");
LOGGER.error("node not suported : '" + child.getValue()
+ "' must be [g,a,path,rect,circle,ellipse,line,polyline,polygon,text]");
}
if (elementParser == null) {
Log.error("error on node: '" + child.getValue() + "' allocation error or not supported ...");
LOGGER.error("error on node: '" + child.getValue() + "' allocation error or not supported ...");
continue;
}
if (!elementParser.parseXML(child, this.transformMatrix, tmpPos)) {
Log.error(" error on node: '" + child.getValue() + "' Sub Parsing ERROR");
LOGGER.error(" error on node: '" + child.getValue() + "' Sub Parsing ERROR");
elementParser = null;
continue;
}

View File

@ -3,7 +3,6 @@ package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.DynamicColor;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.Point;
@ -14,6 +13,8 @@ import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -21,47 +22,49 @@ import org.atriasoft.exml.model.XmlElement;
* @license MPL v2.0 (see license file)
*/
public class Line extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Line.class);
private Vector2f startPos = Vector2f.ZERO; //!< Start line position
private Vector2f stopPos = Vector2f.ZERO; //!< Stop line position
public Line(final PaintState parentPaintState) {
super(parentPaintState);
}
public Line(final Vector2f startPos, final Vector2f stopPos, final PaintState parentPaintState) {
super(parentPaintState);
this.startPos = startPos;
this.stopPos = stopPos;
}
private PathModel createPath() {
PathModel out = new PathModel();
final PathModel out = new PathModel();
out.clear();
out.moveTo(false, this.startPos);
out.lineTo(false, this.stopPos);
out.stop();
return out;
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "Line " + this.startPos + " to " + this.stopPos);
LOGGER.debug(spacingDist(spacing) + "Line " + this.startPos + " to " + this.stopPos);
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::Line");
PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
LOGGER.trace(spacingDist(level) + "DRAW esvg::Line");
final PathModel listElement = createPath();
final Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
PointList listPoints = new PointList();
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
SegmentList listSegmentStroke = new SegmentList();
Weight tmpFill = new Weight();
Weight tmpStroke = new Weight();
DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(),
myRenderer.getInterpolationThreshold());
final SegmentList listSegmentStroke = new SegmentList();
final Weight tmpFill = new Weight();
final Weight tmpStroke = new Weight();
final DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
DynamicColor colorStroke = null;
if (this.paint.strokeWidth > 0.0f) {
colorStroke = DynamicColor.createColor(this.paint.stroke, mtx);
@ -70,7 +73,8 @@ public class Line extends Base {
// No background ...
// check if we need to display stroke:
if (colorStroke != null) {
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap,
this.paint.lineJoin, this.paint.miterLimit);
colorStroke.setViewPort(listSegmentStroke.getViewPort());
listSegmentStroke.applyMatrix(mtx);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
@ -79,24 +83,29 @@ public class Line extends Base {
// add on images:
myRenderer.print(tmpFill, colorFill, tmpStroke, colorStroke, this.paint.opacity);
}
@Override
public void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW Shape esvg::Line");
PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
public void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW Shape esvg::Line");
final PathModel listElement = createPath();
final Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
PointList listPoints;
listPoints = listElement.generateListPoints(level, recurtionMax, threshold);
listPoints.applyMatrix(mtx);
for (List<Point> it : listPoints.data) {
List<Vector2f> listPoint = new ArrayList<>();
for (Point itDot : it) {
for (final List<Point> it : listPoints.data) {
final List<Vector2f> listPoint = new ArrayList<>();
for (final Point itDot : it) {
listPoint.add(itDot.pos);
}
out.add(listPoint);
}
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
// line must have a minimum size...
@ -106,10 +115,10 @@ public class Line extends Base {
}
parseTransform(element);
parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
String content = element.getAttribute("x1", "");
if (content.length() != 0) {
this.startPos = this.startPos.withX(parseLength(content));

View File

@ -3,7 +3,6 @@ package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.Color;
import org.atriasoft.etk.Dimension2f;
import org.atriasoft.etk.Distance;
@ -14,6 +13,8 @@ import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.etk.util.Pair;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -22,63 +23,64 @@ import org.atriasoft.exml.model.XmlNode;
*/
public class LinearGradient extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(LinearGradient.class);
private final List<Pair<Float, Color>> data = new ArrayList<>(); //!< gradient position x1 y1
private String href = ""; //!< gradient position x2 y2
private Dimension2f pos1 = new Dimension2f(new Vector2f(50, 50), Distance.POURCENT);
private Dimension2f pos2 = new Dimension2f(new Vector2f(50, 50), Distance.POURCENT);
public SpreadMethod spread = SpreadMethod.PAD; //!< in case of using a single gradient in multiple gradient, the gradient is store in an other element...
public GradientUnits unit = GradientUnits.GRADIENT_UNITS_OBJECT_BOUNDING_BOX; //!< incompatible with href
public LinearGradient(final PaintState parentPaintState) {
super(parentPaintState);
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "LinearGradient " + this.pos1 + " to " + this.pos2);
for (Pair<Float, Color> it : this.data) {
Log.debug(spacingDist(spacing + 1) + "STOP: offset=" + it.first + " color=" + it.second);
LOGGER.debug(spacingDist(spacing) + "LinearGradient " + this.pos1 + " to " + this.pos2);
for (final Pair<Float, Color> it : this.data) {
LOGGER.debug(spacingDist(spacing + 1) + "STOP: offset=" + it.first + " color=" + it.second);
}
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::LinearGradient");
LOGGER.trace(spacingDist(level) + "DRAW esvg::LinearGradient");
}
public List<Pair<Float, Color>> getColors(final EsvgDocument document) {
if (this.href.isEmpty()) {
return this.data;
}
if (document == null) {
Log.error("Get null input for document");
LOGGER.error("Get null input for document");
return this.data;
}
Base base = document.getReference(this.href);
final Base base = document.getReference(this.href);
if (base == null) {
Log.error("Can not get base : '" + this.href + "'");
LOGGER.error("Can not get base : '" + this.href + "'");
return this.data;
}
if (base instanceof RadialGradient gradientR) {
if (base instanceof final RadialGradient gradientR) {
return gradientR.getColors(document);
}
if (base instanceof LinearGradient gradientL) {
if (base instanceof final LinearGradient gradientL) {
return gradientL.getColors(document);
}
return this.data;
}
public Dimension2f getPosition1() {
return this.pos1;
}
public Dimension2f getPosition2() {
return this.pos2;
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
{
@ -87,16 +89,16 @@ public class LinearGradient extends Base {
if (element == null) {
return false;
}
// ---------------- get unique ID ----------------
this.id = element.getAttribute("id", "");
//parseTransform(element);
//parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
String contentX = element.getAttribute("x1", "");
String contentY = element.getAttribute("y1", "");
if (!contentX.isEmpty() && !contentY.isEmpty()) {
@ -113,7 +115,8 @@ public class LinearGradient extends Base {
} else {
this.unit = GradientUnits.GRADIENT_UNITS_OBJECT_BOUNDING_BOX;
if (contentX.length() != 0 && contentX != "objectBoundingBox") {
Log.error("Parsing error of 'gradientUnits' ==> not suported value: '" + contentX + "' not in : {userSpaceOnUse/objectBoundingBox} use objectBoundingBox");
LOGGER.error("Parsing error of 'gradientUnits' ==> not suported value: '" + contentX
+ "' not in : {userSpaceOnUse/objectBoundingBox} use objectBoundingBox");
}
}
contentX = element.getAttribute("spreadMethod", "");
@ -124,7 +127,8 @@ public class LinearGradient extends Base {
} else {
this.spread = SpreadMethod.PAD;
if (contentX.length() != 0 && !contentX.equals("pad")) {
Log.error("Parsing error of 'spreadMethod' ==> not suported value: '" + contentX + "' not in : {reflect/repeate/pad} use pad");
LOGGER.error("Parsing error of 'spreadMethod' ==> not suported value: '" + contentX
+ "' not in : {reflect/repeate/pad} use pad");
}
}
// note: xlink:href is incompatible with subNode "stop"
@ -133,19 +137,20 @@ public class LinearGradient extends Base {
this.href = this.href.substring(1);
}
// parse all sub node :
for (XmlNode it : element.getNodes()) {
if (it instanceof XmlElement child) {
for (final XmlNode it : element.getNodes()) {
if (it instanceof final XmlElement child) {
if (child.getValue().equals("stop")) {
float offset = 100;
Color stopColor = Color.NONE;
String content = child.getAttribute("offset", "");
if (content.length() != 0) {
Pair<Float, Distance> tmp = parseLength2(content);
final Pair<Float, Distance> tmp = parseLength2(content);
if (tmp.second == Distance.PIXEL) {
// special case ==> all time % then no type define ==> % in [0.0 .. 1.0]
offset = tmp.first * 100.0f;
} else if (tmp.second != Distance.POURCENT) {
Log.error("offset : " + content + " res=" + tmp.first + "," + tmp.second + " Not support other than pourcent %");
LOGGER.error("offset : " + content + " res=" + tmp.first + "," + tmp.second
+ " Not support other than pourcent %");
} else {
offset = tmp.first;
}
@ -153,29 +158,30 @@ public class LinearGradient extends Base {
content = child.getAttribute("stop-color", "");
if (content.length() != 0) {
stopColor = parseColor(content).first;
Log.verbose(" color : '" + content + "' == > " + stopColor);
LOGGER.trace(" color : '" + content + "' == > " + stopColor);
}
content = child.getAttribute("stop-opacity", "");
if (content.length() != 0) {
float opacity = parseLength(content);
opacity = FMath.avg(0.0f, opacity, 1.0f);
stopColor = stopColor.withA(opacity);
Log.verbose(" opacity : '" + content + "' == > " + stopColor);
LOGGER.trace(" opacity : '" + content + "' == > " + stopColor);
}
this.data.add(new Pair<>(offset, stopColor));
} else {
Log.error(" node not suported : '" + child.getValue() + "' must be [stop]");
LOGGER.error(" node not suported : '" + child.getValue() + "' must be [stop]");
}
}
}
if (this.data.size() != 0) {
if (!this.href.isEmpty()) {
Log.error(" node can not have an xlink:href element with sub node named: stop ==> removing href");
LOGGER.error(
" node can not have an xlink:href element with sub node named: stop ==> removing href");
this.href = "";
}
}
return true;
}
}
}

View File

@ -3,7 +3,6 @@ package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.DynamicColor;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.Point;
@ -15,6 +14,8 @@ import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -22,6 +23,8 @@ import org.atriasoft.exml.model.XmlElement;
* @license MPL v2.0 (see license file)
*/
public class Path extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Path.class);
private record Command(
char cmd,
String[] listElem,
@ -31,42 +34,43 @@ public class Path extends Base {
this.listElem = listElem;
this.offset = offset;
}
Command(final char cmd, final int offset) {
this(cmd, null, offset);
}
}
public static PathModel createPathModel(final String d) {
final PathModel out = new PathModel();
Log.verbose("Parse Path : \"" + d + "\"");
LOGGER.trace("Parse Path : \"" + d + "\"");
final List<String> commandsSplited = Path.splitCommand(d);
String[] listDot = null;
// TODO REWORK this, can be done with a simple split and search in a list...
for (Command sss = Path.extractCmd(commandsSplited, 0); sss != null; sss = Path.extractCmd(commandsSplited, sss.offset())) {
for (Command sss = Path.extractCmd(commandsSplited, 0); sss != null;
sss = Path.extractCmd(commandsSplited, sss.offset())) {
boolean relative = false;
listDot = sss.listElem();
// Log.verbose("Find new command : '" + sss.cmd + "'");
// LOGGER.trace("Find new command : '" + sss.cmd + "'");
// if (listDot != null) {
// for (int jjj = 0; jjj < listDot.length; jjj++) {
// Log.verbose(" -> '" + listDot[jjj] + "'");
// LOGGER.trace(" -> '" + listDot[jjj] + "'");
// }
// } else {
// Log.verbose(" -> no elements");
// LOGGER.trace(" -> no elements");
// }
switch (sss.cmd) {
case 'm': // Move to (relative)
relative = true;
case 'M': // Move to (absolute)
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
if (listDot.length % 2 != 0) {
Log.warning("the PATH command " + sss.cmd + " must be a multiple of 2");
LOGGER.warn("the PATH command " + sss.cmd + " must be a multiple of 2");
break;
}
// 2 Elements ...
@ -74,134 +78,145 @@ public class Path extends Base {
out.moveTo(relative, new Vector2f(Float.parseFloat(listDot[0]), Float.parseFloat(listDot[1])));
}
for (int iii = 2; iii < listDot.length; iii += 2) {
out.lineTo(relative, new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])));
out.lineTo(relative,
new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])));
}
break;
case 'l': // Line to (relative)
relative = true;
case 'L': // Line to (absolute)
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
if (listDot.length % 2 != 0) {
Log.warning("the PATH command " + sss.cmd + " must be a multiple of 2");
LOGGER.warn("the PATH command " + sss.cmd + " must be a multiple of 2");
break;
}
for (int iii = 0; iii < listDot.length; iii += 2) {
out.lineTo(relative, new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])));
out.lineTo(relative,
new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])));
}
break;
case 'v': // Vertical Line to (relative)
relative = true;
case 'V': // Vertical Line to (absolute)
// 1 Element ...
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
for (int iii = 0; iii < listDot.length; iii++) {
out.lineToV(relative, Float.parseFloat(listDot[iii]));
for (final String element : listDot) {
out.lineToV(relative, Float.parseFloat(element));
}
break;
case 'h': // Horizantal Line to (relative)
relative = true;
case 'H': // Horizantal Line to (absolute)
// 1 Element ...
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
for (int iii = 0; iii < listDot.length; iii++) {
out.lineToH(relative, Float.parseFloat(listDot[iii]));
for (final String element : listDot) {
out.lineToH(relative, Float.parseFloat(element));
}
break;
case 'q': // Quadratic Bezier curve (relative)
relative = true;
case 'Q': // Quadratic Bezier curve (absolute)
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
// 4 Elements ...
if (listDot.length % 4 != 0) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot.length + " (must have 4 numbers)");
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = "
+ listDot.length + " (must have 4 numbers)");
break;
}
for (int iii = 0; iii < listDot.length; iii += 4) {
out.bezierCurveTo(relative, new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])),
out.bezierCurveTo(relative,
new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])),
new Vector2f(Float.parseFloat(listDot[iii + 2]), Float.parseFloat(listDot[iii + 3])));
}
break;
case 't': // smooth quadratic Bezier curve to (relative)
relative = true;
case 'T': // smooth quadratic Bezier curve to (absolute)
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
// 4 Elements ...
if (listDot.length % 2 != 0) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot.length + " (must have 2 numbers)");
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = "
+ listDot.length + " (must have 2 numbers)");
break;
}
// 2 Elements ...
for (int iii = 0; iii < listDot.length; iii += 2) {
out.bezierSmoothCurveTo(relative, new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])));
out.bezierSmoothCurveTo(relative,
new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])));
}
break;
case 'c': // curve to (relative)
relative = true;
case 'C': // curve to (absolute)
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
// 6 Elements ...
if (listDot.length % 6 != 0) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot.length + "(Must be a multiple of 6)");
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = "
+ listDot.length + "(Must be a multiple of 6)");
break;
}
for (int iii = 0; iii < listDot.length; iii += 6) {
out.curveTo(relative, new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])),
out.curveTo(relative,
new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])),
new Vector2f(Float.parseFloat(listDot[iii + 2]), Float.parseFloat(listDot[iii + 3])),
new Vector2f(Float.parseFloat(listDot[iii + 4]), Float.parseFloat(listDot[iii + 5])));
}
break;
case 's': // smooth curve to (relative)
relative = true;
case 'S': // smooth curve to (absolute)
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
// 4 Elements ...
if (listDot.length % 4 != 0) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot.length + "(Must be a multiple of 4)");
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = "
+ listDot.length + "(Must be a multiple of 4)");
break;
}
for (int iii = 0; iii < listDot.length; iii += 4) {
out.smoothCurveTo(relative, new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])),
out.smoothCurveTo(relative,
new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])),
new Vector2f(Float.parseFloat(listDot[iii + 2]), Float.parseFloat(listDot[iii + 3])));
}
break;
case 'a': // elliptical Arc (relative)
relative = true;
case 'A': // elliptical Arc (absolute)
if (listDot == null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = " + listDot);
break;
}
// 4 element ff,ff f i,i ff,ff Elements ...
if (listDot.length % 7 != 0) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot.length);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = "
+ listDot.length);
break;
}
for (int iii = 0; iii < listDot.length; iii += 7) {
@ -213,7 +228,9 @@ public class Path extends Base {
if (Integer.parseInt(listDot[iii + 4]) == 0) {
sweepFlag = false;
}
out.ellipticTo(relative, new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])), Float.parseFloat(listDot[iii + 2]), largeArcFlag, sweepFlag,
out.ellipticTo(relative,
new Vector2f(Float.parseFloat(listDot[iii]), Float.parseFloat(listDot[iii + 1])),
Float.parseFloat(listDot[iii + 2]), largeArcFlag, sweepFlag,
new Vector2f(Float.parseFloat(listDot[iii + 5]), Float.parseFloat(listDot[iii + 6])));
}
break;
@ -222,38 +239,39 @@ public class Path extends Base {
case 'Z': // closepath (absolute)
// 0 Element ...
if (listDot != null) {
Log.warning("the PATH command " + sss.cmd + " has not the good number of element = " + listDot.length);
LOGGER.warn("the PATH command " + sss.cmd + " has not the good number of element = "
+ listDot.length);
break;
}
out.close(relative);
break;
default:
Log.error("Unknow error : '" + sss.cmd + "'");
LOGGER.error("Unknow error : '" + sss.cmd + "'");
}
}
return out;
}
//return the next char position ... (after 'X' or NULL)
private static Command extractCmd(final List<String> input, final int offset) {
if (input.size() <= offset) {
// Log.warning("parse command : END");
// LOGGER.warn("parse command : END");
return null;
}
// Log.warning("parse command : (rest) " + offset);
// LOGGER.warn("parse command : (rest) " + offset);
// for (int iii = offset; iii < input.size(); iii++) {
// Log.warning(" -[" + iii + "] '" + input.get(iii) + "'");
// LOGGER.warn(" -[" + iii + "] '" + input.get(iii) + "'");
// }
if (input.get(offset).length() != 1) {
Log.error("Error in the SVG Path : '" + input.get(offset) + "' [" + Integer.toString(offset));
LOGGER.error("Error in the SVG Path : '" + input.get(offset) + "' [" + Integer.toString(offset));
return null;
}
final char cmd = input.get(offset).charAt(0);
if (!((cmd <= 'Z' && cmd >= 'A') || (cmd <= 'z' && cmd >= 'a'))) {
Log.error("Error in the SVG Path : '" + cmd + "' [" + Integer.toString(offset));
LOGGER.error("Error in the SVG Path : '" + cmd + "' [" + Integer.toString(offset));
return null;
}
//Log.verbose("Find command : " + cmd);
//LOGGER.trace("Find command : " + cmd);
if (input.size() == offset) {
return new Command(cmd, offset + 1);
}
@ -275,7 +293,7 @@ public class Path extends Base {
}
return new Command(cmd, outputList, iii);
}
static List<String> splitCommand(final String data) {
final List<String> out = new ArrayList<>();
final StringBuilder tmpString = new StringBuilder(20);
@ -300,7 +318,7 @@ public class Path extends Base {
isNumber = false;
out.add(Character.toString(it));
} else {
Log.error("Can not parse path : '" + it + "'");
LOGGER.error("Can not parse path : '" + it + "'");
}
}
final String elements = tmpString.toString();
@ -309,31 +327,32 @@ public class Path extends Base {
}
return out;
}
public PathModel listElement = new PathModel();
public Path(final PaintState parentPaintState) {
super(parentPaintState);
}
public Path(final PathModel elements, final PaintState parentPaintState) {
super(parentPaintState);
this.listElement = elements;
}
@Override
void display(final int spacing) {
this.listElement.display(spacing);
}
@Override
void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::Path");
LOGGER.trace(spacingDist(level) + "DRAW esvg::Path");
final Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
PointList listPoints = new PointList();
listPoints = this.listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
listPoints = this.listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(),
myRenderer.getInterpolationThreshold());
//listPoints.applyMatrix(mtx);
final SegmentList listSegmentFill = new SegmentList();
final SegmentList listSegmentStroke = new SegmentList();
@ -354,7 +373,8 @@ public class Path extends Base {
}
// check if we need to display stroke:
if (colorStroke != null) {
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap,
this.paint.lineJoin, this.paint.miterLimit);
colorStroke.setViewPort(listSegmentStroke.getViewPort());
listSegmentStroke.applyMatrix(mtx);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
@ -366,15 +386,20 @@ public class Path extends Base {
//myRenderer.addDebugSegment(listSegmentStroke);
//this.listElement.debugInformation.applyMatrix(mtx);
//myRenderer.addDebugSegment(this.listElement.debugInformation);
}
@Override
void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW Shape esvg::Path");
void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW Shape esvg::Path");
final Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
PointList listPoints = new PointList();
listPoints = this.listElement.generateListPoints(level, recurtionMax, threshold);
listPoints.applyMatrix(mtx);
@ -386,7 +411,7 @@ public class Path extends Base {
out.add(listPoint);
}
}
@Override
boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
if (element == null) {
@ -394,17 +419,17 @@ public class Path extends Base {
}
parseTransform(element);
parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
final String elementXML1 = element.getAttribute("d", "");
if (elementXML1.length() == 0) {
Log.warning("path: missing 'd' attribute or empty");
LOGGER.warn("path: missing 'd' attribute or empty");
return false;
}
this.listElement = Path.createPathModel(elementXML1);
return this.listElement != null;
}
}

View File

@ -1,5 +1,8 @@
package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.render.DynamicColor;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.Point;
@ -10,11 +13,8 @@ import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -23,14 +23,15 @@ import org.atriasoft.esvg.internal.Log;
*/
public class Polygon extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Polygon.class);
private final List<Vector2f> listPoint = new ArrayList<>(); //!< list of all point of the polygone
public Polygon(final PaintState parentPaintState) {
super(parentPaintState);
}
private PathModel createPath() {
PathModel out = new PathModel();
final PathModel out = new PathModel();
out.moveTo(false, this.listPoint.get(0));
for (int iii = 1; iii < this.listPoint.size(); iii++) {
out.lineTo(false, this.listPoint.get(iii));
@ -38,28 +39,29 @@ public class Polygon extends Base {
out.close();
return out;
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "Polygon nbPoint=" + this.listPoint.size());
LOGGER.debug(spacingDist(spacing) + "Polygon nbPoint=" + this.listPoint.size());
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::Polygon");
PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
LOGGER.trace(spacingDist(level) + "DRAW esvg::Polygon");
final PathModel listElement = createPath();
final Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
PointList listPoints = new PointList();
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(),
myRenderer.getInterpolationThreshold());
//listPoints.applyMatrix(mtx);
SegmentList listSegmentFill = new SegmentList();
SegmentList listSegmentStroke = new SegmentList();
Weight tmpFill = new Weight();
Weight tmpStroke = new Weight();
DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
final SegmentList listSegmentFill = new SegmentList();
final SegmentList listSegmentStroke = new SegmentList();
final Weight tmpFill = new Weight();
final Weight tmpStroke = new Weight();
final DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
DynamicColor colorStroke = null;
if (this.paint.strokeWidth > 0.0f) {
colorStroke = DynamicColor.createColor(this.paint.stroke, mtx);
@ -74,7 +76,8 @@ public class Polygon extends Base {
}
// check if we need to display stroke:
if (colorStroke != null) {
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap,
this.paint.lineJoin, this.paint.miterLimit);
colorStroke.setViewPort(listSegmentStroke.getViewPort());
listSegmentStroke.applyMatrix(mtx);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
@ -85,24 +88,29 @@ public class Polygon extends Base {
//myRenderer.addDebugSegment(listSegmentFill);
//myRenderer.addDebugSegment(listSegmentStroke);
}
@Override
public void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW Shape esvg::Polygon");
PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
public void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW Shape esvg::Polygon");
final PathModel listElement = createPath();
final Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
PointList listPoints;
listPoints = listElement.generateListPoints(level, recurtionMax, threshold);
listPoints.applyMatrix(mtx);
for (List<Point> it : listPoints.data) {
List<Vector2f> listPoint = new ArrayList<>();
for (Point itDot : it) {
for (final List<Point> it : listPoints.data) {
final List<Vector2f> listPoint = new ArrayList<>();
for (final Point itDot : it) {
listPoint.add(itDot.pos);
}
out.add(listPoint);
}
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
if (element == null) {
@ -110,25 +118,25 @@ public class Polygon extends Base {
}
parseTransform(element);
parsePaintAttr(element);
Log.verbose("parsed P1. trans: " + this.transformMatrix);
LOGGER.trace("parsed P1. trans: " + this.transformMatrix);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
Log.verbose("parsed P2. trans: " + this.transformMatrix);
String sss1 = element.getAttribute("points", "");
LOGGER.trace("parsed P2. trans: " + this.transformMatrix);
final String sss1 = element.getAttribute("points", "");
if (sss1.length() == 0) {
Log.error("(l "/*+element.Pos()*/ + ") polygon: missing points attribute");
LOGGER.error("(l "/*+element.Pos()*/ + ") polygon: missing points attribute");
return false;
}
sizeMax.value = Vector2f.ZERO;
Log.verbose("Parse polyline : '" + sss1 + "'");
String[] elems = sss1.split(" ");
for (String elem : elems) {
Vector2f pos = Vector2f.valueOf(elem);
LOGGER.trace("Parse polyline : '" + sss1 + "'");
final String[] elems = sss1.split(" ");
for (final String elem : elems) {
final Vector2f pos = Vector2f.valueOf(elem);
this.listPoint.add(pos);
sizeMax.value = Vector2f.max(sizeMax.value, pos);
}

View File

@ -1,5 +1,8 @@
package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.render.DynamicColor;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.Point;
@ -10,11 +13,8 @@ import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -22,14 +22,15 @@ import org.atriasoft.esvg.internal.Log;
* @license MPL v2.0 (see license file)
*/
public class Polyline extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Polyline.class);
private final List<Vector2f> listPoint = new ArrayList<>(); //!< list of all point of the polyline
public Polyline(final PaintState parentPaintState) {
super(parentPaintState);
}
private PathModel createPath() {
PathModel out = new PathModel();
final PathModel out = new PathModel();
out.clear();
out.moveTo(false, this.listPoint.get(0));
for (int iii = 1; iii < this.listPoint.size(); iii++) {
@ -38,28 +39,29 @@ public class Polyline extends Base {
out.stop();
return out;
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "Polyline nbPoint=" + this.listPoint.size());
LOGGER.debug(spacingDist(spacing) + "Polyline nbPoint=" + this.listPoint.size());
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::Polyline");
PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
LOGGER.trace(spacingDist(level) + "DRAW esvg::Polyline");
final PathModel listElement = createPath();
final Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
PointList listPoints;
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(),
myRenderer.getInterpolationThreshold());
//listPoints.applyMatrix(mtx);
SegmentList listSegmentFill = new SegmentList();
SegmentList listSegmentStroke = new SegmentList();
Weight tmpFill = new Weight();
Weight tmpStroke = new Weight();
DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
final SegmentList listSegmentFill = new SegmentList();
final SegmentList listSegmentStroke = new SegmentList();
final Weight tmpFill = new Weight();
final Weight tmpStroke = new Weight();
final DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
DynamicColor colorStroke = null;
if (this.paint.strokeWidth > 0.0f) {
colorStroke = DynamicColor.createColor(this.paint.stroke, mtx);
@ -74,7 +76,8 @@ public class Polyline extends Base {
}
// check if we need to display stroke:
if (colorStroke != null) {
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap,
this.paint.lineJoin, this.paint.miterLimit);
colorStroke.setViewPort(listSegmentStroke.getViewPort());
listSegmentStroke.applyMatrix(mtx);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
@ -85,24 +88,29 @@ public class Polyline extends Base {
//myRenderer.addDebugSegment(listSegmentFill);
//myRenderer.addDebugSegment(listSegmentStroke);
}
@Override
public void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW Shape esvg::Polyline");
PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
public void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW Shape esvg::Polyline");
final PathModel listElement = createPath();
final Matrix2x3f mtx = this.transformMatrix.multiply(basicTrans);
PointList listPoints;
listPoints = listElement.generateListPoints(level, recurtionMax, threshold);
listPoints.applyMatrix(mtx);
for (List<Point> it : listPoints.data) {
List<Vector2f> listPoint = new ArrayList<>();
for (Point itDot : it) {
for (final List<Point> it : listPoints.data) {
final List<Vector2f> listPoint = new ArrayList<>();
for (final Point itDot : it) {
listPoint.add(itDot.pos);
}
out.add(listPoint);
}
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
// line must have a minimum size...
@ -112,20 +120,20 @@ public class Polyline extends Base {
}
parseTransform(element);
parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
String sss1 = element.getAttribute("points", "");
final String sss1 = element.getAttribute("points", "");
if (sss1.length() == 0) {
Log.error("polyline: missing points attribute");
LOGGER.error("polyline: missing points attribute");
return false;
}
sizeMax.value = Vector2f.ZERO;
Log.verbose("Parse polyline : '" + sss1 + "'");
String[] elems = sss1.split(" ");
for (String elem : elems) {
Vector2f pos = Vector2f.valueOf(elem);
LOGGER.trace("Parse polyline : '" + sss1 + "'");
final String[] elems = sss1.split(" ");
for (final String elem : elems) {
final Vector2f pos = Vector2f.valueOf(elem);
this.listPoint.add(pos);
sizeMax.value = Vector2f.max(sizeMax.value, pos);
}

View File

@ -3,7 +3,6 @@ package org.atriasoft.esvg;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.Color;
import org.atriasoft.etk.Dimension1f;
import org.atriasoft.etk.Dimension2f;
@ -15,6 +14,8 @@ import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.etk.util.Pair;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -22,6 +23,7 @@ import org.atriasoft.exml.model.XmlNode;
* @license MPL v2.0 (see license file)
*/
public class RadialGradient extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(RadialGradient.class);
private Dimension2f center = new Dimension2f(new Vector2f(50, 50), Distance.POURCENT); //!< gradient position cx cy
private final List<Pair<Float, Color>> data = new ArrayList<>(); //!< incompatible with href
private Dimension2f focal = new Dimension2f(new Vector2f(50, 50), Distance.POURCENT); //!< gradient Focal fx fy
@ -29,58 +31,59 @@ public class RadialGradient extends Base {
private Dimension1f radius = new Dimension1f(50, Distance.POURCENT); //!< Radius of the gradient
public SpreadMethod spread = SpreadMethod.PAD;
public GradientUnits unit = GradientUnits.GRADIENT_UNITS_OBJECT_BOUNDING_BOX;
public RadialGradient(final PaintState parentPaintState) {
super(parentPaintState);
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "RadialGradient center=" + this.center + " focal=" + this.focal + " radius=" + this.radius);
for (Pair<Float, Color> it : this.data) {
Log.debug(spacingDist(spacing + 1) + "STOP: offset=" + it.first + " color=" + it.second);
LOGGER.debug(spacingDist(spacing) + "RadialGradient center=" + this.center + " focal=" + this.focal + " radius="
+ this.radius);
for (final Pair<Float, Color> it : this.data) {
LOGGER.debug(spacingDist(spacing + 1) + "STOP: offset=" + it.first + " color=" + it.second);
}
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::RadialGradient");
LOGGER.trace(spacingDist(level) + "DRAW esvg::RadialGradient");
}
public Dimension2f getCenter() {
return this.center;
}
public List<Pair<Float, Color>> getColors(final EsvgDocument document) {
if (this.href.isEmpty()) {
return this.data;
}
if (document == null) {
Log.error("Get null input for document");
LOGGER.error("Get null input for document");
return this.data;
}
Base base = document.getReference(this.href);
final Base base = document.getReference(this.href);
if (base == null) {
Log.error("Can not get base : '" + this.href + "'");
LOGGER.error("Can not get base : '" + this.href + "'");
return this.data;
}
if (base instanceof RadialGradient gradientR) {
if (base instanceof final RadialGradient gradientR) {
return gradientR.getColors(document);
}
if (base instanceof LinearGradient gradientL) {
if (base instanceof final LinearGradient gradientL) {
return gradientL.getColors(document);
}
return this.data;
}
public Dimension2f getFocal() {
return this.focal;
}
public Dimension1f getRadius() {
return this.radius;
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
// line must have a minimum size...
@ -88,16 +91,16 @@ public class RadialGradient extends Base {
if (element == null) {
return false;
}
// ---------------- get unique ID ----------------
this.id = element.getAttribute("id", "");
//parseTransform(element);
//parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
String contentX = element.getAttribute("cx", "");
String contentY = element.getAttribute("cy", "");
if (!contentX.isEmpty() && !contentY.isEmpty()) {
@ -118,7 +121,8 @@ public class RadialGradient extends Base {
} else {
this.unit = GradientUnits.GRADIENT_UNITS_OBJECT_BOUNDING_BOX;
if (contentX.length() != 0 && contentX != "objectBoundingBox") {
Log.error("Parsing error of 'gradientUnits' ==> not suported value: '" + contentX + "' not in : {userSpaceOnUse/objectBoundingBox} use objectBoundingBox");
LOGGER.error("Parsing error of 'gradientUnits' ==> not suported value: '" + contentX
+ "' not in : {userSpaceOnUse/objectBoundingBox} use objectBoundingBox");
}
}
contentX = element.getAttribute("spreadMethod", "");
@ -129,7 +133,8 @@ public class RadialGradient extends Base {
} else {
this.spread = SpreadMethod.PAD;
if (contentX.length() != 0 && !contentX.equals("pad")) {
Log.error("Parsing error of 'spreadMethod' ==> not suported value: '" + contentX + "' not in : {reflect/repeate/pad} use pad");
LOGGER.error("Parsing error of 'spreadMethod' ==> not suported value: '" + contentX
+ "' not in : {reflect/repeate/pad} use pad");
}
}
// note: xlink:href is incompatible with subNode "stop"
@ -138,19 +143,20 @@ public class RadialGradient extends Base {
this.href = this.href.substring(1);
}
// parse all sub node :
for (XmlNode it : element.getNodes()) {
if (it instanceof XmlElement child) {
for (final XmlNode it : element.getNodes()) {
if (it instanceof final XmlElement child) {
if (child.getValue().equals("stop")) {
float offset = 100;
Color stopColor = Color.NONE;
String content = child.getAttribute("offset", "");
if (content.length() != 0) {
Pair<Float, Distance> tmp = parseLength2(content);
final Pair<Float, Distance> tmp = parseLength2(content);
if (tmp.second == Distance.PIXEL) {
// special case ==> all time % then no type define ==> % in [0.0 .. 1.0]
offset = tmp.first * 100.0f;
} else if (tmp.second != Distance.POURCENT) {
Log.error("offset : " + content + " res=" + tmp.first + "," + tmp.second + " Not support other than pourcent %");
LOGGER.error("offset : " + content + " res=" + tmp.first + "," + tmp.second
+ " Not support other than pourcent %");
} else {
offset = tmp.first;
}
@ -158,28 +164,28 @@ public class RadialGradient extends Base {
content = child.getAttribute("stop-color", "");
if (content.length() != 0) {
stopColor = parseColor(content).first;
Log.verbose(" color : \"" + content + "\" == > " + stopColor);
LOGGER.trace(" color : \"" + content + "\" == > " + stopColor);
}
content = child.getAttribute("stop-opacity", "");
if (content.length() != 0) {
float opacity = parseLength(content);
opacity = FMath.avg(0.0f, opacity, 1.0f);
stopColor = stopColor.withA(opacity);
Log.verbose(" opacity : '" + content + "' == > " + stopColor);
LOGGER.trace(" opacity : '" + content + "' == > " + stopColor);
}
this.data.add(new Pair<>(offset, stopColor));
} else {
Log.error("node not suported : '" + child.getValue() + "' must be [stop]");
LOGGER.error("node not suported : '" + child.getValue() + "' must be [stop]");
}
}
}
if (this.data.size() != 0) {
if (!this.href.isEmpty()) {
Log.error("node can not have an xlink:href element with sub node named: stop ==> removing href");
LOGGER.error("node can not have an xlink:href element with sub node named: stop ==> removing href");
this.href = "";
}
}
return true;
}
}

View File

@ -1,20 +1,20 @@
package org.atriasoft.esvg;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.exml.model.XmlElement;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.DynamicColor;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.Point;
import org.atriasoft.esvg.render.PointList;
import org.atriasoft.esvg.render.SegmentList;
import org.atriasoft.esvg.render.Weight;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -22,29 +22,31 @@ import org.atriasoft.etk.util.Dynamic;
* @license MPL v2.0 (see license file)
*/
public class Rectangle extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Rectangle.class);
private Vector2f position = Vector2f.ZERO; //!< position of the rectangle
private Vector2f roundedCorner = Vector2f.ZERO; //!< property of the rounded corner
private Vector2f size = Vector2f.ZERO; //!< size of the rectangle
public Rectangle(final PaintState parentPaintState) {
super(parentPaintState);
}
public Rectangle(final Vector2f position, final Vector2f size, final PaintState parentPaintState) {
super(parentPaintState);
this.position = position;
this.size = size;
}
public Rectangle(final Vector2f position, final Vector2f size, final Vector2f roundedCorner, final PaintState parentPaintState) {
public Rectangle(final Vector2f position, final Vector2f size, final Vector2f roundedCorner,
final PaintState parentPaintState) {
super(parentPaintState);
this.position = position;
this.size = size;
this.roundedCorner = roundedCorner;
}
private PathModel createPath() {
PathModel out = new PathModel();
final PathModel out = new PathModel();
out.clear();
if (this.roundedCorner.x() == 0.0f || this.roundedCorner.y() == 0.0f) {
out.moveTo(false, this.position);
@ -55,43 +57,50 @@ public class Rectangle extends Base {
// Rounded rectangle
out.moveTo(false, this.position.add(this.roundedCorner.x(), 0.0f));
out.lineToH(true, this.size.x() - this.roundedCorner.x() * 2.0f);
out.curveTo(true, new Vector2f(this.roundedCorner.x() * Base.kappa90, 0.0f), new Vector2f(this.roundedCorner.x(), this.roundedCorner.y() * (1.0f - Base.kappa90)),
out.curveTo(true, new Vector2f(this.roundedCorner.x() * Base.kappa90, 0.0f),
new Vector2f(this.roundedCorner.x(), this.roundedCorner.y() * (1.0f - Base.kappa90)),
new Vector2f(this.roundedCorner.x(), this.roundedCorner.y()));
out.lineToV(true, this.size.y() - this.roundedCorner.y() * 2.0f);
out.curveTo(true, new Vector2f(0.0f, this.roundedCorner.y() * Base.kappa90), new Vector2f(-this.roundedCorner.x() * (1.0f - Base.kappa90), this.roundedCorner.y()),
out.curveTo(true, new Vector2f(0.0f, this.roundedCorner.y() * Base.kappa90),
new Vector2f(-this.roundedCorner.x() * (1.0f - Base.kappa90), this.roundedCorner.y()),
new Vector2f(-this.roundedCorner.x(), this.roundedCorner.y()));
out.lineToH(true, -(this.size.x() - this.roundedCorner.x() * 2.0f));
out.curveTo(true, new Vector2f(-this.roundedCorner.x() * Base.kappa90, 0.0f), new Vector2f(-this.roundedCorner.x(), -this.roundedCorner.y() * (1.0f - Base.kappa90)),
out.curveTo(true, new Vector2f(-this.roundedCorner.x() * Base.kappa90, 0.0f),
new Vector2f(-this.roundedCorner.x(), -this.roundedCorner.y() * (1.0f - Base.kappa90)),
new Vector2f(-this.roundedCorner.x(), -this.roundedCorner.y()));
out.lineToV(true, -(this.size.y() - this.roundedCorner.y() * 2.0f));
out.curveTo(true, new Vector2f(0.0f, -this.roundedCorner.y() * Base.kappa90), new Vector2f(this.roundedCorner.x() * (1.0f - Base.kappa90), -this.roundedCorner.y()),
out.curveTo(true, new Vector2f(0.0f, -this.roundedCorner.y() * Base.kappa90),
new Vector2f(this.roundedCorner.x() * (1.0f - Base.kappa90), -this.roundedCorner.y()),
new Vector2f(this.roundedCorner.x(), -this.roundedCorner.y()));
}
out.close();
return out;
}
@Override
public void display(final int spacing) {
Log.debug(spacingDist(spacing) + "Rectangle : pos=" + this.position + " size=" + this.size + " corner=" + this.roundedCorner);
LOGGER.debug(spacingDist(spacing) + "Rectangle : pos=" + this.position + " size=" + this.size + " corner="
+ this.roundedCorner);
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::Rectangle: fill=" + this.paint.fill.first + "/" + this.paint.fill.second + " stroke=" + this.paint.stroke.first + "/" + this.paint.stroke.second);
PathModel listElement = createPath();
LOGGER.trace(spacingDist(level) + "DRAW esvg::Rectangle: fill=" + this.paint.fill.first + "/"
+ this.paint.fill.second + " stroke=" + this.paint.stroke.first + "/" + this.paint.stroke.second);
final PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix;
mtx = mtx.multiply(basicTrans);
listElement.display(2);
PointList listPoints = new PointList();
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(),
myRenderer.getInterpolationThreshold());
//listPoints.applyMatrix(mtx);
SegmentList listSegmentFill = new SegmentList();
SegmentList listSegmentStroke = new SegmentList();
Weight tmpFill = new Weight();
Weight tmpStroke = new Weight();
DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
final SegmentList listSegmentFill = new SegmentList();
final SegmentList listSegmentStroke = new SegmentList();
final Weight tmpFill = new Weight();
final Weight tmpStroke = new Weight();
final DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
DynamicColor colorStroke = null;
if (this.paint.strokeWidth > 0.0f) {
colorStroke = DynamicColor.createColor(this.paint.stroke, mtx);
@ -106,7 +115,8 @@ public class Rectangle extends Base {
}
// check if we need to display stroke:
if (colorStroke != null) {
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap,
this.paint.lineJoin, this.paint.miterLimit);
colorStroke.setViewPort(listSegmentStroke.getViewPort());
listSegmentStroke.applyMatrix(mtx);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
@ -116,27 +126,32 @@ public class Rectangle extends Base {
myRenderer.print(tmpFill, colorFill, tmpStroke, colorStroke, this.paint.opacity);
//myRenderer.addDebugSegment(listSegmentFill);
//myRenderer.addDebugSegment(listSegmentStroke)
}
@Override
public void drawShapePoints(final List<List<Vector2f>> out, final int recurtionMax, final float threshold, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW Shape esvg::Rectangle");
PathModel listElement = createPath();
public void drawShapePoints(
final List<List<Vector2f>> out,
final int recurtionMax,
final float threshold,
final Matrix2x3f basicTrans,
final int level) {
LOGGER.trace(spacingDist(level) + "DRAW Shape esvg::Rectangle");
final PathModel listElement = createPath();
Matrix2x3f mtx = this.transformMatrix;
mtx = mtx.multiply(basicTrans);
PointList listPoints;
listPoints = listElement.generateListPoints(level, recurtionMax, threshold);
listPoints.applyMatrix(mtx);
for (List<Point> it : listPoints.data) {
List<Vector2f> listPoint = new ArrayList<>();
for (Point itDot : it) {
for (final List<Point> it : listPoints.data) {
final List<Vector2f> listPoint = new ArrayList<>();
for (final Point itDot : it) {
listPoint.add(itDot.pos);
}
out.add(listPoint);
}
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
if (element == null) {
@ -145,16 +160,16 @@ public class Rectangle extends Base {
this.position = Vector2f.ZERO;
this.size = Vector2f.ZERO;
this.roundedCorner = Vector2f.ZERO;
parseTransform(element);
parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
this.position = parseXmlPosition(element);
this.size = parseXmlSize(element);
String content = element.getAttribute("rx", "");
if (content.length() != 0) {
this.roundedCorner = this.roundedCorner.withX(parseLength(content));
@ -163,7 +178,8 @@ public class Rectangle extends Base {
if (content.length() != 0) {
this.roundedCorner = this.roundedCorner.withY(parseLength(content));
}
sizeMax.value = new Vector2f(this.position.x() + this.size.x() + this.paint.strokeWidth, this.position.y() + this.size.y() + this.paint.strokeWidth);
sizeMax.value = new Vector2f(this.position.x() + this.size.x() + this.paint.strokeWidth,
this.position.y() + this.size.y() + this.paint.strokeWidth);
return true;
}
}

View File

@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.font.Glyph;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.DynamicColor;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.esvg.render.PointList;
@ -16,6 +15,8 @@ import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.atriasoft.exml.model.XmlText;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -23,106 +24,116 @@ import org.atriasoft.exml.model.XmlText;
* @license MPL v2.0 (see license file)
*/
public class Text extends Base {
static final Logger LOGGER = LoggerFactory.getLogger(Text.class);
private float fontSize = 42;
private Vector2f position = Vector2f.ZERO;
private final List<TextSpan> texts = new ArrayList<>();
public Text(final PaintState parentPaintState) {
super(parentPaintState);
}
public Text(final Vector2f position, final float fontSize, final String decoratedText, final PaintState parentPaintState) {
public Text(final Vector2f position, final float fontSize, final String decoratedText,
final PaintState parentPaintState) {
super(parentPaintState);
this.position = position;
this.fontSize = fontSize;
this.texts.add(new TextSpan(position, decoratedText, FontProperty.DEFAULT_FONT.withSize(fontSize), parentPaintState.clone()));
}
public Text(final Vector2f position, final String fontName,final float fontSize, final String decoratedText, final PaintState parentPaintState) {
super(parentPaintState);
this.position = position;
this.fontSize = fontSize;
this.texts.add(new TextSpan(position, decoratedText, FontProperty.DEFAULT_FONT.withSize(fontSize).withFontName(fontName), parentPaintState.clone()));
this.texts.add(new TextSpan(position, decoratedText, FontProperty.DEFAULT_FONT.withSize(fontSize),
parentPaintState.clone()));
}
public Text(final Vector2f position, final String fontName, final float fontSize, final String decoratedText,
final PaintState parentPaintState) {
super(parentPaintState);
this.position = position;
this.fontSize = fontSize;
this.texts.add(new TextSpan(position, decoratedText,
FontProperty.DEFAULT_FONT.withSize(fontSize).withFontName(fontName), parentPaintState.clone()));
}
@Override
public void display(final int spacing) {
Log.verbose(spacingDist(spacing) + "Text : ");
for (TextSpan elem : this.texts) {
Log.debug(spacingDist(spacing + 1) + elem.toString());
LOGGER.trace(spacingDist(spacing) + "Text : ");
for (final TextSpan elem : this.texts) {
LOGGER.debug(spacingDist(spacing + 1) + elem.toString());
}
}
@Override
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
Log.verbose(spacingDist(level) + "DRAW esvg::Text ==> position = " + this.position);
LOGGER.trace(spacingDist(level) + "DRAW esvg::Text ==> position = " + this.position);
if (this.texts.size() == 0) {
Log.verbose(spacingDist(level + 1) + "No text ...");
LOGGER.trace(spacingDist(level + 1) + "No text ...");
return;
}
boolean withKerning = true;
for (TextSpan elem : this.texts) {
final boolean withKerning = true;
for (final TextSpan elem : this.texts) {
// get the font or a generic font of the program.
EsvgFont font = FontCache.getFont(elem.fontState().fontName(), elem.fontState().bold(), elem.fontState().italic());
final EsvgFont font = FontCache.getFont(elem.fontState().fontName(), elem.fontState().bold(),
elem.fontState().italic());
if (font == null) {
Log.error("Can not get the font :" + elem.fontState());
LOGGER.error("Can not get the font :" + elem.fontState());
return;
}
int realSize = font.calculateFontRealHeight((int) elem.fontState().fontSize());
float scale = realSize / font.getUnitsPerEm();
//Log.warning("elem.fontState() =" + elem.fontState());
//Log.warning("scale =" + scale + " font size = " + elem.fontState().fontSize() + " realSize=" + realSize);
final int realSize = font.calculateFontRealHeight((int) elem.fontState().fontSize());
final float scale = realSize / font.getUnitsPerEm();
//LOGGER.warn("elem.fontState() =" + elem.fontState());
//LOGGER.warn("scale =" + scale + " font size = " + elem.fontState().fontSize() + " realSize=" + realSize);
float offsetWriting = 0;
int lastValue = 0;
for (char uVal : elem.text().toCharArray()) {
Log.verbose(spacingDist(level) + " elem.position = " + elem.position());
Glyph glyph = font.getGlyph(uVal);
for (final char uVal : elem.text().toCharArray()) {
LOGGER.trace(
spacingDist(level) + " elem.position = " + elem.position());
final Glyph glyph = font.getGlyph(uVal);
if (glyph == null) {
//lastValue = uVal;
continue;
}
if (withKerning) {
offsetWriting -= glyph.getKerning(lastValue) * scale;
Log.verbose(" ==> kerning offset = " + (glyph.getKerning(lastValue) * scale));
LOGGER.trace(" ==> kerning offset = " + (glyph.getKerning(lastValue) * scale));
lastValue = uVal;
}
float advenceXLocal = glyph.getHorizAdvX() * scale;
final float advenceXLocal = glyph.getHorizAdvX() * scale;
//Matrix2x3f mtx = this.transformMatrix;
Vector2f tranlate = new Vector2f(elem.position().x() + offsetWriting, elem.position().y() - font.getDescent() * scale);
Log.verbose("translate : " + tranlate);
Matrix2x3f translateGlyph = Matrix2x3f.createTranslate(tranlate);
Matrix2x3f scaleGlyph = Matrix2x3f.createScale(new Vector2f(scale, -scale));
final Vector2f tranlate = new Vector2f(elem.position().x() + offsetWriting,
elem.position().y() - font.getDescent() * scale);
LOGGER.trace("translate : " + tranlate);
final Matrix2x3f translateGlyph = Matrix2x3f.createTranslate(tranlate);
final Matrix2x3f scaleGlyph = Matrix2x3f.createScale(new Vector2f(scale, -scale));
//Matrix2x3f translateGlyph = Matrix2x3f.createTranslate(tranlate).multiply(Matrix2x3f.createScale(scale));
//mtx = translateGlyph.multiply(this.transformMatrix);
//mtx = mtx.multiply(translateGlyph);
//mtx = mtx.multiply(basicTrans);
//Matrix2x3f mtx = this.transformMatrix;
//mtx = mtx.multiply(basicTrans);
Matrix2x3f mtx = scaleGlyph;
mtx = mtx.multiply(translateGlyph);
mtx = mtx.multiply(this.transformMatrix);
mtx = mtx.multiply(basicTrans);
//Matrix2x3f mtx = this.transformMatrix;
//mtx = mtx.multiply(basicTrans);
PathModel listElement = glyph.getModel();
final PathModel listElement = glyph.getModel();
if (listElement != null) {
//--------------------------------------------------
// -- Generate Fill weight
//--------------------------------------------------
PointList listPoints = listElement.generateListPoints(level, myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
Weight tmpFill = new Weight();
final PointList listPoints = listElement.generateListPoints(level,
myRenderer.getInterpolationRecurtionMax(), myRenderer.getInterpolationThreshold());
final DynamicColor colorFill = DynamicColor.createColor(this.paint.fill, mtx);
final Weight tmpFill = new Weight();
// Check if we need to display background
if (colorFill != null) {
SegmentList listSegmentFill = new SegmentList();
final SegmentList listSegmentFill = new SegmentList();
listSegmentFill.createSegmentList(listPoints);
colorFill.setViewPort(listSegmentFill.getViewPort());
listSegmentFill.applyMatrix(mtx);
@ -130,36 +141,38 @@ public class Text extends Base {
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
tmpFill.generate(myRenderer.getSize(), myRenderer.getNumberSubScanLine(), listSegmentFill);
}
//--------------------------------------------------
// -- Generate Stroke weight
//--------------------------------------------------
Weight tmpStroke = new Weight();
final Weight tmpStroke = new Weight();
DynamicColor colorStroke = null;
if (this.paint.strokeWidth > 0.0f) {
colorStroke = DynamicColor.createColor(this.paint.stroke, mtx);
if (colorStroke == null) {
Log.verbose("Color stroke is null: ...");
LOGGER.trace("Color stroke is null: ...");
} else {
// check if we need to display stroke:
SegmentList listSegmentStroke = new SegmentList();
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth, this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
final SegmentList listSegmentStroke = new SegmentList();
listSegmentStroke.createSegmentListStroke(listPoints, this.paint.strokeWidth,
this.paint.lineCap, this.paint.lineJoin, this.paint.miterLimit);
colorStroke.setViewPort(listSegmentStroke.getViewPort());
listSegmentStroke.applyMatrix(mtx);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
tmpStroke.generate(myRenderer.getSize(), myRenderer.getNumberSubScanLine(), listSegmentStroke);
tmpStroke.generate(myRenderer.getSize(), myRenderer.getNumberSubScanLine(),
listSegmentStroke);
}
}
// add on images:
myRenderer.print(tmpFill, colorFill, tmpStroke, colorStroke, this.paint.opacity);
}
offsetWriting += advenceXLocal;
//Log.warning("offset X =" + offsetWriting + " + " + advenceXLocal + " " + uVal);
//LOGGER.warn("offset X =" + offsetWriting + " + " + advenceXLocal + " " + uVal);
}
}
}
@Override
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
// line must have a minimum size...
@ -169,37 +182,37 @@ public class Text extends Base {
}
parseTransform(element);
parsePaintAttr(element);
// add the property of the parrent modifications ...
this.transformMatrix = this.transformMatrix.multiply(parentTrans);
boolean italic = false;
String fontStyle = element.getAttribute("font-style", "normal");
final String fontStyle = element.getAttribute("font-style", "normal");
if ("italic".equals(fontStyle)) {
italic = true;
} else if ("normal".equals(fontStyle)) {
italic = false;
} else {
Log.error("can not parse font-style='" + fontStyle + "' support ['normal', 'italic']");
LOGGER.error("can not parse font-style='" + fontStyle + "' support ['normal', 'italic']");
}
boolean bold = false;
String fontWeight = element.getAttribute("font-weight", "normal");
final String fontWeight = element.getAttribute("font-weight", "normal");
if ("bold".equals(fontWeight)) {
bold = true;
} else if ("normal".equals(fontWeight)) {
bold = false;
} else {
Log.error("can not parse font-weight='" + fontWeight + "' support ['normal', 'bold']");
LOGGER.error("can not parse font-weight='" + fontWeight + "' support ['normal', 'bold']");
}
String fontFamily = element.getAttribute("font-family", "FreeSans");
if (fontStyle.contains(";")) {
fontFamily = fontFamily.split(";")[0];
}
Log.info("Get font family: '" + fontFamily + "'");
float fontSize = parseLength(element.getAttribute("font-size", "50"));
LOGGER.info("Get font family: '" + fontFamily + "'");
final float fontSize = parseLength(element.getAttribute("font-size", "50"));
this.position = Vector2f.ZERO;
String content = element.getAttribute("x", "");
if (content.length() != 0) {
this.position = this.position.withX(parseLength(content));
@ -208,31 +221,32 @@ public class Text extends Base {
if (content.length() != 0) {
this.position = this.position.withY(parseLength(content));
}
// parse all subElement in the Text <TSPAN/>
for (XmlNode elem : element.getNodes()) {
if (elem instanceof XmlElement elementSpan && "tspan".equals(elementSpan.getValue())) {
} else if (elem instanceof XmlText elementText) {
this.texts.add(new TextSpan(this.position, elementText.getValue(), new FontProperty(fontFamily, fontSize, bold, italic), this.paint.clone()));
for (final XmlNode elem : element.getNodes()) {
if (elem instanceof final XmlElement elementSpan && "tspan".equals(elementSpan.getValue())) {
} else if (elem instanceof final XmlText elementText) {
this.texts.add(new TextSpan(this.position, elementText.getValue(),
new FontProperty(fontFamily, fontSize, bold, italic), this.paint.clone()));
} else {
Log.warning("not managed element : " + elem);
LOGGER.warn("not managed element : " + elem);
}
}
//sizeMax.value = Vector2f.max(this.startPos, this.stopPos);
return true;
}
/*
public Weight render(final String data, final int fontSize, final boolean withKerning) {
int widthOut = calculateWidth(data, fontSize, withKerning);
int realSize = calculateFontRealHeight(fontSize);
float scale = realSize / (float) this.unitsPerEm;
Weight weight = new Weight(new Vector2i(widthOut, realSize));
int offsetWriting = 0;
int lastValue = 0;
for (char uVal : data.toCharArray()) {
@ -243,12 +257,12 @@ public class Text extends Base {
}
if (withKerning) {
offsetWriting -= glyph.getKerning(lastValue) * scale;
Log.info(" ==> kerning offset = " + (glyph.getKerning(lastValue) * scale));
LOGGER.info(" ==> kerning offset = " + (glyph.getKerning(lastValue) * scale));
lastValue = uVal;
}
float advenceXLocal = glyph.getHorizAdvX() * scale;
RenderingConfig config = new RenderingConfig(10, 0.25f, 8);
Matrix2x3f transform = Matrix2x3f.createTranslate(new Vector2f(0, -this.descent)).multiply(Matrix2x3f.createScale(scale));
PathModel model = glyph.getModel();
@ -257,7 +271,7 @@ public class Text extends Base {
weight.fusion(redered, offsetWriting, 0);
}
offsetWriting += advenceXLocal;
}
return weight;
}
@ -269,17 +283,21 @@ record FontProperty(
float fontSize,
boolean bold,
boolean italic) {
public static final FontProperty DEFAULT_FONT = new FontProperty("FreeSans", 15, false, false);
public FontProperty withSize(final float fontSize) {
return new FontProperty(this.fontName, fontSize, this.bold, this.italic);
}
public FontProperty withFontName(final String fontName) {
return new FontProperty(fontName, this.fontSize, this.bold, this.italic);
}
public FontProperty withBold(final boolean bold) {
return new FontProperty(this.fontName, this.fontSize, bold, this.italic);
}
public FontProperty withSize(final boolean italic) {
return new FontProperty(this.fontName, this.fontSize, this.bold, italic);
}

View File

@ -5,77 +5,80 @@ import java.util.List;
import org.atriasoft.esvg.EsvgFont;
import org.atriasoft.esvg.Path;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.PathModel;
import org.atriasoft.exml.model.XmlElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Glyph {
private static final boolean LAZY_MODE = true;
static final Logger LOGGER = LoggerFactory.getLogger(Glyph.class);
public static Glyph valueOf(final XmlElement element) {
return Glyph.valueOf(element, null);
}
public static Glyph valueOf(final XmlElement element, final EsvgFont font) {
if (element == null) {
return null;
}
String name = element.getAttribute("glyph-name", null);
Log.verbose("get glyph name = '" + name + "'");
String tmpValue = element.getAttribute("horiz-adv-x", null);
final String name = element.getAttribute("glyph-name", null);
LOGGER.trace("get glyph name = '" + name + "'");
final String tmpValue = element.getAttribute("horiz-adv-x", null);
int horizAdvX = font == null ? 100 : font.getHorizAdvX();
if (tmpValue != null && tmpValue.length() != 0) {
horizAdvX = Integer.parseInt(tmpValue);
}
Log.verbose(" horizAdvX= '" + horizAdvX + "'");
String unicode = element.getAttribute("unicode", null);
Log.verbose(" unicode= '" + unicode + "'");
LOGGER.trace(" horizAdvX= '" + horizAdvX + "'");
final String unicode = element.getAttribute("unicode", null);
LOGGER.trace(" unicode= '" + unicode + "'");
if (unicode == null) {
Log.debug("Not manage glyph : '" + name + "' (missing unicode value)");
LOGGER.debug("Not manage glyph : '" + name + "' (missing unicode value)");
return null;
}
String d = element.getAttribute("d", null);
Log.verbose(" d= '" + d + "'");
final String d = element.getAttribute("d", null);
LOGGER.trace(" d= '" + d + "'");
int unicodeValue = 0;
if (unicode.startsWith("&#x") && unicode.endsWith(";")) {
String subElement = unicode.substring(3, unicode.length() - 1);
final String subElement = unicode.substring(3, unicode.length() - 1);
if (subElement.indexOf("&") != -1) {
Log.debug("not supported glyph concatenarion" + name + " value='" + unicode + "'");
LOGGER.debug("not supported glyph concatenarion" + name + " value='" + unicode + "'");
return null;
}
unicodeValue = Integer.parseInt(subElement, 16);
} else if (unicode.startsWith("&#") && unicode.endsWith(";")) {
String subElement = unicode.substring(2, unicode.length() - 1);
final String subElement = unicode.substring(2, unicode.length() - 1);
if (subElement.indexOf("&") != -1) {
Log.debug("not supported glyph concatenarion" + name + " value='" + unicode + "'");
LOGGER.debug("not supported glyph concatenarion" + name + " value='" + unicode + "'");
return null;
}
unicodeValue = Integer.parseInt(subElement, 16);
} else if (unicode.length() != 1) {
Log.debug("not supported glyph concatenarion" + name + " value='" + unicode + "'");
LOGGER.debug("not supported glyph concatenarion" + name + " value='" + unicode + "'");
return null;
} else {
unicodeValue = unicode.charAt(0);
}
Log.verbose(" unicodeValue= '" + unicodeValue + "'");
Glyph out = new Glyph(horizAdvX, d, name, unicode, unicodeValue);
LOGGER.trace(" unicodeValue= '" + unicodeValue + "'");
final Glyph out = new Glyph(horizAdvX, d, name, unicode, unicodeValue);
if (!Glyph.LAZY_MODE) {
// when not in lazy mode we force the parsing of the model, this permit to check the whole font... otherwise many font is really big > 8000 glyph, then it is a waste of time...
out.getModel();
}
return out;
}
private int horizAdvX;
private List<Kerning> kernings = new ArrayList<>();
private PathModel model;
private String name;
private final String path;
private String unicode;
private int unicodeValue;
public Glyph(final int horizAdvX, final PathModel model, final String name, final String unicode, final int unicodeValue) {
public Glyph(final int horizAdvX, final PathModel model, final String name, final String unicode,
final int unicodeValue) {
this.horizAdvX = horizAdvX;
this.model = model;
this.path = null;
@ -83,8 +86,9 @@ public class Glyph {
this.unicode = unicode;
this.unicodeValue = unicodeValue;
}
public Glyph(final int horizAdvX, final String path, final String name, final String unicode, final int unicodeValue) {
public Glyph(final int horizAdvX, final String path, final String name, final String unicode,
final int unicodeValue) {
this.horizAdvX = horizAdvX;
this.model = null;
this.path = path;
@ -92,73 +96,74 @@ public class Glyph {
this.unicode = unicode;
this.unicodeValue = unicodeValue;
}
public void addKerning(final List<Kerning> elementsKerning) {
this.kernings.addAll(elementsKerning);
}
public int getHorizAdvX() {
return this.horizAdvX;
}
public float getKerning(final int unicodeValue) {
if (unicodeValue == 0) {
return 0.0f;
}
for (Kerning elem : this.kernings) {
for (final Kerning elem : this.kernings) {
if (elem.unicode() == unicodeValue) {
Log.verbose("Get kerning between : '" + (char) this.unicodeValue + "' and '" + (char) unicodeValue + "' => " + elem.offset());
LOGGER.trace("Get kerning between : '" + (char) this.unicodeValue + "' and '" + (char) unicodeValue
+ "' => " + elem.offset());
return elem.offset();
}
}
return 0;
}
public List<Kerning> getKernings() {
return this.kernings;
}
public PathModel getModel() {
if (this.model == null && this.path != null) {
this.model = Path.createPathModel(this.path);
}
return this.model;
}
public String getName() {
return this.name;
}
public String getUnicode() {
return this.unicode;
}
public Integer getUnicodeValue() {
return this.unicodeValue;
}
public void setHorizAdvX(final int horizAdvX) {
this.horizAdvX = horizAdvX;
}
public void setKernings(final List<Kerning> kernings) {
this.kernings = kernings;
}
public void setModel(final PathModel model) {
this.model = model;
}
public void setName(final String name) {
this.name = name;
}
public void setUnicode(final String unicode) {
this.unicode = unicode;
}
public void setUnicodeValue(final int unicodeValue) {
this.unicodeValue = unicodeValue;
}
}

View File

@ -1,76 +0,0 @@
package org.atriasoft.esvg.internal;
import org.atriasoft.reggol.LogLevel;
import org.atriasoft.reggol.Logger;
public class Log {
private static final boolean FORCE_ALL = false;
private static final String LIB_NAME = "esvg";
private static final String LIB_NAME_DRAW = Logger.getDrawableName(Log.LIB_NAME);
private static final boolean PRINT_CRITICAL = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.CRITICAL);
private static final boolean PRINT_DEBUG = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.DEBUG);
private static final boolean PRINT_ERROR = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.ERROR);
private static final boolean PRINT_INFO = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.INFO);
private static final boolean PRINT_PRINT = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.PRINT);
private static final boolean PRINT_TODO = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.TODO);
private static final boolean PRINT_VERBOSE = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.VERBOSE);
private static final boolean PRINT_WARNING = Logger.getNeedPrint(Log.LIB_NAME, LogLevel.WARNING);
public static void critical(final Exception e, final String data) {
e.printStackTrace();
if (PRINT_CRITICAL || FORCE_ALL) {
Logger.critical(LIB_NAME_DRAW, data + " : " + e.getMessage());
}
}
public static void critical(final String data, final Object... objects) {
if (PRINT_CRITICAL || FORCE_ALL) {
Logger.critical(LIB_NAME_DRAW, data, objects);
}
}
public static void debug(final String data, final Object... objects) {
if (PRINT_DEBUG || FORCE_ALL) {
Logger.debug(LIB_NAME_DRAW, data, objects);
}
}
public static void error(final String data, final Object... objects) {
if (PRINT_ERROR || FORCE_ALL) {
Logger.error(LIB_NAME_DRAW, data, objects);
}
}
public static void info(final String data, final Object... objects) {
if (PRINT_INFO || FORCE_ALL) {
Logger.info(LIB_NAME_DRAW, data, objects);
}
}
public static void print(final String data, final Object... objects) {
if (PRINT_PRINT || FORCE_ALL) {
Logger.print(LIB_NAME_DRAW, data, objects);
}
}
public static void todo(final String data, final Object... objects) {
if (PRINT_TODO || FORCE_ALL) {
Logger.todo(LIB_NAME_DRAW, data, objects);
}
}
public static void verbose(final String data, final Object... objects) {
if (PRINT_VERBOSE || FORCE_ALL) {
Logger.verbose(LIB_NAME_DRAW, data, objects);
}
}
public static void warning(final String data, final Object... objects) {
if (PRINT_WARNING || FORCE_ALL) {
Logger.warning(LIB_NAME_DRAW, data, objects);
}
}
private Log() {}
}

View File

@ -1,12 +1,13 @@
package org.atriasoft.esvg.render;
import org.atriasoft.esvg.EsvgDocument;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.Color;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.math.Vector2i;
import org.atriasoft.etk.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -15,22 +16,24 @@ import org.atriasoft.etk.util.Pair;
*/
public interface DynamicColor {
static final Logger LOGGER = LoggerFactory.getLogger(DynamicColor.class);
public static DynamicColor createColor(final Pair<Color, String> color, final Matrix2x3f mtx) {
// Check if need to create a color:
if (color.first.a() == 0 && color.second.isEmpty()) {
return null;
}
if (color.second.isEmpty()) {
Log.verbose("use stroke color :" + color);
LOGGER.trace("use stroke color :" + color);
return new DynamicColorUni(color.first);
}
return new DynamicColorSpecial(color.second, mtx);
}
void generate(EsvgDocument document);
public Color getColor(Vector2i pos);
public void setViewPort(Pair<Vector2f, Vector2f> viewPort);
}

View File

@ -8,7 +8,6 @@ import org.atriasoft.esvg.GradientUnits;
import org.atriasoft.esvg.LinearGradient;
import org.atriasoft.esvg.RadialGradient;
import org.atriasoft.esvg.SpreadMethod;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.Color;
import org.atriasoft.etk.Dimension1f;
import org.atriasoft.etk.Dimension2f;
@ -18,38 +17,54 @@ import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.math.Vector2i;
import org.atriasoft.etk.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DynamicColorSpecial implements DynamicColor {
protected static Vector2f getIntersect(final Vector2f point1, final Vector2f vect1, final Vector2f point2, final Vector2f vect2) {
float diviseur = vect1.x() * vect2.y() - vect1.y() * vect2.x();
static final Logger LOGGER = LoggerFactory.getLogger(DynamicColorSpecial.class);
protected static Vector2f getIntersect(
final Vector2f point1,
final Vector2f vect1,
final Vector2f point2,
final Vector2f vect2) {
final float diviseur = vect1.x() * vect2.y() - vect1.y() * vect2.x();
if (diviseur != 0.0f) {
float mmm = (vect1.x() * point1.y() - vect1.x() * point2.y() - vect1.y() * point1.x() + vect1.y() * point2.x()) / diviseur;
final float mmm = (vect1.x() * point1.y() - vect1.x() * point2.y() - vect1.y() * point1.x()
+ vect1.y() * point2.x()) / diviseur;
return point2.add(vect2.multiply(mmm));
}
Log.error("Get divider / 0.0f");
LOGGER.error("Get divider / 0.0f");
return point2;
}
protected static Pair<Vector2f, Vector2f> intersectLineToCircle(final Vector2f pos1, final Vector2f pos2) {
return DynamicColorSpecial.intersectLineToCircle(pos1, pos2, Vector2f.ZERO);
}
protected static Pair<Vector2f, Vector2f> intersectLineToCircle(final Vector2f pos1, final Vector2f pos2, final Vector2f center) {
protected static Pair<Vector2f, Vector2f> intersectLineToCircle(
final Vector2f pos1,
final Vector2f pos2,
final Vector2f center) {
return DynamicColorSpecial.intersectLineToCircle(pos1, pos2, center, 1.0f);
}
protected static Pair<Vector2f, Vector2f> intersectLineToCircle(final Vector2f pos1, final Vector2f pos2, final Vector2f center, final float radius) {
protected static Pair<Vector2f, Vector2f> intersectLineToCircle(
final Vector2f pos1,
final Vector2f pos2,
final Vector2f center,
final float radius) {
Vector2f v1;
Vector2f v2;
//vector2D from point 1 to point 2
v1 = pos2.less(pos1);
//vector2D from point 1 to the circle's center
v2 = center.less(pos1);
float dot = v1.dot(v2);
Vector2f proj1 = new Vector2f(((dot / (v1.length2())) * v1.x()), ((dot / (v1.length2())) * v1.y()));
Vector2f midpt = pos1.add(proj1);
final float dot = v1.dot(v2);
final Vector2f proj1 = new Vector2f(((dot / (v1.length2())) * v1.x()), ((dot / (v1.length2())) * v1.y()));
final Vector2f midpt = pos1.add(proj1);
float distToCenter = midpt.less(center).length2();
if (distToCenter > radius * radius) {
return new Pair<>(Vector2f.ZERO, Vector2f.ZERO);
@ -69,7 +84,7 @@ public class DynamicColorSpecial implements DynamicColor {
v1 = v1.multiply(distToIntersection);
return new Pair<>(midpt.add(v1), midpt.less(v1));
}
public Vector2f axeX;
public Vector2f axeY;
public Vector2f baseSize;
@ -81,56 +96,56 @@ public class DynamicColorSpecial implements DynamicColor {
public float focalLength;
public boolean linear;
public Matrix2x3f matrix;
public Vector2f pos1; // in radius ==> center
public Vector2f pos2; // in radius ==> radius end position
public SpreadMethod spread;
public GradientUnits unit;
public Pair<Vector2f, Vector2f> viewPort;
public DynamicColorSpecial(final String link, final Matrix2x3f mtx) {
this.linear = true;
this.colorName = link;
this.matrix = mtx;
this.viewPort = new Pair<>(Vector2f.MAX_VALUE, Vector2f.MAX_VALUE);
}
@Override
public void generate(final EsvgDocument document) {
if (document == null) {
Log.error("Get null input for document");
LOGGER.error("Get null input for document");
}
Base base = document.getReference(this.colorName);
final Base base = document.getReference(this.colorName);
if (base == null) {
Log.error("Can not get base : '" + this.colorName + "'");
LOGGER.error("Can not get base : '" + this.colorName + "'");
return;
}
// Now we can know if we use linear or radial gradient ...
if (base instanceof LinearGradient gradient) {
if (base instanceof final LinearGradient gradient) {
this.linear = true;
Log.verbose("get for color linear:");
LOGGER.trace("get for color linear:");
gradient.display(2);
this.unit = gradient.unit;
this.spread = gradient.spread;
Log.verbose(" viewport = {" + this.viewPort.first + "," + this.viewPort.second + "}");
Vector2f size = this.viewPort.second.less(this.viewPort.first);
Dimension2f dimPos1 = gradient.getPosition1();
LOGGER.trace(" viewport = {" + this.viewPort.first + "," + this.viewPort.second + "}");
final Vector2f size = this.viewPort.second.less(this.viewPort.first);
final Dimension2f dimPos1 = gradient.getPosition1();
this.pos1 = dimPos1.getPixel(size);
if (dimPos1.getType() == Distance.POURCENT) {
this.pos1 = this.pos1.add(this.viewPort.first);
}
Dimension2f dimPos2 = gradient.getPosition2();
final Dimension2f dimPos2 = gradient.getPosition2();
this.pos2 = dimPos2.getPixel(size);
if (dimPos2.getType() == Distance.POURCENT) {
this.pos2 = this.pos2.add(this.viewPort.first);
}
// in the basic vertion of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object..
Vector2f delta = this.pos2.less(this.pos1);
final Vector2f delta = this.pos2.less(this.pos1);
if (delta.x() < 0.0f) {
this.axeX = new Vector2f(-1.0f, 0.0f);
} else {
@ -147,41 +162,41 @@ public class DynamicColorSpecial implements DynamicColor {
this.axeX = this.matrix.applyScaleRotation(this.axeX);
this.axeY = this.matrix.applyScaleRotation(this.axeY);
// in the basic vertion of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object..
Vector2f intersecX = DynamicColorSpecial.getIntersect(this.pos1, this.axeX, this.pos2, this.axeY);
Vector2f intersecY = DynamicColorSpecial.getIntersect(this.pos1, this.axeY, this.pos2, this.axeX);
final Vector2f intersecX = DynamicColorSpecial.getIntersect(this.pos1, this.axeX, this.pos2, this.axeY);
final Vector2f intersecY = DynamicColorSpecial.getIntersect(this.pos1, this.axeY, this.pos2, this.axeX);
this.baseSize = new Vector2f((this.pos1.less(intersecX)).length(), (this.pos1.less(intersecY)).length());
// get all the colors
this.data = gradient.getColors(document);
} else {
this.linear = false;
if (!(base instanceof RadialGradient gradient)) {
Log.error("Can not cast in a linear gradient: '" + this.colorName + "' ==> wrong type");
if (!(base instanceof final RadialGradient gradient)) {
LOGGER.error("Can not cast in a linear gradient: '" + this.colorName + "' ==> wrong type");
return;
}
Log.verbose("get for color Radial:");
LOGGER.trace("get for color Radial:");
gradient.display(2);
this.unit = gradient.unit;
this.spread = gradient.spread;
Log.verbose(" viewport = {" + this.viewPort.first + "," + this.viewPort.second + "}");
Vector2f size = this.viewPort.second.less(this.viewPort.first);
Dimension2f dimCenter = gradient.getCenter();
LOGGER.trace(" viewport = {" + this.viewPort.first + "," + this.viewPort.second + "}");
final Vector2f size = this.viewPort.second.less(this.viewPort.first);
final Dimension2f dimCenter = gradient.getCenter();
Vector2f center = dimCenter.getPixel(size);
if (dimCenter.getType() == Distance.POURCENT) {
center = center.add(this.viewPort.first);
}
Dimension2f dimFocal = gradient.getFocal();
final Dimension2f dimFocal = gradient.getFocal();
Vector2f focal = dimFocal.getPixel(size);
if (dimFocal.getType() == Distance.POURCENT) {
focal = focal.add(this.viewPort.first);
}
Dimension1f dimRadius = gradient.getRadius();
final Dimension1f dimRadius = gradient.getRadius();
// in the basic vertion of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object)..
if (center == focal) {
this.centerIsFocal = true;
this.pos2 = new Vector2f(dimRadius.getPixel(size.x()), dimRadius.getPixel(size.y()));
this.pos2 = this.pos2.add(center);
Vector2f delta = center.less(this.pos2);
final Vector2f delta = center.less(this.pos2);
if (delta.x() < 0.0f) {
this.axeX = new Vector2f(-1.0f, 0.0f);
} else {
@ -197,8 +212,9 @@ public class DynamicColorSpecial implements DynamicColor {
this.centerIsFocal = false;
this.axeX = center.less(focal).safeNormalize();
this.axeY = new Vector2f(this.axeX.y(), -this.axeX.x());
this.pos2 = this.axeX.multiply(dimRadius.getPixel(size.x())).add(this.axeY.multiply(dimRadius.getPixel(size.y())));
this.pos2 = this.axeX.multiply(dimRadius.getPixel(size.x()))
.add(this.axeY.multiply(dimRadius.getPixel(size.y())));
this.pos2 = this.pos2.add(center);
this.pos1 = center;
}
@ -209,25 +225,26 @@ public class DynamicColorSpecial implements DynamicColor {
this.axeX = this.matrix.applyScaleRotation(this.axeX);
this.axeY = this.matrix.applyScaleRotation(this.axeY);
// in the basic version of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object..
Vector2f intersecX = DynamicColorSpecial.getIntersect(this.pos1, this.axeX, this.pos2, this.axeY);
Vector2f intersecY = DynamicColorSpecial.getIntersect(this.pos1, this.axeY, this.pos2, this.axeX);
final Vector2f intersecX = DynamicColorSpecial.getIntersect(this.pos1, this.axeX, this.pos2, this.axeY);
final Vector2f intersecY = DynamicColorSpecial.getIntersect(this.pos1, this.axeY, this.pos2, this.axeX);
this.baseSize = new Vector2f((intersecX.less(this.pos1)).length(), (intersecY.less(this.pos1)).length());
if (!this.centerIsFocal) {
this.focalLength = (center.less(this.matrix.multiply(focal))).length();
if (this.focalLength >= this.baseSize.x()) {
Log.debug("Change position of the Focal ... ==> set it inside the circle");
LOGGER.debug("Change position of the Focal ... ==> set it inside the circle");
this.focalLength = this.baseSize.x() * 0.999998f;
this.clipOut = true;
} else {
this.clipOut = false;
}
}
Log.verbose("baseSize=" + this.baseSize + " this.pos1=" + this.pos1 + " dim=" + dimCenter + " this.focal=" + this.focal + " this.pos2=" + this.pos2 + " dim=" + dimRadius);
LOGGER.trace("baseSize=" + this.baseSize + " this.pos1=" + this.pos1 + " dim=" + dimCenter
+ " this.focal=" + this.focal + " this.pos2=" + this.pos2 + " dim=" + dimRadius);
// get all the colors
this.data = gradient.getColors(document);
}
}
@Override
public Color getColor(final Vector2i pos) {
if (this.data.size() < 2) {
@ -238,16 +255,17 @@ public class DynamicColorSpecial implements DynamicColor {
}
return getColorRadial(pos);
}
private Color getColorLinear(final Vector2i pos) {
float ratio = 0.0f;
if (this.unit == GradientUnits.GRADIENT_UNITS_USER_SPACE_ON_USE) {
Vector2f vectorBase = this.pos2.less(this.pos1);
Vector2f vectorOrtho = new Vector2f(vectorBase.y(), -vectorBase.x());
Vector2f intersec = DynamicColorSpecial.getIntersect(this.pos1, vectorBase, new Vector2f(pos.x(), pos.y()), vectorOrtho);
float baseSize = vectorBase.length();
Vector2f vectorBaseDraw = intersec.less(this.pos1);
float baseDraw = vectorBaseDraw.length();
final Vector2f vectorBase = this.pos2.less(this.pos1);
final Vector2f vectorOrtho = new Vector2f(vectorBase.y(), -vectorBase.x());
final Vector2f intersec = DynamicColorSpecial.getIntersect(this.pos1, vectorBase,
new Vector2f(pos.x(), pos.y()), vectorOrtho);
final float baseSize = vectorBase.length();
final Vector2f vectorBaseDraw = intersec.less(this.pos1);
final float baseDraw = vectorBaseDraw.length();
ratio = baseDraw / baseSize;
switch (this.spread) {
default:
@ -267,7 +285,7 @@ public class DynamicColorSpecial implements DynamicColor {
ratio *= -1.0;
}
ratio -= ((int) (ratio));
if (ratio < 0.0f) {
ratio = 1.0f - FMath.abs(ratio);
}
@ -275,10 +293,12 @@ public class DynamicColorSpecial implements DynamicColor {
}
} else {
// in the basic vertion of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object..
Vector2f intersecX = DynamicColorSpecial.getIntersect(this.pos1, this.axeX, new Vector2f(pos.x(), pos.y()), this.axeY);
Vector2f intersecY = DynamicColorSpecial.getIntersect(this.pos1, this.axeY, new Vector2f(pos.x(), pos.y()), this.axeX);
Vector2f vectorBaseDrawX = intersecX.less(this.pos1);
Vector2f vectorBaseDrawY = intersecY.less(this.pos1);
final Vector2f intersecX = DynamicColorSpecial.getIntersect(this.pos1, this.axeX,
new Vector2f(pos.x(), pos.y()), this.axeY);
final Vector2f intersecY = DynamicColorSpecial.getIntersect(this.pos1, this.axeY,
new Vector2f(pos.x(), pos.y()), this.axeX);
final Vector2f vectorBaseDrawX = intersecX.less(this.pos1);
final Vector2f vectorBaseDrawY = intersecY.less(this.pos1);
float baseDrawX = vectorBaseDrawX.length();
float baseDrawY = vectorBaseDrawY.length();
if (this.axeX.dot(vectorBaseDrawX) < 0) {
@ -289,7 +309,8 @@ public class DynamicColorSpecial implements DynamicColor {
}
if (this.baseSize.x() + this.baseSize.y() != 0.0f) {
if (this.baseSize.x() != 0.0f && this.baseSize.y() != 0.0f) {
ratio = (baseDrawX * this.baseSize.y() + baseDrawY * this.baseSize.x()) / (this.baseSize.x() * this.baseSize.y() * 2.0f);
ratio = (baseDrawX * this.baseSize.y() + baseDrawY * this.baseSize.x())
/ (this.baseSize.x() * this.baseSize.y() * 2.0f);
} else if (this.baseSize.x() != 0.0f) {
ratio = baseDrawX / this.baseSize.x();
} else {
@ -328,22 +349,29 @@ public class DynamicColorSpecial implements DynamicColor {
if (ratio <= this.data.get(iii).first * 0.01f) {
float localRatio = ratio - this.data.get(iii - 1).first * 0.01f;
localRatio = localRatio / ((this.data.get(iii).first - this.data.get(iii - 1).first) * 0.01f);
return new Color(this.data.get(iii - 1).second.r() * (1.0 - localRatio) + this.data.get(iii).second.r() * localRatio,
this.data.get(iii - 1).second.g() * (1.0 - localRatio) + this.data.get(iii).second.g() * localRatio,
this.data.get(iii - 1).second.b() * (1.0 - localRatio) + this.data.get(iii).second.b() * localRatio,
this.data.get(iii - 1).second.a() * (1.0 - localRatio) + this.data.get(iii).second.a() * localRatio);
return new Color(
this.data.get(iii - 1).second.r() * (1.0 - localRatio)
+ this.data.get(iii).second.r() * localRatio,
this.data.get(iii - 1).second.g() * (1.0 - localRatio)
+ this.data.get(iii).second.g() * localRatio,
this.data.get(iii - 1).second.b() * (1.0 - localRatio)
+ this.data.get(iii).second.b() * localRatio,
this.data.get(iii - 1).second.a() * (1.0 - localRatio)
+ this.data.get(iii).second.a() * localRatio);
}
}
return Color.GREEN;
}
private Color getColorRadial(final Vector2i pos) {
float ratio = 0.0f;
// in the basic vertion of the gradient the color is calculated with the ration in X and Y in the bonding box associated (it is rotate with the object)..
Vector2f intersecX = DynamicColorSpecial.getIntersect(this.pos1, this.axeX, new Vector2f(pos.x(), pos.y()), this.axeY);
Vector2f intersecY = DynamicColorSpecial.getIntersect(this.pos1, this.axeY, new Vector2f(pos.x(), pos.y()), this.axeX);
Vector2f vectorBaseDrawX = intersecX.less(this.pos1);
Vector2f vectorBaseDrawY = intersecY.less(this.pos1);
final Vector2f intersecX = DynamicColorSpecial.getIntersect(this.pos1, this.axeX,
new Vector2f(pos.x(), pos.y()), this.axeY);
final Vector2f intersecY = DynamicColorSpecial.getIntersect(this.pos1, this.axeY,
new Vector2f(pos.x(), pos.y()), this.axeX);
final Vector2f vectorBaseDrawX = intersecX.less(this.pos1);
final Vector2f vectorBaseDrawY = intersecY.less(this.pos1);
float baseDrawX = vectorBaseDrawX.length();
float baseDrawY = vectorBaseDrawY.length();
// specal case when focal == center (this is faster ...)
@ -376,15 +404,16 @@ public class DynamicColorSpecial implements DynamicColor {
if (this.clipOut && baseDrawX <= -1.0f) {
ratio = 1.0f;
} else {
float tmpLength = -this.focalLength / this.baseSize.x();
Vector2f focalCenter = new Vector2f(tmpLength, 0.0f);
Vector2f currentPoint = new Vector2f(baseDrawX, baseDrawY);
final float tmpLength = -this.focalLength / this.baseSize.x();
final Vector2f focalCenter = new Vector2f(tmpLength, 0.0f);
final Vector2f currentPoint = new Vector2f(baseDrawX, baseDrawY);
if (focalCenter == currentPoint) {
ratio = 0.0f;
} else {
Pair<Vector2f, Vector2f> positions = DynamicColorSpecial.intersectLineToCircle(focalCenter, currentPoint);
float lenghtBase = currentPoint.less(focalCenter).length();
float lenghtBorder1 = positions.first.less(focalCenter).length();
final Pair<Vector2f, Vector2f> positions = DynamicColorSpecial.intersectLineToCircle(focalCenter,
currentPoint);
final float lenghtBase = currentPoint.less(focalCenter).length();
final float lenghtBorder1 = positions.first.less(focalCenter).length();
//float lenghtBorder2 = positions.second.less(focalCenter).length();
ratio = lenghtBase / lenghtBorder1;
}
@ -418,15 +447,20 @@ public class DynamicColorSpecial implements DynamicColor {
if (ratio <= this.data.get(iii).first * 0.01f) {
float localRatio = ratio - this.data.get(iii - 1).first * 0.01f;
localRatio = localRatio / ((this.data.get(iii).first - this.data.get(iii - 1).first) * 0.01f);
return new Color(this.data.get(iii - 1).second.r() * (1.0 - localRatio) + this.data.get(iii).second.r() * localRatio,
this.data.get(iii - 1).second.g() * (1.0 - localRatio) + this.data.get(iii).second.g() * localRatio,
this.data.get(iii - 1).second.b() * (1.0 - localRatio) + this.data.get(iii).second.b() * localRatio,
this.data.get(iii - 1).second.a() * (1.0 - localRatio) + this.data.get(iii).second.a() * localRatio);
return new Color(
this.data.get(iii - 1).second.r() * (1.0 - localRatio)
+ this.data.get(iii).second.r() * localRatio,
this.data.get(iii - 1).second.g() * (1.0 - localRatio)
+ this.data.get(iii).second.g() * localRatio,
this.data.get(iii - 1).second.b() * (1.0 - localRatio)
+ this.data.get(iii).second.b() * localRatio,
this.data.get(iii - 1).second.a() * (1.0 - localRatio)
+ this.data.get(iii).second.a() * localRatio);
}
}
return Color.GREEN;
}
@Override
public void setViewPort(final Pair<Vector2f, Vector2f> viewPort) {
this.viewPort = viewPort;

View File

@ -5,11 +5,12 @@ import java.util.List;
import org.atriasoft.esvg.CapMode;
import org.atriasoft.esvg.JoinMode;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.math.FMath;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.math.Vector2i;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -18,29 +19,41 @@ import org.atriasoft.etk.math.Vector2i;
*/
public class PathModel {
private static void interpolateCubicBezier(final List<Point> listPoint, final int recurtionMax, final float threshold, final Vector2f pos1, final Vector2f pos2, final Vector2f pos3,
final Vector2f pos4, final int level, final PointType type) {
static final Logger LOGGER = LoggerFactory.getLogger(PathModel.class);
private static void interpolateCubicBezier(
final List<Point> listPoint,
final int recurtionMax,
final float threshold,
final Vector2f pos1,
final Vector2f pos2,
final Vector2f pos3,
final Vector2f pos4,
final int level,
final PointType type) {
if (level > recurtionMax) {
return;
}
Vector2f pos12 = pos1.add(pos2).multiply(0.5f);
Vector2f pos23 = pos2.add(pos3).multiply(0.5f);
Vector2f pos34 = pos3.add(pos4).multiply(0.5f);
final Vector2f pos12 = pos1.add(pos2).multiply(0.5f);
final Vector2f pos23 = pos2.add(pos3).multiply(0.5f);
final Vector2f pos34 = pos3.add(pos4).multiply(0.5f);
Vector2f delta = pos4.less(pos1);
float distance2 = Math.abs(((pos2.x() - pos4.x()) * delta.y() - (pos2.y() - pos4.y()) * delta.x()));
float distance3 = Math.abs(((pos3.x() - pos4.x()) * delta.y() - (pos3.y() - pos4.y()) * delta.x()));
final Vector2f delta = pos4.less(pos1);
final float distance2 = Math.abs(((pos2.x() - pos4.x()) * delta.y() - (pos2.y() - pos4.y()) * delta.x()));
final float distance3 = Math.abs(((pos3.x() - pos4.x()) * delta.y() - (pos3.y() - pos4.y()) * delta.x()));
if ((distance2 + distance3) * (distance2 + distance3) < threshold * delta.length2()) {
listPoint.add(new Point(pos4, type));
return;
}
Vector2f pos123 = pos12.add(pos23).multiply(0.5f);
Vector2f pos234 = pos23.add(pos34).multiply(0.5f);
Vector2f pos1234 = pos123.add(pos234).multiply(0.5f);
final Vector2f pos123 = pos12.add(pos23).multiply(0.5f);
final Vector2f pos234 = pos23.add(pos34).multiply(0.5f);
final Vector2f pos1234 = pos123.add(pos234).multiply(0.5f);
PathModel.interpolateCubicBezier(listPoint, recurtionMax, threshold, pos1, pos12, pos123, pos1234, level + 1, PointType.interpolation);
PathModel.interpolateCubicBezier(listPoint, recurtionMax, threshold, pos1234, pos234, pos34, pos4, level + 1, type);
PathModel.interpolateCubicBezier(listPoint, recurtionMax, threshold, pos1, pos12, pos123, pos1234, level + 1,
PointType.interpolation);
PathModel.interpolateCubicBezier(listPoint, recurtionMax, threshold, pos1234, pos234, pos34, pos4, level + 1,
type);
}
/**
@ -95,20 +108,24 @@ public class PathModel {
}
public void display(final int spacing) {
Log.warning(PathModel.spacingDist(spacing) + "Path");
for (Element it : this.listElement) {
LOGGER.warn(PathModel.spacingDist(spacing) + "Path");
for (final Element it : this.listElement) {
if (it == null) {
continue;
}
Log.warning(PathModel.spacingDist(spacing + 1) + it);
LOGGER.warn(PathModel.spacingDist(spacing + 1) + it);
}
}
public Weight drawFill(final Vector2i size, final Matrix2x3f basicTrans, final int level, final RenderingConfig config) {
public Weight drawFill(
final Vector2i size,
final Matrix2x3f basicTrans,
final int level,
final RenderingConfig config) {
PointList listPoints = new PointList();
listPoints = generateListPoints(level, config.recurtionMax(), config.interpolationThreshold());
SegmentList listSegment = new SegmentList();
Weight weight = new Weight();
final SegmentList listSegment = new SegmentList();
final Weight weight = new Weight();
// Check if we need to display background
listSegment.createSegmentList(listPoints);
listSegment.applyMatrix(basicTrans);
@ -117,11 +134,16 @@ public class PathModel {
return weight;
}
public Weight drawStroke(final Vector2i size, final Matrix2x3f basicTrans, final int level, final float strokeWidth, final RenderingConfig config) {
public Weight drawStroke(
final Vector2i size,
final Matrix2x3f basicTrans,
final int level,
final float strokeWidth,
final RenderingConfig config) {
PointList listPoints = new PointList();
listPoints = generateListPoints(level, config.recurtionMax(), config.interpolationThreshold());
SegmentList listSegment = new SegmentList();
Weight weight = new Weight();
final SegmentList listSegment = new SegmentList();
final Weight weight = new Weight();
// Check if we need to display background
listSegment.createSegmentListStroke(listPoints, strokeWidth, CapMode.BUTT, JoinMode.MITER, 4.0f);
listSegment.applyMatrix(basicTrans);
@ -130,7 +152,13 @@ public class PathModel {
return weight;
}
public void ellipticTo(final boolean relative, final Vector2f radius, final float angle, final boolean largeArcFlag, final boolean sweepFlag, final Vector2f pos) {
public void ellipticTo(
final boolean relative,
final Vector2f radius,
final float angle,
final boolean largeArcFlag,
final boolean sweepFlag,
final Vector2f pos) {
this.listElement.add(new ElementElliptic(relative, radius, angle, largeArcFlag, sweepFlag, pos));
}
@ -143,22 +171,23 @@ public class PathModel {
}
public PointList generateListPoints(final int level, final int recurtionMax, final float threshold) {
Log.verbose(PathModel.spacingDist(level) + "Generate List Points ... from a path");
PointList out = new PointList();
LOGGER.trace(PathModel.spacingDist(level) + "Generate List Points ... from a path");
final PointList out = new PointList();
List<Point> tmpListPoint = new ArrayList<>();
Vector2f lastPosition = Vector2f.ZERO;
Vector2f lastAngle = Vector2f.ZERO;
// Foreach element, we move in the path:
for (Element it : this.listElement) {
for (final Element it : this.listElement) {
if (it == null) {
continue;
}
Log.verbose(PathModel.spacingDist(level + 1) + " Draw : " + it.toString());
LOGGER.trace(PathModel.spacingDist(level + 1) + " Draw : " + it.toString());
switch (it.getType()) {
case STOP:
if (tmpListPoint.size() != 0) {
if (tmpListPoint.size() == 0) {
Log.warning(PathModel.spacingDist(level + 1) + " Request path stop of not starting path ...");
LOGGER.warn(
PathModel.spacingDist(level + 1) + " Request path stop of not starting path ...");
} else {
tmpListPoint.get(tmpListPoint.size() - 1).setEndPath();
out.addList(tmpListPoint);
@ -171,14 +200,17 @@ public class PathModel {
case CLOSE:
if (tmpListPoint.size() != 0) {
if (tmpListPoint.size() == 0) {
Log.warning(PathModel.spacingDist(level + 1) + " Request path close of not starting path ...");
LOGGER.warn(
PathModel.spacingDist(level + 1) + " Request path close of not starting path ...");
} else {
// find the previous tart of the path ...
tmpListPoint.get(0).type = PointType.join;
// Remove the last point if it is the same position...
Vector2f delta = (tmpListPoint.get(0).pos.less(tmpListPoint.get(tmpListPoint.size() - 1).pos)).abs();
final Vector2f delta = (tmpListPoint.get(0).pos
.less(tmpListPoint.get(tmpListPoint.size() - 1).pos)).abs();
if (delta.x() <= 0.00001 && delta.y() <= 0.00001) {
Log.verbose(" Remove point Z property : " + tmpListPoint.get(tmpListPoint.size() - 1).pos + " with delta=" + delta);
LOGGER.trace(" Remove point Z property : "
+ tmpListPoint.get(tmpListPoint.size() - 1).pos + " with delta=" + delta);
tmpListPoint.remove(tmpListPoint.size() - 1);
}
out.addList(tmpListPoint);
@ -244,14 +276,15 @@ public class PathModel {
if (tmpListPoint.size() == 0) {
tmpListPoint.add(new Point(lastPosition, PointType.join));
} {
Vector2f lastPosStore = lastPosition;
final Vector2f lastPosStore = lastPosition;
if (!it.getRelative()) {
lastPosition = Vector2f.ZERO;
}
Vector2f pos1 = lastPosition.add(it.getPos1());
Vector2f pos2 = lastPosition.add(it.getPos2());
Vector2f pos = lastPosition.add(it.getPos());
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, lastPosStore, pos1, pos2, pos, 0, PointType.join);
final Vector2f pos1 = lastPosition.add(it.getPos1());
final Vector2f pos2 = lastPosition.add(it.getPos2());
final Vector2f pos = lastPosition.add(it.getPos());
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, lastPosStore, pos1, pos2,
pos, 0, PointType.join);
lastPosition = pos;
lastAngle = pos2;
}
@ -261,15 +294,16 @@ public class PathModel {
if (tmpListPoint.size() == 0) {
tmpListPoint.add(new Point(lastPosition, PointType.join));
} {
Vector2f lastPosStore = lastPosition;
final Vector2f lastPosStore = lastPosition;
if (!it.getRelative()) {
lastPosition = Vector2f.ZERO;
}
Vector2f pos2 = lastPosition.add(it.getPos2());
Vector2f pos = lastPosition.add(it.getPos());
final Vector2f pos2 = lastPosition.add(it.getPos2());
final Vector2f pos = lastPosition.add(it.getPos());
// generate Pos 1
Vector2f pos1 = lastPosStore.multiply(2.0f).less(lastAngle);
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, lastPosStore, pos1, pos2, pos, 0, PointType.join);
final Vector2f pos1 = lastPosStore.multiply(2.0f).less(lastAngle);
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, lastPosStore, pos1, pos2,
pos, 0, PointType.join);
lastPosition = pos;
lastAngle = pos2;
}
@ -279,16 +313,17 @@ public class PathModel {
if (tmpListPoint.size() == 0) {
tmpListPoint.add(new Point(lastPosition, PointType.join));
} {
Vector2f lastPosStore = lastPosition;
final Vector2f lastPosStore = lastPosition;
if (!it.getRelative()) {
lastPosition = Vector2f.ZERO;
}
Vector2f pos = lastPosition.add(it.getPos());
Vector2f tmp1 = lastPosition.add(it.getPos1());
final Vector2f pos = lastPosition.add(it.getPos());
final Vector2f tmp1 = lastPosition.add(it.getPos1());
// generate pos1 and pos2
Vector2f pos1 = lastPosStore.add(tmp1.less(lastPosStore).multiply(0.666666666f));
Vector2f pos2 = pos.add(tmp1.less(pos).multiply(0.666666666f));
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, lastPosStore, pos1, pos2, pos, 0, PointType.join);
final Vector2f pos1 = lastPosStore.add(tmp1.less(lastPosStore).multiply(0.666666666f));
final Vector2f pos2 = pos.add(tmp1.less(pos).multiply(0.666666666f));
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, lastPosStore, pos1, pos2,
pos, 0, PointType.join);
lastPosition = pos;
lastAngle = tmp1;
}
@ -298,16 +333,17 @@ public class PathModel {
if (tmpListPoint.size() == 0) {
tmpListPoint.add(new Point(lastPosition, PointType.join));
} {
Vector2f lastPosStore = lastPosition;
final Vector2f lastPosStore = lastPosition;
if (!it.getRelative()) {
lastPosition = Vector2f.ZERO;
}
Vector2f pos = lastPosition.add(it.getPos());
Vector2f tmp1 = lastPosStore.multiply(2.0f).less(lastAngle);
final Vector2f pos = lastPosition.add(it.getPos());
final Vector2f tmp1 = lastPosStore.multiply(2.0f).less(lastAngle);
// generate pos1 and pos2
Vector2f pos1 = lastPosStore.add(tmp1.less(lastPosStore).multiply(0.666666666f));
Vector2f pos2 = pos.add(tmp1.less(pos).multiply(0.66666666f));
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, lastPosStore, pos1, pos2, pos, 0, PointType.join);
final Vector2f pos1 = lastPosStore.add(tmp1.less(lastPosStore).multiply(0.666666666f));
final Vector2f pos2 = pos.add(tmp1.less(pos).multiply(0.66666666f));
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, lastPosStore, pos1, pos2,
pos, 0, PointType.join);
lastPosition = pos;
lastAngle = tmp1;
}
@ -317,25 +353,28 @@ public class PathModel {
if (tmpListPoint.size() == 0) {
tmpListPoint.add(new Point(lastPosition, PointType.join));
} {
ElementElliptic tmpIt = (ElementElliptic) it;
Log.todo(PathModel.spacingDist(level + 1) + " Elliptic arc: radius=" + tmpIt.getPos1());
Log.todo(PathModel.spacingDist(level + 1) + " angle=" + tmpIt.angle);
Log.todo(PathModel.spacingDist(level + 1) + " this.largeArcFlag=" + tmpIt.largeArcFlag);
Log.todo(PathModel.spacingDist(level + 1) + " this.sweepFlag=" + tmpIt.sweepFlag);
final ElementElliptic tmpIt = (ElementElliptic) it;
LOGGER.info(
"TODO:" + PathModel.spacingDist(level + 1) + " Elliptic arc: radius=" + tmpIt.getPos1());
LOGGER.info("TODO:" + PathModel.spacingDist(level + 1) + " angle=" + tmpIt.angle);
LOGGER.info("TODO:" + PathModel.spacingDist(level + 1) + " this.largeArcFlag="
+ tmpIt.largeArcFlag);
LOGGER.info("TODO:" + PathModel.spacingDist(level + 1) + " this.sweepFlag="
+ tmpIt.sweepFlag);
Vector2f lastPosStore = lastPosition;
final Vector2f lastPosStore = lastPosition;
if (!it.getRelative()) {
lastPosition = Vector2f.ZERO;
}
Vector2f pos = lastPosition.add(it.getPos());
float rotationX = tmpIt.angle * ((float) Math.PI / 180.0f);
final Vector2f pos = lastPosition.add(it.getPos());
final float rotationX = tmpIt.angle * ((float) Math.PI / 180.0f);
Vector2f radius = tmpIt.getPos1();
//this.debugInformation.addSegment(lastPosStore, pos);
Vector2f delta = lastPosStore.less(pos);
float ddd = delta.length();
if (ddd < 1e-6f || radius.x() < 1e-6f || radius.y() < 1e-6f) {
Log.warning("Degenerate arc in Line");
LOGGER.warn("Degenerate arc in Line");
if (tmpListPoint.size() == 0) {
tmpListPoint.add(new Point(lastPosition, PointType.join));
}
@ -345,18 +384,21 @@ public class PathModel {
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
// procedure describe here : http://www.w3.org/TR/SVG11/implnote.html#ArcConversionCenterToEndpoint
// Compute delta'
Matrix2x3f matrixRotationCenter = Matrix2x3f.createRotate(-rotationX);
Vector2f deltaPrim = matrixRotationCenter.multiply(delta.multiply(0.5f));
ddd = (deltaPrim.x() * deltaPrim.x()) / (radius.x() * radius.x()) + (deltaPrim.y() * deltaPrim.y()) / (radius.y() * radius.y());
final Matrix2x3f matrixRotationCenter = Matrix2x3f.createRotate(-rotationX);
final Vector2f deltaPrim = matrixRotationCenter.multiply(delta.multiply(0.5f));
ddd = (deltaPrim.x() * deltaPrim.x()) / (radius.x() * radius.x())
+ (deltaPrim.y() * deltaPrim.y()) / (radius.y() * radius.y());
if (ddd > 1.0f) {
ddd = (float) Math.sqrt(ddd);
radius = radius.multiply(ddd);
}
// Compute center'
float sss = 0.0f;
float ssa = radius.x() * radius.x() * radius.y() * radius.y() - radius.x() * radius.x() * deltaPrim.y() * deltaPrim.y()
float ssa = radius.x() * radius.x() * radius.y() * radius.y()
- radius.x() * radius.x() * deltaPrim.y() * deltaPrim.y()
- radius.y() * radius.y() * deltaPrim.x() * deltaPrim.x();
float ssb = radius.x() * radius.x() * deltaPrim.y() * deltaPrim.y() + radius.y() * radius.y() * deltaPrim.x() * deltaPrim.x();
final float ssb = radius.x() * radius.x() * deltaPrim.y() * deltaPrim.y()
+ radius.y() * radius.y() * deltaPrim.x() * deltaPrim.x();
if (ssa < 0.0f) {
ssa = 0.0f;
}
@ -366,19 +408,21 @@ public class PathModel {
if (tmpIt.largeArcFlag == tmpIt.sweepFlag) {
sss *= -1.0f;
}
Vector2f centerPrime = new Vector2f(sss * radius.x() * deltaPrim.y() / radius.y(), sss * -radius.y() * deltaPrim.x() / radius.x());
final Vector2f centerPrime = new Vector2f(sss * radius.x() * deltaPrim.y() / radius.y(),
sss * -radius.y() * deltaPrim.x() / radius.x());
// Compute center from center'
Matrix2x3f matrix = Matrix2x3f.createRotate(rotationX);
Vector2f center = lastPosStore.multiply(pos).multiply(0.5f).add(matrix.multiply(centerPrime));
final Matrix2x3f matrix = Matrix2x3f.createRotate(rotationX);
final Vector2f center = lastPosStore.multiply(pos).multiply(0.5f)
.add(matrix.multiply(centerPrime));
//this.debugInformation.addSegment(center-Vector2f(3.0,3.0), center+Vector2f(3.0,3.0));
// this.debugInformation.addSegment(center-Vector2f(3.0,-3.0), center+Vector2f(3.0,-3.0));
// Calculate theta1, and delta theta.
Vector2f vectorA = deltaPrim.less(centerPrime).devide(radius);
Vector2f vectorB = deltaPrim.add(centerPrime).devide(radius.multiply(-1.0f));
final Vector2f vectorA = deltaPrim.less(centerPrime).devide(radius);
final Vector2f vectorB = deltaPrim.add(centerPrime).devide(radius.multiply(-1.0f));
//this.debugInformation.addSegment(center, center+vectorA*radius.x());
//this.debugInformation.addSegment(center, center+vectorB*radius.y());
// Initial angle
float theta1 = PathModel.vectorAngle(new Vector2f(1.0f, 0.0f), vectorA);
final float theta1 = PathModel.vectorAngle(new Vector2f(1.0f, 0.0f), vectorA);
// Delta angle
float deltaTheta = PathModel.vectorAngle(vectorA, vectorB);
// special case of invert angle...
@ -397,9 +441,9 @@ public class PathModel {
matrix.translate(center);
// Split arc into max 90 degree segments.
// The loop assumes an iteration per end point (including start and end), this +1.
int ndivs = (int) (Math.abs(deltaTheta) / ((float) Math.PI * 0.5f)) + 1;
final int ndivs = (int) (Math.abs(deltaTheta) / ((float) Math.PI * 0.5f)) + 1;
float hda = (deltaTheta / ndivs) * 0.5f;
final float hda = (deltaTheta / ndivs) * 0.5f;
float kappa = (float) Math.abs(4.0f / 3.0f * (1.0f - Math.cos(hda)) / Math.sin(hda));
if (deltaTheta < 0.0f) {
kappa = -kappa;
@ -407,21 +451,24 @@ public class PathModel {
Vector2f pointPosPrevious = Vector2f.ZERO;
Vector2f tangentPrevious = Vector2f.ZERO;
for (int iii = 0; iii <= ndivs; ++iii) {
float a = theta1 + deltaTheta * ((float) iii / (float) ndivs);
final float a = theta1 + deltaTheta * ((float) iii / (float) ndivs);
delta = new Vector2f(FMath.cos(a), FMath.sin(a));
// position
Vector2f pointPos = matrix.multiply(new Vector2f(delta.x() * radius.x(), delta.y() * radius.y()));
final Vector2f pointPos = matrix
.multiply(new Vector2f(delta.x() * radius.x(), delta.y() * radius.y()));
// tangent
Vector2f tangent = matrix.applyScaleRotation(new Vector2f(-delta.y() * radius.x() * kappa, delta.x() * radius.y() * kappa));
final Vector2f tangent = matrix.applyScaleRotation(
new Vector2f(-delta.y() * radius.x() * kappa, delta.x() * radius.y() * kappa));
if (iii > 0) {
Vector2f zlastPosStore = lastPosition;
final Vector2f zlastPosStore = lastPosition;
if (!it.getRelative()) {
lastPosition = Vector2f.ZERO;
}
Vector2f zpos1 = pointPosPrevious.add(tangentPrevious);
Vector2f zpos2 = pointPos.less(tangent);
Vector2f zpos = pointPos;
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, zlastPosStore, zpos1, zpos2, zpos, 0, PointType.join);
final Vector2f zpos1 = pointPosPrevious.add(tangentPrevious);
final Vector2f zpos2 = pointPos.less(tangent);
final Vector2f zpos = pointPos;
PathModel.interpolateCubicBezier(tmpListPoint, recurtionMax, threshold, zlastPosStore,
zpos1, zpos2, zpos, 0, PointType.join);
lastPosition = zpos;
lastAngle = zpos2;
}
@ -433,13 +480,13 @@ public class PathModel {
}
break;
default:
Log.error(PathModel.spacingDist(level + 1) + " Unknow PATH commant (internal error)");
LOGGER.error(PathModel.spacingDist(level + 1) + " Unknow PATH commant (internal error)");
break;
}
}
// special case : No request end of path ==> open path:
if (tmpListPoint.size() != 0) {
Log.verbose("Auto-end PATH");
LOGGER.trace("Auto-end PATH");
tmpListPoint.get(tmpListPoint.size() - 1).setEndPath();
out.addList(tmpListPoint);
tmpListPoint = new ArrayList<>();

View File

@ -1,7 +1,8 @@
package org.atriasoft.esvg.render;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.math.Vector2f;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -10,6 +11,7 @@ import org.atriasoft.etk.math.Vector2f;
*/
public class Point {
static final Logger LOGGER = LoggerFactory.getLogger(Point.class);
public Vector2f delta = Vector2f.ZERO;
public float len = 0;
public Vector2f miterAxe = Vector2f.ZERO;
@ -20,35 +22,35 @@ public class Point {
public Vector2f posNext = Vector2f.ZERO;
public Vector2f posPrevious = Vector2f.ZERO;
public PointType type;
public Point() {
this.pos = Vector2f.ZERO;
this.type = PointType.join;
}
public Point(final Vector2f pos) {
this.pos = pos;
this.type = PointType.join;
}
public Point(final Vector2f pos, final PointType type) {
this.pos = pos;
this.type = type;
}
void normalize(final Vector2f nextPoint) {
this.delta = nextPoint.less(this.pos);
this.len = this.delta.length();
}
void setEndPath() {
if (this.type == PointType.interpolation) {
Log.warning("Request stop path of an interpolate Point");
LOGGER.warn("Request stop path of an interpolate Point");
this.type = PointType.stop;
return;
}
if (this.type == PointType.stop) {
Log.warning("Request stop path of an STOP Point");
LOGGER.warn("Request stop path of an STOP Point");
return;
}
if (this.type == PointType.start) {

View File

@ -3,46 +3,48 @@ package org.atriasoft.esvg.render;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PointList {
static final Logger LOGGER = LoggerFactory.getLogger(PointList.class);
public List<List<Point>> data = new ArrayList<>();
public PointList() {
}
public void addList(final List<Point> list) {
this.data.add(list);
// TODO : Add a checker of correct list ...
}
public void applyMatrix(final Matrix2x3f transformationMatrix) {
for (List<Point> it : this.data) {
for (Point val : it) {
for (final List<Point> it : this.data) {
for (final Point val : it) {
val.pos = transformationMatrix.multiply(val.pos);
}
}
}
public void display() {
Log.verbose(" Display list of points : size=" + this.data.size());
for (List<Point> it : this.data) {
Log.verbose(" Find List " + it.size() + " members");
LOGGER.trace(" Display list of points : size=" + this.data.size());
for (final List<Point> it : this.data) {
LOGGER.trace(" Find List " + it.size() + " members");
for (int iii = 0; iii < it.size(); ++iii) {
Point elem = it.get(iii);
Log.verbose(" [" + iii + "] Find " + elem.type + " " + elem.pos);
final Point elem = it.get(iii);
LOGGER.trace(" [" + iii + "] Find " + elem.type + " " + elem.pos);
}
}
}
public Pair<Vector2f, Vector2f> getViewPort() {
Pair<Vector2f, Vector2f> out = new Pair<>(Vector2f.MAX_VALUE, Vector2f.MIN_VALUE);
for (List<Point> it : this.data) {
for (Point it2 : it) {
for (final List<Point> it : this.data) {
for (final Point it2 : it) {
out = new Pair<>(Vector2f.min(out.first, it2.pos), Vector2f.max(out.second, it2.pos));
}
}

View File

@ -4,14 +4,15 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.atriasoft.esvg.CapMode;
import org.atriasoft.esvg.JoinMode;
import org.atriasoft.etk.math.FMath;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.util.Dynamic;
import org.atriasoft.etk.util.Pair;
import org.atriasoft.etk.math.FMath;
import org.atriasoft.etk.math.Matrix2x3f;
import org.atriasoft.esvg.CapMode;
import org.atriasoft.esvg.JoinMode;
import org.atriasoft.esvg.internal.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** @file
* @author Edouard DUPIN
@ -20,13 +21,20 @@ import org.atriasoft.esvg.internal.Log;
*/
public class SegmentList {
static Vector2f getIntersect(final Vector2f point1, final Vector2f vect1, final Vector2f point2, final Vector2f vect2) {
float diviseur = vect1.x() * vect2.y() - vect1.y() * vect2.x();
static final Logger LOGGER = LoggerFactory.getLogger(SegmentList.class);
static Vector2f getIntersect(
final Vector2f point1,
final Vector2f vect1,
final Vector2f point2,
final Vector2f vect2) {
final float diviseur = vect1.x() * vect2.y() - vect1.y() * vect2.x();
if (diviseur != 0.0f) {
float mmm = (vect1.x() * point1.y() - vect1.x() * point2.y() - vect1.y() * point1.x() + vect1.y() * point2.x()) / diviseur;
final float mmm = (vect1.x() * point1.y() - vect1.x() * point2.y() - vect1.y() * point1.x()
+ vect1.y() * point2.x()) / diviseur;
return point2.add(vect2.multiply(mmm));
}
Log.error("Get divider / 0.0f");
LOGGER.error("Get divider / 0.0f");
return point2;
}
@ -55,16 +63,16 @@ public class SegmentList {
}
public void applyMatrix(final Matrix2x3f transformationMatrix) {
for (Segment it : this.data) {
for (final Segment it : this.data) {
it.applyMatrix(transformationMatrix);
}
}
public void clearHorizontals() {
// TODO Auto-generated method stub
Iterator<Segment> itr = this.data.iterator();
final Iterator<Segment> itr = this.data.iterator();
while (itr.hasNext()) {
Segment seg = itr.next();
final Segment seg = itr.next();
if (seg.p0.y() == seg.p1.y()) {
itr.remove();
}
@ -72,7 +80,7 @@ public class SegmentList {
}
public void createSegmentList(final PointList listPoint) {
for (List<Point> it : listPoint.data) {
for (final List<Point> it : listPoint.data) {
// Build Segments
for (int iii = 0, jjj = it.size() - 1; iii < it.size(); jjj = iii++) {
addSegment(it.get(jjj), it.get(iii));
@ -80,45 +88,52 @@ public class SegmentList {
}
}
public void createSegmentListStroke(final PointList listPoint, final float width, final CapMode cap, final JoinMode join, final float miterLimit) {
for (List<Point> itListPoint : listPoint.data) {
public void createSegmentListStroke(
final PointList listPoint,
final float width,
final CapMode cap,
final JoinMode join,
final float miterLimit) {
for (final List<Point> itListPoint : listPoint.data) {
// generate for every point all the orthogonal elements
//
// normal edge * end path
// (mitter) * | * * * * * * * * * * * * * *
// * |<--*----this | *
// * | * this -->| *
// * * * | *
// * . | . * . . . . . . . . * *
// * . | . * | *
// * A . | . B * | *
// . * . | *
// . * * . * * * * * * * * * * * * *
// * *
// * *
for (int idPevious = itListPoint.size() - 1, idCurrent = 0, idNext = 1; idCurrent < itListPoint.size(); idPevious = idCurrent++, idNext++) {
//
// normal edge * end path
// (mitter) * | * * * * * * * * * * * * * *
// * |<--*----this | *
// * | * this -->| *
// * * * | *
// * . | . * . . . . . . . . * *
// * . | . * | *
// * A . | . B * | *
// . * . | *
// . * * . * * * * * * * * * * * * *
// * *
// * *
for (int idPevious = itListPoint.size() - 1, idCurrent = 0, idNext = 1; idCurrent < itListPoint.size();
idPevious = idCurrent++, idNext++) {
if (idNext == itListPoint.size()) {
idNext = 0;
}
if (itListPoint.get(idCurrent).type == PointType.join || itListPoint.get(idCurrent).type == PointType.interpolation) {
if (itListPoint.get(idCurrent).type == PointType.join
|| itListPoint.get(idCurrent).type == PointType.interpolation) {
if (idPevious < 0) {
Log.error("an error occure a previous ID is < 0.... ");
LOGGER.error("an error occure a previous ID is < 0.... ");
continue;
}
if (idNext >= itListPoint.size()) {
Log.error("an error occure a next ID is >= nbPoint len .... ");
LOGGER.error("an error occure a next ID is >= nbPoint len .... ");
continue;
}
//Log.debug("JOIN : id : prev/curr/next : " + idPevious + "/" + idCurrent + "/" + idNext);
//Log.debug("JOIN : val : prev/curr/next : " + itListPoint.get(idPevious).pos + "/" + itListPoint.get(idCurrent).pos + "/" + itListPoint.get(idNext).pos);
//LOGGER.debug("JOIN : id : prev/curr/next : " + idPevious + "/" + idCurrent + "/" + idNext);
//LOGGER.debug("JOIN : val : prev/curr/next : " + itListPoint.get(idPevious).pos + "/" + itListPoint.get(idCurrent).pos + "/" + itListPoint.get(idNext).pos);
Vector2f vecA = itListPoint.get(idCurrent).pos.less(itListPoint.get(idPevious).pos);
//Log.debug("JOIN : vecA : " + vecA);
//LOGGER.debug("JOIN : vecA : " + vecA);
vecA = vecA.safeNormalize();
Vector2f vecB = itListPoint.get(idNext).pos.less(itListPoint.get(idCurrent).pos);
//Log.debug("JOIN : vecB : " + vecB);
//LOGGER.debug("JOIN : vecB : " + vecB);
vecB = vecB.safeNormalize();
Vector2f vecC = vecA.less(vecB);
//Log.debug("JOIN : vecC : " + vecC);
//LOGGER.debug("JOIN : vecC : " + vecC);
if (vecC.isZero()) {
// special case: 1 line ...
itListPoint.get(idCurrent).miterAxe = new Vector2f(vecA.y(), vecA.x());
@ -134,7 +149,7 @@ public class SegmentList {
vecB = itListPoint.get(idCurrent).pos.less(itListPoint.get(idPevious).pos);
vecB = vecB.safeNormalize();
itListPoint.get(idCurrent).orthoAxePrevious = new Vector2f(vecB.y(), -vecB.x());
//Log.debug("JOIN : miterAxe " + itListPoint.get(idCurrent).miterAxe);
//LOGGER.debug("JOIN : miterAxe " + itListPoint.get(idCurrent).miterAxe);
} else if (itListPoint.get(idCurrent).type == PointType.start) {
itListPoint.get(idCurrent).posNext = itListPoint.get(idNext).pos;
Vector2f vecB = itListPoint.get(idNext).pos.less(itListPoint.get(idCurrent).pos);
@ -144,7 +159,7 @@ public class SegmentList {
itListPoint.get(idCurrent).orthoAxeNext = itListPoint.get(idCurrent).miterAxe;
} else if (itListPoint.get(idCurrent).type == PointType.stop) {
if (idPevious < 0) {
Log.error("an error occure a previous ID is < 0.... ");
LOGGER.error("an error occure a previous ID is < 0.... ");
continue;
}
itListPoint.get(idCurrent).posPrevious = itListPoint.get(idPevious).pos;
@ -154,25 +169,27 @@ public class SegmentList {
itListPoint.get(idCurrent).orthoAxePrevious = itListPoint.get(idCurrent).miterAxe;
itListPoint.get(idCurrent).orthoAxeNext = itListPoint.get(idCurrent).miterAxe;
} else {
Log.todo("Unsupported type of point ....");
LOGGER.info("TODO: Unsupported type of point ....");
}
}
// create segment list:
boolean haveStartLine = false;
Dynamic<Vector2f> leftPoint = new Dynamic<Vector2f>(Vector2f.ZERO);
final Dynamic<Vector2f> leftPoint = new Dynamic<>(Vector2f.ZERO);
Dynamic<Vector2f> rightPoint = new Dynamic<Vector2f>(Vector2f.ZERO);
final Dynamic<Vector2f> rightPoint = new Dynamic<>(Vector2f.ZERO);
if (itListPoint.size() > 0) {
if (itListPoint.get(0).type == PointType.join) {
Point it = itListPoint.get(itListPoint.size() - 1);
final Point it = itListPoint.get(itListPoint.size() - 1);
// Calculate the perpendicular axis ...
leftPoint.value = it.pos.add(it.orthoAxePrevious.multiply(width * 0.5f));
rightPoint.value = it.pos.less(it.orthoAxePrevious.multiply(width * 0.5f));
// cyclic path...
if (it.type == PointType.interpolation) {
leftPoint.value = SegmentList.getIntersect(leftPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
rightPoint.value = SegmentList.getIntersect(rightPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
leftPoint.value = SegmentList.getIntersect(leftPoint.value, it.pos.less(it.posPrevious), it.pos,
it.miterAxe);
rightPoint.value = SegmentList.getIntersect(rightPoint.value, it.pos.less(it.posPrevious),
it.pos, it.miterAxe);
} else if (it.type == PointType.join) {
// Calculate the perpendicular axis ...
leftPoint.value = it.pos.add(it.orthoAxePrevious.multiply(width * 0.5f));
@ -180,12 +197,14 @@ public class SegmentList {
// project on the miter Axis ...
switch (join) {
case MITER: {
Vector2f left = SegmentList.getIntersect(leftPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
Vector2f right = SegmentList.getIntersect(rightPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
final Vector2f left = SegmentList.getIntersect(leftPoint.value,
it.pos.less(it.posPrevious), it.pos, it.miterAxe);
final Vector2f right = SegmentList.getIntersect(rightPoint.value,
it.pos.less(it.posPrevious), it.pos, it.miterAxe);
// Check the miter limit:
float limitRight = (left.less(it.pos)).length() / width * 2.0f;
float limitLeft = (right.less(it.pos)).length() / width * 2.0f;
Log.verbose(" miter Limit: " + limitRight + " " + limitLeft + " <= " + miterLimit);
final float limitRight = (left.less(it.pos)).length() / width * 2.0f;
final float limitLeft = (right.less(it.pos)).length() / width * 2.0f;
LOGGER.trace(" miter Limit: " + limitRight + " " + limitLeft + " <= " + miterLimit);
if (limitRight <= miterLimit && limitLeft <= miterLimit) {
leftPoint.value = left;
rightPoint.value = right;
@ -194,14 +213,16 @@ public class SegmentList {
}
case ROUND:
case BEVEL: {
Vector2f axePrevious = (it.pos.less(it.posPrevious)).safeNormalize();
Vector2f axeNext = (it.posNext.less(it.pos)).safeNormalize();
float cross = axePrevious.cross(axeNext);
final Vector2f axePrevious = (it.pos.less(it.posPrevious)).safeNormalize();
final Vector2f axeNext = (it.posNext.less(it.pos)).safeNormalize();
final float cross = axePrevious.cross(axeNext);
if (cross > 0.0f) {
rightPoint.value = SegmentList.getIntersect(rightPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
rightPoint.value = SegmentList.getIntersect(rightPoint.value,
it.pos.less(it.posPrevious), it.pos, it.miterAxe);
leftPoint.value = it.pos.add(it.orthoAxeNext.multiply(width * 0.5f));
} else {
leftPoint.value = SegmentList.getIntersect(leftPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
leftPoint.value = SegmentList.getIntersect(leftPoint.value,
it.pos.less(it.posPrevious), it.pos, it.miterAxe);
rightPoint.value = it.pos.less(it.orthoAxeNext.multiply(width * 0.5f));
}
break;
@ -210,106 +231,112 @@ public class SegmentList {
break;
}
} else {
Log.error("Start list point with a join, but last lement is not a join");
LOGGER.error("Start list point with a join, but last lement is not a join");
}
}
}
for (Point it : itListPoint) {
for (final Point it : itListPoint) {
switch (it.type) {
case single:
// just do nothing ....
Log.verbose("Find Single " + it.pos);
LOGGER.trace("Find Single " + it.pos);
break;
case start:
Log.verbose("Find Start " + it.pos);
LOGGER.trace("Find Start " + it.pos);
if (haveStartLine) {
// close previous :
Log.warning(" find a non close path ...");
LOGGER.warn(" find a non close path ...");
addSegment(leftPoint.value, rightPoint.value);
}
haveStartLine = true;
startStopPoint(leftPoint, rightPoint, it, cap, width, true);
break;
case stop:
Log.verbose("Find Stop " + it.pos);
LOGGER.trace("Find Stop " + it.pos);
if (!haveStartLine) {
Log.warning("find close path without start part ...");
LOGGER.warn("find close path without start part ...");
break;
}
haveStartLine = false;
startStopPoint(leftPoint, rightPoint, it, cap, width, false);
break;
case interpolation: {
Log.verbose("Find interpolation " + it.pos);
Vector2f left = SegmentList.getIntersect(leftPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
Vector2f right = SegmentList.getIntersect(rightPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
LOGGER.trace("Find interpolation " + it.pos);
final Vector2f left = SegmentList.getIntersect(leftPoint.value, it.pos.less(it.posPrevious),
it.pos, it.miterAxe);
final Vector2f right = SegmentList.getIntersect(rightPoint.value, it.pos.less(it.posPrevious),
it.pos, it.miterAxe);
//Draw from previous point:
addSegment(leftPoint.value, left);
Log.verbose(" segment :" + leftPoint + " . " + left);
LOGGER.trace(" segment :" + leftPoint + " . " + left);
addSegment(right, rightPoint.value);
Log.verbose(" segment :" + right + " . " + rightPoint);
LOGGER.trace(" segment :" + right + " . " + rightPoint);
leftPoint.value = left;
rightPoint.value = right;
}
break;
case join:
Log.verbose("Find join " + it.pos);
LOGGER.trace("Find join " + it.pos);
switch (join) {
case MITER: {
Vector2f left = SegmentList.getIntersect(leftPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
Vector2f right = SegmentList.getIntersect(rightPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
final Vector2f left = SegmentList.getIntersect(leftPoint.value,
it.pos.less(it.posPrevious), it.pos, it.miterAxe);
final Vector2f right = SegmentList.getIntersect(rightPoint.value,
it.pos.less(it.posPrevious), it.pos, it.miterAxe);
// Check the miter limit:
float limitRight = left.less(it.pos).length() / width * 2.0f;
float limitLeft = right.less(it.pos).length() / width * 2.0f;
Log.verbose(" miter Limit: " + limitRight + " " + limitLeft + " <= " + miterLimit);
final float limitRight = left.less(it.pos).length() / width * 2.0f;
final float limitLeft = right.less(it.pos).length() / width * 2.0f;
LOGGER.trace(" miter Limit: " + limitRight + " " + limitLeft + " <= " + miterLimit);
if (limitRight <= miterLimit && limitLeft <= miterLimit) {
//Draw from previous point:
addSegment(leftPoint.value, left);
Log.verbose(" segment :" + leftPoint + " . " + left);
LOGGER.trace(" segment :" + leftPoint + " . " + left);
addSegment(right, rightPoint.value);
Log.verbose(" segment :" + right + " . " + rightPoint);
LOGGER.trace(" segment :" + right + " . " + rightPoint);
leftPoint.value = left;
rightPoint.value = right;
break;
}
Log.verbose(" Find miter Limit ... ==> create BEVEL");
LOGGER.trace(" Find miter Limit ... ==> create BEVEL");
}
case ROUND:
case BEVEL: {
Vector2f axePrevious = (it.pos.less(it.posPrevious)).safeNormalize();
Vector2f axeNext = (it.posNext.less(it.pos)).safeNormalize();
float cross = axePrevious.cross(axeNext);
final Vector2f axePrevious = (it.pos.less(it.posPrevious)).safeNormalize();
final Vector2f axeNext = (it.posNext.less(it.pos)).safeNormalize();
final float cross = axePrevious.cross(axeNext);
if (cross > 0.0f) {
Vector2f right = SegmentList.getIntersect(rightPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
Vector2f left1 = it.pos.add(it.orthoAxePrevious.multiply(width * 0.5f));
Vector2f left2 = it.pos.add(it.orthoAxeNext.multiply(width * 0.5f));
final Vector2f right = SegmentList.getIntersect(rightPoint.value,
it.pos.less(it.posPrevious), it.pos, it.miterAxe);
final Vector2f left1 = it.pos.add(it.orthoAxePrevious.multiply(width * 0.5f));
final Vector2f left2 = it.pos.add(it.orthoAxeNext.multiply(width * 0.5f));
//Draw from previous point:
addSegment(leftPoint.value, left1);
Log.verbose(" segment :" + leftPoint + " . " + left1);
LOGGER.trace(" segment :" + leftPoint + " . " + left1);
if (join != JoinMode.ROUND) {
// Miter and bevel:
addSegment(left1, left2);
Log.verbose(" segment :" + left1 + " . " + left2);
LOGGER.trace(" segment :" + left1 + " . " + left2);
} else {
createSegmentListStroke(left1, left2, it.pos, width, false);
}
addSegment(right, rightPoint.value);
Log.verbose(" segment :" + right + " . " + rightPoint);
LOGGER.trace(" segment :" + right + " . " + rightPoint);
leftPoint.value = left2;
rightPoint.value = right;
} else {
Vector2f left = SegmentList.getIntersect(leftPoint.value, it.pos.less(it.posPrevious), it.pos, it.miterAxe);
Vector2f right1 = it.pos.less(it.orthoAxePrevious.multiply(width * 0.5f));
Vector2f right2 = it.pos.less(it.orthoAxeNext.multiply(width * 0.5f));
final Vector2f left = SegmentList.getIntersect(leftPoint.value,
it.pos.less(it.posPrevious), it.pos, it.miterAxe);
final Vector2f right1 = it.pos.less(it.orthoAxePrevious.multiply(width * 0.5f));
final Vector2f right2 = it.pos.less(it.orthoAxeNext.multiply(width * 0.5f));
//Draw from previous point:
addSegment(leftPoint.value, left);
Log.verbose(" segment :" + leftPoint + " . " + left);
LOGGER.trace(" segment :" + leftPoint + " . " + left);
addSegment(right1, rightPoint.value);
Log.verbose(" segment :" + right1 + " . " + rightPoint);
LOGGER.trace(" segment :" + right1 + " . " + rightPoint);
if (join != JoinMode.ROUND) {
// Miter and bevel:
addSegment(right2, right1);
Log.verbose(" segment :" + right2 + " . " + right1);
LOGGER.trace(" segment :" + right2 + " . " + right1);
} else {
createSegmentListStroke(right1, right2, it.pos, width, true);
}
@ -330,15 +357,21 @@ public class SegmentList {
}
private void createSegmentListStroke(final Vector2f point1, final Vector2f point2, final Vector2f center, final float width, final boolean isStart) {
private void createSegmentListStroke(
final Vector2f point1,
final Vector2f point2,
final Vector2f center,
final float width,
final boolean isStart) {
int nbDot = (int) width;
if (nbDot <= 2) {
nbDot = 2;
}
float angleToDraw = FMath.acos((point1.less(center)).safeNormalize().dot((point2.less(center)).safeNormalize()));
float baseAngle = angleToDraw / nbDot;
final float angleToDraw = FMath
.acos((point1.less(center)).safeNormalize().dot((point2.less(center)).safeNormalize()));
final float baseAngle = angleToDraw / nbDot;
float iii;
Vector2f axe = (point1.less(center)).safeNormalize();
final Vector2f axe = (point1.less(center)).safeNormalize();
Vector2f ppp1 = point1;
@ -350,68 +383,74 @@ public class SegmentList {
} else {
tmpMat = Matrix2x3f.createRotate(iii);
}
Vector2f axeRotate = tmpMat.multiply(axe);
final Vector2f axeRotate = tmpMat.multiply(axe);
ppp2 = center.add(axeRotate.multiply(width * 0.5f));
if (isStart) {
addSegment(ppp2, ppp1);
Log.verbose(" segment :" + ppp2 + " . " + ppp1);
LOGGER.trace(" segment :" + ppp2 + " . " + ppp1);
} else {
addSegment(ppp1, ppp2);
Log.verbose(" segment :" + ppp1 + " . " + ppp2);
LOGGER.trace(" segment :" + ppp1 + " . " + ppp2);
}
ppp1 = ppp2;
}
if (isStart) {
addSegment(point2, ppp1);
Log.verbose(" segment :" + point2 + " . " + ppp1);
LOGGER.trace(" segment :" + point2 + " . " + ppp1);
} else {
addSegment(ppp1, point2);
Log.verbose(" segment :" + ppp1 + " . " + point2);
LOGGER.trace(" segment :" + ppp1 + " . " + point2);
}
}
public Pair<Vector2f, Vector2f> getViewPort() {
Pair<Vector2f, Vector2f> out = new Pair<>(Vector2f.MAX_VALUE, Vector2f.MIN_VALUE);
for (Segment it : this.data) {
for (final Segment it : this.data) {
out = new Pair<>(Vector2f.min(out.first, it.p0, it.p1), Vector2f.max(out.second, it.p0, it.p1));
}
return out;
}
private void startStopPoint(final Dynamic<Vector2f> leftPoint, final Dynamic<Vector2f> rightPoint, final Point point, final CapMode cap, final float width, final boolean isStart) {
private void startStopPoint(
final Dynamic<Vector2f> leftPoint,
final Dynamic<Vector2f> rightPoint,
final Point point,
final CapMode cap,
final float width,
final boolean isStart) {
switch (cap) {
case BUTT: {
Vector2f left = point.pos.add(point.miterAxe.multiply(width * 0.5f));
Vector2f right = point.pos.less(point.miterAxe.multiply(width * 0.5f));
final Vector2f left = point.pos.add(point.miterAxe.multiply(width * 0.5f));
final Vector2f right = point.pos.less(point.miterAxe.multiply(width * 0.5f));
if (!isStart) {
//Draw from previous point:
addSegment(leftPoint.value, left);
Log.verbose(" segment :" + leftPoint + " . " + left);
LOGGER.trace(" segment :" + leftPoint + " . " + left);
addSegment(right, rightPoint.value);
Log.verbose(" segment :" + right + " . " + rightPoint);
LOGGER.trace(" segment :" + right + " . " + rightPoint);
}
leftPoint.value = left;
rightPoint.value = right;
}
if (!isStart) {
addSegment(leftPoint.value, rightPoint.value);
Log.verbose(" segment :" + leftPoint + " . " + rightPoint);
LOGGER.trace(" segment :" + leftPoint + " . " + rightPoint);
} else {
addSegment(rightPoint.value, leftPoint.value);
Log.verbose(" segment :" + rightPoint + " . " + leftPoint);
LOGGER.trace(" segment :" + rightPoint + " . " + leftPoint);
}
break;
case ROUND: {
if (!isStart) {
Vector2f left = point.pos.add(point.miterAxe.multiply(width * 0.5f));
Vector2f right = point.pos.less(point.miterAxe.multiply(width * 0.5f));
final Vector2f left = point.pos.add(point.miterAxe.multiply(width * 0.5f));
final Vector2f right = point.pos.less(point.miterAxe.multiply(width * 0.5f));
if (!isStart) {
//Draw from previous point:
addSegment(leftPoint.value, left);
Log.verbose(" segment :" + leftPoint + " . " + left);
LOGGER.trace(" segment :" + leftPoint + " . " + left);
addSegment(right, rightPoint.value);
Log.verbose(" segment :" + right + " . " + rightPoint);
LOGGER.trace(" segment :" + right + " . " + rightPoint);
}
leftPoint.value = left;
rightPoint.value = right;
@ -434,30 +473,30 @@ public class SegmentList {
}
Vector2f left = point.pos.add(point.miterAxe.multiply(width * 0.5f));
Vector2f right = point.pos.less(point.miterAxe.multiply(width * 0.5f));
Matrix2x3f tmpMat = Matrix2x3f.createTranslate(nextAxe.safeNormalize().multiply(width * -0.5f));
final Matrix2x3f tmpMat = Matrix2x3f.createTranslate(nextAxe.safeNormalize().multiply(width * -0.5f));
left = tmpMat.multiply(left);
right = tmpMat.multiply(right);
if (!isStart) {
//Draw from previous point:
addSegment(leftPoint.value, left);
Log.verbose(" segment :" + leftPoint + " . " + left);
LOGGER.trace(" segment :" + leftPoint + " . " + left);
addSegment(right, rightPoint.value);
Log.verbose(" segment :" + right + " . " + rightPoint);
LOGGER.trace(" segment :" + right + " . " + rightPoint);
}
leftPoint.value = left;
rightPoint.value = right;
if (!isStart) {
addSegment(leftPoint.value, rightPoint.value);
Log.verbose(" segment :" + leftPoint + " . " + rightPoint);
LOGGER.trace(" segment :" + leftPoint + " . " + rightPoint);
} else {
addSegment(rightPoint.value, leftPoint.value);
Log.verbose(" segment :" + rightPoint + " . " + leftPoint);
LOGGER.trace(" segment :" + rightPoint + " . " + leftPoint);
}
Log.verbose(" segment :" + leftPoint + " . " + rightPoint);
LOGGER.trace(" segment :" + leftPoint + " . " + rightPoint);
}
break;
default:
Log.error(" Undefined CAP TYPE");
LOGGER.error(" Undefined CAP TYPE");
break;
}
}

View File

@ -4,27 +4,29 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.etk.math.FMath;
import org.atriasoft.etk.math.Vector2f;
import org.atriasoft.etk.math.Vector2i;
import org.atriasoft.etk.util.ArraysTools;
import org.atriasoft.etk.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Weight {
static final Logger LOGGER = LoggerFactory.getLogger(Weight.class);
private float[][] data = null;
private Vector2i size;
// constructor :
public Weight() {
this.size = Vector2i.ZERO;
}
public Weight(final Vector2i size) {
this.size = size;
resize(size);
}
public void append(final int posY, final Scanline data) {
if (posY >= 0 && posY < this.size.y()) {
for (int xxx = 0; xxx < this.size.x(); ++xxx) {
@ -32,28 +34,29 @@ public class Weight {
}
}
}
public void clear(final float fill) {
ArraysTools.fill2(this.data, fill);
}
public void fusion(final Weight redered, final int offsetXXX, final int offsetYYY) {
for (int yyy = 0; yyy < redered.getHeight(); yyy++) {
for (int xxx = 0; xxx < redered.getWidth(); xxx++) {
this.data[offsetYYY + yyy][offsetXXX + xxx] = FMath.avg(0.0f, this.data[offsetYYY + yyy][offsetXXX + xxx] + redered.get(xxx, yyy), 1.0f);
this.data[offsetYYY + yyy][offsetXXX + xxx] = FMath.avg(0.0f,
this.data[offsetYYY + yyy][offsetXXX + xxx] + redered.get(xxx, yyy), 1.0f);
}
}
}
public void generate(final Vector2i size, final int subSamplingCount, final SegmentList listSegment) {
resize(size);
// for each lines:
for (int yyy = 0; yyy < size.y(); ++yyy) {
Log.verbose("Weighting ... " + yyy + " / " + size.y());
LOGGER.trace("Weighting ... " + yyy + " / " + size.y());
// Reduce the number of lines in the subsampling parsing:
List<Segment> availlableSegmentPixel = new ArrayList<Segment>();
for (Segment it : listSegment.data) {
final List<Segment> availlableSegmentPixel = new ArrayList<>();
for (final Segment it : listSegment.data) {
if (it.p0.y() < yyy + 1 && it.p1.y() > (yyy)) {
availlableSegmentPixel.add(it);
}
@ -61,20 +64,21 @@ public class Weight {
if (availlableSegmentPixel.size() == 0) {
continue;
}
Log.verbose(" Find Basic segments " + availlableSegmentPixel.size());
LOGGER.trace(" Find Basic segments " + availlableSegmentPixel.size());
// This represent the pondaration on the subSampling
float deltaSize = 1.0f / subSamplingCount;
final float deltaSize = 1.0f / subSamplingCount;
for (int kkk = 0; kkk < subSamplingCount; ++kkk) {
Log.verbose(" Scanline ... " + kkk + " / " + subSamplingCount);
Scanline scanline = new Scanline(size.x());
LOGGER.trace(" Scanline ... " + kkk + " / " + subSamplingCount);
final Scanline scanline = new Scanline(size.x());
//find all the segment that cross the middle of the line of the center of the pixel line:
float subSamplingCenterPos = yyy + deltaSize * 0.5f + deltaSize * kkk;
List<Segment> availlableSegment = new ArrayList<>();
final float subSamplingCenterPos = yyy + deltaSize * 0.5f + deltaSize * kkk;
final List<Segment> availlableSegment = new ArrayList<>();
// find in the subList ...
for (Segment it : availlableSegmentPixel) {
for (final Segment it : availlableSegmentPixel) {
if (it.p0.y() <= subSamplingCenterPos && it.p1.y() > subSamplingCenterPos) {
// check if we not get 2 identical lines:
if (availlableSegment.size() > 0 && availlableSegment.get(availlableSegment.size() - 1).p1 == it.p0
if (availlableSegment.size() > 0
&& availlableSegment.get(availlableSegment.size() - 1).p1 == it.p0
&& availlableSegment.get(availlableSegment.size() - 1).direction == it.direction) {
// we not add this point in this case to prevent double count of the same point.
} else {
@ -82,27 +86,27 @@ public class Weight {
}
}
}
Log.verbose(" Availlable Segment " + availlableSegment.size());
LOGGER.trace(" Availlable Segment " + availlableSegment.size());
if (availlableSegment.size() == 0) {
continue;
}
for (Segment it : availlableSegment) {
Log.verbose(" Availlable Segment " + it.p0 + " . " + it.p1 + " dir=" + it.direction);
for (final Segment it : availlableSegment) {
LOGGER.trace(" Availlable Segment " + it.p0 + " . " + it.p1 + " dir=" + it.direction);
}
// x position, angle
List<Pair<Float, Integer>> listPosition = new ArrayList<>();
for (Segment it : availlableSegment) {
Vector2f delta = it.p0.less(it.p1);
final List<Pair<Float, Integer>> listPosition = new ArrayList<>();
for (final Segment it : availlableSegment) {
final Vector2f delta = it.p0.less(it.p1);
// x = coefficent*y+bbb;
float coefficient = delta.x() / delta.y();
float bbb = it.p0.x() - coefficient * it.p0.y();
float xpos = coefficient * subSamplingCenterPos + bbb;
listPosition.add(new Pair<Float, Integer>(xpos, it.direction));
final float coefficient = delta.x() / delta.y();
final float bbb = it.p0.x() - coefficient * it.p0.y();
final float xpos = coefficient * subSamplingCenterPos + bbb;
listPosition.add(new Pair<>(xpos, it.direction));
}
Log.verbose(" List position " + listPosition.size());
LOGGER.trace(" List position " + listPosition.size());
// now we order position of the xPosition:
Collections.sort(listPosition, (e1, e2) -> ((int) (e1.first - e2.first)));
// move through all element in the point:
int lastState = 0;
float currentValue = 0.0f;
@ -111,30 +115,30 @@ public class Weight {
// * current pos
// * pos ...
// TODO Code the Odd/even and non-zero ...
for (Pair<Float, Integer> it : listPosition) {
for (final Pair<Float, Integer> it : listPosition) {
if (currentPos != it.first.intValue()) {
// fill to the new pos -1:
float endValue = FMath.min(1.0f, FMath.abs(lastState)) * deltaSize;
final float endValue = FMath.min(1.0f, FMath.abs(lastState)) * deltaSize;
for (int iii = currentPos + 1; iii < it.first.intValue(); ++iii) {
scanline.set(iii, endValue);
}
currentPos = it.first.intValue();
currentValue = endValue;
}
int oldState = lastState;
final int oldState = lastState;
lastState += it.second;
if (oldState == 0) {
// nothing to draw before ...
float ratio = 1.0f - (it.first - it.first.intValue());
final float ratio = 1.0f - (it.first - it.first.intValue());
currentValue += ratio * deltaSize;
} else if (lastState == 0) {
// something new to draw ...
float ratio = 1.0f - (it.first - it.first.intValue());
final float ratio = 1.0f - (it.first - it.first.intValue());
currentValue -= ratio * deltaSize;
} else {
// nothing to do ...
}
if (currentPos == it.first.intValue()) {
scanline.set(currentPos, currentValue);
}
@ -142,7 +146,7 @@ public class Weight {
// if the counter is not at 0 ==> fill if to the end with full value ... 2.0
if (lastState != 0) {
// just past the last state to the end of the image ...
Log.error("end of Path whith no end ... " + currentPos + " . " + size.x());
LOGGER.error("end of Path whith no end ... " + currentPos + " . " + size.x());
for (int xxx = currentPos; xxx < size.x(); ++xxx) {
scanline.set(xxx, 100.0f);
}
@ -151,50 +155,50 @@ public class Weight {
}
}
}
public float get(final int xxx, final int yyy) {
if (this.data == null) {
return 0;
}
return this.data[yyy][xxx];
}
public float get(final Vector2i pos) {
if (this.data == null) {
return 0;
}
return this.data[pos.y()][pos.x()];
}
public int getHeight() {
return this.size.y();
}
public Vector2i getSize() {
return this.size;
}
public int getWidth() {
return this.size.x();
}
// -----------------------------------------------
// -- basic tools :
// -----------------------------------------------
public void resize(final Vector2i size) {
this.size = size;
if (this.size.x() <= 0) {
Log.error("Error in the Weight size : " + this.size);
LOGGER.error("Error in the Weight size : " + this.size);
this.size = this.size.withX(1);
}
if (this.size.y() <= 0) {
Log.error("Error in the Weight size : " + this.size);
LOGGER.error("Error in the Weight size : " + this.size);
this.size = this.size.withY(1);
}
this.data = new float[this.size.y()][this.size.x()];
clear(0);
}
public void set(final int posY, final Scanline data) {
if (posY >= 0 && posY < this.size.y()) {
for (int xxx = 0; xxx < this.size.x(); ++xxx) {
@ -202,9 +206,9 @@ public class Weight {
}
}
}
public void set(final Vector2i pos, final float newColor) {
this.data[pos.y()][pos.x()] = newColor;
}
}

View File

@ -1,36 +1,37 @@
package test.atriasoft.esvg;
import org.atriasoft.egami.ImageFloatRGBA;
import org.atriasoft.egami.Image;
import org.atriasoft.egami.ImageFloatRGBA;
import org.atriasoft.esvg.EsvgDocument;
import org.atriasoft.esvg.internal.Log;
import org.atriasoft.esvg.render.Weight;
import org.atriasoft.etk.Color;
import org.atriasoft.etk.Uri;
import org.atriasoft.etk.math.Vector2i;
import org.atriasoft.pngencoder.PngEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConfigTest {
static final Logger LOGGER = LoggerFactory.getLogger(ConfigTest.class);
public static final String BASE_PATH = "./testResult/";//"~/dev/workspace-game/atriasoft/esvg/";
public static final boolean VISUAL_DEBUG = true;
public static void generateAnImage(final EsvgDocument doc, final Uri uri) {
Image data = doc.renderImageFloatRGBA(null, ConfigTest.VISUAL_DEBUG);
final Image data = doc.renderImageFloatRGBA(null, ConfigTest.VISUAL_DEBUG);
if (data == null) {
Log.critical("No data generated ...");
LOGGER.error("No data generated ...");
}
Log.warning("Save file in " + uri.getPath());
byte[] outElem = new PngEncoder().withBufferedImage(data).withCompressionLevel(9).toBytes();
Log.warning("outsize = " + outElem.length);
LOGGER.warn("Save file in " + uri.getPath());
final byte[] outElem = new PngEncoder().withBufferedImage(data).withCompressionLevel(9).toBytes();
LOGGER.warn("outsize = " + outElem.length);
new PngEncoder().withBufferedImage(data).withCompressionLevel(9).toFile(uri.getPath());
}
public static void generateAnImage(final Weight weight, final Uri uri) {
ImageFloatRGBA image = new ImageFloatRGBA(weight.getWidth() + 2, weight.getHeight() + 2);
final ImageFloatRGBA image = new ImageFloatRGBA(weight.getWidth() + 2, weight.getHeight() + 2);
for (int yyy = 0; yyy < weight.getHeight(); yyy++) {
for (int xxx = 0; xxx < weight.getWidth(); xxx++) {
float elem = weight.get(new Vector2i(xxx, yyy));
final float elem = weight.get(new Vector2i(xxx, yyy));
image.setColorFloat(xxx, yyy, 1.0f, 1.0f, 1.0f, elem);
}
}
@ -42,11 +43,11 @@ public class ConfigTest {
image.setColor(xxx, 0, Color.ORANGE);
image.setColor(xxx, weight.getHeight() + 1, Color.ORANGE);
}
Log.warning("Save file in " + uri.getPath());
byte[] outElem = new PngEncoder().withBufferedImage(image).withCompressionLevel(9).toBytes();
Log.warning("outsize = " + outElem.length);
LOGGER.warn("Save file in " + uri.getPath());
final byte[] outElem = new PngEncoder().withBufferedImage(image).withCompressionLevel(9).toBytes();
LOGGER.warn("outsize = " + outElem.length);
new PngEncoder().withBufferedImage(image).withCompressionLevel(9).toFile(uri.getPath());
}
private ConfigTest() {}
}

View File

@ -2,140 +2,187 @@ package test.atriasoft.esvg;
import org.atriasoft.esvg.EsvgDocument;
import org.atriasoft.etk.Uri;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class TestCap {
@Test
public void testTestCapButt() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,75 80,75' stroke='green' stroke-width='20' fill='none' stroke-linecap='butt'/>"
+ " <polyline points='80,25 20,25' stroke='orange' stroke-width='20' fill='none' stroke-linecap='butt'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,75 80,75' stroke='green' stroke-width='20' fill='none' stroke-linecap='butt'/>\
<polyline points='80,25 20,25' stroke='orange' stroke-width='20' fill='none' stroke-linecap='butt'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapbutt.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapbutt.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapbutt.png"));
}
@Test
public void testTestCapButtDiag1() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,20 80,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='butt'/>"
+ " <polyline points='80,20 20,80' stroke='orange' stroke-width='20' fill='none' stroke-linecap='butt'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,20 80,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='butt'/>\
<polyline points='80,20 20,80' stroke='orange' stroke-width='20' fill='none' stroke-linecap='butt'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapbuttDiag1.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapbuttDiag1.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapbuttDiag1.png"));
}
@Test
public void testTestCapButtDiag2() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,80 80,20' stroke='green' stroke-width='20' fill='none' stroke-linecap='butt'/>"
+ " <polyline points='80,80 20,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='butt'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,80 80,20' stroke='green' stroke-width='20' fill='none' stroke-linecap='butt'/>\
<polyline points='80,80 20,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='butt'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapbuttDiag2.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapbuttDiag2.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapbuttDiag2.png"));
}
@Test
public void testTestCapButtVert() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='25,20 25,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='butt'/>"
+ " <polyline points='75,80 75,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='butt'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='25,20 25,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='butt'/>\
<polyline points='75,80 75,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='butt'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapbuttVert.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapbuttVert.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapbuttVert.png"));
}
@Test
public void testTestCapRound() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,75 80,75' stroke='green' stroke-width='20' fill='none' stroke-linecap='round'/>"
+ " <polyline points='80,25 20,25' stroke='orange' stroke-width='20' fill='none' stroke-linecap='round'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,75 80,75' stroke='green' stroke-width='20' fill='none' stroke-linecap='round'/>\
<polyline points='80,25 20,25' stroke='orange' stroke-width='20' fill='none' stroke-linecap='round'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapround.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapround.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapround.png"));
}
@Test
public void testTestCapRoundDiag1() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,20 80,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='round'/>"
+ " <polyline points='80,20 20,80' stroke='orange' stroke-width='20' fill='none' stroke-linecap='round'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,20 80,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='round'/>\
<polyline points='80,20 20,80' stroke='orange' stroke-width='20' fill='none' stroke-linecap='round'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCaproundDiag1.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCaproundDiag1.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCaproundDiag1.png"));
}
@Test
public void testTestCapRoundDiag2() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,80 80,20' stroke='green' stroke-width='20' fill='none' stroke-linecap='round'/>"
+ " <polyline points='80,80 20,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='round'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,80 80,20' stroke='green' stroke-width='20' fill='none' stroke-linecap='round'/>\
<polyline points='80,80 20,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='round'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCaproundDiag2.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCaproundDiag2.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCaproundDiag2.png"));
}
@Test
public void testTestCapRoundVert() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='25,20 25,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='round'/>"
+ " <polyline points='75,80 75,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='round'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='25,20 25,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='round'/>\
<polyline points='75,80 75,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='round'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCaproundVert.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCaproundVert.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCaproundVert.png"));
}
@Test
public void testTestCapSquare() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,75 80,75' stroke='green' stroke-width='20' fill='none' stroke-linecap='square'/>"
+ " <polyline points='80,25 20,25' stroke='orange' stroke-width='20' fill='none' stroke-linecap='square'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,75 80,75' stroke='green' stroke-width='20' fill='none' stroke-linecap='square'/>\
<polyline points='80,25 20,25' stroke='orange' stroke-width='20' fill='none' stroke-linecap='square'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapsquare.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapsquare.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapsquare.png"));
}
@Test
public void testTestCapSquareDiag1() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,20 80,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='square'/>"
+ " <polyline points='80,20 20,80' stroke='orange' stroke-width='20' fill='none' stroke-linecap='square'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,20 80,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='square'/>\
<polyline points='80,20 20,80' stroke='orange' stroke-width='20' fill='none' stroke-linecap='square'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapsquareDiag1.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapsquareDiag1.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapsquareDiag1.png"));
}
@Test
public void testTestCapSquareDiag2() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='20,80 80,20' stroke='green' stroke-width='20' fill='none' stroke-linecap='square'/>"
+ " <polyline points='80,80 20,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='square'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='20,80 80,20' stroke='green' stroke-width='20' fill='none' stroke-linecap='square'/>\
<polyline points='80,80 20,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='square'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapsquareDiag2.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapsquareDiag2.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapsquareDiag2.png"));
}
@Test
public void testTestCapSquareVert() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>"
+ " <polyline points='25,20 25,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='square'/>"
+ " <polyline points='75,80 75,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='square'/>" + "</svg>";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<polyline points='25,20 25,80' stroke='green' stroke-width='20' fill='none' stroke-linecap='square'/>\
<polyline points='75,80 75,20' stroke='orange' stroke-width='20' fill='none' stroke-linecap='square'/>\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapsquareVert.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCapsquareVert.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCapsquareVert.png"));
}
}

View File

@ -2,37 +2,50 @@ package test.atriasoft.esvg;
import org.atriasoft.esvg.EsvgDocument;
import org.atriasoft.etk.Uri;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class TestCircle {
@Test
public void testTestCircleFill() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>" + " <circle cx='50' cy='50' r='40' fill='red' />" + "</svg>";
EsvgDocument doc = new EsvgDocument();
public void testTestCircleFill() throws RuntimeException {
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<circle cx='50' cy='50' r='40' fill='red' />\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCirclefill.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCirclefill.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCirclefill.png"));
}
@Test
public void testTestCircleFillandstroke() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>" + " <circle cx='50' cy='50' r='40' stroke='green' stroke-width='3' fill='red' />"
+ "</svg>";
EsvgDocument doc = new EsvgDocument();
public void testTestCircleFillandstroke() throws RuntimeException {
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<circle cx='50' cy='50' r='40' stroke='green' stroke-width='3' fill='red' />\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCirclefillandstroke.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCirclefillandstroke.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCirclefillandstroke.png"));
}
@Test
public void testTestCircleStroke() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" + "<svg height='100' width='100'>" + " <circle cx='50' cy='50' r='40' stroke='green' stroke-width='3' />" + "</svg>";
EsvgDocument doc = new EsvgDocument();
public void testTestCircleStroke() throws RuntimeException {
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>\
<svg height='100' width='100'>\
<circle cx='50' cy='50' r='40' stroke='green' stroke-width='3' />\
</svg>""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCirclestroke.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestCirclestroke.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestCirclestroke.png"));
}
}

View File

@ -2,7 +2,6 @@ package test.atriasoft.esvg;
import org.atriasoft.esvg.EsvgDocument;
import org.atriasoft.etk.Uri;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -10,251 +9,405 @@ class TestGradientLinear {
@Test
public void testTestGradientLinearDiag1() {
//@formatter:off
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
+ "<svg height='100' width='100'>\n"
+ " <defs>\n"
+ " <linearGradient id='grad2' x1='0%' y1='0%' x2='100%' y2='100%'>\n"
+ " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
+ " </linearGradient>\n"
+ " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
+ "</svg>\n";
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='0%' y1='0%' x2='100%' y2='100%'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
//@formatter:on
EsvgDocument doc = new EsvgDocument();
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag1.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag1.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag1.png"));
}
@Test
public void testTestGradientLinearDiag1Partiel() {
//@formatter:off
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
+ "<svg height='100' width='100'>\n"
+ " <defs>\n"
+ " <linearGradient id='grad2' x1='40%' y1='40%' x2='70%' y2='70%'>\n"
+ " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " </linearGradient>\n"
+ " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n"
+ "</svg>\n";
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='40%' y1='40%' x2='70%' y2='70%'>
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
//@formatter:on
EsvgDocument doc = new EsvgDocument();
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag1Partiel.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag1Partiel.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag1Partiel.png"));
}
@Test
public void testTestGradientLinearDiag2() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>\n" + " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2.png"));
}
@Test
public void testTestGradientLinearDiag2Rotate0() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='0%' y1='50%' x2='100%' y2='50%'>\n" + " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse transform='rotate (30 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='0%' y1='50%' x2='100%' y2='50%'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse transform='rotate (30 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate0.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate0.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate0.png"));
}
@Test
public void testTestGradientLinearDiag2Rotate1() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>\n" + " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse transform='rotate (45 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse transform='rotate (45 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate1.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate1.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate1.png"));
}
@Test
public void testTestGradientLinearDiag2Rotate2() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>\n" + " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse transform='rotate (-45 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse transform='rotate (-45 50 50)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate2.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate2.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2Rotate2.png"));
}
@Test
public void testTestGradientLinearDiag2scale() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>\n" + " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse transform='scale (0.5 2.0) translate (10,-25)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse transform='scale (0.5 2.0) translate (10,-25)' cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2scale.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri
.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2scale.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLineardiag2scale.png"));
}
@Test
public void testTestGradientLinearHorizontal() {
//@formatter:off
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
+ "<svg height='100' width='100'>\n"
+ " <defs>\n"
+ " <linearGradient id='grad1' x1='0%' y1='0%' x2='100%' y2='0%'>\n"
+ " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
+ " </linearGradient>\n"
+ " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n"
+ "</svg>\n";
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad1' x1='0%' y1='0%' x2='100%' y2='0%'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
//@formatter:on
EsvgDocument doc = new EsvgDocument();
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearhorizontal.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri
.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearhorizontal.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearhorizontal.png"));
}
@Test
public void testTestGradientLinearInternalHref() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n" + " <linearGradient id='grad2Values'>\n"
+ " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n"
+ " </linearGradient>\n" + " <linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%' xlink:href='#grad2Values' />\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2Values'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
<linearGradient id='grad2' x1='0%' y1='100%' x2='100%' y2='0%' xlink:href='#grad2Values' />
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearinternalHref.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLinearinternalHref.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearinternalHref.png"));
}
@Test
public void testTestGradientLinearUnitBoxspreadNone() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='40%' y1='40%' x2='60%' y2='60%'>\n" + " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='40%' y1='40%' x2='60%' y2='60%'>
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadNone.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadNone.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadNone.png"));
}
@Test
public void testTestGradientLinearUnitBoxspreadPad() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='40%' y1='40%' x2='60%' y2='60%' spreadMethod='pad'>\n" + " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='40%' y1='40%' x2='60%' y2='60%' spreadMethod='pad'>
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadPad.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadPad.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadPad.png"));
}
@Test
public void testTestGradientLinearUnitBoxspreadReflect() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='40%' y1='40%' x2='60%' y2='60%' spreadMethod='reflect'>\n" + " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='40%' y1='40%' x2='60%' y2='60%' spreadMethod='reflect'>
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadReflect.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadReflect.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadReflect.png"));
}
@Test
public void testTestGradientLinearUnitBoxspreadRepeat() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='40%' y1='40%' x2='60%' y2='60%' spreadMethod='repeat'>\n" + " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='40%' y1='40%' x2='60%' y2='60%' spreadMethod='repeat'>
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadRepeat.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadRepeat.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitBoxspreadRepeat.png"));
}
@Test
public void testTestGradientLinearUnitUserspreadNone() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='45' y1='45' x2='55' y2='55' gradientUnits='userSpaceOnUse'>\n"
+ " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " </linearGradient>\n" + " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='45' y1='45' x2='55' y2='55' gradientUnits='userSpaceOnUse'>
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadNone.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadNone.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadNone.png"));
}
@Test
public void testTestGradientLinearUnitUserspreadPad() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='45' y1='45' x2='55' y2='55' spreadMethod='pad' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " </linearGradient>\n" + " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='45' y1='45' x2='55' y2='55' spreadMethod='pad' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadPad.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadPad.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadPad.png"));
}
@Test
public void testTestGradientLinearUnitUserspreadReflect() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='45' y1='45' x2='55' y2='55' spreadMethod='reflect' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " </linearGradient>\n" + " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='45' y1='45' x2='55' y2='55' spreadMethod='reflect' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadReflect.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadReflect.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadReflect.png"));
}
@Test
public void testTestGradientLinearUnitUserspreadRepeate() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='45' y1='45' x2='55' y2='55' spreadMethod='repeat' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " </linearGradient>\n" + " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='45' y1='45' x2='55' y2='55' spreadMethod='repeat' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)'/>
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadRepeate.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadRepeate.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearunitUserspreadRepeate.png"));
}
@Test
public void testTestGradientLinearVertical() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <linearGradient id='grad2' x1='0%' y1='0%' x2='0%' y2='100%'>\n" + " <stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />\n" + " </linearGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<linearGradient id='grad2' x1='0%' y1='0%' x2='0%' y2='100%'>
<stop offset='0%' style='stop-color:rgb(255,0,0);stop-opacity:1' />
<stop offset='45%' style='stop-color:rgb(0,255,0);stop-opacity:1' />
<stop offset='55%' style='stop-color:rgb(0,0,255);stop-opacity:1' />
<stop offset='100%' style='stop-color:rgb(255,0,255);stop-opacity:1' />
</linearGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearvertical.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri
.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientLinearvertical.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientLinearvertical.png"));
}
}

View File

@ -2,217 +2,387 @@ package test.atriasoft.esvg;
import org.atriasoft.esvg.EsvgDocument;
import org.atriasoft.etk.Uri;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class TestGradientRadial {
@Test
public void testTestGradientRadialCircle() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50%' cy='50%' r='50%' fx='50%' fy='50%'>\n" + " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='50' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50%' cy='50%' r='50%' fx='50%' fy='50%'>
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='50' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialcircle.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialcircle.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialcircle.png"));
}
@Test
public void testTestGradientRadialFull() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50%' cy='50%' r='50%' fx='50%' fy='50%'>\n" + " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50%' cy='50%' r='50%' fx='50%' fy='50%'>
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialfull.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialfull.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialfull.png"));
}
@Test
public void testTestGradientRadialPartial() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad2' cx='20%' cy='30%' r='30%' fx='50%' fy='50%'>\n" + " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad2' cx='20%' cy='30%' r='30%' fx='50%' fy='50%'>
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad2)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialpartial.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri
.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialpartial.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialpartial.png"));
}
@Test
public void testTestGradientRadialUnitBoxspreadNone() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50%' cy='50%' r='10%' fx='50%' fy='50%'>\n" + " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50%' cy='50%' r='10%' fx='50%' fy='50%'>
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadNone.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadNone.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadNone.png"));
}
@Test
public void testTestGradientRadialUnitBoxspreadPad() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50%' cy='50%' r='10%' fx='50%' fy='50%' spreadMethod='pad'>\n" + " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50%' cy='50%' r='10%' fx='50%' fy='50%' spreadMethod='pad'>
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadPad.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadPad.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadPad.png"));
}
@Test
public void testTestGradientRadialUnitBoxspreadReflect() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50%' cy='50%' r='10%' fx='50%' fy='50%' spreadMethod='reflect'>\n" + " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50%' cy='50%' r='10%' fx='50%' fy='50%' spreadMethod='reflect'>
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadReflect.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadReflect.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadReflect.png"));
}
@Test
public void testTestGradientRadialUnitBoxspreadRepeat() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50%' cy='50%' r='10%' fx='50%' fy='50%' spreadMethod='repeat'>\n" + " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n"
+ " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n" + " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n"
+ " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n" + " </defs>\n"
+ " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50%' cy='50%' r='10%' fx='50%' fy='50%' spreadMethod='repeat'>
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadRepeat.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadRepeat.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitBoxspreadRepeat.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadNone() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='10' fx='50' fy='50%' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='10' fx='50' fy='50%' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadNone.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadNone.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadNone.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadPad() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='10' fx='50' fy='50' spreadMethod='pad' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='10' fx='50' fy='50' spreadMethod='pad' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadPad.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadPad.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadPad.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadPadunCenter() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='24' fx='40' fy='40' spreadMethod='pad' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='24' fx='40' fy='40' spreadMethod='pad' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadPadunCenter.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadPadunCenter.png"));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadPadunCenter.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc,
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadPadunCenter.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadReflect() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='10' fx='50' fy='50' spreadMethod='reflect' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='10' fx='50' fy='50' spreadMethod='reflect' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadReflect.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadReflect.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadReflect.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadReflectunCenter() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='24' fx='40' fy='40' spreadMethod='reflect' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='24' fx='40' fy='40' spreadMethod='reflect' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadReflectunCenter.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadReflectunCenter.png"));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadReflectunCenter.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc,
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadReflectunCenter.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadRepeat() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='10' fx='50' fy='50' spreadMethod='repeat' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='10' fx='50' fy='50' spreadMethod='repeat' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeat.svg"), data.replace("'", "\"")));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeat.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeat.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadRepeatout() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='24' fx='20' fy='40' spreadMethod='reflect' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='24' fx='20' fy='40' spreadMethod='reflect' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatout.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatout.png"));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatout.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc,
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatout.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadRepeatunCenter() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='24' fx='40' fy='40' spreadMethod='repeat' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='24' fx='40' fy='40' spreadMethod='repeat' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatunCenter.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatunCenter.png"));
Assertions.assertDoesNotThrow(
() -> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatunCenter.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc,
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatunCenter.png"));
}
@Test
public void testTestGradientRadialUnitUserspreadRepeatunCenter2() {
String data = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<svg height='100' width='100'>\n" + " <defs>\n"
+ " <radialGradient id='grad1' cx='50' cy='50' r='24' fx='60' fy='60' spreadMethod='repeat' gradientUnits='userSpaceOnUse' >\n"
+ " <stop offset='0%' style='stop-color:orange;stop-opacity:1' />\n" + " <stop offset='45%' style='stop-color:red;stop-opacity:1' />\n"
+ " <stop offset='55%' style='stop-color:blue;stop-opacity:1' />\n" + " <stop offset='100%' style='stop-color:green;stop-opacity:1' />\n" + " </radialGradient>\n"
+ " </defs>\n" + " <ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />\n" + "</svg>\n";
EsvgDocument doc = new EsvgDocument();
final String data = """
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='100' width='100'>
<defs>
<radialGradient id='grad1' cx='50' cy='50' r='24' fx='60' fy='60' spreadMethod='repeat' gradientUnits='userSpaceOnUse' >
<stop offset='0%' style='stop-color:orange;stop-opacity:1' />
<stop offset='45%' style='stop-color:red;stop-opacity:1' />
<stop offset='55%' style='stop-color:blue;stop-opacity:1' />
<stop offset='100%' style='stop-color:green;stop-opacity:1' />
</radialGradient>
</defs>
<ellipse cx='50' cy='50' rx='50' ry='20' fill='url(#grad1)' />
</svg>
""";
final EsvgDocument doc = new EsvgDocument();
doc.parse(data);
Assertions.assertDoesNotThrow(()-> Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatunCenter2.svg"), data.replace("'", "\"")));
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatunCenter2.png"));
Assertions.assertDoesNotThrow(() -> Uri.writeAll(
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatunCenter2.svg"),
data.replace("'", "\"")));
ConfigTest.generateAnImage(doc,
new Uri(ConfigTest.BASE_PATH + "TestGradientRadialunitUserspreadRepeatunCenter2.png"));
}
}