Created
July 24, 2025 10:41
-
-
Save ozzi-/5a67f1955001105d0bc73698ec77cbb9 to your computer and use it in GitHub Desktop.
xls magic because why not
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.IOException; | |
| import java.nio.charset.StandardCharsets; | |
| import java.util.regex.Matcher; | |
| import java.util.regex.Pattern; | |
| import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; | |
| import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; | |
| import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; | |
| import org.slf4j.Logger; | |
| import org.slf4j.LoggerFactory; | |
| public class XlsStyleHelper { | |
| static Logger logger = LoggerFactory.getLogger(XlsStyleHelper.class); | |
| private XlsStyleHelper() { | |
| } | |
| public static ByteArrayOutputStream forceTextStyleInXls(ByteArrayOutputStream originalZip) throws IOException { | |
| ByteArrayOutputStream modifiedXLS = new ByteArrayOutputStream(); | |
| try (ZipArchiveInputStream zis = new ZipArchiveInputStream(new ByteArrayInputStream(originalZip.toByteArray())); ZipArchiveOutputStream zos = new ZipArchiveOutputStream(modifiedXLS)) { | |
| ZipArchiveEntry entry; | |
| while ((entry = zis.getNextEntry()) != null) { | |
| if ("xl/styles.xml".equals(entry.getName())) { | |
| copyEntryAndReplace(entry.getName(), zis, zos, false); | |
| } else if ("xl/worksheets/sheet1.xml".equals(entry.getName())) { | |
| copyEntryAndReplace(entry.getName(), zis, zos, true); | |
| } else { | |
| copyEntry(zis, zos, entry); | |
| } | |
| } | |
| } | |
| return modifiedXLS; | |
| } | |
| public static void copyEntry(ZipArchiveInputStream zis, ZipArchiveOutputStream zos, ZipArchiveEntry entry) throws IOException { | |
| zos.putArchiveEntry(new ZipArchiveEntry(entry.getName())); | |
| byte[] buffer = new byte[1024]; | |
| int len; | |
| while ((len = zis.read(buffer)) > 0) { | |
| zos.write(buffer, 0, len); | |
| } | |
| zos.closeArchiveEntry(); | |
| } | |
| public static void copyEntryAndReplace(String entryName, ZipArchiveInputStream zis, ZipArchiveOutputStream zos, boolean isSheet) throws IOException { | |
| ByteArrayOutputStream entryOutputStream = new ByteArrayOutputStream(); | |
| byte[] buffer = new byte[1024]; | |
| int len; | |
| while ((len = zis.read(buffer)) > 0) { | |
| entryOutputStream.write(buffer, 0, len); | |
| } | |
| // Replace the content in styles.xml | |
| String content = entryOutputStream.toString(StandardCharsets.UTF_8); | |
| if (isSheet) { | |
| content = enforceTextType(content); | |
| content = enforceTextStyle(content); | |
| } else { | |
| content = addTextCellStyle(content); | |
| } | |
| ZipArchiveEntry newEntry = new ZipArchiveEntry(entryName); | |
| newEntry.setSize(content.getBytes(StandardCharsets.UTF_8).length); | |
| newEntry.setUnixMode(0); | |
| newEntry.setTime(System.currentTimeMillis()); | |
| zos.putArchiveEntry(newEntry); | |
| zos.write(content.getBytes(StandardCharsets.UTF_8)); | |
| zos.closeArchiveEntry(); | |
| } | |
| private static String addTextCellStyle(String content) { | |
| String[] from = { "<cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/></cellXfs>" }; | |
| String[] to = { | |
| "<cellXfs count=\"2\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/><xf numFmtId=\"49\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/></cellXfs>" }; | |
| for (int i = 0; i < from.length; i++) { | |
| content = content.replace(from[i], to[i]); | |
| } | |
| return content; | |
| } | |
| private static String enforceTextType(String content) { | |
| String regex = "<c r=\"([^\"]+)\" t=\"n\"><v>(\\d+\\.\\d+)<\\/v><\\/c>"; | |
| Pattern pattern = Pattern.compile(regex); | |
| Matcher matcher = pattern.matcher(content); | |
| return matcher.replaceAll("<c r=\"$1\" t=\"inlineStr\" s=\"1\"><is><t>$2</t></is></c>"); | |
| } | |
| private static String enforceTextStyle(String content) { | |
| // enforce text style everywhere | |
| String regex = "<([^<>]+)(?<!s=\"1\") t=\"s\">"; // Match tag string tags that don't have styling 1 (TEXT) yet | |
| Pattern pattern = Pattern.compile(regex); | |
| Matcher matcher = pattern.matcher(content); | |
| StringBuilder modifiedInput = new StringBuilder(); | |
| int lastEnd = 0; | |
| while (matcher.find()) { | |
| modifiedInput.append(content, lastEnd, matcher.start()); | |
| String modifiedTag = matcher.group(0).replace(" t=\"s\"", " s=\"1\" t=\"s\""); | |
| modifiedInput.append(modifiedTag); | |
| lastEnd = matcher.end(); | |
| } | |
| modifiedInput.append(content.substring(lastEnd)); | |
| content = modifiedInput.toString(); | |
| return content; | |
| } | |
| public static String cleanStringForXls(String input) { | |
| String regex = "(\\!)*\\[[^\\]]*]\\(([^\\)]+)\\)"; | |
| Pattern pattern = Pattern.compile(regex); | |
| Matcher matcher = pattern.matcher(input); | |
| StringBuilder sb = new StringBuilder(); | |
| while (matcher.find()) { | |
| String replacemenet = "error"; | |
| try { | |
| replacemenet = matcher.group(2).startsWith("api/1") ? "" : matcher.group(2); | |
| } catch (Exception e) { | |
| logger.warn("Error while cleaning string for xls", e); | |
| } | |
| matcher.appendReplacement(sb, replacemenet); | |
| } | |
| matcher.appendTail(sb); | |
| return sb.toString().replace("\\", "\\\\"); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment