Fixed: #2545 FLA export - normalize font em square

This commit is contained in:
Jindra Petřík
2025-10-27 21:50:10 +01:00
parent b66b82d10c
commit f04fcb151e
11 changed files with 645 additions and 179 deletions

View File

@@ -17,9 +17,17 @@
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.tags.DefineEditTextTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.dynamictext.CharacterWithStyle;
import com.jpexs.decompiler.flash.tags.dynamictext.TextStyle;
import com.jpexs.decompiler.flash.tags.text.xml.XmlException;
import com.jpexs.decompiler.flash.tags.text.xml.XmlLexer;
import com.jpexs.decompiler.flash.tags.text.xml.XmlParsedSymbol;
import com.jpexs.decompiler.flash.tags.text.xml.XmlSymbolType;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
@@ -28,60 +36,67 @@ import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
import java.awt.Font;
import java.awt.Point;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* Font size / orientation normalizer.
* Does following:
* - shrinks oversized fonts to 1024 em
* - fixes vertically flipped fonts / texts
* - fixes zero/1unit spaces font glyph advance
* - fixes zero last glyph advance in texts
*
* Font size / orientation normalizer. Does following: - shrinks oversized fonts
* to 1024 em - fixes vertically flipped fonts / texts - fixes zero/1unit spaces
* font glyph advance - fixes zero last glyph advance in texts
*
* @author JPEXS
*/
public class FontNormalizer {
/**
* Normalizes fonts in the SWF file in place.
*
* @param swf SWF
*/
public void normalizeFonts(SWF swf) {
normalizeFonts(swf, true, new LinkedHashMap<>(), new LinkedHashMap<>());
}
/**
* Normalizes fonts in the SWF file creating clones of font/text tags.
*
* @param swf SWF file
* @param outFonts Modified fonts (clone) - fontId to fontTag
* @param outTexts Modified texts (clone) - textId to textTag
*/
public void normalizeFonts(SWF swf, Map<Integer, FontTag> outFonts, Map<Integer, StaticTextTag> outTexts) {
public void normalizeFonts(SWF swf, Map<Integer, FontTag> outFonts, Map<Integer, TextTag> outTexts) {
normalizeFonts(swf, false, outFonts, outTexts);
}
/**
* Normalizes fonts in the SWF file.
*
* @param swf SWF file
* @param inPlace Modify tags in SWF file (true) or create clones (false)
* @param outFonts Modified fonts - fontId to fontTag
* @param outTexts Modified texts - textId to textTag
*/
public void normalizeFonts(SWF swf, boolean inPlace, Map<Integer, FontTag> outFonts, Map<Integer, StaticTextTag> outTexts) {
public void normalizeFonts(SWF swf, boolean inPlace, Map<Integer, FontTag> outFonts, Map<Integer, TextTag> outTexts) {
Map<Integer, CharacterTag> characters = swf.getCharacters(!inPlace);
Map<Integer, StaticTextTag> texts = new LinkedHashMap<>();
Map<Integer, TextTag> texts = new LinkedHashMap<>();
for (int characterId : characters.keySet()) {
CharacterTag character = characters.get(characterId);
if (character instanceof StaticTextTag) {
texts.put(characterId, (StaticTextTag) character);
if (character instanceof TextTag) {
texts.put(characterId, (TextTag) character);
}
}
@@ -89,21 +104,29 @@ public class FontNormalizer {
Set<Integer> notInvertedFontIds = new LinkedHashSet<>();
Set<Integer> fontIds = new LinkedHashSet<>();
for (StaticTextTag text : texts.values()) {
boolean inverted = false;
if (text.textMatrix != null) {
if (text.textMatrix.scaleY < 0) {
inverted = true;
}
for (TextTag text : texts.values()) {
if (text instanceof DefineEditTextTag) {
DefineEditTextTag detext = (DefineEditTextTag) text;
fontIds.addAll(getDefineEditTextFonts(detext));
}
for (TEXTRECORD rec : text.textRecords) {
if (rec.styleFlagsHasFont) {
if (inverted) {
invertedFontIds.add(rec.fontId);
} else {
notInvertedFontIds.add(rec.fontId);
if (text instanceof StaticTextTag) {
StaticTextTag stext = (StaticTextTag) text;
boolean inverted = false;
if (stext.textMatrix != null) {
if (stext.textMatrix.scaleY < 0) {
inverted = true;
}
}
for (TEXTRECORD rec : stext.textRecords) {
if (rec.styleFlagsHasFont) {
if (inverted) {
invertedFontIds.add(rec.fontId);
} else {
notInvertedFontIds.add(rec.fontId);
}
fontIds.add(rec.fontId);
}
fontIds.add(rec.fontId);
}
}
}
@@ -121,61 +144,71 @@ public class FontNormalizer {
}
FontTag font = (FontTag) fontCharacter;
int minY = Integer.MAX_VALUE;
int maxY = Integer.MIN_VALUE;
for (SHAPE shp : font.getGlyphShapeTable()) {
RECT b = shp.getBounds(1);
if (b.Ymin < minY) {
minY = b.Ymin;
}
if (b.Ymax > maxY) {
maxY = b.Ymax;
}
}
int maxH = maxY - minY;
int originalEmSize = (int) Math.round(maxH / font.getDivider());
double scale = 1.0;
boolean willModify = false;
if (originalEmSize > 1024) {
scale = 1024.0 / originalEmSize;
willModify = true;
}
if (invertedFontIds.contains(fontId)) {
willModify = true;
}
double newScale = 1;
String systemFont = font.getSystemFontName();
List<SHAPE> shapes1 = font.getGlyphShapeTable();
Double h = null;
Double systemH = null;
for (int i = 0; i < shapes1.size(); i++) {
RECT b = shapes1.get(i).getBounds(1);
h = b.getHeight() / font.getDivider();
if (h <= 0) {
continue;
}
char c = font.glyphToChar(i);
Font f = new Font(systemFont, (font.isBold() ? Font.BOLD : 0) | (font.isItalic() ? Font.ITALIC : 0), 1000);
if (!f.canDisplay(c)) {
continue;
}
FontRenderContext frc = new FontRenderContext(null, true, true);
GlyphVector gv = f.createGlyphVector(frc, new char[]{c});
systemH = gv.getGlyphOutline(0).getBounds2D().getHeight();
break;
}
if (h != null && systemH != null) {
newScale = systemH / h;
willModify = true;
}
final double scale = newScale;
int spaceGlyph = font.charToGlyph(' ');
int nonBreakingSpaceGlyph = font.charToGlyph((char) 0xA0);
if (spaceGlyph != -1 && font.getGlyphAdvance(spaceGlyph) <= 1) {
willModify = true;
}
if (nonBreakingSpaceGlyph != -1 && font.getGlyphAdvance(nonBreakingSpaceGlyph) <= 1) {
willModify = true;
}
if (!willModify) {
continue;
}
//scale = 1;
fontNewScale.put(fontId, scale);
FontTag font2;
if (inPlace) {
font2 = font;
} else {
} else {
try {
font2 = (FontTag) font.cloneTag();
} catch (InterruptedException | IOException ex) {
continue;
}
}
outFonts.put(fontId, font2);
outFonts.put(fontId, font2);
List<SHAPE> shapes = font2.getGlyphShapeTable();
Matrix matrix = new Matrix();
@@ -199,110 +232,460 @@ public class FontNormalizer {
font2.setAscent((int) Math.round(font2.getAscent() * scale));
font2.setDescent((int) Math.round(font2.getDescent() * scale));
font2.setLeading((int) Math.round(font2.getLeading() * scale));
if (invertedFontIds.contains(fontId)) {
int ascent = font2.getAscent();
int descent = font2.getDescent();
//switch ascent and descent
font2.setAscent(descent);
font2.setDescent(ascent);
//what to do with leading?
}
}
}
if (spaceGlyph != -1 && font.getGlyphAdvance(spaceGlyph) <= 1) {
font2.setGlyphAdvance(spaceGlyph, 512);
}
if (nonBreakingSpaceGlyph != -1 && font.getGlyphAdvance(nonBreakingSpaceGlyph) <= 1) {
font2.setGlyphAdvance(nonBreakingSpaceGlyph, 512);
}
font2.setModified(true);
}
for (int textId : texts.keySet()) {
int fontId = -1;
int textHeight = 12 * 20;
StaticTextTag text = texts.get(textId);
StaticTextTag text2 = null;
for (int i = 0; i < text.textRecords.size(); i++) {
TEXTRECORD rec = text.textRecords.get(i);
if (rec.styleFlagsHasFont) {
fontId = rec.fontId;
if (fontNewScale.containsKey(fontId) || invertedFontIds.contains(fontId)) {
if (text2 == null) {
if (inPlace) {
text2 = text;
} else {
try {
text2 = (StaticTextTag) text.cloneTag();
} catch (InterruptedException | IOException ex) {
break;
if (texts.get(textId) instanceof DefineEditTextTag) {
DefineEditTextTag text = (DefineEditTextTag) texts.get(textId);
scaleDefineEditTextFonts(text, fontNewScale, inPlace, outTexts);
} else if (texts.get(textId) instanceof StaticTextTag) {
StaticTextTag text = (StaticTextTag) texts.get(textId);
StaticTextTag text2 = null;
for (int i = 0; i < text.textRecords.size(); i++) {
TEXTRECORD rec = text.textRecords.get(i);
if (rec.styleFlagsHasFont) {
fontId = rec.fontId;
if (fontNewScale.containsKey(fontId) || invertedFontIds.contains(fontId)) {
if (text2 == null) {
if (inPlace) {
text2 = text;
} else {
try {
text2 = (StaticTextTag) text.cloneTag();
} catch (InterruptedException | IOException ex) {
break;
}
}
outTexts.put(textId, text2);
text2.setModified(true);
}
outTexts.put(textId, text2);
text2.setModified(true);
}
}
textHeight = text.textRecords.get(i).textHeight;
if (fontNewScale.containsKey(fontId)) {
text2.textRecords.get(i).textHeight /= fontNewScale.get(fontId);
textHeight = text2.textRecords.get(i).textHeight;
}
if (invertedFontIds.contains(fontId)) {
if (text2.textMatrix != null && text2.textMatrix.scaleY < 0) {
text2.textMatrix.scaleY *= -1;
}
}
}
if (invertedFontIds.contains(fontId)) {
if (rec.styleFlagsHasYOffset) {
text2.textRecords.get(i).yOffset = - rec.yOffset;
}
}
if (!rec.glyphEntries.isEmpty() && rec.glyphEntries.get(rec.glyphEntries.size() - 1).glyphAdvance == 0) {
FontTag font;
if (outFonts.containsKey(fontId)) {
font = outFonts.get(fontId);
} else {
font = swf.getFont(fontId);
}
if (font != null) {
if (text2 == null) {
if (inPlace) {
text2 = text;
} else {
try {
text2 = (StaticTextTag) text.cloneTag();
} catch (InterruptedException | IOException ex) {
break;
}
}
outTexts.put(textId, text2);
text2.setModified(true);
}
GLYPHENTRY lastGlyphEntry = text2.textRecords.get(i).glyphEntries.get(rec.glyphEntries.size() - 1);
lastGlyphEntry.glyphAdvance = (int) Math.round(font.getGlyphAdvance(lastGlyphEntry.glyphIndex) * textHeight / (1024.0 * font.getDivider()));
textHeight = text.textRecords.get(i).textHeight;
if (i + 1 < text.textRecords.size()) {
TEXTRECORD nextRec = text2.textRecords.get(i + 1);
if (!nextRec.styleFlagsHasXOffset && !nextRec.glyphEntries.isEmpty()) {
nextRec.glyphEntries.get(0).glyphAdvance -= lastGlyphEntry.glyphAdvance;
if (fontNewScale.containsKey(fontId)) {
text2.textRecords.get(i).textHeight = round20(text2.textRecords.get(i).textHeight / fontNewScale.get(fontId));
textHeight = text2.textRecords.get(i).textHeight;
}
if (invertedFontIds.contains(fontId)) {
if (text2.textMatrix != null && text2.textMatrix.scaleY < 0) {
text2.textMatrix.scaleY *= -1;
}
}
}
}
if (invertedFontIds.contains(fontId)) {
if (rec.styleFlagsHasYOffset) {
text2.textRecords.get(i).yOffset = -rec.yOffset;
}
}
if (!rec.glyphEntries.isEmpty() && rec.glyphEntries.get(rec.glyphEntries.size() - 1).glyphAdvance == 0) {
FontTag font;
if (outFonts.containsKey(fontId)) {
font = outFonts.get(fontId);
} else {
font = swf.getFont(fontId);
}
if (font != null) {
if (text2 == null) {
if (inPlace) {
text2 = text;
} else {
try {
text2 = (StaticTextTag) text.cloneTag();
} catch (InterruptedException | IOException ex) {
break;
}
}
outTexts.put(textId, text2);
text2.setModified(true);
}
GLYPHENTRY lastGlyphEntry = text2.textRecords.get(i).glyphEntries.get(rec.glyphEntries.size() - 1);
lastGlyphEntry.glyphAdvance = (int) Math.round(font.getGlyphAdvance(lastGlyphEntry.glyphIndex) * textHeight / (1024.0 * font.getDivider()));
if (i + 1 < text.textRecords.size()) {
TEXTRECORD nextRec = text2.textRecords.get(i + 1);
if (!nextRec.styleFlagsHasXOffset && !nextRec.glyphEntries.isEmpty()) {
nextRec.glyphEntries.get(0).glyphAdvance -= lastGlyphEntry.glyphAdvance;
}
}
}
}
}
}
}
swf.clearShapeCache();
}
private static int round20(double val) {
return (int) Math.floor(val / 20.0) * 20;
}
private Set<Integer> getDefineEditTextFonts(DefineEditTextTag text) {
Set<Integer> ret = new LinkedHashSet<>();
TextStyle style = new TextStyle();
if (text.fontClass != null) {
style.font = text.getSwf().getFontByClass(text.fontClass);
} else {
style.font = text.getSwf().getFont(text.fontId);
}
int fontId = text.getSwf().getCharacterId(style.font);
ret.add(fontId);
if (text.html) {
final Stack<TextStyle> styles = new Stack<>();
styles.add(style);
XmlLexer lexer = new XmlLexer(new StringReader(text.initialText));
try {
XmlParsedSymbol s = lexer.yylex();
boolean inOpenTag = false;
String attributeName = null;
String tagName = null;
Map<String, String> attributes = new LinkedHashMap<>();
loops:
while (s.type != XmlSymbolType.EOF) {
switch (s.type) {
case TAG_OPEN:
inOpenTag = true;
attributeName = null;
tagName = (String) s.value;
attributes.clear();
break;
case ATTRIBUTE:
attributeName = (String) s.value;
break;
case ATTRIBUTE_VALUE:
if (attributeName == null) {
//Error
break loops;
}
attributes.put(attributeName, (String) s.value);
break;
case TAG_OPEN_END:
style = styles.peek();
switch (tagName) {
case "p":
// todo: parse the following attribute:
// align
break;
case "a":
// todo: handle link - href, target attributes
break;
case "b":
style = style.clone();
style.bold = true;
styles.add(style);
break;
case "i":
style = style.clone();
style.italic = true;
styles.add(style);
break;
case "u":
style = style.clone();
style.underlined = true;
styles.add(style);
break;
case "font":
style = style.clone();
String face = attributes.get("face");
if (face != null && face.length() > 0) {
style.fontFace = face;
}
String size = attributes.get("size");
if (size != null && size.length() > 0) {
if (style.fontFace != null && text.useOutlines) {
CharacterTag ct = text.getSwf().getCharacterByExportName(style.fontFace);
if (ct != null && (ct instanceof FontTag)) {
style.font = (FontTag) ct;
} else {
style.font = text.getSwf().getFontByNameInTag(style.fontFace, style.bold, style.italic);
}
if (style.font == null) {
style.fontFace = null;
} else {
fontId = text.getSwf().getCharacterId(style.font);
ret.add(fontId);
}
}
}
styles.add(style);
break;
}
tagName = null;
break;
case TAG_CLOSE:
tagName = (String) s.value;
switch (tagName) {
case "b":
case "i":
case "u":
case "font":
styles.pop();
break;
}
tagName = null;
break;
}
s = lexer.yylex();
}
} catch (IOException ex) {
//Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex);
} catch (XmlException ex) {
//Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex);
//ex.printStackTrace();
}
}
return ret;
}
private void scaleDefineEditTextFonts(DefineEditTextTag text, Map<Integer, Double> fontNewScale, boolean inPlace, Map<Integer, TextTag> outTexts) {
String str = "";
TextStyle style = new TextStyle();
if (text.fontClass != null) {
style.font = text.getSwf().getFontByClass(text.fontClass);
} else {
style.font = text.getSwf().getFont(text.fontId);
}
int textId = text.getSwf().getCharacterId(text);
int fontId = text.getSwf().getCharacterId(style.font);
DefineEditTextTag text2 = null;
if (fontNewScale.containsKey(fontId)) {
if (inPlace) {
text2 = text;
} else {
try {
text2 = (DefineEditTextTag) text.cloneTag();
} catch (InterruptedException | IOException ex) {
return;
}
}
text2.fontHeight = round20(text2.fontHeight / fontNewScale.get(fontId));
outTexts.put(textId, text2);
text2.setModified(true);
}
style.fontHeight = text.fontHeight;
style.fontLeading = text.leading;
if (text.hasTextColor) {
style.textColor = text.textColor;
}
if (text.hasText) {
str = text.initialText;
}
style.leftMargin = text.leftMargin;
final List<CharacterWithStyle> ret = new ArrayList<>();
if (text.html) {
StringBuilder sb = new StringBuilder();
//SAXParserFactory factory = SAXParserFactory.newInstance();
//SAXParser saxParser;
final Stack<TextStyle> styles = new Stack<>();
styles.add(style);
XmlLexer lexer = new XmlLexer(new StringReader(text.initialText));
try {
XmlParsedSymbol s = lexer.yylex();
boolean inOpenTag = false;
String attributeName = null;
String tagName = null;
Map<String, String> attributes = new LinkedHashMap<>();
loops:
while (s.type != XmlSymbolType.EOF) {
switch (s.type) {
case TAG_OPEN:
inOpenTag = true;
attributeName = null;
tagName = (String) s.value;
attributes.clear();
break;
case ATTRIBUTE:
attributeName = (String) s.value;
break;
case ATTRIBUTE_VALUE:
if (attributeName == null) {
//Error
break loops;
}
attributes.put(attributeName, (String) s.value);
break;
case TAG_OPEN_END:
style = styles.peek();
switch (tagName) {
case "p":
// todo: parse the following attribute:
// align
break;
case "a":
// todo: handle link - href, target attributes
break;
case "b":
style = style.clone();
style.bold = true;
styles.add(style);
break;
case "i":
style = style.clone();
style.italic = true;
styles.add(style);
break;
case "u":
style = style.clone();
style.underlined = true;
styles.add(style);
break;
case "font":
style = style.clone();
String face = attributes.get("face");
if (face != null && face.length() > 0) {
style.fontFace = face;
}
String size = attributes.get("size");
if (size != null && size.length() > 0) {
if (style.fontFace != null && text.useOutlines) {
CharacterTag ct = text.getSwf().getCharacterByExportName(style.fontFace);
if (ct != null && (ct instanceof FontTag)) {
style.font = (FontTag) ct;
} else {
style.font = text.getSwf().getFontByNameInTag(style.fontFace, style.bold, style.italic);
}
if (style.font == null) {
style.fontFace = null;
} else {
fontId = text.getSwf().getCharacterId(style.font);
if (fontNewScale.containsKey(fontId)) {
if (text2 == null) {
if (inPlace) {
text2 = text;
} else {
try {
text2 = (DefineEditTextTag) text.cloneTag();
} catch (InterruptedException | IOException ex) {
return;
}
}
outTexts.put(textId, text2);
text2.setModified(true);
}
try {
char firstChar = size.charAt(0);
if (firstChar != '+' && firstChar != '-') {
int fontSize = Integer.parseInt(size);
//style.fontHeight = (int) Math.round(fontSize * SWF.unitDivisor);
attributes.put("size", "" + Math.round(fontSize / fontNewScale.get(fontId)));
} else {
int fontSizeDelta = (int) Math.round(Integer.parseInt(size.substring(1)) * SWF.unitDivisor);
attributes.put("size", "" + firstChar + Math.round(fontSizeDelta / fontNewScale.get(fontId)));
/*if (firstChar == '+') {
style.fontHeight = style.fontHeight + fontSizeDelta;
} else {
style.fontHeight = style.fontHeight - fontSizeDelta;
}*/
}
style.fontLeading = text.leading;
} catch (NumberFormatException nfe) {
//do not change fontHeight or leading
}
}
}
}
}
styles.add(style);
break;
}
sb.append("<").append(tagName);
for (String key : attributes.keySet()) {
sb.append(" ").append(key).append("=").append("\"").append(attributes.get(key)).append("\"");
}
sb.append(">");
tagName = null;
break;
case TAG_CLOSE:
tagName = (String) s.value;
switch (tagName) {
case "b":
case "i":
case "u":
case "font":
styles.pop();
break;
}
sb.append("</").append(tagName).append(">");
tagName = null;
break;
case ENTITY:
sb.append("&").append(s.value).append(";");
break;
case CHARACTER:
sb.append(s.value);
break;
}
s = lexer.yylex();
}
if (text2 != null) {
text2.initialText = sb.toString();
}
} catch (IOException ex) {
//Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex);
} catch (XmlException ex) {
//Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex);
//ex.printStackTrace();
}
}
}
// Percentile helper (p in <0,100>)
private static double percentile(List<Double> values, double p) {
if (values.isEmpty()) {
return 0.0;
}
Collections.sort(values);
double rank = (p / 100.0) * (values.size() - 1);
int lo = (int) Math.floor(rank);
int hi = (int) Math.ceil(rank);
if (lo == hi) {
return values.get(lo);
}
double w = rank - lo;
return values.get(lo) * (1.0 - w) + values.get(hi) * w;
}
private void transformSHAPE(Matrix matrix, SHAPE shape) {
int x = 0;
int y = 0;

View File

@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.DynamicTextGlyphEntry;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
@@ -71,7 +72,7 @@ public class DualPdfGraphics2D extends Graphics2D implements BlendModeSettable,
private final Map<Integer, Font> existingFonts;
private Map<Integer, FontTag> normalizedFonts = new LinkedHashMap<>();
private Map<Integer, StaticTextTag> normalizedTexts = new LinkedHashMap<>();
private Map<Integer, TextTag> normalizedTexts = new LinkedHashMap<>();
public DualPdfGraphics2D(Graphics2D first, PDFGraphics second, Map<Integer, Font> existingFonts) {
this.imageGraphics = first;
@@ -694,7 +695,7 @@ public class DualPdfGraphics2D extends Graphics2D implements BlendModeSettable,
}
@Override
public void setNormalizedFonts(Map<Integer, FontTag> normalizedFonts, Map<Integer, StaticTextTag> normalizedTexts) {
public void setNormalizedFonts(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts) {
this.normalizedFonts = normalizedFonts;
this.normalizedTexts = normalizedTexts;
}
@@ -705,7 +706,7 @@ public class DualPdfGraphics2D extends Graphics2D implements BlendModeSettable,
}
@Override
public Map<Integer, StaticTextTag> getNormalizedTexts() {
public Map<Integer, TextTag> getNormalizedTexts() {
return normalizedTexts;
}
}

View File

@@ -42,6 +42,7 @@ import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Frame;
@@ -324,7 +325,7 @@ public class FrameExporter {
FontNormalizer normalizer = new FontNormalizer();
Map<Integer, FontTag> normalizedFonts = new LinkedHashMap<>();
Map<Integer, StaticTextTag> normalizedTexts = new LinkedHashMap<>();
Map<Integer, TextTag> normalizedTexts = new LinkedHashMap<>();
normalizer.normalizeFonts(tim.timelined.getSwf(), normalizedFonts, normalizedTexts);
@@ -608,7 +609,7 @@ public class FrameExporter {
FontNormalizer normalizer = new FontNormalizer();
Map<Integer, FontTag> normalizedFonts = new LinkedHashMap<>();
Map<Integer, StaticTextTag> normalizedTexts = new LinkedHashMap<>();
Map<Integer, TextTag> normalizedTexts = new LinkedHashMap<>();
normalizer.normalizeFonts(tim.timelined.getSwf(), normalizedFonts, normalizedTexts);
File f = new File(foutdir + File.separator + "frames.pdf");

View File

@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import java.util.Map;
/**
@@ -26,10 +27,10 @@ import java.util.Map;
* @author JPEXS
*/
public interface RequiresNormalizedFonts {
public void setNormalizedFonts(Map<Integer, FontTag> normalizedFonts, Map<Integer, StaticTextTag> normalizedTexts);
public void setNormalizedFonts(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts);
public Map<Integer, FontTag> getNormalizedFonts();
public Map<Integer, StaticTextTag> getNormalizedTexts();
public Map<Integer, TextTag> getNormalizedTexts();
}

View File

@@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.types.BlendMode;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.RECT;
@@ -98,10 +99,10 @@ public class SVGExporter implements RequiresNormalizedFonts {
private double zoom;
private Map<Integer, FontTag> normalizedFonts = new LinkedHashMap<>();
private Map<Integer, StaticTextTag> normalizedTexts = new LinkedHashMap<>();
private Map<Integer, TextTag> normalizedTexts = new LinkedHashMap<>();
@Override
public void setNormalizedFonts(Map<Integer, FontTag> normalizedFonts, Map<Integer, StaticTextTag> normalizedTexts) {
public void setNormalizedFonts(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts) {
this.normalizedFonts = normalizedFonts;
this.normalizedTexts = normalizedTexts;
}
@@ -112,7 +113,7 @@ public class SVGExporter implements RequiresNormalizedFonts {
}
@Override
public Map<Integer, StaticTextTag> getNormalizedTexts() {
public Map<Integer, TextTag> getNormalizedTexts() {
return normalizedTexts;
}

View File

@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.RequiresNormalizedFonts;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
@@ -326,8 +327,8 @@ public class DefineEditTextTag extends TextTag {
case '\'':
str = "&apos;";
break;
*/
*/
int lastHtmlSourcePos = -1;
for (TEXTRECORD r : recs) {
if (r instanceof AdvancedTextRecord) {
@@ -616,7 +617,7 @@ public class DefineEditTextTag extends TextTag {
addCharacters(ret, txt, style);
}
};
*/
*/
XmlLexer lexer = new XmlLexer(new StringReader(str));
try {
@@ -803,7 +804,7 @@ public class DefineEditTextTag extends TextTag {
} catch (ParserConfigurationException | SAXException | IOException ex) {
Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, "Error parsing text " + getCharacterId(), ex);
}
*/
*/
} else {
addCharacters(ret, str, style, 0);
}
@@ -1283,6 +1284,12 @@ public class DefineEditTextTag extends TextTag {
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) {
int realTextId = getSwf().getCharacterId(this);
if (exporter.getNormalizedTexts().containsKey(realTextId) && exporter.getNormalizedTexts().get(realTextId) instanceof DefineEditTextTag) {
DefineEditTextTag normalizedText = (DefineEditTextTag) exporter.getNormalizedTexts().get(realTextId);
normalizedText.render(TextRenderMode.SVG, null, exporter, null, new Matrix(), colorTransform, 1, 0, 0);
return;
}
render(TextRenderMode.SVG, null, exporter, null, new Matrix(), colorTransform, 1, 0, 0);
}
@@ -1292,6 +1299,16 @@ public class DefineEditTextTag extends TextTag {
}
private void render(TextRenderMode renderMode, SerializableImage image, SVGExporter svgExporter, StringBuilder htmlCanvasBuilder, Matrix transformation, ColorTransform colorTransform, double zoom, int selectionStart, int selectionEnd) {
if (image.getGraphics() instanceof RequiresNormalizedFonts) {
RequiresNormalizedFonts g = (RequiresNormalizedFonts) image.getGraphics();
Map<Integer, TextTag> normalizedTexts = g.getNormalizedTexts();
int realTextId = getSwf().getCharacterId(this);
if (normalizedTexts.containsKey(realTextId) && normalizedTexts.get(realTextId) instanceof DefineEditTextTag && normalizedTexts.get(realTextId) != this) {
DefineEditTextTag normalizedText = (DefineEditTextTag) normalizedTexts.get(realTextId);
normalizedText.render(renderMode, image, svgExporter, htmlCanvasBuilder, transformation, colorTransform, zoom, selectionStart, selectionEnd);
return;
}
}
if (border) {
// border is always black, fill color is always white?
RGB borderColor = new RGBA(Color.black);
@@ -1606,7 +1623,7 @@ public class DefineEditTextTag extends TextTag {
txt = txt.replace("&apos;", "'");
return txt;
}
@Override
public Map<String, String> getNameProperties() {
Map<String, String> ret = super.getNameProperties();
@@ -1616,4 +1633,5 @@ public class DefineEditTextTag extends TextTag {
}
return ret;
}
}

View File

@@ -423,6 +423,18 @@ public abstract class FontTag extends DrawableTag implements AloneTag {
* @return System font name
*/
public String getSystemFontName() {
String ret = getSystemFontNameNoDefault();
if (ret == null) {
return defaultFontName;
}
return ret;
}
/**
* Gets system font name without backup to default font
* @return System font name
*/
public String getSystemFontNameNoDefault() {
FontTag.ensureLoaded();
int fontId = getCharacterId();
String selectedFont = swf.sourceFontNamesMap.get(fontId);
@@ -443,7 +455,7 @@ public abstract class FontTag extends DrawableTag implements AloneTag {
}
// findInstalledFontName always returns an available font name
return FontTag.findInstalledFontName(getFontName());
return FontTag.findInstalledFontNameNoDefault(getFontName());
}
/**
@@ -664,11 +676,11 @@ public abstract class FontTag extends DrawableTag implements AloneTag {
}
/**
* Finds installed font name.
* Finds installed font name without backup to default font name
* @param fontName Font name
* @return Installed font name
*/
public static String findInstalledFontName(String fontName) {
public static String findInstalledFontNameNoDefault(String fontName) {
ensureLoaded();
if (installedFontsByName.containsKey(fontName)) {
return fontName;
@@ -679,7 +691,20 @@ public abstract class FontTag extends DrawableTag implements AloneTag {
return beforeUnderscore;
}
}
return defaultFontName;
return null;
}
/**
* Finds installed font name.
* @param fontName Font name
* @return Installed font name
*/
public static String findInstalledFontName(String fontName) {
String ret = findInstalledFontNameNoDefault(fontName);
if (ret == null) {
return defaultFontName;
}
return ret;
}
@Override

View File

@@ -1030,10 +1030,10 @@ public abstract class StaticTextTag extends TextTag {
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, SerializableImage fullImage, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, Matrix fullTransformation, ColorTransform colorTransform, double unzoom, boolean sameImage, ExportRectangle viewRect, ExportRectangle viewRectRaw, boolean scaleStrokes, int drawMode, int blendMode, boolean canUseSmoothing) {
if (image.getGraphics() instanceof RequiresNormalizedFonts) {
RequiresNormalizedFonts g = (RequiresNormalizedFonts) image.getGraphics();
Map<Integer, StaticTextTag> normalizedTexts = g.getNormalizedTexts();
Map<Integer, TextTag> normalizedTexts = g.getNormalizedTexts();
int realTextId = getSwf().getCharacterId(this);
if (normalizedTexts.containsKey(realTextId)) {
StaticTextTag normalizedText = normalizedTexts.get(realTextId);
if (normalizedTexts.containsKey(realTextId) && normalizedTexts.get(realTextId) instanceof StaticTextTag) {
StaticTextTag normalizedText = (StaticTextTag) normalizedTexts.get(realTextId);
staticTextToImage(swf, normalizedText.textRecords, getTextNum(), image, normalizedText.textMatrix, transformation, colorTransform, renderContext.selectionText == this ? renderContext.selectionStart : 0, renderContext.selectionText == this ? renderContext.selectionEnd : 0);
return;
}
@@ -1053,8 +1053,8 @@ public abstract class StaticTextTag extends TextTag {
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, Matrix transformation, Matrix strokeTransformation) {
int realTextId = getSwf().getCharacterId(this);
if (exporter.getNormalizedTexts().containsKey(realTextId)) {
StaticTextTag normalizedText = exporter.getNormalizedTexts().get(realTextId);
if (exporter.getNormalizedTexts().containsKey(realTextId) && exporter.getNormalizedTexts().get(realTextId) instanceof StaticTextTag) {
StaticTextTag normalizedText = (StaticTextTag) exporter.getNormalizedTexts().get(realTextId);
staticTextToSVG(swf, normalizedText.textRecords, getTextNum(), exporter, getRect(), normalizedText.textMatrix, colorTransform, exporter.getZoom(), transformation);
return;
}

View File

@@ -386,9 +386,10 @@ public abstract class TextTag extends DrawableTag {
* Gets text records attributes.
* @param list Text records
* @param swf SWF
* @param normalizedFonts Normalized fonts
* @return Text records attributes
*/
public static Map<String, Object> getTextRecordsAttributes(List<TEXTRECORD> list, SWF swf) {
public static Map<String, Object> getTextRecordsAttributes(List<TEXTRECORD> list, SWF swf, Map<Integer, FontTag> normalizedFonts) {
Map<String, Object> att = new HashMap<>();
RECT textBounds = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE);
FontTag font = null;
@@ -416,8 +417,14 @@ public abstract class TextTag extends DrawableTag {
if (rec.styleFlagsHasFont) {
FontTag font2 = rec.getFont(swf);
if (font2 != null) {
int fontId = swf.getCharacterId(font2);
if (normalizedFonts.containsKey(fontId)) {
font2 = normalizedFonts.get(fontId);
}
font = font2;
}
}
textHeight = rec.textHeight;
if (font == null) {
Logger.getLogger(TextTag.class.getName()).log(Level.SEVERE, "Font with id={0} was not found.", rec.fontId);

View File

@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.xfl;
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
import com.jpexs.decompiler.flash.FlashPlayerVersion;
import com.jpexs.decompiler.flash.FontNormalizer;
import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.SWF;
@@ -1574,16 +1575,16 @@ public class XFLConverter {
return date.getTime() / 1000;
}
private void convertLibrary(Reference<Integer> lastItemIdNumber, Set<CharacterTag> charactersExportedInFirstFrame, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, Map<CharacterTag, ScriptPack> characterScriptPacks, List<CharacterTag> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Set<ShapeTag> smallShapes) throws XMLStreamException {
private void convertLibrary(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts, Reference<Integer> lastItemIdNumber, Set<CharacterTag> charactersExportedInFirstFrame, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, Map<CharacterTag, ScriptPack> characterScriptPacks, List<CharacterTag> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Set<ShapeTag> smallShapes) throws XMLStreamException {
statusStack.pushStatus("media");
convertMedia(lastItemIdNumber, charactersExportedInFirstFrame, lastImportedId, characterNameMap, characterImportLinkageURL, characters, swf, characterVariables, characterClasses, tags, files, datfiles, writer, statusStack);
statusStack.popStatus();
statusStack.pushStatus("symbols");
convertSymbols(lastItemIdNumber, charactersExportedInFirstFrame, characterImportLinkageURL, characters, lastImportedId, characterNameMap, swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, files, flaVersion, writer, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, smallShapes);
convertSymbols(normalizedFonts, normalizedTexts, lastItemIdNumber, charactersExportedInFirstFrame, characterImportLinkageURL, characters, lastImportedId, characterNameMap, swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, files, flaVersion, writer, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, smallShapes);
statusStack.popStatus();
}
private void convertSymbols(Reference<Integer> lastItemIdNumber, Set<CharacterTag> charactersExportedInFirstFrame, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, Map<CharacterTag, ScriptPack> characterScriptPacks, List<CharacterTag> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<String, byte[]> files, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Set<ShapeTag> smallShapes) throws XMLStreamException {
private void convertSymbols(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts, Reference<Integer> lastItemIdNumber, Set<CharacterTag> charactersExportedInFirstFrame, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, Map<CharacterTag, ScriptPack> characterScriptPacks, List<CharacterTag> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<String, byte[]> files, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Set<ShapeTag> smallShapes) throws XMLStreamException {
//boolean hasSymbol = false;
Reference<Integer> nextClipId = new Reference<>(-1);
writer.writeStartElement("symbols");
@@ -1759,7 +1760,11 @@ public class XFLConverter {
statusStack.popStatus();
} else if (character instanceof TextTag) {
statusStack.pushStatus(character.toString());
convertText(frame, new AccessibilityBag() /*???*/, null, (TextTag) character, matrix, filters, recCharWriter, characterImportLinkageURL, lastImportedId, characterNameMap, characters);
int realCharacterId = swf.getCharacterId(character);
if (normalizedTexts.containsKey(realCharacterId)) {
character = normalizedTexts.get(realCharacterId);
}
convertText(normalizedFonts, frame, new AccessibilityBag() /*???*/, null, (TextTag) character, matrix, filters, recCharWriter, characterImportLinkageURL, lastImportedId, characterNameMap, characters);
statusStack.popStatus();
} else if (character instanceof DefineVideoStreamTag) {
statusStack.pushStatus(character.toString());
@@ -1827,9 +1832,9 @@ public class XFLConverter {
}
final ScriptPack spriteScriptPack = characterScriptPacks.containsKey(sprite) ? characterScriptPacks.get(sprite) : null;
extractMultilevelClips(characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, sprite.getTags(), swf.getCharacterId(sprite), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
extractMultilevelClips(normalizedFonts, normalizedTexts, characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, sprite.getTags(), swf.getCharacterId(sprite), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
convertTimelines(characterScriptPacks, lastImportedId, characterNameMap, swf, swf.getAbcIndex(), sprite, swf.getCharacterId(sprite), characterVariables.get(sprite), nonLibraryShapes, tags, sprite.getTags(), getSymbolName(lastImportedId, characterNameMap, swf, symbol), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
convertTimelines(normalizedFonts, normalizedTexts, characterScriptPacks, lastImportedId, characterNameMap, swf, swf.getAbcIndex(), sprite, swf.getCharacterId(sprite), characterVariables.get(sprite), nonLibraryShapes, tags, sprite.getTags(), getSymbolName(lastImportedId, characterNameMap, swf, symbol), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
} else if (symbol instanceof ShapeTag) {
symbolStr.writeStartElement("timeline");
@@ -1870,11 +1875,11 @@ public class XFLConverter {
}
statusStack.pushStatus("extracting multilevel clips");
extractMultilevelClips(characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, swf.getTags(), -1, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
extractMultilevelClips(normalizedFonts, normalizedTexts, characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, swf.getTags(), -1, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
statusStack.popStatus();
statusStack.pushStatus("converting multiusage morphshapes");
extractMultiUsageMorphShapes(characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, writer, swf, nonLibraryShapes, flaVersion, files, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
extractMultiUsageMorphShapes(normalizedFonts, normalizedTexts, characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, writer, swf, nonLibraryShapes, flaVersion, files, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
statusStack.popStatus();
/*if (hasSymbol) {
@@ -2522,7 +2527,7 @@ public class XFLConverter {
writer.writeEndElement();
}
private static void convertFrames(AccessibilityBag accessibility, String symbolName, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, List<Integer> onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List<CharacterTag> nonLibraryShapes, ReadOnlyTagList timelineTags, int depth, FLAVersion flaVersion, XFLXmlWriter writer, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Set<ShapeTag> smallShapes) throws XMLStreamException {
private static void convertFrames(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts, AccessibilityBag accessibility, String symbolName, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, List<Integer> onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List<CharacterTag> nonLibraryShapes, ReadOnlyTagList timelineTags, int depth, FLAVersion flaVersion, XFLXmlWriter writer, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Set<ShapeTag> smallShapes) throws XMLStreamException {
Logger.getLogger(XFLConverter.class.getName()).log(Level.FINE, "Converting frames of {0}", symbolName);
boolean lastIn = false;
XFLXmlWriter writer2 = new XFLXmlWriter();
@@ -2771,7 +2776,11 @@ public class XFLConverter {
shapeTween = false;
if (character instanceof TextTag) {
statusStack.pushStatus(character.toString());
convertText(frame, accessibility, instanceName, (TextTag) character, matrix, filters, elementsWriter, characterImportLinkageURL, lastImportedId, characterNameMap, characters);
int realCharacterId = swf.getCharacterId(character);
if (normalizedTexts.containsKey(realCharacterId)) {
character = normalizedTexts.get(realCharacterId);
}
convertText(normalizedFonts, frame, accessibility, instanceName, (TextTag) character, matrix, filters, elementsWriter, characterImportLinkageURL, lastImportedId, characterNameMap, characters);
statusStack.popStatus();
} else if (character instanceof DefineVideoStreamTag) {
convertVideoInstance(instanceName, matrix, (DefineVideoStreamTag) character, elementsWriter);
@@ -3956,6 +3965,8 @@ public class XFLConverter {
}
private void addExtractedClip(
Map<Integer, FontTag> normalizedFonts,
Map<Integer, TextTag> normalizedTexts,
Map<CharacterTag, ScriptPack> characterScriptPacks,
Reference<Integer> lastItemIdNumber,
Reference<Integer> lastImportedId,
@@ -3978,7 +3989,7 @@ public class XFLConverter {
) throws XMLStreamException {
XFLXmlWriter symbolStr = new XFLXmlWriter();
extractMultilevelClips(characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, timelineTags, spriteId, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
extractMultilevelClips(normalizedFonts, normalizedTexts, characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, timelineTags, spriteId, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
if (nextClipId.getVal() < 0) {
nextClipId.setVal(swf.getNextCharacterId());
@@ -3996,7 +4007,7 @@ public class XFLConverter {
"lastModified", Long.toString(getTimestamp(swf))});
symbolStr.writeAttribute("symbolType", "graphic");
convertTimelines(characterScriptPacks, lastImportedId, characterNameMap, swf, swf.getAbcIndex(), null, objectId, "", nonLibraryShapes, timelineTags, timelineTags, getMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
convertTimelines(normalizedFonts, normalizedTexts, characterScriptPacks, lastImportedId, characterNameMap, swf, swf.getAbcIndex(), null, objectId, "", nonLibraryShapes, timelineTags, timelineTags, getMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
symbolStr.writeEndElement(); // DOMSymbolItem
String symbolStr2 = prettyFormatXML(symbolStr.toString());
@@ -4072,6 +4083,8 @@ public class XFLConverter {
}
private void extractMultiUsageMorphShapes(
Map<Integer, FontTag> normalizedFonts,
Map<Integer, TextTag> normalizedTexts,
Map<CharacterTag, ScriptPack> characterScriptPacks,
Reference<Integer> lastItemIdNumber,
Reference<Integer> lastImportedId,
@@ -4117,7 +4130,7 @@ public class XFLConverter {
}
}
convertTimelines(characterScriptPacks, lastImportedId, characterNameMap, swf, swf.getAbcIndex(), null, objectId, "", nonLibraryShapes, swf.getTags(), new ReadOnlyTagList(timelineTags), getSymbolName(lastImportedId, characterNameMap, swf, swf.getCharacter(objectId)), flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>(), statusStack, characterImportLinkageURL, characters, smallShapes);
convertTimelines(normalizedFonts, normalizedTexts, characterScriptPacks, lastImportedId, characterNameMap, swf, swf.getAbcIndex(), null, objectId, "", nonLibraryShapes, swf.getTags(), new ReadOnlyTagList(timelineTags), getSymbolName(lastImportedId, characterNameMap, swf, swf.getCharacter(objectId)), flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>(), statusStack, characterImportLinkageURL, characters, smallShapes);
symbolStr.writeEndElement(); // DOMSymbolItem
String symbolStr2 = prettyFormatXML(symbolStr.toString());
@@ -4138,6 +4151,8 @@ public class XFLConverter {
}
private void extractMultilevelClips(
Map<Integer, FontTag> normalizedFonts,
Map<Integer, TextTag> normalizedTexts,
Map<CharacterTag, ScriptPack> characterScriptPacks,
Reference<Integer> lastItemIdNumber,
Reference<Integer> lastImportedId,
@@ -4369,7 +4384,7 @@ public class XFLConverter {
//set timelined?
delegatedTimeline.add(showFrame);
}
addExtractedClip(characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, new ReadOnlyTagList(delegatedTimeline), spriteId, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
addExtractedClip(normalizedFonts, normalizedTexts, characterScriptPacks, lastItemIdNumber, lastImportedId, characterNameMap, new ReadOnlyTagList(delegatedTimeline), spriteId, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
placeToMaskedSymbol.put(secondPlace, new MultiLevelClip(secondPlace, nextClipId.getVal(), numFrames));
}
}
@@ -4391,7 +4406,7 @@ public class XFLConverter {
}
//Note: symbolId argument might be a virtual symbol like MaskedSymbol
private void convertTimelines(Map<CharacterTag, ScriptPack> characterScriptPacks, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, AbcIndexing abcIndex, CharacterTag sprite, int symbolId, String linkageIdentifier, List<CharacterTag> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, String spriteName, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, ScriptPack scriptPack, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Set<ShapeTag> smallShapes) throws XMLStreamException {
private void convertTimelines(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts, Map<CharacterTag, ScriptPack> characterScriptPacks, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, AbcIndexing abcIndex, CharacterTag sprite, int symbolId, String linkageIdentifier, List<CharacterTag> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, String spriteName, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, ScriptPack scriptPack, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Set<ShapeTag> smallShapes) throws XMLStreamException {
ScriptPack characterScriptPack = sprite == null ? null : characterScriptPacks.containsKey(sprite) ? characterScriptPacks.get(sprite) : null;
if (sprite == null && symbolId == -1) {
@@ -4782,7 +4797,7 @@ public class XFLConverter {
"color", randomOutlineColor(),
"layerType", "mask",
"locked", "true"});
convertFrames(accessibility, symbolName, lastImportedId, characterNameMap, swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, sceneTimelineTags, po.getDepth(), flaVersion, writer, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
convertFrames(normalizedFonts, normalizedTexts, accessibility, symbolName, lastImportedId, characterNameMap, swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, sceneTimelineTags, po.getDepth(), flaVersion, writer, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
writer.writeEndElement();
int parentIndex = index;
@@ -4804,7 +4819,7 @@ public class XFLConverter {
handledClips.add(po2);
for (int ndx = po.getClipDepth() - 1; ndx > po2.getClipDepth(); ndx--) {
boolean nonEmpty = writeLayer(accessibility, symbolName, lastImportedId, characterNameMap, swf, index, depthToFramesList.get(ndx), ndx, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, sceneTimelineTags, flaVersion, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
boolean nonEmpty = writeLayer(normalizedFonts, normalizedTexts, accessibility, symbolName, lastImportedId, characterNameMap, swf, index, depthToFramesList.get(ndx), ndx, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, sceneTimelineTags, flaVersion, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
for (int i = clipFrame; i <= lastFrame; i++) {
depthToFramesList.get(ndx).remove((Integer) i);
}
@@ -4883,7 +4898,7 @@ public class XFLConverter {
}
for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) {
boolean nonEmpty = writeLayer(accessibility, symbolName, lastImportedId, characterNameMap, swf, index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, sceneTimelineTags, flaVersion, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
boolean nonEmpty = writeLayer(normalizedFonts, normalizedTexts, accessibility, symbolName, lastImportedId, characterNameMap, swf, index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, sceneTimelineTags, flaVersion, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
for (int i = clipFrame; i <= lastFrame; i++) {
depthToFramesList.get(nd).remove((Integer) i);
}
@@ -4920,7 +4935,7 @@ public class XFLConverter {
}
}
boolean nonEmpty = writeLayer(accessibility, symbolName, lastImportedId, characterNameMap, swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, sceneTimelineTags, flaVersion, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
boolean nonEmpty = writeLayer(normalizedFonts, normalizedTexts, accessibility, symbolName, lastImportedId, characterNameMap, swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, sceneTimelineTags, flaVersion, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
if (nonEmpty) {
index++;
}
@@ -4959,7 +4974,7 @@ public class XFLConverter {
writer.writeEndElement(); //DOMLayer
}
private boolean writeLayer(AccessibilityBag accessibility, String symbolName, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, int index, List<Integer> onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List<CharacterTag> nonLibraryShapes, ReadOnlyTagList timelineTags, FLAVersion flaVersion, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Set<ShapeTag> smallShapes) throws XMLStreamException {
private boolean writeLayer(Map<Integer, FontTag> normalizedFonts, Map<Integer, TextTag> normalizedTexts, AccessibilityBag accessibility, String symbolName, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, int index, List<Integer> onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List<CharacterTag> nonLibraryShapes, ReadOnlyTagList timelineTags, FLAVersion flaVersion, List<Integer> multiUsageMorphShapes, StatusStack statusStack, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Set<ShapeTag> smallShapes) throws XMLStreamException {
XFLXmlWriter layerPrev = new XFLXmlWriter();
statusStack.pushStatus("layer " + (index + 1));
//System.err.println("- writing layer " + (index + 1) + (startFrame == 0 && endFrame == Integer.MAX_VALUE ? ", all frames": ", frame " + startFrame + " to " + endFrame));
@@ -4978,7 +4993,7 @@ public class XFLConverter {
layerPrev.writeCharacters(""); // todo honfika: hack to close start tag
String layerAfter = "</DOMLayer>";
int prevLength = writer.length();
convertFrames(accessibility, symbolName, lastImportedId, characterNameMap, swf, onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, timelineTags, d, flaVersion, writer, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
convertFrames(normalizedFonts, normalizedTexts, accessibility, symbolName, lastImportedId, characterNameMap, swf, onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, timelineTags, d, flaVersion, writer, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
statusStack.popStatus();
return writer.length() != prevLength;
}
@@ -5058,7 +5073,7 @@ public class XFLConverter {
return ret;
}
private static void convertText(int frame, AccessibilityBag accessibility, String instanceName, TextTag tag, MATRIX m, List<FILTER> filters, XFLXmlWriter writer, Map<CharacterTag, String> characterImportLinkageURL, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, Set<CharacterTag> characters) throws XMLStreamException {
private static void convertText(Map<Integer, FontTag> normalizedFonts, int frame, AccessibilityBag accessibility, String instanceName, TextTag tag, MATRIX m, List<FILTER> filters, XFLXmlWriter writer, Map<CharacterTag, String> characterImportLinkageURL, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, Set<CharacterTag> characters) throws XMLStreamException {
MATRIX matrix = new MATRIX(m);
CSMSettingsTag csmts = null;
XFLXmlWriter filterStr = new XFLXmlWriter();
@@ -5131,7 +5146,7 @@ public class XFLConverter {
writer.writeAttribute("antiAliasSharpness", antiAliasSharpness);
writer.writeAttribute("antiAliasThickness", antiAliasThickness);
}
Map<String, Object> attrs = TextTag.getTextRecordsAttributes(textRecords, swf);
Map<String, Object> attrs = TextTag.getTextRecordsAttributes(textRecords, swf, normalizedFonts);
writer.writeAttribute("width", tag.getBounds().getWidth() / 2);
writer.writeAttribute("height", tag.getBounds().getHeight());
writer.writeAttribute("autoExpand", true);
@@ -5168,6 +5183,9 @@ public class XFLConverter {
fontName = null;
textHeight = rec.textHeight;
font = ((Tag) tag).getSwf().getFont(fontId);
if (normalizedFonts.containsKey(fontId)) {
font = normalizedFonts.get(fontId);
}
if (font != null) {
DefineFontNameTag dfn = (DefineFontNameTag) font.getSwf().getCharacterIdTag(font.getCharacterId(), DefineFontNameTag.ID);
@@ -5240,6 +5258,10 @@ public class XFLConverter {
DefineEditTextTag det = (DefineEditTextTag) tag;
String tagName;
FontTag ft = det.getSwf().getFont(det.fontId);
if (normalizedFonts.containsKey(det.fontId)) {
ft = normalizedFonts.get(det.fontId);
}
if (ft != null && ft.isSmall()) {
fontRenderingMode = "bitmap";
}
@@ -5644,12 +5666,17 @@ public class XFLConverter {
convertFonts(lastItemIdNumber, lastImportedId, characterNameMap, swf, characters, domDocument, statusStack, characterVariables, characterClasses, charactersExportedInFirstFrame, characterImportLinkageURL);
Set<ShapeTag> smallShapes = getSmallShapes(swf);
Map<Integer, FontTag> normalizedFonts = new HashMap<>();
Map<Integer, TextTag> normalizedTexts = new HashMap<>();
FontNormalizer fontNormalizer = new FontNormalizer();
fontNormalizer.normalizeFonts(swf, normalizedFonts, normalizedTexts);
convertLibrary(lastItemIdNumber, charactersExportedInFirstFrame, characterImportLinkageURL, characters, lastImportedId, characterNameMap, swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), files, datfiles, flaVersion, domDocument, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, smallShapes);
convertLibrary(normalizedFonts, normalizedTexts, lastItemIdNumber, charactersExportedInFirstFrame, characterImportLinkageURL, characters, lastImportedId, characterNameMap, swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), files, datfiles, flaVersion, domDocument, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, smallShapes);
//domDocument.writeStartElement("timelines");
statusStack.pushStatus("main timeline");
convertTimelines(characterScriptPacks, lastImportedId, characterNameMap, swf, swf.getAbcIndex(), null, -1, null, nonLibraryShapes, swf.getTags(), swf.getTags(), null, flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
convertTimelines(normalizedFonts, normalizedTexts, characterScriptPacks, lastImportedId, characterNameMap, swf, swf.getAbcIndex(), null, -1, null, nonLibraryShapes, swf.getTags(), swf.getTags(), null, flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack, characterImportLinkageURL, characters, smallShapes);
statusStack.popStatus();
//domDocument.writeEndElement();