diff --git a/gui/release.md b/gui/release.md index fbd9fd8..bcd20f9 100644 --- a/gui/release.md +++ b/gui/release.md @@ -1,6 +1,17 @@ # MarkdownToPdf GUI Release History (Note, dates are in yyyy-MM-dd format) +## 0.2.0 (2026-08-14) +- Added a pluggable file-access layer for sandboxed distributions. Stored projects are retained + when their files are temporarily inaccessible, and the application can ask to locate an + inaccessible project Markdown file and remember the new location. +- App-store distributions can disable GitHub update checks with + `-Dmd2pdf.update.enabled=false`; this also hides the related **Help** menu items. +- **View external** now lets sandboxed distributions choose where to save the rendered PDF before + opening it in the system viewer. PDF export and file dialogs also handle inaccessible or stale + initial directories more safely. +- CSS style parsing and syntax-highlight injection now behave consistently in every system locale. + ## 0.1.1 (2026-08-11) - Added an update check: **Help > Check for Updates…** asks GitHub Releases whether a newer version ships an archive for this platform, and offers to open the release page. It also diff --git a/gui/src/main/java/se/alipsa/md2pdf/gui/HtmlUtils.java b/gui/src/main/java/se/alipsa/md2pdf/gui/HtmlUtils.java index 7d45547..9a5b6f5 100644 --- a/gui/src/main/java/se/alipsa/md2pdf/gui/HtmlUtils.java +++ b/gui/src/main/java/se/alipsa/md2pdf/gui/HtmlUtils.java @@ -31,13 +31,23 @@ private HtmlUtils() {} */ static String injectSyntaxHighlighting(String html) { if (html == null) return ""; - int idx = html.toLowerCase().lastIndexOf(""); + int idx = lastHeadEndTagStart(html); if (idx >= 0) { return html.substring(0, idx) + HIGHLIGHT_INJECTION + html.substring(idx); } return HIGHLIGHT_INJECTION + html; } + private static int lastHeadEndTagStart(String html) { + String closingHead = ""; + for (int i = html.length() - closingHead.length(); i >= 0; i--) { + if (html.regionMatches(true, i, closingHead, 0, closingHead.length())) { + return i; + } + } + return -1; + } + private static String loadResource(String path) { try (InputStream is = HtmlUtils.class.getResourceAsStream(path)) { if (is == null) return ""; diff --git a/gui/src/main/java/se/alipsa/md2pdf/model/StyleProfile.java b/gui/src/main/java/se/alipsa/md2pdf/model/StyleProfile.java index fefd308..368681b 100644 --- a/gui/src/main/java/se/alipsa/md2pdf/model/StyleProfile.java +++ b/gui/src/main/java/se/alipsa/md2pdf/model/StyleProfile.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Properties; @@ -212,7 +213,7 @@ private static List extractBlocks(String css) { } private static String normalizeSelector(String sel) { - return sel.replaceAll("\\s+", " ").trim().toLowerCase(); + return sel.replaceAll("\\s+", " ").trim().toLowerCase(Locale.ROOT); } private static Map parseDeclarations(String decls) { @@ -222,7 +223,7 @@ private static Map parseDeclarations(String decls) { if (decl.isEmpty()) continue; int colon = decl.indexOf(':'); if (colon < 0) continue; - String prop = decl.substring(0, colon).trim().toLowerCase(); + String prop = decl.substring(0, colon).trim().toLowerCase(Locale.ROOT); String value = decl.substring(colon + 1).trim(); map.put(prop, value); } diff --git a/gui/src/test/java/se/alipsa/md2pdf/gui/HtmlUtilsTest.java b/gui/src/test/java/se/alipsa/md2pdf/gui/HtmlUtilsTest.java new file mode 100644 index 0000000..ae8a6b1 --- /dev/null +++ b/gui/src/test/java/se/alipsa/md2pdf/gui/HtmlUtilsTest.java @@ -0,0 +1,25 @@ +package se.alipsa.md2pdf.gui; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Locale; +import org.junit.jupiter.api.Test; + +class HtmlUtilsTest { + + @Test + void syntaxHighlightingPreservesHeadWithTurkishCapitalI() { + Locale originalLocale = Locale.getDefault(); + try { + Locale.setDefault(Locale.US); + String html = ""; + + String highlighted = HtmlUtils.injectSyntaxHighlighting(html); + + assertTrue(highlighted.contains("")); + assertTrue(highlighted.indexOf("