Files
jpexs-decompiler/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java
2013-05-16 22:21:53 +02:00

2039 lines
93 KiB
Java

/*
* Copyright (C) 2013 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.xfl;
import com.jpexs.decompiler.flash.Main;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag;
import com.jpexs.decompiler.flash.tags.DefineButton2Tag;
import com.jpexs.decompiler.flash.tags.DefineEditTextTag;
import com.jpexs.decompiler.flash.tags.DefineFontNameTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DefineText2Tag;
import com.jpexs.decompiler.flash.tags.DefineTextTag;
import com.jpexs.decompiler.flash.tags.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SymbolClassTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.types.BUTTONRECORD;
import com.jpexs.decompiler.flash.types.CXFORM;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import static com.jpexs.decompiler.flash.types.FILLSTYLE.*;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.GRADIENT;
import com.jpexs.decompiler.flash.types.GRADRECORD;
import com.jpexs.decompiler.flash.types.LINESTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
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.SHAPE;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.decompiler.flash.types.filters.BEVELFILTER;
import com.jpexs.decompiler.flash.types.filters.BLURFILTER;
import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER;
import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER;
import com.jpexs.decompiler.flash.types.filters.FILTER;
import com.jpexs.decompiler.flash.types.filters.GLOWFILTER;
import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER;
import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER;
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.imageio.ImageIO;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/**
*
* @author JPEXS
*/
public class XFLConverter {
public static final int KEY_MODE_NORMAL = 9728;
public static final int KEY_MODE_CLASSIC_TWEEN = 22017;
public static final int KEY_MODE_SHAPE_TWEEN = 17922;
public static final int KEY_MODE_MOTION_TWEEN = 8195;
private XFLConverter() {
}
public static String convertShapeEdge(MATRIX mat, SHAPERECORD record, int x, int y) {
String ret = "";
if (record instanceof StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) record;
Point p = new Point(scr.moveDeltaX, scr.moveDeltaY);
p = mat.apply(p);
if (scr.stateMoveTo) {
return "! " + p.x + " " + p.y;
}
return "";
}
if (record instanceof StraightEdgeRecord) {
StraightEdgeRecord ser = (StraightEdgeRecord) record;
if (ser.generalLineFlag || ser.vertLineFlag) {
y += ser.deltaY;
}
if (ser.generalLineFlag || (!ser.vertLineFlag)) {
x += ser.deltaX;
}
Point p = new Point(x, y);
p = mat.apply(p);
return "| " + p.x + " " + p.y;
}
if (record instanceof CurvedEdgeRecord) {
CurvedEdgeRecord cer = (CurvedEdgeRecord) record;
int controlX = cer.controlDeltaX + x;
int controlY = cer.controlDeltaY + y;
int anchorX = cer.anchorDeltaX + controlX;
int anchorY = cer.anchorDeltaY + controlY;
Point control = new Point(controlX, controlY);
control = mat.apply(control);
Point anchor = new Point(anchorX, anchorY);
anchor = mat.apply(anchor);
return "[ " + control.x + " " + control.y + " " + anchor.x + " " + anchor.y;
}
return ret;
}
public static String convertShapeEdges(MATRIX mat, List<SHAPERECORD> records) {
String ret = "";
int x = 0;
int y = 0;
for (SHAPERECORD rec : records) {
ret += convertShapeEdge(mat, rec, x, y);
x = rec.changeX(x);
y = rec.changeY(y);
}
return ret;
}
public static String convertLineStyle(LINESTYLE ls, int shapeNum) {
return "<SolidStroke weight=\"" + (((float) ls.width) / 20.0) + "\">"
+ "<fill>"
+ "<SolidColor color=\"" + (shapeNum == 3 ? ls.colorA.toHexRGB() : ls.color.toHexRGB())
+ (shapeNum == 3 ? " alpha=\"" + ls.colorA.getAlphaFloat() + "\"" : "")
+ "\"/>"
+ "</fill>"
+ "</SolidStroke>";
}
public static String convertLineStyle(HashMap<Integer, CharacterTag> characters, LINESTYLE2 ls, int shapeNum) {
String ret = "";
String params = "";
if (ls.pixelHintingFlag) {
params += " pixelHinting=\"true\"";
}
if ((!ls.noHScaleFlag) && (!ls.noVScaleFlag)) {
params += " scaleMode=\"normal\"";
} else if ((!ls.noHScaleFlag) && ls.noVScaleFlag) {
params += " scaleMode=\"horizontal\"";
} else if (ls.noHScaleFlag && (!ls.noVScaleFlag)) {
params += " scaleMode=\"vertical\"";
}
switch (ls.startCapStyle) { //What about endCapStyle?
case LINESTYLE2.NO_CAP:
params += " caps=\"none\"";
break;
case LINESTYLE2.SQUARE_CAP:
params += " caps=\"square\"";
break;
}
switch (ls.joinStyle) {
case LINESTYLE2.BEVEL_JOIN:
params += " joints=\"bevel\"";
break;
case LINESTYLE2.MITER_JOIN:
params += " joints=\"miter\"";
float miterLimitFactor = toFloat(ls.miterLimitFactor);
if (miterLimitFactor != 3.0f) {
params += " miterLimit=\"" + miterLimitFactor + "\"";
}
break;
}
ret += "<SolidStroke weight=\"" + (((float) ls.width) / 20.0) + "\"";
ret += params;
ret += ">";
ret += "<fill>";
if (!ls.hasFillFlag) {
ret += "<SolidColor color=\"" + ls.color.toHexRGB()
+ (ls.color.getAlphaFloat() != 1 ? " alpha=\"" + ls.color.getAlphaFloat() + "\"" : "")
+ "\"/>";
} else {
ret += convertFillStyle(characters, ls.fillType, shapeNum);
}
ret += "</fill>";
ret += "</SolidStroke>";
return ret;
}
private static float toFloat(int i) {
return ((float) i) / (1 << 16);
}
public static String convertFillStyle(HashMap<Integer, CharacterTag> characters, FILLSTYLE fs, int shapeNum) {
String ret = "";
//ret += "<FillStyle index=\"" + index + "\">";
switch (fs.fillStyleType) {
case SOLID:
ret += "<SolidColor color=\"";
if (shapeNum >= 3) {
ret += fs.colorA.toHexRGB();
} else {
ret += fs.color.toHexRGB();
}
ret += "\"";
if (shapeNum >= 3) {
ret += " alpha=\"" + fs.colorA.getAlphaFloat() + "\"";
}
ret += " />";
break;
case REPEATING_BITMAP:
case CLIPPED_BITMAP:
case NON_SMOOTHED_REPEATING_BITMAP:
case NON_SMOOTHED_CLIPPED_BITMAP:
ret += "<BitmapFill";
ret += " bitmapPath=\"";
CharacterTag bitmapCh = characters.get(fs.bitmapId);
if (bitmapCh instanceof ImageTag) {
ImageTag it = (ImageTag) bitmapCh;
ret += "bitmap" + bitmapCh.getCharacterID() + "." + it.getImageFormat();
}
ret += "\"";
if ((fs.fillStyleType == CLIPPED_BITMAP) || (fs.fillStyleType == NON_SMOOTHED_CLIPPED_BITMAP)) {
ret += " bitmapIsClipped=\"true\"";
}
ret += ">";
ret += "<matrix>" + convertMatrix(fs.bitmapMatrix) + "</matrix>";
ret += "</BitmapFill>";
break;
case LINEAR_GRADIENT:
case RADIAL_GRADIENT:
case FOCAL_RADIAL_GRADIENT:
if (fs.fillStyleType == LINEAR_GRADIENT) {
ret += "<LinearGradient";
} else {
ret += "<RadialGradient";
ret += " focalPointRatio=\"";
if (fs.fillStyleType == FOCAL_RADIAL_GRADIENT) {
ret += fs.focalGradient.focalPoint;
} else {
ret += "0";
}
ret += "\"";
}
int interpolationMode;
if (fs.fillStyleType == FOCAL_RADIAL_GRADIENT) {
interpolationMode = fs.focalGradient.interPolationMode;
} else {
interpolationMode = fs.gradient.interPolationMode;
}
int spreadMode;
if (fs.fillStyleType == FOCAL_RADIAL_GRADIENT) {
spreadMode = fs.focalGradient.spreadMode;
} else {
spreadMode = fs.gradient.spreadMode;
}
if (interpolationMode == GRADIENT.INTERPOLATION_LINEAR_RGB_MODE) {
ret += " interpolationMethod=\"linearRGB\"";
}
switch (spreadMode) {
case GRADIENT.SPREAD_PAD_MODE:
break;
case GRADIENT.SPREAD_REFLECT_MODE:
ret += " spreadMethod=\"reflect\"";
break;
case GRADIENT.SPREAD_REPEAT_MODE:
ret += " spreadMethod=\"repeat\"";
break;
}
ret += ">";
ret += "<matrix>" + convertMatrix(fs.gradientMatrix) + "</matrix>";
GRADRECORD records[];
if (fs.fillStyleType == FOCAL_RADIAL_GRADIENT) {
records = fs.focalGradient.gradientRecords;
} else {
records = fs.gradient.gradientRecords;
}
for (GRADRECORD rec : records) {
ret += "<GradientEntry";
ret += " color=\"" + (shapeNum == 3 ? rec.colorA.toHexRGB() : rec.color.toHexRGB()) + "\"";
if (shapeNum == 3) {
ret += " alpha=\"" + rec.colorA.getAlphaFloat() + "\"";
}
ret += " ratio=\"" + rec.getRatioFloat() + "\"";
ret += " />";
}
if (fs.fillStyleType == LINEAR_GRADIENT) {
ret += "</LinearGradient>";
} else {
ret += "</RadialGradient>";
}
break;
}
//ret += "</FillStyle>";
return ret;
}
public static String convertMatrix(MATRIX m) {
String ret = "";
ret += "<Matrix ";
ret += "tx=\"" + (((float) m.translateX) / 20.0) + "\" ";
ret += "ty=\"" + (((float) m.translateY) / 20.0) + "\" ";
if (m.hasScale) {
ret += "a=\"" + m.toFloat(m.scaleX) + "\" ";
ret += "d=\"" + m.toFloat(m.scaleY) + "\" ";
}
if (m.hasRotate) {
ret += "b=\"" + m.toFloat(m.rotateSkew0) + "\" ";
ret += "c=\"" + m.toFloat(m.rotateSkew1) + "\" ";
}
ret += "/>";
return ret;
}
public static String convertShape(HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, SHAPE shape) {
return convertShape(characters, mat, shapeNum, shape.shapeRecords);
}
public static String convertShape(HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, SHAPEWITHSTYLE shape) {
return convertShape(characters, mat, shapeNum, shape.shapeRecords, shape.fillStyles, shape.lineStyles);
}
public static String convertShape(HashMap<Integer, CharacterTag> characters, MATRIX mat, ShapeTag shape) {
return convertShape(characters, mat, shape.getShapeNum(), shape.getShapes());
}
public static String convertShape(HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords) {
return convertShape(characters, mat, shapeNum, shapeRecords, null, null);
}
public static String convertShape(HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles) {
String ret = "";
if (mat == null) {
mat = new MATRIX();
}
List<SHAPERECORD> edges = new ArrayList<SHAPERECORD>();
int fillStyleCount = 0;
int lineStyleCount = 0;
int lastFillStyleCount = 0;
int lastLineStyleCount = 0;
String edgeStyle = "";
int fillStyle0 = -1;
int fillStyle1 = -1;
int strokeStyle = -1;
String edgesStr = "";
String fillsStr = "";
String strokesStr = "";
fillsStr += "<fills>";
strokesStr += "<strokes>";
edgesStr += "<edges>";
if (fillStyles != null) {
for (FILLSTYLE fs : fillStyles.fillStyles) {
fillsStr += "<FillStyle index=\"" + (fillStyleCount + 1) + "\">";
fillsStr += convertFillStyle(characters, fs, shapeNum);
fillsStr += "</FillStyle>";
fillStyleCount++;
}
lastFillStyleCount = fillStyleCount;
}
if (lineStyles != null) {
if (shapeNum == 4) {
for (int l = 0; l < lineStyles.lineStyles2.length; l++) {
strokesStr += "<StrokeStyle index=\"" + (lineStyleCount + 1) + "\">";
strokesStr += convertLineStyle(characters, lineStyles.lineStyles2[l], shapeNum);
strokesStr += "</StrokeStyle>";
lineStyleCount++;
}
} else {
for (int l = 0; l < lineStyles.lineStyles.length; l++) {
strokesStr += "<StrokeStyle index=\"" + (lineStyleCount + 1) + "\">";
strokesStr += convertLineStyle(lineStyles.lineStyles[l], shapeNum);
strokesStr += "</StrokeStyle>";
lineStyleCount++;
}
}
lastLineStyleCount = lineStyleCount;
}
for (SHAPERECORD edge : shapeRecords) {
if (edge instanceof StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) edge;
if (scr.stateNewStyles) {
for (int f = 0; f < scr.fillStyles.fillStyles.length; f++) {
fillsStr += "<FillStyle index=\"" + (fillStyleCount + 1) + "\">";
fillsStr += convertFillStyle(characters, scr.fillStyles.fillStyles[f], shapeNum);
fillsStr += "</FillStyle>";
fillStyleCount++;
}
if (shapeNum == 4) {
for (int l = 0; l < scr.lineStyles.lineStyles2.length; l++) {
strokesStr += "<StrokeStyle index=\"" + (lineStyleCount + 1) + "\">";
strokesStr += convertLineStyle(characters, scr.lineStyles.lineStyles2[l], shapeNum);
strokesStr += "</StrokeStyle>";
lineStyleCount++;
}
} else {
for (int l = 0; l < scr.lineStyles.lineStyles.length; l++) {
strokesStr += "<StrokeStyle index=\"" + (lineStyleCount + 1) + "\">";
strokesStr += convertLineStyle(scr.lineStyles.lineStyles[l], shapeNum);
strokesStr += "</StrokeStyle>";
lineStyleCount++;
}
}
lastFillStyleCount = scr.fillStyles.fillStyles.length;
lastLineStyleCount = (shapeNum == 4) ? scr.lineStyles.lineStyles2.length : scr.lineStyles.lineStyles.length;
}
if (scr.stateFillStyle0) {
/*edgeStyle += " fillStyle0=\"";
edgeStyle += fillStyleCount - lastFillStyleCount + scr.fillStyle0;
edgeStyle += "\"";*/
fillStyle0 = fillStyleCount - lastFillStyleCount + scr.fillStyle0;
}
if (scr.stateFillStyle1) {
/*edgeStyle += " fillStyle1=\"";
edgeStyle += fillStyleCount - lastFillStyleCount + scr.fillStyle1;
edgeStyle += "\"";*/
fillStyle1 = fillStyleCount - lastFillStyleCount + scr.fillStyle1;
}
if (scr.stateLineStyle) {
/*edgeStyle += " strokeStyle=\"";
edgeStyle += lineStyleCount - lastLineStyleCount + scr.lineStyle;
edgeStyle += "\"";*/
strokeStyle = lineStyleCount - lastLineStyleCount + scr.lineStyle;
}
if (!edges.isEmpty()) {
edgesStr += "<Edge";
if (fillStyle0 > -1) {
edgesStr += " fillStyle0=\"" + fillStyle0 + "\"";
}
if (fillStyle1 > -1) {
edgesStr += " fillStyle1=\"" + fillStyle1 + "\"";
}
if (strokeStyle > -1) {
edgesStr += " strokeStyle=\"" + strokeStyle + "\"";
}
edgesStr += " edges=\"" + convertShapeEdges(mat, edges) + "\" />";
edgeStyle = "";
strokeStyle = -1;
fillStyle0 = -1;
fillStyle1 = -1;
}
edges.clear();
}
edges.add(edge);
}
if (!edges.isEmpty()) {
edgesStr += "<Edge";
if (fillStyle0 > -1) {
edgesStr += " fillStyle0=\"" + fillStyle0 + "\"";
}
if (fillStyle1 > -1) {
edgesStr += " fillStyle1=\"" + fillStyle1 + "\"";
}
if (strokeStyle > -1) {
edgesStr += " strokeStyle=\"" + strokeStyle + "\"";
}
edgesStr += " edges=\"" + convertShapeEdges(mat, edges) + "\" />";
edgeStyle = "";
}
edges.clear();
fillsStr += "</fills>";
strokesStr += "</strokes>";
edgesStr += "</edges>";
ret += "<DOMShape>";
ret += fillsStr;
ret += strokesStr;
ret += edgesStr;
ret += "</DOMShape>";
return ret;
}
private static int getLayerCount(List<Tag> tags) {
int maxDepth = 0;
for (Tag t : tags) {
if (t instanceof PlaceObjectTypeTag) {
int d = ((PlaceObjectTypeTag) t).getDepth();
if (d > maxDepth) {
maxDepth = d;
}
int cd = ((PlaceObjectTypeTag) t).getClipDepth();
if (cd > maxDepth) {
maxDepth = cd;
}
}
}
return maxDepth;
}
private static List<Integer> getOneInstanceShapes(List<Tag> tags, HashMap<Integer, CharacterTag> characters) {
HashMap<Integer, Integer> usages = new HashMap<Integer, Integer>();
for (Tag t : tags) {
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag po = (PlaceObjectTypeTag) t;
int ch = po.getCharacterId();
if (ch > -1) {
if (!usages.containsKey(ch)) {
usages.put(ch, 0);
}
int usageCount = usages.get(ch);
if (po.getName() != null) {
usageCount++;
}
if (po.getColorTransform() != null) {
usageCount++;
}
if (po.getColorTransformWithAlpha() != null) {
usageCount++;
}
if (po.cacheAsBitmap()) {
usageCount++;
}
usages.put(ch, usageCount + 1);
}
}
}
List<Integer> ret = new ArrayList<Integer>();
for (int ch : usages.keySet()) {
if (usages.get(ch) < 2) {
ret.add(ch);
}
}
return ret;
}
private static HashMap<Integer, CharacterTag> getCharacters(List<Tag> tags) {
HashMap<Integer, CharacterTag> ret = new HashMap<Integer, CharacterTag>();
for (Tag t : tags) {
if (t instanceof CharacterTag) {
CharacterTag ct = (CharacterTag) t;
ret.put(ct.getCharacterID(), ct);
}
}
return ret;
}
private static final String[] BLENDMODES = {
null,
null,
"layer",
"multiply",
"screen",
"lighten",
"darken",
"difference",
"add",
"subtract",
"invert",
"alpha",
"erase",
"overlay",
"hardligh"
};
private static double radToDeg(double rad) {
return rad * 180 / Math.PI;
}
private static String doubleToString(double d) {
String ds = "" + d;
if (ds.endsWith(".0")) {
ds = ds.substring(0, ds.length() - 2);
}
return ds;
}
public static String convertFilter(FILTER filter) {
String ret = "";
if (filter instanceof DROPSHADOWFILTER) {
DROPSHADOWFILTER dsf = (DROPSHADOWFILTER) filter;
ret += "<DropShadowFilter";
if (dsf.dropShadowColor.alpha != 255) {
ret += " alpha=\"" + doubleToString(dsf.dropShadowColor.getAlphaFloat()) + "\"";
}
ret += " angle=\"" + doubleToString(radToDeg(dsf.angle)) + "\"";
ret += " blurX=\"" + doubleToString(dsf.blurX) + "\"";
ret += " blurY=\"" + doubleToString(dsf.blurY) + "\"";
ret += " color=\"" + dsf.dropShadowColor.toHexRGB() + "\"";
ret += " distance=\"" + doubleToString(dsf.distance) + "\"";
if (!dsf.compositeSource) {
ret += " hideObject=\"true\"";
}
if (dsf.innerShadow) {
ret += " inner=\"true\"";
}
if (dsf.knockout) {
ret += " knockout=\"true\"";
}
ret += " quality=\"" + dsf.passes + "\"";
ret += " strength=\"" + doubleToString(dsf.strength) + "\"";
ret += " />";
} else if (filter instanceof BLURFILTER) {
BLURFILTER bf = (BLURFILTER) filter;
ret += "<BlurFilter";
ret += " blurX=\"" + doubleToString(bf.blurX) + "\"";
ret += " blurY=\"" + doubleToString(bf.blurY) + "\"";
ret += " quality=\"" + bf.passes + "\"";
ret += " />";
} else if (filter instanceof GLOWFILTER) {
GLOWFILTER gf = (GLOWFILTER) filter;
ret += "<GlowFilter";
if (gf.glowColor.alpha != 255) {
ret += " alpha=\"" + gf.glowColor.getAlphaFloat() + "\"";
}
ret += " blurX=\"" + doubleToString(gf.blurX) + "\"";
ret += " blurY=\"" + doubleToString(gf.blurY) + "\"";
ret += " color=\"" + gf.glowColor.toHexRGB() + "\"";
if (gf.innerGlow) {
ret += " inner=\"true\"";
}
if (gf.knockout) {
ret += " knockout=\"true\"";
}
ret += " quality=\"" + gf.passes + "\"";
ret += " strength=\"" + doubleToString(gf.strength) + "\"";
ret += " />";
} else if (filter instanceof BEVELFILTER) {
BEVELFILTER bf = (BEVELFILTER) filter;
ret += "<BevelFilter";
ret += " blurX=\"" + doubleToString(bf.blurX) + "\"";
ret += " blurY=\"" + doubleToString(bf.blurY) + "\"";
ret += " quality=\"" + bf.passes + "\"";
ret += " angle=\"" + doubleToString(radToDeg(bf.angle)) + "\"";
ret += " distance=\"" + bf.distance + "\"";
if (bf.highlightColor.alpha != 255) {
ret += " highlightAlpha=\"" + bf.highlightColor.getAlphaFloat() + "\"";
}
ret += " highlightColor=\"" + bf.highlightColor.toHexRGB() + "\"";
if (bf.knockout) {
ret += " knockout=\"true\"";
}
if (bf.shadowColor.alpha != 255) {
ret += " shadowAlpha=\"" + bf.shadowColor.getAlphaFloat() + "\"";
}
ret += " shadowColor=\"" + bf.shadowColor.toHexRGB() + "\"";
ret += " strength=\"" + doubleToString(bf.strength) + "\"";
if (bf.onTop && !bf.innerShadow) {
ret += " type=\"full\"";
} else if (!bf.innerShadow) {
ret += " type=\"outer\"";
}
ret += " />";
} else if (filter instanceof GRADIENTGLOWFILTER) {
GRADIENTGLOWFILTER ggf = (GRADIENTGLOWFILTER) filter;
ret += "<GradientGlowFilter";
ret += " angle=\"" + doubleToString(radToDeg(ggf.angle)) + "\"";
ret += " blurX=\"" + doubleToString(ggf.blurX) + "\"";
ret += " blurY=\"" + doubleToString(ggf.blurY) + "\"";
ret += " quality=\"" + ggf.passes + "\"";
ret += " distance=\"" + doubleToString(ggf.distance) + "\"";
if (ggf.knockout) {
ret += " knockout=\"true\"";
}
ret += " strength=\"" + doubleToString(ggf.strength) + "\"";
if (ggf.onTop && !ggf.innerShadow) {
ret += " type=\"full\"";
} else if (!ggf.innerShadow) {
ret += " type=\"outer\"";
}
ret += ">";
for (int g = 0; g < ggf.gradientColors.length; g++) {
RGBA gc = ggf.gradientColors[g];
ret += "<GradientEntry color=\"" + gc.toHexRGB() + "\"";
if (gc.alpha != 255) {
ret += " alpha=\"" + gc.getAlphaFloat() + "\"";
}
ret += " ratio=\"" + doubleToString(((float) ggf.gradientRatio[g]) / 255.0) + "\"";
ret += "/>";
}
ret += "</GradientGlowFilter>";
} else if (filter instanceof GRADIENTBEVELFILTER) {
GRADIENTBEVELFILTER gbf = (GRADIENTBEVELFILTER) filter;
ret += "<GradientBevelFilter";
ret += " angle=\"" + doubleToString(radToDeg(gbf.angle)) + "\"";
ret += " blurX=\"" + doubleToString(gbf.blurX) + "\"";
ret += " blurY=\"" + doubleToString(gbf.blurY) + "\"";
ret += " quality=\"" + gbf.passes + "\"";
ret += " distance=\"" + doubleToString(gbf.distance) + "\"";
if (gbf.knockout) {
ret += " knockout=\"true\"";
}
ret += " strength=\"" + doubleToString(gbf.strength) + "\"";
if (gbf.onTop && !gbf.innerShadow) {
ret += " type=\"full\"";
} else if (!gbf.innerShadow) {
ret += " type=\"outer\"";
}
ret += ">";
for (int g = 0; g < gbf.gradientColors.length; g++) {
RGBA gc = gbf.gradientColors[g];
ret += "<GradientEntry color=\"" + gc.toHexRGB() + "\"";
if (gc.alpha != 255) {
ret += " alpha=\"" + gc.getAlphaFloat() + "\"";
}
ret += " ratio=\"" + doubleToString(((float) gbf.gradientRatio[g]) / 255.0) + "\"";
ret += "/>";
}
ret += "</GradientBevelFilter>";
} else if (filter instanceof COLORMATRIXFILTER) {
COLORMATRIXFILTER cmf = (COLORMATRIXFILTER) filter;
ret += convertAdjustColorFilter(cmf);
}
return ret;
}
public static String convertSymbolInstance(String name, MATRIX matrix, CXFORM colorTransform, CXFORMWITHALPHA colorTransformAlpha, boolean cacheAsBitmap, int blendMode, List<FILTER> filters, boolean isVisible, RGBA backgroundColor, CharacterTag tag, HashMap<Integer, CharacterTag> characters) {
String ret = "";
if (matrix == null) {
matrix = new MATRIX();
}
ret += "<DOMSymbolInstance libraryItemName=\"" + "Symbol " + tag.getCharacterID() + "\"";
if (name != null) {
ret += " name=\"" + name + "\"";
}
String blendModeStr = null;
if (blendMode < BLENDMODES.length) {
blendModeStr = BLENDMODES[blendMode];
}
if (blendModeStr != null) {
ret += " blendMode=\"" + blendModeStr + "\"";
}
if (tag instanceof ShapeTag) {
ret += " symbolType=\"graphic\" loop=\"loop\"";
} else if (tag instanceof DefineSpriteTag) {
DefineSpriteTag sprite = (DefineSpriteTag) tag;
RECT spriteRect = sprite.getRect(characters);
double centerPoint3DX = twipToPixel(matrix.translateX + spriteRect.getWidth() / 2);
double centerPoint3DY = twipToPixel(matrix.translateY + spriteRect.getHeight() / 2);
ret += " centerPoint3DX=\"" + centerPoint3DX + "\" centerPoint3DY=\"" + centerPoint3DY + "\"";
} else if (tag instanceof ButtonTag) {
ret += " symbolType=\"button\"";
}
if (cacheAsBitmap) {
ret += " cacheAsBitmap=\"true\"";
}
if (!isVisible) {
ret += " isVisible=\"false\"";
}
ret += ">";
ret += "<matrix>";
ret += convertMatrix(matrix);
ret += "</matrix>";
ret += "<transformationPoint><Point/></transformationPoint>";
if (backgroundColor != null) {
ret += "<MatteColor color=\"" + backgroundColor.toHexRGB() + "\"";
if (backgroundColor.alpha != 255) {
ret += " alpha=\"" + doubleToString(backgroundColor.getAlphaFloat()) + "\"";
}
ret += "/>";
}
if (colorTransform != null) {
ret += "<color><Color";
if (colorTransform.hasMultTerms) {
ret += " redMultiplier=\"" + (((float) colorTransform.redMultTerm) / 256.0f) + "\"";
ret += " greenMultiplier=\"" + (((float) colorTransform.greenMultTerm) / 256.0f) + "\"";
ret += " blueMultiplier=\"" + (((float) colorTransform.blueMultTerm) / 256.0f) + "\"";
}
if (colorTransform.hasAddTerms) {
ret += " redOffset=\"" + colorTransform.redAddTerm + "\"";
ret += " greenOffset=\"" + colorTransform.greenAddTerm + "\"";
ret += " blueOffset=\"" + colorTransform.blueAddTerm + "\"";
}
ret += "/></color>";
} else if (colorTransformAlpha != null) {
ret += "<color><Color";
if (colorTransformAlpha.hasMultTerms) {
ret += " alphaMultiplier=\"" + (((float) colorTransformAlpha.alphaMultTerm) / 256.0f) + "\"";
ret += " redMultiplier=\"" + (((float) colorTransformAlpha.redMultTerm) / 256.0f) + "\"";
ret += " greenMultiplier=\"" + (((float) colorTransformAlpha.greenMultTerm) / 256.0f) + "\"";
ret += " blueMultiplier=\"" + (((float) colorTransformAlpha.blueMultTerm) / 256.0f) + "\"";
}
if (colorTransformAlpha.hasAddTerms) {
ret += " alphaOffset=\"" + colorTransformAlpha.alphaAddTerm + "\"";
ret += " redOffset=\"" + colorTransformAlpha.redAddTerm + "\"";
ret += " greenOffset=\"" + colorTransformAlpha.greenAddTerm + "\"";
ret += " blueOffset=\"" + colorTransformAlpha.blueAddTerm + "\"";
}
ret += "/></color>";
}
if (!filters.isEmpty()) {
ret += "<filters>";
for (FILTER f : filters) {
ret += convertFilter(f);
}
ret += "</filters>";
}
ret += "</DOMSymbolInstance>";
return ret;
}
private static long getTimestamp() {
return new Date().getTime() / 1000;
}
public static String convertLibrary(Map<Integer, String> characterClasses, List<Integer> oneInstanceShapes, String backgroundColor, List<Tag> tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files) {
String ret = "";
List<String> media = new ArrayList<String>();
List<String> symbols = new ArrayList<String>();
for (int ch : characters.keySet()) {
CharacterTag symbol = characters.get(ch);
if ((symbol instanceof ShapeTag) && oneInstanceShapes.contains(symbol.getCharacterID())) {
continue; //shapes with 1 ocurrence are not added to library
}
if ((symbol instanceof ShapeTag) || (symbol instanceof DefineSpriteTag) || (symbol instanceof ButtonTag)) {
String symbolStr = "";
symbolStr += "<DOMSymbolItem xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://ns.adobe.com/xfl/2008/\" name=\"Symbol " + symbol.getCharacterID() + "\" lastModified=\"" + getTimestamp() + "\""; //TODO:itemID
if (symbol instanceof ShapeTag) {
symbolStr += " symbolType=\"graphic\"";
} else if (symbol instanceof ButtonTag) {
symbolStr += " symbolType=\"button\"";
if (((ButtonTag) symbol).trackAsMenu()) {
symbolStr += " trackAsMenu=\"true\"";
}
}
if (characterClasses.containsKey(symbol.getCharacterID())) {
symbolStr += " linkageExportForAS=\"true\" linkageClassName=\"" + characterClasses.get(symbol.getCharacterID()) + "\"";
}
symbolStr += ">";
symbolStr += "<timeline>";
String itemIcon = null;
if (symbol instanceof ButtonTag) {
itemIcon = "0";
symbolStr += "<DOMTimeline name=\"Symbol " + symbol.getCharacterID() + "\" currentFrame=\"0\">";
symbolStr += "<layers>";
symbolStr += "<DOMLayer name=\"Layer 1\" current=\"true\" isSelected=\"true\">"; //color=\"#4FFF4F\"
symbolStr += "<frames>";
ButtonTag button = (ButtonTag) symbol;
List<BUTTONRECORD> records = button.getRecords();
String frames[] = {"", "", "", ""};
int lastFrame = 0;
int frame = 0;
for (BUTTONRECORD rec : records) {
CXFORMWITHALPHA colorTransformAlpha = null;
int blendMode = 0;
List<FILTER> filters = new ArrayList<FILTER>();
if (button instanceof DefineButton2Tag) {
colorTransformAlpha = rec.colorTransform;
if (rec.buttonHasBlendMode) {
blendMode = rec.blendMode;
}
if (rec.buttonHasFilterList) {
filters = rec.filterList;
}
}
String recCharStr = convertSymbolInstance(null, null, null, colorTransformAlpha, false, blendMode, filters, true, null, characters.get(rec.characterId), characters);
if (rec.buttonStateUp) {
frame = 1;
}
if (rec.buttonStateOver) {
frame = 2;
}
if (rec.buttonStateDown) {
frame = 3;
}
if (rec.buttonStateHitTest) {
frame = 4;
}
int duration = frame - lastFrame;
lastFrame = frame;
if (duration > 0) {
symbolStr += "<DOMFrame index=\"";
symbolStr += (frame - 1);
symbolStr += "\"";
if (duration > 1) {
symbolStr += " duration=\"" + duration + "\"";
}
symbolStr += " keyMode=\"" + KEY_MODE_NORMAL + "\">";
symbolStr += "<elements>";
symbolStr += recCharStr;
symbolStr += "</elements>";
symbolStr += "</DOMFrame>";
}
}
symbolStr += "</frames>";
symbolStr += "</DOMLayer>";
symbolStr += "</layers>";
symbolStr += "</DOMTimeline>";
} else if (symbol instanceof DefineSpriteTag) {
DefineSpriteTag sprite = (DefineSpriteTag) symbol;
symbolStr += convertTimeline(oneInstanceShapes, backgroundColor, sprite.getSubTags(), characters, "Symbol " + symbol.getCharacterID());
} else if (symbol instanceof ShapeTag) {
itemIcon = "1";
ShapeTag shape = (ShapeTag) symbol;
symbolStr += "<DOMTimeline name=\"Symbol " + symbol.getCharacterID() + "\" currentFrame=\"0\">";
symbolStr += "<layers>";
symbolStr += "<DOMLayer name=\"Layer 1\" current=\"true\" isSelected=\"true\">"; //color=\"#4FFF4F\"
symbolStr += "<frames>";
symbolStr += "<DOMFrame index=\"0\" keyMode=\"" + KEY_MODE_NORMAL + "\">";
symbolStr += "<elements>";
symbolStr += convertShape(characters, null, shape);
symbolStr += "</elements>";
symbolStr += "</DOMFrame>";
symbolStr += "</frames>";
symbolStr += "</DOMLayer>";
symbolStr += "</layers>";
symbolStr += "</DOMTimeline>";
}
symbolStr += "</timeline>";
symbolStr += "</DOMSymbolItem>";
symbolStr = prettyFormatXML(symbolStr);
String symbolFile = "Symbol " + symbol.getCharacterID() + ".xml";
try {
files.put(symbolFile, symbolStr.getBytes("UTF-8"));
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex);
}
String symbLinkStr = "";
symbLinkStr += "<Include href=\"" + symbolFile + "\"";
if (itemIcon != null) {
symbLinkStr += " itemIcon=\"" + itemIcon + "\"";
}
symbLinkStr += " loadImmediate=\"false\" lastModified=\"" + getTimestamp() + "\"/>"; //TODO: itemID=\"518de416-00000341\"
symbols.add(symbLinkStr);
} else if (symbol instanceof ImageTag) {
ImageTag imageTag = (ImageTag) symbol;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedImage image = imageTag.getImage(tags);
String format = imageTag.getImageFormat();
try {
ImageIO.write(image, format.toUpperCase(), baos);
} catch (IOException ex) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex);
}
String symbolFile = "bitmap" + symbol.getCharacterID() + "." + imageTag.getImageFormat();
files.put(symbolFile, baos.toByteArray());
String mediaLinkStr = "<DOMBitmapItem name=\"" + symbolFile + "\" sourceLastImported=\"" + getTimestamp() + "\" externalFileSize=\"" + baos.toByteArray().length + "\"";
if (format.equals("png") || format.equals("gif")) {
mediaLinkStr += " useImportedJPEGData=\"false\" compressionType=\"lossless\" originalCompressionType=\"lossless\"";
} else if (format.equals("jpg")) {
mediaLinkStr += " isJPEG=\"true\"";
}
if (characterClasses.containsKey(symbol.getCharacterID())) {
mediaLinkStr += " linkageExportForAS=\"true\" linkageClassName=\"" + characterClasses.get(symbol.getCharacterID()) + "\"";
}
mediaLinkStr += " quality=\"50\" href=\"" + symbolFile + "\" bitmapDataHRef=\"M " + (media.size() + 1) + " " + getTimestamp() + ".dat\" frameRight=\"" + image.getWidth() + "\" frameBottom=\"" + image.getHeight() + "\"/>\n";
media.add(mediaLinkStr);
}
//TODO: sound, video...
}
if (!media.isEmpty()) {
ret += "<media>";
for (String m : media) {
ret += m;
}
ret += "</media>";
}
if (!symbols.isEmpty()) {
ret += "<symbols>";
for (String s : symbols) {
ret += s;
}
ret += "</symbols>";
}
return ret;
}
private static String prettyFormatXML(String input) {
int indent = 5;
try {
Source xmlInput = new StreamSource(new StringReader(input));
StringWriter stringWriter = new StringWriter();
StreamResult xmlOutput = new StreamResult(stringWriter);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setAttribute("indent-number", indent);
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(xmlInput, xmlOutput);
return xmlOutput.getWriter().toString();
} catch (Exception e) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, "Pretty print error", e);
return input;
}
}
private static String convertFrames(String prevStr, String afterStr, List<Integer> oneInstanceShapes, List<Tag> tags, HashMap<Integer, CharacterTag> characters, int depth) {
String ret = "";
prevStr += "<frames>";
int frame = -1;
String elements = "";
String lastElements = "";
int characterId = -1;
int duration = 1;
for (Tag t : tags) {
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag po = (PlaceObjectTypeTag) t;
if (po.getDepth() == depth) {
int newCharId = po.getCharacterId();
if (newCharId == -1) {
newCharId = characterId;
}
characterId = newCharId;
if (characters.containsKey(characterId)) {
CharacterTag ch = characters.get(characterId);
if ((ch instanceof ShapeTag) && oneInstanceShapes.contains(characterId)) {
elements += convertShape(characters, po.getMatrix(), (ShapeTag) ch);
} else if (ch instanceof TextTag) {
elements += convertText(tags, (TextTag) ch, po.getMatrix(), po.getFilters());
} else {
elements += convertSymbolInstance(po.getName(), po.getMatrix(), po.getColorTransform(), po.getColorTransformWithAlpha(), po.cacheAsBitmap(), po.getBlendMode(), po.getFilters(), po.isVisible(), po.getBackgroundColor(), characters.get(characterId), characters);
}
}
}
}
if (t instanceof ShowFrameTag) {
if (!elements.equals("")) {
if (!lastElements.equals("")) {
ret += "<DOMFrame index=\"" + frame + "\"";
if (duration > 1) {
ret += " duration=\"" + duration + "\"";
}
ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">";
ret += "<elements>";
ret += lastElements;
ret += "</elements>";
ret += "</DOMFrame>";
}
frame += duration;
duration = 1;
lastElements = elements;
elements = "";
} else {
duration++;
}
}
}
if (!lastElements.equals("")) {
ret += "<DOMFrame index=\"" + frame + "\"";
if (duration > 1) {
ret += " duration=\"" + duration + "\"";
}
ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">";
ret += "<elements>";
ret += lastElements;
ret += "</elements>";
ret += "</DOMFrame>";
frame += duration;
duration = 1;
elements = "";
}
afterStr = "</frames>" + afterStr;
if (!ret.equals("")) {
ret = prevStr + ret + afterStr;
}
return ret;
}
public static String convertTimeline(List<Integer> oneInstanceShapes, String backgroundColor, List<Tag> tags, HashMap<Integer, CharacterTag> characters, String name) {
String ret = "";
ret += "<DOMTimeline name=\"" + name + "\">";
ret += "<layers>";
int layerCount = getLayerCount(tags);
Stack<Integer> parentLayers = new Stack<Integer>();
int index = 0;
for (int d = layerCount; d >= 1; d--, index++) {
for (Tag t : tags) {
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag po = (PlaceObjectTypeTag) t;
if (po.getClipDepth() == d) {
for (int m = po.getDepth(); m < po.getClipDepth(); m++) {
parentLayers.push(index);
}
ret += "<DOMLayer name=\"Layer " + index + "\" color=\"" + backgroundColor + "\" ";
ret += " layerType=\"mask\" locked=\"true\"";
ret += ">";
ret += convertFrames("", "", oneInstanceShapes, tags, characters, po.getDepth());
ret += "</DOMLayer>";
index++;
break;
}
}
}
boolean hasClipDepth = false;
for (Tag t : tags) {
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag po = (PlaceObjectTypeTag) t;
if (po.getDepth() == d) {
if (po.getClipDepth() != -1) {
hasClipDepth = true;
break;
}
}
}
}
if (hasClipDepth) {
index--;
continue;
}
int parentLayer = -1;
if (!parentLayers.isEmpty()) {
parentLayer = parentLayers.pop();
}
String layerPrev = "";
layerPrev += "<DOMLayer name=\"Layer " + index + "\" color=\"" + backgroundColor + "\" ";
if (d == 1) {
layerPrev += " current=\"true\" isSelected=\"true\"";
}
if (parentLayer != -1) {
if (parentLayer != d) {
layerPrev += " parentLayerIndex=\"" + (parentLayer) + "\" locked=\"true\"";
}
}
layerPrev += ">";
String layerAfter = "</DOMLayer>";
String cf = convertFrames(layerPrev, layerAfter, oneInstanceShapes, tags, characters, d);
if (cf.equals("")) {
index--;
}
ret += cf;
}
ret += "</layers>";
ret += "</DOMTimeline>";
return ret;
}
private static void writeFile(byte data[], String file) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write(data);
} catch (IOException iex) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, "Error during file write", iex);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException ex) {
//ignore
}
}
}
}
private static Map<Integer, String> getCharacterClasses(List<Tag> tags) {
Map<Integer, String> ret = new HashMap<Integer, String>();
for (Tag t : tags) {
if (t instanceof SymbolClassTag) {
SymbolClassTag sc = (SymbolClassTag) t;
for (int i = 0; i < sc.tagIDs.length; i++) {
if (!ret.containsKey(sc.tagIDs[i]) && !ret.containsValue(sc.classNames[i])) {
ret.put(sc.tagIDs[i], sc.classNames[i]);
}
}
}
}
return ret;
}
public static String convertText(List<Tag> tags, TextTag tag, MATRIX matrix, List<FILTER> filters) {
String ret = "";
if (matrix == null) {
matrix = new MATRIX();
}
CSMTextSettingsTag csmts = null;
String filterStr = "";
if (!filters.isEmpty()) {
filterStr += "<filters>";
for (FILTER f : filters) {
filterStr += convertFilter(f);
}
filterStr += "</filters>";
}
for (Tag t : tags) {
if (t instanceof CSMTextSettingsTag) {
CSMTextSettingsTag c = (CSMTextSettingsTag) t;
if (c.textID == tag.getCharacterID()) {
csmts = c;
break;
}
}
}
String fontRenderingMode = "standard";
String antiAlias = "";
if (csmts != null) {
if (csmts.thickness == 0 & csmts.sharpness == 0) {
fontRenderingMode = null;
} else {
fontRenderingMode = "customThicknessSharpness";
}
antiAlias = " antiAliasSharpness=\"" + doubleToString(csmts.sharpness) + "\" antiAliasThickness=\"" + doubleToString(csmts.thickness) + "\"";
}
String matStr = "";
matStr += "<matrix>";
matStr += convertMatrix(matrix);
matStr += "</matrix>";
if ((tag instanceof DefineTextTag) || (tag instanceof DefineText2Tag)) {
List<TEXTRECORD> textRecords = new ArrayList<TEXTRECORD>();
if (tag instanceof DefineTextTag) {
textRecords = ((DefineTextTag) tag).textRecords;
} else if (tag instanceof DefineText2Tag) {
textRecords = ((DefineText2Tag) tag).textRecords;
}
looprec:
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasFont) {
for (Tag t : tags) {
if (t instanceof FontTag) {
FontTag ft = (FontTag) t;
if (ft.getFontId() == rec.fontId) {
if (ft.isSmall()) {
fontRenderingMode = "bitmap";
break looprec;
}
}
}
}
}
}
ret += "<DOMStaticText";
if (fontRenderingMode != null) {
ret += " fontRenderingMode=\"" + fontRenderingMode + "\"";
}
ret += antiAlias;
ret += " width=\"" + tag.getBounds().getWidth() / 2 + "\" height=\"" + tag.getBounds().getHeight() + "\" autoExpand=\"true\" isSelectable=\"false\">";
ret += matStr;
ret += "<textRuns>";
int fontId = -1;
FontTag font = null;
String fontName = null;
String psFontName = null;
String availableFonts[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
int textHeight = -1;
RGB textColor = null;
RGBA textColorA = null;
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasColor) {
if (tag instanceof DefineTextTag) {
textColor = rec.textColor;
} else {
textColorA = rec.textColorA;
}
}
if (rec.styleFlagsHasFont) {
fontId = rec.fontId;
fontName = null;
textHeight = rec.textHeight;
font = null;
for (Tag t : tags) {
if (t instanceof FontTag) {
if (((FontTag) t).getFontId() == fontId) {
font = (FontTag) t;
}
}
if (t instanceof DefineFontNameTag) {
if (((DefineFontNameTag) t).fontId == fontId) {
fontName = ((DefineFontNameTag) t).fontName;
}
}
}
if ((fontName == null) && (font != null)) {
fontName = font.getFontName(tags);
}
psFontName = null;
if (fontName != null) {
for (String avFont : availableFonts) {
if (avFont.equals(fontName)) {
Font f = new Font(fontName, 0, 10);
psFontName = f.getPSName();
}
}
}
}
ret += "<DOMTextRun>";
ret += "<characters>" + xmlString(rec.getText(tags, font)) + "</characters>";
ret += "<textAttrs>";
ret += "<DOMTextAttrs aliasText=\"false\" rotation=\"true\" indent=\"5\" leftMargin=\"2\" letterSpacing=\"1\" lineSpacing=\"6\" rightMargin=\"3\" size=\"" + twipToPixel(textHeight) + "\" bitmapSize=\"1040\"";
if (textColor != null) {
ret += " fillColor=\"" + textColor.toHexRGB() + "\"";
} else if (textColorA != null) {
ret += " fillColor=\"" + textColorA.toHexRGB() + "\" alpha=\"" + textColorA.getAlphaFloat() + "\"";
}
ret += " face=\"" + psFontName + "\"";
ret += "/>";
ret += "</textAttrs>";
ret += "</DOMTextRun>";
}
ret += "</textRuns>";
ret += filterStr;
ret += "</DOMStaticText>";
} else if (tag instanceof DefineEditTextTag) {
DefineEditTextTag det = (DefineEditTextTag) tag;
String tagName;
for (Tag t : tags) {
if (t instanceof FontTag) {
FontTag ft = (FontTag) t;
if (ft.getFontId() == det.fontId) {
if (ft.isSmall()) {
fontRenderingMode = "bitmap";
break;
}
}
}
}
if (!det.useOutlines) {
fontRenderingMode = "device";
}
if (det.wasStatic) {
tagName = "DOMStaticText";
} else if (det.readOnly) {
tagName = "DOMDynamicText";
} else {
tagName = "DOMInputText";
}
ret += "<" + tagName;
if (fontRenderingMode != null) {
ret += " fontRenderingMode=\"" + fontRenderingMode + "\"";
}
ret += antiAlias;
double width = twipToPixel(det.getBounds().getWidth());
double height = twipToPixel(det.getBounds().getHeight());
if (det.hasLayout) {
width -= twipToPixel(det.rightMargin);
width -= twipToPixel(det.leftMargin);
//height-=det.
}
ret += " width=\"" + width + "\"";
ret += " height=\"" + height + "\"";
if (det.border) {
ret += " border=\"true\"";
}
if (det.html) {
ret += " renderAsHTML=\"true\"";
}
if (det.noSelect) {
ret += " isSelectable=\"false\"";
}
if (det.multiline && det.wordWrap) {
ret += " lineType=\"multiline\"";
} else if (det.multiline && (!det.wordWrap)) {
ret += " lineType=\"multiline no wrap\"";
} else if (det.password) {
ret += " lineType=\"password\"";
}
if (det.hasMaxLength) {
ret += " maxCharacters=\"" + det.maxLength + "\"";
}
if (!det.variableName.equals("")) {
ret += " variableName=\"" + det.variableName + "\"";
}
ret += ">";
ret += matStr;
ret += "<textRuns>";
if (det.hasText) {
ret += convertHTMLText(tags, det, det.initialText);
}
ret += "</textRuns>";
ret += filterStr;
ret += "</" + tagName + ">";
}
return ret;
}
public static void convertSWF(SWF swf, String swfFileName, String outfile, boolean compressed) {
String domDocument = "";
String baseName = swfFileName;
File f = new File(baseName);
baseName = f.getName();
if (baseName.contains(".")) {
baseName = baseName.substring(0, baseName.lastIndexOf("."));
}
domDocument += "<DOMDocument xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://ns.adobe.com/xfl/2008/\" currentTimeline=\"1\" xflVersion=\"2.2\" creatorInfo=\"" + Main.applicationName + "\" platform=\"Windows\" versionInfo=\"Saved by " + Main.applicationVerName + "\" majorVersion=\"" + Main.version + "\" buildNumber=\"\" nextSceneIdentifier=\"2\" playOptionsPlayLoop=\"false\" playOptionsPlayPages=\"false\" playOptionsPlayFrameActions=\"false\" autoSaveHasPrompted=\"true\">";
HashMap<String, byte[]> files = new HashMap<String, byte[]>();
HashMap<Integer, CharacterTag> characters = getCharacters(swf.tags);
List<Integer> oneInstaceShapes = getOneInstanceShapes(swf.tags, characters);
Map<Integer, String> characterClasses = getCharacterClasses(swf.tags);
String backgroundColor = "#ffffff";
for (Tag t : swf.tags) {
if (t instanceof SetBackgroundColorTag) {
SetBackgroundColorTag sbc = (SetBackgroundColorTag) t;
backgroundColor = sbc.backgroundColor.toHexRGB();
}
}
domDocument += convertLibrary(characterClasses, oneInstaceShapes, backgroundColor, swf.tags, characters, files);
domDocument += "<timelines>";
domDocument += convertTimeline(oneInstaceShapes, backgroundColor, swf.tags, characters, "Scene 1");
domDocument += "</timelines>";
domDocument += "</DOMDocument>";
domDocument = prettyFormatXML(domDocument);
boolean asV3Found = false;
for (Tag t : swf.tags) {
if (t instanceof ABCContainerTag) {
asV3Found = true;
}
}
String publishSettings = "<flash_profiles>\n"
+ "<flash_profile version=\"1.0\" name=\"Default\" current=\"true\">\n"
+ " <PublishFormatProperties enabled=\"true\">\n"
+ " <defaultNames>1</defaultNames>\n"
+ " <flash>1</flash>\n"
+ " <projectorWin>0</projectorWin>\n"
+ " <projectorMac>0</projectorMac>\n"
+ " <html>1</html>\n"
+ " <gif>0</gif>\n"
+ " <jpeg>0</jpeg>\n"
+ " <png>0</png>\n"
+ " <qt>0</qt>\n"
+ " <rnwk>0</rnwk>\n"
+ " <swc>0</swc>\n"
+ " <flashDefaultName>1</flashDefaultName>\n"
+ " <projectorWinDefaultName>1</projectorWinDefaultName>\n"
+ " <projectorMacDefaultName>1</projectorMacDefaultName>\n"
+ " <htmlDefaultName>1</htmlDefaultName>\n"
+ " <gifDefaultName>1</gifDefaultName>\n"
+ " <jpegDefaultName>1</jpegDefaultName>\n"
+ " <pngDefaultName>1</pngDefaultName>\n"
+ " <qtDefaultName>1</qtDefaultName>\n"
+ " <rnwkDefaultName>1</rnwkDefaultName>\n"
+ " <swcDefaultName>1</swcDefaultName>\n"
+ " <flashFileName>" + baseName + ".swf</flashFileName>\n"
+ " <projectorWinFileName>" + baseName + ".exe</projectorWinFileName>\n"
+ " <projectorMacFileName>" + baseName + ".app</projectorMacFileName>\n"
+ " <htmlFileName>" + baseName + ".html</htmlFileName>\n"
+ " <gifFileName>" + baseName + ".gif</gifFileName>\n"
+ " <jpegFileName>" + baseName + ".jpg</jpegFileName>\n"
+ " <pngFileName>" + baseName + ".png</pngFileName>\n"
+ " <qtFileName>" + baseName + ".mov</qtFileName>\n"
+ " <rnwkFileName>" + baseName + ".smil</rnwkFileName>\n"
+ " <swcFileName>Untitled-4.swc</swcFileName>\n"
+ " </PublishFormatProperties>\n"
+ " <PublishHtmlProperties enabled=\"true\">\n"
+ " <VersionDetectionIfAvailable>0</VersionDetectionIfAvailable>\n"
+ " <VersionInfo>12,0,0,0;11,2,0,0;11,1,0,0;10,3,0,0;10,2,153,0;10,1,52,0;9,0,124,0;8,0,24,0;7,0,14,0;6,0,79,0;5,0,58,0;4,0,32,0;3,0,8,0;2,0,1,12;1,0,0,1;</VersionInfo>\n"
+ " <UsingDefaultContentFilename>1</UsingDefaultContentFilename>\n"
+ " <UsingDefaultAlternateFilename>1</UsingDefaultAlternateFilename>\n"
+ " <ContentFilename>" + baseName + "_content.html</ContentFilename>\n"
+ " <AlternateFilename>" + baseName + "_alternate.html</AlternateFilename>\n"
+ " <UsingOwnAlternateFile>0</UsingOwnAlternateFile>\n"
+ " <OwnAlternateFilename></OwnAlternateFilename>\n"
+ " <Width>" + twipToPixel(swf.displayRect.getWidth()) + "</Width>\n"
+ " <Height>" + twipToPixel(swf.displayRect.getHeight()) + "</Height>\n"
+ " <Align>0</Align>\n"
+ " <Units>0</Units>\n"
+ " <Loop>1</Loop>\n"
+ " <StartPaused>0</StartPaused>\n"
+ " <Scale>0</Scale>\n"
+ " <HorizontalAlignment>1</HorizontalAlignment>\n"
+ " <VerticalAlignment>1</VerticalAlignment>\n"
+ " <Quality>4</Quality>\n"
+ " <DeblockingFilter>0</DeblockingFilter>\n"
+ " <WindowMode>0</WindowMode>\n"
+ " <DisplayMenu>1</DisplayMenu>\n"
+ " <DeviceFont>0</DeviceFont>\n"
+ " <TemplateFileName></TemplateFileName>\n"
+ " <showTagWarnMsg>1</showTagWarnMsg>\n"
+ " </PublishHtmlProperties>\n"
+ " <PublishFlashProperties enabled=\"true\">\n"
+ " <TopDown></TopDown>\n"
+ " <FireFox></FireFox>\n"
+ " <Report>0</Report>\n"
+ " <Protect>0</Protect>\n"
+ " <OmitTraceActions>0</OmitTraceActions>\n"
+ " <Quality>80</Quality>\n"
+ " <DeblockingFilter>0</DeblockingFilter>\n"
+ " <StreamFormat>0</StreamFormat>\n"
+ " <StreamCompress>7</StreamCompress>\n"
+ " <EventFormat>0</EventFormat>\n"
+ " <EventCompress>7</EventCompress>\n"
+ " <OverrideSounds>0</OverrideSounds>\n"
+ " <Version>15</Version>\n"
+ " <ExternalPlayer>FlashPlayer11.2</ExternalPlayer>\n"
+ " <ActionScriptVersion>" + (asV3Found ? "3" : "2") + "</ActionScriptVersion>\n"
+ " <PackageExportFrame>1</PackageExportFrame>\n"
+ " <PackagePaths></PackagePaths>\n"
+ " <AS3PackagePaths>.</AS3PackagePaths>\n"
+ " <AS3ConfigConst>CONFIG::FLASH_AUTHORING=&quot;true&quot;;</AS3ConfigConst>\n"
+ " <DebuggingPermitted>0</DebuggingPermitted>\n"
+ " <DebuggingPassword></DebuggingPassword>\n"
+ " <CompressMovie>" + (swf.compressed ? "1" : "0") + "</CompressMovie>\n"
+ " <CompressionType>" + (swf.lzma ? "1" : "0") + "</CompressionType>\n"
+ " <InvisibleLayer>1</InvisibleLayer>\n"
+ " <DeviceSound>0</DeviceSound>\n"
+ " <StreamUse8kSampleRate>0</StreamUse8kSampleRate>\n"
+ " <EventUse8kSampleRate>0</EventUse8kSampleRate>\n"
+ " <UseNetwork>0</UseNetwork>\n"
+ " <DocumentClass>" + (characterClasses.containsKey(0) ? characterClasses.get(0) : "") + "</DocumentClass>\n"
+ " <AS3Strict>2</AS3Strict>\n"
+ " <AS3Coach>4</AS3Coach>\n"
+ " <AS3AutoDeclare>4096</AS3AutoDeclare>\n"
+ " <AS3Dialect>AS3</AS3Dialect>\n"
+ " <AS3ExportFrame>1</AS3ExportFrame>\n"
+ " <AS3Optimize>1</AS3Optimize>\n"
+ " <ExportSwc>0</ExportSwc>\n"
+ " <ScriptStuckDelay>15</ScriptStuckDelay>\n"
+ " <IncludeXMP>1</IncludeXMP>\n"
+ " <HardwareAcceleration>0</HardwareAcceleration>\n"
+ " <AS3Flags>4102</AS3Flags>\n"
+ " <DefaultLibraryLinkage>rsl</DefaultLibraryLinkage>\n"
+ " <RSLPreloaderMethod>wrap</RSLPreloaderMethod>\n"
+ " <RSLPreloaderSWF>$(AppConfig)/ActionScript 3.0/rsls/loader_animation.swf</RSLPreloaderSWF>\n"
+ " <LibraryPath>\n"
+ " <library-path-entry>\n"
+ " <swc-path>$(AppConfig)/ActionScript 3.0/libs</swc-path>\n"
+ " <linkage>merge</linkage>\n"
+ " </library-path-entry>\n"
+ " <library-path-entry>\n"
+ " <swc-path>$(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc</swc-path>\n"
+ " <linkage usesDefault=\"true\">rsl</linkage>\n"
+ " <rsl-url>http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz</rsl-url>\n"
+ " <policy-file-url>http://fpdownload.adobe.com/pub/swz/crossdomain.xml</policy-file-url>\n"
+ " <rsl-url>textLayout_2.0.0.232.swz</rsl-url>\n"
+ " </library-path-entry>\n"
+ " </LibraryPath>\n"
+ " <LibraryVersions>\n"
+ " <library-version>\n"
+ " <swc-path>$(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc</swc-path>\n"
+ " <feature name=\"tlfText\" majorVersion=\"2\" minorVersion=\"0\" build=\"232\"/>\n"
+ " <rsl-url>http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz</rsl-url>\n"
+ " <policy-file-url>http://fpdownload.adobe.com/pub/swz/crossdomain.xml</policy-file-url>\n"
+ " <rsl-url>textLayout_2.0.0.232.swz</rsl-url>\n"
+ " </library-version>\n"
+ " </LibraryVersions>\n"
+ " </PublishFlashProperties>\n"
+ " <PublishJpegProperties enabled=\"true\">\n"
+ " <Width>" + twipToPixel(swf.displayRect.getWidth()) + "</Width>\n"
+ " <Height>" + twipToPixel(swf.displayRect.getHeight()) + "</Height>\n"
+ " <Progressive>0</Progressive>\n"
+ " <DPI>4718592</DPI>\n"
+ " <Size>0</Size>\n"
+ " <Quality>80</Quality>\n"
+ " <MatchMovieDim>1</MatchMovieDim>\n"
+ " </PublishJpegProperties>\n"
+ " <PublishRNWKProperties enabled=\"true\">\n"
+ " <exportFlash>1</exportFlash>\n"
+ " <flashBitRate>0</flashBitRate>\n"
+ " <exportAudio>1</exportAudio>\n"
+ " <audioFormat>0</audioFormat>\n"
+ " <singleRateAudio>0</singleRateAudio>\n"
+ " <realVideoRate>100000</realVideoRate>\n"
+ " <speed28K>1</speed28K>\n"
+ " <speed56K>1</speed56K>\n"
+ " <speedSingleISDN>0</speedSingleISDN>\n"
+ " <speedDualISDN>0</speedDualISDN>\n"
+ " <speedCorporateLAN>0</speedCorporateLAN>\n"
+ " <speed256K>0</speed256K>\n"
+ " <speed384K>0</speed384K>\n"
+ " <speed512K>0</speed512K>\n"
+ " <exportSMIL>1</exportSMIL>\n"
+ " </PublishRNWKProperties>\n"
+ " <PublishGifProperties enabled=\"true\">\n"
+ " <Width>" + twipToPixel(swf.displayRect.getWidth()) + "</Width>\n"
+ " <Height>" + twipToPixel(swf.displayRect.getHeight()) + "</Height>\n"
+ " <Animated>0</Animated>\n"
+ " <MatchMovieDim>1</MatchMovieDim>\n"
+ " <Loop>1</Loop>\n"
+ " <LoopCount></LoopCount>\n"
+ " <OptimizeColors>1</OptimizeColors>\n"
+ " <Interlace>0</Interlace>\n"
+ " <Smooth>1</Smooth>\n"
+ " <DitherSolids>0</DitherSolids>\n"
+ " <RemoveGradients>0</RemoveGradients>\n"
+ " <TransparentOption></TransparentOption>\n"
+ " <TransparentAlpha>128</TransparentAlpha>\n"
+ " <DitherOption></DitherOption>\n"
+ " <PaletteOption></PaletteOption>\n"
+ " <MaxColors>255</MaxColors>\n"
+ " <PaletteName></PaletteName>\n"
+ " </PublishGifProperties>\n"
+ " <PublishPNGProperties enabled=\"true\">\n"
+ " <Width>" + twipToPixel(swf.displayRect.getWidth()) + "</Width>\n"
+ " <Height>" + twipToPixel(swf.displayRect.getHeight()) + "</Height>\n"
+ " <OptimizeColors>1</OptimizeColors>\n"
+ " <Interlace>0</Interlace>\n"
+ " <Transparent>0</Transparent>\n"
+ " <Smooth>1</Smooth>\n"
+ " <DitherSolids>0</DitherSolids>\n"
+ " <RemoveGradients>0</RemoveGradients>\n"
+ " <MatchMovieDim>1</MatchMovieDim>\n"
+ " <DitherOption></DitherOption>\n"
+ " <FilterOption></FilterOption>\n"
+ " <PaletteOption></PaletteOption>\n"
+ " <BitDepth>24-bit with Alpha</BitDepth>\n"
+ " <MaxColors>255</MaxColors>\n"
+ " <PaletteName></PaletteName>\n"
+ " </PublishPNGProperties>\n"
+ " <PublishQTProperties enabled=\"true\">\n"
+ " <Width>" + twipToPixel(swf.displayRect.getWidth()) + "</Width>\n"
+ " <Height>" + twipToPixel(swf.displayRect.getHeight()) + "</Height>\n"
+ " <MatchMovieDim>1</MatchMovieDim>\n"
+ " <UseQTSoundCompression>0</UseQTSoundCompression>\n"
+ " <AlphaOption></AlphaOption>\n"
+ " <LayerOption></LayerOption>\n"
+ " <QTSndSettings>00000000</QTSndSettings>\n"
+ " <ControllerOption>0</ControllerOption>\n"
+ " <Looping>0</Looping>\n"
+ " <PausedAtStart>0</PausedAtStart>\n"
+ " <PlayEveryFrame>0</PlayEveryFrame>\n"
+ " <Flatten>1</Flatten>\n"
+ " </PublishQTProperties>\n"
+ "</flash_profile>\n"
+ "</flash_profiles>";
if (compressed) {
ZipOutputStream out = null;
try {
out = new ZipOutputStream(new FileOutputStream(outfile));
out.putNextEntry(new ZipEntry("DOMDocument.xml"));
out.write(domDocument.getBytes("UTF-8"));
out.putNextEntry(new ZipEntry("PublishSettings.xml"));
out.write(publishSettings.getBytes("UTF-8"));
for (String fileName : files.keySet()) {
out.putNextEntry(new ZipEntry("LIBRARY/" + fileName));
out.write(files.get(fileName));
}
} catch (IOException ex) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException ex) {
//ignore
}
}
}
} else {
File xfl = new File(outfile);
File outDir = xfl.getParentFile();
outDir.mkdirs();
try {
writeFile(domDocument.getBytes("UTF-8"), outDir.getAbsolutePath() + File.separator + "DOMDocument.xml");
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex);
}
try {
writeFile(publishSettings.getBytes("UTF-8"), outDir.getAbsolutePath() + File.separator + "PublishSettings.xml");
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex);
}
File libraryDir = new File(outDir.getAbsolutePath() + File.separator + "LIBRARY");
libraryDir.mkdir();
for (String fileName : files.keySet()) {
writeFile(files.get(fileName), libraryDir.getAbsolutePath() + File.separator + fileName);
}
writeFile("PROXY-CS5".getBytes(), outfile);
}
}
private static int normHue(double h) {
if (Double.isNaN(h)) {
h = -Math.PI;
}
int ret = (int) Math.round(h * 180 / Math.PI);
while (ret > 180) {
ret -= 360;
}
while (ret < -180) {
ret += 360;
}
return ret;
}
private static int normBrightness(double b) {
if (Double.isNaN(b)) {
b = -100;
}
return (int) Math.round(b);
}
private static int normSaturation(double s) {
if (Double.isNaN(s)) {
return -100;
} else if (s == 1) {
return 0;
} else if (s - 1 < 0) {
return (int) Math.round((s - 1) * 100);
} else {
return (int) Math.round(((s - 1) * 100) / 3);
}
}
private static int normContrast(double c) {
double ctrMap[] = {
// 0 1 2 3 4 5 6 7 8 9
/*0*/0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
/*1*/ 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
/*2*/ 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
/*3*/ 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
/*4*/ 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
/*5*/ 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
/*6*/ 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
/*7*/ 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
/*8*/ 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
/*9*/ 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
/*10*/ 10.0};
if (c == 127) {
return 0;
} else if (c - 127 < 0) {
return (int) Math.round((c - 127) * 100.0 / 127.0);
} else {
c = (c - 127) / 127;
for (int i = 0; i < ctrMap.length; i++) {
if (ctrMap[i] >= c) {
return i;
}
}
}
return ctrMap.length - 1;
}
private static boolean sameDouble(double a, double b) {
final double EPSILON = 0.00001;
return a == b ? true : Math.abs(a - b) < EPSILON;
}
public static String convertAdjustColorFilter(COLORMATRIXFILTER filter) {
float matrix[][] = new float[5][5];
int index = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 5; j++) {
matrix[j][i] = filter.matrix[index];
index++;
}
}
double a11 = matrix[0][0], a12 = matrix[0][1], a13 = matrix[0][2],
a21 = matrix[1][0], a22 = matrix[1][1], a23 = matrix[1][2],
a31 = matrix[2][0], a32 = matrix[2][1], a33 = matrix[2][2],
a41 = matrix[4][0];
double b, c, h, s;
b = (24872168661075.0 * a11 * a11 - 151430415740925.0 * a12 + 341095051289483.0 * a12 * a12 - 15302094789450.0 * a13 + 82428663495404.0 * a12 * a13
- 4592294873812.0 * a13 * a13 + 43556251470.0 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13
- 930 * a11 * (287 * a12 + 178 * a13)) + 2384730956550.0 * a12 * a41 + 240977870700.0 * a13 * a41
- 685925220 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13))
* a41 + 465 * a11 * (466201717582.0 * a12 + 55756962908.0 * a13 + 764132175 * (-127 + 2 * a41)))
/ (391687695450.0 * a11 * a11 + 5371575610858.0 * a12 * a12 + 1298089188904.0 * a12 * a13 - 72319604312.0 * a13 * a13
+ 1860 * a11 * (1835439833 * a12 + 219515602 * a13));
c = (127 * (495225 * a11 + 1661845 * a12 + 167930 * a13
+ 478 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13))))
/ 717495;
h = 2 * (Math.atan((-465 * a11 + 287 * a12 + 178 * a13 + Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13
- 930 * a11 * (287 * a12 + 178 * a13))) / (500. * (a12 - a13))) + Math.PI/*+ Pi*C(1)*/);
s = (1543 * (-103355550 * a11 * a11 - 158872382 * a12 * a12 + 190161784 * a12 * a13 - 134644952 * a13 * a13
+ 1661845 * a12 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13
- 930 * a11 * (287 * a12 + 178 * a13)) + 167930 * a13
* Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13))
+ 465 * a11 * (274372 * a12 + 170168 * a13 + 1065 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13
+ 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13)))))
/ (195843847725.0 * a11 * a11 + 2685787805429.0 * a12 * a12 + 649044594452.0 * a12 * a13 - 36159802156.0 * a13 * a13
+ 930 * a11 * (1835439833 * a12 + 219515602 * a13));
if (sameDouble(410 * a12, 1543 * a31) && sameDouble(410 * a12, 1543 * a32) && sameDouble(3047 * a12, 1543 * a21) && sameDouble(3047 * a12, 1543 * a23)
&& sameDouble(a22, a11 + (1504 * a12) / 1543.) && sameDouble((1133 * a12) / 1543. + a33, a11)
/*&& (b == (195961 * a11 + 439039 * a12 + 1543 * (-127 + 2 * a41)) / (3086 * a11 + 6914 * a12))
&& (c == 127 * a11 + (439039 * a12) / 1543.) && (s == (1543 * (a11 - a12)) / (1543 * a11 + 3457 * a12))
*/ && !sameDouble(a11, a12) && !sameDouble(1543 * a11 + 3457 * a12, 0)) {
h = 0;
}
return "<AdjustColorFilter brightness=\"" + normBrightness(b) + "\" contrast=\"" + normContrast(c) + "\" saturation=\"" + normSaturation(s) + "\" hue=\"" + normHue(h) + "\"/>";
}
private static String convertHTMLText(List<Tag> tags, DefineEditTextTag det, String html) {
HTMLTextParser tparser = new HTMLTextParser(tags, det);
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(tparser);
reader.setErrorHandler(tparser);
reader.parse(new InputSource(new InputStreamReader(new ByteArrayInputStream(html.getBytes("UTF-8")), "UTF-8")));
} catch (Exception e) {
Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, "Error while converting HTML", e);
}
return tparser.result;
}
private static String xmlString(String s) {
return s.replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;").replace("&", "&amp;").replace("\r\n", "&#xD;").replace("\r", "&#xD;").replace("\n", "&#xD;");
}
private static double twipToPixel(double tw) {
return tw / 20.0;
}
private static class HTMLTextParser extends DefaultHandler {
public String result = "";
private String fontFace = "";
private String color = "";
private int size = -1;
private int indent = -1;
private int leftMargin = -1;
private int rightMargin = -1;
private int lineSpacing = -1;
private double letterSpacing = -1;
private String alignment = null;
private List<Tag> tags;
private boolean bold = false;
private boolean italic = false;
private boolean underline = false;
private boolean li = false;
private String url = null;
private String target = null;
public HTMLTextParser(List<Tag> tags, DefineEditTextTag det) {
if (det.hasLayout) {
leftMargin = det.leftMargin;
rightMargin = det.rightMargin;
indent = det.indent;
lineSpacing = det.leading;
String alignNames[] = {"left", "right", "center", "justify"};
alignment = alignNames[det.align];
}
this.tags = tags;
}
@Override
public void startDocument() throws SAXException {
}
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
if (qName.equals("a")) {
String href = attributes.getValue("href");
if (href != null) {
url = href;
}
String t = attributes.getValue("target");
if (t != null) {
target = t;
}
} else if (qName.equals("b")) {
bold = true;
} else if (qName.equals("i")) {
italic = true;
} else if (qName.equals("u")) {
underline = true;
} else if (qName.equals("li")) {
li = true;
} else if (qName.equals("p")) {
String a = attributes.getValue("align");
if (a != null) {
alignment = a;
}
if (!result.equals("")) {
putText("\r\n");
}
} else if (qName.equals("font")) {
//kerning ?
String ls = attributes.getValue("letterSpacing");
if (ls != null) {
letterSpacing = Double.parseDouble(ls);
}
String s = attributes.getValue("size");
if (s != null) {
size = Integer.parseInt(s);
}
String c = attributes.getValue("color");
if (c != null) {
color = c;
}
String f = attributes.getValue("face");
if (f != null) {
for (Tag t : tags) {
if (t instanceof FontTag) {
FontTag ft = (FontTag) t;
String fontName = "";
if (f.equals(ft.getFontName(tags))) {
for (Tag u : tags) {
if (u instanceof DefineFontNameTag) {
if (((DefineFontNameTag) u).fontId == ft.getFontId()) {
fontName = ((DefineFontNameTag) u).fontName;
}
}
}
if (fontName == null) {
fontName = ft.getFontName(tags);
}
fontFace = new Font(fontName, (italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0) | (!italic && !bold ? Font.PLAIN : 0), size < 0 ? 10 : size).getPSName();
break;
}
}
}
}
}
//textformat,tab,br
}
@Override
public void endElement(String uri, String localName,
String qName) throws SAXException {
if (qName.equals("a")) {
url = null;
target = null;
}
if (qName.equals("b")) {
bold = false;
}
if (qName.equals("i")) {
italic = false;
}
if (qName.equals("u")) {
underline = false;
}
if (qName.equals("li")) {
li = false;
}
}
private void putText(String txt) {
result += "<DOMTextRun>";
result += "<characters>" + xmlString(txt) + "</characters>";
result += "<textAttrs>";
result += "<DOMTextAttrs";
if (alignment != null) {
result += " alignment=\"" + alignment + "\"";
}
result += " rotation=\"true\""; //?
if (indent > -1) {
result += " indent=\"" + twipToPixel(indent) + "\"";
}
if (leftMargin > -1) {
result += " leftMargin=\"" + twipToPixel(leftMargin) + "\"";
}
if (letterSpacing > -1) {
result += " letterSpacing=\"" + letterSpacing + "\"";
}
if (lineSpacing > -1) {
result += " lineSpacing=\"" + twipToPixel(lineSpacing) + "\"";
}
if (rightMargin > -1) {
result += " rightMargin=\"" + twipToPixel(rightMargin) + "\"";
}
if (size > -1) {
result += " size=\"" + size + "\"";
result += " bitmapSize=\"" + (size * 20) + "\"";
}
if (fontFace != null) {
result += " face=\"" + fontFace + "\"";
}
if (color != null) {
result += " fillColor=\"" + color + "\"";
}
if (url != null) {
result += " url=\"" + url + "\"";
}
if (target != null) {
result += " target=\"" + target + "\"";
}
result += "/>";
result += "</textAttrs>";
result += "</DOMTextRun>";
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
putText(new String(ch, start, length));
}
@Override
public void endDocument() {
}
}
}