From 6949793130a8e56bd60b9e9b395f773ef10d9d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 17 Jan 2026 18:30:17 +0100 Subject: [PATCH] Fixed: #2612 SVG export - handle incorrect surrogate pairs in text --- CHANGELOG.md | 2 ++ .../decompiler/flash/tags/base/TextTag.java | 33 ++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 089a37e4c..04764030a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ All notable changes to this project will be documented in this file. - FLA export - incorrect handling of imported sprites - [#2586] AS3 direct editation + decompilation - XML escape sequences and other XML problems - [#2600] Sprite/button outline incorrect calculation caused by clipping +- [#2612] SVG export - handle incorrect surrogate pairs in text ### Changed - [#2575] dumpSWF CLI command only allows single SWF dump (no imports, etc.) @@ -4095,6 +4096,7 @@ Major version of SWF to XML export changed to 2. [#2595]: https://www.free-decompiler.com/flash/issues/2595 [#2586]: https://www.free-decompiler.com/flash/issues/2586 [#2600]: https://www.free-decompiler.com/flash/issues/2600 +[#2612]: https://www.free-decompiler.com/flash/issues/2612 [#2556]: https://www.free-decompiler.com/flash/issues/2556 [#2536]: https://www.free-decompiler.com/flash/issues/2536 [#2537]: https://www.free-decompiler.com/flash/issues/2537 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 438fabf68..dfcd01016 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 @@ -982,6 +982,30 @@ public abstract class TextTag extends DrawableTag { return family; } + private static String sanitizeUtf16(String s) { + if (s == null) { + return null; + } + + StringBuilder out = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (Character.isHighSurrogate(c)) { + if (i + 1 < s.length() && Character.isLowSurrogate(s.charAt(i + 1))) { + out.append(c).append(s.charAt(i + 1)); + i++; // validní pár + } else { + out.append('\uFFFD'); // broken high surrogate + } + } else if (Character.isLowSurrogate(c)) { + out.append('\uFFFD'); // alone low surrogate + } else { + out.append(c); + } + } + return out.toString(); + } + /** * Converts static text to SVG. * @param swf SWF @@ -1039,16 +1063,17 @@ public abstract class TextTag extends DrawableTag { exporter.createSubGroup(new Matrix(textMatrix), null); if (exporter.useTextTag) { - StringBuilder text = new StringBuilder(); + StringBuilder textBuilder = new StringBuilder(); int totalAdvance = 0; for (GLYPHENTRY entry : rec.glyphEntries) { if (entry.glyphIndex != -1) { char ch = font.glyphToChar(entry.glyphIndex); - text.append(ch); + textBuilder.append(ch); totalAdvance += entry.glyphAdvance; } } - + String text = sanitizeUtf16(textBuilder.toString()); + boolean hasOffset = x != 0 || y != 0; if (hasOffset) { exporter.createSubGroup(Matrix.getTranslateInstance(x, y), null); @@ -1062,7 +1087,7 @@ public abstract class TextTag extends DrawableTag { textElement.setAttribute("textLength", Double.toString(totalAdvance / SWF.unitDivisor)); textElement.setAttribute("lengthAdjust", "spacing"); textElement.setAttribute("style", "white-space: pre"); - textElement.setTextContent(text.toString()); + textElement.setTextContent(text); RGBA colorA = new RGBA(textColor); textElement.setAttribute("fill", colorA.toHexRGB());