Fixed: #2464 SVG export - minimum stroke width of 1 px

This commit is contained in:
Jindra Petřík
2025-06-01 23:41:14 +02:00
parent cc61cb2e99
commit 821fa68a6b
20 changed files with 172 additions and 84 deletions
@@ -312,7 +312,7 @@ public class FrameExporter {
rect.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect, settings.zoom, "frame", fbackgroundColor);
tim.toSVG(frame, 0, null, 0, exporter, null, 0);
tim.toSVG(frame, 0, null, 0, exporter, null, 0, new Matrix(), new Matrix());
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
}
ret.add(f);
@@ -113,46 +113,52 @@ public class MorphShapeExporter {
MorphShapeTag mst = (MorphShapeTag) t;
new RetryTask(() -> {
ShapeTag st = mst.getStartShapeTag();
Matrix m;
RECT rect = st.getRect();
m = Matrix.getScaleInstance(settings.zoom);
m.translate(-rect.Xmin, -rect.Ymin);
switch (settings.mode) {
case SVG_START_END:
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fileStart))) {
ExportRectangle rect = new ExportRectangle(mst.getStartBounds());
rect.xMax *= settings.zoom;
rect.yMax *= settings.zoom;
rect.xMin *= settings.zoom;
rect.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect, settings.zoom, "shape");
mst.getStartShapeTag().toSVG(exporter, -2, new CXFORMWITHALPHA(), 0);
ExportRectangle rect2 = new ExportRectangle(mst.getStartBounds());
rect2.xMax *= settings.zoom;
rect2.yMax *= settings.zoom;
rect2.xMin *= settings.zoom;
rect2.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect2, settings.zoom, "shape");
mst.getStartShapeTag().toSVG(exporter, -2, new CXFORMWITHALPHA(), 0, m, m);
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
}
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fileEnd))) {
ExportRectangle rect = new ExportRectangle(mst.getStartBounds());
rect.xMax *= settings.zoom;
rect.yMax *= settings.zoom;
rect.xMin *= settings.zoom;
rect.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect, settings.zoom, "shape");
mst.getEndShapeTag().toSVG(exporter, -2, new CXFORMWITHALPHA(), 0);
ExportRectangle rect2 = new ExportRectangle(mst.getStartBounds());
rect2.xMax *= settings.zoom;
rect2.yMax *= settings.zoom;
rect2.xMin *= settings.zoom;
rect2.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect2, settings.zoom, "shape");
mst.getEndShapeTag().toSVG(exporter, -2, new CXFORMWITHALPHA(), 0, m, m);
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
}
break;
case SVG:
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) {
ExportRectangle rect = new ExportRectangle(mst.getRect());
rect.xMax *= settings.zoom;
rect.yMax *= settings.zoom;
rect.xMin *= settings.zoom;
rect.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect, settings.zoom, "morphshape");
mst.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0);
ExportRectangle rect2 = new ExportRectangle(mst.getRect());
rect2.xMax *= settings.zoom;
rect2.yMax *= settings.zoom;
rect2.xMin *= settings.zoom;
rect2.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect2, settings.zoom, "morphshape");
mst.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0, m, m);
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
}
break;
case PNG_START_END:
case BMP_START_END:
double unzoom = settings.zoom;
ShapeTag st = mst.getStartShapeTag();
RECT rect = st.getRect();
st = mst.getStartShapeTag();
rect = st.getRect();
int newWidth = (int) (rect.getWidth() * settings.zoom / SWF.unitDivisor) + 1;
int newHeight = (int) (rect.getHeight() * settings.zoom / SWF.unitDivisor) + 1;
SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE);
@@ -165,7 +171,7 @@ public class MorphShapeExporter {
g.fillRect(0, 0, img.getWidth(), img.getHeight());
}
}
Matrix m = Matrix.getScaleInstance(settings.zoom);
m = Matrix.getScaleInstance(settings.zoom);
m.translate(-rect.Xmin, -rect.Ymin);
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true);
if (settings.mode == MorphShapeExportMode.PNG_START_END) {
@@ -102,22 +102,24 @@ public class ShapeExporter {
final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName() + settings.getFileExtension()));
new RetryTask(() -> {
Matrix m = Matrix.getScaleInstance(settings.zoom);
RECT rect = st.getRect();
m.translate(-rect.Xmin, -rect.Ymin);
switch (settings.mode) {
case SVG:
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) {
ExportRectangle rect = new ExportRectangle(st.getRect());
rect.xMax *= settings.zoom;
rect.yMax *= settings.zoom;
rect.xMin *= settings.zoom;
rect.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect, settings.zoom, "shape");
st.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0);
ExportRectangle rect2 = new ExportRectangle(st.getRect());
rect2.xMax *= settings.zoom;
rect2.yMax *= settings.zoom;
rect2.xMin *= settings.zoom;
rect2.yMin *= settings.zoom;
SVGExporter exporter = new SVGExporter(rect2, settings.zoom, "shape");
st.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0, m, m);
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
}
break;
case PNG:
case BMP:
RECT rect = st.getRect();
int newWidth = (int) (rect.getWidth() * settings.zoom / SWF.unitDivisor) + 1;
int newHeight = (int) (rect.getHeight() * settings.zoom / SWF.unitDivisor) + 1;
SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB_PRE);
@@ -129,9 +131,7 @@ public class ShapeExporter {
g.setColor(backColor.toColor());
g.fillRect(0, 0, img.getWidth(), img.getHeight());
}
}
Matrix m = Matrix.getScaleInstance(settings.zoom);
m.translate(-rect.Xmin, -rect.Ymin);
}
st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true);
if (settings.mode == ShapeExportMode.PNG) {
ImageHelper.write(img.getBufferedImage(), ImageFormat.PNG, file);
@@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.modes.TextExportMode;
import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings;
@@ -86,10 +87,11 @@ public class TextExporter {
final TextTag textTag = (TextTag) t;
final File file = new File(outdir + File.separator + Helper.makeFileName(textTag.getCharacterExportFileName() + ".svg"));
new RetryTask(() -> {
Matrix m = Matrix.getScaleInstance(settings.zoom);
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) {
ExportRectangle rect = new ExportRectangle(textTag.getRect());
SVGExporter exporter = new SVGExporter(rect, settings.zoom, "text");
textTag.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0);
textTag.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0, m, m);
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
}
}, handler).run();
@@ -34,6 +34,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -52,6 +53,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* SVG exporter.
@@ -82,6 +84,8 @@ public class SVGExporter {
public Map<ExportKey, String> exportedTags = new HashMap<>();
public Set<ExportKey> exportedSmallStrokesTags = new HashSet<>();
public Map<Tag, Map<Integer, String>> exportedChars = new HashMap<>();
private final Map<String, Integer> lastIds = new HashMap<>();
@@ -90,6 +94,8 @@ public class SVGExporter {
public boolean useTextTag = Configuration.textExportExportFontFace.get();
private double zoom;
public static class ExportKey {
private final Tag tag;
@@ -136,7 +142,7 @@ public class SVGExporter {
return false;
}
return Objects.equals(this.colorTransform, other.colorTransform);
}
}
}
public SVGExporter(ExportRectangle bounds, double zoom, String objectType) {
@@ -144,6 +150,7 @@ public class SVGExporter {
}
public SVGExporter(ExportRectangle bounds, double zoom, String objectType, Color backgroundColor) {
this.zoom = zoom;
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
@@ -185,6 +192,10 @@ public class SVGExporter {
gradients = new ArrayList<>();
}
public double getZoom() {
return zoom;
}
private Element getDefs() {
if (_svgDefs == null) {
_svgDefs = _svg.createElement("defs");
@@ -202,10 +213,28 @@ public class SVGExporter {
}
return _svgStyle;
}
public final boolean hasSmallStrokes() {
NodeList nodes = _svgGs.peek().getElementsByTagName("path");
for (int i = 0; i < nodes.getLength(); i++) {
Element e = (Element) nodes.item(i);
if ("true".equals(e.getAttribute("ffdec:has-small-stroke"))) {
return true;
}
}
nodes = _svgGs.peek().getElementsByTagName("use");
for (int i = 0; i < nodes.getLength(); i++) {
Element e = (Element) nodes.item(i);
if ("true".equals(e.getAttribute("ffdec:has-small-stroke"))) {
return true;
}
}
return false;
}
public final void createDefGroup(ExportRectangle bounds, String id) {
createDefGroup(bounds, id, 1);
}
}
public final void createDefGroup(ExportRectangle bounds, String id, double zoom) {
Element g = _svg.createElement("g");
@@ -521,11 +550,11 @@ public class SVGExporter {
_svgGs.peek().appendChild(filtersElement);
}
public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect) {
return addUse(transform, boundRect, href, instanceName, scalingRect, null, null, BlendMode.NORMAL, new ArrayList<>());
public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect, boolean hasSmallStrokes) {
return addUse(transform, boundRect, href, instanceName, scalingRect, null, null, BlendMode.NORMAL, new ArrayList<>(), hasSmallStrokes);
}
public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect, String characterId, String characterName, int blendMode, List<FILTER> filters) {
public Element addUse(Matrix transform, RECT boundRect, String href, String instanceName, RECT scalingRect, String characterId, String characterName, int blendMode, List<FILTER> filters, boolean hasSmallStrokes) {
if (scalingRect != null && (transform == null || (Double.compare(transform.rotateSkew0, 0.0) == 0 && Double.compare(transform.rotateSkew1, 0.0) == 0))) {
addScalingGridUse(transform, boundRect, href, instanceName, scalingRect, characterId, characterName, blendMode, filters);
return null; //??
@@ -545,6 +574,9 @@ public class SVGExporter {
if (characterName != null && !characterName.isEmpty()) {
image.setAttribute("ffdec:characterName", characterName);
}
if (hasSmallStrokes) {
image.setAttribute("ffdec:has-small-stroke", "true");
}
setBlendMode(image, blendMode);
@@ -207,13 +207,7 @@ public class BitmapExporter extends ShapeExporterBase {
com.jpexs.decompiler.flash.exporters.commonshape.Point p11 = strokeTransformation.transform(1, 1);
thicknessScale = p00.distanceTo(p11) / Math.sqrt(2);
thicknessScaleX = Math.abs(p11.x - p00.x);
thicknessScaleY = Math.abs(p11.y - p00.y);
/*Matrix transPre = transformation.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor));
p00 = transPre.transform(0, 0);
p11 = transPre.transform(1, 1);
realZoom = p00.distanceTo(p11) / Math.sqrt(2);
System.out.println("realZoom=" + realZoom);*/
thicknessScaleY = Math.abs(p11.y - p00.y);
}
/**
@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.exporters.shape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
@@ -74,6 +75,16 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter {
* Exporter
*/
private final SVGExporter exporter;
/**
* Display zoom
*/
private final double displayZoom;
/**
* Thickness scale
*/
private final double thicknessScale;
/**
* Constructor.
@@ -85,14 +96,21 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter {
* @param exporter Exporter
* @param defaultColor Default color
* @param colorTransform Color transform
* @param zoom Zoom
* @param zoom Zoom - shape zoom
* @param displayZoom Display zoom - overal SVG zoom
* @param strokeTransformation Stroke transformation
*/
public SVGShapeExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, int id, SVGExporter exporter, Color defaultColor, ColorTransform colorTransform, double zoom) {
public SVGShapeExporter(int windingRule, int shapeNum, SWF swf, SHAPE shape, int id, SVGExporter exporter, Color defaultColor, ColorTransform colorTransform, double zoom, double displayZoom, Matrix strokeTransformation) {
super(windingRule, shapeNum, swf, shape, colorTransform, zoom);
this.swf = swf;
this.id = id;
this.defaultColor = defaultColor;
this.exporter = exporter;
this.displayZoom = displayZoom;
com.jpexs.decompiler.flash.exporters.commonshape.Point p00 = strokeTransformation.transform(0, 0);
com.jpexs.decompiler.flash.exporters.commonshape.Point p11 = strokeTransformation.transform(1, 1);
thicknessScale = p00.distanceTo(p11) / Math.sqrt(2);
}
@Override
@@ -217,11 +235,19 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter {
}
path.setAttribute("fill", "#ff0000");
}
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit, boolean noClose) {
finalizePath();
thickness *= zoom / SWF.unitDivisor;
//always display minimum stroke of 1 pixel, no matter how zoomed it is
if (thickness * displayZoom * thicknessScale < 1 * SWF.unitDivisor) {
path.setAttribute("ffdec:has-small-stroke", "true");
path.setAttribute("ffdec:original-stroke-width", Double.toString(thickness * displayZoom / SWF.unitDivisor));
thickness = 1 * SWF.unitDivisor / displayZoom / thicknessScale;
}
thickness *= zoom / SWF.unitDivisor;
path.setAttribute("fill", "none");
if (color != null) {
path.setAttribute("stroke", color.toHexRGB());
@@ -1040,7 +1040,7 @@ public class DefineEditTextTag extends TextTag {
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) {
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) {
render(TextRenderMode.SVG, null, exporter, null, new Matrix(), colorTransform, 1);
}
@@ -1062,7 +1062,7 @@ public class DefineEditTextTag extends TextTag {
drawBorderHtmlCanvas(swf, htmlCanvasBuilder, borderColor, fillColor, getRect(), getTextMatrix(), colorTransform, zoom);
break;
case SVG:
drawBorderSVG(swf, svgExporter, borderColor, fillColor, getRect(), getTextMatrix(), colorTransform, zoom);
drawBorderSVG(swf, svgExporter, borderColor, fillColor, getRect(), getTextMatrix(), transformation, colorTransform, zoom);
break;
}
}
@@ -1076,7 +1076,7 @@ public class DefineEditTextTag extends TextTag {
staticTextToHtmlCanvas(zoom, swf, allTextRecords, 2, htmlCanvasBuilder, getBounds(), getTextMatrix(), colorTransform);
break;
case SVG:
staticTextToSVG(swf, allTextRecords, 2, svgExporter, getBounds(), getTextMatrix(), colorTransform, zoom);
staticTextToSVG(swf, allTextRecords, 2, svgExporter, getBounds(), getTextMatrix(), colorTransform, zoom, transformation);
break;
}
}
@@ -419,8 +419,8 @@ public class DefineSpriteTag extends DrawableTag implements Timelined {
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1);
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException {
getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation);
}
@Override
@@ -420,7 +420,7 @@ public class DefineVideoStreamTag extends DrawableTag implements BoundedTag, Tim
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException {
}
@Override
@@ -131,8 +131,8 @@ public abstract class ButtonTag extends DrawableTag implements Timelined {
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1);
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException {
getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, transformation, strokeTransformation);
}
/**
@@ -113,9 +113,11 @@ public abstract class DrawableTag extends CharacterTag implements BoundedTag {
* @param ratio Ratio
* @param colorTransform Color transform
* @param level Level
* @param transformation Transformation
* @param strokeTransformation Stroke transformation
* @throws IOException On I/O error
*/
public abstract void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException;
public abstract void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException;
/**
* Converts the drawable to HTML canvas.
@@ -686,7 +686,7 @@ public abstract class FontTag extends DrawableTag implements AloneTag {
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) {
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) {
}
@@ -346,8 +346,8 @@ public abstract class ImageTag extends DrawableTag {
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, getShape(1), getCharacterId(), exporter, null, colorTransform, 1);
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException {
SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, getShape(1), getCharacterId(), exporter, null, colorTransform, 1, exporter.getZoom(), strokeTransformation);
shapeExporter.export();
}
@@ -420,16 +420,16 @@ public abstract class MorphShapeTag extends DrawableTag {
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) {
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) {
if (ratio == -2) {
SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0);
SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535);
SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(getShapeNum(), swf, beginShapes, endShapes, getCharacterId(), exporter, null, colorTransform, 1);
SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(getShapeNum(), swf, beginShapes, endShapes, getCharacterId(), exporter, null, colorTransform, exporter.getZoom());
shapeExporter.export();
} else {
SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio);
SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD /*??? FIXME*/, getShapeNum() == 2 ? 4 : 1, swf, shapes, getCharacterId(), exporter, null, colorTransform, 1);
SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD /*??? FIXME*/, getShapeNum() == 2 ? 4 : 1, swf, shapes, getCharacterId(), exporter, null, colorTransform, 1, exporter.getZoom(), strokeTransformation);
shapeExporter.export();
}
}
@@ -277,8 +277,8 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject {
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException {
SVGShapeExporter shapeExporter = new SVGShapeExporter(getWindingRule(), getShapeNum(), swf, getShapes(), getCharacterId(), exporter, null, colorTransform, 1);
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException {
SVGShapeExporter shapeExporter = new SVGShapeExporter(getWindingRule(), getShapeNum(), swf, getShapes(), getCharacterId(), exporter, null, colorTransform, 1, exporter.getZoom(), strokeTransformation);
shapeExporter.export();
}
@@ -1032,9 +1032,8 @@ public abstract class StaticTextTag extends TextTag {
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) {
staticTextToSVG(swf, textRecords, getTextNum(), exporter, getRect(), textMatrix, colorTransform, 1);
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) {
staticTextToSVG(swf, textRecords, getTextNum(), exporter, getRect(), textMatrix, colorTransform, exporter.getZoom(), transformation);
}
@Override
@@ -558,10 +558,14 @@ public abstract class TextTag extends DrawableTag {
* @param colorTransform Color transform
* @param zoom Zoom
*/
public static void drawBorderSVG(SWF swf, SVGExporter exporter, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, ColorTransform colorTransform, double zoom) {
public static void drawBorderSVG(SWF swf, SVGExporter exporter, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform, double zoom) {
exporter.createSubGroup(new Matrix(textMatrix), null);
SHAPE shape = getBorderShape(borderColor, fillColor, rect);
SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, shape, 0, exporter, null, colorTransform, zoom);
Matrix mat = transformation.clone();
mat = mat.concatenate(new Matrix(textMatrix));
//??FIXME
SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, shape, 0, exporter, null, colorTransform, 1, zoom, mat);
shapeExporter.export();
exporter.endGroup();
}
@@ -838,8 +842,9 @@ public abstract class TextTag extends DrawableTag {
* @param textMatrix Text matrix
* @param colorTransform Color transform
* @param zoom Zoom
* @param transformation Transformation
*/
public static void staticTextToSVG(SWF swf, List<TEXTRECORD> textRecords, int numText, SVGExporter exporter, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform, double zoom) {
public static void staticTextToSVG(SWF swf, List<TEXTRECORD> textRecords, int numText, SVGExporter exporter, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform, double zoom, Matrix transformation) {
int textColor = 0;
FontTag font = null;
int textHeight = 12;
@@ -914,8 +919,19 @@ public abstract class TextTag extends DrawableTag {
exporter.endGroup();
}
} else {
Matrix mat0 = transformation.clone();
mat0 = mat0.concatenate(new Matrix(textMatrix));
Matrix matScale = Matrix.getScaleInstance(rat);
for (GLYPHENTRY entry : rec.glyphEntries) {
Matrix mat = Matrix.getTranslateInstance(x, y).concatenate(Matrix.getScaleInstance(rat));
matScale.translateX = x;
matScale.translateY = y;
Matrix matX = mat0.concatenate(matScale);
if (entry.glyphIndex != -1) {
// shapeNum: 1
SHAPE shape = glyphs.get(entry.glyphIndex);
@@ -936,7 +952,7 @@ public abstract class TextTag extends DrawableTag {
if (charId == null) {
charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontNameIntag() + "_" + ch));
exporter.createDefGroup(null, charId);
SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, shape, 0, exporter, null, colorTransform, zoom);
SVGShapeExporter shapeExporter = new SVGShapeExporter(ShapeTag.WIND_EVEN_ODD, 1, swf, shape, 0, exporter, null, colorTransform, zoom, zoom, matX);
shapeExporter.export();
if (!exporter.endGroup()) {
charId = "";
@@ -946,7 +962,7 @@ public abstract class TextTag extends DrawableTag {
}
if (!"".equals(charId)) {
Element charImage = exporter.addUse(mat, bounds, charId, null, null);
Element charImage = exporter.addUse(mat, bounds, charId, null, null, false);
RGBA colorA = new RGBA(textColor);
charImage.setAttribute("fill", colorA.toHexRGB());
if (colorA.alpha != 255) {
@@ -1597,7 +1597,7 @@ public class Timeline {
* @param level Level
* @throws IOException On I/O error
*/
public void toSVG(int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level) throws IOException {
public void toSVG(int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) throws IOException {
if (getFrameCount() <= frame) {
return;
}
@@ -1650,6 +1650,9 @@ public class Timeline {
clrTrans = clrTrans == null ? layer.colorTransForm : colorTransform.merge(layer.colorTransForm);
}
Matrix layerMatrix = new Matrix(layer.getDrawingMatrix());
Matrix absMat = strokeTransformation.concatenate(layerMatrix);
if (character instanceof DrawableTag) {
DrawableTag drawable = (DrawableTag) character;
@@ -1669,26 +1672,32 @@ public class Timeline {
exporter.createClipPath(mat, clipName);
SvgClip clip = new SvgClip(clipName, layer.clipDepth);
clips.add(clip);
drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1);
drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1, transformation, strokeTransformation);
exporter.endGroup();
} else {
boolean createNew = false;
SVGExporter.ExportKey exportKey = new SVGExporter.ExportKey(drawableTag, clrTrans, layer.ratio, layer.clipDepth > -1);
if (exporter.exportedTags.containsKey(exportKey)) {
if (exporter.exportedTags.containsKey(exportKey)
&& !exporter.exportedSmallStrokesTags.contains(exportKey)) {
assetName = exporter.exportedTags.get(exportKey);
} else {
assetName = getTagIdPrefix(drawableTag, exporter);
exporter.exportedTags.put(exportKey, assetName);
createNew = true;
}
boolean hasSmallStrokes = false;
if (createNew) {
exporter.createDefGroup(new ExportRectangle(boundRect), assetName);
drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1);
exporter.endGroup();
drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1, transformation, absMat);
hasSmallStrokes = exporter.hasSmallStrokes();
exporter.endGroup();
}
Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix));
exporter.addUse(mat, boundRect, assetName, layer.instanceName, scalingGrid == null ? null : scalingGrid.splitter, String.valueOf(drawable.getCharacterId()), String.join("___", drawable.getClassNames()), layer.blendMode, layer.filters);
exporter.addUse(mat, boundRect, assetName, layer.instanceName, scalingGrid == null ? null : scalingGrid.splitter, String.valueOf(drawable.getCharacterId()), String.join("___", drawable.getClassNames()), layer.blendMode, layer.filters, hasSmallStrokes);
if (hasSmallStrokes) {
exporter.exportedSmallStrokesTags.add(exportKey);
}
}
}
}