Base class for DefineXTags, getting the images, fonts, text from the characterMap instead of iterating over all tags

This commit is contained in:
honfika@gmail.com
2015-05-14 13:32:49 +02:00
parent 15744df8fd
commit 64c9a7e544
23 changed files with 1174 additions and 1694 deletions

View File

@@ -390,6 +390,32 @@ public final class SWF implements SWFContainerItem, Timelined {
return null;
}
public ImageTag getImage(int imageId) {
CharacterTag characterTag = getCharacters().get(imageId);
if (characterTag instanceof ImageTag) {
return (ImageTag) characterTag;
}
if (characterTag != null) {
logger.log(Level.SEVERE, "CharacterTag should be an ImageTag. characterId: {0}", imageId);
}
return null;
}
public TextTag getText(int textId) {
CharacterTag characterTag = getCharacters().get(textId);
if (characterTag instanceof TextTag) {
return (TextTag) characterTag;
}
if (characterTag != null) {
logger.log(Level.SEVERE, "CharacterTag should be a TextTag. characterId: {0}", textId);
}
return null;
}
public List<ABCContainerTag> getAbcList() {
if (abcList == null) {
synchronized (this) {

View File

@@ -2889,14 +2889,14 @@ public class SWFInputStream implements AutoCloseable {
/**
* Reads one TEXTRECORD value from the stream
*
* @param inDefineText2
* @param defineTextNum
* @param glyphBits
* @param advanceBits
* @param name
* @return TEXTRECORD value
* @throws IOException
*/
public TEXTRECORD readTEXTRECORD(boolean inDefineText2, int glyphBits, int advanceBits, String name) throws IOException {
public TEXTRECORD readTEXTRECORD(int defineTextNum, int glyphBits, int advanceBits, String name) throws IOException {
TEXTRECORD ret = new TEXTRECORD();
newDumpLevel(name, "TEXTRECORD");
int first = (int) readUB(1, "first"); // always 1
@@ -2913,7 +2913,7 @@ public class SWFInputStream implements AutoCloseable {
ret.fontId = readUI16("fontId");
}
if (ret.styleFlagsHasColor) {
if (inDefineText2) {
if (defineTextNum == 2) {
ret.textColorA = readRGBA("textColorA");
} else {
ret.textColor = readRGB("textColor");

View File

@@ -1484,12 +1484,12 @@ public class SWFOutputStream extends OutputStream {
* Writes TEXTRECORD value to the stream
*
* @param value TEXTRECORD value
* @param inDefineText2
* @param defineTextNum
* @param glyphBits
* @param advanceBits
* @throws IOException
*/
public void writeTEXTRECORD(TEXTRECORD value, boolean inDefineText2, int glyphBits, int advanceBits) throws IOException {
public void writeTEXTRECORD(TEXTRECORD value, int defineTextNum, int glyphBits, int advanceBits) throws IOException {
writeUB(1, 1);
writeUB(3, 0);
writeUB(1, value.styleFlagsHasFont ? 1 : 0);
@@ -1500,7 +1500,7 @@ public class SWFOutputStream extends OutputStream {
writeUI16(value.fontId);
}
if (value.styleFlagsHasColor) {
if (inDefineText2) {
if (defineTextNum == 2) {
writeRGBA(value.textColorA);
} else {
writeRGB(value.textColor);

View File

@@ -21,7 +21,6 @@ import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.Point;
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
@@ -221,22 +220,12 @@ public class CanvasMorphShapeExporter extends MorphShapeExporterBase {
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, Matrix matrixEnd, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = null;
for (Tag t : swf.tags) {
if (t instanceof ImageTag) {
ImageTag i = (ImageTag) t;
if (i.getCharacterId() == bitmapId) {
image = i;
SerializableImage img = i.getImage();
fillWidth = img.getWidth();
fillHeight = img.getHeight();
break;
}
}
}
ImageTag image = swf.getImage(bitmapId);
if (image != null) {
SerializableImage img = image.getImage();
if (img != null) {
fillWidth = img.getWidth();
fillHeight = img.getHeight();
colorTransform.apply(img);
if (matrix != null) {
fillMatrix = matrix;

View File

@@ -20,7 +20,6 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
@@ -107,16 +106,7 @@ public class SVGMorphShapeExporter extends DefaultSVGMorphShapeExporter {
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, Matrix matrixEnd, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = null;
for (Tag t : swf.tags) {
if (t instanceof ImageTag) {
ImageTag i = (ImageTag) t;
if (i.getCharacterId() == bitmapId) {
image = i;
break;
}
}
}
ImageTag image = swf.getImage(bitmapId);
if (image != null) {
SerializableImage img = image.getImage();
if (img != null) {

View File

@@ -19,7 +19,6 @@ package com.jpexs.decompiler.flash.exporters.shape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
@@ -308,10 +307,9 @@ public class BitmapExporter extends ShapeExporterBase {
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
CharacterTag character = swf.getCharacter(bitmapId);
ImageTag image = character instanceof ImageTag ? (ImageTag) character : null;
if (image != null) {
SerializableImage img = image.getImage();
ImageTag imageTag = swf.getImage(bitmapId);
if (imageTag != null) {
SerializableImage img = imageTag.getImage();
if (img != null) {
img = colorTransform.apply(img);
fillPaint = new TexturePaint(img.getBufferedImage(), new java.awt.Rectangle(img.getWidth(), img.getHeight()));

View File

@@ -19,7 +19,6 @@ package com.jpexs.decompiler.flash.exporters.shape;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.Point;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
@@ -252,22 +251,12 @@ public class CanvasShapeExporter extends ShapeExporterBase {
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = null;
for (Tag t : swf.tags) {
if (t instanceof ImageTag) {
ImageTag i = (ImageTag) t;
if (i.getCharacterId() == bitmapId) {
image = i;
SerializableImage im = i.getImage();
fillWidth = im.getWidth();
fillHeight = im.getHeight();
break;
}
}
}
ImageTag image = swf.getImage(bitmapId);
if (image != null) {
SerializableImage img = image.getImage();
if (img != null) {
fillWidth = img.getWidth();
fillHeight = img.getHeight();
colorTransform.apply(img);
if (matrix != null) {
fillMatrix = matrix;

View File

@@ -20,7 +20,6 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.helpers.ImageHelper;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
@@ -104,16 +103,7 @@ public class SVGShapeExporter extends DefaultSVGShapeExporter {
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
finalizePath();
ImageTag image = null;
for (Tag t : swf.tags) {
if (t instanceof ImageTag) {
ImageTag i = (ImageTag) t;
if (i.getCharacterId() == bitmapId) {
image = i;
break;
}
}
}
ImageTag image = swf.getImage(bitmapId);
if (image != null) {
SerializableImage img = image.getImage();
if (img != null) {

View File

@@ -19,7 +19,6 @@ package com.jpexs.decompiler.flash.importers;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler;
import com.jpexs.decompiler.flash.tags.base.TextImportErrorHandler;
import com.jpexs.decompiler.flash.tags.base.TextTag;
@@ -72,17 +71,12 @@ public class TextImporter {
Map<Integer, String[]> records = splitTextRecords(texts);
if (records != null) {
for (int characterId : records.keySet()) {
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (textTag.getCharacterId() == characterId) {
String[] currentRecords = records.get(characterId);
String text = textTag.getFormattedText().text;
if (!saveText(textTag, text, currentRecords)) {
return;
}
break;
}
TextTag textTag = swf.getText(characterId);
if (textTag != null) {
String[] currentRecords = records.get(characterId);
String text = textTag.getFormattedText().text;
if (!saveText(textTag, text, currentRecords)) {
return;
}
}
}
@@ -94,16 +88,11 @@ public class TextImporter {
Map<Integer, String[]> records = splitTextRecords(texts);
if (records != null) {
for (int characterId : records.keySet()) {
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (textTag.getCharacterId() == characterId) {
String[] currentRecords = records.get(characterId);
if (!saveText(textTag, currentRecords[0], null)) {
return;
}
break;
}
TextTag textTag = swf.getText(characterId);
if (textTag != null) {
String[] currentRecords = records.get(characterId);
if (!saveText(textTag, currentRecords[0], null)) {
return;
}
}
}
@@ -134,28 +123,18 @@ public class TextImporter {
boolean formatted = !texts.contains(recordSeparator) && texts.startsWith("[" + Helper.newLine);
if (!formatted) {
String[] records = texts.split(recordSeparator);
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (textTag.getCharacterId() == characterId) {
String text = textTag.getFormattedText().text;
if (!saveText(textTag, text, records)) {
return;
}
break;
}
TextTag textTag = swf.getText(characterId);
if (textTag != null) {
String text = textTag.getFormattedText().text;
if (!saveText(textTag, text, records)) {
return;
}
}
} else {
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (textTag.getCharacterId() == characterId) {
if (!saveText(textTag, texts, null)) {
return;
}
break;
}
TextTag textTag = swf.getText(characterId);
if (textTag != null) {
if (!saveText(textTag, texts, null)) {
return;
}
}
}

View File

@@ -228,7 +228,7 @@ public class DefineEditTextTag extends TextTag {
private List<CharacterWithStyle> getTextWithStyle() {
String str = "";
TextStyle style = new TextStyle();
style.font = getFontTag();
style.font = swf.getFont(fontId);
style.fontHeight = fontHeight;
style.fontLeading = leading;
if (hasTextColor) {
@@ -900,11 +900,26 @@ public class DefineEditTextTag extends TextTag {
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?
@@ -1098,28 +1113,11 @@ public class DefineEditTextTag extends TextTag {
return "";
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public ExportRectangle calculateTextBounds() {
return null;
}
private FontTag getFontTag() {
FontTag font = null;
for (Tag tag : swf.tags) {
if (tag instanceof FontTag) {
if (((FontTag) tag).getFontId() == fontId) {
font = (FontTag) tag;
}
}
}
return font;
}
@Override
public int getNumFrames() {
return 1;
@@ -1130,11 +1128,6 @@ public class DefineEditTextTag extends TextTag {
return true;
}
@Override
public String toHtmlCanvas(double unitDivisor) {
return render(true, null, new Matrix(), new ColorTransform());
}
@Override
public void setCharacterId(int characterId) {
this.characterID = characterId;

View File

@@ -377,6 +377,11 @@ public class DefineMorphShape2Tag extends MorphShapeTag {
return shape;
}
@Override
public int getUsedParameters() {
return PARAMETER_RATIO;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
SHAPEWITHSTYLE shape = getShapeAtRatio(ratio);

View File

@@ -353,6 +353,11 @@ public class DefineMorphShapeTag extends MorphShapeTag {
return shape;
}
@Override
public int getUsedParameters() {
return PARAMETER_RATIO;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
SHAPEWITHSTYLE shape = getShapeAtRatio(ratio);

View File

@@ -321,6 +321,11 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli
return modified;
}
@Override
public int getUsedParameters() {
return PARAMETER_FRAME | PARAMETER_TIME | PARAMETER_RATIO; // inner tags can contain morphshapes, too
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
SWF.frameToImage(getTimeline(), frame, time, renderContext, image, transformation, colorTransform);

View File

@@ -16,519 +16,24 @@
*/
package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.AppResources;
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.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.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGBA;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.SerializableImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
*
* @author JPEXS
*/
public class DefineText2Tag extends TextTag {
@SWFType(BasicType.UI16)
public int characterID;
private int glyphBits;
private int advanceBits;
public RECT textBounds;
public MATRIX textMatrix;
public List<TEXTRECORD> textRecords;
public class DefineText2Tag extends StaticTextTag {
public static final int ID = 33;
@Override
public RECT getBounds() {
return textBounds;
}
@Override
public MATRIX getTextMatrix() {
return textMatrix;
}
@Override
public void setBounds(RECT r) {
textBounds = r;
}
@Override
public List<String> getTexts() {
FontTag fnt = null;
List<String> ret = new ArrayList<>();
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont) {
for (Tag t : swf.tags) {
if (t instanceof FontTag) {
if (((FontTag) t).getFontId() == rec.fontId) {
fnt = ((FontTag) t);
break;
}
}
}
}
if (rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) {
/*if (!ret.isEmpty()) {
ret += "\r\n";
}*/
}
if (fnt == null) {
ret.add(AppResources.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId)));
} else {
ret.add(rec.getText(fnt));
}
}
return ret;
}
@Override
public List<Integer> getFontIds() {
List<Integer> ret = new ArrayList<>();
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont) {
ret.add(rec.fontId);
}
}
return ret;
}
@Override
public HighlightedText getFormattedText() {
FontTag fnt = null;
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
writer.append("[").newLine();
writer.append("xmin " + textBounds.Xmin).newLine();
writer.append("ymin " + textBounds.Ymin).newLine();
writer.append("xmax " + textBounds.Xmax).newLine();
writer.append("ymax " + textBounds.Ymax).newLine();
if (textMatrix.translateX != 0) {
writer.append("translatex " + textMatrix.translateX).newLine();
}
if (textMatrix.translateY != 0) {
writer.append("translatey " + textMatrix.translateY).newLine();
}
if (textMatrix.hasScale) {
writer.append("scalex " + textMatrix.scaleX).newLine();
writer.append("scaley " + textMatrix.scaleY).newLine();
}
if (textMatrix.hasRotate) {
writer.append("rotateskew0 " + textMatrix.rotateSkew0).newLine();
writer.append("rotateskew1 " + textMatrix.rotateSkew1).newLine();
}
writer.append("]");
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont || rec.styleFlagsHasColor || rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) {
writer.append("[").newLine();
if (rec.styleFlagsHasFont) {
for (Tag t : swf.tags) {
if (t instanceof FontTag) {
if (((FontTag) t).getFontId() == rec.fontId) {
fnt = ((FontTag) t);
break;
}
}
}
writer.append("font " + rec.fontId).newLine();
writer.append("height " + rec.textHeight).newLine();
}
if (rec.styleFlagsHasColor) {
writer.append("color " + rec.textColorA.toHexARGB()).newLine();
}
if (rec.styleFlagsHasXOffset) {
writer.append("x " + rec.xOffset).newLine();
}
if (rec.styleFlagsHasYOffset) {
writer.append("y " + rec.yOffset).newLine();
}
writer.append("]");
}
if (fnt == null) {
writer.append(AppResources.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId)));
} else {
writer.hilightSpecial(Helper.escapeActionScriptString(rec.getText(fnt)).replace("[", "\\[").replace("]", "\\]"), 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;
List<TEXTRECORD> textRecords = new ArrayList<>();
RGBA colorA = null;
int fontId = -1;
int textHeight = -1;
FontTag font = null;
String fontName = null;
Integer x = null;
Integer y = null;
int currentX = 0;
int currentY = 0;
int maxX = Integer.MIN_VALUE;
int minX = Integer.MAX_VALUE;
MATRIX textMatrix = new MATRIX();
textMatrix.hasRotate = false;
textMatrix.hasScale = false;
RECT textBounds = new RECT();
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 "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()) {
colorA = 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 "font":
try {
fontId = Integer.parseInt(paramValue);
FontTag ft = swf.getFont(fontId);
if (ft == null) {
throw new TextParseException("Font not found.", lexer.yyline());
}
font = (FontTag) ft;
fontName = font.getSystemFontName();
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid font id - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "height":
try {
textHeight = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid font height - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "x":
try {
x = Integer.parseInt(paramValue);
currentX = x;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid x position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "y":
try {
y = Integer.parseInt(paramValue);
currentY = y;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid y position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "xmin":
try {
textBounds.Xmin = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid xmin position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "xmax":
try {
textBounds.Xmax = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid xmax position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "ymin":
try {
textBounds.Ymin = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid ymin position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "ymax":
try {
textBounds.Ymax = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid ymax position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "scalex":
try {
textMatrix.scaleX = Integer.parseInt(paramValue);
textMatrix.hasScale = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid scalex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "scaley":
try {
textMatrix.scaleY = Integer.parseInt(paramValue);
textMatrix.hasScale = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid scalex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "rotateskew0":
try {
textMatrix.rotateSkew0 = Integer.parseInt(paramValue);
textMatrix.hasRotate = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid rotateskew0 value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "rotateskew1":
try {
textMatrix.rotateSkew1 = Integer.parseInt(paramValue);
textMatrix.hasRotate = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid rotateskew1 value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "translatex":
try {
textMatrix.translateX = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid translatex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "translatey":
try {
textMatrix.translateY = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid translatey value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
default:
throw new TextParseException("Unrecognized parameter name: " + paramName, lexer.yyline());
}
break;
case TEXT:
String txt = (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++];
if (txt == null || (font == null && txt.isEmpty())) {
continue;
}
if (font == null) {
throw new TextParseException("Font not defined", lexer.yyline());
}
while (txt.charAt(0) == '\r' || txt.charAt(0) == '\n') {
txt = txt.substring(1);
}
while (txt.charAt(txt.length() - 1) == '\r' || txt.charAt(txt.length() - 1) == '\n') {
txt = txt.substring(0, txt.length() - 1);
}
StringBuilder txtSb = new StringBuilder();
for (int i = 0; i < txt.length(); i++) {
char c = txt.charAt(i);
if (!font.containsChar(c)) {
if (!missingCharHandler.handle(this, font, c)) {
if (!missingCharHandler.getIgnoreMissingCharacters()) {
return false;
}
} else {
return setFormattedText(missingCharHandler, formattedText, texts);
}
} else {
txtSb.append(c);
}
}
txt = txtSb.toString();
TEXTRECORD tr = new TEXTRECORD();
textRecords.add(tr);
if (fontId > -1) {
tr.fontId = fontId;
tr.textHeight = textHeight;
fontId = -1;
tr.styleFlagsHasFont = true;
}
if (colorA != null) {
tr.textColorA = colorA;
tr.styleFlagsHasColor = true;
colorA = null;
}
if (x != null) {
tr.xOffset = x;
tr.styleFlagsHasXOffset = true;
x = null;
}
if (y != null) {
tr.yOffset = y;
tr.styleFlagsHasYOffset = true;
y = null;
}
tr.glyphEntries = new ArrayList<>(txt.length());
for (int i = 0; i < txt.length(); i++) {
char c = txt.charAt(i);
Character nextChar = null;
if (i + 1 < txt.length()) {
nextChar = txt.charAt(i + 1);
}
GLYPHENTRY ge = new GLYPHENTRY();
ge.glyphIndex = font.charToGlyph(c);
int advance;
if (font.hasLayout()) {
int kerningAdjustment = 0;
if (nextChar != null) {
kerningAdjustment = font.getCharKerningAdjustment(c, nextChar);
}
advance = (int) Math.round(((double) textHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment)) / (font.getDivider() * 1024.0));
} else {
advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar));
}
ge.glyphAdvance = advance;
tr.glyphEntries.add(ge);
currentX += advance;
}
if (currentX > maxX) {
maxX = currentX;
}
if (currentX < minX) {
minX = currentX;
}
break;
}
}
setModified(true);
this.textRecords = textRecords;
this.textBounds = textBounds;
} catch (IOException ex) {
return false;
} catch (TextParseException ex) {
throw ex;
}
updateTextBounds();
return true;
}
@Override
public void updateTextBounds() {
updateTextBounds(textBounds);
}
@Override
public boolean alignText(TextAlign textAlign) {
alignText(swf, textRecords, textAlign);
setModified(true);
return true;
}
@Override
public boolean translateText(int diff) {
textMatrix.translateX += diff;
updateTextBounds();
setModified(true);
return true;
}
@Override
public RECT getRect(Set<BoundedTag> added) {
return textBounds;
}
@Override
public int getCharacterId() {
return 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(textBounds);
sos.writeMatrix(textMatrix);
int glyphBits = 0;
int advanceBits = 0;
for (TEXTRECORD tr : textRecords) {
for (GLYPHENTRY ge : tr.glyphEntries) {
glyphBits = SWFOutputStream.enlargeBitCountU(glyphBits, ge.glyphIndex);
advanceBits = SWFOutputStream.enlargeBitCountS(advanceBits, ge.glyphAdvance);
}
}
if (Configuration.debugCopy.get()) {
glyphBits = Math.max(glyphBits, this.glyphBits);
advanceBits = Math.max(advanceBits, this.advanceBits);
}
sos.writeUI8(glyphBits);
sos.writeUI8(advanceBits);
for (TEXTRECORD tr : textRecords) {
sos.writeTEXTRECORD(tr, true, glyphBits, advanceBits);
}
sos.writeUI8(0);
} catch (IOException e) {
throw new Error("This should never happen.", e);
}
return baos.toByteArray();
}
/**
* Constructor
*
@@ -557,100 +62,7 @@ public class DefineText2Tag extends TextTag {
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
characterID = sis.readUI16("characterID");
textBounds = sis.readRECT("textBounds");
textMatrix = sis.readMatrix("textMatrix");
glyphBits = sis.readUI8("glyphBits");
advanceBits = sis.readUI8("advanceBits");
textRecords = new ArrayList<>();
TEXTRECORD tr;
while ((tr = sis.readTEXTRECORD(true, glyphBits, advanceBits, "record")) != null) {
textRecords.add(tr);
}
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
for (TEXTRECORD tr : textRecords) {
if (tr.styleFlagsHasFont) {
needed.add(tr.fontId);
}
}
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = false;
for (TEXTRECORD tr : textRecords) {
if (tr.fontId == oldCharacterId) {
tr.fontId = newCharacterId;
modified = true;
}
}
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = false;
for (TEXTRECORD tr : textRecords) {
if (tr.fontId == characterId) {
tr.styleFlagsHasFont = false;
tr.fontId = 0;
modified = true;
}
}
if (modified) {
setModified(true);
}
return modified;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
staticTextToImage(swf, textRecords, 2, image, getTextMatrix(), transformation, colorTransform);
/*try {
DefineText2Tag originalTag = (DefineText2Tag) getOriginalTag();
if (isModified()) {
originalTag.toImage(frame, time, ratio, stateUnderCursor, mouseButton, image, transformation, new ConstantColorColorTransform(0xFFC0C0C0));
}
staticTextToImage(swf, textRecords, 2, image, getTextMatrix(), transformation, new ConstantColorColorTransform(0xFF000000));
} catch (InterruptedException | IOException ex) {
Logger.getLogger(DefineText2Tag.class.getName()).log(Level.SEVERE, null, ex);
}*/
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
staticTextToSVG(swf, textRecords, 2, exporter, getRect(), getTextMatrix(), colorTransform, zoom);
}
@Override
public ExportRectangle calculateTextBounds() {
return calculateTextBounds(swf, textRecords, getTextMatrix());
}
@Override
public int getNumFrames() {
return 1;
}
@Override
public boolean isSingleFrame() {
return true;
}
@Override
public String toHtmlCanvas(double unitDivisor) {
return staticTextToHtmlCanvas(unitDivisor, swf, textRecords, 2, textBounds, textMatrix, new ColorTransform());
}
@Override
public void setCharacterId(int characterId) {
this.characterID = characterId;
public int getTextNum() {
return 2;
}
}

View File

@@ -16,475 +16,24 @@
*/
package com.jpexs.decompiler.flash.tags;
import com.jpexs.decompiler.flash.AppResources;
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.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.tags.base.StaticTextTag;
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.TEXTRECORD;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.SerializableImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
*
* @author JPEXS
*/
public class DefineTextTag extends TextTag {
@SWFType(BasicType.UI16)
public int characterID;
private int glyphBits;
private int advanceBits;
public RECT textBounds;
public MATRIX textMatrix;
public List<TEXTRECORD> textRecords;
public class DefineTextTag extends StaticTextTag {
public static final int ID = 11;
@Override
public MATRIX getTextMatrix() {
return textMatrix;
}
@Override
public RECT getBounds() {
return textBounds;
}
@Override
public void setBounds(RECT r) {
textBounds = r;
}
@Override
public List<String> getTexts() {
FontTag fnt = null;
List<String> ret = new ArrayList<>();
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont) {
for (Tag t : swf.tags) {
if (t instanceof FontTag) {
if (((FontTag) t).getFontId() == rec.fontId) {
fnt = ((FontTag) t);
break;
}
}
}
}
if (rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) {
/*if (!ret.isEmpty()) {
ret += "\r\n";
}*/
}
if (fnt == null) {
ret.add(AppResources.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId)));
} else {
ret.add(rec.getText(fnt));
}
}
return ret;
}
@Override
public List<Integer> getFontIds() {
List<Integer> ret = new ArrayList<>();
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont) {
ret.add(rec.fontId);
}
}
return ret;
}
@Override
public HighlightedText getFormattedText() {
FontTag fnt = null;
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
writer.append("[").newLine();
writer.append("xmin " + textBounds.Xmin).newLine();
writer.append("ymin " + textBounds.Ymin).newLine();
writer.append("xmax " + textBounds.Xmax).newLine();
writer.append("ymax " + textBounds.Ymax).newLine();
if (textMatrix.translateX != 0) {
writer.append("translatex " + textMatrix.translateX).newLine();
}
if (textMatrix.translateY != 0) {
writer.append("translatey " + textMatrix.translateY).newLine();
}
if (textMatrix.hasScale) {
writer.append("scalex " + textMatrix.scaleX).newLine();
writer.append("scaley " + textMatrix.scaleY).newLine();
}
if (textMatrix.hasRotate) {
writer.append("rotateskew0 " + textMatrix.rotateSkew0).newLine();
writer.append("rotateskew1 " + textMatrix.rotateSkew1).newLine();
}
writer.append("]");
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont || rec.styleFlagsHasColor || rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) {
writer.append("[").newLine();
if (rec.styleFlagsHasFont) {
for (Tag t : swf.tags) {
if (t instanceof FontTag) {
if (((FontTag) t).getFontId() == rec.fontId) {
fnt = ((FontTag) t);
break;
}
}
}
writer.append("font " + rec.fontId).newLine();
writer.append("height " + rec.textHeight).newLine();
}
if (rec.styleFlagsHasColor) {
writer.append("color " + rec.textColor.toHexRGB()).newLine();
}
if (rec.styleFlagsHasXOffset) {
writer.append("x " + rec.xOffset).newLine();
}
if (rec.styleFlagsHasYOffset) {
writer.append("y " + rec.yOffset).newLine();
}
writer.append("]");
}
if (fnt == null) {
writer.append(AppResources.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId)));
} else {
writer.hilightSpecial(Helper.escapeActionScriptString(rec.getText(fnt)).replace("[", "\\[").replace("]", "\\]"), 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;
List<TEXTRECORD> textRecords = new ArrayList<>();
RGB color = null;
int fontId = -1;
int textHeight = -1;
FontTag font = null;
String fontName = null;
Integer x = null;
Integer y = null;
int currentX = 0;
int currentY = 0;
int maxX = Integer.MIN_VALUE;
int minX = Integer.MAX_VALUE;
MATRIX textMatrix = new MATRIX();
textMatrix.hasRotate = false;
textMatrix.hasScale = false;
RECT textBounds = new RECT();
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 "color":
Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue);
if (m.matches()) {
color = new RGB(Integer.parseInt(m.group(1), 16), Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16));
} else {
throw new TextParseException("Invalid color. Valid format is #rrggbb. Found: " + paramValue, lexer.yyline());
}
break;
case "font":
try {
fontId = Integer.parseInt(paramValue);
FontTag ft = swf.getFont(fontId);
if (ft == null) {
throw new TextParseException("Font not found.", lexer.yyline());
}
font = (FontTag) ft;
fontName = font.getSystemFontName();
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid font id - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "height":
try {
textHeight = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid font height - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "x":
try {
x = Integer.parseInt(paramValue);
currentX = x;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid x position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "y":
try {
y = Integer.parseInt(paramValue);
currentY = y;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid y position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "xmin":
try {
textBounds.Xmin = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid xmin position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "xmax":
try {
textBounds.Xmax = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid xmax position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "ymin":
try {
textBounds.Ymin = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid ymin position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "ymax":
try {
textBounds.Ymax = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid ymax position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "scalex":
try {
textMatrix.scaleX = Integer.parseInt(paramValue);
textMatrix.hasScale = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid scalex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "scaley":
try {
textMatrix.scaleY = Integer.parseInt(paramValue);
textMatrix.hasScale = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid scalex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "rotateskew0":
try {
textMatrix.rotateSkew0 = Integer.parseInt(paramValue);
textMatrix.hasRotate = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid rotateskew0 value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "rotateskew1":
try {
textMatrix.rotateSkew1 = Integer.parseInt(paramValue);
textMatrix.hasRotate = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid rotateskew1 value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "translatex":
try {
textMatrix.translateX = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid translatex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "translatey":
try {
textMatrix.translateY = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid translatey value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
default:
throw new TextParseException("Unrecognized parameter name: " + paramName, lexer.yyline());
}
break;
case TEXT:
String txt = (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++];
if (txt == null || (font == null && txt.isEmpty())) {
continue;
}
if (font == null) {
throw new TextParseException("Font not defined", lexer.yyline());
}
while (txt.charAt(0) == '\r' || txt.charAt(0) == '\n') {
txt = txt.substring(1);
}
while (txt.charAt(txt.length() - 1) == '\r' || txt.charAt(txt.length() - 1) == '\n') {
txt = txt.substring(0, txt.length() - 1);
}
StringBuilder txtSb = new StringBuilder();
for (int i = 0; i < txt.length(); i++) {
char c = txt.charAt(i);
if (!font.containsChar(c)) {
if (!missingCharHandler.handle(this, font, c)) {
if (!missingCharHandler.getIgnoreMissingCharacters()) {
return false;
}
} else {
return setFormattedText(missingCharHandler, formattedText, texts);
}
} else {
txtSb.append(c);
}
}
txt = txtSb.toString();
TEXTRECORD tr = new TEXTRECORD();
textRecords.add(tr);
if (fontId > -1) {
tr.fontId = fontId;
tr.textHeight = textHeight;
fontId = -1;
tr.styleFlagsHasFont = true;
}
if (color != null) {
tr.textColor = color;
tr.styleFlagsHasColor = true;
color = null;
}
if (x != null) {
tr.xOffset = x;
tr.styleFlagsHasXOffset = true;
x = null;
}
if (y != null) {
tr.yOffset = y;
tr.styleFlagsHasYOffset = true;
y = null;
}
tr.glyphEntries = new ArrayList<>(txt.length());
for (int i = 0; i < txt.length(); i++) {
char c = txt.charAt(i);
Character nextChar = null;
if (i + 1 < txt.length()) {
nextChar = txt.charAt(i + 1);
}
GLYPHENTRY ge = new GLYPHENTRY();
ge.glyphIndex = font.charToGlyph(c);
int advance;
if (font.hasLayout()) {
int kerningAdjustment = 0;
if (nextChar != null) {
kerningAdjustment = font.getCharKerningAdjustment(c, nextChar);
}
advance = (int) Math.round(((double) textHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment)) / (font.getDivider() * 1024.0));
} else {
advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar));
}
ge.glyphAdvance = advance;
tr.glyphEntries.add(ge);
currentX += advance;
}
if (currentX > maxX) {
maxX = currentX;
}
if (currentX < minX) {
minX = currentX;
}
break;
}
}
setModified(true);
this.textRecords = textRecords;
this.textMatrix = textMatrix;
this.textBounds = textBounds;
} catch (IOException ex) {
return false;
} catch (TextParseException ex) {
throw ex;
}
updateTextBounds();
return true;
}
@Override
public void updateTextBounds() {
updateTextBounds(textBounds);
}
@Override
public boolean alignText(TextAlign textAlign) {
alignText(swf, textRecords, textAlign);
setModified(true);
return true;
}
@Override
public boolean translateText(int diff) {
textMatrix.translateX += diff;
updateTextBounds();
setModified(true);
return true;
}
@Override
public int getCharacterId() {
return characterID;
}
/**
* Constructor
*
@@ -500,57 +49,6 @@ public class DefineTextTag extends TextTag {
advanceBits = 0;
}
public DefineTextTag(SWF swf, int characterID, RECT textBounds, MATRIX textMatrix, List<TEXTRECORD> textRecords) {
super(swf, ID, "DefineText", null);
this.characterID = characterID;
this.textBounds = textBounds;
this.textMatrix = textMatrix;
this.textRecords = textRecords;
this.glyphBits = 0;
this.advanceBits = 0;
}
/**
* 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(textBounds);
sos.writeMatrix(textMatrix);
int glyphBits = 0;
int advanceBits = 0;
for (TEXTRECORD tr : textRecords) {
for (GLYPHENTRY ge : tr.glyphEntries) {
glyphBits = SWFOutputStream.enlargeBitCountU(glyphBits, ge.glyphIndex);
advanceBits = SWFOutputStream.enlargeBitCountS(advanceBits, ge.glyphAdvance);
}
}
if (Configuration.debugCopy.get()) {
glyphBits = Math.max(glyphBits, this.glyphBits);
advanceBits = Math.max(advanceBits, this.advanceBits);
}
sos.writeUI8(glyphBits);
sos.writeUI8(advanceBits);
for (TEXTRECORD tr : textRecords) {
sos.writeTEXTRECORD(tr, false, glyphBits, advanceBits);
}
sos.writeUI8(0);
} catch (IOException e) {
throw new Error("This should never happen.", e);
}
return baos.toByteArray();
}
/**
* Constructor
*
@@ -564,105 +62,7 @@ public class DefineTextTag extends TextTag {
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
characterID = sis.readUI16("characterID");
textBounds = sis.readRECT("textBounds");
textMatrix = sis.readMatrix("textMatrix");
glyphBits = sis.readUI8("glyphBits");
advanceBits = sis.readUI8("advanceBits");
textRecords = new ArrayList<>();
TEXTRECORD tr;
while ((tr = sis.readTEXTRECORD(false, glyphBits, advanceBits, "record")) != null) {
textRecords.add(tr);
}
}
@Override
public RECT getRect(Set<BoundedTag> added) {
return textBounds;
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
for (TEXTRECORD tr : textRecords) {
if (tr.styleFlagsHasFont) {
needed.add(tr.fontId);
}
}
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = false;
for (TEXTRECORD tr : textRecords) {
if (tr.fontId == oldCharacterId) {
tr.fontId = newCharacterId;
modified = true;
}
}
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = false;
for (TEXTRECORD tr : textRecords) {
if (tr.fontId == characterId) {
tr.styleFlagsHasFont = false;
tr.fontId = 0;
modified = true;
}
}
if (modified) {
setModified(true);
}
return modified;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
staticTextToImage(swf, textRecords, 1, image, getTextMatrix(), transformation, colorTransform);
/*try {
DefineTextTag originalTag = (DefineTextTag) getOriginalTag();
if (isModified()) {
originalTag.toImage(frame, time, ratio, stateUnderCursor, mouseButton, image, transformation, new ConstantColorColorTransform(0xFFC0C0C0));
}
staticTextToImage(swf, textRecords, 1, image, getTextMatrix(), transformation, new ConstantColorColorTransform(0xFF000000));
} catch (InterruptedException | IOException ex) {
Logger.getLogger(DefineTextTag.class.getName()).log(Level.SEVERE, null, ex);
}*/
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
staticTextToSVG(swf, textRecords, 1, exporter, getRect(), getTextMatrix(), colorTransform, zoom);
}
@Override
public ExportRectangle calculateTextBounds() {
return calculateTextBounds(swf, textRecords, getTextMatrix());
}
@Override
public int getNumFrames() {
public int getTextNum() {
return 1;
}
@Override
public boolean isSingleFrame() {
return true;
}
@Override
public String toHtmlCanvas(double unitDivisor) {
return staticTextToHtmlCanvas(unitDivisor, swf, textRecords, 1, textBounds, textMatrix, new ColorTransform());
}
@Override
public void setCharacterId(int characterId) {
this.characterID = characterId;
}
}

View File

@@ -60,6 +60,11 @@ public abstract class ButtonTag extends CharacterTag implements DrawableTag, Tim
return getRect(new HashSet<>());
}
@Override
public int getUsedParameters() {
return PARAMETER_FRAME | PARAMETER_TIME | PARAMETER_RATIO; // inner tags can contain morphshapes, too
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
return getTimeline().getOutline(frame, time, ratio, renderContext, transformation);

View File

@@ -29,6 +29,14 @@ import java.io.IOException;
*/
public interface DrawableTag extends BoundedTag {
public static final int PARAMETER_FRAME = 1;
public static final int PARAMETER_TIME = 2;
public static final int PARAMETER_RATIO = 4;
public int getUsedParameters();
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation);
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform);

View File

@@ -23,8 +23,6 @@ import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.helpers.FontHelper;
import com.jpexs.decompiler.flash.tags.DefineFontNameTag;
import com.jpexs.decompiler.flash.tags.DefineText2Tag;
import com.jpexs.decompiler.flash.tags.DefineTextTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
@@ -196,10 +194,8 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable
protected void shiftGlyphIndices(int fontId, int startIndex) {
for (Tag t : swf.tags) {
List<TEXTRECORD> textRecords = null;
if (t instanceof DefineTextTag) {
textRecords = ((DefineTextTag) t).textRecords;
} else if (t instanceof DefineText2Tag) {
textRecords = ((DefineText2Tag) t).textRecords;
if (t instanceof StaticTextTag) {
textRecords = ((StaticTextTag) t).textRecords;
}
if (textRecords != null) {
@@ -298,6 +294,17 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable
return defaultFontName;
}
@Override
public int getUsedParameters() {
return PARAMETER_FRAME;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
RECT r = getRect();
return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()));
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
SHAPERECORD.shapeListToImage(swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform);
@@ -307,6 +314,26 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
}
@Override
public String toHtmlCanvas(double unitDivisor) {
List<SHAPE> shapes = getGlyphShapeTable();
StringBuilder sb = new StringBuilder();
sb.append("\tdefaultFill = textColor;\r\n");
sb.append("\tswitch(ch){\r\n");
for (int i = 0; i < shapes.size(); i++) {
char c = glyphToChar(i);
String cs = "" + c;
cs = cs.replace("\\", "\\\\").replace("\"", "\\\"");
sb.append("\t\tcase \"").append(cs).append("\":\r\n");
CanvasShapeExporter exporter = new CanvasShapeExporter(null, unitDivisor, swf, shapes.get(i), new ColorTransform(), 0, 0);
exporter.export();
sb.append("\t\t").append(exporter.getShapeData().replaceAll("\r\n", "\r\n\t\t"));
sb.append("\tbreak;\r\n");
}
sb.append("\t}\r\n");
return sb.toString();
}
@Override
public int getNumFrames() {
int frameCount = (getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1;
@@ -321,12 +348,6 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable
return true;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
RECT r = getRect();
return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()));
}
@Override
public RECT getRect() {
return getRect(null); // parameter not used
@@ -362,26 +383,6 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable
return dfn.fontCopyright;
}
@Override
public String toHtmlCanvas(double unitDivisor) {
List<SHAPE> shapes = getGlyphShapeTable();
StringBuilder sb = new StringBuilder();
sb.append("\tdefaultFill = textColor;\r\n");
sb.append("\tswitch(ch){\r\n");
for (int i = 0; i < shapes.size(); i++) {
char c = glyphToChar(i);
String cs = "" + c;
cs = cs.replace("\\", "\\\\").replace("\"", "\\\"");
sb.append("\t\tcase \"").append(cs).append("\":\r\n");
CanvasShapeExporter exporter = new CanvasShapeExporter(null, unitDivisor, swf, shapes.get(i), new ColorTransform(), 0, 0);
exporter.export();
sb.append("\t\t").append(exporter.getShapeData().replaceAll("\r\n", "\r\n\t\t"));
sb.append("\tbreak;\r\n");
}
sb.append("\t}\r\n");
return sb.toString();
}
public RECT getGlyphBounds(int glyphIndex) {
return getGlyphShapeTable().get(glyphIndex).getBounds();
}

View File

@@ -182,6 +182,16 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag {
return new RECT(0, widthInTwips, 0, heightInTwips);
}
@Override
public int getUsedParameters() {
return 0;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
return transformation.toTransform().createTransformedShape(getShape().getOutline());
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
BitmapExporter.export(swf, getShape(), null, image, transformation, colorTransform);
@@ -210,11 +220,6 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag {
return true;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
return transformation.toTransform().createTransformedShape(getShape().getOutline());
}
public void clearCache() {
cachedImage = null;
}

View File

@@ -66,6 +66,11 @@ public abstract class ShapeTag extends CharacterTag implements DrawableTag, Lazy
return getRect(null); // parameter not used
}
@Override
public int getUsedParameters() {
return 0;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
return transformation.toTransform().createTransformedShape(getShapes().getOutline());

View File

@@ -0,0 +1,648 @@
/*
* 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.base;
import com.jpexs.decompiler.flash.AppResources;
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.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.SWFType;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.SerializableImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author JPEXS
*/
public abstract class StaticTextTag extends TextTag {
@SWFType(BasicType.UI16)
public int characterID;
protected int glyphBits;
protected int advanceBits;
public RECT textBounds;
public MATRIX textMatrix;
public List<TEXTRECORD> textRecords;
public abstract int getTextNum();
public StaticTextTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
@Override
public RECT getBounds() {
return textBounds;
}
@Override
public MATRIX getTextMatrix() {
return textMatrix;
}
@Override
public void setBounds(RECT r) {
textBounds = r;
}
@Override
public List<String> getTexts() {
FontTag fnt = null;
List<String> ret = new ArrayList<>();
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont) {
FontTag fnt2 = swf.getFont(rec.fontId);
if (fnt2 != null) {
fnt = fnt2;
}
}
if (rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) {
/*if (!ret.isEmpty()) {
ret += "\r\n";
}*/
}
if (fnt == null) {
ret.add(AppResources.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId)));
} else {
ret.add(rec.getText(fnt));
}
}
return ret;
}
@Override
public List<Integer> getFontIds() {
List<Integer> ret = new ArrayList<>();
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont) {
ret.add(rec.fontId);
}
}
return ret;
}
@Override
public void updateTextBounds() {
updateTextBounds(textBounds);
}
@Override
public boolean alignText(TextAlign textAlign) {
alignText(swf, textRecords, textAlign);
setModified(true);
return true;
}
@Override
public boolean translateText(int diff) {
textMatrix.translateX += diff;
updateTextBounds();
setModified(true);
return true;
}
@Override
public RECT getRect(Set<BoundedTag> added) {
return textBounds;
}
@Override
public ExportRectangle calculateTextBounds() {
return calculateTextBounds(swf, textRecords, getTextMatrix());
}
@Override
public int getNumFrames() {
return 1;
}
@Override
public boolean isSingleFrame() {
return true;
}
@Override
public void setCharacterId(int characterId) {
this.characterID = characterId;
}
@Override
public int getCharacterId() {
return characterID;
}
@Override
public HighlightedText getFormattedText() {
FontTag fnt = null;
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
writer.append("[").newLine();
writer.append("xmin " + textBounds.Xmin).newLine();
writer.append("ymin " + textBounds.Ymin).newLine();
writer.append("xmax " + textBounds.Xmax).newLine();
writer.append("ymax " + textBounds.Ymax).newLine();
if (textMatrix.translateX != 0) {
writer.append("translatex " + textMatrix.translateX).newLine();
}
if (textMatrix.translateY != 0) {
writer.append("translatey " + textMatrix.translateY).newLine();
}
if (textMatrix.hasScale) {
writer.append("scalex " + textMatrix.scaleX).newLine();
writer.append("scaley " + textMatrix.scaleY).newLine();
}
if (textMatrix.hasRotate) {
writer.append("rotateskew0 " + textMatrix.rotateSkew0).newLine();
writer.append("rotateskew1 " + textMatrix.rotateSkew1).newLine();
}
writer.append("]");
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont || rec.styleFlagsHasColor || rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) {
writer.append("[").newLine();
if (rec.styleFlagsHasFont) {
FontTag fnt2 = swf.getFont(rec.fontId);
if (fnt2 != null) {
fnt = fnt2;
}
writer.append("font " + rec.fontId).newLine();
writer.append("height " + rec.textHeight).newLine();
}
if (rec.styleFlagsHasColor) {
if (getTextNum() == 1) {
writer.append("color " + rec.textColor.toHexRGB()).newLine();
} else {
writer.append("color " + rec.textColorA.toHexARGB()).newLine();
}
}
if (rec.styleFlagsHasXOffset) {
writer.append("x " + rec.xOffset).newLine();
}
if (rec.styleFlagsHasYOffset) {
writer.append("y " + rec.yOffset).newLine();
}
writer.append("]");
}
if (fnt == null) {
writer.append(AppResources.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId)));
} else {
writer.hilightSpecial(Helper.escapeActionScriptString(rec.getText(fnt)).replace("[", "\\[").replace("]", "\\]"), 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;
List<TEXTRECORD> textRecords = new ArrayList<>();
RGB color = null;
RGBA colorA = null;
int fontId = -1;
int textHeight = -1;
FontTag font = null;
String fontName = null;
Integer x = null;
Integer y = null;
int currentX = 0;
int currentY = 0;
int maxX = Integer.MIN_VALUE;
int minX = Integer.MAX_VALUE;
MATRIX textMatrix = new MATRIX();
textMatrix.hasRotate = false;
textMatrix.hasScale = false;
RECT textBounds = new RECT();
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 "color":
if (getTextNum() == 1) {
Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue);
if (m.matches()) {
color = new RGB(Integer.parseInt(m.group(1), 16), Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16));
} else {
throw new TextParseException("Invalid color. Valid format is #rrggbb. Found: " + paramValue, lexer.yyline());
}
} else {
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()) {
colorA = 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 "font":
try {
fontId = Integer.parseInt(paramValue);
FontTag ft = swf.getFont(fontId);
if (ft == null) {
throw new TextParseException("Font not found.", lexer.yyline());
}
font = (FontTag) ft;
fontName = font.getSystemFontName();
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid font id - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "height":
try {
textHeight = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid font height - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "x":
try {
x = Integer.parseInt(paramValue);
currentX = x;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid x position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "y":
try {
y = Integer.parseInt(paramValue);
currentY = y;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid y position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "xmin":
try {
textBounds.Xmin = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid xmin position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "xmax":
try {
textBounds.Xmax = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid xmax position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "ymin":
try {
textBounds.Ymin = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid ymin position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "ymax":
try {
textBounds.Ymax = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid ymax position - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "scalex":
try {
textMatrix.scaleX = Integer.parseInt(paramValue);
textMatrix.hasScale = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid scalex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "scaley":
try {
textMatrix.scaleY = Integer.parseInt(paramValue);
textMatrix.hasScale = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid scalex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "rotateskew0":
try {
textMatrix.rotateSkew0 = Integer.parseInt(paramValue);
textMatrix.hasRotate = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid rotateskew0 value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "rotateskew1":
try {
textMatrix.rotateSkew1 = Integer.parseInt(paramValue);
textMatrix.hasRotate = true;
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid rotateskew1 value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "translatex":
try {
textMatrix.translateX = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid translatex value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
case "translatey":
try {
textMatrix.translateY = Integer.parseInt(paramValue);
} catch (NumberFormatException nfe) {
throw new TextParseException("Invalid translatey value - number expected. Found: " + paramValue, lexer.yyline());
}
break;
default:
throw new TextParseException("Unrecognized parameter name: " + paramName, lexer.yyline());
}
break;
case TEXT:
String txt = (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++];
if (txt == null || (font == null && txt.isEmpty())) {
continue;
}
if (font == null) {
throw new TextParseException("Font not defined", lexer.yyline());
}
while (txt.charAt(0) == '\r' || txt.charAt(0) == '\n') {
txt = txt.substring(1);
}
while (txt.charAt(txt.length() - 1) == '\r' || txt.charAt(txt.length() - 1) == '\n') {
txt = txt.substring(0, txt.length() - 1);
}
StringBuilder txtSb = new StringBuilder();
for (int i = 0; i < txt.length(); i++) {
char c = txt.charAt(i);
if (!font.containsChar(c)) {
if (!missingCharHandler.handle(this, font, c)) {
if (!missingCharHandler.getIgnoreMissingCharacters()) {
return false;
}
} else {
return setFormattedText(missingCharHandler, formattedText, texts);
}
} else {
txtSb.append(c);
}
}
txt = txtSb.toString();
TEXTRECORD tr = new TEXTRECORD();
textRecords.add(tr);
if (fontId > -1) {
tr.fontId = fontId;
tr.textHeight = textHeight;
fontId = -1;
tr.styleFlagsHasFont = true;
}
if (getTextNum() == 1) {
if (color != null) {
tr.textColor = color;
tr.styleFlagsHasColor = true;
color = null;
}
} else {
if (colorA != null) {
tr.textColorA = colorA;
tr.styleFlagsHasColor = true;
colorA = null;
}
}
if (x != null) {
tr.xOffset = x;
tr.styleFlagsHasXOffset = true;
x = null;
}
if (y != null) {
tr.yOffset = y;
tr.styleFlagsHasYOffset = true;
y = null;
}
tr.glyphEntries = new ArrayList<>(txt.length());
for (int i = 0; i < txt.length(); i++) {
char c = txt.charAt(i);
Character nextChar = null;
if (i + 1 < txt.length()) {
nextChar = txt.charAt(i + 1);
}
GLYPHENTRY ge = new GLYPHENTRY();
ge.glyphIndex = font.charToGlyph(c);
int advance;
if (font.hasLayout()) {
int kerningAdjustment = 0;
if (nextChar != null) {
kerningAdjustment = font.getCharKerningAdjustment(c, nextChar);
}
advance = (int) Math.round(((double) textHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment)) / (font.getDivider() * 1024.0));
} else {
advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar));
}
ge.glyphAdvance = advance;
tr.glyphEntries.add(ge);
currentX += advance;
}
if (currentX > maxX) {
maxX = currentX;
}
if (currentX < minX) {
minX = currentX;
}
break;
}
}
setModified(true);
this.textRecords = textRecords;
this.textMatrix = textMatrix;
this.textBounds = textBounds;
} catch (IOException ex) {
return false;
} catch (TextParseException ex) {
throw ex;
}
updateTextBounds();
return true;
}
/**
* 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(textBounds);
sos.writeMatrix(textMatrix);
int glyphBits = 0;
int advanceBits = 0;
for (TEXTRECORD tr : textRecords) {
for (GLYPHENTRY ge : tr.glyphEntries) {
glyphBits = SWFOutputStream.enlargeBitCountU(glyphBits, ge.glyphIndex);
advanceBits = SWFOutputStream.enlargeBitCountS(advanceBits, ge.glyphAdvance);
}
}
if (Configuration.debugCopy.get()) {
glyphBits = Math.max(glyphBits, this.glyphBits);
advanceBits = Math.max(advanceBits, this.advanceBits);
}
sos.writeUI8(glyphBits);
sos.writeUI8(advanceBits);
for (TEXTRECORD tr : textRecords) {
sos.writeTEXTRECORD(tr, getTextNum(), glyphBits, advanceBits);
}
sos.writeUI8(0);
} catch (IOException e) {
throw new Error("This should never happen.", e);
}
return baos.toByteArray();
}
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
characterID = sis.readUI16("characterID");
textBounds = sis.readRECT("textBounds");
textMatrix = sis.readMatrix("textMatrix");
glyphBits = sis.readUI8("glyphBits");
advanceBits = sis.readUI8("advanceBits");
textRecords = new ArrayList<>();
TEXTRECORD tr;
while ((tr = sis.readTEXTRECORD(getTextNum(), glyphBits, advanceBits, "record")) != null) {
textRecords.add(tr);
}
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
for (TEXTRECORD tr : textRecords) {
if (tr.styleFlagsHasFont) {
needed.add(tr.fontId);
}
}
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = false;
for (TEXTRECORD tr : textRecords) {
if (tr.fontId == oldCharacterId) {
tr.fontId = newCharacterId;
modified = true;
}
}
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = false;
for (TEXTRECORD tr : textRecords) {
if (tr.fontId == characterId) {
tr.styleFlagsHasFont = false;
tr.fontId = 0;
modified = true;
}
}
if (modified) {
setModified(true);
}
return modified;
}
@Override
public int getUsedParameters() {
return 0;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
staticTextToImage(swf, textRecords, getTextNum(), image, textMatrix, transformation, colorTransform);
/*try {
TextTag originalTag = (TextTag) getOriginalTag();
if (isModified()) {
originalTag.toImage(frame, time, ratio, renderContext, image, transformation, new ConstantColorColorTransform(0xFFC0C0C0));
}
staticTextToImage(swf, textRecords, getTextNum(), image, getTextMatrix(), transformation, new ConstantColorColorTransform(0xFF000000));
} catch (InterruptedException | IOException ex) {
Logger.getLogger(TextTag.class.getName()).log(Level.SEVERE, null, ex);
}*/
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
staticTextToSVG(swf, textRecords, getTextNum(), exporter, getRect(), textMatrix, colorTransform, zoom);
}
@Override
public String toHtmlCanvas(double unitDivisor) {
return staticTextToHtmlCanvas(unitDivisor, swf, textRecords, getTextNum(), textBounds, textMatrix, new ColorTransform());
}
}