svg export: text export as real text

This commit is contained in:
Honfika
2014-04-26 15:03:10 +02:00
parent 71a108cf61
commit 7e3a38bda4
5 changed files with 131 additions and 51 deletions

View File

@@ -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)...
}

View File

@@ -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

View File

@@ -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))

View File

@@ -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) {

View File

@@ -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();
}
}