aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.gradle10
-rw-r--r--build.xml15
-rw-r--r--doap_POI.rdf7
-rw-r--r--legal/NOTICE2
-rw-r--r--osgi/pom.xml4
-rw-r--r--poi-ooxml-lite/missing-xsbs.txt1
-rw-r--r--poi-ooxml/build.gradle2
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java2
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java4
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java2
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java22
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/AutoSizeColumnTracker.java4
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java3
-rw-r--r--poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFSheet.java56
-rw-r--r--poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java8
-rw-r--r--poi-scratchpad/src/main/java/org/apache/poi/hwpf/HWPFDocument.java3
-rw-r--r--poi-scratchpad/src/main/java/org/apache/poi/hwpf/converter/AbstractWordUtils.java5
-rw-r--r--poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/PAPBinTable.java4
-rw-r--r--poi-scratchpad/src/test/java/org/apache/poi/hsmf/TestBasics.java38
-rw-r--r--poi-scratchpad/src/test/java/org/apache/poi/hwpf/model/TestPAPBinTable.java3
-rw-r--r--poi-scratchpad/src/test/java/org/apache/poi/hwpf/usermodel/TestBugs.java126
-rw-r--r--poi/src/main/java/org/apache/poi/common/usermodel/HyperlinkType.java15
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java4
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java3
-rw-r--r--poi/src/main/java/org/apache/poi/sl/extractor/SlideShowExtractor.java4
-rw-r--r--poi/src/main/java/org/apache/poi/ss/formula/functions/TextFunction.java13
-rw-r--r--poi/src/main/java/org/apache/poi/ss/usermodel/DataFormatter.java14
-rw-r--r--poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java4
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/record/TestRecordFactory.java35
-rw-r--r--poi/src/test/java/org/apache/poi/ss/formula/eval/TestFormulaBugs.java26
-rw-r--r--poi/src/test/java/org/apache/poi/ss/formula/functions/TestValue.java11
-rw-r--r--poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestHyperlink.java117
-rw-r--r--poi/src/test/java/org/apache/poi/ss/usermodel/TestDataFormatter.java23
-rw-r--r--poi/src/test/java/org/apache/poi/util/DefaultTempFileCreationStrategyTest.java149
-rw-r--r--test-data/diagram/clusterfuzz-testcase-minimized-POIVisioFuzzer-5842659694215168.vsdxbin0 -> 35605 bytes
-rw-r--r--test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5195207308541952.docbin0 -> 57344 bytes
-rw-r--r--test-data/document/header_footer_replace.docbin0 -> 28160 bytes
-rw-r--r--test-data/poifs/MailSentPropertyMultiple.msgbin0 -> 22528 bytes
-rw-r--r--test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-4838644450394112.pptxbin0 -> 23331 bytes
-rw-r--r--test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-4986044400861184.pptxbin0 -> 68600 bytes
-rw-r--r--test-data/spreadsheet/stress.xlsbin70656 -> 72192 bytes
41 files changed, 631 insertions, 108 deletions
diff --git a/build.gradle b/build.gradle
index 83ae193dd5..13ea2c604f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -37,7 +37,7 @@ plugins {
id "com.github.spotbugs" version '6.0.27'
id 'de.thetaphi.forbiddenapis' version '3.8'
id 'org.sonarqube' version '4.0.0.2929'
- id 'org.cyclonedx.bom' version '1.10.0'
+ id 'org.cyclonedx.bom' version '2.0.0'
id 'com.adarshr.test-logger' version '3.2.0'
}
@@ -100,7 +100,7 @@ allprojects {
// apply plugin: 'eclipse'
apply plugin: 'idea'
- version = '5.4.0'
+ version = '5.4.1-SNAPSHOT'
}
/**
@@ -118,8 +118,8 @@ subprojects {
apply plugin: 'com.adarshr.test-logger'
ext {
- bouncyCastleVersion = '1.79'
- commonsCodecVersion = '1.17.1'
+ bouncyCastleVersion = '1.80'
+ commonsCodecVersion = '1.17.2'
commonsCompressVersion = '1.27.1'
commonsIoVersion = '2.18.0'
commonsMathVersion = '3.6.1'
@@ -215,7 +215,7 @@ subprojects {
if (jdkVersion > 8) addBooleanOption('html5', true)
addBooleanOption('Xdoclint:all,-missing', true)
links 'https://poi.apache.org/apidocs/dev/'
- links 'https://docs.oracle.com/javase/8/docs/api/'
+ if (jdkVersion >= 23) links 'https://docs.oracle.com/en/java/javase/23/docs/api/' else links 'https://docs.oracle.com/javase/8/docs/api/'
links 'https://xmlbeans.apache.org/docs/5.0.0/'
links 'https://commons.apache.org/proper/commons-compress/apidocs/'
use = true
diff --git a/build.xml b/build.xml
index a386b61d4b..92c9977505 100644
--- a/build.xml
+++ b/build.xml
@@ -42,7 +42,7 @@ under the License.
<description>The Apache POI project Ant build.</description>
- <property name="version.id" value="5.4.0"/>
+ <property name="version.id" value="5.4.1-SNAPSHOT"/>
<property name="release.rc" value=""/>
<property environment="env"/>
@@ -263,7 +263,7 @@ under the License.
<!-- jars in the /lib directory, see the fetch-jars target-->
- <dependency prefix="main.commons-codec" artifact="commons-codec:commons-codec:1.17.1" usage="main"/>
+ <dependency prefix="main.commons-codec" artifact="commons-codec:commons-codec:1.17.2" usage="main"/>
<dependency prefix="main.commons-collections4" artifact="org.apache.commons:commons-collections4:4.4" usage="main"/>
<dependency prefix="main.commons-math3" artifact="org.apache.commons:commons-math3:3.6.1" usage="main"/>
<dependency prefix="main.commons-io" artifact="commons-io:commons-io:2.18.0" usage="main"/>
@@ -296,11 +296,11 @@ under the License.
<!-- xml signature libs - not part of the distribution -->
<dependency prefix="dsig.xmlsec" artifact="org.apache.santuario:xmlsec:3.0.5" usage="ooxml-provided"/>
- <dependency prefix="dsig.bouncycastle-prov" artifact="org.bouncycastle:bcprov-jdk18on:1.79" usage="ooxml-provided"/>
- <dependency prefix="dsig.bouncycastle-pkix" artifact="org.bouncycastle:bcpkix-jdk18on:1.79" usage="ooxml-provided"/>
- <dependency prefix="dsig.bouncycastle-util" artifact="org.bouncycastle:bcutil-jdk18on:1.79" usage="ooxml-provided"/>
+ <dependency prefix="dsig.bouncycastle-prov" artifact="org.bouncycastle:bcprov-jdk18on:1.80" usage="ooxml-provided"/>
+ <dependency prefix="dsig.bouncycastle-pkix" artifact="org.bouncycastle:bcpkix-jdk18on:1.80" usage="ooxml-provided"/>
+ <dependency prefix="dsig.bouncycastle-util" artifact="org.bouncycastle:bcutil-jdk18on:1.80" usage="ooxml-provided"/>
<!-- only used for signing the release - not used with the ooxml signatures -->
- <dependency prefix="dsig.bouncycastle-bcpg" artifact="org.bouncycastle:bcpg-jdk18on:1.79" usage="util"/>
+ <dependency prefix="dsig.bouncycastle-bcpg" artifact="org.bouncycastle:bcpg-jdk18on:1.80" usage="util"/>
<dependency prefix="ooxml.test.stax2" artifact="org.codehaus.woodstox:stax2-api:4.2.1" usage="ooxml-provided"/>
<!-- svg/batik/pdf libs - not part of the distribution - move batik to its own directory because of JPMS module-path issues -->
@@ -332,7 +332,7 @@ under the License.
<!-- jars in the ooxml-lib directory, see the fetch-ooxml-jars target-->
<dependency prefix="ooxml.curvesapi" artifact="com.github.virtuald:curvesapi:1.08" usage="ooxml"/>
- <dependency prefix="ooxml.xmlbeans" artifact="org.apache.xmlbeans:xmlbeans:5.2.2" usage="ooxml"/>
+ <dependency prefix="ooxml.xmlbeans" artifact="org.apache.xmlbeans:xmlbeans:5.3.0" usage="ooxml"/>
<dependency prefix="ooxml.commons-compress" artifact="org.apache.commons:commons-compress:1.27.1" usage="ooxml"/>
<dependency prefix="ooxml.commons-lang3" artifact="org.apache.commons:commons-lang3:3.12.0" usage="ooxml"/>
@@ -689,6 +689,7 @@ under the License.
<include name="xmlbeans-4*.jar"/>
<include name="xmlbeans-5.0*.jar"/>
<include name="xmlbeans-5.1.0.jar"/>
+ <include name="xmlbeans-5.2.2.jar"/>
</fileset>
<fileset dir="${basedir}/lib/ooxml-provided">
<include name="bc*-1.6*.jar"/>
diff --git a/doap_POI.rdf b/doap_POI.rdf
index b3d0b7e2e4..88606eb0bd 100644
--- a/doap_POI.rdf
+++ b/doap_POI.rdf
@@ -37,6 +37,13 @@
<category rdf:resource="https://projects.apache.org/category/library" />
<release>
<Version>
+ <name>Apache POI 5.4.0</name>
+ <created>2025-01-08</created>
+ <revision>5.4.0</revision>
+ </Version>
+ </release>
+ <release>
+ <Version>
<name>Apache POI 5.3.0</name>
<created>2024-07-02</created>
<revision>5.3.0</revision>
diff --git a/legal/NOTICE b/legal/NOTICE
index 2ca7e7b6af..d600ff75df 100644
--- a/legal/NOTICE
+++ b/legal/NOTICE
@@ -1,5 +1,5 @@
Apache POI
-Copyright 2003-2024 The Apache Software Foundation
+Copyright 2003-2025 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).
diff --git a/osgi/pom.xml b/osgi/pom.xml
index 51acd6b66e..c4854f4c12 100644
--- a/osgi/pom.xml
+++ b/osgi/pom.xml
@@ -24,12 +24,12 @@
<groupId>org.apache.poi</groupId>
<artifactId>poi-bundle</artifactId>
<packaging>bundle</packaging>
- <version>5.4.0</version>
+ <version>5.4.1-SNAPSHOT</version>
<name>Apache POI OSGi bundle</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <poi.version>5.4.0</poi.version>
+ <poi.version>5.4.1-SNAPSHOT</poi.version>
<pax.exam.version>4.14.0</pax.exam.version>
</properties>
diff --git a/poi-ooxml-lite/missing-xsbs.txt b/poi-ooxml-lite/missing-xsbs.txt
index cd5b063e63..49a2c51783 100644
--- a/poi-ooxml-lite/missing-xsbs.txt
+++ b/poi-ooxml-lite/missing-xsbs.txt
@@ -128,3 +128,4 @@ ststrokelinestylea509type
sttargetscreensize4357type
stverticalanchor22cctype
stwrapside6d02type
+wsdrd172doctype \ No newline at end of file
diff --git a/poi-ooxml/build.gradle b/poi-ooxml/build.gradle
index 843c21ead5..0ec5fda5d6 100644
--- a/poi-ooxml/build.gradle
+++ b/poi-ooxml/build.gradle
@@ -233,7 +233,7 @@ javadoc {
options {
if (jdkVersion > 8) addBooleanOption('html5', true)
links 'https://poi.apache.org/apidocs/dev/'
- links 'https://docs.oracle.com/javase/8/docs/api/'
+ if (jdkVersion >= 23) links 'https://docs.oracle.com/en/java/javase/23/docs/api/' else links 'https://docs.oracle.com/javase/8/docs/api/'
use = true
splitIndex = true
source = "1.8"
diff --git a/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java b/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
index a2987a742b..4b487fcc9e 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
@@ -154,7 +154,7 @@ import org.w3c.dom.events.MutationEvent;
* <p>To use SignatureInfo and its sibling classes, you'll need to have the following libs
* in the classpath:</p>
* <ul>
- * <li>BouncyCastle bcpkix and bcprov (tested against 1.79)</li>
+ * <li>BouncyCastle bcpkix and bcprov (tested against 1.80)</li>
* <li>Apache Santuario "xmlsec" (tested against 3.0.x)</li>
* <li>and log4j-api (tested against 2.22.x)</li>
* </ul>
diff --git a/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java b/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java
index 1dcb6cbdfc..99561a6a7d 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java
@@ -73,6 +73,10 @@ public class EllipticalArcTo implements GeometryRow {
for (CellType cell : row.getCellArray()) {
String cellName = cell.getN();
+ if (cellName == null) {
+ throw new POIXMLException("Missing cellName in EllipticalArcTo row");
+ }
+
switch (cellName) {
case "X":
x = XDGFCell.parseDoubleValue(cell);
diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java
index b554cdd6a9..4dcc60e77b 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java
@@ -79,7 +79,7 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFSh
}
CTPoint2D off = xfrm.getOff();
- if (off == null) {
+ if (off == null || off.getX() == null || off.getY() == null) {
throw new IllegalArgumentException("Could not retrieve Off from the XML object");
}
double x = Units.toPoints(POIXMLUnits.parseLength(off.xgetX()));
diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java
index 5e9a58f511..b5cc39728b 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java
@@ -394,8 +394,12 @@ public class XSSFSheetXMLHandler extends DefaultHandler {
// Try to use the value as a formattable number
double d = Double.parseDouble(fv);
thisStr = formatter.formatRawCellContents(d, this.formatIndex, this.formatString);
- } catch (NumberFormatException e) {
+ } catch (Exception e) {
// Formula is a String result not a Numeric one
+ LOG.atInfo().log(
+ "Error formatting cell '{}' - will use its raw value instead (format '{}')",
+ cellRef,
+ this.formatString);
thisStr = fv;
}
} else {
@@ -426,10 +430,20 @@ public class XSSFSheetXMLHandler extends DefaultHandler {
case NUMBER:
String n = value.toString();
- if (this.formatString != null && n.length() > 0)
- thisStr = formatter.formatRawCellContents(Double.parseDouble(n), this.formatIndex, this.formatString);
- else
+ if (this.formatString != null && n.length() > 0) {
+ try {
+ thisStr = formatter.formatRawCellContents(
+ Double.parseDouble(n), this.formatIndex, this.formatString);
+ } catch (Exception e) {
+ LOG.atInfo().log(
+ "Error formatting cell '{}' - will use its raw value instead (format '{}')",
+ cellRef,
+ this.formatString);
+ thisStr = n;
+ }
+ } else {
thisStr = n;
+ }
break;
default:
diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/AutoSizeColumnTracker.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/AutoSizeColumnTracker.java
index b6b674a53b..de85d9a4d4 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/AutoSizeColumnTracker.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/AutoSizeColumnTracker.java
@@ -392,9 +392,9 @@ import org.apache.poi.util.Internal;
*/
private void updateColumnWidth(final Cell cell, final ColumnWidthPair pair) {
final double unmergedWidth =
- SheetUtil.getCellWidth(cell, defaultCharWidth, dataFormatter, false) + arbitraryExtraWidth;
+ SheetUtil.getCellWidth(cell, defaultCharWidth, dataFormatter, false);
final double mergedWidth =
- SheetUtil.getCellWidth(cell, defaultCharWidth, dataFormatter, true) + arbitraryExtraWidth;
+ SheetUtil.getCellWidth(cell, defaultCharWidth, dataFormatter, true);
pair.setMaxColumnWidths(unmergedWidth, mergedWidth);
}
}
diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
index 08b7fb9c45..fc5462620e 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
@@ -1666,7 +1666,8 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
}
// get the best-fit width of rows currently in the random access window
- final int activeWidth = (int) (256 * SheetUtil.getColumnWidth(this, column, useMergedCells));
+ final double w1 = SheetUtil.getColumnWidth(this, column, useMergedCells);
+ final int activeWidth = (int) ((256 * w1) + getArbitraryExtraWidth());
// the best-fit width for both flushed rows and random access window rows
// flushedWidth or activeWidth may be negative if column contains only blank cells
diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFSheet.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFSheet.java
index 015f608cf2..5ab64a69aa 100644
--- a/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFSheet.java
+++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFSheet.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
@@ -45,7 +46,7 @@ public final class TestSXSSFSheet extends BaseTestXSheet {
@AfterEach
- void tearDown(){
+ void tearDown() {
SXSSFITestDataProvider.instance.cleanup();
}
@@ -84,9 +85,9 @@ public final class TestSXSSFSheet extends BaseTestXSheet {
}
/**
- * Bug 35084: cloning cells with formula
- *
- * The test is disabled because cloning of sheets is not supported in SXSSF
+ * Bug 35084: cloning cells with formula
+ * <p>
+ * The test is disabled because cloning of sheets is not supported in SXSSF
*/
@Override
@Test
@@ -193,9 +194,9 @@ public final class TestSXSSFSheet extends BaseTestXSheet {
//one level
sheet.groupRow(9, 10);
- try(UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get()) {
+ try (UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get()) {
workbook.write(bos);
- try(XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+ try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
CTRow ctrow = xssfSheet.getRow(9).getCTRow();
@@ -223,9 +224,9 @@ public final class TestSXSSFSheet extends BaseTestXSheet {
//two level
sheet.groupRow(10, 13);
- try(UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get()) {
+ try (UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get()) {
workbook.write(bos);
- try(XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+ try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
CTRow ctrow = xssfSheet.getRow(9).getCTRow();
@@ -242,4 +243,43 @@ public final class TestSXSSFSheet extends BaseTestXSheet {
}
}
}
+
+ @Test
+ void autosizeWithArbitraryExtraWidth() throws IOException {
+ final int extra = 100;
+ final String longText =
+ "This is a very long text that will exceed default column width for sure.";
+ int width0, width1 = 0;
+ try (SXSSFWorkbook workbook0 = new SXSSFWorkbook()) {
+ SXSSFSheet sheet = workbook0.createSheet();
+ sheet.trackColumnForAutoSizing(0);
+
+ SXSSFRow row = sheet.createRow(0);
+ row.createCell(0).setCellValue(longText);
+ sheet.autoSizeColumn(0);
+
+ try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+ workbook0.write(bos);
+ }
+ width0 = sheet.getColumnWidth(0);
+ }
+
+ try (SXSSFWorkbook workbook1 = new SXSSFWorkbook()) {
+ SXSSFSheet sheet = workbook1.createSheet();
+ sheet.setArbitraryExtraWidth(extra);
+ sheet.trackColumnForAutoSizing(0);
+
+ SXSSFRow row = sheet.createRow(0);
+ row.createCell(0).setCellValue(longText);
+ sheet.autoSizeColumn(0);
+
+ try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+ workbook1.write(bos);
+ }
+ width1 = sheet.getColumnWidth(0);
+ }
+
+ assertEquals(width0 + extra, width1);
+ }
+
}
diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java b/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java
index 91692e3897..7b037f3e5b 100644
--- a/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java
+++ b/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java
@@ -209,6 +209,12 @@ public abstract class PropertiesChunk extends Chunk {
int id = LittleEndian.readUShort(value);
long flags = LittleEndian.readUInt(value);
+ boolean multivalued = false;
+ if ((typeID & Types.MULTIVALUED_FLAG) != 0) {
+ typeID -= Types.MULTIVALUED_FLAG;
+ multivalued = true;
+ }
+
// Turn the Type and ID into helper objects
MAPIType type = Types.getById(typeID);
MAPIProperty prop = MAPIProperty.get(id);
@@ -255,7 +261,7 @@ public abstract class PropertiesChunk extends Chunk {
// to another chunk which holds the data itself
boolean isPointer = false;
int length = type.getLength();
- if (type.isPointer()) {
+ if (type.isPointer() || multivalued) {
isPointer = true;
length = 8;
}
diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/HWPFDocument.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/HWPFDocument.java
index 3c4275b2a9..a4fd555a91 100644
--- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/HWPFDocument.java
+++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/HWPFDocument.java
@@ -765,7 +765,8 @@ public final class HWPFDocument extends HWPFDocumentCore {
// write out the PAPBinTable.
_fib.setFcPlcfbtePapx(tableOffset);
- _pbt.writeTo(wordDocumentStream, tableStream, _cft.getTextPieceTable());
+ // Right now we don't know how to save dataStream modifications, so we can just pipe them to a black hole.
+ _pbt.writeTo(wordDocumentStream, tableStream, new ByteArrayOutputStream(), _cft.getTextPieceTable());
_fib.setLcbPlcfbtePapx(tableStream.size() - tableOffset);
tableOffset = tableStream.size();
diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/converter/AbstractWordUtils.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/converter/AbstractWordUtils.java
index dc996af43d..9ef569fc6b 100644
--- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/converter/AbstractWordUtils.java
+++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/converter/AbstractWordUtils.java
@@ -240,6 +240,11 @@ public class AbstractWordUtils {
}
}
+ // ensure the format does not grow too large, number-format
+ // can be roman-numbers, where very large numbers would have
+ // very many "M" and thus may cause memory to overload
+ IOUtils.safelyAllocateCheck(num, MAX_BULLET_BUFFER_SIZE/10);
+
bulletBuffer.append( NumberFormatter.getNumber( num,
list.getNumberFormat( level ) ) );
} else {
diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/PAPBinTable.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/PAPBinTable.java
index 32e9ca9a36..225fc97e32 100644
--- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/PAPBinTable.java
+++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/PAPBinTable.java
@@ -366,7 +366,7 @@ public class PAPBinTable
}
public void writeTo( ByteArrayOutputStream wordDocumentStream,
- ByteArrayOutputStream tableStream, CharIndexTranslator translator )
+ ByteArrayOutputStream tableStream, ByteArrayOutputStream dataStream, CharIndexTranslator translator )
throws IOException
{
@@ -401,7 +401,7 @@ public class PAPBinTable
PAPFormattedDiskPage pfkp = new PAPFormattedDiskPage();
pfkp.fill(overflow);
- byte[] bufFkp = pfkp.toByteArray(tableStream, translator);
+ byte[] bufFkp = pfkp.toByteArray(dataStream, translator);
wordDocumentStream.write(bufFkp);
overflow = pfkp.getOverflow();
diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hsmf/TestBasics.java b/poi-scratchpad/src/test/java/org/apache/poi/hsmf/TestBasics.java
index 83f59bd789..49dea29aff 100644
--- a/poi-scratchpad/src/test/java/org/apache/poi/hsmf/TestBasics.java
+++ b/poi-scratchpad/src/test/java/org/apache/poi/hsmf/TestBasics.java
@@ -19,13 +19,20 @@ package org.apache.poi.hsmf;
import static org.apache.poi.POITestCase.assertContains;
import static org.apache.poi.POITestCase.assertStartsWith;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+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.IOException;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hsmf.datatypes.AttachmentChunks;
+import org.apache.poi.hsmf.datatypes.Chunks;
import org.apache.poi.hsmf.datatypes.DirectoryChunk;
+import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.hsmf.exceptions.ChunkNotFoundException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
@@ -273,4 +280,33 @@ public final class TestBasics {
}
}
}
+
+ @Test
+ void testBug69315() throws Exception {
+ POIDataSamples testData = POIDataSamples.getPOIFSInstance();
+ try (MAPIMessage mapi = new MAPIMessage(testData.openResourceAsStream("MailSentPropertyMultiple.msg"))) {
+ assertNotNull(mapi.getAttachmentFiles());
+ assertNotNull(mapi.getDisplayBCC());
+ assertNotNull(mapi.getMessageDate());
+
+ Chunks chunks = mapi.getMainChunks();
+ assertNotNull(chunks);
+ assertNotNull(chunks.getRawProperties());
+ assertNotNull(chunks.getRawProperties().get(MAPIProperty.CLIENT_SUBMIT_TIME));
+
+ AttachmentChunks[] attachments = mapi.getAttachmentFiles();
+ for (AttachmentChunks attachment : attachments) {
+ DirectoryChunk chunkDirectory = attachment.getAttachmentDirectory();
+ if (chunkDirectory != null) {
+ MAPIMessage attachmentMSG = chunkDirectory.getAsEmbeddedMessage();
+ assertNotNull(attachmentMSG);
+ String body = attachmentMSG.getTextBody();
+ assertNotNull(body);
+ }
+ }
+
+ assertNull(mapi.getSummaryInformation());
+ assertNull(mapi.getDocumentSummaryInformation());
+ }
+ }
}
diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/model/TestPAPBinTable.java b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/model/TestPAPBinTable.java
index b65843ab63..6588162260 100644
--- a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/model/TestPAPBinTable.java
+++ b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/model/TestPAPBinTable.java
@@ -59,7 +59,8 @@ public final class TestPAPBinTable {
HWPFFileSystem fileSys = new HWPFFileSystem();
ByteArrayOutputStream tableOut = fileSys.getStream( "1Table" );
ByteArrayOutputStream mainOut = fileSys.getStream( "WordDocument" );
- _pAPBinTable.writeTo( mainOut, tableOut, fakeTPT );
+ ByteArrayOutputStream dataOut = fileSys.getStream("Data");
+ _pAPBinTable.writeTo(mainOut, tableOut, dataOut, fakeTPT);
byte[] newTableStream = tableOut.toByteArray();
byte[] newMainStream = mainOut.toByteArray();
diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/usermodel/TestBugs.java b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/usermodel/TestBugs.java
index be98329282..e2e6ce89de 100644
--- a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/usermodel/TestBugs.java
+++ b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/usermodel/TestBugs.java
@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
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.io.File;
import java.io.IOException;
@@ -36,6 +37,8 @@ import java.util.Collection;
import java.util.List;
import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.poi.extractor.ExtractorFactory;
+import org.apache.poi.extractor.POITextExtractor;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.HWPFOldDocument;
import org.apache.poi.hwpf.HWPFTestDataSamples;
@@ -314,64 +317,66 @@ class TestBugs {
* CharacterRun.replaceText()
*/
@Test
- void test47287() {
- HWPFDocument doc = openSampleFile("Bug47287.doc");
- String[] values = {"1-1", "1-2", "1-3", "1-4", "1-5", "1-6", "1-7",
- "1-8", "1-9", "1-10", "1-11", "1-12", "1-13", "1-14", "1-15",};
- int usedVal = 0;
- String PLACEHOLDER = "\u2002\u2002\u2002\u2002\u2002";
- Range r = doc.getRange();
- for (int x = 0; x < r.numSections(); x++) {
- Section s = r.getSection(x);
- for (int y = 0; y < s.numParagraphs(); y++) {
- Paragraph p = s.getParagraph(y);
-
- for (int z = 0; z < p.numCharacterRuns(); z++) {
- boolean isFound = false;
-
- // character run
- CharacterRun run = p.getCharacterRun(z);
- // character run text
- String text = run.text();
- String oldText = text;
- int c = text.indexOf("FORMTEXT ");
- if (c < 0) {
- int k = text.indexOf(PLACEHOLDER);
- if (k >= 0) {
- text = text.substring(0, k) + values[usedVal]
- + text.substring(k + PLACEHOLDER.length());
- usedVal++;
- isFound = true;
- }
- } else {
- for (; c >= 0; c = text.indexOf("FORMTEXT ", c
- + "FORMTEXT ".length())) {
- int k = text.indexOf(PLACEHOLDER, c);
+ void test47287() throws IOException {
+ try (HWPFDocument doc = openSampleFile("Bug47287.doc")) {
+ String[] values = { "1-1", "1-2", "1-3", "1-4", "1-5", "1-6", "1-7",
+ "1-8", "1-9", "1-10", "1-11", "1-12", "1-13", "1-14", "1-15",
+ };
+ int usedVal = 0;
+ String PLACEHOLDER = "\u2002\u2002\u2002\u2002\u2002";
+ Range r = doc.getRange();
+ for (int x = 0; x < r.numSections(); x++) {
+ Section s = r.getSection(x);
+ for (int y = 0; y < s.numParagraphs(); y++) {
+ Paragraph p = s.getParagraph(y);
+
+ for (int z = 0; z < p.numCharacterRuns(); z++) {
+ boolean isFound = false;
+
+ // character run
+ CharacterRun run = p.getCharacterRun(z);
+ // character run text
+ String text = run.text();
+ String oldText = text;
+ int c = text.indexOf("FORMTEXT ");
+ if (c < 0) {
+ int k = text.indexOf(PLACEHOLDER);
if (k >= 0) {
- text = text.substring(0, k)
- + values[usedVal]
- + text.substring(k
- + PLACEHOLDER.length());
+ text = text.substring(0, k) + values[usedVal]
+ + text.substring(k + PLACEHOLDER.length());
usedVal++;
isFound = true;
}
+ } else {
+ for (; c >= 0; c = text.indexOf("FORMTEXT ", c
+ + "FORMTEXT ".length())) {
+ int k = text.indexOf(PLACEHOLDER, c);
+ if (k >= 0) {
+ text = text.substring(0, k)
+ + values[usedVal]
+ + text.substring(k
+ + PLACEHOLDER.length());
+ usedVal++;
+ isFound = true;
+ }
+ }
+ }
+ if (isFound) {
+ run.replaceText(oldText, text, 0);
}
- }
- if (isFound) {
- run.replaceText(oldText, text, 0);
- }
+ }
}
}
- }
- String docText = r.text();
+ String docText = r.text();
- assertContains(docText, "1-1");
- assertContains(docText, "1-12");
+ assertContains(docText, "1-1");
+ assertContains(docText, "1-12");
- assertNotContained(docText, "1-13");
- assertNotContained(docText, "1-15");
+ assertNotContained(docText, "1-13");
+ assertNotContained(docText, "1-15");
+ }
}
/**
@@ -718,10 +723,13 @@ class TestBugs {
assertEquals(section2NumColumns, section.getNumColumns());
}
+ /**
+ * [RESOLVED FIXED] Bug 57603 - failed to create Word 2003 with seven or more columns
+ */
@Test
void test57603SevenRowTable() throws Exception {
try (HWPFDocument hwpfDocument = openSampleFile("57603-seven_columns.doc")) {
- assertThrows(ArrayIndexOutOfBoundsException.class, () -> HWPFTestDataSamples.writeOutAndReadBack(hwpfDocument));
+ HWPFTestDataSamples.writeOutAndReadBack(hwpfDocument);
}
}
@@ -780,4 +788,26 @@ class TestBugs {
assertNotNull(pictures);
}
}
+
+ //
+ @Test
+ void test58805() throws IOException {
+ try (HWPFDocument doc = openSampleFile("header_footer_replace.doc")) {
+
+ Range oRange = doc.getHeaderStoryRange();
+ for (int i = 0; i < oRange.numCharacterRuns(); i++) {
+ CharacterRun run = oRange.getCharacterRun(i);
+ run.replaceText("_TEST_", "This text is longer than the initial text. It goes on and on without interruption.");
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ doc.write(out);
+ out.flush();
+
+ POITextExtractor extractor = ExtractorFactory.createExtractor(new ByteArrayInputStream(out.toByteArray()));
+ assertThrows(IllegalArgumentException.class,
+ () -> /*String text =*/ extractor.getText());
+ // assertFalse(text.contains("_TEST_"), "Had: " + text);
+ }
+ }
}
diff --git a/poi/src/main/java/org/apache/poi/common/usermodel/HyperlinkType.java b/poi/src/main/java/org/apache/poi/common/usermodel/HyperlinkType.java
index 6152a85e45..d38fa16c88 100644
--- a/poi/src/main/java/org/apache/poi/common/usermodel/HyperlinkType.java
+++ b/poi/src/main/java/org/apache/poi/common/usermodel/HyperlinkType.java
@@ -25,7 +25,7 @@ public enum HyperlinkType {
/** Not a hyperlink */
@Internal
NONE(-1),
-
+
/**
* Link to an existing file or web page
*/
@@ -37,7 +37,10 @@ public enum HyperlinkType {
DOCUMENT(2),
/**
- * Link to an E-mail address
+ * Link to an E-mail address.
+ *
+ * Please note that this currently only works if the address in the hyperlink
+ * uses the prefix "mailto:" as the binary formats do not persis this type.
*/
EMAIL(3),
@@ -45,13 +48,13 @@ public enum HyperlinkType {
* Link to a file
*/
FILE(4);
-
-
+
+
/** @deprecated POI 3.15 beta 3 */
@Internal(since="3.15 beta 3")
@Deprecated
private final int code;
-
+
/**
* The codes don't have any real meaning.
* They are bytes that are read in and written out from HSSF, HSLF, XSSF, and XSLF are different
@@ -66,7 +69,7 @@ public enum HyperlinkType {
HyperlinkType(int code) {
this.code = code;
}
-
+
/**
* @deprecated POI 3.15 beta 3
*
diff --git a/poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java b/poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java
index 16f1a04d04..08b49f5be4 100644
--- a/poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java
+++ b/poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java
@@ -34,8 +34,8 @@ import org.apache.poi.util.RecordFormatException;
public final class RecordFactory {
private static final int NUM_RECORDS = 512;
- // how many records we read at max by default (can be adjusted via IOUtils)
- //increased to 5 million due to https://bz.apache.org/bugzilla/show_bug.cgi?id=65887
+ // how many records we read at max by default (can be adjusted via the static setters)
+ // increased to 5 million due to https://bz.apache.org/bugzilla/show_bug.cgi?id=65887
private static final int DEFAULT_MAX_NUMBER_OF_RECORDS = 5_000_000;
private static int MAX_NUMBER_OF_RECORDS = DEFAULT_MAX_NUMBER_OF_RECORDS;
diff --git a/poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java b/poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java
index 06420f394b..561768f32b 100644
--- a/poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java
+++ b/poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java
@@ -216,7 +216,8 @@ public final class RecordInputStream implements LittleEndianInput {
_currentDataLength = _bhi.readDataSize();
if (_currentDataLength > MAX_RECORD_DATA_SIZE) {
throw new RecordFormatException("The content of an excel record cannot exceed "
- + MAX_RECORD_DATA_SIZE + " bytes");
+ + MAX_RECORD_DATA_SIZE + " bytes, but had: " + _currentDataLength +
+ " for record with sid: " + _currentSid);
}
}
diff --git a/poi/src/main/java/org/apache/poi/sl/extractor/SlideShowExtractor.java b/poi/src/main/java/org/apache/poi/sl/extractor/SlideShowExtractor.java
index 1b1480caa6..7579f8a9f8 100644
--- a/poi/src/main/java/org/apache/poi/sl/extractor/SlideShowExtractor.java
+++ b/poi/src/main/java/org/apache/poi/sl/extractor/SlideShowExtractor.java
@@ -364,6 +364,10 @@ public class SlideShowExtractor<
// PowerPoint seems to store files with \r as the line break
// The messes things up on everything but a Mac, so translate them to \n
String txt = tr.getRawText();
+ if (txt == null) {
+ return "";
+ }
+
txt = txt.replace('\r', '\n');
txt = txt.replace((char) 0x0B, sep);
diff --git a/poi/src/main/java/org/apache/poi/ss/formula/functions/TextFunction.java b/poi/src/main/java/org/apache/poi/ss/formula/functions/TextFunction.java
index 518fe08e0c..5a2b613c93 100644
--- a/poi/src/main/java/org/apache/poi/ss/formula/functions/TextFunction.java
+++ b/poi/src/main/java/org/apache/poi/ss/formula/functions/TextFunction.java
@@ -108,12 +108,14 @@ public abstract class TextFunction implements Function {
return new NumberEval(arg.length());
}
};
+
public static final Function LOWER = new SingleArgTextFunc() {
@Override
protected ValueEval evaluate(String arg) {
return new StringEval(arg.toLowerCase(Locale.ROOT));
}
};
+
public static final Function UPPER = new SingleArgTextFunc() {
@Override
protected ValueEval evaluate(String arg) {
@@ -246,13 +248,16 @@ public abstract class TextFunction implements Function {
private static final class LeftRight extends Var1or2ArgFunction {
private static final ValueEval DEFAULT_ARG1 = new NumberEval(1.0);
private final boolean _isLeft;
+
protected LeftRight(boolean isLeft) {
_isLeft = isLeft;
}
+
@Override
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
return evaluate(srcRowIndex, srcColumnIndex, arg0, DEFAULT_ARG1);
}
+
@Override
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
ValueEval arg1) {
@@ -369,7 +374,6 @@ public abstract class TextFunction implements Function {
try {
valueDouble = DateUtil.parseDateTime(evaluated);
} catch (Exception ignored) {
- valueDouble = null;
}
}
}
@@ -393,7 +397,7 @@ public abstract class TextFunction implements Function {
* Using it instead of {@link OperandResolver#coerceValueToString(ValueEval)} in order to handle booleans differently.
*/
private String formatPatternValueEval2String(ValueEval ve) {
- String format = null;
+ final String format;
if (!(ve instanceof BoolEval) && (ve instanceof StringValueEval)) {
StringValueEval sve = (StringValueEval) ve;
format = sve.getStringValue();
@@ -414,6 +418,7 @@ public abstract class TextFunction implements Function {
public SearchFind(boolean isCaseSensitive) {
_isCaseSensitive = isCaseSensitive;
}
+
@Override
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
try {
@@ -424,6 +429,7 @@ public abstract class TextFunction implements Function {
return e.getErrorEval();
}
}
+
@Override
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
ValueEval arg2) {
@@ -440,6 +446,7 @@ public abstract class TextFunction implements Function {
return e.getErrorEval();
}
}
+
private ValueEval eval(String haystack, String needle, int startIndex) {
int result;
if (_isCaseSensitive) {
@@ -454,6 +461,7 @@ public abstract class TextFunction implements Function {
return new NumberEval(result + 1.);
}
}
+
/**
* Implementation of the FIND() function.<p>
*
@@ -468,6 +476,7 @@ public abstract class TextFunction implements Function {
* Author: Torstein Tauno Svendsen (torstei@officenet.no)
*/
public static final Function FIND = new SearchFind(true);
+
/**
* Implementation of the FIND() function.<p>
*
diff --git a/poi/src/main/java/org/apache/poi/ss/usermodel/DataFormatter.java b/poi/src/main/java/org/apache/poi/ss/usermodel/DataFormatter.java
index b3de0462b0..bfe795025e 100644
--- a/poi/src/main/java/org/apache/poi/ss/usermodel/DataFormatter.java
+++ b/poi/src/main/java/org/apache/poi/ss/usermodel/DataFormatter.java
@@ -163,6 +163,11 @@ public class DataFormatter {
private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})");
/**
+ * For handling '0#' properly
+ */
+ private static final Pattern decimalFormatFix = Pattern.compile("0+#");
+
+ /**
* Cells formatted with a date or time format and which contain invalid date or time values
* show 255 pound signs ("#").
*/
@@ -380,7 +385,7 @@ public class DataFormatter {
// String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0];
// this replace is done to fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63211
- String formatStr = formatStrIn.replace("\\%", "\'%\'");
+ String formatStr = formatStrIn.replace("\\%", "'%'");
// Excel supports 2+ part conditional data formats, eg positive/negative/zero,
// or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds
@@ -700,7 +705,7 @@ public class DataFormatter {
private String cleanFormatForNumber(String formatStrIn) {
// this replace is done to fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63211
- String formatStr = formatStrIn.replace("\\%", "\'%\'");
+ String formatStr = formatStrIn.replace("\\%", "'%'");
StringBuilder sb = new StringBuilder(formatStr);
@@ -850,6 +855,11 @@ public class DataFormatter {
}
}
+ // Excel ignores leading zeros, but Java fails with an exception below
+ if (decimalFormatFix.matcher(format).matches()) {
+ format = "#";
+ }
+
try {
return new InternalDecimalFormatWithScale(format, symbols);
} catch(IllegalArgumentException iae) {
diff --git a/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java b/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java
index bead9cef3c..0f44be4279 100644
--- a/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java
+++ b/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java
@@ -78,8 +78,8 @@ public class DateUtil {
private static final Pattern date_ptrn5 = Pattern.compile("^\\[DBNum([123])]");
private static final DateTimeFormatter dateTimeFormats = new DateTimeFormatterBuilder()
- .appendPattern("[dd MMM[ yyyy]][[ ]h:m[:s][.SSS] a][[ ]H:m[:s][.SSS]]")
- .appendPattern("[[yyyy ]dd-MMM[-yyyy]][[ ]h:m[:s][.SSS] a][[ ]H:m[:s][.SSS]]")
+ .appendPattern("[d[.] [MMMM][MMM][ yyyy]][[ ]h:m[:s][.SSS] a][[ ]H:m[:s][.SSS]]")
+ .appendPattern("[[yyyy ]d-[MMMM][MMM][-yyyy]][[ ]h:m[:s][.SSS] a][[ ]H:m[:s][.SSS]]")
.appendPattern("[M/dd[/yyyy]][[ ]h:m[:s][.SSS] a][[ ]H:m[:s][.SSS]]")
.appendPattern("[[yyyy/]M/dd][[ ]h:m[:s][.SSS] a][[ ]H:m[:s][.SSS]]")
.parseDefaulting(ChronoField.YEAR_OF_ERA, LocaleUtil.getLocaleCalendar().get(Calendar.YEAR))
diff --git a/poi/src/test/java/org/apache/poi/hssf/record/TestRecordFactory.java b/poi/src/test/java/org/apache/poi/hssf/record/TestRecordFactory.java
index 26793e1930..a44cce0e42 100644
--- a/poi/src/test/java/org/apache/poi/hssf/record/TestRecordFactory.java
+++ b/poi/src/test/java/org/apache/poi/hssf/record/TestRecordFactory.java
@@ -19,7 +19,8 @@ package org.apache.poi.hssf.record;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -27,13 +28,17 @@ import java.io.InputStream;
import java.util.List;
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
+import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.HexRead;
+import org.apache.poi.util.RecordFormatException;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.Isolated;
/**
* Tests the record factory
*/
+@Isolated // changes static values, so other tests should not run at the same time
final class TestRecordFactory {
@@ -178,11 +183,11 @@ final class TestRecordFactory {
List<org.apache.poi.hssf.record.Record> records = RecordFactory.createRecords(new ByteArrayInputStream(data));
assertEquals(5, records.size());
- assertTrue(records.get(0) instanceof ObjRecord);
- assertTrue(records.get(1) instanceof DrawingRecord);
- assertTrue(records.get(2) instanceof TextObjectRecord);
- assertTrue(records.get(3) instanceof ContinueRecord);
- assertTrue(records.get(4) instanceof ObjRecord);
+ assertInstanceOf(ObjRecord.class, records.get(0));
+ assertInstanceOf(DrawingRecord.class, records.get(1));
+ assertInstanceOf(TextObjectRecord.class, records.get(2));
+ assertInstanceOf(ContinueRecord.class, records.get(3));
+ assertInstanceOf(ObjRecord.class, records.get(4));
//serialize and verify that the serialized data is the same as the original
UnsynchronizedByteArrayOutputStream out = UnsynchronizedByteArrayOutputStream.builder().get();
@@ -231,4 +236,22 @@ final class TestRecordFactory {
assertEquals(5, outRecs.size());
}
}
+
+ @Test
+ void testMaxNumberOfRecords() {
+ int prev = RecordFactory.getMaxNumberOfRecords();
+
+ try {
+ // check setter/getter
+ RecordFactory.setMaxNumberOfRecords(0);
+ assertEquals(0, RecordFactory.getMaxNumberOfRecords());
+
+ // check exception when exceeding the limit
+ //noinspection resource
+ assertThrows(RecordFormatException.class,
+ () -> HSSFTestDataSamples.openSampleWorkbook("SampleSS.xls"));
+ } finally {
+ RecordFactory.setMaxNumberOfRecords(prev);
+ }
+ }
}
diff --git a/poi/src/test/java/org/apache/poi/ss/formula/eval/TestFormulaBugs.java b/poi/src/test/java/org/apache/poi/ss/formula/eval/TestFormulaBugs.java
index 1e03fa8621..6692fc81b4 100644
--- a/poi/src/test/java/org/apache/poi/ss/formula/eval/TestFormulaBugs.java
+++ b/poi/src/test/java/org/apache/poi/ss/formula/eval/TestFormulaBugs.java
@@ -243,4 +243,30 @@ final class TestFormulaBugs {
CellValue value = evaluateFormulaInCell(wb, cell, formula);
assertEquals(expectedValue, value.getStringValue());
}
+
+ @Test
+ public void testFormula_58571() throws IOException {
+ try (Workbook wb = new HSSFWorkbook()) {
+ FormulaEvaluator eval = wb.getCreationHelper().createFormulaEvaluator();
+
+ Sheet sheet = wb.createSheet("test");
+ Row row = sheet.createRow(0);
+ Cell cell = row.createCell(0);
+ // it seems the two double-quotes are replaced with one double-quote when parsing the function
+ // this does not work: cell.setCellFormula("TEXT(B1, \"h\"\"h\"\" m\"\"m\"\"\")");
+ cell.setCellFormula("TEXT(B1, \"h\"\"\"\"h\"\"\"\" m\"\"\"\"m\"\"\"\"\")");
+
+ Cell cellValue = row.createCell(1);
+ cellValue.setCellValue(0.0104166666666666);
+
+ CellValue value = eval.evaluate(cell);
+ assertEquals(CellType.STRING, value.getCellType());
+ assertEquals("0h 15m", value.getStringValue());
+
+ cellValue.setCellValue(1.123);
+ value = eval.evaluate(cell);
+ assertEquals(CellType.STRING, value.getCellType());
+ assertEquals("0h 15m", value.getStringValue());
+ }
+ }
}
diff --git a/poi/src/test/java/org/apache/poi/ss/formula/functions/TestValue.java b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestValue.java
index 33d1e7bfa4..a5d6e8948b 100644
--- a/poi/src/test/java/org/apache/poi/ss/formula/functions/TestValue.java
+++ b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestValue.java
@@ -32,6 +32,9 @@ import org.apache.poi.ss.util.Utils;
import org.junit.jupiter.api.Test;
import java.io.IOException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
/**
* Tests for {@link Value}
@@ -86,6 +89,14 @@ final class TestValue {
}
@Test
+ void testDates() {
+ confirmValue("1 January 2025", 45658);
+ confirmValue("01 January 2025", 45658);
+ confirmValue("1 Jan 2025", 45658);
+ confirmValue("01 Jan 2025", 45658);
+ }
+
+ @Test
void testErrors() {
confirmValueError("1+1");
confirmValueError("1 1");
diff --git a/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestHyperlink.java b/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestHyperlink.java
index e5ef5312d9..76f11890c0 100644
--- a/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestHyperlink.java
+++ b/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestHyperlink.java
@@ -21,11 +21,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.ss.ITestDataProvider;
+import org.apache.poi.ss.SpreadsheetVersion;
import org.junit.jupiter.api.Test;
/**
@@ -142,5 +145,119 @@ public abstract class BaseTestHyperlink {
wb.close();
}
+ @Test
+ void testHyperlinkEmailType69265_https() throws IOException {
+ boolean isHSSF = _testDataProvider.getSpreadsheetVersion() == SpreadsheetVersion.EXCEL97;
+
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ try (Workbook workbook = _testDataProvider.createWorkbook()) {
+ Sheet sheet = workbook.createSheet("Hyperlink Example");
+
+ Row row = sheet.createRow(0);
+ Cell cell = row.createCell(0);
+
+ // Create a hyperlink
+ CreationHelper createHelper = workbook.getCreationHelper();
+ Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.EMAIL);
+ hyperlink.setLabel("mylabel");
+ hyperlink.setAddress("https://www.example.com");
+
+ // Set the label and the hyperlink
+ cell.setCellValue("Click here");
+ cell.setHyperlink(hyperlink);
+
+ // Get the cell value and hyperlink address
+ assertEquals("Click here", cell.getStringCellValue());
+ Hyperlink cellHyperlink = cell.getHyperlink();
+ assertEquals("https://www.example.com", cellHyperlink.getAddress());
+
+ // HSSF does not support Email, thus falls back to URL, HSSF also uses a hardcoded "label"
+ assertEquals(
+ isHSSF ? HyperlinkType.URL : HyperlinkType.EMAIL,
+ cellHyperlink.getType());
+ assertEquals(
+ isHSSF ? "url" : "mylabel",
+ cellHyperlink.getLabel());
+
+ workbook.write(out);
+ }
+
+ out.flush();
+
+ try (Workbook wbBack = WorkbookFactory.create(new ByteArrayInputStream(out.toByteArray()))) {
+ Sheet sheet = wbBack.getSheet("Hyperlink Example");
+ Row row = sheet.getRow(0);
+ Cell cell = row.getCell(0);
+
+ Hyperlink hyperlink = cell.getHyperlink();
+
+ // when not using "mailto:", it is reverted back to URL currently
+ assertEquals(
+ HyperlinkType.URL,
+ hyperlink.getType());
+ assertEquals(
+ isHSSF ? "url" : "mylabel",
+ hyperlink.getLabel());
+ }
+ }
+ }
+
+ @Test
+ void testHyperlinkEmailType69265_mailto() throws IOException {
+ boolean isHSSF = _testDataProvider.getSpreadsheetVersion() == SpreadsheetVersion.EXCEL97;
+
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ try (Workbook workbook = _testDataProvider.createWorkbook()) {
+ Sheet sheet = workbook.createSheet("Hyperlink Example");
+
+ Row row = sheet.createRow(0);
+ Cell cell = row.createCell(0);
+
+ // Create a hyperlink
+ CreationHelper createHelper = workbook.getCreationHelper();
+ Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.EMAIL);
+ hyperlink.setLabel("mylabel");
+ hyperlink.setAddress("mailto://www.example.com");
+
+ // Set the label and the hyperlink
+ cell.setCellValue("Click here");
+ cell.setHyperlink(hyperlink);
+
+ // Get the cell value and hyperlink address
+ assertEquals("Click here", cell.getStringCellValue());
+ Hyperlink cellHyperlink = cell.getHyperlink();
+ assertEquals("mailto://www.example.com", cellHyperlink.getAddress());
+
+ // "mailto:" is converted to type "EMAIL"
+ assertEquals(
+ HyperlinkType.EMAIL,
+ cellHyperlink.getType());
+ assertEquals(
+ isHSSF ? "url" : "mylabel",
+ cellHyperlink.getLabel());
+
+ workbook.write(out);
+ }
+
+ out.flush();
+
+ try (Workbook wbBack = WorkbookFactory.create(new ByteArrayInputStream(out.toByteArray()))) {
+ Sheet sheet = wbBack.getSheet("Hyperlink Example");
+ Row row = sheet.getRow(0);
+ Cell cell = row.getCell(0);
+
+ Hyperlink hyperlink = cell.getHyperlink();
+
+ // "mailto:" is converted to type "EMAIL"
+ assertEquals(
+ HyperlinkType.EMAIL,
+ hyperlink.getType());
+ assertEquals(
+ isHSSF ? "url" : "mylabel",
+ hyperlink.getLabel());
+ }
+ }
+ }
+
public abstract Hyperlink copyHyperlink(Hyperlink link);
}
diff --git a/poi/src/test/java/org/apache/poi/ss/usermodel/TestDataFormatter.java b/poi/src/test/java/org/apache/poi/ss/usermodel/TestDataFormatter.java
index cf6c2c7e9d..0d49bee3ab 100644
--- a/poi/src/test/java/org/apache/poi/ss/usermodel/TestDataFormatter.java
+++ b/poi/src/test/java/org/apache/poi/ss/usermodel/TestDataFormatter.java
@@ -1207,4 +1207,27 @@ class TestDataFormatter {
assertEquals("25571.751069247686", df.formatCellValue(cell));*/
}
}
+
+ @Test
+ void testBug65190() {
+ DataFormatter formatter = new DataFormatter(Locale.ENGLISH);
+
+ assertEquals("12334567890",
+ formatter.formatRawCellContents(12334567890.0, 0, "0"));
+ assertEquals("12334567890",
+ formatter.formatRawCellContents(12334567890.0, 0, "#"));
+ assertEquals("12334567890",
+ formatter.formatRawCellContents(12334567890.0, 0, "#0"));
+ assertEquals("12334567890",
+ formatter.formatRawCellContents(12334567890.0, 0, "0#"));
+
+ assertEquals("12334567890123",
+ formatter.formatRawCellContents(12334567890123.0, 0, "0"));
+ assertEquals("12334567890123",
+ formatter.formatRawCellContents(12334567890123.0, 0, "#"));
+ assertEquals("12334567890123",
+ formatter.formatRawCellContents(12334567890123.0, 0, "#0"));
+ assertEquals("12334567890123",
+ formatter.formatRawCellContents(12334567890123.0, 0, "0#"));
+ }
}
diff --git a/poi/src/test/java/org/apache/poi/util/DefaultTempFileCreationStrategyTest.java b/poi/src/test/java/org/apache/poi/util/DefaultTempFileCreationStrategyTest.java
new file mode 100644
index 0000000000..4e4f6b779e
--- /dev/null
+++ b/poi/src/test/java/org/apache/poi/util/DefaultTempFileCreationStrategyTest.java
@@ -0,0 +1,149 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.util;
+
+import static org.apache.poi.util.DefaultTempFileCreationStrategy.DELETE_FILES_ON_EXIT;
+import static org.apache.poi.util.DefaultTempFileCreationStrategy.POIFILES;
+import static org.apache.poi.util.TempFile.JAVA_IO_TMPDIR;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class DefaultTempFileCreationStrategyTest {
+
+ private String propBefore;
+ private String tmpBefore;
+
+ @BeforeEach
+ void before() {
+ propBefore = System.getProperty(DELETE_FILES_ON_EXIT);
+ tmpBefore = System.getProperty(JAVA_IO_TMPDIR);
+ }
+
+ @AfterEach
+ void after() {
+ if (propBefore == null) {
+ System.clearProperty(DELETE_FILES_ON_EXIT);
+ } else {
+ System.setProperty(DELETE_FILES_ON_EXIT, propBefore);
+ }
+
+ System.setProperty(JAVA_IO_TMPDIR, tmpBefore);
+ }
+
+ @Test
+ void testDefaultFile() throws IOException {
+ DefaultTempFileCreationStrategy strategy = new DefaultTempFileCreationStrategy();
+ checkGetFile(strategy);
+ }
+
+ private static void checkGetFile(DefaultTempFileCreationStrategy strategy) throws IOException {
+ File file = strategy.createTempFile("POITest", ".tmp");
+ try {
+ assertTrue(file.getParentFile().exists(),
+ "Failed for " + file.getParentFile());
+
+ assertTrue(file.exists(),
+ "Failed for " + file);
+ } finally {
+ assertTrue(file.delete());
+ }
+ }
+
+ @Test
+ void testDefaultDir() throws IOException {
+ DefaultTempFileCreationStrategy strategy = new DefaultTempFileCreationStrategy();
+ File dir = strategy.createTempDirectory("POITest");
+ try {
+ assertTrue(dir.getParentFile().exists(),
+ "Failed for " + dir.getParentFile());
+
+ assertTrue(dir.exists(),
+ "Failed for " + dir);
+ } finally {
+ assertTrue(dir.delete());
+ }
+ }
+
+ @Test
+ void testWithProperty() throws IOException {
+ System.setProperty(DELETE_FILES_ON_EXIT, "true");
+
+ // we can set the property, but not easily check if it works
+ // so let's just call the main method
+ testDefaultFile();
+ }
+
+ @Test
+ void testEmptyTempDir() {
+ System.clearProperty(JAVA_IO_TMPDIR);
+
+ DefaultTempFileCreationStrategy strategy = new DefaultTempFileCreationStrategy();
+ assertThrows(IOException.class,
+ () -> strategy.createTempDirectory("POITest"));
+ }
+
+ @Test
+ void testCustomDir() throws IOException {
+ File dirTest = File.createTempFile("POITest", ".dir");
+ try {
+ assertTrue(dirTest.delete());
+
+ DefaultTempFileCreationStrategy strategy = new DefaultTempFileCreationStrategy(dirTest);
+ checkGetFile(strategy);
+ } finally {
+ FileUtils.deleteDirectory(dirTest);
+ }
+ }
+
+ @Test
+ void testCustomDirExists() throws IOException {
+ File dirTest = File.createTempFile("POITest", ".dir");
+ try {
+ assertTrue(dirTest.delete());
+ assertTrue(dirTest.mkdir());
+
+ DefaultTempFileCreationStrategy strategy = new DefaultTempFileCreationStrategy(dirTest);
+ checkGetFile(strategy);
+ } finally {
+ FileUtils.deleteDirectory(dirTest);
+ }
+ }
+
+ @Test
+ void testCustomDirAndPoiFilesExists() throws IOException {
+ File dirTest = File.createTempFile("POITest", ".dir");
+ try {
+ assertTrue(dirTest.delete());
+ assertTrue(dirTest.mkdir());
+ assertTrue(new File(dirTest, POIFILES).mkdir());
+
+ DefaultTempFileCreationStrategy strategy = new DefaultTempFileCreationStrategy(dirTest);
+ checkGetFile(strategy);
+ } finally {
+ FileUtils.deleteDirectory(dirTest);
+ }
+ }
+} \ No newline at end of file
diff --git a/test-data/diagram/clusterfuzz-testcase-minimized-POIVisioFuzzer-5842659694215168.vsdx b/test-data/diagram/clusterfuzz-testcase-minimized-POIVisioFuzzer-5842659694215168.vsdx
new file mode 100644
index 0000000000..e6e6a3a902
--- /dev/null
+++ b/test-data/diagram/clusterfuzz-testcase-minimized-POIVisioFuzzer-5842659694215168.vsdx
Binary files differ
diff --git a/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5195207308541952.doc b/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5195207308541952.doc
new file mode 100644
index 0000000000..1d36046066
--- /dev/null
+++ b/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5195207308541952.doc
Binary files differ
diff --git a/test-data/document/header_footer_replace.doc b/test-data/document/header_footer_replace.doc
new file mode 100644
index 0000000000..029ccb56f2
--- /dev/null
+++ b/test-data/document/header_footer_replace.doc
Binary files differ
diff --git a/test-data/poifs/MailSentPropertyMultiple.msg b/test-data/poifs/MailSentPropertyMultiple.msg
new file mode 100644
index 0000000000..d07feb178e
--- /dev/null
+++ b/test-data/poifs/MailSentPropertyMultiple.msg
Binary files differ
diff --git a/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-4838644450394112.pptx b/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-4838644450394112.pptx
new file mode 100644
index 0000000000..b223ef94f8
--- /dev/null
+++ b/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-4838644450394112.pptx
Binary files differ
diff --git a/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-4986044400861184.pptx b/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-4986044400861184.pptx
new file mode 100644
index 0000000000..df4a713ff0
--- /dev/null
+++ b/test-data/slideshow/clusterfuzz-testcase-minimized-POIXSLFFuzzer-4986044400861184.pptx
Binary files differ
diff --git a/test-data/spreadsheet/stress.xls b/test-data/spreadsheet/stress.xls
index 1667a30b74..ab8a975ae8 100644
--- a/test-data/spreadsheet/stress.xls
+++ b/test-data/spreadsheet/stress.xls
Binary files differ