diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java
index 0502dfcd6..64052703c 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java
@@ -1,1136 +1,1145 @@
-/*
- * Copyright (C) 2010-2015 JPEXS, All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3.0 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library.
- */
-package com.jpexs.decompiler.flash.tags;
-
-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.commonshape.ExportRectangle;
-import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
-import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
-import com.jpexs.decompiler.flash.helpers.HighlightedText;
-import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
-import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
-import com.jpexs.decompiler.flash.tags.base.BoundedTag;
-import com.jpexs.decompiler.flash.tags.base.FontTag;
-import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler;
-import com.jpexs.decompiler.flash.tags.base.RenderContext;
-import com.jpexs.decompiler.flash.tags.base.TextTag;
-import com.jpexs.decompiler.flash.tags.dynamictext.CharacterWithStyle;
-import com.jpexs.decompiler.flash.tags.dynamictext.DynamicTextModel;
-import com.jpexs.decompiler.flash.tags.dynamictext.GlyphCharacter;
-import com.jpexs.decompiler.flash.tags.dynamictext.Paragraph;
-import com.jpexs.decompiler.flash.tags.dynamictext.SameStyleTextRecord;
-import com.jpexs.decompiler.flash.tags.dynamictext.TextStyle;
-import com.jpexs.decompiler.flash.tags.dynamictext.Word;
-import com.jpexs.decompiler.flash.tags.text.ParsedSymbol;
-import com.jpexs.decompiler.flash.tags.text.TextAlign;
-import com.jpexs.decompiler.flash.tags.text.TextLexer;
-import com.jpexs.decompiler.flash.tags.text.TextParseException;
-import com.jpexs.decompiler.flash.types.BasicType;
-import com.jpexs.decompiler.flash.types.ColorTransform;
-import com.jpexs.decompiler.flash.types.GLYPHENTRY;
-import com.jpexs.decompiler.flash.types.MATRIX;
-import com.jpexs.decompiler.flash.types.RECT;
-import com.jpexs.decompiler.flash.types.RGB;
-import com.jpexs.decompiler.flash.types.RGBA;
-import com.jpexs.decompiler.flash.types.TEXTRECORD;
-import com.jpexs.decompiler.flash.types.annotations.Conditional;
-import com.jpexs.decompiler.flash.types.annotations.SWFType;
-import com.jpexs.helpers.ByteArrayRange;
-import com.jpexs.helpers.SerializableImage;
-import com.jpexs.helpers.utf8.Utf8Helper;
-import java.awt.Color;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-import java.util.Stack;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-/**
- *
- * @author JPEXS
- */
-public class DefineEditTextTag extends TextTag {
-
- public static final int ID = 37;
-
- public static final String NAME = "DefineEditText";
-
- @SWFType(BasicType.UI16)
- public int characterID;
-
- public RECT bounds;
-
- public boolean hasText;
-
- public boolean wordWrap;
-
- public boolean multiline;
-
- public boolean password;
-
- public boolean readOnly;
-
- public boolean hasTextColor;
-
- public boolean hasMaxLength;
-
- public boolean hasFont;
-
- public boolean hasFontClass;
-
- public boolean autoSize;
-
- public boolean hasLayout;
-
- public boolean noSelect;
-
- public boolean border;
-
- public boolean wasStatic;
-
- public boolean html;
-
- public boolean useOutlines;
-
- @SWFType(BasicType.UI16)
- @Conditional("hasFont")
- public int fontId;
-
- @Conditional("hasFontClass")
- public String fontClass;
-
- @SWFType(BasicType.UI16)
- @Conditional("hasFont")
- public int fontHeight;
-
- @Conditional("hasTextColor")
- public RGBA textColor;
-
- @SWFType(BasicType.UI16)
- @Conditional("hasMaxLength")
- public int maxLength;
-
- @SWFType(BasicType.UI8)
- @Conditional("hasLayout")
- public int align;
-
- @SWFType(BasicType.UI16)
- @Conditional("hasLayout")
- public int leftMargin;
-
- @SWFType(BasicType.UI16)
- @Conditional("hasLayout")
- public int rightMargin;
-
- @SWFType(BasicType.UI16)
- @Conditional("hasLayout")
- public int indent;
-
- @SWFType(BasicType.SI16)
- @Conditional("hasLayout")
- public int leading;
-
- public String variableName;
-
- @Conditional("hasText")
- public String initialText;
-
- @Override
- public RECT getBounds() {
- return bounds;
- }
-
- @Override
- public MATRIX getTextMatrix() {
- MATRIX matrix = new MATRIX();
- matrix.translateX = bounds.Xmin;
- matrix.translateY = bounds.Ymin;
- return matrix;
- }
-
- @Override
- public void setBounds(RECT r) {
- bounds = r;
- }
-
- private String stripTags(String inp) {
- boolean intag = false;
- String outp = "";
- inp = inp.replaceAll("
", "\r\n");
- for (int i = 0; i < inp.length(); ++i) {
- if (!intag && inp.charAt(i) == '<') {
- intag = true;
- continue;
- }
- if (intag && inp.charAt(i) == '>') {
- intag = false;
- continue;
- }
- if (!intag) {
- outp += inp.charAt(i);
- }
- }
- return outp;
- }
-
- private String entitiesReplace(String s) {
- s = s.replace("<", "<");
- s = s.replace(">", ">");
- s = s.replace("&", "&");
- s = s.replace(""", "\"");
- return s;
- }
-
- @Override
- public List getTexts() {
- String ret = "";
- if (hasText) {
- ret = initialText;
- }
- if (html) {
- ret = stripTags(ret);
- ret = entitiesReplace(ret);
- }
- return Arrays.asList(ret);
- }
-
- private List getTextWithStyle() {
- String str = "";
- TextStyle style = new TextStyle();
- style.font = swf.getFont(fontId);
- style.fontHeight = fontHeight;
- style.fontLeading = leading;
- if (hasTextColor) {
- style.textColor = textColor;
- }
- if (hasText) {
- str = initialText;
- }
- final List ret = new ArrayList<>();
- if (html) {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- SAXParser saxParser;
- final Stack styles = new Stack<>();
- styles.add(style);
- try {
- saxParser = factory.newSAXParser();
- DefaultHandler handler = new DefaultHandler() {
-
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
- TextStyle style = styles.peek();
- switch (qName) {
- case "p":
- // todo: parse the following attribute:
- // align
- 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 color = attributes.getValue("color");
- if (color != null) {
- if (color.startsWith("#")) {
- style.textColor = new RGBA(Color.decode(color));
- }
- }
- String size = attributes.getValue("size");
- if (size != null && size.length() > 0) {
- char firstChar = size.charAt(0);
- if (firstChar != '+' && firstChar != '-') {
- int fontSize = Integer.parseInt(size);
- style.fontHeight = (int) Math.round(fontSize * (style.font == null ? 1 : style.font.getDivider()));
- style.fontLeading = leading;
- } else {
- // todo: parse relative sizes
- }
- }
- // todo: parse the following attributes:
- // face, letterSpacing, kerning
- styles.add(style);
- break;
- case "br":
- case "sbr": // what's this?
- CharacterWithStyle cs = new CharacterWithStyle();
- cs.character = '\n';
- cs.style = style;
- ret.add(cs);
- break;
- }
- //ret = entitiesReplace(ret);
- }
-
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
- switch (qName) {
- case "b":
- case "i":
- case "u":
- case "font":
- styles.pop();
- break;
- case "p":
- TextStyle style = styles.peek();
- CharacterWithStyle cs = new CharacterWithStyle();
- cs.character = '\n';
- cs.style = style;
- ret.add(cs);
- break;
- }
- }
-
- @Override
- public void characters(char[] ch, int start, int length) throws SAXException {
- String txt = new String(ch, start, length);
- TextStyle style = styles.peek();
- addCharacters(ret, txt, style);
- }
- };
- str = " \n"
- + "]>" + str + "";
- saxParser.parse(new ByteArrayInputStream(str.getBytes(Utf8Helper.charset)), handler);
- } catch (ParserConfigurationException | SAXException | IOException ex) {
- Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex);
- }
- } else {
- addCharacters(ret, str, style);
- }
- return ret;
- }
-
- private void addCharacters(List list, String str, TextStyle style) {
- for (int i = 0; i < str.length(); i++) {
- char ch = str.charAt(i);
- CharacterWithStyle cs = new CharacterWithStyle();
- cs.character = ch;
- cs.style = style;
- list.add(cs);
- }
- }
-
- @Override
- public List getFontIds() {
- List ret = new ArrayList<>();
- ret.add(fontId);
- return ret;
- }
-
- @Override
- public HighlightedText getFormattedText() {
- HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
- writer.append("[");
- String[] alignNames = {"left", "right", "center", "justify"};
- String alignment;
- if (align < alignNames.length) {
- alignment = alignNames[align];
- } else {
- alignment = "unknown";
- }
- writer.newLine();
- writer.append("xmin " + bounds.Xmin).newLine();
- writer.append("ymin " + bounds.Ymin).newLine();
- writer.append("xmax " + bounds.Xmax).newLine();
- writer.append("ymax " + bounds.Ymax).newLine();
- if (wordWrap) {
- writer.append("wordwrap 1").newLine();
- }
- if (multiline) {
- writer.append("multiline 1").newLine();
- }
- if (password) {
- writer.append("password 1").newLine();
- }
- if (readOnly) {
- writer.append("readonly 1").newLine();
- }
- if (autoSize) {
- writer.append("autosize 1").newLine();
- }
- if (noSelect) {
- writer.append("noselect 1").newLine();
- }
- if (border) {
- writer.append("border 1").newLine();
- }
- if (wasStatic) {
- writer.append("wasstatic 1").newLine();
- }
- if (html) {
- writer.append("html 1").newLine();
- }
- if (useOutlines) {
- writer.append("useoutlines 1").newLine();
- }
- if (hasFont) {
- writer.append("font " + fontId).newLine();
- writer.append("height " + fontHeight).newLine();
- }
- if (hasTextColor) {
- writer.append("color " + textColor.toHexARGB()).newLine();
- }
- if (hasFontClass) {
- writer.append("fontclass " + fontClass).newLine();
- }
- if (hasMaxLength) {
- writer.append("maxlength " + maxLength).newLine();
- }
- writer.append("align " + alignment).newLine();
- if (hasLayout) {
- writer.append("leftmargin " + leftMargin).newLine();
- writer.append("rightmargin " + rightMargin).newLine();
- writer.append("indent " + indent).newLine();
- writer.append("leading " + leading).newLine();
- }
- if (!variableName.isEmpty()) {
- writer.append("variablename " + variableName).newLine();
- }
- writer.append("]");
- if (hasText) {
- String text = initialText.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]");
- writer.hilightSpecial(text, HighlightSpecialType.TEXT);
- }
- return new HighlightedText(writer);
- }
-
- @Override
- public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws TextParseException {
- try {
- TextLexer lexer = new TextLexer(new StringReader(formattedText));
- ParsedSymbol s = null;
- formattedText = "";
- RECT bounds = new RECT(this.bounds);
- boolean wordWrap = false;
- boolean multiline = false;
- boolean password = false;
- boolean readOnly = false;
- boolean autoSize = false;
- boolean noSelect = false;
- boolean border = false;
- boolean wasStatic = false;
- boolean html = false;
- boolean useOutlines = false;
- int fontId = -1;
- int fontHeight = -1;
- String fontClass = null;
- RGBA textColor = null;
- int maxLength = -1;
- int align = -1;
- int leftMargin = -1;
- int rightMargin = -1;
- int indent = -1;
- int leading = -1;
- String variableName = null;
-
- int textIdx = 0;
- while ((s = lexer.yylex()) != null) {
- switch (s.type) {
- case PARAMETER:
- String paramName = (String) s.values[0];
- String paramValue = (String) s.values[1];
- switch (paramName) {
- case "xmin":
- try {
- bounds.Xmin = Integer.parseInt(paramValue);
- } catch (NumberFormatException nfe) {
- throw new TextParseException("Invalid xmin value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "ymin":
- try {
- bounds.Ymin = Integer.parseInt(paramValue);
- } catch (NumberFormatException nfe) {
- throw new TextParseException("Invalid ymin value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "xmax":
- try {
- bounds.Xmax = Integer.parseInt(paramValue);
- } catch (NumberFormatException nfe) {
- throw new TextParseException("Invalid xmax value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "ymax":
- try {
- bounds.Ymax = Integer.parseInt(paramValue);
- } catch (NumberFormatException nfe) {
- throw new TextParseException("Invalid ymax value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "wordwrap":
- if (paramValue.equals("1")) {
- wordWrap = true;
- }
- break;
- case "multiline":
- if (paramValue.equals("1")) {
- multiline = true;
- }
- break;
- case "password":
- if (paramValue.equals("1")) {
- password = true;
- }
- break;
- case "readonly":
- if (paramValue.equals("1")) {
- readOnly = true;
- }
- break;
- case "autosize":
- if (paramValue.equals("1")) {
- autoSize = true;
- }
- break;
- case "noselect":
- if (paramValue.equals("1")) {
- noSelect = true;
- }
- break;
- case "border":
- if (paramValue.equals("1")) {
- border = true;
- }
- break;
- case "wasstatic":
- if (paramValue.equals("1")) {
- wasStatic = true;
- }
- break;
- case "html":
- if (paramValue.equals("1")) {
- html = true;
- }
- break;
- case "useoutlines":
- if (paramValue.equals("1")) {
- useOutlines = true;
- }
- break;
- case "font":
- try {
- fontId = Integer.parseInt(paramValue);
-
- FontTag ft = swf.getFont(fontId);
- if (ft == null) {
- throw new TextParseException("Font not found.", lexer.yyline());
- }
- } catch (NumberFormatException ne) {
- throw new TextParseException("Invalid font value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "fontclass":
- fontClass = paramValue;
- break;
- case "height":
- try {
- fontHeight = Integer.parseInt(paramValue);
- } catch (NumberFormatException ne) {
- throw new TextParseException("Invalid height value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "color":
- Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue);
- if (m.matches()) {
- textColor = new RGBA(Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16), Integer.parseInt(m.group(4), 16), Integer.parseInt(m.group(1), 16));
- } else {
- throw new TextParseException("Invalid color. Valid format is #aarrggbb. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "maxlength":
- try {
- maxLength = Integer.parseInt(paramValue);
- } catch (NumberFormatException ne) {
- throw new TextParseException("Invalid maxLength value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "align":
- switch (paramValue) {
- case "left":
- align = 0;
- break;
- case "right":
- align = 1;
- break;
- case "center":
- align = 2;
- break;
- case "justify":
- align = 3;
- break;
- default:
- throw new TextParseException("Invalid align value. Expected one of: left,right,center or justify. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "leftmargin":
- try {
- leftMargin = Integer.parseInt(paramValue);
- } catch (NumberFormatException ne) {
- throw new TextParseException("Invalid leftmargin value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "rightmargin":
- try {
- rightMargin = Integer.parseInt(paramValue);
- } catch (NumberFormatException ne) {
- throw new TextParseException("Invalid rightmargin value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "indent":
- try {
- indent = Integer.parseInt(paramValue);
- } catch (NumberFormatException ne) {
- throw new TextParseException("Invalid indent value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "leading":
- try {
- leading = Integer.parseInt(paramValue);
- } catch (NumberFormatException ne) {
- throw new TextParseException("Invalid leading value. Number expected. Found: " + paramValue, lexer.yyline());
- }
- break;
- case "variablename":
- variableName = paramValue;
- break;
- default:
- throw new TextParseException("Unrecognized parameter name: " + paramName, lexer.yyline());
- }
- break;
- case TEXT:
- String s2 = (String) s.values[0];
- if (s2 == null) {
- s2 = "";
- }
-
- formattedText += (texts == null || textIdx >= texts.length) ? s2 : texts[textIdx++];
- formattedText = formattedText.replace("\r\n", "\r");
- break;
- }
- }
-
- setModified(true);
- this.bounds = bounds;
- if (formattedText.length() > 0) {
- initialText = formattedText;
- this.hasText = true;
- } else {
- this.hasText = false;
- }
- this.wordWrap = wordWrap;
- this.multiline = multiline;
- this.password = password;
- this.readOnly = readOnly;
- this.noSelect = noSelect;
- this.border = border;
- this.wasStatic = wasStatic;
- this.html = html;
- this.useOutlines = useOutlines;
- if (textColor != null) {
- hasTextColor = true;
- this.textColor = textColor;
- }
- if (maxLength > -1) {
- this.maxLength = maxLength;
- hasMaxLength = true;
- }
- if (fontId > -1) {
- this.fontId = fontId;
- }
- if (fontHeight > -1) {
- this.fontHeight = fontHeight;
- }
- if (fontClass != null) {
- this.fontClass = fontClass;
- hasFontClass = true;
- }
- this.autoSize = autoSize;
- this.align = align;
- if ((leftMargin > -1)
- || (rightMargin > -1)
- || (indent > -1)
- || (leading > -1)) {
- this.leftMargin = leftMargin;
- this.rightMargin = rightMargin;
- this.indent = indent;
- this.leading = leading;
- hasLayout = true;
- }
- if (variableName == null) {
- variableName = "";
- }
- this.variableName = variableName;
-
- } catch (IOException ex) {
- Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex);
- return false;
- }
-
- return true;
- }
-
- @Override
- public void updateTextBounds() {
- }
-
- @Override
- public boolean alignText(TextAlign textAlign) {
- return true;
- }
-
- @Override
- public boolean translateText(int diff) {
- return true;
- }
-
- @Override
- public RECT getRect(Set added) {
- return bounds;
- }
-
- @Override
- public int getCharacterId() {
- return characterID;
- }
-
- @Override
- public void setCharacterId(int characterId) {
- this.characterID = characterId;
- }
-
- /**
- * Gets data bytes
- *
- * @return Bytes of data
- */
- @Override
- public byte[] getData() {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- OutputStream os = baos;
- SWFOutputStream sos = new SWFOutputStream(os, getVersion());
- try {
- sos.writeUI16(characterID);
- sos.writeRECT(bounds);
- sos.writeUB(1, hasText ? 1 : 0);
- sos.writeUB(1, wordWrap ? 1 : 0);
- sos.writeUB(1, multiline ? 1 : 0);
- sos.writeUB(1, password ? 1 : 0);
- sos.writeUB(1, readOnly ? 1 : 0);
- sos.writeUB(1, hasTextColor ? 1 : 0);
- sos.writeUB(1, hasMaxLength ? 1 : 0);
- sos.writeUB(1, hasFont ? 1 : 0);
- sos.writeUB(1, hasFontClass ? 1 : 0);
- sos.writeUB(1, autoSize ? 1 : 0);
- sos.writeUB(1, hasLayout ? 1 : 0);
- sos.writeUB(1, noSelect ? 1 : 0);
- sos.writeUB(1, border ? 1 : 0);
- sos.writeUB(1, wasStatic ? 1 : 0);
- sos.writeUB(1, html ? 1 : 0);
- sos.writeUB(1, useOutlines ? 1 : 0);
- if (hasFont) {
- sos.writeUI16(fontId);
- }
- if (hasFontClass) {
- sos.writeString(fontClass);
- }
- if (hasFont) {
- sos.writeUI16(fontHeight);
- }
- if (hasTextColor) {
- sos.writeRGBA(textColor);
- }
- if (hasMaxLength) {
- sos.writeUI16(maxLength);
- }
- if (hasLayout) {
- sos.writeUI8(align);
- sos.writeUI16(leftMargin);
- sos.writeUI16(rightMargin);
- sos.writeUI16(indent);
- sos.writeSI16(leading);
- }
- sos.writeString(variableName);
- if (hasText) {
- sos.writeString(initialText);
- }
-
- } catch (IOException e) {
- throw new Error("This should never happen.", e);
- }
- return baos.toByteArray();
- }
-
- /**
- * Constructor
- *
- * @param swf
- */
- public DefineEditTextTag(SWF swf) {
- super(swf, ID, NAME, null);
- characterID = swf.getNextCharacterId();
- bounds = new RECT();
- variableName = "";
- }
-
- /**
- * Constructor
- *
- * @param sis
- * @param data
- * @throws IOException
- */
- public DefineEditTextTag(SWFInputStream sis, ByteArrayRange data) throws IOException {
- super(sis.getSwf(), ID, NAME, data);
- readData(sis, data, 0, false, false, false);
- }
-
- @Override
- public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
- characterID = sis.readUI16("characterID");
- bounds = sis.readRECT("bounds");
- hasText = sis.readUB(1, "hasText") == 1;
- wordWrap = sis.readUB(1, "wordWrap") == 1;
- multiline = sis.readUB(1, "multiline") == 1;
- password = sis.readUB(1, "password") == 1;
- readOnly = sis.readUB(1, "readOnly") == 1;
- hasTextColor = sis.readUB(1, "hasTextColor") == 1;
- hasMaxLength = sis.readUB(1, "hasMaxLength") == 1;
- hasFont = sis.readUB(1, "hasFont") == 1;
- hasFontClass = sis.readUB(1, "hasFontClass") == 1;
- autoSize = sis.readUB(1, "autoSize") == 1;
- hasLayout = sis.readUB(1, "hasLayout") == 1;
- noSelect = sis.readUB(1, "noSelect") == 1;
- border = sis.readUB(1, "border") == 1;
- wasStatic = sis.readUB(1, "wasStatic") == 1;
- html = sis.readUB(1, "html") == 1;
- useOutlines = sis.readUB(1, "useOutlines") == 1;
- if (hasFont) {
- fontId = sis.readUI16("fontId");
- }
- if (hasFontClass) {
- fontClass = sis.readString("fontClass");
- }
- if (hasFont) {
- fontHeight = sis.readUI16("fontHeight");
- }
- if (hasTextColor) {
- textColor = sis.readRGBA("textColor");
- }
- if (hasMaxLength) {
- maxLength = sis.readUI16("maxLength");
- }
- if (hasLayout) {
- align = sis.readUI8("align"); //0 left, 1 right, 2 center, 3 justify
- leftMargin = sis.readUI16("leftMargin");
- rightMargin = sis.readUI16("rightMargin");
- indent = sis.readUI16("indent");
- leading = sis.readSI16("leading");
- }
- variableName = sis.readString("variableName");
- if (hasText) {
- initialText = sis.readString("initialText");
- }
-
- }
-
- @Override
- public void getNeededCharacters(Set needed) {
- if (hasFont) {
- needed.add(fontId);
- }
- }
-
- @Override
- public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
- if (fontId == oldCharacterId) {
- fontId = newCharacterId;
- setModified(true);
- return true;
- }
- return false;
- }
-
- @Override
- public boolean removeCharacter(int characterId) {
- if (fontId == characterId) {
- hasFont = false;
- fontId = 0;
- setModified(true);
- return true;
- }
- return false;
- }
-
- @Override
- public int getUsedParameters() {
- return 0;
- }
-
- @Override
- public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
- render(false, image, transformation, colorTransform);
- }
-
- @Override
- public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
- // todo: implement
- }
-
- @Override
- public String toHtmlCanvas(double unitDivisor) {
- return render(true, null, new Matrix(), new ColorTransform());
- }
-
- private String render(boolean canvas, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
- if (border) {
- // border is always black, fill color is always white?
- RGB borderColor = new RGBA(Color.black);
- RGB fillColor = new RGBA(Color.white);
- if (!canvas) {
- drawBorder(swf, image, borderColor, fillColor, getRect(), getTextMatrix(), transformation, colorTransform);
- } else {
- // TODO: draw border
- }
- }
- if (hasText) {
- DynamicTextModel textModel = new DynamicTextModel();
- List txt = getTextWithStyle();
- TextStyle lastStyle = null;
- char prevChar = 0;
- boolean lastWasWhiteSpace = false;
- for (int i = 0; i < txt.size(); i++) {
- CharacterWithStyle cs = txt.get(i);
- char c = cs.character;
- if (c != '\r' && c != '\n') {
- // create new SameStyleTextRecord for all words and all diffrent style text parts
- if (lastWasWhiteSpace && !Character.isWhitespace(c)) {
- textModel.newWord();
- lastWasWhiteSpace = false;
- }
- if (cs.style != lastStyle) {
- lastStyle = cs.style;
- textModel.style = lastStyle;
- textModel.newRecord();
- }
- Character nextChar = null;
- if (i + 1 < txt.size()) {
- nextChar = txt.get(i + 1).character;
- }
- int advance;
- FontTag font = lastStyle.font;
- GLYPHENTRY ge = new GLYPHENTRY();
- ge.glyphIndex = font == null ? -1 : font.charToGlyph(c);
- if (font != null && font.hasLayout()) {
- int kerningAdjustment = 0;
- if (nextChar != null) {
- kerningAdjustment = font.getCharKerningAdjustment(c, nextChar);
- kerningAdjustment /= font.getDivider();
- }
- advance = (int) Math.round(Math.round((double) lastStyle.fontHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0)));
- } else {
- String fontName = FontTag.defaultFontName;
- int fontStyle = font == null ? 0 : font.getFontStyle();
- advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, fontStyle, (int) (lastStyle.fontHeight / SWF.unitDivisor), c, nextChar));
- }
- ge.glyphAdvance = advance;
- textModel.addGlyph(c, ge);
- if (Character.isWhitespace(c)) {
- lastWasWhiteSpace = true;
- }
- } else {
- if (multiline) {
- textModel.newParagraph();
- }
- }
- prevChar = c;
- }
-
- textModel.calculateTextWidths();
- List> lines;
- if (multiline && wordWrap) {
- lines = new ArrayList<>();
- for (Paragraph paragraph : textModel.paragraphs) {
- List line = new ArrayList<>();
- int lineLength = 0;
- for (Word word : paragraph.words) {
- if (lineLength + word.width <= bounds.getWidth()) {
- line.addAll(word.records);
- lineLength += word.width;
- } else {
- lines.add(line);
- line = new ArrayList<>();
- line.addAll(word.records);
- lineLength = 0;
- }
- }
- if (!line.isEmpty()) {
- lines.add(line);
- }
- }
- } else {
- lines = new ArrayList<>();
- for (Paragraph paragraph : textModel.paragraphs) {
- List line = new ArrayList<>();
- for (Word word : paragraph.words) {
- for (SameStyleTextRecord tr : word.records) {
- line.add(tr);
- }
- }
- lines.add(line);
- }
- }
-
- // remove spaces after last word
- for (List line : lines) {
- boolean removed = true;
- while (removed) {
- removed = false;
- while (line.size() > 0 && line.get(line.size() - 1).glyphEntries.isEmpty()) {
- line.remove(line.size() - 1);
- removed = true;
- }
- if (line.size() > 0) {
- SameStyleTextRecord lastRecord = line.get(line.size() - 1);
- while (lastRecord.glyphEntries.size() > 0
- && Character.isWhitespace(lastRecord.glyphEntries.get(lastRecord.glyphEntries.size() - 1).character)) {
- lastRecord.glyphEntries.remove(lastRecord.glyphEntries.size() - 1);
- removed = true;
- }
- }
- }
- }
-
- textModel.calculateTextWidths();
-
- List allTextRecords = new ArrayList<>();
- int lastHeight = 0;
- int yOffset = 0;
- for (List line : lines) {
- int width = 0;
- int currentOffset = 0;
- if (line.isEmpty()) {
- currentOffset = lastHeight;
- } else {
- for (SameStyleTextRecord tr : line) {
- width += tr.width;
- int lineHeight = tr.style.fontHeight + tr.style.fontLeading;
- lastHeight = lineHeight;
- if (lineHeight > currentOffset) {
- currentOffset = lineHeight;
- }
- }
- }
- yOffset += currentOffset;
- int alignOffset = 0;
- switch (align) {
- case 0: // left
- alignOffset = 0;
- break;
- case 1: // right
- alignOffset = bounds.getWidth() - width;
- break;
- case 2: // center
- alignOffset = (bounds.getWidth() - width) / 2;
- break;
- case 3: // justify
- // todo;
- break;
- }
- for (SameStyleTextRecord tr : line) {
- tr.xOffset = alignOffset;
- alignOffset += tr.width;
- }
- for (SameStyleTextRecord tr : line) {
- TEXTRECORD tr2 = new TEXTRECORD();
- tr2.styleFlagsHasFont = fontId != 0;
- tr2.fontId = fontId;
- tr2.textHeight = tr.style.fontHeight;
- if (tr.style.textColor != null) {
- tr2.styleFlagsHasColor = true;
- tr2.textColorA = tr.style.textColor;
- }
- // always add xOffset, because no xOffset and 0 xOffset is diffrent in text rendering
- tr2.styleFlagsHasXOffset = true;
- tr2.xOffset = tr.xOffset;
- if (yOffset != 0) {
- tr2.styleFlagsHasYOffset = true;
- tr2.yOffset = yOffset;
- }
- tr2.glyphEntries = new ArrayList<>(tr.glyphEntries.size());
- for (GlyphCharacter ge : tr.glyphEntries) {
- tr2.glyphEntries.add(ge.glyphEntry);
- }
- allTextRecords.add(tr2);
- }
- }
-
- if (canvas) {
- return staticTextToHtmlCanvas(1, swf, allTextRecords, 2, getBounds(), getTextMatrix(), colorTransform);
- } else {
- staticTextToImage(swf, allTextRecords, 2, image, getTextMatrix(), transformation, colorTransform);
- }
- }
-
- return "";
- }
-
- @Override
- public ExportRectangle calculateTextBounds() {
- return null;
- }
-
- @Override
- public int getNumFrames() {
- return 1;
- }
-
- @Override
- public boolean isSingleFrame() {
- return true;
- }
-}
+/*
+ * Copyright (C) 2010-2015 JPEXS, All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.
+ */
+package com.jpexs.decompiler.flash.tags;
+
+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.commonshape.ExportRectangle;
+import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
+import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
+import com.jpexs.decompiler.flash.helpers.HighlightedText;
+import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
+import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
+import com.jpexs.decompiler.flash.tags.base.BoundedTag;
+import com.jpexs.decompiler.flash.tags.base.FontTag;
+import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler;
+import com.jpexs.decompiler.flash.tags.base.RenderContext;
+import com.jpexs.decompiler.flash.tags.base.TextTag;
+import com.jpexs.decompiler.flash.tags.dynamictext.CharacterWithStyle;
+import com.jpexs.decompiler.flash.tags.dynamictext.DynamicTextModel;
+import com.jpexs.decompiler.flash.tags.dynamictext.GlyphCharacter;
+import com.jpexs.decompiler.flash.tags.dynamictext.Paragraph;
+import com.jpexs.decompiler.flash.tags.dynamictext.SameStyleTextRecord;
+import com.jpexs.decompiler.flash.tags.dynamictext.TextStyle;
+import com.jpexs.decompiler.flash.tags.dynamictext.Word;
+import com.jpexs.decompiler.flash.tags.text.ParsedSymbol;
+import com.jpexs.decompiler.flash.tags.text.TextAlign;
+import com.jpexs.decompiler.flash.tags.text.TextLexer;
+import com.jpexs.decompiler.flash.tags.text.TextParseException;
+import com.jpexs.decompiler.flash.types.BasicType;
+import com.jpexs.decompiler.flash.types.ColorTransform;
+import com.jpexs.decompiler.flash.types.DynamicTextGlyphEntry;
+import com.jpexs.decompiler.flash.types.MATRIX;
+import com.jpexs.decompiler.flash.types.RECT;
+import com.jpexs.decompiler.flash.types.RGB;
+import com.jpexs.decompiler.flash.types.RGBA;
+import com.jpexs.decompiler.flash.types.TEXTRECORD;
+import com.jpexs.decompiler.flash.types.annotations.Conditional;
+import com.jpexs.decompiler.flash.types.annotations.SWFType;
+import com.jpexs.helpers.ByteArrayRange;
+import com.jpexs.helpers.SerializableImage;
+import com.jpexs.helpers.utf8.Utf8Helper;
+import java.awt.Color;
+import java.awt.Font;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ *
+ * @author JPEXS
+ */
+public class DefineEditTextTag extends TextTag {
+
+ public static final int ID = 37;
+
+ public static final String NAME = "DefineEditText";
+
+ @SWFType(BasicType.UI16)
+ public int characterID;
+
+ public RECT bounds;
+
+ public boolean hasText;
+
+ public boolean wordWrap;
+
+ public boolean multiline;
+
+ public boolean password;
+
+ public boolean readOnly;
+
+ public boolean hasTextColor;
+
+ public boolean hasMaxLength;
+
+ public boolean hasFont;
+
+ public boolean hasFontClass;
+
+ public boolean autoSize;
+
+ public boolean hasLayout;
+
+ public boolean noSelect;
+
+ public boolean border;
+
+ public boolean wasStatic;
+
+ public boolean html;
+
+ public boolean useOutlines;
+
+ @SWFType(BasicType.UI16)
+ @Conditional("hasFont")
+ public int fontId;
+
+ @Conditional("hasFontClass")
+ public String fontClass;
+
+ @SWFType(BasicType.UI16)
+ @Conditional("hasFont")
+ public int fontHeight;
+
+ @Conditional("hasTextColor")
+ public RGBA textColor;
+
+ @SWFType(BasicType.UI16)
+ @Conditional("hasMaxLength")
+ public int maxLength;
+
+ @SWFType(BasicType.UI8)
+ @Conditional("hasLayout")
+ public int align;
+
+ @SWFType(BasicType.UI16)
+ @Conditional("hasLayout")
+ public int leftMargin;
+
+ @SWFType(BasicType.UI16)
+ @Conditional("hasLayout")
+ public int rightMargin;
+
+ @SWFType(BasicType.UI16)
+ @Conditional("hasLayout")
+ public int indent;
+
+ @SWFType(BasicType.SI16)
+ @Conditional("hasLayout")
+ public int leading;
+
+ public String variableName;
+
+ @Conditional("hasText")
+ public String initialText;
+
+ @Override
+ public RECT getBounds() {
+ return bounds;
+ }
+
+ @Override
+ public MATRIX getTextMatrix() {
+ MATRIX matrix = new MATRIX();
+ matrix.translateX = bounds.Xmin;
+ matrix.translateY = bounds.Ymin;
+ return matrix;
+ }
+
+ @Override
+ public void setBounds(RECT r) {
+ bounds = r;
+ }
+
+ private String stripTags(String inp) {
+ boolean intag = false;
+ String outp = "";
+ inp = inp.replaceAll("
", "\r\n");
+ for (int i = 0; i < inp.length(); ++i) {
+ if (!intag && inp.charAt(i) == '<') {
+ intag = true;
+ continue;
+ }
+ if (intag && inp.charAt(i) == '>') {
+ intag = false;
+ continue;
+ }
+ if (!intag) {
+ outp += inp.charAt(i);
+ }
+ }
+ return outp;
+ }
+
+ private String entitiesReplace(String s) {
+ s = s.replace("<", "<");
+ s = s.replace(">", ">");
+ s = s.replace("&", "&");
+ s = s.replace(""", "\"");
+ return s;
+ }
+
+ @Override
+ public List getTexts() {
+ String ret = "";
+ if (hasText) {
+ ret = initialText;
+ }
+ if (html) {
+ ret = stripTags(ret);
+ ret = entitiesReplace(ret);
+ }
+ return Arrays.asList(ret);
+ }
+
+ private List getTextWithStyle() {
+ String str = "";
+ TextStyle style = new TextStyle();
+ style.font = swf.getFont(fontId);
+ style.fontHeight = fontHeight;
+ style.fontLeading = leading;
+ if (hasTextColor) {
+ style.textColor = textColor;
+ }
+ if (hasText) {
+ str = initialText;
+ }
+ final List ret = new ArrayList<>();
+ if (html) {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ SAXParser saxParser;
+ final Stack styles = new Stack<>();
+ styles.add(style);
+ try {
+ saxParser = factory.newSAXParser();
+ DefaultHandler handler = new DefaultHandler() {
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ TextStyle style = styles.peek();
+ switch (qName) {
+ case "p":
+ // todo: parse the following attribute:
+ // align
+ 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 color = attributes.getValue("color");
+ if (color != null) {
+ if (color.startsWith("#")) {
+ style.textColor = new RGBA(Color.decode(color));
+ }
+ }
+ String size = attributes.getValue("size");
+ if (size != null && size.length() > 0) {
+ char firstChar = size.charAt(0);
+ if (firstChar != '+' && firstChar != '-') {
+ int fontSize = Integer.parseInt(size);
+ style.fontHeight = (int) Math.round(fontSize * (style.font == null ? 1 : style.font.getDivider()));
+ style.fontLeading = leading;
+ } else {
+ // todo: parse relative sizes
+ }
+ }
+ String face = attributes.getValue("face");
+ {
+ if (face != null && face.length() > 0) {
+ style.fontFace = face;
+ }
+ }
+ // todo: parse the following attributes: letterSpacing, kerning
+ styles.add(style);
+ break;
+ case "br":
+ case "sbr": // what's this?
+ CharacterWithStyle cs = new CharacterWithStyle();
+ cs.character = '\n';
+ cs.style = style;
+ ret.add(cs);
+ break;
+ }
+ //ret = entitiesReplace(ret);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ switch (qName) {
+ case "b":
+ case "i":
+ case "u":
+ case "font":
+ styles.pop();
+ break;
+ case "p":
+ TextStyle style = styles.peek();
+ CharacterWithStyle cs = new CharacterWithStyle();
+ cs.character = '\n';
+ cs.style = style;
+ ret.add(cs);
+ break;
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ String txt = new String(ch, start, length);
+ TextStyle style = styles.peek();
+ addCharacters(ret, txt, style);
+ }
+ };
+ str = " \n"
+ + "]>" + str + "";
+ saxParser.parse(new ByteArrayInputStream(str.getBytes(Utf8Helper.charset)), handler);
+ } catch (ParserConfigurationException | SAXException | IOException ex) {
+ Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ } else {
+ addCharacters(ret, str, style);
+ }
+ return ret;
+ }
+
+ private void addCharacters(List list, String str, TextStyle style) {
+ for (int i = 0; i < str.length(); i++) {
+ char ch = str.charAt(i);
+ CharacterWithStyle cs = new CharacterWithStyle();
+ cs.character = ch;
+ cs.style = style;
+ list.add(cs);
+ }
+ }
+
+ @Override
+ public List getFontIds() {
+ List ret = new ArrayList<>();
+ ret.add(fontId);
+ return ret;
+ }
+
+ @Override
+ public HighlightedText getFormattedText() {
+ HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
+ writer.append("[");
+ String[] alignNames = {"left", "right", "center", "justify"};
+ String alignment;
+ if (align < alignNames.length) {
+ alignment = alignNames[align];
+ } else {
+ alignment = "unknown";
+ }
+ writer.newLine();
+ writer.append("xmin " + bounds.Xmin).newLine();
+ writer.append("ymin " + bounds.Ymin).newLine();
+ writer.append("xmax " + bounds.Xmax).newLine();
+ writer.append("ymax " + bounds.Ymax).newLine();
+ if (wordWrap) {
+ writer.append("wordwrap 1").newLine();
+ }
+ if (multiline) {
+ writer.append("multiline 1").newLine();
+ }
+ if (password) {
+ writer.append("password 1").newLine();
+ }
+ if (readOnly) {
+ writer.append("readonly 1").newLine();
+ }
+ if (autoSize) {
+ writer.append("autosize 1").newLine();
+ }
+ if (noSelect) {
+ writer.append("noselect 1").newLine();
+ }
+ if (border) {
+ writer.append("border 1").newLine();
+ }
+ if (wasStatic) {
+ writer.append("wasstatic 1").newLine();
+ }
+ if (html) {
+ writer.append("html 1").newLine();
+ }
+ if (useOutlines) {
+ writer.append("useoutlines 1").newLine();
+ }
+ if (hasFont) {
+ writer.append("font " + fontId).newLine();
+ writer.append("height " + fontHeight).newLine();
+ }
+ if (hasTextColor) {
+ writer.append("color " + textColor.toHexARGB()).newLine();
+ }
+ if (hasFontClass) {
+ writer.append("fontclass " + fontClass).newLine();
+ }
+ if (hasMaxLength) {
+ writer.append("maxlength " + maxLength).newLine();
+ }
+ writer.append("align " + alignment).newLine();
+ if (hasLayout) {
+ writer.append("leftmargin " + leftMargin).newLine();
+ writer.append("rightmargin " + rightMargin).newLine();
+ writer.append("indent " + indent).newLine();
+ writer.append("leading " + leading).newLine();
+ }
+ if (!variableName.isEmpty()) {
+ writer.append("variablename " + variableName).newLine();
+ }
+ writer.append("]");
+ if (hasText) {
+ String text = initialText.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]");
+ writer.hilightSpecial(text, HighlightSpecialType.TEXT);
+ }
+ return new HighlightedText(writer);
+ }
+
+ @Override
+ public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws TextParseException {
+ try {
+ TextLexer lexer = new TextLexer(new StringReader(formattedText));
+ ParsedSymbol s = null;
+ formattedText = "";
+ RECT bounds = new RECT(this.bounds);
+ boolean wordWrap = false;
+ boolean multiline = false;
+ boolean password = false;
+ boolean readOnly = false;
+ boolean autoSize = false;
+ boolean noSelect = false;
+ boolean border = false;
+ boolean wasStatic = false;
+ boolean html = false;
+ boolean useOutlines = false;
+ int fontId = -1;
+ int fontHeight = -1;
+ String fontClass = null;
+ RGBA textColor = null;
+ int maxLength = -1;
+ int align = -1;
+ int leftMargin = -1;
+ int rightMargin = -1;
+ int indent = -1;
+ int leading = -1;
+ String variableName = null;
+
+ int textIdx = 0;
+ while ((s = lexer.yylex()) != null) {
+ switch (s.type) {
+ case PARAMETER:
+ String paramName = (String) s.values[0];
+ String paramValue = (String) s.values[1];
+ switch (paramName) {
+ case "xmin":
+ try {
+ bounds.Xmin = Integer.parseInt(paramValue);
+ } catch (NumberFormatException nfe) {
+ throw new TextParseException("Invalid xmin value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "ymin":
+ try {
+ bounds.Ymin = Integer.parseInt(paramValue);
+ } catch (NumberFormatException nfe) {
+ throw new TextParseException("Invalid ymin value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "xmax":
+ try {
+ bounds.Xmax = Integer.parseInt(paramValue);
+ } catch (NumberFormatException nfe) {
+ throw new TextParseException("Invalid xmax value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "ymax":
+ try {
+ bounds.Ymax = Integer.parseInt(paramValue);
+ } catch (NumberFormatException nfe) {
+ throw new TextParseException("Invalid ymax value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "wordwrap":
+ if (paramValue.equals("1")) {
+ wordWrap = true;
+ }
+ break;
+ case "multiline":
+ if (paramValue.equals("1")) {
+ multiline = true;
+ }
+ break;
+ case "password":
+ if (paramValue.equals("1")) {
+ password = true;
+ }
+ break;
+ case "readonly":
+ if (paramValue.equals("1")) {
+ readOnly = true;
+ }
+ break;
+ case "autosize":
+ if (paramValue.equals("1")) {
+ autoSize = true;
+ }
+ break;
+ case "noselect":
+ if (paramValue.equals("1")) {
+ noSelect = true;
+ }
+ break;
+ case "border":
+ if (paramValue.equals("1")) {
+ border = true;
+ }
+ break;
+ case "wasstatic":
+ if (paramValue.equals("1")) {
+ wasStatic = true;
+ }
+ break;
+ case "html":
+ if (paramValue.equals("1")) {
+ html = true;
+ }
+ break;
+ case "useoutlines":
+ if (paramValue.equals("1")) {
+ useOutlines = true;
+ }
+ break;
+ case "font":
+ try {
+ fontId = Integer.parseInt(paramValue);
+
+ FontTag ft = swf.getFont(fontId);
+ if (ft == null) {
+ throw new TextParseException("Font not found.", lexer.yyline());
+ }
+ } catch (NumberFormatException ne) {
+ throw new TextParseException("Invalid font value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "fontclass":
+ fontClass = paramValue;
+ break;
+ case "height":
+ try {
+ fontHeight = Integer.parseInt(paramValue);
+ } catch (NumberFormatException ne) {
+ throw new TextParseException("Invalid height value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "color":
+ Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue);
+ if (m.matches()) {
+ textColor = new RGBA(Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16), Integer.parseInt(m.group(4), 16), Integer.parseInt(m.group(1), 16));
+ } else {
+ throw new TextParseException("Invalid color. Valid format is #aarrggbb. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "maxlength":
+ try {
+ maxLength = Integer.parseInt(paramValue);
+ } catch (NumberFormatException ne) {
+ throw new TextParseException("Invalid maxLength value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "align":
+ switch (paramValue) {
+ case "left":
+ align = 0;
+ break;
+ case "right":
+ align = 1;
+ break;
+ case "center":
+ align = 2;
+ break;
+ case "justify":
+ align = 3;
+ break;
+ default:
+ throw new TextParseException("Invalid align value. Expected one of: left,right,center or justify. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "leftmargin":
+ try {
+ leftMargin = Integer.parseInt(paramValue);
+ } catch (NumberFormatException ne) {
+ throw new TextParseException("Invalid leftmargin value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "rightmargin":
+ try {
+ rightMargin = Integer.parseInt(paramValue);
+ } catch (NumberFormatException ne) {
+ throw new TextParseException("Invalid rightmargin value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "indent":
+ try {
+ indent = Integer.parseInt(paramValue);
+ } catch (NumberFormatException ne) {
+ throw new TextParseException("Invalid indent value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "leading":
+ try {
+ leading = Integer.parseInt(paramValue);
+ } catch (NumberFormatException ne) {
+ throw new TextParseException("Invalid leading value. Number expected. Found: " + paramValue, lexer.yyline());
+ }
+ break;
+ case "variablename":
+ variableName = paramValue;
+ break;
+ default:
+ throw new TextParseException("Unrecognized parameter name: " + paramName, lexer.yyline());
+ }
+ break;
+ case TEXT:
+ String s2 = (String) s.values[0];
+ if (s2 == null) {
+ s2 = "";
+ }
+
+ formattedText += (texts == null || textIdx >= texts.length) ? s2 : texts[textIdx++];
+ formattedText = formattedText.replace("\r\n", "\r");
+ break;
+ }
+ }
+
+ setModified(true);
+ this.bounds = bounds;
+ if (formattedText.length() > 0) {
+ initialText = formattedText;
+ this.hasText = true;
+ } else {
+ this.hasText = false;
+ }
+ this.wordWrap = wordWrap;
+ this.multiline = multiline;
+ this.password = password;
+ this.readOnly = readOnly;
+ this.noSelect = noSelect;
+ this.border = border;
+ this.wasStatic = wasStatic;
+ this.html = html;
+ this.useOutlines = useOutlines;
+ if (textColor != null) {
+ hasTextColor = true;
+ this.textColor = textColor;
+ }
+ if (maxLength > -1) {
+ this.maxLength = maxLength;
+ hasMaxLength = true;
+ }
+ if (fontId > -1) {
+ this.fontId = fontId;
+ }
+ if (fontHeight > -1) {
+ this.fontHeight = fontHeight;
+ }
+ if (fontClass != null) {
+ this.fontClass = fontClass;
+ hasFontClass = true;
+ }
+ this.autoSize = autoSize;
+ this.align = align;
+ if ((leftMargin > -1)
+ || (rightMargin > -1)
+ || (indent > -1)
+ || (leading > -1)) {
+ this.leftMargin = leftMargin;
+ this.rightMargin = rightMargin;
+ this.indent = indent;
+ this.leading = leading;
+ hasLayout = true;
+ }
+ if (variableName == null) {
+ variableName = "";
+ }
+ this.variableName = variableName;
+
+ } catch (IOException ex) {
+ Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex);
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void updateTextBounds() {
+ }
+
+ @Override
+ public boolean alignText(TextAlign textAlign) {
+ return true;
+ }
+
+ @Override
+ public boolean translateText(int diff) {
+ return true;
+ }
+
+ @Override
+ public RECT getRect(Set added) {
+ return bounds;
+ }
+
+ @Override
+ public int getCharacterId() {
+ return characterID;
+ }
+
+ @Override
+ public void setCharacterId(int characterId) {
+ this.characterID = characterId;
+ }
+
+ /**
+ * Gets data bytes
+ *
+ * @return Bytes of data
+ */
+ @Override
+ public byte[] getData() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStream os = baos;
+ SWFOutputStream sos = new SWFOutputStream(os, getVersion());
+ try {
+ sos.writeUI16(characterID);
+ sos.writeRECT(bounds);
+ sos.writeUB(1, hasText ? 1 : 0);
+ sos.writeUB(1, wordWrap ? 1 : 0);
+ sos.writeUB(1, multiline ? 1 : 0);
+ sos.writeUB(1, password ? 1 : 0);
+ sos.writeUB(1, readOnly ? 1 : 0);
+ sos.writeUB(1, hasTextColor ? 1 : 0);
+ sos.writeUB(1, hasMaxLength ? 1 : 0);
+ sos.writeUB(1, hasFont ? 1 : 0);
+ sos.writeUB(1, hasFontClass ? 1 : 0);
+ sos.writeUB(1, autoSize ? 1 : 0);
+ sos.writeUB(1, hasLayout ? 1 : 0);
+ sos.writeUB(1, noSelect ? 1 : 0);
+ sos.writeUB(1, border ? 1 : 0);
+ sos.writeUB(1, wasStatic ? 1 : 0);
+ sos.writeUB(1, html ? 1 : 0);
+ sos.writeUB(1, useOutlines ? 1 : 0);
+ if (hasFont) {
+ sos.writeUI16(fontId);
+ }
+ if (hasFontClass) {
+ sos.writeString(fontClass);
+ }
+ if (hasFont) {
+ sos.writeUI16(fontHeight);
+ }
+ if (hasTextColor) {
+ sos.writeRGBA(textColor);
+ }
+ if (hasMaxLength) {
+ sos.writeUI16(maxLength);
+ }
+ if (hasLayout) {
+ sos.writeUI8(align);
+ sos.writeUI16(leftMargin);
+ sos.writeUI16(rightMargin);
+ sos.writeUI16(indent);
+ sos.writeSI16(leading);
+ }
+ sos.writeString(variableName);
+ if (hasText) {
+ sos.writeString(initialText);
+ }
+
+ } catch (IOException e) {
+ throw new Error("This should never happen.", e);
+ }
+ return baos.toByteArray();
+ }
+
+ /**
+ * Constructor
+ *
+ * @param swf
+ */
+ public DefineEditTextTag(SWF swf) {
+ super(swf, ID, NAME, null);
+ characterID = swf.getNextCharacterId();
+ bounds = new RECT();
+ variableName = "";
+ }
+
+ /**
+ * Constructor
+ *
+ * @param sis
+ * @param data
+ * @throws IOException
+ */
+ public DefineEditTextTag(SWFInputStream sis, ByteArrayRange data) throws IOException {
+ super(sis.getSwf(), ID, NAME, data);
+ readData(sis, data, 0, false, false, false);
+ }
+
+ @Override
+ public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
+ characterID = sis.readUI16("characterID");
+ bounds = sis.readRECT("bounds");
+ hasText = sis.readUB(1, "hasText") == 1;
+ wordWrap = sis.readUB(1, "wordWrap") == 1;
+ multiline = sis.readUB(1, "multiline") == 1;
+ password = sis.readUB(1, "password") == 1;
+ readOnly = sis.readUB(1, "readOnly") == 1;
+ hasTextColor = sis.readUB(1, "hasTextColor") == 1;
+ hasMaxLength = sis.readUB(1, "hasMaxLength") == 1;
+ hasFont = sis.readUB(1, "hasFont") == 1;
+ hasFontClass = sis.readUB(1, "hasFontClass") == 1;
+ autoSize = sis.readUB(1, "autoSize") == 1;
+ hasLayout = sis.readUB(1, "hasLayout") == 1;
+ noSelect = sis.readUB(1, "noSelect") == 1;
+ border = sis.readUB(1, "border") == 1;
+ wasStatic = sis.readUB(1, "wasStatic") == 1;
+ html = sis.readUB(1, "html") == 1;
+ useOutlines = sis.readUB(1, "useOutlines") == 1;
+ if (hasFont) {
+ fontId = sis.readUI16("fontId");
+ }
+ if (hasFontClass) {
+ fontClass = sis.readString("fontClass");
+ }
+ if (hasFont) {
+ fontHeight = sis.readUI16("fontHeight");
+ }
+ if (hasTextColor) {
+ textColor = sis.readRGBA("textColor");
+ }
+ if (hasMaxLength) {
+ maxLength = sis.readUI16("maxLength");
+ }
+ if (hasLayout) {
+ align = sis.readUI8("align"); //0 left, 1 right, 2 center, 3 justify
+ leftMargin = sis.readUI16("leftMargin");
+ rightMargin = sis.readUI16("rightMargin");
+ indent = sis.readUI16("indent");
+ leading = sis.readSI16("leading");
+ }
+ variableName = sis.readString("variableName");
+ if (hasText) {
+ initialText = sis.readString("initialText");
+ }
+
+ }
+
+ @Override
+ public void getNeededCharacters(Set needed) {
+ if (hasFont) {
+ needed.add(fontId);
+ }
+ }
+
+ @Override
+ public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
+ if (fontId == oldCharacterId) {
+ fontId = newCharacterId;
+ setModified(true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean removeCharacter(int characterId) {
+ if (fontId == characterId) {
+ hasFont = false;
+ fontId = 0;
+ setModified(true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int getUsedParameters() {
+ return 0;
+ }
+
+ @Override
+ public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
+ render(false, image, transformation, colorTransform);
+ }
+
+ @Override
+ public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
+ // todo: implement
+ }
+
+ @Override
+ public String toHtmlCanvas(double unitDivisor) {
+ return render(true, null, new Matrix(), new ColorTransform());
+ }
+
+ private String render(boolean canvas, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
+ if (border) {
+ // border is always black, fill color is always white?
+ RGB borderColor = new RGBA(Color.black);
+ RGB fillColor = new RGBA(Color.white);
+ if (!canvas) {
+ drawBorder(swf, image, borderColor, fillColor, getRect(), getTextMatrix(), transformation, colorTransform);
+ } else {
+ // TODO: draw border
+ }
+ }
+ if (hasText) {
+ DynamicTextModel textModel = new DynamicTextModel();
+ List txt = getTextWithStyle();
+ TextStyle lastStyle = null;
+ char prevChar = 0;
+ boolean lastWasWhiteSpace = false;
+ for (int i = 0; i < txt.size(); i++) {
+ CharacterWithStyle cs = txt.get(i);
+ char c = cs.character;
+ if (c != '\r' && c != '\n') {
+ // create new SameStyleTextRecord for all words and all diffrent style text parts
+ if (lastWasWhiteSpace && !Character.isWhitespace(c)) {
+ textModel.newWord();
+ lastWasWhiteSpace = false;
+ }
+ if (cs.style != lastStyle) {
+ lastStyle = cs.style;
+ textModel.style = lastStyle;
+ textModel.newRecord();
+ }
+ Character nextChar = null;
+ if (i + 1 < txt.size()) {
+ nextChar = txt.get(i + 1).character;
+ }
+ int advance;
+ FontTag font = lastStyle.font;
+ DynamicTextGlyphEntry ge = new DynamicTextGlyphEntry();
+ ge.fontFace = lastStyle.fontFace;
+ ge.fontStyle = (lastStyle.bold ? Font.BOLD : 0) | (lastStyle.italic ? Font.ITALIC : 0);
+ ge.character = c;
+ ge.glyphIndex = font == null ? -1 : font.charToGlyph(c);
+ if (font != null && font.hasLayout()) {
+ int kerningAdjustment = 0;
+ if (nextChar != null) {
+ kerningAdjustment = font.getCharKerningAdjustment(c, nextChar);
+ kerningAdjustment /= font.getDivider();
+ }
+ advance = (int) Math.round(Math.round((double) lastStyle.fontHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0)));
+ } else {
+ String fontName = lastStyle.fontFace != null ? lastStyle.fontFace : FontTag.defaultFontName;
+ int fontStyle = font == null ? ge.fontStyle : font.getFontStyle();
+ advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, fontStyle, (int) (lastStyle.fontHeight / SWF.unitDivisor), c, nextChar));
+ }
+ ge.glyphAdvance = advance;
+ textModel.addGlyph(c, ge);
+ if (Character.isWhitespace(c)) {
+ lastWasWhiteSpace = true;
+ }
+ } else {
+ if (multiline) {
+ textModel.newParagraph();
+ }
+ }
+ prevChar = c;
+ }
+
+ textModel.calculateTextWidths();
+ List> lines;
+ if (multiline && wordWrap) {
+ lines = new ArrayList<>();
+ for (Paragraph paragraph : textModel.paragraphs) {
+ List line = new ArrayList<>();
+ int lineLength = 0;
+ for (Word word : paragraph.words) {
+ if (lineLength + word.width <= bounds.getWidth()) {
+ line.addAll(word.records);
+ lineLength += word.width;
+ } else {
+ lines.add(line);
+ line = new ArrayList<>();
+ line.addAll(word.records);
+ lineLength = 0;
+ }
+ }
+ if (!line.isEmpty()) {
+ lines.add(line);
+ }
+ }
+ } else {
+ lines = new ArrayList<>();
+ for (Paragraph paragraph : textModel.paragraphs) {
+ List line = new ArrayList<>();
+ for (Word word : paragraph.words) {
+ for (SameStyleTextRecord tr : word.records) {
+ line.add(tr);
+ }
+ }
+ lines.add(line);
+ }
+ }
+
+ // remove spaces after last word
+ for (List line : lines) {
+ boolean removed = true;
+ while (removed) {
+ removed = false;
+ while (line.size() > 0 && line.get(line.size() - 1).glyphEntries.isEmpty()) {
+ line.remove(line.size() - 1);
+ removed = true;
+ }
+ if (line.size() > 0) {
+ SameStyleTextRecord lastRecord = line.get(line.size() - 1);
+ while (lastRecord.glyphEntries.size() > 0
+ && Character.isWhitespace(lastRecord.glyphEntries.get(lastRecord.glyphEntries.size() - 1).character)) {
+ lastRecord.glyphEntries.remove(lastRecord.glyphEntries.size() - 1);
+ removed = true;
+ }
+ }
+ }
+ }
+
+ textModel.calculateTextWidths();
+
+ List allTextRecords = new ArrayList<>();
+ int lastHeight = 0;
+ int yOffset = 0;
+ for (List line : lines) {
+ int width = 0;
+ int currentOffset = 0;
+ if (line.isEmpty()) {
+ currentOffset = lastHeight;
+ } else {
+ for (SameStyleTextRecord tr : line) {
+ width += tr.width;
+ int lineHeight = tr.style.fontHeight + tr.style.fontLeading;
+ lastHeight = lineHeight;
+ if (lineHeight > currentOffset) {
+ currentOffset = lineHeight;
+ }
+ }
+ }
+ yOffset += currentOffset;
+ int alignOffset = 0;
+ switch (align) {
+ case 0: // left
+ alignOffset = 0;
+ break;
+ case 1: // right
+ alignOffset = bounds.getWidth() - width;
+ break;
+ case 2: // center
+ alignOffset = (bounds.getWidth() - width) / 2;
+ break;
+ case 3: // justify
+ // todo;
+ break;
+ }
+ for (SameStyleTextRecord tr : line) {
+ tr.xOffset = alignOffset;
+ alignOffset += tr.width;
+ }
+ for (SameStyleTextRecord tr : line) {
+ TEXTRECORD tr2 = new TEXTRECORD();
+ tr2.styleFlagsHasFont = fontId != 0;
+ tr2.fontId = fontId;
+ tr2.textHeight = tr.style.fontHeight;
+ if (tr.style.textColor != null) {
+ tr2.styleFlagsHasColor = true;
+ tr2.textColorA = tr.style.textColor;
+ }
+ // always add xOffset, because no xOffset and 0 xOffset is diffrent in text rendering
+ tr2.styleFlagsHasXOffset = true;
+ tr2.xOffset = tr.xOffset;
+ if (yOffset != 0) {
+ tr2.styleFlagsHasYOffset = true;
+ tr2.yOffset = yOffset;
+ }
+ tr2.glyphEntries = new ArrayList<>(tr.glyphEntries.size());
+ for (GlyphCharacter ge : tr.glyphEntries) {
+ tr2.glyphEntries.add(ge.glyphEntry);
+ }
+ allTextRecords.add(tr2);
+ }
+ }
+
+ if (canvas) {
+ return staticTextToHtmlCanvas(1, swf, allTextRecords, 2, getBounds(), getTextMatrix(), colorTransform);
+ } else {
+ staticTextToImage(swf, allTextRecords, 2, image, getTextMatrix(), transformation, colorTransform);
+ }
+ }
+
+ return "";
+ }
+
+ @Override
+ public ExportRectangle calculateTextBounds() {
+ return null;
+ }
+
+ @Override
+ public int getNumFrames() {
+ return 1;
+ }
+
+ @Override
+ public boolean isSingleFrame() {
+ return true;
+ }
+}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java
index 4409306d2..8463c3803 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java
@@ -32,6 +32,7 @@ import com.jpexs.decompiler.flash.tags.text.JustifyAlignGlyphEntry;
import com.jpexs.decompiler.flash.tags.text.TextAlign;
import com.jpexs.decompiler.flash.tags.text.TextParseException;
import com.jpexs.decompiler.flash.types.ColorTransform;
+import com.jpexs.decompiler.flash.types.DynamicTextGlyphEntry;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
@@ -424,7 +425,8 @@ public abstract class TextTag extends CharacterTag implements DrawableTag {
y = rec.yOffset;
}
- double rat = textHeight / 1024.0 / (font == null ? 1 : font.getDivider());
+ double divider = font == null ? 1 : font.getDivider();
+ double rat = textHeight / 1024.0 / divider;
Color textColor2 = new Color(textColor, true);
for (GLYPHENTRY entry : rec.glyphEntries) {
@@ -433,9 +435,18 @@ public abstract class TextTag extends CharacterTag implements DrawableTag {
Matrix matTr = Matrix.getTranslateInstance(x, y);
mat = mat.concatenate(matTr);
mat = mat.concatenate(Matrix.getScaleInstance(rat));
+ SHAPE shape = null;
if (entry.glyphIndex != -1 && glyphs != null) {
// shapeNum: 1
- SHAPE shape = glyphs.get(entry.glyphIndex);
+ shape = glyphs.get(entry.glyphIndex);
+ } else if (entry instanceof DynamicTextGlyphEntry) {
+ DynamicTextGlyphEntry dynamicEntry = (DynamicTextGlyphEntry) entry;
+ if (dynamicEntry.fontFace != null) {
+ shape = SHAPERECORD.fontCharacterToSHAPE(new Font(dynamicEntry.fontFace, dynamicEntry.fontStyle, 12), (int) Math.round(divider * 1024), dynamicEntry.character);
+ }
+ }
+
+ if (shape != null) {
BitmapExporter.export(swf, shape, textColor2, image, mat, colorTransform);
if (SHAPERECORD.DRAW_BOUNDING_BOX) {
RGB borderColor = new RGBA(Color.black);
@@ -444,8 +455,9 @@ public abstract class TextTag extends CharacterTag implements DrawableTag {
mat = Matrix.getTranslateInstance(bounds.Xmin, bounds.Ymin).preConcatenate(mat);
TextTag.drawBorder(swf, image, borderColor, fillColor, bounds, new MATRIX(), mat, colorTransform);
}
- x += entry.glyphAdvance;
}
+
+ x += entry.glyphAdvance;
}
}
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/dynamictext/TextStyle.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/dynamictext/TextStyle.java
index ae96988a2..781ae255b 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/dynamictext/TextStyle.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/dynamictext/TextStyle.java
@@ -1,18 +1,19 @@
/*
* Copyright (C) 2010-2015 JPEXS, All rights reserved.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
- * License along with this library.
*/
+ * License along with this library.
+ */
package com.jpexs.decompiler.flash.tags.dynamictext;
import com.jpexs.decompiler.flash.tags.base.FontTag;
@@ -27,6 +28,8 @@ public final class TextStyle implements Cloneable {
public FontTag font;
+ public String fontFace;
+
public int fontHeight;
public int fontLeading;
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/DynamicTextGlyphEntry.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/DynamicTextGlyphEntry.java
new file mode 100644
index 000000000..5720d3ce2
--- /dev/null
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/DynamicTextGlyphEntry.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010-2015 JPEXS, All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.
+ */
+package com.jpexs.decompiler.flash.types;
+
+/**
+ *
+ * @author JPEXS
+ */
+public class DynamicTextGlyphEntry extends GLYPHENTRY {
+
+ public String fontFace;
+
+ public int fontStyle;
+
+ public char character;
+}