/* * Decompiled with CFR 0.152. * * Could not load the following classes: * org.apache.commons.lang3.StringUtils * org.slf4j.Logger * org.slf4j.LoggerFactory */ package com.jby.officecore.utils; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class EqToLatexUtil { private static final Logger log = LoggerFactory.getLogger(EqToLatexUtil.class); private static final Pattern LONG_EQUALS_PATTERN = Pattern.compile("^=+$"); private static final int LONG_EQUALS_MIN_LENGTH = 3; private static final Map COMMAND_HANDLERS = new HashMap(); public static String sanitizeInput(String input) { if (input == null) { return ""; } HashMap replacements = new HashMap(); replacements.put("\uff08", "("); replacements.put("\uff09", ")"); replacements.put("\uff5b", "{"); replacements.put("\uff5d", "}"); replacements.put("\uff0b", "+"); replacements.put("\uff0d", "-"); replacements.put("\u2212", "-"); replacements.put("\u00d7", " \\times "); replacements.put("\u00f7", " \\div "); replacements.put("\uff1d", "="); replacements.put("\u2260", "\\ne "); replacements.put("\u2248", " \\approx "); replacements.put("\u2264", "\\le "); replacements.put("\u2265", "\\ge "); replacements.put("\uff1c", "<"); replacements.put("\uff1e", "> "); replacements.put("\u00b7", " \\cdot "); replacements.put("\u2026", "\\dots "); replacements.put("\uff5e", "~"); replacements.put("\uff1b", ";"); replacements.put("\uff1a", ":"); replacements.put("\uf028", "("); replacements.put("\uf029", ")"); replacements.put("~", "\\sim"); replacements.put("\u221d", "\\propto"); String sanitized = input; for (Map.Entry entry : replacements.entrySet()) { sanitized = sanitized.replace((CharSequence)entry.getKey(), (CharSequence)entry.getValue()); } sanitized = sanitized.replaceAll("(?i)eq", "EQ "); return sanitized.trim(); } private static String getCharFromOpt(String opt, int startIdx, String defaultChar) { if (opt.length() <= startIdx) { return defaultChar; } String charPart = opt.substring(startIdx).trim(); if (charPart.isEmpty()) { return defaultChar; } if (charPart.startsWith("\\")) { return charPart.substring(1); } return charPart; } private static String escapeChar(String ch) { String specialChars = "\\{}$&#%"; if ("\\{}$&#%".contains(ch)) { return "\\" + ch; } if ("~".equals(ch)) { return "\\ "; } return ch; } public static String convert(String eqField) { if (eqField == null) { throw new IllegalArgumentException("\u8f93\u5165\u5fc5\u987b\u662f\u5b57\u7b26\u4e32\u7c7b\u578b"); } if (eqField.trim().isEmpty()) { return ""; } try { String sanitizedField = EqToLatexUtil.sanitizeInput(EqToLatexUtil.stripFormattingMarkers(eqField)); String content = sanitizedField.replaceAll("^\\s*\\{?\\s*EQ\\s*|\\s*EQ\\s*|\\s*}\\s*$", "").trim(); String result = EqToLatexUtil.parseInstructions(content, new HashMap()); result = EqToLatexUtil.replaceJbyuWithMeasurements(result); result = result.replaceAll("JBYU2BEGIN", "\\\\underline{").replaceAll("JBYU2END", "}"); result = result.replaceAll("\u2192", "\\\\to "); log.info("\u539fEQ\u516c\u5f0f\uff1a{} \u8f6c\u6362\u540e\u7684Latex\u4ee3\u7801\uff1a{}", (Object)eqField, (Object)result); return result; } catch (Exception error) { System.err.println("\u8f6c\u6362\u8fc7\u7a0b\u4e2d\u51fa\u73b0\u9519\u8bef: " + error.getMessage()); error.printStackTrace(); return "\\text{\u8f6c\u6362\u9519\u8bef: " + error.getMessage() + "}"; } } private static String stripFormattingMarkers(String input) { return input.replaceAll("\\\\(?:\\s*\\\\?JBYU\\s*2(?:BEGIN|END))+\\s*(?!(?:[eE][qQ])\\b)(?=[A-Za-z])", Matcher.quoteReplacement("\\")).replaceAll("\\\\?JBYU\\s*2BEGIN", "").replaceAll("\\\\?JBYU\\s*2END", ""); } private static String parseInstructions(String str, Map context) { ArrayList output = new ArrayList(); int index = 0; while (index < str.length()) { int textEnd; char ch = str.charAt(index); if (ch == '\\') { String lastOutput; ParseResult result = EqToLatexUtil.parseCommand(str, index, context); String trimmedOutput = result.output.trim(); if ((trimmedOutput.startsWith("^") || trimmedOutput.startsWith("_")) && !output.isEmpty()) { lastOutput = (String)output.get(output.size() - 1); if (EqToLatexUtil.isConsecutiveScript(lastOutput, trimmedOutput)) { output.set(output.size() - 1, EqToLatexUtil.mergeConsecutiveScripts(lastOutput, trimmedOutput)); } else { output.set(output.size() - 1, lastOutput + result.output); } } else if ((trimmedOutput.startsWith("\\^") || trimmedOutput.startsWith("\\_")) && !output.isEmpty()) { lastOutput = (String)output.get(output.size() - 1); if (EqToLatexUtil.isConsecutiveScript(lastOutput, trimmedOutput)) { output.set(output.size() - 1, EqToLatexUtil.mergeConsecutiveScripts(lastOutput, trimmedOutput)); } else { output.set(output.size() - 1, lastOutput + result.output); } } else if (EqToLatexUtil.isSpecialScriptMarker(trimmedOutput) && !output.isEmpty() && EqToLatexUtil.isSpecialScriptMarker((String)output.get(output.size() - 1))) { String merged = EqToLatexUtil.mergeSpecialScriptMarkers((String)output.get(output.size() - 1), trimmedOutput); output.set(output.size() - 1, merged); } else { output.add(result.output); } index = result.newIndex; continue; } if (ch == '{' || ch == '}') { ++index; continue; } for (textEnd = index; textEnd < str.length() && "\\{}".indexOf(str.charAt(textEnd)) == -1; ++textEnd) { } String text = str.substring(index, textEnd); output.add(text.replaceAll("[&%$#_]", "\\\\$0")); index = textEnd; } String result = String.join((CharSequence)" ", output).replaceAll("\\s+", " ").trim(); return EqToLatexUtil.postProcessConsecutiveScripts(result); } private static ParseResult parseCommand(String str, int startIndex, Map context) { int argParenStartIndex; String cmdName; int currentIndex = startIndex; ArrayList commandChain = new ArrayList(); ArrayList commandEndIndices = new ArrayList(); while (currentIndex < str.length()) { int cmdNameEnd; int tempIndex; for (tempIndex = currentIndex; tempIndex < str.length() && Character.isWhitespace(str.charAt(tempIndex)); ++tempIndex) { } if (tempIndex >= str.length() || str.charAt(tempIndex) != '\\') { currentIndex = tempIndex; break; } currentIndex = tempIndex; for (cmdNameEnd = currentIndex + 1; cmdNameEnd < str.length() && Character.isLetter(str.charAt(cmdNameEnd)); ++cmdNameEnd) { } String cmdName2 = str.substring(currentIndex + 1, cmdNameEnd); String token = "\\" + cmdName2; int argStartIndex = cmdNameEnd; List numericArgCmds = Arrays.asList("co", "vs", "hs", "fo", "ba", "up", "do", "ai", "di"); if (numericArgCmds.contains(cmdName2.toLowerCase())) { int numEnd; for (numEnd = argStartIndex; numEnd < str.length() && Character.isDigit(str.charAt(numEnd)); ++numEnd) { } if (numEnd > argStartIndex) { token = token + str.substring(argStartIndex, numEnd); argStartIndex = numEnd; } } else if (Arrays.asList("lc", "rc", "bc", "fc", "vc").contains(cmdName2.toLowerCase()) && argStartIndex < str.length()) { if (str.charAt(argStartIndex) == '\\' && argStartIndex + 1 < str.length()) { token = token + str.substring(argStartIndex, argStartIndex + 2); argStartIndex += 2; } else { token = token + str.charAt(argStartIndex); ++argStartIndex; } } commandChain.add(token); commandEndIndices.add(argStartIndex); currentIndex = argStartIndex; } if (commandChain.isEmpty()) { return new ParseResult(EqToLatexUtil.escapeChar(String.valueOf(str.charAt(startIndex))), startIndex + 1); } CommandHandler mainCommandHandler = null; String mainCommandName = ""; String firstToken = (String)commandChain.get(0); Matcher matcher = Pattern.compile("^\\\\([a-zA-Z]+)").matcher(firstToken); if (matcher.find() && COMMAND_HANDLERS.containsKey(cmdName = matcher.group(1).toLowerCase())) { mainCommandHandler = COMMAND_HANDLERS.get(cmdName); mainCommandName = cmdName; } if (mainCommandHandler == null) { return new ParseResult((String)commandChain.get(0), (Integer)commandEndIndices.get(0)); } ArrayList args = new ArrayList(); for (argParenStartIndex = currentIndex; argParenStartIndex < str.length() && Character.isWhitespace(str.charAt(argParenStartIndex)); ++argParenStartIndex) { } if (argParenStartIndex < str.length() && (str.charAt(argParenStartIndex) == '(' || str.charAt(argParenStartIndex) == '[')) { ArgumentResult argsResult = EqToLatexUtil.extractArguments(str, argParenStartIndex); if (argsResult.success) { List rawArgs = EqToLatexUtil.splitArguments(argsResult.content); HashMap childContext = new HashMap(); childContext.put("parentCommand", mainCommandName); for (String arg : rawArgs) { args.add(EqToLatexUtil.parseInstructions(arg, childContext)); } currentIndex = argsResult.endIndex; } else { throw new RuntimeException("Mismatched parentheses starting at index " + argParenStartIndex); } } String output = mainCommandHandler.handle(commandChain, args, mainCommandName, context); return new ParseResult(output, currentIndex); } private static ArgumentResult extractArguments(String str, int startIndex) { char openParen = str.charAt(startIndex); char closeParen = openParen == '(' ? (char)')' : ']'; int depth = 1; StringBuilder content = new StringBuilder(); int index = startIndex + 1; while (index < str.length() && depth > 0) { char ch = str.charAt(index); if (ch == '\\' && index + 1 < str.length()) { content.append(ch).append(str.charAt(index + 1)); index += 2; continue; } if (ch == openParen) { if (++depth > 1) { content.append(ch); } ++index; continue; } if (ch == closeParen) { if (--depth > 0) { content.append(ch); } ++index; continue; } if (depth > 0) { content.append(ch); } ++index; } return new ArgumentResult(depth == 0, content.toString(), index); } private static List splitArguments(String str) { ArrayList args = new ArrayList(); StringBuilder current = new StringBuilder(); int depth = 0; int i = 0; while (i < str.length()) { char ch = str.charAt(i); if (ch == '\\' && i + 1 < str.length()) { current.append(ch).append(str.charAt(i + 1)); i += 2; continue; } if (ch == '(') { ++depth; } else if (ch == ')') { --depth; } if (ch == ',' && depth == 0) { args.add(current.toString().trim()); current = new StringBuilder(); } else { current.append(ch); } ++i; } if (current.length() > 0) { args.add(current.toString().trim()); } return args; } private static String handleFraction(List options, List args, String mainCommandName, Map context) { if (args.size() != 2) { return "\\text{\u9519\u8bef\u7684\u5206\u6570\u53c2\u6570}"; } return String.format("\\dfrac{%s}{%s}", args.get(0), args.get(1)); } private static String handleRoot(List options, List args, String mainCommandName, Map context) { if (args.isEmpty()) { return ""; } if (args.size() == 1) { return String.format("\\sqrt{%s}", args.get(0)); } return String.format("\\sqrt[%s]{%s}", args.get(0), args.get(1)); } private static String handleIntegral(List options, List args, String mainCommandName, Map context) { String symbol = "\\int"; if (options.stream().anyMatch(o -> o.toLowerCase().startsWith("\\su"))) { symbol = "\\sum"; } else if (options.stream().anyMatch(o -> o.toLowerCase().startsWith("\\pr"))) { symbol = "\\prod"; } else { String fc = options.stream().filter(o -> o.toLowerCase().startsWith("\\fc")).findFirst().orElse(null); String vc = options.stream().filter(o -> o.toLowerCase().startsWith("\\vc")).findFirst().orElse(null); if (fc != null) { symbol = EqToLatexUtil.escapeChar(EqToLatexUtil.getCharFromOpt(fc, 3, "")); } else if (vc != null) { symbol = EqToLatexUtil.escapeChar(EqToLatexUtil.getCharFromOpt(vc, 3, "")); } } if (args.isEmpty()) { return symbol; } String lower = args.get(0); String upper = args.size() > 1 ? args.get(1) : ""; String integrand = args.size() > 2 ? args.subList(2, args.size()).stream().collect(Collectors.joining(" ")) : ""; String limits = !lower.isEmpty() || !upper.isEmpty() ? String.format("^{%s}_{%s}", upper, lower) : ""; return String.format("%s%s %s", symbol, limits, integrand); } private static String handleScript(List options, List args, String mainCommandName, Map context) { ArrayList argsCopy = new ArrayList(args); if (argsCopy.size() > 2) { String arrayContent = String.join((CharSequence)"\\\\", argsCopy); return String.format("\\begin{array}{l}%s\\end{array}", arrayContent); } String up = ""; String down = ""; boolean doOptExists = options.stream().anyMatch(o -> o.toLowerCase().startsWith("\\do")); if (argsCopy.size() == 2) { up = (String)argsCopy.remove(0); down = (String)argsCopy.remove(0); } else if (argsCopy.size() == 1) { if (doOptExists) { down = (String)argsCopy.remove(0); } else { up = (String)argsCopy.remove(0); } } if ("o".equals(context.get("parentCommand"))) { String upTrimmed = up.trim(); if ("\u2192".equals(upTrimmed) || "\\rightarrow".equals(upTrimmed)) { return "OVERSET_ARROW"; } if ("^".equals(upTrimmed) || "IS_HAT".equals(upTrimmed)) { return "OVERSET_HAT"; } if (!up.isEmpty() && !down.isEmpty()) { return "OVER_UNDER_SCRIPT:" + up + "," + down; } if (!up.isEmpty()) { return "OVER_SCRIPT:" + up; } if (!down.isEmpty()) { return "UNDER_SCRIPT:" + down; } } StringBuilder output = new StringBuilder(); if (!up.isEmpty()) { output.append(String.format("^{%s}", up)); } if (!down.isEmpty()) { output.append(String.format("_{%s}", down)); } return output.toString(); } private static String handleBrackets(List options, List args, String mainCommandName, Map context) { String content = String.join((CharSequence)",", args); HashMap delimiterPairMap = new HashMap(); delimiterPairMap.put("(", ")"); delimiterPairMap.put("[", "]"); delimiterPairMap.put("{", "}"); delimiterPairMap.put("<", ">"); String left = null; String right = null; String bcOpt = options.stream().filter(o -> o.startsWith("\\bc")).findFirst().orElse(null); String lcOpt = options.stream().filter(o -> o.startsWith("\\lc")).findFirst().orElse(null); String rcOpt = options.stream().filter(o -> o.startsWith("\\rc")).findFirst().orElse(null); if (bcOpt != null) { String charStr = EqToLatexUtil.getCharFromOpt(bcOpt, 3, "|"); left = EqToLatexUtil.getDelimiter(charStr); right = EqToLatexUtil.getDelimiter(delimiterPairMap.getOrDefault(charStr, charStr)); } else { if (lcOpt != null) { left = EqToLatexUtil.getDelimiter(EqToLatexUtil.getCharFromOpt(lcOpt, 3, "(")); } if (rcOpt != null) { right = EqToLatexUtil.getDelimiter(EqToLatexUtil.getCharFromOpt(rcOpt, 3, ")")); } } if (bcOpt == null && lcOpt == null && rcOpt == null) { left = "("; right = ")"; } return String.format("\\left%s %s \\right%s", StringUtils.isNotBlank((CharSequence)left) ? left : ".", content, StringUtils.isNotBlank((CharSequence)right) ? right : "."); } private static String getDelimiter(String ch) { return "{".equals(ch) || "}".equals(ch) ? "\\" + ch : ch; } private static String handleArray(List options, List args, String mainCommandName, Map context) { if (args.isEmpty()) { return ""; } if (args.size() == 1) { return args.get(0); } int cols = 1; String colAlign = "c"; for (String opt : options) { Matcher colMatch = Pattern.compile("^\\\\co(\\d+)$").matcher(opt); if (colMatch.matches()) { cols = Integer.parseInt(colMatch.group(1)); } if ("\\al".equals(opt)) { colAlign = "l"; continue; } if ("\\ac".equals(opt)) { colAlign = "c"; continue; } if (!"\\ar".equals(opt)) continue; colAlign = "r"; } StringBuilder alignSpec = new StringBuilder(); for (int i = 0; i < cols; ++i) { alignSpec.append(colAlign); } ArrayList matrixRows = new ArrayList(); int i = 0; while ((double)i < Math.ceil((double)args.size() / (double)cols)) { ArrayList row = new ArrayList(args.subList(i * cols, Math.min((i + 1) * cols, args.size()))); while (row.size() < cols) { row.add(""); } matrixRows.add(String.join((CharSequence)" & ", row)); ++i; } return String.format("\\begin{array}{%s}%s\\end{array}", alignSpec, String.join((CharSequence)"\\\\", matrixRows)); } private static String handleBox(List options, List args, String mainCommandName, Map context) { if (options.stream().anyMatch(o -> o.equalsIgnoreCase("\\to"))) { return args.isEmpty() ? "\\overline{}" : String.format("\\overline{%s}", args.get(0)); } String content = String.join((CharSequence)",", args); return args.isEmpty() ? "\\boxed{}" : String.format("\\boxed{%s}", content); } private static String handleList(List options, List args, String mainCommandName, Map context) { return String.join((CharSequence)",\\ ", args); } private static String handleOverstrike(List options, List args, String mainCommandName, Map context) { String limitResult; String firstArg; if (args.isEmpty()) { return ""; } if (args.size() >= 1 && ("lim".equals(firstArg = args.get(0).trim().toLowerCase()) || "limit".equals(firstArg)) && (limitResult = EqToLatexUtil.handleLimitExpression(args)) != null) { return limitResult; } String chemResult = EqToLatexUtil.handleChemicalSymbols(args, context); if (chemResult != null) { return chemResult; } boolean allArgsAreScriptMarkers = true; for (String arg : args) { if (EqToLatexUtil.isScriptMarker(arg)) continue; allArgsAreScriptMarkers = false; break; } if (allArgsAreScriptMarkers) { boolean hasAlignCenter = options.stream().anyMatch(o -> "\\ac".equalsIgnoreCase((String)o)); if (hasAlignCenter) { ArrayList scriptContents = new ArrayList(); for (String script2 : args) { if (!script2.startsWith("OVER_SCRIPT:")) continue; scriptContents.add(script2.substring("OVER_SCRIPT:".length()).trim()); } if (!scriptContents.isEmpty()) { String result = (String)scriptContents.get(scriptContents.size() - 1); for (int i = scriptContents.size() - 2; i >= 0; --i) { result = String.format("\\rlap{%s}{%s}", scriptContents.get(i), result); } return String.format("^{%s}", result); } } StringBuilder up = new StringBuilder(); StringBuilder down = new StringBuilder(); args.forEach(script -> { if (script.startsWith("OVER_UNDER_SCRIPT:")) { String[] parts = script.substring("OVER_UNDER_SCRIPT:".length()).split(",", 2); up.append(up.length() > 0 ? " " : "").append(parts[0]); down.append(down.length() > 0 ? " " : "").append(parts[1]); } else if (script.startsWith("OVER_SCRIPT:")) { up.append(up.length() > 0 ? " " : "").append(script.substring("OVER_SCRIPT:".length())); } else if (script.startsWith("UNDER_SCRIPT:")) { down.append(down.length() > 0 ? " " : "").append(script.substring("UNDER_SCRIPT:".length())); } else if ("OVERSET_ARROW".equals(script)) { up.append(up.length() > 0 ? " " : "").append("\\rightarrow"); } else if ("OVERSET_HAT".equals(script)) { up.append(up.length() > 0 ? " " : "").append("^"); } else { MathJaxScripts extracted = EqToLatexUtil.extractMathJaxScripts(script); if (!extracted.up.isEmpty()) { up.append(up.length() > 0 ? " " : "").append(extracted.up); } if (!extracted.down.isEmpty()) { down.append(down.length() > 0 ? " " : "").append(extracted.down); } } }); String newUp = up.toString().replaceAll("\\s?OVER_SCRIPT:|\\s?UNDER_SCRIPT:", ""); String newDown = down.toString().replaceAll("\\s?OVER_SCRIPT:|\\s?UNDER_SCRIPT:", ""); StringBuilder output = new StringBuilder(); if (up.length() > 0) { output.append(String.format("^{%s}", newUp)); } if (down.length() > 0) { output.append(String.format("_{%s}", newDown)); } return output.toString(); } int baseIndex = -1; for (int i = 0; i < args.size(); ++i) { if (EqToLatexUtil.isScriptMarker(args.get(i))) continue; baseIndex = i; break; } if (baseIndex == -1) { String result = args.get(0); for (int i = 1; i < args.size(); ++i) { result = String.format("\\rlap{%s}{%s}", args.get(i), result); } return result; } String base = args.get(baseIndex); base = EqToLatexUtil.resolveNestedScripts(base); ArrayList otherArgs = new ArrayList(); for (int i = 0; i < args.size(); ++i) { if (i == baseIndex) continue; otherArgs.add(args.get(i)); } boolean otherArgsAreScriptMarkers = true; for (String arg : otherArgs) { if (EqToLatexUtil.isScriptMarker(arg)) continue; otherArgsAreScriptMarkers = false; break; } if (!otherArgs.isEmpty() && otherArgsAreScriptMarkers) { StringBuilder over = new StringBuilder(); StringBuilder under = new StringBuilder(); otherArgs.forEach(script -> { if ("OVERSET_ARROW".equals(script)) { over.append("\\rightarrow"); } else if ("OVERSET_HAT".equals(script)) { over.append("^"); } else if (script.startsWith("OVER_UNDER_SCRIPT:")) { String[] parts = script.substring("OVER_UNDER_SCRIPT:".length()).split(",", 2); over.append(over.length() > 0 ? " " : "").append(parts[0]); under.append(under.length() > 0 ? " " : "").append(parts[1]); } else if (script.startsWith("OVER_SCRIPT:")) { over.append(over.length() > 0 ? " " : "").append(script.substring("OVER_SCRIPT:".length())); } else if (script.startsWith("UNDER_SCRIPT:")) { under.append(under.length() > 0 ? " " : "").append(script.substring("UNDER_SCRIPT:".length())); } }); if ("^".equals(over.toString())) { return String.format("\\hat{%s}", base); } String cleanOver = over.toString().replace(" ", "").replace("\\to", "\\rightarrow").replace("\u2192", "\\rightarrow"); if ("\\rightarrow".equals(cleanOver) || cleanOver.contains("\\rightarrow") && (cleanOver.matches(".*[\u2014\u2013\u2212\u2015\u2012\u2010\\-].*") || cleanOver.contains("OVERSET_ARROW"))) { return String.format("\\overrightarrow{%s}", base); } if ("\\rightarrow".equals(over.toString())) { return String.format("\\overrightarrow{%s}", base); } String result = base; if (over.length() > 0) { result = String.format("\\overset{%s}{%s}", over.toString(), result); } if (under.length() > 0) { result = String.format("\\underset{%s}{%s}", under.toString(), result); } return result; } String result = base; for (int i = otherArgs.size() - 1; i >= 0; --i) { result = String.format("\\rlap{%s}{%s}", otherArgs.get(i), result); } return result; } private static String handleChemicalSymbols(List args, Map context) { if (args.isEmpty()) { return null; } String firstArg = args.get(0).trim(); if (LONG_EQUALS_PATTERN.matcher(firstArg).matches() && firstArg.length() >= 3) { return EqToLatexUtil.handleLongEqual(args); } if ((firstArg.contains("\u2192") || firstArg.contains("\\rightarrow")) && !firstArg.contains("\u2190") && !firstArg.contains("\\leftarrow")) { return EqToLatexUtil.handleRightArrow(args); } if (firstArg.matches("^\u2190[\u2015\uff0d\u2014\\-]+$") || firstArg.contains("\u2190") || firstArg.contains("\\leftarrow")) { return EqToLatexUtil.handleLeftArrow(args); } return null; } private static String handleLongEqual(List args) { String upper = ""; String lower = ""; for (int i = 1; i < args.size(); ++i) { String content; String arg = args.get(i); if (arg.startsWith("OVER_SCRIPT:")) { upper = (upper + " " + arg.substring("OVER_SCRIPT:".length())).trim(); continue; } if (arg.startsWith("UNDER_SCRIPT:")) { lower = (lower + " " + arg.substring("UNDER_SCRIPT:".length())).trim(); continue; } if (arg.startsWith("OVER_UNDER_SCRIPT:")) { String[] parts = arg.substring("OVER_UNDER_SCRIPT:".length()).split(",", 2); upper = (upper + " " + parts[0]).trim(); lower = (lower + " " + (parts.length > 1 ? parts[1] : "")).trim(); continue; } if (arg.startsWith("^{") && arg.endsWith("}")) { content = arg.substring(2, arg.length() - 1); upper = (upper + " " + content).trim(); continue; } if (!arg.startsWith("_{") || !arg.endsWith("}")) continue; content = arg.substring(2, arg.length() - 1); lower = (lower + " " + content).trim(); } if (!upper.isEmpty() && !lower.isEmpty()) { return String.format("\\xlongequal[%s]{%s}", lower, upper); } if (!upper.isEmpty()) { return String.format("\\xlongequal{%s}", upper); } return "\\xlongequal{}"; } private static String handleRightArrow(List args) { String upper = ""; String lower = ""; for (int i = 1; i < args.size(); ++i) { String content; String arg = args.get(i); if (arg.startsWith("OVER_SCRIPT:")) { upper = (upper + " " + arg.substring("OVER_SCRIPT:".length())).trim(); continue; } if (arg.startsWith("UNDER_SCRIPT:")) { lower = (lower + " " + arg.substring("UNDER_SCRIPT:".length())).trim(); continue; } if (arg.startsWith("OVER_UNDER_SCRIPT:")) { String[] parts = arg.substring("OVER_UNDER_SCRIPT:".length()).split(",", 2); upper = (upper + " " + parts[0]).trim(); lower = (lower + " " + (parts.length > 1 ? parts[1] : "")).trim(); continue; } if (arg.startsWith("^{") && arg.endsWith("}")) { content = arg.substring(2, arg.length() - 1); upper = (upper + " " + content).trim(); continue; } if (!arg.startsWith("_{") || !arg.endsWith("}")) continue; content = arg.substring(2, arg.length() - 1); lower = (lower + " " + content).trim(); } if (!upper.isEmpty() && !lower.isEmpty()) { return String.format("\\xrightarrow[%s]{%s}", lower, upper); } if (!upper.isEmpty()) { return String.format("\\xrightarrow{%s}", upper); } if (!lower.isEmpty()) { return String.format("\\xrightarrow[%s]{}", lower); } return "\\xrightarrow{}"; } private static String handleLeftArrow(List args) { String upper = ""; String lower = ""; for (int i = 1; i < args.size(); ++i) { String content; String arg = args.get(i); if (arg.startsWith("OVER_SCRIPT:")) { upper = (upper + " " + arg.substring("OVER_SCRIPT:".length())).trim(); continue; } if (arg.startsWith("UNDER_SCRIPT:")) { lower = (lower + " " + arg.substring("UNDER_SCRIPT:".length())).trim(); continue; } if (arg.startsWith("OVER_UNDER_SCRIPT:")) { String[] parts = arg.substring("OVER_UNDER_SCRIPT:".length()).split(",", 2); upper = (upper + " " + parts[0]).trim(); lower = (lower + " " + (parts.length > 1 ? parts[1] : "")).trim(); continue; } if (arg.startsWith("^{") && arg.endsWith("}")) { content = arg.substring(2, arg.length() - 1); upper = (upper + " " + content).trim(); continue; } if (!arg.startsWith("_{") || !arg.endsWith("}")) continue; content = arg.substring(2, arg.length() - 1); lower = (lower + " " + content).trim(); } if (!upper.isEmpty() && !lower.isEmpty()) { return String.format("\\xleftarrow[%s]{%s}", lower, upper); } if (!upper.isEmpty()) { return String.format("\\xleftarrow{%s}", upper); } if (!lower.isEmpty()) { return String.format("\\xleftarrow[%s]{}", lower); } return "\\xleftarrow{}"; } private static String handleDisplacement(List options, List args, String mainCommandName, Map context) { if (args.isEmpty()) { return ""; } return String.join((CharSequence)" ", args); } private static String handleUp(List options, List args, String mainCommandName, Map context) { return String.format("^{%s}", String.join((CharSequence)",", args)); } private static String handleDo(List options, List args, String mainCommandName, Map context) { return String.format("_{%s}", String.join((CharSequence)",", args)); } public static String replaceJbyuWithMeasurements(String input) { String pattern = "(JBYU1JBYU)+"; Matcher matcher = Pattern.compile(pattern).matcher(input); StringBuffer result = new StringBuffer(); while (matcher.find()) { String match = matcher.group(); int count = match.length() / 4; int groups = (count + 4) / 5; StringBuilder replacement = new StringBuilder(); for (int i = 0; i < groups; ++i) { replacement.append("\\\\underline{\\\\qquad}"); } matcher.appendReplacement(result, replacement.toString()); } matcher.appendTail(result); return result.toString(); } private static String handleLimitExpression(List args) { Matcher m; if (args.size() < 2) { return null; } String limitArg = args.get(1).trim(); Pattern nestedDoPattern = Pattern.compile("\\\\s\\\\do4\\(\\\\s\\\\do4\\(([^)]+)\\)\\)"); Matcher match = nestedDoPattern.matcher(limitArg); if (match.find()) { return String.format("\\lim_{%s}", match.group(1)); } Pattern singleDoPattern = Pattern.compile("\\\\s\\\\do4\\(([^)]+)\\)"); Matcher singleMatch = singleDoPattern.matcher(limitArg); if (singleMatch.find()) { return String.format("\\lim_{%s}", singleMatch.group(1)); } if (limitArg.startsWith("_{") && limitArg.endsWith("}")) { return String.format("\\lim_{%s}", limitArg.substring(2, limitArg.length() - 1)); } if (limitArg.matches("^_\\{_\\{([^}]+)}}$") && (m = Pattern.compile("^_\\{_\\{([^}]+)}}$").matcher(limitArg)).find()) { return String.format("\\lim_{%s}", m.group(1)); } Pattern anySubPattern = Pattern.compile("_\\{([^}]+)}"); Matcher anySubMatch = anySubPattern.matcher(limitArg); if (anySubMatch.find()) { Matcher inner; String limitContent = anySubMatch.group(1); if (limitContent.contains("_{") && limitContent.contains("}") && (inner = Pattern.compile("_\\{([^}]+)}").matcher(limitContent)).find()) { limitContent = inner.group(1); } return String.format("\\lim_{%s}", limitContent); } if (!limitArg.isEmpty() && !limitArg.contains("\\")) { return String.format("\\lim_{%s}", limitArg); } return null; } private static boolean isConsecutiveScript(String first, String second) { if (first == null || second == null) { return false; } String firstTrimmed = first.trim(); String secondTrimmed = second.trim(); boolean firstIsSuper = firstTrimmed.matches(".*\\\\?\\^\\{\\s*[^}]*\\s*}$"); boolean secondIsSuper = secondTrimmed.matches("^\\s*\\\\?\\^\\{.*"); boolean firstIsSub = firstTrimmed.matches(".*\\\\?_\\{\\s*[^}]*\\s*}$"); boolean secondIsSub = secondTrimmed.matches("^\\s*\\\\?_\\{.*"); return firstIsSuper && secondIsSuper || firstIsSub && secondIsSub; } private static String mergeConsecutiveScripts(String first, String second) { if (EqToLatexUtil.isConsecutiveScript(first, second)) { Matcher firstMatch = Pattern.compile("(\\\\?)(\\^|_)\\{([^}]*)}$").matcher(first.trim()); if (!firstMatch.find()) { return first + second; } String firstBackslash = firstMatch.group(1); String scriptType = firstMatch.group(2); String firstContent = firstMatch.group(3); Matcher secondMatch = Pattern.compile("^\\s*(\\\\?)(\\^|_)\\{([^}]*)}").matcher(second.trim()); if (!secondMatch.find() || !secondMatch.group(2).equals(scriptType)) { return first + second; } String secondContent = secondMatch.group(3); String mergedContent = firstContent + secondContent; String beforeScript = first.replaceAll("(\\\\?)(\\^|_)\\{[^}]*}$", ""); return beforeScript + firstBackslash + scriptType + "{" + mergedContent + "}"; } return first + second; } private static String postProcessConsecutiveScripts(String text) { String previousText; String result = text; do { previousText = result; result = result.replaceAll("(\\\\?)\\^\\{([^}]*?)}\\s+(\\\\?)\\^\\{([^}]*?)}", "$1^{$2$4}"); } while (!(result = result.replaceAll("(\\\\?)_\\{([^}]*?)}\\s+(\\\\?)_\\{([^}]*?)}", "$1_{$2$4}")).equals(previousText)); return result; } private static boolean isScriptMarker(String str) { if (str == null) { return false; } return str.equals("OVERSET_ARROW") || str.equals("OVERSET_HAT") || str.startsWith("OVER_UNDER_SCRIPT:") || str.startsWith("OVER_SCRIPT:") || str.startsWith("UNDER_SCRIPT:") || str.matches("^\\^\\{.*}$") || str.matches("^_\\{.*}$") || str.matches("^(\\^\\{[^}]*})+$") || str.matches("^(_\\{[^}]*})+$") || str.matches("^(\\^\\{[^}]*}|_\\{[^}]*})+$"); } private static MathJaxScripts extractMathJaxScripts(String mathjaxScript) { MathJaxScripts result = new MathJaxScripts(); Matcher upMatcher = Pattern.compile("\\^\\{([^}]*)}").matcher(mathjaxScript); while (upMatcher.find()) { result.up = result.up + upMatcher.group(1); } Matcher downMatcher = Pattern.compile("_\\{([^}]*)}").matcher(mathjaxScript); while (downMatcher.find()) { result.down = result.down + downMatcher.group(1); } return result; } private static String resolveNestedScripts(String text) { if (text == null) { return null; } String result = text; result = result.replaceAll("OVERSET_ARROW", "\\\\rightarrow"); result = result.replaceAll("OVERSET_HAT", "^"); Pattern p = Pattern.compile("OVER_UNDER_SCRIPT:([^ ]+)"); Matcher m = p.matcher(result); StringBuffer sb = new StringBuffer(); while (m.find()) { String content = m.group(1); String[] parts = content.split(",", 2); String up = parts[0].trim(); String down = (parts.length > 1 ? parts[1] : "").trim(); m.appendReplacement(sb, "^{" + up + "}_{" + down + "}"); } m.appendTail(sb); result = sb.toString(); result = result.replaceAll("OVER_SCRIPT:([^ ]+)", "^{$1}"); result = result.replaceAll("UNDER_SCRIPT:([^ ]+)", "_{$1}"); return result; } private static boolean isSpecialScriptMarker(String str) { return EqToLatexUtil.isScriptMarker(str); } private static String mergeSpecialScriptMarkers(String first, String second) { String upFirst = ""; String downFirst = ""; if (first.startsWith("OVER_SCRIPT:")) { upFirst = first.substring("OVER_SCRIPT:".length()); } else if (first.startsWith("UNDER_SCRIPT:")) { downFirst = first.substring("UNDER_SCRIPT:".length()); } else if (first.startsWith("OVER_UNDER_SCRIPT:")) { String[] parts = first.substring("OVER_UNDER_SCRIPT:".length()).split(",", 2); upFirst = parts[0]; downFirst = parts.length > 1 ? parts[1] : ""; } String upSecond = ""; String downSecond = ""; if (second.startsWith("OVER_SCRIPT:")) { upSecond = second.substring("OVER_SCRIPT:".length()); } else if (second.startsWith("UNDER_SCRIPT:")) { downSecond = second.substring("UNDER_SCRIPT:".length()); } else if (second.startsWith("OVER_UNDER_SCRIPT:")) { String[] parts = second.substring("OVER_UNDER_SCRIPT:".length()).split(",", 2); upSecond = parts[0]; downSecond = parts.length > 1 ? parts[1] : ""; } String upMerged = (upFirst + upSecond).trim(); String downMerged = (downFirst + downSecond).trim(); if (!upMerged.isEmpty() && !downMerged.isEmpty()) { return "OVER_UNDER_SCRIPT:" + upMerged + "," + downMerged; } if (!upMerged.isEmpty()) { return "OVER_SCRIPT:" + upMerged; } if (!downMerged.isEmpty()) { return "UNDER_SCRIPT:" + downMerged; } return first; } public static void main(String[] args) { EqToLatexUtil.expect("P1-1", "eq \\o\\al(\\s\\up4(\u00b7),\\s\\do4(\u00b7))", "^{\\cdot}_{\\cdot}"); EqToLatexUtil.expect("P1-2", "eq \\o(Cl,\\s\\up6(\\s\\up4(\u00b7\u00b7)),\\s\\do4(\\s\\do4(\u00b7\u00b7)))", "\\underset{_{\\cdot \\cdot}}{\\overset{^{\\cdot \\cdot}}{Cl}}"); EqToLatexUtil.expect("P1-3", "eq \\o\\al(\\s\\up4(\u00b7),\\s\\do4(\u00b7))", "^{\\cdot}_{\\cdot}"); EqToLatexUtil.expect("P3-1", "\\JBYU2BEGINeq \\\\JBYU2END\\JBYU2BEGINo\\al(\\JBYU2END\\JBYU2BEGIN\\s\\up4(\u00b7)\\JBYU2END,\\JBYU2BEGIN\\s\\do4(\u00b7)\\JBYU2END)\\JBYU2END", "^{\\cdot}_{\\cdot}"); EqToLatexUtil.expect("P3-2", "\\JBYU2BEGINeq \\\\JBYU2END\\JBYU2BEGINo(Cl,\\s\\up6(\\JBYU2BEGIN\\s\\up4(\u00b7\u00b7)\\JBYU2END),\\s\\do4(\\JBYU2BEGIN\\s\\do4(\u00b7\u00b7)\\JBYU2END))\\JBYU2END", "\\underset{_{\\cdot \\cdot}}{\\overset{^{\\cdot \\cdot}}{Cl}}"); EqToLatexUtil.expect("P3-3", "\\JBYU2BEGINeq \\\\JBYU2END\\JBYU2BEGINo\\al(\\JBYU2END\\JBYU2BEGIN\\s\\up4(\u00b7)\\JBYU2END,\\JBYU2BEGIN\\s\\do4(\u00b7)\\JBYU2END)\\JBYU2END", "^{\\cdot}_{\\cdot}"); System.out.println("OK"); } private static void expect(String label, String input, String expected) { String actual = EqToLatexUtil.convert(input); if (!expected.equals(actual)) { throw new AssertionError((Object)(label + " expected " + expected + " but got " + actual)); } } static { COMMAND_HANDLERS.put("f", EqToLatexUtil::handleFraction); COMMAND_HANDLERS.put("r", EqToLatexUtil::handleRoot); COMMAND_HANDLERS.put("i", EqToLatexUtil::handleIntegral); COMMAND_HANDLERS.put("s", EqToLatexUtil::handleScript); COMMAND_HANDLERS.put("b", EqToLatexUtil::handleBrackets); COMMAND_HANDLERS.put("a", EqToLatexUtil::handleArray); COMMAND_HANDLERS.put("x", EqToLatexUtil::handleBox); COMMAND_HANDLERS.put("l", EqToLatexUtil::handleList); COMMAND_HANDLERS.put("o", EqToLatexUtil::handleOverstrike); COMMAND_HANDLERS.put("d", EqToLatexUtil::handleDisplacement); COMMAND_HANDLERS.put("up", EqToLatexUtil::handleUp); COMMAND_HANDLERS.put("do", EqToLatexUtil::handleDo); } private static class MathJaxScripts { String up = ""; String down = ""; private MathJaxScripts() { } } private static class ArgumentResult { boolean success; String content; int endIndex; ArgumentResult(boolean success, String content, int endIndex) { this.success = success; this.content = content; this.endIndex = endIndex; } } private static class ParseResult { String output; int newIndex; ParseResult(String output, int newIndex) { this.output = output; this.newIndex = newIndex; } } @FunctionalInterface static interface CommandHandler { public String handle(List var1, List var2, String var3, Map var4); } }