diff --git a/java-svc/src/main/java/com/wordparse/config/WordParseConfig.java b/java-svc/src/main/java/com/wordparse/config/WordParseConfig.java index fbc965f..8eb1998 100644 --- a/java-svc/src/main/java/com/wordparse/config/WordParseConfig.java +++ b/java-svc/src/main/java/com/wordparse/config/WordParseConfig.java @@ -13,12 +13,16 @@ public class WordParseConfig { private Map cleanStyleMap = Map.of(); private String transformScriptsPath = "/opt/transform"; private String pythonCommand = "python3"; + /** 私有区符号图的基址(默认我们自己的 RustFS,不依赖 jby CDN) */ + private String symbolBaseUrl = "https://fs.code-lab.cn/temp/wordparse-symbols/"; public String getTmpPath() { return tmpPath; } public Double getImageScalingFactor() { return imageScalingFactor; } public Map getCleanStyleMap() { return cleanStyleMap; } public String getTransformScriptsPath() { return transformScriptsPath; } public String getPythonCommand() { return pythonCommand; } + public String getSymbolBaseUrl() { return symbolBaseUrl; } + public void setSymbolBaseUrl(String symbolBaseUrl) { this.symbolBaseUrl = symbolBaseUrl; } public void setTmpPath(String v) { this.tmpPath = v; } public void setImageScalingFactor(Double v) { this.imageScalingFactor = v; } diff --git a/java-svc/src/main/java/com/wordparse/core/WordParseUtil.java b/java-svc/src/main/java/com/wordparse/core/WordParseUtil.java index 425992b..465d0db 100644 --- a/java-svc/src/main/java/com/wordparse/core/WordParseUtil.java +++ b/java-svc/src/main/java/com/wordparse/core/WordParseUtil.java @@ -69,8 +69,39 @@ public class WordParseUtil { this.ossService = ossService; } + /** PUA 字符 → 4 位十六进制编号(与 static/symbols 下的文件名一致) */ + private static String symbolCode(String sym) { + return String.format("%04X", (int) sym.charAt(0)); + } + + /** 记录未映射的私有区字符,便于后续扩充 PUA_TO_UNICODE */ + private static void warnUnknownPua(String content) { + java.util.Set unknown = new java.util.TreeSet<>(); + for (int i = 0; i < content.length(); i++) { + char c = content.charAt(i); + if (c >= 0xE000 && c <= 0xF8FF) { + String s = String.valueOf(c); + if (!PUA_TO_UNICODE.containsKey(s) && !java.util.Arrays.asList(SYMBOL).contains(s)) { + unknown.add(String.format("U+%04X", (int) c)); + } + } + } + if (!unknown.isEmpty()) { + log.warn("存在未映射的私有区符号(建议补充 PUA_TO_UNICODE): {}", String.join(",", unknown)); + } + } + private static final String MATH_PLACEHOLDER = "%s"; - private static final String[] SYMBOL = {"\\ue0fc", "\\ue100", "\\ue101"}; + private static final String[] SYMBOL = {"\ue0fc", "\ue100", "\ue101"}; + + /** + * 私有区(PUA)符号码位 → 标准 Unicode。 + * 教辅排版软件把 ⊆ 等数学符号写成 PUA,docx4j 会原样输出,前端显示成豆腐块。 + * 证据驱动映射:未收录的码位由 warnUnknownPua 记日志,确认含义后再补。 + */ + private static final Map PUA_TO_UNICODE = Map.of( + "\ue089", "⊆" // 10阶段滚动检测(一):B?A / {2,3}?A,与同文档内真 ⊆ 用法完全一致 + ); // ---------- Class-level constants (brand-neutral replacements for jby Constants) ---------- private static final Set RETAIN_CLASS_VALUE_SET = Set.of( @@ -158,8 +189,13 @@ public class WordParseUtil { content = content.replace("\u00A0", " "); content = content.replace("\uf028", "("); content = content.replace("\uf029", ")"); + // PUA 符号 → 标准 Unicode(先于 SYMBOL 占位符,避免冲突) + for (Map.Entry pua : PUA_TO_UNICODE.entrySet()) { + content = content.replace(pua.getKey(), pua.getValue()); + } + warnUnknownPua(content); for (String sl : SYMBOL) { - content = content.replace(sl, "@@@" + sl.substring(2).toUpperCase() + "@@@"); + content = content.replace(sl, "@@@" + symbolCode(sl) + "@@@"); } content = content.replace("", ""); @@ -1200,14 +1236,13 @@ public class WordParseUtil { "$1"); for (String sym : SYMBOL) { Element imgElement = new Element("img"); - imgElement.attr(HtmlAttr.SRC.getValue(), - "https://resource.xinjiaoyu.com/prod/office/unicode-symbol/" - + sym.substring(2).toUpperCase() + ".svg"); + imgElement.attr(HtmlAttr.SRC.getValue(), config.getSymbolBaseUrl() + symbolCode(sym) + ".svg"); imgElement.attr(HtmlAttr.WIDTH.getValue(), "14"); imgElement.attr(HtmlAttr.HEIGHT.getValue(), "14"); - result = result.replace("@@@" + sym.substring(2).toUpperCase() + "@@@", imgElement.outerHtml()); - result = result.replace("\ue225\ue226", "\\(\\rightleftharpoons\\)"); + result = result.replace("@@@" + symbolCode(sym) + "@@@", imgElement.outerHtml()); } + // ⇌(U+E225 U+E226 成对出现)直接转 LaTeX + result = result.replace("\ue225\ue226", "\\(\\rightleftharpoons\\)"); writeHtml(result); return result; } diff --git a/java-svc/src/main/resources/application.yml.example b/java-svc/src/main/resources/application.yml.example index e51fcdf..a1ff2f0 100644 --- a/java-svc/src/main/resources/application.yml.example +++ b/java-svc/src/main/resources/application.yml.example @@ -14,6 +14,7 @@ wordparse: image-scaling-factor: 2.0 transform-scripts-path: /opt/transform python-command: python3 + symbol-base-url: https://fs.code-lab.cn/temp/wordparse-symbols/ oss: endpoint: https://fs.code-lab.cn access-key: ${WORDPARSE_OSS_AK:}