[DEV] add render of basic text in svg and many code normalisation
This commit is contained in:
parent
add77c6128
commit
6f57f68505
16
.classpath
16
.classpath
@ -17,17 +17,12 @@
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="lib/pngdecoder.jar">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5">
|
||||
<attributes>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-exml">
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-etk">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
@ -37,12 +32,17 @@
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-etk">
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-exml">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/pngencoder">
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-egami">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/atriasoft-png-encoder">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
|
@ -1,66 +0,0 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -.
|
||||
|
||||
<svg
|
||||
xmlns:dc='http://purl.org/dc/elements/1.1/'
|
||||
xmlns:cc='http://creativecommons.org/ns#'
|
||||
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
|
||||
xmlns:svg='http://www.w3.org/2000/svg'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd'
|
||||
xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape'
|
||||
id='svg3411'
|
||||
version='1.1'
|
||||
inkscape:version='0.91 r13725'
|
||||
width='100'
|
||||
height='100'
|
||||
viewBox='0 0 100 100'
|
||||
sodipodi:docname='ArtTime20.svg'>
|
||||
<metadata
|
||||
id='metadata3417'>
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about=''>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource='http://purl.org/dc/dcmitype/StillImage' />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id='defs3415' />
|
||||
<sodipodi:namedview
|
||||
pagecolor='#ffffff'
|
||||
bordercolor='#666666'
|
||||
borderopacity='1'
|
||||
objecttolerance='10'
|
||||
gridtolerance='10'
|
||||
guidetolerance='10'
|
||||
inkscape:pageopacity='0'
|
||||
inkscape:pageshadow='2'
|
||||
inkscape:window-width='1600'
|
||||
inkscape:window-height='836'
|
||||
id='namedview3413'
|
||||
showgrid='true'
|
||||
inkscape:zoom='7.1484076'
|
||||
inkscape:cx='32.846023'
|
||||
inkscape:cy='55.531879'
|
||||
inkscape:window-x='0'
|
||||
inkscape:window-y='27'
|
||||
inkscape:window-maximized='1'
|
||||
inkscape:current-layer='svg3411'>
|
||||
<inkscape:grid
|
||||
type='xygrid'
|
||||
id='grid3423' />
|
||||
</sodipodi:namedview>
|
||||
<g
|
||||
id='g4311'
|
||||
transform='translate(2.6978375,-4.244742)'>
|
||||
<path
|
||||
inkscape:connector-curvature='0'
|
||||
id='path4228'
|
||||
d='m 42.01082,10 c 0,2.666667 0,5.333333 0,8 1.666667,0 3.333333,0 5,0 0,-2.666667 0,-5.333333 0,-8 1.666667,0 3.333333,0 5,0 0,2.666667 0,5.333333 0,8'
|
||||
style='fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1' />
|
||||
</g>
|
||||
</svg>
|
@ -10,6 +10,7 @@ open module org.atriasoft.esvg {
|
||||
requires transitive io.scenarium.logger;
|
||||
requires transitive org.atriasoft.etk;
|
||||
requires transitive org.atriasoft.exml;
|
||||
requires com.pngencoder;
|
||||
requires org.atriasoft.pngencoder;
|
||||
requires java.desktop;
|
||||
requires org.atriasoft.egami;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public class Base {
|
||||
|
||||
protected PaintState paint = new PaintState();
|
||||
|
||||
protected Matrix2x3f transformMatrix = Matrix2x3f.IDENTITY; //!< specific render of the curent element
|
||||
protected Matrix2x3f transformMatrix = Matrix2x3f.IDENTITY; //!< specific render of the current element
|
||||
|
||||
public Base() {
|
||||
this.paint = new PaintState();
|
||||
@ -369,30 +369,27 @@ public class Base {
|
||||
if (data.length() != 0) {
|
||||
float[] elements = FMath.getTableFloat(data, " ", 3);
|
||||
if (elements != null) {
|
||||
float angle = elements[0] / 180 * FMath.PI;
|
||||
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])));
|
||||
} else {
|
||||
float elem = Float.parseFloat(data);
|
||||
float angle = elem / 180 * FMath.PI;
|
||||
Log.verbose("rotate : " + angle + "rad, " + (angle / FMath.PI * 180) + "0");
|
||||
elem = (float) Math.toRadians(elem);
|
||||
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createRotate(elem));
|
||||
}
|
||||
}
|
||||
data = Base.extractTransformData(inputString, "skewX");
|
||||
if (data.length() != 0) {
|
||||
float angle = Float.parseFloat(data);
|
||||
angle = angle / 180 * FMath.PI;
|
||||
Log.verbose("skewX : " + angle + "rad, " + (angle / FMath.PI * 180) + "0");
|
||||
angle = (float) Math.toRadians(angle);
|
||||
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createSkew(new Vector2f(angle, 0.0f)));
|
||||
}
|
||||
data = Base.extractTransformData(inputString, "skewY");
|
||||
if (data.length() != 0) {
|
||||
float angle = Float.parseFloat(data);
|
||||
angle = angle / 180 * FMath.PI;
|
||||
Log.verbose("skewY : " + angle + "rad, " + (angle / FMath.PI * 180) + "0");
|
||||
angle = (float) Math.toRadians(angle);
|
||||
this.transformMatrix = this.transformMatrix.multiply(Matrix2x3f.createSkew(new Vector2f(0.0f, angle)));
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,12 @@ public class Circle extends Base {
|
||||
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();
|
||||
out.moveTo(false, this.position.addX(this.radius));
|
||||
|
@ -28,6 +28,12 @@ public class Ellipse extends Base {
|
||||
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();
|
||||
out.moveTo(false, this.c.add(this.r.x(), 0.0f));
|
||||
|
@ -1,10 +1,18 @@
|
||||
package org.atriasoft.esvg;
|
||||
|
||||
import org.atriasoft.etk.ConfigFont;
|
||||
import org.atriasoft.etk.Configs;
|
||||
import org.atriasoft.etk.Uri;
|
||||
|
||||
public class Esvg {
|
||||
public static void init() {
|
||||
Uri.addLibrary("esvg", Esvg.class, "/resources/esvg/");
|
||||
|
||||
ConfigFont fonts = Configs.getConfigFonts();
|
||||
// add default Esvg fonts:
|
||||
fonts.add("FreeSherif", new Uri("FONTS", "FreeSherif.svg", "esvg"));
|
||||
fonts.add("FreeSans", new Uri("FONTS", "FreeSans.svg", "esvg"));
|
||||
fonts.add("FreeMono", new Uri("FONTS", "FreeMono.svg", "esvg"));
|
||||
}
|
||||
|
||||
private Esvg() {}
|
||||
|
@ -3,8 +3,8 @@ package org.atriasoft.esvg;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.atriasoft.egami.ImageFloatRGBA;
|
||||
import org.atriasoft.esvg.internal.Log;
|
||||
import org.atriasoft.etk.Color;
|
||||
import org.atriasoft.etk.Uri;
|
||||
import org.atriasoft.etk.util.Dynamic;
|
||||
import org.atriasoft.etk.math.Matrix2x3f;
|
||||
@ -29,6 +29,14 @@ public class EsvgDocument extends Base {
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
@ -399,11 +407,11 @@ public class EsvgDocument extends Base {
|
||||
* @param size Size expected of the rendered image (value <=0 if it need to be automatic.) return the size generate
|
||||
* @return Vector of the data used to display (simple vector: generic to transmit)
|
||||
*/
|
||||
public Color[][] renderImageFloatRGBA(final Vector2i size) {
|
||||
public ImageFloatRGBA renderImageFloatRGBA(final Vector2i size) {
|
||||
return renderImageFloatRGBA(size, false);
|
||||
}
|
||||
|
||||
public Color[][] renderImageFloatRGBA(Vector2i size, final boolean visualDebug) {
|
||||
public ImageFloatRGBA renderImageFloatRGBA(Vector2i size, final boolean visualDebug) {
|
||||
if (size == null) {
|
||||
size = new Vector2i((int) this.size.x(), (int) this.size.y());
|
||||
} else {
|
||||
|
@ -23,6 +23,58 @@ import org.atriasoft.exml.model.XmlNode;
|
||||
|
||||
// https://www.w3.org/TR/SVGTiny12/fonts.html
|
||||
|
||||
/*
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
Y | | | |
|
||||
^ |------------| |------------|
|
||||
|
|
||||
advance.y: /-> |
|
||||
| |
|
||||
| |
|
||||
sizeTex.x /-> | | |------------| |------------|
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
| | | | A | | G |
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
\-> | | |------------| |------------|
|
||||
/--> | |
|
||||
\--> \-> |
|
||||
bearing.y |
|
||||
|>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> X
|
||||
<------------------------> : advance.x
|
||||
<------------> : sizeTexture.x
|
||||
<---> : bearing.x
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_
|
||||
*----------------------* ^ ==> calculateFontRealHeight(fontSize);
|
||||
| | | ^ ==> getAscent(fontSize);
|
||||
| | | | _
|
||||
| /\ | | | ^ ==> Font Height (height of a capital letter) = fontSize
|
||||
| / \ | | | |
|
||||
| / \ | | | |
|
||||
| /------\ | | | |
|
||||
| / \ | | | |
|
||||
| / \ | |___|____|________________________==> render line
|
||||
| | | ^
|
||||
| | | |
|
||||
| | | |==> getDescent(fontSize);
|
||||
| | | |
|
||||
*----------------------* | |
|
||||
|
||||
|
||||
*/
|
||||
|
||||
public class EsvgFont {
|
||||
|
||||
/**
|
||||
@ -57,20 +109,12 @@ public class EsvgFont {
|
||||
Log.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 (XmlNode values : fontElement.getNodes()) {
|
||||
if (values.getValue().equals("glyph")) {
|
||||
nbGlyph++;
|
||||
Log.info("find flyph: " + nbGlyph);
|
||||
Glyph tmp = Glyph.valueOf(values.toElement());
|
||||
if (tmp != null) {
|
||||
font.glyphs.put(tmp.getUnicodeValue(), tmp);
|
||||
}
|
||||
} else if (values.getValue().equals("hkern")) {
|
||||
// check later ...
|
||||
} else if (values.getValue().equals("missing-glyph")) {
|
||||
font.missingGlyph = Glyph.valueOf(values.toElement());
|
||||
} else if (values.getValue().equals("font-face")) {
|
||||
if (values.getValue().equals("font-face")) {
|
||||
if (values instanceof XmlElement fontFace) {
|
||||
font.fontFamily = fontFace.getAttribute("font-family", "unknown");
|
||||
font.fontStretch = fontFace.getAttribute("font-stretch", "normal");
|
||||
@ -103,6 +147,22 @@ public class EsvgFont {
|
||||
int stop = Integer.parseInt(tmpSplit[1], 16);
|
||||
font.unicodeRange = new Pair<>(start, stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (XmlNode values : fontElement.getNodes()) {
|
||||
if (values.getValue().equals("glyph")) {
|
||||
nbGlyph++;
|
||||
//Log.info("find flyph: " + nbGlyph);
|
||||
Glyph tmp = Glyph.valueOf(values.toElement(), font);
|
||||
if (tmp != null) {
|
||||
font.glyphs.put(tmp.getUnicodeValue(), tmp);
|
||||
}
|
||||
} else if (values.getValue().equals("hkern")) {
|
||||
// check later ...
|
||||
} else if (values.getValue().equals("missing-glyph")) {
|
||||
font.missingGlyph = Glyph.valueOf(values.toElement(), font);
|
||||
} else if (values.getValue().equals("font-face")) {
|
||||
// already done ...
|
||||
} else {
|
||||
Log.warning("unsupported node name :" + values.getValue());
|
||||
}
|
||||
@ -136,6 +196,7 @@ public class EsvgFont {
|
||||
for (Map.Entry<Integer, Glyph> entry : font.glyphs.entrySet()) {
|
||||
if (entry.getValue().getName().equals(g1Splited[iii])) {
|
||||
entry.getValue().addKerning(elementsKerning);
|
||||
font.hasKerning = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -157,9 +218,10 @@ public class EsvgFont {
|
||||
private String fontStretch = "normal";
|
||||
private int fontWeight = 400;
|
||||
private final Map<Integer, Glyph> glyphs = new HashMap<>();
|
||||
private boolean hasKerning = false;
|
||||
// The horizontal advance after rendering the glyph in horizontal orientation. If the attribute is not specified, the effect is as if the attribute were set to the value of the font's 'horiz-adv-x' attribute.
|
||||
// Glyph widths are required to be non-negative, even if the glyph is typically rendered right-to-left, as in Hebrew and Arabic scripts.
|
||||
private final int horizAdvX = 0;
|
||||
private int horizAdvX = 100;
|
||||
private Glyph missingGlyph = null;
|
||||
private int[] panose1 = { 2, 2, 6, 3, 5, 4, 5, 2, 3, 4 };
|
||||
private int underlinePosition = -150;
|
||||
@ -179,6 +241,31 @@ public class EsvgFont {
|
||||
|
||||
}
|
||||
|
||||
public float calculateFontSizeWithHeight(final float fontHeight) {
|
||||
return fontHeight * this.capHeight / this.unitsPerEm;
|
||||
}
|
||||
|
||||
public Vector2f calculateRenderOffset(final int fontSize) {
|
||||
int realSize = calculateFontRealHeight(fontSize);
|
||||
float deltaY = realSize * this.ascent / this.unitsPerEm;
|
||||
return new Vector2f(0, deltaY);
|
||||
}
|
||||
|
||||
public float calculateSclaleFactor(final int fontSize) {
|
||||
int realSize = calculateFontRealHeight(fontSize);
|
||||
return (float) realSize / (float) this.unitsPerEm;
|
||||
}
|
||||
|
||||
public int calculateWidth(final int uVal, final int fontSize) {
|
||||
Glyph glyph = getGlyph(uVal);
|
||||
if (glyph == null) {
|
||||
return 0;
|
||||
}
|
||||
int realSize = calculateFontRealHeight(fontSize);
|
||||
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);
|
||||
}
|
||||
@ -203,6 +290,20 @@ public class EsvgFont {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rendering size of the specific glyph (size rendered in the Weight class).
|
||||
* @param unicodeValue Unicode value to render
|
||||
* @param fontSize Size of the font
|
||||
* @return the size in pixel of the rendering elements
|
||||
*/
|
||||
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) {
|
||||
Glyph out = this.glyphs.get(glyphIndex);
|
||||
if (out == null) {
|
||||
@ -211,13 +312,36 @@ public class EsvgFont {
|
||||
return out;
|
||||
}
|
||||
|
||||
public Glyph getGlyphNullIfMissing(final int glyphIndex) {
|
||||
Glyph out = this.glyphs.get(glyphIndex);
|
||||
if (out == null) {
|
||||
return null;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public int getHorizAdvX() {
|
||||
return this.horizAdvX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of available glyph in the Font
|
||||
* @return the glyph count.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public boolean hasKerning() {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
return this.hasKerning;
|
||||
}
|
||||
|
||||
public Weight render(final int uVal, final int fontSize) {
|
||||
@ -233,7 +357,7 @@ public class EsvgFont {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
Weight data = glyph.getModel().drawFill(new Vector2i((int) (glyph.getHorizAdvX() * scale), realSize), transform, 8, config);
|
||||
Weight data = glyph.getModel().drawFill(calculateWidthRendering(uVal, fontSize), transform, 8, config);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -269,7 +393,7 @@ public class EsvgFont {
|
||||
Matrix2x3f transform = Matrix2x3f.createTranslate(new Vector2f(0, -this.descent)).multiply(Matrix2x3f.createScale(scale));
|
||||
PathModel model = glyph.getModel();
|
||||
if (model != null) {
|
||||
Weight redered = model.drawFill(new Vector2i((int) (glyph.getHorizAdvX() * scale), realSize), transform, 8, config);
|
||||
Weight redered = model.drawFill(calculateWidthRendering((int) uVal, fontSize), transform, 8, config);
|
||||
weight.fusion(redered, offsetWriting, 0);
|
||||
}
|
||||
offsetWriting += advenceXLocal;
|
||||
|
86
src/org/atriasoft/esvg/FontCache.java
Normal file
86
src/org/atriasoft/esvg/FontCache.java
Normal file
@ -0,0 +1,86 @@
|
||||
package org.atriasoft.esvg;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.atriasoft.etk.ConfigFont;
|
||||
import org.atriasoft.etk.Configs;
|
||||
import org.atriasoft.etk.Uri;
|
||||
|
||||
public class FontCache {
|
||||
private static final Map<String, EsvgFont> CACHE_FONTS = new HashMap<>();
|
||||
|
||||
public static boolean existFont(final String fontName, final boolean bold, final boolean italic) {
|
||||
ConfigFont fontsConfigs = Configs.getConfigFonts();
|
||||
Uri baseUri = fontsConfigs.getFontUri(fontName);
|
||||
if (baseUri == null) {
|
||||
return false;
|
||||
}
|
||||
if (bold && italic) {
|
||||
Uri theoricUri = new Uri(baseUri.getGroup(), baseUri.getPath().replace(".svg", "BoldOblique.svg"), baseUri.getproperties());
|
||||
return theoricUri.exist();
|
||||
}
|
||||
if (bold && !italic) {
|
||||
Uri theoricUri = new Uri(baseUri.getGroup(), baseUri.getPath().replace(".svg", "Bold.svg"), baseUri.getproperties());
|
||||
return theoricUri.exist();
|
||||
}
|
||||
if (!bold && italic) {
|
||||
Uri theoricUri = new Uri(baseUri.getGroup(), baseUri.getPath().replace(".svg", "Oblique.svg"), baseUri.getproperties());
|
||||
return theoricUri.exist();
|
||||
}
|
||||
if (!bold && !italic) {
|
||||
Uri theoricUri = baseUri;
|
||||
return theoricUri.exist();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static EsvgFont getFont(final String fontName, final boolean bold, final boolean italic) {
|
||||
String finalName = fontName + "__" + bold + "__" + italic;
|
||||
EsvgFont font = FontCache.CACHE_FONTS.get(finalName);
|
||||
if (font != null) {
|
||||
return font;
|
||||
}
|
||||
// try to find it:
|
||||
ConfigFont fontsConfigs = Configs.getConfigFonts();
|
||||
Uri baseUri = fontsConfigs.getFontUri(fontName);
|
||||
if (baseUri != null) {
|
||||
Uri theoricUri = null;
|
||||
if (bold && italic) {
|
||||
theoricUri = new Uri(baseUri.getGroup(), baseUri.getPath().replace(".svg", "BoldOblique.svg"), baseUri.getproperties());
|
||||
if (!theoricUri.exist()) {
|
||||
theoricUri = null;
|
||||
}
|
||||
}
|
||||
if (theoricUri == null || (bold && !italic)) {
|
||||
theoricUri = new Uri(baseUri.getGroup(), baseUri.getPath().replace(".svg", "Bold.svg"), baseUri.getproperties());
|
||||
if (!theoricUri.exist()) {
|
||||
theoricUri = null;
|
||||
}
|
||||
}
|
||||
if (theoricUri == null || (!bold && italic)) {
|
||||
theoricUri = new Uri(baseUri.getGroup(), baseUri.getPath().replace(".svg", "Oblique.svg"), baseUri.getproperties());
|
||||
if (!theoricUri.exist()) {
|
||||
theoricUri = null;
|
||||
}
|
||||
}
|
||||
if (theoricUri == null || (!bold && !italic)) {
|
||||
theoricUri = baseUri;
|
||||
if (!theoricUri.exist()) {
|
||||
theoricUri = null;
|
||||
}
|
||||
}
|
||||
if (theoricUri != null) {
|
||||
FontCache.CACHE_FONTS.put(finalName, EsvgFont.load(theoricUri));
|
||||
return FontCache.CACHE_FONTS.get(finalName);
|
||||
}
|
||||
}
|
||||
String defaultFontName = Configs.getConfigFonts().getName();
|
||||
if (defaultFontName.equals(fontName)) {
|
||||
return null;
|
||||
}
|
||||
return FontCache.getFont(defaultFontName, bold, italic);
|
||||
}
|
||||
|
||||
private FontCache() {}
|
||||
}
|
@ -6,5 +6,5 @@ package org.atriasoft.esvg;
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
public enum GradientUnits {
|
||||
gradientUnitsobjectBoundingBox, gradientUnitsuserSpaceOnUse
|
||||
GRADIENT_UNITS_OBJECT_BOUNDING_BOX, GRADIENT_UNITS_USER_SPACE_ON_USE
|
||||
}
|
||||
|
255
src/org/atriasoft/esvg/GraphicContext.java
Normal file
255
src/org/atriasoft/esvg/GraphicContext.java
Normal file
@ -0,0 +1,255 @@
|
||||
package org.atriasoft.esvg;
|
||||
|
||||
import org.atriasoft.egami.ImageByteRGBA;
|
||||
import org.atriasoft.esvg.internal.Log;
|
||||
import org.atriasoft.esvg.render.PathModel;
|
||||
import org.atriasoft.etk.Color;
|
||||
import org.atriasoft.etk.math.Vector2f;
|
||||
import org.atriasoft.etk.math.Vector2i;
|
||||
|
||||
/**
|
||||
* Graphic context is used to manage dynamic
|
||||
* @author heero
|
||||
*
|
||||
*/
|
||||
public class GraphicContext {
|
||||
private EsvgDocument document;
|
||||
PaintState paintState;
|
||||
private PathModel path = null;
|
||||
private Vector2i size = Vector2i.VALUE_32;
|
||||
|
||||
public GraphicContext() {
|
||||
clear();
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
public Color getColorFill() {
|
||||
return this.paintState.getFill();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stroke color.
|
||||
* @return Stroke color.
|
||||
*/
|
||||
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 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");
|
||||
return;
|
||||
}
|
||||
this.path.lineTo(false, pos);
|
||||
}
|
||||
|
||||
public void pathLineTo(final Vector2f pos) {
|
||||
if (this.path == null) {
|
||||
Log.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");
|
||||
return;
|
||||
}
|
||||
this.path.moveTo(false, pos);
|
||||
}
|
||||
|
||||
public void pathMoveTo(final Vector2f pos) {
|
||||
if (this.path == null) {
|
||||
Log.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;
|
||||
}
|
||||
this.path.close(false);
|
||||
this.document.addElement(new Path(this.path, this.paintState.clone()));
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
public void pathStopLinked() {
|
||||
if (this.path == null) {
|
||||
return;
|
||||
}
|
||||
this.path.close(true);
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
pathStop();
|
||||
}
|
||||
this.document.addElement(new Rectangle(position, width, this.paintState.clone()));
|
||||
}
|
||||
|
||||
public ImageByteRGBA render() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String renderSvg() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the fill color
|
||||
* @param color Color to set on fill
|
||||
* @apiNote use clearFill() if you want to remove drawing of fill
|
||||
*/
|
||||
public void setColorFill(final Color color) {
|
||||
this.paintState.setFill(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the stroke color
|
||||
* @param color Color to set on stroke
|
||||
* @apiNote use clearStroke() if you want to remove drawing of stroke
|
||||
*/
|
||||
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
|
||||
* @param yyy Height of the image
|
||||
* @apiNote It will clear the current context.
|
||||
*/
|
||||
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
|
||||
* @apiNote It will clear the current context.
|
||||
*/
|
||||
private void setSize(final Vector2i size) {
|
||||
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, height, data, this.paintState.clone()));
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -28,6 +28,12 @@ public class Line extends Base {
|
||||
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();
|
||||
out.clear();
|
||||
@ -52,8 +58,6 @@ public class Line extends Base {
|
||||
|
||||
PointList listPoints = new PointList();
|
||||
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();
|
||||
@ -74,9 +78,6 @@ public class Line extends Base {
|
||||
}
|
||||
// add on images:
|
||||
myRenderer.print(tmpFill, colorFill, tmpStroke, colorStroke, this.paint.opacity);
|
||||
//myRenderer.addDebugSegment(listSegmentFill);
|
||||
//myRenderer.addDebugSegment(listSegmentStroke);
|
||||
//myRenderer.addDebugSegment(listElement.debugInformation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,7 +29,7 @@ public class LinearGradient extends Base {
|
||||
|
||||
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.gradientUnitsobjectBoundingBox; //!< incompatible with href
|
||||
public GradientUnits unit = GradientUnits.GRADIENT_UNITS_OBJECT_BOUNDING_BOX; //!< incompatible with href
|
||||
|
||||
public LinearGradient(final PaintState parentPaintState) {
|
||||
super(parentPaintState);
|
||||
@ -109,9 +109,9 @@ public class LinearGradient extends Base {
|
||||
}
|
||||
contentX = element.getAttribute("gradientUnits", "");
|
||||
if (contentX.equals("userSpaceOnUse")) {
|
||||
this.unit = GradientUnits.gradientUnitsuserSpaceOnUse;
|
||||
this.unit = GradientUnits.GRADIENT_UNITS_USER_SPACE_ON_USE;
|
||||
} else {
|
||||
this.unit = GradientUnits.gradientUnitsobjectBoundingBox;
|
||||
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");
|
||||
}
|
||||
|
@ -30,6 +30,14 @@ public class PaintState {
|
||||
this.opacity = 1.0f;
|
||||
}
|
||||
|
||||
public void clearFill() {
|
||||
this.fill = new Pair<Color, String>(Color.NONE, "");
|
||||
}
|
||||
|
||||
public void clearStroke() {
|
||||
this.stroke = new Pair<Color, String>(Color.NONE, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PaintState clone() {
|
||||
PaintState out = new PaintState();
|
||||
@ -45,4 +53,60 @@ public class PaintState {
|
||||
return out;
|
||||
}
|
||||
|
||||
public Color getFill() {
|
||||
return this.fill.first;
|
||||
}
|
||||
|
||||
public CapMode getLineCap() {
|
||||
return this.lineCap;
|
||||
}
|
||||
|
||||
public JoinMode getLineJoin() {
|
||||
return this.lineJoin;
|
||||
}
|
||||
|
||||
public float getMiterLimit() {
|
||||
return this.miterLimit;
|
||||
}
|
||||
|
||||
public float getOpacity() {
|
||||
return this.opacity;
|
||||
}
|
||||
|
||||
public Color getStroke() {
|
||||
return this.stroke.first;
|
||||
}
|
||||
|
||||
public float getStrokeWidth() {
|
||||
return this.strokeWidth;
|
||||
}
|
||||
|
||||
public void setFill(final Color color) {
|
||||
this.fill = new Pair<Color, String>(color, "");
|
||||
}
|
||||
|
||||
public void setLineCap(final CapMode lineCap) {
|
||||
this.lineCap = lineCap;
|
||||
}
|
||||
|
||||
public void setLineJoin(final JoinMode lineJoin) {
|
||||
this.lineJoin = lineJoin;
|
||||
}
|
||||
|
||||
public void setMiterLimit(final float miterLimit) {
|
||||
this.miterLimit = miterLimit;
|
||||
}
|
||||
|
||||
public void setOpacity(final float opacity) {
|
||||
this.opacity = opacity;
|
||||
}
|
||||
|
||||
public void setStroke(final Color color) {
|
||||
this.stroke = new Pair<Color, String>(color, "");
|
||||
}
|
||||
|
||||
public void setStrokeWidth(final float strokeWidth) {
|
||||
this.strokeWidth = strokeWidth;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -316,6 +316,11 @@ public class Path extends Base {
|
||||
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);
|
||||
|
@ -28,7 +28,7 @@ public class RadialGradient extends Base {
|
||||
private String href = ""; //!< in case of using a single gradient in multiple gradient, the gradient is store in an other element...
|
||||
private Dimension1D radius = new Dimension1D(50, Distance.POURCENT); //!< Radius of the gradient
|
||||
public SpreadMethod spread = SpreadMethod.PAD;
|
||||
public GradientUnits unit = GradientUnits.gradientUnitsobjectBoundingBox;
|
||||
public GradientUnits unit = GradientUnits.GRADIENT_UNITS_OBJECT_BOUNDING_BOX;
|
||||
|
||||
public RadialGradient(final PaintState parentPaintState) {
|
||||
super(parentPaintState);
|
||||
@ -114,9 +114,9 @@ public class RadialGradient extends Base {
|
||||
}
|
||||
contentX = element.getAttribute("gradientUnits", "");
|
||||
if (contentX.equals("userSpaceOnUse")) {
|
||||
this.unit = GradientUnits.gradientUnitsuserSpaceOnUse;
|
||||
this.unit = GradientUnits.GRADIENT_UNITS_USER_SPACE_ON_USE;
|
||||
} else {
|
||||
this.unit = GradientUnits.gradientUnitsobjectBoundingBox;
|
||||
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");
|
||||
}
|
||||
|
@ -30,6 +30,19 @@ public class Rectangle extends Base {
|
||||
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) {
|
||||
super(parentPaintState);
|
||||
this.position = position;
|
||||
this.size = size;
|
||||
this.roundedCorner = roundedCorner;
|
||||
}
|
||||
|
||||
private PathModel createPath() {
|
||||
PathModel out = new PathModel();
|
||||
out.clear();
|
||||
|
@ -3,6 +3,7 @@ package org.atriasoft.esvg;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.atriasoft.egami.ImageFloatRGBA;
|
||||
import org.atriasoft.esvg.render.DynamicColor;
|
||||
import org.atriasoft.esvg.render.DynamicColorSpecial;
|
||||
import org.atriasoft.esvg.render.Point;
|
||||
@ -13,7 +14,6 @@ import org.atriasoft.etk.Color;
|
||||
import org.atriasoft.etk.math.FMath;
|
||||
import org.atriasoft.etk.math.Vector2f;
|
||||
import org.atriasoft.etk.math.Vector2i;
|
||||
import org.atriasoft.etk.util.ArraysTools;
|
||||
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
@ -22,7 +22,7 @@ import org.atriasoft.etk.util.ArraysTools;
|
||||
*/
|
||||
public class Renderer {
|
||||
private static final boolean DEBUG_MODE = false;
|
||||
protected Color[][] buffer; // for debug
|
||||
protected ImageFloatRGBA buffer; // for debug
|
||||
protected EsvgDocument document; // for debug
|
||||
|
||||
private int factor = 1;
|
||||
@ -81,9 +81,9 @@ public class Renderer {
|
||||
float xpos = coefficient * subSamplingCenterPos + bbb;
|
||||
if (xpos >= 0 && xpos < dynamicSize.x() && yyy >= 0 && yyy < dynamicSize.y()) {
|
||||
if (it.direction == 1.0f) {
|
||||
this.buffer[yyy][(int) (xpos)] = Color.BLUE;
|
||||
this.buffer.setColor((int) xpos, yyy, Color.BLUE);
|
||||
} else {
|
||||
this.buffer[yyy][(int) (xpos)] = Color.DARK_RED;
|
||||
this.buffer.setColor((int) xpos, yyy, Color.DARK_RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,16 +119,16 @@ public class Renderer {
|
||||
float ypos = coefficient * subSamplingCenterPos + bbb;
|
||||
if (ypos >= 0 && ypos < dynamicSize.y() && xxx >= 0 && xxx < dynamicSize.y()) {
|
||||
if (it.direction == 1.0f) {
|
||||
this.buffer[(int) (ypos)][xxx] = Color.BLUE;
|
||||
this.buffer.setColor(xxx, (int) ypos, Color.BLUE);
|
||||
} else {
|
||||
this.buffer[(int) (ypos)][xxx] = Color.DARK_RED;
|
||||
this.buffer.setColor(xxx, (int) ypos, Color.DARK_RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Color[][] getData() {
|
||||
ImageFloatRGBA getData() {
|
||||
return this.buffer;
|
||||
}
|
||||
|
||||
@ -208,11 +208,11 @@ public class Renderer {
|
||||
for (int deltaX = 0; deltaX < this.factor; deltaX++) {
|
||||
int idx = xxx * this.factor + deltaX;
|
||||
int idy = yyy * this.factor + deltaY;
|
||||
this.buffer[idy][idx] = mergeColor(this.buffer[idy][idx], intermediateColor);
|
||||
this.buffer.mergeColor(idx, idy, intermediateColor);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.buffer[yyy][xxx] = mergeColor(this.buffer[yyy][xxx], intermediateColor);
|
||||
this.buffer.mergeColor(xxx, yyy, intermediateColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -256,11 +256,10 @@ public class Renderer {
|
||||
public void setSize(final Vector2i size) {
|
||||
this.size = size;
|
||||
if (Renderer.DEBUG_MODE) {
|
||||
this.buffer = new Color[this.size.y()][this.size.x()];
|
||||
this.buffer = new ImageFloatRGBA(this.size);
|
||||
} else {
|
||||
this.buffer = new Color[this.size.y() * this.factor][this.size.x() * this.factor];
|
||||
this.buffer = new ImageFloatRGBA(this.size.multiply(this.factor));
|
||||
}
|
||||
ArraysTools.fill2(this.buffer, Color.NONE);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,103 +0,0 @@
|
||||
package org.atriasoft.esvg;
|
||||
|
||||
import org.atriasoft.esvg.render.Weight;
|
||||
import org.atriasoft.etk.Color;
|
||||
import org.atriasoft.etk.math.FMath;
|
||||
import org.atriasoft.etk.math.Vector2i;
|
||||
import org.atriasoft.etk.util.ArraysTools;
|
||||
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
public class RendererFont {
|
||||
protected float[][] buffer; // for debug
|
||||
protected EsvgFont document; // for debug
|
||||
|
||||
protected int interpolationRecurtionMax = 10;
|
||||
|
||||
protected float interpolationThreshold = 0.25f;
|
||||
protected int nbSubScanLine = 8;
|
||||
protected Vector2i size;
|
||||
|
||||
public RendererFont(final Vector2i size, final EsvgFont document) {
|
||||
this(size, document, false);
|
||||
}
|
||||
|
||||
public RendererFont(final Vector2i size, final EsvgFont document, final boolean visualDebug) {
|
||||
this.size = size;
|
||||
this.document = document;
|
||||
setSize(size);
|
||||
}
|
||||
|
||||
float[][] getData() {
|
||||
return this.buffer;
|
||||
}
|
||||
|
||||
int getInterpolationRecurtionMax() {
|
||||
return this.interpolationRecurtionMax;
|
||||
}
|
||||
|
||||
float getInterpolationThreshold() {
|
||||
return this.interpolationThreshold;
|
||||
}
|
||||
|
||||
int getNumberSubScanLine() {
|
||||
return this.nbSubScanLine;
|
||||
}
|
||||
|
||||
Vector2i getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
protected float mergeColor(final float base, final float integration) {
|
||||
return FMath.avg(0.0f, integration + base, 1.0f);
|
||||
}
|
||||
|
||||
public void print(final Weight weightFill, final Weight weightStroke, final float opacity) {
|
||||
// all together
|
||||
for (int yyy = 0; yyy < this.size.y(); ++yyy) {
|
||||
for (int xxx = 0; xxx < this.size.x(); ++xxx) {
|
||||
|
||||
Vector2i pos = new Vector2i(xxx, yyy);
|
||||
float valueFill = weightFill.get(pos);
|
||||
float valueStroke = weightStroke.get(pos);
|
||||
// calculate merge of stroke and fill value:
|
||||
Color intermediateColorFill = Color.NONE;
|
||||
/*
|
||||
Color intermediateColorStroke = Color.NONE;
|
||||
if (colorFill != null && valueFill != 0.0f) {
|
||||
intermediateColorFill = colorFill.getColor(pos);
|
||||
intermediateColorFill = intermediateColorFill.withA(intermediateColorFill.a() * valueFill);
|
||||
}
|
||||
if (colorStroke != null && valueStroke != 0.0f) {
|
||||
intermediateColorStroke = colorStroke.getColor(pos);
|
||||
intermediateColorStroke = intermediateColorStroke.withA(intermediateColorStroke.a() * valueStroke);
|
||||
}
|
||||
Color intermediateColor = mergeColor(intermediateColorFill, intermediateColorStroke);
|
||||
intermediateColor = intermediateColor.withA(intermediateColor.a() * opacity);
|
||||
this.buffer[yyy][xxx] = mergeColor(this.buffer[yyy][xxx], intermediateColor);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setInterpolationRecurtionMax(final int value) {
|
||||
this.interpolationRecurtionMax = FMath.avg(1, value, 200);
|
||||
}
|
||||
|
||||
void setInterpolationThreshold(final float value) {
|
||||
this.interpolationThreshold = FMath.avg(0.0f, value, 20000.0f);
|
||||
}
|
||||
|
||||
void setNumberSubScanLine(final int value) {
|
||||
this.nbSubScanLine = FMath.avg(1, value, 200);
|
||||
}
|
||||
|
||||
public void setSize(final Vector2i size) {
|
||||
this.size = size;
|
||||
this.buffer = new float[this.size.y()][this.size.x()];
|
||||
ArraysTools.fill2(this.buffer, 0.0f);
|
||||
}
|
||||
|
||||
}
|
@ -1,31 +1,271 @@
|
||||
package org.atriasoft.esvg;
|
||||
|
||||
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;
|
||||
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.atriasoft.exml.model.XmlNode;
|
||||
import org.atriasoft.exml.model.XmlText;
|
||||
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
public class Text extends Base {
|
||||
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) {
|
||||
super(parentPaintState);
|
||||
this.position = position;
|
||||
this.fontSize = fontSize;
|
||||
this.texts.add(new TextSpan(position, decoratedText, FontProperty.DEFAULT_FONT, parentPaintState.clone()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(final int spacing) {
|
||||
Log.debug(spacingDist(spacing) + "Text");
|
||||
Log.debug(spacingDist(spacing) + "Text : ");
|
||||
for (TextSpan elem : this.texts) {
|
||||
Log.debug(spacingDist(spacing + 1) + elem.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(final Renderer myRenderer, final Matrix2x3f basicTrans, final int level) {
|
||||
Log.warning(spacingDist(level) + "DRAW esvg::Text ==> position = " + this.position);
|
||||
if (this.texts.size() == 0) {
|
||||
Log.verbose(spacingDist(level + 1) + "No text ...");
|
||||
return;
|
||||
}
|
||||
boolean withKerning = true;
|
||||
|
||||
for (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());
|
||||
if (font == null) {
|
||||
Log.error("Can not get the font :" + elem.fontState());
|
||||
return;
|
||||
}
|
||||
|
||||
int realSize = font.calculateFontRealHeight((int) elem.fontState().fontSize());
|
||||
float scale = realSize / font.getUnitsPerEm();
|
||||
|
||||
float offsetWriting = 0;
|
||||
int lastValue = 0;
|
||||
for (char uVal : elem.text().toCharArray()) {
|
||||
Log.warning(spacingDist(level) + " elem.position = " + elem.position());
|
||||
Glyph glyph = font.getGlyph(uVal);
|
||||
if (glyph == null) {
|
||||
lastValue = uVal;
|
||||
continue;
|
||||
}
|
||||
if (withKerning) {
|
||||
//offsetWriting -= glyph.getKerning(lastValue) * scale;
|
||||
Log.info(" ==> kerning offset = " + (glyph.getKerning(lastValue) * scale));
|
||||
lastValue = uVal;
|
||||
}
|
||||
|
||||
float advenceXLocal = glyph.getHorizAdvX() * scale;
|
||||
|
||||
//Matrix2x3f mtx = this.transformMatrix;
|
||||
Vector2f tranlate = new Vector2f(elem.position().x() + offsetWriting, elem.position().y() - font.getDescent() * scale);
|
||||
Log.warning("translate : " + tranlate);
|
||||
Matrix2x3f translateGlyph = Matrix2x3f.createTranslate(tranlate);
|
||||
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();
|
||||
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();
|
||||
// Check if we need to display background
|
||||
if (colorFill != null) {
|
||||
SegmentList listSegmentFill = new SegmentList();
|
||||
listSegmentFill.createSegmentList(listPoints);
|
||||
colorFill.setViewPort(listSegmentFill.getViewPort());
|
||||
listSegmentFill.applyMatrix(mtx);
|
||||
// TODO but need check ... listSegmentFill.clearHorizontals();
|
||||
// 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();
|
||||
DynamicColor colorStroke = null;
|
||||
if (this.paint.strokeWidth > 0.0f) {
|
||||
colorStroke = DynamicColor.createColor(this.paint.stroke, mtx);
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
// add on images:
|
||||
myRenderer.print(tmpFill, colorFill, tmpStroke, colorStroke, this.paint.opacity);
|
||||
}
|
||||
offsetWriting += advenceXLocal;
|
||||
Log.error("offset X =" + offsetWriting + " + " + advenceXLocal + " " + uVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parseXML(final XmlElement element, final Matrix2x3f parentTrans, final Dynamic<Vector2f> sizeMax) {
|
||||
sizeMax.value = Vector2f.ZERO;
|
||||
Log.error("NOT IMPLEMENTED");
|
||||
return false;
|
||||
// line must have a minimum size...
|
||||
this.paint.strokeWidth = 0;
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
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");
|
||||
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']");
|
||||
}
|
||||
boolean bold = false;
|
||||
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']");
|
||||
}
|
||||
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"));
|
||||
this.position = Vector2f.ZERO;
|
||||
|
||||
String content = element.getAttribute("x", "");
|
||||
if (content.length() != 0) {
|
||||
this.position = this.position.withX(parseLength(content));
|
||||
}
|
||||
content = element.getAttribute("y", "");
|
||||
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()));
|
||||
} else {
|
||||
Log.warning("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()) {
|
||||
Glyph glyph = getGlyph(uVal);
|
||||
if (glyph == null) {
|
||||
lastValue = uVal;
|
||||
continue;
|
||||
}
|
||||
if (withKerning) {
|
||||
offsetWriting -= glyph.getKerning(lastValue) * scale;
|
||||
Log.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();
|
||||
if (model != null) {
|
||||
Weight redered = model.drawFill(calculateWidthRendering((int) uVal, fontSize), transform, 8, config);
|
||||
weight.fusion(redered, offsetWriting, 0);
|
||||
}
|
||||
offsetWriting += advenceXLocal;
|
||||
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
record FontProperty(
|
||||
String fontName,
|
||||
float fontSize,
|
||||
boolean bold,
|
||||
boolean italic) {
|
||||
public static final FontProperty DEFAULT_FONT = new FontProperty("FreeSans", 15, false, false);
|
||||
}
|
||||
|
||||
record TextSpan(
|
||||
Vector2f position,
|
||||
String text,
|
||||
FontProperty fontState,
|
||||
PaintState paintState) {}
|
||||
|
||||
/**
|
||||
sample:
|
||||
*/
|
||||
|
@ -3,6 +3,7 @@ package org.atriasoft.esvg.font;
|
||||
import java.util.ArrayList;
|
||||
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;
|
||||
@ -12,12 +13,20 @@ public class Glyph {
|
||||
private static final boolean LAZY_MODE = true;
|
||||
|
||||
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 + "'");
|
||||
int horizAdvX = Integer.parseInt(element.getAttribute("horiz-adv-x", "0"));
|
||||
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 + "'");
|
||||
@ -63,6 +72,7 @@ public class Glyph {
|
||||
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) {
|
||||
@ -97,7 +107,7 @@ public class Glyph {
|
||||
}
|
||||
for (Kerning elem : this.kernings) {
|
||||
if (elem.unicode() == unicodeValue) {
|
||||
Log.info("Get kerning between : '" + (char) this.unicodeValue + "' and '" + (char) unicodeValue + "' => " + elem.offset());
|
||||
Log.verbose("Get kerning between : '" + (char) this.unicodeValue + "' and '" + (char) unicodeValue + "' => " + elem.offset());
|
||||
return elem.offset();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,36 @@
|
||||
package org.atriasoft.esvg.font;
|
||||
|
||||
/**
|
||||
* @notindoc
|
||||
* Kerning properties of one specific Glyph with an other
|
||||
*
|
||||
* Without Kerning :
|
||||
* [pre]
|
||||
*
|
||||
* \ / /\
|
||||
* \ / / \
|
||||
* \ / / \
|
||||
* \ / /------\
|
||||
* \ / / \
|
||||
* \/ / \
|
||||
* v v a a
|
||||
* [/pre]
|
||||
*
|
||||
* With Kerning :
|
||||
* [pre]
|
||||
*
|
||||
* \ / /\
|
||||
* \ / / \
|
||||
* \ / / \
|
||||
* \ / /\
|
||||
* \ / / \
|
||||
* \/ / \
|
||||
* v a v a
|
||||
* [/pre]
|
||||
*
|
||||
* @note The "Kerning" is the methode to provide a better display for some string like
|
||||
* the "VA" has 2 letter that overlap themself. This name Kerning
|
||||
*/
|
||||
public record Kerning(
|
||||
float offset,
|
||||
int unicode) {}
|
||||
|
@ -241,7 +241,7 @@ public class DynamicColorSpecial implements DynamicColor {
|
||||
|
||||
private Color getColorLinear(final Vector2i pos) {
|
||||
float ratio = 0.0f;
|
||||
if (this.unit == GradientUnits.gradientUnitsuserSpaceOnUse) {
|
||||
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);
|
||||
|
@ -10,7 +10,7 @@ import org.atriasoft.etk.math.Vector2f;
|
||||
|
||||
public class ElementBezierCurveTo extends Element {
|
||||
public ElementBezierCurveTo(final boolean relative, final Vector2f pos1, final Vector2f pos) {
|
||||
super(PathType.bezierCurveTo, relative);
|
||||
super(PathType.BEZIER_CURVE_TO, relative);
|
||||
this.pos = pos;
|
||||
this.pos1 = pos1;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import org.atriasoft.etk.math.Vector2f;
|
||||
public class ElementBezierSmoothCurveTo extends Element {
|
||||
|
||||
ElementBezierSmoothCurveTo(final boolean relative, final Vector2f pos) {
|
||||
super(PathType.bezierSmoothCurveTo, relative);
|
||||
super(PathType.BEZIER_SMOOTH_CURVE_TO, relative);
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,11 @@ package org.atriasoft.esvg.render;
|
||||
|
||||
public class ElementClose extends Element {
|
||||
ElementClose() {
|
||||
super(PathType.close, false);
|
||||
super(PathType.CLOSE, false);
|
||||
}
|
||||
|
||||
ElementClose(final boolean relative) {
|
||||
super(PathType.close, relative);
|
||||
super(PathType.CLOSE, relative);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -10,7 +10,7 @@ import org.atriasoft.etk.math.Vector2f;
|
||||
|
||||
public class ElementCurveTo extends Element {
|
||||
public ElementCurveTo(final boolean relative, final Vector2f pos1, final Vector2f pos2, final Vector2f pos) {
|
||||
super(PathType.curveTo, relative);
|
||||
super(PathType.CURVE_TO, relative);
|
||||
this.pos = pos;
|
||||
this.pos1 = pos1;
|
||||
this.pos2 = pos2;
|
||||
|
@ -14,7 +14,7 @@ public class ElementElliptic extends Element {
|
||||
|
||||
public ElementElliptic(final boolean relative, final Vector2f radius, // in this.pos1
|
||||
final float angle, final boolean largeArcFlag, final boolean sweepFlag, final Vector2f pos) {
|
||||
super(PathType.elliptic, relative);
|
||||
super(PathType.ELLIPTIC, relative);
|
||||
this.pos1 = radius;
|
||||
this.pos = pos;
|
||||
this.angle = angle;
|
||||
|
@ -9,7 +9,7 @@ import org.atriasoft.etk.math.Vector2f;
|
||||
|
||||
public class ElementLineTo extends Element {
|
||||
public ElementLineTo(final boolean relative, final Vector2f pos) {
|
||||
super(PathType.lineTo, relative);
|
||||
super(PathType.LINE_TO, relative);
|
||||
this.pos = pos;
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package org.atriasoft.esvg.render;
|
||||
|
||||
public class ElementLineToH extends Element {
|
||||
public ElementLineToH(final boolean relative, final float poX) {
|
||||
super(PathType.lineToH, relative);
|
||||
super(PathType.LINE_TO_H, relative);
|
||||
this.pos = this.pos.withX(poX);
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package org.atriasoft.esvg.render;
|
||||
|
||||
public class ElementLineToV extends Element {
|
||||
public ElementLineToV(final boolean relative, final float posY) {
|
||||
super(PathType.lineToV, relative);
|
||||
super(PathType.LINE_TO_V, relative);
|
||||
this.pos = this.pos.withY(posY);
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import org.atriasoft.etk.math.Vector2f;
|
||||
|
||||
public class ElementMoveTo extends Element {
|
||||
public ElementMoveTo(final boolean relative, final Vector2f pos) {
|
||||
super(PathType.moveTo, relative);
|
||||
super(PathType.MOVE_TO, relative);
|
||||
this.pos = pos;
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import org.atriasoft.etk.math.Vector2f;
|
||||
|
||||
public class ElementSmoothCurveTo extends Element {
|
||||
public ElementSmoothCurveTo(final boolean relative, final Vector2f pos2, final Vector2f pos) {
|
||||
super(PathType.moveTo, relative);
|
||||
super(PathType.MOVE_TO, relative);
|
||||
this.pos = pos;
|
||||
this.pos2 = pos2;
|
||||
|
||||
|
@ -2,7 +2,7 @@ package org.atriasoft.esvg.render;
|
||||
|
||||
public class ElementStop extends Element {
|
||||
ElementStop() {
|
||||
super(PathType.stop, false);
|
||||
super(PathType.STOP, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,7 +155,7 @@ public class PathModel {
|
||||
}
|
||||
Log.verbose(PathModel.spacingDist(level + 1) + " Draw : " + it.toString());
|
||||
switch (it.getType()) {
|
||||
case stop:
|
||||
case STOP:
|
||||
if (tmpListPoint.size() != 0) {
|
||||
if (tmpListPoint.size() == 0) {
|
||||
Log.warning(PathModel.spacingDist(level + 1) + " Request path stop of not starting path ...");
|
||||
@ -168,7 +168,7 @@ public class PathModel {
|
||||
lastAngle = Vector2f.ZERO;
|
||||
// nothing alse to do ...
|
||||
break;
|
||||
case close:
|
||||
case CLOSE:
|
||||
if (tmpListPoint.size() != 0) {
|
||||
if (tmpListPoint.size() == 0) {
|
||||
Log.warning(PathModel.spacingDist(level + 1) + " Request path close of not starting path ...");
|
||||
@ -188,7 +188,7 @@ public class PathModel {
|
||||
lastAngle = Vector2f.ZERO;
|
||||
// nothing alse to do ...
|
||||
break;
|
||||
case moveTo:
|
||||
case MOVE_TO:
|
||||
// stop last path
|
||||
if (tmpListPoint.size() != 0) {
|
||||
tmpListPoint.get(tmpListPoint.size() - 1).setEndPath();
|
||||
@ -203,7 +203,7 @@ public class PathModel {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.start));
|
||||
lastAngle = lastPosition;
|
||||
break;
|
||||
case lineTo:
|
||||
case LINE_TO:
|
||||
// If no previous point, we need to create the last point has start ...
|
||||
if (tmpListPoint.size() == 0) {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.start));
|
||||
@ -215,7 +215,7 @@ public class PathModel {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.join));
|
||||
lastAngle = lastPosition;
|
||||
break;
|
||||
case lineToH:
|
||||
case LINE_TO_H:
|
||||
// If no previous point, we need to create the last point has start ...
|
||||
if (tmpListPoint.size() == 0) {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.start));
|
||||
@ -227,7 +227,7 @@ public class PathModel {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.join));
|
||||
lastAngle = lastPosition;
|
||||
break;
|
||||
case lineToV:
|
||||
case LINE_TO_V:
|
||||
// If no previous point, we need to create the last point has start ...
|
||||
if (tmpListPoint.size() == 0) {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.start));
|
||||
@ -239,7 +239,7 @@ public class PathModel {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.join));
|
||||
lastAngle = lastPosition;
|
||||
break;
|
||||
case curveTo:
|
||||
case CURVE_TO:
|
||||
// If no previous point, we need to create the last point has start ...
|
||||
if (tmpListPoint.size() == 0) {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.join));
|
||||
@ -256,7 +256,7 @@ public class PathModel {
|
||||
lastAngle = pos2;
|
||||
}
|
||||
break;
|
||||
case smoothCurveTo:
|
||||
case SMOOTH_CURVE_TO:
|
||||
// If no previous point, we need to create the last point has start ...
|
||||
if (tmpListPoint.size() == 0) {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.join));
|
||||
@ -274,7 +274,7 @@ public class PathModel {
|
||||
lastAngle = pos2;
|
||||
}
|
||||
break;
|
||||
case bezierCurveTo:
|
||||
case BEZIER_CURVE_TO:
|
||||
// If no previous point, we need to create the last point has start ...
|
||||
if (tmpListPoint.size() == 0) {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.join));
|
||||
@ -293,7 +293,7 @@ public class PathModel {
|
||||
lastAngle = tmp1;
|
||||
}
|
||||
break;
|
||||
case bezierSmoothCurveTo:
|
||||
case BEZIER_SMOOTH_CURVE_TO:
|
||||
// If no previous point, we need to create the last point has start ...
|
||||
if (tmpListPoint.size() == 0) {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.join));
|
||||
@ -312,7 +312,7 @@ public class PathModel {
|
||||
lastAngle = tmp1;
|
||||
}
|
||||
break;
|
||||
case elliptic:
|
||||
case ELLIPTIC:
|
||||
// If no previous point, we need to create the last point has start ...
|
||||
if (tmpListPoint.size() == 0) {
|
||||
tmpListPoint.add(new Point(lastPosition, PointType.join));
|
||||
|
@ -1,5 +1,5 @@
|
||||
package org.atriasoft.esvg.render;
|
||||
|
||||
public enum PathType {
|
||||
bezierCurveTo, bezierSmoothCurveTo, close, curveTo, elliptic, lineTo, lineToH, lineToV, moveTo, smoothCurveTo, stop,
|
||||
BEZIER_CURVE_TO, BEZIER_SMOOTH_CURVE_TO, CLOSE, CURVE_TO, ELLIPTIC, LINE_TO, LINE_TO_H, LINE_TO_V, MOVE_TO, SMOOTH_CURVE_TO, STOP,
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.atriasoft.esvg.render;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.atriasoft.etk.math.Vector2f;
|
||||
@ -34,11 +35,6 @@ public class SegmentList {
|
||||
public SegmentList() {}
|
||||
|
||||
public void addSegment(final Point pos0, final Point pos1) {
|
||||
// Skip horizontal Segments
|
||||
if (pos0.pos.y() == pos1.pos.y()) {
|
||||
// remove /0 operation
|
||||
return;
|
||||
}
|
||||
this.data.add(new Segment(pos0.pos, pos1.pos));
|
||||
}
|
||||
|
||||
@ -64,6 +60,17 @@ public class SegmentList {
|
||||
}
|
||||
}
|
||||
|
||||
public void clearHorizontals() {
|
||||
// TODO Auto-generated method stub
|
||||
Iterator<Segment> itr = this.data.iterator();
|
||||
while (itr.hasNext()) {
|
||||
Segment seg = itr.next();
|
||||
if (seg.p0.y() == seg.p1.y()) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void createSegmentList(final PointList listPoint) {
|
||||
for (List<Point> it : listPoint.data) {
|
||||
// Build Segments
|
||||
|
@ -1,7 +1,7 @@
|
||||
package test.atriasoft.esvg;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import org.atriasoft.egami.ImageFloatRGBA;
|
||||
import org.atriasoft.egami.Image;
|
||||
import org.atriasoft.esvg.EsvgDocument;
|
||||
import org.atriasoft.esvg.internal.Log;
|
||||
import org.atriasoft.esvg.render.Weight;
|
||||
@ -9,52 +9,43 @@ import org.atriasoft.etk.Color;
|
||||
import org.atriasoft.etk.Uri;
|
||||
import org.atriasoft.etk.math.Vector2i;
|
||||
|
||||
import com.pngencoder.PngEncoder;
|
||||
import org.atriasoft.pngencoder.PngEncoder;
|
||||
|
||||
public class ConfigTest {
|
||||
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) {
|
||||
Color[][] data = doc.renderImageFloatRGBA(null, ConfigTest.VISUAL_DEBUG);
|
||||
if (data.length == 0) {
|
||||
Image data = doc.renderImageFloatRGBA(null, ConfigTest.VISUAL_DEBUG);
|
||||
if (data == null) {
|
||||
Log.critical("No data generated ...");
|
||||
}
|
||||
BufferedImage bufferedImage = new BufferedImage(data[0].length, data.length, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int yyy = 0; yyy < data.length; yyy++) {
|
||||
for (int xxx = 0; xxx < data[yyy].length; xxx++) {
|
||||
Color elem = data[yyy][xxx];
|
||||
int tmpColor = ((int) (elem.a() * 255.0f) << 24) + ((int) (elem.r() * 255.0f) << 16) + ((int) (elem.g() * 255.0f) << 8) + ((int) (elem.b() * 255.0f));
|
||||
bufferedImage.setRGB(xxx, yyy, tmpColor);
|
||||
}
|
||||
}
|
||||
Log.warning("Save file in " + uri.getPath());
|
||||
byte[] outElem = new PngEncoder().withBufferedImage(bufferedImage).withCompressionLevel(9).toBytes();
|
||||
byte[] outElem = new PngEncoder().withBufferedImage(data).withCompressionLevel(9).toBytes();
|
||||
Log.warning("outsize = " + outElem.length);
|
||||
new PngEncoder().withBufferedImage(bufferedImage).withCompressionLevel(9).toFile(uri.getPath());
|
||||
new PngEncoder().withBufferedImage(data).withCompressionLevel(9).toFile(uri.getPath());
|
||||
}
|
||||
|
||||
public static void generateAnImage(final Weight weight, final Uri uri) {
|
||||
BufferedImage bufferedImage = new BufferedImage(weight.getWidth() + 2, weight.getHeight() + 2, BufferedImage.TYPE_INT_ARGB);
|
||||
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));
|
||||
int tmpColor = (0xFF << 24) + ((int) (elem * 255.0f) << 16) + ((int) (elem * 255.0f) << 8) + ((int) (elem * 255.0f));
|
||||
bufferedImage.setRGB(xxx + 1, 1 + weight.getHeight() - 1 - yyy, tmpColor);
|
||||
image.setColorFloat(xxx, yyy, 1.0f, 1.0f, 1.0f, elem);
|
||||
}
|
||||
}
|
||||
for (int yyy = 0; yyy < weight.getHeight() + 2; yyy++) {
|
||||
bufferedImage.setRGB(0, yyy, 0xFFFF0000);
|
||||
bufferedImage.setRGB(weight.getWidth() + 1, yyy, 0xFFFF0000);
|
||||
image.setColor(0, yyy, Color.ORANGE);
|
||||
image.setColor(weight.getWidth() + 1, yyy, Color.ORANGE);
|
||||
}
|
||||
for (int xxx = 0; xxx < weight.getWidth() + 2; xxx++) {
|
||||
bufferedImage.setRGB(xxx, 0, 0xFFFF0000);
|
||||
bufferedImage.setRGB(xxx, weight.getHeight() + 1, 0xFFFF0000);
|
||||
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(bufferedImage).withCompressionLevel(9).toBytes();
|
||||
byte[] outElem = new PngEncoder().withBufferedImage(image).withCompressionLevel(9).toBytes();
|
||||
Log.warning("outsize = " + outElem.length);
|
||||
new PngEncoder().withBufferedImage(bufferedImage).withCompressionLevel(9).toFile(uri.getPath());
|
||||
new PngEncoder().withBufferedImage(image).withCompressionLevel(9).toFile(uri.getPath());
|
||||
}
|
||||
|
||||
private ConfigTest() {}
|
||||
|
117
test/src/test/atriasoft/esvg/TestText.java
Normal file
117
test/src/test/atriasoft/esvg/TestText.java
Normal file
@ -0,0 +1,117 @@
|
||||
package test.atriasoft.esvg;
|
||||
|
||||
import org.atriasoft.esvg.Esvg;
|
||||
import org.atriasoft.esvg.EsvgDocument;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.atriasoft.etk.Uri;
|
||||
|
||||
class TestText {
|
||||
@Test
|
||||
public void testTextBase() {
|
||||
Esvg.init();
|
||||
//@formatter:off
|
||||
String data = "\n"
|
||||
+ "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
|
||||
+ "<svg\n"
|
||||
+ " width='300'\n"
|
||||
+ " height='300'\n"
|
||||
+ " version='1.1'>\n"
|
||||
+ " <g>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:normal;font-weight:normal;font-size:15.5px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='10'\n"
|
||||
+ " y='50'>Hello</text>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:normal;font-weight:bold;font-size:15.5px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='30'\n"
|
||||
+ " y='100'>Hello Bold</text>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:italic;font-weight:normal;font-size:15.5px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='50'\n"
|
||||
+ " y='150'>Hello Italic</text>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:italic;font-weight:bold;font-size:15.5px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='70'\n"
|
||||
+ " y='200'>Hello Bold Italic</text>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:italic;font-weight:bold;font-size:15.5px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='-274.79779'\n"
|
||||
+ " y='72.401932'\n"
|
||||
+ " transform='rotate(-137.0948)'>Hello Rotated</text>\n"
|
||||
+ " </g>\n"
|
||||
+ "</svg>\n";
|
||||
//@formatter:on
|
||||
EsvgDocument doc = new EsvgDocument();
|
||||
doc.parse(data);
|
||||
Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestTextFull.svg"), data.replace("'", "\""));
|
||||
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestTextFull.png"));
|
||||
}
|
||||
|
||||
public void testTextFull() {
|
||||
//@formatter:off
|
||||
String data = "\n"
|
||||
+ "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
|
||||
+ "<svg\n"
|
||||
+ " width='300'\n"
|
||||
+ " height='300'\n"
|
||||
+ " version='1.1'>\n"
|
||||
+ " <g>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;line-height:1.25;font-family:sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583'\n"
|
||||
+ " x='52.662182'\n"
|
||||
+ " y='11.725324'><tspan\n"
|
||||
+ " sodipodi:role='line'\n"
|
||||
+ " x='52.662182'\n"
|
||||
+ " y='11.725324'\n"
|
||||
+ " style='font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;font-family:sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583'>H<tspan\n"
|
||||
+ " style='fill:#d38b00;fill-opacity:1'\n"
|
||||
+ " >e</tspan>llo</tspan><tspan\n"
|
||||
+ " sodipodi:role='line'\n"
|
||||
+ " x='52.662182'\n"
|
||||
+ " y='19.662823'\n"
|
||||
+ " style='font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:6.35px;font-family:sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583'\n"
|
||||
+ " >sq<tspan\n"
|
||||
+ " style='fill:#8c0000;fill-opacity:1'>m</tspan>l</tspan><tspan\n"
|
||||
+ " x='52.662182'\n"
|
||||
+ " y='27.600323'\n"
|
||||
+ " style='font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;font-family:sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583'\n"
|
||||
+ " >k<tspan\n"
|
||||
+ " style='fill:#000063;fill-opacity:1'\n"
|
||||
+ " >s</tspan>d</tspan></text>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:normal;font-weight:normal;font-size:5.64444444px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='1.3595439'\n"
|
||||
+ " y='44.163139'><tspan\n"
|
||||
+ " x='1.3595439'\n"
|
||||
+ " y='44.163139'\n"
|
||||
+ " style='stroke-width:0.264583;font-family:sans-serif;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:5.64444444px;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'>Hello</tspan></text>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:normal;font-weight:normal;font-size:5.64444444px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='1.1807559'\n"
|
||||
+ " y='55.513321'><tspan\n"
|
||||
+ " x='1.1807559'\n"
|
||||
+ " y='55.513321'\n"
|
||||
+ " style='stroke-width:0.264583;font-weight:bold;font-family:sans-serif;font-style:normal;font-stretch:normal;font-variant:normal;font-size:5.64444444px;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal'>Hello Bold</tspan></text>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:normal;font-weight:normal;font-size:5.64444444px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='1.196579'\n"
|
||||
+ " y='65.77121'><tspan\n"
|
||||
+ " x='1.196579'\n"
|
||||
+ " y='65.77121'\n"
|
||||
+ " style='stroke-width:0.264583;font-style:italic;font-family:sans-serif;font-weight:normal;font-stretch:normal;font-variant:normal;font-size:5.64444444px;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal'>Hello Italic</tspan></text>\n"
|
||||
+ " <text\n"
|
||||
+ " style='font-style:normal;font-weight:normal;font-size:5.64444444px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583;font-stretch:normal;font-variant:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;'\n"
|
||||
+ " x='1.9766912'\n"
|
||||
+ " y='76.594482'><tspan\n"
|
||||
+ " x='1.9766912'\n"
|
||||
+ " y='76.594482'\n"
|
||||
+ " style='stroke-width:0.264583;font-weight:bold;font-style:italic;font-family:sans-serif;font-stretch:normal;font-variant:normal;font-size:5.64444444px;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal'>Hello Bold Italic</tspan></text>\n"
|
||||
+ " </g>\n"
|
||||
+ "</svg>\n";
|
||||
//@formatter:on
|
||||
EsvgDocument doc = new EsvgDocument();
|
||||
doc.parse(data);
|
||||
Uri.writeAll(new Uri(ConfigTest.BASE_PATH + "TestTextFull.svg"), data.replace("'", "\""));
|
||||
ConfigTest.generateAnImage(doc, new Uri(ConfigTest.BASE_PATH + "TestTextFull.png"));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user