PDF text is written directly instead of transparently

This commit is contained in:
Jindra Petřík
2021-03-22 22:48:53 +01:00
parent 0d8e1d05e8
commit 62aabf1057
4 changed files with 164 additions and 133 deletions

View File

@@ -1,6 +1,16 @@
package com.jpexs.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.tags.DefineTextTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.DynamicTextGlyphEntry;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import gnu.jpdf.PDFGraphics;
import java.awt.Color;
import java.awt.Composite;
@@ -24,22 +34,29 @@ import java.awt.image.BufferedImageOp;
import java.awt.image.ImageObserver;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderableImage;
import java.io.File;
import java.io.IOException;
import java.text.AttributedCharacterIterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author JPEXS
*/
public class DualPdfGraphics2D extends Graphics2D implements BlendModeSetable, GraphicsGroupable {
public class DualPdfGraphics2D extends Graphics2D implements BlendModeSetable, GraphicsGroupable, GraphicsTextDrawable {
private final Graphics2D imageGraphics;
private final PDFGraphics pdfGraphics;
private final Map<Integer, Font> existingFonts;
public DualPdfGraphics2D(Graphics2D first, PDFGraphics second) {
public DualPdfGraphics2D(Graphics2D first, PDFGraphics second, Map<Integer, Font> existingFonts) {
this.imageGraphics = first;
this.pdfGraphics = second;
this.existingFonts = existingFonts;
}
@Override
@@ -523,4 +540,125 @@ public class DualPdfGraphics2D extends Graphics2D implements BlendModeSetable, G
pdfGraphics.drawXObject(g);
}
@Override
public void drawTextRecords(SWF swf, List<TEXTRECORD> textRecords, int numText, MATRIX textMatrixM, Matrix transformation, ColorTransform colorTransform) {
Matrix textMatrix = new Matrix(textMatrixM);
Matrix mat = transformation.clone();
Matrix mat0 = mat.concatenate(textMatrix);
Matrix trans = mat0.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor));
FontTag font = null;
int textHeight = 12;
int x = 0;
int y = 0;
int textColor = 0;
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasColor) {
if (numText > 1) {
textColor = rec.textColorA.toInt();
} else {
textColor = rec.textColor.toInt();
}
if (colorTransform != null) {
textColor = colorTransform.apply(textColor);
}
}
if (rec.styleFlagsHasFont) {
font = swf.getFont(rec.fontId);
textHeight = rec.textHeight;
}
if (rec.styleFlagsHasXOffset) {
int offsetX = rec.xOffset;
x = offsetX;
}
if (rec.styleFlagsHasYOffset) {
int offsetY = rec.yOffset;
y = offsetY;
}
StringBuilder text = new StringBuilder();
int deltaX = 0;
setColor(Color.green);
for (int i = 0; i < rec.glyphEntries.size(); i++) {
GLYPHENTRY entry = rec.glyphEntries.get(i);
GLYPHENTRY nextEntry = i < rec.glyphEntries.size() - 1 ? rec.glyphEntries.get(i + 1) : null;
if (entry.glyphIndex != -1) {
Character currentChar = font.glyphToChar(entry.glyphIndex);
Character nextChar = nextEntry == null ? null : font.glyphToChar(nextEntry.glyphIndex);
int calcAdvance = StaticTextTag.getAdvance(font, entry.glyphIndex, textHeight, currentChar, nextChar);
int spacing = entry.glyphAdvance - calcAdvance;
char ch = font.glyphToChar(entry.glyphIndex);
if (spacing != 0) {
if (!text.isEmpty()) {
drawText(x, y, trans, textColor, existingFonts, font, text.toString(), textHeight, pdfGraphics);
}
drawText(x + deltaX, y, trans, textColor, existingFonts, font, "" + currentChar, textHeight, pdfGraphics);
text = new StringBuilder();
x = x + deltaX + entry.glyphAdvance;
deltaX = 0;
} else {
text.append(ch);
deltaX += entry.glyphAdvance;
}
} else if (entry instanceof DynamicTextGlyphEntry) {
DynamicTextGlyphEntry dynamicEntry = (DynamicTextGlyphEntry) entry;
text.append(dynamicEntry.character);
deltaX += entry.glyphAdvance;
}
}
if (!text.isEmpty()) {
drawText(x, y, trans, textColor, existingFonts, font, text.toString(), textHeight, pdfGraphics);
}
}
}
private static void drawText(float x, float y, Matrix trans, int textColor, Map<Integer, Font> existingFonts, FontTag font, String text, int textHeight, PDFGraphics g) {
int fontId = font.getFontId();
if (existingFonts.containsKey(fontId)) {
g.setExistingTtfFont(existingFonts.get(fontId).deriveFont((float) textHeight));
} else {
if (font.getCharacterCount() < 1) {
String fontName = font.getFontName();
File fontFile = FontTag.fontNameToFile(fontName);
if (fontFile == null) {
fontFile = FontTag.fontNameToFile("Times New Roman");
}
if (fontFile == null) {
fontFile = FontTag.fontNameToFile("Arial");
}
if (fontFile == null) {
throw new RuntimeException("Font " + fontName + " not found in your system");
}
Font f = new Font("/MYFONT" + fontId, font.getFontStyle(), textHeight);
existingFonts.put(fontId, f);
try {
g.setTtfFont(f, fontFile);
} catch (IOException ex) {
Logger.getLogger(FrameExporter.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
FontExporter fe = new FontExporter();
File tempFile;
try {
tempFile = File.createTempFile("ffdec_font_export_", ".ttf");
fe.exportFont(font, FontExportMode.TTF, tempFile);
Font f = new Font("/MYFONT" + fontId, font.getFontStyle(), textHeight);
existingFonts.put(fontId, f);
g.setTtfFont(f, tempFile);
} catch (IOException ex) {
Logger.getLogger(FrameExporter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
g.setTransform(trans.toTransform());
Color textColor2 = new Color(textColor, true);
g.setColor(textColor2);
g.drawString(text, (float) x, (float) y);
}
}

View File

@@ -532,7 +532,7 @@ public class FrameExporter {
return compositeGraphics;
}
final Graphics2D parentGraphics = (Graphics2D) super.getGraphics();
compositeGraphics = new DualPdfGraphics2D(parentGraphics, (PDFGraphics) g);
compositeGraphics = new DualPdfGraphics2D(parentGraphics, (PDFGraphics) g, existingFonts);
return compositeGraphics;
}
@@ -562,8 +562,6 @@ public class FrameExporter {
} catch (Exception ex) {
ex.printStackTrace();
}
printStringsToImage(existingFonts, g, fframe, swf, tim, transformation);
g.dispose();
/*if (frameImages.hasNext()) {
img = frameImages.next();
@@ -590,134 +588,6 @@ public class FrameExporter {
return ret;
}
private static void printStringsToImage(Map<Integer, Font> existingFonts, Graphics2D g, int frame, SWF swf, Timeline tim, Matrix transformation) {
double unzoom = SWF.unitDivisor;
Matrix absoluteTransformation = transformation;
int maxDepth = tim.getMaxDepth();
int time = frame;
Frame frameObj = tim.getFrame(frame);
for (int i = 1; i <= maxDepth; i++) {
if (!frameObj.layers.containsKey(i)) {
continue;
}
DepthState layer = frameObj.layers.get(i);
if (!swf.getCharacters().containsKey(layer.characterId)) {
continue;
}
if (!layer.isVisible) {
continue;
}
CharacterTag character = swf.getCharacter(layer.characterId);
Matrix layerMatrix = new Matrix(layer.matrix);
Matrix absMat = absoluteTransformation.concatenate(layerMatrix);
if (character instanceof DrawableTag) {
printStringsDrawDrawable(existingFonts, g, swf, layerMatrix, transformation, absMat, time, layer.ratio, (DrawableTag) character, unzoom, layer.colorTransForm);
}
}
}
private static void printStringsDrawDrawable(Map<Integer, Font> existingFonts, Graphics2D g, SWF swf, Matrix layerMatrix, Matrix transformation, Matrix absMat, int time, int ratio, DrawableTag drawable, double unzoom, ColorTransform colorTransform) {
int drawableFrameCount = drawable.getNumFrames();
if (drawableFrameCount == 0) {
drawableFrameCount = 1;
}
Matrix mat = transformation.concatenate(layerMatrix);
int dframe = time % drawableFrameCount;
Matrix m = mat; //mat.preConcatenate(Matrix.getTranslateInstance(-rect.xMin, -rect.yMin));
if (drawable instanceof DefineSpriteTag) {
printStringsToImage(existingFonts, g, dframe, swf, ((Timelined) drawable).getTimeline(), m);
} else if (drawable instanceof TextTag) {
TextTag textTag = (TextTag) drawable;
List<TEXTRECORD> textRecords = new ArrayList<>();
if (textTag instanceof StaticTextTag) {
textRecords = ((StaticTextTag) textTag).textRecords;
} else if (textTag instanceof DefineEditTextTag) {
DefineEditTextTag editText = (DefineEditTextTag) textTag;
if (editText.hasText) {
textRecords = editText.getTextRecords();
}
}
Matrix textMatrix = new Matrix(textTag.getTextMatrix());
Matrix mat0 = mat.concatenate(textMatrix);
Matrix trans = mat0.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor));
FontTag font = null;
int textHeight = 12;
int x = 0;
int y = 0;
int textColor = 0;
for (TEXTRECORD rec : textRecords) {
if (rec.styleFlagsHasColor) {
if (!(textTag instanceof DefineTextTag)) {
textColor = rec.textColorA.toInt();
} else {
textColor = rec.textColor.toInt();
}
if (colorTransform != null) {
textColor = colorTransform.apply(textColor);
}
}
if (rec.styleFlagsHasFont) {
font = swf.getFont(rec.fontId);
textHeight = rec.textHeight;
}
if (rec.styleFlagsHasXOffset) {
int offsetX = rec.xOffset;
x = offsetX;
}
if (rec.styleFlagsHasYOffset) {
int offsetY = rec.yOffset;
y = offsetY;
}
StringBuilder text = new StringBuilder();
int deltaX = 0;
g.setColor(Color.green);
for (int i = 0; i < rec.glyphEntries.size(); i++) {
GLYPHENTRY entry = rec.glyphEntries.get(i);
GLYPHENTRY nextEntry = i < rec.glyphEntries.size() - 1 ? rec.glyphEntries.get(i + 1) : null;
Character currentChar = font.glyphToChar(entry.glyphIndex);
Character nextChar = nextEntry == null ? null : font.glyphToChar(nextEntry.glyphIndex);
if (entry.glyphIndex != -1) {
int calcAdvance = StaticTextTag.getAdvance(font, entry.glyphIndex, textHeight, currentChar, nextChar);
int spacing = entry.glyphAdvance - calcAdvance;
char ch = font.glyphToChar(entry.glyphIndex);
if (spacing != 0) {
if (!text.isEmpty()) {
drawText(x, y, trans, textColor, existingFonts, font, text.toString(), textHeight, g);
}
drawText(x + deltaX, y, trans, textColor, existingFonts, font, "" + currentChar, textHeight, g);
text = new StringBuilder();
x = x + deltaX + entry.glyphAdvance;
deltaX = 0;
}
else {
text.append(ch);
deltaX += entry.glyphAdvance;
}
} else if (entry instanceof DynamicTextGlyphEntry) {
DynamicTextGlyphEntry dynamicEntry = (DynamicTextGlyphEntry) entry;
text.append(dynamicEntry.character);
deltaX += entry.glyphAdvance;
}
}
if (!text.isEmpty()) {
drawText(x, y, trans, textColor, existingFonts, font, text.toString(), textHeight, g);
}
}
}
}
private static void drawText(float x, float y, Matrix trans, int textColor, Map<Integer, Font> existingFonts, FontTag font, String text, int textHeight, Graphics g) {
int fontId = font.getFontId();

View File

@@ -0,0 +1,17 @@
package com.jpexs.decompiler.flash.exporters;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.helpers.SerializableImage;
import java.util.List;
/**
*
* @author JPEXS
*/
public interface GraphicsTextDrawable {
public void drawTextRecords(SWF swf, List<TEXTRECORD> textRecords, int numText, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform);
}

View File

@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.FontExporter;
import com.jpexs.decompiler.flash.exporters.GraphicsTextDrawable;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
@@ -444,6 +445,11 @@ public abstract class TextTag extends DrawableTag {
}
public static void staticTextToImage(SWF swf, List<TEXTRECORD> textRecords, int numText, SerializableImage image, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) {
if (image.getGraphics() instanceof GraphicsTextDrawable) {
//custom drawing
((GraphicsTextDrawable) image.getGraphics()).drawTextRecords(swf, textRecords, numText, textMatrix, transformation, colorTransform);
return;
}
int textColor = 0;
FontTag font = null;
int textHeight = 12;