mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-12 15:02:21 +00:00
svg export: text export as real text
This commit is contained in:
@@ -2291,7 +2291,7 @@ public final class SWF implements TreeItem, Timelined {
|
||||
// TODO: if (layer.filters != null)
|
||||
// TODO: if (layer.blendMode > 1)
|
||||
Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix));
|
||||
exporter.addImage(mat, boundRect, assetName);
|
||||
exporter.addUse(mat, boundRect, assetName);
|
||||
|
||||
// TODO: if (layer.clipDepth > -1)...
|
||||
}
|
||||
|
||||
@@ -274,6 +274,9 @@ public class Configuration {
|
||||
@ConfigurationDefaultBoolean(true)
|
||||
public static final ConfigurationItem<Boolean> packJavaScripts = null;
|
||||
|
||||
@ConfigurationDefaultBoolean(false)
|
||||
public static final ConfigurationItem<Boolean> textExportExportFontFace = null;
|
||||
|
||||
private enum OSId {
|
||||
|
||||
WINDOWS, OSX, UNIX
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.base.FontTag;
|
||||
import com.jpexs.decompiler.flash.types.ColorTransform;
|
||||
import com.jpexs.decompiler.flash.types.SHAPE;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import fontastic.FGlyph;
|
||||
import fontastic.FPoint;
|
||||
import fontastic.Fontastic;
|
||||
@@ -35,6 +36,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -76,6 +79,17 @@ public class FontExporter {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public byte[] exportFont(final FontTag t, FontExportMode mode) {
|
||||
try {
|
||||
File f = File.createTempFile("temp", ".ttf");
|
||||
exportFont(t, mode, f);
|
||||
return Helper.readFile(f.getPath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(FontExporter.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public void exportFont(final FontTag t, FontExportMode mode, File file) throws IOException {
|
||||
List<SHAPE> shapes = t.getGlyphShapeTable();
|
||||
Fontastic f = new Fontastic(t.getFontName(), file);
|
||||
@@ -86,7 +100,6 @@ public class FontExporter {
|
||||
f.setVersion("1.0");
|
||||
f.setAscender(t.getAscent() / t.getDivider());
|
||||
f.setDescender(t.getDescent() / t.getDivider());
|
||||
//f.set
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
SHAPE s = shapes.get(i);
|
||||
@@ -102,8 +115,6 @@ public class FontExporter {
|
||||
}
|
||||
|
||||
List<FPoint> path = new ArrayList<>();
|
||||
private double lastX = 0;
|
||||
private double lastY = 0;
|
||||
|
||||
@Override
|
||||
protected void finalizePath() {
|
||||
@@ -117,22 +128,16 @@ public class FontExporter {
|
||||
@Override
|
||||
public void moveTo(double x, double y) {
|
||||
finalizePath();
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
path.add(new FPoint(new PVector(transformX(x), transformY(y))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lineTo(double x, double y) {
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
path.add(new FPoint(new PVector(transformX(x), transformY(y))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void curveTo(double controlX, double controlY, double anchorX, double anchorY) {
|
||||
lastX = anchorX;
|
||||
lastY = anchorY;
|
||||
path.add(new FPoint(
|
||||
new PVector(transformX(anchorX), transformY(anchorY)),
|
||||
new PVector(transformX(controlX), transformY(controlY))
|
||||
|
||||
@@ -17,18 +17,22 @@
|
||||
package com.jpexs.decompiler.flash.exporters.commonshape;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.types.RECT;
|
||||
import com.jpexs.decompiler.flash.types.RGBA;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.awt.Color;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
@@ -39,6 +43,7 @@ import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import org.w3c.dom.Attr;
|
||||
import org.w3c.dom.CDATASection;
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.DocumentType;
|
||||
@@ -56,12 +61,15 @@ public class SVGExporter {
|
||||
|
||||
protected Document _svg;
|
||||
protected Element _svgDefs;
|
||||
protected CDATASection _svgStyle;
|
||||
protected Stack<Element> _svgGs = new Stack<>();
|
||||
public List<Element> gradients;
|
||||
protected int lastPatternId;
|
||||
public Map<Tag, String> exportedTags = new HashMap<>();
|
||||
public Map<Tag, Map<Character, String>> exportedChars = new HashMap<>();
|
||||
private Map<String, Integer> lastIds = new HashMap<>();
|
||||
private final Map<String, Integer> lastIds = new HashMap<>();
|
||||
private final HashSet<String> fontFaces = new HashSet<>();
|
||||
public boolean useTextTag = Configuration.textExportExportFontFace.get();
|
||||
|
||||
public SVGExporter(ExportRectangle bounds) {
|
||||
|
||||
@@ -74,8 +82,6 @@ public class SVGExporter {
|
||||
_svg = impl.createDocument(sNamespace, "svg", svgDocType);
|
||||
Element svgRoot = _svg.getDocumentElement();
|
||||
svgRoot.setAttribute("xmlns:xlink", xlinkNamespace);
|
||||
_svgDefs = _svg.createElement("defs");
|
||||
svgRoot.appendChild(_svgDefs);
|
||||
if (bounds != null) {
|
||||
svgRoot.setAttribute("width", (bounds.getWidth() / SWF.unitDivisor) + "px");
|
||||
svgRoot.setAttribute("height", (bounds.getHeight() / SWF.unitDivisor) + "px");
|
||||
@@ -87,6 +93,24 @@ public class SVGExporter {
|
||||
gradients = new ArrayList<>();
|
||||
}
|
||||
|
||||
private Element getDefs() {
|
||||
if (_svgDefs == null) {
|
||||
_svgDefs = _svg.createElement("defs");
|
||||
_svg.getDocumentElement().appendChild(_svgDefs);
|
||||
}
|
||||
return _svgDefs;
|
||||
}
|
||||
|
||||
private CDATASection getStyle() {
|
||||
if (_svgStyle == null) {
|
||||
Element style = _svg.createElement("style");
|
||||
_svgStyle = _svg.createCDATASection("");
|
||||
style.appendChild(_svgStyle);
|
||||
_svgDefs.appendChild(style);
|
||||
}
|
||||
return _svgStyle;
|
||||
}
|
||||
|
||||
public final void createDefGroup(ExportRectangle bounds, String id) {
|
||||
Element g = _svg.createElement("g");
|
||||
if (bounds != null) {
|
||||
@@ -99,7 +123,7 @@ public class SVGExporter {
|
||||
if (_svgGs.size() == 0) {
|
||||
_svg.getDocumentElement().appendChild(g);
|
||||
} else {
|
||||
_svgDefs.appendChild(g);
|
||||
getDefs().appendChild(g);
|
||||
}
|
||||
_svgGs.add(g);
|
||||
}
|
||||
@@ -133,7 +157,7 @@ public class SVGExporter {
|
||||
}
|
||||
|
||||
public void addToDefs(Node newChild) {
|
||||
_svgDefs.appendChild(newChild);
|
||||
getDefs().appendChild(newChild);
|
||||
}
|
||||
|
||||
public Element createElement(String tagName) {
|
||||
@@ -161,7 +185,7 @@ public class SVGExporter {
|
||||
attr.setValue("background: " + new RGBA(backGroundColor).toHexARGB());
|
||||
}
|
||||
|
||||
public Element addImage(Matrix transform, RECT boundRect, String href) {
|
||||
public Element addUse(Matrix transform, RECT boundRect, String href) {
|
||||
Element image = _svg.createElement("use");
|
||||
if (transform != null) {
|
||||
double translateX = roundPixels400(transform.translateX / SWF.unitDivisor);
|
||||
@@ -180,6 +204,20 @@ public class SVGExporter {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void addStyle(String fontFace, byte[] data) {
|
||||
if (!fontFaces.contains(fontFace)){
|
||||
fontFaces.add(fontFace);
|
||||
String base64Data = DatatypeConverter.printBase64Binary(data);
|
||||
String value = getStyle().getTextContent();
|
||||
value += Helper.newLine;
|
||||
value += " @font-face {" + Helper.newLine;
|
||||
value += " font-family: \"" + fontFace + "\";" + Helper.newLine;
|
||||
value += " src: url('data:font/truetype;base64,[" + base64Data + "]') format(\"truetype\");" + Helper.newLine;
|
||||
value += " }" + Helper.newLine;
|
||||
getStyle().setTextContent(value);
|
||||
}
|
||||
}
|
||||
|
||||
public String getUniqueId(String prefix) {
|
||||
Integer lastId = lastIds.get(prefix);
|
||||
if (lastId == null) {
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package com.jpexs.decompiler.flash.tags.base;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.exporters.FontExporter;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
|
||||
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
|
||||
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
|
||||
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
|
||||
@@ -384,54 +386,86 @@ public abstract class TextTag extends CharacterTag implements BoundedTag, Drawab
|
||||
glyphs = font.getGlyphShapeTable();
|
||||
textHeight = rec.textHeight;
|
||||
}
|
||||
int offsetX = 0;
|
||||
int offsetY = 0;
|
||||
if (rec.styleFlagsHasXOffset) {
|
||||
x = rec.xOffset;
|
||||
offsetX = rec.xOffset;
|
||||
x = offsetX;
|
||||
}
|
||||
if (rec.styleFlagsHasYOffset) {
|
||||
y = rec.yOffset;
|
||||
offsetY = rec.yOffset;
|
||||
y = offsetY;
|
||||
}
|
||||
|
||||
double rat = textHeight / 1024.0 / font.getDivider();
|
||||
|
||||
for (GLYPHENTRY entry : rec.glyphEntries) {
|
||||
Matrix mat = (new Matrix(textMatrix).concatenate(Matrix.getTranslateInstance(x - bounds.Xmin, y - bounds.Ymin))).concatenate(Matrix.getScaleInstance(rat));
|
||||
if (entry.glyphIndex != -1) {
|
||||
// shapeNum: 1
|
||||
SHAPE shape = glyphs.get(entry.glyphIndex);
|
||||
char ch = font.glyphToChar(entry.glyphIndex);
|
||||
|
||||
String charId = null;
|
||||
Map<Character, String> chs;
|
||||
if (exporter.exportedChars.containsKey(font)) {
|
||||
chs = exporter.exportedChars.get(font);
|
||||
if (chs.containsKey(ch)) {
|
||||
charId = chs.get(ch);
|
||||
}
|
||||
} else {
|
||||
chs = new HashMap<>();
|
||||
exporter.exportedChars.put(font, chs);
|
||||
}
|
||||
|
||||
if (charId == null) {
|
||||
charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontName() + "_" + ch));
|
||||
exporter.createDefGroup(null, charId);
|
||||
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, null, colorTransform);
|
||||
shapeExporter.export();
|
||||
exporter.endGroup();
|
||||
chs.put(ch, charId);
|
||||
exporter.createSubGroup(new Matrix(textMatrix), null);
|
||||
if (true || exporter.useTextTag) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
for (GLYPHENTRY entry : rec.glyphEntries) {
|
||||
if (entry.glyphIndex != -1) {
|
||||
char ch = font.glyphToChar(entry.glyphIndex);
|
||||
text.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
Element charImage = exporter.addImage(mat, bounds, charId);
|
||||
if (textColor != null) {
|
||||
RGBA colorA = new RGBA(textColor);
|
||||
charImage.setAttribute("fill", colorA.toHexRGB());
|
||||
if (colorA.alpha != 255) {
|
||||
charImage.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat()));
|
||||
boolean hasOffset = offsetX != 0 || offsetY != 0;
|
||||
if (hasOffset) {
|
||||
exporter.createSubGroup(Matrix.getTranslateInstance(offsetX, offsetY), null);
|
||||
}
|
||||
|
||||
Element textElement = exporter.createElement("text");
|
||||
textElement.setAttribute("font-size", Double.toString(rat * 1024));
|
||||
textElement.setAttribute("font-family", font.getFontName());
|
||||
textElement.setTextContent(text.toString());
|
||||
exporter.addToGroup(textElement);
|
||||
exporter.addStyle(font.getFontName(), new FontExporter().exportFont(font, FontExportMode.TTF));
|
||||
|
||||
if (hasOffset) {
|
||||
exporter.endGroup();
|
||||
}
|
||||
} else {
|
||||
for (GLYPHENTRY entry : rec.glyphEntries) {
|
||||
Matrix mat = Matrix.getTranslateInstance(x, y).concatenate(Matrix.getScaleInstance(rat));
|
||||
if (entry.glyphIndex != -1) {
|
||||
// shapeNum: 1
|
||||
SHAPE shape = glyphs.get(entry.glyphIndex);
|
||||
char ch = font.glyphToChar(entry.glyphIndex);
|
||||
|
||||
String charId = null;
|
||||
Map<Character, String> chs;
|
||||
if (exporter.exportedChars.containsKey(font)) {
|
||||
chs = exporter.exportedChars.get(font);
|
||||
if (chs.containsKey(ch)) {
|
||||
charId = chs.get(ch);
|
||||
}
|
||||
} else {
|
||||
chs = new HashMap<>();
|
||||
exporter.exportedChars.put(font, chs);
|
||||
}
|
||||
|
||||
if (charId == null) {
|
||||
charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontName() + "_" + ch));
|
||||
exporter.createDefGroup(null, charId);
|
||||
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, null, colorTransform);
|
||||
shapeExporter.export();
|
||||
exporter.endGroup();
|
||||
chs.put(ch, charId);
|
||||
}
|
||||
|
||||
Element charImage = exporter.addUse(mat, bounds, charId);
|
||||
if (textColor != null) {
|
||||
RGBA colorA = new RGBA(textColor);
|
||||
charImage.setAttribute("fill", colorA.toHexRGB());
|
||||
if (colorA.alpha != 255) {
|
||||
charImage.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat()));
|
||||
}
|
||||
}
|
||||
x += entry.glyphAdvance;
|
||||
}
|
||||
x += entry.glyphAdvance;
|
||||
}
|
||||
}
|
||||
exporter.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user