This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new 0d253449b6 TIKA-4993: two workarounds for 6.0.0 (#3434)
0d253449b6 is described below

commit 0d253449b6f178ecc5ba80663f6c113b06de0b0a
Author: Tim Allison <[email protected]>
AuthorDate: Tue Oct 27 12:07:18 2026 -0400

    TIKA-4993: two workarounds for 6.0.0 (#3434)
---
 .../tika/parser/microsoft/ExcelExtractor.java      |  8 +++-
 .../tika/parser/microsoft/HSLFExtractor.java       | 27 ++++++++++-
 .../parser/microsoft/TikaExcelDataFormatter.java   | 16 ++++---
 .../tika/parser/microsoft/ExcelParserTest.java     | 28 +++++++++++
 .../parser/microsoft/PowerPointParserTest.java     | 55 ++++++++++++++++++++++
 5 files changed, 125 insertions(+), 9 deletions(-)

diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ExcelExtractor.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ExcelExtractor.java
index 7a2a7ab90f..d93f9e781f 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ExcelExtractor.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ExcelExtractor.java
@@ -892,7 +892,13 @@ public class ExcelExtractor extends AbstractPOIFSExtractor 
{
                             .formatRawCellContents(value, 
getFormatIndex(cell), formatString,
                                     false);
                 }
-                return super.formatNumberDateCell(cell);
+                try {
+                    return super.formatNumberDateCell(cell);
+                } catch (RuntimeException e) {
+                    // TODO: remove on POI 6.0.1, as in TikaExcelDataFormatter
+                    return tikaExcelDataFormatter.formatRawCellContents(value, 
0, "General",
+                            false);
+                }
             }
         }
     }
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/HSLFExtractor.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/HSLFExtractor.java
index b3c0e76adc..99116d094f 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/HSLFExtractor.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/HSLFExtractor.java
@@ -17,6 +17,7 @@
 package org.apache.tika.parser.microsoft;
 
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -28,6 +29,7 @@ import org.apache.poi.common.usermodel.Hyperlink;
 import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
 import org.apache.poi.hslf.model.HeadersFooters;
 import org.apache.poi.hslf.record.DocInfoListContainer;
+import org.apache.poi.hslf.record.ExOleObjStg;
 import org.apache.poi.hslf.record.RecordContainer;
 import org.apache.poi.hslf.record.RecordTypes;
 import org.apache.poi.hslf.record.VBAInfoAtom;
@@ -53,6 +55,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.sl.usermodel.Comment;
 import org.apache.poi.sl.usermodel.ShapeContainer;
 import org.apache.poi.sl.usermodel.SimpleShape;
+import org.apache.poi.util.RecordFormatException;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
@@ -81,6 +84,23 @@ public class HSLFExtractor extends AbstractPOIFSExtractor {
 
     private static final int EXT_TIME_NODE_CONTAINER = 0xf144;
 
+    /**
+     * POI 6 caps an embedded OLE object at 100 MB uncompressed; POI 5 had no 
cap, and real
+     * files carry objects over 130 MB.
+     */
+    public static final int MAX_OLE_OBJECT_LENGTH = 250_000_000;
+
+    static {
+        raiseOleObjectLength();
+    }
+
+    /** Sets POI's static OLE object cap; never lowers one the caller already 
raised. */
+    public static void raiseOleObjectLength() {
+        if (ExOleObjStg.getMaxRecordLength() < MAX_OLE_OBJECT_LENGTH) {
+            ExOleObjStg.setMaxRecordLength(MAX_OLE_OBJECT_LENGTH);
+        }
+    }
+
     public HSLFExtractor(ParseContext context, Metadata metadata) {
         super(context, metadata);
     }
@@ -239,7 +259,8 @@ public class HSLFExtractor extends AbstractPOIFSExtractor {
                         //coz ppts can have empty pfs...shrug...
                         //we may need to add more stringent requirements
                         if (pfs.getRoot().getEntryNames().size() < 1) {
-                            return;
+                            i++;
+                            continue;
                         }
                         Metadata metadata = Metadata.newInstance(context);
                         if (inferredExtension) {
@@ -268,7 +289,9 @@ public class HSLFExtractor extends AbstractPOIFSExtractor {
                                 null, null, xhtml, true);
                     }
                 }
-            } catch (IOException | TikaException e) {
+            } catch (IOException | TikaException | RecordFormatException | 
UncheckedIOException e) {
+                // POI 6 throws these for an object over MAX_OLE_OBJECT_LENGTH 
or longer than it
+                // claims; skip it, keep the rest
                 EmbeddedDocumentUtil.recordException(e, parentMetadata, 
context);
             }
             i++;
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/TikaExcelDataFormatter.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/TikaExcelDataFormatter.java
index 532c7d47b0..4841b11169 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/TikaExcelDataFormatter.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/TikaExcelDataFormatter.java
@@ -44,13 +44,17 @@ public class TikaExcelDataFormatter extends DataFormatter {
     @Override
     public String formatRawCellContents(double value, int formatIndex, String 
formatString,
                                         boolean use1904Windowing) {
-        if (DateUtil.isADateFormat(formatIndex, formatString)) {
-            String activeDateFormatString =
-                    (dateOverrideFormatString == null) ? formatString : 
dateOverrideFormatString;
-            return super.formatRawCellContents(value, formatIndex, 
activeDateFormatString,
+        String activeFormatString = formatString;
+        if (dateOverrideFormatString != null && 
DateUtil.isADateFormat(formatIndex, formatString)) {
+            activeFormatString = dateOverrideFormatString;
+        }
+        try {
+            return super.formatRawCellContents(value, formatIndex, 
activeFormatString,
                     use1904Windowing);
-        } else {
-            return super.formatRawCellContents(value, formatIndex, 
formatString, use1904Windowing);
+        } catch (RuntimeException e) {
+            // TODO: remove on POI 6.0.1, whose DataFormatter falls back per 
value itself; 6.0.0
+            // throws from SimpleFraction on overflow for a fraction format 
with text, e.g. #\ ???/???
+            return super.formatRawCellContents(value, 0, "General", 
use1904Windowing);
         }
     }
 
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/ExcelParserTest.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/ExcelParserTest.java
index 900b1f6397..a70bae5148 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/ExcelParserTest.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/ExcelParserTest.java
@@ -31,6 +31,8 @@ import java.util.UUID;
 import org.apache.poi.hpsf.CustomProperties;
 import org.apache.poi.hpsf.DocumentSummaryInformation;
 import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.util.LocaleUtil;
 import org.junit.jupiter.api.AfterAll;
@@ -809,4 +811,30 @@ public class ExcelParserTest extends TikaTest {
         }
         assertTrue(imagesChecked >= 4, "expected at least four embedded 
images, got " + imagesChecked);
     }
+
+    // POI 6.0.0 throws from SimpleFraction for the second value once the 
format is cached
+    @Test
+    public void testFractionOverflowKeepsSheet() throws Exception {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (HSSFWorkbook wb = new HSSFWorkbook()) {
+            HSSFCellStyle style = wb.createCellStyle();
+            style.setDataFormat(wb.createDataFormat().getFormat("#\\ 
???/???"));
+            HSSFSheet sheet = wb.createSheet();
+            double[] values = {1.25, 0.00308641975308642};
+            for (int i = 0; i < values.length; i++) {
+                sheet.createRow(i).createCell(0).setCellValue(values[i]);
+                sheet.getRow(i).getCell(0).setCellStyle(style);
+            }
+            sheet.createRow(2).createCell(0).setCellValue("after");
+            wb.write(bos);
+        }
+        String xml;
+        try (TikaInputStream tis = TikaInputStream.get(bos.toByteArray())) {
+            xml = getXML(tis, AUTO_DETECT_PARSER, new Metadata()).xml;
+        }
+        assertContains("1   1/4", xml);
+        // falls back to General; POI 6.0.1 shows this as "1/324"
+        assertContains("0.0030864198", xml);
+        assertContains("after", xml);
+    }
 }
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/PowerPointParserTest.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/PowerPointParserTest.java
index a6efbdc0a0..00db2b089e 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/PowerPointParserTest.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/PowerPointParserTest.java
@@ -22,10 +22,17 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
+import org.apache.poi.hslf.record.ExOleObjStg;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.LocaleUtil;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
@@ -467,4 +474,52 @@ public class PowerPointParserTest extends TikaTest {
         }
         assertTrue(imagesChecked > 0, "expected at least one embedded image");
     }
+
+    @Test
+    public void testOleObjectCapRaised() {
+        HSLFExtractor.raiseOleObjectLength();
+        assertTrue(ExOleObjStg.getMaxRecordLength() >= 
HSLFExtractor.MAX_OLE_OBJECT_LENGTH);
+    }
+
+    // an object over the cap is recorded and skipped; it used to end the loop
+    @Test
+    public void testOleObjectOverCapKeepsLaterObjects() throws Exception {
+        byte[] big = xls("big", 2_000);
+        byte[] small = xls("small", 1);
+        ByteArrayOutputStream ppt = new ByteArrayOutputStream();
+        try (HSLFSlideShow ss = new HSLFSlideShow()) {
+            ss.createSlide();
+            ss.addEmbed(new POIFSFileSystem(new ByteArrayInputStream(big)));
+            ss.addEmbed(new POIFSFileSystem(new ByteArrayInputStream(small)));
+            ss.write(ppt);
+        }
+        HSLFExtractor.raiseOleObjectLength();
+        int cap = ExOleObjStg.getMaxRecordLength();
+        ExOleObjStg.setMaxRecordLength((big.length + small.length) / 2);
+        List<Metadata> metadataList;
+        try (TikaInputStream tis = TikaInputStream.get(ppt.toByteArray())) {
+            metadataList = getRecursiveMetadata(tis, true);
+        } finally {
+            ExOleObjStg.setMaxRecordLength(cap);
+        }
+        List<Metadata> xls = metadataList.stream()
+                .filter(m -> 
"application/vnd.ms-excel".equals(m.get(HttpHeaders.CONTENT_TYPE)))
+                .toList();
+        assertEquals(1, xls.size());
+        assertContains("small0", 
xls.get(0).get(TikaCoreProperties.TIKA_CONTENT));
+        assertContains("exceeds maximum allowed",
+                
metadataList.get(0).get(TikaCoreProperties.TIKA_META_EXCEPTION_WARNING));
+    }
+
+    private static byte[] xls(String text, int rows) throws Exception {
+        try (HSSFWorkbook wb = new HSSFWorkbook();
+                ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+            HSSFSheet sheet = wb.createSheet();
+            for (int r = 0; r < rows; r++) {
+                sheet.createRow(r).createCell(0).setCellValue(text + r);
+            }
+            wb.write(bos);
+            return bos.toByteArray();
+        }
+    }
 }

Reply via email to