From 62aabf1057d29c76e9cfd6b43db2a0d1ab6562dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 22 Mar 2021 22:48:53 +0100 Subject: [PATCH] PDF text is written directly instead of transparently --- .../flash/exporters/DualPdfGraphics2D.java | 142 +++++++++++++++++- .../flash/exporters/FrameExporter.java | 132 +--------------- .../flash/exporters/GraphicsTextDrawable.java | 17 +++ .../decompiler/flash/tags/base/TextTag.java | 6 + 4 files changed, 164 insertions(+), 133 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/GraphicsTextDrawable.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/DualPdfGraphics2D.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/DualPdfGraphics2D.java index 99410af6e..945719737 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/DualPdfGraphics2D.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/DualPdfGraphics2D.java @@ -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 existingFonts; - public DualPdfGraphics2D(Graphics2D first, PDFGraphics second) { + public DualPdfGraphics2D(Graphics2D first, PDFGraphics second, Map 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 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 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); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java index 4f038babc..dbff29981 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java @@ -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 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 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 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 existingFonts, FontTag font, String text, int textHeight, Graphics g) { int fontId = font.getFontId(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/GraphicsTextDrawable.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/GraphicsTextDrawable.java new file mode 100644 index 000000000..2d32953e3 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/GraphicsTextDrawable.java @@ -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 textRecords, int numText, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index 4ddaa0610..ed2b834cf 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -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 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;